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 test_allocate_ip_from_pool(self): test_opts = dict() self.helper.prepare_test(test_opts) subnet_id = 'subnet-id' allocation_pools = [{ 'start': '11.11.1.1', 'end': '11.11.1.150' }, { 'start': '11.11.1.151', 'end': '11.11.1.253' }] mac = ':'.join(['00'] * 6) dns_view = self.ib_cxt.mapping.dns_view zone_auth = 'ib.com' hostname = mock.ANY ea_ip_address = mock.ANY ipam_controller = ipam.IpamSyncController(self.ib_cxt) ipam_controller.pattern_builder = mock.Mock() ipam_controller.pattern_builder.get_zone_name.return_value = zone_auth ipam_controller.allocate_ip_from_pool(subnet_id, allocation_pools, mac) ipam_controller.pattern_builder.get_zone_name.assert_called_once_with( is_external=self.ib_cxt.network_is_external) self.ib_cxt.ip_alloc.allocate_ip_from_range.assert_called_once_with( self.helper.options['network_view'], dns_view, zone_auth, hostname, mac, allocation_pools[0]['start'], allocation_pools[0]['end'], ea_ip_address)
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 _test_create_subnet_existing_external_network(self, tenant_mock): test_opts = { 'network_name': 'extnet', 'subnet_name': 'extsub', 'cidr': '172.192.1.0/24', 'external': True, 'network_exists': True } self.helper.prepare_test(test_opts) ipam_controller = ipam.IpamSyncController(self.ib_cxt) ipam_controller._allocate_pools = mock.Mock() tenant = mock.Mock() tenant_mock.search = mock.Mock(return_value=tenant) with mock.patch.object(ib_objects.Network, 'search', return_value=mock.Mock()): rollback_list = [] ipam_controller.create_subnet(rollback_list) tenant_mock.search.assert_called_once_with(self.ib_cxt.connector, id=self.ib_cxt.tenant_id) tenant.update.assert_called_once_with() assert tenant.name == self.ib_cxt.tenant_name self.ib_cxt.ibom.update_network_options.assert_called_once_with( mock.ANY, mock.ANY)
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 create_restart_data(self, allow_service_restart=True): helper = IpamControllerTestHelper() ib_cxt = helper.ib_cxt ib_cxt.grid_config.allow_service_restart = allow_service_restart ib_cxt.grid_config.dhcp_support = True ib_cxt.ibom.restart_all_services = mock.Mock() ipam_controller = ipam.IpamSyncController(ib_cxt) member = {'name': 'member1'} mock.patch.object(ipam_controller, '_get_service_members', return_value=[member['name']]).start() return ib_cxt, ipam_controller, member
def test_deallocate_ip(self): test_opts = dict() self.helper.prepare_test(test_opts) ip_address = '11.11.1.1' dns_view = self.ib_cxt.mapping.dns_view ipam_controller = ipam.IpamSyncController(self.ib_cxt) ipam_controller.deallocate_ip(ip_address) self.ib_cxt.ip_alloc.deallocate_ip.assert_called_once_with( self.helper.options['network_view'], dns_view, ip_address)
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 test_delete_subnet_for_external_network_deletable(self): test_opts = {'external': True, 'network_exists': True} self.helper.prepare_test(test_opts) self.grid_config.admin_network_deletion = True ipam_controller = ipam.IpamSyncController(self.ib_cxt) ipam_controller._release_service_members = mock.Mock() with mock.patch.object(ib_objects.Network, 'search_all', return_value=[]): ipam_controller.delete_subnet() self.ib_cxt.ibom.delete_network.assert_called_once_with( self.helper.options['network_view'], self.helper.subnet['cidr'])
def _test_create_subnet_existing_private_network(self): test_opts = { 'network_exists': True, 'external': False, 'shared': False } self.helper.prepare_test(test_opts) self.ib_cxt.mapping.shared = False self.ib_cxt.network_is_shared_or_external = False ipam_controller = ipam.IpamSyncController(self.ib_cxt) with mock.patch.object(ib_objects.Network, 'search', return_value=mock.Mock()): self.assertRaises(exc.InfobloxPrivateSubnetAlreadyExist, ipam_controller.create_subnet, [])
def test_delete_subnet_for_private_network(self): test_opts = { 'network_exists': True, 'external': False, 'shared': False } self.helper.prepare_test(test_opts) self.ib_cxt.mapping.shared = False self.ib_cxt.network_is_shared_or_external = False ipam_controller = ipam.IpamSyncController(self.ib_cxt) ipam_controller._release_service_members = mock.Mock() with mock.patch.object(ib_objects.Network, 'search_all', return_value=[]): ipam_controller.delete_subnet() self.ib_cxt.ibom.delete_network.assert_called_once_with( self.helper.options['network_view'], self.helper.subnet['cidr'])
def get_subnet(self, subnet_id): """Retrieve an IPAM subnet. :param subnet_id: Neutron subnet identifier :returns: a InfobloxSubnet instance """ neutron_subnet = self._fetch_subnet(subnet_id) subnet_request = self._build_request_from_subnet(neutron_subnet) 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) ib_network = ipam_controller.get_subnet() return InfobloxSubnet(subnet_request, neutron_subnet, ib_network, ib_cxt)
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 _test_create_subnet_new_network_view(self, ip_range_mock, tenant_mock): test_opts = dict() self.helper.prepare_test(test_opts) ipam_controller = ipam.IpamSyncController(self.ib_cxt) ip_range_mock.search = mock.Mock(return_value=None) tenant = mock.Mock() tenant_mock.search = mock.Mock(return_value=tenant) with mock.patch.object(ib_objects.Network, 'search', return_value=None): ipam_controller._register_mapping_member = mock.Mock() rollback_list = [] ipam_controller.create_subnet(rollback_list) tenant_mock.search.assert_called_once_with(self.ib_cxt.connector, id=self.ib_cxt.tenant_id) tenant.update.assert_called_once_with() assert tenant.name == self.ib_cxt.tenant_name self.validate_network_creation(self.helper.options['network_view'], self.helper.subnet)
def test_update_subnet_allocation_pools(self): test_opts = { 'network_exists': True, 'external': False, 'shared': False } self.helper.prepare_test(test_opts) self.ib_cxt.mapping.shared = False self.ib_cxt.network_is_shared_or_external = False new_pools = (netaddr.IPRange('11.11.1.25', '11.11.1.30'), netaddr.IPRange('11.11.1.45', '11.11.1.60')) self.ib_cxt.subnet['allocation_pools'] = new_pools ip_version = self.ib_cxt.subnet['ip_version'] connector = mock.Mock() ib_pools = (ib_objects.IPRange(connector, start_addr='11.11.1.3', end_addr='11.11.1.19'), ib_objects.IPRange(connector, start_addr='11.11.1.25', end_addr='11.11.1.30')) ipam_controller = ipam.IpamSyncController(self.ib_cxt) rollback_list = [] with mock.patch.object(ib_objects.IPRange, 'search_all', return_value=ib_pools): ipam_controller.update_subnet_allocation_pools(rollback_list) # 1st range from ib_pools should be removed ib_pools[0].connector.delete_object.assert_called_once_with(None) # 2nd pool from new_pools should be added self.ib_cxt.ibom.create_ip_range.assert_called_once_with( self.helper.options['network_view'], netaddr.IPAddress(new_pools[1].first, ip_version).format(), netaddr.IPAddress(new_pools[1].last, ip_version).format(), self.helper.subnet['cidr'], mock.ANY, mock.ANY)
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 test_delete_subnet_for_external_network_not_deletable(self): test_opts = {'external': True, 'network_exists': True} self.helper.prepare_test(test_opts) ipam_controller = ipam.IpamSyncController(self.ib_cxt) ipam_controller._release_service_members = mock.Mock() ib_network_ea = self._create_ib_network_ea() ib_network_mock = mock.Mock(extattrs=ib_network_ea) ib_range_ea = self._create_ib_range_ea() ib_ranges_mock = [mock.Mock(extattrs=ib_range_ea)] expected_ea = self._reset_ib_range_ea() with mock.patch.object(ib_objects.Network, 'search_all', return_value=[]): with mock.patch.object(ib_objects.IPRange, 'search_all', return_value=ib_ranges_mock): ipam_controller.delete_subnet(ib_network_mock) assert ib_network_mock.update.called assert ib_network_mock.extattrs.to_dict() == expected_ea assert ib_ranges_mock[0].update.called assert ib_ranges_mock[0].extattrs.to_dict() == expected_ea
def test_allocate_specific_ip(self): test_opts = dict() self.helper.prepare_test(test_opts) ip_address = '11.11.1.3' mac = ':'.join(['00'] * 6) dns_view = self.ib_cxt.mapping.dns_view zone_auth = 'ib.com' hostname = mock.ANY ea_ip_address = mock.ANY ipam_controller = ipam.IpamSyncController(self.ib_cxt) ipam_controller.pattern_builder = mock.Mock() ipam_controller.pattern_builder.get_zone_name.return_value = zone_auth ipam_controller.allocate_specific_ip(ip_address, mac) ipam_controller.pattern_builder.get_zone_name.assert_called_once_with( is_external=self.ib_cxt.network_is_external) self.ib_cxt.ip_alloc.allocate_given_ip.assert_called_once_with( self.helper.options['network_view'], dns_view, zone_auth, hostname, mac, ip_address, ea_ip_address)
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...")