Add CLI support for Overclouds and Overcloud Roles

Add support the full range of CRUD commands for Overclouds and Overcloud Roles
in the CLI interface.

Change-Id: I34b42674586bff081eb3936cb5934d539510e371
This commit is contained in:
Dougal Matthews 2014-01-30 12:09:38 +00:00
parent 55d9fda3d3
commit 29dca9d6a0
9 changed files with 787 additions and 87 deletions

View File

@ -109,40 +109,22 @@ def attr_proxy(attr, formatter=lambda a: a, allow_undefined=True):
return formatter_proxy
def capacities_formatter(capacities):
'''Formats a list of capacities for output. Capacity is a dict
containing 'name', 'value' and 'unit' keys.
'''
sorted_capacities = sorted(capacities,
key=lambda c: c['name'])
return '\n'.join(['{0}: {1} {2}'.format(c['name'], c['value'], c['unit'])
for c in sorted_capacities])
def attributes_formatter(attributes):
"""Given a simple dict format the keyvalue pairs with one on each line.
"""
return u"\n".join(u"{0}={1}".format(k, v) for k, v in
sorted(attributes.items()))
def links_formatter(links):
'''Formats a list of links. Link is a dict that has 'href' and
'rel' keys.
'''
sorted_links = sorted(links, key=lambda l: l['rel'])
return '\n'.join(['{0}: {1}'.format(l['rel'], l['href'])
for l in sorted_links])
def counts_formatter(counts):
"""Given a list of dicts that represent Overcloud Roles output the
Overcloud Role ID with the num_noces
"""
pretty_counts = []
def resource_links_formatter(links):
'''Formats an array of resource links. Resource link is a dict
with keys 'id' and 'links'. Under 'links' key there is an array of
links. Link is a dict with 'href' and 'rel' keys. Currently we
expect only one link to be in the array, so we print the first
one. (We cannot fetch by 'rel', values in 'rel' are not used
consistently.)
'''
sorted_links = sorted(links, key=lambda l: l['id'])
return '\n'.join(['{0}: {1}'.format(l['id'], l['links'][0]['href'])
for l in sorted_links])
for count in counts:
line = "{0}={1}".format(count['overcloud_role_id'], count['num_nodes'])
pretty_counts.append(line)
def resource_link_formatter(link):
'''Formats one resource link. See docs of
`resource_links_formatter` for more details.
'''
return resource_links_formatter([link])
return u"\n".join(pretty_counts)

View File

@ -138,3 +138,63 @@ def exit(msg=''):
if msg:
print(msg, file=sys.stderr)
sys.exit(1)
def format_attributes(params):
'''Reformat attributes into dict of format expected by the API.'''
if not params:
return {}
# expect multiple invocations of --parameters but fall back
# to ; delimited if only one --parameters is specified
if len(params) == 1:
params = params[0].split(';')
parameters = {}
for p in params:
try:
(n, v) = p.split(('='), 1)
except ValueError:
msg = '%s(%s). %s.' % ('Malformed parameter', p,
'Use the key=value format')
raise exc.CommandError(msg)
if n not in parameters:
parameters[n] = v
else:
if not isinstance(parameters[n], list):
parameters[n] = [parameters[n]]
parameters[n].append(v)
return parameters
def format_roles(params):
'''Reformat attributes into dict of format expected by the API.'''
if not params:
return []
# expect multiple invocations of --parameters but fall back
# to ; delimited if only one --parameters is specified
if len(params) == 1:
params = params[0].split(';')
parameters = []
for p in params:
try:
(n, v) = p.split(('='), 1)
except ValueError:
msg = '%s(%s). %s.' % ('Malformed parameter', p,
'Use the key=value format')
raise exc.CommandError(msg)
v = int(v)
parameters.append({
'overcloud_role_id': n,
'num_nodes': v
})
return parameters

View File

@ -75,57 +75,27 @@ class PrintTest(tutils.TestCase):
class FormattersTest(tutils.TestCase):
def test_attr_formatter_plain(self):
obj = mock.Mock()
obj.foo = 'bar'
foo_formatter = fmt.attr_proxy('foo')
self.assertEqual('bar', foo_formatter(obj))
def test_attributes_formatter(self):
"""Test the attributes formatter displays the attributes correctly."""
def test_attr_formatter_chained(self):
obj = mock.Mock()
obj.letters = ['a', 'b', 'c']
letters_formatter = fmt.attr_proxy('letters', len)
self.assertEqual(3, letters_formatter(obj))
def test_capacities_formatter(self):
capacities = [
{'name': 'memory', 'value': '1024', 'unit': 'MB'},
{'name': 'cpu', 'value': '2', 'unit': 'CPU'},
]
self.assertEqual(
('cpu: 2 CPU\n'
'memory: 1024 MB'),
fmt.capacities_formatter(capacities),
)
def test_links_formatter(self):
links = [
{'rel': 'self', 'href': 'http://self-url'},
{'rel': 'parent', 'href': 'http://parent-url'},
]
self.assertEqual(
('parent: http://parent-url\n'
'self: http://self-url'),
fmt.links_formatter(links),
)
def test_resource_links_formatter(self):
resource_links = [
{'id': 3, 'links': [{'rel': 'self', 'href': 'http://three'}]},
{'id': 5, 'links': [{'rel': 'self', 'href': 'http://five'}]},
]
self.assertEqual(
('3: http://three\n'
'5: http://five'),
fmt.resource_links_formatter(resource_links),
)
def test_resource_link_formatter(self):
resource_link = {
'id': 3,
'links': [{'rel': 'self', 'href': 'http://three'}]
attributes = {
'password': 'pass',
'mysql_host': 'http://somewhere',
'a thing': 'a value'
}
self.assertEqual(
('3: http://three'),
fmt.resource_link_formatter(resource_link),
("a thing=a value\nmysql_host=http://somewhere\npassword=pass"),
fmt.attributes_formatter(attributes),
)
def test_counts_formatter(self):
resource_link = [
{'overcloud_role_id': 1, 'num_nodes': 10},
{'overcloud_role_id': 2, 'num_nodes': 20}
]
self.assertEqual(
("1=10\n2=20"),
fmt.counts_formatter(resource_link),
)

View File

@ -59,6 +59,224 @@ tests = [
'error: unrecognized arguments: -r',
],
'return_code': 2,
},
# Overcloud Role - Create
{
'commands': ['overcloud-role-create -h',
'overcloud-role-create --help',
'help overcloud-role-create'],
'test_identifiers': ['test_overcloud_role_create_dash_h',
'test_overcloud_role_create_dashdash_help',
'test_help_overcloud_role_create'],
'out_includes': [
'-d <DESCRIPTION>, --description <DESCRIPTION>',
'-i <IMAGE NAME>, --image-name <IMAGE NAME>',
'-f <FLAVOR ID>, --flavor-id <FLAVOR ID>'
],
'out_excludes': [
'-i <ID>, --id <ID>',
'overcloud-role-list',
'overcloud-create',
'--os-username OS_USERNAME',
'Display help for <subcommand>',
],
'err_string': '',
'return_code': 0,
},
# Overcloud Role - Delete
{
'commands': ['overcloud-role-delete -h',
'overcloud-role-delete --help',
'help overcloud-role-delete'],
'test_identifiers': ['test_overcloud_role_delete_dash_h',
'test_overcloud_role_delete_dashdash_help',
'test_help_overcloud_role_delete'],
'out_includes': [
'<ID> ID of Overcloud Role to show.',
],
'out_excludes': [
'overcloud-role-list',
'overcloud-delete',
'--os-username OS_USERNAME',
'Display help for <subcommand>',
'-d <DESCRIPTION>, --description <DESCRIPTION>',
'-i <IMAGE NAME>, --image-name <IMAGE NAME>',
'-f <FLAVOR ID>, --flavor-id <FLAVOR ID>'
],
'err_string': '',
'return_code': 0,
},
# Overcloud Role - List
{
'commands': ['overcloud-role-list -h',
'overcloud-role-list --help',
'help overcloud-role-list'],
'test_identifiers': ['test_overcloud_role_list_dash_h',
'test_overcloud_role_list_dashdash_help',
'test_help_overcloud_role_list'],
'out_includes': [
],
'out_excludes': [
'overcloud-role-delete',
'overcloud-list',
'--os-username OS_USERNAME',
'Display help for <subcommand>',
'-n <NAME>, --name <NAME>',
'-d <DESCRIPTION>, --description <DESCRIPTION>',
'-i <IMAGE NAME>, --image-name <IMAGE NAME>',
'-f <FLAVOR ID>, --flavor-id <FLAVOR ID>'
],
'err_string': '',
'return_code': 0,
},
# Overcloud Role - Show
{
'commands': ['overcloud-role-show -h',
'overcloud-role-show --help',
'help overcloud-role-show'],
'test_identifiers': ['test_overcloud_role_show_dash_h',
'test_overcloud_role_show_dashdash_help',
'test_help_overcloud_role_show'],
'out_includes': [
'<ID> ID of Overcloud Role to show.',
],
'out_excludes': [
'overcloud-role-list',
'overcloud-show',
'--os-username OS_USERNAME',
'Display help for <subcommand>',
],
'err_string': '',
'return_code': 0,
},
# Overcloud Role - Update
{
'commands': ['overcloud-role-update -h',
'overcloud-role-update --help',
'help overcloud-role-update'],
'test_identifiers': ['test_overcloud_role_update_dash_h',
'test_overcloud_role_update_dashdash_help',
'test_help_overcloud_role_update'],
'out_includes': [
'-n <NAME>, --name <NAME>',
'-d <DESCRIPTION>, --description <DESCRIPTION>',
'-i <IMAGE NAME>, --image-name <IMAGE NAME>',
'-f <FLAVOR ID>, --flavor-id <FLAVOR ID>'
],
'out_excludes': [
'overcloud-role-list',
'overcloud-update',
'--os-username OS_USERNAME',
'Display help for <subcommand>',
],
'err_string': '',
'return_code': 0,
},
# Overcloud - Create
{
'commands': ['overcloud-create -h',
'overcloud-create --help',
'help overcloud-create'],
'test_identifiers': ['test_overcloud_create_dash_h',
'test_overcloud_create_dashdash_help',
'test_help_overcloud_create'],
'out_includes': [
],
'out_excludes': [
'-i <ID>, --id <ID>',
'overcloud-list',
'overcloud-role-create',
'--os-username OS_USERNAME',
'Display help for <subcommand>',
],
'err_string': '',
'return_code': 0,
},
# Overcloud - Delete
{
'commands': ['overcloud-delete -h',
'overcloud-delete --help',
'help overcloud-delete'],
'test_identifiers': ['test_overcloud_delete_dash_h',
'test_overcloud_delete_dashdash_help',
'test_help_overcloud_delete'],
'out_includes': [
'<ID> ID of Overcloud to show.',
],
'out_excludes': [
'overcloud-list',
'overcloud-role-delete',
'--os-username OS_USERNAME',
'Display help for <subcommand>',
'-d <DESCRIPTION>, --description <DESCRIPTION>',
'-i <IMAGE NAME>, --image-name <IMAGE NAME>',
'-f <FLAVOR ID>, --flavor-id <FLAVOR ID>'
],
'err_string': '',
'return_code': 0,
},
# Overcloud - List
{
'commands': ['overcloud-list -h',
'overcloud-list --help',
'help overcloud-list'],
'test_identifiers': ['test_overcloud_list_dash_h',
'test_overcloud_list_dashdash_help',
'test_help_overcloud_list'],
'out_includes': [
],
'out_excludes': [
'overcloud-delete',
'overcloud-role-list',
'--os-username OS_USERNAME',
'Display help for <subcommand>',
'-n <NAME>, --name <NAME>',
'-d <DESCRIPTION>, --description <DESCRIPTION>',
'-i <IMAGE NAME>, --image-name <IMAGE NAME>',
'-f <FLAVOR ID>, --flavor-id <FLAVOR ID>'
],
'err_string': '',
'return_code': 0,
},
# Overcloud - Show
{
'commands': ['overcloud-show -h',
'overcloud-show --help',
'help overcloud-show'],
'test_identifiers': ['test_overcloud_show_dash_h',
'test_overcloud_show_dashdash_help',
'test_help_overcloud_show'],
'out_includes': [
'<ID> ID of Overcloud to show.',
],
'out_excludes': [
'overcloud-list',
'overcloud-role-show',
'--os-username OS_USERNAME',
'Display help for <subcommand>',
],
'err_string': '',
'return_code': 0,
},
# Overcloud - Update
{
'commands': ['overcloud-update -h',
'overcloud-update --help',
'help overcloud-update'],
'test_identifiers': ['test_overcloud_update_dash_h',
'test_overcloud_update_dashdash_help',
'test_help_overcloud_update'],
'out_includes': [
'-n <NAME>, --name <NAME>'
],
'out_excludes': [
'overcloud-list',
'overcloud-role-update',
'--os-username OS_USERNAME',
'Display help for <subcommand>',
],
'err_string': '',
'return_code': 0,
}
]
@ -77,16 +295,33 @@ def create_test_method(command, expected_values):
))
return test_command_method
# creates a method for each command found in tests
# to let developer see what test is failing in test results,
# ie: ... HelpCommandTest.test_help_flavor_list
# this way dev can "just search" for "test_help_flavor_list"
# and he will find actual data used in failing test
# Create a method for each command found in the above tests. The tests will be
# constructed on the HelpCommandTest class with the name given in the test
# identifiers. This way the developer can search the above structure for the
# identifier and find the actual data used in the test.
duplicated = []
for test in tests:
commands = test.get('commands')
for index, command in enumerate(commands):
test_command_method = create_test_method(command, test)
test_command_method.__name__ = test.get('test_identifiers')[index]
if hasattr(HelpCommandTest, test_command_method.__name__):
duplicated.append(test_command_method.__name__)
setattr(HelpCommandTest,
test_command_method.__name__,
test_command_method)
# Finally add a meta test to verify that no test identifiers were used twice
# which would result in only the last test being added.
def _meta_verify_test_builder(self):
self.assertEqual(
duplicated, [], "Expected no test identifiers to be "
"duplicated but found {0}".format(len(duplicated))
)
setattr(HelpCommandTest, 'test_test_builder_for_duplicates',
_meta_verify_test_builder)

View File

@ -0,0 +1,127 @@
# 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 io
import mock
import tuskarclient.tests.utils as tutils
from tuskarclient.v1 import overcloud_roles_shell
def empty_args():
args = mock.Mock(spec=[])
for attr in ['id', 'name', 'subnet', 'capacities', 'slots',
'resource_class']:
setattr(args, attr, None)
return args
def mock_overcloud():
overcloud = mock.Mock()
overcloud.id = '5'
overcloud.name = 'Testing Overcloud Role'
return overcloud
class RacksShellTest(tutils.TestCase):
def setUp(self):
self.outfile = io.StringIO()
self.tuskar = mock.MagicMock()
self.shell = overcloud_roles_shell
super(RacksShellTest, self).setUp()
@mock.patch('tuskarclient.common.utils.find_resource')
@mock.patch('tuskarclient.v1.overcloud_roles_shell.print_role_detail')
def test_overcloud_role_show(self, mock_print_detail, mock_find_resource):
mock_find_resource.return_value = mock_overcloud()
args = empty_args()
args.id = '5'
self.shell.do_overcloud_role_show(self.tuskar, args,
outfile=self.outfile)
mock_find_resource.assert_called_with(self.tuskar.overcloud_roles, '5')
mock_print_detail.assert_called_with(mock_find_resource.return_value,
outfile=self.outfile)
@mock.patch('tuskarclient.common.formatting.print_list')
def test_overcloud_role_list(self, mock_print_list):
args = empty_args()
self.shell.do_overcloud_role_list(self.tuskar, args,
outfile=self.outfile)
# testing the other arguments would be just copy-paste
mock_print_list.assert_called_with(
self.tuskar.overcloud_roles.list.return_value, mock.ANY,
outfile=self.outfile
)
@mock.patch('tuskarclient.common.utils.find_resource')
@mock.patch('tuskarclient.v1.overcloud_roles_shell.print_role_detail')
def test_overcloud_role_create(self, mock_print, mock_find_resource):
mock_find_resource.return_value = mock_overcloud()
args = empty_args()
args.name = 'My Overcloud Role'
args.description = 'This is an Overcloud Role.'
args.image_name = 'image'
args.flavor_id = '1'
self.shell.do_overcloud_role_create(self.tuskar, args,
outfile=self.outfile)
self.tuskar.overcloud_roles.create.assert_called_with(
name='My Overcloud Role',
flavor_id='1',
description='This is an Overcloud Role.',
image_name='image'
)
mock_print.assert_called_with(
self.tuskar.overcloud_roles.create.return_value,
outfile=self.outfile
)
@mock.patch('tuskarclient.common.utils.find_resource')
@mock.patch('tuskarclient.v1.overcloud_roles_shell.print_role_detail')
def test_overcloud_role_update(self, mock_print, mock_find_resource):
mock_find_resource.return_value = mock_overcloud()
args = empty_args()
args.id = '5'
args.name = 'My Overcloud Role'
args.description = 'This is an Overcloud Role.'
args.image_name = 'image'
args.flavor_id = '1'
self.shell.do_overcloud_role_update(self.tuskar, args,
outfile=self.outfile)
self.tuskar.overcloud_roles.update.assert_called_with(
'5',
name='My Overcloud Role',
flavor_id='1',
description='This is an Overcloud Role.',
image_name='image'
)
mock_print.assert_called_with(
self.tuskar.overcloud_roles.update.return_value,
outfile=self.outfile
)
@mock.patch('tuskarclient.common.utils.find_resource')
def test_overcloud_role_delete(self, mock_find_resource):
mock_find_resource.return_value = mock_overcloud()
args = empty_args()
args.id = '5'
self.shell.do_overcloud_role_delete(self.tuskar, args,
outfile=self.outfile)
self.tuskar.overcloud_roles.delete.assert_called_with('5')
self.assertEqual('Deleted Overcloud Role "Testing Overcloud Role".\n',
self.outfile.getvalue())

View File

@ -0,0 +1,114 @@
# 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 io
import mock
import tuskarclient.tests.utils as tutils
from tuskarclient.v1 import overclouds_shell
def empty_args():
args = mock.Mock(spec=[])
for attr in ['id', 'name', 'subnet', 'capacities', 'slots',
'resource_class']:
setattr(args, attr, None)
return args
def mock_overcloud():
overcloud = mock.Mock()
overcloud.id = '5'
overcloud.name = 'My Overcloud'
return overcloud
class RacksShellTest(tutils.TestCase):
def setUp(self):
self.outfile = io.StringIO()
self.tuskar = mock.MagicMock()
self.shell = overclouds_shell
super(RacksShellTest, self).setUp()
@mock.patch('tuskarclient.common.utils.find_resource')
@mock.patch('tuskarclient.v1.overclouds_shell.print_overcloud_detail')
def test_overcloud_show(self, mock_print_detail, mock_find_resource):
mock_find_resource.return_value = mock_overcloud()
args = empty_args()
args.id = '5'
self.shell.do_overcloud_show(self.tuskar, args, outfile=self.outfile)
mock_find_resource.assert_called_with(self.tuskar.overclouds, '5')
mock_print_detail.assert_called_with(mock_find_resource.return_value,
outfile=self.outfile)
@mock.patch('tuskarclient.common.formatting.print_list')
def test_overcloud_list(self, mock_print_list):
args = empty_args()
self.shell.do_overcloud_list(self.tuskar, args, outfile=self.outfile)
# testing the other arguments would be just copy-paste
mock_print_list.assert_called_with(
self.tuskar.overclouds.list.return_value, mock.ANY, mock.ANY,
outfile=self.outfile
)
@mock.patch('tuskarclient.common.utils.find_resource')
@mock.patch('tuskarclient.v1.overclouds_shell.print_overcloud_detail')
def test_overcloud_create(self, mock_print_detail, mock_find_resource):
mock_find_resource.return_value = mock_overcloud()
args = empty_args()
args.name = 'my_overcloud'
args.attributes = None
args.roles = None
self.shell.do_overcloud_create(self.tuskar, args, outfile=self.outfile)
self.tuskar.overclouds.create.assert_called_with(
name='my_overcloud',
counts=[],
attributes={}
)
mock_print_detail.assert_called_with(
self.tuskar.overclouds.create.return_value, outfile=self.outfile)
@mock.patch('tuskarclient.common.utils.find_resource')
@mock.patch('tuskarclient.v1.overclouds_shell.print_overcloud_detail')
def test_overcloud_update(self, mock_print_detail, mock_find_resource):
mock_find_resource.return_value = mock_overcloud()
args = empty_args()
args.id = '5'
args.name = 'my_overcloud'
args.attributes = None
args.roles = None
self.shell.do_overcloud_update(self.tuskar, args, outfile=self.outfile)
self.tuskar.overclouds.update.assert_called_with(
'5',
name='my_overcloud',
attributes={},
counts=[]
)
mock_print_detail.assert_called_with(
self.tuskar.overclouds.update.return_value, outfile=self.outfile)
@mock.patch('tuskarclient.common.utils.find_resource')
def test_overcloud_delete(self, mock_find_resource):
mock_find_resource.return_value = mock_overcloud()
args = empty_args()
args.id = '5'
self.shell.do_overcloud_delete(self.tuskar, args, outfile=self.outfile)
self.tuskar.overclouds.delete.assert_called_with('5')
self.assertEqual('Deleted Overcloud "My Overcloud".\n',
self.outfile.getvalue())

View File

@ -0,0 +1,93 @@
# 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 __future__ import print_function
import sys
import tuskarclient.common.formatting as fmt
from tuskarclient.common import utils
@utils.arg('id', metavar="<ID>", help="ID of Overcloud Role to show.")
def do_overcloud_role_show(tuskar, args, outfile=sys.stdout):
"""Given a Tuskar client instance and the command line arguments display
the detail to the user.
"""
overcloud_role = utils.find_resource(tuskar.overcloud_roles, args.id)
print_role_detail(overcloud_role, outfile=outfile)
def do_overcloud_role_list(tuskar, args, outfile=sys.stdout):
overcloud_roles = tuskar.overcloud_roles.list()
fields = ['id', 'name', 'image_name', 'flavor_id']
fmt.print_list(overcloud_roles, fields, outfile=outfile)
@utils.arg('name', help="Name of the Overcloud Role to create.")
@utils.arg('-d', '--description', metavar="<DESCRIPTION>",
help='User-readable text describing the overcloud.')
@utils.arg('-i', '--image-name', metavar="<IMAGE NAME>",
help='Name of the image in Glance to be used for this Role.')
@utils.arg('-f', '--flavor-id', metavar="<FLAVOR ID>",
help='UUID of the flavor of node this role should be deployed on.')
def do_overcloud_role_create(tuskar, args, outfile=sys.stdout):
overcloud_role_dict = create_overcloud_role_dict(args)
overcloud_role = tuskar.overcloud_roles.create(**overcloud_role_dict)
print_role_detail(overcloud_role, outfile=outfile)
@utils.arg('id', metavar="<ID>", help="ID of overcloud to show.")
@utils.arg('-n', '--name', metavar="<NAME>",
help='Name of the Overcloud Role to update.')
@utils.arg('-d', '--description', metavar="<DESCRIPTION>",
help='User-readable text describing the overcloud.')
@utils.arg('-i', '--image-name', metavar="<IMAGE NAME>",
help='Name of the image in Glance to be used for this Role.')
@utils.arg('-f', '--flavor-id', metavar="<FLAVOR ID>",
help='UUID of the flavor of node this role should be deployed on.')
def do_overcloud_role_update(tuskar, args, outfile=sys.stdout):
overcloud_role = utils.find_resource(tuskar.overcloud_roles, args.id)
overcloud_role_dict = create_overcloud_role_dict(args)
updated_overcloud_role = tuskar.overcloud_roles.update(
overcloud_role.id,
**overcloud_role_dict
)
print_role_detail(updated_overcloud_role, outfile=outfile)
@utils.arg('id', metavar="<ID>", help="ID of Overcloud Role to show.")
def do_overcloud_role_delete(tuskar, args, outfile=sys.stdout):
overcloud_role = utils.find_resource(tuskar.overcloud_roles, args.id)
tuskar.overcloud_roles.delete(args.id)
print(u'Deleted Overcloud Role "%s".' % overcloud_role.name, file=outfile)
def create_overcloud_role_dict(args):
"""Marshal command line arguments to an API request dict."""
overcloud_role_dict = {}
simple_fields = ['name', 'description', 'image_name', 'flavor_id']
for field_name in simple_fields:
field_value = vars(args)[field_name]
if field_value is not None:
overcloud_role_dict[field_name] = field_value
utils.marshal_association(args, overcloud_role_dict, 'resource_class')
return overcloud_role_dict
def print_role_detail(overcloud_role, outfile=sys.stdout):
"""Print detailed Overcloud Role information (overcloud-role-show etc.)."""
overcloud_role_dict = overcloud_role.to_dict()
fmt.print_dict(overcloud_role_dict, outfile=outfile)

View File

@ -0,0 +1,115 @@
# 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 __future__ import print_function
import sys
import tuskarclient.common.formatting as fmt
from tuskarclient.common import utils
@utils.arg('id', metavar="<ID>", help="ID of Overcloud to show.")
def do_overcloud_show(tuskar, args, outfile=sys.stdout):
"""Given a Tuskar client instance and the command line arguments display
the detail to the user.
"""
overcloud = utils.find_resource(tuskar.overclouds, args.id)
print_overcloud_detail(overcloud, outfile=outfile)
def do_overcloud_list(tuskar, args, outfile=sys.stdout):
overclouds = tuskar.overclouds.list()
fields = ['id', 'name', 'description', 'stack_id', 'attributes', 'counts']
formatters = {
'attributes': fmt.attributes_formatter,
'counts': fmt.counts_formatter,
}
fmt.print_list(overclouds, fields, formatters, outfile=outfile)
@utils.arg('name', help="Name of the Overcloud to create.")
@utils.arg('-d', '--description', metavar="<DESCRIPTION>",
help='User-readable text describing the overcloud.')
@utils.arg('-s', '--stack-id', metavar="<STACK ID>",
help='UID of the stack in Heat.')
@utils.arg('--attributes', metavar='<KEY1=VALUE1;KEY2=VALUE2...>',
help='This can be specified multiple times, or once with parameters'
' separated by semicolon.',
action='append')
@utils.arg('--roles', metavar='<ROLE NAME=COUNT;ROLE NAME=COUNT...>',
help='This can be specified multiple times, or once with parameters'
' separated by semicolon.',
action='append')
def do_overcloud_create(tuskar, args, outfile=sys.stdout):
overcloud_dict = create_overcloud_dict(args)
overcloud = tuskar.overclouds.create(**overcloud_dict)
print_overcloud_detail(overcloud, outfile=outfile)
@utils.arg('id', metavar="<ID>", help="ID of Overcloud to show.")
@utils.arg('-n', '--name', metavar="<NAME>",
help='Name of the Overcloud Role to update.')
@utils.arg('-d', '--description', metavar="<DESCRIPTION>",
help='User-readable text describing the overcloud.')
@utils.arg('-s', '--stack-id', metavar="<STACK ID>",
help='UID of the stack in Heat.')
@utils.arg('--attributes', metavar='<KEY1=VALUE1;KEY2=VALUE2...>',
help='This can be specified multiple times, or once with parameters'
' separated by semicolon.',
action='append')
@utils.arg('--roles', metavar='<ROLE NAME=COUNT;ROLE NAME=COUNT...>',
help='This can be specified multiple times, or once with parameters'
' separated by semicolon.',
action='append')
def do_overcloud_update(tuskar, args, outfile=sys.stdout):
overcloud = utils.find_resource(tuskar.overclouds, args.id)
overcloud_dict = create_overcloud_dict(args)
updated_overcloud = tuskar.overclouds.update(overcloud.id,
**overcloud_dict)
print_overcloud_detail(updated_overcloud, outfile=outfile)
@utils.arg('id', metavar="<ID>", help="ID of Overcloud to show.")
def do_overcloud_delete(tuskar, args, outfile=sys.stdout):
overcloud = utils.find_resource(tuskar.overclouds, args.id)
tuskar.overclouds.delete(args.id)
print(u'Deleted Overcloud "%s".' % overcloud.name, file=outfile)
def create_overcloud_dict(args):
"""Marshal command line arguments to an API request dict."""
overcloud_dict = {}
simple_fields = ['name', 'description']
for field_name in simple_fields:
field_value = vars(args).get(field_name)
if field_value is not None:
overcloud_dict[field_name] = field_value
overcloud_dict['attributes'] = utils.format_attributes(args.attributes)
overcloud_dict['counts'] = utils.format_roles(args.roles)
utils.marshal_association(args, overcloud_dict, 'resource_class')
return overcloud_dict
def print_overcloud_detail(overcloud, outfile=sys.stdout):
"""Print detailed overcloud information (for overcloud-show etc.)."""
formatters = {
'attributes': fmt.attributes_formatter,
'counts': fmt.counts_formatter,
}
overcloud_dict = overcloud.to_dict()
fmt.print_dict(overcloud_dict, formatters, outfile=outfile)

View File

@ -11,8 +11,12 @@
# under the License.
from tuskarclient.common import utils
from tuskarclient.v1 import overcloud_roles_shell
from tuskarclient.v1 import overclouds_shell
COMMAND_MODULES = [
overcloud_roles_shell,
overclouds_shell
]