diff --git a/doc/source/cli/v2/plans.rst b/doc/source/cli/v2/plans.rst index a3b6536..14288b6 100644 --- a/doc/source/cli/v2/plans.rst +++ b/doc/source/cli/v2/plans.rst @@ -137,7 +137,7 @@ This will unassign Role from a Plan. This will not delete the Role from Tuskar. Output of this command is the same as for plan-show. Show Plan’s scale ------------------------ +----------------- *tuskar plan-show-scale plan_uuid* Usage example: @@ -149,7 +149,7 @@ Usage example: Output of this command is a table containing role names with versions and their counts. Scaling a Plan ------------------------ +-------------- *tuskar plan-scale --count= plan_uuid* Usage example: @@ -161,6 +161,31 @@ Usage example: This will scale given Plan’s role with specified count of nodes. Output of this command is a short summary of changed values. +Show Plan’s Flavors assigned to Roles +------------------------------------- +*tuskar plan-show-flavors plan_uuid* + +Usage example: + +:: + + tuskar plan-show-flavors c367b394-7179-4c44-85ed-bf84baaf9fee + +Output of this command is a table containing roles and assigned flavors. + +Assign Flavors to Roles in a Plan +--------------------------------- +*tuskar plan-flavor --flavor= plan_uuid* + +Usage example: + +:: + + tuskar plan-flavor compute-1 --flavor=baremetal c367b394-7179-4c44-85ed-bf84baaf9fee + +This will update role-flavor assignment in a Plan. +Output of this command is a short summary of changed values. + Changing a Plan’s Configuration Values -------------------------------------- *tuskar plan-update [-h] [-A ] plan_uuid* diff --git a/tuskarclient/tests/v2/test_plans_shell.py b/tuskarclient/tests/v2/test_plans_shell.py index fff4b4c..27f7622 100644 --- a/tuskarclient/tests/v2/test_plans_shell.py +++ b/tuskarclient/tests/v2/test_plans_shell.py @@ -29,7 +29,9 @@ def mock_plan(): plan.uuid = '5' plan.name = 'My Plan' plan.to_dict.return_value = {'uuid': 5, 'name': 'My Plan'} - plan.parameters = [{'name': 'compute-1::count', 'value': '2'}] + plan.parameters = [] + plan.parameters.append({'name': 'compute-1::count', 'value': '2'}) + plan.parameters.append({'name': 'compute-1::Flavor', 'value': 'baremetal'}) return plan @@ -79,6 +81,19 @@ class PlansShellTest(BasePlansShellTest): mock_print_dict.assert_called_with({'compute-1': '2'}, outfile=self.outfile) + @mock.patch('tuskarclient.common.utils.find_resource') + @mock.patch('tuskarclient.common.formatting.print_dict') + def test_plan_show_flavors(self, mock_print_dict, mock_find_resource): + mock_find_resource.return_value = mock_plan() + args = empty_args() + args.plan = '5' + + self.shell.do_plan_show_flavors(self.tuskar, args, + outfile=self.outfile) + mock_find_resource.assert_called_with(self.tuskar.plans, '5') + mock_print_dict.assert_called_with({'compute-1': 'baremetal'}, + outfile=self.outfile) + @mock.patch('tuskarclient.common.utils.find_resource') def test_plan_delete(self, mock_find_resource): mock_find_resource.return_value = mock_plan() @@ -151,6 +166,29 @@ class PlansShellTest(BasePlansShellTest): sorted(self.tuskar.plans.patch.call_args[0][1], key=lambda k: k['name'])) + @mock.patch('tuskarclient.common.utils.find_resource') + def test_plan_flavor(self, mock_find_resource): + mock_find_resource.return_value = mock_plan() + role = mock.Mock() + role.name = 'compute' + role.version = 1 + self.tuskar.roles.list.return_value = [role] + + args = empty_args() + args.plan_uuid = 'plan_uuid' + args.role_name = 'compute-1' + args.flavor = 'baremetalssd' + + attributes = [{'name': 'compute-1::Flavor', 'value': 'baremetalssd'}] + + self.shell.do_plan_flavor(self.tuskar, args, outfile=self.outfile) + self.tuskar.plans.patch.assert_called_once() + self.assertEqual('plan_uuid', self.tuskar.plans.patch.call_args[0][0]) + self.assertEqual( + sorted(attributes, key=lambda k: k['name']), + sorted(self.tuskar.plans.patch.call_args[0][1], + key=lambda k: k['name'])) + @mock.patch('tuskarclient.v2.plans_shell.print_plan_detail') def test_plan_patch(self, mock_print_detail): args = empty_args() @@ -194,6 +232,13 @@ class PlansShellTest(BasePlansShellTest): self.shell.do_plan_show(self.tuskar, args, outfile=self.outfile) mock_find_resource.assert_called_with(self.tuskar.plans, '5') + def test_filter_parameters_to_dict(self): + parameters = [{'name': 'compute-1::count', 'value': '2'}] + self.assertEqual( + self.shell.filter_parameters_to_dict(parameters, 'count'), + {'compute-1': '2'} + ) + @mock.patch('tuskarclient.v2.plans_shell.print', create=True) @mock.patch('tuskarclient.v2.plans_shell.os.mkdir', create=True) @mock.patch('tuskarclient.v2.plans_shell.os.path.isdir', create=True) diff --git a/tuskarclient/v2/plans_shell.py b/tuskarclient/v2/plans_shell.py index 6fead44..e4a880f 100644 --- a/tuskarclient/v2/plans_shell.py +++ b/tuskarclient/v2/plans_shell.py @@ -44,15 +44,29 @@ def do_plan_show(tuskar, args, outfile=sys.stdout): def do_plan_show_scale(tuskar, args, outfile=sys.stdout): """Show scale counts of Plan.""" plan = utils.find_resource(tuskar.plans, args.plan) - scales = dict() - - for param in plan.parameters: - if param['name'].endswith('::count'): - scales[param['name'].replace('::count', '')] = param["value"] - + scales = filter_parameters_to_dict(plan.parameters, 'count') fmt.print_dict(scales, outfile=outfile) +@utils.arg('plan', metavar="", + help="UUID of the Plan to show a scale.") +def do_plan_show_flavors(tuskar, args, outfile=sys.stdout): + """Show flavors assigned to roles of Plan.""" + plan = utils.find_resource(tuskar.plans, args.plan) + flavors = filter_parameters_to_dict(plan.parameters, 'Flavor') + fmt.print_dict(flavors, outfile=outfile) + + +def filter_parameters_to_dict(parameters, param_name): + """Filters list of parameters for given parameter name suffix.""" + filtered_params = {} + suffix = '::{0}'.format(param_name) + for param in parameters: + if param['name'].endswith(suffix): + filtered_params[param['name'].replace(suffix, '')] = param["value"] + return filtered_params + + def print_plan_detail(plan, outfile=sys.stdout): """Print detailed Plan information (for plan-show etc.).""" @@ -143,6 +157,41 @@ def do_plan_scale(tuskar, args, outfile=sys.stdout): format(args.role_name), file=sys.stderr) +@utils.arg('role_name', help="Name of role which you want to flavor.") +@utils.arg('plan_uuid', help="UUID of the Plan to modify.") +@utils.arg('-F', '--flavor', help="Flavor which shall be assigned to role.", + required=True) +def do_plan_flavor(tuskar, args, outfile=sys.stdout): + """Change flavor of role in the plan.""" + roles = tuskar.roles.list() + plan = utils.find_resource(tuskar.plans, args.plan_uuid) + attributes = [] + + for role in roles: + versioned_name = "{name}-{v}".format(name=role.name, v=role.version) + if versioned_name == args.role_name: + role_name_key = versioned_name + "::Flavor" + attributes.append({'name': role_name_key, + 'value': args.flavor}) + old_val = [p['value'] for p in plan.parameters + if p['name'] == role_name_key][0] + + if old_val != args.flavor: + print("Changing {role} flavor: {old_val} -> {new_val}".format( + role=args.role_name, old_val=old_val, new_val=args.flavor + ), file=outfile) + else: + print("Keeping flavor {role} unchanged: {flavor}".format( + role=args.role_name, flavor=old_val), file=outfile) + return + + if attributes: + return tuskar.plans.patch(args.plan_uuid, attributes) + else: + print("ERROR: no roles were found in the Plan with the name {0}". + format(args.role_name), file=sys.stderr) + + @utils.arg('plan_uuid', help="UUID of the Plan to modify.") @utils.arg('-A', '--attribute', dest='attributes', metavar='', help='This can be specified multiple times.',