diff --git a/tuskarclient/common/utils.py b/tuskarclient/common/utils.py index 6894994..b627324 100644 --- a/tuskarclient/common/utils.py +++ b/tuskarclient/common/utils.py @@ -15,6 +15,7 @@ from __future__ import print_function +import sys import uuid from oslo_utils import importutils @@ -116,7 +117,7 @@ def format_key_value(params): yield name, value -def format_attributes(params): +def format_key_value_args(params): """Reformat CLI attributes into the structure expected by the API. The format expected by the API for attributes is a dictionary consisting @@ -135,3 +136,33 @@ def format_attributes(params): attributes[key] = value return attributes + + +def parameters_args_to_patch(parameters): + """Create a list of patch dicts to update the parameters in the API.""" + + return [{'name': pair[0], 'value': pair[1]} + for pair in sorted(format_key_value_args(parameters).items())] + + +def args_to_patch(flavors, roles, parameter): + """Create a list of dicts to update the given parameter in the API.""" + + role_flavors_dict = format_key_value_args(flavors) + + roles = dict(("{0}-{1}".format(r.name, r.version), r) for r in roles) + patch = [] + + for role_name, flavor in sorted(role_flavors_dict.items()): + + if role_name in roles: + patch.append({ + 'name': "{0}::{1}".format(role_name, parameter), + 'value': flavor + }) + else: + print("ERROR: no roles were found in the Plan with the name {0}". + format(role_name), file=sys.stderr) + continue + + return patch diff --git a/tuskarclient/osc/v2/plan.py b/tuskarclient/osc/v2/plan.py index 2048a8f..1d8dae4 100644 --- a/tuskarclient/osc/v2/plan.py +++ b/tuskarclient/osc/v2/plan.py @@ -10,12 +10,17 @@ # License for the specific language governing permissions and limitations # under the License. +from __future__ import print_function + import logging +import sys from cliff import command from cliff import lister from cliff import show +from tuskarclient.common import utils + class CreateManagementPlan(show.ShowOne): """Create a Management Plan.""" @@ -103,11 +108,57 @@ class SetManagementPlan(show.ShowOne): def get_parser(self, prog_name): parser = super(SetManagementPlan, self).get_parser(prog_name) + + parser.add_argument( + 'plan_uuid', + help="The UUID of the plan being updated." + ) + + parser.add_argument( + '-P', '--parameter', dest='parameters', metavar='', + help=('Set a parameter in the Plan. This can be specified ' + 'multiple times.'), + action='append' + ) + + parser.add_argument( + '-F', '--flavor', dest='flavors', metavar='', + help=('Set the flavor for a role in the Plan. This can be ' + 'specified multiple times.'), + action='append' + ) + + parser.add_argument( + '-S', '--scale', dest='scales', metavar='', + help=('Set the Scale count for a role in the Plan. This can be ' + 'specified multiple times.'), + action='append' + ) + return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)" % parsed_args) + client = self.app.client_manager.management + + plan = client.plans.get(parsed_args.plan_uuid) + roles = plan.roles + + patch = [] + + patch.extend(utils.parameters_args_to_patch(parsed_args.parameters)) + patch.extend(utils.args_to_patch(parsed_args.flavors, roles, "Flavor")) + patch.extend(utils.args_to_patch(parsed_args.scales, roles, "count")) + + if len(patch) > 0: + plan = client.plans.patch(parsed_args.plan_uuid, patch) + else: + print(("WARNING: No valid arguments passed. No update operation " + "has been performed."), file=sys.stderr) + + return self.dict2columns(plan.to_dict()) + class ShowManagementPlan(show.ShowOne): """Show a Management Plan.""" diff --git a/tuskarclient/tests/common/test_utils.py b/tuskarclient/tests/common/test_utils.py index 0220278..a0044c2 100644 --- a/tuskarclient/tests/common/test_utils.py +++ b/tuskarclient/tests/common/test_utils.py @@ -96,3 +96,59 @@ class FindResourceTest(test_utils.TestCase): utils.find_resource, self.manager, 'My Overcloud 2') + + +class ParseCLIArgsTest(test_utils.TestCase): + + def setUp(self): + super(ParseCLIArgsTest, self).setUp() + + self.mock_role1 = mock.Mock() + self.mock_role1.configure_mock(name="role1", version=1) + + self.mock_role2 = mock.Mock() + self.mock_role2.configure_mock(name="role2", version=2) + + self.roles = [self.mock_role1, self.mock_role2] + + def test_parameters_args_to_patch(self): + + args = [ + "parameter1=value1", + "parameter2=value2", + ] + + result = utils.parameters_args_to_patch(args) + + self.assertEqual([ + {'name': 'parameter1', 'value': 'value1'}, + {'name': 'parameter2', 'value': 'value2'}, + ], result) + + def test_flavors_args_to_patch(self): + + args = [ + "role1-1=flavor1", + "role2-2=flavor2", + ] + + result = utils.args_to_patch(args, self.roles, "Flavor") + + self.assertEqual([ + {'name': 'role1-1::Flavor', 'value': 'flavor1'}, + {'name': 'role2-2::Flavor', 'value': 'flavor2'} + ], result) + + def test_scale_args_to_patch(self): + + args = [ + "role1-1=1", + "role2-2=2", + ] + + result = utils.args_to_patch(args, self.roles, "count") + + self.assertEqual([ + {'name': 'role1-1::count', 'value': '1'}, + {'name': 'role2-2::count', 'value': '2'} + ], result) diff --git a/tuskarclient/tests/osc/v2/test_plans.py b/tuskarclient/tests/osc/v2/test_plans.py index b18841b..fccc7d7 100644 --- a/tuskarclient/tests/osc/v2/test_plans.py +++ b/tuskarclient/tests/osc/v2/test_plans.py @@ -115,14 +115,103 @@ class TestSetManagementPlan(TestPlans): super(TestSetManagementPlan, self).setUp() self.cmd = plan.SetManagementPlan(self.app, None) - def test_update_plan(self): - arglist = [] - verifylist = [] + def test_update_plan_nothing(self): + arglist = ['UUID1', ] + verifylist = [ + ('plan_uuid', "UUID1"), + ('parameters', None), + ('flavors', None), + ('scales', None), + ] + + self.management_mock.plans.get.return_value = fakes.mock_plans[1] + self.management_mock.plans.patch.return_value = fakes.mock_plans[1] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) + self.management_mock.plans.patch.assert_not_called() + + def test_update_plan_parameters(self): + arglist = ['UUID1', '-P', 'A=1', '-P', 'B=2'] + verifylist = [ + ('plan_uuid', "UUID1"), + ('parameters', ['A=1', 'B=2']), + ('flavors', None), + ('scales', None), + ] + + self.management_mock.plans.get.return_value = fakes.mock_plans[1] + self.management_mock.plans.patch.return_value = fakes.mock_plans[1] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertEqual([ + ('description', 'name', 'roles', 'uuid'), + ('Plan 2', 'Plan 2 Name', [], 'UUID2') + ], list(result)) + + self.management_mock.plans.patch.assert_called_with('UUID1', [ + {'value': '1', 'name': 'A'}, + {'value': '2', 'name': 'B'} + ]) + + def test_update_plan_flavors(self): + arglist = ['UUID1', '-F', 'Role 1 Name-1=strawberry', + '-F', 'Role 2 Name-2=cherry'] + verifylist = [ + ('plan_uuid', "UUID1"), + ('parameters', None), + ('flavors', ['Role 1 Name-1=strawberry', 'Role 2 Name-2=cherry']), + ('scales', None), + ] + + self.management_mock.plans.get.return_value = fakes.mock_plans[0] + self.management_mock.plans.patch.return_value = fakes.mock_plans[1] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertEqual([ + ('description', 'name', 'roles', 'uuid'), + ('Plan 2', 'Plan 2 Name', [], 'UUID2') + ], list(result)) + + self.management_mock.plans.patch.assert_called_with('UUID1', [ + {'value': 'strawberry', 'name': 'Role 1 Name-1::Flavor'}, + {'value': 'cherry', 'name': 'Role 2 Name-2::Flavor'} + ]) + + def test_update_plan_scale(self): + arglist = ['UUID1', '-S', 'Role 1 Name-1=2', '-S', 'Role 2 Name-2=3'] + verifylist = [ + ('plan_uuid', "UUID1"), + ('parameters', None), + ('flavors', None), + ('scales', ['Role 1 Name-1=2', 'Role 2 Name-2=3']), + ] + + self.management_mock.plans.get.return_value = fakes.mock_plans[0] + self.management_mock.plans.patch.return_value = fakes.mock_plans[1] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertEqual([ + ('description', 'name', 'roles', 'uuid'), + ('Plan 2', 'Plan 2 Name', [], 'UUID2') + ], list(result)) + + self.management_mock.plans.patch.assert_called_with('UUID1', [ + {'value': '2', 'name': 'Role 1 Name-1::count'}, + {'value': '3', 'name': 'Role 2 Name-2::count'} + ]) + class TestShowManagementPlan(TestPlans): diff --git a/tuskarclient/v2/plans_shell.py b/tuskarclient/v2/plans_shell.py index 2bcc02f..15bda85 100644 --- a/tuskarclient/v2/plans_shell.py +++ b/tuskarclient/v2/plans_shell.py @@ -244,7 +244,7 @@ def do_plan_update(tuskar, args, outfile=sys.stdout): parameters = [{'name': pair[0], 'value': pair[1]} for pair - in utils.format_attributes(parameters).items()] + in utils.format_key_value_args(parameters).items()] return tuskar.plans.patch(args.plan_uuid, parameters)