def __init__(self, f5_global_routed_mode, conf, driver, l3_binding=None): self.f5_global_routed_mode = f5_global_routed_mode self.conf = conf self.driver = driver self.l3_binding = l3_binding self.l2_service = L2ServiceBuilder(driver, f5_global_routed_mode) self.bigip_selfip_manager = BigipSelfIpManager(self.driver, self.l2_service, self.driver.l3_binding) self.bigip_snat_manager = BigipSnatManager(self.driver, self.l2_service, self.driver.l3_binding) self.rds_cache = {} self.interface_mapping = self.l2_service.interface_mapping self.network_helper = NetworkHelper() self.service_adapter = self.driver.service_adapter
def __init__(self, f5_global_routed_mode, conf, driver): self.f5_global_routed_mode = f5_global_routed_mode self.conf = conf self.driver = driver self.l2_service = L2ServiceBuilder(conf, f5_global_routed_mode) self.bigip_selfip_manager = BigipSelfIpManager( self.conf, self.l2_service, self.driver.l3_binding) self.bigip_snat_manager = BigipSnatManager( self.conf, self.l2_service, self.driver.l3_binding) self.rds_cache = {} self.interface_mapping = self.l2_service.interface_mapping
def __init__(self, f5_global_routed_mode, conf, driver, l3_binding=None): self.f5_global_routed_mode = f5_global_routed_mode self.conf = conf self.driver = driver self.l3_binding = l3_binding self.l2_service = L2ServiceBuilder(driver, f5_global_routed_mode) self.bigip_selfip_manager = BigipSelfIpManager( self.driver, self.l2_service, self.driver.l3_binding) self.bigip_snat_manager = BigipSnatManager( self.driver, self.l2_service, self.driver.l3_binding) self.rds_cache = {} self.interface_mapping = self.l2_service.interface_mapping self.network_helper = NetworkHelper() self.service_adapter = self.driver.service_adapter
class NetworkServiceBuilder(object): def __init__(self, f5_global_routed_mode, conf, driver, l3_binding=None): self.f5_global_routed_mode = f5_global_routed_mode self.conf = conf self.driver = driver self.l3_binding = l3_binding self.l2_service = L2ServiceBuilder(driver, f5_global_routed_mode) self.bigip_selfip_manager = BigipSelfIpManager(self.driver, self.l2_service, self.driver.l3_binding) self.bigip_snat_manager = BigipSnatManager(self.driver, self.l2_service, self.driver.l3_binding) self.rds_cache = {} self.interface_mapping = self.l2_service.interface_mapping self.network_helper = NetworkHelper() self.service_adapter = self.driver.service_adapter def post_init(self): # Run and Post Initialization Tasks """ # run any post initialized tasks, now that the agent # is fully connected self.l2_service.post_init() def tunnel_sync(self, tunnel_ips): self.l2_service.tunnel_sync(tunnel_ips) def set_tunnel_rpc(self, tunnel_rpc): # Provide FDB Connector with ML2 RPC access """ self.l2_service.set_tunnel_rpc(tunnel_rpc) def set_l2pop_rpc(self, l2pop_rpc): # Provide FDB Connector with ML2 RPC access """ self.l2_service.set_l2pop_rpc(l2pop_rpc) def initialize_vcmp(self): self.l2_service.initialize_vcmp_manager() def initialize_tunneling(self): # setup tunneling vtep_folder = self.conf.f5_vtep_folder vtep_selfip_name = self.conf.f5_vtep_selfip_name local_ips = [] for bigip in self.driver.get_all_bigips(): if not vtep_folder or vtep_folder.lower() == 'none': vtep_folder = 'Common' if vtep_selfip_name and \ not vtep_selfip_name.lower() == 'none': # profiles may already exist # create vxlan_multipoint_profile` self.network_helper.create_vxlan_multipoint_profile( bigip, 'vxlan_ovs', partition='Common') # create l2gre_multipoint_profile self.network_helper.create_l2gre_multipoint_profile( bigip, 'gre_ovs', partition='Common') # find the IP address for the selfip for each box local_ip = self.bigip_selfip_manager.get_selfip_addr( bigip, vtep_selfip_name, partition=vtep_folder) if local_ip: bigip.local_ip = local_ip local_ips.append(local_ip) else: raise f5_ex.MissingVTEPAddress( 'device %s missing vtep selfip %s' % (bigip.device_name, '/' + vtep_folder + '/' + vtep_selfip_name)) return local_ips def prep_service_networking(self, service, traffic_group): # Assure network connectivity is established on all bigips if self.conf.f5_global_routed_mode or not service['loadbalancer']: return if self.conf.use_namespaces: try: LOG.debug("Annotating the service definition networks " "with route domain ID.") self._annotate_service_route_domains(service) except Exception as err: LOG.exception(err) raise f5_ex.RouteDomainCreationException( "Route domain annotation error") # Per Device Network Connectivity (VLANs or Tunnels) subnetsinfo = self._get_subnets_to_assure(service) for (assure_bigip, subnetinfo) in (itertools.product(self.driver.get_all_bigips(), subnetsinfo)): LOG.debug("Assuring per device network connectivity " "for %s on subnet %s." % (assure_bigip.hostname, subnetinfo['subnet'])) # Make sure the L2 network is established self.l2_service.assure_bigip_network(assure_bigip, subnetinfo['network']) # Connect the BigIP device to network, by getting # a self-ip address on the subnet. self.bigip_selfip_manager.assure_bigip_selfip( assure_bigip, service, subnetinfo) # L3 Shared Config assure_bigips = self.driver.get_config_bigips() LOG.debug("Getting subnetinfo for ...") LOG.debug(assure_bigips) for subnetinfo in subnetsinfo: if self.conf.f5_snat_addresses_per_subnet > 0: self._assure_subnet_snats(assure_bigips, service, subnetinfo) if subnetinfo['is_for_member'] and not self.conf.f5_snat_mode: try: self._allocate_gw_addr(subnetinfo) except KeyError as err: raise f5_ex.VirtualServerCreationException(err.message) for assure_bigip in assure_bigips: # If we are not using SNATS, attempt to become # the subnet's default gateway. self.bigip_selfip_manager.assure_gateway_on_subnet( assure_bigip, subnetinfo, traffic_group) def _annotate_service_route_domains(self, service): # Add route domain notation to pool member and vip addresses. LOG.debug("Service before route domains: %s" % service) tenant_id = service['loadbalancer']['tenant_id'] self.update_rds_cache(tenant_id) if 'members' in service: for member in service['members']: if 'address' in member: LOG.debug("processing member %s" % member['address']) if 'network_id' in member and member['network_id']: member_network = ( self.service_adapter.get_network_from_service( service, member['network_id'])) member_subnet = ( self.service_adapter.get_subnet_from_service( service, member['subnet_id'])) if member_network: self.assign_route_domain(tenant_id, member_network, member_subnet) rd_id = ('%' + str(member_network['route_domain_id'])) member['address'] += rd_id else: member['address'] += '%0' if 'vip_address' in service['loadbalancer']: loadbalancer = service['loadbalancer'] if 'network_id' in loadbalancer: lb_network = self.service_adapter.get_network_from_service( service, loadbalancer['network_id']) vip_subnet = self.service_adapter.get_subnet_from_service( service, loadbalancer['vip_subnet_id']) self.assign_route_domain(tenant_id, lb_network, vip_subnet) rd_id = '%' + str(lb_network['route_domain_id']) service['loadbalancer']['vip_address'] += rd_id else: service['loadbalancer']['vip_address'] += '%0' LOG.debug("Service after route domains: %s" % service) def assign_route_domain(self, tenant_id, network, subnet): # Assign route domain for a network if self.l2_service.is_common_network(network): network['route_domain_id'] = 0 return LOG.debug("assign route domain get from cache %s" % network) route_domain_id = self.get_route_domain_from_cache(network) if route_domain_id is not None: network['route_domain_id'] = route_domain_id return LOG.debug("max namespaces: %s" % self.conf.max_namespaces_per_tenant) LOG.debug("max namespaces == 1: %s" % (self.conf.max_namespaces_per_tenant == 1)) if self.conf.max_namespaces_per_tenant == 1: bigip = self.driver.get_bigip() LOG.debug("bigip before get_domain: %s" % bigip) partition_id = self.service_adapter.get_folder_name(tenant_id) tenant_rd = self.network_helper.get_route_domain( bigip, partition=partition_id) network['route_domain_id'] = tenant_rd.id return LOG.debug("assign route domain checking for available route domain") # need new route domain ? check_cidr = netaddr.IPNetwork(subnet['cidr']) placed_route_domain_id = None for route_domain_id in self.rds_cache[tenant_id]: LOG.debug("checking rd %s" % route_domain_id) rd_entry = self.rds_cache[tenant_id][route_domain_id] overlapping_subnet = None for net_shortname in rd_entry: LOG.debug("checking net %s" % net_shortname) net_entry = rd_entry[net_shortname] for exist_subnet_id in net_entry['subnets']: if exist_subnet_id == subnet['id']: continue exist_subnet = net_entry['subnets'][exist_subnet_id] exist_cidr = exist_subnet['cidr'] if check_cidr in exist_cidr or exist_cidr in check_cidr: overlapping_subnet = exist_subnet LOG.debug( 'rd %s: overlaps with subnet %s id: %s' % ((route_domain_id, exist_subnet, exist_subnet_id))) break if overlapping_subnet: # no need to keep looking break if not overlapping_subnet: placed_route_domain_id = route_domain_id break if placed_route_domain_id is None: if (len(self.rds_cache[tenant_id]) < self.conf.max_namespaces_per_tenant): placed_route_domain_id = self._create_aux_rd(tenant_id) self.rds_cache[tenant_id][placed_route_domain_id] = {} LOG.debug("Tenant %s now has %d route domains" % (tenant_id, len(self.rds_cache[tenant_id]))) else: raise Exception("Cannot allocate route domain") LOG.debug("Placed in route domain %s" % placed_route_domain_id) rd_entry = self.rds_cache[tenant_id][placed_route_domain_id] net_short_name = self.get_neutron_net_short_name(network) if net_short_name not in rd_entry: rd_entry[net_short_name] = {'subnets': {}} net_subnets = rd_entry[net_short_name]['subnets'] net_subnets[subnet['id']] = {'cidr': check_cidr} network['route_domain_id'] = placed_route_domain_id def _create_aux_rd(self, tenant_id): # Create a new route domain route_domain_id = None for bigip in self.driver.get_all_bigips(): partition_id = self.service_adapter.get_folder_name(tenant_id) bigip_route_domain_id = self.network_helper.create_route_domain( bigip, partition=partition_id, strictness=self.conf.f5_route_domain_strictness, is_aux=True) if route_domain_id is None: route_domain_id = bigip_route_domain_id.id elif bigip_route_domain_id.id != route_domain_id: # FixME error LOG.debug( "Bigips allocated two different route domains!: %s %s" % (bigip_route_domain_id, route_domain_id)) LOG.debug("Allocated route domain %s for tenant %s" % (route_domain_id, tenant_id)) return route_domain_id # The purpose of the route domain subnet cache is to # determine whether there is an existing bigip # subnet that conflicts with a new one being # assigned to the route domain. """ # route domain subnet cache rds_cache = {'<tenant_id>': { {'0': { '<network type>-<segmentation id>': [ 'subnets': [ '<subnet id>': { 'cidr': '<cidr>' } ], '1': {}}}} """ def update_rds_cache(self, tenant_id): # Update the route domain cache from bigips if tenant_id not in self.rds_cache: LOG.debug("rds_cache: adding tenant %s" % tenant_id) self.rds_cache[tenant_id] = {} for bigip in self.driver.get_all_bigips(): self.update_rds_cache_bigip(tenant_id, bigip) LOG.debug("rds_cache updated: " + str(self.rds_cache)) def update_rds_cache_bigip(self, tenant_id, bigip): # Update the route domain cache for this tenant # with information from bigip's vlan and tunnels LOG.debug("rds_cache: processing bigip %s" % bigip.device_name) route_domain_ids = self.network_helper.get_route_domain_ids( bigip, partition=self.service_adapter.get_folder_name(tenant_id)) # LOG.debug("rds_cache: got bigip route domains: %s" % route_domains) for route_domain_id in route_domain_ids: self.update_rds_cache_bigip_rd_vlans(tenant_id, bigip, route_domain_id) def update_rds_cache_bigip_rd_vlans(self, tenant_id, bigip, route_domain_id): # Update the route domain cache with information # from the bigip vlans and tunnels from # this route domain LOG.debug("rds_cache: processing bigip %s rd %s" % (bigip.device_name, route_domain_id)) # this gets tunnels too partition_id = self.service_adapter.get_folder_name(tenant_id) rd_vlans = self.network_helper.get_vlans_in_route_domain_by_id( bigip, partition=partition_id, id=route_domain_id) LOG.debug("rds_cache: bigip %s rd %s vlans: %s" % (bigip.device_name, route_domain_id, rd_vlans)) if len(rd_vlans) == 0: LOG.debug("No vlans found for route domain: %d" % (route_domain_id)) return # make sure this rd has a cache entry tenant_entry = self.rds_cache[tenant_id] if route_domain_id not in tenant_entry: tenant_entry[route_domain_id] = {} # for every VLAN or TUNNEL on this bigip... for rd_vlan in rd_vlans: self.update_rds_cache_bigip_vlan(tenant_id, bigip, route_domain_id, rd_vlan) def update_rds_cache_bigip_vlan(self, tenant_id, bigip, route_domain_id, rd_vlan): # Update the route domain cache with information # from the bigip vlan or tunnel LOG.debug("rds_cache: processing bigip %s rd %d vlan %s" % (bigip.device_name, route_domain_id, rd_vlan)) net_short_name = self.get_bigip_net_short_name(bigip, tenant_id, rd_vlan) # make sure this net has a cache entry tenant_entry = self.rds_cache[tenant_id] rd_entry = tenant_entry[route_domain_id] if net_short_name not in rd_entry: rd_entry[net_short_name] = {'subnets': {}} net_subnets = rd_entry[net_short_name]['subnets'] partition_id = self.service_adapter.get_folder_name(tenant_id) LOG.debug("Calling get_selfips with: partition %s and vlan_name %s", partition_id, rd_vlan) selfips = self.bigip_selfip_manager.get_selfips(bigip, partition=partition_id, vlan_name=rd_vlan) LOG.debug("rds_cache: got selfips") for selfip in selfips: LOG.debug( "rds_cache: processing bigip %s rd %s vlan %s self %s" % (bigip.device_name, route_domain_id, rd_vlan, selfip.name)) if bigip.device_name not in selfip.name: LOG.error( "rds_cache: Found unexpected selfip %s for tenant %s" % (selfip.name, tenant_id)) continue subnet_id = selfip.name.split(bigip.device_name + '-')[1] # convert 10.1.1.1%1/24 to 10.1.1.1/24 (addr, netbits) = selfip.address.split('/') addr = addr.split('%')[0] selfip.address = addr + '/' + netbits # selfip addresses will have slash notation: 10.1.1.1/24 netip = netaddr.IPNetwork(selfip.address) LOG.debug("rds_cache: updating subnet %s with %s" % (subnet_id, str(netip.cidr))) net_subnets[subnet_id] = {'cidr': netip.cidr} LOG.debug("rds_cache: now %s" % self.rds_cache) def get_route_domain_from_cache(self, network): # Get route domain from cache by network net_short_name = self.get_neutron_net_short_name(network) for tenant_id in self.rds_cache: tenant_cache = self.rds_cache[tenant_id] for route_domain_id in tenant_cache: if net_short_name in tenant_cache[route_domain_id]: return route_domain_id def remove_from_rds_cache(self, network, subnet): # Get route domain from cache by network LOG.debug("remove_from_rds_cache") net_short_name = self.get_neutron_net_short_name(network) for tenant_id in self.rds_cache: LOG.debug("rds_cache: processing remove for %s" % tenant_id) deleted_rds = [] tenant_cache = self.rds_cache[tenant_id] for route_domain_id in tenant_cache: if net_short_name in tenant_cache[route_domain_id]: net_entry = tenant_cache[route_domain_id][net_short_name] if subnet['id'] in net_entry['subnets']: del net_entry['subnets'][subnet['id']] if len(net_entry['subnets']) == 0: del net_entry['subnets'] if len(tenant_cache[route_domain_id][net_short_name]) == 0: del tenant_cache[route_domain_id][net_short_name] if len(self.rds_cache[tenant_id][route_domain_id]) == 0: deleted_rds.append(route_domain_id) for rd in deleted_rds: LOG.debug("removing route domain %d from tenant %s" % (rd, tenant_id)) del self.rds_cache[tenant_id][rd] def get_bigip_net_short_name(self, bigip, tenant_id, network_name): # Return <network_type>-<seg_id> for bigip network LOG.debug("get_bigip_net_short_name: %s:%s" % (tenant_id, network_name)) partition_id = self.service_adapter.get_folder_name(tenant_id) LOG.debug("network_name %s", network_name.split('/')) network_name = network_name.split("/")[-1] if 'tunnel-gre-' in network_name: tunnel_key = self.network_helper.get_tunnel_key( bigip, network_name, partition=partition_id) return 'gre-%s' % tunnel_key elif 'tunnel-vxlan-' in network_name: LOG.debug("Getting tunnel key for VXLAN: %s", network_name) tunnel_key = self.network_helper.get_tunnel_key( bigip, network_name, partition=partition_id) return 'vxlan-%s' % tunnel_key else: LOG.debug("Getting tunnel key for VLAN: %s", network_name) vlan_id = self.network_helper.get_vlan_id(bigip, name=network_name, partition=partition_id) return 'vlan-%s' % vlan_id @staticmethod def get_neutron_net_short_name(network): # Return <network_type>-<seg_id> for neutron network net_type = network['provider:network_type'] net_seg_key = network['provider:segmentation_id'] return net_type + '-' + str(net_seg_key) def _assure_subnet_snats(self, assure_bigips, service, subnetinfo): # Ensure snat for subnet exists on bigips tenant_id = service['loadbalancer']['tenant_id'] subnet = subnetinfo['subnet'] snats_per_subnet = self.conf.f5_snat_addresses_per_subnet assure_bigips = \ [bigip for bigip in assure_bigips if tenant_id not in bigip.assured_tenant_snat_subnets or subnet['id'] not in bigip.assured_tenant_snat_subnets[tenant_id]] LOG.debug("_assure_subnet_snats: getting snat addrs for: %s" % subnet['id']) if len(assure_bigips): snat_addrs = self.bigip_snat_manager.get_snat_addrs( subnetinfo, tenant_id, snats_per_subnet) if len(snat_addrs) != snats_per_subnet: raise f5_ex.SNAT_CreationException( "Unable to satisfy request to allocate %d " "snats. Actual SNAT count: %d SNATs" % (snats_per_subnet, len(snat_addrs))) for assure_bigip in assure_bigips: self.bigip_snat_manager.assure_bigip_snats( assure_bigip, subnetinfo, snat_addrs, tenant_id) def _allocate_gw_addr(self, subnetinfo): # Create a name for the port and for the IP Forwarding # Virtual Server as well as the floating Self IP which # will answer ARP for the members need_port_for_gateway = False network = subnetinfo['network'] subnet = subnetinfo['subnet'] if not network or not subnet: LOG.error('Attempted to create default gateway' ' for network with no id...skipping.') return if not subnet['gateway_ip']: raise KeyError("attempting to create gateway on subnet without " "gateway ip address specified.") gw_name = "gw-" + subnet['id'] ports = self.driver.plugin_rpc.get_port_by_name(port_name=gw_name) if len(ports) < 1: need_port_for_gateway = True # There was no port on this agent's host, so get one from Neutron if need_port_for_gateway: try: rpc = self.driver.plugin_rpc new_port = rpc.create_port_on_subnet_with_specific_ip( subnet_id=subnet['id'], mac_address=None, name=gw_name, ip_address=subnet['gateway_ip']) LOG.info('gateway IP for subnet %s will be port %s' % (subnet['id'], new_port['id'])) except Exception as exc: ermsg = 'Invalid default gateway for subnet %s:%s - %s.' \ % (subnet['id'], subnet['gateway_ip'], exc.message) ermsg += " SNAT will not function and load balancing" ermsg += " support will likely fail. Enable f5_snat_mode." LOG.exception(ermsg) return True def post_service_networking(self, service, all_subnet_hints): # Assure networks are deleted from big-ips if self.conf.f5_global_routed_mode: return # L2toL3 networking layer # Non Shared Config - Local Per BIG-IP self.update_bigip_l2(service) # Delete shared config objects deleted_names = set() for bigip in self.driver.get_config_bigips(): LOG.debug('post_service_networking: calling ' '_assure_delete_networks del nets sh for bigip %s %s' % (bigip.device_name, all_subnet_hints)) subnet_hints = all_subnet_hints[bigip.device_name] deleted_names = deleted_names.union( self._assure_delete_nets_shared(bigip, service, subnet_hints)) # Delete non shared config objects for bigip in self.driver.get_all_bigips(): LOG.debug(' post_service_networking: calling ' ' _assure_delete_networks del nets ns for bigip %s' % bigip.device_name) subnet_hints = all_subnet_hints[bigip.device_name] deleted_names = deleted_names.union( self._assure_delete_nets_nonshared(bigip, service, subnet_hints)) for port_name in deleted_names: LOG.debug(' post_service_networking: calling ' ' del port %s' % port_name) self.driver.plugin_rpc.delete_port_by_name(port_name=port_name) def update_bigip_l2(self, service): # Update fdb entries on bigip loadbalancer = service['loadbalancer'] service_adapter = self.service_adapter for bigip in self.driver.get_all_bigips(): for member in service['members']: LOG.debug("update_bigip_l2 update service members") member['network'] = service_adapter.get_network_from_service( service, member['network_id']) member_status = member['provisioning_status'] if member_status == plugin_const.PENDING_DELETE: self.delete_bigip_member_l2(bigip, loadbalancer, member) else: self.update_bigip_member_l2(bigip, loadbalancer, member) if "network_id" not in loadbalancer: LOG.error("update_bigip_l2, expected network ID") return LOG.debug("update_bigip_l2 get network for ID %s" % loadbalancer["network_id"]) loadbalancer['network'] = service_adapter.get_network_from_service( service, loadbalancer['network_id']) lb_status = loadbalancer['provisioning_status'] if lb_status == plugin_const.PENDING_DELETE: self.delete_bigip_vip_l2(bigip, loadbalancer) else: LOG.debug("update_bigip_l2 calling update_bigip_vip_l2") self.update_bigip_vip_l2(bigip, loadbalancer) LOG.debug("update_bigip_l2 complete") def update_bigip_member_l2(self, bigip, loadbalancer, member): # update pool member l2 records network = member['network'] if network: if self.l2_service.is_common_network(network): net_folder = 'Common' else: net_folder = self.service_adapter.get_folder_name( loadbalancer['tenant_id']) fdb_info = { 'network': network, 'ip_address': member['address'], 'mac_address': member['port']['mac_address'] } self.l2_service.add_bigip_fdbs(bigip, net_folder, fdb_info, member) def delete_bigip_member_l2(self, bigip, loadbalancer, member): # Delete pool member l2 records network = member['network'] if network: if 'port' in member: if self.l2_service.is_common_network(network): net_folder = 'Common' else: net_folder = self.service_adapter.get_folder_name( loadbalancer['tenant_id']) fdb_info = { 'network': network, 'ip_address': member['address'], 'mac_address': member['port']['mac_address'] } self.l2_service.delete_bigip_fdbs(bigip, net_folder, fdb_info, member) else: LOG.error('Member on SDN has no port. Manual ' 'removal on the BIG-IP will be ' 'required. Was the vm instance ' 'deleted before the pool member ' 'was deleted?') def update_bigip_vip_l2(self, bigip, loadbalancer): # Update vip l2 records network = loadbalancer['network'] if network: if self.l2_service.is_common_network(network): net_folder = 'Common' else: net_folder = self.service_adapter.get_folder_name( loadbalancer['tenant_id']) fdb_info = { 'network': network, 'ip_address': None, 'mac_address': None } self.l2_service.add_bigip_fdbs(bigip, net_folder, fdb_info, loadbalancer) def delete_bigip_vip_l2(self, bigip, loadbalancer): # Delete loadbalancer l2 records network = loadbalancer['network'] if network: if self.l2_service.is_common_network(network): net_folder = 'Common' else: net_folder = self.service_adapter.get_folder_name( loadbalancer['tenant_id']) fdb_info = { 'network': network, 'ip_address': None, 'mac_address': None } self.l2_service.delete_bigip_fdbs(bigip, net_folder, fdb_info, loadbalancer) def _assure_delete_nets_shared(self, bigip, service, subnet_hints): # Assure shared configuration (which syncs) is deleted deleted_names = set() tenant_id = service['loadbalancer']['tenant_id'] delete_gateway = self.bigip_selfip_manager.delete_gateway_on_subnet for subnetinfo in self._get_subnets_to_delete(bigip, service, subnet_hints): try: if not self.conf.f5_snat_mode: gw_name = delete_gateway(bigip, subnetinfo) deleted_names.add(gw_name) my_deleted_names, my_in_use_subnets = \ self.bigip_snat_manager.delete_bigip_snats( bigip, subnetinfo, tenant_id) deleted_names = deleted_names.union(my_deleted_names) for in_use_subnetid in my_in_use_subnets: subnet_hints['check_for_delete_subnets'].pop( in_use_subnetid, None) except NeutronException as exc: LOG.error("assure_delete_nets_shared: exception: %s" % str(exc.msg)) except Exception as exc: LOG.error("assure_delete_nets_shared: exception: %s" % str(exc.message)) return deleted_names def _assure_delete_nets_nonshared(self, bigip, service, subnet_hints): # Delete non shared base objects for networks deleted_names = set() for subnetinfo in self._get_subnets_to_delete(bigip, service, subnet_hints): try: network = subnetinfo['network'] if self.l2_service.is_common_network(network): network_folder = 'Common' else: network_folder = self.service_adapter.get_folder_name( service['loadbalancer']['tenant_id']) subnet = subnetinfo['subnet'] if self.conf.f5_populate_static_arp: self.network_helper.arp_delete_by_subnet( bigip, subnet=subnet['cidr'], mask=None, partition=network_folder) local_selfip_name = "local-" + bigip.device_name + \ "-" + subnet['id'] selfip_address = self.bigip_selfip_manager.get_selfip_addr( bigip, local_selfip_name, partition=network_folder) if not selfip_address: LOG.error("Failed to get self IP address %s in cleanup.", local_selfip_name) self.bigip_selfip_manager.delete_selfip( bigip, local_selfip_name, partition=network_folder) if self.l3_binding and selfip_address: self.l3_binding.unbind_address(subnet_id=subnet['id'], ip_address=selfip_address) deleted_names.add(local_selfip_name) self.l2_service.delete_bigip_network(bigip, network) if subnet['id'] not in subnet_hints['do_not_delete_subnets']: subnet_hints['do_not_delete_subnets'].append(subnet['id']) self.remove_from_rds_cache(network, subnet) tenant_id = service['loadbalancer']['tenant_id'] if tenant_id in bigip.assured_tenant_snat_subnets: tenant_snat_subnets = \ bigip.assured_tenant_snat_subnets[tenant_id] if subnet['id'] in tenant_snat_subnets: tenant_snat_subnets.remove(subnet['id']) except NeutronException as exc: LOG.error("assure_delete_nets_nonshared: exception: %s" % str(exc.msg)) except Exception as exc: LOG.error("assure_delete_nets_nonshared: exception: %s" % str(exc.message)) return deleted_names def _get_subnets_to_delete(self, bigip, service, subnet_hints): # Clean up any Self IP, SNATs, networks, and folder for # services items that we deleted. subnets_to_delete = [] for subnetinfo in subnet_hints['check_for_delete_subnets'].values(): subnet = self.service_adapter.get_subnet_from_service( service, subnetinfo['subnet_id']) subnetinfo['subnet'] = subnet network = self.service_adapter.get_network_from_service( service, subnetinfo['network_id']) subnetinfo['network'] = network route_domain = network['route_domain_id'] if not subnet: continue if not self._ips_exist_on_subnet(bigip, service, subnet, route_domain): subnets_to_delete.append(subnetinfo) return subnets_to_delete def _ips_exist_on_subnet(self, bigip, service, subnet, route_domain): # Does the big-ip have any IP addresses on this subnet? LOG.debug("_ips_exist_on_subnet entry %s rd %s" % (str(subnet['cidr']), route_domain)) route_domain = str(route_domain) ipsubnet = netaddr.IPNetwork(subnet['cidr']) # Are there any virtual addresses on this subnet? folder = self.service_adapter.get_folder_name( service['loadbalancer']['tenant_id']) virtual_services = self.network_helper.get_virtual_service_insertion( bigip, partition=folder) for virt_serv in virtual_services: (_, dest) = virt_serv.items()[0] LOG.debug(" _ips_exist_on_subnet: checking vip %s" % str(dest['address'])) if len(dest['address'].split('%')) > 1: vip_route_domain = dest['address'].split('%')[1] else: vip_route_domain = '0' if vip_route_domain != route_domain: continue vip_addr = strip_domain_address(dest['address']) if netaddr.IPAddress(vip_addr) in ipsubnet: LOG.debug(" _ips_exist_on_subnet: found") return True # If there aren't any virtual addresses, are there # node addresses on this subnet? nodes = self.network_helper.get_node_addresses(bigip, partition=folder) for node in nodes: LOG.debug(" _ips_exist_on_subnet: checking node %s" % str(node)) if len(node.split('%')) > 1: node_route_domain = node.split('%')[1] else: node_route_domain = '0' if node_route_domain != route_domain: continue node_addr = strip_domain_address(node) if netaddr.IPAddress(node_addr) in ipsubnet: LOG.debug(" _ips_exist_on_subnet: found") return True LOG.debug(" _ips_exist_on_subnet exit %s" % str(subnet['cidr'])) # nothing found return False def add_bigip_fdb(self, bigip, fdb): self.l2_service.add_bigip_fdb(bigip, fdb) def remove_bigip_fdb(self, bigip, fdb): self.l2_service.remove_bigip_fdb(bigip, fdb) def update_bigip_fdb(self, bigip, fdb): self.l2_service.update_bigip_fdb(bigip, fdb) def set_context(self, context): self.l2_service.set_context(context) def vlan_exists(self, network, folder='Common'): return False def _get_subnets_to_assure(self, service): # Examine service and return active networks networks = dict() loadbalancer = service['loadbalancer'] service_adapter = self.service_adapter lb_status = loadbalancer['provisioning_status'] if lb_status != plugin_const.PENDING_DELETE: if 'network_id' in loadbalancer: network = service_adapter.get_network_from_service( service, loadbalancer['network_id']) subnet = service_adapter.get_subnet_from_service( service, loadbalancer['vip_subnet_id']) networks[network['id']] = { 'network': network, 'subnet': subnet, 'is_for_member': False } for member in service['members']: if member['provisioning_status'] != plugin_const.PENDING_DELETE: if 'network_id' in member: network = service_adapter.get_network_from_service( service, member['network_id']) subnet = service_adapter.get_subnet_from_service( service, member['subnet_id']) networks[network['id']] = { 'network': network, 'subnet': subnet, 'is_for_member': True } return networks.values()
class NetworkServiceBuilder(object): def __init__(self, f5_global_routed_mode, conf, driver): self.f5_global_routed_mode = f5_global_routed_mode self.conf = conf self.driver = driver self.l2_service = L2ServiceBuilder(conf, f5_global_routed_mode) self.bigip_selfip_manager = BigipSelfIpManager( self.conf, self.l2_service, self.driver.l3_binding) self.bigip_snat_manager = BigipSnatManager( self.conf, self.l2_service, self.driver.l3_binding) self.rds_cache = {} self.interface_mapping = self.l2_service.interface_mapping def post_init(self): # Run and Post Initialization Tasks """ # run any post initialized tasks, now that the agent # is fully connected self.l2_service.post_init() def tunnel_sync(self, tunnel_ips): self.l2_service.tunnel_sync(tunnel_ips) def set_tunnel_rpc(self, tunnel_rpc): # Provide FDB Connector with ML2 RPC access """ self.l2_service.set_tunnel_rpc(tunnel_rpc) def set_l2pop_rpc(self, l2pop_rpc): # Provide FDB Connector with ML2 RPC access """ self.l2_service.set_l2pop_rpc(l2pop_rpc) def initialize_tunneling(self): # setup tunneling vtep_folder = self.conf.f5_vtep_folder vtep_selfip_name = self.conf.f5_vtep_selfip_name local_ips = [] for bigip in self.driver.get_all_bigips(): if not vtep_folder or vtep_folder.lower() == 'none': vtep_folder = 'Common' if vtep_selfip_name and \ not vtep_selfip_name.lower() == 'none': # profiles may already exist bigip.vxlan.create_multipoint_profile( name='vxlan_ovs', folder='Common') bigip.l2gre.create_multipoint_profile( name='gre_ovs', folder='Common') # find the IP address for the selfip for each box local_ip = bigip.selfip.get_addr(vtep_selfip_name, vtep_folder) if local_ip: bigip.local_ip = local_ip local_ips.append(local_ip) else: raise f5ex.MissingVTEPAddress( 'device %s missing vtep selfip %s' % (bigip.device_name, '/' + vtep_folder + '/' + vtep_selfip_name)) return local_ips def prep_service_networking(self, service, traffic_group): # Assure network connectivity is established on all bigips if self.conf.f5_global_routed_mode or not service['loadbalancer']: return if self.conf.use_namespaces: self._annotate_service_route_domains(service) # Per Device Network Connectivity (VLANs or Tunnels) subnetsinfo = _get_subnets_to_assure(service) for (assure_bigip, subnetinfo) in \ itertools.product(self.driver.get_all_bigips(), subnetsinfo): self.l2_service.assure_bigip_network( assure_bigip, subnetinfo['network']) self.bigip_selfip_manager.assure_bigip_selfip( assure_bigip, service, subnetinfo) # L3 Shared Config assure_bigips = self.driver.get_config_bigips() for subnetinfo in subnetsinfo: if self.conf.f5_snat_addresses_per_subnet > 0: self._assure_subnet_snats(assure_bigips, service, subnetinfo) if subnetinfo['is_for_member'] and not self.conf.f5_snat_mode: self._allocate_gw_addr(subnetinfo) for assure_bigip in assure_bigips: # if we are not using SNATS, attempt to become # the subnet's default gateway. self.bigip_selfip_manager.assure_gateway_on_subnet( assure_bigip, subnetinfo, traffic_group) def _annotate_service_route_domains(self, service): # Add route domain notation to pool member and vip addresses. LOG.debug("Service before route domains: %s" % service) tenant_id = service['pool']['tenant_id'] self.update_rds_cache(tenant_id) if 'members' in service: for member in service['members']: LOG.debug("processing member %s" % member['address']) if 'address' in member: if 'network' in member and member['network']: self.assign_route_domain( tenant_id, member['network'], member['subnet']) rd_id = '%' + str(member['network']['route_domain_id']) member['address'] += rd_id else: member['address'] += '%0' if 'vip' in service and 'address' in service['vip']: vip = service['vip'] if 'network' in vip and vip['network']: self.assign_route_domain( tenant_id, vip['network'], vip['subnet']) rd_id = '%' + str(vip['network']['route_domain_id']) service['vip']['address'] += rd_id else: service['vip']['address'] += '%0' LOG.debug("Service after route domains: %s" % service) def assign_route_domain(self, tenant_id, network, subnet): # Assign route domain for a network if self.l2_service.is_common_network(network): network['route_domain_id'] = 0 return LOG.debug("assign route domain get from cache %s" % network) route_domain_id = self.get_route_domain_from_cache(network) if route_domain_id is not None: network['route_domain_id'] = route_domain_id return LOG.debug("max namespaces: %s" % self.conf.max_namespaces_per_tenant) LOG.debug("max namespaces ==1: %s" % (self.conf.max_namespaces_per_tenant == 1)) if self.conf.max_namespaces_per_tenant == 1: bigip = self.driver.get_bigip() LOG.debug("bigip before get_domain: %s" % bigip) tenant_rd = bigip.route.get_domain(folder=tenant_id) network['route_domain_id'] = tenant_rd return LOG.debug("assign route domain checking for available route domain") # need new route domain ? check_cidr = netaddr.IPNetwork(subnet['cidr']) placed_route_domain_id = None for route_domain_id in self.rds_cache[tenant_id]: LOG.debug("checking rd %s" % route_domain_id) rd_entry = self.rds_cache[tenant_id][route_domain_id] overlapping_subnet = None for net_shortname in rd_entry: LOG.debug("checking net %s" % net_shortname) net_entry = rd_entry[net_shortname] for exist_subnet_id in net_entry['subnets']: if exist_subnet_id == subnet['id']: continue exist_subnet = net_entry['subnets'][exist_subnet_id] exist_cidr = exist_subnet['cidr'] if check_cidr in exist_cidr or exist_cidr in check_cidr: overlapping_subnet = exist_subnet LOG.debug('rd %s: overlaps with subnet %s id: %s' % ( (route_domain_id, exist_subnet, exist_subnet_id))) break if overlapping_subnet: # no need to keep looking break if not overlapping_subnet: placed_route_domain_id = route_domain_id break if placed_route_domain_id is None: if (len(self.rds_cache[tenant_id]) < self.conf.max_namespaces_per_tenant): placed_route_domain_id = self._create_aux_rd(tenant_id) self.rds_cache[tenant_id][placed_route_domain_id] = {} LOG.debug("Tenant %s now has %d route domains" % (tenant_id, len(self.rds_cache[tenant_id]))) else: raise Exception("Cannot allocate route domain") LOG.debug("Placed in route domain %s" % placed_route_domain_id) rd_entry = self.rds_cache[tenant_id][placed_route_domain_id] net_short_name = self.get_neutron_net_short_name(network) if net_short_name not in rd_entry: rd_entry[net_short_name] = {'subnets': {}} net_subnets = rd_entry[net_short_name]['subnets'] net_subnets[subnet['id']] = {'cidr': check_cidr} network['route_domain_id'] = placed_route_domain_id def _create_aux_rd(self, tenant_id): # Create a new route domain route_domain_id = None for bigip in self.driver.get_all_bigips(): # folder = bigip.decorate_folder(tenant_id) bigip_id = bigip.route.create_domain( folder=tenant_id, strict_route_isolation=self.conf.f5_route_domain_strictness, is_aux=True) if route_domain_id is None: route_domain_id = bigip_id elif bigip_id != route_domain_id: LOG.debug( "Bigips allocated two different route domains!: %s %s" % (bigip_id, route_domain_id)) LOG.debug("Allocated route domain %s for tenant %s" % (route_domain_id, tenant_id)) return route_domain_id # The purpose of the route domain subnet cache is to # determine whether there is an existing bigip # subnet that conflicts with a new one being # assigned to the route domain. """ # route domain subnet cache rds_cache = {'<tenant_id>': { {'0': { '<network type>-<segmentation id>': [ 'subnets': [ '<subnet id>': { 'cidr': '<cidr>' } ], '1': {}}}} """ def update_rds_cache(self, tenant_id): # Update the route domain cache from bigips if tenant_id not in self.rds_cache: LOG.debug("rds_cache: adding tenant %s" % tenant_id) self.rds_cache[tenant_id] = {} for bigip in self.driver.get_all_bigips(): self.update_rds_cache_bigip(tenant_id, bigip) LOG.debug("rds_cache updated: " + str(self.rds_cache)) def update_rds_cache_bigip(self, tenant_id, bigip): # Update the route domain cache for this tenant # with information from bigip's vlan and tunnels LOG.debug("rds_cache: processing bigip %s" % bigip.device_name) route_domain_ids = bigip.route.get_domain_ids(folder=tenant_id) # LOG.debug("rds_cache: got bigip route domains: %s" % route_domains) for route_domain_id in route_domain_ids: self.update_rds_cache_bigip_rd_vlans( tenant_id, bigip, route_domain_id) def update_rds_cache_bigip_rd_vlans( self, tenant_id, bigip, route_domain_id): # Update the route domain cache with information # from the bigip vlans and tunnels from # this route domain LOG.debug("rds_cache: processing bigip %s rd %s" % (bigip.device_name, route_domain_id)) # this gets tunnels too rd_vlans = bigip.route.get_vlans_in_domain_by_id( folder=tenant_id, route_domain_id=route_domain_id) LOG.debug("rds_cache: bigip %s rd %s vlans: %s" % (bigip.device_name, route_domain_id, rd_vlans)) if len(rd_vlans) == 0: return # make sure this rd has a cache entry tenant_entry = self.rds_cache[tenant_id] if route_domain_id not in tenant_entry: tenant_entry[route_domain_id] = {} # for every VLAN or TUNNEL on this bigip... for rd_vlan in rd_vlans: self.update_rds_cache_bigip_vlan( tenant_id, bigip, route_domain_id, rd_vlan) def update_rds_cache_bigip_vlan( self, tenant_id, bigip, route_domain_id, rd_vlan): # Update the route domain cache with information # from the bigip vlan or tunnel LOG.debug("rds_cache: processing bigip %s rd %d vlan %s" % (bigip.device_name, route_domain_id, rd_vlan)) net_short_name = self.get_bigip_net_short_name( bigip, tenant_id, rd_vlan) # make sure this net has a cache entry tenant_entry = self.rds_cache[tenant_id] rd_entry = tenant_entry[route_domain_id] if net_short_name not in rd_entry: rd_entry[net_short_name] = {'subnets': {}} net_subnets = rd_entry[net_short_name]['subnets'] selfips = bigip.selfip.get_selfips(folder=tenant_id, vlan=rd_vlan) LOG.debug("rds_cache: got selfips: %s" % selfips) for selfip in selfips: LOG.debug("rds_cache: processing bigip %s rd %s vlan %s self %s" % (bigip.device_name, route_domain_id, rd_vlan, selfip['name'])) if bigip.device_name not in selfip['name']: LOG.error("rds_cache: Found unexpected selfip %s for tenant %s" % (selfip['name'], tenant_id)) continue subnet_id = selfip['name'].split(bigip.device_name + '-')[1] # convert 10.1.1.1%1/24 to 10.1.1.1/24 addr = selfip['address'].split('/')[0] addr = addr.split('%')[0] netbits = selfip['address'].split('/')[1] selfip['address'] = addr + '/' + netbits # selfip addresses will have slash notation: 10.1.1.1/24 netip = netaddr.IPNetwork(selfip['address']) LOG.debug("rds_cache: updating subnet %s with %s" % (subnet_id, str(netip.cidr))) net_subnets[subnet_id] = {'cidr': netip.cidr} LOG.debug("rds_cache: now %s" % self.rds_cache) def get_route_domain_from_cache(self, network): # Get route domain from cache by network net_short_name = self.get_neutron_net_short_name(network) for tenant_id in self.rds_cache: tenant_cache = self.rds_cache[tenant_id] for route_domain_id in tenant_cache: if net_short_name in tenant_cache[route_domain_id]: return route_domain_id def remove_from_rds_cache(self, network, subnet): # Get route domain from cache by network net_short_name = self.get_neutron_net_short_name(network) for tenant_id in self.rds_cache: tenant_cache = self.rds_cache[tenant_id] for route_domain_id in tenant_cache: if net_short_name in tenant_cache[route_domain_id]: net_entry = tenant_cache[route_domain_id][net_short_name] if subnet['id'] in net_entry: del net_entry[subnet['id']] @staticmethod def get_bigip_net_short_name(bigip, tenant_id, network_name): # Return <network_type>-<seg_id> for bigip network if '_tunnel-gre-' in network_name: tunnel_key = bigip.l2gre.get_tunnel_key( name=network_name, folder=tenant_id) return 'gre-%s' % tunnel_key elif '_tunnel-vxlan-' in network_name: tunnel_key = bigip.vxlan.get_tunnel_key( name=network_name, folder=tenant_id) return 'vxlan-%s' % tunnel_key else: vlan_id = bigip.vlan.get_id(name=network_name, folder=tenant_id) return 'vlan-%s' % vlan_id @staticmethod def get_neutron_net_short_name(network): # Return <network_type>-<seg_id> for neutron network net_type = network['provider:network_type'] net_seg_key = network['provider:segmentation_id'] return net_type + '-' + str(net_seg_key) def _assure_subnet_snats(self, assure_bigips, service, subnetinfo): # Ensure snat for subnet exists on bigips tenant_id = service['pool']['tenant_id'] subnet = subnetinfo['subnet'] assure_bigips = \ [bigip for bigip in assure_bigips if tenant_id not in bigip.assured_tenant_snat_subnets or subnet['id'] not in bigip.assured_tenant_snat_subnets[tenant_id]] if len(assure_bigips): snat_addrs = self.bigip_snat_manager.get_snat_addrs( subnetinfo, tenant_id) for assure_bigip in assure_bigips: self.bigip_snat_manager.assure_bigip_snats( assure_bigip, subnetinfo, snat_addrs, tenant_id) def _allocate_gw_addr(self, subnetinfo): # Create a name for the port and for the IP Forwarding # Virtual Server as well as the floating Self IP which # will answer ARP for the members need_port_for_gateway = False network = subnetinfo['network'] if not network: LOG.error('Attempted to create default gateway' ' for network with no id.. skipping.') return subnet = subnetinfo['subnet'] gw_name = "gw-" + subnet['id'] ports = self.driver.plugin_rpc.get_port_by_name(port_name=gw_name) if len(ports) < 1: need_port_for_gateway = True # There was no port on this agent's host, so get one from Neutron if need_port_for_gateway: try: rpc = self.driver.plugin_rpc new_port = rpc.create_port_on_subnet_with_specific_ip( subnet_id=subnet['id'], mac_address=None, name=gw_name, ip_address=subnet['gateway_ip']) LOG.info('gateway IP for subnet %s will be port %s' % (subnet['id'], new_port['id'])) except Exception as exc: ermsg = 'Invalid default gateway for subnet %s:%s - %s.' \ % (subnet['id'], subnet['gateway_ip'], exc.message) ermsg += " SNAT will not function and load balancing" ermsg += " support will likely fail. Enable f5_snat_mode." LOG.error(ermsg) return True def post_service_networking(self, service, all_subnet_hints): # Assure networks are deleted from big-ips if self.conf.f5_global_routed_mode: return # L2toL3 networking layer # Non Shared Config - Local Per BIG-IP self.update_bigip_l2(service) # Delete shared config objects deleted_names = set() for bigip in self.driver.get_config_bigips(): LOG.debug(' post_service_networking: calling ' '_assure_delete_networks del nets sh for bigip %s %s' % (bigip.device_name, all_subnet_hints)) subnet_hints = all_subnet_hints[bigip.device_name] deleted_names = deleted_names.union( self._assure_delete_nets_shared(bigip, service, subnet_hints)) # avoids race condition: # deletion of shared ip objects must sync before we # remove the selfips or vlans from the peer bigips. self.driver.sync_if_clustered() # Delete non shared config objects for bigip in self.driver.get_all_bigips(): LOG.debug(' post_service_networking: calling ' ' _assure_delete_networks del nets ns for bigip %s' % bigip.device_name) if self.conf.f5_sync_mode == 'replication': subnet_hints = all_subnet_hints[bigip.device_name] else: # If in autosync mode, then the IP operations were performed # on just the primary big-ip, and so that is where the subnet # hints are stored. So, just use those hints for every bigip. device_name = self.driver.get_bigip().device_name subnet_hints = all_subnet_hints[device_name] deleted_names = deleted_names.union( self._assure_delete_nets_nonshared( bigip, service, subnet_hints)) for port_name in deleted_names: LOG.debug(' post_service_networking: calling ' ' del port %s' % port_name) self.driver.plugin_rpc.delete_port_by_name( port_name=port_name) def update_bigip_l2(self, service): # Update fdb entries on bigip vip = service['vip'] pool = service['pool'] for bigip in self.driver.get_all_bigips(): for member in service['members']: if member['status'] == plugin_const.PENDING_DELETE: self.delete_bigip_member_l2(bigip, pool, member) else: self.update_bigip_member_l2(bigip, pool, member) if 'id' in vip: if vip['status'] == plugin_const.PENDING_DELETE: self.delete_bigip_vip_l2(bigip, vip) else: self.update_bigip_vip_l2(bigip, vip) def update_bigip_member_l2(self, bigip, pool, member): # update pool member l2 records network = member['network'] if network: if self.l2_service.is_common_network(network): net_folder = 'Common' else: net_folder = pool['tenant_id'] fdb_info = {'network': network, 'ip_address': member['address'], 'mac_address': member['port']['mac_address']} self.l2_service.add_bigip_fdbs( bigip, net_folder, fdb_info, member) def delete_bigip_member_l2(self, bigip, pool, member): # Delete pool member l2 records network = member['network'] if network: if member['port']: if self.l2_service.is_common_network(network): net_folder = 'Common' else: net_folder = pool['tenant_id'] fdb_info = {'network': network, 'ip_address': member['address'], 'mac_address': member['port']['mac_address']} self.l2_service.delete_bigip_fdbs( bigip, net_folder, fdb_info, member) else: LOG.error('Member on SDN has no port. Manual ' 'removal on the BIG-IP will be ' 'required. Was the vm instance ' 'deleted before the pool member ' 'was deleted?') def update_bigip_vip_l2(self, bigip, vip): # Update vip l2 records network = vip['network'] if network: if self.l2_service.is_common_network(network): net_folder = 'Common' else: net_folder = vip['tenant_id'] fdb_info = {'network': network, 'ip_address': None, 'mac_address': None} self.l2_service.add_bigip_fdbs( bigip, net_folder, fdb_info, vip) def delete_bigip_vip_l2(self, bigip, vip): # Delete vip l2 records network = vip['network'] if network: if self.l2_service.is_common_network(network): net_folder = 'Common' else: net_folder = vip['tenant_id'] fdb_info = {'network': network, 'ip_address': None, 'mac_address': None} self.l2_service.delete_bigip_fdbs( bigip, net_folder, fdb_info, vip) def _assure_delete_nets_shared(self, bigip, service, subnet_hints): # Assure shared configuration (which syncs) is deleted deleted_names = set() tenant_id = service['pool']['tenant_id'] delete_gateway = self.bigip_selfip_manager.delete_gateway_on_subnet for subnetinfo in _get_subnets_to_delete(bigip, service, subnet_hints): try: if not self.conf.f5_snat_mode: gw_name = delete_gateway(bigip, subnetinfo) deleted_names.add(gw_name) my_deleted_names, my_in_use_subnets = \ self.bigip_snat_manager.delete_bigip_snats( bigip, subnetinfo, tenant_id) deleted_names = deleted_names.union(my_deleted_names) for in_use_subnetid in my_in_use_subnets: subnet_hints['check_for_delete_subnets'].pop( in_use_subnetid, None) except NeutronException as exc: LOG.error("assure_delete_nets_shared: exception: %s" % str(exc.msg)) except Exception as exc: LOG.error("assure_delete_nets_shared: exception: %s" % str(exc.message)) return deleted_names def _assure_delete_nets_nonshared(self, bigip, service, subnet_hints): # Delete non shared base objects for networks deleted_names = set() for subnetinfo in _get_subnets_to_delete(bigip, service, subnet_hints): try: network = subnetinfo['network'] if self.l2_service.is_common_network(network): network_folder = 'Common' else: network_folder = service['pool']['tenant_id'] subnet = subnetinfo['subnet'] if self.conf.f5_populate_static_arp: bigip.arp.delete_by_subnet(subnet=subnet['cidr'], mask=None, folder=network_folder) local_selfip_name = "local-" + bigip.device_name + \ "-" + subnet['id'] selfip_address = bigip.selfip.get_addr(name=local_selfip_name, folder=network_folder) bigip.selfip.delete(name=local_selfip_name, folder=network_folder) if self.l3_binding: self.l3_binding.unbind_address(subnet_id=subnet['id'], ip_address=selfip_address) deleted_names.add(local_selfip_name) self.l2_service.delete_bigip_network(bigip, network) if subnet['id'] not in subnet_hints['do_not_delete_subnets']: subnet_hints['do_not_delete_subnets'].append(subnet['id']) self.remove_from_rds_cache(network, subnet) tenant_id = service['pool']['tenant_id'] if tenant_id in bigip.assured_tenant_snat_subnets: tenant_snat_subnets = \ bigip.assured_tenant_snat_subnets[tenant_id] if subnet['id'] in tenant_snat_subnets: tenant_snat_subnets.remove(subnet['id']) except NeutronException as exc: LOG.error("assure_delete_nets_nonshared: exception: %s" % str(exc.msg)) except Exception as exc: LOG.error("assure_delete_nets_nonshared: exception: %s" % str(exc.message)) return deleted_names def add_bigip_fdb(self, bigip, fdb): self.l2_service.add_bigip_fdb(bigip, fdb) def remove_bigip_fdb(self, bigip, fdb): self.l2_service.remove_bigip_fdb(bigip, fdb) def update_bigip_fdb(self, bigip, fdb): self.l2_service.update_bigip_fdb(bigip, fdb) def set_context(self, context): self.l2_service.set_context(context) def vlan_exists(self, network, folder='Common'): return False
class NetworkServiceBuilder(object): def __init__(self, f5_global_routed_mode, conf, driver, l3_binding=None): self.f5_global_routed_mode = f5_global_routed_mode self.conf = conf self.driver = driver self.l3_binding = l3_binding self.l2_service = L2ServiceBuilder(driver, f5_global_routed_mode) self.bigip_selfip_manager = BigipSelfIpManager( self.driver, self.l2_service, self.driver.l3_binding) self.bigip_snat_manager = BigipSnatManager( self.driver, self.l2_service, self.driver.l3_binding) self.vlan_manager = resource_helper.BigIPResourceHelper( resource_helper.ResourceType.vlan) self.rds_cache = {} self.interface_mapping = self.l2_service.interface_mapping self.network_helper = NetworkHelper() self.service_adapter = self.driver.service_adapter def post_init(self): # Run and Post Initialization Tasks """ # run any post initialized tasks, now that the agent # is fully connected self.l2_service.post_init() def tunnel_sync(self, tunnel_ips): self.l2_service.tunnel_sync(tunnel_ips) def set_tunnel_rpc(self, tunnel_rpc): # Provide FDB Connector with ML2 RPC access """ self.l2_service.set_tunnel_rpc(tunnel_rpc) def set_l2pop_rpc(self, l2pop_rpc): # Provide FDB Connector with ML2 RPC access """ self.l2_service.set_l2pop_rpc(l2pop_rpc) def initialize_vcmp(self): self.l2_service.initialize_vcmp_manager() def initialize_tunneling(self): # setup tunneling vtep_folder = self.conf.f5_vtep_folder vtep_selfip_name = self.conf.f5_vtep_selfip_name local_ips = [] for bigip in self.driver.get_all_bigips(): bigip.local_ip = None if not vtep_folder or vtep_folder.lower() == 'none': vtep_folder = 'Common' if vtep_selfip_name and \ not vtep_selfip_name.lower() == 'none': # profiles may already exist # create vxlan_multipoint_profile` self.network_helper.create_vxlan_multipoint_profile( bigip, 'vxlan_ovs', partition='Common') # create l2gre_multipoint_profile self.network_helper.create_l2gre_multipoint_profile( bigip, 'gre_ovs', partition='Common') # find the IP address for the selfip for each box local_ip = self.bigip_selfip_manager.get_selfip_addr( bigip, vtep_selfip_name, partition=vtep_folder ) if local_ip: bigip.local_ip = local_ip local_ips.append(local_ip) else: raise f5_ex.MissingVTEPAddress( 'device %s missing vtep selfip %s' % (bigip.device_name, '/' + vtep_folder + '/' + vtep_selfip_name)) return local_ips def prep_service_networking(self, service, traffic_group): # Assure network connectivity is established on all bigips if self.conf.f5_global_routed_mode or not service['loadbalancer']: return if self.conf.use_namespaces: try: LOG.debug("Annotating the service definition networks " "with route domain ID.") self._annotate_service_route_domains(service) except Exception as err: LOG.exception(err) raise f5_ex.RouteDomainCreationException( "Route domain annotation error") # Per Device Network Connectivity (VLANs or Tunnels) subnetsinfo = self._get_subnets_to_assure(service) for (assure_bigip, subnetinfo) in ( itertools.product(self.driver.get_all_bigips(), subnetsinfo)): LOG.debug("Assuring per device network connectivity " "for %s on subnet %s." % (assure_bigip.hostname, subnetinfo['subnet'])) # Make sure the L2 network is established self.l2_service.assure_bigip_network( assure_bigip, subnetinfo['network']) # Connect the BigIP device to network, by getting # a self-ip address on the subnet. self.bigip_selfip_manager.assure_bigip_selfip( assure_bigip, service, subnetinfo) # L3 Shared Config assure_bigips = self.driver.get_config_bigips() LOG.debug("Getting subnetinfo for ...") LOG.debug(assure_bigips) for subnetinfo in subnetsinfo: if self.conf.f5_snat_addresses_per_subnet > 0: self._assure_subnet_snats(assure_bigips, service, subnetinfo) if subnetinfo['is_for_member'] and not self.conf.f5_snat_mode: try: self._allocate_gw_addr(subnetinfo) except KeyError as err: raise f5_ex.VirtualServerCreationException(err.message) for assure_bigip in assure_bigips: # If we are not using SNATS, attempt to become # the subnet's default gateway. self.bigip_selfip_manager.assure_gateway_on_subnet( assure_bigip, subnetinfo, traffic_group) def _annotate_service_route_domains(self, service): # Add route domain notation to pool member and vip addresses. LOG.debug("Service before route domains: %s" % service) tenant_id = service['loadbalancer']['tenant_id'] self.update_rds_cache(tenant_id) if 'members' in service: for member in service['members']: if 'address' in member: LOG.debug("processing member %s" % member['address']) if 'network_id' in member and member['network_id']: member_network = ( self.service_adapter.get_network_from_service( service, member['network_id'] )) member_subnet = ( self.service_adapter.get_subnet_from_service( service, member['subnet_id'] )) if member_network: self.assign_route_domain( tenant_id, member_network, member_subnet) rd_id = ( '%' + str(member_network['route_domain_id']) ) member['address'] += rd_id else: member['address'] += '%0' if 'vip_address' in service['loadbalancer']: loadbalancer = service['loadbalancer'] if 'network_id' in loadbalancer: lb_network = self.service_adapter.get_network_from_service( service, loadbalancer['network_id']) vip_subnet = self.service_adapter.get_subnet_from_service( service, loadbalancer['vip_subnet_id']) self.assign_route_domain( tenant_id, lb_network, vip_subnet) rd_id = '%' + str(lb_network['route_domain_id']) service['loadbalancer']['vip_address'] += rd_id else: service['loadbalancer']['vip_address'] += '%0' LOG.debug("Service after route domains: %s" % service) def assign_route_domain(self, tenant_id, network, subnet): # Assign route domain for a network if self.l2_service.is_common_network(network): network['route_domain_id'] = 0 return LOG.debug("assign route domain get from cache %s" % network) route_domain_id = self.get_route_domain_from_cache(network) if route_domain_id is not None: network['route_domain_id'] = route_domain_id return LOG.debug("max namespaces: %s" % self.conf.max_namespaces_per_tenant) LOG.debug("max namespaces == 1: %s" % (self.conf.max_namespaces_per_tenant == 1)) if self.conf.max_namespaces_per_tenant == 1: bigip = self.driver.get_bigip() LOG.debug("bigip before get_domain: %s" % bigip) partition_id = self.service_adapter.get_folder_name( tenant_id) tenant_rd = self.network_helper.get_route_domain( bigip, partition=partition_id) network['route_domain_id'] = tenant_rd.id return LOG.debug("assign route domain checking for available route domain") # need new route domain ? check_cidr = netaddr.IPNetwork(subnet['cidr']) placed_route_domain_id = None for route_domain_id in self.rds_cache[tenant_id]: LOG.debug("checking rd %s" % route_domain_id) rd_entry = self.rds_cache[tenant_id][route_domain_id] overlapping_subnet = None for net_shortname in rd_entry: LOG.debug("checking net %s" % net_shortname) net_entry = rd_entry[net_shortname] for exist_subnet_id in net_entry['subnets']: if exist_subnet_id == subnet['id']: continue exist_subnet = net_entry['subnets'][exist_subnet_id] exist_cidr = exist_subnet['cidr'] if check_cidr in exist_cidr or exist_cidr in check_cidr: overlapping_subnet = exist_subnet LOG.debug('rd %s: overlaps with subnet %s id: %s' % ( (route_domain_id, exist_subnet, exist_subnet_id))) break if overlapping_subnet: # no need to keep looking break if not overlapping_subnet: placed_route_domain_id = route_domain_id break if placed_route_domain_id is None: if (len(self.rds_cache[tenant_id]) < self.conf.max_namespaces_per_tenant): placed_route_domain_id = self._create_aux_rd(tenant_id) self.rds_cache[tenant_id][placed_route_domain_id] = {} LOG.debug("Tenant %s now has %d route domains" % (tenant_id, len(self.rds_cache[tenant_id]))) else: raise Exception("Cannot allocate route domain") LOG.debug("Placed in route domain %s" % placed_route_domain_id) rd_entry = self.rds_cache[tenant_id][placed_route_domain_id] net_short_name = self.get_neutron_net_short_name(network) if net_short_name not in rd_entry: rd_entry[net_short_name] = {'subnets': {}} net_subnets = rd_entry[net_short_name]['subnets'] net_subnets[subnet['id']] = {'cidr': check_cidr} network['route_domain_id'] = placed_route_domain_id def _create_aux_rd(self, tenant_id): # Create a new route domain route_domain_id = None for bigip in self.driver.get_all_bigips(): partition_id = self.service_adapter.get_folder_name(tenant_id) bigip_route_domain_id = self.network_helper.create_route_domain( bigip, partition=partition_id, strictness=self.conf.f5_route_domain_strictness, is_aux=True) if route_domain_id is None: route_domain_id = bigip_route_domain_id.id elif bigip_route_domain_id.id != route_domain_id: # FixME error LOG.debug( "Bigips allocated two different route domains!: %s %s" % (bigip_route_domain_id, route_domain_id)) LOG.debug("Allocated route domain %s for tenant %s" % (route_domain_id, tenant_id)) return route_domain_id # The purpose of the route domain subnet cache is to # determine whether there is an existing bigip # subnet that conflicts with a new one being # assigned to the route domain. """ # route domain subnet cache rds_cache = {'<tenant_id>': { {'0': { '<network type>-<segmentation id>': [ 'subnets': [ '<subnet id>': { 'cidr': '<cidr>' } ], '1': {}}}} """ def update_rds_cache(self, tenant_id): # Update the route domain cache from bigips if tenant_id not in self.rds_cache: LOG.debug("rds_cache: adding tenant %s" % tenant_id) self.rds_cache[tenant_id] = {} for bigip in self.driver.get_all_bigips(): self.update_rds_cache_bigip(tenant_id, bigip) LOG.debug("rds_cache updated: " + str(self.rds_cache)) def update_rds_cache_bigip(self, tenant_id, bigip): # Update the route domain cache for this tenant # with information from bigip's vlan and tunnels LOG.debug("rds_cache: processing bigip %s" % bigip.device_name) route_domain_ids = self.network_helper.get_route_domain_ids( bigip, partition=self.service_adapter.get_folder_name(tenant_id)) # LOG.debug("rds_cache: got bigip route domains: %s" % route_domains) for route_domain_id in route_domain_ids: self.update_rds_cache_bigip_rd_vlans( tenant_id, bigip, route_domain_id) def update_rds_cache_bigip_rd_vlans( self, tenant_id, bigip, route_domain_id): # Update the route domain cache with information # from the bigip vlans and tunnels from # this route domain LOG.debug("rds_cache: processing bigip %s rd %s" % (bigip.device_name, route_domain_id)) # this gets tunnels too partition_id = self.service_adapter.get_folder_name(tenant_id) rd_vlans = self.network_helper.get_vlans_in_route_domain_by_id( bigip, partition=partition_id, id=route_domain_id ) LOG.debug("rds_cache: bigip %s rd %s vlans: %s" % (bigip.device_name, route_domain_id, rd_vlans)) if len(rd_vlans) == 0: LOG.debug("No vlans found for route domain: %d" % (route_domain_id)) return # make sure this rd has a cache entry tenant_entry = self.rds_cache[tenant_id] if route_domain_id not in tenant_entry: tenant_entry[route_domain_id] = {} # for every VLAN or TUNNEL on this bigip... for rd_vlan in rd_vlans: self.update_rds_cache_bigip_vlan( tenant_id, bigip, route_domain_id, rd_vlan) def update_rds_cache_bigip_vlan( self, tenant_id, bigip, route_domain_id, rd_vlan): # Update the route domain cache with information # from the bigip vlan or tunnel LOG.debug("rds_cache: processing bigip %s rd %d vlan %s" % (bigip.device_name, route_domain_id, rd_vlan)) net_short_name = self.get_bigip_net_short_name( bigip, tenant_id, rd_vlan) # make sure this net has a cache entry tenant_entry = self.rds_cache[tenant_id] rd_entry = tenant_entry[route_domain_id] if net_short_name not in rd_entry: rd_entry[net_short_name] = {'subnets': {}} net_subnets = rd_entry[net_short_name]['subnets'] partition_id = self.service_adapter.get_folder_name(tenant_id) LOG.debug("Calling get_selfips with: partition %s and vlan_name %s", partition_id, rd_vlan) selfips = self.bigip_selfip_manager.get_selfips( bigip, partition=partition_id, vlan_name=rd_vlan ) LOG.debug("rds_cache: got selfips") for selfip in selfips: LOG.debug("rds_cache: processing bigip %s rd %s vlan %s self %s" % (bigip.device_name, route_domain_id, rd_vlan, selfip.name)) if bigip.device_name not in selfip.name: LOG.error("rds_cache: Found unexpected selfip %s for tenant %s" % (selfip.name, tenant_id)) continue subnet_id = selfip.name.split(bigip.device_name + '-')[1] # convert 10.1.1.1%1/24 to 10.1.1.1/24 (addr, netbits) = selfip.address.split('/') addr = addr.split('%')[0] selfip.address = addr + '/' + netbits # selfip addresses will have slash notation: 10.1.1.1/24 netip = netaddr.IPNetwork(selfip.address) LOG.debug("rds_cache: updating subnet %s with %s" % (subnet_id, str(netip.cidr))) net_subnets[subnet_id] = {'cidr': netip.cidr} LOG.debug("rds_cache: now %s" % self.rds_cache) def get_route_domain_from_cache(self, network): # Get route domain from cache by network net_short_name = self.get_neutron_net_short_name(network) for tenant_id in self.rds_cache: tenant_cache = self.rds_cache[tenant_id] for route_domain_id in tenant_cache: if net_short_name in tenant_cache[route_domain_id]: return route_domain_id def remove_from_rds_cache(self, network, subnet): # Get route domain from cache by network LOG.debug("remove_from_rds_cache") net_short_name = self.get_neutron_net_short_name(network) for tenant_id in self.rds_cache: LOG.debug("rds_cache: processing remove for %s" % tenant_id) deleted_rds = [] tenant_cache = self.rds_cache[tenant_id] for route_domain_id in tenant_cache: if net_short_name in tenant_cache[route_domain_id]: net_entry = tenant_cache[route_domain_id][net_short_name] if subnet['id'] in net_entry['subnets']: del net_entry['subnets'][subnet['id']] if len(net_entry['subnets']) == 0: del net_entry['subnets'] if len(tenant_cache[route_domain_id][net_short_name]) == 0: del tenant_cache[route_domain_id][net_short_name] if len(self.rds_cache[tenant_id][route_domain_id]) == 0: deleted_rds.append(route_domain_id) for rd in deleted_rds: LOG.debug("removing route domain %d from tenant %s" % (rd, tenant_id)) del self.rds_cache[tenant_id][rd] def get_bigip_net_short_name(self, bigip, tenant_id, network_name): # Return <network_type>-<seg_id> for bigip network LOG.debug("get_bigip_net_short_name: %s:%s" % ( tenant_id, network_name)) partition_id = self.service_adapter.get_folder_name(tenant_id) LOG.debug("network_name %s", network_name.split('/')) network_name = network_name.split("/")[-1] if 'tunnel-gre-' in network_name: tunnel_key = self.network_helper.get_tunnel_key( bigip, network_name, partition=partition_id ) return 'gre-%s' % tunnel_key elif 'tunnel-vxlan-' in network_name: LOG.debug("Getting tunnel key for VXLAN: %s", network_name) tunnel_key = self.network_helper.get_tunnel_key( bigip, network_name, partition=partition_id ) return 'vxlan-%s' % tunnel_key else: LOG.debug("Getting tunnel key for VLAN: %s", network_name) vlan_id = self.network_helper.get_vlan_id(bigip, name=network_name, partition=partition_id) return 'vlan-%s' % vlan_id @staticmethod def get_neutron_net_short_name(network): # Return <network_type>-<seg_id> for neutron network net_type = network['provider:network_type'] net_seg_key = network['provider:segmentation_id'] return net_type + '-' + str(net_seg_key) def _assure_subnet_snats(self, assure_bigips, service, subnetinfo): # Ensure snat for subnet exists on bigips tenant_id = service['loadbalancer']['tenant_id'] subnet = subnetinfo['subnet'] snats_per_subnet = self.conf.f5_snat_addresses_per_subnet assure_bigips = \ [bigip for bigip in assure_bigips if tenant_id not in bigip.assured_tenant_snat_subnets or subnet['id'] not in bigip.assured_tenant_snat_subnets[tenant_id]] LOG.debug("_assure_subnet_snats: getting snat addrs for: %s" % subnet['id']) if len(assure_bigips): snat_addrs = self.bigip_snat_manager.get_snat_addrs( subnetinfo, tenant_id, snats_per_subnet) if len(snat_addrs) != snats_per_subnet: raise f5_ex.SNAT_CreationException( "Unable to satisfy request to allocate %d " "snats. Actual SNAT count: %d SNATs" % (snats_per_subnet, len(snat_addrs))) for assure_bigip in assure_bigips: self.bigip_snat_manager.assure_bigip_snats( assure_bigip, subnetinfo, snat_addrs, tenant_id) def _allocate_gw_addr(self, subnetinfo): # Create a name for the port and for the IP Forwarding # Virtual Server as well as the floating Self IP which # will answer ARP for the members need_port_for_gateway = False network = subnetinfo['network'] subnet = subnetinfo['subnet'] if not network or not subnet: LOG.error('Attempted to create default gateway' ' for network with no id...skipping.') return if not subnet['gateway_ip']: raise KeyError("attempting to create gateway on subnet without " "gateway ip address specified.") gw_name = "gw-" + subnet['id'] ports = self.driver.plugin_rpc.get_port_by_name(port_name=gw_name) if len(ports) < 1: need_port_for_gateway = True # There was no port on this agent's host, so get one from Neutron if need_port_for_gateway: try: rpc = self.driver.plugin_rpc new_port = rpc.create_port_on_subnet_with_specific_ip( subnet_id=subnet['id'], mac_address=None, name=gw_name, ip_address=subnet['gateway_ip']) LOG.info('gateway IP for subnet %s will be port %s' % (subnet['id'], new_port['id'])) except Exception as exc: ermsg = 'Invalid default gateway for subnet %s:%s - %s.' \ % (subnet['id'], subnet['gateway_ip'], exc.message) ermsg += " SNAT will not function and load balancing" ermsg += " support will likely fail. Enable f5_snat_mode." LOG.exception(ermsg) return True def post_service_networking(self, service, all_subnet_hints): # Assure networks are deleted from big-ips if self.conf.f5_global_routed_mode: return # L2toL3 networking layer # Non Shared Config - Local Per BIG-IP self.update_bigip_l2(service) # Delete shared config objects deleted_names = set() for bigip in self.driver.get_config_bigips(): LOG.debug('post_service_networking: calling ' '_assure_delete_networks del nets sh for bigip %s %s' % (bigip.device_name, all_subnet_hints)) subnet_hints = all_subnet_hints[bigip.device_name] deleted_names = deleted_names.union( self._assure_delete_nets_shared(bigip, service, subnet_hints)) # Delete non shared config objects for bigip in self.driver.get_all_bigips(): LOG.debug(' post_service_networking: calling ' ' _assure_delete_networks del nets ns for bigip %s' % bigip.device_name) subnet_hints = all_subnet_hints[bigip.device_name] deleted_names = deleted_names.union( self._assure_delete_nets_nonshared( bigip, service, subnet_hints) ) for port_name in deleted_names: LOG.debug(' post_service_networking: calling ' ' del port %s' % port_name) self.driver.plugin_rpc.delete_port_by_name( port_name=port_name) def update_bigip_l2(self, service): # Update fdb entries on bigip loadbalancer = service['loadbalancer'] service_adapter = self.service_adapter for bigip in self.driver.get_all_bigips(): for member in service['members']: LOG.debug("update_bigip_l2 update service members") member['network'] = service_adapter.get_network_from_service( service, member['network_id'] ) member_status = member['provisioning_status'] if member_status == plugin_const.PENDING_DELETE: self.delete_bigip_member_l2(bigip, loadbalancer, member) else: self.update_bigip_member_l2(bigip, loadbalancer, member) if "network_id" not in loadbalancer: LOG.error("update_bigip_l2, expected network ID") return LOG.debug("update_bigip_l2 get network for ID %s" % loadbalancer["network_id"]) loadbalancer['network'] = service_adapter.get_network_from_service( service, loadbalancer['network_id'] ) lb_status = loadbalancer['provisioning_status'] if lb_status == plugin_const.PENDING_DELETE: self.delete_bigip_vip_l2(bigip, loadbalancer) else: LOG.debug("update_bigip_l2 calling update_bigip_vip_l2") self.update_bigip_vip_l2(bigip, loadbalancer) LOG.debug("update_bigip_l2 complete") def update_bigip_member_l2(self, bigip, loadbalancer, member): # update pool member l2 records network = member['network'] if network: if self.l2_service.is_common_network(network): net_folder = 'Common' else: net_folder = self.service_adapter.get_folder_name( loadbalancer['tenant_id'] ) fdb_info = {'network': network, 'ip_address': member['address'], 'mac_address': member['port']['mac_address']} self.l2_service.add_bigip_fdbs( bigip, net_folder, fdb_info, member) def delete_bigip_member_l2(self, bigip, loadbalancer, member): # Delete pool member l2 records network = member['network'] if network: if 'port' in member: if self.l2_service.is_common_network(network): net_folder = 'Common' else: net_folder = self.service_adapter.get_folder_name( loadbalancer['tenant_id'] ) fdb_info = {'network': network, 'ip_address': member['address'], 'mac_address': member['port']['mac_address']} self.l2_service.delete_bigip_fdbs( bigip, net_folder, fdb_info, member) else: LOG.error('Member on SDN has no port. Manual ' 'removal on the BIG-IP will be ' 'required. Was the vm instance ' 'deleted before the pool member ' 'was deleted?') def update_bigip_vip_l2(self, bigip, loadbalancer): # Update vip l2 records network = loadbalancer['network'] if network: if self.l2_service.is_common_network(network): net_folder = 'Common' else: net_folder = self.service_adapter.get_folder_name( loadbalancer['tenant_id'] ) fdb_info = {'network': network, 'ip_address': None, 'mac_address': None} self.l2_service.add_bigip_fdbs( bigip, net_folder, fdb_info, loadbalancer) def delete_bigip_vip_l2(self, bigip, loadbalancer): # Delete loadbalancer l2 records network = loadbalancer['network'] if network: if self.l2_service.is_common_network(network): net_folder = 'Common' else: net_folder = self.service_adapter.get_folder_name( loadbalancer['tenant_id'] ) fdb_info = {'network': network, 'ip_address': None, 'mac_address': None} self.l2_service.delete_bigip_fdbs( bigip, net_folder, fdb_info, loadbalancer) def _assure_delete_nets_shared(self, bigip, service, subnet_hints): # Assure shared configuration (which syncs) is deleted deleted_names = set() tenant_id = service['loadbalancer']['tenant_id'] delete_gateway = self.bigip_selfip_manager.delete_gateway_on_subnet for subnetinfo in self._get_subnets_to_delete(bigip, service, subnet_hints): try: if not self.conf.f5_snat_mode: gw_name = delete_gateway(bigip, subnetinfo) deleted_names.add(gw_name) my_deleted_names, my_in_use_subnets = \ self.bigip_snat_manager.delete_bigip_snats( bigip, subnetinfo, tenant_id) deleted_names = deleted_names.union(my_deleted_names) for in_use_subnetid in my_in_use_subnets: subnet_hints['check_for_delete_subnets'].pop( in_use_subnetid, None) except NeutronException as exc: LOG.error("assure_delete_nets_shared: exception: %s" % str(exc.msg)) except Exception as exc: LOG.error("assure_delete_nets_shared: exception: %s" % str(exc.message)) return deleted_names def _assure_delete_nets_nonshared(self, bigip, service, subnet_hints): # Delete non shared base objects for networks deleted_names = set() for subnetinfo in self._get_subnets_to_delete(bigip, service, subnet_hints): try: network = subnetinfo['network'] if self.l2_service.is_common_network(network): network_folder = 'Common' else: network_folder = self.service_adapter.get_folder_name( service['loadbalancer']['tenant_id']) subnet = subnetinfo['subnet'] if self.conf.f5_populate_static_arp: self.network_helper.arp_delete_by_subnet( bigip, subnet=subnet['cidr'], mask=None, partition=network_folder ) local_selfip_name = "local-" + bigip.device_name + \ "-" + subnet['id'] selfip_address = self.bigip_selfip_manager.get_selfip_addr( bigip, local_selfip_name, partition=network_folder ) if not selfip_address: LOG.error("Failed to get self IP address %s in cleanup.", local_selfip_name) self.bigip_selfip_manager.delete_selfip( bigip, local_selfip_name, partition=network_folder ) if self.l3_binding and selfip_address: self.l3_binding.unbind_address(subnet_id=subnet['id'], ip_address=selfip_address) deleted_names.add(local_selfip_name) self.l2_service.delete_bigip_network(bigip, network) if subnet['id'] not in subnet_hints['do_not_delete_subnets']: subnet_hints['do_not_delete_subnets'].append(subnet['id']) self.remove_from_rds_cache(network, subnet) tenant_id = service['loadbalancer']['tenant_id'] if tenant_id in bigip.assured_tenant_snat_subnets: tenant_snat_subnets = \ bigip.assured_tenant_snat_subnets[tenant_id] if subnet['id'] in tenant_snat_subnets: tenant_snat_subnets.remove(subnet['id']) except NeutronException as exc: LOG.error("assure_delete_nets_nonshared: exception: %s" % str(exc.msg)) except Exception as exc: LOG.error("assure_delete_nets_nonshared: exception: %s" % str(exc.message)) return deleted_names def _get_subnets_to_delete(self, bigip, service, subnet_hints): # Clean up any Self IP, SNATs, networks, and folder for # services items that we deleted. subnets_to_delete = [] for subnetinfo in subnet_hints['check_for_delete_subnets'].values(): subnet = self.service_adapter.get_subnet_from_service( service, subnetinfo['subnet_id']) subnetinfo['subnet'] = subnet network = self.service_adapter.get_network_from_service( service, subnetinfo['network_id']) subnetinfo['network'] = network route_domain = network['route_domain_id'] if not subnet: continue if not self._ips_exist_on_subnet( bigip, service, subnet, route_domain): subnets_to_delete.append(subnetinfo) return subnets_to_delete def _ips_exist_on_subnet(self, bigip, service, subnet, route_domain): # Does the big-ip have any IP addresses on this subnet? LOG.debug("_ips_exist_on_subnet entry %s rd %s" % (str(subnet['cidr']), route_domain)) route_domain = str(route_domain) ipsubnet = netaddr.IPNetwork(subnet['cidr']) # Are there any virtual addresses on this subnet? folder = self.service_adapter.get_folder_name( service['loadbalancer']['tenant_id'] ) virtual_services = self.network_helper.get_virtual_service_insertion( bigip, partition=folder ) for virt_serv in virtual_services: (_, dest) = virt_serv.items()[0] LOG.debug(" _ips_exist_on_subnet: checking vip %s" % str(dest['address'])) if len(dest['address'].split('%')) > 1: vip_route_domain = dest['address'].split('%')[1] else: vip_route_domain = '0' if vip_route_domain != route_domain: continue vip_addr = strip_domain_address(dest['address']) if netaddr.IPAddress(vip_addr) in ipsubnet: LOG.debug(" _ips_exist_on_subnet: found") return True # If there aren't any virtual addresses, are there # node addresses on this subnet? nodes = self.network_helper.get_node_addresses( bigip, partition=folder ) for node in nodes: LOG.debug(" _ips_exist_on_subnet: checking node %s" % str(node)) if len(node.split('%')) > 1: node_route_domain = node.split('%')[1] else: node_route_domain = '0' if node_route_domain != route_domain: continue node_addr = strip_domain_address(node) if netaddr.IPAddress(node_addr) in ipsubnet: LOG.debug(" _ips_exist_on_subnet: found") return True LOG.debug(" _ips_exist_on_subnet exit %s" % str(subnet['cidr'])) # nothing found return False def add_bigip_fdb(self, bigip, fdb): self.l2_service.add_bigip_fdb(bigip, fdb) def remove_bigip_fdb(self, bigip, fdb): self.l2_service.remove_bigip_fdb(bigip, fdb) def update_bigip_fdb(self, bigip, fdb): self.l2_service.update_bigip_fdb(bigip, fdb) def set_context(self, context): self.l2_service.set_context(context) def vlan_exists(self, bigip, network, folder='Common'): return self.vlan_manager.exists(bigip, name=network, partition=folder) def _get_subnets_to_assure(self, service): # Examine service and return active networks networks = dict() loadbalancer = service['loadbalancer'] service_adapter = self.service_adapter lb_status = loadbalancer['provisioning_status'] if lb_status != plugin_const.PENDING_DELETE: if 'network_id' in loadbalancer: network = service_adapter.get_network_from_service( service, loadbalancer['network_id'] ) subnet = service_adapter.get_subnet_from_service( service, loadbalancer['vip_subnet_id'] ) networks[network['id']] = {'network': network, 'subnet': subnet, 'is_for_member': False} for member in service['members']: if member['provisioning_status'] != plugin_const.PENDING_DELETE: if 'network_id' in member: network = service_adapter.get_network_from_service( service, member['network_id'] ) subnet = service_adapter.get_subnet_from_service( service, member['subnet_id'] ) networks[network['id']] = {'network': network, 'subnet': subnet, 'is_for_member': True} return networks.values()