Ejemplo n.º 1
0
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)
Ejemplo n.º 3
0
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
Ejemplo n.º 4
0
 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'))]
Ejemplo n.º 5
0
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
Ejemplo n.º 6
0
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)
Ejemplo n.º 7
0
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)
Ejemplo n.º 8
0
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]
Ejemplo n.º 9
0
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
Ejemplo n.º 10
0
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
Ejemplo n.º 11
0
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
Ejemplo n.º 12
0
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
Ejemplo n.º 13
0
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]
Ejemplo n.º 14
0
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
Ejemplo n.º 15
0
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)
Ejemplo n.º 16
0
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)
Ejemplo n.º 17
0
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
Ejemplo n.º 18
0
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)
Ejemplo n.º 20
0
 class TestObject(base.NovaObject):
     fields = {
         'addr': fields.IPAddressField(),
         'cidr': fields.IPNetworkField()
     }
Ejemplo n.º 21
0
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)