def deallocate(self, address): """Deallocate previously allocated address. :param address: The address to deallocate. :type address: A subclass of netaddr.IPAddress or convertible to one. :returns: None """ if not self._ib_network: return ip_addr = str(address) address_request = self._build_address_request_from_ib_address(ip_addr) if not address_request: return ipam_controller = ipam.IpamSyncController(self._ib_cxt) dns_controller = dns.DnsController(self._ib_cxt) ipam_controller.deallocate_ip(ip_addr) port_name = (address_request.port_name if hasattr(address_request, 'port_name') else None) dns_controller.unbind_names(ip_addr, None, address_request.port_id, address_request.tenant_id, address_request.device_id, address_request.device_owner, port_name)
def update_subnet(self, rollback_list, subnet_request): """Update IPAM Subnet. Updates allocation pools, dns zones, or EAs for the subnet in the Infoblox backend. """ neutron_subnet = self._build_subnet_from_request(subnet_request) ib_network = self._get_ib_network(neutron_subnet['id'], neutron_subnet['ip_version']) if not ib_network: raise exc.InfobloxCannotFindSubnet(subnet_id=neutron_subnet['id'], cidr=neutron_subnet['cidr']) ib_cxt = ib_context.InfobloxContext(self._context, self._context.user_id, None, neutron_subnet, self._grid_config, plugin=self._plugin, ib_network=ib_network) ipam_controller = ipam.IpamSyncController(ib_cxt) dns_controller = dns.DnsController(ib_cxt) ipam_controller.update_subnet_allocation_pools(rollback_list) if self._is_new_zone_required(neutron_subnet, ib_network): # subnet name is used in the domain suffix pattern and the name # has been changed; we need to create new zones. dns_controller.create_dns_zones(rollback_list) ipam_controller.update_subnet_details(ib_network)
def delete_instance_sync(self, payload): """Notifies that an instance has been deleted.""" instance_id = payload.get('instance_id') session = self.context.session dbi.remove_instance(session, instance_id) if self.traceable: LOG.info("Deleted instance: %s", instance_id) vm_id_ea = ib_objects.EA({'VM ID': instance_id}) subnets = dbi.get_external_subnets(self.context.session) for cur_subnet in subnets: subnet = self.plugin.get_subnet(self.context, cur_subnet.id) network = self.plugin.get_network(self.context, cur_subnet.network_id) ib_context = context.InfobloxContext( self.context, self.user_id, network, subnet, self.grid_config, self.plugin, self._cached_grid_members, self._cached_network_views, self._cached_mapping_conditions) dns_controller = dns.DnsController(ib_context) connector = ib_context.connector netview = ib_context.mapping.network_view dns_view = ib_context.mapping.dns_view ib_address = ib_objects.FixedAddress.search( connector, network_view=netview, network=subnet['cidr'], search_extattrs=vm_id_ea) if not ib_address: ib_address = ib_objects.HostRecord.search( connector, view=dns_view, zone=dns_controller.dns_zone, search_extattrs=vm_id_ea) if not ib_address: continue if hasattr(ib_address, 'ips'): ips = [ipaddr.ip for ipaddr in ib_address.ips if netaddr.IPAddress(ipaddr.ip) in netaddr.IPNetwork(subnet['cidr'])] else: ips = [ib_address.ip] tenant_id = ib_address.extattrs.get('Tenant ID') db_ports = dbi.get_floatingip_ports( session, ips, cur_subnet.network_id) for port in db_ports: port_id = port[0] device_id = port[1] device_owner = port[2] floating_ip = port[3] port_name = port[4] dns_controller.bind_names( floating_ip, None, port_id, tenant_id, device_id, device_owner, False, port_name) LOG.info("Instance deletion sync: instance id = %s, " "floating ip = %s, port id = %s, device owner = %s", instance_id, floating_ip, port_id, device_owner)
def update_network_sync(self, need_new_zones=False): """Updates EAs for each subnet that belongs to the updated network.""" session = self.ib_cxt.context.session network = self.ib_cxt.network network_id = network.get('id') subnets = dbi.get_subnets_by_network_id(session, network_id) for subnet in subnets: network_view = None cidr = subnet.get('cidr') subnet_id = subnet.get('id') netview_mappings = dbi.get_network_view_mappings( session, network_id=network_id, subnet_id=subnet_id) if netview_mappings: netview_row = utils.find_one_in_list( 'id', netview_mappings[0].network_view_id, self.ib_cxt.discovered_network_views) network_view = netview_row.network_view ib_network = None if network_view: ib_network = self.ib_cxt.ibom.get_network(network_view, cidr) ea_network = eam.get_ea_for_network(self.ib_cxt.user_id, self.ib_cxt.tenant_id, self.ib_cxt.tenant_name, network, subnet) self.ib_cxt.ibom.update_network_options(ib_network, ea_network) if need_new_zones: # Need context with ib_network to create zones ib_cxt = context.InfobloxContext(self.ib_cxt.context, self.ib_cxt.user_id, network, subnet, self.grid_config, self.ib_cxt.plugin, ib_network=ib_network) dns_controller = dns.DnsController(ib_cxt) rollback_list = [] dns_controller.create_dns_zones(rollback_list) else: self.ib_cxt.subnet = subnet dns_controller = dns.DnsController(self.ib_cxt) dns_controller.update_dns_zones()
def update_floatingip_sync(self, payload): """Notifies that the floating ip has been updated. update could be either association if port_id is not empty or dissociation if port_id is None. """ floatingip = payload.get('floatingip') if self.traceable: LOG.info("Updated floatingip: %s", floatingip) session = self.context.session floating_ip_id = floatingip.get('id') tenant_id = floatingip.get('tenant_id') associated_port_id = floatingip.get('port_id') network_id = floatingip.get('floating_network_id') floating_ip = floatingip.get('floating_ip_address') instance_name = None # find mapping subnet id by network id and floating ip since # subnet info is not passed. subnet = self._get_mapping_neutron_subnet(network_id, floating_ip) if subnet is None: return network = self.plugin.get_network(self.context, network_id) ib_context = context.InfobloxContext(self.context, self.user_id, network, subnet, self.grid_config, self.plugin, self._cached_grid_members, self._cached_network_views, self._cached_mapping_conditions) dns_controller = dns.DnsController(ib_context) if associated_port_id: instance_name = self._get_instance_name_from_fip(floatingip) is_floating_ip = True db_port = dbi.get_port_by_id(session, associated_port_id) else: is_floating_ip = False db_floatingip = dbi.get_floatingip_by_id(session, floating_ip_id) db_port = dbi.get_port_by_id(session, db_floatingip.floating_port_id) dns_controller.bind_names(floating_ip, instance_name, db_port.id, tenant_id, db_port.device_id, db_port.device_owner, is_floating_ip, db_port.name) LOG.info("Floating ip update sync: floating ip = %s, " "instance name = %s, port id = %s, device id: %s, " "device owner = %s", floating_ip, instance_name, db_port.id, db_port.device_id, db_port.device_owner)
def allocate(self, address_request): """Allocate an IP address based on the request passed in. :param address_request: Specifies what to allocate. :type address_request: A subclass of AddressRequest :returns: A netaddr.IPAddress """ # Validate if network is available for which port # association request came. # This handle case where subnet is in process of deletion and # port allocation comes for update_port. if not self._ib_network: raise Exception("IB Network: %s not Found in the NIOS" % (self._neutron_subnet['cidr'])) if not self._validate_network_availability(): raise Exception( "IB Network: %s not Found under Network View: %s" % (self._neutron_subnet['cidr'], self._ib_network.network_view)) ipam_controller = ipam.IpamSyncController(self._ib_cxt) dns_controller = dns.DnsController(self._ib_cxt) if isinstance(address_request, ipam_req.SpecificAddressRequest): allocated_ip = ipam_controller.allocate_specific_ip( str(address_request.address), address_request.mac, address_request.port_id, address_request.tenant_id, address_request.device_id, address_request.device_owner) else: allocated_ip = ipam_controller.allocate_ip_from_pool( self._neutron_subnet.get('id'), self._neutron_subnet.get('allocation_pools'), address_request.mac, address_request.port_id, address_request.tenant_id, address_request.device_id, address_request.device_owner) port_name = (address_request.port_name if hasattr( address_request, 'port_name') else None) if allocated_ip and address_request.device_owner: # we can deal with instance name as hostname in the ipam agent. instance_name = None try: dns_controller.bind_names(allocated_ip, instance_name, address_request.port_id, address_request.tenant_id, address_request.device_id, address_request.device_owner, port_name=port_name) except Exception: with excutils.save_and_reraise_exception(): ipam_controller.deallocate_ip(allocated_ip, address_request.device_owner) return allocated_ip
def allocate_subnet(self, rollback_list, subnet_request): """Create an IPAM subnet from the subnet request which contains cidr. Allocates a subnet to the Infoblox backend. :param subnet_request: instance of SubnetRequest child :returns: a InfobloxSubnet instance """ # if subnetpool is defined, the request is AnySubnetRequest, so # we need to convert it to SpecificSubnetRequest calling # SubnetAllocator; however, calling this will not pass custom # parameters we defined so we need to get them back from the original # subnet_request. if self._subnetpool: orig_request = { 'name': subnet_request.name, 'network_id': subnet_request.network_id, 'subnetpool_id': subnet_request.subnetpool_id, 'enable_dhcp': subnet_request.enable_dhcp, 'dns_nameservers': subnet_request.dns_nameservers } subnet = super(InfobloxPool, self).allocate_subnet(subnet_request) subnet_request = subnet.get_details() subnet_request.name = orig_request['name'] subnet_request.network_id = orig_request['network_id'] subnet_request.subnetpool_id = orig_request['subnetpool_id'] subnet_request.enable_dhcp = orig_request['enable_dhcp'] subnet_request.dns_nameservers = orig_request['dns_nameservers'] # SubnetRequest must be SpecificSubnet at this point if not isinstance(subnet_request, ipam_req.SpecificSubnetRequest): raise ipam_exc.InvalidSubnetRequestType( subnet_type=type(subnet_request)) neutron_subnet = self._build_subnet_from_request(subnet_request) ib_cxt = ib_context.InfobloxContext(self._context, self._context.user_id, None, neutron_subnet, self._grid_config, plugin=self._plugin) ipam_controller = ipam.IpamSyncController(ib_cxt) dns_controller = dns.DnsController(ib_cxt) ib_network = self._create_ib_network(rollback_list, ipam_controller) if ib_network: dns_controller.create_dns_zones(rollback_list) LOG.info("Created DNS zones.") return InfobloxSubnet(subnet_request, neutron_subnet, ib_network, ib_cxt)
def setUp(self): super(DnsControllerTestCase, self).setUp() self.neutron_cxt = context.get_admin_context() self.test_dns_zone = 'infoblox.com' self.ib_cxt = self._get_ib_context() self.ib_cxt.context = self.neutron_cxt self.ib_cxt.network_is_external = False self.ib_cxt.grid_config.zone_creation_strategy = ( self._get_default_zone_creation_strategy()) self.test_zone_format = "IPV%s" % self.ib_cxt.subnet['ip_version'] self.controller = dns.DnsController(self.ib_cxt) self.controller.pattern_builder = mock.Mock() self.controller.pattern_builder.get_zone_name.return_value = ( self.test_dns_zone)
def test_unbind_names_without_name(self): ip_address = '11.11.1.2' port_id = 'port-id' port_name = 'port-name' self.ib_cxt.grid_config.default_host_name_pattern = '{instance_name}' self.ib_cxt.grid_config.default_domain_name_pattern = '{subnet_id}.com' controller = dns.DnsController(self.ib_cxt) controller.unbind_names(ip_address, None, port_id, device_owner='compute:nova', port_name=port_name) assert self.ib_cxt.ip_alloc.method_calls == [ mock.call.unbind_names( self.ib_cxt.mapping.network_view, self.ib_cxt.mapping.dns_view, ip_address, None, None)]
def allocate(self, address_request): """Allocate an IP address based on the request passed in. :param address_request: Specifies what to allocate. :type address_request: A subclass of AddressRequest :returns: A netaddr.IPAddress """ ipam_controller = ipam.IpamSyncController(self._ib_cxt) dns_controller = dns.DnsController(self._ib_cxt) if isinstance(address_request, ipam_req.SpecificAddressRequest): allocated_ip = ipam_controller.allocate_specific_ip( str(address_request.address), address_request.mac, address_request.port_id, address_request.tenant_id, address_request.device_id, address_request.device_owner) else: allocated_ip = ipam_controller.allocate_ip_from_pool( self._neutron_subnet.get('id'), self._neutron_subnet.get('allocation_pools'), address_request.mac, address_request.port_id, address_request.tenant_id, address_request.device_id, address_request.device_owner) port_name = (address_request.port_name if hasattr( address_request, 'port_name') else None) if allocated_ip and address_request.device_owner: # we can deal with instance name as hostname in the ipam agent. instance_name = None try: dns_controller.bind_names(allocated_ip, instance_name, address_request.port_id, address_request.tenant_id, address_request.device_id, address_request.device_owner, port_name=port_name) except Exception: with excutils.save_and_reraise_exception(): ipam_controller.deallocate_ip(allocated_ip) return allocated_ip
def remove_subnet(self, subnet_id): """Remove IPAM Subnet. Removes a subnet from the Infoblox backend. """ ib_network = self._get_ib_network(subnet_id) if not ib_network: return neutron_subnet = self._build_subnet_from_ib_network(ib_network) ib_cxt = ib_context.InfobloxContext(self._context, self._context.user_id, None, neutron_subnet, self._grid_config, plugin=self._plugin, ib_network=ib_network) ipam_controller = ipam.IpamSyncController(ib_cxt) dns_controller = dns.DnsController(ib_cxt) ipam_controller.delete_subnet(ib_network) dns_controller.delete_dns_zones(ib_network=ib_network)
def _process_port(self, port, event, instance_name=None): for fixed_ip in port['fixed_ips']: subnet_id = fixed_ip['subnet_id'] subnet = self.plugin.get_subnet(self.context, subnet_id) if not subnet: LOG.warning("No subnet was found for subnet_id=%s", subnet_id) continue ib_context = context.InfobloxContext( self.context, self.user_id, None, subnet, self.grid_config, self.plugin, self._cached_grid_members, self._cached_network_views, self._cached_mapping_conditions) dns_controller = dns.DnsController(ib_context) if instance_name is not None: dns_controller.bind_names(fixed_ip['ip_address'], instance_name, port['id'], port['tenant_id'], port['device_id'], port['device_owner'], port_name=port['name']) LOG.info( "%s sync: ip = %s, instance name = %s, " "port id = %s, device id: %s, device owner: %s", event, fixed_ip['ip_address'], instance_name, port['id'], port['device_id'], port['device_owner']) else: dns_controller.unbind_names( fixed_ip['ip_address'], None, port['id'], port['tenant_id'], None, const.NEUTRON_DEVICE_OWNER_COMPUTE_NOVA, port_name=port['name'])
def sync_neutron_to_infoblox(context, credentials, grid_manager): """Sync neutron objects to Infoblox grid Prerequisites: 1. network views to sync must have "Cloud Adapter ID" EA set. 2. infoblox agent sync should have been processed and updated members and network views. """ LOG.info("Starting migration...\n") delete_unknown_ips = cfg.CONF.delete_unknown_ips grid_config = grid_manager.grid_config grid_id = grid_config.grid_id session = context.session neutron_api = neutron_client.Client(**credentials) payload = neutron_api.list_networks() networks = payload['networks'] if not networks: LOG.info("No network exists...Exiting...") return payload = neutron_api.list_subnets() subnets = payload['subnets'] if not subnets: LOG.info("No subnet exists...Exiting...") return payload = neutron_api.list_ports() ports = payload['ports'] nova_api = nova_client.Client(NOVA_API_VERSION, session=credentials['session']) instance_names_by_instance_id = dict() instance_names_by_floating_ip = dict() for server in nova_api.servers.list(search_opts={'all_tenants': 1}): instance_names_by_instance_id[server.id] = server.name floating_ips = [] for net in server.addresses: floating_ips += [ ip['addr'] for ip in server.addresses[net] if ip['OS-EXT-IPS:type'] == 'floating' ] for fip in floating_ips: instance_names_by_floating_ip[fip] = server.name user_id = neutron_api.httpclient.get_user_id() user_tenant_id = neutron_api.httpclient.get_project_id() ib_networks = [] should_exit = False # sync subnets for subnet in subnets: subnet_id = subnet['id'] subnet_name = subnet['name'] network_id = subnet['network_id'] network = utils.find_one_in_list('id', network_id, networks) if not network: LOG.warning("network (%s) is not found. Skipping subnet (%s)", network_id, subnet_id) continue network_name = network['name'] ib_cxt = ib_context.InfobloxContext(context, user_id, network, subnet, grid_config, plugin=neutron_api) db_mapped_netview = dbi.get_network_view_by_mapping( session, grid_id=grid_id, network_id=network_id, subnet_id=subnet_id) if db_mapped_netview: LOG.info("Mapping found for network (%s), subnet (%s)", network_name, subnet_name) if len(db_mapped_netview) > 1: LOG.warning("More that one db_mapped_netview returned") if delete_unknown_ips: ib_network = ib_objects.Network.search( ib_cxt.connector, network_view=db_mapped_netview[0].network_view, cidr=subnet.get('cidr')) ib_networks.append(ib_network) continue ipam_controller = ipam.IpamSyncController(ib_cxt) dns_controller = dns.DnsController(ib_cxt) rollback_list = [] try: ib_network = ipam_controller.create_subnet(rollback_list) if ib_network: if delete_unknown_ips: ib_networks.append(ib_network) dns_controller.create_dns_zones(rollback_list) LOG.info("Created network (%s), subnet (%s)", network_name, subnet_name) except Exception as e: LOG.error(_LE("Error occurred: %(error)s"), {'error': e}) for ib_obj in reversed(rollback_list): try: ib_obj.delete() except ib_exc.InfobloxException as e: LOG.warning( _LW("Unable to delete %(obj)s due to " "error: %(error)s."), { 'obj': ib_obj, 'error': e }) should_exit = True break if should_exit: LOG.info("Exiting due to the error in creating subnet...") return # sync ports for port in ports: port_id = port['id'] port_name = port['name'] port_mac_address = port['mac_address'] tenant_id = port.get('tenant_id') or user_tenant_id network_id = port['network_id'] device_owner = port['device_owner'] device_id = port['device_id'] instance_name = (instance_names_by_instance_id[device_id] if device_id in instance_names_by_instance_id else None) network = utils.find_one_in_list('id', network_id, networks) if not network: LOG.error("network (%s) not found", network_id) break for ip_set in port.get('fixed_ips'): subnet_id = ip_set['subnet_id'] ip_address = ip_set['ip_address'] LOG.info("Adding port for %s: %s...", device_owner, ip_address) subnet = utils.find_one_in_list('id', subnet_id, subnets) if not subnet: should_exit = True LOG.error("subnet (%s) not found", subnet_id) break ib_cxt = ib_context.InfobloxContext(context, user_id, network, subnet, grid_config, plugin=neutron_api) connector = ib_cxt.connector netview = ib_cxt.mapping.network_view search_fields = {'network_view': netview, 'ip_address': ip_address} obj_type = ('ipv4address' if utils.get_ip_version(ip_address) == 4 else 'ipv6address') ib_address = connector.get_object(obj_type, search_fields, return_fields=['objects'], force_proxy=True) if ib_address and ib_address[0]['objects']: LOG.info("%s is found...no need to create", ip_address) continue ipam_controller = ipam.IpamSyncController(ib_cxt) dns_controller = dns.DnsController(ib_cxt) # for a floating ip port, check for its association. # if associated, then port info needs to be the associated port, # not the floating ip port because the associated port contains # actual attached device info is_floating_ip = False if ip_address in instance_names_by_floating_ip: db_floatingip = dbi.get_floatingip_by_ip_address( session, ip_address) db_port = dbi.get_port_by_id(session, db_floatingip.fixed_port_id) port_id = db_port.id port_name = db_port.name tenant_id = db_port.tenant_id device_id = db_port.device_id device_owner = db_port.device_owner instance_name = instance_names_by_floating_ip[ip_address] is_floating_ip = True allocated_ip = ipam_controller.allocate_specific_ip( ip_address, port_mac_address, port_id, tenant_id, device_id, device_owner) if allocated_ip and device_owner: try: dns_controller.bind_names(allocated_ip, instance_name, port_id, tenant_id, device_id, device_owner, is_floating_ip, port_name) except Exception as e: should_exit = True LOG.error("Unable to allocate ip (%s): %s", ip_address, e) ipam_controller.deallocate_ip(allocated_ip) break LOG.info("Allocated %s", ip_address) if should_exit: LOG.info("Existing due to error in port creation...") break if delete_unknown_ips: LOG.info("Start deleting unknown Fixed IP's from Infoblox...") for ib_network in ib_networks: nw_ea = ib_network.extattrs # Skip network if it doesn't have EA or if EA indicates it's # shared or external. if (not nw_ea or nw_ea.get('Is External') or nw_ea.get('Is Shared')): continue LOG.info("Searching for Fixed IP: network_view='%s', cidr='%s'" % (ib_network.network_view, ib_network.network)) fixed_ips = ib_objects.FixedAddress.search_all( ib_cxt.connector, network_view=ib_network.network_view, network=ib_network.network) if not fixed_ips: LOG.info("No FixedIP found: network_view='%s', cidr='%s'" % (ib_network.network_view, ib_network.network)) continue for fixed_ip in fixed_ips: ea = fixed_ip.extattrs port_id = None if ea: port_id = ea.get('Port ID') # Delete Fixed IP if: # - Fixed IP does not have 'Port ID' EA, or # - No port_id in neutron matches 'Port ID' EA value if not (port_id and utils.find_one_in_list('id', port_id, ports)): LOG.info("Deleting Fixed IP from Infoblox: '%s'" % fixed_ip) fixed_ip.delete() LOG.info("Ending migration...")