diff --git a/setup.cfg b/setup.cfg index 97aed85..58c85a7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -26,8 +26,9 @@ console_scripts = sushycli = sushycli.cmd.sushycli:main sushycli = - power = sushycli.power:Power - version = sushycli.version:Version + system_power_show = sushycli.system_power:SystemPowerShow + system_power = sushycli.system_power:SystemPowerSet + version_show = sushycli.version:VersionShow [build_sphinx] source-dir = doc/source diff --git a/sushycli/power.py b/sushycli/base.py similarity index 56% rename from sushycli/power.py rename to sushycli/base.py index 3fc04eb..e6efb49 100644 --- a/sushycli/power.py +++ b/sushycli/base.py @@ -17,14 +17,12 @@ import sushy from cliff import command +from cliff import lister -class Power(command.Command): - """Change machine power state""" +class BaseParserMixIn(object): - def get_parser(self, prog_name): - """Power state command parser""" - parser = super(Power, self).get_parser(prog_name) + def _add_options(self, parser): parser.add_argument( '--username', @@ -40,35 +38,33 @@ class Power(command.Command): help='Redfish BMC service endpoint URL e.g. ' 'http://localhost:8000') - parser.add_argument( - '--system-id', - required=True, - help='The canonical path to the ComputerSystem ' - 'resource that the driver will interact with. ' - 'It should include the root service, version and ' - 'the unique resource path to a ComputerSystem. ' - 'For example: /redfish/v1/Systems/1') - - parser.add_argument( - 'state', - metavar='on|off', - type=lambda x: x.lower(), - choices=['on', 'off'], - help='Set machine power state') - return parser def take_action(self, args): - """Power state command action""" + """Common command action""" root = sushy.Sushy( args.service_endpoint, username=args.username, password=args.password) - sys_inst = root.get_system(args.system_id) + return root - sys_inst.reset_system( - sushy.RESET_TYPE_ON - if args.state == 'on' else sushy.RESET_TYPE_FORCE_OFF) - return 0 +class BaseCommand(BaseParserMixIn, command.Command): + """Common base for all sushycli commands""" + + def get_parser(self, prog_name): + """Common command parser""" + parser = super(BaseCommand, self).get_parser(prog_name) + + return self._add_options(parser) + + +class BaseLister(BaseParserMixIn, lister.Lister): + """Common base for all sushycli listers""" + + def get_parser(self, prog_name): + """Common lister parser""" + parser = super(BaseLister, self).get_parser(prog_name) + + return self._add_options(parser) diff --git a/sushycli/system_power.py b/sushycli/system_power.py new file mode 100644 index 0000000..69f77e9 --- /dev/null +++ b/sushycli/system_power.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- + +# Copyright 2010-2020 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. + +import sushy + +from sushycli import base + + +SYSTEM_ID_HELP = ( + 'The canonical path to the ComputerSystem resource that the driver ' + 'will interact with. It should include the root service, version and ' + 'the unique resource path to a ComputerSystem. For example: ' + '/redfish/v1/Systems/1' +) + + +class SystemPowerShow(base.BaseLister): + """Show machine power state""" + + def get_parser(self, prog_name): + """SystemPowerShow state command parser""" + parser = super(SystemPowerShow, self).get_parser(prog_name) + + parser.add_argument( + '--system-id', + required=True, + help=SYSTEM_ID_HELP) + + return parser + + def take_action(self, args): + """SystemPowerShow state command action""" + + root = super(SystemPowerShow, self).take_action(args) + + sys_inst = root.get_system(args.system_id) + + return ['Power state'], [[sys_inst.power_state]] + + +class SystemPowerSet(base.BaseCommand): + """Change machine power state""" + + def get_parser(self, prog_name): + """SystemPowerSet state command parser""" + parser = super(SystemPowerSet, self).get_parser(prog_name) + + parser.add_argument( + '--system-id', + required=True, + help=SYSTEM_ID_HELP) + + parser.add_argument( + 'state', + metavar='on|off', + type=lambda x: x.lower(), + choices=['on', 'off'], + help='Set machine power state') + + return parser + + def take_action(self, args): + """SystemPowerSet state command action""" + + root = super(SystemPowerSet, self).take_action(args) + + sys_inst = root.get_system(args.system_id) + + sys_inst.reset_system( + sushy.RESET_TYPE_ON + if args.state == 'on' else sushy.RESET_TYPE_FORCE_OFF) + + return 0 diff --git a/sushycli/tests/unit/cmd/test_sushycli.py b/sushycli/tests/unit/cmd/test_sushycli.py index 3dfbcd1..efad346 100644 --- a/sushycli/tests/unit/cmd/test_sushycli.py +++ b/sushycli/tests/unit/cmd/test_sushycli.py @@ -26,9 +26,62 @@ from sushycli.tests.unit import base @mock.patch.object(sushy, 'Sushy', autospec=True) class SuchyCliTestCase(base.TestCase): - def test_power_on(self, mock_sushy): + @mock.patch('sys.stdout.write', autospec=True) + def test_version(self, mock_write, mock_sushy): - main(['power', + mock_root = mock_sushy.return_value + + mock_root.redfish_version = '1.2.3' + + main(['version', 'show', + '--username', 'jelly', '--password', 'fish', + '--service-endpoint', 'http://fish.me']) + + mock_sushy.assert_called_once_with( + 'http://fish.me', password='fish', username='jelly') + + expected_calles = [ + mock.call('+---------+\n' + '| Version |' + '\n+---------+\n' + '| 1.2.3 |\n' + '+---------+'), + mock.call('\n') + ] + + mock_write.assert_has_calls(expected_calles) + + @mock.patch('sys.stdout.write', autospec=True) + def test_system_power_show(self, mock_write, mock_sushy): + + mock_root = mock_sushy.return_value + + mock_system = mock_root.get_system.return_value + + mock_system.power_state = 'on' + + main(['system', 'power', 'show', + '--username', 'jelly', '--password', 'fish', + '--service-endpoint', 'http://fish.me', + '--system-id', '/redfish/v1/Systems/1']) + + mock_sushy.assert_called_once_with( + 'http://fish.me', password='fish', username='jelly') + + expected_calles = [ + mock.call('+-------------+\n' + '| Power state |\n' + '+-------------+\n' + '| on |\n' + '+-------------+'), + mock.call('\n') + ] + + mock_write.assert_has_calls(expected_calles) + + def test_system_power_on(self, mock_sushy): + + main(['system', 'power', '--username', 'jelly', '--password', 'fish', '--service-endpoint', 'http://fish.me', '--system-id', '/redfish/v1/Systems/1', @@ -46,9 +99,9 @@ class SuchyCliTestCase(base.TestCase): mock_system.reset_system.assert_called_once_with('on') - def test_power_off(self, mock_sushy): + def test_system_power_off(self, mock_sushy): - main(['power', + main(['system', 'power', '--username', 'jelly', '--password', 'fish', '--service-endpoint', 'http://fish.me', '--system-id', '/redfish/v1/Systems/1', diff --git a/sushycli/version.py b/sushycli/version.py index 65cbae2..a8da606 100644 --- a/sushycli/version.py +++ b/sushycli/version.py @@ -14,15 +14,21 @@ # License for the specific language governing permissions and limitations # under the License. -from cliff.command import Command -import sushy +from sushycli.base import BaseLister -class Version(Command): - """A simple command that render the Redfish version""" +class VersionShow(BaseLister): + """Read supported Redfish protocol version of remote agent""" - def take_action(self, parsed_args): - s = sushy.Sushy('http://localhost:8000/redfish/v1', - username='foo', password='bar') + def get_parser(self, prog_name): + """Redfish version command parser""" + parser = super(VersionShow, self).get_parser(prog_name) - return s.redfish_version + return parser + + def take_action(self, args): + """Redfish version command action""" + + root = super(VersionShow, self).take_action(args) + + return ['Version'], [[root.redfish_version]]