def _try_generate_ip(context, subnets): """Generate an IP address. The IP address will be generated from one of the subnets defined on the network. """ range_qry = context.session.query( models_v2.IPAvailabilityRange).join( models_v2.IPAllocationPool).with_lockmode('update') for subnet in subnets: ip_range = range_qry.filter_by(subnet_id=subnet['id']).first() if not ip_range: LOG.debug("All IPs from subnet %(subnet_id)s (%(cidr)s) " "allocated", {'subnet_id': subnet['id'], 'cidr': subnet['cidr']}) continue ip_address = ip_range['first_ip'] if ip_range['first_ip'] == ip_range['last_ip']: # No more free indices on subnet => delete LOG.debug("No more free IP's in slice. Deleting " "allocation pool.") context.session.delete(ip_range) else: # increment the first free new_first_ip = str(netaddr.IPAddress(ip_address) + 1) ip_range['first_ip'] = new_first_ip LOG.debug("Allocated IP - %(ip_address)s from %(first_ip)s " "to %(last_ip)s", {'ip_address': ip_address, 'first_ip': ip_address, 'last_ip': ip_range['last_ip']}) return {'ip_address': ip_address, 'subnet_id': subnet['id']} raise n_exc.IpAddressGenerationFailure(net_id=subnets[0]['network_id'])
def _choose_available_subnet(self, context, net_id, version=None, ip_address=None): filters = {} if version: filters["version"] = version subnets = db_api.subnet_find_allocation_counts(context, net_id, scope=db_api.ALL, **filters) for subnet, ips_in_subnet in subnets: ipnet = netaddr.IPNetwork(subnet["cidr"]) if ip_address and ip_address not in ipnet: continue ip_policy_rules = None if not ip_address: ip_policy_rules = self.get_ip_policy_rule_set(subnet) policy_size = ip_policy_rules.size if ip_policy_rules else 0 if ipnet.size > (ips_in_subnet + policy_size): return subnet raise exceptions.IpAddressGenerationFailure(net_id=net_id)
def _choose_available_subnet(self, context, net_id, version=None, segment_id=None, ip_address=None, reallocated_ips=None): subnets = super(QuarkIpamBOTHREQ, self)._choose_available_subnet( context, net_id, version, segment_id, ip_address, reallocated_ips) if len(reallocated_ips) + len(subnets) < 2: raise exceptions.IpAddressGenerationFailure(net_id=net_id) return subnets
def _choose_available_subnet(self, context, net_id, version=None, segment_id=None, ip_address=None, reallocated_ips=None): filters = {} if version: filters["ip_version"] = version subnet = self.select_subnet(context, net_id, ip_address, segment_id, **filters) if subnet: return [subnet] raise exceptions.IpAddressGenerationFailure(net_id=net_id)
def test_generate_ip_exhausted_pool(self): with mock.patch.object(non_ipam.IpamNonPluggableBackend, '_try_generate_ip') as generate: with mock.patch.object(non_ipam.IpamNonPluggableBackend, '_rebuild_availability_ranges') as rebuild: exception = n_exc.IpAddressGenerationFailure(net_id='n') # fail first call but not second generate.side_effect = [exception, None] non_ipam.IpamNonPluggableBackend._generate_ip('c', 's') self.assertEqual(2, generate.call_count) rebuild.assert_called_once_with('c', 's')
def _ipam_allocate_single_ip(self, context, ipam_driver, port, subnets): """Allocates single ip from set of subnets Raises n_exc.IpAddressGenerationFailure if allocation failed for all subnets. """ for subnet in subnets: try: return [ self._ipam_try_allocate_ip(context, ipam_driver, port, subnet), subnet ] except ipam_exc.IpAddressGenerationFailure: continue raise n_exc.IpAddressGenerationFailure(net_id=port['network_id'])
def _allocate_floatingip_from_configured_subnets(self, context): cfg.CONF.register_opts(explicit_floating_ip_opts, group='akanda') # NOTE(dhellmann): There may be a better way to do this, but # the "filter" argument to get_subnets() is not documented so # who knows. e_context = context.elevated() subnets = [ self._get_subnet(e_context, unicode(s)) for s in cfg.CONF.akanda.floatingip_subnet ] if not subnets: LOG.error('config setting akanda.floatingip_subnet missing') raise q_exc.IpAddressGenerationFailure(net_id='UNKNOWN') # The base class method _generate_ip() handles the allocation # ranges and going from one subnet to the next when a network # is exhausted. return self._generate_ip(context, subnets)
def _choose_available_subnet(self, context, net_id, version=None, segment_id=None, ip_address=None, reallocated_ips=None): both_subnet_versions = [] need_versions = [4, 6] for i in reallocated_ips: if i["version"] in need_versions: need_versions.remove(i["version"]) filters = {} for ver in need_versions: filters["ip_version"] = ver sub = self.select_subnet(context, net_id, ip_address, segment_id, **filters) if sub: both_subnet_versions.append(sub) if not reallocated_ips and not both_subnet_versions: raise exceptions.IpAddressGenerationFailure(net_id=net_id) return both_subnet_versions
def test_create_port_catch_ip_generation_failure_reraise(self): self.assertRaises( n_exc.IpAddressGenerationFailure, self._test__port_action_with_failures, exc=n_exc.IpAddressGenerationFailure(net_id='foo_network_id'), action='create_port')
def _allocate_from_v6_subnet(self, context, net_id, subnet, port_id, reuse_after, ip_address=None, **kwargs): """This attempts to allocate v6 addresses as per RFC2462 and RFC3041. To accomodate this, we effectively treat all v6 assignment as a first time allocation utilizing the MAC address of the VIF. Because we recycle MACs, we will eventually attempt to recreate a previously generated v6 address. Instead of failing, we've opted to handle reallocating that address in this method. This should provide a performance boost over attempting to check each and every subnet in the existing reallocate logic, as we'd have to iterate over each and every subnet returned """ LOG.info("Attempting to allocate a v6 address - [{0}]".format( utils.pretty_kwargs(network_id=net_id, subnet=subnet, port_id=port_id, ip_address=ip_address))) if ip_address: LOG.info("IP %s explicitly requested, deferring to standard " "allocation" % ip_address) return self._allocate_from_subnet(context, net_id=net_id, subnet=subnet, port_id=port_id, reuse_after=reuse_after, ip_address=ip_address, **kwargs) else: mac = kwargs.get("mac_address") if mac: mac = kwargs["mac_address"].get("address") ip_policy_cidrs = models.IPPolicy.get_ip_policy_cidrs(subnet) for tries, ip_address in enumerate( generate_v6(mac, port_id, subnet["cidr"])): LOG.info("Attempt {0} of {1}".format( tries + 1, CONF.QUARK.v6_allocation_attempts)) if tries > CONF.QUARK.v6_allocation_attempts - 1: LOG.info("Exceeded v6 allocation attempts, bailing") raise exceptions.IpAddressGenerationFailure( net_id=net_id) ip_address = netaddr.IPAddress(ip_address).ipv6() LOG.info("Generated a new v6 address {0}".format( str(ip_address))) # NOTE(mdietz): treating the IPSet as a boolean caused netaddr # to attempt to enumerate the entire set! if (ip_policy_cidrs is not None and ip_address in ip_policy_cidrs): LOG.info("Address {0} excluded by policy".format( str(ip_address))) continue # TODO(mdietz): replace this with a compare-and-swap loop with context.session.begin(): address = db_api.ip_address_find( context, network_id=net_id, ip_address=ip_address, scope=db_api.ONE, reuse_after=reuse_after, deallocated=True, subnet_id=subnet["id"], lock_mode=True) if address: LOG.info("Address {0} exists, claiming".format( str(ip_address))) return db_api.ip_address_update( context, address, deallocated=False, deallocated_at=None, used_by_tenant_id=context.tenant_id, allocated_at=timeutils.utcnow(), address_type=kwargs.get('address_type', ip_types.FIXED)) # This triggers when the IP is allocated to another tenant, # either because we missed it due to our filters above, or # in an extremely unlikely race between the find and here. try: with context.session.begin(): return db_api.ip_address_create( context, address=ip_address, subnet_id=subnet["id"], version=subnet["ip_version"], network_id=net_id, address_type=kwargs.get('address_type', ip_types.FIXED)) except db_exception.DBDuplicateEntry: LOG.info("{0} exists but was already " "allocated".format(str(ip_address))) LOG.debug("Duplicate entry found when inserting subnet_id" " %s ip_address %s", subnet["id"], ip_address)
def allocate_ip_address(self, context, net_id, port_id, reuse_after, version=None, ip_address=None): elevated = context.elevated() if ip_address: ip_address = netaddr.IPAddress(ip_address) address = db_api.ip_address_find(elevated, network_id=net_id, reuse_after=reuse_after, deallocated=True, scope=db_api.ONE, ip_address=ip_address) if address: return db_api.ip_address_update(elevated, address, deallocated=False, deallocated_at=None) subnet = self._choose_available_subnet(elevated, net_id, ip_address=ip_address, version=version) ip_policy_rules = self.get_ip_policy_rule_set(subnet) # Creating this IP for the first time next_ip = None if ip_address: next_ip = ip_address address = db_api.ip_address_find(elevated, network_id=net_id, ip_address=next_ip, tenant_id=elevated.tenant_id, scope=db_api.ONE) if address: raise exceptions.IpAddressGenerationFailure(net_id=net_id) else: address = True while address: next_ip_int = int(subnet["next_auto_assign_ip"]) next_ip = netaddr.IPAddress(next_ip_int) if subnet["ip_version"] == 4: next_ip = next_ip.ipv4() subnet["next_auto_assign_ip"] = next_ip_int + 1 if ip_policy_rules and next_ip in ip_policy_rules: continue address = db_api.ip_address_find(elevated, network_id=net_id, ip_address=next_ip, tenant_id=elevated.tenant_id, scope=db_api.ONE) address = db_api.ip_address_create(elevated, address=next_ip, subnet_id=subnet["id"], version=subnet["ip_version"], network_id=net_id) address["deallocated"] = 0 return address
def allocate_ip_address(self, context, new_addresses, net_id, port_id, reuse_after, segment_id=None, version=None, ip_addresses=None, subnets=None, **kwargs): elevated = context.elevated() subnets = subnets or [] ip_addresses = ip_addresses or [] ipam_log = kwargs.get('ipam_log', None) LOG.info("Starting a new IP address(es) allocation. Strategy " "is {0} - [{1}]".format( self.get_name(), utils.pretty_kwargs(network_id=net_id, port_id=port_id, new_addresses=new_addresses, ip_addresses=ip_addresses, subnets=subnets, segment_id=segment_id, version=version))) def _try_reallocate_ip_address(ipam_log, ip_addr=None): new_addresses.extend(self.attempt_to_reallocate_ip( context, net_id, port_id, reuse_after, version=None, ip_address=ip_addr, segment_id=segment_id, subnets=subnets, **kwargs)) def _try_allocate_ip_address(ipam_log, ip_addr=None, sub=None): for retry in xrange(CONF.QUARK.ip_address_retry_max): attempt = None if ipam_log: attempt = ipam_log.make_entry("_try_allocate_ip_address") LOG.info("Allocating new IP attempt {0} of {1}".format( retry + 1, CONF.QUARK.ip_address_retry_max)) if not sub: subnets = self._choose_available_subnet( elevated, net_id, version, segment_id=segment_id, ip_address=ip_addr, reallocated_ips=new_addresses) else: subnets = [self.select_subnet(context, net_id, ip_addr, segment_id, subnet_ids=[sub])] LOG.info("Subnet selection returned {0} viable subnet(s) - " "IDs: {1}".format(len(subnets), ", ".join([str(s["id"]) for s in subnets if s]))) try: self._allocate_ips_from_subnets(context, new_addresses, net_id, subnets, port_id, reuse_after, ip_addr, **kwargs) except q_exc.IPAddressRetryableFailure: LOG.exception("Error in allocating IP") if attempt: LOG.debug("ATTEMPT FAILED") attempt.failed() remaining = CONF.QUARK.ip_address_retry_max - retry - 1 if remaining > 0: LOG.info("{0} retries remain, retrying...".format( remaining)) else: LOG.info("No retries remaing, bailing") continue finally: if attempt: attempt.end() break ip_addresses = [netaddr.IPAddress(ip_address) for ip_address in ip_addresses] if ip_addresses: for ip_address in ip_addresses: _try_reallocate_ip_address(ipam_log, ip_address) else: _try_reallocate_ip_address(ipam_log) if self.is_strategy_satisfied(new_addresses): return else: LOG.info("Reallocated addresses {0} but still need more addresses " "to satisfy strategy {1}. Falling back to creating " "IPs".format(new_addresses, self.get_name())) if ip_addresses or subnets: for ip_address, subnet in itertools.izip_longest(ip_addresses, subnets): _try_allocate_ip_address(ipam_log, ip_address, subnet) else: _try_allocate_ip_address(ipam_log) if self.is_strategy_satisfied(new_addresses, allocate_complete=True): self._notify_new_addresses(context, new_addresses) LOG.info("IPAM for port ID {0} completed with addresses " "{1}".format(port_id, [a["address_readable"] for a in new_addresses])) return ipam_log.failed() raise exceptions.IpAddressGenerationFailure(net_id=net_id)
def allocate_ip_address(self, context, new_addresses, net_id, port_id, reuse_after, segment_id=None, version=None, ip_address=None, subnets=None, **kwargs): elevated = context.elevated() if ip_address: ip_address = netaddr.IPAddress(ip_address) new_addresses.extend( self.attempt_to_reallocate_ip(context, net_id, port_id, reuse_after, version=None, ip_address=ip_address, segment_id=segment_id, subnets=subnets, **kwargs)) if self.is_strategy_satisfied(new_addresses): return for retry in xrange(cfg.CONF.QUARK.ip_address_retry_max): if not subnets: subs = self._choose_available_subnet( elevated, net_id, version, segment_id=segment_id, ip_address=ip_address, reallocated_ips=new_addresses) else: subs = [ self.select_subnet(context, net_id, ip_address, segment_id, subnet_ids=subnets) ] try: self._allocate_ips_from_subnets(context, new_addresses, net_id, subs, port_id, reuse_after, ip_address, **kwargs) except q_exc.IPAddressRetryableFailure: LOG.exception("Error in allocating IP") continue break if self.is_strategy_satisfied(new_addresses, allocate_complete=True): self._notify_new_addresses(context, new_addresses) return raise exceptions.IpAddressGenerationFailure(net_id=net_id)
def _allocate_from_v6_subnet(self, context, net_id, subnet, port_id, reuse_after, ip_address=None, **kwargs): """This attempts to allocate v6 addresses as per RFC2462 and RFC3041. To accomodate this, we effectively treat all v6 assignment as a first time allocation utilizing the MAC address of the VIF. Because we recycle MACs, we will eventually attempt to recreate a previously generated v6 address. Instead of failing, we've opted to handle reallocating that address in this method. This should provide a performance boost over attempting to check each and every subnet in the existing reallocate logic, as we'd have to iterate over each and every subnet returned """ if not (ip_address is None and "mac_address" in kwargs and kwargs["mac_address"]): return self._allocate_from_subnet(context, net_id, subnet, reuse_after, ip_address, **kwargs) else: ip_policy_cidrs = models.IPPolicy.get_ip_policy_cidrs(subnet) for tries, ip_address in enumerate( generate_v6(kwargs["mac_address"]["address"], port_id, subnet["cidr"])): if tries > CONF.QUARK.v6_allocation_attempts - 1: raise exceptions.IpAddressGenerationFailure(net_id=net_id) ip_address = netaddr.IPAddress(ip_address) # NOTE(mdietz): treating the IPSet as a boolean caused netaddr # to attempt to enumerate the entire set! if (ip_policy_cidrs is not None and ip_address in ip_policy_cidrs): continue # TODO(mdietz): replace this with a compare-and-swap loop with context.session.begin(): address = db_api.ip_address_find(context, network_id=net_id, ip_address=ip_address, scope=db_api.ONE, reuse_after=reuse_after, deallocated=True, subnet_id=subnet["id"], lock_mode=True) if address: return db_api.ip_address_update( context, address, deallocated=False, deallocated_at=None, used_by_tenant_id=context.tenant_id, allocated_at=timeutils.utcnow()) # This triggers when the IP is allocated to another tenant, # either because we missed it due to our filters above, or # in an extremely unlikely race between the find and here. try: with context.session.begin(): return db_api.ip_address_create( context, address=ip_address, subnet_id=subnet["id"], version=subnet["ip_version"], network_id=net_id) except db_exception.DBDuplicateEntry: LOG.debug( "Duplicate entry found when inserting subnet_id" " %s ip_address %s", subnet["id"], ip_address)
def attempt_to_reallocate_ip(self, context, net_id, port_id, reuse_after, version=None, ip_address=None, segment_id=None, subnets=None, **kwargs): version = version or [4, 6] elevated = context.elevated() if version == 6 and "mac_address" in kwargs and kwargs["mac_address"]: # Defers to the create case. The reason why is we'd have to look # up subnets here to correctly generate the v6. If we split them # up into reallocate and create, we'd be looking up the same # subnets twice, which is a waste of time. # TODO(mdietz): after reviewing this code, this block annoyingly # doesn't trigger in the ANY case, since we end up # using a list of [4, 6]. It works as expected most # of the time, but we can anticipate that isolated # networks will end up using sequential assignment. # Probably want to rework this logic to compensate # at some point. Considering they all come from the # same MAC address pool, nothing bad will happen, # just worth noticing and fixing. return [] sub_ids = [] if subnets: sub_ids = subnets else: if segment_id: subnets = db_api.subnet_find(elevated, network_id=net_id, segment_id=segment_id) sub_ids = [s["id"] for s in subnets] if not sub_ids: raise exceptions.IpAddressGenerationFailure(net_id=net_id) ip_kwargs = { "network_id": net_id, "reuse_after": reuse_after, "deallocated": True, "scope": db_api.ONE, "ip_address": ip_address, "lock_mode": True, "version": version, "order_by": "address", "do_not_use": False } if sub_ids: ip_kwargs["subnet_id"] = sub_ids # We never want to take the chance of an infinite loop here. Instead, # we'll clean up multiple bad IPs if we find them (assuming something # is really wrong) for retry in xrange(cfg.CONF.QUARK.ip_address_retry_max): get_policy = models.IPPolicy.get_ip_policy_cidrs try: with context.session.begin(): # NOTE(mdietz): Before I removed the lazy=joined, this # raised with an unknown column "address" # error. address = db_api.ip_address_find(elevated, **ip_kwargs) if address: # NOTE(mdietz): We should always be in the CIDR but we # also said that before :-/ subnet = address.get('subnet') if subnet: policy = get_policy(subnet) cidr = netaddr.IPNetwork(address["subnet"]["cidr"]) addr = netaddr.IPAddress(int(address["address"])) if address["subnet"]["ip_version"] == 4: addr = addr.ipv4() else: addr = addr.ipv6() if policy is not None and addr in policy: context.session.delete(address) continue if addr in cidr: updated_address = db_api.ip_address_update( elevated, address, deallocated=False, deallocated_at=None, used_by_tenant_id=context.tenant_id, allocated_at=timeutils.utcnow()) return [updated_address] else: # Make sure we never find it again context.session.delete(address) else: break except Exception: LOG.exception("Error in reallocate ip...") return []
def test_create_port_catch_and_handle_ip_generation_failure(self): self.plugin.get_subnet.side_effect = ( n_exc.SubnetNotFound(subnet_id='foo_subnet_id')) self._test__port_action_with_failures( exc=n_exc.IpAddressGenerationFailure(net_id='foo_network_id'), action='create_port')
def attempt_to_reallocate_ip(self, context, net_id, port_id, reuse_after, version=None, ip_address=None, segment_id=None, subnets=None, **kwargs): version = version or [4, 6] elevated = context.elevated() LOG.info("Attempting to reallocate an IP (step 1 of 3) - [{0}]".format( utils.pretty_kwargs(network_id=net_id, port_id=port_id, version=version, segment_id=segment_id, subnets=subnets))) if version == 6: # Defers to the create case. The reason why is we'd have to look # up subnets here to correctly generate the v6. If we split them # up into reallocate and create, we'd be looking up the same # subnets twice, which is a waste of time. # TODO(mdietz): after reviewing this code, this block annoyingly # doesn't trigger in the ANY case, since we end up # using a list of [4, 6]. It works as expected most # of the time, but we can anticipate that isolated # networks will end up using sequential assignment. # Probably want to rework this logic to compensate # at some point. Considering they all come from the # same MAC address pool, nothing bad will happen, # just worth noticing and fixing. LOG.info("Identified as v6 case, deferring to IP create path") return [] sub_ids = [] if subnets: sub_ids = subnets elif segment_id: subnets = db_api.subnet_find(elevated, network_id=net_id, segment_id=segment_id) sub_ids = [s["id"] for s in subnets] if not sub_ids: LOG.info("No subnets matching segment_id {0} could be " "found".format(segment_id)) raise exceptions.IpAddressGenerationFailure( net_id=net_id) ip_kwargs = { "network_id": net_id, "reuse_after": reuse_after, "deallocated": True, "ip_address": ip_address, "version": version, } if ip_address: del ip_kwargs["deallocated"] if sub_ids: ip_kwargs["subnet_id"] = sub_ids ipam_log = kwargs.get('ipam_log', None) for retry in xrange(CONF.QUARK.ip_address_retry_max): attempt = None if ipam_log: attempt = ipam_log.make_entry("attempt_to_reallocate_ip") LOG.info("Attempt {0} of {1}".format( retry + 1, CONF.QUARK.ip_address_retry_max)) try: with context.session.begin(): transaction = db_api.transaction_create(context) m = models.IPAddress update_kwargs = { m.transaction_id: transaction.id, m.address_type: kwargs.get("address_type", ip_types.FIXED), m.deallocated: False, m.deallocated_at: None, m.used_by_tenant_id: context.tenant_id, m.allocated_at: timeutils.utcnow(), } result = db_api.ip_address_reallocate( elevated, update_kwargs, **ip_kwargs) if not result: LOG.info("Couldn't update any reallocatable addresses " "given the criteria") if attempt: attempt.failed() break updated_address = db_api.ip_address_reallocate_find( elevated, transaction.id) if not updated_address: if attempt: attempt.failed() continue LOG.info("Address {0} is reallocated".format( updated_address["address_readable"])) return [updated_address] except Exception: if attempt: attempt.failed() LOG.exception("Error in reallocate ip...") finally: if attempt: attempt.end() return []
def _validate_subnet(self, context, s, cur_subnet=None): """Validate a subnet spec.""" # This method will validate attributes which may change during # create_subnet() and update_subnet(). # The method requires the subnet spec 's' has 'ip_version' field. # If 's' dict does not have 'ip_version' field in an API call # (e.g., update_subnet()), you need to set 'ip_version' field # before calling this method. ip_ver = s['ip_version'] if attributes.is_attr_set(s.get('cidr')): self._validate_ip_version(ip_ver, s['cidr'], 'cidr') # TODO(watanabe.isao): After we found a way to avoid the re-sync # from the agent side, this restriction could be removed. if cur_subnet: dhcp_was_enabled = cur_subnet.enable_dhcp else: dhcp_was_enabled = False if s.get('enable_dhcp') and not dhcp_was_enabled: subnet_prefixlen = netaddr.IPNetwork(s['cidr']).prefixlen error_message = _("Subnet has a prefix length that is " "incompatible with DHCP service enabled.") if ((ip_ver == 4 and subnet_prefixlen > 30) or (ip_ver == 6 and subnet_prefixlen > 126)): raise n_exc.InvalidInput(error_message=error_message) else: # NOTE(watanabe.isao): The following restriction is necessary # only when updating subnet. if cur_subnet: range_qry = context.session.query( models_v2.IPAvailabilityRange).join( models_v2.IPAllocationPool) ip_range = range_qry.filter_by(subnet_id=s['id']).first() if not ip_range: raise n_exc.IpAddressGenerationFailure( net_id=cur_subnet.network_id) if attributes.is_attr_set(s.get('gateway_ip')): self._validate_ip_version(ip_ver, s['gateway_ip'], 'gateway_ip') if (cfg.CONF.force_gateway_on_subnet and not ipam.utils.check_gateway_in_subnet( s['cidr'], s['gateway_ip'])): error_message = _("Gateway is not valid on subnet") raise n_exc.InvalidInput(error_message=error_message) # Ensure the gateway IP is not assigned to any port # skip this check in case of create (s parameter won't have id) # NOTE(salv-orlando): There is slight chance of a race, when # a subnet-update and a router-interface-add operation are # executed concurrently if cur_subnet: alloc_qry = context.session.query(models_v2.IPAllocation) allocated = alloc_qry.filter_by( ip_address=cur_subnet['gateway_ip'], subnet_id=cur_subnet['id']).first() if allocated and allocated['port_id']: raise n_exc.GatewayIpInUse( ip_address=cur_subnet['gateway_ip'], port_id=allocated['port_id']) if attributes.is_attr_set(s.get('dns_nameservers')): if len(s['dns_nameservers']) > cfg.CONF.max_dns_nameservers: raise n_exc.DNSNameServersExhausted( subnet_id=s.get('id', _('new subnet')), quota=cfg.CONF.max_dns_nameservers) for dns in s['dns_nameservers']: try: netaddr.IPAddress(dns) except Exception: raise n_exc.InvalidInput( error_message=(_("Error parsing dns address %s") % dns)) self._validate_ip_version(ip_ver, dns, 'dns_nameserver') if attributes.is_attr_set(s.get('host_routes')): if len(s['host_routes']) > cfg.CONF.max_subnet_host_routes: raise n_exc.HostRoutesExhausted( subnet_id=s.get('id', _('new subnet')), quota=cfg.CONF.max_subnet_host_routes) # check if the routes are all valid for rt in s['host_routes']: self._validate_host_route(rt, ip_ver) if ip_ver == 4: if attributes.is_attr_set(s.get('ipv6_ra_mode')): raise n_exc.InvalidInput( error_message=(_("ipv6_ra_mode is not valid when " "ip_version is 4"))) if attributes.is_attr_set(s.get('ipv6_address_mode')): raise n_exc.InvalidInput( error_message=(_("ipv6_address_mode is not valid when " "ip_version is 4"))) if ip_ver == 6: self._validate_ipv6_attributes(s, cur_subnet)