# Copyright 2016 Huawei Technologies Co.,LTD. # All Rights Reserved. # # 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 oslo_db import exception as db_exc from oslo_log import log as logging from oslo_versionedobjects import base as object_base from mogan.db import api as dbapi from mogan import objects from mogan.objects import base from mogan.objects import fields as object_fields OPTIONAL_ATTRS = ['nics', 'fault'] LOG = logging.getLogger(__name__) @base.MoganObjectRegistry.register class Server(base.MoganObject, 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), 'description': object_fields.StringField(nullable=True), 'project_id': object_fields.UUIDField(nullable=True), 'user_id': object_fields.UUIDField(nullable=True), 'status': object_fields.StringField(nullable=True), 'power_state': object_fields.StringField(nullable=True), 'flavor_uuid': object_fields.UUIDField(nullable=True), 'availability_zone': object_fields.StringField(nullable=True), 'image_uuid': object_fields.UUIDField(nullable=True), 'nics': object_fields.ObjectField('ServerNics', nullable=True), 'fault': object_fields.ObjectField('ServerFault', nullable=True), 'node': object_fields.StringField(nullable=True), 'node_uuid': object_fields.UUIDField(nullable=True), 'launched_at': object_fields.DateTimeField(nullable=True), 'metadata': object_fields.FlexibleDictField(nullable=True), 'partitions': object_fields.FlexibleDictField(nullable=True), 'locked_by': object_fields.StringField(nullable=True), 'affinity_zone': object_fields.StringField(nullable=True), 'key_name': object_fields.StringField(nullable=True), 'system_metadata': object_fields.FlexibleDictField(nullable=True), } def __init__(self, context=None, **kwargs): server_nics = kwargs.pop('nics', None) if server_nics and isinstance(server_nics, list): nics_obj = objects.ServerNics(context) for nic in server_nics: nic_obj = objects.ServerNic( context, server_uuid=kwargs['uuid'], **nic) nics_obj.objects.append(nic_obj) kwargs['nics'] = nics_obj super(Server, self).__init__(context=context, **kwargs) @staticmethod def _from_db_object(server, db_server, expected_attrs=None): """Method to help with migration to objects. Converts a database entity to a formal object. :param server: An object of the Server class. :param db_server: A DB Server model of the object :return: The object of the class with the database entity added """ for field in set(server.fields) - set(OPTIONAL_ATTRS): if field == 'metadata': server[field] = db_server['extra'] else: server[field] = db_server[field] if expected_attrs is None: expected_attrs = [] if 'nics' in expected_attrs: server._load_server_nics(server._context, server.uuid) else: server.nics = None if 'fault' in expected_attrs: server._load_fault(server._context, server.uuid) server.obj_reset_changes() return server def _load_server_nics(self, context, server_uuid): self.nics = objects.ServerNics.get_by_server_uuid( context=context, server_uuid=server_uuid) @staticmethod def _from_db_object_list(db_objects, cls, context): """Converts a list of database entities to a list of formal objects.""" servers = [] for obj in db_objects: expected_attrs = ['nics', 'fault'] servers.append(Server._from_db_object(cls(context), obj, expected_attrs)) return servers def _load_fault(self, context, server_uuid): self.fault = objects.ServerFault.get_latest_for_server( context=context, server_uuid=server_uuid) def _save_nics(self, context): for nic_obj in self.nics or []: nic_obj.save(context) def as_dict(self): data = dict(self.items()) if 'nics' in data: data.update(nics=data['nics'].as_list_of_dict()) if 'fault' in data: if data['fault'] is not None: data.update(fault=data['fault'].as_fault_dict()) else: data.update(fault={}) return data @classmethod def list(cls, context, limit=None, marker=None, sort_key=None, sort_dir=None, project_only=False, filters=None): """Return a list of Server objects.""" db_servers = cls.dbapi.server_get_all(context, project_only=project_only, limit=limit, marker=marker, sort_key=sort_key, sort_dir=sort_dir, filters=filters) return Server._from_db_object_list(db_servers, cls, context) @classmethod def get(cls, context, uuid): """Find a server and return a Server object.""" expected_attrs = ['nics', 'fault'] db_server = cls.dbapi.server_get(context, uuid) server = Server._from_db_object(cls(context), db_server, expected_attrs) return server def create(self, context=None): """Create a Server record in the DB.""" values = self.obj_get_changes() metadata = values.pop('metadata', None) if metadata is not None: values['extra'] = metadata server_nics = values.pop('nics', None) if server_nics: values['nics'] = server_nics.as_list_of_dict() db_server = self.dbapi.server_create(context, values) expected_attrs = None if server_nics: expected_attrs = ['nics'] self._from_db_object(self, db_server, expected_attrs) def destroy(self, context=None): """Delete the Server from the DB.""" self.dbapi.server_destroy(context, self.uuid) self.obj_reset_changes() def save(self, context=None): """Save updates to this Server.""" updates = self.obj_get_changes() for field in list(updates): if (self.obj_attr_is_set(field) and isinstance(self.fields[field], object_fields.ObjectField) and getattr(self, field, None) is not None): try: getattr(self, '_save_%s' % field)(context) except AttributeError: LOG.exception('No save handler for %s', field, server=self) except db_exc.DBReferenceError as exp: if exp.key != 'server_uuid': raise updates.pop(field) metadata = updates.pop('metadata', None) if metadata is not None: updates['extra'] = metadata self.dbapi.server_update(context, self.uuid, updates) self.obj_reset_changes() def refresh(self, context=None): """Refresh the object by re-fetching from the DB.""" current = self.__class__.get(context, self.uuid) self.obj_refresh(current) self.obj_reset_changes()