class ComputeNode(compute_node.ComputeNode): # This change just aim to adding pci_stats to compute_nodes obj fields = { 'id': fields.IntegerField(read_only=True), 'service_id': fields.IntegerField(), 'vcpus': fields.IntegerField(), 'memory_mb': fields.IntegerField(), 'local_gb': fields.IntegerField(), 'vcpus_used': fields.IntegerField(), 'memory_mb_used': fields.IntegerField(), 'local_gb_used': fields.IntegerField(), 'hypervisor_type': fields.StringField(), 'hypervisor_version': fields.IntegerField(), 'hypervisor_hostname': fields.StringField(nullable=True), 'free_ram_mb': fields.IntegerField(nullable=True), 'free_disk_gb': fields.IntegerField(nullable=True), 'current_workload': fields.IntegerField(nullable=True), 'running_vms': fields.IntegerField(nullable=True), 'cpu_info': fields.StringField(nullable=True), 'disk_available_least': fields.IntegerField(nullable=True), 'metrics': fields.StringField(nullable=True), 'stats': fields.DictOfNullableStringsField(nullable=True), 'host_ip': fields.IPAddressField(nullable=True), 'numa_topology': fields.StringField(nullable=True), 'pci_stats': fields.StringField(nullable=True), }
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) else: network_id, address = net_tuple return cls(network_id=network_id, address=address)
class NetworkRequest(obj_base.NovaObject): # Version 1.0: Initial version # Version 1.1: Added pci_request_id # Version 1.2: Added tag field # Version 1.3: Added arq_uuid and device_profile VERSION = '1.3' 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), 'tag': fields.StringField(nullable=True), # arq_uuid save cyborg managed port device, pass # arq info from conductor to compute 'arq_uuid': fields.UUIDField(nullable=True), # tranfer port's device_profile info from api to conductor 'device_profile': fields.StringField(nullable=True) } def obj_make_compatible(self, primitive, target_version): target_version = versionutils.convert_version_to_tuple(target_version) if target_version < (1, 2) and 'tag' in primitive: del primitive['tag'] if target_version < (1, 3) and 'arq_uuid' in primitive: del primitive['arq_uuid'] if target_version < (1, 3) and 'device_profile' in primitive: del primitive['device_profile'] 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 return (self.network_id, address, self.port_id, self.pci_request_id, self.arq_uuid, self.device_profile) @classmethod def from_tuple(cls, net_tuple): (network_id, address, port_id, pci_request_id, arq_uuid, device_profile) = net_tuple return cls(network_id=network_id, address=address, port_id=port_id, pci_request_id=pci_request_id, arq_uuid=arq_uuid, device_profile=device_profile) @property def auto_allocate(self): return self.network_id == NETWORK_ID_AUTO @property def no_allocate(self): return self.network_id == NETWORK_ID_NONE
def setUp(self): super(TestIPAddress, self).setUp() self.field = fields.IPAddressField() self.coerce_good_values = [('1.2.3.4', netaddr.IPAddress('1.2.3.4')), ('::1', netaddr.IPAddress('::1')), (netaddr.IPAddress('::1'), netaddr.IPAddress('::1'))] self.coerce_bad_values = ['1-2', 'foo'] self.to_primitive_values = [(netaddr.IPAddress('1.2.3.4'), '1.2.3.4'), (netaddr.IPAddress('::1'), '::1')] self.from_primitive_values = [('1.2.3.4', netaddr.IPAddress('1.2.3.4')), ('::1', netaddr.IPAddress('::1'))]
class NetworkRequest(obj_base.NovaObject): # Version 1.0: Initial version # Version 1.1: Added pci_request_id # Version 1.2: Added tag field VERSION = '1.2' # WRS: Added vif_model 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), 'tag': fields.StringField(nullable=True), 'vif_model': fields.StringField(nullable=True), } def obj_make_compatible(self, primitive, target_version): target_version = versionutils.convert_version_to_tuple(target_version) if target_version < (1, 2) and 'tag' in primitive: del primitive['tag'] 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, self.vif_model) else: return self.network_id, address @classmethod def from_tuple(cls, net_tuple): if len(net_tuple) == 5: network_id, address, port_id, pci_request_id, vif_model = net_tuple return cls(network_id=network_id, address=address, port_id=port_id, pci_request_id=pci_request_id, vif_model=vif_model) else: network_id, address = net_tuple return cls(network_id=network_id, address=address) @property def auto_allocate(self): return self.network_id == NETWORK_ID_AUTO @property def no_allocate(self): return self.network_id == NETWORK_ID_NONE
class RemoteAssistance(base.NovaObject, base.NovaObjectDictCompat): VERSION = '1.1' fields = { 'id': fields.IntegerField(), 'instance_id': fields.UUIDField(), 'created_at': fields.DateTimeField(nullable=True), 'updated_at': fields.DateTimeField(nullable=True), 'client_ip': fields.IPAddressField(nullable=True), 'instance_name': fields.StringField(nullable=True), 'password': fields.IntegerField(nullable=True), 'status': fields.StringField(nullable=True), } @staticmethod def _from_db_object(context, remote, db_remote): for field in remote.fields: remote[field] = db_remote[field] remote._context = context remote.obj_reset_changes() return remote @base.remotable_classmethod def create_remote_assistance(cls, context, *argws, **kwargs): db_remote = db.create_remote(context, *argws, **kwargs) @base.remotable_classmethod def get_remote_assistance(cls, context, *argws): db_remote = db.get_remote_assistance(context, *argws) if not db_remote: return db_remote try: return cls._from_db_object(context, cls(), db_remote) except Exception: return db_remote # @base.remotable_classmethod # def delete_remote_assistance(cls, context, *args): # db_remote = db.delete_remote_assistance(context, *args) # if db_remote: # return db_remote @base.remotable_classmethod def update_remote_assistance(cls, context, *args, **kwargs): db_remote = db.update_remote_assistance(context, *args, **kwargs)
class NetworkRequest(obj_base.NovaObject): # Version 1.0: Initial version # Version 1.1: Added pci_request_id VERSION = '1.0' 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 ComputeNode(base.NovaPersistentObject, base.NovaObject): # Version 1.0: Initial version # Version 1.1: Added get_by_service_id() # Version 1.2: String attributes updated to support unicode # Version 1.3: Added stats field # Version 1.4: Added host ip field # Version 1.5: Added numa_topology field # Version 1.6: Added supported_hv_specs # Version 1.7: Added host field # Version 1.8: Added get_by_host_and_nodename() # Version 1.9: Added pci_device_pools # Version 1.10: Added get_first_node_by_host_for_old_compat() # Version 1.11: PciDevicePoolList version 1.1 # Version 1.12: HVSpec version 1.1 # Version 1.13: Changed service_id field to be nullable # Version 1.14: Added cpu_allocation_ratio and ram_allocation_ratio # Version 1.15: Added uuid # Version 1.16: Added disk_allocation_ratio VERSION = '1.16' fields = { 'id': fields.IntegerField(read_only=True), 'uuid': fields.UUIDField(read_only=True), 'service_id': fields.IntegerField(nullable=True), 'host': fields.StringField(nullable=True), 'vcpus': fields.IntegerField(), 'memory_mb': fields.IntegerField(), 'local_gb': fields.IntegerField(), 'vcpus_used': fields.IntegerField(), 'memory_mb_used': fields.IntegerField(), 'local_gb_used': fields.IntegerField(), 'hypervisor_type': fields.StringField(), 'hypervisor_version': fields.IntegerField(), 'hypervisor_hostname': fields.StringField(nullable=True), 'free_ram_mb': fields.IntegerField(nullable=True), 'free_disk_gb': fields.IntegerField(nullable=True), 'current_workload': fields.IntegerField(nullable=True), 'running_vms': fields.IntegerField(nullable=True), # TODO(melwitt): cpu_info is non-nullable in the schema but we must # wait until version 2.0 of ComputeNode to change it to non-nullable 'cpu_info': fields.StringField(nullable=True), 'disk_available_least': fields.IntegerField(nullable=True), 'metrics': fields.StringField(nullable=True), 'stats': fields.DictOfNullableStringsField(nullable=True), 'host_ip': fields.IPAddressField(nullable=True), # TODO(rlrossit): because of history, numa_topology is held here as a # StringField, not a NUMATopology object. In version 2 of ComputeNode # this will be converted over to a fields.ObjectField('NUMATopology') 'numa_topology': fields.StringField(nullable=True), # NOTE(pmurray): the supported_hv_specs field maps to the # supported_instances field in the database 'supported_hv_specs': fields.ListOfObjectsField('HVSpec'), # NOTE(pmurray): the pci_device_pools field maps to the # pci_stats field in the database 'pci_device_pools': fields.ObjectField('PciDevicePoolList', nullable=True), 'cpu_allocation_ratio': fields.FloatField(), 'ram_allocation_ratio': fields.FloatField(), 'disk_allocation_ratio': fields.FloatField(), } def obj_make_compatible(self, primitive, target_version): super(ComputeNode, self).obj_make_compatible(primitive, target_version) target_version = versionutils.convert_version_to_tuple(target_version) if target_version < (1, 16): if 'disk_allocation_ratio' in primitive: del primitive['disk_allocation_ratio'] if target_version < (1, 15): if 'uuid' in primitive: del primitive['uuid'] if target_version < (1, 14): if 'ram_allocation_ratio' in primitive: del primitive['ram_allocation_ratio'] if 'cpu_allocation_ratio' in primitive: del primitive['cpu_allocation_ratio'] if target_version < (1, 13) and primitive.get('service_id') is None: # service_id is non-nullable in versions before 1.13 try: service = objects.Service.get_by_compute_host( self._context, primitive['host']) primitive['service_id'] = service.id except (exception.ComputeHostNotFound, KeyError): # NOTE(hanlind): In case anything goes wrong like service not # found or host not being set, catch and set a fake value just # to allow for older versions that demand a value to work. # Setting to -1 will, if value is later used result in a # ServiceNotFound, so should be safe. primitive['service_id'] = -1 if target_version < (1, 7) and 'host' in primitive: del primitive['host'] if target_version < (1, 5) and 'numa_topology' in primitive: del primitive['numa_topology'] if target_version < (1, 4) and 'host_ip' in primitive: del primitive['host_ip'] if target_version < (1, 3) and 'stats' in primitive: # pre 1.3 version does not have a stats field del primitive['stats'] @staticmethod def _host_from_db_object(compute, db_compute): if (('host' not in db_compute or db_compute['host'] is None) and 'service_id' in db_compute and db_compute['service_id'] is not None): # FIXME(sbauza) : Unconverted compute record, provide compatibility # This has to stay until we can be sure that any/all compute nodes # in the database have been converted to use the host field # Service field of ComputeNode could be deprecated in a next patch, # so let's use directly the Service object try: service = objects.Service.get_by_id(compute._context, db_compute['service_id']) except exception.ServiceNotFound: compute.host = None return try: compute.host = service.host except (AttributeError, exception.OrphanedObjectError): # Host can be nullable in Service compute.host = None elif 'host' in db_compute and db_compute['host'] is not None: # New-style DB having host as a field compute.host = db_compute['host'] else: # We assume it should not happen but in case, let's set it to None compute.host = None @staticmethod def _from_db_object(context, compute, db_compute): special_cases = set([ 'stats', 'supported_hv_specs', 'host', 'pci_device_pools', ]) fields = set(compute.fields) - special_cases for key in fields: value = db_compute[key] # NOTE(sbauza): Since all compute nodes don't possibly run the # latest RT code updating allocation ratios, we need to provide # a backwards compatible way of hydrating them. # As we want to care about our operators and since we don't want to # ask them to change their configuration files before upgrading, we # prefer to hardcode the default values for the ratios here until # the next release (Newton) where the opt default values will be # restored for both cpu (16.0), ram (1.5) and disk (1.0) # allocation ratios. # TODO(sbauza): Remove that in the next major version bump where # we break compatibilility with old Liberty computes if (key == 'cpu_allocation_ratio' or key == 'ram_allocation_ratio' or key == 'disk_allocation_ratio'): if value == 0.0: # Operator has not yet provided a new value for that ratio # on the compute node value = None if value is None: # ResourceTracker is not updating the value (old node) # or the compute node is updated but the default value has # not been changed value = getattr(CONF, key) if value == 0.0 and key == 'cpu_allocation_ratio': # It's not specified either on the controller value = 16.0 if value == 0.0 and key == 'ram_allocation_ratio': # It's not specified either on the controller value = 1.5 if value == 0.0 and key == 'disk_allocation_ratio': # It's not specified either on the controller value = 1.0 setattr(compute, key, value) stats = db_compute['stats'] if stats: compute.stats = jsonutils.loads(stats) sup_insts = db_compute.get('supported_instances') if sup_insts: hv_specs = jsonutils.loads(sup_insts) hv_specs = [ objects.HVSpec.from_list(hv_spec) for hv_spec in hv_specs ] compute.supported_hv_specs = hv_specs pci_stats = db_compute.get('pci_stats') if pci_stats is not None: pci_stats = pci_device_pool.from_pci_stats(pci_stats) compute.pci_device_pools = pci_stats compute._context = context # Make sure that we correctly set the host field depending on either # host column is present in the table or not compute._host_from_db_object(compute, db_compute) compute.obj_reset_changes() return compute @base.remotable_classmethod def get_by_id(cls, context, compute_id): db_compute = db.compute_node_get(context, compute_id) return cls._from_db_object(context, cls(), db_compute) # NOTE(hanlind): This is deprecated and should be removed on the next # major version bump @base.remotable_classmethod def get_by_service_id(cls, context, service_id): db_computes = db.compute_nodes_get_by_service_id(context, service_id) # NOTE(sbauza): Old version was returning an item, we need to keep this # behaviour for backwards compatibility db_compute = db_computes[0] return cls._from_db_object(context, cls(), db_compute) @base.remotable_classmethod def get_by_host_and_nodename(cls, context, host, nodename): db_compute = db.compute_node_get_by_host_and_nodename( context, host, nodename) return cls._from_db_object(context, cls(), db_compute) # TODO(pkholkin): Remove this method in the next major version bump @base.remotable_classmethod def get_first_node_by_host_for_old_compat(cls, context, host, use_slave=False): computes = ComputeNodeList.get_all_by_host(context, host, use_slave) # FIXME(sbauza): Some hypervisors (VMware, Ironic) can return multiple # nodes per host, we should return all the nodes and modify the callers # instead. # Arbitrarily returning the first node. return computes[0] @staticmethod def _convert_stats_to_db_format(updates): stats = updates.pop('stats', None) if stats is not None: updates['stats'] = jsonutils.dumps(stats) @staticmethod def _convert_host_ip_to_db_format(updates): host_ip = updates.pop('host_ip', None) if host_ip: updates['host_ip'] = str(host_ip) @staticmethod def _convert_supported_instances_to_db_format(updates): hv_specs = updates.pop('supported_hv_specs', None) if hv_specs is not None: hv_specs = [hv_spec.to_list() for hv_spec in hv_specs] updates['supported_instances'] = jsonutils.dumps(hv_specs) @staticmethod def _convert_pci_stats_to_db_format(updates): if 'pci_device_pools' in updates: pools = updates.pop('pci_device_pools') if pools is not None: pools = jsonutils.dumps(pools.obj_to_primitive()) updates['pci_stats'] = pools def update_inventory(self): """Update inventory records from legacy model values.""" inventory_list = \ objects.InventoryList.get_all_by_resource_provider_uuid( self._context, self.uuid) if not inventory_list: return False for inventory in inventory_list: if inventory.resource_class == fields.ResourceClass.VCPU: key = 'vcpus' elif inventory.resource_class == fields.ResourceClass.MEMORY_MB: key = 'memory_mb' elif inventory.resource_class == fields.ResourceClass.DISK_GB: key = 'local_gb' else: LOG.warning(_LW('Unknown inventory class %s for compute node'), inventory.resource_class) continue if key in self.obj_what_changed(): inventory.total = getattr(self, key) inventory.save() return True def _ensure_resource_provider(self): shortname = self.host.split('.')[0] rp_name = 'compute-%s-%s' % (shortname, self.uuid) rp = objects.ResourceProvider(context=self._context, uuid=self.uuid, name=rp_name) try: rp.create() except db_exc.DBDuplicateEntry: rp = objects.ResourceProvider.get_by_uuid(self._context, self.uuid) if rp.name != rp_name: # FIXME(danms): We probably need a .save() operation on RP # so that we can update this LOG.warning( _LW('Compute node %(uuid)s changed name ' 'from %(old)s to %(new)s'), { 'uuid': self.uuid, 'old': rp.name, 'new': rp_name }) return rp def create_inventory(self): """Create the initial inventory objects for this compute node. This is only ever called once, either for the first time when a compute is created, or after an upgrade where the required services have reached the required version. """ rp = self._ensure_resource_provider() cpu = objects.Inventory(context=self._context, resource_provider=rp, resource_class=fields.ResourceClass.VCPU, total=self.vcpus, reserved=0, min_unit=1, max_unit=1, step_size=1, allocation_ratio=self.cpu_allocation_ratio) cpu.create() mem = objects.Inventory(context=self._context, resource_provider=rp, resource_class=fields.ResourceClass.MEMORY_MB, total=self.memory_mb, reserved=0, min_unit=1, max_unit=1, step_size=1, allocation_ratio=self.ram_allocation_ratio) mem.create() # FIXME(danms): Eventually we want to not write this record # if the compute host is on shared storage. We'll need some # indication from it to that effect, so for now we always # write it so that we can make all the usual machinery depend # on these records instead of the legacy columns. disk = objects.Inventory(context=self._context, resource_provider=rp, resource_class=fields.ResourceClass.DISK_GB, total=self.local_gb, reserved=0, min_unit=1, max_unit=1, step_size=1, allocation_ratio=self.disk_allocation_ratio) disk.create() @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() if 'uuid' not in updates: updates['uuid'] = uuidutils.generate_uuid() self.uuid = updates['uuid'] self._convert_stats_to_db_format(updates) self._convert_host_ip_to_db_format(updates) self._convert_supported_instances_to_db_format(updates) self._convert_pci_stats_to_db_format(updates) db_compute = db.compute_node_create(self._context, updates) self._from_db_object(self._context, self, db_compute) @base.remotable def save(self, prune_stats=False): # NOTE(belliott) ignore prune_stats param, no longer relevant updates = self.obj_get_changes() updates.pop('id', None) self._convert_stats_to_db_format(updates) self._convert_host_ip_to_db_format(updates) self._convert_supported_instances_to_db_format(updates) self._convert_pci_stats_to_db_format(updates) db_compute = db.compute_node_update(self._context, self.id, updates) self._from_db_object(self._context, self, db_compute) @base.remotable def destroy(self): db.compute_node_delete(self._context, self.id) def update_from_virt_driver(self, resources): # NOTE(pmurray): the virt driver provides a dict of values that # can be copied into the compute node. The names and representation # do not exactly match. # TODO(pmurray): the resources dict should be formalized. keys = [ "vcpus", "memory_mb", "local_gb", "cpu_info", "vcpus_used", "memory_mb_used", "local_gb_used", "numa_topology", "hypervisor_type", "hypervisor_version", "hypervisor_hostname", "disk_available_least", "host_ip" ] for key in keys: if key in resources: setattr(self, key, resources[key]) # supported_instances has a different name in compute_node if 'supported_instances' in resources: si = resources['supported_instances'] self.supported_hv_specs = [objects.HVSpec.from_list(s) for s in si]
class LibvirtLiveMigrateData(LiveMigrateData): # Version 1.0: Initial version # Version 1.1: Added target_connect_addr # Version 1.2: Added 'serial_listen_ports' to allow live migration with # serial console. # Version 1.3: Added 'supported_perf_events' # Version 1.4: Added old_vol_attachment_ids # Version 1.5: Added src_supports_native_luks # Version 1.6: Added wait_for_vif_plugged # Version 1.7: Added dst_wants_file_backed_memory # Version 1.8: Added file_backed_memory_discard # Version 1.9: Inherited vifs from LiveMigrateData # Version 1.10: Added dst_numa_info, src_supports_numa_live_migration, and # dst_supports_numa_live_migration fields VERSION = '1.10' fields = { 'filename': fields.StringField(), # FIXME: image_type should be enum? 'image_type': fields.StringField(), 'block_migration': fields.BooleanField(), 'disk_over_commit': fields.BooleanField(), 'disk_available_mb': fields.IntegerField(nullable=True), 'is_shared_instance_path': fields.BooleanField(), 'is_shared_block_storage': fields.BooleanField(), 'instance_relative_path': fields.StringField(), 'graphics_listen_addr_vnc': fields.IPAddressField(nullable=True), 'graphics_listen_addr_spice': fields.IPAddressField(nullable=True), 'serial_listen_addr': fields.StringField(nullable=True), 'serial_listen_ports': fields.ListOfIntegersField(), 'bdms': fields.ListOfObjectsField('LibvirtLiveMigrateBDMInfo'), 'target_connect_addr': fields.StringField(nullable=True), 'supported_perf_events': fields.ListOfStringsField(), # TODO(lyarwood): No longer used, drop in version 2.0 'src_supports_native_luks': fields.BooleanField(), 'dst_wants_file_backed_memory': fields.BooleanField(), # TODO(lyarwood): No longer used, drop in version 2.0 'file_backed_memory_discard': fields.BooleanField(), # TODO(artom) (src|dst)_supports_numa_live_migration are only used as # flags to indicate that the compute host is new enough to perform a # NUMA-aware live migration. Remove in version 2.0. 'src_supports_numa_live_migration': fields.BooleanField(), 'dst_supports_numa_live_migration': fields.BooleanField(), 'dst_numa_info': fields.ObjectField('LibvirtLiveMigrateNUMAInfo'), } def obj_make_compatible(self, primitive, target_version): super(LibvirtLiveMigrateData, self).obj_make_compatible(primitive, target_version) target_version = versionutils.convert_version_to_tuple(target_version) if (target_version < (1, 10) and 'src_supports_numa_live_migration' in primitive): del primitive['src_supports_numa_live_migration'] if (target_version < (1, 10) and 'dst_supports_numa_live_migration' in primitive): del primitive['dst_supports_numa_live_migration'] if target_version < (1, 10) and 'dst_numa_info' in primitive: del primitive['dst_numa_info'] if target_version < (1, 9) and 'vifs' in primitive: del primitive['vifs'] if target_version < (1, 8): if 'file_backed_memory_discard' in primitive: del primitive['file_backed_memory_discard'] if target_version < (1, 7): if 'dst_wants_file_backed_memory' in primitive: del primitive['dst_wants_file_backed_memory'] if target_version < (1, 6) and 'wait_for_vif_plugged' in primitive: del primitive['wait_for_vif_plugged'] if target_version < (1, 5): if 'src_supports_native_luks' in primitive: del primitive['src_supports_native_luks'] if target_version < (1, 4): if 'old_vol_attachment_ids' in primitive: del primitive['old_vol_attachment_ids'] if target_version < (1, 3): if 'supported_perf_events' in primitive: del primitive['supported_perf_events'] if target_version < (1, 2): if 'serial_listen_ports' in primitive: del primitive['serial_listen_ports'] if target_version < (1, 1) and 'target_connect_addr' in primitive: del primitive['target_connect_addr'] def is_on_shared_storage(self): return self.is_shared_block_storage or self.is_shared_instance_path
class LibvirtLiveMigrateData(LiveMigrateData): # Version 1.0: Initial version # Version 1.1: Added target_connect_addr # Version 1.2: Added 'serial_listen_ports' to allow live migration with # serial console. # Version 1.3: Added 'supported_perf_events' # Version 1.4: Added old_vol_attachment_ids # Version 1.5: Added src_supports_native_luks # Version 1.6: Added wait_for_vif_plugged # Version 1.7: Added dst_wants_file_backed_memory # Version 1.8: Added file_backed_memory_discard # Version 1.9: Inherited vifs from LiveMigrateData VERSION = '1.9' fields = { 'filename': fields.StringField(), # FIXME: image_type should be enum? 'image_type': fields.StringField(), 'block_migration': fields.BooleanField(), 'disk_over_commit': fields.BooleanField(), 'disk_available_mb': fields.IntegerField(nullable=True), 'is_shared_instance_path': fields.BooleanField(), 'is_shared_block_storage': fields.BooleanField(), 'instance_relative_path': fields.StringField(), 'graphics_listen_addr_vnc': fields.IPAddressField(nullable=True), 'graphics_listen_addr_spice': fields.IPAddressField(nullable=True), 'serial_listen_addr': fields.StringField(nullable=True), 'serial_listen_ports': fields.ListOfIntegersField(), 'bdms': fields.ListOfObjectsField('LibvirtLiveMigrateBDMInfo'), 'target_connect_addr': fields.StringField(nullable=True), 'supported_perf_events': fields.ListOfStringsField(), 'src_supports_native_luks': fields.BooleanField(), 'dst_wants_file_backed_memory': fields.BooleanField(), # file_backed_memory_discard is ignored unless # dst_wants_file_backed_memory is set 'file_backed_memory_discard': fields.BooleanField(), } def obj_make_compatible(self, primitive, target_version): super(LibvirtLiveMigrateData, self).obj_make_compatible(primitive, target_version) target_version = versionutils.convert_version_to_tuple(target_version) if target_version < (1, 9) and 'vifs' in primitive: del primitive['vifs'] if target_version < (1, 8): if 'file_backed_memory_discard' in primitive: del primitive['file_backed_memory_discard'] if target_version < (1, 7): if 'dst_wants_file_backed_memory' in primitive: del primitive['dst_wants_file_backed_memory'] if target_version < (1, 6) and 'wait_for_vif_plugged' in primitive: del primitive['wait_for_vif_plugged'] if target_version < (1, 5): if 'src_supports_native_luks' in primitive: del primitive['src_supports_native_luks'] if target_version < (1, 4): if 'old_vol_attachment_ids' in primitive: del primitive['old_vol_attachment_ids'] if target_version < (1, 3): if 'supported_perf_events' in primitive: del primitive['supported_perf_events'] if target_version < (1, 2): if 'serial_listen_ports' in primitive: del primitive['serial_listen_ports'] if target_version < (1, 1) and 'target_connect_addr' in primitive: del primitive['target_connect_addr'] def _bdms_to_legacy(self, legacy): if not self.obj_attr_is_set('bdms'): return legacy['volume'] = {} for bdmi in self.bdms: legacy['volume'][bdmi.serial] = { 'disk_info': bdmi.as_disk_info(), 'connection_info': bdmi.connection_info } def _bdms_from_legacy(self, legacy_pre_result): self.bdms = [] volume = legacy_pre_result.get('volume', {}) for serial in volume: vol = volume[serial] bdmi = objects.LibvirtLiveMigrateBDMInfo(serial=serial) bdmi.connection_info = vol['connection_info'] bdmi.bus = vol['disk_info']['bus'] bdmi.dev = vol['disk_info']['dev'] bdmi.type = vol['disk_info']['type'] if 'format' in vol: bdmi.format = vol['disk_info']['format'] if 'boot_index' in vol: bdmi.boot_index = int(vol['disk_info']['boot_index']) self.bdms.append(bdmi) def is_on_shared_storage(self): return self.is_shared_block_storage or self.is_shared_instance_path
class LibvirtLiveMigrateData(LiveMigrateData): # Version 1.0: Initial version # Version 1.1: Added target_connect_addr # Version 1.2: Added 'serial_listen_ports' to allow live migration with # serial console. # Version 1.3: Added 'supported_perf_events' # Version 1.4: Added old_vol_attachment_ids # Version 1.5: Added src_supports_native_luks # Version 1.6: Added wait_for_vif_plugged # Version 1.7: Added dst_wants_file_backed_memory # Version 1.8: Added file_backed_memory_discard # Version 1.9: Inherited vifs from LiveMigrateData VERSION = '1.9' fields = { 'filename': fields.StringField(), # FIXME: image_type should be enum? 'image_type': fields.StringField(), 'block_migration': fields.BooleanField(), 'disk_over_commit': fields.BooleanField(), 'disk_available_mb': fields.IntegerField(nullable=True), 'is_shared_instance_path': fields.BooleanField(), 'is_shared_block_storage': fields.BooleanField(), 'instance_relative_path': fields.StringField(), 'graphics_listen_addr_vnc': fields.IPAddressField(nullable=True), 'graphics_listen_addr_spice': fields.IPAddressField(nullable=True), 'serial_listen_addr': fields.StringField(nullable=True), 'serial_listen_ports': fields.ListOfIntegersField(), 'bdms': fields.ListOfObjectsField('LibvirtLiveMigrateBDMInfo'), 'target_connect_addr': fields.StringField(nullable=True), 'supported_perf_events': fields.ListOfStringsField(), 'src_supports_native_luks': fields.BooleanField(), 'dst_wants_file_backed_memory': fields.BooleanField(), # file_backed_memory_discard is ignored unless # dst_wants_file_backed_memory is set 'file_backed_memory_discard': fields.BooleanField(), } def obj_make_compatible(self, primitive, target_version): super(LibvirtLiveMigrateData, self).obj_make_compatible( primitive, target_version) target_version = versionutils.convert_version_to_tuple(target_version) if target_version < (1, 9) and 'vifs' in primitive: del primitive['vifs'] if target_version < (1, 8): if 'file_backed_memory_discard' in primitive: del primitive['file_backed_memory_discard'] if target_version < (1, 7): if 'dst_wants_file_backed_memory' in primitive: del primitive['dst_wants_file_backed_memory'] if target_version < (1, 6) and 'wait_for_vif_plugged' in primitive: del primitive['wait_for_vif_plugged'] if target_version < (1, 5): if 'src_supports_native_luks' in primitive: del primitive['src_supports_native_luks'] if target_version < (1, 4): if 'old_vol_attachment_ids' in primitive: del primitive['old_vol_attachment_ids'] if target_version < (1, 3): if 'supported_perf_events' in primitive: del primitive['supported_perf_events'] if target_version < (1, 2): if 'serial_listen_ports' in primitive: del primitive['serial_listen_ports'] if target_version < (1, 1) and 'target_connect_addr' in primitive: del primitive['target_connect_addr'] def is_on_shared_storage(self): return self.is_shared_block_storage or self.is_shared_instance_path
class LibvirtLiveMigrateData(LiveMigrateData): # Version 1.0: Initial version # Version 1.1: Added target_connect_addr # Version 1.2: Added 'serial_listen_ports' to allow live migration with # serial console. VERSION = '1.2' fields = { 'filename': fields.StringField(), # FIXME: image_type should be enum? 'image_type': fields.StringField(), 'block_migration': fields.BooleanField(), 'disk_over_commit': fields.BooleanField(), 'disk_available_mb': fields.IntegerField(nullable=True), 'is_shared_instance_path': fields.BooleanField(), 'is_shared_block_storage': fields.BooleanField(), 'instance_relative_path': fields.StringField(), 'graphics_listen_addr_vnc': fields.IPAddressField(nullable=True), 'graphics_listen_addr_spice': fields.IPAddressField(nullable=True), 'serial_listen_addr': fields.StringField(nullable=True), 'serial_listen_ports': fields.ListOfIntegersField(), 'bdms': fields.ListOfObjectsField('LibvirtLiveMigrateBDMInfo'), 'target_connect_addr': fields.StringField(nullable=True), } def obj_make_compatible(self, primitive, target_version): super(LibvirtLiveMigrateData, self).obj_make_compatible(primitive, target_version) target_version = versionutils.convert_version_to_tuple(target_version) if target_version < (1, 2): if 'serial_listen_ports' in primitive: del primitive['serial_listen_ports'] if target_version < (1, 1) and 'target_connect_addr' in primitive: del primitive['target_connect_addr'] def _bdms_to_legacy(self, legacy): if not self.obj_attr_is_set('bdms'): return legacy['volume'] = {} for bdmi in self.bdms: legacy['volume'][bdmi.serial] = { 'disk_info': bdmi.as_disk_info(), 'connection_info': bdmi.connection_info } def _bdms_from_legacy(self, legacy_pre_result): self.bdms = [] volume = legacy_pre_result.get('volume', {}) for serial in volume: vol = volume[serial] bdmi = objects.LibvirtLiveMigrateBDMInfo(serial=serial) bdmi.connection_info = vol['connection_info'] bdmi.bus = vol['disk_info']['bus'] bdmi.dev = vol['disk_info']['dev'] bdmi.type = vol['disk_info']['type'] if 'format' in vol: bdmi.format = vol['disk_info']['format'] if 'boot_index' in vol: bdmi.boot_index = int(vol['disk_info']['boot_index']) self.bdms.append(bdmi) def to_legacy_dict(self, pre_migration_result=False): LOG.debug('Converting to legacy: %s' % self) legacy = super(LibvirtLiveMigrateData, self).to_legacy_dict() keys = (set(self.fields.keys()) - set(LiveMigrateData.fields.keys()) - {'bdms'}) legacy.update( {k: getattr(self, k) for k in keys if self.obj_attr_is_set(k)}) graphics_vnc = legacy.pop('graphics_listen_addr_vnc', None) graphics_spice = legacy.pop('graphics_listen_addr_spice', None) transport_target = legacy.pop('target_connect_addr', None) live_result = { 'graphics_listen_addrs': { 'vnc': graphics_vnc and str(graphics_vnc), 'spice': graphics_spice and str(graphics_spice), }, 'serial_listen_addr': legacy.pop('serial_listen_addr', None), 'target_connect_addr': transport_target, } if pre_migration_result: legacy['pre_live_migration_result'] = live_result self._bdms_to_legacy(live_result) LOG.debug('Legacy result: %s' % legacy) return legacy def from_legacy_dict(self, legacy): LOG.debug('Converting legacy dict to obj: %s' % legacy) super(LibvirtLiveMigrateData, self).from_legacy_dict(legacy) keys = set(self.fields.keys()) - set(LiveMigrateData.fields.keys()) for k in keys - {'bdms'}: if k in legacy: setattr(self, k, legacy[k]) if 'pre_live_migration_result' in legacy: pre_result = legacy['pre_live_migration_result'] self.graphics_listen_addr_vnc = \ pre_result['graphics_listen_addrs'].get('vnc') self.graphics_listen_addr_spice = \ pre_result['graphics_listen_addrs'].get('spice') self.target_connect_addr = pre_result.get('target_connect_addr') if 'serial_listen_addr' in pre_result: self.serial_listen_addr = pre_result['serial_listen_addr'] self._bdms_from_legacy(pre_result) LOG.debug('Converted object: %s' % self) def is_on_shared_storage(self): return self.is_shared_block_storage or self.is_shared_instance_path
class ComputeNode(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version # Version 1.1: Added get_by_service_id() # Version 1.2: String attributes updated to support unicode # Version 1.3: Added stats field # Version 1.4: Added host ip field # Version 1.5: Added numa_topology field # Version 1.6: Added supported_hv_specs # Version 1.7: Added host field # Version 1.8: Added get_by_host_and_nodename() # Version 1.9: Added pci_device_pools # Version 1.10: Added get_first_node_by_host_for_old_compat() # Version 1.11: PciDevicePoolList version 1.1 # Version 1.12: HVSpec version 1.1 # Version 1.13: Changed service_id field to be nullable # Version 1.14: Added cpu_allocation_ratio and ram_allocation_ratio VERSION = '1.14' fields = { 'id': fields.IntegerField(read_only=True), 'service_id': fields.IntegerField(nullable=True), 'host': fields.StringField(nullable=True), 'vcpus': fields.IntegerField(), 'memory_mb': fields.IntegerField(), 'local_gb': fields.IntegerField(), 'vcpus_used': fields.IntegerField(), 'memory_mb_used': fields.IntegerField(), 'local_gb_used': fields.IntegerField(), 'hypervisor_type': fields.StringField(), 'hypervisor_version': fields.IntegerField(), 'hypervisor_hostname': fields.StringField(nullable=True), 'free_ram_mb': fields.IntegerField(nullable=True), 'free_disk_gb': fields.IntegerField(nullable=True), 'current_workload': fields.IntegerField(nullable=True), 'running_vms': fields.IntegerField(nullable=True), 'cpu_info': fields.StringField(nullable=True), 'disk_available_least': fields.IntegerField(nullable=True), 'metrics': fields.StringField(nullable=True), 'stats': fields.DictOfNullableStringsField(nullable=True), 'host_ip': fields.IPAddressField(nullable=True), 'numa_topology': fields.StringField(nullable=True), # NOTE(pmurray): the supported_hv_specs field maps to the # supported_instances field in the database 'supported_hv_specs': fields.ListOfObjectsField('HVSpec'), # NOTE(pmurray): the pci_device_pools field maps to the # pci_stats field in the database 'pci_device_pools': fields.ObjectField('PciDevicePoolList', nullable=True), 'cpu_allocation_ratio': fields.FloatField(), 'ram_allocation_ratio': fields.FloatField(), } def obj_make_compatible(self, primitive, target_version): super(ComputeNode, self).obj_make_compatible(primitive, target_version) target_version = versionutils.convert_version_to_tuple(target_version) if target_version < (1, 14): if 'ram_allocation_ratio' in primitive: del primitive['ram_allocation_ratio'] if 'cpu_allocation_ratio' in primitive: del primitive['cpu_allocation_ratio'] if target_version < (1, 13) and primitive.get('service_id') is None: # service_id is non-nullable in versions before 1.13 try: service = objects.Service.get_by_compute_host( self._context, primitive['host']) primitive['service_id'] = service.id except (exception.ComputeHostNotFound, KeyError): # NOTE(hanlind): In case anything goes wrong like service not # found or host not being set, catch and set a fake value just # to allow for older versions that demand a value to work. # Setting to -1 will, if value is later used result in a # ServiceNotFound, so should be safe. primitive['service_id'] = -1 if target_version < (1, 7) and 'host' in primitive: del primitive['host'] if target_version < (1, 5) and 'numa_topology' in primitive: del primitive['numa_topology'] if target_version < (1, 4) and 'host_ip' in primitive: del primitive['host_ip'] if target_version < (1, 3) and 'stats' in primitive: # pre 1.3 version does not have a stats field del primitive['stats'] @staticmethod def _host_from_db_object(compute, db_compute): if (('host' not in db_compute or db_compute['host'] is None) and 'service_id' in db_compute and db_compute['service_id'] is not None): # FIXME(sbauza) : Unconverted compute record, provide compatibility # This has to stay until we can be sure that any/all compute nodes # in the database have been converted to use the host field # Service field of ComputeNode could be deprecated in a next patch, # so let's use directly the Service object try: service = objects.Service.get_by_id( compute._context, db_compute['service_id']) except exception.ServiceNotFound: compute['host'] = None return try: compute['host'] = service.host except (AttributeError, exception.OrphanedObjectError): # Host can be nullable in Service compute['host'] = None elif 'host' in db_compute and db_compute['host'] is not None: # New-style DB having host as a field compute['host'] = db_compute['host'] else: # We assume it should not happen but in case, let's set it to None compute['host'] = None @staticmethod def _from_db_object(context, compute, db_compute): special_cases = set([ 'stats', 'supported_hv_specs', 'host', 'pci_device_pools', ]) fields = set(compute.fields) - special_cases for key in fields: value = db_compute[key] # NOTE(sbauza): Since all compute nodes don't possibly run the # latest RT code updating allocation ratios, we need to provide # a backwards compatible way of hydrating them. # As we want to care about our operators and since we don't want to # ask them to change their configuration files before upgrading, we # prefer to hardcode the default values for the ratios here until # the next release (Mitaka) where the opt default values will be # restored for both cpu (16.0) and ram (1.5) allocation ratios. # TODO(sbauza): Remove that in the next major version bump where # we break compatibilility with old Kilo computes if key == 'cpu_allocation_ratio' or key == 'ram_allocation_ratio': if value == 0.0: # Operator has not yet provided a new value for that ratio # on the compute node value = None if value is None: # ResourceTracker is not updating the value (old node) # or the compute node is updated but the default value has # not been changed value = getattr(CONF, key) if value == 0.0 and key == 'cpu_allocation_ratio': # It's not specified either on the controller value = 16.0 if value == 0.0 and key == 'ram_allocation_ratio': # It's not specified either on the controller value = 1.5 compute[key] = value stats = db_compute['stats'] if stats: compute['stats'] = jsonutils.loads(stats) sup_insts = db_compute.get('supported_instances') if sup_insts: hv_specs = jsonutils.loads(sup_insts) hv_specs = [objects.HVSpec.from_list(hv_spec) for hv_spec in hv_specs] compute['supported_hv_specs'] = hv_specs pci_stats = db_compute.get('pci_stats') compute.pci_device_pools = pci_device_pool.from_pci_stats(pci_stats) compute._context = context # Make sure that we correctly set the host field depending on either # host column is present in the table or not compute._host_from_db_object(compute, db_compute) compute.obj_reset_changes() return compute @base.remotable_classmethod def get_by_id(cls, context, compute_id): db_compute = db.compute_node_get(context, compute_id) return cls._from_db_object(context, cls(), db_compute) # NOTE(hanlind): This is deprecated and should be removed on the next # major version bump @base.remotable_classmethod def get_by_service_id(cls, context, service_id): db_computes = db.compute_nodes_get_by_service_id(context, service_id) # NOTE(sbauza): Old version was returning an item, we need to keep this # behaviour for backwards compatibility db_compute = db_computes[0] return cls._from_db_object(context, cls(), db_compute) @base.remotable_classmethod def get_by_host_and_nodename(cls, context, host, nodename): try: db_compute = db.compute_node_get_by_host_and_nodename( context, host, nodename) except exception.ComputeHostNotFound: # FIXME(sbauza): Some old computes can still have no host record # We need to provide compatibility by using the old service_id # record. # We assume the compatibility as an extra penalty of one more DB # call but that's necessary until all nodes are upgraded. try: service = objects.Service.get_by_compute_host(context, host) db_computes = db.compute_nodes_get_by_service_id( context, service.id) except exception.ServiceNotFound: # We need to provide the same exception upstream raise exception.ComputeHostNotFound(host=host) db_compute = None for compute in db_computes: if compute['hypervisor_hostname'] == nodename: db_compute = compute # We can avoid an extra call to Service object in # _from_db_object db_compute['host'] = service.host break if not db_compute: raise exception.ComputeHostNotFound(host=host) return cls._from_db_object(context, cls(), db_compute) @base.remotable_classmethod def get_first_node_by_host_for_old_compat(cls, context, host, use_slave=False): computes = ComputeNodeList.get_all_by_host(context, host, use_slave) # FIXME(sbauza): Some hypervisors (VMware, Ironic) can return multiple # nodes per host, we should return all the nodes and modify the callers # instead. # Arbitrarily returning the first node. return computes[0] @staticmethod def _convert_stats_to_db_format(updates): stats = updates.pop('stats', None) if stats is not None: updates['stats'] = jsonutils.dumps(stats) @staticmethod def _convert_host_ip_to_db_format(updates): host_ip = updates.pop('host_ip', None) if host_ip: updates['host_ip'] = str(host_ip) @staticmethod def _convert_supported_instances_to_db_format(updates): hv_specs = updates.pop('supported_hv_specs', None) if hv_specs is not None: hv_specs = [hv_spec.to_list() for hv_spec in hv_specs] updates['supported_instances'] = jsonutils.dumps(hv_specs) @staticmethod def _convert_pci_stats_to_db_format(updates): pools = updates.pop('pci_device_pools', None) if pools: updates['pci_stats'] = jsonutils.dumps(pools.obj_to_primitive()) @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() self._convert_stats_to_db_format(updates) self._convert_host_ip_to_db_format(updates) self._convert_supported_instances_to_db_format(updates) self._convert_pci_stats_to_db_format(updates) db_compute = db.compute_node_create(self._context, updates) self._from_db_object(self._context, self, db_compute) @base.remotable def save(self, prune_stats=False): # NOTE(belliott) ignore prune_stats param, no longer relevant updates = self.obj_get_changes() updates.pop('id', None) self._convert_stats_to_db_format(updates) self._convert_host_ip_to_db_format(updates) self._convert_supported_instances_to_db_format(updates) self._convert_pci_stats_to_db_format(updates) db_compute = db.compute_node_update(self._context, self.id, updates) self._from_db_object(self._context, self, db_compute) @base.remotable def destroy(self): db.compute_node_delete(self._context, self.id) def update_from_virt_driver(self, resources): # NOTE(pmurray): the virt driver provides a dict of values that # can be copied into the compute node. The names and representation # do not exactly match. # TODO(pmurray): the resources dict should be formalized. keys = ["vcpus", "memory_mb", "local_gb", "cpu_info", "vcpus_used", "memory_mb_used", "local_gb_used", "numa_topology", "hypervisor_type", "hypervisor_version", "hypervisor_hostname", "disk_available_least", "host_ip"] for key in keys: if key in resources: self[key] = resources[key] # supported_instances has a different name in compute_node if 'supported_instances' in resources: si = resources['supported_instances'] self.supported_hv_specs = [objects.HVSpec.from_list(s) for s in si]
class ComputeNode(base.NovaPersistentObject, base.NovaObject): # Version 1.0: Initial version # Version 1.1: Added get_by_service_id() # Version 1.2: String attributes updated to support unicode # Version 1.3: Added stats field # Version 1.4: Added host ip field # Version 1.5: Added numa_topology field # Version 1.6: Added supported_instances VERSION = '1.6' fields = { 'id': fields.IntegerField(read_only=True), 'service_id': fields.IntegerField(), 'vcpus': fields.IntegerField(), 'memory_mb': fields.IntegerField(), 'local_gb': fields.IntegerField(), 'vcpus_used': fields.IntegerField(), 'memory_mb_used': fields.IntegerField(), 'local_gb_used': fields.IntegerField(), 'hypervisor_type': fields.StringField(), 'hypervisor_version': fields.IntegerField(), 'hypervisor_hostname': fields.StringField(nullable=True), 'free_ram_mb': fields.IntegerField(nullable=True), 'free_disk_gb': fields.IntegerField(nullable=True), 'current_workload': fields.IntegerField(nullable=True), 'running_vms': fields.IntegerField(nullable=True), 'cpu_info': fields.StringField(nullable=True), 'disk_available_least': fields.IntegerField(nullable=True), 'metrics': fields.StringField(nullable=True), 'stats': fields.DictOfNullableStringsField(nullable=True), 'host_ip': fields.IPAddressField(nullable=True), 'numa_topology': fields.StringField(nullable=True), # NOTE(pmurray): the supported_hv_specs field maps to the # supported_instances field in the database 'supported_hv_specs': fields.ListOfObjectsField('HVSpec'), } def obj_make_compatible(self, primitive, target_version): target_version = utils.convert_version_to_tuple(target_version) if target_version < (1, 6) and 'supported_hv_specs' in primitive: del primitive['supported_hv_specs'] if target_version < (1, 5) and 'numa_topology' in primitive: del primitive['numa_topology'] if target_version < (1, 4) and 'host_ip' in primitive: del primitive['host_ip'] if target_version < (1, 3) and 'stats' in primitive: # pre 1.3 version does not have a stats field del primitive['stats'] @staticmethod def _from_db_object(context, compute, db_compute): fields = set(compute.fields) - set(['stats', 'supported_hv_specs']) for key in fields: compute[key] = db_compute[key] stats = db_compute['stats'] if stats: compute['stats'] = jsonutils.loads(stats) sup_insts = db_compute.get('supported_instances') if sup_insts: hv_specs = jsonutils.loads(sup_insts) hv_specs = [ objects.HVSpec.from_list(hv_spec) for hv_spec in hv_specs ] compute['supported_hv_specs'] = hv_specs compute._context = context compute.obj_reset_changes() return compute @base.remotable_classmethod def get_by_id(cls, context, compute_id): db_compute = db.compute_node_get(context, compute_id) return cls._from_db_object(context, cls(), db_compute) @base.remotable_classmethod def get_by_service_id(cls, context, service_id): db_compute = db.compute_node_get_by_service_id(context, service_id) return cls._from_db_object(context, cls(), db_compute) def _convert_stats_to_db_format(self, updates): stats = updates.pop('stats', None) if stats is not None: updates['stats'] = jsonutils.dumps(stats) def _convert_host_ip_to_db_format(self, updates): host_ip = updates.pop('host_ip', None) if host_ip: updates['host_ip'] = str(host_ip) def _convert_supported_instances_to_db_format(selfself, updates): hv_specs = updates.pop('supported_hv_specs', None) if hv_specs is not None: hv_specs = [hv_spec.to_list() for hv_spec in hv_specs] updates['supported_instances'] = jsonutils.dumps(hv_specs) @base.remotable def create(self, context): if self.obj_attr_is_set('id'): raise exception.ObjectActionError(action='create', reason='already created') updates = self.obj_get_changes() self._convert_stats_to_db_format(updates) self._convert_host_ip_to_db_format(updates) self._convert_supported_instances_to_db_format(updates) db_compute = db.compute_node_create(context, updates) self._from_db_object(context, self, db_compute) @base.remotable def save(self, context, prune_stats=False): # NOTE(belliott) ignore prune_stats param, no longer relevant updates = self.obj_get_changes() updates.pop('id', None) self._convert_stats_to_db_format(updates) self._convert_host_ip_to_db_format(updates) self._convert_supported_instances_to_db_format(updates) db_compute = db.compute_node_update(context, self.id, updates) self._from_db_object(context, self, db_compute) @base.remotable def destroy(self, context): db.compute_node_delete(context, self.id) @property def service(self): if not hasattr(self, '_cached_service'): self._cached_service = objects.Service.get_by_id( self._context, self.service_id) return self._cached_service
class FloatingIP(obj_base.NovaPersistentObject, obj_base.NovaObject, obj_base.NovaObjectDictCompat): # Version 1.0: Initial version # Version 1.1: Added _get_addresses_by_instance_uuid() # Version 1.2: FixedIP <= version 1.2 # Version 1.3: FixedIP <= version 1.3 # Version 1.4: FixedIP <= version 1.4 # Version 1.5: FixedIP <= version 1.5 # Version 1.6: FixedIP <= version 1.6 # Version 1.7: FixedIP <= version 1.11 # Version 1.8: FixedIP <= version 1.12 # Version 1.9: FixedIP <= version 1.13 VERSION = '1.9' fields = { 'id': fields.IntegerField(), 'address': fields.IPAddressField(), 'fixed_ip_id': fields.IntegerField(nullable=True), 'project_id': fields.UUIDField(nullable=True), 'host': fields.StringField(nullable=True), 'auto_assigned': fields.BooleanField(), 'pool': fields.StringField(nullable=True), 'interface': fields.StringField(nullable=True), 'fixed_ip': fields.ObjectField('FixedIP', nullable=True), } obj_relationships = { 'fixed_ip': [('1.0', '1.1'), ('1.2', '1.2'), ('1.3', '1.3'), ('1.4', '1.4'), ('1.5', '1.5'), ('1.6', '1.6'), ('1.7', '1.11'), ('1.8', '1.12'), ('1.9', '1.13')], } @staticmethod def _from_db_object(context, floatingip, db_floatingip, expected_attrs=None): if expected_attrs is None: expected_attrs = [] for field in floatingip.fields: if field not in FLOATING_IP_OPTIONAL_ATTRS: floatingip[field] = db_floatingip[field] if ('fixed_ip' in expected_attrs and db_floatingip['fixed_ip'] is not None): floatingip.fixed_ip = objects.FixedIP._from_db_object( context, objects.FixedIP(context), db_floatingip['fixed_ip']) floatingip._context = context floatingip.obj_reset_changes() return floatingip def obj_load_attr(self, attrname): if attrname not in FLOATING_IP_OPTIONAL_ATTRS: raise exception.ObjectActionError( action='obj_load_attr', reason='attribute %s is not lazy-loadable' % attrname) if not self._context: raise exception.OrphanedObjectError(method='obj_load_attr', objtype=self.obj_name()) if self.fixed_ip_id is not None: self.fixed_ip = objects.FixedIP.get_by_id( self._context, self.fixed_ip_id, expected_attrs=['network']) else: self.fixed_ip = None @obj_base.remotable_classmethod def get_by_id(cls, context, id): db_floatingip = db.floating_ip_get(context, id) # XXX joins fixed.instance return cls._from_db_object(context, cls(context), db_floatingip, expected_attrs=['fixed_ip']) @obj_base.remotable_classmethod def get_by_address(cls, context, address): db_floatingip = db.floating_ip_get_by_address(context, str(address)) return cls._from_db_object(context, cls(context), db_floatingip) @obj_base.remotable_classmethod def get_pool_names(cls, context): return [x['name'] for x in db.floating_ip_get_pools(context)] @obj_base.remotable_classmethod def allocate_address(cls, context, project_id, pool, auto_assigned=False): return db.floating_ip_allocate_address(context, project_id, pool, auto_assigned=auto_assigned) @obj_base.remotable_classmethod def associate(cls, context, floating_address, fixed_address, host): db_fixed = db.floating_ip_fixed_ip_associate(context, str(floating_address), str(fixed_address), host) if db_fixed is None: return None floating = FloatingIP(context=context, address=floating_address, host=host, fixed_ip_id=db_fixed['id'], fixed_ip=objects.FixedIP._from_db_object( context, objects.FixedIP(context), db_fixed, expected_attrs=['network'])) return floating @obj_base.remotable_classmethod def deallocate(cls, context, address): return db.floating_ip_deallocate(context, str(address)) @obj_base.remotable_classmethod def destroy(cls, context, address): db.floating_ip_destroy(context, str(address)) @obj_base.remotable_classmethod def disassociate(cls, context, address): db_fixed = db.floating_ip_disassociate(context, str(address)) return cls(context=context, address=address, fixed_ip_id=db_fixed['id'], fixed_ip=objects.FixedIP._from_db_object( context, objects.FixedIP(context), db_fixed, expected_attrs=['network'])) @obj_base.remotable_classmethod def _get_addresses_by_instance_uuid(cls, context, instance_uuid): return db.instance_floating_address_get_all(context, instance_uuid) @classmethod def get_addresses_by_instance(cls, context, instance): return cls._get_addresses_by_instance_uuid(context, instance['uuid']) @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') if 'fixed_ip_id' in updates: reason = 'fixed_ip_id is not mutable' raise exception.ObjectActionError(action='save', reason=reason) # NOTE(danms): Make sure we don't pass the calculated fixed_ip # relationship to the DB update method updates.pop('fixed_ip', None) db_floatingip = db.floating_ip_update(self._context, str(self.address), updates) self._from_db_object(self._context, self, db_floatingip)
class Network(obj_base.NovaPersistentObject, obj_base.NovaObject): # 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 = tuple(int(x) for x in target_version.split('.')) 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, context): updates = self._get_primitive_changes() if 'id' in updates: raise exception.ObjectActionError(action='create', reason='already created') db_network = db.network_create_safe(context, updates) self._from_db_object(context, self, db_network) @obj_base.remotable def destroy(self, context): db.network_delete_safe(context, self.id) self.deleted = True self.obj_reset_changes(['deleted']) @obj_base.remotable def save(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 ComputeNode(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version # Version 1.1: Added get_by_service_id() # Version 1.2: String attributes updated to support unicode # Version 1.3: Added stats field # Version 1.4: Added host ip field # Version 1.5: Added numa_topology field # Version 1.6: Added supported_hv_specs # Version 1.7: Added host field # Version 1.8: Added get_by_host_and_nodename() # Version 1.9: Added pci_device_pools # Version 1.10: Added get_first_node_by_host_for_old_compat() VERSION = '1.10' fields = { 'id': fields.IntegerField(read_only=True), 'service_id': fields.IntegerField(), 'host': fields.StringField(nullable=True), 'vcpus': fields.IntegerField(), 'memory_mb': fields.IntegerField(), 'local_gb': fields.IntegerField(), 'vcpus_used': fields.IntegerField(), 'memory_mb_used': fields.IntegerField(), 'local_gb_used': fields.IntegerField(), 'hypervisor_type': fields.StringField(), 'hypervisor_version': fields.IntegerField(), 'hypervisor_hostname': fields.StringField(nullable=True), 'free_ram_mb': fields.IntegerField(nullable=True), 'free_disk_gb': fields.IntegerField(nullable=True), 'current_workload': fields.IntegerField(nullable=True), 'running_vms': fields.IntegerField(nullable=True), 'cpu_info': fields.StringField(nullable=True), 'disk_available_least': fields.IntegerField(nullable=True), 'metrics': fields.StringField(nullable=True), 'stats': fields.DictOfNullableStringsField(nullable=True), 'host_ip': fields.IPAddressField(nullable=True), 'numa_topology': fields.StringField(nullable=True), # NOTE(pmurray): the supported_hv_specs field maps to the # supported_instances field in the database 'supported_hv_specs': fields.ListOfObjectsField('HVSpec'), # NOTE(pmurray): the pci_device_pools field maps to the # pci_stats field in the database 'pci_device_pools': fields.ObjectField('PciDevicePoolList', nullable=True), } obj_relationships = { 'pci_device_pools': [('1.9', '1.0')], 'supported_hv_specs': [('1.6', '1.0')], } def obj_make_compatible(self, primitive, target_version): super(ComputeNode, self).obj_make_compatible(primitive, target_version) target_version = utils.convert_version_to_tuple(target_version) if target_version < (1, 7) and 'host' in primitive: del primitive['host'] if target_version < (1, 5) and 'numa_topology' in primitive: del primitive['numa_topology'] if target_version < (1, 4) and 'host_ip' in primitive: del primitive['host_ip'] if target_version < (1, 3) and 'stats' in primitive: # pre 1.3 version does not have a stats field del primitive['stats'] @staticmethod def _host_from_db_object(compute, db_compute): if (('host' not in db_compute or db_compute['host'] is None) and 'service_id' in db_compute and db_compute['service_id'] is not None): # FIXME(sbauza) : Unconverted compute record, provide compatibility # This has to stay until we can be sure that any/all compute nodes # in the database have been converted to use the host field # Service field of ComputeNode could be deprecated in a next patch, # so let's use directly the Service object try: service = objects.Service.get_by_id(compute._context, db_compute['service_id']) except exception.ServiceNotFound: compute['host'] = None return try: compute['host'] = service.host except (AttributeError, exception.OrphanedObjectError): # Host can be nullable in Service compute['host'] = None elif 'host' in db_compute and db_compute['host'] is not None: # New-style DB having host as a field compute['host'] = db_compute['host'] else: # We assume it should not happen but in case, let's set it to None compute['host'] = None @staticmethod def _from_db_object(context, compute, db_compute): special_cases = set([ 'stats', 'supported_hv_specs', 'host', 'pci_device_pools', ]) fields = set(compute.fields) - special_cases for key in fields: compute[key] = db_compute[key] stats = db_compute['stats'] if stats: compute['stats'] = jsonutils.loads(stats) sup_insts = db_compute.get('supported_instances') if sup_insts: hv_specs = jsonutils.loads(sup_insts) hv_specs = [ objects.HVSpec.from_list(hv_spec) for hv_spec in hv_specs ] compute['supported_hv_specs'] = hv_specs pci_stats = db_compute.get('pci_stats') compute.pci_device_pools = pci_device_pool.from_pci_stats(pci_stats) compute._context = context # Make sure that we correctly set the host field depending on either # host column is present in the table or not compute._host_from_db_object(compute, db_compute) compute.obj_reset_changes() return compute @base.remotable_classmethod def get_by_id(cls, context, compute_id): db_compute = db.compute_node_get(context, compute_id) return cls._from_db_object(context, cls(), db_compute) @base.remotable_classmethod def get_by_service_id(cls, context, service_id): db_computes = db.compute_nodes_get_by_service_id(context, service_id) # NOTE(sbauza): Old version was returning an item, we need to keep this # behaviour for backwards compatibility db_compute = db_computes[0] return cls._from_db_object(context, cls(), db_compute) @base.remotable_classmethod def get_by_host_and_nodename(cls, context, host, nodename): try: db_compute = db.compute_node_get_by_host_and_nodename( context, host, nodename) except exception.ComputeHostNotFound: # FIXME(sbauza): Some old computes can still have no host record # We need to provide compatibility by using the old service_id # record. # We assume the compatibility as an extra penalty of one more DB # call but that's necessary until all nodes are upgraded. try: service = objects.Service.get_by_compute_host(context, host) db_computes = db.compute_nodes_get_by_service_id( context, service.id) except exception.ServiceNotFound: # We need to provide the same exception upstream raise exception.ComputeHostNotFound(host=host) db_compute = None for compute in db_computes: if compute['hypervisor_hostname'] == nodename: db_compute = compute # We can avoid an extra call to Service object in # _from_db_object db_compute['host'] = service.host break if not db_compute: raise exception.ComputeHostNotFound(host=host) return cls._from_db_object(context, cls(), db_compute) @base.remotable_classmethod def get_first_node_by_host_for_old_compat(cls, context, host, use_slave=False): computes = ComputeNodeList.get_all_by_host(context, host, use_slave) # FIXME(sbauza): Some hypervisors (VMware, Ironic) can return multiple # nodes per host, we should return all the nodes and modify the callers # instead. # Arbitrarily returning the first node. return computes[0] @staticmethod def _convert_stats_to_db_format(updates): stats = updates.pop('stats', None) if stats is not None: updates['stats'] = jsonutils.dumps(stats) @staticmethod def _convert_host_ip_to_db_format(updates): host_ip = updates.pop('host_ip', None) if host_ip: updates['host_ip'] = str(host_ip) @staticmethod def _convert_supported_instances_to_db_format(updates): hv_specs = updates.pop('supported_hv_specs', None) if hv_specs is not None: hv_specs = [hv_spec.to_list() for hv_spec in hv_specs] updates['supported_instances'] = jsonutils.dumps(hv_specs) @staticmethod def _convert_pci_stats_to_db_format(updates): pools = updates.pop('pci_device_pools', None) if pools: updates['pci_stats'] = jsonutils.dumps(pools.obj_to_primitive()) @base.remotable def create(self, context): if self.obj_attr_is_set('id'): raise exception.ObjectActionError(action='create', reason='already created') updates = self.obj_get_changes() self._convert_stats_to_db_format(updates) self._convert_host_ip_to_db_format(updates) self._convert_supported_instances_to_db_format(updates) self._convert_pci_stats_to_db_format(updates) db_compute = db.compute_node_create(context, updates) self._from_db_object(context, self, db_compute) @base.remotable def save(self, context, prune_stats=False): # NOTE(belliott) ignore prune_stats param, no longer relevant updates = self.obj_get_changes() updates.pop('id', None) self._convert_stats_to_db_format(updates) self._convert_host_ip_to_db_format(updates) self._convert_supported_instances_to_db_format(updates) self._convert_pci_stats_to_db_format(updates) db_compute = db.compute_node_update(context, self.id, updates) self._from_db_object(context, self, db_compute) @base.remotable def destroy(self, context): db.compute_node_delete(context, self.id) @property def service(self): if not hasattr(self, '_cached_service'): self._cached_service = objects.Service.get_by_id( self._context, self.service_id) return self._cached_service
class ComputeNode(base.NovaPersistentObject, base.NovaObject): # Version 1.0: Initial version # Version 1.1: Added get_by_service_id() # Version 1.2: String attributes updated to support unicode # Version 1.3: Added stats field # Version 1.4: Added host ip field # Version 1.5: Added numa_topology field # Version 1.6: Added supported_hv_specs # Version 1.7: Added host field # Version 1.8: Added get_by_host_and_nodename() # Version 1.9: Added pci_device_pools # Version 1.10: Added get_first_node_by_host_for_old_compat() # Version 1.11: PciDevicePoolList version 1.1 # Version 1.12: HVSpec version 1.1 # Version 1.13: Changed service_id field to be nullable # Version 1.14: Added cpu_allocation_ratio and ram_allocation_ratio # Version 1.15: Added uuid # Version 1.16: Added disk_allocation_ratio # Version 1.17: Added mapped # Version 1.18: Added get_by_uuid(). VERSION = '1.18' fields = { 'id': fields.IntegerField(read_only=True), 'uuid': fields.UUIDField(read_only=True), 'service_id': fields.IntegerField(nullable=True), 'host': fields.StringField(nullable=True), 'vcpus': fields.IntegerField(), 'memory_mb': fields.IntegerField(), 'local_gb': fields.IntegerField(), 'vcpus_used': fields.IntegerField(), 'memory_mb_used': fields.IntegerField(), 'local_gb_used': fields.IntegerField(), 'hypervisor_type': fields.StringField(), 'hypervisor_version': fields.IntegerField(), 'hypervisor_hostname': fields.StringField(nullable=True), 'free_ram_mb': fields.IntegerField(nullable=True), 'free_disk_gb': fields.IntegerField(nullable=True), 'current_workload': fields.IntegerField(nullable=True), 'running_vms': fields.IntegerField(nullable=True), # TODO(melwitt): cpu_info is non-nullable in the schema but we must # wait until version 2.0 of ComputeNode to change it to non-nullable 'cpu_info': fields.StringField(nullable=True), 'disk_available_least': fields.IntegerField(nullable=True), 'metrics': fields.StringField(nullable=True), 'stats': fields.DictOfNullableStringsField(nullable=True), 'host_ip': fields.IPAddressField(nullable=True), # TODO(rlrossit): because of history, numa_topology is held here as a # StringField, not a NUMATopology object. In version 2 of ComputeNode # this will be converted over to a fields.ObjectField('NUMATopology') 'numa_topology': fields.StringField(nullable=True), # NOTE(pmurray): the supported_hv_specs field maps to the # supported_instances field in the database 'supported_hv_specs': fields.ListOfObjectsField('HVSpec'), # NOTE(pmurray): the pci_device_pools field maps to the # pci_stats field in the database 'pci_device_pools': fields.ObjectField('PciDevicePoolList', nullable=True), 'cpu_allocation_ratio': fields.FloatField(), 'ram_allocation_ratio': fields.FloatField(), 'disk_allocation_ratio': fields.FloatField(), 'mapped': fields.IntegerField(), } def obj_make_compatible(self, primitive, target_version): super(ComputeNode, self).obj_make_compatible(primitive, target_version) target_version = versionutils.convert_version_to_tuple(target_version) if target_version < (1, 17): if 'mapped' in primitive: del primitive['mapped'] if target_version < (1, 16): if 'disk_allocation_ratio' in primitive: del primitive['disk_allocation_ratio'] if target_version < (1, 15): if 'uuid' in primitive: del primitive['uuid'] if target_version < (1, 14): if 'ram_allocation_ratio' in primitive: del primitive['ram_allocation_ratio'] if 'cpu_allocation_ratio' in primitive: del primitive['cpu_allocation_ratio'] if target_version < (1, 13) and primitive.get('service_id') is None: # service_id is non-nullable in versions before 1.13 try: service = objects.Service.get_by_compute_host( self._context, primitive['host']) primitive['service_id'] = service.id except (exception.ComputeHostNotFound, KeyError): # NOTE(hanlind): In case anything goes wrong like service not # found or host not being set, catch and set a fake value just # to allow for older versions that demand a value to work. # Setting to -1 will, if value is later used result in a # ServiceNotFound, so should be safe. primitive['service_id'] = -1 if target_version < (1, 7) and 'host' in primitive: del primitive['host'] if target_version < (1, 5) and 'numa_topology' in primitive: del primitive['numa_topology'] if target_version < (1, 4) and 'host_ip' in primitive: del primitive['host_ip'] if target_version < (1, 3) and 'stats' in primitive: # pre 1.3 version does not have a stats field del primitive['stats'] @staticmethod def _host_from_db_object(compute, db_compute): if (('host' not in db_compute or db_compute['host'] is None) and 'service_id' in db_compute and db_compute['service_id'] is not None): # FIXME(sbauza) : Unconverted compute record, provide compatibility # This has to stay until we can be sure that any/all compute nodes # in the database have been converted to use the host field # Service field of ComputeNode could be deprecated in a next patch, # so let's use directly the Service object try: service = objects.Service.get_by_id( compute._context, db_compute['service_id']) except exception.ServiceNotFound: compute.host = None return try: compute.host = service.host except (AttributeError, exception.OrphanedObjectError): # Host can be nullable in Service compute.host = None elif 'host' in db_compute and db_compute['host'] is not None: # New-style DB having host as a field compute.host = db_compute['host'] else: # We assume it should not happen but in case, let's set it to None compute.host = None @staticmethod def _from_db_object(context, compute, db_compute): special_cases = set([ 'stats', 'supported_hv_specs', 'host', 'pci_device_pools', ]) fields = set(compute.fields) - special_cases online_updates = {} for key in fields: value = db_compute[key] # NOTE(sbauza): Since all compute nodes don't possibly run the # latest RT code updating allocation ratios, we need to provide # a backwards compatible way of hydrating them. # As we want to care about our operators and since we don't want to # ask them to change their configuration files before upgrading, we # prefer to hardcode the default values for the ratios here until # the next release (Newton) where the opt default values will be # restored for both cpu (16.0), ram (1.5) and disk (1.0) # allocation ratios. # TODO(yikun): Remove this online migration code when all ratio # values are NOT 0.0 or NULL ratio_keys = ['cpu_allocation_ratio', 'ram_allocation_ratio', 'disk_allocation_ratio'] if key in ratio_keys and value in (None, 0.0): # ResourceTracker is not updating the value (old node) # or the compute node is updated but the default value has # not been changed r = getattr(CONF, key) # NOTE(yikun): If the allocation ratio record is not set, the # allocation ratio will be changed to the # CONF.x_allocation_ratio value if x_allocation_ratio is # set, and fallback to use the CONF.initial_x_allocation_ratio # otherwise. init_x_ratio = getattr(CONF, 'initial_%s' % key) value = r if r else init_x_ratio online_updates[key] = value elif key == 'mapped': value = 0 if value is None else value setattr(compute, key, value) if online_updates: db.compute_node_update(context, compute.id, online_updates) stats = db_compute['stats'] if stats: compute.stats = jsonutils.loads(stats) sup_insts = db_compute.get('supported_instances') if sup_insts: hv_specs = jsonutils.loads(sup_insts) hv_specs = [objects.HVSpec.from_list(hv_spec) for hv_spec in hv_specs] compute.supported_hv_specs = hv_specs pci_stats = db_compute.get('pci_stats') if pci_stats is not None: pci_stats = pci_device_pool.from_pci_stats(pci_stats) compute.pci_device_pools = pci_stats compute._context = context # Make sure that we correctly set the host field depending on either # host column is present in the table or not compute._host_from_db_object(compute, db_compute) compute.obj_reset_changes() return compute @base.remotable_classmethod def get_by_id(cls, context, compute_id): db_compute = db.compute_node_get(context, compute_id) return cls._from_db_object(context, cls(), db_compute) @base.remotable_classmethod def get_by_uuid(cls, context, compute_uuid): nodes = ComputeNodeList.get_all_by_uuids(context, [compute_uuid]) # We have a unique index on the uuid column so we can get back 0 or 1. if not nodes: raise exception.ComputeHostNotFound(host=compute_uuid) return nodes[0] # NOTE(hanlind): This is deprecated and should be removed on the next # major version bump @base.remotable_classmethod def get_by_service_id(cls, context, service_id): db_computes = db.compute_nodes_get_by_service_id(context, service_id) # NOTE(sbauza): Old version was returning an item, we need to keep this # behaviour for backwards compatibility db_compute = db_computes[0] return cls._from_db_object(context, cls(), db_compute) @base.remotable_classmethod def get_by_host_and_nodename(cls, context, host, nodename): db_compute = db.compute_node_get_by_host_and_nodename( context, host, nodename) return cls._from_db_object(context, cls(), db_compute) # TODO(pkholkin): Remove this method in the next major version bump @base.remotable_classmethod def get_first_node_by_host_for_old_compat(cls, context, host, use_slave=False): computes = ComputeNodeList.get_all_by_host(context, host, use_slave) # FIXME(sbauza): Ironic deployments can return multiple # nodes per host, we should return all the nodes and modify the callers # instead. # Arbitrarily returning the first node. return computes[0] @staticmethod def _convert_stats_to_db_format(updates): stats = updates.pop('stats', None) if stats is not None: updates['stats'] = jsonutils.dumps(stats) @staticmethod def _convert_host_ip_to_db_format(updates): host_ip = updates.pop('host_ip', None) if host_ip: updates['host_ip'] = str(host_ip) @staticmethod def _convert_supported_instances_to_db_format(updates): hv_specs = updates.pop('supported_hv_specs', None) if hv_specs is not None: hv_specs = [hv_spec.to_list() for hv_spec in hv_specs] updates['supported_instances'] = jsonutils.dumps(hv_specs) @staticmethod def _convert_pci_stats_to_db_format(updates): if 'pci_device_pools' in updates: pools = updates.pop('pci_device_pools') if pools is not None: pools = jsonutils.dumps(pools.obj_to_primitive()) updates['pci_stats'] = pools @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() if 'uuid' not in updates: updates['uuid'] = uuidutils.generate_uuid() self.uuid = updates['uuid'] self._convert_stats_to_db_format(updates) self._convert_host_ip_to_db_format(updates) self._convert_supported_instances_to_db_format(updates) self._convert_pci_stats_to_db_format(updates) db_compute = db.compute_node_create(self._context, updates) self._from_db_object(self._context, self, db_compute) @base.remotable def save(self, prune_stats=False): # NOTE(belliott) ignore prune_stats param, no longer relevant updates = self.obj_get_changes() updates.pop('id', None) self._convert_stats_to_db_format(updates) self._convert_host_ip_to_db_format(updates) self._convert_supported_instances_to_db_format(updates) self._convert_pci_stats_to_db_format(updates) db_compute = db.compute_node_update(self._context, self.id, updates) self._from_db_object(self._context, self, db_compute) @base.remotable def destroy(self): db.compute_node_delete(self._context, self.id) def update_from_virt_driver(self, resources): # NOTE(pmurray): the virt driver provides a dict of values that # can be copied into the compute node. The names and representation # do not exactly match. # TODO(pmurray): the resources dict should be formalized. keys = ["vcpus", "memory_mb", "local_gb", "cpu_info", "vcpus_used", "memory_mb_used", "local_gb_used", "numa_topology", "hypervisor_type", "hypervisor_version", "hypervisor_hostname", "disk_available_least", "host_ip", "uuid"] for key in keys: if key in resources: # The uuid field is read-only so it should only be set when # creating the compute node record for the first time. Ignore # it otherwise. if key == 'uuid' and 'uuid' in self: continue setattr(self, key, resources[key]) # supported_instances has a different name in compute_node if 'supported_instances' in resources: si = resources['supported_instances'] self.supported_hv_specs = [objects.HVSpec.from_list(s) for s in si]
class FloatingIP(obj_base.NovaPersistentObject, obj_base.NovaObject): fields = { 'id': fields.IntegerField(), 'address': fields.IPAddressField(), 'fixed_ip_id': fields.IntegerField(nullable=True), 'project_id': fields.UUIDField(nullable=True), 'host': fields.StringField(nullable=True), 'auto_assigned': fields.BooleanField(), 'pool': fields.StringField(nullable=True), 'interface': fields.StringField(nullable=True), 'fixed_ip': fields.ObjectField('FixedIP', nullable=True), } @staticmethod def _from_db_object(context, floatingip, db_floatingip, expected_attrs=None): if expected_attrs is None: expected_attrs = [] for field in floatingip.fields: if field not in FLOATING_IP_OPTIONAL_ATTRS: floatingip[field] = db_floatingip[field] if 'fixed_ip' in expected_attrs: floatingip.fixed_ip = fixed_ip.FixedIP._from_db_object( context, fixed_ip.FixedIP(), db_floatingip['fixed_ip']) floatingip._context = context floatingip.obj_reset_changes() return floatingip @obj_base.remotable_classmethod def get_by_id(cls, context, id): db_floatingip = db.floating_ip_get(context, id) # XXX joins fixed.instance return cls._from_db_object(context, cls(), db_floatingip, ['fixed_ip']) @obj_base.remotable_classmethod def get_by_address(cls, context, address): db_floatingip = db.floating_ip_get_by_address(context, address) return cls._from_db_object(context, cls(), db_floatingip) @obj_base.remotable_classmethod def get_pool_names(cls, context): return [x['name'] for x in db.floating_ip_get_pools(context)] @obj_base.remotable_classmethod def allocate_address(cls, context, project_id, pool, auto_assigned=False): return db.floating_ip_allocate_address(context, project_id, pool, auto_assigned=auto_assigned) @obj_base.remotable_classmethod def associate(cls, context, floating_address, fixed_address, host): db_fixed = db.floating_ip_fixed_ip_associate(context, floating_address, fixed_address, host) if db_fixed is None: return None floating = FloatingIP( context=context, address=floating_address, host=host, fixed_ip_id=db_fixed['id'], fixed_ip=fixed_ip.FixedIP._from_db_object( context, fixed_ip.FixedIP(), db_fixed, expected_attrs=['network'])) return floating @obj_base.remotable_classmethod def deallocate(cls, context, address): db.floating_ip_deallocate(context, address) @obj_base.remotable_classmethod def destroy(cls, context, address): db.floating_ip_destroy(context, address) @obj_base.remotable_classmethod def disassociate(cls, context, address): db_fixed = db.floating_ip_disassociate(context, address) floating = FloatingIP( context=context, address=address, fixed_ip_id=db_fixed['id'], fixed_ip=fixed_ip.FixedIP._from_db_object( context, fixed_ip.FixedIP(), db_fixed, expected_attrs=['network'])) return floating @obj_base.remotable def save(self, context): updates = self.obj_get_changes() if 'address' in updates: raise exception.ObjectActionError(action='save', reason='address is not mutable') db_floatingip = db.floating_ip_update(context, str(self.address), updates) self._from_db_object(context, self, db_floatingip)
class TestObject(base.NovaObject): fields = { 'addr': fields.IPAddressField(), 'cidr': fields.IPNetworkField() }
class FloatingIP(obj_base.NovaPersistentObject, obj_base.NovaObject): # Version 1.0: Initial version # Version 1.1: Added _get_addresses_by_instance_uuid() VERSION = '1.1' fields = { 'id': fields.IntegerField(), 'address': fields.IPAddressField(), 'fixed_ip_id': fields.IntegerField(nullable=True), 'project_id': fields.UUIDField(nullable=True), 'host': fields.StringField(nullable=True), 'auto_assigned': fields.BooleanField(), 'pool': fields.StringField(nullable=True), 'interface': fields.StringField(nullable=True), 'fixed_ip': fields.ObjectField('FixedIP', nullable=True), } @staticmethod def _from_db_object(context, floatingip, db_floatingip, expected_attrs=None): if expected_attrs is None: expected_attrs = [] for field in floatingip.fields: if field not in FLOATING_IP_OPTIONAL_ATTRS: floatingip[field] = db_floatingip[field] if 'fixed_ip' in expected_attrs: floatingip.fixed_ip = objects.FixedIP._from_db_object( context, objects.FixedIP(context), db_floatingip['fixed_ip']) floatingip._context = context floatingip.obj_reset_changes() return floatingip def obj_load_attr(self, attrname): if attrname not in FLOATING_IP_OPTIONAL_ATTRS: raise exception.ObjectActionError( action='obj_load_attr', reason='attribute %s is not lazy-loadable' % attrname) if not self._context: raise exception.OrphanedObjectError(method='obj_load_attr', objtype=self.obj_name()) if self.fixed_ip_id is not None: self.fixed_ip = objects.FixedIP.get_by_id( self._context, self.fixed_ip_id, expected_attrs=['network']) else: self.fixed_ip = None @obj_base.remotable_classmethod def get_by_id(cls, context, id): db_floatingip = db.floating_ip_get(context, id) # XXX joins fixed.instance return cls._from_db_object(context, cls(context), db_floatingip, expected_attrs=['fixed_ip']) @obj_base.remotable_classmethod def get_by_address(cls, context, address): db_floatingip = db.floating_ip_get_by_address(context, address) return cls._from_db_object(context, cls(context), db_floatingip) @obj_base.remotable_classmethod def get_pool_names(cls, context): return [x['name'] for x in db.floating_ip_get_pools(context)] @obj_base.remotable_classmethod def allocate_address(cls, context, project_id, pool, auto_assigned=False): return db.floating_ip_allocate_address(context, project_id, pool, auto_assigned=auto_assigned) @obj_base.remotable_classmethod def associate(cls, context, floating_address, fixed_address, host): db_fixed = db.floating_ip_fixed_ip_associate(context, floating_address, fixed_address, host) if db_fixed is None: return None floating = FloatingIP(context=context, address=floating_address, host=host, fixed_ip_id=db_fixed['id'], fixed_ip=objects.FixedIP._from_db_object( context, objects.FixedIP(context), db_fixed, expected_attrs=['network'])) return floating @obj_base.remotable_classmethod def deallocate(cls, context, address): db.floating_ip_deallocate(context, address) @obj_base.remotable_classmethod def destroy(cls, context, address): db.floating_ip_destroy(context, address) @obj_base.remotable_classmethod def disassociate(cls, context, address): db_fixed = db.floating_ip_disassociate(context, address) return cls(context=context, address=address, fixed_ip_id=db_fixed['id'], fixed_ip=objects.FixedIP._from_db_object( context, objects.FixedIP(context), db_fixed, expected_attrs=['network'])) @obj_base.remotable_classmethod def _get_addresses_by_instance_uuid(cls, context, instance_uuid): return db.instance_floating_address_get_all(context, instance_uuid) @classmethod def get_addresses_by_instance(cls, context, instance): return cls._get_addresses_by_instance_uuid(context, instance['uuid']) @obj_base.remotable def save(self, context): updates = self.obj_get_changes() if 'address' in updates: raise exception.ObjectActionError(action='save', reason='address is not mutable') db_floatingip = db.floating_ip_update(context, str(self.address), updates) self._from_db_object(context, self, db_floatingip)