diff options
59 files changed, 354 insertions, 57 deletions
diff --git a/nova/api/openstack/compute/contrib/attach_interfaces.py b/nova/api/openstack/compute/contrib/attach_interfaces.py index 7c79dbcf7f..481e1a3e0a 100644 --- a/nova/api/openstack/compute/contrib/attach_interfaces.py +++ b/nova/api/openstack/compute/contrib/attach_interfaces.py @@ -135,7 +135,7 @@ class InterfaceAttachmentController(object): except NotImplementedError: msg = _("Network driver does not support this function.") raise webob.exc.HTTPNotImplemented(explanation=msg) - except exception.InterfaceAttachFailed as e: + except exception.InterfaceAttachFailed: msg = _("Failed to attach interface") raise webob.exc.HTTPInternalServerError(explanation=msg) except exception.InstanceInvalidState as state_error: diff --git a/nova/api/openstack/compute/contrib/consoles.py b/nova/api/openstack/compute/contrib/consoles.py index 39ed03f70d..0a8a00aac3 100644 --- a/nova/api/openstack/compute/contrib/consoles.py +++ b/nova/api/openstack/compute/contrib/consoles.py @@ -44,7 +44,7 @@ class ConsolesController(wsgi.Controller): output = self.compute_api.get_vnc_console(context, instance, console_type) - except exception.InstanceNotReady as e: + except exception.InstanceNotReady: raise webob.exc.HTTPConflict( explanation=_('Instance not yet ready')) except exception.InstanceNotFound as e: diff --git a/nova/api/openstack/compute/contrib/server_external_events.py b/nova/api/openstack/compute/contrib/server_external_events.py index 2e33178305..fcc42b9c82 100644 --- a/nova/api/openstack/compute/contrib/server_external_events.py +++ b/nova/api/openstack/compute/contrib/server_external_events.py @@ -55,7 +55,7 @@ class ServerExternalEventsController(wsgi.Controller): client_event = dict(_event) event = objects.InstanceExternalEvent(context) - status = client_event.get('status') + status = client_event.get('status', 'completed') if status not in external_event_obj.EVENT_STATUSES: raise webob.exc.HTTPBadRequest( _('Invalid event status `%s\'') % status) diff --git a/nova/api/openstack/compute/plugins/v3/servers.py b/nova/api/openstack/compute/plugins/v3/servers.py index da14b29657..f263a8094f 100644 --- a/nova/api/openstack/compute/plugins/v3/servers.py +++ b/nova/api/openstack/compute/plugins/v3/servers.py @@ -563,7 +563,7 @@ class ServersController(wsgi.Controller): try: flavor_id = self._flavor_id_from_req_data(body) - except ValueError as error: + except ValueError: msg = _("Invalid flavorRef provided.") raise exc.HTTPBadRequest(explanation=msg) diff --git a/nova/api/openstack/compute/servers.py b/nova/api/openstack/compute/servers.py index 8f8f90f396..e0e1de6d1e 100644 --- a/nova/api/openstack/compute/servers.py +++ b/nova/api/openstack/compute/servers.py @@ -636,7 +636,7 @@ class Controller(wsgi.Controller): except UnicodeDecodeError as error: msg = "UnicodeError: %s" % error raise exc.HTTPBadRequest(explanation=msg) - except Exception as error: + except Exception: # The remaining cases can be handled in a standard fashion. self._handle_create_exception(*sys.exc_info()) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index ec76bf2b58..d08e87026d 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -1386,15 +1386,18 @@ class ComputeManager(manager.Manager): volume = self.volume_api.get(context, vol_id) volume_status = volume['status'] if volume_status not in ['creating', 'downloading']: - if volume_status != 'available': - LOG.warning(_LW("Volume id: %s finished being created but " - "was not set as 'available'"), vol_id) - return attempt + if volume_status == 'available': + return attempt + LOG.warning(_LW("Volume id: %(vol_id)s finished being " + "created but its status is %(vol_status)s."), + {'vol_id': vol_id, + 'vol_status': volume_status}) + break greenthread.sleep(CONF.block_device_allocate_retries_interval) - # NOTE(harlowja): Should only happen if we ran out of attempts raise exception.VolumeNotCreated(volume_id=vol_id, seconds=int(time.time() - start), - attempts=attempts) + attempts=attempt, + volume_status=volume_status) def _decode_files(self, injected_files): """Base64 decode the list of files to inject.""" diff --git a/nova/exception.py b/nova/exception.py index bba76200c0..1f36eb4495 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -275,7 +275,7 @@ class VolumeUnattached(Invalid): class VolumeNotCreated(NovaException): msg_fmt = _("Volume %(volume_id)s did not finish being created" " even after we waited %(seconds)s seconds or %(attempts)s" - " attempts.") + " attempts. And its status is %(volume_status)s.") class InvalidKeypair(Invalid): diff --git a/nova/objects/agent.py b/nova/objects/agent.py index 260737288e..d6d5b04041 100644 --- a/nova/objects/agent.py +++ b/nova/objects/agent.py @@ -20,6 +20,7 @@ from nova.objects import fields # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class Agent(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): VERSION = '1.0' @@ -70,6 +71,7 @@ class Agent(base.NovaPersistentObject, base.NovaObject, self.obj_reset_changes() +@base.NovaObjectRegistry.register class AgentList(base.ObjectListBase, base.NovaObject): VERSION = '1.0' diff --git a/nova/objects/aggregate.py b/nova/objects/aggregate.py index 937196fb44..3c2a03ddaf 100644 --- a/nova/objects/aggregate.py +++ b/nova/objects/aggregate.py @@ -21,6 +21,7 @@ from nova.objects import fields # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class Aggregate(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -150,6 +151,7 @@ class Aggregate(base.NovaPersistentObject, base.NovaObject, return self.metadata.get('availability_zone', None) +@base.NovaObjectRegistry.register class AggregateList(base.ObjectListBase, base.NovaObject): # Version 1.0: Initial version # Version 1.1: Added key argument to get_by_host() diff --git a/nova/objects/bandwidth_usage.py b/nova/objects/bandwidth_usage.py index dc463e1837..918df017ee 100644 --- a/nova/objects/bandwidth_usage.py +++ b/nova/objects/bandwidth_usage.py @@ -16,6 +16,7 @@ from nova.objects import fields # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class BandwidthUsage(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -65,6 +66,7 @@ class BandwidthUsage(base.NovaPersistentObject, base.NovaObject, self._from_db_object(self._context, self, db_bw_usage) +@base.NovaObjectRegistry.register class BandwidthUsageList(base.ObjectListBase, base.NovaObject): # Version 1.0: Initial version # Version 1.1: Add use_slave to get_by_uuids diff --git a/nova/objects/base.py b/nova/objects/base.py index 58a4beddc7..0cf23cf0b4 100644 --- a/nova/objects/base.py +++ b/nova/objects/base.py @@ -97,6 +97,17 @@ def make_class_properties(cls): setattr(cls, name, property(getter, setter, deleter)) +# NOTE(danms): This is transitional to get the registration decorator +# on everything before we make a cut over +class NovaObjectRegistry(object): + classes = [] + + @classmethod + def register(cls, obj_cls): + cls.classes.append(obj_cls.obj_name()) + return obj_cls + + class NovaObjectMetaclass(type): """Metaclass that allows tracking of object classes.""" diff --git a/nova/objects/block_device.py b/nova/objects/block_device.py index 9210df6e1c..5fa2e147f4 100644 --- a/nova/objects/block_device.py +++ b/nova/objects/block_device.py @@ -38,6 +38,7 @@ def _expected_cols(expected_attrs): # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class BlockDeviceMapping(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -232,6 +233,7 @@ class BlockDeviceMapping(base.NovaPersistentObject, base.NovaObject, self.obj_reset_changes(fields=['instance']) +@base.NovaObjectRegistry.register class BlockDeviceMappingList(base.ObjectListBase, base.NovaObject): # Version 1.0: Initial version # Version 1.1: BlockDeviceMapping <= version 1.1 diff --git a/nova/objects/cell_mapping.py b/nova/objects/cell_mapping.py index b8c76e2843..f337e4d330 100644 --- a/nova/objects/cell_mapping.py +++ b/nova/objects/cell_mapping.py @@ -17,6 +17,7 @@ from nova.objects import base from nova.objects import fields +@base.NovaObjectRegistry.register class CellMapping(base.NovaTimestampObject, base.NovaObject): # Version 1.0: Initial version VERSION = '1.0' diff --git a/nova/objects/compute_node.py b/nova/objects/compute_node.py index e11ca577b7..27625f9db8 100644 --- a/nova/objects/compute_node.py +++ b/nova/objects/compute_node.py @@ -24,6 +24,7 @@ from nova import utils # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class ComputeNode(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -271,6 +272,7 @@ class ComputeNode(base.NovaPersistentObject, base.NovaObject, return self._cached_service +@base.NovaObjectRegistry.register class ComputeNodeList(base.ObjectListBase, base.NovaObject): # Version 1.0: Initial version # ComputeNode <= version 1.2 diff --git a/nova/objects/dns_domain.py b/nova/objects/dns_domain.py index 08f333b245..7dd5bd6675 100644 --- a/nova/objects/dns_domain.py +++ b/nova/objects/dns_domain.py @@ -19,6 +19,7 @@ from nova.objects import fields # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class DNSDomain(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -58,6 +59,7 @@ class DNSDomain(base.NovaPersistentObject, base.NovaObject, db.dnsdomain_unregister(context, domain) +@base.NovaObjectRegistry.register class DNSDomainList(base.ObjectListBase, base.NovaObject): # Version 1.0: Initial version VERSION = '1.0' diff --git a/nova/objects/ec2.py b/nova/objects/ec2.py index a02cbf6bba..18e7d06bee 100644 --- a/nova/objects/ec2.py +++ b/nova/objects/ec2.py @@ -20,6 +20,7 @@ from nova.objects import fields # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class EC2InstanceMapping(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -60,6 +61,7 @@ class EC2InstanceMapping(base.NovaPersistentObject, base.NovaObject, # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class EC2VolumeMapping(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -100,6 +102,7 @@ class EC2VolumeMapping(base.NovaPersistentObject, base.NovaObject, # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class EC2SnapshotMapping(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -140,6 +143,7 @@ class EC2SnapshotMapping(base.NovaPersistentObject, base.NovaObject, # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class S3ImageMapping(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -179,6 +183,7 @@ class S3ImageMapping(base.NovaPersistentObject, base.NovaObject, return cls._from_db_object(context, cls(context), db_s3imap) +@base.NovaObjectRegistry.register class EC2Ids(base.NovaObject): # Version 1.0: Initial version VERSION = '1.0' diff --git a/nova/objects/external_event.py b/nova/objects/external_event.py index 1effae001f..1d884e6e02 100644 --- a/nova/objects/external_event.py +++ b/nova/objects/external_event.py @@ -29,6 +29,7 @@ EVENT_STATUSES = ['failed', 'completed', 'in-progress'] # TODO(berrange): Remove NovaObjectDictCompat +@obj_base.NovaObjectRegistry.register class InstanceExternalEvent(obj_base.NovaObject, obj_base.NovaObjectDictCompat): # Version 1.0: Initial version diff --git a/nova/objects/fields.py b/nova/objects/fields.py index fff3f4a443..106818b30b 100644 --- a/nova/objects/fields.py +++ b/nova/objects/fields.py @@ -371,7 +371,7 @@ class HVType(Enum): try: value = hv_type.canonicalize(value) except exception.InvalidHypervisorVirtType: - msg = _("Hypervisr virt type '%s' is not valid") % value + msg = _("Hypervisor virt type '%s' is not valid") % value raise ValueError(msg) return super(HVType, self).coerce(obj, attr, value) diff --git a/nova/objects/fixed_ip.py b/nova/objects/fixed_ip.py index 38742d2d07..3895260365 100644 --- a/nova/objects/fixed_ip.py +++ b/nova/objects/fixed_ip.py @@ -27,6 +27,7 @@ FIXED_IP_OPTIONAL_ATTRS = ['instance', 'network', 'virtual_interface', # TODO(berrange): Remove NovaObjectDictCompat +@obj_base.NovaObjectRegistry.register class FixedIP(obj_base.NovaPersistentObject, obj_base.NovaObject, obj_base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -203,6 +204,7 @@ class FixedIP(obj_base.NovaPersistentObject, obj_base.NovaObject, self.obj_reset_changes(['instance_uuid', 'instance']) +@obj_base.NovaObjectRegistry.register class FixedIPList(obj_base.ObjectListBase, obj_base.NovaObject): # Version 1.0: Initial version # Version 1.1: Added get_by_network() diff --git a/nova/objects/flavor.py b/nova/objects/flavor.py index b14efdf4ea..c52d9bf16d 100644 --- a/nova/objects/flavor.py +++ b/nova/objects/flavor.py @@ -23,6 +23,7 @@ OPTIONAL_FIELDS = ['extra_specs', 'projects'] # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class Flavor(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -250,6 +251,7 @@ class Flavor(base.NovaPersistentObject, base.NovaObject, db.flavor_destroy(self._context, self.name) +@base.NovaObjectRegistry.register class FlavorList(base.ObjectListBase, base.NovaObject): VERSION = '1.1' diff --git a/nova/objects/floating_ip.py b/nova/objects/floating_ip.py index e3149b168e..f1fb152e80 100644 --- a/nova/objects/floating_ip.py +++ b/nova/objects/floating_ip.py @@ -22,6 +22,7 @@ FLOATING_IP_OPTIONAL_ATTRS = ['fixed_ip'] # TODO(berrange): Remove NovaObjectDictCompat +@obj_base.NovaObjectRegistry.register class FloatingIP(obj_base.NovaPersistentObject, obj_base.NovaObject, obj_base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -162,6 +163,7 @@ class FloatingIP(obj_base.NovaPersistentObject, obj_base.NovaObject, self._from_db_object(self._context, self, db_floatingip) +@obj_base.NovaObjectRegistry.register class FloatingIPList(obj_base.ObjectListBase, obj_base.NovaObject): # Version 1.3: FloatingIP 1.2 # Version 1.4: FloatingIP 1.3 diff --git a/nova/objects/hv_spec.py b/nova/objects/hv_spec.py index 30880f07a7..8ae320676d 100644 --- a/nova/objects/hv_spec.py +++ b/nova/objects/hv_spec.py @@ -18,6 +18,7 @@ from nova.objects import fields # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class HVSpec(base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version diff --git a/nova/objects/image_meta.py b/nova/objects/image_meta.py index c1daf9822c..bb54c60563 100644 --- a/nova/objects/image_meta.py +++ b/nova/objects/image_meta.py @@ -21,6 +21,7 @@ from nova import utils from nova.virt import hardware +@base.NovaObjectRegistry.register class ImageMeta(base.NovaObject): VERSION = '1.0' @@ -98,6 +99,7 @@ class ImageMeta(base.NovaObject): return cls.from_dict(image_meta) +@base.NovaObjectRegistry.register class ImageMetaProps(base.NovaObject): VERSION = ImageMeta.VERSION diff --git a/nova/objects/instance.py b/nova/objects/instance.py index cc5e5b4f27..e4c159303f 100644 --- a/nova/objects/instance.py +++ b/nova/objects/instance.py @@ -133,6 +133,7 @@ def compat_instance(instance): # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class Instance(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -1169,6 +1170,7 @@ def _make_instance_list(context, inst_list, db_inst_list, expected_attrs): return inst_list +@base.NovaObjectRegistry.register class InstanceList(base.ObjectListBase, base.NovaObject): # Version 1.0: Initial version # Version 1.1: Added use_slave to get_by_host diff --git a/nova/objects/instance_action.py b/nova/objects/instance_action.py index 1a0b05cfd5..651e456821 100644 --- a/nova/objects/instance_action.py +++ b/nova/objects/instance_action.py @@ -21,6 +21,7 @@ from nova.objects import fields # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class InstanceAction(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -93,6 +94,7 @@ class InstanceAction(base.NovaPersistentObject, base.NovaObject, self._from_db_object(self._context, self, db_action) +@base.NovaObjectRegistry.register class InstanceActionList(base.ObjectListBase, base.NovaObject): # Version 1.0: Initial version # InstanceAction <= version 1.1 @@ -112,6 +114,7 @@ class InstanceActionList(base.ObjectListBase, base.NovaObject): # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class InstanceActionEvent(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -204,6 +207,7 @@ class InstanceActionEvent(base.NovaPersistentObject, base.NovaObject, self.finish_with_failure(self._context, exc_val=None, exc_tb=None) +@base.NovaObjectRegistry.register class InstanceActionEventList(base.ObjectListBase, base.NovaObject): fields = { 'objects': fields.ListOfObjectsField('InstanceActionEvent'), diff --git a/nova/objects/instance_fault.py b/nova/objects/instance_fault.py index e900a600c0..d61332aef1 100644 --- a/nova/objects/instance_fault.py +++ b/nova/objects/instance_fault.py @@ -30,6 +30,7 @@ LOG = logging.getLogger(__name__) # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class InstanceFault(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -90,6 +91,7 @@ class InstanceFault(base.NovaPersistentObject, base.NovaObject, LOG.exception(_LE("Failed to notify cells of instance fault")) +@base.NovaObjectRegistry.register class InstanceFaultList(base.ObjectListBase, base.NovaObject): # Version 1.0: Initial version # InstanceFault <= version 1.1 diff --git a/nova/objects/instance_group.py b/nova/objects/instance_group.py index ff57dfc303..abf2fd5269 100644 --- a/nova/objects/instance_group.py +++ b/nova/objects/instance_group.py @@ -24,6 +24,7 @@ from nova import utils # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class InstanceGroup(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -197,6 +198,7 @@ class InstanceGroup(base.NovaPersistentObject, base.NovaObject, return len(instances) +@base.NovaObjectRegistry.register class InstanceGroupList(base.ObjectListBase, base.NovaObject): # Version 1.0: Initial version # InstanceGroup <= version 1.3 diff --git a/nova/objects/instance_info_cache.py b/nova/objects/instance_info_cache.py index 5ec3b1bc1e..a565fc57b5 100644 --- a/nova/objects/instance_info_cache.py +++ b/nova/objects/instance_info_cache.py @@ -26,6 +26,7 @@ LOG = logging.getLogger(__name__) # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class InstanceInfoCache(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version diff --git a/nova/objects/instance_mapping.py b/nova/objects/instance_mapping.py index be6af6d5d2..d9d8a4d41b 100644 --- a/nova/objects/instance_mapping.py +++ b/nova/objects/instance_mapping.py @@ -18,6 +18,7 @@ from nova.objects import base from nova.objects import fields +@base.NovaObjectRegistry.register class InstanceMapping(base.NovaTimestampObject, base.NovaObject): # Version 1.0: Initial version VERSION = '1.0' @@ -107,6 +108,7 @@ class InstanceMapping(base.NovaTimestampObject, base.NovaObject): self._destroy_in_db(self._context, self.instance_uuid) +@base.NovaObjectRegistry.register class InstanceMappingList(base.ObjectListBase, base.NovaObject): # Version 1.0: Initial version VERSION = '1.0' diff --git a/nova/objects/instance_numa_topology.py b/nova/objects/instance_numa_topology.py index dad4c0eec9..f180050a44 100644 --- a/nova/objects/instance_numa_topology.py +++ b/nova/objects/instance_numa_topology.py @@ -22,6 +22,7 @@ from nova.virt import hardware # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class InstanceNUMACell(base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -109,6 +110,7 @@ class InstanceNUMACell(base.NovaObject, # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class InstanceNUMATopology(base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version diff --git a/nova/objects/instance_pci_requests.py b/nova/objects/instance_pci_requests.py index 2a07b148ba..e09d1f613f 100644 --- a/nova/objects/instance_pci_requests.py +++ b/nova/objects/instance_pci_requests.py @@ -19,6 +19,7 @@ from nova import utils # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class InstancePCIRequest(base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -52,6 +53,7 @@ class InstancePCIRequest(base.NovaObject, # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class InstancePCIRequests(base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version diff --git a/nova/objects/keypair.py b/nova/objects/keypair.py index 1a1565de44..139fa4d267 100644 --- a/nova/objects/keypair.py +++ b/nova/objects/keypair.py @@ -24,6 +24,7 @@ KEYPAIR_TYPE_X509 = 'x509' # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class KeyPair(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -78,6 +79,7 @@ class KeyPair(base.NovaPersistentObject, base.NovaObject, db.key_pair_destroy(self._context, self.user_id, self.name) +@base.NovaObjectRegistry.register class KeyPairList(base.ObjectListBase, base.NovaObject): # Version 1.0: Initial version # KeyPair <= version 1.1 diff --git a/nova/objects/migration.py b/nova/objects/migration.py index 7bf48d6b50..c8469f6758 100644 --- a/nova/objects/migration.py +++ b/nova/objects/migration.py @@ -28,6 +28,7 @@ def _determine_migration_type(migration): # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class Migration(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -115,6 +116,7 @@ class Migration(base.NovaPersistentObject, base.NovaObject, return objects.Instance.get_by_uuid(self._context, self.instance_uuid) +@base.NovaObjectRegistry.register class MigrationList(base.ObjectListBase, base.NovaObject): # Version 1.0: Initial version # Migration <= 1.1 diff --git a/nova/objects/network.py b/nova/objects/network.py index c8e997fe5b..26a5af235b 100644 --- a/nova/objects/network.py +++ b/nova/objects/network.py @@ -41,6 +41,7 @@ CONF.register_opts(network_opts) # TODO(berrange): Remove NovaObjectDictCompat +@obj_base.NovaObjectRegistry.register class Network(obj_base.NovaPersistentObject, obj_base.NovaObject, obj_base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -206,6 +207,7 @@ class Network(obj_base.NovaPersistentObject, obj_base.NovaObject, self._from_db_object(context, self, db_network) +@obj_base.NovaObjectRegistry.register class NetworkList(obj_base.ObjectListBase, obj_base.NovaObject): # Version 1.0: Initial version # Version 1.1: Added get_by_project() diff --git a/nova/objects/network_request.py b/nova/objects/network_request.py index 405c3c0d18..3e15480bb6 100644 --- a/nova/objects/network_request.py +++ b/nova/objects/network_request.py @@ -18,6 +18,7 @@ from nova import utils # TODO(berrange): Remove NovaObjectDictCompat +@obj_base.NovaObjectRegistry.register class NetworkRequest(obj_base.NovaObject, obj_base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -57,6 +58,7 @@ class NetworkRequest(obj_base.NovaObject, return cls(network_id=network_id, address=address) +@obj_base.NovaObjectRegistry.register class NetworkRequestList(obj_base.ObjectListBase, obj_base.NovaObject): fields = { 'objects': fields.ListOfObjectsField('NetworkRequest'), diff --git a/nova/objects/numa.py b/nova/objects/numa.py index 112074091c..20df535deb 100644 --- a/nova/objects/numa.py +++ b/nova/objects/numa.py @@ -35,6 +35,7 @@ def all_things_equal(obj_a, obj_b): # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class NUMACell(base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -131,6 +132,7 @@ class NUMACell(base.NovaObject, # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class NUMAPagesTopology(base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -160,6 +162,7 @@ class NUMAPagesTopology(base.NovaObject, # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class NUMATopology(base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -210,6 +213,7 @@ class NUMATopology(base.NovaObject, for cell_dict in data_dict.get('cells', [])]) +@base.NovaObjectRegistry.register class NUMATopologyLimits(base.NovaObject): # Version 1.0: Initial version VERSION = '1.0' diff --git a/nova/objects/pci_device.py b/nova/objects/pci_device.py index 01050fbe36..5098b7569a 100644 --- a/nova/objects/pci_device.py +++ b/nova/objects/pci_device.py @@ -42,6 +42,7 @@ def compare_pci_device_attributes(obj_a, obj_b): # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class PciDevice(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): @@ -200,6 +201,7 @@ class PciDevice(base.NovaPersistentObject, base.NovaObject, self._from_db_object(self._context, self, db_pci) +@base.NovaObjectRegistry.register class PciDeviceList(base.ObjectListBase, base.NovaObject): # Version 1.0: Initial version # PciDevice <= 1.1 diff --git a/nova/objects/pci_device_pool.py b/nova/objects/pci_device_pool.py index 13e57c4560..38799c16ed 100644 --- a/nova/objects/pci_device_pool.py +++ b/nova/objects/pci_device_pool.py @@ -24,6 +24,7 @@ from nova.objects import fields from nova import utils +@base.NovaObjectRegistry.register class PciDevicePool(base.NovaObject): # Version 1.0: Initial version # Version 1.1: Added numa_node field @@ -67,6 +68,7 @@ class PciDevicePool(base.NovaObject): return pci_pool +@base.NovaObjectRegistry.register class PciDevicePoolList(base.ObjectListBase, base.NovaObject): # Version 1.0: Initial version # PciDevicePool <= 1.0 diff --git a/nova/objects/quotas.py b/nova/objects/quotas.py index a1aef29766..2cc8534e9d 100644 --- a/nova/objects/quotas.py +++ b/nova/objects/quotas.py @@ -47,6 +47,7 @@ def ids_from_server_group(context, server_group): # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class Quotas(base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: initial version @@ -144,6 +145,7 @@ class Quotas(base.NovaObject, db.quota_update(context, project_id, resource, limit, user_id=user_id) +@base.NovaObjectRegistry.register class QuotasNoOp(Quotas): def reserve(context, expire=None, project_id=None, user_id=None, **deltas): diff --git a/nova/objects/security_group.py b/nova/objects/security_group.py index 0b56527556..8f5dbca843 100644 --- a/nova/objects/security_group.py +++ b/nova/objects/security_group.py @@ -19,6 +19,7 @@ from nova.objects import fields # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class SecurityGroup(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -73,6 +74,7 @@ class SecurityGroup(base.NovaPersistentObject, base.NovaObject, db.security_group_get(self._context, self.id)) +@base.NovaObjectRegistry.register class SecurityGroupList(base.ObjectListBase, base.NovaObject): # Version 1.0: Initial version # SecurityGroup <= version 1.1 diff --git a/nova/objects/security_group_rule.py b/nova/objects/security_group_rule.py index da75e03738..8e724a2be9 100644 --- a/nova/objects/security_group_rule.py +++ b/nova/objects/security_group_rule.py @@ -22,6 +22,7 @@ OPTIONAL_ATTRS = ['parent_group', 'grantee_group'] # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class SecurityGroupRule(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -84,6 +85,7 @@ class SecurityGroupRule(base.NovaPersistentObject, base.NovaObject, return cls._from_db_object(context, cls(), db_rule) +@base.NovaObjectRegistry.register class SecurityGroupRuleList(base.ObjectListBase, base.NovaObject): fields = { 'objects': fields.ListOfObjectsField('SecurityGroupRule'), diff --git a/nova/objects/service.py b/nova/objects/service.py index ca11066320..ce5dd9b6ce 100644 --- a/nova/objects/service.py +++ b/nova/objects/service.py @@ -27,6 +27,7 @@ LOG = logging.getLogger(__name__) # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class Service(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -179,6 +180,7 @@ class Service(base.NovaPersistentObject, base.NovaObject, db.service_destroy(self._context, self.id) +@base.NovaObjectRegistry.register class ServiceList(base.ObjectListBase, base.NovaObject): # Version 1.0: Initial version # Service <= version 1.2 diff --git a/nova/objects/tag.py b/nova/objects/tag.py index 87a81cb852..c463ddf2b6 100644 --- a/nova/objects/tag.py +++ b/nova/objects/tag.py @@ -16,6 +16,7 @@ from nova.objects import base from nova.objects import fields +@base.NovaObjectRegistry.register class Tag(base.NovaObject): # Version 1.0: Initial version VERSION = '1.0' @@ -43,6 +44,7 @@ class Tag(base.NovaObject): db.instance_tag_delete(context, resource_id, name) +@base.NovaObjectRegistry.register class TagList(base.ObjectListBase, base.NovaObject): # Version 1.0: Initial version VERSION = '1.0' diff --git a/nova/objects/vcpu_model.py b/nova/objects/vcpu_model.py index 87742dc1fa..32940ce26d 100644 --- a/nova/objects/vcpu_model.py +++ b/nova/objects/vcpu_model.py @@ -17,6 +17,7 @@ from nova.objects import base from nova.objects import fields +@base.NovaObjectRegistry.register class VirtCPUModel(base.NovaObject): # Version 1.0: Initial version VERSION = '1.0' @@ -57,6 +58,7 @@ class VirtCPUModel(base.NovaObject): return cls.obj_from_primitive(jsonutils.loads(db_extra['vcpu_model'])) +@base.NovaObjectRegistry.register class VirtCPUFeature(base.NovaObject): VERSION = VirtCPUModel.VERSION diff --git a/nova/objects/virt_cpu_topology.py b/nova/objects/virt_cpu_topology.py index 66a09cd2c1..d0dbd15a98 100644 --- a/nova/objects/virt_cpu_topology.py +++ b/nova/objects/virt_cpu_topology.py @@ -15,6 +15,7 @@ from nova.objects import fields # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class VirtCPUTopology(base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version diff --git a/nova/objects/virtual_interface.py b/nova/objects/virtual_interface.py index be44cfcd83..0414def6a8 100644 --- a/nova/objects/virtual_interface.py +++ b/nova/objects/virtual_interface.py @@ -20,6 +20,7 @@ from nova.objects import fields # TODO(berrange): Remove NovaObjectDictCompat +@base.NovaObjectRegistry.register class VirtualInterface(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version @@ -80,6 +81,7 @@ class VirtualInterface(base.NovaPersistentObject, base.NovaObject, db.virtual_interface_delete_by_instance(context, instance_uuid) +@base.NovaObjectRegistry.register class VirtualInterfaceList(base.ObjectListBase, base.NovaObject): # Version 1.0: Initial version VERSION = '1.0' diff --git a/nova/tests/unit/api/openstack/compute/contrib/test_security_groups.py b/nova/tests/unit/api/openstack/compute/contrib/test_security_groups.py index 33cba6b585..fa6c67bea4 100644 --- a/nova/tests/unit/api/openstack/compute/contrib/test_security_groups.py +++ b/nova/tests/unit/api/openstack/compute/contrib/test_security_groups.py @@ -189,7 +189,7 @@ class TestSecurityGroupsV21(test.TestCase): except webob.exc.HTTPBadRequest as exc: self.assertEqual('description has a minimum character requirement' ' of 1.', exc.explanation) - except exception.InvalidInput as exc: + except exception.InvalidInput: self.fail('Should have raised BadRequest exception instead of') self._assert_no_security_groups_reserved(req.environ['nova.context']) diff --git a/nova/tests/unit/api/openstack/compute/contrib/test_server_external_events.py b/nova/tests/unit/api/openstack/compute/contrib/test_server_external_events.py index 8b5a4e05da..eebc5e9303 100644 --- a/nova/tests/unit/api/openstack/compute/contrib/test_server_external_events.py +++ b/nova/tests/unit/api/openstack/compute/contrib/test_server_external_events.py @@ -60,13 +60,13 @@ class ServerExternalEventsTestV21(test.NoDBTestCase): 'server_uuid': fake_instance_uuids[0], 'status': 'completed'} self.event_2 = {'name': 'network-changed', - 'server_uuid': fake_instance_uuids[1], - 'status': 'completed'} + 'server_uuid': fake_instance_uuids[1]} self.default_body = {'events': [self.event_1, self.event_2]} self.resp_event_1 = dict(self.event_1) self.resp_event_1['code'] = 200 self.resp_event_2 = dict(self.event_2) self.resp_event_2['code'] = 200 + self.resp_event_2['status'] = 'completed' self.default_resp_body = {'events': [self.resp_event_1, self.resp_event_2]} self.req = fakes.HTTPRequest.blank('', use_admin_context=True) diff --git a/nova/tests/unit/compute/test_compute.py b/nova/tests/unit/compute/test_compute.py index 9da07e1d77..f9b15cf895 100644 --- a/nova/tests/unit/compute/test_compute.py +++ b/nova/tests/unit/compute/test_compute.py @@ -452,6 +452,17 @@ class ComputeVolumeTestCase(BaseTestCase): self.compute._await_block_device_map_created, self.context, '1') + def test_await_block_device_created_failed(self): + c = self.compute + + fake_result = {'status': 'error', 'id': 'blah'} + with mock.patch.object(c.volume_api, 'get', + return_value=fake_result) as fake_get: + self.assertRaises(exception.VolumeNotCreated, + c._await_block_device_map_created, + self.context, '1') + fake_get.assert_called_once_with(self.context, '1') + def test_await_block_device_created_slow(self): c = self.compute self.flags(block_device_allocate_retries=4) diff --git a/nova/tests/unit/objects/test_objects.py b/nova/tests/unit/objects/test_objects.py index 300289a256..849b787ab8 100644 --- a/nova/tests/unit/objects/test_objects.py +++ b/nova/tests/unit/objects/test_objects.py @@ -1283,6 +1283,15 @@ class TestObjectVersions(test.NoDBTestCase): 'versions have been bumped, and then update their ' 'hashes here.') + def test_registry_matches_metaclass(self): + reference = set(object_data.keys()) + actual = set(base.NovaObjectRegistry.classes) + test_objects = set(['MyObj', 'MyOwnedObject', 'TestSubclassedObject']) + # NOTE(danms): In the new registry, we don't implicitly track test + # objects, so make sure that the difference between the metaclass and + # the opt-in registry is the set of test objects. + self.assertEqual(test_objects, reference.symmetric_difference(actual)) + def _get_object_field_name(self, field): if isinstance(field._type, fields.Object): return field._type._obj_name diff --git a/nova/tests/unit/virt/ironic/test_driver.py b/nova/tests/unit/virt/ironic/test_driver.py index dfa6b5285c..47d08e0fca 100644 --- a/nova/tests/unit/virt/ironic/test_driver.py +++ b/nova/tests/unit/virt/ironic/test_driver.py @@ -509,9 +509,21 @@ class IronicDriverTestCase(test.NoDBTestCase): 'power_state': ironic_states.NOSTATE, 'provision_state': ironic_states.AVAILABLE}, # a node not in maintenance or bad power state, bad provision state - {'uuid': uuidutils.generate_uuid, + {'uuid': uuidutils.generate_uuid(), + 'power_state': ironic_states.POWER_ON, + 'provision_state': ironic_states.MANAGEABLE}, + # a node in cleaning + {'uuid': uuidutils.generate_uuid(), + 'power_state': ironic_states.POWER_ON, + 'provision_state': ironic_states.CLEANING}, + # a node in deleting + {'uuid': uuidutils.generate_uuid(), 'power_state': ironic_states.POWER_ON, - 'provision_state': ironic_states.MANAGEABLE} + 'provision_state': ironic_states.DELETING}, + # a node in deleted + {'uuid': uuidutils.generate_uuid(), + 'power_state': ironic_states.POWER_ON, + 'provision_state': ironic_states.DELETED} ] for n in node_dicts: node = ironic_utils.get_test_node(**n) @@ -531,20 +543,14 @@ class IronicDriverTestCase(test.NoDBTestCase): {'uuid': uuidutils.generate_uuid(), 'instance_uuid': uuidutils.generate_uuid(), 'provision_state': ironic_states.ACTIVE}, - # a node in deploying but no instance yet - {'uuid': uuidutils.generate_uuid(), - 'provision_state': ironic_states.DEPLOYWAIT}, - # a node that made it to cleaning before losing its instance uuid - {'uuid': uuidutils.generate_uuid, - 'instance_uuid': uuidutils.generate_uuid(), - 'provision_state': ironic_states.CLEANING}, ] for n in node_dicts: node = ironic_utils.get_test_node(**n) self.assertTrue(self.driver._node_resources_used(node)) unused_node = ironic_utils.get_test_node( - power_state=ironic_states.AVAILABLE) + instance_uuid=None, + provision_state=ironic_states.AVAILABLE) self.assertFalse(self.driver._node_resources_used(unused_node)) @mock.patch.object(FAKE_CLIENT.node, 'list') diff --git a/nova/tests/unit/virt/test_block_device.py b/nova/tests/unit/virt/test_block_device.py index d32a20309a..64040a4d79 100644 --- a/nova/tests/unit/virt/test_block_device.py +++ b/nova/tests/unit/virt/test_block_device.py @@ -20,6 +20,7 @@ import six from nova import block_device from nova import context +from nova import exception from nova import test from nova.tests.unit import fake_instance from nova.tests.unit import matchers @@ -328,6 +329,37 @@ class TestDriverBlockDevice(test.NoDBTestCase): self.assertEqual('fake-volume-id-2', test_bdm.volume_id) self.assertEqual(3, test_bdm.volume_size) + def _test_call_wait_func(self, delete_on_termination, delete_fail=False): + test_bdm = self.driver_classes['volume'](self.volume_bdm) + test_bdm['delete_on_termination'] = delete_on_termination + with mock.patch.object(self.volume_api, 'delete') as vol_delete: + wait_func = mock.MagicMock() + mock_exception = exception.VolumeNotCreated(volume_id='fake-id', + seconds=1, + attempts=1, + volume_status='error') + wait_func.side_effect = mock_exception + + if delete_on_termination and delete_fail: + vol_delete.side_effect = Exception() + + self.assertRaises(exception.VolumeNotCreated, + test_bdm._call_wait_func, + context=self.context, + wait_func=wait_func, + volume_api=self.volume_api, + volume_id='fake-id') + self.assertEqual(delete_on_termination, vol_delete.called) + + def test_call_wait_delete_volume(self): + self._test_call_wait_func(True) + + def test_call_wait_delete_volume_fail(self): + self._test_call_wait_func(True, True) + + def test_call_wait_no_delete_volume(self): + self._test_call_wait_func(False) + def _test_volume_attach(self, driver_bdm, bdm_dict, fake_volume, check_attach=True, fail_check_attach=False, driver_attach=False, @@ -560,6 +592,43 @@ class TestDriverBlockDevice(test.NoDBTestCase): self.virt_driver, wait_func) self.assertEqual(test_bdm.volume_id, 'fake-volume-id-2') + def test_snapshot_attach_fail_volume(self): + fail_volume_snapshot = self.snapshot_bdm.copy() + fail_volume_snapshot['volume_id'] = None + test_bdm = self.driver_classes['snapshot'](fail_volume_snapshot) + + snapshot = {'id': 'fake-volume-id-1', + 'attach_status': 'detached'} + volume = {'id': 'fake-volume-id-2', + 'attach_status': 'detached'} + + instance = fake_instance.fake_instance_obj(mock.sentinel.ctx, + **{'uuid': 'fake-uuid'}) + with contextlib.nested( + mock.patch.object(self.volume_api, 'get_snapshot', + return_value=snapshot), + mock.patch.object(self.volume_api, 'create', return_value=volume), + mock.patch.object(self.volume_api, 'delete'), + ) as (vol_get_snap, vol_create, vol_delete): + wait_func = mock.MagicMock() + mock_exception = exception.VolumeNotCreated(volume_id=volume['id'], + seconds=1, + attempts=1, + volume_status='error') + wait_func.side_effect = mock_exception + self.assertRaises(exception.VolumeNotCreated, + test_bdm.attach, context=self.context, + instance=instance, + volume_api=self.volume_api, + virt_driver=self.virt_driver, + wait_func=wait_func) + + vol_get_snap.assert_called_once_with( + self.context, 'fake-snapshot-id-1') + vol_create.assert_called_once_with( + self.context, 3, '', '', snapshot, availability_zone=None) + vol_delete.assert_called_once_with(self.context, volume['id']) + def test_snapshot_attach_volume(self): test_bdm = self.driver_classes['snapshot']( self.snapshot_bdm) @@ -604,6 +673,39 @@ class TestDriverBlockDevice(test.NoDBTestCase): self.virt_driver, wait_func) self.assertEqual(test_bdm.volume_id, 'fake-volume-id-2') + def test_image_attach_fail_volume(self): + fail_volume_image = self.image_bdm.copy() + fail_volume_image['volume_id'] = None + test_bdm = self.driver_classes['image'](fail_volume_image) + + image = {'id': 'fake-image-id-1'} + volume = {'id': 'fake-volume-id-2', + 'attach_status': 'detached'} + + instance = fake_instance.fake_instance_obj(mock.sentinel.ctx, + **{'uuid': 'fake-uuid'}) + with contextlib.nested( + mock.patch.object(self.volume_api, 'create', return_value=volume), + mock.patch.object(self.volume_api, 'delete'), + ) as (vol_create, vol_delete): + wait_func = mock.MagicMock() + mock_exception = exception.VolumeNotCreated(volume_id=volume['id'], + seconds=1, + attempts=1, + volume_status='error') + wait_func.side_effect = mock_exception + self.assertRaises(exception.VolumeNotCreated, + test_bdm.attach, context=self.context, + instance=instance, + volume_api=self.volume_api, + virt_driver=self.virt_driver, + wait_func=wait_func) + + vol_create.assert_called_once_with( + self.context, 1, '', '', image_id=image['id'], + availability_zone=None) + vol_delete.assert_called_once_with(self.context, volume['id']) + def test_image_attach_volume(self): test_bdm = self.driver_classes['image']( self.image_bdm) @@ -626,6 +728,38 @@ class TestDriverBlockDevice(test.NoDBTestCase): self.virt_driver) self.assertEqual(test_bdm.volume_id, 'fake-volume-id-2') + def test_blank_attach_fail_volume(self): + no_blank_volume = self.blank_bdm.copy() + no_blank_volume['volume_id'] = None + test_bdm = self.driver_classes['blank'](no_blank_volume) + instance = fake_instance.fake_instance_obj(mock.sentinel.ctx, + **{'uuid': 'fake-uuid'}) + volume = {'id': 'fake-volume-id-2', + 'display_name': 'fake-uuid-blank-vol'} + + with contextlib.nested( + mock.patch.object(self.volume_api, 'create', return_value=volume), + mock.patch.object(self.volume_api, 'delete'), + ) as (vol_create, vol_delete): + wait_func = mock.MagicMock() + mock_exception = exception.VolumeNotCreated(volume_id=volume['id'], + seconds=1, + attempts=1, + volume_status='error') + wait_func.side_effect = mock_exception + self.assertRaises(exception.VolumeNotCreated, + test_bdm.attach, context=self.context, + instance=instance, + volume_api=self.volume_api, + virt_driver=self.virt_driver, + wait_func=wait_func) + + vol_create.assert_called_once_with( + self.context, test_bdm.volume_size, 'fake-uuid-blank-vol', + '', availability_zone=instance.availability_zone) + vol_delete.assert_called_once_with( + self.context, volume['id']) + def test_blank_attach_volume(self): no_blank_volume = self.blank_bdm.copy() no_blank_volume['volume_id'] = None diff --git a/nova/tests/unit/virt/vmwareapi/test_driver_api.py b/nova/tests/unit/virt/vmwareapi/test_driver_api.py index 5a7cd1135f..9dfbaa30bd 100644 --- a/nova/tests/unit/virt/vmwareapi/test_driver_api.py +++ b/nova/tests/unit/virt/vmwareapi/test_driver_api.py @@ -2309,3 +2309,22 @@ class VMwareAPIVMTestCase(test.NoDBTestCase): test_mor = "domain-26" nodename = "%s.%s" % (test_mor, vmwareapi_fake._FAKE_VCENTER_UUID) self.assertEqual(nodename, self.conn._normalize_nodename(nodename)) + + @mock.patch.object(driver.LOG, 'warning') + def test_min_version(self, mock_warning): + self.conn._check_min_version() + self.assertFalse(mock_warning.called) + + @mock.patch.object(driver.LOG, 'warning') + @mock.patch.object(oslo_vim_util, 'get_vc_version', + return_value='5.0.0') + def test_invalid_min_version(self, mock_version, mock_warning): + self.conn._check_min_version() + # assert that the min version is in a warning message + expected_arg = {'version': constants.MIN_VC_VERSION} + version_arg_found = False + for call in mock_warning.call_args_list: + if call[0][1] == expected_arg: + version_arg_found = True + break + self.assertTrue(version_arg_found) diff --git a/nova/virt/block_device.py b/nova/virt/block_device.py index 7a0c488463..dcac11e92b 100644 --- a/nova/virt/block_device.py +++ b/nova/virt/block_device.py @@ -22,8 +22,10 @@ from oslo_utils import excutils import six from nova import block_device +from nova import exception from nova.i18n import _LE from nova.i18n import _LI +from nova.i18n import _LW from nova import objects from nova.objects import base as obj_base from nova.volume import encryptors @@ -303,6 +305,19 @@ class DriverVolumeBlockDevice(DriverBlockDevice): pass super(DriverVolumeBlockDevice, self).save() + def _call_wait_func(self, context, wait_func, volume_api, volume_id): + try: + wait_func(context, volume_id) + except exception.VolumeNotCreated: + with excutils.save_and_reraise_exception(): + if self['delete_on_termination']: + try: + volume_api.delete(context, volume_id) + except Exception as exc: + LOG.warn(_LW('Failed to delete volume: %(volume_id)s ' + 'due to %(exc)s'), + {'volume_id': volume_id, 'exc': exc}) + class DriverSnapshotBlockDevice(DriverVolumeBlockDevice): @@ -319,7 +334,7 @@ class DriverSnapshotBlockDevice(DriverVolumeBlockDevice): vol = volume_api.create(context, self.volume_size, '', '', snapshot, availability_zone=av_zone) if wait_func: - wait_func(context, vol['id']) + self._call_wait_func(context, wait_func, volume_api, vol['id']) self.volume_id = vol['id'] @@ -342,7 +357,7 @@ class DriverImageBlockDevice(DriverVolumeBlockDevice): '', '', image_id=self.image_id, availability_zone=av_zone) if wait_func: - wait_func(context, vol['id']) + self._call_wait_func(context, wait_func, volume_api, vol['id']) self.volume_id = vol['id'] @@ -364,7 +379,7 @@ class DriverBlankBlockDevice(DriverVolumeBlockDevice): vol = volume_api.create(context, self.volume_size, vol_name, '', availability_zone=av_zone) if wait_func: - wait_func(context, vol['id']) + self._call_wait_func(context, wait_func, volume_api, vol['id']) self.volume_id = vol['id'] diff --git a/nova/virt/ironic/driver.py b/nova/virt/ironic/driver.py index a342e4ea98..1a284691bf 100644 --- a/nova/virt/ironic/driver.py +++ b/nova/virt/ironic/driver.py @@ -222,14 +222,13 @@ class IronicDriver(virt_driver.ComputeDriver): a new instance on the node, has an instance on the node, or is in the process of cleaning up from a deleted instance. Returns True if used. + + If we report resources as consumed for a node that does not have an + instance on it, the resource tracker will notice there's no instances + consuming resources and try to correct us. So only nodes with an + instance attached should report as consumed here. """ - used_provision_states = [ - ironic_states.CLEANING, ironic_states.DEPLOYING, - ironic_states.DEPLOYWAIT, ironic_states.DEPLOYDONE, - ironic_states.ACTIVE, ironic_states.DELETING, - ironic_states.DELETED] - return (node_obj.instance_uuid is not None or - node_obj.provision_state in used_provision_states) + return node_obj.instance_uuid is not None def _node_resource(self, node): """Helper method to create resource dict from node stats.""" diff --git a/nova/virt/libvirt/host.py b/nova/virt/libvirt/host.py index 4bae94caa7..2a4b0e8e24 100644 --- a/nova/virt/libvirt/host.py +++ b/nova/virt/libvirt/host.py @@ -392,25 +392,22 @@ class Host(object): event = args[0] self._events_delayed.pop(event.uuid, None) - if self._lifecycle_delay > 0: - # Cleanup possible delayed stop events. - if event.uuid in self._events_delayed.keys(): - self._events_delayed[event.uuid].cancel() - self._events_delayed.pop(event.uuid, None) - LOG.debug("Removed pending event for %s due to " - "lifecycle event", event.uuid) - - if event.transition == virtevent.EVENT_LIFECYCLE_STOPPED: - # Delay STOPPED event, as they may be followed by a STARTED - # event in case the instance is rebooting - id_ = greenthread.spawn_after(self._lifecycle_delay, - self._event_emit, event) - self._events_delayed[event.uuid] = id_ - # add callback to cleanup self._events_delayed dict after - # event was called - id_.link(event_cleanup, event) - else: - self._event_emit(event) + # Cleanup possible delayed stop events. + if event.uuid in self._events_delayed.keys(): + self._events_delayed[event.uuid].cancel() + self._events_delayed.pop(event.uuid, None) + LOG.debug("Removed pending event for %s due to " + "lifecycle event", event.uuid) + + if event.transition == virtevent.EVENT_LIFECYCLE_STOPPED: + # Delay STOPPED event, as they may be followed by a STARTED + # event in case the instance is rebooting + id_ = greenthread.spawn_after(self._lifecycle_delay, + self._event_emit, event) + self._events_delayed[event.uuid] = id_ + # add callback to cleanup self._events_delayed dict after + # event was called + id_.link(event_cleanup, event) else: self._event_emit(event) diff --git a/nova/virt/libvirt/utils.py b/nova/virt/libvirt/utils.py index 84eba5ab2d..52b315eee1 100644 --- a/nova/virt/libvirt/utils.py +++ b/nova/virt/libvirt/utils.py @@ -238,7 +238,7 @@ def pick_disk_driver_name(hypervisor_version, is_block_dev=False): return 'qemu' else: raise - except processutils.ProcessExecutionError as exc: + except processutils.ProcessExecutionError: LOG.debug("xend is not started") # libvirt will try to use libxl toolstack return 'qemu' @@ -577,7 +577,7 @@ def is_mounted(mount_path, source=None): utils.execute(*check_cmd) return True - except processutils.ProcessExecutionError as exc: + except processutils.ProcessExecutionError: return False except OSError as exc: # info since it's not required to have this tool. diff --git a/nova/virt/vmwareapi/constants.py b/nova/virt/vmwareapi/constants.py index f3ee48be58..dd1523aeee 100644 --- a/nova/virt/vmwareapi/constants.py +++ b/nova/virt/vmwareapi/constants.py @@ -18,6 +18,8 @@ Shared constants across the VMware driver from nova.network import model as network_model +MIN_VC_VERSION = '5.1.0' + DISK_FORMAT_ISO = 'iso' DISK_FORMAT_VMDK = 'vmdk' DISK_FORMATS_ALL = [DISK_FORMAT_ISO, DISK_FORMAT_VMDK] diff --git a/nova/virt/vmwareapi/driver.py b/nova/virt/vmwareapi/driver.py index 0e1b21997f..b2aa55777f 100644 --- a/nova/virt/vmwareapi/driver.py +++ b/nova/virt/vmwareapi/driver.py @@ -32,6 +32,7 @@ from oslo_vmware import vim_util import six from nova import exception +from nova import utils from nova.i18n import _, _LI, _LW from nova.openstack.common import versionutils from nova.virt import driver @@ -149,6 +150,8 @@ class VMwareVCDriver(driver.ComputeDriver): self._session = VMwareAPISession(scheme=scheme) + self._check_min_version() + # Update the PBM location if necessary if CONF.vmware.pbm_enabled: self._update_pbm_location() @@ -199,6 +202,18 @@ class VMwareVCDriver(driver.ComputeDriver): # Register the OpenStack extension self._register_openstack_extension() + def _check_min_version(self): + min_version = utils.convert_version_to_int(constants.MIN_VC_VERSION) + vc_version = vim_util.get_vc_version(self._session) + LOG.info(_LI("VMware VC version: %s"), vc_version) + if min_version > utils.convert_version_to_int(vc_version): + # TODO(garyk): enforce this from M + LOG.warning(_LW('Running Nova with a VMware VC version less than ' + '%(version)s is deprecated. The required minimum ' + 'version of VC will be raised to %(version)s ' + 'in the 2016.1 release.'), + {'version': constants.MIN_VC_VERSION}) + @property def need_legacy_block_device_info(self): return False |