class AddressScope(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = models.AddressScope fields = { 'id': common_types.UUIDField(), 'project_id': obj_fields.StringField(nullable=True), 'name': obj_fields.StringField(), 'shared': obj_fields.BooleanField(), 'ip_version': common_types.IPVersionEnumField(), } @classmethod def get_network_address_scope(cls, context, network_id, ip_version): query = context.session.query(cls.db_model) query = query.join( models_v2.SubnetPool, models_v2.SubnetPool.address_scope_id == cls.db_model.id) query = query.filter( cls.db_model.ip_version == ip_version, models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id, models_v2.Subnet.network_id == network_id) scope_model_obj = query.one_or_none() if scope_model_obj: return cls._load_object(context, scope_model_obj) return None
class SubnetPool(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = models.SubnetPool fields = { 'id': common_types.UUIDField(), 'project_id': obj_fields.StringField(nullable=True), 'name': obj_fields.StringField(nullable=True), 'ip_version': common_types.IPVersionEnumField(), 'default_prefixlen': common_types.IPNetworkPrefixLenField(), 'min_prefixlen': common_types.IPNetworkPrefixLenField(), 'max_prefixlen': common_types.IPNetworkPrefixLenField(), 'shared': obj_fields.BooleanField(), 'is_default': obj_fields.BooleanField(), 'default_quota': obj_fields.IntegerField(nullable=True), 'hash': obj_fields.StringField(nullable=True), 'address_scope_id': common_types.UUIDField(nullable=True), 'prefixes': common_types.ListOfIPNetworksField(nullable=True) } fields_no_update = ['id', 'project_id'] synthetic_fields = ['prefixes'] def from_db_object(self, db_obj): super(SubnetPool, self).from_db_object(db_obj) self.prefixes = [] self.prefixes = [ prefix.cidr for prefix in db_obj.get('prefixes', []) ] self.obj_reset_changes(['prefixes']) def _attach_prefixes(self, prefixes): SubnetPoolPrefix.delete_objects(self.obj_context, subnetpool_id=self.id) for prefix in prefixes: SubnetPoolPrefix(self.obj_context, subnetpool_id=self.id, cidr=prefix).create() self.prefixes = prefixes self.obj_reset_changes(['prefixes']) # TODO(ihrachys): Consider extending base to trigger registered methods def create(self): fields = self.obj_get_changes() with self.db_context_writer(self.obj_context): prefixes = self.prefixes super(SubnetPool, self).create() if 'prefixes' in fields: self._attach_prefixes(prefixes) # TODO(ihrachys): Consider extending base to trigger registered methods def update(self): fields = self.obj_get_changes() with self.db_context_writer(self.obj_context): super(SubnetPool, self).update() if 'prefixes' in fields: self._attach_prefixes(fields['prefixes'])
def setUp(self): super(IPVersionEnumFieldTest, self).setUp() self.field = common_types.IPVersionEnumField() self.coerce_good_values = [(val, val) for val in constants.IP_ALLOWED_VERSIONS] self.coerce_bad_values = [5, 0, -1, 'str'] self.to_primitive_values = self.coerce_good_values self.from_primitive_values = self.coerce_good_values
class AddressScope(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = models.AddressScope fields = { 'id': common_types.UUIDField(), 'project_id': obj_fields.StringField(nullable=True), 'name': obj_fields.StringField(), 'shared': obj_fields.BooleanField(), 'ip_version': common_types.IPVersionEnumField(), }
class Subnet(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = models_v2.Subnet fields = { 'id': common_types.UUIDField(), 'project_id': obj_fields.StringField(nullable=True), 'name': obj_fields.StringField(nullable=True), 'network_id': common_types.UUIDField(), 'segment_id': common_types.UUIDField(nullable=True), # NOTE: subnetpool_id can be 'prefix_delegation' string # when the IPv6 Prefix Delegation is enabled 'subnetpool_id': obj_fields.StringField(nullable=True), 'ip_version': common_types.IPVersionEnumField(), 'cidr': common_types.IPNetworkField(), 'gateway_ip': obj_fields.IPAddressField(nullable=True), 'allocation_pools': obj_fields.ListOfObjectsField('IPAllocationPool', nullable=True), 'enable_dhcp': obj_fields.BooleanField(nullable=True), 'shared': obj_fields.BooleanField(nullable=True), 'dns_nameservers': obj_fields.ListOfObjectsField('DNSNameServer', nullable=True), 'host_routes': obj_fields.ListOfObjectsField('Route', nullable=True), 'ipv6_ra_mode': common_types.IPV6ModeEnumField(nullable=True), 'ipv6_address_mode': common_types.IPV6ModeEnumField(nullable=True), 'service_types': obj_fields.ListOfStringsField(nullable=True) } synthetic_fields = [ 'allocation_pools', 'dns_nameservers', 'host_routes', 'service_types', 'shared' ] foreign_keys = {'Network': {'network_id': 'id'}} fields_no_update = ['project_id', 'network_id'] fields_need_translation = {'host_routes': 'routes'} def __init__(self, context=None, **kwargs): super(Subnet, self).__init__(context, **kwargs) self.add_extra_filter_name('shared') def obj_load_attr(self, attrname): if attrname == 'shared': return self._load_shared() if attrname == 'service_types': return self._load_service_types() super(Subnet, self).obj_load_attr(attrname) def _load_shared(self, db_obj=None): if db_obj: # NOTE(korzen) db_obj is passed when Subnet object is loaded # from DB rbac_entries = db_obj.get('rbac_entries') or {} shared = (rbac_db.RbacNeutronDbObjectMixin.is_network_shared( self.obj_context, rbac_entries)) else: # NOTE(korzen) this case is used when Subnet object was # instantiated and without DB interaction (get_object(s), update, # create), it should be rare case to load 'shared' by that method shared = (rbac_db.RbacNeutronDbObjectMixin.get_shared_with_tenant( self.obj_context.elevated(), network.NetworkRBAC, self.network_id, self.project_id)) setattr(self, 'shared', shared) self.obj_reset_changes(['shared']) def _load_service_types(self, db_obj=None): if db_obj: service_types = db_obj.get('service_types', []) else: service_types = SubnetServiceType.get_objects(self.obj_context, subnet_id=self.id) self.service_types = [ service_type['service_type'] for service_type in service_types ] self.obj_reset_changes(['service_types']) def from_db_object(self, db_obj): super(Subnet, self).from_db_object(db_obj) self._load_shared(db_obj) self._load_service_types(db_obj) @classmethod def modify_fields_from_db(cls, db_obj): # TODO(korzen) remove this method when IP and CIDR decorator ready result = super(Subnet, cls).modify_fields_from_db(db_obj) if 'cidr' in result: result['cidr'] = utils.AuthenticIPNetwork(result['cidr']) if 'gateway_ip' in result and result['gateway_ip'] is not None: result['gateway_ip'] = netaddr.IPAddress(result['gateway_ip']) return result @classmethod def modify_fields_to_db(cls, fields): # TODO(korzen) remove this method when IP and CIDR decorator ready result = super(Subnet, cls).modify_fields_to_db(fields) if 'cidr' in result: result['cidr'] = cls.filter_to_str(result['cidr']) if 'gateway_ip' in result and result['gateway_ip'] is not None: result['gateway_ip'] = cls.filter_to_str(result['gateway_ip']) return result @classmethod def find_candidate_subnets(cls, context, network_id, host, service_type, fixed_configured): """Find canditate subnets for the network, host, and service_type""" query = cls.query_subnets_on_network(context, network_id) query = SubnetServiceType.query_filter_service_subnets( query, service_type) # Select candidate subnets and return them if not cls.is_host_set(host): if fixed_configured: # If fixed_ips in request and host is not known all subnets on # the network are candidates. Host/Segment will be validated # on port update with binding:host_id set. Allocation _cannot_ # be deferred as requested fixed_ips would then be lost. return query.all() # If the host isn't known, we can't allocate on a routed network. # So, exclude any subnets attached to segments. return cls._query_exclude_subnets_on_segments(query).all() # The host is known. Consider both routed and non-routed networks results = cls._query_filter_by_segment_host_mapping(query, host).all() # For now, we're using a simplifying assumption that a host will only # touch one segment in a given routed network. Raise exception # otherwise. This restriction may be relaxed as use cases for multiple # mappings are understood. segment_ids = { subnet.segment_id for subnet, mapping in results if mapping } if 1 < len(segment_ids): raise segment_exc.HostConnectedToMultipleSegments( host=host, network_id=network_id) return [subnet for subnet, _mapping in results] @classmethod def _query_filter_by_segment_host_mapping(cls, query, host): # TODO(tuanvu): find OVO-like solution for handling "join queries" and # write unit test for this function """Excludes subnets on segments not reachable by the host The query gets two kinds of subnets: those that are on segments that the host can reach and those that are not on segments at all (assumed reachable by all hosts). Hence, subnets on segments that the host *cannot* reach are excluded. """ SegmentHostMapping = segment_model.SegmentHostMapping # A host has been provided. Consider these two scenarios # 1. Not a routed network: subnets are not on segments # 2. Is a routed network: only subnets on segments mapped to host # The following join query returns results for either. The two are # guaranteed to be mutually exclusive when subnets are created. query = query.add_entity(SegmentHostMapping) query = query.outerjoin( SegmentHostMapping, and_(cls.db_model.segment_id == SegmentHostMapping.segment_id, SegmentHostMapping.host == host)) # Essentially "segment_id IS NULL XNOR host IS NULL" query = query.filter( or_( and_(cls.db_model.segment_id.isnot(None), SegmentHostMapping.host.isnot(None)), and_(cls.db_model.segment_id.is_(None), SegmentHostMapping.host.is_(None)))) return query @classmethod def query_subnets_on_network(cls, context, network_id): query = model_query.get_collection_query(context, cls.db_model) return query.filter(cls.db_model.network_id == network_id) @classmethod def _query_exclude_subnets_on_segments(cls, query): """Excludes all subnets associated with segments For the case where the host is not known, we don't consider any subnets that are on segments. But, we still consider subnets that are not associated with any segment (i.e. for non-routed networks). """ return query.filter(cls.db_model.segment_id.is_(None)) @classmethod def is_host_set(cls, host): """Utility to tell if the host is set in the port binding""" # This seems redundant, but its not. Host is unset if its None, '', # or ATTR_NOT_SPECIFIED due to differences in host binding # implementations. return host and validators.is_attr_set(host) @classmethod def network_has_no_subnet(cls, context, network_id, host, service_type): # Determine why we found no subnets to raise the right error query = cls.query_subnets_on_network(context, network_id) if cls.is_host_set(host): # Empty because host isn't mapped to a segment with a subnet? s_query = query.filter(cls.db_model.segment_id.isnot(None)) if s_query.limit(1).count() != 0: # It is a routed network but no subnets found for host raise segment_exc.HostNotConnectedToAnySegment( host=host, network_id=network_id) if not query.limit(1).count(): # Network has *no* subnets of any kind. This isn't an error. return True # Does filtering ineligible service subnets makes the list empty? query = SubnetServiceType.query_filter_service_subnets( query, service_type) if query.limit(1).count(): # No, must be a deferred IP port because there are matching # subnets. Happens on routed networks when host isn't known. raise ipam_exceptions.DeferIpam() return False
class SubnetPool(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = models.SubnetPool fields = { 'id': obj_fields.UUIDField(), 'tenant_id': obj_fields.UUIDField(nullable=True), 'name': obj_fields.StringField(nullable=True), 'ip_version': common_types.IPVersionEnumField(), 'default_prefixlen': common_types.IPNetworkPrefixLenField(), 'min_prefixlen': common_types.IPNetworkPrefixLenField(), 'max_prefixlen': common_types.IPNetworkPrefixLenField(), 'shared': obj_fields.BooleanField(), 'is_default': obj_fields.BooleanField(), 'default_quota': obj_fields.IntegerField(nullable=True), 'hash': obj_fields.StringField(nullable=True), 'address_scope_id': obj_fields.UUIDField(nullable=True), 'prefixes': common_types.ListOfIPNetworksField(nullable=True) } fields_no_update = ['id', 'tenant_id'] synthetic_fields = ['prefixes'] @classmethod def modify_fields_from_db(cls, db_obj): fields = super(SubnetPool, cls).modify_fields_from_db(db_obj) if 'prefixes' in fields: fields['prefixes'] = [ netaddr.IPNetwork(prefix.cidr) for prefix in fields['prefixes'] ] return fields @classmethod def modify_fields_to_db(cls, fields): result = super(SubnetPool, cls).modify_fields_to_db(fields) if 'prefixes' in result: result['prefixes'] = [ models.SubnetPoolPrefix(cidr=str(prefix), subnetpool_id=result['id']) for prefix in result['prefixes'] ] return result def reload_prefixes(self): prefixes = [ obj.cidr for obj in SubnetPoolPrefix.get_objects(self.obj_context, subnetpool_id=self.id) ] setattr(self, 'prefixes', prefixes) self.obj_reset_changes(['prefixes']) @classmethod def get_object(cls, context, **kwargs): with db_api.autonested_transaction(context.session): pool_obj = super(SubnetPool, cls).get_object(context, **kwargs) if pool_obj is not None: pool_obj.reload_prefixes() return pool_obj @classmethod def get_objects(cls, context, **kwargs): with db_api.autonested_transaction(context.session): objs = super(SubnetPool, cls).get_objects(context, **kwargs) for obj in objs: obj.reload_prefixes() return objs def _get_changed_synthetic_fields(self): fields = self.obj_get_changes() for field in self._get_changed_persistent_fields(): if field in fields: del fields[field] return fields # TODO(ihrachys): Consider extending base to trigger registered methods def create(self): synthetic_changes = self._get_changed_synthetic_fields() with db_api.autonested_transaction(self.obj_context.session): super(SubnetPool, self).create() if 'prefixes' in synthetic_changes: for prefix in self.prefixes: prefix = SubnetPoolPrefix(self.obj_context, subnetpool_id=self.id, cidr=prefix) prefix.create() self.reload_prefixes() # TODO(ihrachys): Consider extending base to trigger registered methods def update(self): with db_api.autonested_transaction(self.obj_context.session): synthetic_changes = self._get_changed_synthetic_fields() super(SubnetPool, self).update() if synthetic_changes: if 'prefixes' in synthetic_changes: old = SubnetPoolPrefix.get_objects(self.obj_context, subnetpool_id=self.id) for prefix in old: prefix.delete() for prefix in self.prefixes: prefix_obj = SubnetPoolPrefix(self.obj_context, subnetpool_id=self.id, cidr=prefix) prefix_obj.create()
class Subnet(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = models_v2.Subnet fields = { 'id': common_types.UUIDField(), 'project_id': obj_fields.StringField(nullable=True), 'name': obj_fields.StringField(nullable=True), 'network_id': common_types.UUIDField(), 'segment_id': common_types.UUIDField(nullable=True), # NOTE: subnetpool_id can be 'prefix_delegation' string # when the IPv6 Prefix Delegation is enabled 'subnetpool_id': obj_fields.StringField(nullable=True), 'ip_version': common_types.IPVersionEnumField(), 'cidr': common_types.IPNetworkField(), 'gateway_ip': obj_fields.IPAddressField(nullable=True), 'allocation_pools': obj_fields.ListOfObjectsField('IPAllocationPool', nullable=True), 'enable_dhcp': obj_fields.BooleanField(nullable=True), 'shared': obj_fields.BooleanField(nullable=True), 'dns_nameservers': obj_fields.ListOfObjectsField('DNSNameServer', nullable=True), 'host_routes': obj_fields.ListOfObjectsField('Route', nullable=True), 'ipv6_ra_mode': common_types.IPV6ModeEnumField(nullable=True), 'ipv6_address_mode': common_types.IPV6ModeEnumField(nullable=True), 'service_types': obj_fields.ListOfStringsField(nullable=True) } synthetic_fields = [ 'allocation_pools', 'dns_nameservers', 'host_routes', 'service_types', 'shared' ] foreign_keys = {'Network': {'network_id': 'id'}} fields_no_update = ['project_id', 'network_id', 'segment_id'] fields_need_translation = {'host_routes': 'routes'} def __init__(self, context=None, **kwargs): super(Subnet, self).__init__(context, **kwargs) self.add_extra_filter_name('shared') def obj_load_attr(self, attrname): if attrname == 'shared': return self._load_shared() if attrname == 'service_types': return self._load_service_types() super(Subnet, self).obj_load_attr(attrname) def _load_shared(self, db_obj=None): if db_obj: # NOTE(korzen) db_obj is passed when Subnet object is loaded # from DB rbac_entries = db_obj.get('rbac_entries') or {} shared = (rbac_db.RbacNeutronDbObjectMixin.is_network_shared( self.obj_context, rbac_entries)) else: # NOTE(korzen) this case is used when Subnet object was # instantiated and without DB interaction (get_object(s), update, # create), it should be rare case to load 'shared' by that method shared = (rbac_db.RbacNeutronDbObjectMixin.get_shared_with_tenant( self.obj_context.elevated(), rbac_db_models.NetworkRBAC, self.network_id, self.project_id)) setattr(self, 'shared', shared) self.obj_reset_changes(['shared']) def _load_service_types(self, db_obj=None): if db_obj: service_types = db_obj.get('service_types', []) else: service_types = SubnetServiceType.get_objects(self.obj_context, subnet_id=self.id) self.service_types = [ service_type['service_type'] for service_type in service_types ] self.obj_reset_changes(['service_types']) def from_db_object(self, db_obj): super(Subnet, self).from_db_object(db_obj) self._load_shared(db_obj) self._load_service_types(db_obj) @classmethod def modify_fields_from_db(cls, db_obj): # TODO(korzen) remove this method when IP and CIDR decorator ready result = super(Subnet, cls).modify_fields_from_db(db_obj) if 'cidr' in result: result['cidr'] = utils.AuthenticIPNetwork(result['cidr']) if 'gateway_ip' in result and result['gateway_ip'] is not None: result['gateway_ip'] = netaddr.IPAddress(result['gateway_ip']) return result @classmethod def modify_fields_to_db(cls, fields): # TODO(korzen) remove this method when IP and CIDR decorator ready result = super(Subnet, cls).modify_fields_to_db(fields) if 'cidr' in result: result['cidr'] = cls.filter_to_str(result['cidr']) if 'gateway_ip' in result and result['gateway_ip'] is not None: result['gateway_ip'] = cls.filter_to_str(result['gateway_ip']) return result
class Subnet(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = models_v2.Subnet fields = { 'id': obj_fields.UUIDField(), 'project_id': obj_fields.UUIDField(), 'name': obj_fields.StringField(), 'network_id': obj_fields.UUIDField(), 'subnetpool_id': obj_fields.UUIDField(nullable=True), 'ip_version': common_types.IPVersionEnumField(), 'cidr': obj_fields.IPNetworkField(), 'gateway_ip': obj_fields.IPAddressField(nullable=True), 'allocation_pools': obj_fields.ListOfObjectsField('IPAllocationPool', nullable=True), 'enable_dhcp': obj_fields.BooleanField(), 'dns_nameservers': obj_fields.ListOfObjectsField('DNSNameServer', nullable=True), 'host_routes': obj_fields.ListOfObjectsField('Route', nullable=True), 'ipv6_ra_mode': common_types.IPV6ModeEnumField(nullable=True), 'ipv6_address_mode': common_types.IPV6ModeEnumField(nullable=True) } synthetic_fields = ['allocation_pools', 'dns_nameservers', 'host_routes'] foreign_keys = {'network_id': 'id'} fields_need_translation = {'project_id': 'tenant_id'} @classmethod def modify_fields_from_db(cls, db_obj): # TODO(korzen) remove this method when IP and CIDR decorator ready result = super(Subnet, cls).modify_fields_from_db(db_obj) if 'cidr' in result: result['cidr'] = netaddr.IPNetwork(result['cidr']) if 'gateway_ip' in result and result['gateway_ip'] is not None: result['gateway_ip'] = netaddr.IPAddress(result['gateway_ip']) return result @classmethod def modify_fields_to_db(cls, fields): # TODO(korzen) remove this method when IP and CIDR decorator ready result = super(Subnet, cls).modify_fields_to_db(fields) if 'cidr' in result: result['cidr'] = str(result['cidr']) if 'gateway_ip' in result and result['gateway_ip'] is not None: result['gateway_ip'] = str(result['gateway_ip']) return result