class VirtualInterface(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version VERSION = '1.0' fields = { 'id': fields.IntegerField(), 'address': fields.StringField(nullable=True), 'network_id': fields.IntegerField(), 'instance_uuid': fields.UUIDField(), 'uuid': fields.UUIDField(), } @staticmethod def _from_db_object(context, vif, db_vif): for field in vif.fields: vif[field] = db_vif[field] vif._context = context vif.obj_reset_changes() return vif @base.remotable_classmethod def get_by_id(cls, context, vif_id): db_vif = db.virtual_interface_get(context, vif_id) if db_vif: return cls._from_db_object(context, cls(), db_vif) @base.remotable_classmethod def get_by_uuid(cls, context, vif_uuid): db_vif = db.virtual_interface_get_by_uuid(context, vif_uuid) if db_vif: return cls._from_db_object(context, cls(), db_vif) @base.remotable_classmethod def get_by_address(cls, context, address): db_vif = db.virtual_interface_get_by_address(context, address) if db_vif: return cls._from_db_object(context, cls(), db_vif) @base.remotable_classmethod def get_by_instance_and_network(cls, context, instance_uuid, network_id): db_vif = db.virtual_interface_get_by_instance_and_network( context, instance_uuid, network_id) if db_vif: return cls._from_db_object(context, cls(), db_vif) @base.remotable def create(self): if self.obj_attr_is_set('id'): raise exception.ObjectActionError(action='create', reason='already created') updates = self.obj_get_changes() db_vif = db.virtual_interface_create(self._context, updates) self._from_db_object(self._context, self, db_vif) @base.remotable_classmethod def delete_by_instance_uuid(cls, context, instance_uuid): db.virtual_interface_delete_by_instance(context, instance_uuid)
class InstanceFault(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version # Version 1.1: String attributes updated to support unicode # Version 1.2: Added create() VERSION = '1.2' fields = { 'id': fields.IntegerField(), 'instance_uuid': fields.UUIDField(), 'code': fields.IntegerField(), 'message': fields.StringField(nullable=True), 'details': fields.StringField(nullable=True), 'host': fields.StringField(nullable=True), } @staticmethod def _from_db_object(context, fault, db_fault): # NOTE(danms): These are identical right now for key in fault.fields: fault[key] = db_fault[key] fault._context = context fault.obj_reset_changes() return fault @base.remotable_classmethod def get_latest_for_instance(cls, context, instance_uuid): db_faults = db.instance_fault_get_by_instance_uuids( context, [instance_uuid]) if instance_uuid in db_faults and db_faults[instance_uuid]: return cls._from_db_object(context, cls(), db_faults[instance_uuid][0]) @base.remotable def create(self): if self.obj_attr_is_set('id'): raise exception.ObjectActionError(action='create', reason='already created') values = { 'instance_uuid': self.instance_uuid, 'code': self.code, 'message': self.message, 'details': self.details, 'host': self.host, } db_fault = db.instance_fault_create(self._context, values) self._from_db_object(self._context, self, db_fault) self.obj_reset_changes() # Cells should only try sending a message over to patron-cells # if cells is enabled and we're not the API cell. Otherwise, # if the API cell is calling this, we could end up with # infinite recursion. if cells_opts.get_cell_type() == 'compute': try: cells_rpcapi.CellsAPI().instance_fault_create_at_top( self._context, db_fault) except Exception: LOG.exception(_LE("Failed to notify cells of instance fault"))
class NetworkRequest(obj_base.NovaObject, obj_base.NovaObjectDictCompat): # Version 1.0: Initial version # Version 1.1: Added pci_request_id VERSION = '1.1' fields = { 'network_id': fields.StringField(nullable=True), 'address': fields.IPAddressField(nullable=True), 'port_id': fields.UUIDField(nullable=True), 'pci_request_id': fields.UUIDField(nullable=True), } def obj_load_attr(self, attr): setattr(self, attr, None) def to_tuple(self): address = str(self.address) if self.address is not None else None if utils.is_neutron(): return self.network_id, address, self.port_id, self.pci_request_id else: return self.network_id, address @classmethod def from_tuple(cls, net_tuple): if len(net_tuple) == 4: network_id, address, port_id, pci_request_id = net_tuple return cls(network_id=network_id, address=address, port_id=port_id, pci_request_id=pci_request_id) elif len(net_tuple) == 3: # NOTE(alex_xu): This is only for compatible with icehouse , and # should be removed in the next cycle. network_id, address, port_id = net_tuple return cls(network_id=network_id, address=address, port_id=port_id) else: network_id, address = net_tuple return cls(network_id=network_id, address=address)
class BandwidthUsage(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version # Version 1.1: Add use_slave to get_by_instance_uuid_and_mac # Version 1.2: Add update_cells to create VERSION = '1.2' fields = { 'instance_uuid': fields.UUIDField(), 'mac': fields.StringField(), 'start_period': fields.DateTimeField(), 'last_refreshed': fields.DateTimeField(), 'bw_in': fields.IntegerField(), 'bw_out': fields.IntegerField(), 'last_ctr_in': fields.IntegerField(), 'last_ctr_out': fields.IntegerField() } @staticmethod def _from_db_object(context, bw_usage, db_bw_usage): for field in bw_usage.fields: bw_usage[field] = db_bw_usage[field] bw_usage._context = context bw_usage.obj_reset_changes() return bw_usage @base.serialize_args @base.remotable_classmethod def get_by_instance_uuid_and_mac(cls, context, instance_uuid, mac, start_period=None, use_slave=False): db_bw_usage = db.bw_usage_get(context, uuid=instance_uuid, start_period=start_period, mac=mac, use_slave=use_slave) if db_bw_usage: return cls._from_db_object(context, cls(), db_bw_usage) @base.serialize_args @base.remotable def create(self, uuid, mac, bw_in, bw_out, last_ctr_in, last_ctr_out, start_period=None, last_refreshed=None, update_cells=True): db_bw_usage = db.bw_usage_update( self._context, uuid, mac, start_period, bw_in, bw_out, last_ctr_in, last_ctr_out, last_refreshed=last_refreshed, update_cells=update_cells) self._from_db_object(self._context, self, db_bw_usage)
class EC2VolumeMapping(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version VERSION = '1.0' fields = { 'id': fields.IntegerField(), 'uuid': fields.UUIDField(), } @staticmethod def _from_db_object(context, vmap, db_vmap): for field in vmap.fields: vmap[field] = db_vmap[field] vmap._context = context vmap.obj_reset_changes() return vmap @base.remotable def create(self): if self.obj_attr_is_set('id'): raise exception.ObjectActionError(action='create', reason='already created') db_vmap = db.ec2_volume_create(self._context, self.uuid) self._from_db_object(self._context, self, db_vmap) @base.remotable_classmethod def get_by_uuid(cls, context, volume_uuid): db_vmap = db.ec2_volume_get_by_uuid(context, volume_uuid) if db_vmap: return cls._from_db_object(context, cls(context), db_vmap) @base.remotable_classmethod def get_by_id(cls, context, ec2_id): db_vmap = db.ec2_volume_get_by_id(context, ec2_id) if db_vmap: return cls._from_db_object(context, cls(context), db_vmap)
class InstanceExternalEvent(obj_base.NovaObject, obj_base.NovaObjectDictCompat): # Version 1.0: Initial version # Supports network-changed and vif-plugged VERSION = '1.0' fields = { 'instance_uuid': fields.UUIDField(), 'name': fields.StringField(), 'status': fields.StringField(), 'tag': fields.StringField(nullable=True), 'data': fields.DictOfStringsField(), } @staticmethod def make_key(name, tag=None): if tag is not None: return '%s-%s' % (name, tag) else: return name @property def key(self): return self.make_key(self.name, self.tag)
class Network(obj_base.NovaPersistentObject, obj_base.NovaObject, obj_base.NovaObjectDictCompat): # Version 1.0: Initial version # Version 1.1: Added in_use_on_host() # Version 1.2: Added mtu, dhcp_server, enable_dhcp, share_address VERSION = '1.2' fields = { 'id': fields.IntegerField(), 'label': fields.StringField(), 'injected': fields.BooleanField(), 'cidr': fields.IPV4NetworkField(nullable=True), 'cidr_v6': fields.IPV6NetworkField(nullable=True), 'multi_host': fields.BooleanField(), 'netmask': fields.IPV4AddressField(nullable=True), 'gateway': fields.IPV4AddressField(nullable=True), 'broadcast': fields.IPV4AddressField(nullable=True), 'netmask_v6': fields.IPV6AddressField(nullable=True), 'gateway_v6': fields.IPV6AddressField(nullable=True), 'bridge': fields.StringField(nullable=True), 'bridge_interface': fields.StringField(nullable=True), 'dns1': fields.IPAddressField(nullable=True), 'dns2': fields.IPAddressField(nullable=True), 'vlan': fields.IntegerField(nullable=True), 'vpn_public_address': fields.IPAddressField(nullable=True), 'vpn_public_port': fields.IntegerField(nullable=True), 'vpn_private_address': fields.IPAddressField(nullable=True), 'dhcp_start': fields.IPV4AddressField(nullable=True), 'rxtx_base': fields.IntegerField(nullable=True), 'project_id': fields.UUIDField(nullable=True), 'priority': fields.IntegerField(nullable=True), 'host': fields.StringField(nullable=True), 'uuid': fields.UUIDField(), 'mtu': fields.IntegerField(nullable=True), 'dhcp_server': fields.IPAddressField(nullable=True), 'enable_dhcp': fields.BooleanField(), 'share_address': fields.BooleanField(), } @staticmethod def _convert_legacy_ipv6_netmask(netmask): """Handle netmask_v6 possibilities from the database. Historically, this was stored as just an integral CIDR prefix, but in the future it should be stored as an actual netmask. Be tolerant of either here. """ try: prefix = int(netmask) return netaddr.IPNetwork('1::/%i' % prefix).netmask except ValueError: pass try: return netaddr.IPNetwork(netmask).netmask except netaddr.AddrFormatError: raise ValueError( _('IPv6 netmask "%s" must be a netmask ' 'or integral prefix') % netmask) def obj_make_compatible(self, primitive, target_version): target_version = utils.convert_version_to_tuple(target_version) if target_version < (1, 2): if 'mtu' in primitive: del primitive['mtu'] if 'enable_dhcp' in primitive: del primitive['enable_dhcp'] if 'dhcp_server' in primitive: del primitive['dhcp_server'] if 'share_address' in primitive: del primitive['share_address'] @staticmethod def _from_db_object(context, network, db_network): for field in network.fields: db_value = db_network[field] if field is 'netmask_v6' and db_value is not None: db_value = network._convert_legacy_ipv6_netmask(db_value) if field is 'mtu' and db_value is None: db_value = CONF.network_device_mtu if field is 'dhcp_server' and db_value is None: db_value = db_network['gateway'] if field is 'share_address' and CONF.share_dhcp_address: db_value = CONF.share_dhcp_address network[field] = db_value network._context = context network.obj_reset_changes() return network @obj_base.remotable_classmethod def get_by_id(cls, context, network_id, project_only='allow_none'): db_network = db.network_get(context, network_id, project_only=project_only) return cls._from_db_object(context, cls(), db_network) @obj_base.remotable_classmethod def get_by_uuid(cls, context, network_uuid): db_network = db.network_get_by_uuid(context, network_uuid) return cls._from_db_object(context, cls(), db_network) @obj_base.remotable_classmethod def get_by_cidr(cls, context, cidr): db_network = db.network_get_by_cidr(context, cidr) return cls._from_db_object(context, cls(), db_network) @obj_base.remotable_classmethod def associate(cls, context, project_id, network_id=None, force=False): db.network_associate(context, project_id, network_id=network_id, force=force) @obj_base.remotable_classmethod def disassociate(cls, context, network_id, host=False, project=False): db.network_disassociate(context, network_id, host, project) @obj_base.remotable_classmethod def in_use_on_host(cls, context, network_id, host): return db.network_in_use_on_host(context, network_id, host) def _get_primitive_changes(self): changes = {} for key, value in self.obj_get_changes().items(): if isinstance(value, netaddr.IPAddress): changes[key] = str(value) else: changes[key] = value return changes @obj_base.remotable def create(self): updates = self._get_primitive_changes() if 'id' in updates: raise exception.ObjectActionError(action='create', reason='already created') db_network = db.network_create_safe(self._context, updates) self._from_db_object(self._context, self, db_network) @obj_base.remotable def destroy(self): db.network_delete_safe(self._context, self.id) self.deleted = True self.obj_reset_changes(['deleted']) @obj_base.remotable def save(self): context = self._context updates = self._get_primitive_changes() if 'netmask_v6' in updates: # NOTE(danms): For some reason, historical code stores the # IPv6 netmask as just the CIDR mask length, so convert that # back here before saving for now. updates['netmask_v6'] = netaddr.IPNetwork( updates['netmask_v6']).netmask set_host = 'host' in updates if set_host: db.network_set_host(context, self.id, updates.pop('host')) if updates: db_network = db.network_update(context, self.id, updates) elif set_host: db_network = db.network_get(context, self.id) else: db_network = None if db_network is not None: self._from_db_object(context, self, db_network)
class CellMapping(base.NovaTimestampObject, base.NovaObject): # Version 1.0: Initial version VERSION = '1.0' fields = { 'id': fields.IntegerField(read_only=True), 'uuid': fields.UUIDField(), 'name': fields.StringField(nullable=True), 'transport_url': fields.StringField(), 'database_connection': fields.StringField(), } @staticmethod def _from_db_object(context, cell_mapping, db_cell_mapping): for key in cell_mapping.fields: setattr(cell_mapping, key, db_cell_mapping[key]) cell_mapping.obj_reset_changes() cell_mapping._context = context return cell_mapping @staticmethod def _get_by_uuid_from_db(context, uuid): session = db_api.get_api_session() with session.begin(): db_mapping = session.query(api_models.CellMapping).filter_by( uuid=uuid).first() if not db_mapping: raise exception.CellMappingNotFound(uuid=uuid) return db_mapping @base.remotable_classmethod def get_by_uuid(cls, context, uuid): db_mapping = cls._get_by_uuid_from_db(context, uuid) return cls._from_db_object(context, cls(), db_mapping) @staticmethod def _create_in_db(context, updates): session = db_api.get_api_session() db_mapping = api_models.CellMapping() db_mapping.update(updates) db_mapping.save(session) return db_mapping @base.remotable def create(self): db_mapping = self._create_in_db(self._context, self.obj_get_changes()) self._from_db_object(self._context, self, db_mapping) @staticmethod def _save_in_db(context, uuid, updates): session = db_api.get_api_session() with session.begin(): db_mapping = session.query( api_models.CellMapping).filter_by(uuid=uuid).first() if not db_mapping: raise exception.CellMappingNotFound(uuid=uuid) db_mapping.update(updates) session.add(db_mapping) return db_mapping @base.remotable def save(self): changes = self.obj_get_changes() db_mapping = self._save_in_db(self._context, self.uuid, changes) self._from_db_object(self._context, self, db_mapping) self.obj_reset_changes() @staticmethod def _destroy_in_db(context, uuid): session = db_api.get_api_session() with session.begin(): result = session.query(api_models.CellMapping).filter_by( uuid=uuid).delete() if not result: raise exception.CellMappingNotFound(uuid=uuid) @base.remotable def destroy(self): self._destroy_in_db(self._context, self.uuid)
class InstanceGroup(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version # Version 1.1: String attributes updated to support unicode # Version 1.2: Use list/dict helpers for policies, metadetails, members # Version 1.3: Make uuid a non-None real string # Version 1.4: Add add_members() # Version 1.5: Add get_hosts() # Version 1.6: Add get_by_name() # Version 1.7: Deprecate metadetails # Version 1.8: Add count_members_by_user() # Version 1.9: Add get_by_instance_uuid() VERSION = '1.9' fields = { 'id': fields.IntegerField(), 'user_id': fields.StringField(nullable=True), 'project_id': fields.StringField(nullable=True), 'uuid': fields.UUIDField(), 'name': fields.StringField(nullable=True), 'policies': fields.ListOfStringsField(nullable=True), 'members': fields.ListOfStringsField(nullable=True), } def obj_make_compatible(self, primitive, target_version): target_version = utils.convert_version_to_tuple(target_version) if target_version < (1, 7): # NOTE(danms): Before 1.7, we had an always-empty # metadetails property primitive['metadetails'] = {} @staticmethod def _from_db_object(context, instance_group, db_inst): """Method to help with migration to objects. Converts a database entity to a formal object. """ # Most of the field names match right now, so be quick for field in instance_group.fields: if field == 'deleted': instance_group.deleted = db_inst['deleted'] == db_inst['id'] else: instance_group[field] = db_inst[field] instance_group._context = context instance_group.obj_reset_changes() return instance_group @base.remotable_classmethod def get_by_uuid(cls, context, uuid): db_inst = db.instance_group_get(context, uuid) return cls._from_db_object(context, cls(), db_inst) @base.remotable_classmethod def get_by_name(cls, context, name): # TODO(russellb) We need to get the group by name here. There's no # db.api method for this yet. Come back and optimize this by # adding a new query by name. This is unnecessarily expensive if a # tenant has lots of groups. igs = objects.InstanceGroupList.get_by_project_id( context, context.project_id) for ig in igs: if ig.name == name: return ig raise exception.InstanceGroupNotFound(group_uuid=name) @base.remotable_classmethod def get_by_instance_uuid(cls, context, instance_uuid): db_inst = db.instance_group_get_by_instance(context, instance_uuid) return cls._from_db_object(context, cls(), db_inst) @classmethod def get_by_hint(cls, context, hint): if uuidutils.is_uuid_like(hint): return cls.get_by_uuid(context, hint) else: return cls.get_by_name(context, hint) @base.remotable def save(self): """Save updates to this instance group.""" updates = self.obj_get_changes() if not updates: return payload = dict(updates) payload['server_group_id'] = self.uuid db.instance_group_update(self._context, self.uuid, updates) db_inst = db.instance_group_get(self._context, self.uuid) self._from_db_object(self._context, self, db_inst) compute_utils.notify_about_server_group_update(self._context, "update", payload) @base.remotable def refresh(self): """Refreshes the instance group.""" current = self.__class__.get_by_uuid(self._context, self.uuid) for field in self.fields: if self.obj_attr_is_set(field) and self[field] != current[field]: self[field] = current[field] self.obj_reset_changes() @base.remotable def create(self): if self.obj_attr_is_set('id'): raise exception.ObjectActionError(action='create', reason='already created') updates = self.obj_get_changes() payload = dict(updates) updates.pop('id', None) policies = updates.pop('policies', None) members = updates.pop('members', None) db_inst = db.instance_group_create(self._context, updates, policies=policies, members=members) self._from_db_object(self._context, self, db_inst) payload['server_group_id'] = self.uuid compute_utils.notify_about_server_group_update(self._context, "create", payload) @base.remotable def destroy(self): payload = {'server_group_id': self.uuid} db.instance_group_delete(self._context, self.uuid) self.obj_reset_changes() compute_utils.notify_about_server_group_update(self._context, "delete", payload) @base.remotable_classmethod def add_members(cls, context, group_uuid, instance_uuids): payload = { 'server_group_id': group_uuid, 'instance_uuids': instance_uuids } members = db.instance_group_members_add(context, group_uuid, instance_uuids) compute_utils.notify_about_server_group_update(context, "addmember", payload) return list(members) @base.remotable def get_hosts(self, exclude=None): """Get a list of hosts for non-deleted instances in the group This method allows you to get a list of the hosts where instances in this group are currently running. There's also an option to exclude certain instance UUIDs from this calculation. """ filter_uuids = self.members if exclude: filter_uuids = set(filter_uuids) - set(exclude) filters = {'uuid': filter_uuids, 'deleted': False} instances = objects.InstanceList.get_by_filters(self._context, filters=filters) return list( set([instance.host for instance in instances if instance.host])) @base.remotable def count_members_by_user(self, user_id): """Count the number of instances in a group belonging to a user.""" filter_uuids = self.members filters = {'uuid': filter_uuids, 'user_id': user_id, 'deleted': False} instances = objects.InstanceList.get_by_filters(self._context, filters=filters) return len(instances)
class InstanceAction(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version # Version 1.1: String attributes updated to support unicode VERSION = '1.1' fields = { 'id': fields.IntegerField(), 'action': fields.StringField(nullable=True), 'instance_uuid': fields.UUIDField(nullable=True), 'request_id': fields.StringField(nullable=True), 'user_id': fields.StringField(nullable=True), 'project_id': fields.StringField(nullable=True), 'start_time': fields.DateTimeField(nullable=True), 'finish_time': fields.DateTimeField(nullable=True), 'message': fields.StringField(nullable=True), } @staticmethod def _from_db_object(context, action, db_action): for field in action.fields: action[field] = db_action[field] action._context = context action.obj_reset_changes() return action @staticmethod def pack_action_start(context, instance_uuid, action_name): values = { 'request_id': context.request_id, 'instance_uuid': instance_uuid, 'user_id': context.user_id, 'project_id': context.project_id, 'action': action_name, 'start_time': context.timestamp } return values @staticmethod def pack_action_finish(context, instance_uuid): values = { 'request_id': context.request_id, 'instance_uuid': instance_uuid, 'finish_time': timeutils.utcnow() } return values @base.remotable_classmethod def get_by_request_id(cls, context, instance_uuid, request_id): db_action = db.action_get_by_request_id(context, instance_uuid, request_id) if db_action: return cls._from_db_object(context, cls(), db_action) @base.remotable_classmethod def action_start(cls, context, instance_uuid, action_name, want_result=True): values = cls.pack_action_start(context, instance_uuid, action_name) db_action = db.action_start(context, values) if want_result: return cls._from_db_object(context, cls(), db_action) @base.remotable_classmethod def action_finish(cls, context, instance_uuid, want_result=True): values = cls.pack_action_finish(context, instance_uuid) db_action = db.action_finish(context, values) if want_result: return cls._from_db_object(context, cls(), db_action) @base.remotable def finish(self): values = self.pack_action_finish(self._context, self.instance_uuid) db_action = db.action_finish(self._context, values) self._from_db_object(self._context, self, db_action)
class FixedIP(obj_base.NovaPersistentObject, obj_base.NovaObject, obj_base.NovaObjectDictCompat): # Version 1.0: Initial version # Version 1.1: Added virtual_interface field # Version 1.2: Instance version 1.14 # Version 1.3: Instance 1.15 # Version 1.4: Added default_route field # Version 1.5: Added floating_ips field # Version 1.6: Instance 1.16 # Version 1.7: Instance 1.17 # Version 1.8: Instance 1.18 # Version 1.8: Instance 1.19 VERSION = '1.9' fields = { 'id': fields.IntegerField(), 'address': fields.IPV4AndV6AddressField(), 'network_id': fields.IntegerField(nullable=True), 'virtual_interface_id': fields.IntegerField(nullable=True), 'instance_uuid': fields.UUIDField(nullable=True), 'allocated': fields.BooleanField(), 'leased': fields.BooleanField(), 'reserved': fields.BooleanField(), 'host': fields.StringField(nullable=True), 'default_route': fields.BooleanField(), 'instance': fields.ObjectField('Instance', nullable=True), 'network': fields.ObjectField('Network', nullable=True), 'virtual_interface': fields.ObjectField('VirtualInterface', nullable=True), # NOTE(danms): This should not ever be made lazy-loadable # because it would create a bit of a loop between FixedIP # and FloatingIP 'floating_ips': fields.ObjectField('FloatingIPList'), } obj_relationships = { 'instance': [('1.0', '1.13'), ('1.2', '1.14'), ('1.3', '1.15'), ('1.6', '1.16'), ('1.7', '1.17'), ('1.8', '1.18'), ('1.9', '1.19')], 'network': [('1.0', '1.2')], 'virtual_interface': [('1.1', '1.0')], 'floating_ips': [('1.5', '1.7')], } def obj_make_compatible(self, primitive, target_version): super(FixedIP, self).obj_make_compatible(primitive, target_version) target_version = utils.convert_version_to_tuple(target_version) if target_version < (1, 4) and 'default_route' in primitive: del primitive['default_route'] @staticmethod def _from_db_object(context, fixedip, db_fixedip, expected_attrs=None): if expected_attrs is None: expected_attrs = [] for field in fixedip.fields: if field == 'default_route': # NOTE(danms): This field is only set when doing a # FixedIPList.get_by_network() because it's a relatively # special-case thing, so skip it here continue if field not in FIXED_IP_OPTIONAL_ATTRS: fixedip[field] = db_fixedip[field] # NOTE(danms): Instance could be deleted, and thus None if 'instance' in expected_attrs: fixedip.instance = objects.Instance._from_db_object( context, objects.Instance(context), db_fixedip['instance']) if db_fixedip['instance'] else None if 'network' in expected_attrs: fixedip.network = objects.Network._from_db_object( context, objects.Network(context), db_fixedip['network']) if db_fixedip['network'] else None if 'virtual_interface' in expected_attrs: db_vif = db_fixedip['virtual_interface'] vif = objects.VirtualInterface._from_db_object( context, objects.VirtualInterface(context), db_fixedip['virtual_interface']) if db_vif else None fixedip.virtual_interface = vif if 'floating_ips' in expected_attrs: fixedip.floating_ips = obj_base.obj_make_list( context, objects.FloatingIPList(context), objects.FloatingIP, db_fixedip['floating_ips']) fixedip._context = context fixedip.obj_reset_changes() return fixedip @obj_base.remotable_classmethod def get_by_id(cls, context, id, expected_attrs=None): if expected_attrs is None: expected_attrs = [] get_network = 'network' in expected_attrs db_fixedip = db.fixed_ip_get(context, id, get_network=get_network) return cls._from_db_object(context, cls(context), db_fixedip, expected_attrs) @obj_base.remotable_classmethod def get_by_address(cls, context, address, expected_attrs=None): if expected_attrs is None: expected_attrs = [] db_fixedip = db.fixed_ip_get_by_address(context, str(address), columns_to_join=expected_attrs) return cls._from_db_object(context, cls(context), db_fixedip, expected_attrs) @obj_base.remotable_classmethod def get_by_floating_address(cls, context, address): db_fixedip = db.fixed_ip_get_by_floating_address(context, str(address)) if db_fixedip is not None: return cls._from_db_object(context, cls(context), db_fixedip) @obj_base.remotable_classmethod def get_by_network_and_host(cls, context, network_id, host): db_fixedip = db.fixed_ip_get_by_network_host(context, network_id, host) return cls._from_db_object(context, cls(context), db_fixedip) @obj_base.remotable_classmethod def associate(cls, context, address, instance_uuid, network_id=None, reserved=False): db_fixedip = db.fixed_ip_associate(context, address, instance_uuid, network_id=network_id, reserved=reserved) return cls._from_db_object(context, cls(context), db_fixedip) @obj_base.remotable_classmethod def associate_pool(cls, context, network_id, instance_uuid=None, host=None): db_fixedip = db.fixed_ip_associate_pool(context, network_id, instance_uuid=instance_uuid, host=host) return cls._from_db_object(context, cls(context), db_fixedip) @obj_base.remotable_classmethod def disassociate_by_address(cls, context, address): db.fixed_ip_disassociate(context, address) @obj_base.remotable_classmethod def _disassociate_all_by_timeout(cls, context, host, time_str): time = timeutils.parse_isotime(time_str) return db.fixed_ip_disassociate_all_by_timeout(context, host, time) @classmethod def disassociate_all_by_timeout(cls, context, host, time): return cls._disassociate_all_by_timeout(context, host, timeutils.isotime(time)) @obj_base.remotable def create(self): updates = self.obj_get_changes() if 'id' in updates: raise exception.ObjectActionError(action='create', reason='already created') if 'address' in updates: updates['address'] = str(updates['address']) db_fixedip = db.fixed_ip_create(self._context, updates) self._from_db_object(self._context, self, db_fixedip) @obj_base.remotable def save(self): updates = self.obj_get_changes() if 'address' in updates: raise exception.ObjectActionError(action='save', reason='address is not mutable') db.fixed_ip_update(self._context, str(self.address), updates) self.obj_reset_changes() @obj_base.remotable def disassociate(self): db.fixed_ip_disassociate(self._context, str(self.address)) self.instance_uuid = None self.instance = None self.obj_reset_changes(['instance_uuid', 'instance'])
class InstanceInfoCache(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version # Version 1.1: Converted network_info to store the model. # Version 1.2: Added new() and update_cells kwarg to save(). # Version 1.3: Added delete() # Version 1.4: String attributes updated to support unicode # Version 1.5: Actually set the deleted, created_at, updated_at, and # deleted_at attributes VERSION = '1.5' fields = { 'instance_uuid': fields.UUIDField(), 'network_info': fields.Field(fields.NetworkModel(), nullable=True), } @staticmethod def _from_db_object(context, info_cache, db_obj): for field in info_cache.fields: info_cache[field] = db_obj[field] info_cache.obj_reset_changes() info_cache._context = context return info_cache @classmethod def new(cls, context, instance_uuid): """Create an InfoCache object that can be used to create the DB entry for the first time. When save()ing this object, the info_cache_update() DB call will properly handle creating it if it doesn't exist already. """ info_cache = cls() info_cache.instance_uuid = instance_uuid info_cache.network_info = None info_cache._context = context # Leave the fields dirty return info_cache @base.remotable_classmethod def get_by_instance_uuid(cls, context, instance_uuid): db_obj = db.instance_info_cache_get(context, instance_uuid) if not db_obj: raise exception.InstanceInfoCacheNotFound( instance_uuid=instance_uuid) return cls._from_db_object(context, cls(context), db_obj) @staticmethod def _info_cache_cells_update(ctxt, info_cache): cell_type = cells_opts.get_cell_type() if cell_type != 'compute': return cells_api = cells_rpcapi.CellsAPI() try: cells_api.instance_info_cache_update_at_top(ctxt, info_cache) except Exception: LOG.exception(_LE("Failed to notify cells of instance info " "cache update")) @base.remotable def save(self, update_cells=True): if 'network_info' in self.obj_what_changed(): nw_info_json = self.fields['network_info'].to_primitive( self, 'network_info', self.network_info) rv = db.instance_info_cache_update(self._context, self.instance_uuid, {'network_info': nw_info_json}) if update_cells and rv: self._info_cache_cells_update(self._context, rv) self.obj_reset_changes() @base.remotable def delete(self): db.instance_info_cache_delete(self._context, self.instance_uuid) @base.remotable def refresh(self): current = self.__class__.get_by_instance_uuid(self._context, self.instance_uuid) current._context = None for field in self.fields: if self.obj_attr_is_set(field) and self[field] != current[field]: self[field] = current[field] self.obj_reset_changes()
class BlockDeviceMapping(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version # Version 1.1: Add instance_uuid to get_by_volume_id method # Version 1.2: Instance version 1.14 # Version 1.3: Instance version 1.15 # Version 1.4: Instance version 1.16 # Version 1.5: Instance version 1.17 # Version 1.6: Instance version 1.18 # Version 1.7: Add update_or_create method # Version 1.8: Instance version 1.19 VERSION = '1.8' fields = { 'id': fields.IntegerField(), 'instance_uuid': fields.UUIDField(), 'instance': fields.ObjectField('Instance', nullable=True), 'source_type': fields.StringField(nullable=True), 'destination_type': fields.StringField(nullable=True), 'guest_format': fields.StringField(nullable=True), 'device_type': fields.StringField(nullable=True), 'disk_bus': fields.StringField(nullable=True), 'boot_index': fields.IntegerField(nullable=True), 'device_name': fields.StringField(nullable=True), 'delete_on_termination': fields.BooleanField(default=False), 'snapshot_id': fields.StringField(nullable=True), 'volume_id': fields.StringField(nullable=True), 'volume_size': fields.IntegerField(nullable=True), 'image_id': fields.StringField(nullable=True), 'no_device': fields.BooleanField(default=False), 'connection_info': fields.StringField(nullable=True), } obj_relationships = { 'instance': [('1.0', '1.13'), ('1.2', '1.14'), ('1.3', '1.15'), ('1.4', '1.16'), ('1.5', '1.17'), ('1.6', '1.18'), ('1.8', '1.19')], } @staticmethod def _from_db_object(context, block_device_obj, db_block_device, expected_attrs=None): if expected_attrs is None: expected_attrs = [] for key in block_device_obj.fields: if key in BLOCK_DEVICE_OPTIONAL_ATTRS: continue block_device_obj[key] = db_block_device[key] if 'instance' in expected_attrs: my_inst = objects.Instance(context) my_inst._from_db_object(context, my_inst, db_block_device['instance']) block_device_obj.instance = my_inst block_device_obj._context = context block_device_obj.obj_reset_changes() return block_device_obj def _create(self, context, update_or_create=False): """Create the block device record in the database. In case the id field is set on the object, and if the instance is set raise an ObjectActionError. Resets all the changes on the object. Returns None :param context: security context used for database calls :param update_or_create: consider existing block devices for the instance based on the device name and swap, and only update the ones that match. Normally only used when creating the instance for the first time. """ cell_type = cells_opts.get_cell_type() if cell_type == 'api': raise exception.ObjectActionError( action='create', reason='BlockDeviceMapping cannot be ' 'created in the API cell.') if self.obj_attr_is_set('id'): raise exception.ObjectActionError(action='create', reason='already created') updates = self.obj_get_changes() if 'instance' in updates: raise exception.ObjectActionError(action='create', reason='instance assigned') cells_create = update_or_create or None if update_or_create: db_bdm = db.block_device_mapping_update_or_create(context, updates, legacy=False) else: db_bdm = db.block_device_mapping_create(context, updates, legacy=False) self._from_db_object(context, self, db_bdm) if cell_type == 'compute': cells_api = cells_rpcapi.CellsAPI() cells_api.bdm_update_or_create_at_top(context, self, create=cells_create) @base.remotable def create(self): self._create(self._context) @base.remotable def update_or_create(self): self._create(self._context, update_or_create=True) @base.remotable def destroy(self): if not self.obj_attr_is_set('id'): raise exception.ObjectActionError(action='destroy', reason='already destroyed') db.block_device_mapping_destroy(self._context, self.id) delattr(self, base.get_attrname('id')) cell_type = cells_opts.get_cell_type() if cell_type == 'compute': cells_api = cells_rpcapi.CellsAPI() cells_api.bdm_destroy_at_top(self._context, self.instance_uuid, device_name=self.device_name, volume_id=self.volume_id) @base.remotable def save(self): updates = self.obj_get_changes() if 'instance' in updates: raise exception.ObjectActionError(action='save', reason='instance changed') updates.pop('id', None) updated = db.block_device_mapping_update(self._context, self.id, updates, legacy=False) self._from_db_object(self._context, self, updated) cell_type = cells_opts.get_cell_type() if cell_type == 'compute': cells_api = cells_rpcapi.CellsAPI() cells_api.bdm_update_or_create_at_top(self._context, self) @base.remotable_classmethod def get_by_volume_id(cls, context, volume_id, instance_uuid=None, expected_attrs=None): if expected_attrs is None: expected_attrs = [] db_bdm = db.block_device_mapping_get_by_volume_id( context, volume_id, _expected_cols(expected_attrs)) if not db_bdm: raise exception.VolumeBDMNotFound(volume_id=volume_id) # NOTE (ndipanov): Move this to the db layer into a # get_by_instance_and_volume_id method if instance_uuid and instance_uuid != db_bdm['instance_uuid']: raise exception.InvalidVolume( reason=_("Volume does not belong to the " "requested instance.")) return cls._from_db_object(context, cls(), db_bdm, expected_attrs=expected_attrs) @property def is_root(self): return self.boot_index == 0 @property def is_volume(self): return self.destination_type == 'volume' @property def is_image(self): return self.source_type == 'image' def get_image_mapping(self): return block_device.BlockDeviceDict(self).get_image_mapping() def obj_load_attr(self, attrname): if attrname not in BLOCK_DEVICE_OPTIONAL_ATTRS: raise exception.ObjectActionError( action='obj_load_attr', reason='attribute %s not lazy-loadable' % attrname) if not self._context: raise exception.OrphanedObjectError(method='obj_load_attr', objtype=self.obj_name()) LOG.debug("Lazy-loading `%(attr)s' on %(name)s uuid %(uuid)s", { 'attr': attrname, 'name': self.obj_name(), 'uuid': self.uuid, }) self.instance = objects.Instance.get_by_uuid(self._context, self.instance_uuid) self.obj_reset_changes(fields=['instance'])
class InstanceNUMATopology(base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version # Version 1.1: Takes into account pagesize VERSION = '1.1' fields = { # NOTE(danms): The 'id' field is no longer used and should be # removed in the future when convenient 'id': obj_fields.IntegerField(), 'instance_uuid': obj_fields.UUIDField(), 'cells': obj_fields.ListOfObjectsField('InstanceNUMACell'), } obj_relationships = { 'cells': [('1.0', '1.0')], } @classmethod def obj_from_primitive(cls, primitive): if 'patron_object.name' in primitive: obj_topology = super(InstanceNUMATopology, cls).obj_from_primitive(primitive) else: # NOTE(sahid): This compatibility code needs to stay until we can # guarantee that there are no cases of the old format stored in # the database (or forever, if we can never guarantee that). obj_topology = InstanceNUMATopology._from_dict(primitive) obj_topology.id = 0 return obj_topology @classmethod def obj_from_db_obj(cls, instance_uuid, db_obj): primitive = jsonutils.loads(db_obj) obj_topology = cls.obj_from_primitive(primitive) if 'patron_object.name' not in db_obj: obj_topology.instance_uuid = instance_uuid # No benefit to store a list of changed fields obj_topology.obj_reset_changes() return obj_topology # TODO(ndipanov) Remove this method on the major version bump to 2.0 @base.remotable def create(self): self._save() # NOTE(ndipanov): We can't rename create and want to avoid version bump # as this needs to be backported to stable so this is not a @remotable # That's OK since we only call it from inside Instance.save() which is. def _save(self): values = {'numa_topology': self._to_json()} db.instance_extra_update_by_uuid(self._context, self.instance_uuid, values) self.obj_reset_changes() # NOTE(ndipanov): We want to avoid version bump # as this needs to be backported to stable so this is not a @remotable # That's OK since we only call it from inside Instance.save() which is. @classmethod def delete_by_instance_uuid(cls, context, instance_uuid): values = {'numa_topology': None} db.instance_extra_update_by_uuid(context, instance_uuid, values) @base.remotable_classmethod def get_by_instance_uuid(cls, context, instance_uuid): db_extra = db.instance_extra_get_by_instance_uuid( context, instance_uuid, columns=['numa_topology']) if not db_extra: raise exception.NumaTopologyNotFound(instance_uuid=instance_uuid) if db_extra['numa_topology'] is None: return None return cls.obj_from_db_obj(instance_uuid, db_extra['numa_topology']) def _to_json(self): return jsonutils.dumps(self.obj_to_primitive()) def __len__(self): """Defined so that boolean testing works the same as for lists.""" return len(self.cells) def _to_dict(self): # NOTE(sahid): Used as legacy, could be renamed in _legacy_to_dict_ # in the future to avoid confusing. return {'cells': [cell._to_dict() for cell in self.cells]} @classmethod def _from_dict(cls, data_dict): # NOTE(sahid): Used as legacy, could be renamed in _legacy_from_dict_ # in the future to avoid confusing. return cls(cells=[ InstanceNUMACell._from_dict(cell_dict) for cell_dict in data_dict.get('cells', []) ]) @property def cpu_pinning_requested(self): return all(cell.cpu_pinning_requested for cell in self.cells)