diff -Nru nova-2014.1.100+git201408190132~precise/ChangeLog nova-2014.1.100+git201410062002~precise/ChangeLog --- nova-2014.1.100+git201408190132~precise/ChangeLog 2014-08-19 05:32:52.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/ChangeLog 2014-10-07 00:03:56.000000000 +0000 @@ -1,13 +1,62 @@ CHANGES ======= +* Raise descriptive error for over volume quota +* Bump stable/icehouse next version to 2014.1.4 + +2014.1.3 +-------- + +* Adds tests for Hyper-V VM Utils +* Removes unnecessary instructions in test_hypervapi +* Fixes a Hyper-V list_instances localization issue +* Adds list_instance_uuids to the Hyper-V driver +* Add _wrap_db_error() support to Session.commit() +* Neutron: Atomic update of instance info cache +* Ensure info cache updates don't overwhelm cells +* Sync process and str utils from oslo +* remove test_multiprocess_api +* Fixes Hyper-V agent force_hyperv_utils_v1 flag issue +* Fix attaching config drive issue on Hyper-V when migrate instances +* Fix live-migration failure in FC multipath case +* libvirt: Save device_path in connection_info when booting from volume +* Made unassigned networks visible in flat networking +* Do not fail cell's instance deletion, if it's missing info_cache +* Fixes Hyper-V boot from volume root device issue +* Fixes Hyper-V resize down exception +* db: Add @_retry_on_deadlock to service_update() +* Add Hyper-V driver in the "compute_driver" option description +* Block sqlalchemy migrate 0.9.2 as it breaks all of nova +* Move the error check for "brctl addif" +* Fix rootwrap for non openstack.org iqn's +* Fix instance boot when Ceph is used for ephemeral storage +* Make floatingip-ip-delete atomic with neutron +* Fix race condition with vif plugging in finish migrate +* libvirt: Use VIR_DOMAIN_AFFECT_LIVE for paused instances +* add repr for event objects +* make lifecycle event logs more clear +* Catch missing Glance image attrs with None +* Update block_device_info to contain swap and ephemeral disks +* Adds get_instance_disk_info to compute drivers +* Fixes Hyper-V vm state issue +* Fix expected error details from jsonschema +* Include next link when default limit is reached +* Bump stable/icehouse next version to 2014.1.3 +* Fix FloatingIP.save() passing FixedIP object to sqlalchemy * Read deleted instances during lifecycle events +* Add a retry_on_deadlock to reservations_expire +* Add expire reservations in backport position * Fixes Hyper-V SCSI slot selection +* VMware: do not cache image when root_gb is 0 2014.1.2 -------- * Mask block_device_info auth_password in virt driver debug logs +* Fix _parse_datetime in simple tenant usage extension +* Avoid traceback logs from simple tenant usage extension +* replace NovaException with VirtualInterfaceCreate when neutron fails +* libvirt: convert cpu features attribute from list to a set * VMware: Add check for datacenter with no datastore * Fixes hyper-v volume attach when host is AD member * Prevent clean-up of migrating instances on compute init @@ -15,7 +64,9 @@ * Set python hash seed to 0 in tox.ini * Do not pass instances without host to compute API * Do not process events for instances without host +* Delete image when backup operation failed on snapshot step * Attach/detach interface to paused instance with affect live flag +* VM in rescue state must have a restricted set of actions * Turn periodic tasks off in all unit tests * Updated from global requirements * Avoid possible timing attack in metadata api @@ -31,6 +82,7 @@ * Scheduler: enable scheduler hint to pass the group name * Instance groups: add method get_by_hint * Avoid referencing stale instance/network_info dicts in firewall +* libvirt: return the correct instance path while cleanup_resize * Neutronv2 api does not support neutron without port quota * Fix the wrong dest of 'vlan' option and add new 'vlan_start' option * Use default rpc_response_timeout in unit tests @@ -56,6 +108,8 @@ * VMware: ensure rescue instance is deleted when instance is deleted * VMware: Log additional details of suds faults * Add info_cache as expected attribute when evacuate instance +* Fix nova image-show with queued image +* _translate_from_glance() can cause an unnecessary HTTP request * VMware: uncaught exception during snapshot deletion * Catch InstanceNotFound exception if migration fails * Do not wait for neutron event if not powering on libvirt domain @@ -68,6 +122,7 @@ * Use debug level logging in unit tests, but don't save them * support local debug logging * Revert "Use debug level logging during unit tests" +* Loosen import_exceptions to cover all of gettextutils * VMWare: add power off vm before detach disk during unrescue * Check for None or timestamp in availability zone api sample * Pass configured auth strategy to neutronclient diff -Nru nova-2014.1.100+git201408190132~precise/debian/changelog nova-2014.1.100+git201410062002~precise/debian/changelog --- nova-2014.1.100+git201408190132~precise/debian/changelog 2014-08-19 05:33:56.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/debian/changelog 2014-10-07 00:05:54.000000000 +0000 @@ -1,9 +1,134 @@ -nova (1:2014.1.100+git201408190132~precise-0ubuntu1) precise; urgency=medium +nova (1:2014.1.100+git201410062002~precise-0ubuntu1) precise; urgency=medium * Automated Ubuntu testing build: - * No change rebuild. + * [5ec3cd3] Raise descriptive error for over volume quota + * [da69a57] Bump stable/icehouse next version to 2014.1.4 + * [1a95c95] Adds tests for Hyper-V VM Utils + * [bb47d55] Removes unnecessary instructions in test_hypervapi + * [4f41d37] Fixes a Hyper-V list_instances localization issue + * [9015410] Adds list_instance_uuids to the Hyper-V driver + * [3371ad8] Add _wrap_db_error() support to Session.commit() + * [dfb0e0f] Neutron: Atomic update of instance info cache + * [bce481c] Ensure info cache updates don't overwhelm cells + * [f58d95c] Sync process and str utils from oslo + * [4e6371b] remove test_multiprocess_api + * [7e09173] Fixes Hyper-V agent force_hyperv_utils_v1 flag issue + * [7523ab4] Fix attaching config drive issue on Hyper-V when migrate + instances + * [74e0ba7] Fix live-migration failure in FC multipath case + * [b61aa4d] libvirt: Save device_path in connection_info when booting + from volume + * [f93b8ee] Made unassigned networks visible in flat networking + * [82cc3be] Do not fail cell's instance deletion, if it's missing + info_cache + * [d72c0a4] Fixes Hyper-V boot from volume root device issue + * [0d3dad7] Fixes Hyper-V resize down exception + * [5d5970a] db: Add @_retry_on_deadlock to service_update() + * [9596f52] Add Hyper-V driver in the "compute_driver" option + description + * [4a8d6ca] Block sqlalchemy migrate 0.9.2 as it breaks all of nova + * [311ab57] Move the error check for "brctl addif" + * [df09c2a] Fix rootwrap for non openstack.org iqn's + * [1613cd99] Fix instance boot when Ceph is used for ephemeral storage + * [4bc680f] Make floatingip-ip-delete atomic with neutron + * [0d69163] Fix race condition with vif plugging in finish migrate + * [520aa4c] libvirt: Use VIR_DOMAIN_AFFECT_LIVE for paused instances + * [3c34e37] add repr for event objects + * [1b7ab22] make lifecycle event logs more clear + * [e1d6e18] Catch missing Glance image attrs with None + * [b591389] Update block_device_info to contain swap and ephemeral + disks + * [2155188] Adds get_instance_disk_info to compute drivers + * [87f842d] Fixes Hyper-V vm state issue + * [1106ef2] Fix expected error details from jsonschema + * [e5e6bc7] Include next link when default limit is reached + * [cf408e1] Bump stable/icehouse next version to 2014.1.3 + * [526853e] Fix FloatingIP.save() passing FixedIP object to sqlalchemy + * [d8b9ba5] Add a retry_on_deadlock to reservations_expire + * [b53adea] Add expire reservations in backport position. + * [e874ee2] Fixes Hyper-V SCSI slot selection + * [471e644] VMware: do not cache image when root_gb is 0 + * [825cfe4] Fix _parse_datetime in simple tenant usage extension + * [073ee06] Avoid traceback logs from simple tenant usage extension + * [9447203] replace NovaException with VirtualInterfaceCreate when + neutron fails + * [6b7cb1a] libvirt: convert cpu features attribute from list to a set + * [7ca83e8] Delete image when backup operation failed on snapshot step + * [82a13b3] VM in rescue state must have a restricted set of actions + * [e7d2087] shelve doesn't work on nova-cells environment + * [aeb71a8] libvirt: return the correct instance path while + cleanup_resize + * [cce6d22] Fix nova image-show with queued image + * [aff80d5] _translate_from_glance() can cause an unnecessary HTTP + request + * [a058646] Loosen import_exceptions to cover all of gettextutils - -- Openstack Ubuntu Testing Bot Tue, 19 Aug 2014 01:33:09 -0400 + -- Openstack Ubuntu Testing Bot Mon, 06 Oct 2014 20:04:17 -0400 + +nova (1:2014.1.3-0ubuntu1) trusty; urgency=medium + + [ Liam Young ] + * d/p/cells-json-store.patch: Fix issue with nova-cells failing when using + JSON file to store cell information (LP: #1314677). + + [ Corey Bryant ] + * Resynchronize with stable/icehouse (a058646) (LP: #1377136): + - [1a95c95] Adds tests for Hyper-V VM Utils + - [bb47d55] Removes unnecessary instructions in test_hypervapi + - [4f41d37] Fixes a Hyper-V list_instances localization issue + - [9015410] Adds list_instance_uuids to the Hyper-V driver + - [3371ad8] Add _wrap_db_error() support to Session.commit() + - [dfb0e0f] Neutron: Atomic update of instance info cache + - [bce481c] Ensure info cache updates don't overwhelm cells + - [f58d95c] Sync process and str utils from oslo + - [4e6371b] remove test_multiprocess_api + - [7e09173] Fixes Hyper-V agent force_hyperv_utils_v1 flag issue + - [7523ab4] Fix attaching config drive issue on Hyper-V when migrate instances + - [74e0ba7] Fix live-migration failure in FC multipath case + - [b61aa4d] libvirt: Save device_path in connection_info when booting from volume + - [f93b8ee] Made unassigned networks visible in flat networking + - [82cc3be] Do not fail cell's instance deletion, if it's missing info_cache + - [d72c0a4] Fixes Hyper-V boot from volume root device issue + - [0d3dad7] Fixes Hyper-V resize down exception + - [5d5970a] db: Add @_retry_on_deadlock to service_update() + - [9596f52] Add Hyper-V driver in the "compute_driver" option description + - [4a8d6ca] Block sqlalchemy migrate 0.9.2 as it breaks all of nova + - [311ab57] Move the error check for "brctl addif" + - [df09c2a] Fix rootwrap for non openstack.org iqn's + - [1613cd99] Fix instance boot when Ceph is used for ephemeral storage + - [4bc680f] Make floatingip-ip-delete atomic with neutron + - [0d69163] Fix race condition with vif plugging in finish migrate + - [520aa4c] libvirt: Use VIR_DOMAIN_AFFECT_LIVE for paused instances + - [3c34e37] add repr for event objects + - [1b7ab22] make lifecycle event logs more clear + - [e1d6e18] Catch missing Glance image attrs with None + - [b591389] Update block_device_info to contain swap and ephemeral disks + - [2155188] Adds get_instance_disk_info to compute drivers + - [87f842d] Fixes Hyper-V vm state issue + - [1106ef2] Fix expected error details from jsonschema + - [e5e6bc7] Include next link when default limit is reached + - [526853e] Fix FloatingIP.save() passing FixedIP object to sqlalchemy + - [4e1e217] Read deleted instances during lifecycle events + - [d8b9ba5] Add a retry_on_deadlock to reservations_expire + - [b53adea] Add expire reservations in backport position. + - [e874ee2] Fixes Hyper-V SCSI slot selection + - [471e644] VMware: do not cache image when root_gb is 0 + - [825cfe4] Fix _parse_datetime in simple tenant usage extension + - [073ee06] Avoid traceback logs from simple tenant usage extension + - [9447203] replace NovaException with VirtualInterfaceCreate when neutron fails + - [6b7cb1a] libvirt: convert cpu features attribute from list to a set + - [7ca83e8] Delete image when backup operation failed on snapshot step + - [82a13b3] VM in rescue state must have a restricted set of actions + - [e7d2087] shelve doesn't work on nova-cells environment + - [aeb71a8] libvirt: return the correct instance path while cleanup_resize + - [cce6d22] Fix nova image-show with queued image + - [aff80d5] _translate_from_glance() can cause an unnecessary HTTP request + - [a058646] Loosen import_exceptions to cover all of gettextutils + * d/p/libvirt-convert-cpu-features-attribute-from-list-to-.patch: Dropped. + Code has been fixed upstream. + * d/p/libvirt-Handle-unsupported-host-capabilities.patch: Rebased. + + -- Chuck Short Mon, 06 Oct 2014 09:24:45 -0400 nova (1:2014.1.2-0ubuntu1) trusty; urgency=medium diff -Nru nova-2014.1.100+git201408190132~precise/debian/patches/cells-json-store.patch nova-2014.1.100+git201410062002~precise/debian/patches/cells-json-store.patch --- nova-2014.1.100+git201408190132~precise/debian/patches/cells-json-store.patch 1970-01-01 00:00:00.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/debian/patches/cells-json-store.patch 2014-10-07 00:05:54.000000000 +0000 @@ -0,0 +1,76 @@ +Description: Fix nova cells failing with a json topology file bug +Author: Liam Young +Bug: https://bugs.launchpad.net/nova/+bug/1314677 +Forwarded: https://review.openstack.org/#/c/124811/ +diff --git a/nova/cells/state.py b/nova/cells/state.py +index b9112bd..1e12450 100644 +--- a/nova/cells/state.py ++++ b/nova/cells/state.py +@@ -152,10 +152,7 @@ class CellStateManager(base.Base): + cells_config = CONF.cells.cells_config + + if cells_config: +- config_path = CONF.find_file(cells_config) +- if not config_path: +- raise cfg.ConfigFilesNotFoundError(config_files=[cells_config]) +- return CellStateManagerFile(cell_state_cls, config_path) ++ return CellStateManagerFile(cell_state_cls) + + return CellStateManagerDB(cell_state_cls) + +@@ -450,8 +447,11 @@ class CellStateManagerDB(CellStateManager): + + + class CellStateManagerFile(CellStateManager): +- def __init__(self, cell_state_cls, cells_config_path): +- self.cells_config_path = cells_config_path ++ def __init__(self, cell_state_cls=None): ++ cells_config = CONF.cells.cells_config ++ self.cells_config_path = CONF.find_file(cells_config) ++ if not self.cells_config_path: ++ raise cfg.ConfigFilesNotFoundError(config_files=[cells_config]) + super(CellStateManagerFile, self).__init__(cell_state_cls) + + def _cell_data_sync(self, force=False): +diff --git a/nova/tests/cells/test_cells_state_manager.py b/nova/tests/cells/test_cells_state_manager.py +index 1c29927..4841e14 100644 +--- a/nova/tests/cells/test_cells_state_manager.py ++++ b/nova/tests/cells/test_cells_state_manager.py +@@ -16,12 +16,16 @@ + Tests For CellStateManager + """ + ++import mock ++import six ++ + from oslo.config import cfg + + from nova.cells import state + from nova import db + from nova.db.sqlalchemy import models + from nova import exception ++from nova.openstack.common import fileutils + from nova import test + + +@@ -78,6 +82,19 @@ class TestCellsStateManager(test.TestCase): + state.CellStateManager) + self.assertEqual(['no_such_file_exists.conf'], e.config_files) + ++ @mock.patch.object(cfg.ConfigOpts, 'find_file') ++ @mock.patch.object(fileutils, 'read_cached_file') ++ def test_filemanager_returned(self, mock_read_cached_file, mock_find_file): ++ mock_find_file.return_value = "/etc/nova/cells.json" ++ mock_read_cached_file.return_value = (False, six.StringIO({})) ++ self.flags(cells_config='cells.json', group='cells') ++ self.assertIsInstance(state.CellStateManager(), ++ state.CellStateManagerFile) ++ ++ def test_dbmanager_returned(self): ++ self.assertIsInstance(state.CellStateManager(), ++ state.CellStateManagerDB) ++ + def test_capacity_no_reserve(self): + # utilize entire cell + cap = self._capacity(0.0) + diff -Nru nova-2014.1.100+git201408190132~precise/debian/patches/libvirt-convert-cpu-features-attribute-from-list-to-.patch nova-2014.1.100+git201410062002~precise/debian/patches/libvirt-convert-cpu-features-attribute-from-list-to-.patch --- nova-2014.1.100+git201408190132~precise/debian/patches/libvirt-convert-cpu-features-attribute-from-list-to-.patch 2014-08-19 05:33:56.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/debian/patches/libvirt-convert-cpu-features-attribute-from-list-to-.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,160 +0,0 @@ -Description: libvirt: convert cpu features attribute from list to a set - -Currently, the cpu features list which is being sent to libvirt, -when creating a domain or calling compareCPU, must contain only -unique entries. Multiple issues arise when we are updating the -features attribute in LibvirtConfigCPU class (for example during -migration). - -This change will change the features attribute from being a list -to a set. This make the LibvirtConfigCPU class keep only unique -features. -Adjusting the LibvirtConfigCPUFeature class to support set -operations by overriding the __eq__, __ne__ and __hash__ -methods. - -Author: Vladik Romanovsky -Origin: upstream, Change-Id: I6350fe0e827c860aea77cc4fe56f18f5c1483580 -Bug-Ubuntu: #1303536 -Last-Update: 2014-06-25 - ---- - nova/tests/virt/libvirt/test_libvirt.py | 8 ++++---- - nova/tests/virt/libvirt/test_libvirt_config.py | 24 +++++++++++++++++++++++- - nova/virt/libvirt/config.py | 16 +++++++++++++--- - nova/virt/libvirt/driver.py | 2 +- - 4 files changed, 41 insertions(+), 9 deletions(-) - -diff --git a/nova/tests/virt/libvirt/test_libvirt.py b/nova/tests/virt/libvirt/test_libvirt.py -index 1f744b7..9139ab9 100644 ---- a/nova/tests/virt/libvirt/test_libvirt.py -+++ b/nova/tests/virt/libvirt/test_libvirt.py -@@ -2206,8 +2206,8 @@ class LibvirtConnTestCase(test.TestCase): - cpu.model = "Opteron_G4" - cpu.vendor = "AMD" - -- cpu.features.append(vconfig.LibvirtConfigGuestCPUFeature("tm2")) -- cpu.features.append(vconfig.LibvirtConfigGuestCPUFeature("ht")) -+ cpu.add_feature(vconfig.LibvirtConfigGuestCPUFeature("tm2")) -+ cpu.add_feature(vconfig.LibvirtConfigGuestCPUFeature("ht")) - - caps = vconfig.LibvirtConfigCaps() - caps.host = vconfig.LibvirtConfigCapsHost() -@@ -2235,8 +2235,8 @@ class LibvirtConnTestCase(test.TestCase): - self.assertEqual(conf.cpu.model, "Opteron_G4") - self.assertEqual(conf.cpu.vendor, "AMD") - self.assertEqual(len(conf.cpu.features), 2) -- self.assertEqual(conf.cpu.features[0].name, "tm2") -- self.assertEqual(conf.cpu.features[1].name, "ht") -+ self.assertEqual(conf.cpu.features.pop().name, "tm2") -+ self.assertEqual(conf.cpu.features.pop().name, "ht") - - def test_get_guest_cpu_config_custom_old(self): - def get_lib_version_stub(): -diff --git a/nova/tests/virt/libvirt/test_libvirt_config.py b/nova/tests/virt/libvirt/test_libvirt_config.py -index 2251bf8..fa65ebc 100644 ---- a/nova/tests/virt/libvirt/test_libvirt_config.py -+++ b/nova/tests/virt/libvirt/test_libvirt_config.py -@@ -235,8 +235,30 @@ class LibvirtConfigCPUTest(LibvirtConfigBaseTest): - x86_64 - Penryn - Intel -+ - -+ -+ """) -+ -+ def test_only_uniq_cpu_featues(self): -+ obj = config.LibvirtConfigCPU() -+ obj.model = "Penryn" -+ obj.vendor = "Intel" -+ obj.arch = "x86_64" -+ -+ obj.add_feature(config.LibvirtConfigCPUFeature("mtrr")) -+ obj.add_feature(config.LibvirtConfigCPUFeature("apic")) -+ obj.add_feature(config.LibvirtConfigCPUFeature("apic")) -+ obj.add_feature(config.LibvirtConfigCPUFeature("mtrr")) -+ -+ xml = obj.to_xml() -+ self.assertXmlEqual(xml, """ -+ -+ x86_64 -+ Penryn -+ Intel - -+ - - """) - -@@ -285,8 +307,8 @@ class LibvirtConfigGuestCPUTest(LibvirtConfigBaseTest): - x86_64 - Penryn - Intel -- - -+ - - """) - -diff --git a/nova/virt/libvirt/config.py b/nova/virt/libvirt/config.py -index a931b57..f32e178 100644 ---- a/nova/virt/libvirt/config.py -+++ b/nova/virt/libvirt/config.py -@@ -250,6 +250,15 @@ class LibvirtConfigCPUFeature(LibvirtConfigObject): - - return ft - -+ def __eq__(self, obj): -+ return obj.name == self.name -+ -+ def __ne__(self, obj): -+ return obj.name != self.name -+ -+ def __hash__(self): -+ return hash(self.name) -+ - - class LibvirtConfigCPU(LibvirtConfigObject): - -@@ -265,7 +274,7 @@ class LibvirtConfigCPU(LibvirtConfigObject): - self.cores = None - self.threads = None - -- self.features = [] -+ self.features = set() - - def parse_dom(self, xmldoc): - super(LibvirtConfigCPU, self).parse_dom(xmldoc) -@@ -305,13 +314,14 @@ class LibvirtConfigCPU(LibvirtConfigObject): - top.set("threads", str(self.threads)) - cpu.append(top) - -- for f in self.features: -+ # sorting the features to allow more predictable tests -+ for f in sorted(self.features, key=lambda x: x.name): - cpu.append(f.format_dom()) - - return cpu - - def add_feature(self, feat): -- self.features.append(feat) -+ self.features.add(feat) - - - class LibvirtConfigGuestCPUFeature(LibvirtConfigCPUFeature): -diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py -index df367dd..7402363 100644 ---- a/nova/virt/libvirt/driver.py -+++ b/nova/virt/libvirt/driver.py -@@ -2888,7 +2888,7 @@ class LibvirtDriver(driver.ComputeDriver): - for hostfeat in hostcpu.features: - guestfeat = vconfig.LibvirtConfigGuestCPUFeature(hostfeat.name) - guestfeat.policy = "require" -- guestcpu.features.append(guestfeat) -+ guestcpu.add_feature(guestfeat) - - return guestcpu - --- -1.9.1 - diff -Nru nova-2014.1.100+git201408190132~precise/debian/patches/libvirt-Handle-unsupported-host-capabilities.patch nova-2014.1.100+git201410062002~precise/debian/patches/libvirt-Handle-unsupported-host-capabilities.patch --- nova-2014.1.100+git201408190132~precise/debian/patches/libvirt-Handle-unsupported-host-capabilities.patch 2014-08-19 05:33:56.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/debian/patches/libvirt-Handle-unsupported-host-capabilities.patch 2014-10-07 00:05:54.000000000 +0000 @@ -1,10 +1,9 @@ Description: Fix exception when starting LXC containers with libvirt-lxc. Author: Chuck Short Forwarded: Not Needed. -diff -Naurp nova-2014.1.1.orig/nova/tests/virt/libvirt/fakelibvirt.py nova-2014.1.1/nova/tests/virt/libvirt/fakelibvirt.py ---- nova-2014.1.1.orig/nova/tests/virt/libvirt/fakelibvirt.py 2014-06-05 17:33:22.000000000 -0400 -+++ nova-2014.1.1/nova/tests/virt/libvirt/fakelibvirt.py 2014-06-09 12:00:11.032072000 -0400 -@@ -172,18 +172,76 @@ def _parse_disk_info(element): +--- a/nova/tests/virt/libvirt/fakelibvirt.py ++++ b/nova/tests/virt/libvirt/fakelibvirt.py +@@ -172,18 +172,76 @@ class libvirtError(Exception): @@ -89,7 +88,7 @@ class NWFilter(object): -@@ -219,8 +277,10 @@ class Domain(object): +@@ -219,8 +277,10 @@ try: tree = etree.fromstring(xml) except etree.ParseError: @@ -102,7 +101,7 @@ definition = {} -@@ -369,7 +429,11 @@ class Domain(object): +@@ -369,7 +429,11 @@ 123456789L] def migrateToURI(self, desturi, flags, dname, bandwidth): @@ -115,7 +114,7 @@ def attachDevice(self, xml): disk_info = _parse_disk_info(etree.fromstring(xml)) -@@ -380,7 +444,11 @@ class Domain(object): +@@ -380,7 +444,11 @@ def attachDeviceFlags(self, xml, flags): if (flags & VIR_DOMAIN_AFFECT_LIVE and self._state != VIR_DOMAIN_RUNNING): @@ -128,7 +127,7 @@ self.attachDevice(xml) def detachDevice(self, xml): -@@ -533,9 +601,11 @@ class Connection(object): +@@ -533,9 +601,11 @@ 'test:///default'] if uri not in uri_whitelist: @@ -143,7 +142,7 @@ self.readonly = readonly self._uri = uri -@@ -594,16 +664,20 @@ class Connection(object): +@@ -594,16 +664,20 @@ def lookupByID(self, id): if id in self._running_vms: return self._running_vms[id] @@ -170,7 +169,7 @@ def _emit_lifecycle(self, dom, event, detail): if VIR_DOMAIN_EVENT_ID_LIFECYCLE not in self._event_callbacks: -@@ -904,14 +978,21 @@ class Connection(object): +@@ -904,14 +978,21 @@ 'user': 26728850000000L, 'iowait': 6121490000000L} else: @@ -195,7 +194,7 @@ def nwfilterDefineXML(self, xml): nwfilter = NWFilter(self, xml) -@@ -964,6 +1045,24 @@ def registerErrorHandler(handler, ctxt): +@@ -964,6 +1045,24 @@ pass @@ -220,18 +219,37 @@ virDomain = Domain -diff -Naurp nova-2014.1.1.orig/nova/tests/virt/libvirt/test_libvirt.py nova-2014.1.1/nova/tests/virt/libvirt/test_libvirt.py ---- nova-2014.1.1.orig/nova/tests/virt/libvirt/test_libvirt.py 2014-06-05 17:33:22.000000000 -0400 -+++ nova-2014.1.1/nova/tests/virt/libvirt/test_libvirt.py 2014-06-09 12:00:11.040072000 -0400 -@@ -60,6 +60,7 @@ import nova.tests.image.fake - from nova.tests import matchers - from nova.tests.objects import test_pci_device - from nova.tests.virt.libvirt import fake_libvirt_utils -+from nova.tests.virt.libvirt import fakelibvirt - from nova import utils - from nova import version - from nova.virt import configdrive -@@ -80,7 +81,7 @@ from nova.virt import netutils +--- a/nova/virt/libvirt/driver.py ++++ b/nova/virt/libvirt/driver.py +@@ -77,6 +77,7 @@ + from nova.openstack.common import excutils + from nova.openstack.common import fileutils + from nova.openstack.common.gettextutils import _ ++from nova.openstack.common.gettextutils import _LW + from nova.openstack.common import importutils + from nova.openstack.common import jsonutils + from nova.openstack.common import log as logging +@@ -2888,9 +2889,14 @@ + # this -1 checking should be removed later. + if features and features != -1: + self._caps.host.cpu.parse_str(features) +- except libvirt.VIR_ERR_NO_SUPPORT: +- # Note(yjiang5): ignore if libvirt has no support +- pass ++ except libvirt.libvirtError as ex: ++ error_code = ex.get_error_code() ++ if error_code == libvirt.VIR_ERR_NO_SUPPORT: ++ LOG.warn(_LW("URI %(uri)s does not support full set" ++ " of host capabilities: " "%(error)s"), ++ {'uri': self.uri(), 'error': ex}) ++ else: ++ raise + return self._caps + + def get_host_uuid(self): +--- a/nova/tests/virt/libvirt/test_libvirt.py ++++ b/nova/tests/virt/libvirt/test_libvirt.py +@@ -83,7 +83,7 @@ try: import libvirt except ImportError: @@ -240,7 +258,7 @@ libvirt_driver.libvirt = libvirt -@@ -849,6 +850,42 @@ class LibvirtConnTestCase(test.TestCase) +@@ -887,6 +887,42 @@ caps = conn.get_host_capabilities() self.assertIn('aes', [x.name for x in caps.host.cpu.features]) @@ -283,32 +301,3 @@ def test_lxc_get_host_capabilities_failed(self): conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) -diff -Naurp nova-2014.1.1.orig/nova/virt/libvirt/driver.py nova-2014.1.1/nova/virt/libvirt/driver.py ---- nova-2014.1.1.orig/nova/virt/libvirt/driver.py 2014-06-05 17:33:22.000000000 -0400 -+++ nova-2014.1.1/nova/virt/libvirt/driver.py 2014-06-09 11:59:55.232072000 -0400 -@@ -76,6 +76,7 @@ from nova.objects import service as serv - from nova.openstack.common import excutils - from nova.openstack.common import fileutils - from nova.openstack.common.gettextutils import _ -+from nova.openstack.common.gettextutils import _LW - from nova.openstack.common import importutils - from nova.openstack.common import jsonutils - from nova.openstack.common import log as logging -@@ -2859,9 +2860,14 @@ class LibvirtDriver(driver.ComputeDriver - # this -1 checking should be removed later. - if features and features != -1: - self._caps.host.cpu.parse_str(features) -- except libvirt.VIR_ERR_NO_SUPPORT: -- # Note(yjiang5): ignore if libvirt has no support -- pass -+ except libvirt.libvirtError as ex: -+ error_code = ex.get_error_code() -+ if error_code == libvirt.VIR_ERR_NO_SUPPORT: -+ LOG.warn(_LW("URI %(uri)s does not support full set" -+ " of host capabilities: " "%(error)s"), -+ {'uri': self.uri(), 'error': ex}) -+ else: -+ raise - return self._caps - - def get_host_uuid(self): diff -Nru nova-2014.1.100+git201408190132~precise/debian/patches/series nova-2014.1.100+git201410062002~precise/debian/patches/series --- nova-2014.1.100+git201408190132~precise/debian/patches/series 2014-08-19 05:33:56.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/debian/patches/series 2014-10-07 00:05:54.000000000 +0000 @@ -1,7 +1,7 @@ # Ubuntu specific patches below here. Note these can be dropped eventually. -libvirt-convert-cpu-features-attribute-from-list-to-.patch fix-requirements.patch skip_ipv6_test.patch arm-console-patch.patch update-run-tests.patch libvirt-Handle-unsupported-host-capabilities.patch +cells-json-store.patch diff -Nru nova-2014.1.100+git201408190132~precise/doc/source/devref/vmstates.rst nova-2014.1.100+git201410062002~precise/doc/source/devref/vmstates.rst --- nova-2014.1.100+git201408190132~precise/doc/source/devref/vmstates.rst 2014-08-19 05:31:52.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/doc/source/devref/vmstates.rst 2014-10-07 00:02:46.000000000 +0000 @@ -88,6 +88,7 @@ rescue -> error active -> rescue stopped -> rescue + error -> rescue unrescue [shape="rectangle"] unrescue -> active @@ -139,7 +140,9 @@ reboot -> error active -> reboot stopped -> reboot - rescued -> reboot + paused -> reboot + suspended -> reboot + error -> reboot live_migrate [shape="rectangle"] live_migrate -> active @@ -159,4 +162,4 @@ power states when a new VM instance is created. -.. image:: /images/run_instance_walkthrough.png \ No newline at end of file +.. image:: /images/run_instance_walkthrough.png diff -Nru nova-2014.1.100+git201408190132~precise/etc/nova/rootwrap.d/compute.filters nova-2014.1.100+git201410062002~precise/etc/nova/rootwrap.d/compute.filters --- nova-2014.1.100+git201408190132~precise/etc/nova/rootwrap.d/compute.filters 2014-08-19 05:31:52.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/etc/nova/rootwrap.d/compute.filters 2014-10-07 00:02:46.000000000 +0000 @@ -203,7 +203,7 @@ # nova/virt/libvirt/volume.py: sginfo: CommandFilter, sginfo, root sg_scan: CommandFilter, sg_scan, root -ln: RegExpFilter, ln, root, ln, --symbolic, --force, /dev/mapper/ip-.*-iscsi-iqn.2010-10.org.openstack:volume-.*, /dev/disk/by-path/ip-.*-iscsi-iqn.2010-10.org.openstack:volume-.* +ln: RegExpFilter, ln, root, ln, --symbolic, --force, /dev/mapper/ip-.*-iscsi-iqn.*, /dev/disk/by-path/ip-.*-iscsi-iqn.* # nova/volume/encryptors.py: # nova/virt/libvirt/dmcrypt.py: diff -Nru nova-2014.1.100+git201408190132~precise/nova/api/openstack/common.py nova-2014.1.100+git201410062002~precise/nova/api/openstack/common.py --- nova-2014.1.100+git201408190132~precise/nova/api/openstack/common.py 2014-08-19 05:31:52.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/api/openstack/common.py 2014-10-07 00:02:46.000000000 +0000 @@ -538,10 +538,18 @@ items, collection_name, id_key="uuid"): - """Retrieve 'next' link, if applicable.""" + """Retrieve 'next' link, if applicable. This is included if: + 1) 'limit' param is specified and equals the number of items. + 2) 'limit' param is specified but it exceeds CONF.osapi_max_limit, + in this case the number of items is CONF.osapi_max_limit. + 3) 'limit' param is NOT specified but the number of items is + CONF.osapi_max_limit. + """ links = [] - limit = int(request.params.get("limit", 0)) - if limit and limit == len(items): + max_items = min( + int(request.params.get("limit", CONF.osapi_max_limit)), + CONF.osapi_max_limit) + if max_items and max_items == len(items): last_item = items[-1] if id_key in last_item: last_item_id = last_item[id_key] diff -Nru nova-2014.1.100+git201408190132~precise/nova/api/openstack/compute/contrib/floating_ips.py nova-2014.1.100+git201410062002~precise/nova/api/openstack/compute/contrib/floating_ips.py --- nova-2014.1.100+git201408190132~precise/nova/api/openstack/compute/contrib/floating_ips.py 2014-08-19 05:31:52.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/api/openstack/compute/contrib/floating_ips.py 2014-10-07 00:02:46.000000000 +0000 @@ -180,16 +180,14 @@ # get the associated instance object (if any) instance = get_instance_by_floating_ip_addr(self, context, address) - - # disassociate if associated - if floating_ip.get('fixed_ip_id'): - try: - disassociate_floating_ip(self, context, instance, address) - except exception.FloatingIpNotAssociated: - LOG.info(_("Floating ip %s has been disassociated") % address) - - # release ip from project - self.network_api.release_floating_ip(context, address) + try: + self.network_api.disassociate_and_release_floating_ip( + context, instance, floating_ip) + except exception.Forbidden: + raise webob.exc.HTTPForbidden() + except exception.CannotDisassociateAutoAssignedFloatingIP: + msg = _('Cannot disassociate auto assigned floating ip') + raise webob.exc.HTTPForbidden(explanation=msg) return webob.Response(status_int=202) def _get_ip_by_id(self, context, value): diff -Nru nova-2014.1.100+git201408190132~precise/nova/api/openstack/compute/contrib/simple_tenant_usage.py nova-2014.1.100+git201410062002~precise/nova/api/openstack/compute/contrib/simple_tenant_usage.py --- nova-2014.1.100+git201408190132~precise/nova/api/openstack/compute/contrib/simple_tenant_usage.py 2014-08-19 05:31:52.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/api/openstack/compute/contrib/simple_tenant_usage.py 2014-10-07 00:02:46.000000000 +0000 @@ -51,6 +51,13 @@ subelem.text = subelem_tag +def parse_strtime(dstr, fmt): + try: + return timeutils.parse_strtime(dstr, fmt) + except (TypeError, ValueError) as e: + raise exception.InvalidStrTime(reason=unicode(e)) + + class SimpleTenantUsageTemplate(xmlutil.TemplateBuilder): def construct(self): root = xmlutil.TemplateElement('tenant_usage', selector='tenant_usage') @@ -211,13 +218,18 @@ value = timeutils.utcnow() elif isinstance(dtstr, datetime.datetime): value = dtstr - try: - value = timeutils.parse_strtime(dtstr, "%Y-%m-%dT%H:%M:%S") - except Exception: - try: - value = timeutils.parse_strtime(dtstr, "%Y-%m-%dT%H:%M:%S.%f") - except Exception: - value = timeutils.parse_strtime(dtstr, "%Y-%m-%d %H:%M:%S.%f") + else: + for fmt in ["%Y-%m-%dT%H:%M:%S", + "%Y-%m-%dT%H:%M:%S.%f", + "%Y-%m-%d %H:%M:%S.%f"]: + try: + value = parse_strtime(dtstr, fmt) + break + except exception.InvalidStrTime: + pass + else: + msg = _("Datetime is in invalid format") + raise exception.InvalidStrTime(reason=msg) # NOTE(mriedem): Instance object DateTime fields are timezone-aware # so we have to force UTC timezone for comparing this datetime against @@ -249,7 +261,12 @@ authorize_list(context) - (period_start, period_stop, detailed) = self._get_datetime_range(req) + try: + (period_start, period_stop, detailed) = self._get_datetime_range( + req) + except exception.InvalidStrTime as e: + raise exc.HTTPBadRequest(explanation=e.format_message()) + now = timeutils.parse_isotime(timeutils.strtime()) if period_stop > now: period_stop = now @@ -267,7 +284,12 @@ authorize_show(context, {'project_id': tenant_id}) - (period_start, period_stop, ignore) = self._get_datetime_range(req) + try: + (period_start, period_stop, ignore) = self._get_datetime_range( + req) + except exception.InvalidStrTime as e: + raise exc.HTTPBadRequest(explanation=e.format_message()) + now = timeutils.parse_isotime(timeutils.strtime()) if period_stop > now: period_stop = now diff -Nru nova-2014.1.100+git201408190132~precise/nova/cells/messaging.py nova-2014.1.100+git201410062002~precise/nova/cells/messaging.py --- nova-2014.1.100+git201408190132~precise/nova/cells/messaging.py 2014-08-19 05:31:52.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/cells/messaging.py 2014-10-07 00:02:46.000000000 +0000 @@ -846,6 +846,10 @@ instance = {'uuid': instance.uuid} self.msg_runner.instance_destroy_at_top(ctxt, instance) + except exception.InstanceInfoCacheNotFound: + if method != 'delete': + raise + fn = getattr(self.compute_api, method, None) return fn(ctxt, instance, *args, **kwargs) diff -Nru nova-2014.1.100+git201408190132~precise/nova/compute/api.py nova-2014.1.100+git201410062002~precise/nova/compute/api.py --- nova-2014.1.100+git201408190132~precise/nova/compute/api.py 2014-08-19 05:31:52.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/compute/api.py 2014-10-07 00:02:46.000000000 +0000 @@ -1759,8 +1759,7 @@ @check_instance_lock @check_instance_host @check_instance_cell - @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.RESCUED, - vm_states.ERROR], + @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.ERROR], task_state=[None]) def stop(self, context, instance, do_cast=True): """Stop an instance.""" @@ -2522,7 +2521,7 @@ @wrap_check_policy @check_instance_lock @check_instance_cell - @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.RESCUED]) + @check_instance_state(vm_state=[vm_states.ACTIVE]) def pause(self, context, instance): """Pause the given instance.""" instance.task_state = task_states.PAUSING @@ -2549,7 +2548,7 @@ @wrap_check_policy @check_instance_lock @check_instance_cell - @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.RESCUED]) + @check_instance_state(vm_state=[vm_states.ACTIVE]) def suspend(self, context, instance): """Suspend the given instance.""" instance.task_state = task_states.SUSPENDING diff -Nru nova-2014.1.100+git201408190132~precise/nova/compute/manager.py nova-2014.1.100+git201410062002~precise/nova/compute/manager.py --- nova-2014.1.100+git201408190132~precise/nova/compute/manager.py 2014-08-19 05:31:52.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/compute/manager.py 2014-10-07 00:02:46.000000000 +0000 @@ -706,8 +706,8 @@ try: network_info = self._get_instance_nw_info(context, instance) - bdi = self._get_instance_volume_block_device_info(context, - instance) + bdi = self._get_instance_block_device_info(context, + instance) destroy_disks = not (self._is_instance_storage_shared( context, instance)) except exception.InstanceNotFound: @@ -911,8 +911,8 @@ power_on = (instance.system_metadata.get('old_vm_state') != vm_states.STOPPED) - block_dev_info = self._get_instance_volume_block_device_info( - context, instance) + block_dev_info = self._get_instance_block_device_info(context, + instance) self.driver.finish_revert_migration(context, instance, net_info, block_dev_info, power_on) @@ -942,8 +942,7 @@ instance=instance) block_device_info = \ - self._get_instance_volume_block_device_info( - context, instance) + self._get_instance_block_device_info(context, instance) try: self.driver.resume_state_on_host_boot( @@ -988,9 +987,9 @@ return retry_reboot, reboot_type def handle_lifecycle_event(self, event): - LOG.info(_("Lifecycle event %(state)d on VM %(uuid)s") % - {'state': event.get_transition(), - 'uuid': event.get_instance_uuid()}) + LOG.info(_("VM %(state)s (Lifecycle Event)") % + {'state': event.get_name()}, + instance_uuid=event.get_instance_uuid()) context = nova.context.get_admin_context(read_deleted='yes') instance = instance_obj.Instance.get_by_uuid( context, event.get_instance_uuid()) @@ -1717,6 +1716,12 @@ block_device_info['swap']) return block_device_info + except exception.OverQuota: + msg = ('Failed to create block device for instance due to being ' + 'over volume resource quota') + LOG.debug(msg, instance=instance) + raise exception.InvalidBDM() + except Exception: LOG.exception(_('Instance failed block device setup'), instance=instance) @@ -1792,14 +1797,16 @@ self.network_api.deallocate_for_instance( context, instance, requested_networks=requested_networks) - def _get_instance_volume_block_device_info(self, context, instance, - refresh_conn_info=False, - bdms=None): + def _get_instance_block_device_info(self, context, instance, + refresh_conn_info=False, + bdms=None): """Transform volumes to the driver block_device format.""" if not bdms: bdms = (block_device_obj.BlockDeviceMappingList. get_by_instance_uuid(context, instance['uuid'])) + swap = driver_block_device.convert_swap(bdms) + ephemerals = driver_block_device.convert_ephemerals(bdms) block_device_mapping = ( driver_block_device.convert_volumes(bdms) + driver_block_device.convert_snapshots(bdms) + @@ -1817,9 +1824,17 @@ self.driver) if self.use_legacy_block_device_info: + swap = driver_block_device.legacy_block_devices(swap) + ephemerals = driver_block_device.legacy_block_devices(ephemerals) block_device_mapping = driver_block_device.legacy_block_devices( block_device_mapping) - return {'block_device_mapping': block_device_mapping} + + # Get swap out of the list + swap = driver_block_device.get_swap(swap) + + return {'swap': swap, + 'ephemerals': ephemerals, + 'block_device_mapping': block_device_mapping} # NOTE(mikal): No object_compat wrapper on this method because its # callers all pass objects already @@ -2127,7 +2142,7 @@ # NOTE(vish) get bdms before destroying the instance vol_bdms = [bdm for bdm in bdms if bdm.is_volume] - block_device_info = self._get_instance_volume_block_device_info( + block_device_info = self._get_instance_block_device_info( context, instance, bdms=bdms) # NOTE(melwitt): attempt driver destroy before releasing ip, may @@ -2295,8 +2310,8 @@ def _power_on(self, context, instance): network_info = self._get_instance_nw_info(context, instance) - block_device_info = self._get_instance_volume_block_device_info( - context, instance) + block_device_info = self._get_instance_block_device_info(context, + instance) self.driver.power_on(context, instance, network_info, block_device_info) @@ -2515,7 +2530,7 @@ get_by_instance_uuid(context, instance.uuid)) block_device_info = \ - self._get_instance_volume_block_device_info( + self._get_instance_block_device_info( context, instance, bdms=bdms) def detach_block_devices(context, bdms): @@ -2609,8 +2624,8 @@ context = context.elevated() LOG.audit(_("Rebooting instance"), context=context, instance=instance) - block_device_info = self._get_instance_volume_block_device_info( - context, instance) + block_device_info = self._get_instance_block_device_info(context, + instance) network_info = self._get_instance_nw_info(context, instance) @@ -2685,6 +2700,13 @@ self._notify_about_instance_usage(context, instance, "reboot.end") + @delete_image_on_error + def _do_snapshot_instance(self, context, image_id, instance, rotation): + if rotation < 0: + raise exception.RotationRequiredForBackup() + self._snapshot_instance(context, image_id, instance, + task_states.IMAGE_BACKUP) + @wrap_exception() @reverts_task_state @wrap_instance_fault @@ -2695,10 +2717,7 @@ :param backup_type: daily | weekly :param rotation: int representing how many backups to keep around """ - if rotation < 0: - raise exception.RotationRequiredForBackup() - self._snapshot_instance(context, image_id, instance, - task_states.IMAGE_BACKUP) + self._do_snapshot_instance(context, image_id, instance, rotation) self._rotate_backups(context, instance, backup_type, rotation) @wrap_exception() @@ -3195,7 +3214,7 @@ network_info = self._get_instance_nw_info(context, instance) bdms = (block_device_obj.BlockDeviceMappingList. get_by_instance_uuid(context, instance.uuid)) - block_device_info = self._get_instance_volume_block_device_info( + block_device_info = self._get_instance_block_device_info( context, instance, bdms=bdms) self.driver.destroy(context, instance, network_info, @@ -3257,7 +3276,7 @@ self.network_api.setup_networks_on_host(context, instance, migration['source_compute']) - block_device_info = self._get_instance_volume_block_device_info( + block_device_info = self._get_instance_block_device_info( context, instance, refresh_conn_info=True) power_on = old_vm_state != vm_states.STOPPED @@ -3450,7 +3469,7 @@ bdms = (block_device_obj.BlockDeviceMappingList. get_by_instance_uuid(context, instance.uuid)) - block_device_info = self._get_instance_volume_block_device_info( + block_device_info = self._get_instance_block_device_info( context, instance, bdms=bdms) disk_info = self.driver.migrate_disk_and_power_off( @@ -3536,7 +3555,7 @@ context, instance, "finish_resize.start", network_info=network_info) - block_device_info = self._get_instance_volume_block_device_info( + block_device_info = self._get_instance_block_device_info( context, instance, refresh_conn_info=True) # NOTE(mriedem): If the original vm_state was STOPPED, we don't @@ -3739,7 +3758,7 @@ LOG.audit(_('Resuming'), context=context, instance=instance) network_info = self._get_instance_nw_info(context, instance) - block_device_info = self._get_instance_volume_block_device_info( + block_device_info = self._get_instance_block_device_info( context, instance) self.driver.resume(context, instance, network_info, @@ -3825,8 +3844,8 @@ current_power_state = self._get_power_state(context, instance) network_info = self._get_instance_nw_info(context, instance) - block_device_info = self._get_instance_volume_block_device_info( - context, instance) + block_device_info = self._get_instance_block_device_info(context, + instance) self.driver.destroy(context, instance, network_info, block_device_info) @@ -4483,7 +4502,7 @@ required for live migration without shared storage. """ - block_device_info = self._get_instance_volume_block_device_info( + block_device_info = self._get_instance_block_device_info( context, instance, refresh_conn_info=True) network_info = self._get_instance_nw_info(context, instance) @@ -4582,8 +4601,8 @@ ctxt, instance['uuid']) # Cleanup source host post live-migration - block_device_info = self._get_instance_volume_block_device_info( - ctxt, instance, bdms) + block_device_info = self._get_instance_block_device_info( + ctxt, instance, bdms=bdms) self.driver.post_live_migration(ctxt, instance, block_device_info, migrate_data) @@ -4694,8 +4713,8 @@ self._notify_about_instance_usage( context, instance, "live_migration.post.dest.start", network_info=network_info) - block_device_info = self._get_instance_volume_block_device_info( - context, instance) + block_device_info = self._get_instance_block_device_info(context, + instance) self.driver.post_live_migration_at_destination(context, instance, network_info, @@ -4792,8 +4811,8 @@ # NOTE(vish): The mapping is passed in so the driver can disconnect # from remote volumes if necessary - block_device_info = self._get_instance_volume_block_device_info( - context, instance) + block_device_info = self._get_instance_block_device_info(context, + instance) self.driver.rollback_live_migration_at_destination(context, instance, network_info, block_device_info) self._notify_about_instance_usage( diff -Nru nova-2014.1.100+git201408190132~precise/nova/db/sqlalchemy/api.py nova-2014.1.100+git201410062002~precise/nova/db/sqlalchemy/api.py --- nova-2014.1.100+git201408190132~precise/nova/db/sqlalchemy/api.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/db/sqlalchemy/api.py 2014-10-07 00:02:46.000000000 +0000 @@ -525,6 +525,7 @@ @require_admin_context +@_retry_on_deadlock def service_update(context, service_id, values): session = get_session() with session.begin(): @@ -1026,6 +1027,7 @@ float_ip_ref.save(session=session) except db_exc.DBDuplicateEntry: raise exception.FloatingIpExists(address=values['address']) + return float_ip_ref def _dnsdomain_get(context, session, fqdomain): @@ -3349,6 +3351,7 @@ @require_admin_context +@_retry_on_deadlock def reservation_expire(context): session = get_session() with session.begin(): diff -Nru nova-2014.1.100+git201408190132~precise/nova/db/sqlalchemy/migrate_repo/versions/234_add_expire_reservations_index.py nova-2014.1.100+git201410062002~precise/nova/db/sqlalchemy/migrate_repo/versions/234_add_expire_reservations_index.py --- nova-2014.1.100+git201408190132~precise/nova/db/sqlalchemy/migrate_repo/versions/234_add_expire_reservations_index.py 1970-01-01 00:00:00.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/db/sqlalchemy/migrate_repo/versions/234_add_expire_reservations_index.py 2014-10-07 00:02:46.000000000 +0000 @@ -0,0 +1,59 @@ +# All Rights Reserved. +# +# 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 sqlalchemy import Index, MetaData, Table + +from nova.openstack.common.gettextutils import _ +from nova.openstack.common import log as logging + +LOG = logging.getLogger(__name__) + + +def _get_deleted_expire_index(table): + members = sorted(['deleted', 'expire']) + for idx in table.indexes: + if sorted(idx.columns.keys()) == members: + return idx + + +def upgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + reservations = Table('reservations', meta, autoload=True) + if _get_deleted_expire_index(reservations): + LOG.info(_('Skipped adding reservations_deleted_expire_idx ' + 'because an equivalent index already exists.')) + return + + # Based on expire_reservations query + # from: nova/db/sqlalchemy/api.py + index = Index('reservations_deleted_expire_idx', + reservations.c.deleted, reservations.c.expire) + + index.create(migrate_engine) + + +def downgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + reservations = Table('reservations', meta, autoload=True) + + index = _get_deleted_expire_index(reservations) + if index: + index.drop(migrate_engine) + else: + LOG.info(_('Skipped removing reservations_deleted_expire_idx ' + 'because index does not exist.')) diff -Nru nova-2014.1.100+git201408190132~precise/nova/exception.py nova-2014.1.100+git201410062002~precise/nova/exception.py --- nova-2014.1.100+git201408190132~precise/nova/exception.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/exception.py 2014-10-07 00:02:46.000000000 +0000 @@ -333,6 +333,10 @@ msg_fmt = _("Sort key supplied was not valid.") +class InvalidStrTime(Invalid): + msg_fmt = _("Invalid datetime string: %(reason)s") + + class InstanceInvalidState(Invalid): msg_fmt = _("Instance %(instance_uuid)s in %(attr)s %(state)s. Cannot " "%(method)s while the instance is in this state.") diff -Nru nova-2014.1.100+git201408190132~precise/nova/image/glance.py nova-2014.1.100+git201410062002~precise/nova/image/glance.py --- nova-2014.1.100+git201408190132~precise/nova/image/glance.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/image/glance.py 2014-10-07 00:02:46.000000000 +0000 @@ -509,14 +509,38 @@ def _extract_attributes(image): + #NOTE(hdd): If a key is not found, base.Resource.__getattr__() may perform + # a get(), resulting in a useless request back to glance. This list is + # therefore sorted, with dependent attributes as the end + # 'deleted_at' depends on 'deleted' + # 'checksum' depends on 'status' == 'active' IMAGE_ATTRIBUTES = ['size', 'disk_format', 'owner', - 'container_format', 'checksum', 'id', + 'container_format', 'status', 'id', 'name', 'created_at', 'updated_at', - 'deleted_at', 'deleted', 'status', + 'deleted', 'deleted_at', 'checksum', 'min_disk', 'min_ram', 'is_public'] + + queued = getattr(image, 'status') == 'queued' + queued_exclude_attrs = ['disk_format', 'container_format'] output = {} + for attr in IMAGE_ATTRIBUTES: - output[attr] = getattr(image, attr, None) + if attr == 'deleted_at' and not output['deleted']: + output[attr] = None + elif attr == 'checksum' and output['status'] != 'active': + output[attr] = None + # image may not have 'name' attr + elif attr == 'name': + output[attr] = getattr(image, attr, None) + #NOTE(liusheng): queued image may not have these attributes and 'name' + elif queued and attr in queued_exclude_attrs: + output[attr] = getattr(image, attr, None) + else: + # NOTE(xarses): Anything that is caught with the default value + # will result in a additional lookup to glance for said attr. + # Notable attributes that could have this issue: + # disk_format, container_format, name, deleted, checksum + output[attr] = getattr(image, attr, None) output['properties'] = getattr(image, 'properties', {}) diff -Nru nova-2014.1.100+git201408190132~precise/nova/network/api.py nova-2014.1.100+git201410062002~precise/nova/network/api.py --- nova-2014.1.100+git201408190132~precise/nova/network/api.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/network/api.py 2014-10-07 00:02:46.000000000 +0000 @@ -19,6 +19,8 @@ import functools import inspect +from oslo.config import cfg + from nova.compute import flavors from nova.db import base from nova import exception @@ -30,10 +32,13 @@ from nova.objects import instance_info_cache as info_cache_obj from nova.openstack.common import excutils from nova.openstack.common.gettextutils import _ +from nova.openstack.common import lockutils from nova.openstack.common import log as logging from nova import policy from nova import utils +CONF = cfg.CONF + LOG = logging.getLogger(__name__) @@ -57,8 +62,9 @@ msg = _('instance is a required argument to use @refresh_cache') raise Exception(msg) - update_instance_cache_with_nw_info(self, context, instance, - nw_info=res) + with lockutils.lock('refresh_cache-%s' % instance['uuid']): + update_instance_cache_with_nw_info(self, context, instance, + nw_info=res) # return the original function's return value return res @@ -126,12 +132,18 @@ def get_all(self, context): """Get all the networks. - If it is an admin user, api will return all the networks, - if it is a normal user, api will only return the networks which + If it is an admin user then api will return all the + networks. If it is a normal user and nova Flat or FlatDHCP + networking is being used then api will return all + networks. Otherwise api will only return the networks which belong to the user's project. """ + if "nova.network.manager.Flat" in CONF.network_manager: + project_only = "allow_none" + else: + project_only = True try: - return self.db.network_get_all(context, project_only=True) + return self.db.network_get_all(context, project_only=project_only) except exception.NoNetworksFound: return [] @@ -227,6 +239,26 @@ return self.floating_manager.deallocate_floating_ip(context, address, affect_auto_assigned) + def disassociate_and_release_floating_ip(self, context, instance, + floating_ip): + """Removes (deallocates) and deletes the floating ip. + + This api call was added to allow this to be done in one operation + if using neutron. + """ + + address = floating_ip['address'] + if floating_ip.get('fixed_ip_id'): + try: + self.disassociate_floating_ip(context, instance, address) + except exception.FloatingIpNotAssociated: + msg = ("Floating ip %s has already been disassociated, " + "perhaps by another concurrent action.") % address + LOG.debug(msg) + + # release ip from project + return self.release_floating_ip(context, address) + @wrap_check_policy @refresh_cache def associate_floating_ip(self, context, instance, diff -Nru nova-2014.1.100+git201408190132~precise/nova/network/linux_net.py nova-2014.1.100+git201410062002~precise/nova/network/linux_net.py --- nova-2014.1.100+git201408190132~precise/nova/network/linux_net.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/network/linux_net.py 2014-10-07 00:02:46.000000000 +0000 @@ -1537,6 +1537,11 @@ LOG.debug(msg, {'interface': interface, 'bridge': bridge}) out, err = _execute('brctl', 'addif', bridge, interface, check_exit_code=False, run_as_root=True) + if (err and err != "device %s is already a member of a bridge; " + "can't enslave it to bridge %s.\n" % (interface, bridge)): + msg = _('Failed to add interface: %s') % err + raise exception.NovaException(msg) + out, err = _execute('ip', 'link', 'set', interface, 'up', check_exit_code=False, run_as_root=True) @@ -1569,11 +1574,6 @@ _execute('ip', 'route', 'add', *fields, run_as_root=True) - if (err and err != "device %s is already a member of a bridge;" - "can't enslave it to bridge %s.\n" % (interface, bridge)): - msg = _('Failed to add interface: %s') % err - raise exception.NovaException(msg) - if filtering: # Don't forward traffic unless we were told to be a gateway ipv4_filter = iptables_manager.ipv4['filter'] diff -Nru nova-2014.1.100+git201408190132~precise/nova/network/neutronv2/api.py nova-2014.1.100+git201410062002~precise/nova/network/neutronv2/api.py --- nova-2014.1.100+git201408190132~precise/nova/network/neutronv2/api.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/network/neutronv2/api.py 2014-10-07 00:02:46.000000000 +0000 @@ -32,6 +32,7 @@ from nova.network.security_group import openstack_driver from nova.openstack.common import excutils from nova.openstack.common.gettextutils import _ +from nova.openstack.common import lockutils from nova.openstack.common import log as logging from nova.openstack.common import uuidutils @@ -468,7 +469,6 @@ """Return the port for the client given the port id.""" return neutronv2.get_client(context).show_port(port_id) - @refresh_cache def get_instance_nw_info(self, context, instance, networks=None, port_ids=None, use_slave=False): """Return network information for specified instance @@ -477,14 +477,20 @@ # NOTE(geekinutah): It would be nice if use_slave had us call # special APIs that pummeled slaves instead of # the master. For now we just ignore this arg. - result = self._get_instance_nw_info(context, instance, networks, - port_ids) + with lockutils.lock('refresh_cache-%s' % instance['uuid']): + result = self._get_instance_nw_info(context, instance, networks, + port_ids) + update_instance_info_cache(self, context, + instance, + nw_info=result, + update_cells=False) return result def _get_instance_nw_info(self, context, instance, networks=None, port_ids=None): - # keep this caching-free version of the get_instance_nw_info method - # because it is used by the caching logic itself. + # NOTE(danms): This is an inner method intended to be called + # by other code that updates instance nwinfo. It *must* be + # called with the refresh_cache-%(instance_uuid) lock held! LOG.debug(_('get_instance_nw_info() for %s'), instance['display_name']) nw_info = self._build_network_info_model(context, instance, networks, port_ids) @@ -978,9 +984,24 @@ # since it is not used anywhere in nova code and I could # find why this parameter exists. + self._release_floating_ip(context, address) + + def disassociate_and_release_floating_ip(self, context, instance, + floating_ip): + """Removes (deallocates) and deletes the floating ip. + + This api call was added to allow this to be done in one operation + if using neutron. + """ + self._release_floating_ip(context, floating_ip['address'], + raise_if_associated=False) + + def _release_floating_ip(self, context, address, + raise_if_associated=True): client = neutronv2.get_client(context) fip = self._get_floating_ip_by_address(client, address) - if fip['port_id']: + + if raise_if_associated and fip['port_id']: raise exception.FloatingIpAssociated(address=address) client.delete_floatingip(fip['id']) diff -Nru nova-2014.1.100+git201408190132~precise/nova/objects/floating_ip.py nova-2014.1.100+git201410062002~precise/nova/objects/floating_ip.py --- nova-2014.1.100+git201408190132~precise/nova/objects/floating_ip.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/objects/floating_ip.py 2014-10-07 00:02:46.000000000 +0000 @@ -124,6 +124,14 @@ if 'address' in updates: raise exception.ObjectActionError(action='save', reason='address is not mutable') + if 'fixed_ip_id' in updates: + reason = 'fixed_ip_id is not mutable' + raise exception.ObjectActionError(action='save', reason=reason) + + # NOTE(danms): Make sure we don't pass the calculated fixed_ip + # relationship to the DB update method + updates.pop('fixed_ip', None) + db_floatingip = db.floating_ip_update(context, str(self.address), updates) self._from_db_object(context, self, db_floatingip) diff -Nru nova-2014.1.100+git201408190132~precise/nova/openstack/common/db/sqlalchemy/session.py nova-2014.1.100+git201410062002~precise/nova/openstack/common/db/sqlalchemy/session.py --- nova-2014.1.100+git201408190132~precise/nova/openstack/common/db/sqlalchemy/session.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/openstack/common/db/sqlalchemy/session.py 2014-10-07 00:02:46.000000000 +0000 @@ -455,6 +455,10 @@ # unique constraint, from error message. _raise_if_duplicate_entry_error(e, self.bind.dialect.name) raise exception.DBError(e) + except exception.DBError: + # note(zzzeek) - if _wrap_db_error is applied to nested functions, + # ensure an existing DBError is propagated outwards + raise except Exception as e: LOG.exception(_LE('DB exception wrapped.')) raise exception.DBError(e) @@ -692,6 +696,10 @@ def execute(self, *args, **kwargs): return super(Session, self).execute(*args, **kwargs) + @_wrap_db_error + def commit(self, *args, **kwargs): + return super(Session, self).commit(*args, **kwargs) + def get_maker(engine, autocommit=True, expire_on_commit=False): """Return a SQLAlchemy sessionmaker using the given engine.""" diff -Nru nova-2014.1.100+git201408190132~precise/nova/openstack/common/processutils.py nova-2014.1.100+git201410062002~precise/nova/openstack/common/processutils.py --- nova-2014.1.100+git201408190132~precise/nova/openstack/common/processutils.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/openstack/common/processutils.py 2014-10-07 00:02:46.000000000 +0000 @@ -18,7 +18,7 @@ """ import errno -import logging as stdlib_logging +import logging import os import random import shlex @@ -29,7 +29,7 @@ import six from nova.openstack.common.gettextutils import _ -from nova.openstack.common import log as logging +from nova.openstack.common import strutils LOG = logging.getLogger(__name__) @@ -111,8 +111,7 @@ execute this command. Defaults to false. :type shell: boolean :param loglevel: log level for execute commands. - :type loglevel: int. (Should be stdlib_logging.DEBUG or - stdlib_logging.INFO) + :type loglevel: int. (Should be logging.DEBUG or logging.INFO) :returns: (stdout, stderr) from process execution :raises: :class:`UnknownArgumentError` on receiving unknown arguments @@ -127,7 +126,7 @@ run_as_root = kwargs.pop('run_as_root', False) root_helper = kwargs.pop('root_helper', '') shell = kwargs.pop('shell', False) - loglevel = kwargs.pop('loglevel', stdlib_logging.DEBUG) + loglevel = kwargs.pop('loglevel', logging.DEBUG) if isinstance(check_exit_code, bool): ignore_exit_code = not check_exit_code @@ -147,11 +146,12 @@ cmd = shlex.split(root_helper) + list(cmd) cmd = map(str, cmd) + sanitized_cmd = strutils.mask_password(' '.join(cmd)) while attempts > 0: attempts -= 1 try: - LOG.log(loglevel, _('Running cmd (subprocess): %s'), ' '.join(cmd)) + LOG.log(loglevel, _('Running cmd (subprocess): %s'), sanitized_cmd) _PIPE = subprocess.PIPE # pylint: disable=E1101 if os.name == 'nt': @@ -187,16 +187,18 @@ LOG.log(loglevel, _('Result was %s') % _returncode) if not ignore_exit_code and _returncode not in check_exit_code: (stdout, stderr) = result + sanitized_stdout = strutils.mask_password(stdout) + sanitized_stderr = strutils.mask_password(stderr) raise ProcessExecutionError(exit_code=_returncode, - stdout=stdout, - stderr=stderr, - cmd=' '.join(cmd)) + stdout=sanitized_stdout, + stderr=sanitized_stderr, + cmd=sanitized_cmd) return result except ProcessExecutionError: if not attempts: raise else: - LOG.log(loglevel, _('%r failed. Retrying.'), cmd) + LOG.log(loglevel, _('%r failed. Retrying.'), sanitized_cmd) if delay_on_retry: greenthread.sleep(random.randint(20, 200) / 100.0) finally: diff -Nru nova-2014.1.100+git201408190132~precise/nova/openstack/common/strutils.py nova-2014.1.100+git201410062002~precise/nova/openstack/common/strutils.py --- nova-2014.1.100+git201408190132~precise/nova/openstack/common/strutils.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/openstack/common/strutils.py 2014-10-07 00:02:46.000000000 +0000 @@ -23,6 +23,8 @@ import sys import unicodedata +import six + from nova.openstack.common.gettextutils import _ @@ -44,6 +46,39 @@ SLUGIFY_HYPHENATE_RE = re.compile(r"[-\s]+") +# NOTE(flaper87): The following globals are used by `mask_password` +_SANITIZE_KEYS = ['adminPass', 'admin_pass', 'password', 'admin_password'] + +# NOTE(ldbragst): Let's build a list of regex objects using the list of +# _SANITIZE_KEYS we already have. This way, we only have to add the new key +# to the list of _SANITIZE_KEYS and we can generate regular expressions +# for XML and JSON automatically. +_SANITIZE_PATTERNS_2 = [] +_SANITIZE_PATTERNS_1 = [] + +# NOTE(amrith): Some regular expressions have only one parameter, some +# have two parameters. Use different lists of patterns here. +_FORMAT_PATTERNS_1 = [r'(%(key)s\s*[=]\s*)[^\s^\'^\"]+'] +_FORMAT_PATTERNS_2 = [r'(%(key)s\s*[=]\s*[\"\']).*?([\"\'])', + r'(%(key)s\s+[\"\']).*?([\"\'])', + r'([-]{2}%(key)s\s+)[^\'^\"^=^\s]+([\s]*)', + r'(<%(key)s>).*?()', + r'([\"\']%(key)s[\"\']\s*:\s*[\"\']).*?([\"\'])', + r'([\'"].*?%(key)s[\'"]\s*:\s*u?[\'"]).*?([\'"])', + r'([\'"].*?%(key)s[\'"]\s*,\s*\'--?[A-z]+\'\s*,\s*u?' + '[\'"]).*?([\'"])', + r'(%(key)s\s*--?[A-z]+\s*)\S+(\s*)'] + +for key in _SANITIZE_KEYS: + for pattern in _FORMAT_PATTERNS_2: + reg_ex = re.compile(pattern % {'key': key}, re.DOTALL) + _SANITIZE_PATTERNS_2.append(reg_ex) + + for pattern in _FORMAT_PATTERNS_1: + reg_ex = re.compile(pattern % {'key': key}, re.DOTALL) + _SANITIZE_PATTERNS_1.append(reg_ex) + + def int_from_bool_as_string(subject): """Interpret a string as a boolean and return either 1 or 0. @@ -214,3 +249,42 @@ "ascii", "ignore").decode("ascii") value = SLUGIFY_STRIP_RE.sub("", value).strip().lower() return SLUGIFY_HYPHENATE_RE.sub("-", value) + + +def mask_password(message, secret="***"): + """Replace password with 'secret' in message. + + :param message: The string which includes security information. + :param secret: value with which to replace passwords. + :returns: The unicode value of message with the password fields masked. + + For example: + + >>> mask_password("'adminPass' : 'aaaaa'") + "'adminPass' : '***'" + >>> mask_password("'admin_pass' : 'aaaaa'") + "'admin_pass' : '***'" + >>> mask_password('"password" : "aaaaa"') + '"password" : "***"' + >>> mask_password("'original_password' : 'aaaaa'") + "'original_password' : '***'" + >>> mask_password("u'original_password' : u'aaaaa'") + "u'original_password' : u'***'" + """ + message = six.text_type(message) + + # NOTE(ldbragst): Check to see if anything in message contains any key + # specified in _SANITIZE_KEYS, if not then just return the message since + # we don't have to mask any passwords. + if not any(key in message for key in _SANITIZE_KEYS): + return message + + substitute = r'\g<1>' + secret + r'\g<2>' + for pattern in _SANITIZE_PATTERNS_2: + message = re.sub(pattern, substitute, message) + + substitute = r'\g<1>' + secret + for pattern in _SANITIZE_PATTERNS_1: + message = re.sub(pattern, substitute, message) + + return message diff -Nru nova-2014.1.100+git201408190132~precise/nova/storage/linuxscsi.py nova-2014.1.100+git201410062002~precise/nova/storage/linuxscsi.py --- nova-2014.1.100+git201408190132~precise/nova/storage/linuxscsi.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/storage/linuxscsi.py 2014-10-07 00:02:46.000000000 +0000 @@ -15,6 +15,7 @@ """Generic linux scsi subsystem utilities.""" from nova.openstack.common.gettextutils import _ +from nova.openstack.common.gettextutils import _LW from nova.openstack.common import log as logging from nova.openstack.common import loopingcall from nova.openstack.common import processutils @@ -97,7 +98,7 @@ (out, err) = utils.execute('multipath', '-l', device, run_as_root=True) except processutils.ProcessExecutionError as exc: - LOG.warn(_("Multipath call failed exit (%(code)s)") + LOG.warn(_LW("Multipath call failed exit (%(code)s)") % {'code': exc.exit_code}) return None @@ -110,15 +111,15 @@ # device line output is different depending # on /etc/multipath.conf settings. if info[1][:2] == "dm": - mdev = "/dev/%s" % info[1] mdev_id = info[0] + mdev = '/dev/mapper/%s' % mdev_id elif info[2][:2] == "dm": - mdev = "/dev/%s" % info[2] mdev_id = info[1].replace('(', '') mdev_id = mdev_id.replace(')', '') + mdev = '/dev/mapper/%s' % mdev_id if mdev is None: - LOG.warn(_("Couldn't find multipath device %s"), line) + LOG.warn(_LW("Couldn't find multipath device %s"), line) return None LOG.debug(_("Found multipath device = %s"), mdev) @@ -126,6 +127,11 @@ for dev_line in device_lines: if dev_line.find("policy") != -1: continue + if '#' in dev_line: + LOG.warn(_LW('Skip faulty line "%(dev_line)s" of' + ' multipath device %(mdev)s') + % {'mdev': mdev, 'dev_line': dev_line}) + continue dev_line = dev_line.lstrip(' |-`') dev_info = dev_line.split() diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/api/openstack/compute/contrib/test_floating_ips.py nova-2014.1.100+git201410062002~precise/nova/tests/api/openstack/compute/contrib/test_floating_ips.py --- nova-2014.1.100+git201408190132~precise/nova/tests/api/openstack/compute/contrib/test_floating_ips.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/api/openstack/compute/contrib/test_floating_ips.py 2014-10-07 00:02:46.000000000 +0000 @@ -14,9 +14,11 @@ # License for the specific language governing permissions and limitations # under the License. +import contextlib import uuid from lxml import etree +import mock import webob from nova.api.openstack.compute.contrib import floating_ips @@ -102,6 +104,38 @@ return None +class FloatingIpTestNeutron(test.NoDBTestCase): + + def setUp(self): + super(FloatingIpTestNeutron, self).setUp() + self.flags(network_api_class='nova.network.neutronv2.api.API') + self.controller = floating_ips.FloatingIPController() + + def test_floatingip_delete(self): + req = fakes.HTTPRequest.blank('/v2/fake/os-floating-ips/1') + fip_val = {'address': '1.1.1.1', 'fixed_ip_id': '192.168.1.2'} + with contextlib.nested( + mock.patch.object(self.controller.network_api, + 'disassociate_floating_ip'), + mock.patch.object(self.controller.network_api, + 'disassociate_and_release_floating_ip'), + mock.patch.object(self.controller.network_api, + 'release_floating_ip'), + mock.patch.object(self.controller.network_api, + 'get_instance_id_by_floating_address', + return_value=None), + mock.patch.object(self.controller.network_api, + 'get_floating_ip', + return_value=fip_val)) as ( + disoc_fip, dis_and_del, rel_fip, _, _): + self.controller.delete(req, 1) + self.assertFalse(disoc_fip.called) + self.assertFalse(rel_fip.called) + # Only disassociate_and_release_floating_ip is + # called if using neutron + self.assertTrue(dis_and_del.called) + + class FloatingIpTest(test.TestCase): floating_ip = "10.10.10.10" floating_ip_2 = "10.10.10.11" @@ -164,6 +198,25 @@ self._delete_floating_ip() super(FloatingIpTest, self).tearDown() + def test_floatingip_delete(self): + req = fakes.HTTPRequest.blank('/v2/fake/os-floating-ips/1') + fip_val = {'address': '1.1.1.1', 'fixed_ip_id': '192.168.1.2'} + with contextlib.nested( + mock.patch.object(self.controller.network_api, + 'disassociate_floating_ip'), + mock.patch.object(self.controller.network_api, + 'release_floating_ip'), + mock.patch.object(self.controller.network_api, + 'get_instance_id_by_floating_address', + return_value=None), + mock.patch.object(self.controller.network_api, + 'get_floating_ip', + return_value=fip_val)) as ( + disoc_fip, rel_fip, _, _): + self.controller.delete(req, 1) + self.assertTrue(disoc_fip.called) + self.assertTrue(rel_fip.called) + def test_translate_floating_ip_view(self): floating_ip_address = self.floating_ip floating_ip = db.floating_ip_get_by_address(self.context, diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/api/openstack/compute/contrib/test_simple_tenant_usage.py nova-2014.1.100+git201410062002~precise/nova/tests/api/openstack/compute/contrib/test_simple_tenant_usage.py --- nova-2014.1.100+git201408190132~precise/nova/tests/api/openstack/compute/contrib/test_simple_tenant_usage.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/api/openstack/compute/contrib/test_simple_tenant_usage.py 2014-10-07 00:02:46.000000000 +0000 @@ -24,6 +24,7 @@ from nova.compute import vm_states from nova import context from nova import db +from nova import exception from nova.objects import flavor as flavor_obj from nova.objects import instance as instance_obj from nova.openstack.common import jsonutils @@ -253,6 +254,39 @@ init_only=('os-simple-tenant-usage',))) self.assertEqual(res.status_int, 400) + def test_get_tenants_usage_with_invalid_start_date(self): + tenant_id = 0 + req = webob.Request.blank( + '/v2/faketenant_0/os-simple-tenant-usage/' + 'faketenant_%s?start=%s&end=%s' % + (tenant_id, "xxxx", NOW.isoformat())) + req.method = "GET" + req.headers["content-type"] = "application/json" + + res = req.get_response(fakes.wsgi_app( + fake_auth_context=self.user_context, + init_only=('os-simple-tenant-usage',))) + self.assertEqual(res.status_int, 400) + + def _test_get_tenants_usage_with_one_date(self, date_url_param): + req = webob.Request.blank( + '/v2/faketenant_0/os-simple-tenant-usage/' + 'faketenant_0?%s' % date_url_param) + req.method = "GET" + req.headers["content-type"] = "application/json" + res = req.get_response(fakes.wsgi_app( + fake_auth_context=self.user_context, + init_only=('os-simple-tenant-usage',))) + self.assertEqual(200, res.status_int) + + def test_get_tenants_usage_with_no_start_date(self): + self._test_get_tenants_usage_with_one_date( + 'end=%s' % (NOW + datetime.timedelta(5)).isoformat()) + + def test_get_tenants_usage_with_no_end_date(self): + self._test_get_tenants_usage_with_one_date( + 'start=%s' % (NOW - datetime.timedelta(5)).isoformat()) + class SimpleTenantUsageSerializerTest(test.TestCase): def _verify_server_usage(self, raw_usage, tree): @@ -471,3 +505,18 @@ self.inst_obj.instance_type_id = 99 flavor = self.controller._get_flavor(self.context, self.inst_obj, {}) self.assertIsNone(flavor) + + +class SimpleTenantUsageUtils(test.NoDBTestCase): + def test_valid_string(self): + dt = simple_tenant_usage.parse_strtime("2014-02-21T13:47:20.824060", + "%Y-%m-%dT%H:%M:%S.%f") + self.assertEqual(datetime.datetime( + microsecond=824060, second=20, minute=47, hour=13, + day=21, month=2, year=2014), dt) + + def test_invalid_string(self): + self.assertRaises(exception.InvalidStrTime, + simple_tenant_usage.parse_strtime, + "2014-02-21 13:47:20.824060", + "%Y-%m-%dT%H:%M:%S.%f") diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/api/openstack/test_common.py nova-2014.1.100+git201410062002~precise/nova/tests/api/openstack/test_common.py --- nova-2014.1.100+git201408190132~precise/nova/tests/api/openstack/test_common.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/api/openstack/test_common.py 2014-10-07 00:02:46.000000000 +0000 @@ -18,6 +18,8 @@ """ from lxml import etree +import mock +from testtools import matchers import webob import webob.exc import xml.dom.minidom as minidom @@ -375,6 +377,82 @@ self.assertEqual(expected, actual) +class TestCollectionLinks(test.NoDBTestCase): + """Tests the _get_collection_links method.""" + + @mock.patch('nova.api.openstack.common.ViewBuilder._get_next_link') + def test_items_less_than_limit(self, href_link_mock): + items = [ + {"uuid": "123"} + ] + req = mock.MagicMock() + params = mock.PropertyMock(return_value=dict(limit=10)) + type(req).params = params + + builder = common.ViewBuilder() + results = builder._get_collection_links(req, items, "ignored", "uuid") + + href_link_mock.assert_not_called() + self.assertThat(results, matchers.HasLength(0)) + + @mock.patch('nova.api.openstack.common.ViewBuilder._get_next_link') + def test_items_equals_given_limit(self, href_link_mock): + items = [ + {"uuid": "123"} + ] + req = mock.MagicMock() + params = mock.PropertyMock(return_value=dict(limit=1)) + type(req).params = params + + builder = common.ViewBuilder() + results = builder._get_collection_links(req, items, + mock.sentinel.coll_key, + "uuid") + + href_link_mock.assert_called_once_with(req, "123", + mock.sentinel.coll_key) + self.assertThat(results, matchers.HasLength(1)) + + @mock.patch('nova.api.openstack.common.ViewBuilder._get_next_link') + def test_items_equals_default_limit(self, href_link_mock): + items = [ + {"uuid": "123"} + ] + req = mock.MagicMock() + params = mock.PropertyMock(return_value=dict()) + type(req).params = params + self.flags(osapi_max_limit=1) + + builder = common.ViewBuilder() + results = builder._get_collection_links(req, items, + mock.sentinel.coll_key, + "uuid") + + href_link_mock.assert_called_once_with(req, "123", + mock.sentinel.coll_key) + self.assertThat(results, matchers.HasLength(1)) + + @mock.patch('nova.api.openstack.common.ViewBuilder._get_next_link') + def test_items_equals_default_limit_with_given(self, href_link_mock): + items = [ + {"uuid": "123"} + ] + req = mock.MagicMock() + # Given limit is greater then default max, only return default max + params = mock.PropertyMock(return_value=dict(limit=2)) + type(req).params = params + self.flags(osapi_max_limit=1) + + builder = common.ViewBuilder() + results = builder._get_collection_links(req, items, + mock.sentinel.coll_key, + "uuid") + + href_link_mock.assert_called_once_with(req, "123", + mock.sentinel.coll_key) + self.assertThat(results, matchers.HasLength(1)) + + class MetadataXMLDeserializationTest(test.TestCase): deserializer = common.MetadataXMLDeserializer() diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/cells/test_cells_messaging.py nova-2014.1.100+git201410062002~precise/nova/tests/cells/test_cells_messaging.py --- nova-2014.1.100+git201408190132~precise/nova/tests/cells/test_cells_messaging.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/cells/test_cells_messaging.py 2014-10-07 00:02:46.000000000 +0000 @@ -17,6 +17,9 @@ Tests For Cells Messaging module """ +import contextlib + +import mock import mox from oslo.config import cfg from oslo import messaging as oslo_messaging @@ -1131,6 +1134,31 @@ extra_properties='props') self.assertEqual('foo', result) + def test_call_compute_api_with_obj_no_cache(self): + instance = instance_obj.Instance() + instance.uuid = uuidutils.generate_uuid() + error = exception.InstanceInfoCacheNotFound( + instance_uuid=instance.uuid) + with mock.patch.object(instance, 'refresh', side_effect=error): + self.assertRaises(exception.InstanceInfoCacheNotFound, + self.tgt_methods_cls._call_compute_api_with_obj, + self.ctxt, instance, 'snapshot') + + def test_call_delete_compute_api_with_obj_no_cache(self): + instance = instance_obj.Instance() + instance.uuid = uuidutils.generate_uuid() + error = exception.InstanceInfoCacheNotFound( + instance_uuid=instance.uuid) + with contextlib.nested( + mock.patch.object(instance, 'refresh', + side_effect=error), + mock.patch.object(self.tgt_compute_api, 'delete')) as (inst, + delete): + self.tgt_methods_cls._call_compute_api_with_obj(self.ctxt, + instance, + 'delete') + delete.assert_called_once_with(self.ctxt, instance) + def test_call_compute_with_obj_unknown_instance(self): instance = instance_obj.Instance() instance.uuid = uuidutils.generate_uuid() diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/compute/test_compute_api.py nova-2014.1.100+git201410062002~precise/nova/tests/compute/test_compute_api.py --- nova-2014.1.100+git201408190132~precise/nova/tests/compute/test_compute_api.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/compute/test_compute_api.py 2014-10-07 00:02:46.000000000 +0000 @@ -67,6 +67,16 @@ self.context = context.RequestContext(self.user_id, self.project_id) + def _get_vm_states(self, exclude_states=None): + vm_state = set([vm_states.ACTIVE, vm_states.BUILDING, vm_states.PAUSED, + vm_states.SUSPENDED, vm_states.RESCUED, vm_states.STOPPED, + vm_states.RESIZED, vm_states.SOFT_DELETED, + vm_states.DELETED, vm_states.ERROR, vm_states.SHELVED, + vm_states.SHELVED_OFFLOADED]) + if not exclude_states: + exclude_states = set() + return vm_state - exclude_states + def _create_flavor(self, params=None): flavor = {'id': 1, 'flavorid': 1, @@ -204,6 +214,19 @@ self.assertEqual(task_states.SUSPENDING, instance.task_state) + def _test_suspend_fails(self, vm_state): + params = dict(vm_state=vm_state) + instance = self._create_instance_obj(params=params) + self.assertIsNone(instance.task_state) + self.assertRaises(exception.InstanceInvalidState, + self.compute_api.suspend, + self.context, instance) + + def test_suspend_fails_invalid_states(self): + invalid_vm_states = self._get_vm_states(set([vm_states.ACTIVE])) + for state in invalid_vm_states: + self._test_suspend_fails(state) + def test_resume(self): # Ensure instance can be resumed (if suspended). instance = self._create_instance_obj( @@ -309,13 +332,19 @@ def test_stop_stopped_instance_with_bypass(self): self._test_stop(vm_states.STOPPED, force=True) - def test_stop_invalid_state(self): - params = dict(vm_state=vm_states.PAUSED) + def _test_stop_invalid_state(self, vm_state): + params = dict(vm_state=vm_state) instance = self._create_instance_obj(params=params) self.assertRaises(exception.InstanceInvalidState, self.compute_api.stop, self.context, instance) + def test_stop_fails_invalid_states(self): + invalid_vm_states = self._get_vm_states(set([vm_states.ACTIVE, + vm_states.ERROR])) + for state in invalid_vm_states: + self._test_stop_invalid_state(state) + def test_stop_a_stopped_inst(self): params = {'vm_state': vm_states.STOPPED} instance = self._create_instance_obj(params=params) @@ -1203,6 +1232,19 @@ self.assertEqual(task_states.PAUSING, instance.task_state) + def _test_pause_fails(self, vm_state): + params = dict(vm_state=vm_state) + instance = self._create_instance_obj(params=params) + self.assertIsNone(instance.task_state) + self.assertRaises(exception.InstanceInvalidState, + self.compute_api.pause, + self.context, instance) + + def test_pause_fails_invalid_states(self): + invalid_vm_states = self._get_vm_states(set([vm_states.ACTIVE])) + for state in invalid_vm_states: + self._test_pause_fails(state) + def test_unpause(self): # Ensure instance can be unpaused. params = dict(vm_state=vm_states.PAUSED) diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/compute/test_compute_mgr.py nova-2014.1.100+git201410062002~precise/nova/tests/compute/test_compute_mgr.py --- nova-2014.1.100+git201408190132~precise/nova/tests/compute/test_compute_mgr.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/compute/test_compute_mgr.py 2014-10-07 00:02:46.000000000 +0000 @@ -287,7 +287,7 @@ self.mox.StubOutWithMock(self.compute.driver, 'resume_state_on_host_boot') self.mox.StubOutWithMock(self.compute, - '_get_instance_volume_block_device_info') + '_get_instance_block_device_info') self.mox.StubOutWithMock(self.compute, '_set_instance_error_state') self.compute._get_power_state(mox.IgnoreArg(), @@ -297,7 +297,7 @@ self.compute._get_power_state(mox.IgnoreArg(), instance).AndReturn(power_state.SHUTDOWN) self.compute.driver.plug_vifs(instance, mox.IgnoreArg()) - self.compute._get_instance_volume_block_device_info(mox.IgnoreArg(), + self.compute._get_instance_block_device_info(mox.IgnoreArg(), instance).AndReturn('fake-bdm') self.compute.driver.resume_state_on_host_boot(mox.IgnoreArg(), instance, mox.IgnoreArg(), @@ -351,7 +351,7 @@ self.mox.StubOutWithMock(self.compute.driver, 'finish_revert_migration') self.mox.StubOutWithMock(self.compute, - '_get_instance_volume_block_device_info') + '_get_instance_block_device_info') self.mox.StubOutWithMock(self.compute.driver, 'get_info') self.mox.StubOutWithMock(instance, 'save') self.mox.StubOutWithMock(self.compute, '_retry_reboot') @@ -361,7 +361,7 @@ compute_utils.get_nw_info_for_instance(instance).AndReturn( network_model.NetworkInfo()) self.compute.driver.plug_vifs(instance, []) - self.compute._get_instance_volume_block_device_info( + self.compute._get_instance_block_device_info( self.context, instance).AndReturn([]) self.compute.driver.finish_revert_migration(self.context, instance, [], [], power_on) @@ -1161,13 +1161,13 @@ mock.patch.object(self.compute, '_get_instance_nw_info', return_value=None), mock.patch.object(self.compute, - '_get_instance_volume_block_device_info', + '_get_instance_block_device_info', return_value={}), mock.patch.object(self.compute, '_is_instance_storage_shared', return_value=False), mock.patch.object(self.compute.driver, 'destroy') ) as (_get_instances_on_driver, _get_instance_nw_info, - _get_instance_volume_block_device_info, + _get_instance_block_device_info, _is_instance_storage_shared, destroy): self.compute._destroy_evacuated_instances(self.context) destroy.assert_called_once_with(self.context, instance_2, None, @@ -1899,7 +1899,7 @@ mock.patch.object(self.instance, 'save'), mock.patch.object(self.compute, '_notify_about_instance_usage'), mock.patch.object(self.compute, - '_get_instance_volume_block_device_info', + '_get_instance_block_device_info', return_value=None), mock.patch.object(block_device_obj.BlockDeviceMappingList, 'get_by_instance_uuid', diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/compute/test_compute.py nova-2014.1.100+git201410062002~precise/nova/tests/compute/test_compute.py --- nova-2014.1.100+git201408190132~precise/nova/tests/compute/test_compute.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/compute/test_compute.py 2014-10-07 00:02:46.000000000 +0000 @@ -1031,6 +1031,27 @@ self.compute.volume_snapshot_delete, self.context, self.instance_object, 'fake_id', 'fake_id2', {}) + @mock.patch.object(cinder.API, 'create', + side_effect=exception.OverQuota(overs='volumes')) + def test_prep_block_device_over_quota_failure(self, mock_create): + instance = self._create_fake_instance() + bdms = [ + block_device.BlockDeviceDict({ + 'boot_index': 0, + 'guest_format': None, + 'connection_info': None, + 'device_type': u'disk', + 'source_type': 'image', + 'destination_type': 'volume', + 'volume_size': 1, + 'image_id': 1, + 'device_name': '/dev/vdb', + })] + self.assertRaises(exception.InvalidBDM, + compute_manager.ComputeManager()._prep_block_device, + self.context, instance, bdms) + mock_create.assert_called_once() + class ComputeTestCase(BaseTestCase): def test_wrap_instance_fault(self): @@ -1509,6 +1530,30 @@ self._assert_state({'vm_state': vm_states.ERROR, 'task_state': None}) + @mock.patch('nova.compute.manager.ComputeManager._prep_block_device', + side_effect=exception.OverQuota(overs='volumes')) + def test_setup_block_device_over_quota_fail(self, mock_prep_block_dev): + """block device mapping over quota failure test. + + Make sure when we're over volume quota according to Cinder client, the + appropriate exception is raised and the instances to ERROR state, keep + the task state. + """ + instance = self._create_fake_instance() + self.assertRaises(exception.OverQuota, self.compute.run_instance, + self.context, instance=instance, request_spec={}, + filter_properties={}, requested_networks=[], + injected_files=None, admin_password=None, + is_first_time=True, node=None, + legacy_bdm_in_spec=False) + #check state is failed even after the periodic poll + self._assert_state({'vm_state': vm_states.ERROR, + 'task_state': None}) + self.compute.periodic_tasks(context.get_admin_context()) + self._assert_state({'vm_state': vm_states.ERROR, + 'task_state': None}) + mock_prep_block_dev.assert_called_once() + def test_run_instance_spawn_fail(self): """spawn failure test. @@ -2268,7 +2313,7 @@ fake_network.unset_stub_network_methods(self.stubs) self.mox.StubOutWithMock(self.compute, - '_get_instance_volume_block_device_info') + '_get_instance_block_device_info') self.mox.StubOutWithMock(self.compute, '_get_instance_nw_info') self.mox.StubOutWithMock(self.compute, '_notify_about_instance_usage') self.mox.StubOutWithMock(self.compute, '_instance_update') @@ -2320,7 +2365,7 @@ self.mox.StubOutWithMock(self.context, 'elevated') self.context.elevated().AndReturn(econtext) - self.compute._get_instance_volume_block_device_info( + self.compute._get_instance_block_device_info( econtext, instance).AndReturn(fake_block_dev_info) self.compute._get_instance_nw_info(econtext, instance).AndReturn( @@ -2467,7 +2512,7 @@ self._test_reboot(False, fail_reboot=True, fail_running=True) - def test_get_instance_volume_block_device_info_source_image(self): + def test_get_instance_block_device_info_source_image(self): bdms = block_device_obj.block_device_make_list(self.context, [fake_block_device.FakeDbBlockDeviceDict({ 'id': 3, @@ -2487,10 +2532,12 @@ return_value=bdms) ) as mock_get_by_instance: block_device_info = ( - self.compute._get_instance_volume_block_device_info( + self.compute._get_instance_block_device_info( self.context, self._create_fake_instance()) ) expected = { + 'swap': None, + 'ephemerals': [], 'block_device_mapping': [{ 'connection_info': { 'driver_volume_type': 'rbd' @@ -2502,7 +2549,7 @@ self.assertTrue(mock_get_by_instance.called) self.assertEqual(block_device_info, expected) - def test_get_instance_volume_block_device_info_passed_bdms(self): + def test_get_instance_block_device_info_passed_bdms(self): bdms = block_device_obj.block_device_make_list(self.context, [fake_block_device.FakeDbBlockDeviceDict({ 'id': 3, @@ -2516,10 +2563,12 @@ block_device_obj.BlockDeviceMappingList, 'get_by_instance_uuid')) as mock_get_by_instance: block_device_info = ( - self.compute._get_instance_volume_block_device_info( + self.compute._get_instance_block_device_info( self.context, self._create_fake_instance(), bdms=bdms) ) expected = { + 'swap': None, + 'ephemerals': [], 'block_device_mapping': [{ 'connection_info': { 'driver_volume_type': 'rbd' @@ -2531,6 +2580,57 @@ self.assertFalse(mock_get_by_instance.called) self.assertEqual(block_device_info, expected) + def test_get_instance_block_device_info_swap_and_ephemeral(self): + instance = self._create_fake_instance() + + ephemerals = fake_block_device.FakeDbBlockDeviceDict({ + 'id': 1, 'instance_uuid': 'fake-instance', + 'device_name': '/dev/vdb', + 'source_type': 'blank', + 'destination_type': 'local', + 'device_type': 'disk', + 'disk_bus': 'virtio', + 'delete_on_termination': True, + 'guest_format': None, + 'volume_size': 1, + 'boot_index': -1 + }) + swap = fake_block_device.FakeDbBlockDeviceDict({ + 'id': 2, 'instance_uuid': 'fake-instance', + 'device_name': '/dev/vdc', + 'source_type': 'blank', + 'destination_type': 'local', + 'device_type': 'disk', + 'disk_bus': 'virtio', + 'delete_on_termination': True, + 'guest_format': 'swap', + 'volume_size': 1, + 'boot_index': -1 + }) + + bdms = block_device_obj.block_device_make_list(self.context, + [swap, ephemerals]) + + with ( + mock.patch.object(block_device_obj.BlockDeviceMappingList, + 'get_by_instance_uuid', return_value=bdms) + ) as mock_get_by_instance_uuid: + expected_block_device_info = { + 'swap': {'device_name': '/dev/vdc', 'swap_size': 1}, + 'ephemerals': [{'device_name': '/dev/vdb', 'num': 0, 'size': 1, + 'virtual_name': 'ephemeral0'}], + 'block_device_mapping': [] + } + + block_device_info = ( + self.compute._get_instance_block_device_info( + self.context, instance) + ) + + mock_get_by_instance_uuid.assert_called_once_with(self.context, + instance.uuid) + self.assertEqual(expected_block_device_info, block_device_info) + def test_set_admin_password(self): # Ensure instance can have its admin password set. instance = jsonutils.to_primitive(self._create_fake_instance()) @@ -2710,7 +2810,8 @@ self.compute.snapshot_instance(self.context, image_id='fakesnap', instance=inst_obj) - def _test_snapshot_fails(self, raise_during_cleanup): + def _test_snapshot_fails(self, raise_during_cleanup, method, + expected_state=True): def fake_snapshot(*args, **kwargs): raise test.TestingException() @@ -2726,18 +2827,41 @@ self.stubs.Set(fake_image._FakeImageService, 'delete', fake_delete) inst_obj = self._get_snapshotting_instance() - self.assertRaises(test.TestingException, - self.compute.snapshot_instance, - self.context, image_id='fakesnap', - instance=inst_obj) - self.assertTrue(self.fake_image_delete_called) + if method == 'snapshot': + self.assertRaises(test.TestingException, + self.compute.snapshot_instance, + self.context, image_id='fakesnap', + instance=inst_obj) + else: + self.assertRaises(test.TestingException, + self.compute.backup_instance, + self.context, image_id='fakesnap', + instance=inst_obj, backup_type='fake', + rotation=1) + + self.assertEqual(expected_state, self.fake_image_delete_called) self._assert_state({'task_state': None}) + @mock.patch.object(nova.compute.manager.ComputeManager, '_rotate_backups') + def test_backup_fails(self, mock_rotate): + self._test_snapshot_fails(False, 'backup') + + @mock.patch.object(nova.compute.manager.ComputeManager, '_rotate_backups') + def test_backup_fails_cleanup_ignores_exception(self, mock_rotate): + self._test_snapshot_fails(True, 'backup') + + @mock.patch.object(nova.compute.manager.ComputeManager, '_rotate_backups') + @mock.patch.object(nova.compute.manager.ComputeManager, + '_do_snapshot_instance') + def test_backup_fails_rotate_backup(self, mock_snap, mock_rotate): + mock_rotate.side_effect = test.TestingException() + self._test_snapshot_fails(True, 'backup', False) + def test_snapshot_fails(self): - self._test_snapshot_fails(False) + self._test_snapshot_fails(False, 'snapshot') def test_snapshot_fails_cleanup_ignores_exception(self): - self._test_snapshot_fails(True) + self._test_snapshot_fails(True, 'snapshot') def test_snapshot_fails_with_glance_error(self): def fake_snapshot(*args, **kwargs): @@ -3968,7 +4092,7 @@ '_notify_about_instance_usage') self.mox.StubOutWithMock(self.compute.driver, 'finish_migration') self.mox.StubOutWithMock(self.compute, - '_get_instance_volume_block_device_info') + '_get_instance_block_device_info') self.mox.StubOutWithMock(migration, 'save') self.mox.StubOutWithMock(instance, 'save') self.mox.StubOutWithMock(self.context, 'elevated') @@ -4018,7 +4142,7 @@ self.context, instance, 'finish_resize.start', network_info='fake-nwinfo1') - self.compute._get_instance_volume_block_device_info( + self.compute._get_instance_block_device_info( self.context, instance, refresh_conn_info=True).AndReturn('fake-bdminfo') # nova.conf sets the default flavor to m1.small and the test @@ -4599,7 +4723,7 @@ mock.patch.object(block_device_obj.BlockDeviceMappingList, 'get_by_instance_uuid', return_value='fake_bdms'), mock.patch.object( - self.compute, '_get_instance_volume_block_device_info', + self.compute, '_get_instance_block_device_info', return_value='fake_bdinfo'), mock.patch.object(self.compute, '_terminate_volume_connections') ) as (mock_get_by_inst_uuid, mock_get_instance_vol_bdinfo, @@ -4976,7 +5100,8 @@ # creating mocks self.mox.StubOutWithMock(self.compute.driver, 'pre_live_migration') self.compute.driver.pre_live_migration(mox.IsA(c), mox.IsA(instance), - {'block_device_mapping': []}, + {'swap': None, 'ephemerals': [], + 'block_device_mapping': []}, mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg()) @@ -5218,7 +5343,8 @@ self.compute._post_live_migration(c, instance, dest) post_live_migration.assert_has_calls([ - mock.call(c, instance, {'block_device_mapping': []}, None)]) + mock.call(c, instance, {'swap': None, 'ephemerals': [], + 'block_device_mapping': []}, None)]) unfilter_instance.assert_has_calls([mock.call(instance, [])]) migration = {'source_compute': srchost, 'dest_compute': dest, } @@ -5260,7 +5386,7 @@ mock.patch.object(self.compute.instance_events, 'clear_events_for_instance'), mock.patch.object(self.compute, - '_get_instance_volume_block_device_info'), + '_get_instance_block_device_info'), mock.patch.object(block_device_obj.BlockDeviceMappingList, 'get_by_instance_uuid'), mock.patch.object(self.compute.driver, 'get_volume_connector'), @@ -5375,7 +5501,8 @@ self.mox.StubOutWithMock(self.compute.driver, 'rollback_live_migration_at_destination') self.compute.driver.rollback_live_migration_at_destination(c, - instance, [], {'block_device_mapping': []}) + instance, [], {'swap': None, 'ephemerals': [], + 'block_device_mapping': []}) # start test self.mox.ReplayAll() @@ -6076,7 +6203,7 @@ self.mox.StubOutWithMock(self.compute, '_get_instance_nw_info') self.mox.StubOutWithMock(self.compute, - '_get_instance_volume_block_device_info') + '_get_instance_block_device_info') self.mox.StubOutWithMock(self.compute, '_is_instance_storage_shared') self.mox.StubOutWithMock(self.compute.driver, 'destroy') @@ -6086,7 +6213,7 @@ self.compute._get_instance_nw_info(fake_context, evacuated_instance).AndReturn( 'fake_network_info') - self.compute._get_instance_volume_block_device_info( + self.compute._get_instance_block_device_info( fake_context, evacuated_instance).AndReturn('fake_bdi') self.compute._is_instance_storage_shared(fake_context, evacuated_instance).AndReturn(True) @@ -6123,7 +6250,7 @@ self.mox.StubOutWithMock(self.compute, '_get_instance_nw_info') self.mox.StubOutWithMock(self.compute, - '_get_instance_volume_block_device_info') + '_get_instance_block_device_info') self.mox.StubOutWithMock(self.compute.driver, 'check_instance_shared_storage_local') self.mox.StubOutWithMock(self.compute.compute_rpcapi, @@ -6137,7 +6264,7 @@ self.compute._get_instance_nw_info(fake_context, evacuated_instance).AndReturn( 'fake_network_info') - self.compute._get_instance_volume_block_device_info( + self.compute._get_instance_block_device_info( fake_context, evacuated_instance).AndReturn('fake_bdi') self.compute.driver.check_instance_shared_storage_local(fake_context, evacuated_instance).AndReturn({'filename': 'tmpfilename'}) @@ -6179,7 +6306,7 @@ self.mox.StubOutWithMock(self.compute, '_get_instance_nw_info') self.mox.StubOutWithMock(self.compute, - '_get_instance_volume_block_device_info') + '_get_instance_block_device_info') self.mox.StubOutWithMock(self.compute.driver, 'check_instance_shared_storage_local') self.mox.StubOutWithMock(self.compute.compute_rpcapi, @@ -6193,7 +6320,7 @@ self.compute._get_instance_nw_info(fake_context, evacuated_instance).AndReturn( 'fake_network_info') - self.compute._get_instance_volume_block_device_info( + self.compute._get_instance_block_device_info( fake_context, evacuated_instance).AndReturn('fake_bdi') self.compute.driver.check_instance_shared_storage_local(fake_context, evacuated_instance).AndRaise(NotImplementedError()) @@ -10071,13 +10198,13 @@ mock.patch.object(self.compute, '_get_instance_nw_info', side_effect=error), mock.patch.object(self.compute, - '_get_instance_volume_block_device_info'), + '_get_instance_block_device_info'), mock.patch.object(self.compute.driver, 'destroy'), mock.patch.object(self.compute, '_try_deallocate_network') ) as ( elevated_mock, _get_instance_nw_info_mock, - _get_instance_volume_block_device_info_mock, + _get_instance_block_device_info_mock, destroy_mock, _try_deallocate_network_mock ): diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/db/test_db_api.py nova-2014.1.100+git201410062002~precise/nova/tests/db/test_db_api.py --- nova-2014.1.100+git201408190132~precise/nova/tests/db/test_db_api.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/db/test_db_api.py 2014-10-07 00:02:46.000000000 +0000 @@ -4075,7 +4075,9 @@ 'interface': 'some_interface', 'pool': 'some_pool' } - db.floating_ip_update(self.ctxt, float_ip['address'], values) + floating_ref = db.floating_ip_update(self.ctxt, float_ip['address'], + values) + self.assertIsNot(floating_ref, None) updated_float_ip = db.floating_ip_get(self.ctxt, float_ip['id']) self._assertEqualObjects(updated_float_ip, values, ignored_keys=['id', 'address', 'updated_at', diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/image/test_glance.py nova-2014.1.100+git201410062002~precise/nova/tests/image/test_glance.py --- nova-2014.1.100+git201408190132~precise/nova/tests/image/test_glance.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/image/test_glance.py 2014-10-07 00:02:46.000000000 +0000 @@ -572,6 +572,48 @@ self.assertEqual(same_id, image_id) self.assertEqual(service._client.host, 'something-less-likely') + def test_extracting_missing_attributes(self): + """Verify behavior from glance objects that are missing attributes + + This fakes the image class and is missing attribute as the client can + return if they're not set in the database. + """ + class MyFakeGlanceImage(glance_stubs.FakeImage): + def __init__(self, metadata): + IMAGE_ATTRIBUTES = ['size', 'owner', 'id', 'created_at', + 'updated_at', 'status', 'min_disk', + 'min_ram', 'is_public'] + raw = dict.fromkeys(IMAGE_ATTRIBUTES) + raw.update(metadata) + self.__dict__['raw'] = raw + + metadata = { + 'id': 1, + 'created_at': self.NOW_DATETIME, + 'updated_at': self.NOW_DATETIME, + } + image = MyFakeGlanceImage(metadata) + observed = glance._extract_attributes(image) + expected = { + 'id': 1, + 'name': None, + 'is_public': None, + 'size': None, + 'min_disk': None, + 'min_ram': None, + 'disk_format': None, + 'container_format': None, + 'checksum': None, + 'created_at': self.NOW_DATETIME, + 'updated_at': self.NOW_DATETIME, + 'deleted_at': None, + 'deleted': None, + 'status': None, + 'properties': {}, + 'owner': None, + } + self.assertEqual(expected, observed) + def _create_failing_glance_client(info): class MyGlanceStubClient(glance_stubs.StubGlanceClient): @@ -766,6 +808,40 @@ trans_from_mock.assert_not_called() reraise_mock.assert_called_once_with(mock.sentinel.image_id) + @mock.patch('nova.image.glance._is_image_available') + def test_show_queued_image_without_some_attrs(self, is_avail_mock): + is_avail_mock.return_value = True + client = mock.MagicMock() + + # fake image cls without disk_format, container_format, name attributes + class fake_image_cls(object): + id = 'b31aa5dd-f07a-4748-8f15-398346887584' + deleted = False + protected = False + min_disk = 0 + created_at = '2014-05-20T08:16:48' + size = 0 + status = 'queued' + is_public = False + min_ram = 0 + owner = '980ec4870033453ead65c0470a78b8a8' + updated_at = '2014-05-20T08:16:48' + glance_image = fake_image_cls() + client.call.return_value = glance_image + ctx = mock.sentinel.ctx + service = glance.GlanceImageService(client) + image_info = service.show(ctx, glance_image.id) + client.call.assert_called_once_with(ctx, 1, 'get', + glance_image.id) + NOVA_IMAGE_ATTRIBUTES = set(['size', 'disk_format', 'owner', + 'container_format', 'status', 'id', + 'name', 'created_at', 'updated_at', + 'deleted', 'deleted_at', 'checksum', + 'min_disk', 'min_ram', 'is_public', + 'properties']) + + self.assertEqual(NOVA_IMAGE_ATTRIBUTES, set(image_info.keys())) + class TestDetail(test.NoDBTestCase): diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/integrated/test_multiprocess_api.py nova-2014.1.100+git201410062002~precise/nova/tests/integrated/test_multiprocess_api.py --- nova-2014.1.100+git201408190132~precise/nova/tests/integrated/test_multiprocess_api.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/integrated/test_multiprocess_api.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,213 +0,0 @@ -# Copyright (c) 2012 Intel, LLC -# Copyright (c) 2012 OpenStack Foundation -# -# 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. - -""" -Test multiprocess enabled API service. -""" -import errno -import fixtures -import os -import signal -import time -import traceback - -from nova.openstack.common import log as logging -from nova import service -from nova.tests.integrated.api import client -from nova.tests.integrated import integrated_helpers - -LOG = logging.getLogger(__name__) - - -class MultiprocessWSGITest(integrated_helpers._IntegratedTestBase): - _api_version = 'v2' - - def _start_api_service(self): - # Process will be started in _spawn() - self.osapi = service.WSGIService("osapi_compute") - self.auth_url = 'http://%(host)s:%(port)s/%(api_version)s' % ({ - 'host': self.osapi.host, 'port': self.osapi.port, - 'api_version': self._api_version}) - LOG.info('auth_url = %s' % self.auth_url) - - def _get_flags(self): - self.workers = 2 - f = super(MultiprocessWSGITest, self)._get_flags() - f['osapi_compute_workers'] = self.workers - return f - - def _spawn(self): - pid = os.fork() - if pid == 0: - # NOTE(johannes): We can't let the child processes exit back - # into the unit test framework since then we'll have multiple - # processes running the same tests (and possibly forking more - # processes that end up in the same situation). So we need - # to catch all exceptions and make sure nothing leaks out, in - # particlar SystemExit, which is raised by sys.exit(). We use - # os._exit() which doesn't have this problem. - status = 0 - try: - launcher = service.process_launcher() - launcher.launch_service(self.osapi, workers=self.osapi.workers) - launcher.wait() - except SystemExit as exc: - status = exc.code - except BaseException: - # We need to be defensive here too - try: - traceback.print_exc() - except BaseException: - LOG.error("Couldn't print traceback") - status = 2 - - # Really exit - os._exit(status) - - self.pid = pid - - # Wait at most 10 seconds to spawn workers - cond = lambda: self.workers == len(self._get_workers()) - timeout = 10 - self._wait(cond, timeout) - - workers = self._get_workers() - self.assertEqual(len(workers), self.workers) - return workers - - def _wait(self, cond, timeout): - start = time.time() - while True: - if cond(): - break - if time.time() - start > timeout: - break - time.sleep(.1) - - def tearDown(self): - if self.pid: - # Make sure all processes are stopped - os.kill(self.pid, signal.SIGTERM) - - try: - # Make sure we reap our test process - self._reap_test() - except fixtures.TimeoutException: - # If the child gets stuck or is too slow in existing - # after receiving the SIGTERM, gracefully handle the - # timeout exception and try harder to kill it. We need - # to do this otherwise the child process can hold up - # the test run - os.kill(self.pid, signal.SIGKILL) - - super(MultiprocessWSGITest, self).tearDown() - - def _reap_test(self): - pid, status = os.waitpid(self.pid, 0) - self.pid = None - return status - - def _get_workers(self): - # NOTE(hartsocks): use of ps checks the process table for child pid - # entries these processes may be ended but not reaped so ps may - # show processes that are still being cleaned out of the table. - f = os.popen('ps ax -o pid,ppid,command') - # Skip ps header - f.readline() - - processes = [tuple(int(p) for p in l.strip().split()[:2]) - for l in f.readlines()] - return [p for p, pp in processes if pp == self.pid] - - def wait_on_process_until_end(self, worker_pid): - # NOTE: the testing framework itself has a - # built in test timeout function so a test - # stuck in an infinite loop will eventually - # be killed by the test framework. - LOG.info('waiting on process %r to exit' % worker_pid) - - while True: - # poll the process until it isn't there to poll - try: - os.kill(worker_pid, 0) - time.sleep(0.1) - except OSError as err: - # by watching specifically for errno.ESRCH - # we guarantee this loop continues until - # the process table has cleared the pid. - # Child process table entries hang around - # for several cycles in case a parent process - # needs to check their exit state. - if err.errno == errno.ESRCH: - break - LOG.info('process %r has exited' % worker_pid) - - def test_killed_worker_recover(self): - start_workers = self._spawn() - - worker_pid = start_workers[0] - # kill one worker and check if new worker can come up - LOG.info('pid of first child is %s' % worker_pid) - - # signal child - os.kill(worker_pid, signal.SIGTERM) - - self.wait_on_process_until_end(worker_pid) - - # Make sure worker pids don't match - end_workers = self._get_workers() - LOG.info('workers: %r' % end_workers) - self.assertNotEqual(start_workers, end_workers) - - # check if api service still works - flavors = self.api.get_flavors() - self.assertTrue(len(flavors) > 0, 'Num of flavors > 0.') - - def _terminate_with_signal(self, sig): - self._spawn() - - # check if api service is working - flavors = self.api.get_flavors() - self.assertTrue(len(flavors) > 0, 'Num of flavors > 0.') - - worker_pids = self._get_workers() - - LOG.info("sent launcher_process pid: %r signal: %r" % (self.pid, sig)) - os.kill(self.pid, sig) - - # did you know the test framework has a timeout of its own? - # if a test takes too long, the test will be killed. - for pid in worker_pids: - self.wait_on_process_until_end(pid) - - workers = self._get_workers() - self.assertFalse(workers, 'OS processes left %r' % workers) - - def test_terminate_sigkill(self): - self._terminate_with_signal(signal.SIGKILL) - status = self._reap_test() - self.assertTrue(os.WIFSIGNALED(status)) - self.assertEqual(os.WTERMSIG(status), signal.SIGKILL) - - def test_terminate_sigterm(self): - self._terminate_with_signal(signal.SIGTERM) - status = self._reap_test() - self.assertTrue(os.WIFEXITED(status)) - self.assertEqual(os.WEXITSTATUS(status), 0) - - -class MultiprocessWSGITestV3(client.TestOpenStackClientV3Mixin, - MultiprocessWSGITest): - _api_version = 'v3' diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/network/test_api.py nova-2014.1.100+git201410062002~precise/nova/tests/network/test_api.py --- nova-2014.1.100+git201408190132~precise/nova/tests/network/test_api.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/network/test_api.py 2014-10-07 00:02:46.000000000 +0000 @@ -70,6 +70,30 @@ self.context = context.RequestContext('fake-user', 'fake-project') + @mock.patch('nova.db.network_get_all') + def test_get_all(self, mock_get_all): + mock_get_all.return_value = mock.sentinel.get_all + self.assertEqual(mock.sentinel.get_all, + self.network_api.get_all(self.context)) + mock_get_all.assert_called_once_with(self.context, + project_only=True) + + @mock.patch('nova.db.network_get_all') + def test_get_all_liberal(self, mock_get_all): + self.flags(network_manager='nova.network.manager.FlatDHCPManaager') + mock_get_all.return_value = mock.sentinel.get_all + self.assertEqual(mock.sentinel.get_all, + self.network_api.get_all(self.context)) + mock_get_all.assert_called_once_with(self.context, + project_only="allow_none") + + @mock.patch('nova.db.network_get_all') + def test_get_all_no_networks(self, mock_get_all): + mock_get_all.side_effect = exception.NoNetworksFound + self.assertEqual([], self.network_api.get_all(self.context)) + mock_get_all.assert_called_once_with(self.context, + project_only=True) + def test_allocate_for_instance_handles_macs_passed(self): # If a macs argument is supplied to the 'nova-network' API, it is just # ignored. This test checks that the call down to the rpcapi layer diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/network/test_linux_net.py nova-2014.1.100+git201410062002~precise/nova/tests/network/test_linux_net.py --- nova-2014.1.100+git201408190132~precise/nova/tests/network/test_linux_net.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/network/test_linux_net.py 2014-10-07 00:02:46.000000000 +0000 @@ -1014,6 +1014,21 @@ device_exists.assert_has_calls(calls['device_exists']) _execute.assert_has_calls(calls['_execute']) + def test_ensure_bridge_brclt_addif_exception(self): + def fake_execute(*cmd, **kwargs): + if ('brctl', 'addif', 'bridge', 'eth0') == cmd: + return ('', 'some error happens') + else: + return ('', '') + + with contextlib.nested( + mock.patch.object(linux_net, 'device_exists', return_value=True), + mock.patch.object(linux_net, '_execute', fake_execute) + ) as (device_exists, _execute): + driver = linux_net.LinuxBridgeInterfaceDriver() + self.assertRaises(exception.NovaException, + driver.ensure_bridge, 'bridge', 'eth0') + def test_set_device_mtu_configured(self): self.flags(network_device_mtu=10000) calls = [ diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/network/test_neutronv2.py nova-2014.1.100+git201410062002~precise/nova/tests/network/test_neutronv2.py --- nova-2014.1.100+git201408190132~precise/nova/tests/network/test_neutronv2.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/network/test_neutronv2.py 2014-10-07 00:02:46.000000000 +0000 @@ -16,6 +16,7 @@ import copy import uuid +import mock import mox from neutronclient.common import exceptions from neutronclient.v2_0 import client @@ -30,6 +31,7 @@ from nova.network import neutronv2 from nova.network.neutronv2 import api as neutronapi from nova.network.neutronv2 import constants +from nova.objects import instance as instance_obj from nova.openstack.common import jsonutils from nova.openstack.common import local from nova import test @@ -1756,6 +1758,19 @@ self.mox.ReplayAll() api.release_floating_ip(self.context, address) + def test_disassociate_and_release_floating_ip(self): + api = neutronapi.API() + address = self.fip_unassociated['floating_ip_address'] + fip_id = self.fip_unassociated['id'] + floating_ip = {'address': address} + + self.moxed_client.list_floatingips(floating_ip_address=address).\ + AndReturn({'floatingips': [self.fip_unassociated]}) + self.moxed_client.delete_floatingip(fip_id) + self.mox.ReplayAll() + api.disassociate_and_release_floating_ip(self.context, None, + floating_ip) + def test_release_floating_ip_associated(self): api = neutronapi.API() address = self.fip_associated['floating_ip_address'] @@ -2123,6 +2138,26 @@ self.context, fake_fixed) +class TestNeutronv2WithMock(test.TestCase): + """Used to test Neutron V2 API with mock.""" + + def setUp(self): + super(TestNeutronv2WithMock, self).setUp() + self.api = neutronapi.API() + self.context = context.RequestContext( + 'fake-user', 'fake-project', + auth_token='bff4a5a6b9eb4ea2a6efec6eefb77936') + + @mock.patch('nova.openstack.common.lockutils.lock') + def test_get_instance_nw_info_locks_per_instance(self, mock_lock): + instance = instance_obj.Instance(uuid=uuid.uuid4()) + api = neutronapi.API() + mock_lock.side_effect = test.TestingException + self.assertRaises(test.TestingException, + api.get_instance_nw_info, 'context', instance) + mock_lock.assert_called_once_with('refresh_cache-%s' % instance.uuid) + + class TestNeutronv2ModuleMethods(test.TestCase): def test_gather_port_ids_and_networks_wrong_params(self): diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/objects/test_floating_ip.py nova-2014.1.100+git201410062002~precise/nova/tests/objects/test_floating_ip.py --- nova-2014.1.100+git201408190132~precise/nova/tests/objects/test_floating_ip.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/objects/test_floating_ip.py 2014-10-07 00:02:46.000000000 +0000 @@ -16,6 +16,7 @@ import netaddr from nova import exception +from nova.objects import fixed_ip from nova.objects import floating_ip from nova.tests.objects import test_fixed_ip from nova.tests.objects import test_network @@ -129,13 +130,32 @@ floatingip = floating_ip.FloatingIP(context=self.context, id=123, address='1.2.3.4', host='foo') - self.assertRaises(exception.ObjectActionError, floatingip.save) floatingip.obj_reset_changes(['address', 'id']) floatingip.save() self.assertEqual(set(), floatingip.obj_what_changed()) update.assert_called_with(self.context, '1.2.3.4', {'host': 'foo'}) + def test_save_errors(self): + floatingip = floating_ip.FloatingIP(context=self.context, + id=123, host='foo') + floatingip.obj_reset_changes() + floating_ip.address = '1.2.3.4' + self.assertRaises(exception.ObjectActionError, floatingip.save) + + floatingip.obj_reset_changes() + floatingip.fixed_ip_id = 1 + self.assertRaises(exception.ObjectActionError, floatingip.save) + + @mock.patch('nova.db.floating_ip_update') + def test_save_no_fixedip(self, update): + update.return_value = fake_floating_ip + floatingip = floating_ip.FloatingIP(context=self.context, + id=123) + floatingip.fixed_ip = fixed_ip.FixedIP(context=self.context, + id=456) + self.assertNotIn('fixed_ip', update.calls[1]) + @mock.patch('nova.db.floating_ip_get_all') def test_get_all(self, get): get.return_value = [fake_floating_ip] diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/test_api_validation.py nova-2014.1.100+git201410062002~precise/nova/tests/test_api_validation.py --- nova-2014.1.100+git201408190132~precise/nova/tests/test_api_validation.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/test_api_validation.py 2014-10-07 00:02:46.000000000 +0000 @@ -12,6 +12,8 @@ # License for the specific language governing permissions and limitations # under the License. +import re + from nova.api import validation from nova.api.validation import parameter_types from nova import exception @@ -24,11 +26,10 @@ try: method(body=body) except exception.ValidationError as ex: - expected_kwargs = { - 'code': 400, - 'detail': expected_detail - } - self.assertEqual(ex.kwargs, expected_kwargs) + self.assertEqual(400, ex.kwargs['code']) + if not re.match(expected_detail, ex.kwargs['detail']): + self.assertEqual(expected_detail, ex.kwargs['detail'], + 'Exception details did not match expected') except Exception as ex: self.fail('An unexpected exception happens: %s' % ex) else: @@ -314,22 +315,22 @@ def test_validate_integer_range_fails(self): detail = ("Invalid input for field/attribute foo. Value: 0." - " 0.0 is less than the minimum of 1") + " 0(.0)? is less than the minimum of 1") self.check_validation_error(self.post, body={'foo': 0}, expected_detail=detail) detail = ("Invalid input for field/attribute foo. Value: 11." - " 11.0 is greater than the maximum of 10") + " 11(.0)? is greater than the maximum of 10") self.check_validation_error(self.post, body={'foo': 11}, expected_detail=detail) detail = ("Invalid input for field/attribute foo. Value: 0." - " 0.0 is less than the minimum of 1") + " 0(.0)? is less than the minimum of 1") self.check_validation_error(self.post, body={'foo': '0'}, expected_detail=detail) detail = ("Invalid input for field/attribute foo. Value: 11." - " 11.0 is greater than the maximum of 10") + " 11(.0)? is greater than the maximum of 10") self.check_validation_error(self.post, body={'foo': '11'}, expected_detail=detail) @@ -555,7 +556,7 @@ expected_detail=detail) detail = ("Invalid input for field/attribute foo. Value: 65536." - " 65536.0 is greater than the maximum of 65535") + " 65536(.0)? is greater than the maximum of 65535") self.check_validation_error(self.post, body={'foo': 65536}, expected_detail=detail) diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/test_linuxscsi.py nova-2014.1.100+git201410062002~precise/nova/tests/test_linuxscsi.py --- nova-2014.1.100+git201408190132~precise/nova/tests/test_linuxscsi.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/test_linuxscsi.py 2014-10-07 00:02:46.000000000 +0000 @@ -59,7 +59,7 @@ info = linuxscsi.find_multipath_device('/dev/sde') LOG.error("info = %s" % info) - self.assertEqual("/dev/dm-3", info["device"]) + self.assertEqual("/dev/mapper/350002ac20398383d", info["device"]) self.assertEqual("/dev/sde", info['devices'][0]['device']) self.assertEqual("0", info['devices'][0]['host']) self.assertEqual("0", info['devices'][0]['id']) @@ -90,7 +90,8 @@ info = linuxscsi.find_multipath_device('/dev/sde') LOG.error("info = %s" % info) - self.assertEqual("/dev/dm-2", info["device"]) + self.assertEqual("/dev/mapper/36005076da00638089c000000000004d5", + info["device"]) self.assertEqual("/dev/sde", info['devices'][0]['device']) self.assertEqual("6", info['devices'][0]['host']) self.assertEqual("0", info['devices'][0]['channel']) @@ -118,7 +119,8 @@ info = linuxscsi.find_multipath_device('/dev/sdd') LOG.error("info = %s" % info) - self.assertEqual("/dev/dm-2", info["device"]) + self.assertEqual("/dev/mapper/36005076303ffc48e0000000000000101", + info["device"]) self.assertEqual("/dev/sdd", info['devices'][0]['device']) self.assertEqual("6", info['devices'][0]['host']) self.assertEqual("0", info['devices'][0]['channel']) diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/virt/hyperv/db_fakes.py nova-2014.1.100+git201410062002~precise/nova/tests/virt/hyperv/db_fakes.py --- nova-2014.1.100+git201408190132~precise/nova/tests/virt/hyperv/db_fakes.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/virt/hyperv/db_fakes.py 2014-10-07 00:02:46.000000000 +0000 @@ -82,7 +82,7 @@ 'target_discovered': False}}, 'mount_device': 'vda', 'delete_on_termination': False}], - 'root_device_name': None, + 'root_device_name': 'fake_root_device_name', 'ephemerals': [], 'swap': None } diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/virt/hyperv/fake.py nova-2014.1.100+git201410062002~precise/nova/tests/virt/hyperv/fake.py --- nova-2014.1.100+git201408190132~precise/nova/tests/virt/hyperv/fake.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/virt/hyperv/fake.py 2014-10-07 00:02:46.000000000 +0000 @@ -58,6 +58,10 @@ instance_path = self.get_instance_dir(instance_name) return os.path.join(instance_path, 'root.vhd') + def lookup_configdrive_path(self, instance_name): + instance_path = self.get_instance_dir(instance_name) + return os.path.join(instance_path, 'configdrive.iso') + def lookup_ephemeral_vhd_path(self, instance_name): instance_path = self.get_instance_dir(instance_name) if instance_path: diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/virt/hyperv/test_hypervapi.py nova-2014.1.100+git201410062002~precise/nova/tests/virt/hyperv/test_hypervapi.py --- nova-2014.1.100+git201408190132~precise/nova/tests/virt/hyperv/test_hypervapi.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/virt/hyperv/test_hypervapi.py 2014-10-07 00:02:46.000000000 +0000 @@ -125,6 +125,8 @@ fake_get_remote_image_service) def fake_check_min_windows_version(fake_self, major, minor): + if [major, minor] >= [6, 3]: + return False return self._check_min_windows_version_satisfied self.stubs.Set(hostutils.HostUtils, 'check_min_windows_version', fake_check_min_windows_version) @@ -133,10 +135,6 @@ pass self.stubs.Set(time, 'sleep', fake_sleep) - def fake_vmutils__init__(self, host='.'): - pass - vmutils.VMUtils.__init__ = fake_vmutils__init__ - self.stubs.Set(pathutils, 'PathUtils', fake.PathUtils) self._mox.StubOutWithMock(fake.PathUtils, 'open') self._mox.StubOutWithMock(fake.PathUtils, 'copyfile') @@ -734,10 +732,6 @@ m.AndReturn(True) if cow: - m = basevolumeutils.BaseVolumeUtils.volume_in_mapping(mox.IsA(str), - None) - m.AndReturn(False) - self._setup_get_cached_image_mocks(cow) if with_volumes: @@ -770,6 +764,15 @@ else: self.assertIsNone(self._fetched_image) + def test_get_instance_disk_info_is_implemented(self): + # Ensure that the method has been implemented in the driver + try: + disk_info = self._conn.get_instance_disk_info('fake_instance_name') + self.assertIsNone(disk_info) + except NotImplementedError: + self.fail("test_get_instance_disk_info() should not raise " + "NotImplementedError") + def test_snapshot_with_update_failure(self): (snapshot_name, func_call_matcher) = self._setup_snapshot_mocks() @@ -915,7 +918,8 @@ ephemeral_storage=False): vmutils.VMUtils.create_vm(mox.Func(self._check_vm_name), mox.IsA(int), mox.IsA(int), mox.IsA(bool), - CONF.hyperv.dynamic_memory_ratio) + CONF.hyperv.dynamic_memory_ratio, + mox.IsA(list)) if not boot_from_volume: m = vmutils.VMUtils.attach_ide_drive(mox.Func(self._check_vm_name), @@ -1016,9 +1020,10 @@ remove_dir=True) m.AndReturn(self._test_instance_dir) - m = basevolumeutils.BaseVolumeUtils.volume_in_mapping( - mox.IsA(str), block_device_info) - m.AndReturn(boot_from_volume) + if block_device_info: + m = basevolumeutils.BaseVolumeUtils.volume_in_mapping( + 'fake_root_device_name', block_device_info) + m.AndReturn(boot_from_volume) if not boot_from_volume: m = fake.PathUtils.get_instance_dir(mox.Func(self._check_vm_name)) @@ -1502,22 +1507,50 @@ (instance, fake_dest_ip, network_info, flavor) = args self._mox.ReplayAll() - self.assertRaises(vmutils.VHDResizeException, + self.assertRaises(exception.InstanceFaultRollback, self._conn.migrate_disk_and_power_off, self._context, instance, fake_dest_ip, flavor, network_info) self._mox.VerifyAll() - def _test_finish_migration(self, power_on, ephemeral_storage=False): + def _mock_attach_config_drive(self, instance, config_drive_format): + instance['config_drive'] = True + self._mox.StubOutWithMock(fake.PathUtils, 'lookup_configdrive_path') + m = fake.PathUtils.lookup_configdrive_path( + mox.Func(self._check_instance_name)) + + if config_drive_format in constants.DISK_FORMAT_MAP: + m.AndReturn(self._test_instance_dir + '/configdrive.' + + config_drive_format) + else: + m.AndReturn(None) + + m = vmutils.VMUtils.attach_ide_drive( + mox.Func(self._check_instance_name), + mox.IsA(str), + mox.IsA(int), + mox.IsA(int), + mox.IsA(str)) + m.WithSideEffects(self._add_ide_disk).InAnyOrder() + + def _verify_attach_config_drive(self, config_drive_format): + if config_drive_format == constants.IDE_DISK_FORMAT.lower(): + self.assertEqual(self._instance_ide_disks[1], + self._test_instance_dir + '/configdrive.' + + config_drive_format) + elif config_drive_format == constants.IDE_DVD_FORMAT.lower(): + self.assertEqual(self._instance_ide_dvds[0], + self._test_instance_dir + '/configdrive.' + + config_drive_format) + + def _test_finish_migration(self, power_on, ephemeral_storage=False, + config_drive=False, + config_drive_format='iso'): self._instance_data = self._get_instance_data() instance = db.instance_create(self._context, self._instance_data) instance['system_metadata'] = {} network_info = fake_network.fake_get_instance_nw_info(self.stubs) - m = basevolumeutils.BaseVolumeUtils.volume_in_mapping(mox.IsA(str), - None) - m.AndReturn(False) - m = fake.PathUtils.get_instance_dir(mox.IsA(str)) m.AndReturn(self._test_instance_dir) @@ -1557,11 +1590,17 @@ vmutils.VMUtils.set_vm_state(mox.Func(self._check_instance_name), constants.HYPERV_VM_STATE_ENABLED) + if config_drive: + self._mock_attach_config_drive(instance, config_drive_format) + self._mox.ReplayAll() self._conn.finish_migration(self._context, None, instance, "", network_info, None, False, None, power_on) self._mox.VerifyAll() + if config_drive: + self._verify_attach_config_drive(config_drive_format) + def test_finish_migration_power_on(self): self._test_finish_migration(True) @@ -1571,6 +1610,14 @@ def test_finish_migration_with_ephemeral_storage(self): self._test_finish_migration(False, ephemeral_storage=True) + def test_finish_migration_attach_config_drive_iso(self): + self._test_finish_migration(False, config_drive=True, + config_drive_format=constants.IDE_DVD_FORMAT.lower()) + + def test_finish_migration_attach_config_drive_vhd(self): + self._test_finish_migration(False, config_drive=True, + config_drive_format=constants.IDE_DISK_FORMAT.lower()) + def test_confirm_migration(self): self._instance_data = self._get_instance_data() instance = db.instance_create(self._context, self._instance_data) @@ -1582,7 +1629,9 @@ self._conn.confirm_migration(None, instance, network_info) self._mox.VerifyAll() - def _test_finish_revert_migration(self, power_on, ephemeral_storage=False): + def _test_finish_revert_migration(self, power_on, ephemeral_storage=False, + config_drive=False, + config_drive_format='iso'): self._instance_data = self._get_instance_data() instance = db.instance_create(self._context, self._instance_data) network_info = fake_network.fake_get_instance_nw_info(self.stubs) @@ -1590,10 +1639,6 @@ fake_revert_path = ('C:\\FakeInstancesPath\\%s\\_revert' % instance['name']) - m = basevolumeutils.BaseVolumeUtils.volume_in_mapping(mox.IsA(str), - None) - m.AndReturn(False) - m = fake.PathUtils.get_instance_dir(mox.IsA(str), create_dir=False, remove_dir=True) @@ -1620,12 +1665,18 @@ vmutils.VMUtils.set_vm_state(mox.Func(self._check_instance_name), constants.HYPERV_VM_STATE_ENABLED) + if config_drive: + self._mock_attach_config_drive(instance, config_drive_format) + self._mox.ReplayAll() self._conn.finish_revert_migration(self._context, instance, network_info, None, power_on) self._mox.VerifyAll() + if config_drive: + self._verify_attach_config_drive(config_drive_format) + def test_finish_revert_migration_power_on(self): self._test_finish_revert_migration(True) @@ -1641,6 +1692,14 @@ def test_finish_revert_migration_with_ephemeral_storage(self): self._test_finish_revert_migration(False, ephemeral_storage=True) + def test_finish_revert_migration_attach_config_drive_iso(self): + self._test_finish_revert_migration(False, config_drive=True, + config_drive_format=constants.IDE_DVD_FORMAT.lower()) + + def test_finish_revert_migration_attach_config_drive_vhd(self): + self._test_finish_revert_migration(False, config_drive=True, + config_drive_format=constants.IDE_DISK_FORMAT.lower()) + def test_plug_vifs(self): # Check to make sure the method raises NotImplementedError. self.assertRaises(NotImplementedError, diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/virt/hyperv/test_migrationops.py nova-2014.1.100+git201410062002~precise/nova/tests/virt/hyperv/test_migrationops.py --- nova-2014.1.100+git201408190132~precise/nova/tests/virt/hyperv/test_migrationops.py 1970-01-01 00:00:00.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/virt/hyperv/test_migrationops.py 2014-10-07 00:02:46.000000000 +0000 @@ -0,0 +1,46 @@ +# Copyright 2014 IBM Corp. +# +# 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 mock + +from nova import test +from nova.tests import fake_instance +from nova.virt.hyperv import migrationops +from nova.virt.hyperv import vmutils + + +class MigrationOpsTestCase(test.NoDBTestCase): + """Unit tests for the Hyper-V MigrationOps class.""" + + def setUp(self): + super(MigrationOpsTestCase, self).setUp() + self.context = 'fake-context' + + # utilsfactory will check the host OS version via get_hostutils, + # in order to return the proper Utils Class, so it must be mocked. + patched_func = mock.patch.object(migrationops.utilsfactory, + "get_hostutils") + patched_func.start() + self.addCleanup(patched_func.stop) + + self._migrationops = migrationops.MigrationOps() + + def test_check_and_attach_config_drive_unknown_path(self): + instance = fake_instance.fake_instance_obj(self.context) + instance.config_drive = 'True' + self._migrationops._pathutils.lookup_configdrive_path = mock.MagicMock( + return_value=None) + self.assertRaises(vmutils.HyperVException, + self._migrationops._check_and_attach_config_drive, + instance) diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/virt/hyperv/test_pathutils.py nova-2014.1.100+git201410062002~precise/nova/tests/virt/hyperv/test_pathutils.py --- nova-2014.1.100+git201408190132~precise/nova/tests/virt/hyperv/test_pathutils.py 1970-01-01 00:00:00.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/virt/hyperv/test_pathutils.py 2014-10-07 00:02:46.000000000 +0000 @@ -0,0 +1,56 @@ +# Copyright 2014 IBM Corp. +# +# 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 mock + +from nova import test +from nova.virt.hyperv import constants +from nova.virt.hyperv import pathutils + + +class PathUtilsTestCase(test.NoDBTestCase): + """Unit tests for the Hyper-V PathUtils class.""" + + def setUp(self): + self.fake_instance_dir = 'C:/fake_instance_dir' + self.fake_instance_name = 'fake_instance_name' + self._pathutils = pathutils.PathUtils() + super(PathUtilsTestCase, self).setUp() + + def _mock_lookup_configdrive_path(self, ext): + self._pathutils.get_instance_dir = mock.MagicMock( + return_value=self.fake_instance_dir) + + def mock_exists(*args, **kwargs): + path = args[0] + return True if path[(path.rfind('.') + 1):] == ext else False + self._pathutils.exists = mock_exists + configdrive_path = self._pathutils.lookup_configdrive_path( + self.fake_instance_name) + return configdrive_path + + def test_lookup_configdrive_path(self): + for format_ext in constants.DISK_FORMAT_MAP: + configdrive_path = self._mock_lookup_configdrive_path(format_ext) + self.assertEqual(configdrive_path, + self.fake_instance_dir + '/configdrive.' + + format_ext) + + def test_lookup_configdrive_path_non_exist(self): + self._pathutils.get_instance_dir = mock.MagicMock( + return_value=self.fake_instance_dir) + self._pathutils.exists = mock.MagicMock(return_value=False) + configdrive_path = self._pathutils.lookup_configdrive_path( + self.fake_instance_name) + self.assertIsNone(configdrive_path) diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/virt/hyperv/test_utilsfactory.py nova-2014.1.100+git201410062002~precise/nova/tests/virt/hyperv/test_utilsfactory.py --- nova-2014.1.100+git201408190132~precise/nova/tests/virt/hyperv/test_utilsfactory.py 1970-01-01 00:00:00.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/virt/hyperv/test_utilsfactory.py 2014-10-07 00:02:46.000000000 +0000 @@ -0,0 +1,61 @@ +# Copyright 2014 Cloudbase Solutions SRL +# All Rights Reserved. +# +# 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. + +""" +Unit tests for the Hyper-V utils factory. +""" + +import mock +from oslo.config import cfg + +from nova import test +from nova.virt.hyperv import hostutils +from nova.virt.hyperv import utilsfactory +from nova.virt.hyperv import vmutils +from nova.virt.hyperv import vmutilsv2 + +CONF = cfg.CONF + + +class TestHyperVUtilsFactory(test.NoDBTestCase): + + def setUp(self): + super(TestHyperVUtilsFactory, self).setUp() + + def test_get_vmutils_force_v1_and_min_version(self): + self._test_returned_class(None, True, True) + + def test_get_vmutils_v2(self): + self._test_returned_class(vmutilsv2.VMUtilsV2, False, True) + + def test_get_vmutils_v2_r2(self): + self._test_returned_class(vmutils.VMUtils, False, False) + + def test_get_vmutils_force_v1_and_not_min_version(self): + self._test_returned_class(vmutils.VMUtils, True, False) + + def _test_returned_class(self, expected_class, force_v1, os_supports_v2): + CONF.set_override('force_hyperv_utils_v1', force_v1, 'hyperv') + with mock.patch.object( + hostutils.HostUtils, + 'check_min_windows_version') as mock_check_min_windows_version: + mock_check_min_windows_version.return_value = os_supports_v2 + + if os_supports_v2 and force_v1: + self.assertRaises(vmutils.HyperVException, + utilsfactory.get_vmutils) + else: + actual_class = type(utilsfactory.get_vmutils()) + self.assertEqual(actual_class, expected_class) diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/virt/hyperv/test_vmops.py nova-2014.1.100+git201410062002~precise/nova/tests/virt/hyperv/test_vmops.py --- nova-2014.1.100+git201408190132~precise/nova/tests/virt/hyperv/test_vmops.py 1970-01-01 00:00:00.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/virt/hyperv/test_vmops.py 2014-10-07 00:02:46.000000000 +0000 @@ -0,0 +1,57 @@ +# Copyright 2014 IBM Corp. +# +# 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 mock + +from nova import exception +from nova import test +from nova.tests import fake_instance +from nova.virt.hyperv import vmops + + +class VMOpsTestCase(test.NoDBTestCase): + """Unit tests for the Hyper-V VMOps class.""" + + def __init__(self, test_case_name): + super(VMOpsTestCase, self).__init__(test_case_name) + + def setUp(self): + super(VMOpsTestCase, self).setUp() + self.context = 'fake-context' + + # utilsfactory will check the host OS version via get_hostutils, + # in order to return the proper Utils Class, so it must be mocked. + patched_func = mock.patch.object(vmops.utilsfactory, + "get_hostutils") + patched_func.start() + self.addCleanup(patched_func.stop) + + self._vmops = vmops.VMOps() + + def test_attach_config_drive(self): + instance = fake_instance.fake_instance_obj(self.context) + self.assertRaises(exception.InvalidDiskFormat, + self._vmops.attach_config_drive, + instance, 'C:/fake_instance_dir/configdrive.xxx') + + def test_list_instance_uuids(self): + fake_uuid = '4f54fb69-d3a2-45b7-bb9b-b6e6b3d893b3' + with mock.patch.object(self._vmops._vmutils, + 'list_instance_notes') as mock_list_notes: + mock_list_notes.return_value = [('fake_name', [fake_uuid])] + + response = self._vmops.list_instance_uuids() + mock_list_notes.assert_called_once_with() + + self.assertEqual(response, [fake_uuid]) diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/virt/hyperv/test_vmutils.py nova-2014.1.100+git201410062002~precise/nova/tests/virt/hyperv/test_vmutils.py --- nova-2014.1.100+git201408190132~precise/nova/tests/virt/hyperv/test_vmutils.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/virt/hyperv/test_vmutils.py 2014-10-07 00:02:46.000000000 +0000 @@ -1,4 +1,4 @@ -# Copyright 2013 Cloudbase Solutions Srl +# Copyright 2014 Cloudbase Solutions Srl # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -15,8 +15,9 @@ import mock +from nova import exception from nova import test - +from nova.virt.hyperv import constants from nova.virt.hyperv import vmutils @@ -25,10 +26,46 @@ _FAKE_VM_NAME = 'fake_vm' _FAKE_MEMORY_MB = 2 + _FAKE_VCPUS_NUM = 4 + _FAKE_JOB_PATH = 'fake_job_path' + _FAKE_RET_VAL = 0 + _FAKE_RET_VAL_BAD = -1 + _FAKE_CTRL_PATH = 'fake_ctrl_path' + _FAKE_CTRL_ADDR = 0 + _FAKE_DRIVE_ADDR = 0 + _FAKE_MOUNTED_DISK_PATH = 'fake_mounted_disk_path' _FAKE_VM_PATH = "fake_vm_path" _FAKE_VHD_PATH = "fake_vhd_path" _FAKE_DVD_PATH = "fake_dvd_path" _FAKE_VOLUME_DRIVE_PATH = "fake_volume_drive_path" + _FAKE_SNAPSHOT_PATH = "fake_snapshot_path" + _FAKE_RES_DATA = "fake_res_data" + _FAKE_HOST_RESOURCE = "fake_host_resource" + _FAKE_CLASS = "FakeClass" + _FAKE_RES_PATH = "fake_res_path" + _FAKE_RES_NAME = 'fake_res_name' + _FAKE_ADDRESS = "fake_address" + _FAKE_JOB_STATUS_DONE = 7 + _FAKE_JOB_STATUS_BAD = -1 + _FAKE_JOB_DESCRIPTION = "fake_job_description" + _FAKE_ERROR = "fake_error" + _FAKE_ELAPSED_TIME = 0 + _CONCRETE_JOB = "Msvm_ConcreteJob" + _FAKE_DYNAMIC_MEMORY_RATIO = 1.0 + + _FAKE_SUMMARY_INFO = {'NumberOfProcessors': 4, + 'EnabledState': 2, + 'MemoryUsage': 2, + 'UpTime': 1} + + _DEFINE_SYSTEM = 'DefineVirtualSystem' + _DESTROY_SYSTEM = 'DestroyVirtualSystem' + _DESTROY_SNAPSHOT = 'RemoveVirtualSystemSnapshot' + _ADD_RESOURCE = 'AddVirtualSystemResources' + _REMOVE_RESOURCE = 'RemoveVirtualSystemResources' + _SETTING_TYPE = 'SettingType' + + _VIRTUAL_SYSTEM_TYPE_REALIZED = 3 def setUp(self): self._vmutils = vmutils.VMUtils() @@ -41,6 +78,20 @@ self._vmutils.enable_vm_metrics_collection, self._FAKE_VM_NAME) + def test_get_vm_summary_info(self): + self._lookup_vm() + mock_svc = self._vmutils._conn.Msvm_VirtualSystemManagementService()[0] + + mock_summary = mock.MagicMock() + mock_svc.GetSummaryInformation.return_value = (self._FAKE_RET_VAL, + [mock_summary]) + + for (key, val) in self._FAKE_SUMMARY_INFO.items(): + setattr(mock_summary, key, val) + + summary = self._vmutils.get_vm_summary_info(self._FAKE_VM_NAME) + self.assertEqual(self._FAKE_SUMMARY_INFO, summary) + def _lookup_vm(self): mock_vm = mock.MagicMock() self._vmutils._lookup_vm_check = mock.MagicMock( @@ -48,6 +99,25 @@ mock_vm.path_.return_value = self._FAKE_VM_PATH return mock_vm + def test_lookup_vm_ok(self): + mock_vm = mock.MagicMock() + self._vmutils._conn.Msvm_ComputerSystem.return_value = [mock_vm] + vm = self._vmutils._lookup_vm_check(self._FAKE_VM_NAME) + self.assertEqual(mock_vm, vm) + + def test_lookup_vm_multiple(self): + mockvm = mock.MagicMock() + self._vmutils._conn.Msvm_ComputerSystem.return_value = [mockvm, mockvm] + self.assertRaises(vmutils.HyperVException, + self._vmutils._lookup_vm_check, + self._FAKE_VM_NAME) + + def test_lookup_vm_none(self): + self._vmutils._conn.Msvm_ComputerSystem.return_value = [] + self.assertRaises(exception.NotFound, + self._vmutils._lookup_vm_check, + self._FAKE_VM_NAME) + def test_set_vm_memory_static(self): self._test_set_vm_memory_dynamic(1.0) @@ -109,6 +179,7 @@ def _create_mock_disks(self): mock_rasd1 = mock.MagicMock() mock_rasd1.ResourceSubType = self._vmutils._IDE_DISK_RES_SUB_TYPE + mock_rasd1.HostResource = [self._FAKE_VHD_PATH] mock_rasd1.Connection = [self._FAKE_VHD_PATH] mock_rasd2 = mock.MagicMock() @@ -116,3 +187,383 @@ mock_rasd2.HostResource = [self._FAKE_VOLUME_DRIVE_PATH] return [mock_rasd1, mock_rasd2] + + def test_list_instance_notes(self): + vs = mock.MagicMock() + attrs = {'ElementName': 'fake_name', + 'Notes': '4f54fb69-d3a2-45b7-bb9b-b6e6b3d893b3'} + vs.configure_mock(**attrs) + self._vmutils._conn.Msvm_VirtualSystemSettingData.return_value = [vs] + response = self._vmutils.list_instance_notes() + + self.assertEqual([(attrs['ElementName'], [attrs['Notes']])], response) + self._vmutils._conn.Msvm_VirtualSystemSettingData.assert_called_with( + ['ElementName', 'Notes'], + SettingType=self._vmutils._VIRTUAL_SYSTEM_CURRENT_SETTINGS) + + @mock.patch('nova.virt.hyperv.vmutils.VMUtils.check_ret_val') + def test_modify_virtual_system(self, mock_check_ret_val): + mock_vs_man_svc = mock.MagicMock() + mock_vmsetting = mock.MagicMock() + fake_path = 'fake path' + fake_job_path = 'fake job path' + fake_ret_val = 'fake return value' + + mock_vs_man_svc.ModifyVirtualSystem.return_value = (0, fake_job_path, + fake_ret_val) + + self._vmutils._modify_virtual_system(vs_man_svc=mock_vs_man_svc, + vm_path=fake_path, + vmsetting=mock_vmsetting) + + mock_vs_man_svc.ModifyVirtualSystem.assert_called_once_with( + ComputerSystem=fake_path, + SystemSettingData=mock_vmsetting.GetText_(1)) + mock_check_ret_val.assert_called_once_with(fake_ret_val, fake_job_path) + + @mock.patch('nova.virt.hyperv.vmutils.VMUtils.check_ret_val') + @mock.patch('nova.virt.hyperv.vmutils.VMUtils._get_wmi_obj') + @mock.patch('nova.virt.hyperv.vmutils.VMUtils._modify_virtual_system') + @mock.patch('nova.virt.hyperv.vmutils.VMUtils._get_vm_setting_data') + def test_create_vm_obj(self, mock_get_vm_setting_data, + mock_modify_virtual_system, + mock_get_wmi_obj, mock_check_ret_val): + mock_vs_man_svc = mock.MagicMock() + mock_vs_gs_data = mock.MagicMock() + fake_vm_path = 'fake vm path' + fake_job_path = 'fake job path' + fake_ret_val = 'fake return value' + _conn = self._vmutils._conn.Msvm_VirtualSystemGlobalSettingData + + _conn.new.return_value = mock_vs_gs_data + mock_vs_man_svc.DefineVirtualSystem.return_value = (fake_vm_path, + fake_job_path, + fake_ret_val) + + response = self._vmutils._create_vm_obj(vs_man_svc=mock_vs_man_svc, + vm_name='fake vm', + notes='fake notes') + + _conn.new.assert_called_once_with() + self.assertEqual(mock_vs_gs_data.ElementName, 'fake vm') + mock_vs_man_svc.DefineVirtualSystem.assert_called_once_with( + [], None, mock_vs_gs_data.GetText_(1)) + mock_check_ret_val.assert_called_once_with(fake_ret_val, fake_job_path) + + mock_get_wmi_obj.assert_called_with(fake_vm_path) + mock_get_vm_setting_data.assert_called_once_with(mock_get_wmi_obj()) + mock_modify_virtual_system.assert_called_once_with( + mock_vs_man_svc, fake_vm_path, mock_get_vm_setting_data()) + + self.assertEqual(mock_get_vm_setting_data().Notes, + '\n'.join('fake notes')) + self.assertEqual(response, mock_get_wmi_obj()) + + def test_list_instances(self): + vs = mock.MagicMock() + attrs = {'ElementName': 'fake_name'} + vs.configure_mock(**attrs) + self._vmutils._conn.Msvm_VirtualSystemSettingData.return_value = [vs] + response = self._vmutils.list_instances() + + self.assertEqual([(attrs['ElementName'])], response) + self._vmutils._conn.Msvm_VirtualSystemSettingData.assert_called_with( + ['ElementName'], + SettingType=self._vmutils._VIRTUAL_SYSTEM_CURRENT_SETTINGS) + + @mock.patch.object(vmutils.VMUtils, '_set_vm_vcpus') + @mock.patch.object(vmutils.VMUtils, '_set_vm_memory') + @mock.patch.object(vmutils.VMUtils, '_get_wmi_obj') + def test_create_vm(self, mock_get_wmi_obj, mock_set_mem, mock_set_vcpus): + mock_svc = self._vmutils._conn.Msvm_VirtualSystemManagementService()[0] + getattr(mock_svc, self._DEFINE_SYSTEM).return_value = ( + None, self._FAKE_JOB_PATH, self._FAKE_RET_VAL) + + mock_vm = mock_get_wmi_obj.return_value + self._vmutils._conn.Msvm_ComputerSystem.return_value = [mock_vm] + + mock_s = mock.MagicMock() + setattr(mock_s, + self._SETTING_TYPE, + self._VIRTUAL_SYSTEM_TYPE_REALIZED) + mock_vm.associators.return_value = [mock_s] + + self._vmutils.create_vm(self._FAKE_VM_NAME, self._FAKE_MEMORY_MB, + self._FAKE_VCPUS_NUM, False, + self._FAKE_DYNAMIC_MEMORY_RATIO) + + self.assertTrue(getattr(mock_svc, self._DEFINE_SYSTEM).called) + mock_set_mem.assert_called_with(mock_vm, mock_s, self._FAKE_MEMORY_MB, + self._FAKE_DYNAMIC_MEMORY_RATIO) + + mock_set_vcpus.assert_called_with(mock_vm, mock_s, + self._FAKE_VCPUS_NUM, + False) + + def test_get_vm_scsi_controller(self): + self._prepare_get_vm_controller(self._vmutils._SCSI_CTRL_RES_SUB_TYPE) + path = self._vmutils.get_vm_scsi_controller(self._FAKE_VM_NAME) + self.assertEqual(self._FAKE_RES_PATH, path) + + def test_get_vm_ide_controller(self): + self._prepare_get_vm_controller(self._vmutils._IDE_CTRL_RES_SUB_TYPE) + path = self._vmutils.get_vm_ide_controller(self._FAKE_VM_NAME, + self._FAKE_ADDRESS) + self.assertEqual(self._FAKE_RES_PATH, path) + + def _prepare_get_vm_controller(self, resource_sub_type): + mock_vm = self._lookup_vm() + mock_vm_settings = mock.MagicMock() + mock_rasds = mock.MagicMock() + mock_rasds.path_.return_value = self._FAKE_RES_PATH + mock_rasds.ResourceSubType = resource_sub_type + mock_rasds.Address = self._FAKE_ADDRESS + mock_vm_settings.associators.return_value = [mock_rasds] + mock_vm.associators.return_value = [mock_vm_settings] + + def _prepare_resources(self, mock_path, mock_subtype, mock_vm_settings): + mock_rasds = mock_vm_settings.associators.return_value[0] + mock_rasds.path_.return_value = mock_path + mock_rasds.ResourceSubType = mock_subtype + return mock_rasds + + @mock.patch.object(vmutils.VMUtils, '_get_new_resource_setting_data') + @mock.patch.object(vmutils.VMUtils, '_get_vm_ide_controller') + def test_attach_ide_drive(self, mock_get_ide_ctrl, mock_get_new_rsd): + mock_vm = self._lookup_vm() + mock_rsd = mock_get_new_rsd.return_value + + with mock.patch.object(self._vmutils, + '_add_virt_resource') as mock_add_virt_res: + self._vmutils.attach_ide_drive(self._FAKE_VM_NAME, + self._FAKE_CTRL_PATH, + self._FAKE_CTRL_ADDR, + self._FAKE_DRIVE_ADDR) + + mock_add_virt_res.assert_called_with(mock_rsd, + mock_vm.path_.return_value) + + mock_get_ide_ctrl.assert_called_with(mock_vm, self._FAKE_CTRL_ADDR) + self.assertTrue(mock_get_new_rsd.called) + + @mock.patch.object(vmutils.VMUtils, '_get_new_resource_setting_data') + def test_create_scsi_controller(self, mock_get_new_rsd): + mock_vm = self._lookup_vm() + with mock.patch.object(self._vmutils, + '_add_virt_resource') as mock_add_virt_res: + self._vmutils.create_scsi_controller(self._FAKE_VM_NAME) + + mock_add_virt_res.assert_called_with(mock_get_new_rsd.return_value, + mock_vm.path_.return_value) + + @mock.patch.object(vmutils.VMUtils, '_get_new_resource_setting_data') + def test_attach_volume_to_controller(self, mock_get_new_rsd): + mock_vm = self._lookup_vm() + with mock.patch.object(self._vmutils, + '_add_virt_resource') as mock_add_virt_res: + self._vmutils.attach_volume_to_controller( + self._FAKE_VM_NAME, self._FAKE_CTRL_PATH, self._FAKE_CTRL_ADDR, + self._FAKE_MOUNTED_DISK_PATH) + + mock_add_virt_res.assert_called_with(mock_get_new_rsd.return_value, + mock_vm.path_.return_value) + + @mock.patch.object(vmutils.VMUtils, '_modify_virt_resource') + @mock.patch.object(vmutils.VMUtils, '_get_nic_data_by_name') + def test_set_nic_connection(self, mock_get_nic_conn, mock_modify_virt_res): + self._lookup_vm() + mock_nic = mock_get_nic_conn.return_value + self._vmutils.set_nic_connection(self._FAKE_VM_NAME, None, None) + + mock_modify_virt_res.assert_called_with(mock_nic, self._FAKE_VM_PATH) + + @mock.patch.object(vmutils.VMUtils, '_get_new_setting_data') + def test_create_nic(self, mock_get_new_virt_res): + self._lookup_vm() + mock_nic = mock_get_new_virt_res.return_value + + with mock.patch.object(self._vmutils, + '_add_virt_resource') as mock_add_virt_res: + self._vmutils.create_nic( + self._FAKE_VM_NAME, self._FAKE_RES_NAME, self._FAKE_ADDRESS) + + mock_add_virt_res.assert_called_with(mock_nic, self._FAKE_VM_PATH) + + def test_set_vm_state(self): + mock_vm = self._lookup_vm() + mock_vm.RequestStateChange.return_value = ( + self._FAKE_JOB_PATH, self._FAKE_RET_VAL) + + self._vmutils.set_vm_state(self._FAKE_VM_NAME, + constants.HYPERV_VM_STATE_ENABLED) + mock_vm.RequestStateChange.assert_called_with( + constants.HYPERV_VM_STATE_ENABLED) + + def test_destroy_vm(self): + self._lookup_vm() + + mock_svc = self._vmutils._conn.Msvm_VirtualSystemManagementService()[0] + getattr(mock_svc, self._DESTROY_SYSTEM).return_value = ( + self._FAKE_JOB_PATH, self._FAKE_RET_VAL) + + self._vmutils.destroy_vm(self._FAKE_VM_NAME) + + getattr(mock_svc, self._DESTROY_SYSTEM).assert_called_with( + self._FAKE_VM_PATH) + + @mock.patch.object(vmutils.VMUtils, '_wait_for_job') + def test_check_ret_val_ok(self, mock_wait_for_job): + self._vmutils.check_ret_val(constants.WMI_JOB_STATUS_STARTED, + self._FAKE_JOB_PATH) + mock_wait_for_job.assert_called_once_with(self._FAKE_JOB_PATH) + + def test_check_ret_val_exception(self): + self.assertRaises(vmutils.HyperVException, + self._vmutils.check_ret_val, + self._FAKE_RET_VAL_BAD, + self._FAKE_JOB_PATH) + + def test_wait_for_job_done(self): + mockjob = self._prepare_wait_for_job(constants.WMI_JOB_STATE_COMPLETED) + job = self._vmutils._wait_for_job(self._FAKE_JOB_PATH) + self.assertEqual(mockjob, job) + + def test_wait_for_job_exception_concrete_job(self): + mock_job = self._prepare_wait_for_job() + mock_job.path.return_value.Class = self._CONCRETE_JOB + self.assertRaises(vmutils.HyperVException, + self._vmutils._wait_for_job, + self._FAKE_JOB_PATH) + + def test_wait_for_job_exception_with_error(self): + mock_job = self._prepare_wait_for_job() + mock_job.GetError.return_value = (self._FAKE_ERROR, self._FAKE_RET_VAL) + self.assertRaises(vmutils.HyperVException, + self._vmutils._wait_for_job, + self._FAKE_JOB_PATH) + + def test_wait_for_job_exception_no_error(self): + mock_job = self._prepare_wait_for_job() + mock_job.GetError.return_value = (None, None) + self.assertRaises(vmutils.HyperVException, + self._vmutils._wait_for_job, + self._FAKE_JOB_PATH) + + def _prepare_wait_for_job(self, state=_FAKE_JOB_STATUS_BAD): + mock_job = mock.MagicMock() + mock_job.JobState = state + mock_job.Description = self._FAKE_JOB_DESCRIPTION + mock_job.ElapsedTime = self._FAKE_ELAPSED_TIME + + self._vmutils._get_wmi_obj = mock.MagicMock(return_value=mock_job) + return mock_job + + def test_add_virt_resource(self): + mock_svc = self._vmutils._conn.Msvm_VirtualSystemManagementService()[0] + getattr(mock_svc, self._ADD_RESOURCE).return_value = ( + self._FAKE_JOB_PATH, mock.MagicMock(), self._FAKE_RET_VAL) + mock_res_setting_data = mock.MagicMock() + mock_res_setting_data.GetText_.return_value = self._FAKE_RES_DATA + + self._vmutils._add_virt_resource(mock_res_setting_data, + self._FAKE_VM_PATH) + self._assert_add_resources(mock_svc) + + def test_modify_virt_resource(self): + mock_svc = self._vmutils._conn.Msvm_VirtualSystemManagementService()[0] + mock_svc.ModifyVirtualSystemResources.return_value = ( + self._FAKE_JOB_PATH, self._FAKE_RET_VAL) + mock_res_setting_data = mock.MagicMock() + mock_res_setting_data.GetText_.return_value = self._FAKE_RES_DATA + + self._vmutils._modify_virt_resource(mock_res_setting_data, + self._FAKE_VM_PATH) + + mock_svc.ModifyVirtualSystemResources.assert_called_with( + ResourceSettingData=[self._FAKE_RES_DATA], + ComputerSystem=self._FAKE_VM_PATH) + + def test_remove_virt_resource(self): + mock_svc = self._vmutils._conn.Msvm_VirtualSystemManagementService()[0] + getattr(mock_svc, self._REMOVE_RESOURCE).return_value = ( + self._FAKE_JOB_PATH, self._FAKE_RET_VAL) + mock_res_setting_data = mock.MagicMock() + mock_res_setting_data.path_.return_value = self._FAKE_RES_PATH + + self._vmutils._remove_virt_resource(mock_res_setting_data, + self._FAKE_VM_PATH) + self._assert_remove_resources(mock_svc) + + @mock.patch.object(vmutils, 'wmi', create=True) + @mock.patch.object(vmutils.VMUtils, 'check_ret_val') + def test_take_vm_snapshot(self, mock_check_ret_val, mock_wmi): + self._lookup_vm() + + mock_svc = self._get_snapshot_service() + mock_svc.CreateVirtualSystemSnapshot.return_value = ( + self._FAKE_JOB_PATH, self._FAKE_RET_VAL, mock.MagicMock()) + + self._vmutils.take_vm_snapshot(self._FAKE_VM_NAME) + + mock_svc.CreateVirtualSystemSnapshot.assert_called_with( + self._FAKE_VM_PATH) + + mock_check_ret_val.assert_called_once_with(self._FAKE_RET_VAL, + self._FAKE_JOB_PATH) + + def test_remove_vm_snapshot(self): + mock_svc = self._get_snapshot_service() + getattr(mock_svc, self._DESTROY_SNAPSHOT).return_value = ( + self._FAKE_JOB_PATH, self._FAKE_RET_VAL) + + self._vmutils.remove_vm_snapshot(self._FAKE_SNAPSHOT_PATH) + getattr(mock_svc, self._DESTROY_SNAPSHOT).assert_called_with( + self._FAKE_SNAPSHOT_PATH) + + def test_detach_vm_disk(self): + self._lookup_vm() + mock_disk = self._prepare_mock_disk() + + with mock.patch.object(self._vmutils, + '_remove_virt_resource') as mock_rm_virt_res: + self._vmutils.detach_vm_disk(self._FAKE_VM_NAME, + self._FAKE_HOST_RESOURCE) + + mock_rm_virt_res.assert_called_with(mock_disk, self._FAKE_VM_PATH) + + def test_get_mounted_disk_resource_from_path(self): + mock_disk_1 = mock.MagicMock() + mock_disk_2 = mock.MagicMock() + mock_disk_2.HostResource = [self._FAKE_MOUNTED_DISK_PATH] + self._vmutils._conn.query.return_value = [mock_disk_1, mock_disk_2] + + physical_disk = self._vmutils._get_mounted_disk_resource_from_path( + self._FAKE_MOUNTED_DISK_PATH) + + self.assertEqual(mock_disk_2, physical_disk) + + def test_get_controller_volume_paths(self): + self._prepare_mock_disk() + mock_disks = {self._FAKE_RES_PATH: self._FAKE_HOST_RESOURCE} + disks = self._vmutils.get_controller_volume_paths(self._FAKE_RES_PATH) + self.assertEqual(mock_disks, disks) + + def _prepare_mock_disk(self): + mock_disk = mock.MagicMock() + mock_disk.HostResource = [self._FAKE_HOST_RESOURCE] + mock_disk.path.return_value.RelPath = self._FAKE_RES_PATH + mock_disk.ResourceSubType = self._vmutils._IDE_DISK_RES_SUB_TYPE + self._vmutils._conn.query.return_value = [mock_disk] + + return mock_disk + + def _get_snapshot_service(self): + return self._vmutils._conn.Msvm_VirtualSystemManagementService()[0] + + def _assert_add_resources(self, mock_svc): + getattr(mock_svc, self._ADD_RESOURCE).assert_called_with( + [self._FAKE_RES_DATA], self._FAKE_VM_PATH) + + def _assert_remove_resources(self, mock_svc): + getattr(mock_svc, self._REMOVE_RESOURCE).assert_called_with( + [self._FAKE_RES_PATH], self._FAKE_VM_PATH) diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/virt/hyperv/test_vmutilsv2.py nova-2014.1.100+git201410062002~precise/nova/tests/virt/hyperv/test_vmutilsv2.py --- nova-2014.1.100+git201408190132~precise/nova/tests/virt/hyperv/test_vmutilsv2.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/virt/hyperv/test_vmutilsv2.py 2014-10-07 00:02:46.000000000 +0000 @@ -1,4 +1,4 @@ -# Copyright 2013 Cloudbase Solutions Srl +# Copyright 2014 Cloudbase Solutions Srl # # 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 @@ -14,160 +14,51 @@ import mock -from nova import test - +from nova.tests.virt.hyperv import test_vmutils from nova.virt.hyperv import vmutilsv2 -class VMUtilsV2TestCase(test.NoDBTestCase): +class VMUtilsV2TestCase(test_vmutils.VMUtilsTestCase): """Unit tests for the Hyper-V VMUtilsV2 class.""" - _FAKE_VM_NAME = 'fake_vm' - _FAKE_MEMORY_MB = 2 - _FAKE_VCPUS_NUM = 4 - _FAKE_JOB_PATH = 'fake_job_path' - _FAKE_RET_VAL = 0 - _FAKE_CTRL_PATH = 'fake_ctrl_path' - _FAKE_CTRL_ADDR = 0 - _FAKE_DRIVE_ADDR = 0 - _FAKE_MOUNTED_DISK_PATH = 'fake_mounted_disk_path' - _FAKE_VM_PATH = "fake_vm_path" - _FAKE_ENABLED_STATE = 1 - _FAKE_SNAPSHOT_PATH = "_FAKE_SNAPSHOT_PATH" - _FAKE_RES_DATA = "fake_res_data" - _FAKE_RES_PATH = "fake_res_path" - _FAKE_DYNAMIC_MEMORY_RATIO = 1.0 - _FAKE_VHD_PATH = "fake_vhd_path" - _FAKE_VOLUME_DRIVE_PATH = "fake_volume_drive_path" + _DEFINE_SYSTEM = 'DefineSystem' + _DESTROY_SYSTEM = 'DestroySystem' + _DESTROY_SNAPSHOT = 'DestroySnapshot' + + _ADD_RESOURCE = 'AddResourceSettings' + _REMOVE_RESOURCE = 'RemoveResourceSettings' + _SETTING_TYPE = 'VirtualSystemType' + + _VIRTUAL_SYSTEM_TYPE_REALIZED = 'Microsoft:Hyper-V:System:Realized' def setUp(self): + super(VMUtilsV2TestCase, self).setUp() self._vmutils = vmutilsv2.VMUtilsV2() self._vmutils._conn = mock.MagicMock() - super(VMUtilsV2TestCase, self).setUp() - - def _lookup_vm(self): - mock_vm = mock.MagicMock() - self._vmutils._lookup_vm_check = mock.MagicMock( - return_value=mock_vm) - mock_vm.path_.return_value = self._FAKE_VM_PATH - return mock_vm - - def test_create_vm(self): - mock_svc = self._vmutils._conn.Msvm_VirtualSystemManagementService()[0] - mock_svc.DefineSystem.return_value = (None, self._FAKE_JOB_PATH, - self._FAKE_RET_VAL) - - self._vmutils._get_wmi_obj = mock.MagicMock() - mock_vm = self._vmutils._get_wmi_obj.return_value - - mock_s = mock.MagicMock() - mock_s.VirtualSystemType = self._vmutils._VIRTUAL_SYSTEM_TYPE_REALIZED - mock_vm.associators.return_value = [mock_s] - - self._vmutils._set_vm_memory = mock.MagicMock() - self._vmutils._set_vm_vcpus = mock.MagicMock() - - self._vmutils.create_vm(self._FAKE_VM_NAME, self._FAKE_MEMORY_MB, - self._FAKE_VCPUS_NUM, False, - self._FAKE_DYNAMIC_MEMORY_RATIO) - - self.assertTrue(mock_svc.DefineSystem.called) - self._vmutils._set_vm_memory.assert_called_with( - mock_vm, mock_s, self._FAKE_MEMORY_MB, - self._FAKE_DYNAMIC_MEMORY_RATIO) - - self._vmutils._set_vm_vcpus.assert_called_with(mock_vm, mock_s, - self._FAKE_VCPUS_NUM, - False) - - def test_attach_ide_drive(self): - self._lookup_vm() - self._vmutils._get_vm_ide_controller = mock.MagicMock() - self._vmutils._get_new_resource_setting_data = mock.MagicMock() - self._vmutils._add_virt_resource = mock.MagicMock() - - self._vmutils.attach_ide_drive(self._FAKE_VM_NAME, - self._FAKE_CTRL_PATH, - self._FAKE_CTRL_ADDR, - self._FAKE_DRIVE_ADDR) - - self.assertTrue(self._vmutils._get_vm_ide_controller.called) - self.assertTrue(self._vmutils._get_new_resource_setting_data.called) - self.assertTrue(self._vmutils._add_virt_resource.called) - - def test_attach_volume_to_controller(self): - self._lookup_vm() - self._vmutils._add_virt_resource = mock.MagicMock() - - self._vmutils.attach_volume_to_controller(self._FAKE_VM_NAME, - self._FAKE_CTRL_PATH, - self._FAKE_CTRL_ADDR, - self._FAKE_MOUNTED_DISK_PATH) - - self.assertTrue(self._vmutils._add_virt_resource.called) - - def test_create_scsi_controller(self): - self._lookup_vm() - self._vmutils._add_virt_resource = mock.MagicMock() - - self._vmutils.create_scsi_controller(self._FAKE_VM_NAME) - - self.assertTrue(self._vmutils._add_virt_resource.called) - - def test_get_vm_storage_paths(self): - mock_vm = self._lookup_vm() - - mock_vmsettings = [mock.MagicMock()] - mock_vm.associators.return_value = mock_vmsettings - mock_sasds = [] - mock_sasd1 = mock.MagicMock() - mock_sasd1.ResourceSubType = self._vmutils._IDE_DISK_RES_SUB_TYPE - mock_sasd1.HostResource = [self._FAKE_VHD_PATH] - mock_sasd2 = mock.MagicMock() - mock_sasd2.ResourceSubType = self._vmutils._PHYS_DISK_RES_SUB_TYPE - mock_sasd2.HostResource = [self._FAKE_VOLUME_DRIVE_PATH] - mock_sasds.append(mock_sasd1) - mock_sasds.append(mock_sasd2) - mock_vmsettings[0].associators.return_value = mock_sasds - - storage = self._vmutils.get_vm_storage_paths(self._FAKE_VM_NAME) - (disk_files, volume_drives) = storage - - mock_vm.associators.assert_called_with( - wmi_result_class='Msvm_VirtualSystemSettingData') - mock_vmsettings[0].associators.assert_called_with( - wmi_result_class='Msvm_StorageAllocationSettingData') - self.assertEqual([self._FAKE_VHD_PATH], disk_files) - self.assertEqual([self._FAKE_VOLUME_DRIVE_PATH], volume_drives) - - def test_destroy(self): - self._lookup_vm() - + def test_modify_virt_resource(self): mock_svc = self._vmutils._conn.Msvm_VirtualSystemManagementService()[0] - mock_svc.DestroySystem.return_value = (self._FAKE_JOB_PATH, - self._FAKE_RET_VAL) - - self._vmutils.destroy_vm(self._FAKE_VM_NAME) - - mock_svc.DestroySystem.assert_called_with(self._FAKE_VM_PATH) - - def test_get_vm_state(self): - self._vmutils.get_vm_summary_info = mock.MagicMock( - return_value={'EnabledState': self._FAKE_ENABLED_STATE}) + mock_svc.ModifyResourceSettings.return_value = (self._FAKE_JOB_PATH, + mock.MagicMock(), + self._FAKE_RET_VAL) + mock_res_setting_data = mock.MagicMock() + mock_res_setting_data.GetText_.return_value = self._FAKE_RES_DATA - enabled_state = self._vmutils.get_vm_state(self._FAKE_VM_NAME) + self._vmutils._modify_virt_resource(mock_res_setting_data, + self._FAKE_VM_PATH) - self.assertEqual(self._FAKE_ENABLED_STATE, enabled_state) + mock_svc.ModifyResourceSettings.assert_called_with( + ResourceSettings=[self._FAKE_RES_DATA]) - def test_take_vm_snapshot(self): + @mock.patch.object(vmutilsv2, 'wmi', create=True) + @mock.patch.object(vmutilsv2.VMUtilsV2, 'check_ret_val') + def test_take_vm_snapshot(self, mock_check_ret_val, mock_wmi): self._lookup_vm() - mock_svc = self._vmutils._conn.Msvm_VirtualSystemSnapshotService()[0] + mock_svc = self._get_snapshot_service() mock_svc.CreateSnapshot.return_value = (self._FAKE_JOB_PATH, mock.MagicMock(), self._FAKE_RET_VAL) - vmutilsv2.wmi = mock.MagicMock() self._vmutils.take_vm_snapshot(self._FAKE_VM_NAME) @@ -175,70 +66,19 @@ AffectedSystem=self._FAKE_VM_PATH, SnapshotType=self._vmutils._SNAPSHOT_FULL) - def test_remove_vm_snapshot(self): - mock_svc = self._vmutils._conn.Msvm_VirtualSystemSnapshotService()[0] - mock_svc.DestroySnapshot.return_value = (self._FAKE_JOB_PATH, - self._FAKE_RET_VAL) - - self._vmutils.remove_vm_snapshot(self._FAKE_SNAPSHOT_PATH) + mock_check_ret_val.assert_called_once_with(self._FAKE_RET_VAL, + self._FAKE_JOB_PATH) - mock_svc.DestroySnapshot.assert_called_with(self._FAKE_SNAPSHOT_PATH) - - def test_set_nic_connection(self): + @mock.patch.object(vmutilsv2.VMUtilsV2, '_add_virt_resource') + @mock.patch.object(vmutilsv2.VMUtilsV2, '_get_new_setting_data') + @mock.patch.object(vmutilsv2.VMUtilsV2, '_get_nic_data_by_name') + def test_set_nic_connection(self, mock_get_nic_data, mock_get_new_sd, + mock_add_virt_res): self._lookup_vm() - - self._vmutils._get_nic_data_by_name = mock.MagicMock() - self._vmutils._add_virt_resource = mock.MagicMock() - - fake_eth_port = mock.MagicMock() - self._vmutils._get_new_setting_data = mock.MagicMock( - return_value=fake_eth_port) + fake_eth_port = mock_get_new_sd.return_value self._vmutils.set_nic_connection(self._FAKE_VM_NAME, None, None) - - self._vmutils._add_virt_resource.assert_called_with(fake_eth_port, - self._FAKE_VM_PATH) - - def test_add_virt_resource(self): - mock_svc = self._vmutils._conn.Msvm_VirtualSystemManagementService()[0] - mock_svc.AddResourceSettings.return_value = (self._FAKE_JOB_PATH, - mock.MagicMock(), - self._FAKE_RET_VAL) - mock_res_setting_data = mock.MagicMock() - mock_res_setting_data.GetText_.return_value = self._FAKE_RES_DATA - - self._vmutils._add_virt_resource(mock_res_setting_data, - self._FAKE_VM_PATH) - - mock_svc.AddResourceSettings.assert_called_with(self._FAKE_VM_PATH, - [self._FAKE_RES_DATA]) - - def test_modify_virt_resource(self): - mock_svc = self._vmutils._conn.Msvm_VirtualSystemManagementService()[0] - mock_svc.ModifyResourceSettings.return_value = (self._FAKE_JOB_PATH, - mock.MagicMock(), - self._FAKE_RET_VAL) - mock_res_setting_data = mock.MagicMock() - mock_res_setting_data.GetText_.return_value = self._FAKE_RES_DATA - - self._vmutils._modify_virt_resource(mock_res_setting_data, - self._FAKE_VM_PATH) - - mock_svc.ModifyResourceSettings.assert_called_with( - ResourceSettings=[self._FAKE_RES_DATA]) - - def test_remove_virt_resource(self): - mock_svc = self._vmutils._conn.Msvm_VirtualSystemManagementService()[0] - mock_svc.RemoveResourceSettings.return_value = (self._FAKE_JOB_PATH, - self._FAKE_RET_VAL) - mock_res_setting_data = mock.MagicMock() - mock_res_setting_data.path_.return_value = self._FAKE_RES_PATH - - self._vmutils._remove_virt_resource(mock_res_setting_data, - self._FAKE_VM_PATH) - - mock_svc.RemoveResourceSettings.assert_called_with( - [self._FAKE_RES_PATH]) + mock_add_virt_res.assert_called_with(fake_eth_port, self._FAKE_VM_PATH) @mock.patch('nova.virt.hyperv.vmutils.VMUtils._get_vm_disks') def test_enable_vm_metrics_collection(self, mock_get_vm_disks): @@ -267,3 +107,83 @@ MetricCollectionEnabled=self._vmutils._METRIC_ENABLED)) mock_svc.ControlMetrics.assert_has_calls(calls, any_order=True) + + def test_list_instance_notes(self): + vs = mock.MagicMock() + attrs = {'ElementName': 'fake_name', + 'Notes': ['4f54fb69-d3a2-45b7-bb9b-b6e6b3d893b3']} + vs.configure_mock(**attrs) + self._vmutils._conn.Msvm_VirtualSystemSettingData.return_value = [vs] + response = self._vmutils.list_instance_notes() + + self.assertEqual([(attrs['ElementName'], attrs['Notes'])], response) + self._vmutils._conn.Msvm_VirtualSystemSettingData.assert_called_with( + ['ElementName', 'Notes'], + VirtualSystemType=self._vmutils._VIRTUAL_SYSTEM_TYPE_REALIZED) + + @mock.patch('nova.virt.hyperv.vmutilsv2.VMUtilsV2.check_ret_val') + @mock.patch('nova.virt.hyperv.vmutilsv2.VMUtilsV2._get_wmi_obj') + def _test_create_vm_obj(self, mock_get_wmi_obj, mock_check_ret_val, + vm_path): + mock_vs_man_svc = mock.MagicMock() + mock_vs_data = mock.MagicMock() + mock_job = mock.MagicMock() + fake_job_path = 'fake job path' + fake_ret_val = 'fake return value' + _conn = self._vmutils._conn.Msvm_VirtualSystemSettingData + + mock_check_ret_val.return_value = mock_job + _conn.new.return_value = mock_vs_data + mock_vs_man_svc.DefineSystem.return_value = (fake_job_path, + vm_path, + fake_ret_val) + mock_job.associators.return_value = ['fake vm path'] + + response = self._vmutils._create_vm_obj(vs_man_svc=mock_vs_man_svc, + vm_name='fake vm', + notes='fake notes') + + if not vm_path: + mock_job.associators.assert_called_once_with( + self._vmutils._AFFECTED_JOB_ELEMENT_CLASS) + + _conn.new.assert_called_once_with() + self.assertEqual(mock_vs_data.ElementName, 'fake vm') + mock_vs_man_svc.DefineSystem.assert_called_once_with( + ResourceSettings=[], ReferenceConfiguration=None, + SystemSettings=mock_vs_data.GetText_(1)) + mock_check_ret_val.assert_called_once_with(fake_ret_val, fake_job_path) + + mock_get_wmi_obj.assert_called_with('fake vm path') + + self.assertEqual(mock_vs_data.Notes, 'fake notes') + self.assertEqual(response, mock_get_wmi_obj()) + + def test_create_vm_obj(self): + self._test_create_vm_obj(vm_path='fake vm path') + + def test_create_vm_obj_no_vm_path(self): + self._test_create_vm_obj(vm_path=None) + + def test_list_instances(self): + vs = mock.MagicMock() + attrs = {'ElementName': 'fake_name'} + vs.configure_mock(**attrs) + self._vmutils._conn.Msvm_VirtualSystemSettingData.return_value = [vs] + response = self._vmutils.list_instances() + + self.assertEqual([(attrs['ElementName'])], response) + self._vmutils._conn.Msvm_VirtualSystemSettingData.assert_called_with( + ['ElementName'], + VirtualSystemType=self._vmutils._VIRTUAL_SYSTEM_TYPE_REALIZED) + + def _get_snapshot_service(self): + return self._vmutils._conn.Msvm_VirtualSystemSnapshotService()[0] + + def _assert_add_resources(self, mock_svc): + getattr(mock_svc, self._ADD_RESOURCE).assert_called_with( + self._FAKE_VM_PATH, [self._FAKE_RES_DATA]) + + def _assert_remove_resources(self, mock_svc): + getattr(mock_svc, self._REMOVE_RESOURCE).assert_called_with( + [self._FAKE_RES_PATH]) diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/virt/libvirt/test_libvirt_config.py nova-2014.1.100+git201410062002~precise/nova/tests/virt/libvirt/test_libvirt_config.py --- nova-2014.1.100+git201408190132~precise/nova/tests/virt/libvirt/test_libvirt_config.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/virt/libvirt/test_libvirt_config.py 2014-10-07 00:02:46.000000000 +0000 @@ -235,8 +235,30 @@ x86_64 Penryn Intel + + + """) + + def test_only_uniq_cpu_featues(self): + obj = config.LibvirtConfigCPU() + obj.model = "Penryn" + obj.vendor = "Intel" + obj.arch = "x86_64" + + obj.add_feature(config.LibvirtConfigCPUFeature("mtrr")) + obj.add_feature(config.LibvirtConfigCPUFeature("apic")) + obj.add_feature(config.LibvirtConfigCPUFeature("apic")) + obj.add_feature(config.LibvirtConfigCPUFeature("mtrr")) + + xml = obj.to_xml() + self.assertXmlEqual(xml, """ + + x86_64 + Penryn + Intel + """) @@ -285,8 +307,8 @@ x86_64 Penryn Intel - + """) diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/virt/libvirt/test_libvirt.py nova-2014.1.100+git201410062002~precise/nova/tests/virt/libvirt/test_libvirt.py --- nova-2014.1.100+git201408190132~precise/nova/tests/virt/libvirt/test_libvirt.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/virt/libvirt/test_libvirt.py 2014-10-07 00:02:46.000000000 +0000 @@ -56,11 +56,13 @@ from nova.pci import pci_manager from nova import test from nova.tests import fake_block_device +from nova.tests import fake_instance from nova.tests import fake_network import nova.tests.image.fake from nova.tests import matchers from nova.tests.objects import test_pci_device from nova.tests.virt.libvirt import fake_libvirt_utils +from nova.tests.virt.libvirt import fakelibvirt from nova import utils from nova import version from nova.virt import configdrive @@ -2250,8 +2252,8 @@ cpu.model = "Opteron_G4" cpu.vendor = "AMD" - cpu.features.append(vconfig.LibvirtConfigGuestCPUFeature("tm2")) - cpu.features.append(vconfig.LibvirtConfigGuestCPUFeature("ht")) + cpu.add_feature(vconfig.LibvirtConfigGuestCPUFeature("tm2")) + cpu.add_feature(vconfig.LibvirtConfigGuestCPUFeature("ht")) caps = vconfig.LibvirtConfigCaps() caps.host = vconfig.LibvirtConfigCapsHost() @@ -2279,8 +2281,8 @@ self.assertEqual(conf.cpu.model, "Opteron_G4") self.assertEqual(conf.cpu.vendor, "AMD") self.assertEqual(len(conf.cpu.features), 2) - self.assertEqual(conf.cpu.features[0].name, "tm2") - self.assertEqual(conf.cpu.features[1].name, "ht") + self.assertEqual(conf.cpu.features.pop().name, "tm2") + self.assertEqual(conf.cpu.features.pop().name, "ht") def test_get_guest_cpu_config_custom_old(self): def get_lib_version_stub(): @@ -3321,6 +3323,85 @@ {"name": "fake-instance"}, "/dev/sda") + @mock.patch('nova.virt.libvirt.blockinfo.get_info_from_bdm') + @mock.patch('nova.virt.libvirt.driver.LibvirtDriver._lookup_by_name') + def test_attach_volume_with_vir_domain_affect_live_flag(self, + mock_lookup_by_name, mock_get_info): + conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) + instance = fake_instance.fake_instance_obj(mock.sentinel.ctx) + mock_dom = mock.MagicMock() + mock_lookup_by_name.return_value = mock_dom + + connection_info = {"driver_volume_type": "fake", + "data": {"device_path": "/fake", + "access_mode": "rw"}} + bdm = {'device_name': 'vdb', + 'disk_bus': 'fake-bus', + 'device_type': 'fake-type'} + disk_info = {'bus': bdm['disk_bus'], 'type': bdm['device_type'], + 'dev': 'vdb'} + mock_get_info.return_value = disk_info + mock_conf = mock.MagicMock() + flags = (fakelibvirt.VIR_DOMAIN_AFFECT_CONFIG | + fakelibvirt.VIR_DOMAIN_AFFECT_LIVE) + + with contextlib.nested( + mock.patch.object(conn, 'volume_driver_method', + return_value=mock_conf), + mock.patch.object(conn, 'set_cache_mode') + ) as (mock_volume_driver_method, mock_set_cache_mode): + for state in (power_state.RUNNING, power_state.PAUSED): + mock_dom.info.return_value = [state, 512, 512, 2, 1234, 5678] + + conn.attach_volume(self.context, connection_info, instance, + "/dev/vdb", disk_bus=bdm['disk_bus'], + device_type=bdm['device_type']) + + mock_lookup_by_name.assert_called_with(instance['name']) + mock_get_info.assert_called_with(CONF.libvirt.virt_type, bdm) + mock_volume_driver_method.assert_called_with( + 'connect_volume', connection_info, disk_info) + mock_set_cache_mode.assert_called_with(mock_conf) + mock_dom.attachDeviceFlags.assert_called_with( + mock_conf.to_xml(), flags) + + @mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_disk_xml') + @mock.patch('nova.virt.libvirt.driver.LibvirtDriver._lookup_by_name') + def test_detach_volume_with_vir_domain_affect_live_flag(self, + mock_lookup_by_name, mock_get_disk_xml): + conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) + instance = fake_instance.fake_instance_obj(mock.sentinel.ctx) + mock_dom = mock.MagicMock() + mock_xml = \ + """ + + + + + """ + mock_get_disk_xml.return_value = mock_xml + + connection_info = {"driver_volume_type": "fake", + "data": {"device_path": "/fake", + "access_mode": "rw"}} + flags = (fakelibvirt.VIR_DOMAIN_AFFECT_CONFIG | + fakelibvirt.VIR_DOMAIN_AFFECT_LIVE) + + with mock.patch.object(conn, 'volume_driver_method') as \ + mock_volume_driver_method: + for state in (power_state.RUNNING, power_state.PAUSED): + mock_dom.info.return_value = [state, 512, 512, 2, 1234, 5678] + mock_lookup_by_name.return_value = mock_dom + + conn.detach_volume(connection_info, instance, '/dev/vdc') + + mock_lookup_by_name.assert_called_with(instance['name']) + mock_get_disk_xml.assert_called_with(mock_dom.XMLDesc(0), + 'vdc') + mock_dom.detachDeviceFlags.assert_called_with(mock_xml, flags) + mock_volume_driver_method.assert_called_with( + 'disconnect_volume', connection_info, 'vdc') + def test_multi_nic(self): instance_data = dict(self.test_instance) network_info = _fake_network_info(self.stubs, 2) @@ -6951,6 +7032,25 @@ self.assertTrue( block_device_info['block_device_mapping'][0].save.called) + def test_create_propogates_exceptions(self): + self.flags(virt_type='lxc', group='libvirt') + + conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) + instance = instance_obj.Instance(id=1, uuid='fake-uuid') + + with contextlib.nested( + mock.patch.object(conn, 'plug_vifs'), + mock.patch.object(conn, 'firewall_driver'), + mock.patch.object(conn, '_create_domain', + side_effect=exception.NovaException), + mock.patch.object(conn, 'cleanup')) as ( + cleanup, firewall_driver, create, plug_vifs): + self.assertRaises(exception.NovaException, + conn._create_domain_and_network, + self.context, + 'xml', + instance, None) + def test_create_without_pause(self): self.flags(virt_type='lxc', group='libvirt') @@ -7082,6 +7182,93 @@ def test_create_with_network_events_non_neutron(self, is_neutron): self._test_create_with_network_events() + @mock.patch('nova.volume.encryptors.get_encryption_metadata') + @mock.patch('nova.virt.libvirt.blockinfo.get_info_from_bdm') + def test_create_with_bdm(self, get_info_from_bdm, get_encryption_metadata): + conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) + instance = fake_instance.fake_instance_obj(mock.sentinel.ctx) + mock_dom = mock.MagicMock() + mock_encryption_meta = mock.MagicMock() + get_encryption_metadata.return_value = mock_encryption_meta + + fake_xml = """ + + instance-00000001 + 1048576 + 1 + + + + + + + + + """ + fake_volume_id = "fake-volume-id" + connection_info = {"driver_volume_type": "fake", + "data": {"access_mode": "rw", + "volume_id": fake_volume_id}} + + def fake_getitem(*args, **kwargs): + fake_bdm = {'connection_info': connection_info, + 'mount_device': '/dev/vda'} + return fake_bdm.get(args[0]) + + mock_volume = mock.MagicMock() + mock_volume.__getitem__.side_effect = fake_getitem + bdi = {'block_device_mapping': [mock_volume]} + network_info = [network_model.VIF(id='1'), + network_model.VIF(id='2', active=True)] + disk_info = {'bus': 'virtio', 'type': 'file', + 'dev': 'vda'} + get_info_from_bdm.return_value = disk_info + + with contextlib.nested( + mock.patch.object(conn, 'volume_driver_method'), + mock.patch.object(conn, '_get_volume_encryptor'), + mock.patch.object(conn, 'plug_vifs'), + mock.patch.object(conn.firewall_driver, 'setup_basic_filtering'), + mock.patch.object(conn.firewall_driver, + 'prepare_instance_filter'), + mock.patch.object(conn, '_create_domain'), + mock.patch.object(conn.firewall_driver, 'apply_instance_filter'), + ) as (volume_driver_method, get_volume_encryptor, plug_vifs, + setup_basic_filtering, prepare_instance_filter, create_domain, + apply_instance_filter): + volume_driver_method.return_value = mock.MagicMock( + source_path='/path/fake-volume1') + create_domain.return_value = mock_dom + + domain = conn._create_domain_and_network(self.context, fake_xml, + instance, network_info, + block_device_info=bdi) + + get_info_from_bdm.assert_called_once_with(CONF.libvirt.virt_type, + mock_volume) + volume_driver_method.assert_called_once_with('connect_volume', + connection_info, + disk_info) + self.assertEqual(connection_info['data']['device_path'], + '/path/fake-volume1') + mock_volume.save.assert_called_once_with(self.context) + get_encryption_metadata.assert_called_once_with(self.context, + conn._volume_api, fake_volume_id, connection_info) + get_volume_encryptor.assert_called_once_with(connection_info, + mock_encryption_meta) + plug_vifs.assert_called_once_with(instance, network_info) + setup_basic_filtering.assert_called_once_with(instance, + network_info) + prepare_instance_filter.assert_called_once_with(instance, + network_info) + + event = utils.is_neutron() and CONF.vif_plugging_timeout + flag = event and libvirt.VIR_DOMAIN_START_PAUSED or 0 + create_domain.assert_called_once_with(fake_xml, instance=instance, + launch_flags=flag, + power_on=True) + self.assertEqual(mock_dom, domain) + def test_get_neutron_events(self): conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) network_info = [network_model.VIF(id='1'), @@ -8393,11 +8580,13 @@ block_device_info=None, inject_files=True): self.assertFalse(inject_files) - def fake_create_domain(xml, instance=None, launch_flags=0, - power_on=True): + def fake_create_domain_and_network( + context, xml, instance, network_info, + block_device_info=None, power_on=True, reboot=False, + vifs_already_plugged=False): self.fake_create_domain_called = True self.assertEqual(powered_on, power_on) - return mock.MagicMock() + self.assertTrue(vifs_already_plugged) def fake_enable_hairpin(instance): pass @@ -8419,8 +8608,8 @@ self.stubs.Set(self.libvirtconnection, 'plug_vifs', fake_plug_vifs) self.stubs.Set(self.libvirtconnection, '_create_image', fake_create_image) - self.stubs.Set(self.libvirtconnection, '_create_domain', - fake_create_domain) + self.stubs.Set(self.libvirtconnection, '_create_domain_and_network', + fake_create_domain_and_network) self.stubs.Set(self.libvirtconnection, '_enable_hairpin', fake_enable_hairpin) self.stubs.Set(utils, 'execute', fake_execute) @@ -8585,7 +8774,8 @@ self.mox.StubOutWithMock(libvirt_utils, 'get_instance_path') self.mox.StubOutWithMock(utils, 'execute') - libvirt_utils.get_instance_path(ins_ref).AndReturn('/fake/inst') + libvirt_utils.get_instance_path(ins_ref, + forceold=True).AndReturn('/fake/inst') utils.execute('rm', '-rf', '/fake/inst_resize', delay_on_retry=True, attempts=5) @@ -8620,7 +8810,8 @@ self.mox.StubOutWithMock(libvirt_utils, 'get_instance_path') self.mox.StubOutWithMock(utils, 'execute') - libvirt_utils.get_instance_path(ins_ref).AndReturn('/fake/inst') + libvirt_utils.get_instance_path(ins_ref, + forceold=True).AndReturn('/fake/inst') utils.execute('rm', '-rf', '/fake/inst_resize', delay_on_retry=True, attempts=5) diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/virt/libvirt/test_libvirt_volume.py nova-2014.1.100+git201410062002~precise/nova/tests/virt/libvirt/test_libvirt_volume.py --- nova-2014.1.100+git201408190132~precise/nova/tests/virt/libvirt/test_libvirt_volume.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/virt/libvirt/test_libvirt_volume.py 2014-10-07 00:02:46.000000000 +0000 @@ -896,15 +896,22 @@ mount_device = "vde" conf = libvirt_driver.connect_volume(connection_info, self.disk_info) + self.assertEqual('1234567890', + connection_info['data']['multipath_id']) tree = conf.format_dom() - dev_str = '/dev/disk/by-path/pci-0000:05:00.2-fc-0x%s-lun-1' % wwn - self.assertEqual(tree.get('type'), 'block') - self.assertEqual(tree.find('./source').get('dev'), - multipath_devname) + self.assertEqual('block', tree.get('type')) + self.assertEqual(multipath_devname, + tree.find('./source').get('dev')) + # Test the scenario where multipath_id is returned + libvirt_driver.disconnect_volume(connection_info, mount_device) + expected_commands = [] + self.assertEqual(expected_commands, self.executes) + # Test the scenario where multipath_id is not returned connection_info["data"]["devices"] = devices["devices"] + del connection_info["data"]["multipath_id"] libvirt_driver.disconnect_volume(connection_info, mount_device) expected_commands = [] - self.assertEqual(self.executes, expected_commands) + self.assertEqual(expected_commands, self.executes) # Should not work for anything other than string, unicode, and list connection_info = self.fibrechan_connection(self.vol, diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/virt/test_block_device.py nova-2014.1.100+git201410062002~precise/nova/tests/virt/test_block_device.py --- nova-2014.1.100+git201408190132~precise/nova/tests/virt/test_block_device.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/virt/test_block_device.py 2014-10-07 00:02:46.000000000 +0000 @@ -415,8 +415,8 @@ instance = {'id': 'fake_id', 'uuid': 'fake_uuid'} connector = {'ip': 'fake_ip', 'host': 'fake_host'} - connection_info = {'data': {}} - expected_conn_info = {'data': {}, + connection_info = {'data': {'multipath_id': 'fake_multipath_id'}} + expected_conn_info = {'data': {'multipath_id': 'fake_multipath_id'}, 'serial': 'fake-volume-id-2'} self.mox.StubOutWithMock(test_bdm._bdm_obj, 'save') diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/virt/test_events.py nova-2014.1.100+git201410062002~precise/nova/tests/virt/test_events.py --- nova-2014.1.100+git201408190132~precise/nova/tests/virt/test_events.py 1970-01-01 00:00:00.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/virt/test_events.py 2014-10-07 00:02:46.000000000 +0000 @@ -0,0 +1,36 @@ +# All Rights Reserved. +# +# 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 time + +from nova import test +from nova.virt import event + + +class TestEvents(test.NoDBTestCase): + + def test_event_repr(self): + t = time.time() + uuid = '1234' + lifecycle = event.EVENT_LIFECYCLE_RESUMED + + e = event.Event(t) + self.assertEqual(str(e), "" % t) + + e = event.InstanceEvent(uuid, timestamp=t) + self.assertEqual(str(e), "" % (t, uuid)) + + e = event.LifecycleEvent(uuid, lifecycle, timestamp=t) + self.assertEqual(str(e), " Resumed>" % + (t, uuid)) diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/virt/vmwareapi/test_driver_api.py nova-2014.1.100+git201410062002~precise/nova/tests/virt/vmwareapi/test_driver_api.py --- nova-2014.1.100+git201408190132~precise/nova/tests/virt/vmwareapi/test_driver_api.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/virt/vmwareapi/test_driver_api.py 2014-10-07 00:02:46.000000000 +0000 @@ -665,6 +665,18 @@ 'node': self.instance_node}) self._check_vm_info(info, power_state.RUNNING) + def test_spawn_root_size_0(self): + self._create_vm(instance_type='m1.micro') + info = self.conn.get_info({'uuid': self.uuid, + 'node': self.instance_node}) + self._check_vm_info(info, power_state.RUNNING) + cache = ('[%s] vmware_base/%s/%s.vmdk' % + (self.ds, 'fake_image_uuid', 'fake_image_uuid')) + gb_cache = ('[%s] vmware_base/%s/%s.0.vmdk' % + (self.ds, 'fake_image_uuid', 'fake_image_uuid')) + self.assertTrue(vmwareapi_fake.get_file(cache)) + self.assertFalse(vmwareapi_fake.get_file(gb_cache)) + def _spawn_with_delete_exception(self, fault=None): def fake_call_method(module, method, *args, **kwargs): @@ -2411,6 +2423,15 @@ mock_destroy.assert_called_once_with(self.context, "instance", [], None) + def test_get_instance_disk_info_is_implemented(self): + # Ensure that the method has been implemented in the driver + try: + disk_info = self.conn.get_instance_disk_info('fake_instance_name') + self.assertIsNone(disk_info) + except NotImplementedError: + self.fail("test_get_instance_disk_info() should not raise " + "NotImplementedError") + def test_destroy(self): self._create_vm() info = self.conn.get_info({'uuid': self.uuid, diff -Nru nova-2014.1.100+git201408190132~precise/nova/tests/volume/test_cinder.py nova-2014.1.100+git201410062002~precise/nova/tests/volume/test_cinder.py --- nova-2014.1.100+git201408190132~precise/nova/tests/volume/test_cinder.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/tests/volume/test_cinder.py 2014-10-07 00:02:46.000000000 +0000 @@ -86,6 +86,17 @@ self.assertRaises(exception.InvalidInput, self.api.create, self.ctx, 1, '', '') + @mock.patch('nova.volume.cinder.cinderclient') + def test_create_over_quota_failed(self, mock_cinderclient): + mock_cinderclient.return_value.volumes.create.side_effect = ( + cinder_exception.OverLimit(413)) + self.assertRaises(exception.OverQuota, self.api.create, self.ctx, + 1, '', '') + mock_cinderclient.return_value.volumes.create.assert_called_once_with( + 1, user_id=None, imageRef=None, availability_zone=None, + volume_type=None, display_description='', snapshot_id=None, + display_name='', project_id=None, metadata=None) + def test_get_all(self): cinder.cinderclient(self.ctx).AndReturn(self.cinderclient) cinder._untranslate_volume_summary_view(self.ctx, diff -Nru nova-2014.1.100+git201408190132~precise/nova/virt/block_device.py nova-2014.1.100+git201410062002~precise/nova/virt/block_device.py --- nova-2014.1.100+git201408190132~precise/nova/virt/block_device.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/virt/block_device.py 2014-10-07 00:02:46.000000000 +0000 @@ -19,6 +19,7 @@ from nova.objects import block_device as block_device_obj from nova.openstack.common import excutils from nova.openstack.common.gettextutils import _ +from nova.openstack.common.gettextutils import _LI from nova.openstack.common import jsonutils from nova.openstack.common import log as logging from nova.volume import encryptors @@ -209,6 +210,14 @@ except TypeError: self['connection_info'] = None + def _preserve_multipath_id(self, connection_info): + if self['connection_info'] and 'data' in self['connection_info']: + if 'multipath_id' in self['connection_info']['data']: + connection_info['data']['multipath_id'] =\ + self['connection_info']['data']['multipath_id'] + LOG.info(_LI('preserve multipath_id %s'), + connection_info['data']['multipath_id']) + @update_db def attach(self, context, instance, volume_api, virt_driver, do_check_attach=True, do_driver_attach=False): @@ -225,6 +234,7 @@ connector) if 'serial' not in connection_info: connection_info['serial'] = self.volume_id + self._preserve_multipath_id(connection_info) # If do_driver_attach is False, we will attach a volume to an instance # at boot time. So actual attach is done by instance creation code. @@ -267,6 +277,7 @@ connector) if 'serial' not in connection_info: connection_info['serial'] = self.volume_id + self._preserve_multipath_id(connection_info) self['connection_info'] = connection_info def save(self, context): diff -Nru nova-2014.1.100+git201408190132~precise/nova/virt/driver.py nova-2014.1.100+git201410062002~precise/nova/virt/driver.py --- nova-2014.1.100+git201408190132~precise/nova/virt/driver.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/virt/driver.py 2014-10-07 00:02:46.000000000 +0000 @@ -35,7 +35,8 @@ help='Driver to use for controlling virtualization. Options ' 'include: libvirt.LibvirtDriver, xenapi.XenAPIDriver, ' 'fake.FakeDriver, baremetal.BareMetalDriver, ' - 'vmwareapi.VMwareESXDriver, vmwareapi.VMwareVCDriver'), + 'vmwareapi.VMwareESXDriver, vmwareapi.VMwareVCDriver, ' + 'hyperv.HyperVDriver'), cfg.StrOpt('default_ephemeral_format', help='The default format an ephemeral_volume will be ' 'formatted with on creation.'), diff -Nru nova-2014.1.100+git201408190132~precise/nova/virt/event.py nova-2014.1.100+git201410062002~precise/nova/virt/event.py --- nova-2014.1.100+git201408190132~precise/nova/virt/event.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/virt/event.py 2014-10-07 00:02:46.000000000 +0000 @@ -22,11 +22,20 @@ import time +from nova.openstack.common.gettextutils import _ + EVENT_LIFECYCLE_STARTED = 0 EVENT_LIFECYCLE_STOPPED = 1 EVENT_LIFECYCLE_PAUSED = 2 EVENT_LIFECYCLE_RESUMED = 3 +NAMES = { + EVENT_LIFECYCLE_STARTED: _('Started'), + EVENT_LIFECYCLE_STOPPED: _('Stopped'), + EVENT_LIFECYCLE_PAUSED: _('Paused'), + EVENT_LIFECYCLE_RESUMED: _('Resumed') +} + class Event(object): """Base class for all events emitted by a hypervisor. @@ -47,6 +56,11 @@ def get_timestamp(self): return self.timestamp + def __repr__(self): + return "<%s: %s>" % ( + self.__class__.__name__, + self.timestamp) + class InstanceEvent(Event): """Base class for all instance events. @@ -65,6 +79,12 @@ def get_instance_uuid(self): return self.uuid + def __repr__(self): + return "<%s: %s, %s>" % ( + self.__class__.__name__, + self.timestamp, + self.uuid) + class LifecycleEvent(InstanceEvent): """Class for instance lifecycle state change events. @@ -83,3 +103,13 @@ def get_transition(self): return self.transition + + def get_name(self): + return NAMES.get(self.transition, _('Unknown')) + + def __repr__(self): + return "<%s: %s, %s => %s>" % ( + self.__class__.__name__, + self.timestamp, + self.uuid, + self.get_name()) diff -Nru nova-2014.1.100+git201408190132~precise/nova/virt/hyperv/constants.py nova-2014.1.100+git201410062002~precise/nova/virt/hyperv/constants.py --- nova-2014.1.100+git201408190132~precise/nova/virt/hyperv/constants.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/virt/hyperv/constants.py 2014-10-07 00:02:46.000000000 +0000 @@ -66,7 +66,14 @@ VM_SUMMARY_UPTIME = 105 IDE_DISK = "VHD" +IDE_DISK_FORMAT = IDE_DISK IDE_DVD = "DVD" +IDE_DVD_FORMAT = "ISO" + +DISK_FORMAT_MAP = { + IDE_DISK_FORMAT.lower(): IDE_DISK, + IDE_DVD_FORMAT.lower(): IDE_DVD +} DISK_FORMAT_VHD = "VHD" DISK_FORMAT_VHDX = "VHDX" diff -Nru nova-2014.1.100+git201408190132~precise/nova/virt/hyperv/driver.py nova-2014.1.100+git201410062002~precise/nova/virt/hyperv/driver.py --- nova-2014.1.100+git201408190132~precise/nova/virt/hyperv/driver.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/virt/hyperv/driver.py 2014-10-07 00:02:46.000000000 +0000 @@ -46,6 +46,9 @@ def init_host(self, host): pass + def list_instance_uuids(self): + return self._vmops.list_instance_uuids() + def list_instances(self): return self._vmops.list_instances() @@ -160,6 +163,9 @@ return self._livemigrationops.check_can_live_migrate_source( ctxt, instance_ref, dest_check_data) + def get_instance_disk_info(self, instance_name, block_device_info=None): + pass + def plug_vifs(self, instance, network_info): """Plug VIFs into networks.""" msg = _("VIF plugging is not supported by the Hyper-V driver.") diff -Nru nova-2014.1.100+git201408190132~precise/nova/virt/hyperv/__init__.py nova-2014.1.100+git201410062002~precise/nova/virt/hyperv/__init__.py --- nova-2014.1.100+git201408190132~precise/nova/virt/hyperv/__init__.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/virt/hyperv/__init__.py 2014-10-07 00:02:46.000000000 +0000 @@ -12,3 +12,7 @@ # 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.virt.hyperv import driver + +HyperVDriver = driver.HyperVDriver diff -Nru nova-2014.1.100+git201408190132~precise/nova/virt/hyperv/migrationops.py nova-2014.1.100+git201410062002~precise/nova/virt/hyperv/migrationops.py --- nova-2014.1.100+git201408190132~precise/nova/virt/hyperv/migrationops.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/virt/hyperv/migrationops.py 2014-10-07 00:02:46.000000000 +0000 @@ -18,10 +18,12 @@ """ import os +from nova import exception from nova.openstack.common import excutils from nova.openstack.common.gettextutils import _ from nova.openstack.common import log as logging from nova.openstack.common import units +from nova.virt import configdrive from nova.virt.hyperv import imagecache from nova.virt.hyperv import utilsfactory from nova.virt.hyperv import vmops @@ -101,11 +103,13 @@ curr_root_gb = instance['root_gb'] if new_root_gb < curr_root_gb: - raise vmutils.VHDResizeException( - _("Cannot resize the root disk to a smaller size. Current " - "size: %(curr_root_gb)s GB. Requested size: " - "%(new_root_gb)s GB") % - {'curr_root_gb': curr_root_gb, 'new_root_gb': new_root_gb}) + raise exception.InstanceFaultRollback( + vmutils.VHDResizeException( + _("Cannot resize the root disk to a smaller size. " + "Current size: %(curr_root_gb)s GB. Requested size: " + "%(new_root_gb)s GB") % + {'curr_root_gb': curr_root_gb, + 'new_root_gb': new_root_gb})) def migrate_disk_and_power_off(self, context, instance, dest, flavor, network_info, @@ -143,6 +147,17 @@ instance_name) self._pathutils.rename(revert_path, instance_path) + def _check_and_attach_config_drive(self, instance): + if configdrive.required_by(instance): + configdrive_path = self._pathutils.lookup_configdrive_path( + instance.name) + if configdrive_path: + self._vmops.attach_config_drive(instance, configdrive_path) + else: + raise vmutils.HyperVException( + _("Config drive is required by instance: %s, " + "but it does not exist.") % instance.name) + def finish_revert_migration(self, context, instance, network_info, block_device_info=None, power_on=True): LOG.debug(_("finish_revert_migration called"), instance=instance) @@ -160,6 +175,8 @@ self._vmops.create_instance(instance, network_info, block_device_info, root_vhd_path, eph_vhd_path) + self._check_and_attach_config_drive(instance) + if power_on: self._vmops.power_on(instance) @@ -268,5 +285,8 @@ self._vmops.create_instance(instance, network_info, block_device_info, root_vhd_path, eph_vhd_path) + + self._check_and_attach_config_drive(instance) + if power_on: self._vmops.power_on(instance) diff -Nru nova-2014.1.100+git201408190132~precise/nova/virt/hyperv/pathutils.py nova-2014.1.100+git201410062002~precise/nova/virt/hyperv/pathutils.py --- nova-2014.1.100+git201408190132~precise/nova/virt/hyperv/pathutils.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/virt/hyperv/pathutils.py 2014-10-07 00:02:46.000000000 +0000 @@ -19,6 +19,8 @@ from nova.openstack.common.gettextutils import _ from nova.openstack.common import log as logging from nova import utils +from nova.virt.hyperv import constants + from oslo.config import cfg LOG = logging.getLogger(__name__) @@ -131,6 +133,15 @@ def lookup_root_vhd_path(self, instance_name): return self._lookup_vhd_path(instance_name, self.get_root_vhd_path) + def lookup_configdrive_path(self, instance_name): + configdrive_path = None + for format_ext in constants.DISK_FORMAT_MAP: + test_path = self.get_configdrive_path(instance_name, format_ext) + if self.exists(test_path): + configdrive_path = test_path + break + return configdrive_path + def lookup_ephemeral_vhd_path(self, instance_name): return self._lookup_vhd_path(instance_name, self.get_ephemeral_vhd_path) @@ -139,6 +150,10 @@ instance_path = self.get_instance_dir(instance_name) return os.path.join(instance_path, 'root.' + format_ext.lower()) + def get_configdrive_path(self, instance_name, format_ext): + instance_path = self.get_instance_dir(instance_name) + return os.path.join(instance_path, 'configdrive.' + format_ext.lower()) + def get_ephemeral_vhd_path(self, instance_name, format_ext): instance_path = self.get_instance_dir(instance_name) return os.path.join(instance_path, 'ephemeral.' + format_ext.lower()) diff -Nru nova-2014.1.100+git201408190132~precise/nova/virt/hyperv/utilsfactory.py nova-2014.1.100+git201410062002~precise/nova/virt/hyperv/utilsfactory.py --- nova-2014.1.100+git201408190132~precise/nova/virt/hyperv/utilsfactory.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/virt/hyperv/utilsfactory.py 2014-10-07 00:02:46.000000000 +0000 @@ -15,6 +15,7 @@ from oslo.config import cfg +from nova.openstack.common.gettextutils import _ from nova.openstack.common import log as logging from nova.virt.hyperv import hostutils from nova.virt.hyperv import livemigrationutils @@ -57,20 +58,30 @@ return cls +def _get_virt_utils_class(v1_class, v2_class): + # The "root/virtualization" WMI namespace is no longer supported on + # Windows Server / Hyper-V Server 2012 R2 / Windows 8.1 + # (kernel version 6.3) or above. + if (CONF.hyperv.force_hyperv_utils_v1 and + get_hostutils().check_min_windows_version(6, 3)): + raise vmutils.HyperVException( + _('The "force_hyperv_utils_v1" option cannot be set to "True" ' + 'on Windows Server / Hyper-V Server 2012 R2 or above as the WMI ' + '"root/virtualization" namespace is no longer supported.')) + return _get_class(v1_class, v2_class, CONF.hyperv.force_hyperv_utils_v1) + + def get_vmutils(host='.'): - return _get_class(vmutils.VMUtils, vmutilsv2.VMUtilsV2, - CONF.hyperv.force_hyperv_utils_v1)(host) + return _get_virt_utils_class(vmutils.VMUtils, vmutilsv2.VMUtilsV2)(host) def get_vhdutils(): - return _get_class(vhdutils.VHDUtils, vhdutilsv2.VHDUtilsV2, - CONF.hyperv.force_hyperv_utils_v1)() + return _get_virt_utils_class(vhdutils.VHDUtils, vhdutilsv2.VHDUtilsV2)() def get_networkutils(): - return _get_class(networkutils.NetworkUtils, - networkutilsv2.NetworkUtilsV2, - CONF.hyperv.force_hyperv_utils_v1)() + return _get_virt_utils_class(networkutils.NetworkUtils, + networkutilsv2.NetworkUtilsV2)() def get_hostutils(): @@ -91,6 +102,5 @@ def get_rdpconsoleutils(): - return _get_class(rdpconsoleutils.RDPConsoleUtils, - rdpconsoleutilsv2.RDPConsoleUtilsV2, - CONF.hyperv.force_hyperv_utils_v1)() + return _get_virt_utils_class(rdpconsoleutils.RDPConsoleUtils, + rdpconsoleutilsv2.RDPConsoleUtilsV2)() diff -Nru nova-2014.1.100+git201408190132~precise/nova/virt/hyperv/vmops.py nova-2014.1.100+git201410062002~precise/nova/virt/hyperv/vmops.py --- nova-2014.1.100+git201408190132~precise/nova/virt/hyperv/vmops.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/virt/hyperv/vmops.py 2014-10-07 00:02:46.000000000 +0000 @@ -30,6 +30,7 @@ from nova.openstack.common import log as logging from nova.openstack.common import processutils from nova.openstack.common import units +from nova.openstack.common import uuidutils from nova import utils from nova.virt import configdrive from nova.virt.hyperv import constants @@ -114,6 +115,16 @@ "network_api_class: %s") % CONF.network_api_class) + def list_instance_uuids(self): + instance_uuids = [] + for (instance_name, notes) in self._vmutils.list_instance_notes(): + if notes and uuidutils.is_uuid_like(notes[0]): + instance_uuids.append(str(notes[0])) + else: + LOG.debug("Notes not found or not resembling a GUID for " + "instance: %s" % instance_name) + return instance_uuids + def list_instances(self): return self._vmutils.list_instances() @@ -224,8 +235,10 @@ root_vhd_path, eph_vhd_path) if configdrive.required_by(instance): - self._create_config_drive(instance, injected_files, - admin_password) + configdrive_path = self._create_config_drive(instance, + injected_files, + admin_password) + self.attach_config_drive(instance, configdrive_path) self.power_on(instance) except Exception: @@ -240,7 +253,8 @@ instance['memory_mb'], instance['vcpus'], CONF.hyperv.limit_cpu_features, - CONF.hyperv.dynamic_memory_ratio) + CONF.hyperv.dynamic_memory_ratio, + [instance['uuid']]) ctrl_disk_addr = 0 if root_vhd_path: @@ -305,7 +319,6 @@ e, instance=instance) if not CONF.hyperv.config_drive_cdrom: - drive_type = constants.IDE_DISK configdrive_path = os.path.join(instance_path, 'configdrive.vhd') utils.execute(CONF.hyperv.qemu_img_cmd, @@ -319,11 +332,19 @@ attempts=1) self._pathutils.remove(configdrive_path_iso) else: - drive_type = constants.IDE_DVD configdrive_path = configdrive_path_iso - self._vmutils.attach_ide_drive(instance['name'], configdrive_path, - 1, 0, drive_type) + return configdrive_path + + def attach_config_drive(self, instance, configdrive_path): + configdrive_ext = configdrive_path[(configdrive_path.rfind('.') + 1):] + # Do the attach here and if there is a certain file format that isn't + # supported in constants.DISK_FORMAT_MAP then bomb out. + try: + self._vmutils.attach_ide_drive(instance.name, configdrive_path, + 1, 0, constants.DISK_FORMAT_MAP[configdrive_ext]) + except KeyError: + raise exception.InvalidDiskFormat(disk_format=configdrive_ext) def _disconnect_volumes(self, volume_drives): for volume_drive in volume_drives: diff -Nru nova-2014.1.100+git201408190132~precise/nova/virt/hyperv/vmutils.py nova-2014.1.100+git201410062002~precise/nova/virt/hyperv/vmutils.py --- nova-2014.1.100+git201408190132~precise/nova/virt/hyperv/vmutils.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/virt/hyperv/vmutils.py 2014-10-07 00:02:46.000000000 +0000 @@ -80,6 +80,8 @@ 'Msvm_SyntheticEthernetPortSettingData' _AFFECTED_JOB_ELEMENT_CLASS = "Msvm_AffectedJobElement" + _VIRTUAL_SYSTEM_CURRENT_SETTINGS = 3 + _vm_power_states_map = {constants.HYPERV_VM_STATE_ENABLED: 2, constants.HYPERV_VM_STATE_DISABLED: 3, constants.HYPERV_VM_STATE_REBOOT: 10, @@ -96,12 +98,23 @@ def _init_hyperv_wmi_conn(self, host): self._conn = wmi.WMI(moniker='//%s/root/virtualization' % host) + def list_instance_notes(self): + instance_notes = [] + + for vs in self._conn.Msvm_VirtualSystemSettingData( + ['ElementName', 'Notes'], + SettingType=self._VIRTUAL_SYSTEM_CURRENT_SETTINGS): + instance_notes.append((vs.ElementName, + [v for v in vs.Notes.split('\n') if v])) + + return instance_notes + def list_instances(self): """Return the names of all the instances known to Hyper-V.""" - vm_names = [v.ElementName for v in - self._conn.Msvm_ComputerSystem(['ElementName'], - Caption="Virtual Machine")] - return vm_names + return [v.ElementName for v in + self._conn.Msvm_VirtualSystemSettingData( + ['ElementName'], + SettingType=self._VIRTUAL_SYSTEM_CURRENT_SETTINGS)] def get_vm_summary_info(self, vm_name): vm = self._lookup_vm_check(vm_name) @@ -130,7 +143,13 @@ if si.UpTime is not None: up_time = long(si.UpTime) - enabled_state = self._enabled_states_map[si.EnabledState] + # Nova requires a valid state to be returned. Hyper-V has more + # states than Nova, typically intermediate ones and since there is + # no direct mapping for those, ENABLED is the only reasonable option + # considering that in all the non mappable states the instance + # is running. + enabled_state = self._enabled_states_map.get(si.EnabledState, + constants.HYPERV_VM_STATE_ENABLED) summary_info_dict = {'NumberOfProcessors': si.NumberOfProcessors, 'EnabledState': enabled_state, @@ -216,12 +235,12 @@ raise HyperVAuthorizationException(msg) def create_vm(self, vm_name, memory_mb, vcpus_num, limit_cpu_features, - dynamic_memory_ratio): + dynamic_memory_ratio, notes=None): """Creates a VM.""" vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] LOG.debug(_('Creating VM %s'), vm_name) - vm = self._create_vm_obj(vs_man_svc, vm_name) + vm = self._create_vm_obj(vs_man_svc, vm_name, notes) vmsetting = self._get_vm_setting_data(vm) @@ -231,16 +250,30 @@ LOG.debug(_('Set vCPUs for vm %s'), vm_name) self._set_vm_vcpus(vm, vmsetting, vcpus_num, limit_cpu_features) - def _create_vm_obj(self, vs_man_svc, vm_name): + def _create_vm_obj(self, vs_man_svc, vm_name, notes): vs_gs_data = self._conn.Msvm_VirtualSystemGlobalSettingData.new() vs_gs_data.ElementName = vm_name - (job_path, + (vm_path, + job_path, ret_val) = vs_man_svc.DefineVirtualSystem([], None, - vs_gs_data.GetText_(1))[1:] + vs_gs_data.GetText_(1)) self.check_ret_val(ret_val, job_path) - return self._lookup_vm_check(vm_name) + vm = self._get_wmi_obj(vm_path) + + if notes: + vmsetting = self._get_vm_setting_data(vm) + vmsetting.Notes = '\n'.join(notes) + self._modify_virtual_system(vs_man_svc, vm_path, vmsetting) + + return self._get_wmi_obj(vm_path) + + def _modify_virtual_system(self, vs_man_svc, vm_path, vmsetting): + (job_path, ret_val) = vs_man_svc.ModifyVirtualSystem( + ComputerSystem=vm_path, + SystemSettingData=vmsetting.GetText_(1))[1:] + self.check_ret_val(ret_val, job_path) def get_vm_scsi_controller(self, vm_name): vm = self._lookup_vm_check(vm_name) diff -Nru nova-2014.1.100+git201408190132~precise/nova/virt/hyperv/vmutilsv2.py nova-2014.1.100+git201410062002~precise/nova/virt/hyperv/vmutilsv2.py --- nova-2014.1.100+git201408190132~precise/nova/virt/hyperv/vmutilsv2.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/virt/hyperv/vmutilsv2.py 2014-10-07 00:02:46.000000000 +0000 @@ -69,9 +69,27 @@ def _init_hyperv_wmi_conn(self, host): self._conn = wmi.WMI(moniker='//%s/root/virtualization/v2' % host) - def _create_vm_obj(self, vs_man_svc, vm_name): + def list_instance_notes(self): + instance_notes = [] + + for vs in self._conn.Msvm_VirtualSystemSettingData( + ['ElementName', 'Notes'], + VirtualSystemType=self._VIRTUAL_SYSTEM_TYPE_REALIZED): + instance_notes.append((vs.ElementName, [v for v in vs.Notes if v])) + + return instance_notes + + def list_instances(self): + """Return the names of all the instances known to Hyper-V.""" + return [v.ElementName for v in + self._conn.Msvm_VirtualSystemSettingData( + ['ElementName'], + VirtualSystemType=self._VIRTUAL_SYSTEM_TYPE_REALIZED)] + + def _create_vm_obj(self, vs_man_svc, vm_name, notes): vs_data = self._conn.Msvm_VirtualSystemSettingData.new() vs_data.ElementName = vm_name + vs_data.Notes = notes (job_path, vm_path, diff -Nru nova-2014.1.100+git201408190132~precise/nova/virt/hyperv/volumeops.py nova-2014.1.100+git201410062002~precise/nova/virt/hyperv/volumeops.py --- nova-2014.1.100+git201408190132~precise/nova/virt/hyperv/volumeops.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/virt/hyperv/volumeops.py 2014-10-07 00:02:46.000000000 +0000 @@ -67,8 +67,12 @@ self._default_root_device = 'vda' def ebs_root_in_block_devices(self, block_device_info): - return self._volutils.volume_in_mapping(self._default_root_device, - block_device_info) + if block_device_info: + root_device = block_device_info.get('root_device_name') + if not root_device: + root_device = self._default_root_device + return self._volutils.volume_in_mapping(root_device, + block_device_info) def attach_volumes(self, block_device_info, instance_name, ebs_root): mapping = driver.block_device_info_get_mapping(block_device_info) diff -Nru nova-2014.1.100+git201408190132~precise/nova/virt/libvirt/config.py nova-2014.1.100+git201410062002~precise/nova/virt/libvirt/config.py --- nova-2014.1.100+git201408190132~precise/nova/virt/libvirt/config.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/virt/libvirt/config.py 2014-10-07 00:02:46.000000000 +0000 @@ -250,6 +250,15 @@ return ft + def __eq__(self, obj): + return obj.name == self.name + + def __ne__(self, obj): + return obj.name != self.name + + def __hash__(self): + return hash(self.name) + class LibvirtConfigCPU(LibvirtConfigObject): @@ -265,7 +274,7 @@ self.cores = None self.threads = None - self.features = [] + self.features = set() def parse_dom(self, xmldoc): super(LibvirtConfigCPU, self).parse_dom(xmldoc) @@ -305,13 +314,14 @@ top.set("threads", str(self.threads)) cpu.append(top) - for f in self.features: + # sorting the features to allow more predictable tests + for f in sorted(self.features, key=lambda x: x.name): cpu.append(f.format_dom()) return cpu def add_feature(self, feat): - self.features.append(feat) + self.features.add(feat) class LibvirtConfigGuestCPUFeature(LibvirtConfigCPUFeature): diff -Nru nova-2014.1.100+git201408190132~precise/nova/virt/libvirt/driver.py nova-2014.1.100+git201410062002~precise/nova/virt/libvirt/driver.py --- nova-2014.1.100+git201408190132~precise/nova/virt/libvirt/driver.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/virt/libvirt/driver.py 2014-10-07 00:02:46.000000000 +0000 @@ -1169,7 +1169,15 @@ return connector def _cleanup_resize(self, instance, network_info): - target = libvirt_utils.get_instance_path(instance) + "_resize" + # NOTE(wangpan): we get the pre-grizzly instance path firstly, + # so the backup dir of pre-grizzly instance can + # be deleted correctly with grizzly or later nova. + pre_grizzly_name = libvirt_utils.get_instance_path(instance, + forceold=True) + target = pre_grizzly_name + '_resize' + if not os.path.exists(target): + target = libvirt_utils.get_instance_path(instance) + '_resize' + if os.path.exists(target): # Deletion can fail over NFS, so retry the deletion as required. # Set maximum attempt as 5, most test can remove the directory @@ -1239,7 +1247,7 @@ # affect live if the domain is running. flags = libvirt.VIR_DOMAIN_AFFECT_CONFIG state = LIBVIRT_POWER_STATE[virt_dom.info()[0]] - if state == power_state.RUNNING: + if state in (power_state.RUNNING, power_state.PAUSED): flags |= libvirt.VIR_DOMAIN_AFFECT_LIVE # cache device_path in connection_info -- required by encryptors @@ -1371,7 +1379,7 @@ # affect live if the domain is running. flags = libvirt.VIR_DOMAIN_AFFECT_CONFIG state = LIBVIRT_POWER_STATE[virt_dom.info()[0]] - if state == power_state.RUNNING: + if state in (power_state.RUNNING, power_state.PAUSED): flags |= libvirt.VIR_DOMAIN_AFFECT_LIVE virt_dom.detachDeviceFlags(xml, flags) @@ -2909,7 +2917,7 @@ for hostfeat in hostcpu.features: guestfeat = vconfig.LibvirtConfigGuestCPUFeature(hostfeat.name) guestfeat.policy = "require" - guestcpu.features.append(guestfeat) + guestcpu.add_feature(guestfeat) return guestcpu @@ -3614,7 +3622,7 @@ '%(event)s for instance %(uuid)s'), {'event': event_name, 'uuid': instance.uuid}) if CONF.vif_plugging_is_fatal: - raise exception.NovaException() + raise exception.VirtualInterfaceCreateException() def _get_neutron_events(self, network_info): # NOTE(danms): We need to collect any VIFs that are currently @@ -3646,12 +3654,13 @@ disk_info) # cache device_path in connection_info -- required by encryptors - if (not reboot and 'data' in connection_info and - 'volume_id' in connection_info['data']): + if 'data' in connection_info: connection_info['data']['device_path'] = conf.source_path vol['connection_info'] = connection_info vol.save(context) + if (not reboot and 'data' in connection_info and + 'volume_id' in connection_info['data']): volume_id = connection_info['data']['volume_id'] encryption = encryptors.get_encryption_metadata( context, self._volume_api, volume_id, connection_info) @@ -3687,14 +3696,14 @@ self.firewall_driver.apply_instance_filter(instance, network_info) - except exception.NovaException: + except exception.VirtualInterfaceCreateException: # Neutron reported failure and we didn't swallow it, so # bail here - if domain: - domain.destroy() - self.cleanup(context, instance, network_info=network_info, - block_device_info=block_device_info) - raise exception.VirtualInterfaceCreateException() + with excutils.save_and_reraise_exception(): + if domain: + domain.destroy() + self.cleanup(context, instance, network_info=network_info, + block_device_info=block_device_info) except eventlet.timeout.Timeout: # We never heard from Neutron LOG.warn(_('Timeout waiting for vif plugging callback for ' @@ -5045,7 +5054,8 @@ block_device_info=block_device_info, write_to_disk=True) self._create_domain_and_network(context, xml, instance, network_info, - block_device_info, power_on) + block_device_info, power_on, + vifs_already_plugged=True) if power_on: timer = loopingcall.FixedIntervalLoopingCall( self._wait_for_running, diff -Nru nova-2014.1.100+git201408190132~precise/nova/virt/libvirt/imagebackend.py nova-2014.1.100+git201410062002~precise/nova/virt/libvirt/imagebackend.py --- nova-2014.1.100+git201408190132~precise/nova/virt/libvirt/imagebackend.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/virt/libvirt/imagebackend.py 2014-10-07 00:02:46.000000000 +0000 @@ -538,7 +538,7 @@ class Rbd(Image): def __init__(self, instance=None, disk_name=None, path=None, **kwargs): - super(Rbd, self).__init__("block", "rbd", is_block_dev=True) + super(Rbd, self).__init__("block", "rbd", is_block_dev=False) if path: try: self.rbd_name = path.split('/')[1] diff -Nru nova-2014.1.100+git201408190132~precise/nova/virt/libvirt/volume.py nova-2014.1.100+git201410062002~precise/nova/virt/libvirt/volume.py --- nova-2014.1.100+git201408190132~precise/nova/virt/libvirt/volume.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/virt/libvirt/volume.py 2014-10-07 00:02:46.000000000 +0000 @@ -27,6 +27,7 @@ from nova import exception from nova.openstack.common.gettextutils import _ +from nova.openstack.common.gettextutils import _LW from nova.openstack.common import log as logging from nova.openstack.common import loopingcall from nova.openstack.common import processutils @@ -993,7 +994,6 @@ """Detach the volume from instance_name.""" super(LibvirtFibreChannelVolumeDriver, self).disconnect_volume(connection_info, mount_device) - devices = connection_info['data']['devices'] # If this is a multipath device, we need to search again # and make sure we remove all the devices. Some of them @@ -1003,6 +1003,11 @@ mdev_info = linuxscsi.find_multipath_device(multipath_id) devices = mdev_info['devices'] LOG.debug(_("devices to remove = %s"), devices) + else: + # only needed when multipath-tools work improperly + devices = connection_info['data'].get('devices', []) + LOG.warn(_LW("multipath-tools probably work improperly. " + "devices to remove = %s.") % devices) # There may have been more than 1 device mounted # by the kernel for this volume. We have to remove diff -Nru nova-2014.1.100+git201408190132~precise/nova/virt/vmwareapi/driver.py nova-2014.1.100+git201410062002~precise/nova/virt/vmwareapi/driver.py --- nova-2014.1.100+git201408190132~precise/nova/virt/vmwareapi/driver.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/virt/vmwareapi/driver.py 2014-10-07 00:02:46.000000000 +0000 @@ -473,6 +473,9 @@ """Clean up destination node after a failed live migration.""" self.destroy(context, instance, network_info, block_device_info) + def get_instance_disk_info(self, instance_name, block_device_info=None): + pass + def get_vnc_console(self, context, instance): """Return link to instance's VNC console using vCenter logic.""" # In this situation, ESXi and vCenter require different diff -Nru nova-2014.1.100+git201408190132~precise/nova/virt/vmwareapi/vmops.py nova-2014.1.100+git201410062002~precise/nova/virt/vmwareapi/vmops.py --- nova-2014.1.100+git201408190132~precise/nova/virt/vmwareapi/vmops.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/virt/vmwareapi/vmops.py 2014-10-07 00:02:46.000000000 +0000 @@ -567,9 +567,13 @@ root_vmdk_path, dc_info.ref) else: upload_folder = '%s/%s' % (self._base_folder, upload_name) - root_vmdk_name = "%s/%s.%s.vmdk" % (upload_folder, - upload_name, - root_gb) + if root_gb: + root_vmdk_name = "%s/%s.%s.vmdk" % (upload_folder, + upload_name, + root_gb) + else: + root_vmdk_name = "%s/%s.vmdk" % (upload_folder, + upload_name) root_vmdk_path = ds_util.build_datastore_path( data_store_name, root_vmdk_name) diff -Nru nova-2014.1.100+git201408190132~precise/nova/volume/cinder.py nova-2014.1.100+git201410062002~precise/nova/volume/cinder.py --- nova-2014.1.100+git201408190132~precise/nova/volume/cinder.py 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova/volume/cinder.py 2014-10-07 00:02:46.000000000 +0000 @@ -302,6 +302,8 @@ try: item = cinderclient(context).volumes.create(size, **kwargs) return _untranslate_volume_summary_view(context, item) + except cinder_exception.OverLimit: + raise exception.OverQuota(overs='volumes') except cinder_exception.BadRequest as e: raise exception.InvalidInput(reason=unicode(e)) diff -Nru nova-2014.1.100+git201408190132~precise/nova.egg-info/requires.txt nova-2014.1.100+git201410062002~precise/nova.egg-info/requires.txt --- nova-2014.1.100+git201408190132~precise/nova.egg-info/requires.txt 2014-08-19 05:32:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova.egg-info/requires.txt 2014-10-07 00:03:57.000000000 +0000 @@ -12,7 +12,7 @@ greenlet>=0.3.2 PasteDeploy>=1.5.0 Paste -sqlalchemy-migrate>=0.8.2,!=0.8.4 +sqlalchemy-migrate>=0.8.2,!=0.8.4,!=0.9.2 netaddr>=0.7.6 suds>=0.4 paramiko>=1.9.0 diff -Nru nova-2014.1.100+git201408190132~precise/nova.egg-info/SOURCES.txt nova-2014.1.100+git201410062002~precise/nova.egg-info/SOURCES.txt --- nova-2014.1.100+git201408190132~precise/nova.egg-info/SOURCES.txt 2014-08-19 05:32:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/nova.egg-info/SOURCES.txt 2014-10-07 00:03:57.000000000 +0000 @@ -1562,6 +1562,7 @@ nova/db/sqlalchemy/migrate_repo/versions/231_add_ephemeral_key_uuid.py nova/db/sqlalchemy/migrate_repo/versions/232_drop_dump_tables.py nova/db/sqlalchemy/migrate_repo/versions/233_add_stats_in_compute_nodes.py +nova/db/sqlalchemy/migrate_repo/versions/234_add_expire_reservations_index.py nova/db/sqlalchemy/migrate_repo/versions/__init__.py nova/hacking/__init__.py nova/hacking/checks.py @@ -2130,7 +2131,6 @@ nova/tests/integrated/test_api_samples.py nova/tests/integrated/test_extensions.py nova/tests/integrated/test_login.py -nova/tests/integrated/test_multiprocess_api.py nova/tests/integrated/test_servers.py nova/tests/integrated/test_xml.py nova/tests/integrated/api/__init__.py @@ -3362,6 +3362,7 @@ nova/tests/virt/test_block_device.py nova/tests/virt/test_cpu.py nova/tests/virt/test_driver.py +nova/tests/virt/test_events.py nova/tests/virt/test_imagecache.py nova/tests/virt/test_images.py nova/tests/virt/test_virt.py @@ -3396,11 +3397,15 @@ nova/tests/virt/hyperv/db_fakes.py nova/tests/virt/hyperv/fake.py nova/tests/virt/hyperv/test_hypervapi.py +nova/tests/virt/hyperv/test_migrationops.py nova/tests/virt/hyperv/test_networkutilsv2.py +nova/tests/virt/hyperv/test_pathutils.py nova/tests/virt/hyperv/test_rdpconsoleutils.py nova/tests/virt/hyperv/test_rdpconsoleutilsv2.py +nova/tests/virt/hyperv/test_utilsfactory.py nova/tests/virt/hyperv/test_vhdutils.py nova/tests/virt/hyperv/test_vhdutilsv2.py +nova/tests/virt/hyperv/test_vmops.py nova/tests/virt/hyperv/test_vmutils.py nova/tests/virt/hyperv/test_vmutilsv2.py nova/tests/virt/hyperv/test_volumeutils.py diff -Nru nova-2014.1.100+git201408190132~precise/requirements.txt nova-2014.1.100+git201410062002~precise/requirements.txt --- nova-2014.1.100+git201408190132~precise/requirements.txt 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/requirements.txt 2014-10-07 00:02:46.000000000 +0000 @@ -13,7 +13,7 @@ greenlet>=0.3.2 PasteDeploy>=1.5.0 Paste -sqlalchemy-migrate>=0.8.2,!=0.8.4 +sqlalchemy-migrate>=0.8.2,!=0.8.4,!=0.9.2 netaddr>=0.7.6 suds>=0.4 paramiko>=1.9.0 diff -Nru nova-2014.1.100+git201408190132~precise/setup.cfg nova-2014.1.100+git201410062002~precise/setup.cfg --- nova-2014.1.100+git201408190132~precise/setup.cfg 2014-08-19 05:32:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/setup.cfg 2014-10-07 00:03:58.000000000 +0000 @@ -1,6 +1,6 @@ [metadata] name = nova -version = 2014.1.2 +version = 2014.1.4 summary = Cloud computing fabric controller description-file = README.rst diff -Nru nova-2014.1.100+git201408190132~precise/tox.ini nova-2014.1.100+git201410062002~precise/tox.ini --- nova-2014.1.100+git201408190132~precise/tox.ini 2014-08-19 05:31:53.000000000 +0000 +++ nova-2014.1.100+git201410062002~precise/tox.ini 2014-10-07 00:02:46.000000000 +0000 @@ -58,4 +58,4 @@ [hacking] local-check-factory = nova.hacking.checks.factory -import_exceptions = nova.openstack.common.gettextutils._ +import_exceptions = nova.openstack.common.gettextutils