class Chassis(base.APIBase): """API representation of a chassis. This class enforces type checking and value constraints, and converts between the internal object model and the API representation of a chassis. """ uuid = types.uuid "The UUID of the chassis" description = wtypes.text "The description of the chassis" extra = {wtypes.text: types.MultiType(wtypes.text, six.integer_types)} "The metadata of the chassis" links = wsme.wsattr([link.Link], readonly=True) "A list containing a self link and associated chassis links" nodes = wsme.wsattr([link.Link], readonly=True) "Links to the collection of nodes contained in this chassis" def __init__(self, **kwargs): self.fields = objects.Chassis.fields.keys() for k in self.fields: setattr(self, k, kwargs.get(k)) @classmethod def convert_with_links(cls, rpc_chassis, expand=True): chassis = Chassis(**rpc_chassis.as_dict()) if not expand: chassis.unset_fields_except(['uuid', 'description']) else: chassis.nodes = [ link.Link.make_link('self', pecan.request.host_url, 'chassis', chassis.uuid + "/nodes"), link.Link.make_link('bookmark', pecan.request.host_url, 'chassis', chassis.uuid + "/nodes", bookmark=True) ] chassis.links = [ link.Link.make_link('self', pecan.request.host_url, 'chassis', chassis.uuid), link.Link.make_link('bookmark', pecan.request.host_url, 'chassis', chassis.uuid) ] return chassis
class ConsoleInfo(base.APIBase): """API representation of the console information for a node.""" console_enabled = types.boolean """The console state: if the console is enabled or not.""" console_info = { wtypes.text: types.MultiType(wtypes.text, six.integer_types) } """The console information. It typically includes the url to access the console and the type of the application that hosts the console.""" @classmethod def sample(cls): console = {'type': 'shellinabox', 'url': 'http://<hostname>:4201'} return cls(console_enabled=True, console_info=console)
class Chassis(base.APIBase): """API representation of a chassis. This class enforces type checking and value constraints, and converts between the internal object model and the API representation of a chassis. """ uuid = types.uuid "The UUID of the chassis" description = wtypes.text "The description of the chassis" extra = {wtypes.text: types.MultiType(wtypes.text, six.integer_types)} "The metadata of the chassis" links = wsme.wsattr([link.Link], readonly=True) "A list containing a self link and associated chassis links" nodes = wsme.wsattr([link.Link], readonly=True) "Links to the collection of nodes contained in this chassis" def __init__(self, **kwargs): self.fields = [] for field in objects.Chassis.fields: # Skip fields we do not expose. if not hasattr(self, field): continue self.fields.append(field) setattr(self, field, kwargs.get(field)) @classmethod def _convert_with_links(cls, chassis, url, expand=True): if not expand: chassis.unset_fields_except(['uuid', 'description']) else: chassis.nodes = [ link.Link.make_link('self', url, 'chassis', chassis.uuid + "/nodes"), link.Link.make_link('bookmark', url, 'chassis', chassis.uuid + "/nodes", bookmark=True) ] chassis.links = [ link.Link.make_link('self', url, 'chassis', chassis.uuid), link.Link.make_link('bookmark', url, 'chassis', chassis.uuid, bookmark=True) ] return chassis @classmethod def convert_with_links(cls, rpc_chassis, expand=True): chassis = Chassis(**rpc_chassis.as_dict()) return cls._convert_with_links(chassis, pecan.request.host_url, expand) @classmethod def sample(cls, expand=True): time = datetime.datetime(2000, 1, 1, 12, 0, 0) sample = cls(uuid='eaaca217-e7d8-47b4-bb41-3f99f20eed89', extra={}, description='Sample chassis', created_at=time) return cls._convert_with_links(sample, 'http://localhost:6385', expand)
class Node(base.APIBase): """API representation of a bare metal node. This class enforces type checking and value constraints, and converts between the internal object model and the API representation of a node. """ _chassis_uuid = None def _get_chassis_uuid(self): return self._chassis_uuid def _set_chassis_uuid(self, value): if value and self._chassis_uuid != value: try: chassis = objects.Chassis.get(pecan.request.context, value) self._chassis_uuid = chassis.uuid # NOTE(lucasagomes): Create the chassis_id attribute on-the-fly # to satisfy the api -> rpc object # conversion. self.chassis_id = chassis.id except exception.ChassisNotFound as e: # Change error code because 404 (NotFound) is inappropriate # response for a POST request to create a Port e.code = 400 # BadRequest raise e elif value == wtypes.Unset: self._chassis_uuid = wtypes.Unset uuid = types.uuid "Unique UUID for this node" instance_uuid = types.uuid "The UUID of the instance in nova-compute" power_state = wsme.wsattr(wtypes.text, readonly=True) "Represent the current (not transition) power state of the node" target_power_state = wsme.wsattr(wtypes.text, readonly=True) "The user modified desired power state of the node." last_error = wsme.wsattr(wtypes.text, readonly=True) "Any error from the most recent (last) asynchronous transaction that" "started but failed to finish." provision_state = wsme.wsattr(wtypes.text, readonly=True) "Represent the current (not transition) provision state of the node" reservation = wsme.wsattr(wtypes.text, readonly=True) "The hostname of the conductor that holds an exclusive lock on the node." provision_updated_at = datetime.datetime "The UTC date and time of the last provision state change" maintenance = types.boolean "Indicates whether the node is in maintenance mode." target_provision_state = wsme.wsattr(wtypes.text, readonly=True) "The user modified desired provision state of the node." console_enabled = types.boolean "Indicates whether the console access is enabled or disabled on the node." instance_info = { wtypes.text: types.MultiType(wtypes.text, six.integer_types) } "This node's instance info." driver = wsme.wsattr(wtypes.text, mandatory=True) "The driver responsible for controlling the node" driver_info = { wtypes.text: types.MultiType(wtypes.text, six.integer_types) } "This node's driver configuration" extra = {wtypes.text: types.MultiType(wtypes.text, six.integer_types)} "This node's meta data" # NOTE: properties should use a class to enforce required properties # current list: arch, cpus, disk, ram, image properties = {wtypes.text: types.MultiType(wtypes.text, six.integer_types)} "The physical characteristics of this node" chassis_uuid = wsme.wsproperty(types.uuid, _get_chassis_uuid, _set_chassis_uuid) "The UUID of the chassis this node belongs" links = wsme.wsattr([link.Link], readonly=True) "A list containing a self link and associated node links" ports = wsme.wsattr([link.Link], readonly=True) "Links to the collection of ports on this node" def __init__(self, **kwargs): self.fields = [] fields = objects.Node.fields.keys() # NOTE(lucasagomes): chassis_uuid is not part of objects.Node.fields # because it's an API-only attribute. fields.append('chassis_uuid') for k in fields: # Skip fields we do not expose. if not hasattr(self, k): continue self.fields.append(k) setattr(self, k, kwargs.get(k)) # NOTE(lucasagomes): chassis_id is an attribute created on-the-fly # by _set_chassis_uuid(), it needs to be present in the fields so # that as_dict() will contain chassis_id field when converting it # before saving it in the database. self.fields.append('chassis_id') setattr(self, 'chassis_uuid', kwargs.get('chassis_id')) @classmethod def _convert_with_links(cls, node, url, expand=True): if not expand: except_list = [ 'instance_uuid', 'maintenance', 'power_state', 'provision_state', 'uuid' ] node.unset_fields_except(except_list) else: node.ports = [ link.Link.make_link('self', url, 'nodes', node.uuid + "/ports"), link.Link.make_link('bookmark', url, 'nodes', node.uuid + "/ports", bookmark=True) ] # NOTE(lucasagomes): The numeric ID should not be exposed to # the user, it's internal only. node.chassis_id = wtypes.Unset node.links = [ link.Link.make_link('self', url, 'nodes', node.uuid), link.Link.make_link('bookmark', url, 'nodes', node.uuid, bookmark=True) ] return node @classmethod def convert_with_links(cls, rpc_node, expand=True): node = Node(**rpc_node.as_dict()) return cls._convert_with_links(node, pecan.request.host_url, expand) @classmethod def sample(cls, expand=True): time = datetime.datetime(2000, 1, 1, 12, 0, 0) node_uuid = '1be26c0b-03f2-4d2e-ae87-c02d7f33c123' instance_uuid = 'dcf1fbc5-93fc-4596-9395-b80572f6267b' sample = cls(uuid=node_uuid, instance_uuid=instance_uuid, power_state=ir_states.POWER_ON, target_power_state=ir_states.NOSTATE, last_error=None, provision_state=ir_states.ACTIVE, target_provision_state=ir_states.NOSTATE, reservation=None, driver='fake', driver_info={}, extra={}, properties={ 'memory_mb': '1024', 'local_gb': '10', 'cpus': '1' }, updated_at=time, created_at=time, provision_updated_at=time, instance_info={}) # NOTE(matty_dubs): The chassis_uuid getter() is based on the # _chassis_uuid variable: sample._chassis_uuid = 'edcad704-b2da-41d5-96d9-afd580ecfa12' return cls._convert_with_links(sample, 'http://localhost:6385', expand)
class Port(base.APIBase): """API representation of a port. This class enforces type checking and value constraints, and converts between the internal object model and the API representation of a port. """ _node_uuid = None def _get_node_uuid(self): return self._node_uuid def _set_node_uuid(self, value): if value and self._node_uuid != value: try: # FIXME(comstud): One should only allow UUID here, but # there seems to be a bug in that tests are passing an # ID. See bug #1301046 for more details. node = objects.Node.get(pecan.request.context, value) self._node_uuid = node.uuid # NOTE(lucasagomes): Create the node_id attribute on-the-fly # to satisfy the api -> rpc object # conversion. self.node_id = node.id except exception.NodeNotFound as e: # Change error code because 404 (NotFound) is inappropriate # response for a POST request to create a Port e.code = 400 # BadRequest raise e elif value == wtypes.Unset: self._node_uuid = wtypes.Unset uuid = types.uuid "Unique UUID for this port" address = wsme.wsattr(types.macaddress, mandatory=True) "MAC Address for this port" extra = {wtypes.text: types.MultiType(wtypes.text, six.integer_types)} "This port's meta data" node_uuid = wsme.wsproperty(types.uuid, _get_node_uuid, _set_node_uuid, mandatory=True) "The UUID of the node this port belongs to" links = wsme.wsattr([link.Link], readonly=True) "A list containing a self link and associated port links" def __init__(self, **kwargs): self.fields = [] fields = list(objects.Port.fields.keys()) # NOTE(lucasagomes): node_uuid is not part of objects.Port.fields # because it's an API-only attribute fields.append('node_uuid') for field in fields: # Skip fields we do not expose. if not hasattr(self, field): continue self.fields.append(field) setattr(self, field, kwargs.get(field)) # NOTE(lucasagomes): node_id is an attribute created on-the-fly # by _set_node_uuid(), it needs to be present in the fields so # that as_dict() will contain node_id field when converting it # before saving it in the database. self.fields.append('node_id') setattr(self, 'node_uuid', kwargs.get('node_id')) @classmethod def convert_with_links(cls, rpc_port, expand=True): port = Port(**rpc_port.as_dict()) if not expand: port.unset_fields_except(['uuid', 'address']) # never expose the node_id attribute port.node_id = wtypes.Unset port.links = [ link.Link.make_link('self', pecan.request.host_url, 'ports', port.uuid), link.Link.make_link('bookmark', pecan.request.host_url, 'ports', port.uuid, bookmark=True) ] return port @classmethod def sample(cls): sample = cls(uuid='27e3153e-d5bf-4b7e-b517-fb518e17f34c', address='fe:54:00:77:07:d9', extra={'foo': 'bar'}, created_at=datetime.datetime.utcnow(), updated_at=datetime.datetime.utcnow()) # NOTE(lucasagomes): node_uuid getter() method look at the # _node_uuid variable sample._node_uuid = '7ae81bb3-dec3-4289-8d6c-da80bd8001ae' return sample
def test_multitype_tostring(self): vt = types.MultiType(str, int) vts = str(vt) self.assertIn(str(str), vts) self.assertIn(str(int), vts)
def test_invalid_values(self): vt = types.MultiType(wsme.types.text, six.integer_types) self.assertRaises(ValueError, vt.validate, 0.10) self.assertRaises(ValueError, vt.validate, object())
def test_valid_values(self): vt = types.MultiType(wsme.types.text, six.integer_types) value = vt.validate("hello") self.assertEqual("hello", value) value = vt.validate(10) self.assertEqual(10, value)