NSXv BGP: Update edge bgp identifier on router GW updates

When rotuer GW port ip address is changed, we must check if the old
address was used for the edge bgp identifier, if so, update it with new
GW address.

Also takes care of router GW info NAT enable/disable updates.

Change-Id: I5ae25152f06d55b4d631735376df94a7eedb7d95
This commit is contained in:
Roey Chen 2017-04-16 02:30:43 -07:00
parent d85dd105b8
commit 82d045405e
4 changed files with 162 additions and 84 deletions

View File

@ -26,44 +26,7 @@ class EdgeDynamicRoutingDriver(object):
# it will be initialized at subclass
self.vcns = None
def _get_routing_global_config(self, edge_id):
h, config = self.vcns.get_dynamic_routing_service(edge_id)
global_config = config if config else {}
global_config.setdefault('ipPrefixes', {'ipPrefixes': []})
curr_prefixes = [{'ipPrefix': prx}
for prx in global_config['ipPrefixes']['ipPrefixes']]
global_config['ipPrefixes'] = curr_prefixes
return {'routingGlobalConfig': global_config}
def _update_global_routing_config(self, edge_id, **kwargs):
global_config = self._get_routing_global_config(edge_id)
current_prefixes = global_config['routingGlobalConfig']['ipPrefixes']
global_config['routingGlobalConfig']['ecmp'] = True
if 'router_id' in kwargs:
global_config['routingGlobalConfig']['routerId'] = (
kwargs['router_id'])
current_prefixes[:] = [p for p in current_prefixes
if p['ipPrefix']['name'] not in
kwargs.get('prefixes_to_remove', [])]
# Avoid adding duplicate rules when shared router relocation
current_prefixes.extend([p for p in kwargs.get('prefixes_to_add', [])
if p not in current_prefixes])
self.vcns.update_dynamic_routing_service(edge_id, global_config)
def _reset_routing_global_config(self, edge_id):
global_config = self._get_routing_global_config(edge_id)
global_config['routingGlobalConfig']['ecmp'] = False
global_config['routingGlobalConfig'].pop('routerId', None)
global_config['routingGlobalConfig'].pop('ipPrefixes', None)
self.vcns.update_dynamic_routing_service(edge_id, global_config)
def get_routing_bgp_config(self, edge_id):
h, config = self.vcns.get_bgp_routing_config(edge_id)
bgp_config = config if config else {}
def _prepare_bgp_config(self, bgp_config):
bgp_config.setdefault('enabled', False)
bgp_config.setdefault('bgpNeighbours', {'bgpNeighbours': []})
bgp_config.setdefault('redistribution', {'rules': {'rules': []}})
@ -78,6 +41,60 @@ class EdgeDynamicRoutingDriver(object):
redistribution_rules = [{'rule': rule} for rule in
bgp_config['redistribution']['rules']['rules']]
bgp_config['redistribution']['rules'] = redistribution_rules
def _get_routing_config(self, edge_id):
h, config = self.vcns.get_edge_routing_config(edge_id)
# Backend complains when adding this in the request.
config.pop('featureType')
config.pop('ospf')
global_config = config['routingGlobalConfig']
bgp_config = config.get('bgp', {})
self._prepare_bgp_config(bgp_config)
global_config.setdefault('ipPrefixes', {'ipPrefixes': []})
curr_prefixes = [{'ipPrefix': prx}
for prx in global_config['ipPrefixes']['ipPrefixes']]
global_config['ipPrefixes'] = curr_prefixes
static_routing = config.get('staticRouting', {})
static_routes = static_routing.get('staticRoutes', {})
current_routes = [{'route': route}
for route in static_routes.get('staticRoutes', [])]
static_routing['staticRoutes'] = current_routes
return {'routing': config}
def _update_routing_config(self, edge_id, **kwargs):
routing_config = self._get_routing_config(edge_id)
global_config = routing_config['routing']['routingGlobalConfig']
current_prefixes = global_config['ipPrefixes']
global_config['ecmp'] = True
if 'router_id' in kwargs:
global_config['routerId'] = kwargs['router_id']
current_prefixes[:] = [p for p in current_prefixes
if p['ipPrefix']['name'] not in
kwargs.get('prefixes_to_remove', [])]
# Avoid adding duplicate rules when shared router relocation
current_prefixes.extend([p for p in kwargs.get('prefixes_to_add', [])
if p not in current_prefixes])
self.vcns.update_edge_routing_config(edge_id, routing_config)
def _reset_routing_global_config(self, edge_id):
routing_config = self._get_routing_config(edge_id)
global_config = routing_config['routing']['routingGlobalConfig']
global_config['ecmp'] = False
global_config.pop('routerId')
global_config.pop('ipPrefixes')
self.vcns.update_edge_routing_config(edge_id, routing_config)
def get_routing_bgp_config(self, edge_id):
h, config = self.vcns.get_bgp_routing_config(edge_id)
bgp_config = config if config else {}
self._prepare_bgp_config(bgp_config)
return {'bgp': bgp_config}
def _update_bgp_routing_config(self, edge_id, **kwargs):
@ -136,9 +153,9 @@ class EdgeDynamicRoutingDriver(object):
enabled, default_routes, bgp_neighbours,
prefixes, redistribution_rules):
with locking.LockManager.get_lock(str(edge_id)):
self._update_global_routing_config(edge_id,
router_id=prot_router_id,
prefixes_to_add=prefixes)
self._update_routing_config(edge_id,
router_id=prot_router_id,
prefixes_to_add=prefixes)
self._update_bgp_routing_config(edge_id, enabled=enabled,
local_as=local_as,
neighbours_to_add=bgp_neighbours,
@ -169,12 +186,13 @@ class EdgeDynamicRoutingDriver(object):
if default_routes:
self._remove_default_static_routes(edge_id, default_routes)
def update_bgp_neighbour(self, edge_id, bgp_neighbour):
def update_bgp_neighbours(self, edge_id, neighbours_to_add=None,
neighbours_to_remove=None):
with locking.LockManager.get_lock(str(edge_id)):
self._update_bgp_routing_config(
edge_id,
neighbours_to_remove=[bgp_neighbour],
neighbours_to_add=[bgp_neighbour])
neighbours_to_add=neighbours_to_add,
neighbours_to_remove=neighbours_to_remove)
def update_routing_redistribution(self, edge_id, enabled):
with locking.LockManager.get_lock(str(edge_id)):
@ -182,19 +200,17 @@ class EdgeDynamicRoutingDriver(object):
def add_bgp_redistribution_rules(self, edge_id, prefixes, rules):
with locking.LockManager.get_lock(str(edge_id)):
self._update_global_routing_config(edge_id,
prefixes_to_add=prefixes)
self._update_routing_config(edge_id, prefixes_to_add=prefixes)
self._update_bgp_routing_config(edge_id, rules_to_add=rules)
LOG.debug("Added redistribution rules %s on edge %s", rules, edge_id)
def remove_bgp_redistribution_rules(self, edge_id, prefixes):
with locking.LockManager.get_lock(str(edge_id)):
self._update_bgp_routing_config(edge_id, rules_to_remove=prefixes)
self._update_global_routing_config(edge_id,
prefixes_to_remove=prefixes)
self._update_routing_config(edge_id, prefixes_to_remove=prefixes)
LOG.debug("Removed redistribution rules for prefixes %s on edge %s",
prefixes, edge_id)
def update_router_id(self, edge_id, router_id):
with locking.LockManager.get_lock(str(edge_id)):
self._update_global_routing_config(edge_id, router_id=router_id)
self._update_routing_config(edge_id, router_id=router_id)

View File

@ -84,7 +84,7 @@ CERTIFICATE = "certificate"
NETWORK_TYPES = ['Network', 'VirtualWire', 'DistributedVirtualPortgroup']
# Dynamic routing constants
GLOBAL_ROUTING_CONFIG = "routing/config/global"
ROUTING_CONFIG = "routing/config"
BGP_ROUTING_CONFIG = "routing/config/bgp"
@ -1070,14 +1070,14 @@ class Vcns(object):
h, apps = self.do_request(HTTP_GET, uri, decode=True)
return apps
def update_dynamic_routing_service(self, edge_id, request_config):
uri = self._build_uri_path(edge_id, GLOBAL_ROUTING_CONFIG)
def update_edge_routing_config(self, edge_id, request_config):
uri = self._build_uri_path(edge_id, ROUTING_CONFIG)
return self.do_request(HTTP_PUT, uri,
VcnsApiClient.xmldumps(request_config),
format='xml')
def get_dynamic_routing_service(self, edge_id):
uri = self._build_uri_path(edge_id, GLOBAL_ROUTING_CONFIG)
def get_edge_routing_config(self, edge_id):
uri = self._build_uri_path(edge_id, ROUTING_CONFIG)
return self.do_request(HTTP_GET, uri)
def update_bgp_dynamic_routing(self, edge_id, bgp_request):

View File

@ -61,7 +61,7 @@ class NSXvBgpPlugin(service_base.ServicePluginBase, bgp_db.BgpDbMixin):
events.AFTER_DELETE)
registry.subscribe(self.router_gateway_callback,
resources.ROUTER_GATEWAY,
events.AFTER_CREATE)
events.AFTER_UPDATE)
registry.subscribe(self.router_gateway_callback,
resources.ROUTER_GATEWAY,
events.AFTER_DELETE)
@ -80,7 +80,7 @@ class NSXvBgpPlugin(service_base.ServicePluginBase, bgp_db.BgpDbMixin):
def update_bgp_speaker(self, context, bgp_speaker_id, bgp_speaker):
with locking.LockManager.get_lock(str(bgp_speaker_id)):
self.nsxv_driver.update_bgp_speaker(context, bgp_speaker_id,
bgp_speaker)
bgp_speaker)
# TBD(roeyc): rolling back changes on edges base class call failed.
return super(NSXvBgpPlugin, self).update_bgp_speaker(
context, bgp_speaker_id, bgp_speaker)
@ -124,9 +124,9 @@ class NSXvBgpPlugin(service_base.ServicePluginBase, bgp_db.BgpDbMixin):
return peer
def update_bgp_peer(self, context, bgp_peer_id, bgp_peer):
self.nsxv_driver.update_bgp_peer(context, bgp_peer_id, bgp_peer)
super(NSXvBgpPlugin, self).update_bgp_peer(context,
bgp_peer_id, bgp_peer)
self.nsxv_driver.update_bgp_peer(context, bgp_peer_id, bgp_peer)
return self.get_bgp_peer(context, bgp_peer_id)
def delete_bgp_peer(self, context, bgp_peer_id):
@ -140,7 +140,7 @@ class NSXvBgpPlugin(service_base.ServicePluginBase, bgp_db.BgpDbMixin):
def add_bgp_peer(self, context, bgp_speaker_id, bgp_peer_info):
with locking.LockManager.get_lock(str(bgp_speaker_id)):
self.nsxv_driver.add_bgp_peer(context,
bgp_speaker_id, bgp_peer_info)
bgp_speaker_id, bgp_peer_info)
return super(NSXvBgpPlugin, self).add_bgp_peer(context,
bgp_speaker_id,
bgp_peer_info)
@ -151,15 +151,15 @@ class NSXvBgpPlugin(service_base.ServicePluginBase, bgp_db.BgpDbMixin):
if bgp_peer_info['bgp_peer_id'] not in speaker['peers']:
return
self.nsxv_driver.remove_bgp_peer(context,
bgp_speaker_id, bgp_peer_info)
bgp_speaker_id, bgp_peer_info)
return super(NSXvBgpPlugin, self).remove_bgp_peer(
context, bgp_speaker_id, bgp_peer_info)
def add_gateway_network(self, context, bgp_speaker_id, network_info):
with locking.LockManager.get_lock(str(bgp_speaker_id)):
self.nsxv_driver.add_gateway_network(context,
bgp_speaker_id,
network_info)
bgp_speaker_id,
network_info)
return super(NSXvBgpPlugin, self).add_gateway_network(
context, bgp_speaker_id, network_info)
@ -168,8 +168,8 @@ class NSXvBgpPlugin(service_base.ServicePluginBase, bgp_db.BgpDbMixin):
super(NSXvBgpPlugin, self).remove_gateway_network(
context, bgp_speaker_id, network_info)
self.nsxv_driver.remove_gateway_network(context,
bgp_speaker_id,
network_info)
bgp_speaker_id,
network_info)
def get_advertised_routes(self, context, bgp_speaker_id):
return super(NSXvBgpPlugin, self).get_advertised_routes(
@ -196,11 +196,11 @@ class NSXvBgpPlugin(service_base.ServicePluginBase, bgp_db.BgpDbMixin):
continue
if event == events.AFTER_CREATE:
self.nsxv_driver.advertise_subnet(context, speaker_id,
router_id, subnets[0])
router_id, subnets[0])
if event == events.AFTER_DELETE:
subnet_id = port['fixed_ips'][0]['subnet_id']
self.nsxv_driver.withdraw_subnet(context, speaker_id,
router_id, subnet_id)
router_id, subnet_id)
def router_gateway_callback(self, resource, event, trigger, **kwargs):
context = kwargs.get('context') or n_context.get_admin_context()
@ -217,9 +217,14 @@ class NSXvBgpPlugin(service_base.ServicePluginBase, bgp_db.BgpDbMixin):
if event == events.AFTER_DELETE:
gw_ips = kwargs['gateway_ips']
self.nsxv_driver.disable_bgp_on_router(context,
speaker,
router_id,
gw_ips[0])
speaker,
router_id,
gw_ips[0])
if event == events.AFTER_UPDATE:
updated_port = kwargs['updated_port']
router = kwargs['router']
self.nsxv_driver.process_router_gw_port_update(
context, speaker, router, updated_port)
def _before_service_edge_delete_callback(self, resource, event,
trigger, **kwargs):

View File

@ -254,8 +254,10 @@ class NSXvBgpDriver(object):
context.session, bgp_speaker_id)
for binding in bgp_bindings:
try:
self._nsxv.update_bgp_neighbour(binding['edge_id'],
neighbour)
# Neighbours are identified by their ip address
self._nsxv.update_bgp_neighbours(binding['edge_id'],
[neighbour],
[neighbour])
except vcns_exc.VcnsApiException:
LOG.error("Failed to update BGP neighbor '%s' on "
"edge '%s'", old_bgp_peer['peer_ip'],
@ -439,6 +441,59 @@ class NSXvBgpDriver(object):
context.session, bgp_speaker_id)
self._stop_bgp_on_edges(context, bgp_bindings, bgp_speaker_id)
def _update_edge_bgp_identifier(self, context, bgp_binding, speaker,
new_bgp_identifier):
bgp_peers = self._plugin.get_bgp_peers_by_bgp_speaker(context,
speaker['id'])
self._nsxv.update_router_id(bgp_binding['edge_id'], new_bgp_identifier)
nbr_to_remove = gw_bgp_neighbour(bgp_binding['bgp_identifier'],
speaker['local_as'],
self._edge_password)
nbr_to_add = gw_bgp_neighbour(new_bgp_identifier, speaker['local_as'],
self._edge_password)
for gw_edge_id in [peer['esg_id'] for peer in bgp_peers
if peer['esg_id']]:
self._nsxv.update_bgp_neighbours(gw_edge_id,
[nbr_to_add],
[nbr_to_remove])
with context.session.begin(subtransactions=True):
bgp_binding['bgp_identifier'] = new_bgp_identifier
def process_router_gw_port_update(self, context, speaker,
router, updated_port):
router_id = router['id']
gw_fixed_ip = router.gw_port['fixed_ips'][0]['ip_address']
edge_id, advertise_static_routes = (
self._get_router_edge_info(context, router_id))
if not edge_id:
# shared router is not attached on any edge
return
bgp_binding = nsxv_db.get_nsxv_bgp_speaker_binding(
context.session, edge_id)
if bgp_binding:
new_fixed_ip = updated_port['fixed_ips'][0]['ip_address']
fixed_ip_updated = gw_fixed_ip != new_fixed_ip
subnets = self._query_tenant_subnets(context, [router_id])
prefixes, redis_rules = (
self._get_prefixes_and_redistribution_rules(
subnets, advertise_static_routes))
# Handle possible snat/no-nat update
if router.enable_snat:
self._nsxv.remove_bgp_redistribution_rules(edge_id, prefixes)
else:
self._nsxv.add_bgp_redistribution_rules(edge_id, prefixes,
redis_rules)
if bgp_binding['bgp_identifier'] == gw_fixed_ip:
if fixed_ip_updated:
self._update_edge_bgp_identifier(context,
bgp_binding,
speaker,
new_fixed_ip)
def enable_bgp_on_router(self, context, speaker, router_id):
edge_id, advertise_static_routes = (
self._get_router_edge_info(context, router_id))
@ -446,10 +501,7 @@ class NSXvBgpDriver(object):
# shared router is not attached on any edge
return
router = self._core_plugin._get_router(context, router_id)
if router.enable_snat:
subnets = []
else:
subnets = self._query_tenant_subnets(context, [router_id])
subnets = self._query_tenant_subnets(context, [router_id])
bgp_peers = self._plugin.get_bgp_peers_by_bgp_speaker(
context, speaker['id'])
@ -459,14 +511,20 @@ class NSXvBgpDriver(object):
if bgp_binding and subnets:
# Edge already configured with BGP (e.g - shared router edge),
# Add the router attached subnets.
prefixes, redis_rules = (
self._get_prefixes_and_redistribution_rules(
subnets, advertise_static_routes))
self._nsxv.add_bgp_redistribution_rules(edge_id, prefixes,
redis_rules)
if router.enable_snat:
prefixes = [self.prefix_name(subnet['id'])
for subnet in subnets]
self._nsxv.remove_bgp_redistribution_rules(edge_id, prefixes)
else:
prefixes, redis_rules = (
self._get_prefixes_and_redistribution_rules(
subnets, advertise_static_routes))
self._nsxv.add_bgp_redistribution_rules(edge_id, prefixes,
redis_rules)
elif not bgp_binding:
gw_port = router.gw_port['fixed_ips'][0]
bgp_identifier = gw_port['ip_address']
if router.enable_snat:
subnets = []
bgp_identifier = router.gw_port['fixed_ips'][0]['ip_address']
self._start_bgp_on_edge(context, edge_id, speaker, bgp_peers,
bgp_identifier, subnets,
advertise_static_routes)
@ -506,9 +564,8 @@ class NSXvBgpDriver(object):
router = self._core_plugin._get_router(context, routers_ids[0])
new_bgp_identifier = (
router.gw_port['fixed_ips'][0]['ip_address'])
with context.session.begin(subtransactions=True):
bgp_binding['bgp_identifier'] = new_bgp_identifier
self._nsxv.update_router_id(edge_id, new_bgp_identifier)
self._update_edge_bgp_identifier(context, bgp_binding, speaker,
new_bgp_identifier)
else:
self._stop_bgp_on_edges(context, [bgp_binding], speaker['id'])