Add CLI support for runbooks
Enable CRUD support, manual cleaning and servicing with runbooks from the command line interface. Demo Video: https://youtu.be/00PJS4SXFYQ Change-Id: Iec672c505a245991db72afbb9b668220f845ca81
This commit is contained in:
parent
85542f0caf
commit
1035b2b238
@ -37,7 +37,7 @@ from ironicclient import exc
|
||||
# http://specs.openstack.org/openstack/ironic-specs/specs/kilo/api-microversions.html # noqa
|
||||
# for full details.
|
||||
DEFAULT_VER = '1.9'
|
||||
LAST_KNOWN_API_VERSION = 88
|
||||
LAST_KNOWN_API_VERSION = 92
|
||||
LATEST_VERSION = '1.{}'.format(LAST_KNOWN_API_VERSION)
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
@ -214,7 +214,7 @@ def common_params_for_list(args, fields, field_labels):
|
||||
|
||||
|
||||
def common_filters(marker=None, limit=None, sort_key=None, sort_dir=None,
|
||||
fields=None, detail=False):
|
||||
fields=None, detail=False, project=None, public=None):
|
||||
"""Generate common filters for any list request.
|
||||
|
||||
:param marker: entity ID from which to start returning entities.
|
||||
@ -237,6 +237,10 @@ def common_filters(marker=None, limit=None, sort_key=None, sort_dir=None,
|
||||
filters.append('sort_key=%s' % sort_key)
|
||||
if sort_dir is not None:
|
||||
filters.append('sort_dir=%s' % sort_dir)
|
||||
if project is not None:
|
||||
filters.append('project=%s' % project)
|
||||
if public is not None:
|
||||
filters.append('public=True')
|
||||
if fields is not None:
|
||||
filters.append('fields=%s' % ','.join(fields))
|
||||
if detail:
|
||||
|
@ -80,6 +80,8 @@ class ProvisionStateBaremetalNode(command.Command):
|
||||
|
||||
baremetal_client = self.app.client_manager.baremetal
|
||||
|
||||
runbook = getattr(parsed_args, 'runbook', None)
|
||||
|
||||
clean_steps = getattr(parsed_args, 'clean_steps', None)
|
||||
clean_steps = utils.handle_json_arg(clean_steps, 'clean steps')
|
||||
|
||||
@ -109,7 +111,8 @@ class ProvisionStateBaremetalNode(command.Command):
|
||||
cleansteps=clean_steps,
|
||||
deploysteps=deploy_steps,
|
||||
rescue_password=rescue_password,
|
||||
servicesteps=service_steps)
|
||||
servicesteps=service_steps,
|
||||
runbook=runbook)
|
||||
|
||||
|
||||
class ProvisionStateWithWait(ProvisionStateBaremetalNode):
|
||||
@ -289,18 +292,22 @@ class CleanBaremetalNode(ProvisionStateWithWait):
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CleanBaremetalNode, self).get_parser(prog_name)
|
||||
clean_group = parser.add_mutually_exclusive_group(required=True)
|
||||
|
||||
parser.add_argument(
|
||||
clean_group.add_argument(
|
||||
'--clean-steps',
|
||||
metavar='<clean-steps>',
|
||||
required=True,
|
||||
default=None,
|
||||
help=_("The clean steps. May be the path to a YAML file "
|
||||
"containing the clean steps; OR '-', with the clean steps "
|
||||
"being read from standard input; OR a JSON string. The "
|
||||
"value should be a list of clean-step dictionaries; each "
|
||||
"dictionary should have keys 'interface' and 'step', and "
|
||||
"optional key 'args'."))
|
||||
clean_group.add_argument(
|
||||
'--runbook',
|
||||
metavar='<runbook>',
|
||||
help=_("The identifier of a predefined runbook to use for "
|
||||
"cleaning."))
|
||||
return parser
|
||||
|
||||
|
||||
@ -312,18 +319,22 @@ class ServiceBaremetalNode(ProvisionStateWithWait):
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ServiceBaremetalNode, self).get_parser(prog_name)
|
||||
service_group = parser.add_mutually_exclusive_group(required=True)
|
||||
|
||||
parser.add_argument(
|
||||
service_group.add_argument(
|
||||
'--service-steps',
|
||||
metavar='<service-steps>',
|
||||
required=True,
|
||||
default=None,
|
||||
help=_("The service steps. May be the path to a YAML file "
|
||||
"containing the service steps; OR '-', with the service "
|
||||
" steps being read from standard input; OR a JSON string. "
|
||||
"The value should be a list of service-step dictionaries; "
|
||||
"each dictionary should have keys 'interface' and 'step', "
|
||||
"and optional key 'args'."))
|
||||
service_group.add_argument(
|
||||
'--runbook',
|
||||
metavar='<runbook>',
|
||||
help=_("The identifier of a predefined runbook to use for "
|
||||
"servicing."))
|
||||
return parser
|
||||
|
||||
|
||||
|
403
ironicclient/osc/v1/baremetal_runbook.py
Normal file
403
ironicclient/osc/v1/baremetal_runbook.py
Normal file
@ -0,0 +1,403 @@
|
||||
# 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 itertools
|
||||
import json
|
||||
import logging
|
||||
|
||||
from osc_lib.command import command
|
||||
from osc_lib 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
|
||||
|
||||
|
||||
_RUNBOOK_STEPS_HELP = _(
|
||||
"The runbook steps. May be the path to a YAML file containing the "
|
||||
"runbook steps; OR '-', with the runbook steps being read from standard "
|
||||
"input; OR a JSON string. The value should be a list of runbook step "
|
||||
"dictionaries; each dictionary should have keys 'interface', 'step', "
|
||||
"'args' and 'order'.")
|
||||
|
||||
|
||||
class CreateBaremetalRunbook(command.ShowOne):
|
||||
"""Create a new runbook"""
|
||||
|
||||
log = logging.getLogger(__name__ + ".CreateBaremetalRunbook")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CreateBaremetalRunbook, self).get_parser(
|
||||
prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'--name',
|
||||
metavar='<name>',
|
||||
required=True,
|
||||
help=_('Unique name for this runbook. Must be a valid '
|
||||
'trait name')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--uuid',
|
||||
dest='uuid',
|
||||
metavar='<uuid>',
|
||||
help=_('UUID of the runbook.'))
|
||||
parser.add_argument(
|
||||
'--public',
|
||||
metavar='<public>',
|
||||
help=_('Whether the runbook will be private or public.')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--owner',
|
||||
metavar='<owner>',
|
||||
help=_('Owner of the runbook.')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--extra',
|
||||
metavar="<key=value>",
|
||||
action='append',
|
||||
help=_("Record arbitrary key/value metadata. "
|
||||
"Can be specified multiple times."))
|
||||
parser.add_argument(
|
||||
'--steps',
|
||||
metavar="<steps>",
|
||||
required=True,
|
||||
help=_RUNBOOK_STEPS_HELP
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.log.debug("take_action(%s)", parsed_args)
|
||||
baremetal_client = self.app.client_manager.baremetal
|
||||
|
||||
steps = utils.handle_json_arg(parsed_args.steps, 'runbook steps')
|
||||
|
||||
field_list = ['name', 'uuid', 'owner', 'public', 'extra']
|
||||
fields = dict((k, v) for (k, v) in vars(parsed_args).items()
|
||||
if k in field_list and v is not None)
|
||||
fields = utils.args_array_to_dict(fields, 'extra')
|
||||
runbook = baremetal_client.runbook.create(steps=steps,
|
||||
**fields)
|
||||
|
||||
data = dict([(f, getattr(runbook, f, '')) for f in
|
||||
res_fields.RUNBOOK_DETAILED_RESOURCE.fields])
|
||||
|
||||
return self.dict2columns(data)
|
||||
|
||||
|
||||
class ShowBaremetalRunbook(command.ShowOne):
|
||||
"""Show baremetal runbook details."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ShowBaremetalRunbook")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ShowBaremetalRunbook, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
"runbook",
|
||||
metavar="<runbook>",
|
||||
help=_("Name or UUID of the runbook.")
|
||||
)
|
||||
parser.add_argument(
|
||||
'--fields',
|
||||
nargs='+',
|
||||
dest='fields',
|
||||
metavar='<field>',
|
||||
action='append',
|
||||
choices=res_fields.RUNBOOK_DETAILED_RESOURCE.fields,
|
||||
default=[],
|
||||
help=_("One or more runbook fields. Only these fields "
|
||||
"will be fetched from the server.")
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.log.debug("take_action(%s)", parsed_args)
|
||||
|
||||
baremetal_client = self.app.client_manager.baremetal
|
||||
fields = list(itertools.chain.from_iterable(parsed_args.fields))
|
||||
fields = fields if fields else None
|
||||
|
||||
runbook = baremetal_client.runbook.get(
|
||||
parsed_args.runbook, fields=fields)._info
|
||||
|
||||
runbook.pop("links", None)
|
||||
return zip(*sorted(runbook.items()))
|
||||
|
||||
|
||||
class SetBaremetalRunbook(command.Command):
|
||||
"""Set baremetal runbook properties."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".SetBaremetalRunbook")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(SetBaremetalRunbook, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'runbook',
|
||||
metavar='<runbook>',
|
||||
help=_("Name or UUID of the runbook")
|
||||
)
|
||||
parser.add_argument(
|
||||
'--name',
|
||||
metavar='<name>',
|
||||
help=_('Set unique name of the runbook. Must be a valid '
|
||||
'trait name.')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--public',
|
||||
metavar='<public>',
|
||||
help=_('Make a private runbook public.')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--owner',
|
||||
metavar='<owner>',
|
||||
help=_('Set owner of a runbook.')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--steps',
|
||||
metavar="<steps>",
|
||||
help=_RUNBOOK_STEPS_HELP
|
||||
)
|
||||
parser.add_argument(
|
||||
"--extra",
|
||||
metavar="<key=value>",
|
||||
action='append',
|
||||
help=_('Extra to set on this baremetal runbook '
|
||||
'(repeat option to set multiple extras).'),
|
||||
)
|
||||
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.name:
|
||||
name = ["name=%s" % parsed_args.name]
|
||||
properties.extend(utils.args_array_to_patch('add', name))
|
||||
if parsed_args.owner:
|
||||
owner = ["owner=%s" % parsed_args.owner]
|
||||
properties.extend(utils.args_array_to_patch('add', owner))
|
||||
if parsed_args.public:
|
||||
public = ["public=%s" % parsed_args.public]
|
||||
properties.extend(utils.args_array_to_patch('add', public))
|
||||
if parsed_args.steps:
|
||||
steps = utils.handle_json_arg(parsed_args.steps, 'runbook steps')
|
||||
steps = ["steps=%s" % json.dumps(steps)]
|
||||
properties.extend(utils.args_array_to_patch('add', steps))
|
||||
if parsed_args.extra:
|
||||
properties.extend(utils.args_array_to_patch(
|
||||
'add', ['extra/' + x for x in parsed_args.extra]))
|
||||
|
||||
if properties:
|
||||
baremetal_client.runbook.update(parsed_args.runbook,
|
||||
properties)
|
||||
else:
|
||||
self.log.warning("Please specify what to set.")
|
||||
|
||||
|
||||
class UnsetBaremetalRunbook(command.Command):
|
||||
"""Unset baremetal runbook properties."""
|
||||
log = logging.getLogger(__name__ + ".UnsetBaremetalRunbook")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(UnsetBaremetalRunbook, self).get_parser(
|
||||
prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'runbook',
|
||||
metavar='<runbook>',
|
||||
help=_("Name or UUID of the runbook")
|
||||
)
|
||||
parser.add_argument(
|
||||
'--name',
|
||||
action='store_true',
|
||||
help=_('Unset the name of the runbook.')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--public',
|
||||
dest='public',
|
||||
action='store_true',
|
||||
help=_('Make a public runbook private.')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--owner',
|
||||
dest='owner',
|
||||
action='store_true',
|
||||
help=_('Unset owner of a runbook.')
|
||||
)
|
||||
parser.add_argument(
|
||||
"--step",
|
||||
metavar="<key>",
|
||||
action='append',
|
||||
help=_('Step to unset on this baremetal runbook '
|
||||
'(repeat option to unset multiple steps).'),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--extra",
|
||||
metavar="<key>",
|
||||
action='append',
|
||||
help=_('Extra to unset on this baremetal runbook '
|
||||
'(repeat option to unset multiple extras).'),
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.log.debug("take_action(%s)", parsed_args)
|
||||
|
||||
baremetal_client = self.app.client_manager.baremetal
|
||||
|
||||
properties = []
|
||||
for field in ['name', 'owner', 'public']:
|
||||
if getattr(parsed_args, field):
|
||||
properties.extend(utils.args_array_to_patch('remove', [field]))
|
||||
|
||||
if parsed_args.extra:
|
||||
properties.extend(utils.args_array_to_patch('remove',
|
||||
['extra/' + x for x in parsed_args.extra]))
|
||||
if parsed_args.step:
|
||||
properties.extend(utils.args_array_to_patch('remove',
|
||||
['step/' + x for x in parsed_args.step]))
|
||||
|
||||
if properties:
|
||||
baremetal_client.runbook.update(parsed_args.runbook,
|
||||
properties)
|
||||
else:
|
||||
self.log.warning("Please specify what to unset.")
|
||||
|
||||
|
||||
class DeleteBaremetalRunbook(command.Command):
|
||||
"""Delete runbook(s)."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".DeleteBaremetalRunbook")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(DeleteBaremetalRunbook, self).get_parser(
|
||||
prog_name)
|
||||
parser.add_argument(
|
||||
"runbooks",
|
||||
metavar="<runbook>",
|
||||
nargs="+",
|
||||
help=_("Name(s) or UUID(s) of the runbook(s) to delete.")
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.log.debug("take_action(%s)", parsed_args)
|
||||
|
||||
baremetal_client = self.app.client_manager.baremetal
|
||||
|
||||
failures = []
|
||||
for runbook in parsed_args.runbooks:
|
||||
try:
|
||||
baremetal_client.runbook.delete(runbook)
|
||||
print(_('Deleted runbook %s') % runbook)
|
||||
except exc.ClientException as e:
|
||||
failures.append(_("Failed to delete runbook "
|
||||
"%(runbook)s: %(error)s")
|
||||
% {'runbook': runbook, 'error': e})
|
||||
|
||||
if failures:
|
||||
raise exc.ClientException("\n".join(failures))
|
||||
|
||||
|
||||
class ListBaremetalRunbook(command.Lister):
|
||||
"""List baremetal runbooks."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ListBaremetalRunbook")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListBaremetalRunbook, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--limit',
|
||||
metavar='<limit>',
|
||||
type=int,
|
||||
help=_('Maximum number of runbooks to return per request, '
|
||||
'0 for no limit. Default is the maximum number used '
|
||||
'by the Baremetal API Service.')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--marker',
|
||||
metavar='<runbook>',
|
||||
help=_('Runbook UUID (for example, of the last runbook '
|
||||
'in the list from a previous request). Returns '
|
||||
'the list of runbooks after this UUID.')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--sort',
|
||||
metavar="<key>[:<direction>]",
|
||||
help=_('Sort output by specified runbook fields and '
|
||||
'directions (asc or desc) (default: asc). Multiple fields '
|
||||
'and directions can be specified, separated by comma.')
|
||||
)
|
||||
display_group = parser.add_mutually_exclusive_group()
|
||||
display_group.add_argument(
|
||||
'--long',
|
||||
dest='detail',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help=_("Show detailed information about runbooks.")
|
||||
)
|
||||
display_group.add_argument(
|
||||
'--fields',
|
||||
nargs='+',
|
||||
dest='fields',
|
||||
metavar='<field>',
|
||||
action='append',
|
||||
default=[],
|
||||
choices=res_fields.RUNBOOK_DETAILED_RESOURCE.fields,
|
||||
help=_("One or more runbook fields. Only these fields "
|
||||
"will be fetched from the server. Can not be used when "
|
||||
"'--long' is specified.")
|
||||
)
|
||||
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.RUNBOOK_RESOURCE.fields
|
||||
labels = res_fields.RUNBOOK_RESOURCE.labels
|
||||
|
||||
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.detail:
|
||||
params['detail'] = parsed_args.detail
|
||||
columns = res_fields.RUNBOOK_DETAILED_RESOURCE.fields
|
||||
labels = res_fields.RUNBOOK_DETAILED_RESOURCE.labels
|
||||
|
||||
elif parsed_args.fields:
|
||||
params['detail'] = False
|
||||
fields = itertools.chain.from_iterable(parsed_args.fields)
|
||||
resource = res_fields.Resource(list(fields))
|
||||
columns = resource.fields
|
||||
labels = resource.labels
|
||||
params['fields'] = columns
|
||||
|
||||
self.log.debug("params(%s)", params)
|
||||
data = client.runbook.list(**params)
|
||||
|
||||
data = oscutils.sort_items(data, parsed_args.sort)
|
||||
|
||||
return (labels,
|
||||
(oscutils.get_item_properties(s, columns) for s in data))
|
@ -235,6 +235,26 @@ DEPLOY_TEMPLATE = {
|
||||
'steps': baremetal_deploy_template_steps,
|
||||
'extra': baremetal_deploy_template_extra,
|
||||
}
|
||||
|
||||
baremetal_runbook_uuid = 'ddd-tttttt-dddd'
|
||||
baremetal_runbook_name = 'CUSTOM_AWESOME'
|
||||
baremetal_runbook_owner = 'some_user'
|
||||
baremetal_runbook_public = False
|
||||
baremetal_runbook_steps = json.dumps([{
|
||||
'interface': 'raid',
|
||||
'step': 'create_configuration',
|
||||
'args': {},
|
||||
'order': 1
|
||||
}])
|
||||
baremetal_runbook_extra = {'key1': 'value1', 'key2': 'value2'}
|
||||
RUNBOOK = {
|
||||
'uuid': baremetal_runbook_uuid,
|
||||
'name': baremetal_runbook_name,
|
||||
'owner': baremetal_runbook_owner,
|
||||
'public': baremetal_runbook_public,
|
||||
'steps': baremetal_runbook_steps,
|
||||
'extra': baremetal_runbook_extra,
|
||||
}
|
||||
NODE_HISTORY = [
|
||||
{
|
||||
'uuid': 'abcdef1',
|
||||
|
@ -59,7 +59,8 @@ class TestAbort(TestBaremetal):
|
||||
|
||||
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
|
||||
'node_uuid', 'abort', cleansteps=None, configdrive=None,
|
||||
deploysteps=None, rescue_password=None, servicesteps=None)
|
||||
deploysteps=None, rescue_password=None, servicesteps=None,
|
||||
runbook=None)
|
||||
|
||||
|
||||
class TestAdopt(TestBaremetal):
|
||||
@ -83,7 +84,7 @@ class TestAdopt(TestBaremetal):
|
||||
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
|
||||
'node_uuid', 'adopt',
|
||||
cleansteps=None, deploysteps=None, configdrive=None,
|
||||
rescue_password=None, servicesteps=None)
|
||||
rescue_password=None, servicesteps=None, runbook=None)
|
||||
self.baremetal_mock.node.wait_for_provision_state.assert_not_called()
|
||||
|
||||
def test_adopt_baremetal_provision_state_active_and_wait(self):
|
||||
@ -103,7 +104,7 @@ class TestAdopt(TestBaremetal):
|
||||
test_node.set_provision_state.assert_called_once_with(
|
||||
'node_uuid', 'adopt',
|
||||
cleansteps=None, deploysteps=None, configdrive=None,
|
||||
rescue_password=None, servicesteps=None)
|
||||
rescue_password=None, servicesteps=None, runbook=None)
|
||||
test_node.wait_for_provision_state.assert_called_once_with(
|
||||
['node_uuid'], expected_state='active',
|
||||
poll_interval=2, timeout=15)
|
||||
@ -125,7 +126,7 @@ class TestAdopt(TestBaremetal):
|
||||
test_node.set_provision_state.assert_called_once_with(
|
||||
'node_uuid', 'adopt',
|
||||
cleansteps=None, deploysteps=None, configdrive=None,
|
||||
rescue_password=None, servicesteps=None)
|
||||
rescue_password=None, servicesteps=None, runbook=None)
|
||||
test_node.wait_for_provision_state.assert_called_once_with(
|
||||
['node_uuid'], expected_state='active',
|
||||
poll_interval=2, timeout=0)
|
||||
@ -174,7 +175,55 @@ class TestClean(TestBaremetal):
|
||||
|
||||
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
|
||||
'node_uuid', 'clean', cleansteps=steps_dict, configdrive=None,
|
||||
deploysteps=None, rescue_password=None, servicesteps=None)
|
||||
deploysteps=None, rescue_password=None, servicesteps=None,
|
||||
runbook=None)
|
||||
|
||||
def test_clean_with_runbook(self):
|
||||
runbook_name = 'runbook_name'
|
||||
arglist = ['--runbook', runbook_name, 'node_uuid']
|
||||
verifylist = [
|
||||
('runbook', runbook_name),
|
||||
('provision_state', 'clean'),
|
||||
('nodes', ['node_uuid']),
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
|
||||
'node_uuid', 'clean', cleansteps=None, configdrive=None,
|
||||
deploysteps=None, rescue_password=None, servicesteps=None,
|
||||
runbook=runbook_name)
|
||||
|
||||
def test_clean_with_runbook_and_steps(self):
|
||||
runbook_name = 'runbook_name'
|
||||
|
||||
steps_dict = {
|
||||
"clean_steps": [{
|
||||
"interface": "raid",
|
||||
"step": "create_configuration",
|
||||
"args": {"create_nonroot_volumes": False}
|
||||
}, {
|
||||
"interface": "deploy",
|
||||
"step": "erase_devices"
|
||||
}]
|
||||
}
|
||||
steps_json = json.dumps(steps_dict)
|
||||
|
||||
arglist = ['--runbook', runbook_name, '--clean-steps', steps_json,
|
||||
'node_uuid']
|
||||
|
||||
verifylist = [
|
||||
('clean_steps', steps_json),
|
||||
('runbook', runbook_name),
|
||||
('provision_state', 'clean'),
|
||||
('nodes', ['node_uuid']),
|
||||
]
|
||||
|
||||
self.assertRaises(oscutils.ParserException,
|
||||
self.check_parser,
|
||||
self.cmd, arglist, verifylist)
|
||||
|
||||
|
||||
class TestService(TestBaremetal):
|
||||
@ -210,7 +259,54 @@ class TestService(TestBaremetal):
|
||||
|
||||
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
|
||||
'node_uuid', 'service', cleansteps=None, configdrive=None,
|
||||
deploysteps=None, rescue_password=None, servicesteps=steps_dict)
|
||||
deploysteps=None, rescue_password=None, servicesteps=steps_dict,
|
||||
runbook=None)
|
||||
|
||||
def test_service_with_runbook(self):
|
||||
runbook_name = 'runbook_name'
|
||||
arglist = ['--runbook', runbook_name, 'node_uuid']
|
||||
verifylist = [
|
||||
('runbook', runbook_name),
|
||||
('provision_state', 'service'),
|
||||
('nodes', ['node_uuid']),
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
|
||||
'node_uuid', 'service', cleansteps=None, configdrive=None,
|
||||
deploysteps=None, rescue_password=None, servicesteps=None,
|
||||
runbook=runbook_name)
|
||||
|
||||
def test_service_with_runbook_and_steps(self):
|
||||
runbook_name = 'runbook_name'
|
||||
steps_dict = {
|
||||
"service_steps": [{
|
||||
"interface": "raid",
|
||||
"step": "create_configuration",
|
||||
"args": {"create_nonroot_volumes": False}
|
||||
}, {
|
||||
"interface": "deploy",
|
||||
"step": "erase_devices"
|
||||
}]
|
||||
}
|
||||
steps_json = json.dumps(steps_dict)
|
||||
|
||||
arglist = ['--service-steps', steps_json, '--runbook', runbook_name,
|
||||
'node_uuid']
|
||||
|
||||
verifylist = [
|
||||
('service_steps', steps_json),
|
||||
('runbook', runbook_name),
|
||||
('provision_state', 'service'),
|
||||
('nodes', ['node_uuid']),
|
||||
]
|
||||
|
||||
self.assertRaises(oscutils.ParserException,
|
||||
self.check_parser,
|
||||
self.cmd, arglist, verifylist)
|
||||
|
||||
|
||||
class TestInspect(TestBaremetal):
|
||||
@ -233,7 +329,8 @@ class TestInspect(TestBaremetal):
|
||||
|
||||
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
|
||||
'node_uuid', 'inspect', cleansteps=None, configdrive=None,
|
||||
deploysteps=None, rescue_password=None, servicesteps=None)
|
||||
deploysteps=None, rescue_password=None, servicesteps=None,
|
||||
runbook=None)
|
||||
|
||||
|
||||
class TestManage(TestBaremetal):
|
||||
@ -256,7 +353,8 @@ class TestManage(TestBaremetal):
|
||||
|
||||
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
|
||||
'node_uuid', 'manage', cleansteps=None, configdrive=None,
|
||||
deploysteps=None, rescue_password=None, servicesteps=None)
|
||||
deploysteps=None, rescue_password=None, servicesteps=None,
|
||||
runbook=None)
|
||||
|
||||
|
||||
class TestProvide(TestBaremetal):
|
||||
@ -279,7 +377,8 @@ class TestProvide(TestBaremetal):
|
||||
|
||||
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
|
||||
'node_uuid', 'provide', cleansteps=None, configdrive=None,
|
||||
deploysteps=None, rescue_password=None, servicesteps=None)
|
||||
deploysteps=None, rescue_password=None, servicesteps=None,
|
||||
runbook=None)
|
||||
|
||||
|
||||
class TestRebuild(TestBaremetal):
|
||||
@ -302,7 +401,8 @@ class TestRebuild(TestBaremetal):
|
||||
|
||||
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
|
||||
'node_uuid', 'rebuild', cleansteps=None, configdrive=None,
|
||||
deploysteps=None, rescue_password=None, servicesteps=None)
|
||||
deploysteps=None, rescue_password=None, servicesteps=None,
|
||||
runbook=None)
|
||||
|
||||
|
||||
class TestUndeploy(TestBaremetal):
|
||||
@ -325,7 +425,8 @@ class TestUndeploy(TestBaremetal):
|
||||
|
||||
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
|
||||
'node_uuid', 'deleted', cleansteps=None, configdrive=None,
|
||||
deploysteps=None, rescue_password=None, servicesteps=None)
|
||||
deploysteps=None, rescue_password=None, servicesteps=None,
|
||||
runbook=None)
|
||||
|
||||
|
||||
class TestBootdeviceSet(TestBaremetal):
|
||||
@ -1912,7 +2013,7 @@ class TestDeployBaremetalProvisionState(TestBaremetal):
|
||||
'node_uuid', 'active',
|
||||
cleansteps=None, deploysteps=[{"interface": "deploy"}],
|
||||
configdrive='path/to/drive', rescue_password=None,
|
||||
servicesteps=None)
|
||||
servicesteps=None, runbook=None)
|
||||
|
||||
def test_deploy_baremetal_provision_state_active_and_configdrive_dict(
|
||||
self):
|
||||
@ -1931,7 +2032,7 @@ class TestDeployBaremetalProvisionState(TestBaremetal):
|
||||
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
|
||||
'node_uuid', 'active',
|
||||
cleansteps=None, deploysteps=None, configdrive={'meta_data': {}},
|
||||
rescue_password=None, servicesteps=None)
|
||||
rescue_password=None, servicesteps=None, runbook=None)
|
||||
|
||||
def test_deploy_no_wait(self):
|
||||
arglist = ['node_uuid']
|
||||
@ -1996,7 +2097,7 @@ class TestDeployBaremetalProvisionState(TestBaremetal):
|
||||
test_node.set_provision_state.assert_has_calls([
|
||||
mock.call(n, 'active', cleansteps=None, deploysteps=None,
|
||||
configdrive=None, rescue_password=None,
|
||||
servicesteps=None)
|
||||
servicesteps=None, runbook=None)
|
||||
for n in ['node_uuid', 'node_name']
|
||||
])
|
||||
test_node.wait_for_provision_state.assert_called_once_with(
|
||||
@ -2220,7 +2321,7 @@ class TestRescueBaremetalProvisionState(TestBaremetal):
|
||||
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
|
||||
'node_uuid', 'rescue', cleansteps=None, deploysteps=None,
|
||||
configdrive=None, rescue_password='supersecret',
|
||||
servicesteps=None)
|
||||
servicesteps=None, runbook=None)
|
||||
|
||||
def test_rescue_baremetal_provision_state_rescue_and_wait(self):
|
||||
arglist = ['node_uuid',
|
||||
@ -2412,7 +2513,7 @@ class TestRebuildBaremetalProvisionState(TestBaremetal):
|
||||
'node_uuid', 'rebuild',
|
||||
cleansteps=None, deploysteps=[{"interface": "deploy"}],
|
||||
configdrive='path/to/drive', rescue_password=None,
|
||||
servicesteps=None)
|
||||
servicesteps=None, runbook=None)
|
||||
|
||||
def test_rebuild_no_wait(self):
|
||||
arglist = ['node_uuid']
|
||||
@ -2428,7 +2529,7 @@ class TestRebuildBaremetalProvisionState(TestBaremetal):
|
||||
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
|
||||
'node_uuid', 'rebuild',
|
||||
cleansteps=None, deploysteps=None, configdrive=None,
|
||||
rescue_password=None, servicesteps=None)
|
||||
rescue_password=None, servicesteps=None, runbook=None)
|
||||
|
||||
self.baremetal_mock.node.wait_for_provision_state.assert_not_called()
|
||||
|
||||
@ -2546,7 +2647,8 @@ class TestUnrescueBaremetalProvisionState(TestBaremetal):
|
||||
|
||||
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
|
||||
'node_uuid', 'unrescue', cleansteps=None, deploysteps=None,
|
||||
configdrive=None, rescue_password=None, servicesteps=None)
|
||||
configdrive=None, rescue_password=None, servicesteps=None,
|
||||
runbook=None)
|
||||
|
||||
def test_unrescue_baremetal_provision_state_active_and_wait(self):
|
||||
arglist = ['node_uuid',
|
||||
@ -4661,7 +4763,8 @@ class TestUnholdBaremetalProvisionState(TestBaremetal):
|
||||
|
||||
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
|
||||
'node_uuid', 'unhold', cleansteps=None, deploysteps=None,
|
||||
configdrive=None, rescue_password=None, servicesteps=None)
|
||||
configdrive=None, rescue_password=None, servicesteps=None,
|
||||
runbook=None)
|
||||
|
||||
|
||||
class TestListFirmwareComponents(TestBaremetal):
|
||||
|
462
ironicclient/tests/unit/osc/v1/test_baremetal_runbook.py
Normal file
462
ironicclient/tests/unit/osc/v1/test_baremetal_runbook.py
Normal file
@ -0,0 +1,462 @@
|
||||
# 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
|
||||
import json
|
||||
from unittest import mock
|
||||
|
||||
from osc_lib.tests import utils as osctestutils
|
||||
|
||||
from ironicclient import exc
|
||||
from ironicclient.osc.v1 import baremetal_runbook
|
||||
from ironicclient.tests.unit.osc.v1 import fakes as baremetal_fakes
|
||||
|
||||
|
||||
class TestBaremetalRunbook(baremetal_fakes.TestBaremetal):
|
||||
|
||||
def setUp(self):
|
||||
super(TestBaremetalRunbook, self).setUp()
|
||||
|
||||
self.baremetal_mock = self.app.client_manager.baremetal
|
||||
self.baremetal_mock.reset_mock()
|
||||
|
||||
|
||||
class TestCreateBaremetalRunbook(TestBaremetalRunbook):
|
||||
def setUp(self):
|
||||
super(TestCreateBaremetalRunbook, self).setUp()
|
||||
|
||||
self.baremetal_mock.runbook.create.return_value = (
|
||||
baremetal_fakes.FakeBaremetalResource(
|
||||
None,
|
||||
copy.deepcopy(baremetal_fakes.RUNBOOK),
|
||||
loaded=True,
|
||||
))
|
||||
|
||||
# Get the command object to test
|
||||
self.cmd = baremetal_runbook.CreateBaremetalRunbook(
|
||||
self.app, None)
|
||||
|
||||
def test_baremetal_runbook_create(self):
|
||||
arglist = [
|
||||
'--name', baremetal_fakes.baremetal_runbook_name,
|
||||
'--steps', baremetal_fakes.baremetal_runbook_steps,
|
||||
]
|
||||
|
||||
verifylist = [
|
||||
('name', baremetal_fakes.baremetal_runbook_name),
|
||||
('steps', baremetal_fakes.baremetal_runbook_steps),
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
# Set expected values
|
||||
args = {
|
||||
'name': baremetal_fakes.baremetal_runbook_name,
|
||||
'steps': json.loads(
|
||||
baremetal_fakes.baremetal_runbook_steps),
|
||||
}
|
||||
|
||||
self.baremetal_mock.runbook.create.assert_called_once_with(
|
||||
**args)
|
||||
|
||||
def test_baremetal_runbook_create_uuid(self):
|
||||
arglist = [
|
||||
'--name', baremetal_fakes.baremetal_runbook_name,
|
||||
'--steps', baremetal_fakes.baremetal_runbook_steps,
|
||||
'--uuid', baremetal_fakes.baremetal_runbook_uuid,
|
||||
]
|
||||
|
||||
verifylist = [
|
||||
('name', baremetal_fakes.baremetal_runbook_name),
|
||||
('steps', baremetal_fakes.baremetal_runbook_steps),
|
||||
('uuid', baremetal_fakes.baremetal_runbook_uuid),
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
# Set expected values
|
||||
args = {
|
||||
'name': baremetal_fakes.baremetal_runbook_name,
|
||||
'steps': json.loads(
|
||||
baremetal_fakes.baremetal_runbook_steps),
|
||||
'uuid': baremetal_fakes.baremetal_runbook_uuid,
|
||||
}
|
||||
|
||||
self.baremetal_mock.runbook.create.assert_called_once_with(
|
||||
**args)
|
||||
|
||||
def test_baremetal_runbook_create_no_name(self):
|
||||
arglist = [
|
||||
'--steps', baremetal_fakes.baremetal_runbook_steps,
|
||||
]
|
||||
|
||||
verifylist = [
|
||||
('steps', baremetal_fakes.baremetal_runbook_steps),
|
||||
]
|
||||
|
||||
self.assertRaises(osctestutils.ParserException,
|
||||
self.check_parser,
|
||||
self.cmd, arglist, verifylist)
|
||||
self.assertFalse(self.baremetal_mock.runbook.create.called)
|
||||
|
||||
def test_baremetal_runbook_create_no_steps(self):
|
||||
arglist = [
|
||||
'--name', baremetal_fakes.baremetal_runbook_name,
|
||||
]
|
||||
|
||||
verifylist = [
|
||||
('name', baremetal_fakes.baremetal_runbook_name),
|
||||
]
|
||||
|
||||
self.assertRaises(osctestutils.ParserException,
|
||||
self.check_parser,
|
||||
self.cmd, arglist, verifylist)
|
||||
self.assertFalse(self.baremetal_mock.runbook.create.called)
|
||||
|
||||
|
||||
class TestShowBaremetalRunbook(TestBaremetalRunbook):
|
||||
def setUp(self):
|
||||
super(TestShowBaremetalRunbook, self).setUp()
|
||||
|
||||
self.baremetal_mock.runbook.get.return_value = (
|
||||
baremetal_fakes.FakeBaremetalResource(
|
||||
None,
|
||||
copy.deepcopy(baremetal_fakes.RUNBOOK),
|
||||
loaded=True))
|
||||
|
||||
self.cmd = baremetal_runbook.ShowBaremetalRunbook(
|
||||
self.app, None)
|
||||
|
||||
def test_baremetal_runbook_show(self):
|
||||
arglist = [baremetal_fakes.baremetal_runbook_uuid]
|
||||
verifylist = [('runbook',
|
||||
baremetal_fakes.baremetal_runbook_uuid)]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
|
||||
# Set expected values
|
||||
args = [baremetal_fakes.baremetal_runbook_uuid]
|
||||
self.baremetal_mock.runbook.get.assert_called_with(
|
||||
*args, fields=None)
|
||||
|
||||
collist = (
|
||||
'extra',
|
||||
'name',
|
||||
'owner',
|
||||
'public',
|
||||
'steps',
|
||||
'uuid')
|
||||
self.assertEqual(collist, columns)
|
||||
|
||||
datalist = (
|
||||
baremetal_fakes.baremetal_runbook_extra,
|
||||
baremetal_fakes.baremetal_runbook_name,
|
||||
baremetal_fakes.baremetal_runbook_owner,
|
||||
baremetal_fakes.baremetal_runbook_public,
|
||||
baremetal_fakes.baremetal_runbook_steps,
|
||||
baremetal_fakes.baremetal_runbook_uuid)
|
||||
self.assertEqual(datalist, tuple(data))
|
||||
|
||||
def test_baremetal_runbook_show_no_template(self):
|
||||
arglist = []
|
||||
verifylist = []
|
||||
|
||||
self.assertRaises(osctestutils.ParserException,
|
||||
self.check_parser,
|
||||
self.cmd, arglist, verifylist)
|
||||
|
||||
|
||||
class TestBaremetalRunbookSet(TestBaremetalRunbook):
|
||||
def setUp(self):
|
||||
super(TestBaremetalRunbookSet, self).setUp()
|
||||
|
||||
self.baremetal_mock.runbook.update.return_value = (
|
||||
baremetal_fakes.FakeBaremetalResource(
|
||||
None,
|
||||
copy.deepcopy(baremetal_fakes.RUNBOOK),
|
||||
loaded=True))
|
||||
|
||||
self.cmd = baremetal_runbook.SetBaremetalRunbook(
|
||||
self.app, None)
|
||||
|
||||
def test_baremetal_runbook_set_name(self):
|
||||
new_name = 'foo'
|
||||
arglist = [
|
||||
baremetal_fakes.baremetal_runbook_uuid,
|
||||
'--name', new_name]
|
||||
verifylist = [
|
||||
('runbook', baremetal_fakes.baremetal_runbook_uuid),
|
||||
('name', new_name)]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
self.baremetal_mock.runbook.update.assert_called_once_with(
|
||||
baremetal_fakes.baremetal_runbook_uuid,
|
||||
[{'path': '/name', 'value': new_name, 'op': 'add'}])
|
||||
|
||||
def test_baremetal_runbook_set_steps(self):
|
||||
arglist = [
|
||||
baremetal_fakes.baremetal_runbook_uuid,
|
||||
'--steps', baremetal_fakes.baremetal_runbook_steps]
|
||||
verifylist = [
|
||||
('runbook', baremetal_fakes.baremetal_runbook_uuid),
|
||||
('steps', baremetal_fakes.baremetal_runbook_steps)]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
expected_steps = json.loads(
|
||||
baremetal_fakes.baremetal_runbook_steps)
|
||||
self.cmd.take_action(parsed_args)
|
||||
self.baremetal_mock.runbook.update.assert_called_once_with(
|
||||
baremetal_fakes.baremetal_runbook_uuid,
|
||||
[{'path': '/steps', 'value': expected_steps, 'op': 'add'}])
|
||||
|
||||
def test_baremetal_runbook_set_no_options(self):
|
||||
arglist = []
|
||||
verifylist = []
|
||||
self.assertRaises(osctestutils.ParserException,
|
||||
self.check_parser,
|
||||
self.cmd, arglist, verifylist)
|
||||
|
||||
|
||||
class TestBaremetalRunbookUnset(TestBaremetalRunbook):
|
||||
def setUp(self):
|
||||
super(TestBaremetalRunbookUnset, self).setUp()
|
||||
|
||||
self.baremetal_mock.runbook.update.return_value = (
|
||||
baremetal_fakes.FakeBaremetalResource(
|
||||
None,
|
||||
copy.deepcopy(baremetal_fakes.RUNBOOK),
|
||||
loaded=True))
|
||||
|
||||
self.cmd = baremetal_runbook.UnsetBaremetalRunbook(
|
||||
self.app, None)
|
||||
|
||||
def test_baremetal_runbook_unset_extra(self):
|
||||
arglist = [
|
||||
baremetal_fakes.baremetal_runbook_uuid, '--extra', 'key1']
|
||||
verifylist = [('runbook',
|
||||
baremetal_fakes.baremetal_runbook_uuid),
|
||||
('extra', ['key1'])]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
self.baremetal_mock.runbook.update.assert_called_once_with(
|
||||
baremetal_fakes.baremetal_runbook_uuid,
|
||||
[{'path': '/extra/key1', 'op': 'remove'}])
|
||||
|
||||
def test_baremetal_runbook_unset_multiple_extras(self):
|
||||
arglist = [
|
||||
baremetal_fakes.baremetal_runbook_uuid,
|
||||
'--extra', 'key1', '--extra', 'key2']
|
||||
verifylist = [('runbook',
|
||||
baremetal_fakes.baremetal_runbook_uuid),
|
||||
('extra', ['key1', 'key2'])]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
self.baremetal_mock.runbook.update.assert_called_once_with(
|
||||
baremetal_fakes.baremetal_runbook_uuid,
|
||||
[{'path': '/extra/key1', 'op': 'remove'},
|
||||
{'path': '/extra/key2', 'op': 'remove'}])
|
||||
|
||||
def test_baremetal_runbook_unset_no_options(self):
|
||||
arglist = []
|
||||
verifylist = []
|
||||
self.assertRaises(osctestutils.ParserException,
|
||||
self.check_parser,
|
||||
self.cmd, arglist, verifylist)
|
||||
|
||||
def test_baremetal_runbook_unset_no_property(self):
|
||||
uuid = baremetal_fakes.baremetal_runbook_uuid
|
||||
arglist = [uuid]
|
||||
verifylist = [('runbook', uuid)]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
self.cmd.take_action(parsed_args)
|
||||
self.assertFalse(self.baremetal_mock.runbook.update.called)
|
||||
|
||||
|
||||
class TestBaremetalRunbookDelete(TestBaremetalRunbook):
|
||||
def setUp(self):
|
||||
super(TestBaremetalRunbookDelete, self).setUp()
|
||||
|
||||
self.cmd = baremetal_runbook.DeleteBaremetalRunbook(
|
||||
self.app, None)
|
||||
|
||||
def test_baremetal_runbook_delete(self):
|
||||
arglist = ['zzz-zzzzzz-zzzz']
|
||||
verifylist = []
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
args = 'zzz-zzzzzz-zzzz'
|
||||
self.baremetal_mock.runbook.delete.assert_called_with(args)
|
||||
|
||||
def test_baremetal_runbook_delete_multiple(self):
|
||||
arglist = ['zzz-zzzzzz-zzzz', 'fakename']
|
||||
verifylist = []
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
args = ['zzz-zzzzzz-zzzz', 'fakename']
|
||||
self.baremetal_mock.runbook.delete.assert_has_calls(
|
||||
[mock.call(x) for x in args])
|
||||
self.assertEqual(
|
||||
2, self.baremetal_mock.runbook.delete.call_count)
|
||||
|
||||
def test_baremetal_runbook_delete_multiple_with_fail(self):
|
||||
arglist = ['zzz-zzzzzz-zzzz', 'badname']
|
||||
verifylist = []
|
||||
|
||||
self.baremetal_mock.runbook.delete.side_effect = [
|
||||
'', exc.ClientException]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
self.assertRaises(exc.ClientException,
|
||||
self.cmd.take_action,
|
||||
parsed_args)
|
||||
|
||||
args = ['zzz-zzzzzz-zzzz', 'badname']
|
||||
self.baremetal_mock.runbook.delete.assert_has_calls(
|
||||
[mock.call(x) for x in args])
|
||||
self.assertEqual(
|
||||
2, self.baremetal_mock.runbook.delete.call_count)
|
||||
|
||||
def test_baremetal_runbook_delete_no_template(self):
|
||||
arglist = []
|
||||
verifylist = []
|
||||
|
||||
self.assertRaises(osctestutils.ParserException,
|
||||
self.check_parser,
|
||||
self.cmd, arglist, verifylist)
|
||||
|
||||
|
||||
class TestBaremetalRunbookList(TestBaremetalRunbook):
|
||||
def setUp(self):
|
||||
super(TestBaremetalRunbookList, self).setUp()
|
||||
|
||||
self.baremetal_mock.runbook.list.return_value = [
|
||||
baremetal_fakes.FakeBaremetalResource(
|
||||
None,
|
||||
copy.deepcopy(baremetal_fakes.RUNBOOK),
|
||||
loaded=True)
|
||||
]
|
||||
|
||||
self.cmd = baremetal_runbook.ListBaremetalRunbook(
|
||||
self.app, None)
|
||||
|
||||
def test_baremetal_runbook_list(self):
|
||||
arglist = []
|
||||
verifylist = []
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
|
||||
kwargs = {
|
||||
'marker': None,
|
||||
'limit': None}
|
||||
self.baremetal_mock.runbook.list.assert_called_with(**kwargs)
|
||||
|
||||
collist = (
|
||||
"UUID",
|
||||
"Name")
|
||||
self.assertEqual(collist, columns)
|
||||
|
||||
datalist = ((
|
||||
baremetal_fakes.baremetal_runbook_uuid,
|
||||
baremetal_fakes.baremetal_runbook_name
|
||||
), )
|
||||
self.assertEqual(datalist, tuple(data))
|
||||
|
||||
def test_baremetal_runbook_list_long(self):
|
||||
arglist = ['--long']
|
||||
verifylist = [('detail', True)]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
|
||||
kwargs = {
|
||||
'detail': True,
|
||||
'marker': None,
|
||||
'limit': None,
|
||||
}
|
||||
self.baremetal_mock.runbook.list.assert_called_with(**kwargs)
|
||||
|
||||
collist = ('UUID',
|
||||
'Name',
|
||||
'Owner',
|
||||
'Public',
|
||||
'Steps',
|
||||
'Extra',
|
||||
'Created At',
|
||||
'Updated At')
|
||||
self.assertEqual(collist, columns)
|
||||
|
||||
datalist = ((
|
||||
baremetal_fakes.baremetal_runbook_uuid,
|
||||
baremetal_fakes.baremetal_runbook_name,
|
||||
baremetal_fakes.baremetal_runbook_owner,
|
||||
baremetal_fakes.baremetal_runbook_public,
|
||||
baremetal_fakes.baremetal_runbook_steps,
|
||||
baremetal_fakes.baremetal_runbook_extra,
|
||||
'',
|
||||
'',
|
||||
), )
|
||||
self.assertEqual(datalist, tuple(data))
|
||||
|
||||
def test_baremetal_runbook_list_fields(self):
|
||||
arglist = ['--fields', 'uuid', 'steps']
|
||||
verifylist = [('fields', [['uuid', 'steps']])]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
kwargs = {
|
||||
'marker': None,
|
||||
'limit': None,
|
||||
'detail': False,
|
||||
'fields': ('uuid', 'steps')
|
||||
}
|
||||
self.baremetal_mock.runbook.list.assert_called_with(**kwargs)
|
||||
|
||||
def test_baremetal_runbook_list_fields_multiple(self):
|
||||
arglist = ['--fields', 'uuid', 'name', '--fields', 'steps']
|
||||
verifylist = [('fields', [['uuid', 'name'], ['steps']])]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
kwargs = {
|
||||
'marker': None,
|
||||
'limit': None,
|
||||
'detail': False,
|
||||
'fields': ('uuid', 'name', 'steps')
|
||||
}
|
||||
self.baremetal_mock.runbook.list.assert_called_with(**kwargs)
|
||||
|
||||
def test_baremetal_runbook_list_invalid_fields(self):
|
||||
arglist = ['--fields', 'uuid', 'invalid']
|
||||
verifylist = [('fields', [['uuid', 'invalid']])]
|
||||
self.assertRaises(osctestutils.ParserException,
|
||||
self.check_parser,
|
||||
self.cmd, arglist, verifylist)
|
291
ironicclient/tests/unit/v1/test_runbook.py
Normal file
291
ironicclient/tests/unit/v1/test_runbook.py
Normal file
@ -0,0 +1,291 @@
|
||||
# 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 copy
|
||||
|
||||
import testtools
|
||||
from testtools.matchers import HasLength
|
||||
|
||||
from ironicclient import exc
|
||||
from ironicclient.tests.unit import utils
|
||||
import ironicclient.v1.runbook
|
||||
|
||||
RUNBOOK = {'uuid': '11111111-2222-3333-4444-555555555555',
|
||||
'name': 'CUSTOM_RUNBOOK',
|
||||
'steps': {},
|
||||
'extra': {}}
|
||||
|
||||
RUNBOOK2 = {'uuid': '55555555-4444-3333-2222-111111111111',
|
||||
'name': 'CUSTOM_RUNBOOK2',
|
||||
'steps': {},
|
||||
'extra': {}}
|
||||
|
||||
CREATE_RUNBOOK = copy.deepcopy(RUNBOOK)
|
||||
del CREATE_RUNBOOK['uuid']
|
||||
|
||||
CREATE_RUNBOOK_WITH_UUID = copy.deepcopy(RUNBOOK)
|
||||
|
||||
UPDATED_RUNBOOK = copy.deepcopy(RUNBOOK)
|
||||
NEW_NAME = 'CUSTOM_RUNBOOK3'
|
||||
UPDATED_RUNBOOK['name'] = NEW_NAME
|
||||
|
||||
fake_responses = {
|
||||
'/v1/runbooks':
|
||||
{
|
||||
'GET': (
|
||||
{},
|
||||
{"runbooks": [RUNBOOK]},
|
||||
),
|
||||
'POST': (
|
||||
{},
|
||||
CREATE_RUNBOOK,
|
||||
),
|
||||
},
|
||||
'/v1/runbooks/?detail=True':
|
||||
{
|
||||
'GET': (
|
||||
{},
|
||||
{"runbooks": [RUNBOOK]},
|
||||
),
|
||||
},
|
||||
'/v1/runbooks/?fields=uuid,name':
|
||||
{
|
||||
'GET': (
|
||||
{},
|
||||
{"runbooks": [RUNBOOK]},
|
||||
),
|
||||
},
|
||||
'/v1/runbooks/%s' % RUNBOOK['uuid']:
|
||||
{
|
||||
'GET': (
|
||||
{},
|
||||
RUNBOOK,
|
||||
),
|
||||
'DELETE': (
|
||||
{},
|
||||
None,
|
||||
),
|
||||
'PATCH': (
|
||||
{},
|
||||
UPDATED_RUNBOOK,
|
||||
),
|
||||
},
|
||||
'/v1/runbooks/%s?fields=uuid,name' % RUNBOOK['uuid']:
|
||||
{
|
||||
'GET': (
|
||||
{},
|
||||
RUNBOOK,
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
fake_responses_pagination = {
|
||||
'/v1/runbooks':
|
||||
{
|
||||
'GET': (
|
||||
{},
|
||||
{"runbooks": [RUNBOOK],
|
||||
"next": "http://127.0.0.1:6385/v1/runbooks/?limit=1"}
|
||||
),
|
||||
},
|
||||
'/v1/runbooks/?limit=1':
|
||||
{
|
||||
'GET': (
|
||||
{},
|
||||
{"runbooks": [RUNBOOK2]}
|
||||
),
|
||||
},
|
||||
'/v1/runbooks/?marker=%s' % RUNBOOK['uuid']:
|
||||
{
|
||||
'GET': (
|
||||
{},
|
||||
{"runbooks": [RUNBOOK2]}
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||