class InstancePCIRequest(base.NovaObject, base.NovaObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: Add request_id
    VERSION = '1.1'

    fields = {
        'count': fields.IntegerField(),
        'spec': fields.ListOfDictOfNullableStringsField(),
        'alias_name': fields.StringField(nullable=True),
        # A stashed request related to a resize, not current
        'is_new': fields.BooleanField(default=False),
        'request_id': fields.UUIDField(nullable=True),
    }

    def obj_load_attr(self, attr):
        setattr(self, attr, None)

    # NOTE(danms): The dict that this object replaces uses a key of 'new'
    # so we translate it here to our more appropropriately-named 'is_new'.
    # This is not something that affects the obect version, so we could
    # remove this later when all dependent code is fixed.
    @property
    def new(self):
        return self.is_new

    def obj_make_compatible(self, primitive, target_version):
        target_version = utils.convert_version_to_tuple(target_version)
        if target_version < (1, 1) and 'request_id' in primitive:
            del primitive['request_id']
Example #2
0
class InstancePCIRequestPayload(base.NotificationPayloadBase):
    # Version 1.0: Initial version
    VERSION = '1.0'

    SCHEMA = {
        'count': ('pci_request', 'count'),
        'spec': ('pci_request', 'spec'),
        'alias_name': ('pci_request', 'alias_name'),
        'request_id': ('pci_request', 'request_id'),
        'numa_policy': ('pci_request', 'numa_policy')
    }

    fields = {
        'count': fields.IntegerField(),
        'spec': fields.ListOfDictOfNullableStringsField(),
        'alias_name': fields.StringField(nullable=True),
        'request_id': fields.UUIDField(nullable=True),
        'numa_policy': fields.PCINUMAAffinityPolicyField(nullable=True)
    }

    def __init__(self, pci_request):
        super(InstancePCIRequestPayload, self).__init__()
        self.populate_schema(pci_request=pci_request)

    @classmethod
    def from_pci_request_list_obj(cls, pci_request_list):
        """Returns a list of InstancePCIRequestPayload objects
        based on the passed list of InstancePCIRequest objects.
        """
        payloads = []
        for pci_request in pci_request_list:
            payloads.append(cls(pci_request))
        return payloads
Example #3
0
class InstancePCIRequest(base.NovaObject,
                         base.NovaObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: Add request_id
    # Version 1.2: Add PCI NUMA affinity policy
    # Version 1.3: Add requester_id
    VERSION = '1.3'

    fields = {
        'count': fields.IntegerField(),
        'spec': fields.ListOfDictOfNullableStringsField(),
        'alias_name': fields.StringField(nullable=True),
        # Note(moshele): is_new is deprecated and should be removed
        # on major version bump
        'is_new': fields.BooleanField(default=False),
        'request_id': fields.UUIDField(nullable=True),
        'requester_id': fields.StringField(nullable=True),
        'numa_policy': fields.PCINUMAAffinityPolicyField(nullable=True),
    }

    def obj_load_attr(self, attr):
        setattr(self, attr, None)

    def obj_make_compatible(self, primitive, target_version):
        super(InstancePCIRequest, self).obj_make_compatible(primitive,
                                                            target_version)
        target_version = versionutils.convert_version_to_tuple(target_version)
        if target_version < (1, 3) and 'requester_id' in primitive:
            del primitive['requester_id']
        if target_version < (1, 2) and 'numa_policy' in primitive:
            del primitive['numa_policy']
        if target_version < (1, 1) and 'request_id' in primitive:
            del primitive['request_id']
Example #4
0
 def setUp(self):
     super(TestListOfDictOfNullableStringsField, self).setUp()
     self.field = fields.ListOfDictOfNullableStringsField()
     self.coerce_good_values = [([{
         'f': 'b',
         'f1': 'b1'
     }, {
         'f2': 'b2'
     }], [{
         'f': 'b',
         'f1': 'b1'
     }, {
         'f2': 'b2'
     }]), ([{
         'f': 1
     }, {
         'f1': 'b1'
     }], [{
         'f': '1'
     }, {
         'f1': 'b1'
     }]), ([{
         'foo': None
     }], [{
         'foo': None
     }])]
     self.coerce_bad_values = [[{1: 'a'}], ['ham', 1], ['eggs']]
     self.to_primitive_values = [([{
         'f': 'b'
     }, {
         'f1': 'b1'
     }, {
         'f2': None
     }], [{
         'f': 'b'
     }, {
         'f1': 'b1'
     }, {
         'f2': None
     }])]
     self.from_primitive_values = [([{
         'f': 'b'
     }, {
         'f1': 'b1'
     }, {
         'f2': None
     }], [{
         'f': 'b'
     }, {
         'f1': 'b1'
     }, {
         'f2': None
     }])]
Example #5
0
class InstancePCIRequest(base.NovaObject,
                         base.NovaObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: Added request_id field
    # Version 1.2: Added numa_policy field
    # Version 1.3: Added requester_id field
    # Version 1.4: Added 'socket' to numa_policy field
    VERSION = '1.4'

    # Possible sources for a PCI request:
    # FLAVOR_ALIAS : Request originated from a flavor alias.
    # NEUTRON_PORT : Request originated from a neutron port.
    FLAVOR_ALIAS = 0
    NEUTRON_PORT = 1

    fields = {
        'count': fields.IntegerField(),
        'spec': fields.ListOfDictOfNullableStringsField(),
        'alias_name': fields.StringField(nullable=True),
        # Note(moshele): is_new is deprecated and should be removed
        # on major version bump
        'is_new': fields.BooleanField(default=False),
        'request_id': fields.UUIDField(nullable=True),
        'requester_id': fields.StringField(nullable=True),
        'numa_policy': fields.PCINUMAAffinityPolicyField(nullable=True),
    }

    @property
    def source(self):
        # PCI requests originate from two sources: instance flavor alias and
        # neutron SR-IOV ports.
        # SR-IOV ports pci_request don't have an alias_name.
        return (InstancePCIRequest.NEUTRON_PORT if self.alias_name is None
                else InstancePCIRequest.FLAVOR_ALIAS)

    def obj_load_attr(self, attr):
        setattr(self, attr, None)

    def obj_make_compatible(self, primitive, target_version):
        super(InstancePCIRequest, self).obj_make_compatible(primitive,
                                                            target_version)
        target_version = versionutils.convert_version_to_tuple(target_version)
        if target_version < (1, 3) and 'requester_id' in primitive:
            del primitive['requester_id']
        if target_version < (1, 2) and 'numa_policy' in primitive:
            del primitive['numa_policy']
        if target_version < (1, 1) and 'request_id' in primitive:
            del primitive['request_id']
Example #6
0
class ImageMetaProps(base.NovaObject):
    # Version 1.0: Initial version
    # Version 1.1: added os_require_quiesce field
    # Version 1.2: added img_hv_type and img_hv_requested_version fields
    # Version 1.3: HVSpec version 1.1
    # Version 1.4: added hw_vif_multiqueue_enabled field
    # Version 1.5: added os_admin_user field
    VERSION = ImageMeta.VERSION

    # Maximum number of NUMA nodes permitted for the guest topology
    NUMA_NODES_MAX = 128

    # 'hw_' - settings affecting the guest virtual machine hardware
    # 'img_' - settings affecting the use of images by the compute node
    # 'os_' - settings affecting the guest operating system setup

    fields = {
        # name of guest hardware architecture eg i686, x86_64, ppc64
        'hw_architecture': fields.ArchitectureField(),

        # used to decide to expand root disk partition and fs to full size of
        # root disk
        'hw_auto_disk_config': fields.StringField(),

        # whether to display BIOS boot device menu
        'hw_boot_menu': fields.FlexibleBooleanField(),

        # name of the CDROM bus to use eg virtio, scsi, ide
        'hw_cdrom_bus': fields.DiskBusField(),

        # preferred number of CPU cores per socket
        'hw_cpu_cores': fields.IntegerField(),

        # preferred number of CPU sockets
        'hw_cpu_sockets': fields.IntegerField(),

        # maximum number of CPU cores per socket
        'hw_cpu_max_cores': fields.IntegerField(),

        # maximum number of CPU sockets
        'hw_cpu_max_sockets': fields.IntegerField(),

        # maximum number of CPU threads per core
        'hw_cpu_max_threads': fields.IntegerField(),

        # CPU thread allocation policy
        'hw_cpu_policy': fields.CPUAllocationPolicyField(),

        # preferred number of CPU threads per core
        'hw_cpu_threads': fields.IntegerField(),

        # guest ABI version for guest xentools either 1 or 2 (or 3 - depends on
        # Citrix PV tools version installed in image)
        'hw_device_id': fields.IntegerField(),

        # name of the hard disk bus to use eg virtio, scsi, ide
        'hw_disk_bus': fields.DiskBusField(),

        # allocation mode eg 'preallocated'
        'hw_disk_type': fields.StringField(),

        # name of the floppy disk bus to use eg fd, scsi, ide
        'hw_floppy_bus': fields.DiskBusField(),

        # boolean - used to trigger code to inject networking when booting a CD
        # image with a network boot image
        'hw_ipxe_boot': fields.FlexibleBooleanField(),

        # There are sooooooooooo many possible machine types in
        # QEMU - several new ones with each new release - that it
        # is not practical to enumerate them all. So we use a free
        # form string
        'hw_machine_type': fields.StringField(),

        # One of the magic strings 'small', 'any', 'large'
        # or an explicit page size in KB (eg 4, 2048, ...)
        'hw_mem_page_size': fields.StringField(),

        # Number of guest NUMA nodes
        'hw_numa_nodes': fields.IntegerField(),

        # Each list entry corresponds to a guest NUMA node and the
        # set members indicate CPUs for that node
        'hw_numa_cpus': fields.ListOfSetsOfIntegersField(),

        # Each list entry corresponds to a guest NUMA node and the
        # list value indicates the memory size of that node.
        'hw_numa_mem': fields.ListOfIntegersField(),

        # boolean 'yes' or 'no' to enable QEMU guest agent
        'hw_qemu_guest_agent': fields.FlexibleBooleanField(),

        # name of the RNG device type eg virtio
        'hw_rng_model': fields.RNGModelField(),

        # number of serial ports to create
        'hw_serial_port_count': fields.IntegerField(),

        # name of the SCSI bus controller eg 'virtio-scsi', 'lsilogic', etc
        'hw_scsi_model': fields.SCSIModelField(),

        # name of the video adapter model to use, eg cirrus, vga, xen, qxl
        'hw_video_model': fields.VideoModelField(),

        # MB of video RAM to provide eg 64
        'hw_video_ram': fields.IntegerField(),

        # name of a NIC device model eg virtio, e1000, rtl8139
        'hw_vif_model': fields.VIFModelField(),

        # "xen" vs "hvm"
        'hw_vm_mode': fields.VMModeField(),

        # action to take when watchdog device fires eg reset, poweroff, pause,
        # none
        'hw_watchdog_action': fields.WatchdogActionField(),

        # boolean - If true, this will enable the virtio-multiqueue feature
        'hw_vif_multiqueue_enabled': fields.FlexibleBooleanField(),

        # if true download using bittorrent
        'img_bittorrent': fields.FlexibleBooleanField(),

        # Which data format the 'img_block_device_mapping' field is
        # using to represent the block device mapping
        'img_bdm_v2': fields.FlexibleBooleanField(),

        # Block device mapping - the may can be in one or two completely
        # different formats. The 'img_bdm_v2' field determines whether
        # it is in legacy format, or the new current format. Ideally
        # we would have a formal data type for this field instead of a
        # dict, but with 2 different formats to represent this is hard.
        # See nova/block_device.py from_legacy_mapping() for the complex
        # conversion code. So for now leave it as a dict and continue
        # to use existing code that is able to convert dict into the
        # desired internal BDM formats
        'img_block_device_mapping': fields.ListOfDictOfNullableStringsField(),

        # boolean - if True, and image cache set to "some" decides if image
        # should be cached on host when server is booted on that host
        'img_cache_in_nova': fields.FlexibleBooleanField(),

        # Compression level for images. (1-9)
        'img_compression_level': fields.IntegerField(),

        # hypervisor supported version, eg. '>=2.6'
        'img_hv_requested_version': fields.VersionPredicateField(),

        # type of the hypervisor, eg kvm, ironic, xen
        'img_hv_type': fields.HVTypeField(),

        # boolean flag to set space-saving or performance behavior on the
        # Datastore
        'img_linked_clone': fields.FlexibleBooleanField(),

        # Image mappings - related to Block device mapping data - mapping
        # of virtual image names to device names. This could be represented
        # as a formatl data type, but is left as dict for same reason as
        # img_block_device_mapping field. It would arguably make sense for
        # the two to be combined into a single field and data type in the
        # future.
        'img_mappings': fields.ListOfDictOfNullableStringsField(),

        # image project id (set on upload)
        'img_owner_id': fields.StringField(),

        # root device name, used in snapshotting eg /dev/<blah>
        'img_root_device_name': fields.StringField(),

        # boolean - if false don't talk to nova agent
        'img_use_agent': fields.FlexibleBooleanField(),

        # integer value 1
        'img_version': fields.IntegerField(),

        # string of username with admin privileges
        'os_admin_user': fields.StringField(),

        # string of boot time command line arguments for the guest kernel
        'os_command_line': fields.StringField(),

        # the name of the specific guest operating system distro. This
        # is not done as an Enum since the list of operating systems is
        # growing incredibly fast, and valid values can be arbitrarily
        # user defined. Nova has no real need for strict validation so
        # leave it freeform
        'os_distro': fields.StringField(),

        # boolean - if true, then guest must support disk quiesce
        # or snapshot operation will be denied
        'os_require_quiesce': fields.FlexibleBooleanField(),

        # boolean - if using agent don't inject files, assume someone else is
        # doing that (cloud-init)
        'os_skip_agent_inject_files_at_boot': fields.FlexibleBooleanField(),

        # boolean - if using agent don't try inject ssh key, assume someone
        # else is doing that (cloud-init)
        'os_skip_agent_inject_ssh': fields.FlexibleBooleanField(),

        # The guest operating system family such as 'linux', 'windows' - this
        # is a fairly generic type. For a detailed type consider os_distro
        # instead
        'os_type': fields.OSTypeField(),
    }

    # The keys are the legacy property names and
    # the values are the current preferred names
    _legacy_property_map = {
        'architecture': 'hw_architecture',
        'owner_id': 'img_owner_id',
        'vmware_disktype': 'hw_disk_type',
        'vmware_image_version': 'img_version',
        'vmware_ostype': 'os_distro',
        'auto_disk_config': 'hw_auto_disk_config',
        'ipxe_boot': 'hw_ipxe_boot',
        'xenapi_device_id': 'hw_device_id',
        'xenapi_image_compression_level': 'img_compression_level',
        'vmware_linked_clone': 'img_linked_clone',
        'xenapi_use_agent': 'img_use_agent',
        'xenapi_skip_agent_inject_ssh': 'os_skip_agent_inject_ssh',
        'xenapi_skip_agent_inject_files_at_boot':
        'os_skip_agent_inject_files_at_boot',
        'cache_in_nova': 'img_cache_in_nova',
        'vm_mode': 'hw_vm_mode',
        'bittorrent': 'img_bittorrent',
        'mappings': 'img_mappings',
        'block_device_mapping': 'img_block_device_mapping',
        'bdm_v2': 'img_bdm_v2',
        'root_device_name': 'img_root_device_name',
        'hypervisor_version_requires': 'img_hv_requested_version',
    }

    # TODO(berrange): Need to run this from a data migration
    # at some point so we can eventually kill off the compat
    def _set_attr_from_legacy_names(self, image_props):
        for legacy_key in self._legacy_property_map:
            new_key = self._legacy_property_map[legacy_key]

            if legacy_key not in image_props:
                continue

            setattr(self, new_key, image_props[legacy_key])

        vmware_adaptertype = image_props.get("vmware_adaptertype")
        if vmware_adaptertype == "ide":
            setattr(self, "hw_disk_bus", "ide")
        elif vmware_adaptertype is not None:
            setattr(self, "hw_disk_bus", "scsi")
            setattr(self, "hw_scsi_model", vmware_adaptertype)

    def _set_numa_mem(self, image_props):
        hw_numa_mem = []
        hw_numa_mem_set = False
        for cellid in range(ImageMetaProps.NUMA_NODES_MAX):
            memprop = "hw_numa_mem.%d" % cellid
            if memprop not in image_props:
                break
            hw_numa_mem.append(int(image_props[memprop]))
            hw_numa_mem_set = True
            del image_props[memprop]

        if hw_numa_mem_set:
            self.hw_numa_mem = hw_numa_mem

    def _set_numa_cpus(self, image_props):
        hw_numa_cpus = []
        hw_numa_cpus_set = False
        for cellid in range(ImageMetaProps.NUMA_NODES_MAX):
            cpuprop = "hw_numa_cpus.%d" % cellid
            if cpuprop not in image_props:
                break
            hw_numa_cpus.append(hardware.parse_cpu_spec(image_props[cpuprop]))
            hw_numa_cpus_set = True
            del image_props[cpuprop]

        if hw_numa_cpus_set:
            self.hw_numa_cpus = hw_numa_cpus

    def _set_attr_from_current_names(self, image_props):
        for key in self.fields:
            # The two NUMA fields need special handling to
            # un-stringify them correctly
            if key == "hw_numa_mem":
                self._set_numa_mem(image_props)
            elif key == "hw_numa_cpus":
                self._set_numa_cpus(image_props)
            else:
                if key not in image_props:
                    continue

                setattr(self, key, image_props[key])

    @classmethod
    def from_dict(cls, image_props):
        """Create instance from image properties dict

        :param image_props: dictionary of image metdata properties

        Creates a new object instance, initializing from a
        dictionary of image metadata properties

        :returns: an ImageMetaProps instance
        """
        obj = cls()
        # We look to see if the dict has entries for any
        # of the legacy property names first. Then we use
        # the current property names. That way if both the
        # current and legacy names are set, the value
        # associated with the current name takes priority
        obj._set_attr_from_legacy_names(image_props)
        obj._set_attr_from_current_names(image_props)
        return obj

    def get(self, name, defvalue=None):
        """Get the value of an attribute
        :param name: the attribute to request
        :param defvalue: the default value if not set

        This returns the value of an attribute if it is currently
        set, otherwise it will return None.

        This differs from accessing props.attrname, because that
        will raise an exception if the attribute has no value set.

        So instead of

          if image_meta.properties.obj_attr_is_set("some_attr"):
             val = image_meta.properties.some_attr
          else
             val = None

        Callers can rely on unconditional access

             val = image_meta.properties.get("some_attr")

        :returns: the attribute value or None
        """

        if not self.obj_attr_is_set(name):
            return defvalue

        return getattr(self, name)
Example #7
0
class ImageMetaPropsPayload(base.NotificationPayloadBase):
    # Version 1.0: Initial version
    # Version 1.1: Added 'gop', 'virtio' and  'none' to hw_video_model field
    VERSION = '1.1'

    SCHEMA = {
        'hw_architecture': ('image_meta_props', 'hw_architecture'),
        'hw_auto_disk_config': ('image_meta_props', 'hw_auto_disk_config'),
        'hw_boot_menu': ('image_meta_props', 'hw_boot_menu'),
        'hw_cdrom_bus': ('image_meta_props', 'hw_cdrom_bus'),
        'hw_cpu_cores': ('image_meta_props', 'hw_cpu_cores'),
        'hw_cpu_sockets': ('image_meta_props', 'hw_cpu_sockets'),
        'hw_cpu_max_cores': ('image_meta_props', 'hw_cpu_max_cores'),
        'hw_cpu_max_sockets': ('image_meta_props', 'hw_cpu_max_sockets'),
        'hw_cpu_max_threads': ('image_meta_props', 'hw_cpu_max_threads'),
        'hw_cpu_policy': ('image_meta_props', 'hw_cpu_policy'),
        'hw_cpu_thread_policy': ('image_meta_props', 'hw_cpu_thread_policy'),
        'hw_cpu_realtime_mask': ('image_meta_props', 'hw_cpu_realtime_mask'),
        'hw_cpu_threads': ('image_meta_props', 'hw_cpu_threads'),
        'hw_device_id': ('image_meta_props', 'hw_device_id'),
        'hw_disk_bus': ('image_meta_props', 'hw_disk_bus'),
        'hw_disk_type': ('image_meta_props', 'hw_disk_type'),
        'hw_floppy_bus': ('image_meta_props', 'hw_floppy_bus'),
        'hw_firmware_type': ('image_meta_props', 'hw_firmware_type'),
        'hw_ipxe_boot': ('image_meta_props', 'hw_ipxe_boot'),
        'hw_machine_type': ('image_meta_props', 'hw_machine_type'),
        'hw_mem_page_size': ('image_meta_props', 'hw_mem_page_size'),
        'hw_numa_nodes': ('image_meta_props', 'hw_numa_nodes'),
        'hw_numa_cpus': ('image_meta_props', 'hw_numa_cpus'),
        'hw_numa_mem': ('image_meta_props', 'hw_numa_mem'),
        'hw_pointer_model': ('image_meta_props', 'hw_pointer_model'),
        'hw_qemu_guest_agent': ('image_meta_props', 'hw_qemu_guest_agent'),
        'hw_rescue_bus': ('image_meta_props', 'hw_rescue_bus'),
        'hw_rescue_device': ('image_meta_props', 'hw_rescue_device'),
        'hw_rng_model': ('image_meta_props', 'hw_rng_model'),
        'hw_serial_port_count': ('image_meta_props', 'hw_serial_port_count'),
        'hw_scsi_model': ('image_meta_props', 'hw_scsi_model'),
        'hw_video_model': ('image_meta_props', 'hw_video_model'),
        'hw_video_ram': ('image_meta_props', 'hw_video_ram'),
        'hw_vif_model': ('image_meta_props', 'hw_vif_model'),
        'hw_vm_mode': ('image_meta_props', 'hw_vm_mode'),
        'hw_watchdog_action': ('image_meta_props', 'hw_watchdog_action'),
        'hw_vif_multiqueue_enabled':
        ('image_meta_props', 'hw_vif_multiqueue_enabled'),
        'img_bittorrent': ('image_meta_props', 'img_bittorrent'),
        'img_bdm_v2': ('image_meta_props', 'img_bdm_v2'),
        'img_block_device_mapping':
        ('image_meta_props', 'img_block_device_mapping'),
        'img_cache_in_nova': ('image_meta_props', 'img_cache_in_nova'),
        'img_compression_level': ('image_meta_props', 'img_compression_level'),
        'img_hv_requested_version':
        ('image_meta_props', 'img_hv_requested_version'),
        'img_hv_type': ('image_meta_props', 'img_hv_type'),
        'img_config_drive': ('image_meta_props', 'img_config_drive'),
        'img_linked_clone': ('image_meta_props', 'img_linked_clone'),
        'img_mappings': ('image_meta_props', 'img_mappings'),
        'img_owner_id': ('image_meta_props', 'img_owner_id'),
        'img_root_device_name': ('image_meta_props', 'img_root_device_name'),
        'img_use_agent': ('image_meta_props', 'img_use_agent'),
        'img_version': ('image_meta_props', 'img_version'),
        'img_signature': ('image_meta_props', 'img_signature'),
        'img_signature_hash_method': ('image_meta_props',
                                      'img_signature_hash_method'),
        'img_signature_certificate_uuid': ('image_meta_props',
                                           'img_signature_certificate_uuid'),
        'img_signature_key_type': ('image_meta_props',
                                   'img_signature_key_type'),
        'img_hide_hypervisor_id': ('image_meta_props',
                                   'img_hide_hypervisor_id'),
        'os_admin_user': ('image_meta_props', 'os_admin_user'),
        'os_command_line': ('image_meta_props', 'os_command_line'),
        'os_distro': ('image_meta_props', 'os_distro'),
        'os_require_quiesce': ('image_meta_props', 'os_require_quiesce'),
        'os_secure_boot': ('image_meta_props', 'os_secure_boot'),
        'os_skip_agent_inject_files_at_boot':
        ('image_meta_props', 'os_skip_agent_inject_files_at_boot'),
        'os_skip_agent_inject_ssh': ('image_meta_props',
                                     'os_skip_agent_inject_ssh'),
        'os_type': ('image_meta_props', 'os_type'),
        'traits_required': ('image_meta_props', 'traits_required')
    }

    fields = {
        'hw_architecture': fields.ArchitectureField(),
        'hw_auto_disk_config': fields.StringField(),
        'hw_boot_menu': fields.FlexibleBooleanField(),
        'hw_cdrom_bus': fields.DiskBusField(),
        'hw_cpu_cores': fields.IntegerField(),
        'hw_cpu_sockets': fields.IntegerField(),
        'hw_cpu_max_cores': fields.IntegerField(),
        'hw_cpu_max_sockets': fields.IntegerField(),
        'hw_cpu_max_threads': fields.IntegerField(),
        'hw_cpu_policy': fields.CPUAllocationPolicyField(),
        'hw_cpu_thread_policy': fields.CPUThreadAllocationPolicyField(),
        'hw_cpu_realtime_mask': fields.StringField(),
        'hw_cpu_threads': fields.IntegerField(),
        'hw_device_id': fields.IntegerField(),
        'hw_disk_bus': fields.DiskBusField(),
        'hw_disk_type': fields.StringField(),
        'hw_floppy_bus': fields.DiskBusField(),
        'hw_firmware_type': fields.FirmwareTypeField(),
        'hw_ipxe_boot': fields.FlexibleBooleanField(),
        'hw_machine_type': fields.StringField(),
        'hw_mem_page_size': fields.StringField(),
        'hw_numa_nodes': fields.IntegerField(),
        'hw_numa_cpus': fields.ListOfSetsOfIntegersField(),
        'hw_numa_mem': fields.ListOfIntegersField(),
        'hw_pointer_model': fields.PointerModelField(),
        'hw_qemu_guest_agent': fields.FlexibleBooleanField(),
        'hw_rescue_bus': fields.DiskBusField(),
        'hw_rescue_device': fields.BlockDeviceTypeField(),
        'hw_rng_model': fields.RNGModelField(),
        'hw_serial_port_count': fields.IntegerField(),
        'hw_scsi_model': fields.SCSIModelField(),
        'hw_video_model': fields.VideoModelField(),
        'hw_video_ram': fields.IntegerField(),
        'hw_vif_model': fields.VIFModelField(),
        'hw_vm_mode': fields.VMModeField(),
        'hw_watchdog_action': fields.WatchdogActionField(),
        'hw_vif_multiqueue_enabled': fields.FlexibleBooleanField(),
        'img_bittorrent': fields.FlexibleBooleanField(),
        'img_bdm_v2': fields.FlexibleBooleanField(),
        'img_block_device_mapping': fields.ListOfDictOfNullableStringsField(),
        'img_cache_in_nova': fields.FlexibleBooleanField(),
        'img_compression_level': fields.IntegerField(),
        'img_hv_requested_version': fields.VersionPredicateField(),
        'img_hv_type': fields.HVTypeField(),
        'img_config_drive': fields.ConfigDrivePolicyField(),
        'img_linked_clone': fields.FlexibleBooleanField(),
        'img_mappings': fields.ListOfDictOfNullableStringsField(),
        'img_owner_id': fields.StringField(),
        'img_root_device_name': fields.StringField(),
        'img_use_agent': fields.FlexibleBooleanField(),
        'img_version': fields.IntegerField(),
        'img_signature': fields.StringField(),
        'img_signature_hash_method': fields.ImageSignatureHashTypeField(),
        'img_signature_certificate_uuid': fields.UUIDField(),
        'img_signature_key_type': fields.ImageSignatureKeyTypeField(),
        'img_hide_hypervisor_id': fields.FlexibleBooleanField(),
        'os_admin_user': fields.StringField(),
        'os_command_line': fields.StringField(),
        'os_distro': fields.StringField(),
        'os_require_quiesce': fields.FlexibleBooleanField(),
        'os_secure_boot': fields.SecureBootField(),
        'os_skip_agent_inject_files_at_boot': fields.FlexibleBooleanField(),
        'os_skip_agent_inject_ssh': fields.FlexibleBooleanField(),
        'os_type': fields.OSTypeField(),
        'traits_required': fields.ListOfStringsField()
    }

    def __init__(self, image_meta_props):
        super(ImageMetaPropsPayload, self).__init__()
        # NOTE(takashin): If fields are not set in the ImageMetaProps object,
        # it will not set the fields in the ImageMetaPropsPayload
        # in order to avoid too many fields whose values are None.
        self.populate_schema(set_none=False, image_meta_props=image_meta_props)
Example #8
0
class ImageMetaProps(base.NovaObject):
    # Version 1.0: Initial version
    # Version 1.1: added os_require_quiesce field
    # Version 1.2: added img_hv_type and img_hv_requested_version fields
    # Version 1.3: HVSpec version 1.1
    # Version 1.4: added hw_vif_multiqueue_enabled field
    # Version 1.5: added os_admin_user field
    # Version 1.6: Added 'lxc' and 'uml' enum types to DiskBusField
    # Version 1.7: added img_config_drive field
    # Version 1.8: Added 'lxd' to hypervisor types
    # Version 1.9: added hw_cpu_thread_policy field
    # Version 1.10: added hw_cpu_realtime_mask field
    # Version 1.11: Added hw_firmware_type field
    # Version 1.12: Added properties for image signature verification
    # Version 1.13: added os_secure_boot field
    # Version 1.14: Added 'hw_pointer_model' field
    # Version 1.15: Added hw_rescue_bus and hw_rescue_device.
    # Version 1.16: WatchdogActionField supports 'disabled' enum.
    # Version 1.17: Add lan9118 as valid nic for hw_vif_model property for qemu
    # Version 1.18: Pull signature properties from cursive library
    # Version 1.19: Added 'img_hide_hypervisor_id' type field
    # Version 1.20: Added 'traits_required' list field
    # Version 1.21: Added 'hw_time_hpet' field
    # Version 1.22: Added 'gop', 'virtio' and 'none' to hw_video_model field
    # Version 1.23: Added 'hw_pmu' field
    VERSION = '1.23'

    def obj_make_compatible(self, primitive, target_version):
        super(ImageMetaProps,
              self).obj_make_compatible(primitive, target_version)
        target_version = versionutils.convert_version_to_tuple(target_version)
        if target_version < (1, 23):
            primitive.pop('hw_pmu', None)
        # NOTE(sean-k-mooney): unlike other nova object we version this object
        # when composed object are updated.
        if target_version < (1, 22):
            video = primitive.get('hw_video_model', None)
            if video in ('gop', 'virtio', 'none'):
                raise exception.ObjectActionError(
                    action='obj_make_compatible',
                    reason='hw_video_model=%s not supported in version %s' %
                    (video, target_version))
        if target_version < (1, 21):
            primitive.pop('hw_time_hpet', None)
        if target_version < (1, 20):
            primitive.pop('traits_required', None)
        if target_version < (1, 19):
            primitive.pop('img_hide_hypervisor_id', None)
        if target_version < (1, 16) and 'hw_watchdog_action' in primitive:
            # Check to see if hw_watchdog_action was set to 'disabled' and if
            # so, remove it since not specifying it is the same behavior.
            if primitive['hw_watchdog_action'] == \
                    fields.WatchdogAction.DISABLED:
                primitive.pop('hw_watchdog_action')
        if target_version < (1, 15):
            primitive.pop('hw_rescue_bus', None)
            primitive.pop('hw_rescue_device', None)
        if target_version < (1, 14):
            primitive.pop('hw_pointer_model', None)
        if target_version < (1, 13):
            primitive.pop('os_secure_boot', None)
        if target_version < (1, 11):
            primitive.pop('hw_firmware_type', None)
        if target_version < (1, 10):
            primitive.pop('hw_cpu_realtime_mask', None)
        if target_version < (1, 9):
            primitive.pop('hw_cpu_thread_policy', None)
        if target_version < (1, 7):
            primitive.pop('img_config_drive', None)
        if target_version < (1, 5):
            primitive.pop('os_admin_user', None)
        if target_version < (1, 4):
            primitive.pop('hw_vif_multiqueue_enabled', None)
        if target_version < (1, 2):
            primitive.pop('img_hv_type', None)
            primitive.pop('img_hv_requested_version', None)
        if target_version < (1, 1):
            primitive.pop('os_require_quiesce', None)

        if target_version < (1, 6):
            bus = primitive.get('hw_disk_bus', None)
            if bus in ('lxc', 'uml'):
                raise exception.ObjectActionError(
                    action='obj_make_compatible',
                    reason='hw_disk_bus=%s not supported in version %s' %
                    (bus, target_version))

    # Maximum number of NUMA nodes permitted for the guest topology
    NUMA_NODES_MAX = 128

    # 'hw_' - settings affecting the guest virtual machine hardware
    # 'img_' - settings affecting the use of images by the compute node
    # 'os_' - settings affecting the guest operating system setup
    # 'traits_required' - The required traits associated with the image

    fields = {
        # name of guest hardware architecture eg i686, x86_64, ppc64
        'hw_architecture': fields.ArchitectureField(),

        # used to decide to expand root disk partition and fs to full size of
        # root disk
        'hw_auto_disk_config': fields.StringField(),

        # whether to display BIOS boot device menu
        'hw_boot_menu': fields.FlexibleBooleanField(),

        # name of the CDROM bus to use eg virtio, scsi, ide
        'hw_cdrom_bus': fields.DiskBusField(),

        # preferred number of CPU cores per socket
        'hw_cpu_cores': fields.IntegerField(),

        # preferred number of CPU sockets
        'hw_cpu_sockets': fields.IntegerField(),

        # maximum number of CPU cores per socket
        'hw_cpu_max_cores': fields.IntegerField(),

        # maximum number of CPU sockets
        'hw_cpu_max_sockets': fields.IntegerField(),

        # maximum number of CPU threads per core
        'hw_cpu_max_threads': fields.IntegerField(),

        # CPU allocation policy
        'hw_cpu_policy': fields.CPUAllocationPolicyField(),

        # CPU thread allocation policy
        'hw_cpu_thread_policy': fields.CPUThreadAllocationPolicyField(),

        # CPU mask indicates which vCPUs will have realtime enable,
        # example ^0-1 means that all vCPUs except 0 and 1 will have a
        # realtime policy.
        'hw_cpu_realtime_mask': fields.StringField(),

        # preferred number of CPU threads per core
        'hw_cpu_threads': fields.IntegerField(),

        # guest ABI version for guest xentools either 1 or 2 (or 3 - depends on
        # Citrix PV tools version installed in image)
        'hw_device_id': fields.IntegerField(),

        # name of the hard disk bus to use eg virtio, scsi, ide
        'hw_disk_bus': fields.DiskBusField(),

        # allocation mode eg 'preallocated'
        'hw_disk_type': fields.StringField(),

        # name of the floppy disk bus to use eg fd, scsi, ide
        'hw_floppy_bus': fields.DiskBusField(),

        # This indicates the guest needs UEFI firmware
        'hw_firmware_type': fields.FirmwareTypeField(),

        # boolean - used to trigger code to inject networking when booting a CD
        # image with a network boot image
        'hw_ipxe_boot': fields.FlexibleBooleanField(),

        # There are sooooooooooo many possible machine types in
        # QEMU - several new ones with each new release - that it
        # is not practical to enumerate them all. So we use a free
        # form string
        'hw_machine_type': fields.StringField(),

        # One of the magic strings 'small', 'any', 'large'
        # or an explicit page size in KB (eg 4, 2048, ...)
        'hw_mem_page_size': fields.StringField(),

        # Number of guest NUMA nodes
        'hw_numa_nodes': fields.IntegerField(),

        # Each list entry corresponds to a guest NUMA node and the
        # set members indicate CPUs for that node
        'hw_numa_cpus': fields.ListOfSetsOfIntegersField(),

        # Each list entry corresponds to a guest NUMA node and the
        # list value indicates the memory size of that node.
        'hw_numa_mem': fields.ListOfIntegersField(),

        # Generic property to specify the pointer model type.
        'hw_pointer_model': fields.PointerModelField(),

        # boolean 'true' or 'false' to enable virtual performance
        # monitoring unit (vPMU).
        'hw_pmu': fields.FlexibleBooleanField(),

        # boolean 'yes' or 'no' to enable QEMU guest agent
        'hw_qemu_guest_agent': fields.FlexibleBooleanField(),

        # name of the rescue bus to use with the associated rescue device.
        'hw_rescue_bus': fields.DiskBusField(),

        # name of rescue device to use.
        'hw_rescue_device': fields.BlockDeviceTypeField(),

        # name of the RNG device type eg virtio
        'hw_rng_model': fields.RNGModelField(),

        # boolean 'true' or 'false' to enable HPET
        'hw_time_hpet': fields.FlexibleBooleanField(),

        # number of serial ports to create
        'hw_serial_port_count': fields.IntegerField(),

        # name of the SCSI bus controller eg 'virtio-scsi', 'lsilogic', etc
        'hw_scsi_model': fields.SCSIModelField(),

        # name of the video adapter model to use, eg cirrus, vga, xen, qxl
        'hw_video_model': fields.VideoModelField(),

        # MB of video RAM to provide eg 64
        'hw_video_ram': fields.IntegerField(),

        # name of a NIC device model eg virtio, e1000, rtl8139
        'hw_vif_model': fields.VIFModelField(),

        # "xen" vs "hvm"
        'hw_vm_mode': fields.VMModeField(),

        # action to take when watchdog device fires eg reset, poweroff, pause,
        # none
        'hw_watchdog_action': fields.WatchdogActionField(),

        # boolean - If true, this will enable the virtio-multiqueue feature
        'hw_vif_multiqueue_enabled': fields.FlexibleBooleanField(),

        # if true download using bittorrent
        'img_bittorrent': fields.FlexibleBooleanField(),

        # Which data format the 'img_block_device_mapping' field is
        # using to represent the block device mapping
        'img_bdm_v2': fields.FlexibleBooleanField(),

        # Block device mapping - the may can be in one or two completely
        # different formats. The 'img_bdm_v2' field determines whether
        # it is in legacy format, or the new current format. Ideally
        # we would have a formal data type for this field instead of a
        # dict, but with 2 different formats to represent this is hard.
        # See nova/block_device.py from_legacy_mapping() for the complex
        # conversion code. So for now leave it as a dict and continue
        # to use existing code that is able to convert dict into the
        # desired internal BDM formats
        'img_block_device_mapping': fields.ListOfDictOfNullableStringsField(),

        # boolean - if True, and image cache set to "some" decides if image
        # should be cached on host when server is booted on that host
        'img_cache_in_nova': fields.FlexibleBooleanField(),

        # Compression level for images. (1-9)
        'img_compression_level': fields.IntegerField(),

        # hypervisor supported version, eg. '>=2.6'
        'img_hv_requested_version': fields.VersionPredicateField(),

        # type of the hypervisor, eg kvm, ironic, xen
        'img_hv_type': fields.HVTypeField(),

        # Whether the image needs/expected config drive
        'img_config_drive': fields.ConfigDrivePolicyField(),

        # boolean flag to set space-saving or performance behavior on the
        # Datastore
        'img_linked_clone': fields.FlexibleBooleanField(),

        # Image mappings - related to Block device mapping data - mapping
        # of virtual image names to device names. This could be represented
        # as a formal data type, but is left as dict for same reason as
        # img_block_device_mapping field. It would arguably make sense for
        # the two to be combined into a single field and data type in the
        # future.
        'img_mappings': fields.ListOfDictOfNullableStringsField(),

        # image project id (set on upload)
        'img_owner_id': fields.StringField(),

        # root device name, used in snapshotting eg /dev/<blah>
        'img_root_device_name': fields.StringField(),

        # boolean - if false don't talk to nova agent
        'img_use_agent': fields.FlexibleBooleanField(),

        # integer value 1
        'img_version': fields.IntegerField(),

        # base64 of encoding of image signature
        'img_signature': fields.StringField(),

        # string indicating hash method used to compute image signature
        'img_signature_hash_method': fields.ImageSignatureHashTypeField(),

        # string indicating Castellan uuid of certificate
        # used to compute the image's signature
        'img_signature_certificate_uuid': fields.UUIDField(),

        # string indicating type of key used to compute image signature
        'img_signature_key_type': fields.ImageSignatureKeyTypeField(),

        # boolean - hide hypervisor signature on instance
        'img_hide_hypervisor_id': fields.FlexibleBooleanField(),

        # string of username with admin privileges
        'os_admin_user': fields.StringField(),

        # string of boot time command line arguments for the guest kernel
        'os_command_line': fields.StringField(),

        # the name of the specific guest operating system distro. This
        # is not done as an Enum since the list of operating systems is
        # growing incredibly fast, and valid values can be arbitrarily
        # user defined. Nova has no real need for strict validation so
        # leave it freeform
        'os_distro': fields.StringField(),

        # boolean - if true, then guest must support disk quiesce
        # or snapshot operation will be denied
        'os_require_quiesce': fields.FlexibleBooleanField(),

        # Secure Boot feature will be enabled by setting the "os_secure_boot"
        # image property to "required". Other options can be: "disabled" or
        # "optional".
        # "os:secure_boot" flavor extra spec value overrides the image property
        # value.
        'os_secure_boot': fields.SecureBootField(),

        # boolean - if using agent don't inject files, assume someone else is
        # doing that (cloud-init)
        'os_skip_agent_inject_files_at_boot': fields.FlexibleBooleanField(),

        # boolean - if using agent don't try inject ssh key, assume someone
        # else is doing that (cloud-init)
        'os_skip_agent_inject_ssh': fields.FlexibleBooleanField(),

        # The guest operating system family such as 'linux', 'windows' - this
        # is a fairly generic type. For a detailed type consider os_distro
        # instead
        'os_type': fields.OSTypeField(),

        # The required traits associated with the image. Traits are expected to
        # be defined as starting with `trait:` like below:
        # trait:HW_CPU_X86_AVX2=required
        # for trait in image_meta.traits_required:
        # will yield trait strings such as 'HW_CPU_X86_AVX2'
        'traits_required': fields.ListOfStringsField(),
    }

    # The keys are the legacy property names and
    # the values are the current preferred names
    _legacy_property_map = {
        'architecture': 'hw_architecture',
        'owner_id': 'img_owner_id',
        'vmware_disktype': 'hw_disk_type',
        'vmware_image_version': 'img_version',
        'vmware_ostype': 'os_distro',
        'auto_disk_config': 'hw_auto_disk_config',
        'ipxe_boot': 'hw_ipxe_boot',
        'xenapi_device_id': 'hw_device_id',
        'xenapi_image_compression_level': 'img_compression_level',
        'vmware_linked_clone': 'img_linked_clone',
        'xenapi_use_agent': 'img_use_agent',
        'xenapi_skip_agent_inject_ssh': 'os_skip_agent_inject_ssh',
        'xenapi_skip_agent_inject_files_at_boot':
        'os_skip_agent_inject_files_at_boot',
        'cache_in_nova': 'img_cache_in_nova',
        'vm_mode': 'hw_vm_mode',
        'bittorrent': 'img_bittorrent',
        'mappings': 'img_mappings',
        'block_device_mapping': 'img_block_device_mapping',
        'bdm_v2': 'img_bdm_v2',
        'root_device_name': 'img_root_device_name',
        'hypervisor_version_requires': 'img_hv_requested_version',
        'hypervisor_type': 'img_hv_type',
    }

    # TODO(berrange): Need to run this from a data migration
    # at some point so we can eventually kill off the compat
    def _set_attr_from_legacy_names(self, image_props):
        for legacy_key in self._legacy_property_map:
            new_key = self._legacy_property_map[legacy_key]

            if legacy_key not in image_props:
                continue

            setattr(self, new_key, image_props[legacy_key])

        vmware_adaptertype = image_props.get("vmware_adaptertype")
        if vmware_adaptertype == "ide":
            setattr(self, "hw_disk_bus", "ide")
        elif vmware_adaptertype:
            setattr(self, "hw_disk_bus", "scsi")
            setattr(self, "hw_scsi_model", vmware_adaptertype)

    def _set_numa_mem(self, image_props):
        hw_numa_mem = []
        hw_numa_mem_set = False
        for cellid in range(ImageMetaProps.NUMA_NODES_MAX):
            memprop = "hw_numa_mem.%d" % cellid
            if memprop not in image_props:
                break
            hw_numa_mem.append(int(image_props[memprop]))
            hw_numa_mem_set = True
            del image_props[memprop]

        if hw_numa_mem_set:
            self.hw_numa_mem = hw_numa_mem

    def _set_numa_cpus(self, image_props):
        hw_numa_cpus = []
        hw_numa_cpus_set = False
        for cellid in range(ImageMetaProps.NUMA_NODES_MAX):
            cpuprop = "hw_numa_cpus.%d" % cellid
            if cpuprop not in image_props:
                break
            hw_numa_cpus.append(hardware.parse_cpu_spec(image_props[cpuprop]))
            hw_numa_cpus_set = True
            del image_props[cpuprop]

        if hw_numa_cpus_set:
            self.hw_numa_cpus = hw_numa_cpus

    def _set_attr_from_current_names(self, image_props):
        for key in self.fields:
            # The two NUMA fields need special handling to
            # un-stringify them correctly
            if key == "hw_numa_mem":
                self._set_numa_mem(image_props)
            elif key == "hw_numa_cpus":
                self._set_numa_cpus(image_props)
            else:
                # traits_required will be populated by
                # _set_attr_from_trait_names
                if key not in image_props or key == "traits_required":
                    continue

                setattr(self, key, image_props[key])

    def _set_attr_from_trait_names(self, image_props):
        for trait in [
                six.text_type(k[6:]) for k, v in image_props.items()
                if six.text_type(k).startswith("trait:")
                and six.text_type(v) == six.text_type('required')
        ]:
            if 'traits_required' not in self:
                self.traits_required = []
            self.traits_required.append(trait)

    @classmethod
    def from_dict(cls, image_props):
        """Create instance from image properties dict

        :param image_props: dictionary of image metadata properties

        Creates a new object instance, initializing from a
        dictionary of image metadata properties

        :returns: an ImageMetaProps instance
        """
        obj = cls()
        # We look to see if the dict has entries for any
        # of the legacy property names first. Then we use
        # the current property names. That way if both the
        # current and legacy names are set, the value
        # associated with the current name takes priority
        obj._set_attr_from_legacy_names(image_props)
        obj._set_attr_from_current_names(image_props)
        obj._set_attr_from_trait_names(image_props)

        return obj

    def get(self, name, defvalue=None):
        """Get the value of an attribute
        :param name: the attribute to request
        :param defvalue: the default value if not set

        This returns the value of an attribute if it is currently
        set, otherwise it will return None.

        This differs from accessing props.attrname, because that
        will raise an exception if the attribute has no value set.

        So instead of

          if image_meta.properties.obj_attr_is_set("some_attr"):
             val = image_meta.properties.some_attr
          else
             val = None

        Callers can rely on unconditional access

             val = image_meta.properties.get("some_attr")

        :returns: the attribute value or None
        """

        if not self.obj_attr_is_set(name):
            return defvalue

        return getattr(self, name)
Example #9
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(),

        # PF9 added fields
        'pf9_stat': fields.DictOfNullableStringsField(nullable=True),
        'pf9_ip_info': fields.ListOfDictOfNullableStringsField(nullable=True),
        'pf9_networks': fields.ListOfStringsField(nullable=True),
        }

    obj_relationships = {
        'pci_device_pools': [('1.9', '1.0'), ('1.11', '1.1')],
        'supported_hv_specs': [('1.6', '1.0'), ('1.12', '1.1')],
    }

    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, 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',
            'pf9_stat',
            'pf9_ip_info',
            'pf9_networks',
            ])
        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)

        # PF9 change: add pf9_stat, ip_info, pf9_networks and pf9_ext properties, if present in db instance
        if 'pf9_stat' in db_compute.keys():
            stats = {}
            for pf9_stat in db_compute.get('pf9_stat'):
                stats[pf9_stat['key']] = pf9_stat['value']
            compute['pf9_stat'] = stats

        if 'pf9_ip_info' in db_compute.keys():
            ips = []
            for ip_info in db_compute.get('pf9_ip_info'):
                ips.append({'interface_name': ip_info['interface_name'], 
                            'ip': ip_info['ip']})
            compute['pf9_ip_info'] = ips
        else:
            compute['pf9_ip_info'] = []
 
        if 'pf9_networks' in db_compute.keys():
            netlist = []
            for net in db_compute.get('pf9_networks'):
               netlist.append(net['network_id'])
            compute['pf9_networks'] = netlist
        else:
            compute['pf9_networks'] = []

        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)

    @base.remotable_classmethod
    def get_by_service_id_and_node_pf9(cls, context, service_id, node):
        db_compute = db.compute_node_get_by_service_id_and_node(context, service_id, node)
        return cls._from_db_object(context, cls(), db_compute)

    @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",
                "pf9_stat", "pf9_networks", "pf9_ip_info"]
        for key in keys:
            if key in resources:
                self[key] = resources[key]

        # supported_instances has a different name in compute_node
        # TODO(pmurray): change virt drivers not to json encode
        # values they add to the resources dict
        if 'supported_instances' in resources:
            si = resources['supported_instances']
            if isinstance(si, six.string_types):
                si = jsonutils.loads(si)
            self.supported_hv_specs = [objects.HVSpec.from_list(s) for s in si]