Introduce openstackclient plugin

Adds the code necessary for ironicclient to work as a plugin for
openstackclient.

* baremetal list
* baremetal show
* baremetal delete
* baremetal create
* baremetal set
* baremetal unset

Change-Id: I91ea4f4a63b0e7a8ea004c47422d19ca0f288dcd
This commit is contained in:
Brad P. Crochet 2015-07-30 09:50:36 -04:00
parent b5a720f7a4
commit a5a15e40a5
12 changed files with 1079 additions and 0 deletions

View File

View File

@ -0,0 +1,51 @@
# Copyright 2010 Jacob Kaplan-Moss
# Copyright 2011 OpenStack Foundation
# Copyright 2011 Piston Cloud Computing, Inc.
#
# Modified by: Brad P. Crochet <brad@redhat.com>
#
# 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.
"""
OpenStack Client factory.
"""
from oslo_utils import importutils
from ironicclient.common.i18n import _
from ironicclient import exceptions
def get_client_class(version):
version_map = {
'1': 'ironicclient.v1.client.Client',
'1.5': 'ironicclient.v1.client.Client',
'1.6': 'ironicclient.v1.client.Client',
'1.9': 'ironicclient.v1.client.Client',
}
try:
client_path = version_map[str(version)]
except (KeyError, ValueError):
msg = _("Invalid client version '%(version)s'. must be one of: "
"%(keys)s") % {'version': version,
'keys': ', '.join(version_map)}
raise exceptions.UnsupportedVersion(msg)
return importutils.import_class(client_path)
def Client(version, *args, **kwargs):
client_class = get_client_class(version)
return client_class(*args, **kwargs)

View File

@ -0,0 +1,70 @@
#
# Copyright 2015 Red Hat, 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 openstackclient.common import exceptions
from openstackclient.common import utils
from ironicclient.osc import client as ironic_client
LOG = logging.getLogger(__name__)
DEFAULT_BAREMETAL_API_VERSION = '1.6'
API_VERSION_OPTION = 'os_baremetal_api_version'
API_NAME = 'baremetal'
API_VERSIONS = {
'1': 'ironicclient.osc.client',
'1.5': 'ironicclient.osc.client',
'1.6': 'ironicclient.osc.client',
'1.9': 'ironicclient.osc.client',
}
def make_client(instance):
"""Returns a baremetal service client."""
try:
baremetal_client = ironic_client.get_client_class(
instance._api_version[API_NAME])
except Exception:
msg = "Invalid %s client version '%s'. Must be one of %s" % (
(API_NAME, instance._api_version[API_NAME],
", ".join(sorted(API_VERSIONS))))
raise exceptions.UnsupportedVersion(msg)
LOG.debug('Instantiating baremetal client: %s', baremetal_client)
client = baremetal_client(
os_ironic_api_version=instance._api_version[API_NAME],
session=instance.session,
region_name=instance._region_name,
endpoint=instance.auth_ref.auth_url,
)
return client
def build_option_parser(parser):
"""Hook to add global options."""
parser.add_argument(
'--os-baremetal-api-version',
metavar='<baremetal-api-version>',
default=utils.env(
'OS_BAREMETAL_API_VERSION',
default=DEFAULT_BAREMETAL_API_VERSION),
help='Baremetal API version, default=' +
DEFAULT_BAREMETAL_API_VERSION +
' (Env: OS_BAREMETAL_API_VERSION)')
return parser

View File

View File

@ -0,0 +1,320 @@
#
# Copyright 2015 Red Hat, 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
import six
from cliff import command
from cliff import lister
from cliff import show
from openstackclient.common import utils as oscutils
from ironicclient.common.i18n import _
from ironicclient.common import utils
from ironicclient import exc
from ironicclient.v1 import resource_fields as res_fields
class CreateBaremetal(show.ShowOne):
"""Register a new node with the baremetal service"""
log = logging.getLogger(__name__ + ".CreateBaremetal")
def get_parser(self, prog_name):
parser = super(CreateBaremetal, self).get_parser(prog_name)
parser.add_argument(
'--chassis-uuid',
dest='chassis_uuid',
metavar='<chassis>',
help='UUID of the chassis that this node belongs to.')
parser.add_argument(
'--driver',
metavar='<driver>',
required=True,
help='Driver used to control the node [REQUIRED].')
parser.add_argument(
'--driver-info',
metavar='<key=value>',
action='append',
help='Key/value pair used by the driver, such as out-of-band '
'management credentials. Can be specified multiple times.')
parser.add_argument(
'--property',
dest='properties',
metavar='<key=value>',
action='append',
help='Key/value pair describing the physical characteristics of '
'the node. This is exported to Nova and used by the '
'scheduler. Can be specified multiple times.')
parser.add_argument(
'--extra',
metavar='<key=value>',
action='append',
help="Record arbitrary key/value metadata. "
"Can be specified multiple times.")
parser.add_argument(
'--uuid',
metavar='<uuid>',
help="Unique UUID for the node.")
parser.add_argument(
'--name',
metavar='<name>',
help="Unique name for the node.")
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
field_list = ['chassis_uuid', 'driver', 'driver_info',
'properties', 'extra', 'uuid', 'name']
fields = dict((k, v) for (k, v) in vars(parsed_args).items()
if k in field_list and not (v is None))
fields = utils.args_array_to_dict(fields, 'driver_info')
fields = utils.args_array_to_dict(fields, 'extra')
fields = utils.args_array_to_dict(fields, 'properties')
node = baremetal_client.node.create(**fields)._info
node.pop('links', None)
return self.dict2columns(node)
class DeleteBaremetal(command.Command):
"""Unregister a baremetal node"""
log = logging.getLogger(__name__ + ".DeleteBaremetal")
def get_parser(self, prog_name):
parser = super(DeleteBaremetal, self).get_parser(prog_name)
parser.add_argument(
"node",
metavar="<node>",
help="Node to delete (name or ID)")
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
node = oscutils.find_resource(baremetal_client.node,
parsed_args.node)
baremetal_client.node.delete(node.uuid)
class ListBaremetal(lister.Lister):
"""List baremetal nodes"""
log = logging.getLogger(__name__ + ".ListBaremetal")
def get_parser(self, prog_name):
parser = super(ListBaremetal, self).get_parser(prog_name)
parser.add_argument(
'--limit',
metavar='<limit>',
type=int,
help='Maximum number of nodes to return per request, '
'0 for no limit. Default is the maximum number used '
'by the Baremetal API Service.'
)
parser.add_argument(
'--marker',
metavar='<node>',
help='Node UUID (for example, of the last node in the list from '
'a previous request). Returns the list of nodes after this '
'UUID.'
)
parser.add_argument(
'--sort',
metavar="<key>[:<direction>]",
help='Sort output by selected keys and directions(asc or desc) '
'(default: asc), multiple keys and directions can be '
'specified separated by comma',
)
parser.add_argument(
'--maintenance',
dest='maintenance',
action='store_true',
default=False,
help="List nodes in maintenance mode.",
)
parser.add_argument(
'--associated',
dest='associated',
action='store_true',
default=False,
help="List only nodes associated with an instance."
)
parser.add_argument(
'--long',
action='store_true',
default=False,
help="Show detailed information about the nodes."
)
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
client = self.app.client_manager.baremetal
columns = res_fields.NODE_RESOURCE
params = {}
if parsed_args.limit is not None and parsed_args.limit < 0:
raise exc.CommandError(
_('Expected non-negative --limit, got %s') %
parsed_args.limit)
params['limit'] = parsed_args.limit
params['marker'] = parsed_args.marker
if parsed_args.associated:
params['associated'] = parsed_args.associated
if parsed_args.maintenance:
params['maintenance'] = parsed_args.maintenance
if parsed_args.long:
columns = res_fields.NODE_DETAILED_RESOURCE
params['detail'] = parsed_args.long
self.log.debug("params(%s)" % params)
data = client.node.list(**params)
data = oscutils.sort_items(data, parsed_args.sort)
return (columns.labels,
(oscutils.get_item_properties(s, columns.fields, formatters={
'Properties': oscutils.format_dict},) for s in data))
class SetBaremetal(command.Command):
"""Set baremetal properties"""
log = logging.getLogger(__name__ + ".SetBaremetal")
def get_parser(self, prog_name):
parser = super(SetBaremetal, self).get_parser(prog_name)
parser.add_argument(
'node',
metavar='<node>',
help="Name or UUID of the node."
)
parser.add_argument(
"--property",
metavar="<path=value>",
action='append',
help='Property to add to this baremetal host '
'(repeat option to set multiple properties)',
)
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
properties = []
if parsed_args.property:
properties = utils.args_array_to_patch(
'add', parsed_args.property)
baremetal_client.node.update(parsed_args.node, properties)
class ShowBaremetal(show.ShowOne):
"""Show baremetal node details"""
log = logging.getLogger(__name__ + ".ShowBaremetal")
LONG_FIELDS = [
'extra',
'properties',
'ports',
'driver_info',
'driver_internal_info',
'instance_info',
]
def get_parser(self, prog_name):
parser = super(ShowBaremetal, self).get_parser(prog_name)
parser.add_argument(
"node",
metavar="<node>",
help="Name or UUID of the node (or instance UUID if --instance "
"is specified)")
parser.add_argument(
'--instance',
dest='instance_uuid',
action='store_true',
default=False,
help='<node> is an instance UUID.')
parser.add_argument(
'--long',
action='store_true')
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
if parsed_args.instance_uuid:
node = baremetal_client.node.get_by_instance_uuid(
parsed_args.node)._info
else:
node = oscutils.find_resource(baremetal_client.node,
parsed_args.node)._info
node.pop("links", None)
if not parsed_args.long:
for field in self.LONG_FIELDS:
node.pop(field, None)
return zip(*sorted(six.iteritems(node)))
class UnsetBaremetal(command.Command):
"""Unset baremetal properties"""
log = logging.getLogger(__name__ + ".UnsetBaremetal")
def get_parser(self, prog_name):
parser = super(UnsetBaremetal, self).get_parser(prog_name)
parser.add_argument(
'node',
metavar='<node>',
help="Name or UUID of the node."
)
parser.add_argument(
'--property',
metavar='<path>',
action='append',
help='Property to unset on this baremetal host '
'(repeat option to unset multiple properties)',
)
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
if not parsed_args.node and not parsed_args.property:
return
patch = utils.args_array_to_patch('remove', parsed_args.property)
baremetal_client.node.update(parsed_args.node, patch)

View File

View File

@ -0,0 +1,58 @@
#
# Copyright 2015 Red Hat, 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 sys
import six
AUTH_TOKEN = "foobar"
AUTH_URL = "http://0.0.0.0"
class FakeApp(object):
def __init__(self):
_stdout = None
self.client_manager = None
self.stdin = sys.stdin
self.stdout = _stdout or sys.stdout
self.stderr = sys.stderr
self.restapi = None
class FakeClientManager(object):
def __init__(self):
self.identity = None
self.auth_ref = None
class FakeResource(object):
def __init__(self, manager, info, loaded=False):
self.__name__ = type(self).__name__
self.manager = manager
self._info = info
self._add_details(info)
self._loaded = loaded
def _add_details(self, info):
for (k, v) in six.iteritems(info):
setattr(self, k, v)
def __repr__(self):
reprkeys = sorted(k for k in self.__dict__.keys() if k[0] != '_' and
k != 'manager')
info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys)
return "<%s %s>" % (self.__class__.__name__, info)

View File

@ -0,0 +1,52 @@
#
# Copyright 2015 Red Hat, 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 mock
from openstackclient.tests import utils
from ironicclient.tests.unit.osc import fakes
baremetal_uuid = 'xxx-xxxxxx-xxxx'
baremetal_name = 'fake name'
baremetal_instance_uuid = 'yyy-yyyyyy-yyyy'
baremetal_power_state = None
baremetal_provision_state = None
baremetal_maintenance = False
BAREMETAL = {
'uuid': baremetal_uuid,
'name': baremetal_name,
'instance_uuid': baremetal_instance_uuid,
'power_state': baremetal_power_state,
'provision_state': baremetal_provision_state,
'maintenance': baremetal_maintenance,
'links': []
}
class TestBaremetal(utils.TestCommand):
def setUp(self):
super(TestBaremetal, self).setUp()
self.app.client_manager.auth_ref = mock.Mock(auth_token="TOKEN")
self.app.client_manager.baremetal = mock.Mock()
class FakeBaremetalResource(fakes.FakeResource):
def get_keys(self):
return {'property': 'value'}

View File

@ -0,0 +1,515 @@
#
# Copyright 2015 Red Hat, 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 openstackclient.tests import utils as oscutils
from ironicclient.osc.v1 import baremetal
from ironicclient.tests.unit.osc.v1 import fakes as baremetal_fakes
class TestBaremetal(baremetal_fakes.TestBaremetal):
def setUp(self):
super(TestBaremetal, self).setUp()
# Get a shortcut to the FlavorManager Mock
self.baremetal_mock = self.app.client_manager.baremetal
self.baremetal_mock.reset_mock()
class TestBaremetalCreate(TestBaremetal):
def setUp(self):
super(TestBaremetalCreate, self).setUp()
self.baremetal_mock.node.create.return_value = (
baremetal_fakes.FakeBaremetalResource(
None,
copy.deepcopy(baremetal_fakes.BAREMETAL),
loaded=True,
))
# Get the command object to test
self.cmd = baremetal.CreateBaremetal(self.app, None)
self.arglist = ['--driver', 'fake_driver']
self.verifylist = [('driver', 'fake_driver')]
self.collist = (
'instance_uuid',
'maintenance',
'name',
'power_state',
'provision_state',
'uuid'
)
self.datalist = (
'yyy-yyyyyy-yyyy',
baremetal_fakes.baremetal_maintenance,
baremetal_fakes.baremetal_name,
baremetal_fakes.baremetal_power_state,
baremetal_fakes.baremetal_provision_state,
baremetal_fakes.baremetal_uuid,
)
self.actual_kwargs = {
'driver': 'fake_driver',
}
def check_with_options(self, addl_arglist, addl_verifylist, addl_kwargs):
arglist = copy.copy(self.arglist) + addl_arglist
verifylist = copy.copy(self.verifylist) + addl_verifylist
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# DisplayCommandBase.take_action() returns two tuples
columns, data = self.cmd.take_action(parsed_args)
collist = copy.copy(self.collist)
self.assertEqual(collist, columns)
datalist = copy.copy(self.datalist)
self.assertEqual(datalist, tuple(data))
kwargs = copy.copy(self.actual_kwargs)
kwargs.update(addl_kwargs)
self.baremetal_mock.node.create.assert_called_once_with(**kwargs)
def test_baremetal_create_no_options(self):
arglist = []
verifylist = []
self.assertRaises(oscutils.ParserException,
self.check_parser,
self.cmd, arglist, verifylist)
def test_baremetal_create_with_driver(self):
arglist = copy.copy(self.arglist)
verifylist = copy.copy(self.verifylist)
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# DisplayCommandBase.take_action() returns two tuples
columns, data = self.cmd.take_action(parsed_args)
collist = copy.copy(self.collist)
self.assertEqual(collist, columns)
datalist = copy.copy(self.datalist)
self.assertEqual(datalist, tuple(data))
kwargs = copy.copy(self.actual_kwargs)
self.baremetal_mock.node.create.assert_called_once_with(**kwargs)
def test_baremetal_create_with_chassis(self):
self.check_with_options(['--chassis', 'chassis_uuid'],
[('chassis_uuid', 'chassis_uuid')],
{'chassis_uuid': 'chassis_uuid'})
def test_baremetal_create_with_driver_info(self):
self.check_with_options(['--driver-info', 'arg1=val1',
'--driver-info', 'arg2=val2'],
[('driver_info',
['arg1=val1',
'arg2=val2'])],
{'driver_info': {
'arg1': 'val1',
'arg2': 'val2'}})
def test_baremetal_create_with_properties(self):
self.check_with_options(['--property', 'arg1=val1',
'--property', 'arg2=val2'],
[('properties',
['arg1=val1',
'arg2=val2'])],
{'properties': {
'arg1': 'val1',
'arg2': 'val2'}})
def test_baremetal_create_with_extra(self):
self.check_with_options(['--extra', 'arg1=val1',
'--extra', 'arg2=val2'],
[('extra',
['arg1=val1',
'arg2=val2'])],
{'extra': {
'arg1': 'val1',
'arg2': 'val2'}})
def test_baremetal_create_with_uuid(self):
self.check_with_options(['--uuid', 'uuid'],
[('uuid', 'uuid')],
{'uuid': 'uuid'})
def test_baremetal_create_with_name(self):
self.check_with_options(['--name', 'name'],
[('name', 'name')],
{'name': 'name'})
class TestBaremetalDelete(TestBaremetal):
def setUp(self):
super(TestBaremetalDelete, self).setUp()
self.baremetal_mock.node.get.return_value = (
baremetal_fakes.FakeBaremetalResource(
None,
copy.deepcopy(baremetal_fakes.BAREMETAL),
loaded=True,
))
# Get the command object to test
self.cmd = baremetal.DeleteBaremetal(self.app, None)
def test_baremetal_delete(self):
arglist = ['xxx-xxxxxx-xxxx']
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
# Set expected values
args = ['xxx-xxxxxx-xxxx']
self.baremetal_mock.node.delete.assert_called_with(
*args
)
class TestBaremetalList(TestBaremetal):
def setUp(self):
super(TestBaremetalList, self).setUp()
self.baremetal_mock.node.list.return_value = [
baremetal_fakes.FakeBaremetalResource(
None,
copy.deepcopy(baremetal_fakes.BAREMETAL),
loaded=True,
),
]
# Get the command object to test
self.cmd = baremetal.ListBaremetal(self.app, None)
def test_baremetal_list_no_options(self):
arglist = []
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# DisplayCommandBase.take_action() returns two tuples
columns, data = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
'detail': False,
'marker': None,
'limit': None,
}
self.baremetal_mock.node.list.assert_called_with(
**kwargs
)
collist = (
"UUID",
"Name",
"Instance UUID",
"Power State",
"Provisioning State",
"Maintenance"
)
self.assertEqual(collist, columns)
datalist = ((
baremetal_fakes.baremetal_uuid,
baremetal_fakes.baremetal_name,
baremetal_fakes.baremetal_instance_uuid,
baremetal_fakes.baremetal_power_state,
baremetal_fakes.baremetal_provision_state,
baremetal_fakes.baremetal_maintenance,
), )
self.assertEqual(datalist, tuple(data))
def test_baremetal_list_long(self):
arglist = [
'--long',
]
verifylist = [
('long', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# DisplayCommandBase.take_action() returns two tuples
columns, data = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
'detail': True,
'marker': None,
'limit': None,
}
self.baremetal_mock.node.list.assert_called_with(
**kwargs
)
collist = ('Chassis UUID', 'Created At', 'Clean Step',
'Console Enabled', 'Driver', 'Driver Info',
'Driver Internal Info', 'Extra', 'Instance Info',
'Instance UUID', 'Last Error', 'Maintenance',
'Maintenance Reason', 'Power State', 'Properties',
'Provisioning State', 'Provision Updated At', 'Reservation',
'Target Power State', 'Target Provision State',
'Updated At', 'Inspection Finished At',
'Inspection Started At', 'UUID', 'Name')
self.assertEqual(collist, columns)
datalist = ((
'',
'',
'',
'',
'',
'',
'',
'',
'',
baremetal_fakes.baremetal_instance_uuid,
'',
baremetal_fakes.baremetal_maintenance,
'',
baremetal_fakes.baremetal_power_state,
'',
baremetal_fakes.baremetal_provision_state,
'',
'',
'',
'',
'',
'',
'',
baremetal_fakes.baremetal_uuid,
baremetal_fakes.baremetal_name,
), )
self.assertEqual(datalist, tuple(data))
class TestBaremetalSet(TestBaremetal):
def setUp(self):
super(TestBaremetalSet, self).setUp()
self.baremetal_mock.node.update.return_value = (
baremetal_fakes.FakeBaremetalResource(
None,
copy.deepcopy(baremetal_fakes.BAREMETAL),
loaded=True,
))
# Get the command object to test
self.cmd = baremetal.SetBaremetal(self.app, None)
def test_baremetal_set_no_options(self):
arglist = []
verifylist = []
self.assertRaises(oscutils.ParserException,
self.check_parser,
self.cmd, arglist, verifylist)
def test_baremetal_set_one_property(self):
arglist = ['node_uuid', '--property', 'path/to/property=value']
verifylist = [
('node', 'node_uuid'),
('property', ['path/to/property=value']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.baremetal_mock.node.update.assert_called_once_with(
'node_uuid',
[{'path': '/path/to/property', 'value': 'value', 'op': 'add'}])
def test_baremetal_set_multiple_properties(self):
arglist = [
'node_uuid',
'--property', 'path/to/property=value',
'--property', 'other/path=value2'
]
verifylist = [
('node', 'node_uuid'),
('property',
[
'path/to/property=value',
'other/path=value2',
]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.baremetal_mock.node.update.assert_called_once_with(
'node_uuid',
[{'path': '/path/to/property', 'value': 'value', 'op': 'add'},
{'path': '/other/path', 'value': 'value2', 'op': 'add'}]
)
class TestBaremetalShow(TestBaremetal):
def setUp(self):
super(TestBaremetalShow, self).setUp()
self.baremetal_mock.node.get.return_value = (
baremetal_fakes.FakeBaremetalResource(
None,
copy.deepcopy(baremetal_fakes.BAREMETAL),
loaded=True,
))
self.baremetal_mock.node.get_by_instance_uuid.return_value = (
baremetal_fakes.FakeBaremetalResource(
None,
copy.deepcopy(baremetal_fakes.BAREMETAL),
loaded=True,
))
# Get the command object to test
self.cmd = baremetal.ShowBaremetal(self.app, None)
def test_baremetal_show(self):
arglist = ['xxx-xxxxxx-xxxx']
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# DisplayCommandBase.take_action() returns two tuples
columns, data = self.cmd.take_action(parsed_args)
# Set expected values
args = ['xxx-xxxxxx-xxxx']
self.baremetal_mock.node.get.assert_called_with(
*args
)
collist = (
'instance_uuid',
'maintenance',
'name',
'power_state',
'provision_state',
'uuid'
)
self.assertEqual(collist, columns)
datalist = (
'yyy-yyyyyy-yyyy',
baremetal_fakes.baremetal_maintenance,
baremetal_fakes.baremetal_name,
baremetal_fakes.baremetal_power_state,
baremetal_fakes.baremetal_provision_state,
baremetal_fakes.baremetal_uuid
)
self.assertEqual(datalist, tuple(data))
def test_baremetal_show_no_node(self):
arglist = []
verifylist = []
self.assertRaises(oscutils.ParserException,
self.check_parser,
self.cmd, arglist, verifylist)
def test_baremetal_show_with_instance_uuid(self):
arglist = [
'xxx-xxxxxx-xxxx',
'--instance',
]
verifylist = [
('instance_uuid', True)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# DisplayCommandBase.take_action() returns two tuples
columns, data = self.cmd.take_action(parsed_args)
# Set expected values
args = ['xxx-xxxxxx-xxxx']
self.baremetal_mock.node.get_by_instance_uuid.assert_called_with(
*args
)
class TestBaremetalUnset(TestBaremetal):
def setUp(self):
super(TestBaremetalUnset, self).setUp()
self.baremetal_mock.node.update.return_value = (
baremetal_fakes.FakeBaremetalResource(
None,
copy.deepcopy(baremetal_fakes.BAREMETAL),
loaded=True,
))
# Get the command object to test
self.cmd = baremetal.UnsetBaremetal(self.app, None)
def test_baremetal_unset_no_options(self):
arglist = []
verifylist = []
self.assertRaises(oscutils.ParserException,
self.check_parser,
self.cmd, arglist, verifylist)
def test_baremetal_unset_one_property(self):
arglist = ['node_uuid', '--property', 'path/to/property']
verifylist = [('node', 'node_uuid'),
('property', ['path/to/property'])]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.baremetal_mock.node.update.assert_called_once_with(
'node_uuid',
[{'path': '/path/to/property', 'op': 'remove'}])
def test_baremetal_unset_multiple_properties(self):
arglist = ['node_uuid',
'--property', 'path/to/property',
'--property', 'other/path']
verifylist = [('node', 'node_uuid'),
('property',
['path/to/property',
'other/path'])]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.baremetal_mock.node.update.assert_called_once_with(
'node_uuid',
[{'path': '/path/to/property', 'op': 'remove'},
{'path': '/other/path', 'op': 'remove'}]
)

View File

@ -5,10 +5,12 @@ pbr<2.0,>=1.4
anyjson>=0.3.3
appdirs>=1.3.0 # MIT License
dogpile.cache>=0.5.4
cliff>=1.14.0 # Apache-2.0
httplib2>=0.7.5
lxml>=2.3
oslo.i18n>=1.5.0 # Apache-2.0
oslo.utils>=2.0.0 # Apache-2.0
PrettyTable<0.8,>=0.7
python-keystoneclient>=1.6.0
python-openstackclient>=1.5.0
six>=1.9.0

View File

@ -25,6 +25,17 @@ packages = ironicclient
console_scripts =
ironic = ironicclient.shell:main
openstack.cli.extension =
baremetal = ironicclient.osc.plugin
openstack.baremetal.v1 =
baremetal_create = ironicclient.osc.v1.baremetal:CreateBaremetal
baremetal_delete = ironicclient.osc.v1.baremetal:DeleteBaremetal
baremetal_list = ironicclient.osc.v1.baremetal:ListBaremetal
baremetal_set = ironicclient.osc.v1.baremetal:SetBaremetal
baremetal_show = ironicclient.osc.v1.baremetal:ShowBaremetal
baremetal_unset = ironicclient.osc.v1.baremetal:UnsetBaremetal
[pbr]
autodoc_index_modules = True