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)
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)
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)
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)
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), }
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)
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)
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)
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)
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))
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), }
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)
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)
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))
def setUp(self): super(TestFlexibleDictField, self).setUp() self.field = fields.FlexibleDictField()
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)
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 = []
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()
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()
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)
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)
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
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)
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]
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()
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)
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)
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)
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]
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()