Esempio n. 1
0
class PortCRUDPayload(notification.NotificationPayloadBase):
    # Version 1.0: Initial version
    VERSION = '1.0'

    SCHEMA = {
        'address': ('port', 'address'),
        'extra': ('port', 'extra'),
        'local_link_connection': ('port', 'local_link_connection'),
        'pxe_enabled': ('port', 'pxe_enabled'),
        'created_at': ('port', 'created_at'),
        'updated_at': ('port', 'updated_at'),
        'uuid': ('port', 'uuid')
    }

    fields = {
        'address': object_fields.MACAddressField(nullable=True),
        'extra': object_fields.FlexibleDictField(nullable=True),
        'local_link_connection':
        object_fields.FlexibleDictField(nullable=True),
        'pxe_enabled': object_fields.BooleanField(nullable=True),
        'node_uuid': object_fields.UUIDField(),
        'created_at': object_fields.DateTimeField(nullable=True),
        'updated_at': object_fields.DateTimeField(nullable=True),
        'uuid': object_fields.UUIDField()
        # TODO(yuriyz): add "portgroup_uuid" field with portgroup notifications
    }

    def __init__(self, port, node_uuid):
        super(PortCRUDPayload, self).__init__(node_uuid=node_uuid)
        self.populate_schema(port=port)
Esempio n. 2
0
class NodeCRUDPayload(NodePayload):
    """Payload schema for when ironic creates, updates or deletes a node."""
    # Version 1.0: Initial version
    # Version 1.1: Parent NodePayload version 1.3
    # Version 1.2: Parent NodePayload version 1.4
    # Version 1.3: Parent NodePayload version 1.5
    # Version 1.4: Parent NodePayload version 1.6
    # Version 1.5: Parent NodePayload version 1.7
    # Version 1.6: Parent NodePayload version 1.8
    # Version 1.7: Parent NodePayload version 1.9
    # Version 1.8: Parent NodePayload version 1.10
    # Version 1.9: Parent NodePayload version 1.11
    VERSION = '1.9'

    SCHEMA = dict(
        NodePayload.SCHEMA, **{
            'instance_info': ('node', 'instance_info'),
            'driver_info': ('node', 'driver_info')
        })

    fields = {
        'chassis_uuid': object_fields.UUIDField(nullable=True),
        'instance_info': object_fields.FlexibleDictField(nullable=True),
        'driver_info': object_fields.FlexibleDictField(nullable=True)
    }

    def __init__(self, node, chassis_uuid):
        super(NodeCRUDPayload, self).__init__(node, chassis_uuid=chassis_uuid)
Esempio n. 3
0
class VolumeTargetCRUDPayload(notification.NotificationPayloadBase):
    # Version 1.0: Initial Version
    VERSION = '1.0'

    SCHEMA = {
        'boot_index': ('target', 'boot_index'),
        'extra': ('target', 'extra'),
        'properties': ('target', 'properties'),
        'volume_id': ('target', 'volume_id'),
        'volume_type': ('target', 'volume_type'),
        'created_at': ('target', 'created_at'),
        'updated_at': ('target', 'updated_at'),
        'uuid': ('target', 'uuid'),
    }

    fields = {
        'boot_index': object_fields.IntegerField(nullable=True),
        'extra': object_fields.FlexibleDictField(nullable=True),
        'node_uuid': object_fields.UUIDField(),
        'properties': object_fields.FlexibleDictField(nullable=True),
        'volume_id': object_fields.StringField(nullable=True),
        'volume_type': object_fields.StringField(nullable=True),
        'created_at': object_fields.DateTimeField(nullable=True),
        'updated_at': object_fields.DateTimeField(nullable=True),
        'uuid': object_fields.UUIDField(),
    }

    def __init__(self, target, node_uuid):
        super(VolumeTargetCRUDPayload, self).__init__(node_uuid=node_uuid)
        self.populate_schema(target=target)
Esempio n. 4
0
class PortgroupCRUDPayload(notification.NotificationPayloadBase):
    # Version 1.0: Initial version
    VERSION = '1.0'

    SCHEMA = {
        'address': ('portgroup', 'address'),
        'extra': ('portgroup', 'extra'),
        'mode': ('portgroup', 'mode'),
        'name': ('portgroup', 'name'),
        'properties': ('portgroup', 'properties'),
        'standalone_ports_supported': ('portgroup',
                                       'standalone_ports_supported'),
        'created_at': ('portgroup', 'created_at'),
        'updated_at': ('portgroup', 'updated_at'),
        'uuid': ('portgroup', 'uuid')
    }

    fields = {
        'address': object_fields.MACAddressField(nullable=True),
        'extra': object_fields.FlexibleDictField(nullable=True),
        'mode': object_fields.StringField(nullable=True),
        'name': object_fields.StringField(nullable=True),
        'node_uuid': object_fields.UUIDField(),
        'properties': object_fields.FlexibleDictField(nullable=True),
        'standalone_ports_supported': object_fields.BooleanField(
            nullable=True),
        'created_at': object_fields.DateTimeField(nullable=True),
        'updated_at': object_fields.DateTimeField(nullable=True),
        'uuid': object_fields.UUIDField()
    }

    def __init__(self, portgroup, node_uuid):
        super(PortgroupCRUDPayload, self).__init__(node_uuid=node_uuid)
        self.populate_schema(portgroup=portgroup)
Esempio n. 5
0
 class TestObjectMaskSecrets(base.IronicObject):
     VERSION = '1.0'
     fields = {
         'instance_info': fields.FlexibleDictField(nullable=True),
         'driver_info': fields.FlexibleDictField(nullable=True),
         'driver_internal_info': fields.FlexibleDictField(nullable=True),
         'some_dict': fields.FlexibleDictField(nullable=True),
     }
Esempio n. 6
0
class AllocationCRUDPayload(notification.NotificationPayloadBase):
    # Version 1.0: Initial version
    VERSION = '1.0'

    SCHEMA = {
        'candidate_nodes': ('allocation', 'candidate_nodes'),
        'created_at': ('allocation', 'created_at'),
        'extra': ('allocation', 'extra'),
        'last_error': ('allocation', 'last_error'),
        'name': ('allocation', 'name'),
        'resource_class': ('allocation', 'resource_class'),
        'state': ('allocation', 'state'),
        'traits': ('allocation', 'traits'),
        'updated_at': ('allocation', 'updated_at'),
        'uuid': ('allocation', 'uuid')
    }

    fields = {
        'uuid': object_fields.UUIDField(nullable=True),
        'name': object_fields.StringField(nullable=True),
        'node_uuid': object_fields.StringField(nullable=True),
        'state': object_fields.StringField(nullable=True),
        'last_error': object_fields.StringField(nullable=True),
        'resource_class': object_fields.StringField(nullable=True),
        'traits': object_fields.ListOfStringsField(nullable=True),
        'candidate_nodes': object_fields.ListOfStringsField(nullable=True),
        'extra': object_fields.FlexibleDictField(nullable=True),
        'created_at': object_fields.DateTimeField(nullable=True),
        'updated_at': object_fields.DateTimeField(nullable=True),
    }

    def __init__(self, allocation, node_uuid=None):
        super(AllocationCRUDPayload, self).__init__(node_uuid=node_uuid)
        self.populate_schema(allocation=allocation)
Esempio n. 7
0
class VolumeConnectorCRUDPayload(notification.NotificationPayloadBase):
    """Payload schema for CRUD of a volume connector."""
    # Version 1.0: Initial version
    VERSION = '1.0'

    SCHEMA = {
        'extra': ('connector', 'extra'),
        'type': ('connector', 'type'),
        'connector_id': ('connector', 'connector_id'),
        'created_at': ('connector', 'created_at'),
        'updated_at': ('connector', 'updated_at'),
        'uuid': ('connector', 'uuid'),
    }

    fields = {
        'extra': object_fields.FlexibleDictField(nullable=True),
        'type': object_fields.StringField(nullable=True),
        'connector_id': object_fields.StringField(nullable=True),
        'node_uuid': object_fields.UUIDField(),
        'created_at': object_fields.DateTimeField(nullable=True),
        'updated_at': object_fields.DateTimeField(nullable=True),
        'uuid': object_fields.UUIDField(),
    }

    def __init__(self, connector, node_uuid):
        super(VolumeConnectorCRUDPayload, self).__init__(node_uuid=node_uuid)
        self.populate_schema(connector=connector)
Esempio n. 8
0
class NodeSetProvisionStatePayload(NodePayload):
    """Payload schema for when ironic changes a node provision state."""
    # Version 1.0: Initial version
    # Version 1.1: Parent NodePayload version 1.1
    # Version 1.2: Parent NodePayload version 1.2
    # Version 1.3: Parent NodePayload version 1.3
    # Version 1.4: Parent NodePayload version 1.4
    # Version 1.6: Parent NodePayload version 1.6
    # Version 1.7: Parent NodePayload version 1.7
    # Version 1.8: Parent NodePayload version 1.8
    # Version 1.9: Parent NodePayload version 1.9
    # Version 1.10: Parent NodePayload version 1.10
    # Version 1.11: Parent NodePayload version 1.11
    VERSION = '1.11'

    SCHEMA = dict(NodePayload.SCHEMA,
                  **{'instance_info': ('node', 'instance_info')})

    fields = {
        'instance_info': object_fields.FlexibleDictField(nullable=True),
        'event': object_fields.StringField(nullable=True),
        'previous_provision_state': object_fields.StringField(nullable=True),
        'previous_target_provision_state':
        object_fields.StringField(nullable=True)
    }

    def __init__(self, node, prev_state, prev_target, event):
        super(NodeSetProvisionStatePayload,
              self).__init__(node,
                             event=event,
                             previous_provision_state=prev_state,
                             previous_target_provision_state=prev_target)
Esempio n. 9
0
class DeployTemplateCRUDPayload(notification.NotificationPayloadBase):
    # Version 1.0: Initial version
    VERSION = '1.0'

    SCHEMA = {
        'created_at': ('deploy_template', 'created_at'),
        'extra': ('deploy_template', 'extra'),
        'name': ('deploy_template', 'name'),
        'steps': ('deploy_template', 'steps'),
        'updated_at': ('deploy_template', 'updated_at'),
        'uuid': ('deploy_template', 'uuid')
    }

    fields = {
        'created_at': object_fields.DateTimeField(nullable=True),
        'extra': object_fields.FlexibleDictField(nullable=True),
        'name': object_fields.StringField(nullable=False),
        'steps': object_fields.ListOfFlexibleDictsField(nullable=False),
        'updated_at': object_fields.DateTimeField(nullable=True),
        'uuid': object_fields.UUIDField()
    }

    def __init__(self, deploy_template, **kwargs):
        super(DeployTemplateCRUDPayload, self).__init__(**kwargs)
        self.populate_schema(deploy_template=deploy_template)
Esempio n. 10
0
    def test_coerce_nullable_translation(self):
        # non-nullable
        self.assertRaises(ValueError, self.field.coerce, 'obj', 'attr', None)

        # nullable
        self.field = fields.FlexibleDictField(nullable=True)
        self.assertEqual({}, self.field.coerce('obj', 'attr', None))
Esempio n. 11
0
    class TestNotificationPayloadMaskSecrets(
            notification.NotificationPayloadBase):
        VERSION = '1.0'

        SCHEMA = {
            'instance_info': ('test_obj', 'instance_info'),
            'driver_info': ('test_obj', 'driver_info'),
            'driver_internal_info': ('test_obj', 'driver_internal_info'),
            'some_dict': ('test_obj', 'some_dict'),
        }

        fields = {
            'instance_info': fields.FlexibleDictField(nullable=True),
            'driver_info': fields.FlexibleDictField(nullable=True),
            'driver_internal_info': fields.FlexibleDictField(nullable=True),
            'some_dict': fields.FlexibleDictField(nullable=True),
        }
Esempio n. 12
0
class PortCRUDPayload(notification.NotificationPayloadBase):
    # Version 1.0: Initial version
    # Version 1.1: Add "portgroup_uuid" field
    # Version 1.2: Add "physical_network" field
    # Version 1.3: Add "is_smartnic" field
    # Version 1.4: Add "name" field
    VERSION = '1.4'

    SCHEMA = {
        'address': ('port', 'address'),
        'extra': ('port', 'extra'),
        'local_link_connection': ('port', 'local_link_connection'),
        'pxe_enabled': ('port', 'pxe_enabled'),
        'physical_network': ('port', 'physical_network'),
        'created_at': ('port', 'created_at'),
        'updated_at': ('port', 'updated_at'),
        'uuid': ('port', 'uuid'),
        'is_smartnic': ('port', 'is_smartnic'),
        'name': ('port', 'name'),
    }

    fields = {
        'address': object_fields.MACAddressField(nullable=True),
        'extra': object_fields.FlexibleDictField(nullable=True),
        'local_link_connection':
        object_fields.FlexibleDictField(nullable=True),
        'pxe_enabled': object_fields.BooleanField(nullable=True),
        'node_uuid': object_fields.UUIDField(),
        'portgroup_uuid': object_fields.UUIDField(nullable=True),
        'physical_network': object_fields.StringField(nullable=True),
        'created_at': object_fields.DateTimeField(nullable=True),
        'updated_at': object_fields.DateTimeField(nullable=True),
        'uuid': object_fields.UUIDField(),
        'is_smartnic': object_fields.BooleanField(nullable=True,
                                                  default=False),
        'name': object_fields.StringField(nullable=True),
    }

    def __init__(self, port, node_uuid, portgroup_uuid):
        super(PortCRUDPayload, self).__init__(node_uuid=node_uuid,
                                              portgroup_uuid=portgroup_uuid)
        self.populate_schema(port=port)
Esempio n. 13
0
class ChassisCRUDPayload(notification.NotificationPayloadBase):
    # Version 1.0: Initial version
    VERSION = '1.0'

    SCHEMA = {
        'description': ('chassis', 'description'),
        'extra': ('chassis', 'extra'),
        'created_at': ('chassis', 'created_at'),
        'updated_at': ('chassis', 'updated_at'),
        'uuid': ('chassis', 'uuid')
    }

    fields = {
        'description': object_fields.StringField(nullable=True),
        'extra': object_fields.FlexibleDictField(nullable=True),
        'created_at': object_fields.DateTimeField(nullable=True),
        'updated_at': object_fields.DateTimeField(nullable=True),
        'uuid': object_fields.UUIDField()
    }

    def __init__(self, chassis, **kwargs):
        super(ChassisCRUDPayload, self).__init__(**kwargs)
        self.populate_schema(chassis=chassis)
Esempio n. 14
0
class Port(base.IronicObject, object_base.VersionedObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: Add get() and get_by_id() and get_by_address() and
    #              make get_by_uuid() only work with a uuid
    # Version 1.2: Add create() and destroy()
    # Version 1.3: Add list()
    # Version 1.4: Add list_by_node_id()
    # Version 1.5: Add list_by_portgroup_id() and new fields
    #              local_link_connection, portgroup_id and pxe_enabled
    # Version 1.6: Add internal_info field
    # Version 1.7: Add physical_network field
    # Version 1.8: Migrate/copy extra['vif_port_id'] to
    #              internal_info['tenant_vif_port_id'] (not an explicit db
    #              change)
    # Version 1.9: Add support for Smart NIC port
    VERSION = '1.9'

    dbapi = dbapi.get_instance()

    fields = {
        'id': object_fields.IntegerField(),
        'uuid': object_fields.UUIDField(nullable=True),
        'node_id': object_fields.IntegerField(nullable=True),
        'address': object_fields.MACAddressField(nullable=True),
        'extra': object_fields.FlexibleDictField(nullable=True),
        'local_link_connection':
        object_fields.FlexibleDictField(nullable=True),
        'portgroup_id': object_fields.IntegerField(nullable=True),
        'pxe_enabled': object_fields.BooleanField(),
        'internal_info': object_fields.FlexibleDictField(nullable=True),
        'physical_network': object_fields.StringField(nullable=True),
        'is_smartnic': object_fields.BooleanField(nullable=True,
                                                  default=False),
    }

    def _convert_to_version(self,
                            target_version,
                            remove_unavailable_fields=True):
        """Convert to the target version.

        Convert the object to the target version. The target version may be
        the same, older, or newer than the version of the object. This is
        used for DB interactions as well as for serialization/deserialization.

        Version 1.7: physical_network field was added. Its default value is
            None. For versions prior to this, it should be set to None (or
            removed).

        Version 1.8: if extra['vif_port_id'] is specified (non-null) and
            internal_info['tenant_vif_port_id'] is not specified, copy the
            .extra value to internal_info. There is nothing to do here when
            downgrading to an older version.

        Version 1.9: remove is_smartnic field for unsupported versions if
            remove_unavailable_fields is True.

        :param target_version: the desired version of the object
        :param remove_unavailable_fields: True to remove fields that are
            unavailable in the target version; set this to True when
            (de)serializing. False to set the unavailable fields to appropriate
            values; set this to False for DB interactions.
        """
        target_version = versionutils.convert_version_to_tuple(target_version)
        if target_version >= (1, 8):
            if self.obj_attr_is_set('extra'):
                vif = self.extra.get('vif_port_id')
                if vif:
                    internal_info = (self.internal_info
                                     if self.obj_attr_is_set('internal_info')
                                     else {})
                    if 'tenant_vif_port_id' not in internal_info:
                        internal_info['tenant_vif_port_id'] = vif
                        self.internal_info = internal_info

        # Convert the physical_network field.
        physnet_is_set = self.obj_attr_is_set('physical_network')
        if target_version >= (1, 7):
            # Target version supports physical_network. Set it to its default
            # value if it is not set.
            if not physnet_is_set:
                self.physical_network = None
        elif physnet_is_set:
            # Target version does not support physical_network, and it is set.
            if remove_unavailable_fields:
                # (De)serialising: remove unavailable fields.
                delattr(self, 'physical_network')
            elif self.physical_network is not None:
                # DB: set unavailable fields to their default.
                self.physical_network = None

        # Convert is_smartnic field.
        is_smartnic_set = self.obj_attr_is_set('is_smartnic')
        if target_version >= (1, 9):
            # Target version supports is_smartnic. Set it to its default
            # value if it is not set.
            if not is_smartnic_set:
                self.is_smartnic = False

        # handle is_smartnic field in older version
        elif is_smartnic_set:
            # Target version does not support is_smartnic, and it is set.
            if remove_unavailable_fields:
                # (De)serialising: remove unavailable fields.
                delattr(self, 'is_smartnic')
            elif self.is_smartnic is not False:
                # DB: set unavailable fields to their default.
                self.is_smartnic = False

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get(cls, context, port_id):
        """Find a port.

        Find a port based on its id or uuid or MAC address and return a Port
        object.

        :param context: Security context
        :param port_id: the id *or* uuid *or* MAC address of a port.
        :returns: a :class:`Port` object.
        :raises: InvalidIdentity

        """
        if strutils.is_int_like(port_id):
            return cls.get_by_id(context, port_id)
        elif uuidutils.is_uuid_like(port_id):
            return cls.get_by_uuid(context, port_id)
        elif netutils.is_valid_mac(port_id):
            return cls.get_by_address(context, port_id)
        else:
            raise exception.InvalidIdentity(identity=port_id)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_id(cls, context, port_id):
        """Find a port based on its integer ID and return a Port object.

        :param cls: the :class:`Port`
        :param context: Security context
        :param port_id: the ID of a port.
        :returns: a :class:`Port` object.
        :raises: PortNotFound

        """
        db_port = cls.dbapi.get_port_by_id(port_id)
        port = cls._from_db_object(context, cls(), db_port)
        return port

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_uuid(cls, context, uuid):
        """Find a port based on UUID and return a :class:`Port` object.

        :param cls: the :class:`Port`
        :param context: Security context
        :param uuid: the UUID of a port.
        :returns: a :class:`Port` object.
        :raises: PortNotFound

        """
        db_port = cls.dbapi.get_port_by_uuid(uuid)
        port = cls._from_db_object(context, cls(), db_port)
        return port

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_address(cls, context, address):
        """Find a port based on address and return a :class:`Port` object.

        :param cls: the :class:`Port`
        :param context: Security context
        :param address: the address of a port.
        :returns: a :class:`Port` object.
        :raises: PortNotFound

        """
        db_port = cls.dbapi.get_port_by_address(address)
        port = cls._from_db_object(context, cls(), db_port)
        return port

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def list(cls,
             context,
             limit=None,
             marker=None,
             sort_key=None,
             sort_dir=None):
        """Return a list of Port objects.

        :param context: Security context.
        :param limit: maximum number of resources to return in a single result.
        :param marker: pagination marker for large data sets.
        :param sort_key: column to sort results by.
        :param sort_dir: direction to sort. "asc" or "desc".
        :returns: a list of :class:`Port` object.
        :raises: InvalidParameterValue

        """
        db_ports = cls.dbapi.get_port_list(limit=limit,
                                           marker=marker,
                                           sort_key=sort_key,
                                           sort_dir=sort_dir)
        return cls._from_db_object_list(context, db_ports)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def list_by_node_id(cls,
                        context,
                        node_id,
                        limit=None,
                        marker=None,
                        sort_key=None,
                        sort_dir=None):
        """Return a list of Port objects associated with a given node ID.

        :param context: Security context.
        :param node_id: the ID of the node.
        :param limit: maximum number of resources to return in a single result.
        :param marker: pagination marker for large data sets.
        :param sort_key: column to sort results by.
        :param sort_dir: direction to sort. "asc" or "desc".
        :returns: a list of :class:`Port` object.

        """
        db_ports = cls.dbapi.get_ports_by_node_id(node_id,
                                                  limit=limit,
                                                  marker=marker,
                                                  sort_key=sort_key,
                                                  sort_dir=sort_dir)
        return cls._from_db_object_list(context, db_ports)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def list_by_portgroup_id(cls,
                             context,
                             portgroup_id,
                             limit=None,
                             marker=None,
                             sort_key=None,
                             sort_dir=None):
        """Return a list of Port objects associated with a given portgroup ID.

        :param context: Security context.
        :param portgroup_id: the ID of the portgroup.
        :param limit: maximum number of resources to return in a single result.
        :param marker: pagination marker for large data sets.
        :param sort_key: column to sort results by.
        :param sort_dir: direction to sort. "asc" or "desc".
        :returns: a list of :class:`Port` object.

        """
        db_ports = cls.dbapi.get_ports_by_portgroup_id(portgroup_id,
                                                       limit=limit,
                                                       marker=marker,
                                                       sort_key=sort_key,
                                                       sort_dir=sort_dir)
        return cls._from_db_object_list(context, db_ports)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def create(self, context=None):
        """Create a Port record in the DB.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Port(context)
        :raises: MACAlreadyExists if 'address' column is not unique
        :raises: PortAlreadyExists if 'uuid' column is not unique

        """
        values = self.do_version_changes_for_db()
        db_port = self.dbapi.create_port(values)
        self._from_db_object(self._context, self, db_port)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def destroy(self, context=None):
        """Delete the Port from the DB.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Port(context)
        :raises: PortNotFound

        """
        self.dbapi.destroy_port(self.uuid)
        self.obj_reset_changes()

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def save(self, context=None):
        """Save updates to this Port.

        Updates will be made column by column based on the result
        of self.what_changed().

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Port(context)
        :raises: PortNotFound
        :raises: MACAlreadyExists if 'address' column is not unique

        """
        updates = self.do_version_changes_for_db()
        updated_port = self.dbapi.update_port(self.uuid, updates)
        self._from_db_object(self._context, self, updated_port)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def refresh(self, context=None):
        """Loads updates for this Port.

        Loads a port with the same uuid from the database and
        checks for updated attributes. Updates are applied from
        the loaded port column by column, if there are any updates.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Port(context)
        :raises: PortNotFound

        """
        current = self.get_by_uuid(self._context, uuid=self.uuid)
        self.obj_refresh(current)
        self.obj_reset_changes()

    @classmethod
    def supports_physical_network(cls):
        """Return whether the physical_network field is supported.

        :returns: Whether the physical_network field is supported
        :raises: ovo_exception.IncompatibleObjectVersion
        """
        return cls.supports_version((1, 7))

    @classmethod
    def supports_is_smartnic(cls):
        """Return whether is_smartnic field is supported.

        :returns: Whether is_smartnic field is supported
        :raises: ovo_exception.IncompatibleObjectVersion
        """
        return cls.supports_version((1, 9))
Esempio n. 15
0
 def setUp(self):
     super(TestFlexibleDictField, self).setUp()
     self.field = fields.FlexibleDictField()
Esempio n. 16
0
class Node(base.IronicObject, object_base.VersionedObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: Added instance_info
    # Version 1.2: Add get() and get_by_id() and make get_by_uuid()
    #              only work with a uuid
    # Version 1.3: Add create() and destroy()
    # Version 1.4: Add get_by_instance_uuid()
    # Version 1.5: Add list()
    # Version 1.6: Add reserve() and release()
    # Version 1.7: Add conductor_affinity
    # Version 1.8: Add maintenance_reason
    # Version 1.9: Add driver_internal_info
    # Version 1.10: Add name and get_by_name()
    # Version 1.11: Add clean_step
    # Version 1.12: Add raid_config and target_raid_config
    # Version 1.13: Add touch_provisioning()
    VERSION = '1.13'

    dbapi = db_api.get_instance()

    fields = {
        'id': object_fields.IntegerField(),
        'uuid': object_fields.UUIDField(nullable=True),
        'name': object_fields.StringField(nullable=True),
        'chassis_id': object_fields.IntegerField(nullable=True),
        'instance_uuid': object_fields.UUIDField(nullable=True),
        'driver': object_fields.StringField(nullable=True),
        'driver_info': object_fields.FlexibleDictField(nullable=True),
        'driver_internal_info': object_fields.FlexibleDictField(nullable=True),

        # A clean step dictionary, indicating the current clean step
        # being executed, or None, indicating cleaning is not in progress
        # or has not yet started.
        'clean_step': object_fields.FlexibleDictField(nullable=True),
        'raid_config': object_fields.FlexibleDictField(nullable=True),
        'target_raid_config': object_fields.FlexibleDictField(nullable=True),
        'instance_info': object_fields.FlexibleDictField(nullable=True),
        'properties': object_fields.FlexibleDictField(nullable=True),
        'reservation': object_fields.StringField(nullable=True),
        # a reference to the id of the conductor service, not its hostname,
        # that has most recently performed some action which could require
        # local state to be maintained (eg, built a PXE config)
        'conductor_affinity': object_fields.IntegerField(nullable=True),

        # One of states.POWER_ON|POWER_OFF|NOSTATE|ERROR
        'power_state': object_fields.StringField(nullable=True),

        # Set to one of states.POWER_ON|POWER_OFF when a power operation
        # starts, and set to NOSTATE when the operation finishes
        # (successfully or unsuccessfully).
        'target_power_state': object_fields.StringField(nullable=True),
        'provision_state': object_fields.StringField(nullable=True),
        'provision_updated_at': object_fields.DateTimeField(nullable=True),
        'target_provision_state': object_fields.StringField(nullable=True),
        'maintenance': object_fields.BooleanField(),
        'maintenance_reason': object_fields.StringField(nullable=True),
        'console_enabled': object_fields.BooleanField(),

        # Any error from the most recent (last) asynchronous transaction
        # that started but failed to finish.
        'last_error': object_fields.StringField(nullable=True),
        'inspection_finished_at': object_fields.DateTimeField(nullable=True),
        'inspection_started_at': object_fields.DateTimeField(nullable=True),
        'extra': object_fields.FlexibleDictField(nullable=True),
    }

    @staticmethod
    def _from_db_object(node, db_node):
        """Converts a database entity to a formal object."""
        for field in node.fields:
            node[field] = db_node[field]
        node.obj_reset_changes()
        return node

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get(cls, context, node_id):
        """Find a node based on its id or uuid and return a Node object.

        :param node_id: the id *or* uuid of a node.
        :returns: a :class:`Node` object.
        """
        if strutils.is_int_like(node_id):
            return cls.get_by_id(context, node_id)
        elif uuidutils.is_uuid_like(node_id):
            return cls.get_by_uuid(context, node_id)
        else:
            raise exception.InvalidIdentity(identity=node_id)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_id(cls, context, node_id):
        """Find a node based on its integer id and return a Node object.

        :param node_id: the id of a node.
        :returns: a :class:`Node` object.
        """
        db_node = cls.dbapi.get_node_by_id(node_id)
        node = Node._from_db_object(cls(context), db_node)
        return node

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_uuid(cls, context, uuid):
        """Find a node based on uuid and return a Node object.

        :param uuid: the uuid of a node.
        :returns: a :class:`Node` object.
        """
        db_node = cls.dbapi.get_node_by_uuid(uuid)
        node = Node._from_db_object(cls(context), db_node)
        return node

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_name(cls, context, name):
        """Find a node based on name and return a Node object.

        :param name: the logical name of a node.
        :returns: a :class:`Node` object.
        """
        db_node = cls.dbapi.get_node_by_name(name)
        node = Node._from_db_object(cls(context), db_node)
        return node

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_instance_uuid(cls, context, instance_uuid):
        """Find a node based on the instance uuid and return a Node object.

        :param uuid: the uuid of the instance.
        :returns: a :class:`Node` object.
        """
        db_node = cls.dbapi.get_node_by_instance(instance_uuid)
        node = Node._from_db_object(cls(context), db_node)
        return node

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def list(cls,
             context,
             limit=None,
             marker=None,
             sort_key=None,
             sort_dir=None,
             filters=None):
        """Return a list of Node objects.

        :param context: Security context.
        :param limit: maximum number of resources to return in a single result.
        :param marker: pagination marker for large data sets.
        :param sort_key: column to sort results by.
        :param sort_dir: direction to sort. "asc" or "desc".
        :param filters: Filters to apply.
        :returns: a list of :class:`Node` object.

        """
        db_nodes = cls.dbapi.get_node_list(filters=filters,
                                           limit=limit,
                                           marker=marker,
                                           sort_key=sort_key,
                                           sort_dir=sort_dir)
        return [Node._from_db_object(cls(context), obj) for obj in db_nodes]

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def reserve(cls, context, tag, node_id):
        """Get and reserve a node.

        To prevent other ManagerServices from manipulating the given
        Node while a Task is performed, mark it reserved by this host.

        :param context: Security context.
        :param tag: A string uniquely identifying the reservation holder.
        :param node_id: A node id or uuid.
        :raises: NodeNotFound if the node is not found.
        :returns: a :class:`Node` object.

        """
        db_node = cls.dbapi.reserve_node(tag, node_id)
        node = Node._from_db_object(cls(context), db_node)
        return node

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def release(cls, context, tag, node_id):
        """Release the reservation on a node.

        :param context: Security context.
        :param tag: A string uniquely identifying the reservation holder.
        :param node_id: A node id or uuid.
        :raises: NodeNotFound if the node is not found.

        """
        cls.dbapi.release_node(tag, node_id)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def create(self, context=None):
        """Create a Node record in the DB.

        Column-wise updates will be made based on the result of
        self.what_changed(). If target_power_state is provided,
        it will be checked against the in-database copy of the
        node before updates are made.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Node(context)

        """
        values = self.obj_get_changes()
        db_node = self.dbapi.create_node(values)
        self._from_db_object(self, db_node)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def destroy(self, context=None):
        """Delete the Node from the DB.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Node(context)
        """
        self.dbapi.destroy_node(self.uuid)
        self.obj_reset_changes()

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def save(self, context=None):
        """Save updates to this Node.

        Column-wise updates will be made based on the result of
        self.what_changed(). If target_power_state is provided,
        it will be checked against the in-database copy of the
        node before updates are made.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Node(context)
        """
        updates = self.obj_get_changes()
        if 'driver' in updates and 'driver_internal_info' not in updates:
            # Clean driver_internal_info when changes driver
            self.driver_internal_info = {}
            updates = self.obj_get_changes()
        self.dbapi.update_node(self.uuid, updates)
        self.obj_reset_changes()

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def refresh(self, context=None):
        """Refresh the object by re-fetching from the DB.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Node(context)
        """
        current = self.__class__.get_by_uuid(self._context, self.uuid)
        self.obj_refresh(current)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def touch_provisioning(self, context=None):
        """Touch the database record to mark the provisioning as alive."""
        self.dbapi.touch_node_provisioning(self.id)
Esempio n. 17
0
class NodePayload(notification.NotificationPayloadBase):
    """Base class used for all notification payloads about a Node object."""
    # NOTE: This payload does not include the Node fields "chassis_id",
    # "driver_info", "driver_internal_info", "instance_info", "raid_config",
    # "reservation", or "target_raid_config". These were excluded for reasons
    # including:
    # - increased complexity needed for creating the payload
    # - sensitive information in the fields that shouldn't be exposed to
    #   external services
    # - being internal-only or hardware-related fields
    SCHEMA = {
        'clean_step': ('node', 'clean_step'),
        'conductor_group': ('node', 'conductor_group'),
        'console_enabled': ('node', 'console_enabled'),
        'created_at': ('node', 'created_at'),
        'deploy_step': ('node', 'deploy_step'),
        'driver': ('node', 'driver'),
        'extra': ('node', 'extra'),
        'inspection_finished_at': ('node', 'inspection_finished_at'),
        'inspection_started_at': ('node', 'inspection_started_at'),
        'instance_uuid': ('node', 'instance_uuid'),
        'last_error': ('node', 'last_error'),
        'maintenance': ('node', 'maintenance'),
        'maintenance_reason': ('node', 'maintenance_reason'),
        'fault': ('node', 'fault'),
        'name': ('node', 'name'),
        'bios_interface': ('node', 'bios_interface'),
        'boot_interface': ('node', 'boot_interface'),
        'console_interface': ('node', 'console_interface'),
        'deploy_interface': ('node', 'deploy_interface'),
        'inspect_interface': ('node', 'inspect_interface'),
        'management_interface': ('node', 'management_interface'),
        'network_interface': ('node', 'network_interface'),
        'power_interface': ('node', 'power_interface'),
        'raid_interface': ('node', 'raid_interface'),
        'rescue_interface': ('node', 'rescue_interface'),
        'storage_interface': ('node', 'storage_interface'),
        'vendor_interface': ('node', 'vendor_interface'),
        'power_state': ('node', 'power_state'),
        'properties': ('node', 'properties'),
        'protected': ('node', 'protected'),
        'protected_reason': ('node', 'protected_reason'),
        'provision_state': ('node', 'provision_state'),
        'provision_updated_at': ('node', 'provision_updated_at'),
        'resource_class': ('node', 'resource_class'),
        'target_power_state': ('node', 'target_power_state'),
        'target_provision_state': ('node', 'target_provision_state'),
        'updated_at': ('node', 'updated_at'),
        'uuid': ('node', 'uuid')
    }

    # Version 1.0: Initial version, based off of Node version 1.18.
    # Version 1.1: Type of network_interface changed to just nullable string
    #              similar to version 1.20 of Node.
    # Version 1.2: Add nullable to console_enabled and maintenance.
    # Version 1.3: Add dynamic interfaces fields exposed via API.
    # Version 1.4: Add storage interface field exposed via API.
    # Version 1.5: Add rescue interface field exposed via API.
    # Version 1.6: Add traits field exposed via API.
    # Version 1.7: Add fault field exposed via API.
    # Version 1.8: Add bios interface field exposed via API.
    # Version 1.9: Add deploy_step field exposed via API.
    # Version 1.10: Add conductor_group field exposed via API.
    # Version 1.11: Add protected and protected_reason fields exposed via API.
    VERSION = '1.11'
    fields = {
        'clean_step': object_fields.FlexibleDictField(nullable=True),
        'conductor_group': object_fields.StringField(nullable=True),
        'console_enabled': object_fields.BooleanField(nullable=True),
        'created_at': object_fields.DateTimeField(nullable=True),
        'deploy_step': object_fields.FlexibleDictField(nullable=True),
        'driver': object_fields.StringField(nullable=True),
        'extra': object_fields.FlexibleDictField(nullable=True),
        'inspection_finished_at': object_fields.DateTimeField(nullable=True),
        'inspection_started_at': object_fields.DateTimeField(nullable=True),
        'instance_uuid': object_fields.UUIDField(nullable=True),
        'last_error': object_fields.StringField(nullable=True),
        'maintenance': object_fields.BooleanField(nullable=True),
        'maintenance_reason': object_fields.StringField(nullable=True),
        'fault': object_fields.StringField(nullable=True),
        'bios_interface': object_fields.StringField(nullable=True),
        'boot_interface': object_fields.StringField(nullable=True),
        'console_interface': object_fields.StringField(nullable=True),
        'deploy_interface': object_fields.StringField(nullable=True),
        'inspect_interface': object_fields.StringField(nullable=True),
        'management_interface': object_fields.StringField(nullable=True),
        'network_interface': object_fields.StringField(nullable=True),
        'power_interface': object_fields.StringField(nullable=True),
        'raid_interface': object_fields.StringField(nullable=True),
        'rescue_interface': object_fields.StringField(nullable=True),
        'storage_interface': object_fields.StringField(nullable=True),
        'vendor_interface': object_fields.StringField(nullable=True),
        'name': object_fields.StringField(nullable=True),
        'power_state': object_fields.StringField(nullable=True),
        'properties': object_fields.FlexibleDictField(nullable=True),
        'protected': object_fields.BooleanField(nullable=True),
        'protected_reason': object_fields.StringField(nullable=True),
        'provision_state': object_fields.StringField(nullable=True),
        'provision_updated_at': object_fields.DateTimeField(nullable=True),
        'resource_class': object_fields.StringField(nullable=True),
        'target_power_state': object_fields.StringField(nullable=True),
        'target_provision_state': object_fields.StringField(nullable=True),
        'traits': object_fields.ListOfStringsField(nullable=True),
        'updated_at': object_fields.DateTimeField(nullable=True),
        'uuid': object_fields.UUIDField()
    }

    def __init__(self, node, **kwargs):
        super(NodePayload, self).__init__(**kwargs)
        self.populate_schema(node=node)
        # NOTE(mgoddard): Populate traits with a list of trait names, rather
        # than the TraitList object.
        if node.obj_attr_is_set('traits') and node.traits is not None:
            self.traits = node.traits.get_trait_names()
        else:
            self.traits = []
Esempio n. 18
0
class DeployTemplate(base.IronicObject, object_base.VersionedObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: Added 'extra' field
    VERSION = '1.1'

    dbapi = db_api.get_instance()

    fields = {
        'id': object_fields.IntegerField(),
        'uuid': object_fields.UUIDField(nullable=False),
        'name': object_fields.StringField(nullable=False),
        'steps': object_fields.ListOfFlexibleDictsField(nullable=False),
        'extra': object_fields.FlexibleDictField(nullable=True),
    }

    # NOTE(mgoddard): We don't want to enable RPC on this call just yet.
    # Remotable methods can be used in the future to replace current explicit
    # RPC calls.  Implications of calling new remote procedures should be
    # thought through.
    # @object_base.remotable
    def create(self, context=None):
        """Create a DeployTemplate record in the DB.

        :param context: security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: DeployTemplate(context).
        :raises: DeployTemplateDuplicateName if a deploy template with the same
            name exists.
        :raises: DeployTemplateAlreadyExists if a deploy template with the same
            UUID exists.
        """
        values = self.do_version_changes_for_db()
        db_template = self.dbapi.create_deploy_template(values)
        self._from_db_object(self._context, self, db_template)

    # NOTE(mgoddard): We don't want to enable RPC on this call just yet.
    # Remotable methods can be used in the future to replace current explicit
    # RPC calls.  Implications of calling new remote procedures should be
    # thought through.
    # @object_base.remotable
    def save(self, context=None):
        """Save updates to this DeployTemplate.

        Column-wise updates will be made based on the result of
        self.what_changed().

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: DeployTemplate(context)
        :raises: DeployTemplateDuplicateName if a deploy template with the same
            name exists.
        :raises: DeployTemplateNotFound if the deploy template does not exist.
        """
        updates = self.do_version_changes_for_db()
        db_template = self.dbapi.update_deploy_template(self.uuid, updates)
        self._from_db_object(self._context, self, db_template)

    # NOTE(mgoddard): We don't want to enable RPC on this call just yet.
    # Remotable methods can be used in the future to replace current explicit
    # RPC calls.  Implications of calling new remote procedures should be
    # thought through.
    # @object_base.remotable_classmethod
    def destroy(self):
        """Delete the DeployTemplate from the DB.

        :param context: security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: DeployTemplate(context).
        :raises: DeployTemplateNotFound if the deploy template no longer
            appears in the database.
        """
        self.dbapi.destroy_deploy_template(self.id)
        self.obj_reset_changes()

    # NOTE(mgoddard): We don't want to enable RPC on this call just yet.
    # Remotable methods can be used in the future to replace current explicit
    # RPC calls.  Implications of calling new remote procedures should be
    # thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_id(cls, context, template_id):
        """Find a deploy template based on its integer ID.

        :param context: security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: DeployTemplate(context).
        :param template_id: The ID of a deploy template.
        :raises: DeployTemplateNotFound if the deploy template no longer
            appears in the database.
        :returns: a :class:`DeployTemplate` object.
        """
        db_template = cls.dbapi.get_deploy_template_by_id(template_id)
        template = cls._from_db_object(context, cls(), db_template)
        return template

    # NOTE(mgoddard): We don't want to enable RPC on this call just yet.
    # Remotable methods can be used in the future to replace current explicit
    # RPC calls.  Implications of calling new remote procedures should be
    # thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_uuid(cls, context, uuid):
        """Find a deploy template based on its UUID.

        :param context: security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: DeployTemplate(context).
        :param uuid: The UUID of a deploy template.
        :raises: DeployTemplateNotFound if the deploy template no longer
            appears in the database.
        :returns: a :class:`DeployTemplate` object.
        """
        db_template = cls.dbapi.get_deploy_template_by_uuid(uuid)
        template = cls._from_db_object(context, cls(), db_template)
        return template

    # NOTE(mgoddard): We don't want to enable RPC on this call just yet.
    # Remotable methods can be used in the future to replace current explicit
    # RPC calls.  Implications of calling new remote procedures should be
    # thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_name(cls, context, name):
        """Find a deploy template based on its name.

        :param context: security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: DeployTemplate(context).
        :param name: The name of a deploy template.
        :raises: DeployTemplateNotFound if the deploy template no longer
            appears in the database.
        :returns: a :class:`DeployTemplate` object.
        """
        db_template = cls.dbapi.get_deploy_template_by_name(name)
        template = cls._from_db_object(context, cls(), db_template)
        return template

    # NOTE(mgoddard): We don't want to enable RPC on this call just yet.
    # Remotable methods can be used in the future to replace current explicit
    # RPC calls.  Implications of calling new remote procedures should be
    # thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def list(cls,
             context,
             limit=None,
             marker=None,
             sort_key=None,
             sort_dir=None):
        """Return a list of DeployTemplate objects.

        :param context: security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: DeployTemplate(context).
        :param limit: maximum number of resources to return in a single result.
        :param marker: pagination marker for large data sets.
        :param sort_key: column to sort results by.
        :param sort_dir: direction to sort. "asc" or "desc".
        :returns: a list of :class:`DeployTemplate` objects.
        """
        db_templates = cls.dbapi.get_deploy_template_list(limit=limit,
                                                          marker=marker,
                                                          sort_key=sort_key,
                                                          sort_dir=sort_dir)
        return cls._from_db_object_list(context, db_templates)

    # NOTE(mgoddard): We don't want to enable RPC on this call just yet.
    # Remotable methods can be used in the future to replace current explicit
    # RPC calls.  Implications of calling new remote procedures should be
    # thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def list_by_names(cls, context, names):
        """Return a list of DeployTemplate objects matching a set of names.

        :param context: security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: DeployTemplate(context).
        :param names: a list of names to filter by.
        :returns: a list of :class:`DeployTemplate` objects.
        """
        db_templates = cls.dbapi.get_deploy_template_list_by_names(names)
        return cls._from_db_object_list(context, db_templates)

    def refresh(self, context=None):
        """Loads updates for this deploy template.

        Loads a deploy template with the same uuid from the database and
        checks for updated attributes. Updates are applied from
        the loaded template column by column, if there are any updates.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Port(context)
        :raises: DeployTemplateNotFound if the deploy template no longer
            appears in the database.
        """
        current = self.get_by_uuid(self._context, uuid=self.uuid)
        self.obj_refresh(current)
        self.obj_reset_changes()
Esempio n. 19
0
class Allocation(base.IronicObject, object_base.VersionedObjectDictCompat):
    # Version 1.0: Initial version
    VERSION = '1.0'

    dbapi = dbapi.get_instance()

    fields = {
        'id': object_fields.IntegerField(),
        'uuid': object_fields.UUIDField(nullable=True),
        'name': object_fields.StringField(nullable=True),
        'node_id': object_fields.IntegerField(nullable=True),
        'state': object_fields.StringField(nullable=True),
        'last_error': object_fields.StringField(nullable=True),
        'resource_class': object_fields.StringField(nullable=True),
        'traits': object_fields.ListOfStringsField(nullable=True),
        'candidate_nodes': object_fields.ListOfStringsField(nullable=True),
        'extra': object_fields.FlexibleDictField(nullable=True),
        'conductor_affinity': object_fields.IntegerField(nullable=True),
    }

    def _convert_to_version(self,
                            target_version,
                            remove_unavailable_fields=True):
        """Convert to the target version.

        Convert the object to the target version. The target version may be
        the same, older, or newer than the version of the object. This is
        used for DB interactions as well as for serialization/deserialization.

        :param target_version: the desired version of the object
        :param remove_unavailable_fields: True to remove fields that are
            unavailable in the target version; set this to True when
            (de)serializing. False to set the unavailable fields to appropriate
            values; set this to False for DB interactions.
        """

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get(cls, context, allocation_ident):
        """Find an allocation by its ID, UUID or name.

        :param allocation_ident: The ID, UUID or name of an allocation.
        :param context: Security context
        :returns: An :class:`Allocation` object.
        :raises: InvalidIdentity

        """
        if strutils.is_int_like(allocation_ident):
            return cls.get_by_id(context, allocation_ident)
        elif uuidutils.is_uuid_like(allocation_ident):
            return cls.get_by_uuid(context, allocation_ident)
        elif utils.is_valid_logical_name(allocation_ident):
            return cls.get_by_name(context, allocation_ident)
        else:
            raise exception.InvalidIdentity(identity=allocation_ident)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_id(cls, context, allocation_id):
        """Find an allocation by its integer ID.

        :param cls: the :class:`Allocation`
        :param context: Security context
        :param allocation_id: The ID of an allocation.
        :returns: An :class:`Allocation` object.
        :raises: AllocationNotFound

        """
        db_allocation = cls.dbapi.get_allocation_by_id(allocation_id)
        allocation = cls._from_db_object(context, cls(), db_allocation)
        return allocation

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_uuid(cls, context, uuid):
        """Find an allocation by its UUID.

        :param cls: the :class:`Allocation`
        :param context: Security context
        :param uuid: The UUID of an allocation.
        :returns: An :class:`Allocation` object.
        :raises: AllocationNotFound

        """
        db_allocation = cls.dbapi.get_allocation_by_uuid(uuid)
        allocation = cls._from_db_object(context, cls(), db_allocation)
        return allocation

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_name(cls, context, name):
        """Find an allocation based by its name.

        :param cls: the :class:`Allocation`
        :param context: Security context
        :param name: The name of an allocation.
        :returns: An :class:`Allocation` object.
        :raises: AllocationNotFound

        """
        db_allocation = cls.dbapi.get_allocation_by_name(name)
        allocation = cls._from_db_object(context, cls(), db_allocation)
        return allocation

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def list(cls,
             context,
             filters=None,
             limit=None,
             marker=None,
             sort_key=None,
             sort_dir=None):
        """Return a list of Allocation objects.

        :param cls: the :class:`Allocation`
        :param context: Security context.
        :param filters: Filters to apply.
        :param limit: Maximum number of resources to return in a single result.
        :param marker: Pagination marker for large data sets.
        :param sort_key: Column to sort results by.
        :param sort_dir: Direction to sort. "asc" or "desc".
        :returns: A list of :class:`Allocation` object.
        :raises: InvalidParameterValue

        """
        db_allocations = cls.dbapi.get_allocation_list(filters=filters,
                                                       limit=limit,
                                                       marker=marker,
                                                       sort_key=sort_key,
                                                       sort_dir=sort_dir)
        return cls._from_db_object_list(context, db_allocations)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def create(self, context=None):
        """Create a Allocation record in the DB.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Allocation(context)
        :raises: AllocationDuplicateName, AllocationAlreadyExists

        """
        values = self.do_version_changes_for_db()
        db_allocation = self.dbapi.create_allocation(values)
        self._from_db_object(self._context, self, db_allocation)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def destroy(self, context=None):
        """Delete the Allocation from the DB.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Allocation(context)
        :raises: AllocationNotFound

        """
        self.dbapi.destroy_allocation(self.uuid)
        self.obj_reset_changes()

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def save(self, context=None):
        """Save updates to this Allocation.

        Updates will be made column by column based on the result
        of self.what_changed().

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Allocation(context)
        :raises: AllocationNotFound, AllocationDuplicateName

        """
        updates = self.do_version_changes_for_db()
        updated_allocation = self.dbapi.update_allocation(self.uuid, updates)
        self._from_db_object(self._context, self, updated_allocation)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def refresh(self, context=None):
        """Loads updates for this Allocation.

        Loads an allocation with the same uuid from the database and
        checks for updated attributes. Updates are applied from
        the loaded allocation column by column, if there are any updates.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Allocation(context)
        :raises: AllocationNotFound

        """
        current = self.get_by_uuid(self._context, uuid=self.uuid)
        self.obj_refresh(current)
        self.obj_reset_changes()
Esempio n. 20
0
class VolumeConnector(base.IronicObject,
                      object_base.VersionedObjectDictCompat):
    # Version 1.0: Initial version
    VERSION = '1.0'

    dbapi = db_api.get_instance()

    fields = {
        'id': object_fields.IntegerField(),
        'uuid': object_fields.UUIDField(nullable=True),
        'node_id': object_fields.IntegerField(nullable=True),
        'type': object_fields.StringField(nullable=True),
        'connector_id': object_fields.StringField(nullable=True),
        'extra': object_fields.FlexibleDictField(nullable=True),
    }

    @staticmethod
    def _from_db_object_list(db_objects, cls, context):
        """Convert a list of database entities to a list of formal objects."""
        return [VolumeConnector._from_db_object(cls(context), obj)
                for obj in db_objects]

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get(cls, context, ident):
        """Find a volume connector based on its ID or UUID.

        :param context: security context
        :param ident: the database primary key ID *or* the UUID of a volume
                      connector
        :returns: a :class:`VolumeConnector` object
        :raises: InvalidIdentity if ident is neither an integer ID nor a UUID
        :raises: VolumeConnectorNotFound if no volume connector exists with
                 the specified ident
        """
        if strutils.is_int_like(ident):
            return cls.get_by_id(context, ident)
        elif uuidutils.is_uuid_like(ident):
            return cls.get_by_uuid(context, ident)
        else:
            raise exception.InvalidIdentity(identity=ident)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_id(cls, context, db_id):
        """Find a volume connector based on its integer ID.

        :param context: Security context.
        :param db_id: The integer (database primary key) ID of a
                      volume connector.
        :returns: A :class:`VolumeConnector` object.
        :raises: VolumeConnectorNotFound if no volume connector exists with
                 the specified ID.
        """
        db_connector = cls.dbapi.get_volume_connector_by_id(db_id)
        connector = VolumeConnector._from_db_object(cls(context), db_connector)
        return connector

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_uuid(cls, context, uuid):
        """Find a volume connector based on its UUID.

        :param context: security context
        :param uuid: the UUID of a volume connector
        :returns: a :class:`VolumeConnector` object
        :raises: VolumeConnectorNotFound if no volume connector exists with
                 the specified UUID
        """
        db_connector = cls.dbapi.get_volume_connector_by_uuid(uuid)
        connector = VolumeConnector._from_db_object(cls(context), db_connector)
        return connector

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def list(cls, context, limit=None, marker=None,
             sort_key=None, sort_dir=None):
        """Return a list of VolumeConnector objects.

        :param context: security context
        :param limit: maximum number of resources to return in a single result
        :param marker: pagination marker for large data sets
        :param sort_key: column to sort results by
        :param sort_dir: direction to sort. "asc" or "desc".
        :returns: a list of :class:`VolumeConnector` objects
        :raises: InvalidParameterValue if sort_key does not exist
        """
        db_connectors = cls.dbapi.get_volume_connector_list(limit=limit,
                                                            marker=marker,
                                                            sort_key=sort_key,
                                                            sort_dir=sort_dir)
        return VolumeConnector._from_db_object_list(db_connectors,
                                                    cls, context)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def list_by_node_id(cls, context, node_id, limit=None, marker=None,
                        sort_key=None, sort_dir=None):
        """Return a list of VolumeConnector objects related to a given node ID.

        :param context: security context
        :param node_id: the integer ID of the node
        :param limit: maximum number of resources to return in a single result
        :param marker: pagination marker for large data sets
        :param sort_key: column to sort results by
        :param sort_dir: direction to sort. "asc" or "desc".
        :returns: a list of :class:`VolumeConnector` objects
        :raises: InvalidParameterValue if sort_key does not exist
        """
        db_connectors = cls.dbapi.get_volume_connectors_by_node_id(
            node_id,
            limit=limit,
            marker=marker,
            sort_key=sort_key,
            sort_dir=sort_dir)
        return VolumeConnector._from_db_object_list(db_connectors,
                                                    cls, context)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def create(self, context=None):
        """Create a VolumeConnector record in the DB.

        :param context: security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: VolumeConnector(context).
        :raises: VolumeConnectorTypeAndIdAlreadyExists if a volume
                 connector already exists with the same type and connector_id
        :raises: VolumeConnectorAlreadyExists if a volume connector with the
                 same UUID already exists
        """
        values = self.obj_get_changes()
        db_connector = self.dbapi.create_volume_connector(values)
        self._from_db_object(self, db_connector)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def destroy(self, context=None):
        """Delete the VolumeConnector from the DB.

        :param context: security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: VolumeConnector(context).
        :raises: VolumeConnectorNotFound if the volume connector cannot be
                 found
        """
        self.dbapi.destroy_volume_connector(self.uuid)
        self.obj_reset_changes()

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def save(self, context=None):
        """Save updates to this VolumeConnector.

        Updates will be made column by column based on the result
        of self.obj_get_changes().

        :param context: security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: VolumeConnector(context).
        :raises: VolumeConnectorNotFound if the volume connector cannot be
                 found
        :raises: VolumeConnectorTypeAndIdAlreadyExists if another connector
                 already exists with the same values for type and connector_id
                 fields
        :raises: InvalidParameterValue when the UUID is being changed
        """
        updates = self.obj_get_changes()
        updated_connector = self.dbapi.update_volume_connector(self.uuid,
                                                               updates)
        self._from_db_object(self, updated_connector)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def refresh(self, context=None):
        """Load updates for this VolumeConnector.

        Load a volume connector with the same UUID from the database
        and check for updated attributes. If there are any updates,
        they are applied from the loaded volume connector, column by column.

        :param context: security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: VolumeConnector(context).
        """
        current = self.__class__.get_by_uuid(self._context, uuid=self.uuid)
        self.obj_refresh(current)
Esempio n. 21
0
class NodePayload(notification.NotificationPayloadBase):
    """Base class used for all notification payloads about a Node object."""
    # NOTE: This payload does not include the Node fields "chassis_id",
    # "driver_info", "driver_internal_info", "instance_info", "raid_config",
    # "reservation", or "target_raid_config". These were excluded for reasons
    # including:
    # - increased complexity needed for creating the payload
    # - sensitive information in the fields that shouldn't be exposed to
    #   external services
    # - being internal-only or hardware-related fields
    SCHEMA = {
        'clean_step': ('node', 'clean_step'),
        'console_enabled': ('node', 'console_enabled'),
        'created_at': ('node', 'created_at'),
        'driver': ('node', 'driver'),
        'extra': ('node', 'extra'),
        'inspection_finished_at': ('node', 'inspection_finished_at'),
        'inspection_started_at': ('node', 'inspection_started_at'),
        'instance_uuid': ('node', 'instance_uuid'),
        'last_error': ('node', 'last_error'),
        'maintenance': ('node', 'maintenance'),
        'maintenance_reason': ('node', 'maintenance_reason'),
        'name': ('node', 'name'),
        'boot_interface': ('node', 'boot_interface'),
        'console_interface': ('node', 'console_interface'),
        'deploy_interface': ('node', 'deploy_interface'),
        'inspect_interface': ('node', 'inspect_interface'),
        'management_interface': ('node', 'management_interface'),
        'network_interface': ('node', 'network_interface'),
        'power_interface': ('node', 'power_interface'),
        'raid_interface': ('node', 'raid_interface'),
        'vendor_interface': ('node', 'vendor_interface'),
        'power_state': ('node', 'power_state'),
        'properties': ('node', 'properties'),
        'provision_state': ('node', 'provision_state'),
        'provision_updated_at': ('node', 'provision_updated_at'),
        'resource_class': ('node', 'resource_class'),
        'target_power_state': ('node', 'target_power_state'),
        'target_provision_state': ('node', 'target_provision_state'),
        'updated_at': ('node', 'updated_at'),
        'uuid': ('node', 'uuid')
    }
    # TODO(TheJulia): At a later point in time, once storage_interfaces
    # are able to be leveraged, we need to add the sotrage_interface
    # field to payload and increment the object versions for all objects
    # that inherit the NodePayload object.

    # Version 1.0: Initial version, based off of Node version 1.18.
    # Version 1.1: Type of network_interface changed to just nullable string
    #              similar to version 1.20 of Node.
    # Version 1.2: Add nullable to console_enabled and maintenance.
    # Version 1.3: Add dynamic interfaces fields exposed via API.
    VERSION = '1.3'
    fields = {
        'clean_step': object_fields.FlexibleDictField(nullable=True),
        'console_enabled': object_fields.BooleanField(nullable=True),
        'created_at': object_fields.DateTimeField(nullable=True),
        'driver': object_fields.StringField(nullable=True),
        'extra': object_fields.FlexibleDictField(nullable=True),
        'inspection_finished_at': object_fields.DateTimeField(nullable=True),
        'inspection_started_at': object_fields.DateTimeField(nullable=True),
        'instance_uuid': object_fields.UUIDField(nullable=True),
        'last_error': object_fields.StringField(nullable=True),
        'maintenance': object_fields.BooleanField(nullable=True),
        'maintenance_reason': object_fields.StringField(nullable=True),
        'boot_interface': object_fields.StringField(nullable=True),
        'console_interface': object_fields.StringField(nullable=True),
        'deploy_interface': object_fields.StringField(nullable=True),
        'inspect_interface': object_fields.StringField(nullable=True),
        'management_interface': object_fields.StringField(nullable=True),
        'network_interface': object_fields.StringField(nullable=True),
        'power_interface': object_fields.StringField(nullable=True),
        'raid_interface': object_fields.StringField(nullable=True),
        'vendor_interface': object_fields.StringField(nullable=True),
        'name': object_fields.StringField(nullable=True),
        'power_state': object_fields.StringField(nullable=True),
        'properties': object_fields.FlexibleDictField(nullable=True),
        'provision_state': object_fields.StringField(nullable=True),
        'provision_updated_at': object_fields.DateTimeField(nullable=True),
        'resource_class': object_fields.StringField(nullable=True),
        'target_power_state': object_fields.StringField(nullable=True),
        'target_provision_state': object_fields.StringField(nullable=True),
        'updated_at': object_fields.DateTimeField(nullable=True),
        'uuid': object_fields.UUIDField()
    }

    def __init__(self, node, **kwargs):
        super(NodePayload, self).__init__(**kwargs)
        self.populate_schema(node=node)
Esempio n. 22
0
class Node(base.IronicObject, object_base.VersionedObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: Added instance_info
    # Version 1.2: Add get() and get_by_id() and make get_by_uuid()
    #              only work with a uuid
    # Version 1.3: Add create() and destroy()
    # Version 1.4: Add get_by_instance_uuid()
    # Version 1.5: Add list()
    # Version 1.6: Add reserve() and release()
    # Version 1.7: Add conductor_affinity
    # Version 1.8: Add maintenance_reason
    # Version 1.9: Add driver_internal_info
    # Version 1.10: Add name and get_by_name()
    # Version 1.11: Add clean_step
    # Version 1.12: Add raid_config and target_raid_config
    # Version 1.13: Add touch_provisioning()
    # Version 1.14: Add _validate_property_values() and make create()
    #               and save() validate the input of property values.
    # Version 1.15: Add get_by_port_addresses
    # Version 1.16: Add network_interface field
    # Version 1.17: Add resource_class field
    # Version 1.18: Add default setting for network_interface
    # Version 1.19: Add fields: boot_interface, console_interface,
    #               deploy_interface, inspect_interface, management_interface,
    #               power_interface, raid_interface, vendor_interface
    # Version 1.20: Type of network_interface changed to just nullable string
    # Version 1.21: Add storage_interface field
    VERSION = '1.21'

    dbapi = db_api.get_instance()

    fields = {
        'id': object_fields.IntegerField(),

        'uuid': object_fields.UUIDField(nullable=True),
        'name': object_fields.StringField(nullable=True),
        'chassis_id': object_fields.IntegerField(nullable=True),
        'instance_uuid': object_fields.UUIDField(nullable=True),

        'driver': object_fields.StringField(nullable=True),
        'driver_info': object_fields.FlexibleDictField(nullable=True),
        'driver_internal_info': object_fields.FlexibleDictField(nullable=True),

        # A clean step dictionary, indicating the current clean step
        # being executed, or None, indicating cleaning is not in progress
        # or has not yet started.
        'clean_step': object_fields.FlexibleDictField(nullable=True),

        'raid_config': object_fields.FlexibleDictField(nullable=True),
        'target_raid_config': object_fields.FlexibleDictField(nullable=True),

        'instance_info': object_fields.FlexibleDictField(nullable=True),
        'properties': object_fields.FlexibleDictField(nullable=True),
        'reservation': object_fields.StringField(nullable=True),
        # a reference to the id of the conductor service, not its hostname,
        # that has most recently performed some action which could require
        # local state to be maintained (eg, built a PXE config)
        'conductor_affinity': object_fields.IntegerField(nullable=True),

        # One of states.POWER_ON|POWER_OFF|NOSTATE|ERROR
        'power_state': object_fields.StringField(nullable=True),

        # Set to one of states.POWER_ON|POWER_OFF when a power operation
        # starts, and set to NOSTATE when the operation finishes
        # (successfully or unsuccessfully).
        'target_power_state': object_fields.StringField(nullable=True),

        'provision_state': object_fields.StringField(nullable=True),
        'provision_updated_at': object_fields.DateTimeField(nullable=True),
        'target_provision_state': object_fields.StringField(nullable=True),

        'maintenance': object_fields.BooleanField(),
        'maintenance_reason': object_fields.StringField(nullable=True),
        'console_enabled': object_fields.BooleanField(),

        # Any error from the most recent (last) asynchronous transaction
        # that started but failed to finish.
        'last_error': object_fields.StringField(nullable=True),

        # Used by nova to relate the node to a flavor
        'resource_class': object_fields.StringField(nullable=True),

        'inspection_finished_at': object_fields.DateTimeField(nullable=True),
        'inspection_started_at': object_fields.DateTimeField(nullable=True),

        'extra': object_fields.FlexibleDictField(nullable=True),

        'boot_interface': object_fields.StringField(nullable=True),
        'console_interface': object_fields.StringField(nullable=True),
        'deploy_interface': object_fields.StringField(nullable=True),
        'inspect_interface': object_fields.StringField(nullable=True),
        'management_interface': object_fields.StringField(nullable=True),
        'network_interface': object_fields.StringField(nullable=True),
        'power_interface': object_fields.StringField(nullable=True),
        'raid_interface': object_fields.StringField(nullable=True),
        'storage_interface': object_fields.StringField(nullable=True),
        'vendor_interface': object_fields.StringField(nullable=True),
    }

    def _validate_property_values(self, properties):
        """Check if the input of local_gb, cpus and memory_mb are valid.

        :param properties: a dict contains the node's information.
        """
        if not properties:
            return
        invalid_msgs_list = []
        for param in REQUIRED_INT_PROPERTIES:
            value = properties.get(param)
            if value is None:
                continue
            try:
                int_value = int(value)
                assert int_value >= 0
            except (ValueError, AssertionError):
                msg = (('%(param)s=%(value)s') %
                       {'param': param, 'value': value})
                invalid_msgs_list.append(msg)

        if invalid_msgs_list:
            msg = (_('The following properties for node %(node)s '
                     'should be non-negative integers, '
                     'but provided values are: %(msgs)s') %
                   {'node': self.uuid, 'msgs': ', '.join(invalid_msgs_list)})
            raise exception.InvalidParameterValue(msg)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get(cls, context, node_id):
        """Find a node based on its id or uuid and return a Node object.

        :param node_id: the id *or* uuid of a node.
        :returns: a :class:`Node` object.
        """
        if strutils.is_int_like(node_id):
            return cls.get_by_id(context, node_id)
        elif uuidutils.is_uuid_like(node_id):
            return cls.get_by_uuid(context, node_id)
        else:
            raise exception.InvalidIdentity(identity=node_id)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_id(cls, context, node_id):
        """Find a node based on its integer ID and return a Node object.

        :param cls: the :class:`Node`
        :param context: Security context
        :param node_id: the ID of a node.
        :returns: a :class:`Node` object.
        """
        db_node = cls.dbapi.get_node_by_id(node_id)
        node = cls._from_db_object(context, cls(), db_node)
        return node

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_uuid(cls, context, uuid):
        """Find a node based on UUID and return a Node object.

        :param cls: the :class:`Node`
        :param context: Security context
        :param uuid: the UUID of a node.
        :returns: a :class:`Node` object.
        """
        db_node = cls.dbapi.get_node_by_uuid(uuid)
        node = cls._from_db_object(context, cls(), db_node)
        return node

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_name(cls, context, name):
        """Find a node based on name and return a Node object.

        :param cls: the :class:`Node`
        :param context: Security context
        :param name: the logical name of a node.
        :returns: a :class:`Node` object.
        """
        db_node = cls.dbapi.get_node_by_name(name)
        node = cls._from_db_object(context, cls(), db_node)
        return node

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_instance_uuid(cls, context, instance_uuid):
        """Find a node based on the instance UUID and return a Node object.

        :param cls: the :class:`Node`
        :param context: Security context
        :param uuid: the UUID of the instance.
        :returns: a :class:`Node` object.
        """
        db_node = cls.dbapi.get_node_by_instance(instance_uuid)
        node = cls._from_db_object(context, cls(), db_node)
        return node

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def list(cls, context, limit=None, marker=None, sort_key=None,
             sort_dir=None, filters=None):
        """Return a list of Node objects.

        :param cls: the :class:`Node`
        :param context: Security context.
        :param limit: maximum number of resources to return in a single result.
        :param marker: pagination marker for large data sets.
        :param sort_key: column to sort results by.
        :param sort_dir: direction to sort. "asc" or "desc".
        :param filters: Filters to apply.
        :returns: a list of :class:`Node` object.

        """
        db_nodes = cls.dbapi.get_node_list(filters=filters, limit=limit,
                                           marker=marker, sort_key=sort_key,
                                           sort_dir=sort_dir)
        return cls._from_db_object_list(context, db_nodes)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def reserve(cls, context, tag, node_id):
        """Get and reserve a node.

        To prevent other ManagerServices from manipulating the given
        Node while a Task is performed, mark it reserved by this host.

        :param cls: the :class:`Node`
        :param context: Security context.
        :param tag: A string uniquely identifying the reservation holder.
        :param node_id: A node ID or UUID.
        :raises: NodeNotFound if the node is not found.
        :returns: a :class:`Node` object.

        """
        db_node = cls.dbapi.reserve_node(tag, node_id)
        node = cls._from_db_object(context, cls(), db_node)
        return node

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def release(cls, context, tag, node_id):
        """Release the reservation on a node.

        :param context: Security context.
        :param tag: A string uniquely identifying the reservation holder.
        :param node_id: A node id or uuid.
        :raises: NodeNotFound if the node is not found.

        """
        cls.dbapi.release_node(tag, node_id)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def create(self, context=None):
        """Create a Node record in the DB.

        Column-wise updates will be made based on the result of
        self.what_changed(). If target_power_state is provided,
        it will be checked against the in-database copy of the
        node before updates are made.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Node(context)
        :raises: InvalidParameterValue if some property values are invalid.
        """
        values = self.obj_get_changes()
        self._validate_property_values(values.get('properties'))
        db_node = self.dbapi.create_node(values)
        self._from_db_object(self._context, self, db_node)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def destroy(self, context=None):
        """Delete the Node from the DB.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Node(context)
        """
        self.dbapi.destroy_node(self.uuid)
        self.obj_reset_changes()

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def save(self, context=None):
        """Save updates to this Node.

        Column-wise updates will be made based on the result of
        self.what_changed(). If target_power_state is provided,
        it will be checked against the in-database copy of the
        node before updates are made.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Node(context)
        :raises: InvalidParameterValue if some property values are invalid.
        """
        updates = self.obj_get_changes()
        self._validate_property_values(updates.get('properties'))
        if 'driver' in updates and 'driver_internal_info' not in updates:
            # Clean driver_internal_info when changes driver
            self.driver_internal_info = {}
            updates = self.obj_get_changes()
        db_node = self.dbapi.update_node(self.uuid, updates)
        self._from_db_object(self._context, self, db_node)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def refresh(self, context=None):
        """Refresh the object by re-fetching from the DB.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Node(context)
        """
        current = self.get_by_uuid(self._context, self.uuid)
        self.obj_refresh(current)
        self.obj_reset_changes()

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def touch_provisioning(self, context=None):
        """Touch the database record to mark the provisioning as alive."""
        self.dbapi.touch_node_provisioning(self.id)

    @classmethod
    def get_by_port_addresses(cls, context, addresses):
        """Get a node by associated port addresses.

        :param cls: the :class:`Node`
        :param context: Security context.
        :param addresses: A list of port addresses.
        :raises: NodeNotFound if the node is not found.
        :returns: a :class:`Node` object.
        """
        db_node = cls.dbapi.get_node_by_port_addresses(addresses)
        node = cls._from_db_object(context, cls(), db_node)
        return node
Esempio n. 23
0
class Chassis(base.IronicObject, object_base.VersionedObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: Add get() and get_by_id() and make get_by_uuid()
    #              only work with a uuid
    # Version 1.2: Add create() and destroy()
    # Version 1.3: Add list()
    VERSION = '1.3'

    dbapi = dbapi.get_instance()

    fields = {
        'id': object_fields.IntegerField(),
        'uuid': object_fields.UUIDField(nullable=True),
        'extra': object_fields.FlexibleDictField(nullable=True),
        'description': object_fields.StringField(nullable=True),
    }

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get(cls, context, chassis_id):
        """Find a chassis based on its id or uuid and return a Chassis object.

        :param chassis_id: the id *or* uuid of a chassis.
        :returns: a :class:`Chassis` object.
        """
        if strutils.is_int_like(chassis_id):
            return cls.get_by_id(context, chassis_id)
        elif uuidutils.is_uuid_like(chassis_id):
            return cls.get_by_uuid(context, chassis_id)
        else:
            raise exception.InvalidIdentity(identity=chassis_id)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_id(cls, context, chassis_id):
        """Find a chassis based on its integer ID and return a Chassis object.

        :param cls: the :class:`Chassis`
        :param context: Security context
        :param chassis_id: the ID of a chassis.
        :returns: a :class:`Chassis` object.
        """
        db_chassis = cls.dbapi.get_chassis_by_id(chassis_id)
        chassis = cls._from_db_object(context, cls(), db_chassis)
        return chassis

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_uuid(cls, context, uuid):
        """Find a chassis based on UUID and return a :class:`Chassis` object.

        :param cls: the :class:`Chassis`
        :param context: Security context
        :param uuid: the UUID of a chassis.
        :returns: a :class:`Chassis` object.
        """
        db_chassis = cls.dbapi.get_chassis_by_uuid(uuid)
        chassis = cls._from_db_object(context, cls(), db_chassis)
        return chassis

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def list(cls, context, limit=None, marker=None,
             sort_key=None, sort_dir=None):
        """Return a list of Chassis objects.

        :param cls: the :class:`Chassis`
        :param context: Security context.
        :param limit: maximum number of resources to return in a single result.
        :param marker: pagination marker for large data sets.
        :param sort_key: column to sort results by.
        :param sort_dir: direction to sort. "asc" or "desc".
        :returns: a list of :class:`Chassis` object.

        """
        db_chassis = cls.dbapi.get_chassis_list(limit=limit,
                                                marker=marker,
                                                sort_key=sort_key,
                                                sort_dir=sort_dir)
        return cls._from_db_object_list(context, db_chassis)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def create(self, context=None):
        """Create a Chassis record in the DB.

        Column-wise updates will be made based on the result of
        self.what_changed(). If target_power_state is provided,
        it will be checked against the in-database copy of the
        chassis before updates are made.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Chassis(context)

        """
        values = self.obj_get_changes()
        db_chassis = self.dbapi.create_chassis(values)
        self._from_db_object(self._context, self, db_chassis)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def destroy(self, context=None):
        """Delete the Chassis from the DB.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Chassis(context)
        """
        self.dbapi.destroy_chassis(self.uuid)
        self.obj_reset_changes()

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def save(self, context=None):
        """Save updates to this Chassis.

        Updates will be made column by column based on the result
        of self.what_changed().

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Chassis(context)
        """
        updates = self.obj_get_changes()
        updated_chassis = self.dbapi.update_chassis(self.uuid, updates)
        self._from_db_object(self._context, self, updated_chassis)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def refresh(self, context=None):
        """Loads and applies updates for this Chassis.

        Loads a :class:`Chassis` with the same uuid from the database and
        checks for updated attributes. Updates are applied from
        the loaded chassis column by column, if there are any updates.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Chassis(context)
        """
        current = self.get_by_uuid(self._context, uuid=self.uuid)
        self.obj_refresh(current)
Esempio n. 24
0
class Port(base.IronicObject):
    # Version 1.0: Initial version
    # Version 1.1: Add get() and get_by_id() and get_by_address() and
    #              make get_by_uuid() only work with a uuid
    # Version 1.2: Add create() and destroy()
    # Version 1.3: Add list()
    # Version 1.4: Add list_by_node_id()
    VERSION = '1.4'

    dbapi = dbapi.get_instance()

    fields = {
        'id': object_fields.IntegerField(),
        'uuid': object_fields.UUIDField(nullable=True),
        'node_id': object_fields.IntegerField(nullable=True),
        'address': object_fields.MACAddressField(nullable=True),
        'extra': object_fields.FlexibleDictField(nullable=True),
    }

    @staticmethod
    def _from_db_object(port, db_port):
        """Converts a database entity to a formal object."""
        for field in port.fields:
            port[field] = db_port[field]

        port.obj_reset_changes()
        return port

    @staticmethod
    def _from_db_object_list(db_objects, cls, context):
        """Converts a list of database entities to a list of formal objects."""
        return [Port._from_db_object(cls(context), obj) for obj in db_objects]

    @base.remotable_classmethod
    def get(cls, context, port_id):
        """Find a port based on its id or uuid and return a Port object.

        :param port_id: the id *or* uuid of a port.
        :returns: a :class:`Port` object.
        """
        if strutils.is_int_like(port_id):
            return cls.get_by_id(context, port_id)
        elif uuidutils.is_uuid_like(port_id):
            return cls.get_by_uuid(context, port_id)
        elif utils.is_valid_mac(port_id):
            return cls.get_by_address(context, port_id)
        else:
            raise exception.InvalidIdentity(identity=port_id)

    @base.remotable_classmethod
    def get_by_id(cls, context, port_id):
        """Find a port based on its integer id and return a Port object.

        :param port_id: the id of a port.
        :returns: a :class:`Port` object.
        """
        db_port = cls.dbapi.get_port_by_id(port_id)
        port = Port._from_db_object(cls(context), db_port)
        return port

    @base.remotable_classmethod
    def get_by_uuid(cls, context, uuid):
        """Find a port based on uuid and return a :class:`Port` object.

        :param uuid: the uuid of a port.
        :param context: Security context
        :returns: a :class:`Port` object.
        """
        db_port = cls.dbapi.get_port_by_uuid(uuid)
        port = Port._from_db_object(cls(context), db_port)
        return port

    @base.remotable_classmethod
    def get_by_address(cls, context, address):
        """Find a port based on address and return a :class:`Port` object.

        :param address: the address of a port.
        :param context: Security context
        :returns: a :class:`Port` object.
        """
        db_port = cls.dbapi.get_port_by_address(address)
        port = Port._from_db_object(cls(context), db_port)
        return port

    @base.remotable_classmethod
    def list(cls, context, limit=None, marker=None,
             sort_key=None, sort_dir=None):
        """Return a list of Port objects.

        :param context: Security context.
        :param limit: maximum number of resources to return in a single result.
        :param marker: pagination marker for large data sets.
        :param sort_key: column to sort results by.
        :param sort_dir: direction to sort. "asc" or "desc".
        :returns: a list of :class:`Port` object.

        """
        db_ports = cls.dbapi.get_port_list(limit=limit,
                                           marker=marker,
                                           sort_key=sort_key,
                                           sort_dir=sort_dir)
        return Port._from_db_object_list(db_ports, cls, context)

    @base.remotable_classmethod
    def list_by_node_id(cls, context, node_id, limit=None, marker=None,
                        sort_key=None, sort_dir=None):
        """Return a list of Port objects associated with a given node ID.

        :param context: Security context.
        :param node_id: the ID of the node.
        :param limit: maximum number of resources to return in a single result.
        :param marker: pagination marker for large data sets.
        :param sort_key: column to sort results by.
        :param sort_dir: direction to sort. "asc" or "desc".
        :returns: a list of :class:`Port` object.

        """
        db_ports = cls.dbapi.get_ports_by_node_id(node_id, limit=limit,
                                                  marker=marker,
                                                  sort_key=sort_key,
                                                  sort_dir=sort_dir)
        return Port._from_db_object_list(db_ports, cls, context)

    @base.remotable
    def create(self, context=None):
        """Create a Port record in the DB.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Port(context)

        """
        values = self.obj_get_changes()
        db_port = self.dbapi.create_port(values)
        self._from_db_object(self, db_port)

    @base.remotable
    def destroy(self, context=None):
        """Delete the Port from the DB.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Port(context)
        """
        self.dbapi.destroy_port(self.uuid)
        self.obj_reset_changes()

    @base.remotable
    def save(self, context=None):
        """Save updates to this Port.

        Updates will be made column by column based on the result
        of self.what_changed().

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Port(context)
        """
        updates = self.obj_get_changes()
        self.dbapi.update_port(self.uuid, updates)

        self.obj_reset_changes()

    @base.remotable
    def refresh(self, context=None):
        """Loads updates for this Port.

        Loads a port with the same uuid from the database and
        checks for updated attributes. Updates are applied from
        the loaded port column by column, if there are any updates.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Port(context)
        """
        current = self.__class__.get_by_uuid(self._context, uuid=self.uuid)
        for field in self.fields:
            if (hasattr(self, base.get_attrname(field)) and
                    self[field] != current[field]):
                self[field] = current[field]
Esempio n. 25
0
class Portgroup(base.IronicObject, object_base.VersionedObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: Add internal_info field
    # Version 1.2: Add standalone_ports_supported field
    # Version 1.3: Add mode and properties fields
    # Version 1.4: Migrate/copy extra['vif_port_id'] to
    #              internal_info['tenant_vif_port_id'] (not an explicit db
    #              change)
    VERSION = '1.4'

    dbapi = dbapi.get_instance()

    fields = {
        'id': object_fields.IntegerField(),
        'uuid': object_fields.UUIDField(nullable=True),
        'name': object_fields.StringField(nullable=True),
        'node_id': object_fields.IntegerField(nullable=True),
        'address': object_fields.MACAddressField(nullable=True),
        'extra': object_fields.FlexibleDictField(nullable=True),
        'internal_info': object_fields.FlexibleDictField(nullable=True),
        'standalone_ports_supported': object_fields.BooleanField(),
        'mode': object_fields.StringField(nullable=True),
        'properties': object_fields.FlexibleDictField(nullable=True),
    }

    def _convert_to_version(self, target_version,
                            remove_unavailable_fields=True):
        """Convert to the target version.

        Convert the object to the target version. The target version may be
        the same, older, or newer than the version of the object. This is
        used for DB interactions as well as for serialization/deserialization.

        Version 1.4: if extra['vif_port_id'] is specified (non-null) and
            internal_info['tenant_vif_port_id'] is not specified, copy the
            .extra value to internal_info. There is nothing to do here when
            downgrading to an older version.

        :param target_version: the desired version of the object
        :param remove_unavailable_fields: True to remove fields that are
            unavailable in the target version; set this to True when
            (de)serializing. False to set the unavailable fields to appropriate
            values; set this to False for DB interactions.
        """
        target_version = versionutils.convert_version_to_tuple(target_version)
        if target_version >= (1, 4):
            if self.obj_attr_is_set('extra'):
                vif = self.extra.get('vif_port_id')
                if vif:
                    internal_info = (self.internal_info
                                     if self.obj_attr_is_set('internal_info')
                                     else {})
                    if 'tenant_vif_port_id' not in internal_info:
                        internal_info['tenant_vif_port_id'] = vif
                        self.internal_info = internal_info

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get(cls, context, portgroup_ident):
        """Find a portgroup based on its id, uuid, name or address.

        :param portgroup_ident: The id, uuid, name or address of a portgroup.
        :param context: Security context
        :returns: A :class:`Portgroup` object.
        :raises: InvalidIdentity

        """
        if strutils.is_int_like(portgroup_ident):
            return cls.get_by_id(context, portgroup_ident)
        elif uuidutils.is_uuid_like(portgroup_ident):
            return cls.get_by_uuid(context, portgroup_ident)
        elif netutils.is_valid_mac(portgroup_ident):
            return cls.get_by_address(context, portgroup_ident)
        elif utils.is_valid_logical_name(portgroup_ident):
            return cls.get_by_name(context, portgroup_ident)
        else:
            raise exception.InvalidIdentity(identity=portgroup_ident)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_id(cls, context, portgroup_id):
        """Find a portgroup by its integer ID and return a Portgroup object.

        :param cls: the :class:`Portgroup`
        :param context: Security context
        :param portgroup_id: The ID of a portgroup.
        :returns: A :class:`Portgroup` object.
        :raises: PortgroupNotFound

        """
        db_portgroup = cls.dbapi.get_portgroup_by_id(portgroup_id)
        portgroup = cls._from_db_object(context, cls(), db_portgroup)
        return portgroup

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_uuid(cls, context, uuid):
        """Find a portgroup by UUID and return a :class:`Portgroup` object.

        :param cls: the :class:`Portgroup`
        :param context: Security context
        :param uuid: The UUID of a portgroup.
        :returns: A :class:`Portgroup` object.
        :raises: PortgroupNotFound

        """
        db_portgroup = cls.dbapi.get_portgroup_by_uuid(uuid)
        portgroup = cls._from_db_object(context, cls(), db_portgroup)
        return portgroup

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_address(cls, context, address):
        """Find portgroup by address and return a :class:`Portgroup` object.

        :param cls: the :class:`Portgroup`
        :param context: Security context
        :param address: The MAC address of a portgroup.
        :returns: A :class:`Portgroup` object.
        :raises: PortgroupNotFound

        """
        db_portgroup = cls.dbapi.get_portgroup_by_address(address)
        portgroup = cls._from_db_object(context, cls(), db_portgroup)
        return portgroup

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_name(cls, context, name):
        """Find portgroup based on name and return a :class:`Portgroup` object.

        :param cls: the :class:`Portgroup`
        :param context: Security context
        :param name: The name of a portgroup.
        :returns: A :class:`Portgroup` object.
        :raises: PortgroupNotFound

        """
        db_portgroup = cls.dbapi.get_portgroup_by_name(name)
        portgroup = cls._from_db_object(context, cls(), db_portgroup)
        return portgroup

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def list(cls, context, limit=None, marker=None,
             sort_key=None, sort_dir=None):
        """Return a list of Portgroup objects.

        :param cls: the :class:`Portgroup`
        :param context: Security context.
        :param limit: Maximum number of resources to return in a single result.
        :param marker: Pagination marker for large data sets.
        :param sort_key: Column to sort results by.
        :param sort_dir: Direction to sort. "asc" or "desc".
        :returns: A list of :class:`Portgroup` object.
        :raises: InvalidParameterValue

        """
        db_portgroups = cls.dbapi.get_portgroup_list(limit=limit,
                                                     marker=marker,
                                                     sort_key=sort_key,
                                                     sort_dir=sort_dir)
        return cls._from_db_object_list(context, db_portgroups)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def list_by_node_id(cls, context, node_id, limit=None, marker=None,
                        sort_key=None, sort_dir=None):
        """Return a list of Portgroup objects associated with a given node ID.

        :param cls: the :class:`Portgroup`
        :param context: Security context.
        :param node_id: The ID of the node.
        :param limit: Maximum number of resources to return in a single result.
        :param marker: Pagination marker for large data sets.
        :param sort_key: Column to sort results by.
        :param sort_dir: Direction to sort. "asc" or "desc".
        :returns: A list of :class:`Portgroup` object.
        :raises: InvalidParameterValue

        """
        db_portgroups = cls.dbapi.get_portgroups_by_node_id(node_id,
                                                            limit=limit,
                                                            marker=marker,
                                                            sort_key=sort_key,
                                                            sort_dir=sort_dir)
        return cls._from_db_object_list(context, db_portgroups)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def create(self, context=None):
        """Create a Portgroup record in the DB.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Portgroup(context)
        :raises: DuplicateName, MACAlreadyExists, PortgroupAlreadyExists

        """
        values = self.do_version_changes_for_db()
        db_portgroup = self.dbapi.create_portgroup(values)
        self._from_db_object(self._context, self, db_portgroup)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def destroy(self, context=None):
        """Delete the Portgroup from the DB.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Portgroup(context)
        :raises: PortgroupNotEmpty, PortgroupNotFound

        """
        self.dbapi.destroy_portgroup(self.uuid)
        self.obj_reset_changes()

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def save(self, context=None):
        """Save updates to this Portgroup.

        Updates will be made column by column based on the result
        of self.what_changed().

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Portgroup(context)
        :raises: PortgroupNotFound, DuplicateName, MACAlreadyExists

        """
        updates = self.do_version_changes_for_db()
        updated_portgroup = self.dbapi.update_portgroup(self.uuid, updates)
        self._from_db_object(self._context, self, updated_portgroup)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def refresh(self, context=None):
        """Loads updates for this Portgroup.

        Loads a portgroup with the same uuid from the database and
        checks for updated attributes. Updates are applied from
        the loaded portgroup column by column, if there are any updates.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Portgroup(context)
        :raises: PortgroupNotFound

        """
        current = self.get_by_uuid(self._context, uuid=self.uuid)
        self.obj_refresh(current)
        self.obj_reset_changes()
Esempio n. 26
0
class Port(base.IronicObject, object_base.VersionedObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: Add get() and get_by_id() and get_by_address() and
    #              make get_by_uuid() only work with a uuid
    # Version 1.2: Add create() and destroy()
    # Version 1.3: Add list()
    # Version 1.4: Add list_by_node_id()
    # Version 1.5: Add list_by_portgroup_id() and new fields
    #              local_link_connection, portgroup_id and pxe_enabled
    VERSION = '1.5'

    dbapi = dbapi.get_instance()

    fields = {
        'id': object_fields.IntegerField(),
        'uuid': object_fields.UUIDField(nullable=True),
        'node_id': object_fields.IntegerField(nullable=True),
        'address': object_fields.MACAddressField(nullable=True),
        'extra': object_fields.FlexibleDictField(nullable=True),
        'local_link_connection': object_fields.FlexibleDictField(
            nullable=True),
        'portgroup_id': object_fields.IntegerField(nullable=True),
        'pxe_enabled': object_fields.BooleanField()
    }

    @staticmethod
    def _from_db_object(port, db_port):
        """Converts a database entity to a formal object."""
        for field in port.fields:
            port[field] = db_port[field]

        port.obj_reset_changes()
        return port

    @staticmethod
    def _from_db_object_list(db_objects, cls, context):
        """Converts a list of database entities to a list of formal objects."""
        return [Port._from_db_object(cls(context), obj) for obj in db_objects]

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get(cls, context, port_id):
        """Find a port.

        Find a port based on its id or uuid or MAC address and return a Port
        object.

        :param port_id: the id *or* uuid *or* MAC address of a port.
        :returns: a :class:`Port` object.
        :raises: InvalidIdentity

        """
        if strutils.is_int_like(port_id):
            return cls.get_by_id(context, port_id)
        elif uuidutils.is_uuid_like(port_id):
            return cls.get_by_uuid(context, port_id)
        elif utils.is_valid_mac(port_id):
            return cls.get_by_address(context, port_id)
        else:
            raise exception.InvalidIdentity(identity=port_id)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_id(cls, context, port_id):
        """Find a port based on its integer id and return a Port object.

        :param port_id: the id of a port.
        :returns: a :class:`Port` object.
        :raises: PortNotFound

        """
        db_port = cls.dbapi.get_port_by_id(port_id)
        port = Port._from_db_object(cls(context), db_port)
        return port

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_uuid(cls, context, uuid):
        """Find a port based on uuid and return a :class:`Port` object.

        :param uuid: the uuid of a port.
        :param context: Security context
        :returns: a :class:`Port` object.
        :raises: PortNotFound

        """
        db_port = cls.dbapi.get_port_by_uuid(uuid)
        port = Port._from_db_object(cls(context), db_port)
        return port

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_address(cls, context, address):
        """Find a port based on address and return a :class:`Port` object.

        :param address: the address of a port.
        :param context: Security context
        :returns: a :class:`Port` object.
        :raises: PortNotFound

        """
        db_port = cls.dbapi.get_port_by_address(address)
        port = Port._from_db_object(cls(context), db_port)
        return port

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def list(cls, context, limit=None, marker=None,
             sort_key=None, sort_dir=None):
        """Return a list of Port objects.

        :param context: Security context.
        :param limit: maximum number of resources to return in a single result.
        :param marker: pagination marker for large data sets.
        :param sort_key: column to sort results by.
        :param sort_dir: direction to sort. "asc" or "desc".
        :returns: a list of :class:`Port` object.
        :raises: InvalidParameterValue

        """
        db_ports = cls.dbapi.get_port_list(limit=limit,
                                           marker=marker,
                                           sort_key=sort_key,
                                           sort_dir=sort_dir)
        return Port._from_db_object_list(db_ports, cls, context)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def list_by_node_id(cls, context, node_id, limit=None, marker=None,
                        sort_key=None, sort_dir=None):
        """Return a list of Port objects associated with a given node ID.

        :param context: Security context.
        :param node_id: the ID of the node.
        :param limit: maximum number of resources to return in a single result.
        :param marker: pagination marker for large data sets.
        :param sort_key: column to sort results by.
        :param sort_dir: direction to sort. "asc" or "desc".
        :returns: a list of :class:`Port` object.

        """
        db_ports = cls.dbapi.get_ports_by_node_id(node_id, limit=limit,
                                                  marker=marker,
                                                  sort_key=sort_key,
                                                  sort_dir=sort_dir)
        return Port._from_db_object_list(db_ports, cls, context)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def list_by_portgroup_id(cls, context, portgroup_id, limit=None,
                             marker=None, sort_key=None, sort_dir=None):
        """Return a list of Port objects associated with a given portgroup ID.

        :param context: Security context.
        :param portgroup_id: the ID of the portgroup.
        :param limit: maximum number of resources to return in a single result.
        :param marker: pagination marker for large data sets.
        :param sort_key: column to sort results by.
        :param sort_dir: direction to sort. "asc" or "desc".
        :returns: a list of :class:`Port` object.

        """
        db_ports = cls.dbapi.get_ports_by_portgroup_id(portgroup_id,
                                                       limit=limit,
                                                       marker=marker,
                                                       sort_key=sort_key,
                                                       sort_dir=sort_dir)
        return Port._from_db_object_list(db_ports, cls, context)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def create(self, context=None):
        """Create a Port record in the DB.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Port(context)
        :raises: MACAlreadyExists if 'address' column is not unique
        :raises: PortAlreadyExists if 'uuid' column is not unique

        """
        values = self.obj_get_changes()
        db_port = self.dbapi.create_port(values)
        self._from_db_object(self, db_port)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def destroy(self, context=None):
        """Delete the Port from the DB.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Port(context)
        :raises: PortNotFound

        """
        self.dbapi.destroy_port(self.uuid)
        self.obj_reset_changes()

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def save(self, context=None):
        """Save updates to this Port.

        Updates will be made column by column based on the result
        of self.what_changed().

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Port(context)
        :raises: PortNotFound
        :raises: MACAlreadyExists if 'address' column is not unique

        """
        updates = self.obj_get_changes()
        updated_port = self.dbapi.update_port(self.uuid, updates)
        self._from_db_object(self, updated_port)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def refresh(self, context=None):
        """Loads updates for this Port.

        Loads a port with the same uuid from the database and
        checks for updated attributes. Updates are applied from
        the loaded port column by column, if there are any updates.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Port(context)
        :raises: PortNotFound

        """
        current = self.__class__.get_by_uuid(self._context, uuid=self.uuid)
        self.obj_refresh(current)
Esempio n. 27
0
class Portgroup(base.IronicObject, object_base.VersionedObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: Add internal_info field
    # Version 1.2: Add standalone_ports_supported field
    VERSION = '1.2'

    dbapi = dbapi.get_instance()

    fields = {
        'id': object_fields.IntegerField(),
        'uuid': object_fields.UUIDField(nullable=True),
        'name': object_fields.StringField(nullable=True),
        'node_id': object_fields.IntegerField(nullable=True),
        'address': object_fields.MACAddressField(nullable=True),
        'extra': object_fields.FlexibleDictField(nullable=True),
        'internal_info': object_fields.FlexibleDictField(nullable=True),
        'standalone_ports_supported': object_fields.BooleanField(),
    }

    @staticmethod
    def _from_db_object_list(db_objects, cls, context):
        """Converts a list of database entities to a list of formal objects."""
        return [Portgroup._from_db_object(cls(context), obj) for obj in
                db_objects]

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get(cls, context, portgroup_ident):
        """Find a portgroup based on its id, uuid, name or address.

        :param portgroup_ident: The id, uuid, name or address of a portgroup.
        :param context: Security context
        :returns: A :class:`Portgroup` object.
        :raises: InvalidIdentity

        """
        if strutils.is_int_like(portgroup_ident):
            return cls.get_by_id(context, portgroup_ident)
        elif uuidutils.is_uuid_like(portgroup_ident):
            return cls.get_by_uuid(context, portgroup_ident)
        elif utils.is_valid_mac(portgroup_ident):
            return cls.get_by_address(context, portgroup_ident)
        elif utils.is_valid_logical_name(portgroup_ident):
            return cls.get_by_name(context, portgroup_ident)
        else:
            raise exception.InvalidIdentity(identity=portgroup_ident)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_id(cls, context, portgroup_id):
        """Find a portgroup based on its integer id and return a Portgroup object.

        :param portgroup id: The id of a portgroup.
        :param context: Security context
        :returns: A :class:`Portgroup` object.
        :raises: PortgroupNotFound

        """
        db_portgroup = cls.dbapi.get_portgroup_by_id(portgroup_id)
        portgroup = Portgroup._from_db_object(cls(context), db_portgroup)
        return portgroup

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_uuid(cls, context, uuid):
        """Find a portgroup based on uuid and return a :class:`Portgroup` object.

        :param uuid: The uuid of a portgroup.
        :param context: Security context
        :returns: A :class:`Portgroup` object.
        :raises: PortgroupNotFound

        """
        db_portgroup = cls.dbapi.get_portgroup_by_uuid(uuid)
        portgroup = Portgroup._from_db_object(cls(context), db_portgroup)
        return portgroup

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_address(cls, context, address):
        """Find a portgroup based on address and return a :class:`Portgroup` object.

        :param address: The MAC address of a portgroup.
        :param context: Security context
        :returns: A :class:`Portgroup` object.
        :raises: PortgroupNotFound

        """
        db_portgroup = cls.dbapi.get_portgroup_by_address(address)
        portgroup = Portgroup._from_db_object(cls(context), db_portgroup)
        return portgroup

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_name(cls, context, name):
        """Find a portgroup based on name and return a :class:`Portgroup` object.

        :param name: The name of a portgroup.
        :param context: Security context
        :returns: A :class:`Portgroup` object.
        :raises: PortgroupNotFound

        """
        db_portgroup = cls.dbapi.get_portgroup_by_name(name)
        portgroup = Portgroup._from_db_object(cls(context), db_portgroup)
        return portgroup

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def list(cls, context, limit=None, marker=None,
             sort_key=None, sort_dir=None):
        """Return a list of Portgroup objects.

        :param context: Security context.
        :param limit: Maximum number of resources to return in a single result.
        :param marker: Pagination marker for large data sets.
        :param sort_key: Column to sort results by.
        :param sort_dir: Direction to sort. "asc" or "desc".
        :returns: A list of :class:`Portgroup` object.
        :raises: InvalidParameterValue

        """
        db_portgroups = cls.dbapi.get_portgroup_list(limit=limit,
                                                     marker=marker,
                                                     sort_key=sort_key,
                                                     sort_dir=sort_dir)
        return Portgroup._from_db_object_list(db_portgroups, cls, context)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def list_by_node_id(cls, context, node_id, limit=None, marker=None,
                        sort_key=None, sort_dir=None):
        """Return a list of Portgroup objects associated with a given node ID.

        :param context: Security context.
        :param node_id: The ID of the node.
        :param limit: Maximum number of resources to return in a single result.
        :param marker: Pagination marker for large data sets.
        :param sort_key: Column to sort results by.
        :param sort_dir: Direction to sort. "asc" or "desc".
        :returns: A list of :class:`Portgroup` object.
        :raises: InvalidParameterValue

        """
        db_portgroups = cls.dbapi.get_portgroups_by_node_id(node_id,
                                                            limit=limit,
                                                            marker=marker,
                                                            sort_key=sort_key,
                                                            sort_dir=sort_dir)
        return Portgroup._from_db_object_list(db_portgroups, cls, context)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def create(self, context=None):
        """Create a Portgroup record in the DB.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Portgroup(context)
        :raises: DuplicateName, MACAlreadyExists, PortgroupAlreadyExists

        """
        values = self.obj_get_changes()
        db_portgroup = self.dbapi.create_portgroup(values)
        self._from_db_object(self, db_portgroup)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def destroy(self, context=None):
        """Delete the Portgroup from the DB.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Portgroup(context)
        :raises: PortgroupNotEmpty, PortgroupNotFound

        """
        self.dbapi.destroy_portgroup(self.uuid)
        self.obj_reset_changes()

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def save(self, context=None):
        """Save updates to this Portgroup.

        Updates will be made column by column based on the result
        of self.what_changed().

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Portgroup(context)
        :raises: PortgroupNotFound, DuplicateName, MACAlreadyExists

        """
        updates = self.obj_get_changes()
        updated_portgroup = self.dbapi.update_portgroup(self.uuid, updates)
        self._from_db_object(self, updated_portgroup)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def refresh(self, context=None):
        """Loads updates for this Portgroup.

        Loads a portgroup with the same uuid from the database and
        checks for updated attributes. Updates are applied from
        the loaded portgroup column by column, if there are any updates.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Portgroup(context)
        :raises: PortgroupNotFound

        """
        current = self.__class__.get_by_uuid(self._context, uuid=self.uuid)
        self.obj_refresh(current)
Esempio n. 28
0
class Node(base.IronicObject, object_base.VersionedObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: Added instance_info
    # Version 1.2: Add get() and get_by_id() and make get_by_uuid()
    #              only work with a uuid
    # Version 1.3: Add create() and destroy()
    # Version 1.4: Add get_by_instance_uuid()
    # Version 1.5: Add list()
    # Version 1.6: Add reserve() and release()
    # Version 1.7: Add conductor_affinity
    # Version 1.8: Add maintenance_reason
    # Version 1.9: Add driver_internal_info
    # Version 1.10: Add name and get_by_name()
    # Version 1.11: Add clean_step
    # Version 1.12: Add raid_config and target_raid_config
    # Version 1.13: Add touch_provisioning()
    # Version 1.14: Add _validate_property_values() and make create()
    #               and save() validate the input of property values.
    # Version 1.15: Add get_by_port_addresses
    # Version 1.16: Add network_interface field
    # Version 1.17: Add resource_class field
    # Version 1.18: Add default setting for network_interface
    # Version 1.19: Add fields: boot_interface, console_interface,
    #               deploy_interface, inspect_interface, management_interface,
    #               power_interface, raid_interface, vendor_interface
    # Version 1.20: Type of network_interface changed to just nullable string
    # Version 1.21: Add storage_interface field
    # Version 1.22: Add rescue_interface field
    # Version 1.23: Add traits field
    # Version 1.24: Add bios_interface field
    # Version 1.25: Add fault field
    # Version 1.26: Add deploy_step field
    # Version 1.27: Add conductor_group field
    # Version 1.28: Add automated_clean field
    # Version 1.29: Add protected and protected_reason fields
    VERSION = '1.29'

    dbapi = db_api.get_instance()

    fields = {
        'id': object_fields.IntegerField(),
        'uuid': object_fields.UUIDField(nullable=True),
        'name': object_fields.StringField(nullable=True),
        'chassis_id': object_fields.IntegerField(nullable=True),
        'instance_uuid': object_fields.UUIDField(nullable=True),
        'driver': object_fields.StringField(nullable=True),
        'driver_info': object_fields.FlexibleDictField(nullable=True),
        'driver_internal_info': object_fields.FlexibleDictField(nullable=True),

        # A clean step dictionary, indicating the current clean step
        # being executed, or None, indicating cleaning is not in progress
        # or has not yet started.
        'clean_step': object_fields.FlexibleDictField(nullable=True),

        # A deploy step dictionary, indicating the current step
        # being executed, or None, indicating deployment is not in progress
        # or has not yet started.
        'deploy_step': object_fields.FlexibleDictField(nullable=True),
        'raid_config': object_fields.FlexibleDictField(nullable=True),
        'target_raid_config': object_fields.FlexibleDictField(nullable=True),
        'instance_info': object_fields.FlexibleDictField(nullable=True),
        'properties': object_fields.FlexibleDictField(nullable=True),
        'reservation': object_fields.StringField(nullable=True),
        # a reference to the id of the conductor service, not its hostname,
        # that has most recently performed some action which could require
        # local state to be maintained (eg, built a PXE config)
        'conductor_affinity': object_fields.IntegerField(nullable=True),
        'conductor_group': object_fields.StringField(nullable=True),

        # One of states.POWER_ON|POWER_OFF|NOSTATE|ERROR
        'power_state': object_fields.StringField(nullable=True),

        # Set to one of states.POWER_ON|POWER_OFF when a power operation
        # starts, and set to NOSTATE when the operation finishes
        # (successfully or unsuccessfully).
        'target_power_state': object_fields.StringField(nullable=True),
        'provision_state': object_fields.StringField(nullable=True),
        'provision_updated_at': object_fields.DateTimeField(nullable=True),
        'target_provision_state': object_fields.StringField(nullable=True),
        'maintenance': object_fields.BooleanField(),
        'maintenance_reason': object_fields.StringField(nullable=True),
        'fault': object_fields.StringField(nullable=True),
        'console_enabled': object_fields.BooleanField(),

        # Any error from the most recent (last) asynchronous transaction
        # that started but failed to finish.
        'last_error': object_fields.StringField(nullable=True),

        # Used by nova to relate the node to a flavor
        'resource_class': object_fields.StringField(nullable=True),
        'inspection_finished_at': object_fields.DateTimeField(nullable=True),
        'inspection_started_at': object_fields.DateTimeField(nullable=True),
        'extra': object_fields.FlexibleDictField(nullable=True),
        'automated_clean': objects.fields.BooleanField(nullable=True),
        'protected': objects.fields.BooleanField(),
        'protected_reason': object_fields.StringField(nullable=True),
        'bios_interface': object_fields.StringField(nullable=True),
        'boot_interface': object_fields.StringField(nullable=True),
        'console_interface': object_fields.StringField(nullable=True),
        'deploy_interface': object_fields.StringField(nullable=True),
        'inspect_interface': object_fields.StringField(nullable=True),
        'management_interface': object_fields.StringField(nullable=True),
        'network_interface': object_fields.StringField(nullable=True),
        'power_interface': object_fields.StringField(nullable=True),
        'raid_interface': object_fields.StringField(nullable=True),
        'rescue_interface': object_fields.StringField(nullable=True),
        'storage_interface': object_fields.StringField(nullable=True),
        'vendor_interface': object_fields.StringField(nullable=True),
        'traits': object_fields.ObjectField('TraitList', nullable=True),
    }

    def as_dict(self, secure=False):
        d = super(Node, self).as_dict()
        if secure:
            d['driver_info'] = strutils.mask_dict_password(
                d.get('driver_info', {}), "******")
            d['instance_info'] = strutils.mask_dict_password(
                d.get('instance_info', {}), "******")
        return d

    def _validate_property_values(self, properties):
        """Check if the input of local_gb, cpus and memory_mb are valid.

        :param properties: a dict contains the node's information.
        """
        if not properties:
            return
        invalid_msgs_list = []
        for param in REQUIRED_INT_PROPERTIES:
            value = properties.get(param)
            if value is None:
                continue
            try:
                int_value = int(value)
                if int_value < 0:
                    raise ValueError("Value must be non-negative")
            except (ValueError, TypeError):
                msg = (('%(param)s=%(value)s') % {
                    'param': param,
                    'value': value
                })
                invalid_msgs_list.append(msg)

        if invalid_msgs_list:
            msg = (_('The following properties for node %(node)s '
                     'should be non-negative integers, '
                     'but provided values are: %(msgs)s') % {
                         'node': self.uuid,
                         'msgs': ', '.join(invalid_msgs_list)
                     })
            raise exception.InvalidParameterValue(msg)

    def _set_from_db_object(self, context, db_object, fields=None):
        fields = set(fields or self.fields) - {'traits'}
        super(Node, self)._set_from_db_object(context, db_object, fields)
        self.traits = object_base.obj_make_list(context,
                                                objects.TraitList(context),
                                                objects.Trait,
                                                db_object['traits'])
        self.traits.obj_reset_changes()

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get(cls, context, node_id):
        """Find a node based on its id or uuid and return a Node object.

        :param context: Security context
        :param node_id: the id *or* uuid of a node.
        :returns: a :class:`Node` object.
        """
        if strutils.is_int_like(node_id):
            return cls.get_by_id(context, node_id)
        elif uuidutils.is_uuid_like(node_id):
            return cls.get_by_uuid(context, node_id)
        else:
            raise exception.InvalidIdentity(identity=node_id)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_id(cls, context, node_id):
        """Find a node based on its integer ID and return a Node object.

        :param cls: the :class:`Node`
        :param context: Security context
        :param node_id: the ID of a node.
        :returns: a :class:`Node` object.
        """
        db_node = cls.dbapi.get_node_by_id(node_id)
        node = cls._from_db_object(context, cls(), db_node)
        return node

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_uuid(cls, context, uuid):
        """Find a node based on UUID and return a Node object.

        :param cls: the :class:`Node`
        :param context: Security context
        :param uuid: the UUID of a node.
        :returns: a :class:`Node` object.
        """
        db_node = cls.dbapi.get_node_by_uuid(uuid)
        node = cls._from_db_object(context, cls(), db_node)
        return node

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_name(cls, context, name):
        """Find a node based on name and return a Node object.

        :param cls: the :class:`Node`
        :param context: Security context
        :param name: the logical name of a node.
        :returns: a :class:`Node` object.
        """
        db_node = cls.dbapi.get_node_by_name(name)
        node = cls._from_db_object(context, cls(), db_node)
        return node

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_instance_uuid(cls, context, instance_uuid):
        """Find a node based on the instance UUID and return a Node object.

        :param cls: the :class:`Node`
        :param context: Security context
        :param uuid: the UUID of the instance.
        :returns: a :class:`Node` object.
        """
        db_node = cls.dbapi.get_node_by_instance(instance_uuid)
        node = cls._from_db_object(context, cls(), db_node)
        return node

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def list(cls,
             context,
             limit=None,
             marker=None,
             sort_key=None,
             sort_dir=None,
             filters=None):
        """Return a list of Node objects.

        :param cls: the :class:`Node`
        :param context: Security context.
        :param limit: maximum number of resources to return in a single result.
        :param marker: pagination marker for large data sets.
        :param sort_key: column to sort results by.
        :param sort_dir: direction to sort. "asc" or "desc".
        :param filters: Filters to apply.
        :returns: a list of :class:`Node` object.

        """
        db_nodes = cls.dbapi.get_node_list(filters=filters,
                                           limit=limit,
                                           marker=marker,
                                           sort_key=sort_key,
                                           sort_dir=sort_dir)
        return cls._from_db_object_list(context, db_nodes)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def reserve(cls, context, tag, node_id):
        """Get and reserve a node.

        To prevent other ManagerServices from manipulating the given
        Node while a Task is performed, mark it reserved by this host.

        :param cls: the :class:`Node`
        :param context: Security context.
        :param tag: A string uniquely identifying the reservation holder.
        :param node_id: A node ID or UUID.
        :raises: NodeNotFound if the node is not found.
        :returns: a :class:`Node` object.

        """
        db_node = cls.dbapi.reserve_node(tag, node_id)
        node = cls._from_db_object(context, cls(), db_node)
        return node

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def release(cls, context, tag, node_id):
        """Release the reservation on a node.

        :param context: Security context.
        :param tag: A string uniquely identifying the reservation holder.
        :param node_id: A node id or uuid.
        :raises: NodeNotFound if the node is not found.

        """
        cls.dbapi.release_node(tag, node_id)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def create(self, context=None):
        """Create a Node record in the DB.

        Column-wise updates will be made based on the result of
        self.what_changed(). If target_power_state is provided,
        it will be checked against the in-database copy of the
        node before updates are made.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Node(context)
        :raises: InvalidParameterValue if some property values are invalid.
        """
        values = self.do_version_changes_for_db()
        self._validate_property_values(values.get('properties'))
        self._validate_and_remove_traits(values)
        self._validate_and_format_conductor_group(values)
        db_node = self.dbapi.create_node(values)
        self._from_db_object(self._context, self, db_node)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def destroy(self, context=None):
        """Delete the Node from the DB.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Node(context)
        """
        self.dbapi.destroy_node(self.uuid)
        self.obj_reset_changes()

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def save(self, context=None):
        """Save updates to this Node.

        Column-wise updates will be made based on the result of
        self.what_changed(). If target_power_state is provided,
        it will be checked against the in-database copy of the
        node before updates are made.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Node(context)
        :raises: InvalidParameterValue if some property values are invalid.
        """
        updates = self.do_version_changes_for_db()
        self._validate_property_values(updates.get('properties'))
        if 'driver' in updates and 'driver_internal_info' not in updates:
            # Clean driver_internal_info when changes driver
            self.driver_internal_info = {}
            updates = self.do_version_changes_for_db()
        self._validate_and_remove_traits(updates)
        self._validate_and_format_conductor_group(updates)
        db_node = self.dbapi.update_node(self.uuid, updates)
        self._from_db_object(self._context, self, db_node)

    @staticmethod
    def _validate_and_remove_traits(fields):
        """Validate traits in fields for create or update, remove if present.

        :param fields: a dict of Node fields for create or update.
        :raises: BadRequest if fields contains a traits that are not None.
        """
        if 'traits' in fields:
            # NOTE(mgoddard): Traits should be updated via the node
            # object's traits field, which is itself an object. We shouldn't
            # get here with changes to traits, as this should be handled by the
            # API. When services are pinned to Pike, we can get here with
            # traits set to None in updates, due to changes made to the object
            # in _convert_to_version.
            if fields['traits']:
                # NOTE(mgoddard): We shouldn't get here as this should be
                # handled by the API.
                raise exception.BadRequest()
            fields.pop('traits')

    def _validate_and_format_conductor_group(self, fields):
        """Validate conductor_group and format it for our use.

        Currently formatting is just lowercasing it.

        :param fields: a dict of Node fields for create or update.
        :raises: InvalidConductorGroup if validation fails.
        """
        if 'conductor_group' in fields:
            utils.validate_conductor_group(fields['conductor_group'])
            fields['conductor_group'] = fields['conductor_group'].lower()

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def refresh(self, context=None):
        """Refresh the object by re-fetching from the DB.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Node(context)
        """
        current = self.get_by_uuid(self._context, self.uuid)
        self.obj_refresh(current)
        self.obj_reset_changes()

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def touch_provisioning(self, context=None):
        """Touch the database record to mark the provisioning as alive."""
        self.dbapi.touch_node_provisioning(self.id)

    @classmethod
    def get_by_port_addresses(cls, context, addresses):
        """Get a node by associated port addresses.

        :param cls: the :class:`Node`
        :param context: Security context.
        :param addresses: A list of port addresses.
        :raises: NodeNotFound if the node is not found.
        :returns: a :class:`Node` object.
        """
        db_node = cls.dbapi.get_node_by_port_addresses(addresses)
        node = cls._from_db_object(context, cls(), db_node)
        return node

    def _convert_fault_field(self,
                             target_version,
                             remove_unavailable_fields=True):
        fault_is_set = self.obj_attr_is_set('fault')
        if target_version >= (1, 25):
            if not fault_is_set:
                self.fault = None
        elif fault_is_set:
            if remove_unavailable_fields:
                delattr(self, 'fault')
            elif self.fault is not None:
                self.fault = None

    def _convert_deploy_step_field(self,
                                   target_version,
                                   remove_unavailable_fields=True):
        # NOTE(rloo): Typically we set the value to None. However,
        # deploy_step is a FlexibleDictField. Setting it to None
        # causes it to be set to {} under-the-hood. So I am being
        # explicit about that here.
        step_is_set = self.obj_attr_is_set('deploy_step')
        if target_version >= (1, 26):
            if not step_is_set:
                self.deploy_step = {}
        elif step_is_set:
            if remove_unavailable_fields:
                delattr(self, 'deploy_step')
            elif self.deploy_step:
                self.deploy_step = {}

    def _convert_conductor_group_field(self,
                                       target_version,
                                       remove_unavailable_fields=True):
        # NOTE(jroll): The default conductor_group is "", not None
        is_set = self.obj_attr_is_set('conductor_group')
        if target_version >= (1, 27):
            if not is_set:
                self.conductor_group = ''
        elif is_set:
            if remove_unavailable_fields:
                delattr(self, 'conductor_group')
            elif self.conductor_group:
                self.conductor_group = ''

    # NOTE (yolanda): new method created to avoid repeating code in
    # _convert_to_version, and to avoid pep8 too complex error
    def _adjust_field_to_version(self, field_name, field_default_value,
                                 target_version, major, minor,
                                 remove_unavailable_fields):
        field_is_set = self.obj_attr_is_set(field_name)
        if target_version >= (major, minor):
            # target version supports the major/minor specified
            if not field_is_set:
                # set it to its default value if it is not set
                setattr(self, field_name, field_default_value)
        elif field_is_set:
            # target version does not support the field, and it is set
            if remove_unavailable_fields:
                # (De)serialising: remove unavailable fields
                delattr(self, field_name)
            elif getattr(self, field_name) is not field_default_value:
                # DB: set unavailable field to the default value
                setattr(self, field_name, field_default_value)

    def _convert_to_version(self,
                            target_version,
                            remove_unavailable_fields=True):
        """Convert to the target version.

        Convert the object to the target version. The target version may be
        the same, older, or newer than the version of the object. This is
        used for DB interactions as well as for serialization/deserialization.

        Version 1.22: rescue_interface field was added. Its default value is
            None. For versions prior to this, it should be set to None (or
            removed).
        Version 1.23: traits field was added. Its default value is
            None. For versions prior to this, it should be set to None (or
            removed).
        Version 1.24: bios_interface field was added. Its default value is
            None. For versions prior to this, it should be set to None (or
            removed).
        Version 1.25: fault field was added. For versions prior to
            this, it should be removed.
        Version 1.26: deploy_step field was added. For versions prior to
            this, it should be removed.
        Version 1.27: conductor_group field was added. For versions prior to
            this, it should be removed.
        Version 1.28: automated_clean was added. For versions prior to this, it
            should be set to None (or removed).
        Version 1.29: protected was added. For versions prior to this, it
            should be set to False (or removed).

        :param target_version: the desired version of the object
        :param remove_unavailable_fields: True to remove fields that are
            unavailable in the target version; set this to True when
            (de)serializing. False to set the unavailable fields to appropriate
            values; set this to False for DB interactions.
        """
        target_version = versionutils.convert_version_to_tuple(target_version)

        # Convert the different fields depending on version
        fields = [('rescue_interface', 22), ('traits', 23),
                  ('bios_interface', 24), ('automated_clean', 28),
                  ('protected_reason', 29)]
        for name, minor in fields:
            self._adjust_field_to_version(name, None, target_version, 1, minor,
                                          remove_unavailable_fields)

        # NOTE(dtantsur): the default is False for protected
        self._adjust_field_to_version('protected', False, target_version, 1,
                                      29, remove_unavailable_fields)

        self._convert_fault_field(target_version, remove_unavailable_fields)
        self._convert_deploy_step_field(target_version,
                                        remove_unavailable_fields)
        self._convert_conductor_group_field(target_version,
                                            remove_unavailable_fields)
Esempio n. 29
0
class Chassis(base.IronicObject):
    # Version 1.0: Initial version
    # Version 1.1: Add get() and get_by_id() and make get_by_uuid()
    #              only work with a uuid
    # Version 1.2: Add create() and destroy()
    # Version 1.3: Add list()
    VERSION = '1.3'

    dbapi = dbapi.get_instance()

    fields = {
        'id': object_fields.IntegerField(),
        'uuid': object_fields.UUIDField(nullable=True),
        'extra': object_fields.FlexibleDictField(nullable=True),
        'description': object_fields.StringField(nullable=True),
    }

    @staticmethod
    def _from_db_object(chassis, db_chassis):
        """Converts a database entity to a formal :class:`Chassis` object.

        :param chassis: An object of :class:`Chassis`.
        :param db_chassis: A DB model of a chassis.
        :return: a :class:`Chassis` object.
        """
        for field in chassis.fields:
            chassis[field] = db_chassis[field]

        chassis.obj_reset_changes()
        return chassis

    @base.remotable_classmethod
    def get(cls, context, chassis_id):
        """Find a chassis based on its id or uuid and return a Chassis object.

        :param chassis_id: the id *or* uuid of a chassis.
        :returns: a :class:`Chassis` object.
        """
        if strutils.is_int_like(chassis_id):
            return cls.get_by_id(context, chassis_id)
        elif uuidutils.is_uuid_like(chassis_id):
            return cls.get_by_uuid(context, chassis_id)
        else:
            raise exception.InvalidIdentity(identity=chassis_id)

    @base.remotable_classmethod
    def get_by_id(cls, context, chassis_id):
        """Find a chassis based on its integer id and return a Chassis object.

        :param chassis_id: the id of a chassis.
        :returns: a :class:`Chassis` object.
        """
        db_chassis = cls.dbapi.get_chassis_by_id(chassis_id)
        chassis = Chassis._from_db_object(cls(context), db_chassis)
        return chassis

    @base.remotable_classmethod
    def get_by_uuid(cls, context, uuid):
        """Find a chassis based on uuid and return a :class:`Chassis` object.

        :param uuid: the uuid of a chassis.
        :param context: Security context
        :returns: a :class:`Chassis` object.
        """
        db_chassis = cls.dbapi.get_chassis_by_uuid(uuid)
        chassis = Chassis._from_db_object(cls(context), db_chassis)
        return chassis

    @base.remotable_classmethod
    def list(cls,
             context,
             limit=None,
             marker=None,
             sort_key=None,
             sort_dir=None):
        """Return a list of Chassis objects.

        :param context: Security context.
        :param limit: maximum number of resources to return in a single result.
        :param marker: pagination marker for large data sets.
        :param sort_key: column to sort results by.
        :param sort_dir: direction to sort. "asc" or "desc".
        :returns: a list of :class:`Chassis` object.

        """
        db_chassis = cls.dbapi.get_chassis_list(limit=limit,
                                                marker=marker,
                                                sort_key=sort_key,
                                                sort_dir=sort_dir)
        return [
            Chassis._from_db_object(cls(context), obj) for obj in db_chassis
        ]

    @base.remotable
    def create(self, context=None):
        """Create a Chassis record in the DB.

        Column-wise updates will be made based on the result of
        self.what_changed(). If target_power_state is provided,
        it will be checked against the in-database copy of the
        chassis before updates are made.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Chassis(context)

        """
        values = self.obj_get_changes()
        db_chassis = self.dbapi.create_chassis(values)
        self._from_db_object(self, db_chassis)

    @base.remotable
    def destroy(self, context=None):
        """Delete the Chassis from the DB.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Chassis(context)
        """
        self.dbapi.destroy_chassis(self.uuid)
        self.obj_reset_changes()

    @base.remotable
    def save(self, context=None):
        """Save updates to this Chassis.

        Updates will be made column by column based on the result
        of self.what_changed().

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Chassis(context)
        """
        updates = self.obj_get_changes()
        self.dbapi.update_chassis(self.uuid, updates)

        self.obj_reset_changes()

    @base.remotable
    def refresh(self, context=None):
        """Loads and applies updates for this Chassis.

        Loads a :class:`Chassis` with the same uuid from the database and
        checks for updated attributes. Updates are applied from
        the loaded chassis column by column, if there are any updates.

        :param context: Security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: Chassis(context)
        """
        current = self.__class__.get_by_uuid(self._context, uuid=self.uuid)
        for field in self.fields:
            if (hasattr(self, base.get_attrname(field))
                    and self[field] != current[field]):
                self[field] = current[field]
Esempio n. 30
0
class VolumeTarget(base.IronicObject, object_base.VersionedObjectDictCompat):
    # Version 1.0: Initial version
    VERSION = '1.0'

    dbapi = db_api.get_instance()

    fields = {
        'id': object_fields.IntegerField(),
        'uuid': object_fields.UUIDField(nullable=True),
        'node_id': object_fields.IntegerField(nullable=True),
        'volume_type': object_fields.StringField(nullable=True),
        'properties': object_fields.FlexibleDictField(nullable=True),
        'boot_index': object_fields.IntegerField(nullable=True),
        'volume_id': object_fields.StringField(nullable=True),
        'extra': object_fields.FlexibleDictField(nullable=True),
    }

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get(cls, context, ident):
        """Find a volume target based on its ID or UUID.

        :param context: security context
        :param ident: the database primary key ID *or* the UUID of a volume
                      target
        :returns: a :class:`VolumeTarget` object
        :raises: InvalidIdentity if ident is neither an integer ID nor a UUID
        :raises: VolumeTargetNotFound if no volume target with this ident
                 exists
        """
        if strutils.is_int_like(ident):
            return cls.get_by_id(context, ident)
        elif uuidutils.is_uuid_like(ident):
            return cls.get_by_uuid(context, ident)
        else:
            raise exception.InvalidIdentity(identity=ident)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_id(cls, context, db_id):
        """Find a volume target based on its database ID.

        :param cls: the :class:`VolumeTarget`
        :param context: security context
        :param db_id: the database primary key (integer) ID of a volume target
        :returns: a :class:`VolumeTarget` object
        :raises: VolumeTargetNotFound if no volume target with this ID exists
        """
        db_target = cls.dbapi.get_volume_target_by_id(db_id)
        target = cls._from_db_object(context, cls(), db_target)
        return target

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def get_by_uuid(cls, context, uuid):
        """Find a volume target based on its UUID.

        :param cls: the :class:`VolumeTarget`
        :param context: security context
        :param uuid: the UUID of a volume target
        :returns: a :class:`VolumeTarget` object
        :raises: VolumeTargetNotFound if no volume target with this UUID exists
        """
        db_target = cls.dbapi.get_volume_target_by_uuid(uuid)
        target = cls._from_db_object(context, cls(), db_target)
        return target

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def list(cls,
             context,
             limit=None,
             marker=None,
             sort_key=None,
             sort_dir=None):
        """Return a list of VolumeTarget objects.

        :param context: security context
        :param limit: maximum number of resources to return in a single result
        :param marker: pagination marker for large data sets
        :param sort_key: column to sort results by
        :param sort_dir: direction to sort. "asc" or "desc".
        :returns: a list of :class:`VolumeTarget` objects
        :raises: InvalidParameterValue if sort_key does not exist
        """
        db_targets = cls.dbapi.get_volume_target_list(limit=limit,
                                                      marker=marker,
                                                      sort_key=sort_key,
                                                      sort_dir=sort_dir)
        return cls._from_db_object_list(context, db_targets)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def list_by_node_id(cls,
                        context,
                        node_id,
                        limit=None,
                        marker=None,
                        sort_key=None,
                        sort_dir=None):
        """Return a list of VolumeTarget objects related to a given node ID.

        :param context: security context
        :param node_id: the integer ID of the node
        :param limit: maximum number of resources to return in a single result
        :param marker: pagination marker for large data sets
        :param sort_key: column to sort results by
        :param sort_dir: direction to sort. "asc" or "desc".
        :returns: a list of :class:`VolumeTarget` objects
        :raises: InvalidParameterValue if sort_key does not exist
        """
        db_targets = cls.dbapi.get_volume_targets_by_node_id(node_id,
                                                             limit=limit,
                                                             marker=marker,
                                                             sort_key=sort_key,
                                                             sort_dir=sort_dir)
        return cls._from_db_object_list(context, db_targets)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable_classmethod
    @classmethod
    def list_by_volume_id(cls,
                          context,
                          volume_id,
                          limit=None,
                          marker=None,
                          sort_key=None,
                          sort_dir=None):
        """Return a list of VolumeTarget objects related to a given volume ID.

        :param context: security context
        :param volume_id: the UUID of the volume
        :param limit: maximum number of volume targets to return in a
                      single result
        :param marker: pagination marker for large data sets
        :param sort_key: column to sort results by
        :param sort_dir: direction to sort. "asc" or "desc".
        :returns: a list of :class:`VolumeTarget` objects
        :raises: InvalidParameterValue if sort_key does not exist
        """
        db_targets = cls.dbapi.get_volume_targets_by_volume_id(
            volume_id,
            limit=limit,
            marker=marker,
            sort_key=sort_key,
            sort_dir=sort_dir)
        return cls._from_db_object_list(context, db_targets)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def create(self, context=None):
        """Create a VolumeTarget record in the DB.

        :param context: security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: VolumeTarget(context).
        :raises: VolumeTargetBootIndexAlreadyExists if a volume target already
                 exists with the same node ID and boot index
        :raises: VolumeTargetAlreadyExists if a volume target with the same
                 UUID exists
        """
        values = self.do_version_changes_for_db()
        db_target = self.dbapi.create_volume_target(values)
        self._from_db_object(self._context, self, db_target)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def destroy(self, context=None):
        """Delete the VolumeTarget from the DB.

        :param context: security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: VolumeTarget(context).
        :raises: VolumeTargetNotFound if the volume target cannot be found
        """
        self.dbapi.destroy_volume_target(self.uuid)
        self.obj_reset_changes()

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def save(self, context=None):
        """Save updates to this VolumeTarget.

        Updates will be made column by column based on the result
        of self.do_version_changes_for_db().

        :param context: security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: VolumeTarget(context).
        :raises: InvalidParameterValue if the UUID is being changed
        :raises: VolumeTargetBootIndexAlreadyExists if a volume target already
                 exists with the same node ID and boot index values
        :raises: VolumeTargetNotFound if the volume target cannot be found
        """
        updates = self.do_version_changes_for_db()
        updated_target = self.dbapi.update_volume_target(self.uuid, updates)
        self._from_db_object(self._context, self, updated_target)

    # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
    # methods can be used in the future to replace current explicit RPC calls.
    # Implications of calling new remote procedures should be thought through.
    # @object_base.remotable
    def refresh(self, context=None):
        """Loads updates for this VolumeTarget.

        Load a volume target with the same UUID from the database
        and check for updated attributes. If there are any updates,
        they are applied from the loaded volume target, column by column.

        :param context: security context. NOTE: This should only
                        be used internally by the indirection_api.
                        Unfortunately, RPC requires context as the first
                        argument, even though we don't use it.
                        A context should be set when instantiating the
                        object, e.g.: VolumeTarget(context).
        :raises: VolumeTargetNotFound if the volume target cannot be found
        """
        current = self.get_by_uuid(self._context, uuid=self.uuid)
        self.obj_refresh(current)
        self.obj_reset_changes()