Merge "Add metadata objects for device tagging"
This commit is contained in:
@@ -50,6 +50,7 @@ def register_all():
|
||||
__import__('nova.objects.instance_pci_requests')
|
||||
__import__('nova.objects.keypair')
|
||||
__import__('nova.objects.migrate_data')
|
||||
__import__('nova.objects.virt_device_metadata')
|
||||
__import__('nova.objects.migration')
|
||||
__import__('nova.objects.migration_context')
|
||||
__import__('nova.objects.monitor_metric')
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
# under the License.
|
||||
|
||||
from oslo_versionedobjects import fields
|
||||
import re
|
||||
import six
|
||||
|
||||
# TODO(berrange) Temporary import for Arch class
|
||||
@@ -64,6 +65,7 @@ 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
|
||||
@@ -645,6 +647,63 @@ class NonNegativeInteger(FieldType):
|
||||
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()
|
||||
|
||||
|
||||
96
nova/objects/virt_device_metadata.py
Normal file
96
nova/objects/virt_device_metadata.py
Normal file
@@ -0,0 +1,96 @@
|
||||
# Copyright (C) 2016, 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 nova.objects import base
|
||||
from nova.objects import fields
|
||||
|
||||
|
||||
@base.NovaObjectRegistry.register_if(False)
|
||||
class DeviceBus(base.NovaObject):
|
||||
VERSION = '1.0'
|
||||
|
||||
|
||||
@base.NovaObjectRegistry.register
|
||||
class PCIDeviceBus(DeviceBus):
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'address': fields.PCIAddressField(),
|
||||
}
|
||||
|
||||
|
||||
@base.NovaObjectRegistry.register
|
||||
class USBDeviceBus(DeviceBus):
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'address': fields.USBAddressField(),
|
||||
}
|
||||
|
||||
|
||||
@base.NovaObjectRegistry.register
|
||||
class SCSIDeviceBus(DeviceBus):
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'address': fields.SCSIAddressField(),
|
||||
}
|
||||
|
||||
|
||||
@base.NovaObjectRegistry.register
|
||||
class IDEDeviceBus(DeviceBus):
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'address': fields.IDEAddressField(),
|
||||
}
|
||||
|
||||
|
||||
@base.NovaObjectRegistry.register
|
||||
class DeviceMetadata(base.NovaObject):
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'bus': fields.ObjectField("DeviceBus", subclasses=True),
|
||||
'tags': fields.ListOfStringsField(),
|
||||
}
|
||||
|
||||
|
||||
@base.NovaObjectRegistry.register
|
||||
class NetworkInterfaceMetadata(DeviceMetadata):
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'mac': fields.MACAddressField(),
|
||||
}
|
||||
|
||||
|
||||
@base.NovaObjectRegistry.register
|
||||
class DiskMetadata(DeviceMetadata):
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'serial': fields.StringField(nullable=True),
|
||||
'path': fields.StringField(nullable=True),
|
||||
}
|
||||
|
||||
|
||||
@base.NovaObjectRegistry.register
|
||||
class DeviceMetadataList(base.ObjectListBase, base.NovaObject):
|
||||
VERSION = '1.0'
|
||||
fields = {
|
||||
'objects': fields.ListOfObjectsField('DeviceMetadata',
|
||||
subclasses=True),
|
||||
}
|
||||
@@ -536,3 +536,70 @@ class TestNotificationAction(TestField):
|
||||
|
||||
def test_stringify_invalid(self):
|
||||
self.assertRaises(ValueError, self.field.stringify, 'magic')
|
||||
|
||||
|
||||
class TestPCIAddress(TestField):
|
||||
def setUp(self):
|
||||
super(TestPCIAddress, self).setUp()
|
||||
self.field = fields.Field(fields.PCIAddressField())
|
||||
self.coerce_good_values = [('0000:00:02.0', '0000:00:02.0')]
|
||||
self.coerce_bad_values = [
|
||||
'000:00:02.0',
|
||||
'0000:0:02.0',
|
||||
'0000:00:2.0',
|
||||
'0000:00:02.',
|
||||
'-000:00:02.0',
|
||||
'0000:0-:02.0',
|
||||
'0000:00:-2.0',
|
||||
'0000:00:02.-',
|
||||
'000000:02.0',
|
||||
'0000:0:02.0',
|
||||
'0000:00:020',
|
||||
]
|
||||
self.to_primitive_values = self.coerce_good_values
|
||||
self.from_primitive_values = self.coerce_good_values
|
||||
|
||||
|
||||
class TestUSBAddress(TestField):
|
||||
def setUp(self):
|
||||
super(TestUSBAddress, self).setUp()
|
||||
self.field = fields.Field(fields.USBAddressField())
|
||||
self.coerce_good_values = [('0:0', '0:0')]
|
||||
self.coerce_bad_values = [
|
||||
'00',
|
||||
'0:',
|
||||
'0.0',
|
||||
'-.0',
|
||||
]
|
||||
self.to_primitive_values = self.coerce_good_values
|
||||
self.from_primitive_values = self.coerce_good_values
|
||||
|
||||
|
||||
class TestSCSIAddress(TestField):
|
||||
def setUp(self):
|
||||
super(TestSCSIAddress, self).setUp()
|
||||
self.field = fields.Field(fields.SCSIAddressField())
|
||||
self.coerce_good_values = [('1:0:2:0', '1:0:2:0')]
|
||||
self.coerce_bad_values = [
|
||||
'1:0:2',
|
||||
'-:0:2:0',
|
||||
'1:-:2:0',
|
||||
'1:0:-:0',
|
||||
'1:0:2:-',
|
||||
]
|
||||
self.to_primitive_values = self.coerce_good_values
|
||||
self.from_primitive_values = self.coerce_good_values
|
||||
|
||||
|
||||
class TestIDEAddress(TestField):
|
||||
def setUp(self):
|
||||
super(TestIDEAddress, self).setUp()
|
||||
self.field = fields.Field(fields.IDEAddressField())
|
||||
self.coerce_good_values = [('0:0', '0:0')]
|
||||
self.coerce_bad_values = [
|
||||
'0:2',
|
||||
'00',
|
||||
'0',
|
||||
]
|
||||
self.to_primitive_values = self.coerce_good_values
|
||||
self.from_primitive_values = self.coerce_good_values
|
||||
|
||||
@@ -36,6 +36,7 @@ from nova import objects
|
||||
from nova.objects import base
|
||||
from nova.objects import fields
|
||||
from nova.objects import notification
|
||||
from nova.objects import virt_device_metadata
|
||||
from nova import test
|
||||
from nova.tests import fixtures as nova_fixtures
|
||||
from nova.tests.unit import fake_notifier
|
||||
@@ -1111,6 +1112,9 @@ object_data = {
|
||||
'ComputeNodeList': '1.14-3b6f4f5ade621c40e70cb116db237844',
|
||||
'DNSDomain': '1.0-7b0b2dab778454b6a7b6c66afe163a1a',
|
||||
'DNSDomainList': '1.0-4ee0d9efdfd681fed822da88376e04d2',
|
||||
'DeviceMetadata': '1.0-04eb8fd218a49cbc3b1e54b774d179f7',
|
||||
'DeviceMetadataList': '1.0-15ecf022a68ddbb8c2a6739cfc9f8f5e',
|
||||
'DiskMetadata': '1.0-e7a0f1ccccf10d26a76b28e7492f3788',
|
||||
'EC2Ids': '1.0-474ee1094c7ec16f8ce657595d8c49d9',
|
||||
'EC2InstanceMapping': '1.0-a4556eb5c5e94c045fe84f49cf71644f',
|
||||
'EC2SnapshotMapping': '1.0-47e7ddabe1af966dce0cfd0ed6cd7cd1',
|
||||
@@ -1125,6 +1129,7 @@ object_data = {
|
||||
'HostMapping': '1.0-1a3390a696792a552ab7bd31a77ba9ac',
|
||||
'HyperVLiveMigrateData': '1.0-0b868dd6228a09c3f3e47016dddf6a1c',
|
||||
'HVSpec': '1.2-db672e73304da86139086d003f3977e7',
|
||||
'IDEDeviceBus': '1.0-29d4c9f27ac44197f01b6ac1b7e16502',
|
||||
'ImageMeta': '1.8-642d1b2eb3e880a367f37d72dd76162d',
|
||||
'ImageMetaProps': '1.12-6a132dee47931447bf86c03c7006d96c',
|
||||
'Instance': '2.1-416fdd0dfc33dfa12ff2cfdd8cc32e17',
|
||||
@@ -1162,10 +1167,12 @@ object_data = {
|
||||
'NUMATopology': '1.2-c63fad38be73b6afd04715c9c1b29220',
|
||||
'NUMATopologyLimits': '1.0-9463e0edd40f64765ae518a539b9dfd2',
|
||||
'Network': '1.2-a977ab383aa462a479b2fae8211a5dde',
|
||||
'NetworkInterfaceMetadata': '1.0-99a9574d086feb5ad45cd04a34855647',
|
||||
'NetworkList': '1.2-69eca910d8fa035dfecd8ba10877ee59',
|
||||
'NetworkRequest': '1.1-7a3e4ca2ce1e7b62d8400488f2f2b756',
|
||||
'NetworkRequestList': '1.1-15ecf022a68ddbb8c2a6739cfc9f8f5e',
|
||||
'PciDevice': '1.5-0d5abe5c91645b8469eb2a93fc53f932',
|
||||
'PCIDeviceBus': '1.0-2b891cb77e42961044689f3dc2718995',
|
||||
'PciDeviceList': '1.3-52ff14355491c8c580bdc0ba34c26210',
|
||||
'PciDevicePool': '1.1-3f5ddc3ff7bfa14da7f6c7e9904cc000',
|
||||
'PciDevicePoolList': '1.1-15ecf022a68ddbb8c2a6739cfc9f8f5e',
|
||||
@@ -1176,6 +1183,7 @@ object_data = {
|
||||
'S3ImageMapping': '1.0-7dd7366a890d82660ed121de9092276e',
|
||||
'SchedulerLimits': '1.0-249c4bd8e62a9b327b7026b7f19cc641',
|
||||
'SchedulerRetries': '1.1-3c9c8b16143ebbb6ad7030e999d14cc0',
|
||||
'SCSIDeviceBus': '1.0-61c1e89a00901069ab1cf2991681533b',
|
||||
'SecurityGroup': '1.1-0e1b9ba42fe85c13c1437f8b74bdb976',
|
||||
'SecurityGroupList': '1.0-dc8bbea01ba09a2edb6e5233eae85cbc',
|
||||
'SecurityGroupRule': '1.1-ae1da17b79970012e8536f88cb3c6b29',
|
||||
@@ -1188,6 +1196,7 @@ object_data = {
|
||||
'TaskLogList': '1.0-cc8cce1af8a283b9d28b55fcd682e777',
|
||||
'Tag': '1.1-8b8d7d5b48887651a0e01241672e2963',
|
||||
'TagList': '1.1-55231bdb671ecf7641d6a2e9109b5d8e',
|
||||
'USBDeviceBus': '1.0-e4c7dd6032e46cd74b027df5eb2d4750',
|
||||
'VirtCPUFeature': '1.0-3310718d8c72309259a6e39bdefe83ee',
|
||||
'VirtCPUModel': '1.0-6a5cc9f322729fc70ddc6733bacd57d3',
|
||||
'VirtCPUTopology': '1.0-fc694de72e20298f7c6bab1083fd4563',
|
||||
@@ -1243,6 +1252,13 @@ class TestObjectVersions(test.NoDBTestCase):
|
||||
self.assertNotEqual(old_hash, new_hash)
|
||||
|
||||
def test_obj_make_compatible(self):
|
||||
# NOTE(danms): This is normally not registered because it is just a
|
||||
# base class. However, the test fixture below requires it to be
|
||||
# in the registry so that it can verify backports based on its
|
||||
# children. So, register it here, which will be reverted after the
|
||||
# cleanUp for this (and all) tests is run.
|
||||
base.NovaObjectRegistry.register(virt_device_metadata.DeviceBus)
|
||||
|
||||
# Iterate all object classes and verify that we can run
|
||||
# obj_make_compatible with every older version than current.
|
||||
# This doesn't actually test the data conversions, but it at least
|
||||
|
||||
Reference in New Issue
Block a user