Beispiel #1
0
class DVRMacAddress(base.NeutronDbObject):
    # Version 1.0: Initial version
    VERSION = '1.0'

    db_model = dvr_models.DistributedVirtualRouterMacAddress

    primary_keys = ['host']

    fields = {
        'host': obj_fields.StringField(),
        'mac_address': common_types.MACAddressField()
    }

    @classmethod
    def modify_fields_from_db(cls, db_obj):
        fields = super(DVRMacAddress, cls).modify_fields_from_db(db_obj)
        if 'mac_address' in fields:
            # NOTE(tonytan4ever): Here uses AuthenticEUI to retain the format
            # passed from API.
            fields['mac_address'] = utils.AuthenticEUI(fields['mac_address'])
        return fields

    @classmethod
    def modify_fields_to_db(cls, fields):
        result = super(DVRMacAddress, cls).modify_fields_to_db(fields)
        if 'mac_address' in fields:
            result['mac_address'] = cls.filter_to_str(result['mac_address'])
        return result
Beispiel #2
0
class AllowedAddressPair(base.NeutronDbObject):
    # Version 1.0: Initial version
    VERSION = '1.0'

    db_model = models.AllowedAddressPair

    primary_keys = ['port_id', 'mac_address', 'ip_address']

    fields = {
        'port_id': obj_fields.UUIDField(),
        'mac_address': common_types.MACAddressField(),
        'ip_address': obj_fields.IPAddressField(),
    }

    # TODO(mhickey): get rid of it once we switch the db model to using
    # custom types.
    def modify_fields_to_db(self, fields):
        result = super(AllowedAddressPair, self).modify_fields_to_db(fields)
        if 'ip_address' in result:
            result['ip_address'] = str(result['ip_address'])
        if 'mac_address' in result:
            result['mac_address'] = str(result['mac_address'])
        return result

    # TODO(mhickey): get rid of it once we switch the db model to using
    # custom types.
    @classmethod
    def modify_fields_from_db(cls, db_obj):
        fields = super(AllowedAddressPair, cls).modify_fields_from_db(db_obj)
        if 'ip_address' in fields:
            fields['ip_address'] = netaddr.IPAddress(fields['ip_address'])
        if 'mac_address' in fields:
            fields['mac_address'] = netaddr.EUI(fields['mac_address'])
        return fields
Beispiel #3
0
 def setUp(self):
     super(MACAddressFieldTest, self).setUp()
     self.field = common_types.MACAddressField()
     mac1 = tools.get_random_EUI()
     mac2 = tools.get_random_EUI()
     self.coerce_good_values = [(mac1, mac1), (mac2, mac2)]
     self.coerce_bad_values = [
         'XXXX', 'ypp', 'g3:vvv',
         # the field type is strict and does not allow to pass strings, even
         # if they represent a valid MAC address
         tools.get_random_mac(),
     ]
     self.to_primitive_values = self.coerce_good_values
     self.from_primitive_values = self.coerce_good_values
 def setUp(self):
     super(MACAddressFieldTest, self).setUp()
     self.field = common_types.MACAddressField()
     mac1 = tools.get_random_EUI()
     mac2 = tools.get_random_EUI()
     self.coerce_good_values = [(mac1, mac1), (mac2, mac2)]
     self.coerce_bad_values = [
         'XXXX', 'ypp', 'g3:vvv',
         # the field type is strict and does not allow to pass strings, even
         # if they represent a valid MAC address
         net.get_random_mac('fe:16:3e:00:00:00'.split(':')),
     ]
     self.to_primitive_values = ((a1, str(a2))
                                 for a1, a2 in self.coerce_good_values)
     self.from_primitive_values = ((a2, a1)
                                   for a1, a2 in self.to_primitive_values)
Beispiel #5
0
class AllowedAddressPair(base.NeutronDbObject):
    # Version 1.0: Initial version
    VERSION = '1.0'

    db_model = models.AllowedAddressPair

    primary_keys = ['port_id', 'mac_address', 'ip_address']

    fields = {
        'port_id': common_types.UUIDField(),
        'mac_address': common_types.MACAddressField(),
        'ip_address': common_types.IPNetworkField(),
    }

    foreign_keys = {
        'Port': {
            'port_id': 'id'
        },
    }

    # TODO(mhickey): get rid of it once we switch the db model to using
    # custom types.
    @classmethod
    def modify_fields_to_db(cls, fields):
        result = super(AllowedAddressPair, cls).modify_fields_to_db(fields)
        if 'ip_address' in result:
            result['ip_address'] = cls.filter_to_str(result['ip_address'])
        if 'mac_address' in result:
            result['mac_address'] = cls.filter_to_str(result['mac_address'])
        return result

    # TODO(mhickey): get rid of it once we switch the db model to using
    # custom types.
    @classmethod
    def modify_fields_from_db(cls, db_obj):
        fields = super(AllowedAddressPair, cls).modify_fields_from_db(db_obj)
        if 'ip_address' in fields:
            # retain string format as stored in the database
            fields['ip_address'] = utils.AuthenticIPNetwork(
                fields['ip_address'])
        if 'mac_address' in fields:
            # retain string format as stored in the database
            fields['mac_address'] = utils.AuthenticEUI(fields['mac_address'])
        return fields
Beispiel #6
0
class Port(base.NeutronDbObject):
    # Version 1.0: Initial version
    # Version 1.1: Add data_plane_status field
    VERSION = '1.1'

    db_model = models_v2.Port

    fields = {
        'id':
        common_types.UUIDField(),
        'project_id':
        obj_fields.StringField(nullable=True),
        'name':
        obj_fields.StringField(nullable=True),
        'network_id':
        common_types.UUIDField(),
        'mac_address':
        common_types.MACAddressField(),
        'admin_state_up':
        obj_fields.BooleanField(),
        'device_id':
        obj_fields.StringField(),
        'device_owner':
        obj_fields.StringField(),
        'status':
        obj_fields.StringField(),
        'allowed_address_pairs':
        obj_fields.ListOfObjectsField('AllowedAddressPair', nullable=True),
        'binding':
        obj_fields.ObjectField('PortBinding', nullable=True),
        'data_plane_status':
        obj_fields.ObjectField('PortDataPlaneStatus', nullable=True),
        'dhcp_options':
        obj_fields.ListOfObjectsField('ExtraDhcpOpt', nullable=True),
        'distributed_binding':
        obj_fields.ObjectField('DistributedPortBinding', nullable=True),
        'dns':
        obj_fields.ObjectField('PortDNS', nullable=True),
        'fixed_ips':
        obj_fields.ListOfObjectsField('IPAllocation', nullable=True),
        # TODO(ihrachys): consider converting to boolean
        'security':
        obj_fields.ObjectField('PortSecurity', nullable=True),
        'security_group_ids':
        common_types.SetOfUUIDsField(
            nullable=True,
            # TODO(ihrachys): how do we safely pass a mutable default?
            default=None,
        ),
        'qos_policy_id':
        common_types.UUIDField(nullable=True, default=None),
        'binding_levels':
        obj_fields.ListOfObjectsField('PortBindingLevel', nullable=True),

        # TODO(ihrachys): consider adding a 'dns_assignment' fully synthetic
        # field in later object iterations
    }

    extra_filter_names = {'security_group_ids'}

    fields_no_update = ['project_id', 'network_id']

    synthetic_fields = [
        'allowed_address_pairs',
        'binding',
        'binding_levels',
        'data_plane_status',
        'dhcp_options',
        'distributed_binding',
        'dns',
        'fixed_ips',
        'qos_policy_id',
        'security',
        'security_group_ids',
    ]

    fields_need_translation = {
        'binding': 'port_binding',
        'dhcp_options': 'dhcp_opts',
        'distributed_binding': 'distributed_port_binding',
        'security': 'port_security',
    }

    def create(self):
        fields = self.obj_get_changes()
        with db_api.autonested_transaction(self.obj_context.session):
            sg_ids = self.security_group_ids
            if sg_ids is None:
                sg_ids = set()
            qos_policy_id = self.qos_policy_id
            super(Port, self).create()
            if 'security_group_ids' in fields:
                self._attach_security_groups(sg_ids)
            if 'qos_policy_id' in fields:
                self._attach_qos_policy(qos_policy_id)

    def update(self):
        fields = self.obj_get_changes()
        with db_api.autonested_transaction(self.obj_context.session):
            super(Port, self).update()
            if 'security_group_ids' in fields:
                self._attach_security_groups(fields['security_group_ids'])
            if 'qos_policy_id' in fields:
                self._attach_qos_policy(fields['qos_policy_id'])

    def _attach_qos_policy(self, qos_policy_id):
        binding.QosPolicyPortBinding.delete_objects(self.obj_context,
                                                    port_id=self.id)
        if qos_policy_id:
            port_binding_obj = binding.QosPolicyPortBinding(
                self.obj_context, policy_id=qos_policy_id, port_id=self.id)
            port_binding_obj.create()

        self.qos_policy_id = qos_policy_id
        self.obj_reset_changes(['qos_policy_id'])

    def _attach_security_groups(self, sg_ids):
        # TODO(ihrachys): consider introducing an (internal) object for the
        # binding to decouple database operations a bit more
        obj_db_api.delete_objects(
            self.obj_context,
            sg_models.SecurityGroupPortBinding,
            port_id=self.id,
        )
        if sg_ids:
            for sg_id in sg_ids:
                self._attach_security_group(sg_id)
        self.security_group_ids = sg_ids
        self.obj_reset_changes(['security_group_ids'])

    def _attach_security_group(self, sg_id):
        obj_db_api.create_object(self.obj_context,
                                 sg_models.SecurityGroupPortBinding, {
                                     'port_id': self.id,
                                     'security_group_id': sg_id
                                 })

    # TODO(rossella_s): get rid of it once we switch the db model to using
    # custom types.
    @classmethod
    def modify_fields_to_db(cls, fields):
        result = super(Port, cls).modify_fields_to_db(fields)
        if 'mac_address' in result:
            result['mac_address'] = cls.filter_to_str(result['mac_address'])
        return result

    # TODO(rossella_s): get rid of it once we switch the db model to using
    # custom types.
    @classmethod
    def modify_fields_from_db(cls, db_obj):
        fields = super(Port, cls).modify_fields_from_db(db_obj)
        if 'mac_address' in fields:
            fields['mac_address'] = utils.AuthenticEUI(fields['mac_address'])
        distributed_port_binding = fields.get('distributed_binding')
        if distributed_port_binding:
            fields['distributed_binding'] = fields['distributed_binding'][0]
        else:
            fields['distributed_binding'] = None
        return fields

    def from_db_object(self, db_obj):
        super(Port, self).from_db_object(db_obj)
        # extract security group bindings
        if db_obj.get('security_groups', []):
            self.security_group_ids = {
                sg.security_group_id
                for sg in db_obj.security_groups
            }
        else:
            self.security_group_ids = set()
        self.obj_reset_changes(['security_group_ids'])

        # extract qos policy binding
        if db_obj.get('qos_policy_binding'):
            self.qos_policy_id = (db_obj.qos_policy_binding.policy_id)
        else:
            self.qos_policy_id = None
        self.obj_reset_changes(['qos_policy_id'])

    def obj_make_compatible(self, primitive, target_version):
        _target_version = versionutils.convert_version_to_tuple(target_version)

        if _target_version < (1, 1):
            primitive.pop('data_plane_status', None)

    @classmethod
    def get_ports_by_router(cls, context, router_id, owner, subnet):
        rport_qry = context.session.query(models_v2.Port).join(l3.RouterPort)
        ports = rport_qry.filter(
            l3.RouterPort.router_id == router_id,
            l3.RouterPort.port_type == owner,
            models_v2.Port.network_id == subnet['network_id'])
        return [cls._load_object(context, db_obj) for db_obj in ports.all()]
Beispiel #7
0
class Port(base.NeutronDbObject):
    # Version 1.0: Initial version
    VERSION = '1.0'

    db_model = models_v2.Port

    fields = {
        'id':
        common_types.UUIDField(),
        'project_id':
        obj_fields.StringField(nullable=True),
        'name':
        obj_fields.StringField(nullable=True),
        'network_id':
        common_types.UUIDField(),
        'mac_address':
        common_types.MACAddressField(),
        'admin_state_up':
        obj_fields.BooleanField(),
        'device_id':
        obj_fields.StringField(),
        'device_owner':
        obj_fields.StringField(),
        'status':
        obj_fields.StringField(),
        'allowed_address_pairs':
        obj_fields.ListOfObjectsField('AllowedAddressPair', nullable=True),
        'binding':
        obj_fields.ObjectField('PortBinding', nullable=True),
        'dhcp_options':
        obj_fields.ListOfObjectsField('ExtraDhcpOpt', nullable=True),
        'distributed_binding':
        obj_fields.ObjectField('DistributedPortBinding', nullable=True),
        'dns':
        obj_fields.ObjectField('PortDNS', nullable=True),
        'fixed_ips':
        obj_fields.ListOfObjectsField('IPAllocation', nullable=True),
        # TODO(ihrachys): consider converting to boolean
        'security':
        obj_fields.ObjectField('PortSecurity', nullable=True),
        'security_group_ids':
        common_types.SetOfUUIDsField(
            nullable=True,
            # TODO(ihrachys): how do we safely pass a mutable default?
            default=None,
        ),
        'qos_policy_id':
        common_types.UUIDField(nullable=True, default=None),
        'binding_levels':
        obj_fields.ListOfObjectsField('PortBindingLevel', nullable=True),

        # TODO(ihrachys): consider adding a 'dns_assignment' fully synthetic
        # field in later object iterations
    }

    synthetic_fields = [
        'allowed_address_pairs',
        'binding',
        'binding_levels',
        'dhcp_options',
        'distributed_binding',
        'dns',
        'fixed_ips',
        'qos_policy_id',
        'security',
        'security_group_ids',
    ]

    fields_need_translation = {
        'binding': 'port_binding',
        'dhcp_options': 'dhcp_opts',
        'distributed_binding': 'distributed_port_binding',
        'security': 'port_security',
    }

    def create(self):
        fields = self.obj_get_changes()
        with db_api.autonested_transaction(self.obj_context.session):
            sg_ids = self.security_group_ids
            if sg_ids is None:
                sg_ids = set()
            qos_policy_id = self.qos_policy_id
            super(Port, self).create()
            if 'security_group_ids' in fields:
                self._attach_security_groups(sg_ids)
            if 'qos_policy_id' in fields:
                self._attach_qos_policy(qos_policy_id)

    def update(self):
        fields = self.obj_get_changes()
        with db_api.autonested_transaction(self.obj_context.session):
            super(Port, self).update()
            if 'security_group_ids' in fields:
                self._attach_security_groups(fields['security_group_ids'])
            if 'qos_policy_id' in fields:
                self._attach_qos_policy(fields['qos_policy_id'])

    def _attach_qos_policy(self, qos_policy_id):
        # TODO(ihrachys): introduce an object for the binding to isolate
        # database access in a single place, currently scattered between port
        # and policy objects
        obj_db_api.delete_objects(self.obj_context,
                                  qos_models.QosPortPolicyBinding,
                                  port_id=self.id)
        if qos_policy_id:
            obj_db_api.create_object(self.obj_context,
                                     qos_models.QosPortPolicyBinding, {
                                         'port_id': self.id,
                                         'policy_id': qos_policy_id
                                     })
        self.qos_policy_id = qos_policy_id
        self.obj_reset_changes(['qos_policy_id'])

    def _attach_security_groups(self, sg_ids):
        # TODO(ihrachys): consider introducing an (internal) object for the
        # binding to decouple database operations a bit more
        obj_db_api.delete_objects(
            self.obj_context,
            sg_models.SecurityGroupPortBinding,
            port_id=self.id,
        )
        if sg_ids:
            for sg_id in sg_ids:
                self._attach_security_group(sg_id)
        self.security_group_ids = sg_ids
        self.obj_reset_changes(['security_group_ids'])

    def _attach_security_group(self, sg_id):
        obj_db_api.create_object(self.obj_context,
                                 sg_models.SecurityGroupPortBinding, {
                                     'port_id': self.id,
                                     'security_group_id': sg_id
                                 })

    # TODO(rossella_s): get rid of it once we switch the db model to using
    # custom types.
    @classmethod
    def modify_fields_to_db(cls, fields):
        result = super(Port, cls).modify_fields_to_db(fields)
        if 'mac_address' in result:
            result['mac_address'] = cls.filter_to_str(result['mac_address'])
        return result

    # TODO(rossella_s): get rid of it once we switch the db model to using
    # custom types.
    @classmethod
    def modify_fields_from_db(cls, db_obj):
        fields = super(Port, cls).modify_fields_from_db(db_obj)
        if 'mac_address' in fields:
            fields['mac_address'] = utils.AuthenticEUI(fields['mac_address'])
        distributed_port_binding = fields.get('distributed_binding')
        if distributed_port_binding:
            fields['distributed_binding'] = fields['distributed_binding'][0]
        else:
            fields['distributed_binding'] = None
        return fields

    def from_db_object(self, db_obj):
        super(Port, self).from_db_object(db_obj)
        # extract security group bindings
        if db_obj.get('security_groups', []):
            self.security_group_ids = {
                sg.security_group_id
                for sg in db_obj.security_groups
            }
        else:
            self.security_group_ids = set()
        self.obj_reset_changes(['security_group_ids'])

        # extract qos policy binding
        if db_obj.get('qos_policy_binding'):
            self.qos_policy_id = (db_obj.qos_policy_binding.policy_id)
        else:
            self.qos_policy_id = None
        self.obj_reset_changes(['qos_policy_id'])
Beispiel #8
0
class Port(base.NeutronDbObject):
    # Version 1.0: Initial version
    # Version 1.1: Add data_plane_status field
    # Version 1.2: Added segment_id to binding_levels
    # Version 1.3: distributed_binding -> distributed_bindings
    # Version 1.4: Attribute binding becomes ListOfObjectsField
    VERSION = '1.4'

    db_model = models_v2.Port

    fields = {
        'id':
        common_types.UUIDField(),
        'project_id':
        obj_fields.StringField(nullable=True),
        'name':
        obj_fields.StringField(nullable=True),
        'network_id':
        common_types.UUIDField(),
        'mac_address':
        common_types.MACAddressField(),
        'admin_state_up':
        obj_fields.BooleanField(),
        'device_id':
        obj_fields.StringField(),
        'device_owner':
        obj_fields.StringField(),
        'status':
        obj_fields.StringField(),
        'allowed_address_pairs':
        obj_fields.ListOfObjectsField('AllowedAddressPair', nullable=True),
        'bindings':
        obj_fields.ListOfObjectsField('PortBinding', nullable=True),
        'data_plane_status':
        obj_fields.ObjectField('PortDataPlaneStatus', nullable=True),
        'dhcp_options':
        obj_fields.ListOfObjectsField('ExtraDhcpOpt', nullable=True),
        'distributed_bindings':
        obj_fields.ListOfObjectsField('DistributedPortBinding', nullable=True),
        'dns':
        obj_fields.ObjectField('PortDNS', nullable=True),
        'fixed_ips':
        obj_fields.ListOfObjectsField('IPAllocation', nullable=True),
        # TODO(ihrachys): consider converting to boolean
        'security':
        obj_fields.ObjectField('PortSecurity', nullable=True),
        'security_group_ids':
        common_types.SetOfUUIDsField(
            nullable=True,
            # TODO(ihrachys): how do we safely pass a mutable default?
            default=None,
        ),
        'qos_policy_id':
        common_types.UUIDField(nullable=True, default=None),
        'binding_levels':
        obj_fields.ListOfObjectsField('PortBindingLevel', nullable=True),

        # TODO(ihrachys): consider adding a 'dns_assignment' fully synthetic
        # field in later object iterations
    }

    extra_filter_names = {'security_group_ids'}

    fields_no_update = ['project_id', 'network_id']

    synthetic_fields = [
        'allowed_address_pairs',
        'bindings',
        'binding_levels',
        'data_plane_status',
        'dhcp_options',
        'distributed_bindings',
        'dns',
        'fixed_ips',
        'qos_policy_id',
        'security',
        'security_group_ids',
    ]

    fields_need_translation = {
        'bindings': 'port_bindings',
        'dhcp_options': 'dhcp_opts',
        'distributed_bindings': 'distributed_port_binding',
        'security': 'port_security',
    }

    def create(self):
        fields = self.obj_get_changes()
        with self.db_context_writer(self.obj_context):
            sg_ids = self.security_group_ids
            if sg_ids is None:
                sg_ids = set()
            qos_policy_id = self.qos_policy_id
            super(Port, self).create()
            if 'security_group_ids' in fields:
                self._attach_security_groups(sg_ids)
            if 'qos_policy_id' in fields:
                self._attach_qos_policy(qos_policy_id)

    def update(self):
        fields = self.obj_get_changes()
        with self.db_context_writer(self.obj_context):
            super(Port, self).update()
            if 'security_group_ids' in fields:
                self._attach_security_groups(fields['security_group_ids'])
            if 'qos_policy_id' in fields:
                self._attach_qos_policy(fields['qos_policy_id'])

    def _attach_qos_policy(self, qos_policy_id):
        binding.QosPolicyPortBinding.delete_objects(self.obj_context,
                                                    port_id=self.id)
        if qos_policy_id:
            port_binding_obj = binding.QosPolicyPortBinding(
                self.obj_context, policy_id=qos_policy_id, port_id=self.id)
            port_binding_obj.create()

        self.qos_policy_id = qos_policy_id
        self.obj_reset_changes(['qos_policy_id'])

    def _attach_security_groups(self, sg_ids):
        # TODO(ihrachys): consider introducing an (internal) object for the
        # binding to decouple database operations a bit more
        obj_db_api.delete_objects(SecurityGroupPortBinding,
                                  self.obj_context,
                                  port_id=self.id)
        if sg_ids:
            for sg_id in sg_ids:
                self._attach_security_group(sg_id)
        self.security_group_ids = sg_ids
        self.obj_reset_changes(['security_group_ids'])

    def _attach_security_group(self, sg_id):
        obj_db_api.create_object(SecurityGroupPortBinding, self.obj_context, {
            'port_id': self.id,
            'security_group_id': sg_id
        })

    @classmethod
    def get_objects(cls,
                    context,
                    _pager=None,
                    validate_filters=True,
                    security_group_ids=None,
                    **kwargs):
        if security_group_ids:
            ports_with_sg = cls.get_ports_ids_by_security_groups(
                context, security_group_ids)
            port_ids = kwargs.get("id", [])
            if port_ids:
                kwargs['id'] = list(set(port_ids) & set(ports_with_sg))
            else:
                kwargs['id'] = ports_with_sg
        return super(Port, cls).get_objects(context, _pager, validate_filters,
                                            **kwargs)

    @classmethod
    def get_port_ids_filter_by_segment_id(cls, context, segment_id):
        query = context.session.query(models_v2.Port.id)
        query = query.join(
            ml2_models.PortBindingLevel,
            ml2_models.PortBindingLevel.port_id == models_v2.Port.id)
        query = query.filter(
            ml2_models.PortBindingLevel.segment_id == segment_id)
        return [p.id for p in query]

    @classmethod
    def modify_fields_to_db(cls, fields):
        result = super(Port, cls).modify_fields_to_db(fields)

        # TODO(rossella_s): get rid of it once we switch the db model to using
        # custom types.
        if 'mac_address' in result:
            result['mac_address'] = cls.filter_to_str(result['mac_address'])

        # convert None to []
        if 'distributed_port_binding' in result:
            result['distributed_port_binding'] = (
                result['distributed_port_binding'] or [])
        return result

    @classmethod
    def modify_fields_from_db(cls, db_obj):
        fields = super(Port, cls).modify_fields_from_db(db_obj)

        # TODO(rossella_s): get rid of it once we switch the db model to using
        # custom types.
        if 'mac_address' in fields:
            fields['mac_address'] = utils.AuthenticEUI(fields['mac_address'])

        distributed_port_binding = fields.get('distributed_bindings')
        if distributed_port_binding:
            # TODO(ihrachys) support multiple bindings
            fields['distributed_bindings'] = fields['distributed_bindings'][0]
        else:
            fields['distributed_bindings'] = []
        return fields

    def from_db_object(self, db_obj):
        super(Port, self).from_db_object(db_obj)
        # extract security group bindings
        if db_obj.get('security_groups', []):
            self.security_group_ids = {
                sg.security_group_id
                for sg in db_obj.security_groups
            }
        else:
            self.security_group_ids = set()
        self.obj_reset_changes(['security_group_ids'])

        # extract qos policy binding
        if db_obj.get('qos_policy_binding'):
            self.qos_policy_id = (db_obj.qos_policy_binding.policy_id)
        else:
            self.qos_policy_id = None
        self.obj_reset_changes(['qos_policy_id'])

    def obj_make_compatible(self, primitive, target_version):
        _target_version = versionutils.convert_version_to_tuple(target_version)
        if _target_version < (1, 1):
            primitive.pop('data_plane_status', None)
        if _target_version < (1, 2):
            binding_levels = primitive.get('binding_levels', [])
            for lvl in binding_levels:
                lvl['versioned_object.version'] = '1.0'
                lvl['versioned_object.data'].pop('segment_id', None)
        if _target_version < (1, 3):
            bindings = primitive.pop('distributed_bindings', [])
            primitive['distributed_binding'] = (bindings[0]
                                                if bindings else None)
        if _target_version < (1, 4):
            # In version 1.4 we add support for multiple port bindings.
            # Previous versions only support one port binding. The following
            # lines look for the active port binding, which is the only one
            # needed in previous versions
            if 'bindings' in primitive:
                original_bindings = primitive.pop('bindings')
                primitive['binding'] = None
                for a_binding in original_bindings:
                    if (a_binding['versioned_object.data']['status'] ==
                            constants.ACTIVE):
                        primitive['binding'] = a_binding
                        break

    @classmethod
    def get_ports_by_router(cls, context, router_id, owner, subnet):
        rport_qry = context.session.query(models_v2.Port).join(l3.RouterPort)
        ports = rport_qry.filter(
            l3.RouterPort.router_id == router_id,
            l3.RouterPort.port_type == owner,
            models_v2.Port.network_id == subnet['network_id'])
        return [cls._load_object(context, db_obj) for db_obj in ports.all()]

    @classmethod
    def get_ports_ids_by_security_groups(cls,
                                         context,
                                         security_group_ids,
                                         excluded_device_owners=None):
        query = context.session.query(sg_models.SecurityGroupPortBinding)
        query = query.filter(
            sg_models.SecurityGroupPortBinding.security_group_id.in_(
                security_group_ids))
        if excluded_device_owners:
            query = query.join(models_v2.Port)
            query = query.filter(
                ~models_v2.Port.device_owner.in_(excluded_device_owners))
        return [port_binding['port_id'] for port_binding in query.all()]
Beispiel #9
0
class Port(base.NeutronDbObject):
    # Version 1.0: Initial version
    # Version 1.1: Add data_plane_status field
    # Version 1.2: Added segment_id to binding_levels
    # Version 1.3: distributed_binding -> distributed_bindings
    # Version 1.4: Attribute binding becomes ListOfObjectsField
    # Version 1.5: Added qos_network_policy_id field
    VERSION = '1.5'

    db_model = models_v2.Port

    fields = {
        'id':
        common_types.UUIDField(),
        'project_id':
        obj_fields.StringField(nullable=True),
        'name':
        obj_fields.StringField(nullable=True),
        'network_id':
        common_types.UUIDField(),
        'mac_address':
        common_types.MACAddressField(),
        'admin_state_up':
        obj_fields.BooleanField(),
        'device_id':
        obj_fields.StringField(),
        'device_owner':
        obj_fields.StringField(),
        'status':
        obj_fields.StringField(),
        'allowed_address_pairs':
        obj_fields.ListOfObjectsField('AllowedAddressPair', nullable=True),
        'bindings':
        obj_fields.ListOfObjectsField('PortBinding', nullable=True),
        'data_plane_status':
        obj_fields.ObjectField('PortDataPlaneStatus', nullable=True),
        'dhcp_options':
        obj_fields.ListOfObjectsField('ExtraDhcpOpt', nullable=True),
        'distributed_bindings':
        obj_fields.ListOfObjectsField('DistributedPortBinding', nullable=True),
        'dns':
        obj_fields.ObjectField('PortDNS', nullable=True),
        'fixed_ips':
        obj_fields.ListOfObjectsField('IPAllocation', nullable=True),
        # TODO(ihrachys): consider converting to boolean
        'security':
        obj_fields.ObjectField('PortSecurity', nullable=True),
        'security_group_ids':
        common_types.SetOfUUIDsField(
            nullable=True,
            # TODO(ihrachys): how do we safely pass a mutable default?
            default=None,
        ),
        'qos_policy_id':
        common_types.UUIDField(nullable=True, default=None),
        'qos_network_policy_id':
        common_types.UUIDField(nullable=True, default=None),
        'binding_levels':
        obj_fields.ListOfObjectsField('PortBindingLevel', nullable=True),

        # TODO(ihrachys): consider adding a 'dns_assignment' fully synthetic
        # field in later object iterations
    }

    extra_filter_names = {'security_group_ids'}

    fields_no_update = ['project_id', 'network_id']

    synthetic_fields = [
        'allowed_address_pairs',
        'bindings',
        'binding_levels',
        'data_plane_status',
        'dhcp_options',
        'distributed_bindings',
        'dns',
        'fixed_ips',
        'qos_policy_id',
        'qos_network_policy_id',
        'security',
        'security_group_ids',
    ]

    fields_need_translation = {
        'bindings': 'port_bindings',
        'dhcp_options': 'dhcp_opts',
        'distributed_bindings': 'distributed_port_binding',
        'security': 'port_security',
    }

    def create(self):
        fields = self.obj_get_changes()
        with self.db_context_writer(self.obj_context):
            sg_ids = self.security_group_ids
            if sg_ids is None:
                sg_ids = set()
            qos_policy_id = self.qos_policy_id
            super(Port, self).create()
            if 'security_group_ids' in fields:
                self._attach_security_groups(sg_ids)
            if 'qos_policy_id' in fields:
                self._attach_qos_policy(qos_policy_id)

    def update(self):
        fields = self.obj_get_changes()
        with self.db_context_writer(self.obj_context):
            super(Port, self).update()
            if 'security_group_ids' in fields:
                self._attach_security_groups(fields['security_group_ids'])
            if 'qos_policy_id' in fields:
                self._attach_qos_policy(fields['qos_policy_id'])

    def _attach_qos_policy(self, qos_policy_id):
        binding.QosPolicyPortBinding.delete_objects(self.obj_context,
                                                    port_id=self.id)
        if qos_policy_id:
            port_binding_obj = binding.QosPolicyPortBinding(
                self.obj_context, policy_id=qos_policy_id, port_id=self.id)
            port_binding_obj.create()

        self.qos_policy_id = qos_policy_id
        self.obj_reset_changes(['qos_policy_id'])

    def _attach_security_groups(self, sg_ids):
        # TODO(ihrachys): consider introducing an (internal) object for the
        # binding to decouple database operations a bit more
        obj_db_api.delete_objects(SecurityGroupPortBinding,
                                  self.obj_context,
                                  port_id=self.id)
        if sg_ids:
            for sg_id in sg_ids:
                self._attach_security_group(sg_id)
        self.security_group_ids = sg_ids
        self.obj_reset_changes(['security_group_ids'])

    def _attach_security_group(self, sg_id):
        obj_db_api.create_object(SecurityGroupPortBinding, self.obj_context, {
            'port_id': self.id,
            'security_group_id': sg_id
        })

    @classmethod
    def get_objects(cls,
                    context,
                    _pager=None,
                    validate_filters=True,
                    security_group_ids=None,
                    **kwargs):
        if security_group_ids:
            ports_with_sg = cls.get_ports_ids_by_security_groups(
                context, security_group_ids)
            port_ids = kwargs.get("id", [])
            if port_ids:
                kwargs['id'] = list(set(port_ids) & set(ports_with_sg))
            else:
                kwargs['id'] = ports_with_sg
        return super(Port, cls).get_objects(context, _pager, validate_filters,
                                            **kwargs)

    @classmethod
    def get_port_ids_filter_by_segment_id(cls, context, segment_id):
        query = context.session.query(models_v2.Port.id)
        query = query.join(
            ml2_models.PortBindingLevel,
            ml2_models.PortBindingLevel.port_id == models_v2.Port.id)
        query = query.filter(
            ml2_models.PortBindingLevel.segment_id == segment_id)
        return [p.id for p in query]

    @classmethod
    def modify_fields_to_db(cls, fields):
        result = super(Port, cls).modify_fields_to_db(fields)

        # TODO(rossella_s): get rid of it once we switch the db model to using
        # custom types.
        if 'mac_address' in result:
            result['mac_address'] = cls.filter_to_str(result['mac_address'])

        # convert None to []
        if 'distributed_port_binding' in result:
            result['distributed_port_binding'] = (
                result['distributed_port_binding'] or [])
        return result

    @classmethod
    def modify_fields_from_db(cls, db_obj):
        fields = super(Port, cls).modify_fields_from_db(db_obj)

        # TODO(rossella_s): get rid of it once we switch the db model to using
        # custom types.
        if 'mac_address' in fields:
            fields['mac_address'] = net_utils.AuthenticEUI(
                fields['mac_address'])

        distributed_port_binding = fields.get('distributed_bindings')
        if distributed_port_binding:
            # TODO(ihrachys) support multiple bindings
            fields['distributed_bindings'] = fields['distributed_bindings'][0]
        else:
            fields['distributed_bindings'] = []
        return fields

    def from_db_object(self, db_obj):
        super(Port, self).from_db_object(db_obj)
        # extract security group bindings
        if db_obj.get('security_groups', []):
            self.security_group_ids = {
                sg.security_group_id
                for sg in db_obj.security_groups
            }
        else:
            self.security_group_ids = set()
        fields_to_change = ['security_group_ids']

        # extract qos policy binding
        if db_obj.get('qos_policy_binding'):
            self.qos_policy_id = db_obj.qos_policy_binding.policy_id
            fields_to_change.append('qos_policy_id')
        if db_obj.get('qos_network_policy_binding'):
            self.qos_network_policy_id = (
                db_obj.qos_network_policy_binding.policy_id)
            fields_to_change.append('qos_network_policy_binding')

        self.obj_reset_changes(fields_to_change)

    def obj_make_compatible(self, primitive, target_version):
        _target_version = versionutils.convert_version_to_tuple(target_version)
        if _target_version < (1, 1):
            primitive.pop('data_plane_status', None)
        if _target_version < (1, 2):
            binding_levels = primitive.get('binding_levels', [])
            for lvl in binding_levels:
                lvl['versioned_object.version'] = '1.0'
                lvl['versioned_object.data'].pop('segment_id', None)
        if _target_version < (1, 3):
            bindings = primitive.pop('distributed_bindings', [])
            primitive['distributed_binding'] = (bindings[0]
                                                if bindings else None)
        if _target_version < (1, 4):
            # In version 1.4 we add support for multiple port bindings.
            # Previous versions only support one port binding. The following
            # lines look for the active port binding, which is the only one
            # needed in previous versions
            if 'bindings' in primitive:
                original_bindings = primitive.pop('bindings')
                primitive['binding'] = None
                for a_binding in original_bindings:
                    if (a_binding['versioned_object.data']['status'] ==
                            constants.ACTIVE):
                        primitive['binding'] = a_binding
                        break
        if _target_version < (1, 5):
            primitive.pop('qos_network_policy_id', None)

    @classmethod
    def get_ports_by_router_and_network(cls, context, router_id, owner,
                                        network_id):
        """Returns port objects filtering by router ID, owner and network ID"""
        rports_filter = (models_v2.Port.network_id == network_id, )
        router_filter = (models_v2.Port.network_id == network_id, )
        return cls._get_ports_by_router(context, router_id, owner,
                                        rports_filter, router_filter)

    @classmethod
    def get_ports_by_router_and_port(cls, context, router_id, owner, port_id):
        """Returns port objects filtering by router ID, owner and port ID"""
        rports_filter = (l3.RouterPort.port_id == port_id, )
        router_filter = (models_v2.Port.id == port_id, )
        return cls._get_ports_by_router(context, router_id, owner,
                                        rports_filter, router_filter)

    @classmethod
    def _get_ports_by_router(cls, context, router_id, owner, rports_filter,
                             router_filter):
        """Returns port objects filtering by router id and owner

        The method will receive extra filters depending of the caller (filter
        by network or filter by port).

        The ports are retrieved using:
        - The RouterPort registers. Each time a port is assigned to a router,
          a new RouterPort register is added to the DB.
        - The port owner and device_id information.

        Both searches should return the same result. If not, a warning message
        is logged and the port list to be returned is completed with the
        missing ones.
        """
        rports_filter += (l3.RouterPort.router_id == router_id,
                          l3.RouterPort.port_type == owner)
        router_filter += (models_v2.Port.device_id == router_id,
                          models_v2.Port.device_owner == owner)

        ports = context.session.query(models_v2.Port).join(
            l3.RouterPort).filter(*rports_filter)
        ports_rports = [
            cls._load_object(context, db_obj) for db_obj in ports.all()
        ]

        ports = context.session.query(models_v2.Port).filter(*router_filter)
        ports_router = [
            cls._load_object(context, db_obj) for db_obj in ports.all()
        ]

        ports_rports_ids = {p.id for p in ports_rports}
        ports_router_ids = {p.id for p in ports_router}
        missing_port_ids = ports_router_ids - ports_rports_ids
        if missing_port_ids:
            LOG.warning(
                'The following ports, assigned to router '
                '%(router_id)s, do not have a "routerport" register: '
                '%(port_ids)s', {
                    'router_id': router_id,
                    'port_ids': missing_port_ids
                })
            port_objs = [p for p in ports_router if p.id in missing_port_ids]
            ports_rports += port_objs

        return ports_rports

    @classmethod
    def get_ports_ids_by_security_groups(cls,
                                         context,
                                         security_group_ids,
                                         excluded_device_owners=None):
        query = context.session.query(sg_models.SecurityGroupPortBinding)
        query = query.filter(
            sg_models.SecurityGroupPortBinding.security_group_id.in_(
                security_group_ids))
        if excluded_device_owners:
            query = query.join(models_v2.Port)
            query = query.filter(
                ~models_v2.Port.device_owner.in_(excluded_device_owners))
        return [port_binding['port_id'] for port_binding in query.all()]

    @classmethod
    def get_ports_by_binding_type_and_host(cls, context, binding_type, host):
        query = context.session.query(models_v2.Port).join(
            ml2_models.PortBinding)
        query = query.filter(ml2_models.PortBinding.vif_type == binding_type,
                             ml2_models.PortBinding.host == host)
        return [cls._load_object(context, db_obj) for db_obj in query.all()]

    @classmethod
    def get_ports_by_vnic_type_and_host(cls, context, vnic_type, host):
        query = context.session.query(models_v2.Port).join(
            ml2_models.PortBinding)
        query = query.filter(ml2_models.PortBinding.vnic_type == vnic_type,
                             ml2_models.PortBinding.host == host)
        return [cls._load_object(context, db_obj) for db_obj in query.all()]

    @classmethod
    def check_network_ports_by_binding_types(cls,
                                             context,
                                             network_id,
                                             binding_types,
                                             negative_search=False):
        """This method is to check whether networks have ports with given
        binding_types.

        :param context:
        :param network_id: ID of network to check
        :param binding_types: list of binding types to look for
        :param negative_search: if set to true, ports with with binding_type
                                other than "binding_types" will be counted
        :return: True if any port is found, False otherwise
        """
        query = context.session.query(models_v2.Port).join(
            ml2_models.PortBinding)
        query = query.filter(models_v2.Port.network_id == network_id)
        if negative_search:
            query = query.filter(
                ml2_models.PortBinding.vif_type.notin_(binding_types))
        else:
            query = query.filter(
                ml2_models.PortBinding.vif_type.in_(binding_types))
        return bool(query.count())