summaryrefslogtreecommitdiffstats
path: root/nova/objects
diff options
context:
space:
mode:
authorDan Smith <danms@us.ibm.com>2013-05-21 14:42:49 -0700
committerDan Smith <danms@us.ibm.com>2013-05-29 10:53:46 -0700
commit26dcbc51c57b3f1abc292eb01988142b94b43dc1 (patch)
tree92b3244a39878f60add5daf9f3de397e100ec8a6 /nova/objects
parentbd8612c12094dc8858b2c93520e657f575da961a (diff)
downloadnova-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.py238
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]