Add openstack baremetal driver commands

Extends the OSC plugin with new commands related
to the drivers:
  * openstack baremetal driver list
  * openstack baremetal driver passthru call
  * openstack baremetal driver passthru list
  * openstack baremetal driver show

Change-Id: I68ae82d1ff7e2944c1d04f86c4362b6c0f499df1
Partial-Bug: 1526479
Co-Authored-By: Ruby Loo <ruby.loo@intel.com>
This commit is contained in:
Rodion Promyshlennikov 2016-08-29 19:44:35 +03:00 committed by Ruby Loo
parent 8d86ed4a0a
commit e31544bf5d
5 changed files with 416 additions and 0 deletions

View File

@ -0,0 +1,164 @@
# Copyright (c) 2016 Mirantis, Inc.
#
# 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 logging
from osc_lib.command import command
from osc_lib import utils as oscutils
from ironicclient.common import utils
from ironicclient.v1 import resource_fields as res_fields
class ListBaremetalDriver(command.Lister):
"""List the enabled drivers."""
log = logging.getLogger(__name__ + ".ListBaremetalDriver")
def get_parser(self, prog_name):
parser = super(ListBaremetalDriver, self).get_parser(prog_name)
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
client = self.app.client_manager.baremetal
labels = ['Supported driver(s)', 'Active host(s)']
columns = ['name', 'hosts']
drivers = client.driver.list()
drivers = oscutils.sort_items(drivers, 'name')
for d in drivers:
d.hosts = ', '.join(d.hosts)
return (labels,
(oscutils.get_item_properties(s, columns) for s in drivers))
class PassthruCallBaremetalDriver(command.ShowOne):
"""Call a vendor passthru method for a driver."""
log = logging.getLogger(__name__ + ".PassthruCallBaremetalDriver")
http_methods = ['POST', 'PUT', 'GET', 'DELETE', 'PATCH']
def get_parser(self, prog_name):
parser = super(PassthruCallBaremetalDriver, self).get_parser(prog_name)
parser.add_argument(
'driver',
metavar='<driver>',
help='Name of the driver.'
)
parser.add_argument(
'method',
metavar='<method>',
help="Vendor passthru method to be called."
)
parser.add_argument(
'--arg',
metavar='<key=value>',
action='append',
help="Argument to pass to the passthru method (repeat option "
"to specify multiple arguments)."
)
parser.add_argument(
'--http-method',
dest='http_method',
metavar='<http-method>',
choices=self.http_methods,
default='POST',
help="The HTTP method to use in the passthru request. One of "
"%s. Defaults to 'POST'." %
oscutils.format_list(self.http_methods)
)
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
arguments = utils.args_array_to_dict(
{'args': parsed_args.arg}, 'args')['args']
if not arguments:
arguments = {}
response = (baremetal_client.driver.
vendor_passthru(parsed_args.driver,
parsed_args.method,
http_method=parsed_args.http_method,
args=arguments))
return self.dict2columns(response)
class PassthruListBaremetalDriver(command.Lister):
"""List available vendor passthru methods for a driver."""
log = logging.getLogger(__name__ + ".PassthruListBaremetalDriver")
def get_parser(self, prog_name):
parser = super(PassthruListBaremetalDriver, self).get_parser(prog_name)
parser.add_argument(
'driver',
metavar='<driver>',
help='Name of the driver.')
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
columns = res_fields.VENDOR_PASSTHRU_METHOD_RESOURCE.fields
labels = res_fields.VENDOR_PASSTHRU_METHOD_RESOURCE.labels
methods = baremetal_client.driver.get_vendor_passthru_methods(
parsed_args.driver)
params = []
for method, response in methods.items():
response['name'] = method
http_methods = ', '.join(response['http_methods'])
response['http_methods'] = http_methods
params.append(response)
return (labels,
(oscutils.get_dict_properties(s, columns) for s in params))
class ShowBaremetalDriver(command.ShowOne):
"""Show information about a driver."""
log = logging.getLogger(__name__ + ".ShowBaremetalDriver")
def get_parser(self, prog_name):
parser = super(ShowBaremetalDriver, self).get_parser(prog_name)
parser.add_argument(
'driver',
metavar='<driver>',
help='Name of the driver.')
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
driver = baremetal_client.driver.get(parsed_args.driver)._info
driver.pop("links", None)
driver.pop("properties", None)
# NOTE(rloo): this will show the hosts as a comma-separated string
# whereas 'ironic driver show' displays this as a list
# of hosts (eg "host1, host2" vs "[u'host1', u'host2']"
driver['hosts'] = ', '.join(driver.get('hosts', ()))
return zip(*sorted(driver.items()))

View File

@ -47,6 +47,21 @@ BAREMETAL_PORT = {
'node_uuid': baremetal_uuid,
}
baremetal_driver_hosts = ['fake-host1', 'fake-host2']
baremetal_driver_name = 'fakedrivername'
BAREMETAL_DRIVER = {
'hosts': baremetal_driver_hosts,
'name': baremetal_driver_name,
}
baremetal_driver_passthru_method = 'lookup'
BAREMETAL_DRIVER_PASSTHRU = {"lookup": {"attach": "false",
"http_methods": ["POST"],
"description": "",
"async": "false"}}
class TestBaremetal(utils.TestCommand):

View File

@ -0,0 +1,223 @@
# Copyright (c) 2016 Mirantis, Inc.
#
# 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 copy
from osc_lib.tests import utils as oscutils
from ironicclient.osc.v1 import baremetal_driver
from ironicclient.tests.unit.osc.v1 import fakes as baremetal_fakes
class TestBaremetalDriver(baremetal_fakes.TestBaremetal):
def setUp(self):
super(TestBaremetalDriver, self).setUp()
self.baremetal_mock = self.app.client_manager.baremetal
self.baremetal_mock.reset_mock()
class TestListBaremetalDriver(TestBaremetalDriver):
def setUp(self):
super(TestListBaremetalDriver, self).setUp()
self.baremetal_mock.driver.list.return_value = [
baremetal_fakes.FakeBaremetalResource(
None,
copy.deepcopy(baremetal_fakes.BAREMETAL_DRIVER),
loaded=True)
]
self.cmd = baremetal_driver.ListBaremetalDriver(self.app, None)
def test_baremetal_driver_list(self):
arglist = []
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
collist = (
"Supported driver(s)",
"Active host(s)")
self.assertEqual(collist, tuple(columns))
datalist = ((
baremetal_fakes.baremetal_driver_name,
', '.join(baremetal_fakes.baremetal_driver_hosts)), )
self.assertEqual(datalist, tuple(data))
class TestPassthruCallBaremetalDriver(TestBaremetalDriver):
def setUp(self):
super(TestPassthruCallBaremetalDriver, self).setUp()
self.baremetal_mock.driver.vendor_passthru.return_value = (
baremetal_fakes.BAREMETAL_DRIVER_PASSTHRU
)
self.cmd = baremetal_driver.PassthruCallBaremetalDriver(self.app, None)
def test_baremetal_driver_passthru_call_with_min_args(self):
arglist = [
baremetal_fakes.baremetal_driver_name,
baremetal_fakes.baremetal_driver_passthru_method,
]
verifylist = [
('driver', baremetal_fakes.baremetal_driver_name),
('method', baremetal_fakes.baremetal_driver_passthru_method),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
# Set expected values
args = [
baremetal_fakes.baremetal_driver_name,
baremetal_fakes.baremetal_driver_passthru_method,
]
kwargs = {
'http_method': 'POST',
'args': {}
}
(self.baremetal_mock.driver.vendor_passthru.
assert_called_once_with(*args, **kwargs))
def test_baremetal_driver_passthru_call_with_all_args(self):
arglist = [
baremetal_fakes.baremetal_driver_name,
baremetal_fakes.baremetal_driver_passthru_method,
'--arg', 'arg1=val1', '--arg', 'arg2=val2',
'--http-method', 'POST'
]
verifylist = [
('driver', baremetal_fakes.baremetal_driver_name),
('method', baremetal_fakes.baremetal_driver_passthru_method),
('arg', ['arg1=val1', 'arg2=val2']),
('http_method', 'POST')
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
# Set expected values
args = [
baremetal_fakes.baremetal_driver_name,
baremetal_fakes.baremetal_driver_passthru_method,
]
kwargs = {
'http_method': 'POST',
'args': {'arg1': 'val1', 'arg2': 'val2'}
}
(self.baremetal_mock.driver.vendor_passthru.
assert_called_once_with(*args, **kwargs))
def test_baremetal_driver_passthru_call_no_arg(self):
arglist = []
verifylist = []
self.assertRaises(oscutils.ParserException,
self.check_parser,
self.cmd, arglist, verifylist)
class TestPassthruListBaremetalDriver(TestBaremetalDriver):
def setUp(self):
super(TestPassthruListBaremetalDriver, self).setUp()
self.baremetal_mock.driver.get_vendor_passthru_methods.return_value = (
baremetal_fakes.BAREMETAL_DRIVER_PASSTHRU
)
self.cmd = baremetal_driver.PassthruListBaremetalDriver(self.app, None)
def test_baremetal_driver_passthru_list(self):
arglist = ['fakedrivername']
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
args = ['fakedrivername']
(self.baremetal_mock.driver.get_vendor_passthru_methods.
assert_called_with(*args))
collist = (
"Name",
"Supported HTTP methods",
"Async",
"Description",
"Response is attachment",
)
self.assertEqual(collist, tuple(columns))
datalist = (('lookup', 'POST', 'false', '', 'false'),)
self.assertEqual(datalist, tuple(data))
def test_baremetal_driver_passthru_list_no_arg(self):
arglist = []
verifylist = []
self.assertRaises(oscutils.ParserException,
self.check_parser,
self.cmd, arglist, verifylist)
class TestShowBaremetalDriver(TestBaremetalDriver):
def setUp(self):
super(TestShowBaremetalDriver, self).setUp()
self.baremetal_mock.driver.get.return_value = (
baremetal_fakes.FakeBaremetalResource(
None,
copy.deepcopy(baremetal_fakes.BAREMETAL_DRIVER),
loaded=True))
self.cmd = baremetal_driver.ShowBaremetalDriver(self.app, None)
def test_baremetal_driver_show(self):
arglist = ['fakedrivername']
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
args = ['fakedrivername']
self.baremetal_mock.driver.get.assert_called_with(*args)
self.assertFalse(self.baremetal_mock.driver.properties.called)
collist = ('hosts', 'name')
self.assertEqual(collist, columns)
datalist = (
', '.join(baremetal_fakes.baremetal_driver_hosts),
baremetal_fakes.baremetal_driver_name)
self.assertEqual(datalist, tuple(data))
def test_baremetal_driver_show_no_arg(self):
arglist = []
verifylist = []
self.assertRaises(oscutils.ParserException,
self.check_parser,
self.cmd, arglist, verifylist)

View File

@ -0,0 +1,10 @@
---
features:
- |
Extend the OpenStackClient plugin with new commands:
* openstack baremetal driver list
* openstack baremetal driver passthru call
* openstack baremetal driver passthru list
* openstack baremetal driver show

View File

@ -31,6 +31,10 @@ openstack.cli.extension =
openstack.baremetal.v1 =
baremetal_create = ironicclient.osc.v1.baremetal_create:CreateBaremetal
baremetal_delete = ironicclient.osc.v1.baremetal_node:DeleteBaremetal
baremetal_driver_list = ironicclient.osc.v1.baremetal_driver:ListBaremetalDriver
baremetal_driver_passthru_call = ironicclient.osc.v1.baremetal_driver:PassthruCallBaremetalDriver
baremetal_driver_passthru_list = ironicclient.osc.v1.baremetal_driver:PassthruListBaremetalDriver
baremetal_driver_show = ironicclient.osc.v1.baremetal_driver:ShowBaremetalDriver
baremetal_list = ironicclient.osc.v1.baremetal_node:ListBaremetal
baremetal_node_abort = ironicclient.osc.v1.baremetal_node:AbortBaremetalNode
baremetal_node_adopt = ironicclient.osc.v1.baremetal_node:AdoptBaremetalNode