Deploy templates: client support
Adds OSC support for the deploy templates API. Change-Id: I0f2f37e840449ee41f747e2a43ed6f53c927094e Depends-On: https://review.openstack.org/631845 Story: 1722275 Task: 28678
This commit is contained in:
parent
fdba8ed994
commit
cc37253428
@ -43,7 +43,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 = 54
|
||||
LAST_KNOWN_API_VERSION = 55
|
||||
LATEST_VERSION = '1.{}'.format(LAST_KNOWN_API_VERSION)
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
@ -411,3 +411,19 @@ def poll(timeout, poll_interval, poll_delay_function, timeout_message):
|
||||
count += 1
|
||||
|
||||
raise exc.StateTransitionTimeout(timeout_message)
|
||||
|
||||
|
||||
def handle_json_arg(json_arg, info_desc):
|
||||
"""Read a JSON argument from stdin, file or string.
|
||||
|
||||
:param json_arg: May be a file name containing the JSON, a JSON string, or
|
||||
'-' indicating that the argument should be read from standard input.
|
||||
:param info_desc: A string description of the desired information
|
||||
:returns: A list or dictionary parsed from JSON.
|
||||
:raises: InvalidAttribute if the argument cannot be parsed.
|
||||
"""
|
||||
if json_arg == '-':
|
||||
json_arg = get_from_stdin(info_desc)
|
||||
if json_arg:
|
||||
json_arg = handle_json_or_file_arg(json_arg)
|
||||
return json_arg
|
||||
|
345
ironicclient/osc/v1/baremetal_deploy_template.py
Normal file
345
ironicclient/osc/v1/baremetal_deploy_template.py
Normal file
@ -0,0 +1,345 @@
|
||||
# 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
|
||||
|
||||
|
||||
_DEPLOY_STEPS_HELP = _(
|
||||
"The deploy steps in JSON format. May be the path to a file containing "
|
||||
"the deploy steps; OR '-', with the deploy steps being read from standard "
|
||||
"input; OR a string. The value should be a list of deploy-step "
|
||||
"dictionaries; each dictionary should have keys 'interface', 'step', "
|
||||
"'args' and 'priority'.")
|
||||
|
||||
|
||||
class CreateBaremetalDeployTemplate(command.ShowOne):
|
||||
"""Create a new deploy template"""
|
||||
|
||||
log = logging.getLogger(__name__ + ".CreateBaremetalDeployTemplate")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CreateBaremetalDeployTemplate, self).get_parser(
|
||||
prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'name',
|
||||
metavar='<name>',
|
||||
help=_('Unique name for this deploy template. Must be a valid '
|
||||
'trait name')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--uuid',
|
||||
dest='uuid',
|
||||
metavar='<uuid>',
|
||||
help=_('UUID of the deploy template.'))
|
||||
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=_DEPLOY_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, 'deploy steps')
|
||||
|
||||
field_list = ['name', 'uuid', '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')
|
||||
template = baremetal_client.deploy_template.create(steps=steps,
|
||||
**fields)
|
||||
|
||||
data = dict([(f, getattr(template, f, '')) for f in
|
||||
res_fields.DEPLOY_TEMPLATE_DETAILED_RESOURCE.fields])
|
||||
|
||||
return self.dict2columns(data)
|
||||
|
||||
|
||||
class ShowBaremetalDeployTemplate(command.ShowOne):
|
||||
"""Show baremetal deploy template details."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ShowBaremetalDeployTemplate")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ShowBaremetalDeployTemplate, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
"template",
|
||||
metavar="<template>",
|
||||
help=_("Name or UUID of the deploy template.")
|
||||
)
|
||||
parser.add_argument(
|
||||
'--fields',
|
||||
nargs='+',
|
||||
dest='fields',
|
||||
metavar='<field>',
|
||||
action='append',
|
||||
choices=res_fields.DEPLOY_TEMPLATE_DETAILED_RESOURCE.fields,
|
||||
default=[],
|
||||
help=_("One or more deploy template 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
|
||||
|
||||
template = baremetal_client.deploy_template.get(
|
||||
parsed_args.template, fields=fields)._info
|
||||
|
||||
template.pop("links", None)
|
||||
return zip(*sorted(template.items()))
|
||||
|
||||
|
||||
class SetBaremetalDeployTemplate(command.Command):
|
||||
"""Set baremetal deploy template properties."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".SetBaremetalDeployTemplate")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(SetBaremetalDeployTemplate, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'template',
|
||||
metavar='<template>',
|
||||
help=_("Name or UUID of the deploy template")
|
||||
)
|
||||
parser.add_argument(
|
||||
'--name',
|
||||
metavar='<name>',
|
||||
help=_('Set unique name of the deploy template. Must be a valid '
|
||||
'trait name.')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--steps',
|
||||
metavar="<steps>",
|
||||
help=_DEPLOY_STEPS_HELP
|
||||
)
|
||||
parser.add_argument(
|
||||
"--extra",
|
||||
metavar="<key=value>",
|
||||
action='append',
|
||||
help=_('Extra to set on this baremetal deploy template '
|
||||
'(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.steps:
|
||||
steps = utils.handle_json_arg(parsed_args.steps, 'deploy 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.deploy_template.update(parsed_args.template,
|
||||
properties)
|
||||
else:
|
||||
self.log.warning("Please specify what to set.")
|
||||
|
||||
|
||||
class UnsetBaremetalDeployTemplate(command.Command):
|
||||
"""Unset baremetal deploy template properties."""
|
||||
log = logging.getLogger(__name__ + ".UnsetBaremetalDeployTemplate")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(UnsetBaremetalDeployTemplate, self).get_parser(
|
||||
prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'template',
|
||||
metavar='<template>',
|
||||
help=_("Name or UUID of the deploy template")
|
||||
)
|
||||
parser.add_argument(
|
||||
"--extra",
|
||||
metavar="<key>",
|
||||
action='append',
|
||||
help=_('Extra to unset on this baremetal deploy template '
|
||||
'(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 = []
|
||||
if parsed_args.extra:
|
||||
properties.extend(utils.args_array_to_patch('remove',
|
||||
['extra/' + x for x in parsed_args.extra]))
|
||||
|
||||
if properties:
|
||||
baremetal_client.deploy_template.update(parsed_args.template,
|
||||
properties)
|
||||
else:
|
||||
self.log.warning("Please specify what to unset.")
|
||||
|
||||
|
||||
class DeleteBaremetalDeployTemplate(command.Command):
|
||||
"""Delete deploy template(s)."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".DeleteBaremetalDeployTemplate")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(DeleteBaremetalDeployTemplate, self).get_parser(
|
||||
prog_name)
|
||||
parser.add_argument(
|
||||
"templates",
|
||||
metavar="<template>",
|
||||
nargs="+",
|
||||
help=_("Name(s) or UUID(s) of the deploy template(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 template in parsed_args.templates:
|
||||
try:
|
||||
baremetal_client.deploy_template.delete(template)
|
||||
print(_('Deleted deploy template %s') % template)
|
||||
except exc.ClientException as e:
|
||||
failures.append(_("Failed to delete deploy template "
|
||||
"%(template)s: %(error)s")
|
||||
% {'template': template, 'error': e})
|
||||
|
||||
if failures:
|
||||
raise exc.ClientException("\n".join(failures))
|
||||
|
||||
|
||||
class ListBaremetalDeployTemplate(command.Lister):
|
||||
"""List baremetal deploy templates."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ListBaremetalDeployTemplate")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListBaremetalDeployTemplate, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--limit',
|
||||
metavar='<limit>',
|
||||
type=int,
|
||||
help=_('Maximum number of deploy templates to return per request, '
|
||||
'0 for no limit. Default is the maximum number used '
|
||||
'by the Baremetal API Service.')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--marker',
|
||||
metavar='<template>',
|
||||
help=_('DeployTemplate UUID (for example, of the last deploy '
|
||||
'template in the list from a previous request). Returns '
|
||||
'the list of deploy templates after this UUID.')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--sort',
|
||||
metavar="<key>[:<direction>]",
|
||||
help=_('Sort output by specified deploy template 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 deploy templates.")
|
||||
)
|
||||
display_group.add_argument(
|
||||
'--fields',
|
||||
nargs='+',
|
||||
dest='fields',
|
||||
metavar='<field>',
|
||||
action='append',
|
||||
default=[],
|
||||
choices=res_fields.DEPLOY_TEMPLATE_DETAILED_RESOURCE.fields,
|
||||
help=_("One or more deploy template 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.DEPLOY_TEMPLATE_RESOURCE.fields
|
||||
labels = res_fields.DEPLOY_TEMPLATE_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.DEPLOY_TEMPLATE_DETAILED_RESOURCE.fields
|
||||
labels = res_fields.DEPLOY_TEMPLATE_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.deploy_template.list(**params)
|
||||
|
||||
data = oscutils.sort_items(data, parsed_args.sort)
|
||||
|
||||
return (labels,
|
||||
(oscutils.get_item_properties(s, columns) for s in data))
|
@ -66,10 +66,7 @@ class ProvisionStateBaremetalNode(command.Command):
|
||||
baremetal_client = self.app.client_manager.baremetal
|
||||
|
||||
clean_steps = getattr(parsed_args, 'clean_steps', None)
|
||||
if clean_steps == '-':
|
||||
clean_steps = utils.get_from_stdin('clean steps')
|
||||
if clean_steps:
|
||||
clean_steps = utils.handle_json_or_file_arg(clean_steps)
|
||||
clean_steps = utils.handle_json_arg(clean_steps, 'clean steps')
|
||||
|
||||
config_drive = getattr(parsed_args, 'config_drive', None)
|
||||
rescue_password = getattr(parsed_args, 'rescue_password', None)
|
||||
@ -1207,9 +1204,8 @@ class SetBaremetalNode(command.Command):
|
||||
# also being modified.
|
||||
if parsed_args.target_raid_config:
|
||||
raid_config = parsed_args.target_raid_config
|
||||
if raid_config == '-':
|
||||
raid_config = utils.get_from_stdin('target_raid_config')
|
||||
raid_config = utils.handle_json_or_file_arg(raid_config)
|
||||
raid_config = utils.handle_json_arg(raid_config,
|
||||
'target_raid_config')
|
||||
baremetal_client.node.set_target_raid_config(parsed_args.node,
|
||||
raid_config)
|
||||
|
||||
|
@ -443,7 +443,7 @@ class ListBaremetalPort(command.Lister):
|
||||
type=int,
|
||||
help=_('Maximum number of ports to return per request, '
|
||||
'0 for no limit. Default is the maximum number used '
|
||||
'by the Ironic API Service.')
|
||||
'by the Baremetal API Service.')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--marker',
|
||||
|
@ -401,3 +401,60 @@ class TestCase(base.FunctionalTestBase):
|
||||
except exceptions.CommandFailed:
|
||||
if not ignore_exceptions:
|
||||
raise
|
||||
|
||||
def deploy_template_create(self, name, params=''):
|
||||
"""Create baremetal deploy template and add cleanup.
|
||||
|
||||
:param String name: deploy template name
|
||||
:param String params: additional parameters
|
||||
:return: JSON object of created deploy template
|
||||
"""
|
||||
opts = self.get_opts()
|
||||
template = self.openstack('baremetal deploy template create {0} {1} '
|
||||
'{2}'.format(opts, name, params))
|
||||
template = json.loads(template)
|
||||
if not template:
|
||||
self.fail('Baremetal deploy template has not been created!')
|
||||
self.addCleanup(self.deploy_template_delete, template['uuid'], True)
|
||||
return template
|
||||
|
||||
def deploy_template_list(self, fields=None, params=''):
|
||||
"""List baremetal deploy templates.
|
||||
|
||||
:param List fields: List of fields to show
|
||||
:param String params: Additional kwargs
|
||||
:return: list of JSON deploy template objects
|
||||
"""
|
||||
opts = self.get_opts(fields=fields)
|
||||
output = self.openstack('baremetal deploy template list {0} {1}'
|
||||
.format(opts, params))
|
||||
return json.loads(output)
|
||||
|
||||
def deploy_template_show(self, identifier, fields=None, params=''):
|
||||
"""Show specified baremetal deploy template.
|
||||
|
||||
:param String identifier: Name or UUID of the deploy template
|
||||
:param List fields: List of fields to show
|
||||
:param List params: Additional kwargs
|
||||
:return: JSON object of deploy template
|
||||
"""
|
||||
opts = self.get_opts(fields)
|
||||
output = self.openstack('baremetal deploy template show {0} {1} {2}'
|
||||
.format(opts, identifier, params))
|
||||
return json.loads(output)
|
||||
|
||||
def deploy_template_delete(self, identifier, ignore_exceptions=False):
|
||||
"""Try to delete baremetal deploy template by UUID.
|
||||
|
||||
:param String identifier: Name or UUID of the deploy template
|
||||
:param Bool ignore_exceptions: Ignore exception (needed for cleanUp)
|
||||
:return: raw values output
|
||||
:raise: CommandFailed exception when command fails to delete a deploy
|
||||
template
|
||||
"""
|
||||
try:
|
||||
return self.openstack('baremetal deploy template delete {0}'
|
||||
.format(identifier))
|
||||
except exceptions.CommandFailed:
|
||||
if not ignore_exceptions:
|
||||
raise
|
||||
|
@ -0,0 +1,177 @@
|
||||
# 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 json
|
||||
|
||||
import ddt
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib import exceptions
|
||||
|
||||
from ironicclient.tests.functional.osc.v1 import base
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class BaremetalDeployTemplateTests(base.TestCase):
|
||||
"""Functional tests for baremetal deploy template commands."""
|
||||
|
||||
@staticmethod
|
||||
def _get_random_trait():
|
||||
return data_utils.rand_name('CUSTOM', '').replace('-', '_')
|
||||
|
||||
def setUp(self):
|
||||
super(BaremetalDeployTemplateTests, self).setUp()
|
||||
self.steps = json.dumps([{
|
||||
'interface': 'bios',
|
||||
'step': 'apply_configuration',
|
||||
'args': {},
|
||||
'priority': 10,
|
||||
}])
|
||||
name = self._get_random_trait()
|
||||
self.template = self.deploy_template_create(
|
||||
name, params="--steps '%s'" % self.steps)
|
||||
|
||||
def tearDown(self):
|
||||
if self.template is not None:
|
||||
self.deploy_template_delete(self.template['uuid'])
|
||||
super(BaremetalDeployTemplateTests, self).tearDown()
|
||||
|
||||
def test_list(self):
|
||||
"""Check baremetal deploy template list command.
|
||||
|
||||
Test steps:
|
||||
1) Create baremetal deploy template in setUp.
|
||||
2) List baremetal deploy templates.
|
||||
3) Check deploy template name and UUID in deploy templates list.
|
||||
"""
|
||||
template_list = self.deploy_template_list()
|
||||
self.assertIn(self.template['name'],
|
||||
[template['Name']
|
||||
for template in template_list])
|
||||
self.assertIn(self.template['uuid'],
|
||||
[template['UUID']
|
||||
for template in template_list])
|
||||
|
||||
def test_list_long(self):
|
||||
"""Check baremetal deploy template list --long command
|
||||
|
||||
Test steps:
|
||||
1) Create baremetal deploy template in setUp.
|
||||
2) List baremetal deploy templates with detail=True.
|
||||
3) Check deploy template fields in output.
|
||||
"""
|
||||
template_list = self.deploy_template_list(params='--long')
|
||||
template = [template for template in template_list
|
||||
if template['Name'] == self.template['name']][0]
|
||||
self.assertEqual(self.template['extra'], template['Extra'])
|
||||
self.assertEqual(self.template['name'], template['Name'])
|
||||
self.assertEqual(self.template['steps'], template['Steps'])
|
||||
self.assertEqual(self.template['uuid'], template['UUID'])
|
||||
|
||||
def test_show(self):
|
||||
"""Check baremetal deploy template show command with UUID.
|
||||
|
||||
Test steps:
|
||||
1) Create baremetal deploy template in setUp.
|
||||
2) Show baremetal deploy template calling it by UUID.
|
||||
3) Check deploy template fields in output.
|
||||
"""
|
||||
template = self.deploy_template_show(self.template['uuid'])
|
||||
self.assertEqual(self.template['extra'], template['extra'])
|
||||
self.assertEqual(self.template['name'], template['name'])
|
||||
self.assertEqual(self.template['steps'], template['steps'])
|
||||
self.assertEqual(self.template['uuid'], template['uuid'])
|
||||
|
||||
def test_delete(self):
|
||||
"""Check baremetal deploy template delete command.
|
||||
|
||||
Test steps:
|
||||
1) Create baremetal deploy template in setUp.
|
||||
2) Delete baremetal deploy template by UUID.
|
||||
3) Check that deploy template deleted successfully and not in list.
|
||||
"""
|
||||
output = self.deploy_template_delete(self.template['uuid'])
|
||||
self.assertIn('Deleted deploy template {0}'.format(
|
||||
self.template['uuid']), output)
|
||||
template_list = self.deploy_template_list()
|
||||
self.assertNotIn(self.template['name'],
|
||||
[template['Name'] for template in template_list])
|
||||
self.assertNotIn(self.template['uuid'],
|
||||
[template['UUID'] for template in template_list])
|
||||
self.template = None
|
||||
|
||||
def test_set_steps(self):
|
||||
"""Check baremetal deploy template set command for steps.
|
||||
|
||||
Test steps:
|
||||
1) Create baremetal deploy template in setUp.
|
||||
2) Set steps for deploy template.
|
||||
3) Check that baremetal deploy template steps were set.
|
||||
"""
|
||||
steps = [{
|
||||
'interface': 'bios',
|
||||
'step': 'apply_configuration',
|
||||
'args': {},
|
||||
'priority': 20,
|
||||
}]
|
||||
self.openstack("baremetal deploy template set --steps '{0}' {1}"
|
||||
.format(json.dumps(steps), self.template['uuid']))
|
||||
|
||||
show_prop = self.deploy_template_show(self.template['uuid'],
|
||||
fields=['steps'])
|
||||
self.assertEqual(steps, show_prop['steps'])
|
||||
|
||||
def test_set_unset(self):
|
||||
"""Check baremetal deploy template set and unset commands.
|
||||
|
||||
Test steps:
|
||||
1) Create baremetal deploy template in setUp.
|
||||
2) Set extra data for deploy template.
|
||||
3) Check that baremetal deploy template extra data was set.
|
||||
4) Unset extra data for deploy template.
|
||||
5) Check that baremetal deploy template extra data was unset.
|
||||
"""
|
||||
extra_key = 'ext'
|
||||
extra_value = 'testdata'
|
||||
self.openstack(
|
||||
'baremetal deploy template set --extra {0}={1} {2}'
|
||||
.format(extra_key, extra_value, self.template['uuid']))
|
||||
|
||||
show_prop = self.deploy_template_show(self.template['uuid'],
|
||||
fields=['extra'])
|
||||
self.assertEqual(extra_value, show_prop['extra'][extra_key])
|
||||
|
||||
self.openstack('baremetal deploy template unset --extra {0} {1}'
|
||||
.format(extra_key, self.template['uuid']))
|
||||
|
||||
show_prop = self.deploy_template_show(self.template['uuid'],
|
||||
fields=['extra'])
|
||||
self.assertNotIn(extra_key, show_prop['extra'])
|
||||
|
||||
@ddt.data(
|
||||
('--uuid', '', 'expected one argument'),
|
||||
('--uuid', '!@#$^*&%^', 'Expected a UUID'),
|
||||
('', '', 'too few arguments'),
|
||||
('', 'not/a/name', 'Deploy template name must be a valid trait'),
|
||||
('', 'foo', 'Deploy template name must be a valid trait'),
|
||||
('--steps', '', 'expected one argument'),
|
||||
('--steps', '[]', 'No deploy steps specified'))
|
||||
@ddt.unpack
|
||||
def test_create_negative(self, argument, value, ex_text):
|
||||
"""Check errors on invalid input parameters."""
|
||||
base_cmd = 'baremetal deploy template create'
|
||||
if argument != '':
|
||||
base_cmd += ' %s' % self._get_random_trait()
|
||||
if argument != '--steps':
|
||||
base_cmd += " --steps '%s'" % self.steps
|
||||
command = self.construct_cmd(base_cmd, argument, value)
|
||||
self.assertRaisesRegex(exceptions.CommandFailed, ex_text,
|
||||
self.openstack, command)
|
@ -14,6 +14,8 @@
|
||||
# under the License.
|
||||
#
|
||||
|
||||
import json
|
||||
|
||||
import mock
|
||||
from osc_lib.tests import utils
|
||||
|
||||
@ -201,6 +203,22 @@ ALLOCATION = {
|
||||
'node_uuid': baremetal_uuid,
|
||||
}
|
||||
|
||||
baremetal_deploy_template_uuid = 'ddd-tttttt-dddd'
|
||||
baremetal_deploy_template_name = 'DeployTemplate-name'
|
||||
baremetal_deploy_template_steps = json.dumps([{
|
||||
'interface': 'raid',
|
||||
'step': 'create_configuration',
|
||||
'args': {},
|
||||
'priority': 10
|
||||
}])
|
||||
baremetal_deploy_template_extra = {'key1': 'value1', 'key2': 'value2'}
|
||||
DEPLOY_TEMPLATE = {
|
||||
'uuid': baremetal_deploy_template_uuid,
|
||||
'name': baremetal_deploy_template_name,
|
||||
'steps': baremetal_deploy_template_steps,
|
||||
'extra': baremetal_deploy_template_extra,
|
||||
}
|
||||
|
||||
|
||||
class TestBaremetal(utils.TestCommand):
|
||||
|
||||
|
450
ironicclient/tests/unit/osc/v1/test_baremetal_deploy_template.py
Normal file
450
ironicclient/tests/unit/osc/v1/test_baremetal_deploy_template.py
Normal file
@ -0,0 +1,450 @@
|
||||
# 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
|
||||
|
||||
import mock
|
||||
from osc_lib.tests import utils as osctestutils
|
||||
|
||||
from ironicclient import exc
|
||||
from ironicclient.osc.v1 import baremetal_deploy_template
|
||||
from ironicclient.tests.unit.osc.v1 import fakes as baremetal_fakes
|
||||
|
||||
|
||||
class TestBaremetalDeployTemplate(baremetal_fakes.TestBaremetal):
|
||||
|
||||
def setUp(self):
|
||||
super(TestBaremetalDeployTemplate, self).setUp()
|
||||
|
||||
self.baremetal_mock = self.app.client_manager.baremetal
|
||||
self.baremetal_mock.reset_mock()
|
||||
|
||||
|
||||
class TestCreateBaremetalDeployTemplate(TestBaremetalDeployTemplate):
|
||||
def setUp(self):
|
||||
super(TestCreateBaremetalDeployTemplate, self).setUp()
|
||||
|
||||
self.baremetal_mock.deploy_template.create.return_value = (
|
||||
baremetal_fakes.FakeBaremetalResource(
|
||||
None,
|
||||
copy.deepcopy(baremetal_fakes.DEPLOY_TEMPLATE),
|
||||
loaded=True,
|
||||
))
|
||||
|
||||
# Get the command object to test
|
||||
self.cmd = baremetal_deploy_template.CreateBaremetalDeployTemplate(
|
||||
self.app, None)
|
||||
|
||||
def test_baremetal_deploy_template_create(self):
|
||||
arglist = [
|
||||
baremetal_fakes.baremetal_deploy_template_name,
|
||||
'--steps', baremetal_fakes.baremetal_deploy_template_steps,
|
||||
]
|
||||
|
||||
verifylist = [
|
||||
('name', baremetal_fakes.baremetal_deploy_template_name),
|
||||
('steps', baremetal_fakes.baremetal_deploy_template_steps),
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
# Set expected values
|
||||
args = {
|
||||
'name': baremetal_fakes.baremetal_deploy_template_name,
|
||||
'steps': json.loads(
|
||||
baremetal_fakes.baremetal_deploy_template_steps),
|
||||
}
|
||||
|
||||
self.baremetal_mock.deploy_template.create.assert_called_once_with(
|
||||
**args)
|
||||
|
||||
def test_baremetal_deploy_template_create_uuid(self):
|
||||
arglist = [
|
||||
baremetal_fakes.baremetal_deploy_template_name,
|
||||
'--steps', baremetal_fakes.baremetal_deploy_template_steps,
|
||||
'--uuid', baremetal_fakes.baremetal_deploy_template_uuid,
|
||||
]
|
||||
|
||||
verifylist = [
|
||||
('name', baremetal_fakes.baremetal_deploy_template_name),
|
||||
('steps', baremetal_fakes.baremetal_deploy_template_steps),
|
||||
('uuid', baremetal_fakes.baremetal_deploy_template_uuid),
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
# Set expected values
|
||||
args = {
|
||||
'name': baremetal_fakes.baremetal_deploy_template_name,
|
||||
'steps': json.loads(
|
||||
baremetal_fakes.baremetal_deploy_template_steps),
|
||||
'uuid': baremetal_fakes.baremetal_deploy_template_uuid,
|
||||
}
|
||||
|
||||
self.baremetal_mock.deploy_template.create.assert_called_once_with(
|
||||
**args)
|
||||
|
||||
def test_baremetal_deploy_template_create_no_name(self):
|
||||
arglist = [
|
||||
'--steps', baremetal_fakes.baremetal_deploy_template_steps,
|
||||
]
|
||||
|
||||
verifylist = [
|
||||
('steps', baremetal_fakes.baremetal_deploy_template_steps),
|
||||
]
|
||||
|
||||
self.assertRaises(osctestutils.ParserException,
|
||||
self.check_parser,
|
||||
self.cmd, arglist, verifylist)
|
||||
self.assertFalse(self.baremetal_mock.deploy_template.create.called)
|
||||
|
||||
def test_baremetal_deploy_template_create_no_steps(self):
|
||||
arglist = [
|
||||
'--name', baremetal_fakes.baremetal_deploy_template_name,
|
||||
]
|
||||
|
||||
verifylist = [
|
||||
('name', baremetal_fakes.baremetal_deploy_template_name),
|
||||
]
|
||||
|
||||
self.assertRaises(osctestutils.ParserException,
|
||||
self.check_parser,
|
||||
self.cmd, arglist, verifylist)
|
||||
self.assertFalse(self.baremetal_mock.deploy_template.create.called)
|
||||
|
||||
|
||||
class TestShowBaremetalDeployTemplate(TestBaremetalDeployTemplate):
|
||||
def setUp(self):
|
||||
super(TestShowBaremetalDeployTemplate, self).setUp()
|
||||
|
||||
self.baremetal_mock.deploy_template.get.return_value = (
|
||||
baremetal_fakes.FakeBaremetalResource(
|
||||
None,
|
||||
copy.deepcopy(baremetal_fakes.DEPLOY_TEMPLATE),
|
||||
loaded=True))
|
||||
|
||||
self.cmd = baremetal_deploy_template.ShowBaremetalDeployTemplate(
|
||||
self.app, None)
|
||||
|
||||
def test_baremetal_deploy_template_show(self):
|
||||
arglist = [baremetal_fakes.baremetal_deploy_template_uuid]
|
||||
verifylist = [('template',
|
||||
baremetal_fakes.baremetal_deploy_template_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_deploy_template_uuid]
|
||||
self.baremetal_mock.deploy_template.get.assert_called_with(
|
||||
*args, fields=None)
|
||||
|
||||
collist = (
|
||||
'extra',
|
||||
'name',
|
||||
'steps',
|
||||
'uuid')
|
||||
self.assertEqual(collist, columns)
|
||||
|
||||
datalist = (
|
||||
baremetal_fakes.baremetal_deploy_template_extra,
|
||||
baremetal_fakes.baremetal_deploy_template_name,
|
||||
baremetal_fakes.baremetal_deploy_template_steps,
|
||||
baremetal_fakes.baremetal_deploy_template_uuid)
|
||||
self.assertEqual(datalist, tuple(data))
|
||||
|
||||
def test_baremetal_deploy_template_show_no_template(self):
|
||||
arglist = []
|
||||
verifylist = []
|
||||
|
||||
self.assertRaises(osctestutils.ParserException,
|
||||
self.check_parser,
|
||||
self.cmd, arglist, verifylist)
|
||||
|
||||
|
||||
class TestBaremetalDeployTemplateSet(TestBaremetalDeployTemplate):
|
||||
def setUp(self):
|
||||
super(TestBaremetalDeployTemplateSet, self).setUp()
|
||||
|
||||
self.baremetal_mock.deploy_template.update.return_value = (
|
||||
baremetal_fakes.FakeBaremetalResource(
|
||||
None,
|
||||
copy.deepcopy(baremetal_fakes.DEPLOY_TEMPLATE),
|
||||
loaded=True))
|
||||
|
||||
self.cmd = baremetal_deploy_template.SetBaremetalDeployTemplate(
|
||||
self.app, None)
|
||||
|
||||
def test_baremetal_deploy_template_set_name(self):
|
||||
new_name = 'foo'
|
||||
arglist = [
|
||||
baremetal_fakes.baremetal_deploy_template_uuid,
|
||||
'--name', new_name]
|
||||
verifylist = [
|
||||
('template', baremetal_fakes.baremetal_deploy_template_uuid),
|
||||
('name', new_name)]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
self.baremetal_mock.deploy_template.update.assert_called_once_with(
|
||||
baremetal_fakes.baremetal_deploy_template_uuid,
|
||||
[{'path': '/name', 'value': new_name, 'op': 'add'}])
|
||||
|
||||
def test_baremetal_deploy_template_set_steps(self):
|
||||
arglist = [
|
||||
baremetal_fakes.baremetal_deploy_template_uuid,
|
||||
'--steps', baremetal_fakes.baremetal_deploy_template_steps]
|
||||
verifylist = [
|
||||
('template', baremetal_fakes.baremetal_deploy_template_uuid),
|
||||
('steps', baremetal_fakes.baremetal_deploy_template_steps)]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
expected_steps = json.loads(
|
||||
baremetal_fakes.baremetal_deploy_template_steps)
|
||||
self.cmd.take_action(parsed_args)
|
||||
self.baremetal_mock.deploy_template.update.assert_called_once_with(
|
||||
baremetal_fakes.baremetal_deploy_template_uuid,
|
||||
[{'path': '/steps', 'value': expected_steps, 'op': 'add'}])
|
||||
|
||||
def test_baremetal_deploy_template_set_no_options(self):
|
||||
arglist = []
|
||||
verifylist = []
|
||||
self.assertRaises(osctestutils.ParserException,
|
||||
self.check_parser,
|
||||
self.cmd, arglist, verifylist)
|
||||
|
||||
|
||||
class TestBaremetalDeployTemplateUnset(TestBaremetalDeployTemplate):
|
||||
def setUp(self):
|
||||
super(TestBaremetalDeployTemplateUnset, self).setUp()
|
||||
|
||||
self.baremetal_mock.deploy_template.update.return_value = (
|
||||
baremetal_fakes.FakeBaremetalResource(
|
||||
None,
|
||||
copy.deepcopy(baremetal_fakes.DEPLOY_TEMPLATE),
|
||||
loaded=True))
|
||||
|
||||
self.cmd = baremetal_deploy_template.UnsetBaremetalDeployTemplate(
|
||||
self.app, None)
|
||||
|
||||
def test_baremetal_deploy_template_unset_extra(self):
|
||||
arglist = [
|
||||
baremetal_fakes.baremetal_deploy_template_uuid, '--extra', 'key1']
|
||||
verifylist = [('template',
|
||||
baremetal_fakes.baremetal_deploy_template_uuid),
|
||||
('extra', ['key1'])]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
self.baremetal_mock.deploy_template.update.assert_called_once_with(
|
||||
baremetal_fakes.baremetal_deploy_template_uuid,
|
||||
[{'path': '/extra/key1', 'op': 'remove'}])
|
||||
|
||||
def test_baremetal_deploy_template_unset_multiple_extras(self):
|
||||
arglist = [
|
||||
baremetal_fakes.baremetal_deploy_template_uuid,
|
||||
'--extra', 'key1', '--extra', 'key2']
|
||||
verifylist = [('template',
|
||||
baremetal_fakes.baremetal_deploy_template_uuid),
|
||||
('extra', ['key1', 'key2'])]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
self.baremetal_mock.deploy_template.update.assert_called_once_with(
|
||||
baremetal_fakes.baremetal_deploy_template_uuid,
|
||||
[{'path': '/extra/key1', 'op': 'remove'},
|
||||
{'path': '/extra/key2', 'op': 'remove'}])
|
||||
|
||||
def test_baremetal_deploy_template_unset_no_options(self):
|
||||
arglist = []
|
||||
verifylist = []
|
||||
self.assertRaises(osctestutils.ParserException,
|
||||
self.check_parser,
|
||||
self.cmd, arglist, verifylist)
|
||||
|
||||
def test_baremetal_deploy_template_unset_no_property(self):
|
||||
uuid = baremetal_fakes.baremetal_deploy_template_uuid
|
||||
arglist = [uuid]
|
||||
verifylist = [('template', uuid)]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
self.cmd.take_action(parsed_args)
|
||||
self.assertFalse(self.baremetal_mock.deploy_template.update.called)
|
||||
|
||||
|
||||
class TestBaremetalDeployTemplateDelete(TestBaremetalDeployTemplate):
|
||||
def setUp(self):
|
||||
super(TestBaremetalDeployTemplateDelete, self).setUp()
|
||||
|
||||
self.cmd = baremetal_deploy_template.DeleteBaremetalDeployTemplate(
|
||||
self.app, None)
|
||||
|
||||
def test_baremetal_deploy_template_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.deploy_template.delete.assert_called_with(args)
|
||||
|
||||
def test_baremetal_deploy_template_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.deploy_template.delete.has_calls(
|
||||
[mock.call(x) for x in args])
|
||||
self.assertEqual(
|
||||
2, self.baremetal_mock.deploy_template.delete.call_count)
|
||||
|
||||
def test_baremetal_deploy_template_delete_multiple_with_fail(self):
|
||||
arglist = ['zzz-zzzzzz-zzzz', 'badname']
|
||||
verifylist = []
|
||||
|
||||
self.baremetal_mock.deploy_template.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.deploy_template.delete.has_calls(
|
||||
[mock.call(x) for x in args])
|
||||
self.assertEqual(
|
||||
2, self.baremetal_mock.deploy_template.delete.call_count)
|
||||
|
||||
def test_baremetal_deploy_template_delete_no_template(self):
|
||||
arglist = []
|
||||
verifylist = []
|
||||
|
||||
self.assertRaises(osctestutils.ParserException,
|
||||
self.check_parser,
|
||||
self.cmd, arglist, verifylist)
|
||||
|
||||
|
||||
class TestBaremetalDeployTemplateList(TestBaremetalDeployTemplate):
|
||||
def setUp(self):
|
||||
super(TestBaremetalDeployTemplateList, self).setUp()
|
||||
|
||||
self.baremetal_mock.deploy_template.list.return_value = [
|
||||
baremetal_fakes.FakeBaremetalResource(
|
||||
None,
|
||||
copy.deepcopy(baremetal_fakes.DEPLOY_TEMPLATE),
|
||||
loaded=True)
|
||||
]
|
||||
|
||||
self.cmd = baremetal_deploy_template.ListBaremetalDeployTemplate(
|
||||
self.app, None)
|
||||
|
||||
def test_baremetal_deploy_template_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.deploy_template.list.assert_called_with(**kwargs)
|
||||
|
||||
collist = (
|
||||
"UUID",
|
||||
"Name")
|
||||
self.assertEqual(collist, columns)
|
||||
|
||||
datalist = ((
|
||||
baremetal_fakes.baremetal_deploy_template_uuid,
|
||||
baremetal_fakes.baremetal_deploy_template_name
|
||||
), )
|
||||
self.assertEqual(datalist, tuple(data))
|
||||
|
||||
def test_baremetal_deploy_template_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.deploy_template.list.assert_called_with(**kwargs)
|
||||
|
||||
collist = ('UUID', 'Name', 'Steps', 'Extra', 'Created At',
|
||||
'Updated At')
|
||||
self.assertEqual(collist, columns)
|
||||
|
||||
datalist = ((
|
||||
baremetal_fakes.baremetal_deploy_template_uuid,
|
||||
baremetal_fakes.baremetal_deploy_template_name,
|
||||
baremetal_fakes.baremetal_deploy_template_steps,
|
||||
baremetal_fakes.baremetal_deploy_template_extra,
|
||||
'',
|
||||
'',
|
||||
), )
|
||||
self.assertEqual(datalist, tuple(data))
|
||||
|
||||
def test_baremetal_deploy_template_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.deploy_template.list.assert_called_with(**kwargs)
|
||||
|
||||
def test_baremetal_deploy_template_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.deploy_template.list.assert_called_with(**kwargs)
|
||||
|
||||
def test_baremetal_deploy_template_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_deploy_template.py
Normal file
291
ironicclient/tests/unit/v1/test_deploy_template.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.deploy_template
|
||||
|
||||
DEPLOY_TEMPLATE = {'uuid': '11111111-2222-3333-4444-555555555555',
|
||||
'name': 'fake-template',
|
||||
'steps': {},
|
||||
'extra': {}}
|
||||
|
||||
DEPLOY_TEMPLATE2 = {'uuid': '55555555-4444-3333-2222-111111111111',
|
||||
'name': 'fake-template2',
|
||||
'steps': {},
|
||||
'extra': {}}
|
||||
|
||||
CREATE_DEPLOY_TEMPLATE = copy.deepcopy(DEPLOY_TEMPLATE)
|
||||
del CREATE_DEPLOY_TEMPLATE['uuid']
|
||||
|
||||
CREATE_DEPLOY_TEMPLATE_WITH_UUID = copy.deepcopy(DEPLOY_TEMPLATE)
|
||||
|
||||
UPDATED_DEPLOY_TEMPLATE = copy.deepcopy(DEPLOY_TEMPLATE)
|
||||
NEW_NAME = 'fake-template3'
|
||||
UPDATED_DEPLOY_TEMPLATE['name'] = NEW_NAME
|
||||
|
||||
fake_responses = {
|
||||
'/v1/deploy_templates':
|
||||
{
|
||||
'GET': (
|
||||
{},
|
||||
{"deploy_templates": [DEPLOY_TEMPLATE]},
|
||||
),
|
||||
'POST': (
|
||||
{},
|
||||
CREATE_DEPLOY_TEMPLATE,
|
||||
),
|
||||
},
|
||||
'/v1/deploy_templates/?detail=True':
|
||||
{
|
||||
'GET': (
|
||||
{},
|
||||
{"deploy_templates": [DEPLOY_TEMPLATE]},
|
||||
),
|
||||
},
|
||||
'/v1/deploy_templates/?fields=uuid,name':
|
||||
{
|
||||
'GET': (
|
||||
{},
|
||||
{"deploy_templates": [DEPLOY_TEMPLATE]},
|
||||
),
|
||||
},
|
||||
'/v1/deploy_templates/%s' % DEPLOY_TEMPLATE['uuid']:
|
||||
{
|
||||
'GET': (
|
||||
{},
|
||||
DEPLOY_TEMPLATE,
|
||||
),
|
||||
'DELETE': (
|
||||
{},
|
||||
None,
|
||||
),
|
||||
'PATCH': (
|
||||
{},
|
||||
UPDATED_DEPLOY_TEMPLATE,
|
||||
),
|
||||
},
|
||||
'/v1/deploy_templates/%s?fields=uuid,name' % DEPLOY_TEMPLATE['uuid']:
|
||||
{
|
||||
'GET': (
|
||||
{},
|
||||
DEPLOY_TEMPLATE,
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
fake_responses_pagination = {
|
||||
'/v1/deploy_templates':
|
||||
{
|
||||
'GET': (
|
||||
{},
|
||||
{"deploy_templates": [DEPLOY_TEMPLATE],
|
||||
"next": "http://127.0.0.1:6385/v1/deploy_templates/?limit=1"}
|
||||
),
|
||||
},
|
||||
'/v1/deploy_templates/?limit=1':
|
||||
{
|
||||
'GET': (
|
||||
{},
|
||||
{"deploy_templates": [DEPLOY_TEMPLATE2]}
|
||||
),
|
||||
},
|
||||
'/v1/deploy_templates/?marker=%s' % DEPLOY_TEMPLATE['uuid']:
|
||||
{
|
||||
'GET': (
|
||||
{},
|
||||
{"deploy_templates": [DEPLOY_TEMPLATE2]}
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
fake_responses_sorting = {
|
||||
'/v1/deploy_templates/?sort_key=updated_at':
|
||||
{
|
||||
'GET': (
|
||||
{},
|
||||
{"deploy_templates": [DEPLOY_TEMPLATE2, DEPLOY_TEMPLATE]}
|
||||
),
|
||||
},
|
||||
'/v1/deploy_templates/?sort_dir=desc':
|
||||
{
|
||||
'GET': (
|
||||
{},
|
||||
{"deploy_templates": [DEPLOY_TEMPLATE2, DEPLOY_TEMPLATE]}
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class DeployTemplateManagerTest(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(DeployTemplateManagerTest, self).setUp()
|
||||
self.api = utils.FakeAPI(fake_responses)
|
||||
self.mgr = ironicclient.v1.deploy_template.DeployTemplateManager(
|
||||
self.api)
|
||||
|
||||
def test_deploy_templates_list(self):
|
||||
deploy_templates = self.mgr.list()
|
||||
expect = [
|
||||
('GET', '/v1/deploy_templates', {}, None),
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertEqual(1, len(deploy_templates))
|
||||
|
||||
def test_deploy_templates_list_detail(self):
|
||||
deploy_templates = self.mgr.list(detail=True)
|
||||
expect = [
|
||||
('GET', '/v1/deploy_templates/?detail=True', {}, None),
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertEqual(1, len(deploy_templates))
|
||||
|
||||
def test_deploy_template_list_fields(self):
|
||||
deploy_templates = self.mgr.list(fields=['uuid', 'name'])
|
||||
expect = [
|
||||
('GET', '/v1/deploy_templates/?fields=uuid,name', {}, None),
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertEqual(1, len(deploy_templates))
|
||||
|
||||
def test_deploy_template_list_detail_and_fields_fail(self):
|
||||
self.assertRaises(exc.InvalidAttribute, self.mgr.list,
|
||||
detail=True, fields=['uuid', 'name'])
|
||||
|
||||
def test_deploy_templates_list_limit(self):
|
||||
self.api = utils.FakeAPI(fake_responses_pagination)
|
||||
self.mgr = ironicclient.v1.deploy_template.DeployTemplateManager(
|
||||
self.api)
|
||||
deploy_templates = self.mgr.list(limit=1)
|
||||
expect = [
|
||||
('GET', '/v1/deploy_templates/?limit=1', {}, None),
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertThat(deploy_templates, HasLength(1))
|
||||
|
||||
def test_deploy_templates_list_marker(self):
|
||||
self.api = utils.FakeAPI(fake_responses_pagination)
|
||||
self.mgr = ironicclient.v1.deploy_template.DeployTemplateManager(
|
||||
self.api)
|
||||
deploy_templates = self.mgr.list(marker=DEPLOY_TEMPLATE['uuid'])
|
||||
expect = [
|
||||
('GET',
|
||||
'/v1/deploy_templates/?marker=%s' % DEPLOY_TEMPLATE['uuid'], {},
|
||||
None),
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertThat(deploy_templates, HasLength(1))
|
||||
|
||||
def test_deploy_templates_list_pagination_no_limit(self):
|
||||
self.api = utils.FakeAPI(fake_responses_pagination)
|
||||
self.mgr = ironicclient.v1.deploy_template.DeployTemplateManager(
|
||||
self.api)
|
||||
deploy_templates = self.mgr.list(limit=0)
|
||||
expect = [
|
||||
('GET', '/v1/deploy_templates', {}, None),
|
||||
('GET', '/v1/deploy_templates/?limit=1', {}, None)
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertThat(deploy_templates, HasLength(2))
|
||||
|
||||
def test_deploy_templates_list_sort_key(self):
|
||||
self.api = utils.FakeAPI(fake_responses_sorting)
|
||||
self.mgr = ironicclient.v1.deploy_template.DeployTemplateManager(
|
||||
self.api)
|
||||
deploy_templates = self.mgr.list(sort_key='updated_at')
|
||||
expect = [
|
||||
('GET', '/v1/deploy_templates/?sort_key=updated_at', {}, None)
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertEqual(2, len(deploy_templates))
|
||||
|
||||
def test_deploy_templates_list_sort_dir(self):
|
||||
self.api = utils.FakeAPI(fake_responses_sorting)
|
||||
self.mgr = ironicclient.v1.deploy_template.DeployTemplateManager(
|
||||
self.api)
|
||||
deploy_templates = self.mgr.list(sort_dir='desc')
|
||||
expect = [
|
||||
('GET', '/v1/deploy_templates/?sort_dir=desc', {}, None)
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertEqual(2, len(deploy_templates))
|
||||
|
||||
def test_deploy_templates_show(self):
|
||||
deploy_template = self.mgr.get(DEPLOY_TEMPLATE['uuid'])
|
||||
expect = [
|
||||
('GET', '/v1/deploy_templates/%s' % DEPLOY_TEMPLATE['uuid'], {},
|
||||
None),
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertEqual(DEPLOY_TEMPLATE['uuid'], deploy_template.uuid)
|
||||
self.assertEqual(DEPLOY_TEMPLATE['name'], deploy_template.name)
|
||||
self.assertEqual(DEPLOY_TEMPLATE['steps'], deploy_template.steps)
|
||||
self.assertEqual(DEPLOY_TEMPLATE['extra'], deploy_template.extra)
|
||||
|
||||
def test_deploy_template_show_fields(self):
|
||||
deploy_template = self.mgr.get(DEPLOY_TEMPLATE['uuid'],
|
||||
fields=['uuid', 'name'])
|
||||
expect = [
|
||||
('GET', '/v1/deploy_templates/%s?fields=uuid,name' %
|
||||
DEPLOY_TEMPLATE['uuid'], {}, None),
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertEqual(DEPLOY_TEMPLATE['uuid'], deploy_template.uuid)
|
||||
self.assertEqual(DEPLOY_TEMPLATE['name'], deploy_template.name)
|
||||
|
||||
def test_create(self):
|
||||
deploy_template = self.mgr.create(**CREATE_DEPLOY_TEMPLATE)
|
||||
expect = [
|
||||
('POST', '/v1/deploy_templates', {}, CREATE_DEPLOY_TEMPLATE),
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertTrue(deploy_template)
|
||||
|
||||
def test_create_with_uuid(self):
|
||||
deploy_template = self.mgr.create(**CREATE_DEPLOY_TEMPLATE_WITH_UUID)
|
||||
expect = [
|
||||
('POST', '/v1/deploy_templates', {},
|
||||
CREATE_DEPLOY_TEMPLATE_WITH_UUID),
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertTrue(deploy_template)
|
||||
|
||||
def test_delete(self):
|
||||
deploy_template = self.mgr.delete(
|
||||
template_id=DEPLOY_TEMPLATE['uuid'])
|
||||
expect = [
|
||||
('DELETE', '/v1/deploy_templates/%s' % DEPLOY_TEMPLATE['uuid'], {},
|
||||
None),
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertIsNone(deploy_template)
|
||||
|
||||
def test_update(self):
|
||||
patch = {'op': 'replace',
|
||||
'value': NEW_NAME,
|
||||
'path': '/name'}
|
||||
deploy_template = self.mgr.update(
|
||||
template_id=DEPLOY_TEMPLATE['uuid'], patch=patch)
|
||||
expect = [
|
||||
('PATCH', '/v1/deploy_templates/%s' % DEPLOY_TEMPLATE['uuid'],
|
||||
{}, patch),
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertEqual(NEW_NAME, deploy_template.name)
|
@ -23,6 +23,7 @@ from ironicclient import exc
|
||||
from ironicclient.v1 import allocation
|
||||
from ironicclient.v1 import chassis
|
||||
from ironicclient.v1 import conductor
|
||||
from ironicclient.v1 import deploy_template
|
||||
from ironicclient.v1 import driver
|
||||
from ironicclient.v1 import events
|
||||
from ironicclient.v1 import node
|
||||
@ -103,6 +104,8 @@ class Client(object):
|
||||
self.conductor = conductor.ConductorManager(self.http_client)
|
||||
self.events = events.EventManager(self.http_client)
|
||||
self.allocation = allocation.AllocationManager(self.http_client)
|
||||
self.deploy_template = deploy_template.DeployTemplateManager(
|
||||
self.http_client)
|
||||
|
||||
@property
|
||||
def current_api_version(self):
|
||||
|
86
ironicclient/v1/deploy_template.py
Normal file
86
ironicclient/v1/deploy_template.py
Normal file
@ -0,0 +1,86 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from ironicclient.common import base
|
||||
from ironicclient.common.i18n import _
|
||||
from ironicclient.common import utils
|
||||
from ironicclient import exc
|
||||
|
||||
|
||||
class DeployTemplate(base.Resource):
|
||||
def __repr__(self):
|
||||
return "<DeployTemplate %s>" % self._info
|
||||
|
||||
|
||||
class DeployTemplateManager(base.CreateManager):
|
||||
resource_class = DeployTemplate
|
||||
_creation_attributes = ['extra', 'name', 'steps', 'uuid']
|
||||
_resource_name = 'deploy_templates'
|
||||
|
||||
def list(self, limit=None, marker=None, sort_key=None, sort_dir=None,
|
||||
detail=False, fields=None):
|
||||
"""Retrieve a list of deploy templates.
|
||||
|
||||
:param marker: Optional, the UUID of a deploy template, eg the last
|
||||
template from a previous result set. Return the next
|
||||
result set.
|
||||
:param limit: The maximum number of results to return per
|
||||
request, if:
|
||||
|
||||
1) limit > 0, the maximum number of deploy templates to return.
|
||||
2) limit == 0, return the entire list of deploy templates.
|
||||
3) limit param is NOT specified (None), the number of items
|
||||
returned respect the maximum imposed by the Ironic API
|
||||
(see Ironic's api.max_limit option).
|
||||
|
||||
:param sort_key: Optional, field used for sorting.
|
||||
|
||||
:param sort_dir: Optional, direction of sorting, either 'asc' (the
|
||||
default) or 'desc'.
|
||||
|
||||
:param detail: Optional, boolean whether to return detailed information
|
||||
about deploy templates.
|
||||
|
||||
:param fields: Optional, a list with a specified set of fields
|
||||
of the resource to be returned. Can not be used
|
||||
when 'detail' is set.
|
||||
|
||||
:returns: A list of deploy templates.
|
||||
|
||||
"""
|
||||
if limit is not None:
|
||||
limit = int(limit)
|
||||
|
||||
if detail and fields:
|
||||
raise exc.InvalidAttribute(_("Can't fetch a subset of fields "
|
||||
"with 'detail' set"))
|
||||
|
||||
filters = utils.common_filters(marker, limit, sort_key, sort_dir,
|
||||
fields, detail=detail)
|
||||
path = ''
|
||||
if filters:
|
||||
path += '?' + '&'.join(filters)
|
||||
|
||||
if limit is None:
|
||||
return self._list(self._path(path), "deploy_templates")
|
||||
else:
|
||||
return self._list_pagination(self._path(path), "deploy_templates",
|
||||
limit=limit)
|
||||
|
||||
def get(self, template_id, fields=None):
|
||||
return self._get(resource_id=template_id, fields=fields)
|
||||
|
||||
def delete(self, template_id):
|
||||
return self._delete(resource_id=template_id)
|
||||
|
||||
def update(self, template_id, patch):
|
||||
return self._update(resource_id=template_id, patch=patch)
|
@ -104,6 +104,7 @@ class Resource(object):
|
||||
'reservation': 'Reservation',
|
||||
'resource_class': 'Resource Class',
|
||||
'state': 'State',
|
||||
'steps': 'Steps',
|
||||
'target_power_state': 'Target Power State',
|
||||
'target_provision_state': 'Target Provision State',
|
||||
'target_raid_config': 'Target RAID configuration',
|
||||
@ -513,3 +514,21 @@ ALLOCATION_RESOURCE = Resource(
|
||||
'node_uuid',
|
||||
],
|
||||
)
|
||||
|
||||
# Deploy templates
|
||||
DEPLOY_TEMPLATE_DETAILED_RESOURCE = Resource(
|
||||
['uuid',
|
||||
'name',
|
||||
'steps',
|
||||
'extra',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
],
|
||||
sort_excluded=['extra', 'steps']
|
||||
)
|
||||
|
||||
DEPLOY_TEMPLATE_RESOURCE = Resource(
|
||||
['uuid',
|
||||
'name',
|
||||
],
|
||||
)
|
||||
|
11
releasenotes/notes/deploy-templates-df354ce825b00430.yaml
Normal file
11
releasenotes/notes/deploy-templates-df354ce825b00430.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Adds Python API and CLI for the deploy templates API introduced in API
|
||||
version 1.55. The following new commands are available:
|
||||
|
||||
* ``openstack baremetal deploy template create``
|
||||
* ``openstack baremetal deploy template delete``
|
||||
* ``openstack baremetal deploy template list``
|
||||
* ``openstack baremetal deploy template set``
|
||||
* ``openstack baremetal deploy template show``
|
@ -40,6 +40,12 @@ openstack.baremetal.v1 =
|
||||
baremetal_chassis_show = ironicclient.osc.v1.baremetal_chassis:ShowBaremetalChassis
|
||||
baremetal_chassis_unset = ironicclient.osc.v1.baremetal_chassis:UnsetBaremetalChassis
|
||||
baremetal_create = ironicclient.osc.v1.baremetal_create:CreateBaremetal
|
||||
baremetal_deploy_template_create = ironicclient.osc.v1.baremetal_deploy_template:CreateBaremetalDeployTemplate
|
||||
baremetal_deploy_template_delete = ironicclient.osc.v1.baremetal_deploy_template:DeleteBaremetalDeployTemplate
|
||||
baremetal_deploy_template_list = ironicclient.osc.v1.baremetal_deploy_template:ListBaremetalDeployTemplate
|
||||
baremetal_deploy_template_set = ironicclient.osc.v1.baremetal_deploy_template:SetBaremetalDeployTemplate
|
||||
baremetal_deploy_template_unset = ironicclient.osc.v1.baremetal_deploy_template:UnsetBaremetalDeployTemplate
|
||||
baremetal_deploy_template_show = ironicclient.osc.v1.baremetal_deploy_template:ShowBaremetalDeployTemplate
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user