From 19d4c3a6f411b3b96d4a3dffc16b9b272a01971f Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Sun, 5 Sep 2010 05:33:56 +0100 Subject: Bug #630636: XenAPI VM destroy fails when the VM is still running When destroying a VM using the XenAPI backend, if the VM is still running (the usual case) the destroy fails. It needs to be powered-off first. --- nova/virt/xenapi.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/nova/virt/xenapi.py b/nova/virt/xenapi.py index b44ac383a..04069e459 100644 --- a/nova/virt/xenapi.py +++ b/nova/virt/xenapi.py @@ -274,9 +274,19 @@ class XenAPIConnection(object): def destroy(self, instance): vm = yield self._lookup(instance.name) if vm is None: - raise Exception('instance not present %s' % instance.name) - task = yield self._call_xenapi('Async.VM.destroy', vm) - yield self._wait_for_task(task) + # Don't complain, just return. This lets us clean up instances + # that have already disappeared from the underlying platform. + defer.returnValue(None) + try: + task = yield self._call_xenapi('Async.VM.hard_shutdown', vm) + yield self._wait_for_task(task) + except Exception, exn: + logging.warn(exn) + try: + task = yield self._call_xenapi('Async.VM.destroy', vm) + yield self._wait_for_task(task) + except Exception, exn: + logging.warn(exn) def get_info(self, instance_id): vm = self._lookup_blocking(instance_id) -- cgit From b049c032a9f950d67bbfe709802288a7fe28bdd6 Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Sun, 5 Sep 2010 05:56:53 +0100 Subject: Bug #630640: Duplicated power state constants Remove power state constants that have ended up duplicated following a bad merge. They were moved from nova.compute.node.Instance into nova.compute.power_state at the same time that Instance was moved into nova.compute.service. We've ended up with these constants in both places. Remove the ones from service, in favour of the ones in power_state. --- nova/compute/service.py | 8 -------- nova/tests/cloud_unittest.py | 3 ++- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/nova/compute/service.py b/nova/compute/service.py index e59f3fb34..3321c2c00 100644 --- a/nova/compute/service.py +++ b/nova/compute/service.py @@ -224,14 +224,6 @@ class ProductCode(object): class Instance(object): - NOSTATE = 0x00 - RUNNING = 0x01 - BLOCKED = 0x02 - PAUSED = 0x03 - SHUTDOWN = 0x04 - SHUTOFF = 0x05 - CRASHED = 0x06 - def __init__(self, conn, name, data): """ spawn an instance with a given name """ self._conn = conn diff --git a/nova/tests/cloud_unittest.py b/nova/tests/cloud_unittest.py index 900ff5a97..19aa23b9e 100644 --- a/nova/tests/cloud_unittest.py +++ b/nova/tests/cloud_unittest.py @@ -28,6 +28,7 @@ from nova import flags from nova import rpc from nova import test from nova.auth import manager +from nova.compute import power_state from nova.compute import service from nova.endpoint import api from nova.endpoint import cloud @@ -95,7 +96,7 @@ class CloudTestCase(test.BaseTestCase): rv = yield defer.succeed(time.sleep(1)) info = self.cloud._get_instance(instance['instance_id']) logging.debug(info['state']) - if info['state'] == node.Instance.RUNNING: + if info['state'] == power_state.RUNNING: break self.assert_(rv) -- cgit From f1e45e3294622e22e6044027c1d2514f107d6134 Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Fri, 10 Sep 2010 10:56:22 +0100 Subject: Change "exn" to "exc" to fit with the common style. --- nova/virt/xenapi.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/nova/virt/xenapi.py b/nova/virt/xenapi.py index 04069e459..1c6de4403 100644 --- a/nova/virt/xenapi.py +++ b/nova/virt/xenapi.py @@ -280,13 +280,13 @@ class XenAPIConnection(object): try: task = yield self._call_xenapi('Async.VM.hard_shutdown', vm) yield self._wait_for_task(task) - except Exception, exn: - logging.warn(exn) + except Exception, exc: + logging.warn(exc) try: task = yield self._call_xenapi('Async.VM.destroy', vm) yield self._wait_for_task(task) - except Exception, exn: - logging.warn(exn) + except Exception, exc: + logging.warn(exc) def get_info(self, instance_id): vm = self._lookup_blocking(instance_id) @@ -340,9 +340,9 @@ class XenAPIConnection(object): error_info) deferred.errback(XenAPI.Failure(error_info)) #logging.debug('Polling task %s done.', task) - except Exception, exn: - logging.warn(exn) - deferred.errback(exn) + except Exception, exc: + logging.warn(exc) + deferred.errback(exc) @utils.deferredToThread def _call_xenapi(self, method, *args): @@ -368,21 +368,21 @@ class XenAPIConnection(object): def _unwrap_plugin_exceptions(func, *args, **kwargs): try: return func(*args, **kwargs) - except XenAPI.Failure, exn: - logging.debug("Got exception: %s", exn) - if (len(exn.details) == 4 and - exn.details[0] == 'XENAPI_PLUGIN_EXCEPTION' and - exn.details[2] == 'Failure'): + except XenAPI.Failure, exc: + logging.debug("Got exception: %s", exc) + if (len(exc.details) == 4 and + exc.details[0] == 'XENAPI_PLUGIN_EXCEPTION' and + exc.details[2] == 'Failure'): params = None try: - params = eval(exn.details[3]) + params = eval(exc.details[3]) except: - raise exn + raise exc raise XenAPI.Failure(params) else: raise - except xmlrpclib.ProtocolError, exn: - logging.debug("Got exception: %s", exn) + except xmlrpclib.ProtocolError, exc: + logging.debug("Got exception: %s", exc) raise -- cgit From cb13f09d7fe886bc8340770ff8c7011b6dbab0db Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 13 Sep 2010 09:03:14 +0200 Subject: Move vol.destroy() call out of the _check method in test_multiple_volume_race_condition test and into a callback of the DeferredList. This should fix the intermittent failure of that test. I /think/ test_too_many_volumes's failure was caused by test_multiple_volume_race_condition failure, since I have not been able to reproduce its failure after fixing this one. --- nova/tests/volume_unittest.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/nova/tests/volume_unittest.py b/nova/tests/volume_unittest.py index 2a07afe69..540b71585 100644 --- a/nova/tests/volume_unittest.py +++ b/nova/tests/volume_unittest.py @@ -128,7 +128,6 @@ class VolumeTestCase(test.TrialTestCase): volume_service.get_volume, volume_id) - @defer.inlineCallbacks def test_multiple_volume_race_condition(self): vol_size = "5" user_id = "fake" @@ -137,17 +136,28 @@ class VolumeTestCase(test.TrialTestCase): def _check(volume_id): vol = volume_service.get_volume(volume_id) shelf_blade = '%s.%s' % (vol['shelf_id'], vol['blade_id']) - self.assert_(shelf_blade not in shelf_blades) + self.assertTrue(shelf_blade not in shelf_blades, + "Same shelf/blade tuple came back twice") shelf_blades.append(shelf_blade) logging.debug("got %s" % shelf_blade) - vol.destroy() + return vol deferreds = [] for i in range(5): d = self.volume.create_volume(vol_size, user_id, project_id) d.addCallback(_check) d.addErrback(self.fail) deferreds.append(d) - yield defer.DeferredList(deferreds) + def destroy_volumes(retvals): + overall_succes = True + for success, volume in retvals: + if not success: + overall_succes = False + else: + volume.destroy() + self.assertTrue(overall_succes) + d = defer.DeferredList(deferreds) + d.addCallback(destroy_volumes) + return d def test_multi_node(self): # TODO(termie): Figure out how to test with two nodes, -- cgit From c35f0961a030ebefb19c0fbf4a666a0d6ce6be4c Mon Sep 17 00:00:00 2001 From: Jesse Andrews Date: Mon, 13 Sep 2010 00:06:32 -0700 Subject: add a shell to nova-manage, which respects flags (taken from django) --- bin/nova-manage | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/bin/nova-manage b/bin/nova-manage index d2fd49d8d..6e5266767 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -17,6 +17,37 @@ # License for the specific language governing permissions and limitations # under the License. +# Interactive shell based on Django: +# +# Copyright (c) 2005, the Lawrence Journal-World +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of Django nor the names of its contributors may be used +# to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + """ CLI interface for nova management. Connects to the running ADMIN api in the api daemon. @@ -103,6 +134,29 @@ class VpnCommands(object): self.pipe.launch_vpn_instance(project_id) +class ShellCommands(object): + def run(self): + "Runs a Python interactive interpreter. Tries to use IPython, if it's available." + try: + import IPython + # Explicitly pass an empty list as arguments, because otherwise IPython + # would use sys.argv from this script. + shell = IPython.Shell.IPShell(argv=[]) + shell.mainloop() + except ImportError: + import code + try: # Try activating rlcompleter, because it's handy. + import readline + except ImportError: + pass + else: + # We don't have to wrap the following import in a 'try', because + # we already know 'readline' was imported successfully. + import rlcompleter + readline.parse_and_bind("tab:complete") + code.interact() + + class RoleCommands(object): """Class for managing roles.""" @@ -225,6 +279,7 @@ CATEGORIES = [ ('user', UserCommands), ('project', ProjectCommands), ('role', RoleCommands), + ('shell', ShellCommands), ('vpn', VpnCommands), ] -- cgit