
This change adds a new hw:locked_memory extra spec and hw_locked_memory image property to contol preventing guest memory from swapping. This change adds docs and extend the flavor validators for the new extra spec. Also add new image property. Blueprint: libvirt-viommu-device Change-Id: Id3779594f0078a5045031aded2ed68ee4301abbd
741 lines
30 KiB
Python
741 lines
30 KiB
Python
# Copyright 2014 Red Hat, Inc
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import copy
|
|
|
|
from oslo_utils import versionutils
|
|
|
|
from nova import exception
|
|
from nova import objects
|
|
from nova.objects import base
|
|
from nova.objects import fields
|
|
from nova import utils
|
|
from nova.virt import hardware
|
|
|
|
|
|
NULLABLE_STRING_FIELDS = ['name', 'checksum', 'owner',
|
|
'container_format', 'disk_format']
|
|
NULLABLE_INTEGER_FIELDS = ['size', 'virtual_size']
|
|
|
|
|
|
@base.NovaObjectRegistry.register
|
|
class ImageMeta(base.NovaObject):
|
|
# Version 1.0: Initial version
|
|
# Version 1.1: updated ImageMetaProps
|
|
# Version 1.2: ImageMetaProps version 1.2
|
|
# Version 1.3: ImageMetaProps version 1.3
|
|
# Version 1.4: ImageMetaProps version 1.4
|
|
# Version 1.5: ImageMetaProps version 1.5
|
|
# Version 1.6: ImageMetaProps version 1.6
|
|
# Version 1.7: ImageMetaProps version 1.7
|
|
# Version 1.8: ImageMetaProps version 1.8
|
|
VERSION = '1.8'
|
|
|
|
# These are driven by what the image client API returns
|
|
# to Nova from Glance. This is defined in the glance
|
|
# code glance/api/v2/images.py get_base_properties()
|
|
# method. A few things are currently left out:
|
|
# self, file, schema - Nova does not appear to ever use
|
|
# these field; locations - modelling the arbitrary
|
|
# data in the 'metadata' subfield is non-trivial as
|
|
# there's no clear spec.
|
|
#
|
|
# TODO(ft): In version 2.0, these fields should be nullable:
|
|
# name, checksum, owner, size, virtual_size, container_format, disk_format
|
|
#
|
|
fields = {
|
|
'id': fields.UUIDField(),
|
|
'name': fields.StringField(),
|
|
'status': fields.StringField(),
|
|
'visibility': fields.StringField(),
|
|
'protected': fields.FlexibleBooleanField(),
|
|
'checksum': fields.StringField(),
|
|
'owner': fields.StringField(),
|
|
'size': fields.IntegerField(),
|
|
'virtual_size': fields.IntegerField(),
|
|
'container_format': fields.StringField(),
|
|
'disk_format': fields.StringField(),
|
|
'created_at': fields.DateTimeField(nullable=True),
|
|
'updated_at': fields.DateTimeField(nullable=True),
|
|
'tags': fields.ListOfStringsField(),
|
|
'direct_url': fields.StringField(),
|
|
'min_ram': fields.IntegerField(),
|
|
'min_disk': fields.IntegerField(),
|
|
'properties': fields.ObjectField('ImageMetaProps'),
|
|
}
|
|
|
|
@classmethod
|
|
def from_dict(cls, image_meta):
|
|
"""Create instance from image metadata dict
|
|
|
|
:param image_meta: image metadata dictionary
|
|
|
|
Creates a new object instance, initializing from the
|
|
properties associated with the image metadata instance
|
|
|
|
:returns: an ImageMeta instance
|
|
"""
|
|
if image_meta is None:
|
|
image_meta = {}
|
|
|
|
# We must turn 'properties' key dict into an object
|
|
# so copy image_meta to avoid changing original
|
|
image_meta = copy.deepcopy(image_meta)
|
|
image_meta["properties"] = \
|
|
objects.ImageMetaProps.from_dict(
|
|
image_meta.get("properties", {}))
|
|
|
|
# Some fields are nullable in Glance DB schema, but was not marked that
|
|
# in ImageMeta initially by mistake. To keep compatibility with compute
|
|
# nodes which are run with previous versions these fields are still
|
|
# not nullable in ImageMeta, but the code below converts None to
|
|
# appropriate empty values.
|
|
for fld in NULLABLE_STRING_FIELDS:
|
|
if fld in image_meta and image_meta[fld] is None:
|
|
image_meta[fld] = ''
|
|
for fld in NULLABLE_INTEGER_FIELDS:
|
|
if fld in image_meta and image_meta[fld] is None:
|
|
image_meta[fld] = 0
|
|
|
|
return cls(**image_meta)
|
|
|
|
@classmethod
|
|
def from_instance(cls, instance):
|
|
"""Create instance from instance system metadata
|
|
|
|
:param instance: Instance object
|
|
|
|
Creates a new object instance, initializing from the
|
|
system metadata "image_*" properties associated with
|
|
instance
|
|
|
|
:returns: an ImageMeta instance
|
|
"""
|
|
sysmeta = utils.instance_sys_meta(instance)
|
|
image_meta = utils.get_image_from_system_metadata(sysmeta)
|
|
|
|
# NOTE(lyarwood): Provide the id of the image in image_meta if it
|
|
# wasn't persisted in the system_metadata of the instance previously.
|
|
# This is only provided to allow users of image_meta to avoid the need
|
|
# to pass around references to instance.image_ref alongside image_meta.
|
|
if image_meta.get('id') is None and instance.image_ref:
|
|
image_meta['id'] = instance.image_ref
|
|
|
|
return cls.from_dict(image_meta)
|
|
|
|
@classmethod
|
|
def from_image_ref(cls, context, image_api, image_ref):
|
|
"""Create instance from glance image
|
|
|
|
:param context: the request context
|
|
:param image_api: the glance client API
|
|
:param image_ref: the glance image identifier
|
|
|
|
Creates a new object instance, initializing from the
|
|
properties associated with a glance image
|
|
|
|
:returns: an ImageMeta instance
|
|
"""
|
|
|
|
image_meta = image_api.get(context, image_ref)
|
|
image = cls.from_dict(image_meta)
|
|
setattr(image, "id", image_ref)
|
|
return image
|
|
|
|
|
|
@base.NovaObjectRegistry.register
|
|
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.24: Added 'hw_mem_encryption' field
|
|
# Version 1.25: Added 'hw_pci_numa_affinity_policy' field
|
|
# Version 1.26: Added 'mixed' to 'hw_cpu_policy' field
|
|
# Version 1.27: Added 'hw_tpm_model' and 'hw_tpm_version' fields
|
|
# Version 1.28: Added 'socket' to 'hw_pci_numa_affinity_policy'
|
|
# Version 1.29: Added 'hw_input_bus' field
|
|
# Version 1.30: Added 'bochs' as an option to 'hw_video_model'
|
|
# Version 1.31: Added 'hw_emulation_architecture' field
|
|
# Version 1.32: Added 'hw_ephemeral_encryption' and
|
|
# 'hw_ephemeral_encryption_format' fields
|
|
# Version 1.33: Added 'hw_locked_memory' field
|
|
# NOTE(efried): When bumping this version, the version of
|
|
# ImageMetaPropsPayload must also be bumped. See its docstring for details.
|
|
VERSION = '1.33'
|
|
|
|
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, 33):
|
|
primitive.pop('hw_locked_memory', None)
|
|
if target_version < (1, 32):
|
|
primitive.pop('hw_ephemeral_encryption', None)
|
|
primitive.pop('hw_ephemeral_encryption_format', None)
|
|
if target_version < (1, 31):
|
|
primitive.pop('hw_emulation_architecture', None)
|
|
if target_version < (1, 30):
|
|
video = primitive.get('hw_video_model', None)
|
|
if video == fields.VideoModel.BOCHS:
|
|
raise exception.ObjectActionError(
|
|
action='obj_make_compatible',
|
|
reason='hw_video_model=%s not supported in version %s' %
|
|
(video, target_version))
|
|
if target_version < (1, 29):
|
|
primitive.pop('hw_input_bus', None)
|
|
if target_version < (1, 28):
|
|
policy = primitive.get('hw_pci_numa_affinity_policy', None)
|
|
if policy == fields.PCINUMAAffinityPolicy.SOCKET:
|
|
raise exception.ObjectActionError(
|
|
action='obj_make_compatible',
|
|
reason='hw_numa_affinity_policy=%s not supported '
|
|
'in version %s' %
|
|
(policy, target_version))
|
|
if target_version < (1, 27):
|
|
primitive.pop('hw_tpm_model', None)
|
|
primitive.pop('hw_tpm_version', None)
|
|
if target_version < (1, 26):
|
|
policy = primitive.get('hw_cpu_policy', None)
|
|
if policy == fields.CPUAllocationPolicy.MIXED:
|
|
raise exception.ObjectActionError(
|
|
action='obj_make_compatible',
|
|
reason='hw_cpu_policy=%s not supported in version %s' %
|
|
(policy, target_version))
|
|
if target_version < (1, 25):
|
|
primitive.pop('hw_pci_numa_affinity_policy', None)
|
|
if target_version < (1, 24):
|
|
primitive.pop('hw_mem_encryption', None)
|
|
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(),
|
|
|
|
# hw_architecture field is leveraged for checks against physical nodes
|
|
# name of desired emulation architecture eg i686, x86_64, ppc64
|
|
'hw_emulation_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(),
|
|
|
|
# name of the input bus type to use, e.g. usb, virtio
|
|
'hw_input_bus': fields.InputBusField(),
|
|
|
|
# boolean - used to trigger code to inject networking when booting a CD
|
|
# image with a network boot image
|
|
'hw_ipxe_boot': fields.FlexibleBooleanField(),
|
|
|
|
# string - make sure ``locked`` element is present in the
|
|
# ``memoryBacking``.
|
|
'hw_locked_memory': 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(),
|
|
|
|
# boolean indicating that the guest needs to be booted with
|
|
# encrypted memory
|
|
'hw_mem_encryption': fields.FlexibleBooleanField(),
|
|
|
|
# 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(),
|
|
|
|
# Enum field to specify pci device NUMA affinity.
|
|
'hw_pci_numa_affinity_policy': fields.PCINUMAAffinityPolicyField(),
|
|
|
|
# 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
|
|
# NOTE(kchamart): Although this is currently not used anymore,
|
|
# we should not remove / deprecate it yet, as we are likely to
|
|
# extend this field to allow two more values to support "VirtIO
|
|
# transitional/non-transitional devices" (refer to the note in
|
|
# RNGModel() class in nova/objects/fields.py), and thus expose
|
|
# to the user again.
|
|
'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(),
|
|
|
|
# name of emulated TPM model to use.
|
|
'hw_tpm_model': fields.TPMModelField(),
|
|
# version of emulated TPM to use.
|
|
'hw_tpm_version': fields.TPMVersionField(),
|
|
|
|
# boolean - if true will enable ephemeral encryption for instance
|
|
'hw_ephemeral_encryption': fields.FlexibleBooleanField(),
|
|
# encryption format to be used when ephemeral encryption is enabled
|
|
'hw_ephemeral_encryption_format':
|
|
fields.BlockDeviceEncryptionFormatTypeField(),
|
|
|
|
# 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 [str(k[6:]) for k, v in image_props.items()
|
|
if str(k).startswith("trait:") and
|
|
str(v) == '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)
|