From 3b415d1d41396429c3afba29cae93167c8b73d3d Mon Sep 17 00:00:00 2001 From: Alessandro Pilotti Date: Thu, 2 Feb 2017 14:13:41 +0200 Subject: [PATCH] Adds display idle timeout plugin Adds a new plugin for setting the timeout before the display is turned off. The timeout value, in seconds, is controlled by the "display_idle_timeout" option. Setting the value to 0 disables the timeout and leaves the display always on. Change-Id: Ibc7dbad3b1ce6ec06ff0d50d937409914d18685a Implements: blueprint display-idle-timeout-plugin Co-Authored-By: Stefan Caraiman --- cloudbaseinit/conf/default.py | 4 ++ .../plugins/windows/displayidletimeout.py | 33 +++++++++++ .../windows/test_displayidletimeout.py | 53 ++++++++++++++++++ .../tests/utils/windows/test_powercfg.py | 56 +++++++++++++++++++ cloudbaseinit/utils/windows/powercfg.py | 27 +++++++++ 5 files changed, 173 insertions(+) create mode 100644 cloudbaseinit/plugins/windows/displayidletimeout.py create mode 100644 cloudbaseinit/tests/plugins/windows/test_displayidletimeout.py create mode 100644 cloudbaseinit/tests/utils/windows/test_powercfg.py create mode 100644 cloudbaseinit/utils/windows/powercfg.py diff --git a/cloudbaseinit/conf/default.py b/cloudbaseinit/conf/default.py index 63f9fa4f..70a2c7e9 100644 --- a/cloudbaseinit/conf/default.py +++ b/cloudbaseinit/conf/default.py @@ -204,6 +204,10 @@ class GlobalOptions(conf_base.Options): 'set_unique_boot_disk_id', default=True, help='Sets a new random unique id on the boot disk to avoid ' 'collisions'), + cfg.IntOpt( + 'display_idle_timeout', default=0, + help='The idle timeout, in seconds, before powering off ' + 'the display. Set 0 to leave the display always on'), ] self._cli_options = [ diff --git a/cloudbaseinit/plugins/windows/displayidletimeout.py b/cloudbaseinit/plugins/windows/displayidletimeout.py new file mode 100644 index 00000000..64b21863 --- /dev/null +++ b/cloudbaseinit/plugins/windows/displayidletimeout.py @@ -0,0 +1,33 @@ +# Copyright (c) 2017 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 +# 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 oslo_log import log as oslo_logging + +from cloudbaseinit import conf as cloudbaseinit_conf +from cloudbaseinit.plugins.common import base +from cloudbaseinit.utils.windows import powercfg + +CONF = cloudbaseinit_conf.CONF +LOG = oslo_logging.getLogger(__name__) + + +class DisplayIdleTimeoutConfigPlugin(base.BasePlugin): + def execute(self, service, shared_data): + LOG.info("Setting display idle timeout: %s", CONF.display_idle_timeout) + powercfg.set_display_idle_timeout(CONF.display_idle_timeout) + + return base.PLUGIN_EXECUTION_DONE, False + + def get_os_requirements(self): + return 'win32', (6, 2) diff --git a/cloudbaseinit/tests/plugins/windows/test_displayidletimeout.py b/cloudbaseinit/tests/plugins/windows/test_displayidletimeout.py new file mode 100644 index 00000000..129cb365 --- /dev/null +++ b/cloudbaseinit/tests/plugins/windows/test_displayidletimeout.py @@ -0,0 +1,53 @@ +# Copyright (c) 2017 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 +# 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 unittest + +try: + import unittest.mock as mock +except ImportError: + import mock + +from cloudbaseinit import conf as cloudbaseinit_conf +from cloudbaseinit.plugins.common import base +from cloudbaseinit.plugins.windows import displayidletimeout +from cloudbaseinit.tests import testutils + +CONF = cloudbaseinit_conf.CONF + + +class DisplayIdleTimeoutConfigPluginTests(unittest.TestCase): + + def setUp(self): + self._displayplugin = (displayidletimeout. + DisplayIdleTimeoutConfigPlugin()) + self.snatcher = testutils.LogSnatcher( + 'cloudbaseinit.plugins.windows.displayidletimeout') + + @mock.patch('cloudbaseinit.utils.windows.powercfg.' + 'set_display_idle_timeout') + def test_execute(self, mock_set_display): + expected_logging = [ + "Setting display idle timeout: %s" % CONF.display_idle_timeout] + expected_result = (base.PLUGIN_EXECUTION_DONE, False) + with self.snatcher: + result = self._displayplugin.execute(mock.sentinel.service, + mock.sentinel.data) + self.assertEqual(self.snatcher.output, expected_logging) + self.assertEqual(result, expected_result) + mock_set_display.assert_called_once_with(CONF.display_idle_timeout) + + def test_get_os_requirements(self): + result = self._displayplugin.get_os_requirements() + self.assertEqual(result, ('win32', (6, 2))) diff --git a/cloudbaseinit/tests/utils/windows/test_powercfg.py b/cloudbaseinit/tests/utils/windows/test_powercfg.py new file mode 100644 index 00000000..ac0b4088 --- /dev/null +++ b/cloudbaseinit/tests/utils/windows/test_powercfg.py @@ -0,0 +1,56 @@ +# Copyright (c) 2017 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 +# 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 unittest + +try: + import unittest.mock as mock +except ImportError: + import mock + +from cloudbaseinit import exception +from cloudbaseinit.utils.windows import powercfg + + +class PowerCfgTests(unittest.TestCase): + + @mock.patch('cloudbaseinit.osutils.factory.get_os_utils') + def _test_set_display_idle_timeout(self, mock_get_osutils, fail=False): + mock_osutils = mock.Mock() + mock_get_osutils.return_value = mock_osutils + mock_out = 1 + mock_err = None + mock_ret_val = None + expected_args = ["powercfg.exe", "/setacvalueindex", "SCHEME_CURRENT", + "SUB_VIDEO", "VIDEOIDLE", str(0)] + if fail: + mock_ret_val = 1 + mock_err = 1 + mock_osutils.execute_system32_process.return_value = (mock_out, + mock_err, + mock_ret_val) + if fail: + self.assertRaises(exception.CloudbaseInitException, + powercfg.set_display_idle_timeout) + else: + powercfg.set_display_idle_timeout() + mock_get_osutils.assert_called_once_with() + (mock_osutils. + execute_system32_process.assert_called_once_with(expected_args)) + + def test_set_display_idle_timeout(self): + self._test_set_display_idle_timeout() + + def test_set_display_idle_timeout_fail(self): + self._test_set_display_idle_timeout(fail=True) diff --git a/cloudbaseinit/utils/windows/powercfg.py b/cloudbaseinit/utils/windows/powercfg.py new file mode 100644 index 00000000..1cab41a0 --- /dev/null +++ b/cloudbaseinit/utils/windows/powercfg.py @@ -0,0 +1,27 @@ +# Copyright (c) 2017 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 +# 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 cloudbaseinit import exception +from cloudbaseinit.osutils import factory as osutils_factory + + +def set_display_idle_timeout(seconds=0): + osutils = osutils_factory.get_os_utils() + args = ["powercfg.exe", "/setacvalueindex", "SCHEME_CURRENT", + "SUB_VIDEO", "VIDEOIDLE", str(int(seconds))] + (out, err, ret_val) = osutils.execute_system32_process(args) + if ret_val: + raise exception.CloudbaseInitException( + 'PowerCfg failed.\nOutput: %(out)s\nError:' + ' %(err)s' % {'out': out, 'err': err})