From abe9432c708381911130b88a88dc22cc9afd4d25 Mon Sep 17 00:00:00 2001 From: Adelina Tuvenie Date: Thu, 9 Oct 2014 11:38:51 +0300 Subject: [PATCH] Adds host power actions support for Hyper-V This patch introduces host power off and host reboot support for the Nova Hyper-V driver. Startup cannot be supported as the driver runs on the host itself. Implements: blueprint hyper-v-host-power-actions Change-Id: I86ca35ff2527f643d72fb949870da69ce63277ae --- nova/tests/unit/virt/hyperv/test_hostutils.py | 29 ++++++++++++ nova/tests/virt/hyperv/test_hostops.py | 45 +++++++++++++++++++ nova/virt/hyperv/constants.py | 4 ++ nova/virt/hyperv/hostops.py | 9 +++- nova/virt/hyperv/hostutils.py | 21 ++++++++- 5 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 nova/tests/virt/hyperv/test_hostops.py diff --git a/nova/tests/unit/virt/hyperv/test_hostutils.py b/nova/tests/unit/virt/hyperv/test_hostutils.py index 998692d350..2eaf0d17b1 100644 --- a/nova/tests/unit/virt/hyperv/test_hostutils.py +++ b/nova/tests/unit/virt/hyperv/test_hostutils.py @@ -15,6 +15,7 @@ import mock from nova import test +from nova.virt.hyperv import constants from nova.virt.hyperv import hostutils @@ -95,3 +96,31 @@ class HostUtilsTestCase(test.NoDBTestCase): self._hostutils._conn_cimv2.Win32_OperatingSystem.return_value = [os] self.assertEqual(expected, self._hostutils.check_min_windows_version(6, 2)) + + def _test_host_power_action(self, action): + fake_win32 = mock.MagicMock() + fake_win32.Win32Shutdown = mock.MagicMock() + + self._hostutils._conn_cimv2.Win32_OperatingSystem.return_value = [ + fake_win32] + + if action == constants.HOST_POWER_ACTION_SHUTDOWN: + self._hostutils.host_power_action(action) + fake_win32.Win32Shutdown.assert_called_with( + self._hostutils._HOST_FORCED_SHUTDOWN) + elif action == constants.HOST_POWER_ACTION_REBOOT: + self._hostutils.host_power_action(action) + fake_win32.Win32Shutdown.assert_called_with( + self._hostutils._HOST_FORCED_REBOOT) + else: + self.assertRaises(NotImplementedError, + self._hostutils.host_power_action, action) + + def test_host_shutdown(self): + self._test_host_power_action(constants.HOST_POWER_ACTION_SHUTDOWN) + + def test_host_reboot(self): + self._test_host_power_action(constants.HOST_POWER_ACTION_REBOOT) + + def test_host_startup(self): + self._test_host_power_action(constants.HOST_POWER_ACTION_STARTUP) diff --git a/nova/tests/virt/hyperv/test_hostops.py b/nova/tests/virt/hyperv/test_hostops.py new file mode 100644 index 0000000000..79c8eb640e --- /dev/null +++ b/nova/tests/virt/hyperv/test_hostops.py @@ -0,0 +1,45 @@ +# 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. + +import mock + +from nova import test +from nova.virt.hyperv import constants +from nova.virt.hyperv import hostops + + +class HostOpsTestCase(test.NoDBTestCase): + + def setUp(self): + self._hostops = hostops.HostOps() + super(HostOpsTestCase, self).setUp() + + def _test_host_power_action(self, action): + self._hostops._hostutils.host_power_action = mock.Mock() + + self._hostops.host_power_action(action) + self._hostops._hostutils.host_power_action.assert_called_with( + action) + + def test_host_power_action_shutdown(self): + self._test_host_power_action(constants.HOST_POWER_ACTION_SHUTDOWN) + + def test_host_power_action_reboot(self): + self._test_host_power_action(constants.HOST_POWER_ACTION_REBOOT) + + def test_host_power_action_exception(self): + self.assertRaises(NotImplementedError, + self._hostops.host_power_action, + constants.HOST_POWER_ACTION_STARTUP) diff --git a/nova/virt/hyperv/constants.py b/nova/virt/hyperv/constants.py index 4aaaf2ffa8..764a2bf4c2 100644 --- a/nova/virt/hyperv/constants.py +++ b/nova/virt/hyperv/constants.py @@ -84,3 +84,7 @@ VHD_TYPE_FIXED = 2 VHD_TYPE_DYNAMIC = 3 SCSI_CONTROLLER_SLOTS_NUMBER = 64 + +HOST_POWER_ACTION_SHUTDOWN = "shutdown" +HOST_POWER_ACTION_REBOOT = "reboot" +HOST_POWER_ACTION_STARTUP = "startup" diff --git a/nova/virt/hyperv/hostops.py b/nova/virt/hyperv/hostops.py index 2fe6563003..b671a5f29f 100644 --- a/nova/virt/hyperv/hostops.py +++ b/nova/virt/hyperv/hostops.py @@ -28,6 +28,7 @@ from oslo.utils import units from nova.compute import arch from nova.compute import hvtype from nova.compute import vm_mode +from nova.i18n import _ from nova.openstack.common import log as logging from nova.virt.hyperv import constants from nova.virt.hyperv import utilsfactory @@ -140,7 +141,13 @@ class HostOps(object): def host_power_action(self, action): """Reboots, shuts down or powers up the host.""" - pass + if action in [constants.HOST_POWER_ACTION_SHUTDOWN, + constants.HOST_POWER_ACTION_REBOOT]: + self._hostutils.host_power_action(action) + else: + if action == constants.HOST_POWER_ACTION_STARTUP: + raise NotImplementedError( + _("Host PowerOn is not supported by the Hyper-V driver")) def get_host_ip_addr(self): host_ip = CONF.my_ip diff --git a/nova/virt/hyperv/hostutils.py b/nova/virt/hyperv/hostutils.py index e7fc9b68e8..f65bdaabe8 100644 --- a/nova/virt/hyperv/hostutils.py +++ b/nova/virt/hyperv/hostutils.py @@ -20,11 +20,18 @@ import sys if sys.platform == 'win32': import wmi +from nova.i18n import _ +from nova.virt.hyperv import constants + class HostUtils(object): + + _HOST_FORCED_REBOOT = 6 + _HOST_FORCED_SHUTDOWN = 12 + def __init__(self): if sys.platform == 'win32': - self._conn_cimv2 = wmi.WMI(moniker='//./root/cimv2') + self._conn_cimv2 = wmi.WMI(privileges=["Shutdown"]) def get_cpus_info(self): cpus = self._conn_cimv2.query("SELECT * FROM Win32_Processor " @@ -78,3 +85,15 @@ class HostUtils(object): def get_host_tick_count64(self): return ctypes.windll.kernel32.GetTickCount64() + + def host_power_action(self, action): + win32_os = self._conn_cimv2.Win32_OperatingSystem()[0] + + if action == constants.HOST_POWER_ACTION_SHUTDOWN: + win32_os.Win32Shutdown(self._HOST_FORCED_SHUTDOWN) + elif action == constants.HOST_POWER_ACTION_REBOOT: + win32_os.Win32Shutdown(self._HOST_FORCED_REBOOT) + else: + raise NotImplementedError( + _("Host %(action)s is not supported by the Hyper-V driver") % + {"action": action})