diff options
| author | Dan Smith <danms@us.ibm.com> | 2013-05-21 14:42:49 -0700 |
|---|---|---|
| committer | Dan Smith <danms@us.ibm.com> | 2013-05-29 10:53:46 -0700 |
| commit | 26dcbc51c57b3f1abc292eb01988142b94b43dc1 (patch) | |
| tree | 92b3244a39878f60add5daf9f3de397e100ec8a6 /nova/objects | |
| parent | bd8612c12094dc8858b2c93520e657f575da961a (diff) | |
| download | nova-26dcbc51c57b3f1abc292eb01988142b94b43dc1.tar.gz nova-26dcbc51c57b3f1abc292eb01988142b94b43dc1.tar.xz nova-26dcbc51c57b3f1abc292eb01988142b94b43dc1.zip | |
Add instance object
This adds the instance object and tests.
Related to bp/unified-internal-objects
Change-Id: If8cf9aa20950b0f543228ca3dd86527c830790c7
Diffstat (limited to 'nova/objects')
| -rw-r--r-- | nova/objects/instance.py | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/nova/objects/instance.py b/nova/objects/instance.py new file mode 100644 index 000000000..836d78c08 --- /dev/null +++ b/nova/objects/instance.py @@ -0,0 +1,238 @@ +# Copyright 2013 IBM Corp. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from nova import db +from nova import notifications +from nova.objects import base +from nova.objects import utils as obj_utils +from nova import utils + +from oslo.config import cfg + + +CONF = cfg.CONF + + +class Instance(base.NovaObject): + fields = { + 'id': int, + + 'user_id': obj_utils.str_or_none, + 'project_id': obj_utils.str_or_none, + + 'image_ref': obj_utils.str_or_none, + 'kernel_id': obj_utils.str_or_none, + 'ramdisk_id': obj_utils.str_or_none, + 'hostname': obj_utils.str_or_none, + + 'launch_index': obj_utils.int_or_none, + 'key_name': obj_utils.str_or_none, + 'key_data': obj_utils.str_or_none, + + 'power_state': obj_utils.int_or_none, + 'vm_state': obj_utils.str_or_none, + 'task_state': obj_utils.str_or_none, + + 'memory_mb': obj_utils.int_or_none, + 'vcpus': obj_utils.int_or_none, + 'root_gb': obj_utils.int_or_none, + 'ephemeral_gb': obj_utils.int_or_none, + + 'host': obj_utils.str_or_none, + 'node': obj_utils.str_or_none, + + 'instance_type_id': obj_utils.int_or_none, + + 'user_data': obj_utils.str_or_none, + + 'reservation_id': obj_utils.str_or_none, + + 'scheduled_at': obj_utils.datetime_or_none, + 'launched_at': obj_utils.datetime_or_none, + 'terminated_at': obj_utils.datetime_or_none, + + 'availability_zone': obj_utils.str_or_none, + + 'display_name': obj_utils.str_or_none, + 'display_description': obj_utils.str_or_none, + + 'launched_on': obj_utils.str_or_none, + 'locked': bool, + + 'os_type': obj_utils.str_or_none, + 'architecture': obj_utils.str_or_none, + 'vm_mode': obj_utils.str_or_none, + 'uuid': obj_utils.str_or_none, + + 'root_device_name': obj_utils.str_or_none, + 'default_ephemeral_device': obj_utils.str_or_none, + 'default_swap_device': obj_utils.str_or_none, + 'config_drive': obj_utils.str_or_none, + + 'access_ip_v4': obj_utils.ip_or_none(4), + 'access_ip_v6': obj_utils.ip_or_none(6), + + 'auto_disk_config': bool, + 'progress': obj_utils.int_or_none, + + 'shutdown_terminate': bool, + 'disable_terminate': bool, + + 'cell_name': obj_utils.str_or_none, + + 'metadata': dict, + 'system_metadata': dict, + + } + + @property + def name(self): + try: + base_name = CONF.instance_name_template % self.id + except TypeError: + # Support templates like "uuid-%(uuid)s", etc. + info = {} + # NOTE(russellb): Don't use self.iteritems() here, as it will + # result in infinite recursion on the name property. + for key in self.fields: + # prevent recursion if someone specifies %(name)s + # %(name)s will not be valid. + if key == 'name': + continue + info[key] = self[key] + try: + base_name = CONF.instance_name_template % info + except KeyError: + base_name = self.uuid + return base_name + + def _attr_access_ip_v4_to_primitive(self): + if self.access_ip_v4 is not None: + return str(self.access_ip_v4) + else: + return None + + def _attr_access_ip_v6_to_primitive(self): + if self.access_ip_v6 is not None: + return str(self.access_ip_v6) + else: + return None + + _attr_scheduled_at_to_primitive = obj_utils.dt_serializer('scheduled_at') + _attr_launched_at_to_primitive = obj_utils.dt_serializer('launched_at') + _attr_terminated_at_to_primitive = obj_utils.dt_serializer('terminated_at') + + _attr_scheduled_at_from_primitive = obj_utils.dt_deserializer + _attr_launched_at_from_primitive = obj_utils.dt_deserializer + _attr_terminated_at_from_primitive = obj_utils.dt_deserializer + + @staticmethod + def _from_db_object(instance, db_inst, expected_attrs=None): + """Method to help with migration to objects. + + Converts a database entity to a formal object. + """ + if expected_attrs is None: + expected_attrs = [] + # Most of the field names match right now, so be quick + for field in instance.fields: + if field in ['metadata', 'system_metadata']: + continue + instance[field] = db_inst[field] + + if 'metadata' in expected_attrs: + instance['metadata'] = utils.metadata_to_dict(db_inst['metadata']) + if 'system_metadata' in expected_attrs: + instance['system_metadata'] = utils.metadata_to_dict( + db_inst['system_metadata']) + + instance.obj_reset_changes() + return instance + + @base.remotable_classmethod + def get_by_uuid(cls, context, uuid=None, expected_attrs=None): + if expected_attrs is None: + expected_attrs = [] + + # Construct DB-specific columns from generic expected_attrs + columns_to_join = [] + if 'metadata' in expected_attrs: + columns_to_join.append('metadata') + if 'system_metadata' in expected_attrs: + columns_to_join.append('system_metadata') + + db_inst = db.instance_get_by_uuid(context, uuid, + columns_to_join) + return Instance._from_db_object(cls(), db_inst, expected_attrs) + + @base.remotable + def save(self, context, expected_task_state=None): + """Save updates to this instance + + Column-wise updates will be made based on the result of + self.what_changed(). If expected_task_state is provided, + it will be checked against the in-database copy of the + instance before updates are made. + :param context: Security context + :param expected_task_state: Optional tuple of valid task states + for the instance to be in. + """ + updates = {} + changes = self.obj_what_changed() + for field in changes: + updates[field] = self[field] + if expected_task_state is not None: + updates['expected_task_state'] = expected_task_state + old_ref, inst_ref = db.instance_update_and_get_original(context, + self.uuid, + updates) + + expected_attrs = [] + for attr in ('metadata', 'system_metadata'): + if hasattr(self, base.get_attrname(attr)): + expected_attrs.append(attr) + Instance._from_db_object(self, inst_ref, expected_attrs) + if 'vm_state' in changes or 'task_state' in changes: + notifications.send_update(context, old_ref, inst_ref) + + self.obj_reset_changes() + + @base.remotable + def refresh(self, context): + extra = [] + for field in ['system_metadata', 'metadata']: + if hasattr(self, base.get_attrname(field)): + extra.append(field) + current = self.__class__.get_by_uuid(context, uuid=self.uuid, + expected_attrs=extra) + for field in self.fields: + if (hasattr(self, base.get_attrname(field)) and + self[field] != current[field]): + self[field] = current[field] + + def obj_load(self, attrname): + extra = [] + if attrname == 'system_metadata': + extra.append('system_metadata') + elif attrname == 'metadata': + extra.append('metadata') + + if not extra: + raise Exception('Cannot load "%s" from instance' % attrname) + + # NOTE(danms): This could be optimized to just load the bits we need + instance = self.__class__.get_by_uuid(self._context, + uuid=self.uuid, + expected_attrs=extra) + self[attrname] = instance[attrname] |
