This patch series adds the ability for a user to specify a tag to be applied to a network interface or a disk when booting an instance. This tag is then exposed through the metadata API. For example, a user boots a VM with two network interfaces. One is connected to a private network, the other to the public Internet. There is currently no direct way to tell which interface is which. Specifying tags allows the user to distinguish between the two interfaces. This patch introduces the new metadata objects. Implements: blueprint virt-device-role-tagging Co-authored-by: Vladik Romanovsky <vromanso@redhat.com> Change-Id: I8e7100f83ef8504f19b156283e0ade0fcb37dc56
849 lines
22 KiB
Python
849 lines
22 KiB
Python
# Copyright 2013 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.
|
|
|
|
from oslo_versionedobjects import fields
|
|
import re
|
|
import six
|
|
|
|
# TODO(berrange) Temporary import for Arch class
|
|
from nova.compute import arch
|
|
# TODO(berrange) Temporary import for CPU* classes
|
|
from nova.compute import cpumodel
|
|
# TODO(berrange) Temporary import for HVType class
|
|
from nova.compute import hv_type
|
|
# TODO(berrange) Temporary import for VMMode class
|
|
from nova.compute import vm_mode
|
|
from nova import exception
|
|
from nova.i18n import _
|
|
from nova.network import model as network_model
|
|
|
|
|
|
# Import field errors from oslo.versionedobjects
|
|
KeyTypeError = fields.KeyTypeError
|
|
ElementTypeError = fields.ElementTypeError
|
|
|
|
|
|
# Import fields from oslo.versionedobjects
|
|
BooleanField = fields.BooleanField
|
|
UnspecifiedDefault = fields.UnspecifiedDefault
|
|
IntegerField = fields.IntegerField
|
|
UUIDField = fields.UUIDField
|
|
FloatField = fields.FloatField
|
|
StringField = fields.StringField
|
|
SensitiveStringField = fields.SensitiveStringField
|
|
EnumField = fields.EnumField
|
|
DateTimeField = fields.DateTimeField
|
|
DictOfStringsField = fields.DictOfStringsField
|
|
DictOfNullableStringsField = fields.DictOfNullableStringsField
|
|
DictOfIntegersField = fields.DictOfIntegersField
|
|
ListOfStringsField = fields.ListOfStringsField
|
|
SetOfIntegersField = fields.SetOfIntegersField
|
|
ListOfSetsOfIntegersField = fields.ListOfSetsOfIntegersField
|
|
ListOfDictOfNullableStringsField = fields.ListOfDictOfNullableStringsField
|
|
DictProxyField = fields.DictProxyField
|
|
ObjectField = fields.ObjectField
|
|
ListOfObjectsField = fields.ListOfObjectsField
|
|
VersionPredicateField = fields.VersionPredicateField
|
|
FlexibleBooleanField = fields.FlexibleBooleanField
|
|
DictOfListOfStringsField = fields.DictOfListOfStringsField
|
|
IPAddressField = fields.IPAddressField
|
|
IPV4AddressField = fields.IPV4AddressField
|
|
IPV6AddressField = fields.IPV6AddressField
|
|
IPNetworkField = fields.IPNetworkField
|
|
IPV4NetworkField = fields.IPV4NetworkField
|
|
IPV6NetworkField = fields.IPV6NetworkField
|
|
AutoTypedField = fields.AutoTypedField
|
|
BaseEnumField = fields.BaseEnumField
|
|
MACAddressField = fields.MACAddressField
|
|
|
|
|
|
# NOTE(danms): These are things we need to import for some of our
|
|
# own implementations below, our tests, or other transitional
|
|
# bits of code. These should be removable after we finish our
|
|
# conversion
|
|
Enum = fields.Enum
|
|
Field = fields.Field
|
|
FieldType = fields.FieldType
|
|
Set = fields.Set
|
|
Dict = fields.Dict
|
|
List = fields.List
|
|
Object = fields.Object
|
|
IPAddress = fields.IPAddress
|
|
IPV4Address = fields.IPV4Address
|
|
IPV6Address = fields.IPV6Address
|
|
IPNetwork = fields.IPNetwork
|
|
IPV4Network = fields.IPV4Network
|
|
IPV6Network = fields.IPV6Network
|
|
|
|
|
|
class Architecture(Enum):
|
|
# TODO(berrange): move all constants out of 'nova.compute.arch'
|
|
# into fields on this class
|
|
def __init__(self, **kwargs):
|
|
super(Architecture, self).__init__(
|
|
valid_values=arch.ALL, **kwargs)
|
|
|
|
def coerce(self, obj, attr, value):
|
|
try:
|
|
value = arch.canonicalize(value)
|
|
except exception.InvalidArchitectureName:
|
|
msg = _("Architecture name '%s' is not valid") % value
|
|
raise ValueError(msg)
|
|
return super(Architecture, self).coerce(obj, attr, value)
|
|
|
|
|
|
class BlockDeviceDestinationType(Enum):
|
|
"""Represents possible destination_type values for a BlockDeviceMapping."""
|
|
|
|
LOCAL = 'local'
|
|
VOLUME = 'volume'
|
|
|
|
ALL = (LOCAL, VOLUME)
|
|
|
|
def __init__(self):
|
|
super(BlockDeviceDestinationType, self).__init__(
|
|
valid_values=BlockDeviceDestinationType.ALL)
|
|
|
|
|
|
class BlockDeviceSourceType(Enum):
|
|
"""Represents the possible source_type values for a BlockDeviceMapping."""
|
|
|
|
BLANK = 'blank'
|
|
IMAGE = 'image'
|
|
SNAPSHOT = 'snapshot'
|
|
VOLUME = 'volume'
|
|
|
|
ALL = (BLANK, IMAGE, SNAPSHOT, VOLUME)
|
|
|
|
def __init__(self):
|
|
super(BlockDeviceSourceType, self).__init__(
|
|
valid_values=BlockDeviceSourceType.ALL)
|
|
|
|
|
|
class BlockDeviceType(Enum):
|
|
"""Represents possible device_type values for a BlockDeviceMapping."""
|
|
|
|
CDROM = 'cdrom'
|
|
DISK = 'disk'
|
|
FLOPPY = 'floppy'
|
|
FS = 'fs'
|
|
LUN = 'lun'
|
|
|
|
ALL = (CDROM, DISK, FLOPPY, FS, LUN)
|
|
|
|
def __init__(self):
|
|
super(BlockDeviceType, self).__init__(
|
|
valid_values=BlockDeviceType.ALL)
|
|
|
|
|
|
class ConfigDrivePolicy(Enum):
|
|
OPTIONAL = "optional"
|
|
MANDATORY = "mandatory"
|
|
|
|
ALL = (OPTIONAL, MANDATORY)
|
|
|
|
def __init__(self):
|
|
super(ConfigDrivePolicy, self).__init__(
|
|
valid_values=ConfigDrivePolicy.ALL)
|
|
|
|
|
|
class CPUAllocationPolicy(Enum):
|
|
|
|
DEDICATED = "dedicated"
|
|
SHARED = "shared"
|
|
|
|
ALL = (DEDICATED, SHARED)
|
|
|
|
def __init__(self):
|
|
super(CPUAllocationPolicy, self).__init__(
|
|
valid_values=CPUAllocationPolicy.ALL)
|
|
|
|
|
|
class CPUThreadAllocationPolicy(Enum):
|
|
|
|
# prefer (default): The host may or may not have hyperthreads. This
|
|
# retains the legacy behavior, whereby siblings are preferred when
|
|
# available. This is the default if no policy is specified.
|
|
PREFER = "prefer"
|
|
# isolate: The host may or many not have hyperthreads. If hyperthreads are
|
|
# present, each vCPU will be placed on a different core and no vCPUs from
|
|
# other guests will be able to be placed on the same core, i.e. one
|
|
# thread sibling is guaranteed to always be unused. If hyperthreads are
|
|
# not present, each vCPU will still be placed on a different core and
|
|
# there are no thread siblings to be concerned with.
|
|
ISOLATE = "isolate"
|
|
# require: The host must have hyperthreads. Each vCPU will be allocated on
|
|
# thread siblings.
|
|
REQUIRE = "require"
|
|
|
|
ALL = (PREFER, ISOLATE, REQUIRE)
|
|
|
|
def __init__(self):
|
|
super(CPUThreadAllocationPolicy, self).__init__(
|
|
valid_values=CPUThreadAllocationPolicy.ALL)
|
|
|
|
|
|
class CPUMode(Enum):
|
|
# TODO(berrange): move all constants out of 'nova.compute.cpumodel'
|
|
# into fields on this class
|
|
def __init__(self, **kwargs):
|
|
super(CPUMode, self).__init__(
|
|
valid_values=cpumodel.ALL_CPUMODES, **kwargs)
|
|
|
|
|
|
class CPUMatch(Enum):
|
|
# TODO(berrange): move all constants out of 'nova.compute.cpumodel'
|
|
# into fields on this class
|
|
def __init__(self, **kwargs):
|
|
super(CPUMatch, self).__init__(
|
|
valid_values=cpumodel.ALL_MATCHES, **kwargs)
|
|
|
|
|
|
class CPUFeaturePolicy(Enum):
|
|
# TODO(berrange): move all constants out of 'nova.compute.cpumodel'
|
|
# into fields on this class
|
|
def __init__(self, **kwargs):
|
|
super(CPUFeaturePolicy, self).__init__(
|
|
valid_values=cpumodel.ALL_POLICIES, **kwargs)
|
|
|
|
|
|
class DiskBus(Enum):
|
|
|
|
FDC = "fdc"
|
|
IDE = "ide"
|
|
SATA = "sata"
|
|
SCSI = "scsi"
|
|
USB = "usb"
|
|
VIRTIO = "virtio"
|
|
XEN = "xen"
|
|
LXC = "lxc"
|
|
UML = "uml"
|
|
|
|
ALL = (FDC, IDE, SATA, SCSI, USB, VIRTIO, XEN, LXC, UML)
|
|
|
|
def __init__(self):
|
|
super(DiskBus, self).__init__(
|
|
valid_values=DiskBus.ALL)
|
|
|
|
|
|
class FirmwareType(Enum):
|
|
|
|
UEFI = "uefi"
|
|
BIOS = "bios"
|
|
|
|
ALL = (UEFI, BIOS)
|
|
|
|
def __init__(self):
|
|
super(FirmwareType, self).__init__(
|
|
valid_values=FirmwareType.ALL)
|
|
|
|
|
|
class HVType(Enum):
|
|
# TODO(berrange): move all constants out of 'nova.compute.hv_type'
|
|
# into fields on this class
|
|
def __init__(self):
|
|
super(HVType, self).__init__(
|
|
valid_values=hv_type.ALL)
|
|
|
|
def coerce(self, obj, attr, value):
|
|
try:
|
|
value = hv_type.canonicalize(value)
|
|
except exception.InvalidHypervisorVirtType:
|
|
msg = _("Hypervisor virt type '%s' is not valid") % value
|
|
raise ValueError(msg)
|
|
return super(HVType, self).coerce(obj, attr, value)
|
|
|
|
|
|
class ImageSignatureHashType(Enum):
|
|
# Represents the possible hash methods used for image signing
|
|
def __init__(self):
|
|
self.hashes = ('SHA-224', 'SHA-256', 'SHA-384', 'SHA-512')
|
|
super(ImageSignatureHashType, self).__init__(
|
|
valid_values=self.hashes
|
|
)
|
|
|
|
|
|
class ImageSignatureKeyType(Enum):
|
|
# Represents the possible keypair types used for image signing
|
|
def __init__(self):
|
|
self.key_types = (
|
|
'DSA', 'ECC_SECT571K1', 'ECC_SECT409K1', 'ECC_SECT571R1',
|
|
'ECC_SECT409R1', 'ECC_SECP521R1', 'ECC_SECP384R1', 'RSA-PSS'
|
|
)
|
|
super(ImageSignatureKeyType, self).__init__(
|
|
valid_values=self.key_types
|
|
)
|
|
|
|
|
|
class OSType(Enum):
|
|
|
|
LINUX = "linux"
|
|
WINDOWS = "windows"
|
|
|
|
ALL = (LINUX, WINDOWS)
|
|
|
|
def __init__(self):
|
|
super(OSType, self).__init__(
|
|
valid_values=OSType.ALL)
|
|
|
|
def coerce(self, obj, attr, value):
|
|
# Some code/docs use upper case or initial caps
|
|
# so canonicalize to all lower case
|
|
value = value.lower()
|
|
return super(OSType, self).coerce(obj, attr, value)
|
|
|
|
|
|
class ResourceClass(Enum):
|
|
"""Classes of resources provided to consumers."""
|
|
|
|
VCPU = 'VCPU'
|
|
MEMORY_MB = 'MEMORY_MB'
|
|
DISK_GB = 'DISK_GB'
|
|
PCI_DEVICE = 'PCI_DEVICE'
|
|
SRIOV_NET_VF = 'SRIOV_NET_VF'
|
|
NUMA_SOCKET = 'NUMA_SOCKET'
|
|
NUMA_CORE = 'NUMA_CORE'
|
|
NUMA_THREAD = 'NUMA_THREAD'
|
|
NUMA_MEMORY_MB = 'NUMA_MEMORY_MB'
|
|
IPV4_ADDRESS = 'IPV4_ADDRESS'
|
|
|
|
# The ordering here is relevant. If you must add a value, only
|
|
# append.
|
|
ALL = (VCPU, MEMORY_MB, DISK_GB, PCI_DEVICE, SRIOV_NET_VF, NUMA_SOCKET,
|
|
NUMA_CORE, NUMA_THREAD, NUMA_MEMORY_MB, IPV4_ADDRESS)
|
|
|
|
def __init__(self):
|
|
super(ResourceClass, self).__init__(
|
|
valid_values=ResourceClass.ALL)
|
|
|
|
@classmethod
|
|
def index(cls, value):
|
|
"""Return an index into the Enum given a value."""
|
|
return cls.ALL.index(value)
|
|
|
|
@classmethod
|
|
def from_index(cls, index):
|
|
"""Return the Enum value at a given index."""
|
|
return cls.ALL[index]
|
|
|
|
|
|
class RNGModel(Enum):
|
|
|
|
VIRTIO = "virtio"
|
|
|
|
ALL = (VIRTIO,)
|
|
|
|
def __init__(self):
|
|
super(RNGModel, self).__init__(
|
|
valid_values=RNGModel.ALL)
|
|
|
|
|
|
class SCSIModel(Enum):
|
|
|
|
BUSLOGIC = "buslogic"
|
|
IBMVSCSI = "ibmvscsi"
|
|
LSILOGIC = "lsilogic"
|
|
LSISAS1068 = "lsisas1068"
|
|
LSISAS1078 = "lsisas1078"
|
|
VIRTIO_SCSI = "virtio-scsi"
|
|
VMPVSCSI = "vmpvscsi"
|
|
|
|
ALL = (BUSLOGIC, IBMVSCSI, LSILOGIC, LSISAS1068,
|
|
LSISAS1078, VIRTIO_SCSI, VMPVSCSI)
|
|
|
|
def __init__(self):
|
|
super(SCSIModel, self).__init__(
|
|
valid_values=SCSIModel.ALL)
|
|
|
|
def coerce(self, obj, attr, value):
|
|
# Some compat for strings we'd see in the legacy
|
|
# vmware_adaptertype image property
|
|
value = value.lower()
|
|
if value == "lsilogicsas":
|
|
value = SCSIModel.LSISAS1068
|
|
elif value == "paravirtual":
|
|
value = SCSIModel.VMPVSCSI
|
|
|
|
return super(SCSIModel, self).coerce(obj, attr, value)
|
|
|
|
|
|
class VideoModel(Enum):
|
|
|
|
CIRRUS = "cirrus"
|
|
QXL = "qxl"
|
|
VGA = "vga"
|
|
VMVGA = "vmvga"
|
|
XEN = "xen"
|
|
|
|
ALL = (CIRRUS, QXL, VGA, VMVGA, XEN)
|
|
|
|
def __init__(self):
|
|
super(VideoModel, self).__init__(
|
|
valid_values=VideoModel.ALL)
|
|
|
|
|
|
class VIFModel(Enum):
|
|
|
|
LEGACY_VALUES = {"virtuale1000":
|
|
network_model.VIF_MODEL_E1000,
|
|
"virtuale1000e":
|
|
network_model.VIF_MODEL_E1000E,
|
|
"virtualpcnet32":
|
|
network_model.VIF_MODEL_PCNET,
|
|
"virtualsriovethernetcard":
|
|
network_model.VIF_MODEL_SRIOV,
|
|
"virtualvmxnet":
|
|
network_model.VIF_MODEL_VMXNET,
|
|
"virtualvmxnet3":
|
|
network_model.VIF_MODEL_VMXNET3,
|
|
}
|
|
|
|
def __init__(self):
|
|
super(VIFModel, self).__init__(
|
|
valid_values=network_model.VIF_MODEL_ALL)
|
|
|
|
def _get_legacy(self, value):
|
|
return value
|
|
|
|
def coerce(self, obj, attr, value):
|
|
# Some compat for strings we'd see in the legacy
|
|
# hw_vif_model image property
|
|
value = value.lower()
|
|
value = VIFModel.LEGACY_VALUES.get(value, value)
|
|
return super(VIFModel, self).coerce(obj, attr, value)
|
|
|
|
|
|
class VMMode(Enum):
|
|
# TODO(berrange): move all constants out of 'nova.compute.vm_mode'
|
|
# into fields on this class
|
|
def __init__(self):
|
|
super(VMMode, self).__init__(
|
|
valid_values=vm_mode.ALL)
|
|
|
|
def coerce(self, obj, attr, value):
|
|
try:
|
|
value = vm_mode.canonicalize(value)
|
|
except exception.InvalidVirtualMachineMode:
|
|
msg = _("Virtual machine mode '%s' is not valid") % value
|
|
raise ValueError(msg)
|
|
return super(VMMode, self).coerce(obj, attr, value)
|
|
|
|
|
|
class WatchdogAction(Enum):
|
|
|
|
NONE = "none"
|
|
PAUSE = "pause"
|
|
POWEROFF = "poweroff"
|
|
RESET = "reset"
|
|
|
|
ALL = (NONE, PAUSE, POWEROFF, RESET)
|
|
|
|
def __init__(self):
|
|
super(WatchdogAction, self).__init__(
|
|
valid_values=WatchdogAction.ALL)
|
|
|
|
|
|
class MonitorMetricType(Enum):
|
|
|
|
CPU_FREQUENCY = "cpu.frequency"
|
|
CPU_USER_TIME = "cpu.user.time"
|
|
CPU_KERNEL_TIME = "cpu.kernel.time"
|
|
CPU_IDLE_TIME = "cpu.idle.time"
|
|
CPU_IOWAIT_TIME = "cpu.iowait.time"
|
|
CPU_USER_PERCENT = "cpu.user.percent"
|
|
CPU_KERNEL_PERCENT = "cpu.kernel.percent"
|
|
CPU_IDLE_PERCENT = "cpu.idle.percent"
|
|
CPU_IOWAIT_PERCENT = "cpu.iowait.percent"
|
|
CPU_PERCENT = "cpu.percent"
|
|
NUMA_MEM_BW_MAX = "numa.membw.max"
|
|
NUMA_MEM_BW_CURRENT = "numa.membw.current"
|
|
|
|
ALL = (
|
|
CPU_FREQUENCY,
|
|
CPU_USER_TIME,
|
|
CPU_KERNEL_TIME,
|
|
CPU_IDLE_TIME,
|
|
CPU_IOWAIT_TIME,
|
|
CPU_USER_PERCENT,
|
|
CPU_KERNEL_PERCENT,
|
|
CPU_IDLE_PERCENT,
|
|
CPU_IOWAIT_PERCENT,
|
|
CPU_PERCENT,
|
|
NUMA_MEM_BW_MAX,
|
|
NUMA_MEM_BW_CURRENT,
|
|
)
|
|
|
|
def __init__(self):
|
|
super(MonitorMetricType, self).__init__(
|
|
valid_values=MonitorMetricType.ALL)
|
|
|
|
|
|
class HostStatus(Enum):
|
|
|
|
UP = "UP" # The nova-compute is up.
|
|
DOWN = "DOWN" # The nova-compute is forced_down.
|
|
MAINTENANCE = "MAINTENANCE" # The nova-compute is disabled.
|
|
UNKNOWN = "UNKNOWN" # The nova-compute has not reported.
|
|
NONE = "" # No host or nova-compute.
|
|
|
|
ALL = (UP, DOWN, MAINTENANCE, UNKNOWN, NONE)
|
|
|
|
def __init__(self):
|
|
super(HostStatus, self).__init__(
|
|
valid_values=HostStatus.ALL)
|
|
|
|
|
|
class PciDeviceStatus(Enum):
|
|
|
|
AVAILABLE = "available"
|
|
CLAIMED = "claimed"
|
|
ALLOCATED = "allocated"
|
|
REMOVED = "removed" # The device has been hot-removed and not yet deleted
|
|
DELETED = "deleted" # The device is marked not available/deleted.
|
|
UNCLAIMABLE = "unclaimable"
|
|
UNAVAILABLE = "unavailable"
|
|
|
|
ALL = (AVAILABLE, CLAIMED, ALLOCATED, REMOVED, DELETED, UNAVAILABLE,
|
|
UNCLAIMABLE)
|
|
|
|
def __init__(self):
|
|
super(PciDeviceStatus, self).__init__(
|
|
valid_values=PciDeviceStatus.ALL)
|
|
|
|
|
|
class PciDeviceType(Enum):
|
|
|
|
# NOTE(jaypipes): It's silly that the word "type-" is in these constants,
|
|
# but alas, these were the original constant strings used...
|
|
STANDARD = "type-PCI"
|
|
SRIOV_PF = "type-PF"
|
|
SRIOV_VF = "type-VF"
|
|
|
|
ALL = (STANDARD, SRIOV_PF, SRIOV_VF)
|
|
|
|
def __init__(self):
|
|
super(PciDeviceType, self).__init__(
|
|
valid_values=PciDeviceType.ALL)
|
|
|
|
|
|
class DiskFormat(Enum):
|
|
RBD = "rbd"
|
|
LVM = "lvm"
|
|
QCOW2 = "qcow2"
|
|
RAW = "raw"
|
|
PLOOP = "ploop"
|
|
VHD = "vhd"
|
|
VMDK = "vmdk"
|
|
VDI = "vdi"
|
|
ISO = "iso"
|
|
|
|
ALL = (RBD, LVM, QCOW2, RAW, PLOOP, VHD, VMDK, VDI, ISO)
|
|
|
|
def __init__(self):
|
|
super(DiskFormat, self).__init__(
|
|
valid_values=DiskFormat.ALL)
|
|
|
|
|
|
class NotificationPriority(Enum):
|
|
AUDIT = 'audit'
|
|
CRITICAL = 'critical'
|
|
DEBUG = 'debug'
|
|
INFO = 'info'
|
|
ERROR = 'error'
|
|
SAMPLE = 'sample'
|
|
WARN = 'warn'
|
|
|
|
ALL = (AUDIT, CRITICAL, DEBUG, INFO, ERROR, SAMPLE, WARN)
|
|
|
|
def __init__(self):
|
|
super(NotificationPriority, self).__init__(
|
|
valid_values=NotificationPriority.ALL)
|
|
|
|
|
|
class NotificationPhase(Enum):
|
|
START = 'start'
|
|
END = 'end'
|
|
ERROR = 'error'
|
|
|
|
ALL = (START, END, ERROR)
|
|
|
|
def __init__(self):
|
|
super(NotificationPhase, self).__init__(
|
|
valid_values=NotificationPhase.ALL)
|
|
|
|
|
|
class NotificationAction(Enum):
|
|
UPDATE = 'update'
|
|
|
|
ALL = (UPDATE,)
|
|
|
|
def __init__(self):
|
|
super(NotificationAction, self).__init__(
|
|
valid_values=NotificationAction.ALL)
|
|
|
|
|
|
class IPV4AndV6Address(IPAddress):
|
|
@staticmethod
|
|
def coerce(obj, attr, value):
|
|
result = IPAddress.coerce(obj, attr, value)
|
|
if result.version != 4 and result.version != 6:
|
|
raise ValueError(_('Network "%(val)s" is not valid '
|
|
'in field %(attr)s') %
|
|
{'val': value, 'attr': attr})
|
|
return result
|
|
|
|
|
|
class NetworkModel(FieldType):
|
|
@staticmethod
|
|
def coerce(obj, attr, value):
|
|
if isinstance(value, network_model.NetworkInfo):
|
|
return value
|
|
elif isinstance(value, six.string_types):
|
|
# Hmm, do we need this?
|
|
return network_model.NetworkInfo.hydrate(value)
|
|
else:
|
|
raise ValueError(_('A NetworkModel is required in field %s') %
|
|
attr)
|
|
|
|
@staticmethod
|
|
def to_primitive(obj, attr, value):
|
|
return value.json()
|
|
|
|
@staticmethod
|
|
def from_primitive(obj, attr, value):
|
|
return network_model.NetworkInfo.hydrate(value)
|
|
|
|
def stringify(self, value):
|
|
return 'NetworkModel(%s)' % (
|
|
','.join([str(vif['id']) for vif in value]))
|
|
|
|
|
|
class NonNegativeFloat(FieldType):
|
|
@staticmethod
|
|
def coerce(obj, attr, value):
|
|
v = float(value)
|
|
if v < 0:
|
|
raise ValueError(_('Value must be >= 0 for field %s') % attr)
|
|
return v
|
|
|
|
|
|
class NonNegativeInteger(FieldType):
|
|
@staticmethod
|
|
def coerce(obj, attr, value):
|
|
v = int(value)
|
|
if v < 0:
|
|
raise ValueError(_('Value must be >= 0 for field %s') % attr)
|
|
return v
|
|
|
|
|
|
class AddressBase(FieldType):
|
|
@staticmethod
|
|
def coerce(obj, attr, value):
|
|
if re.match(obj.PATTERN, str(value)):
|
|
return str(value)
|
|
else:
|
|
raise ValueError(_('Value must match %s') % obj.PATTERN)
|
|
|
|
|
|
class PCIAddress(AddressBase):
|
|
PATTERN = '[a-f0-9]{4}:[a-f0-9]{2}:[a-f0-9]{2}.[a-f0-9]'
|
|
|
|
@staticmethod
|
|
def coerce(obj, attr, value):
|
|
return AddressBase.coerce(PCIAddress, attr, value)
|
|
|
|
|
|
class USBAddress(AddressBase):
|
|
PATTERN = '[a-f0-9]+:[a-f0-9]+'
|
|
|
|
@staticmethod
|
|
def coerce(obj, attr, value):
|
|
return AddressBase.coerce(USBAddress, attr, value)
|
|
|
|
|
|
class SCSIAddress(AddressBase):
|
|
PATTERN = '[a-f0-9]+:[a-f0-9]+:[a-f0-9]+:[a-f0-9]+'
|
|
|
|
@staticmethod
|
|
def coerce(obj, attr, value):
|
|
return AddressBase.coerce(SCSIAddress, attr, value)
|
|
|
|
|
|
class IDEAddress(AddressBase):
|
|
PATTERN = '[0-1]:[0-1]'
|
|
|
|
@staticmethod
|
|
def coerce(obj, attr, value):
|
|
return AddressBase.coerce(IDEAddress, attr, value)
|
|
|
|
|
|
class PCIAddressField(AutoTypedField):
|
|
AUTO_TYPE = PCIAddress()
|
|
|
|
|
|
class USBAddressField(AutoTypedField):
|
|
AUTO_TYPE = USBAddress()
|
|
|
|
|
|
class SCSIAddressField(AutoTypedField):
|
|
AUTO_TYPE = SCSIAddress()
|
|
|
|
|
|
class IDEAddressField(AutoTypedField):
|
|
AUTO_TYPE = IDEAddress()
|
|
|
|
|
|
class ArchitectureField(BaseEnumField):
|
|
AUTO_TYPE = Architecture()
|
|
|
|
|
|
class BlockDeviceDestinationTypeField(BaseEnumField):
|
|
AUTO_TYPE = BlockDeviceDestinationType()
|
|
|
|
|
|
class BlockDeviceSourceTypeField(BaseEnumField):
|
|
AUTO_TYPE = BlockDeviceSourceType()
|
|
|
|
|
|
class BlockDeviceTypeField(BaseEnumField):
|
|
AUTO_TYPE = BlockDeviceType()
|
|
|
|
|
|
class ConfigDrivePolicyField(BaseEnumField):
|
|
AUTO_TYPE = ConfigDrivePolicy()
|
|
|
|
|
|
class CPUAllocationPolicyField(BaseEnumField):
|
|
AUTO_TYPE = CPUAllocationPolicy()
|
|
|
|
|
|
class CPUThreadAllocationPolicyField(BaseEnumField):
|
|
AUTO_TYPE = CPUThreadAllocationPolicy()
|
|
|
|
|
|
class CPUModeField(BaseEnumField):
|
|
AUTO_TYPE = CPUMode()
|
|
|
|
|
|
class CPUMatchField(BaseEnumField):
|
|
AUTO_TYPE = CPUMatch()
|
|
|
|
|
|
class CPUFeaturePolicyField(BaseEnumField):
|
|
AUTO_TYPE = CPUFeaturePolicy()
|
|
|
|
|
|
class DiskBusField(BaseEnumField):
|
|
AUTO_TYPE = DiskBus()
|
|
|
|
|
|
class FirmwareTypeField(BaseEnumField):
|
|
AUTO_TYPE = FirmwareType()
|
|
|
|
|
|
class HVTypeField(BaseEnumField):
|
|
AUTO_TYPE = HVType()
|
|
|
|
|
|
class ImageSignatureHashTypeField(BaseEnumField):
|
|
AUTO_TYPE = ImageSignatureHashType()
|
|
|
|
|
|
class ImageSignatureKeyTypeField(BaseEnumField):
|
|
AUTO_TYPE = ImageSignatureKeyType()
|
|
|
|
|
|
class OSTypeField(BaseEnumField):
|
|
AUTO_TYPE = OSType()
|
|
|
|
|
|
class ResourceClassField(BaseEnumField):
|
|
AUTO_TYPE = ResourceClass()
|
|
|
|
def index(self, value):
|
|
"""Return an index into the Enum given a value."""
|
|
return self._type.index(value)
|
|
|
|
def from_index(self, index):
|
|
"""Return the Enum value at a given index."""
|
|
return self._type.from_index(index)
|
|
|
|
|
|
class RNGModelField(BaseEnumField):
|
|
AUTO_TYPE = RNGModel()
|
|
|
|
|
|
class SCSIModelField(BaseEnumField):
|
|
AUTO_TYPE = SCSIModel()
|
|
|
|
|
|
class VideoModelField(BaseEnumField):
|
|
AUTO_TYPE = VideoModel()
|
|
|
|
|
|
class VIFModelField(BaseEnumField):
|
|
AUTO_TYPE = VIFModel()
|
|
|
|
|
|
class VMModeField(BaseEnumField):
|
|
AUTO_TYPE = VMMode()
|
|
|
|
|
|
class WatchdogActionField(BaseEnumField):
|
|
AUTO_TYPE = WatchdogAction()
|
|
|
|
|
|
class MonitorMetricTypeField(BaseEnumField):
|
|
AUTO_TYPE = MonitorMetricType()
|
|
|
|
|
|
class PciDeviceStatusField(BaseEnumField):
|
|
AUTO_TYPE = PciDeviceStatus()
|
|
|
|
|
|
class PciDeviceTypeField(BaseEnumField):
|
|
AUTO_TYPE = PciDeviceType()
|
|
|
|
|
|
class DiskFormatField(BaseEnumField):
|
|
AUTO_TYPE = DiskFormat()
|
|
|
|
|
|
class NotificationPriorityField(BaseEnumField):
|
|
AUTO_TYPE = NotificationPriority()
|
|
|
|
|
|
class NotificationPhaseField(BaseEnumField):
|
|
AUTO_TYPE = NotificationPhase()
|
|
|
|
|
|
class NotificationActionField(BaseEnumField):
|
|
AUTO_TYPE = NotificationAction()
|
|
|
|
|
|
class IPV4AndV6AddressField(AutoTypedField):
|
|
AUTO_TYPE = IPV4AndV6Address()
|
|
|
|
|
|
class ListOfIntegersField(AutoTypedField):
|
|
AUTO_TYPE = List(fields.Integer())
|
|
|
|
|
|
class NonNegativeFloatField(AutoTypedField):
|
|
AUTO_TYPE = NonNegativeFloat()
|
|
|
|
|
|
class NonNegativeIntegerField(AutoTypedField):
|
|
AUTO_TYPE = NonNegativeInteger()
|