From e450261b02c2d99fabe1a785111e228381e050a9 Mon Sep 17 00:00:00 2001 From: Ethan Lynn Date: Tue, 23 Aug 2016 10:44:20 +0800 Subject: [PATCH] Add profile validate operation to senlinclient Add profile validate operation to senlinclient Change-Id: I7fd1e5e3fa9115ba270f2c0e5f645ac5183622e3 --- senlinclient/tests/unit/v1/test_profile.py | 46 ++++++++++++++++ senlinclient/tests/unit/v1/test_shell.py | 42 +++++++++++++++ senlinclient/v1/client.py | 8 +++ senlinclient/v1/profile.py | 62 ++++++++++++++++++++++ senlinclient/v1/shell.py | 38 +++++++++++++ setup.cfg | 1 + 6 files changed, 197 insertions(+) diff --git a/senlinclient/tests/unit/v1/test_profile.py b/senlinclient/tests/unit/v1/test_profile.py index 411d4891..7782e1e2 100644 --- a/senlinclient/tests/unit/v1/test_profile.py +++ b/senlinclient/tests/unit/v1/test_profile.py @@ -402,3 +402,49 @@ class TestProfileUpdate(TestProfile): self.cmd.take_action, parsed_args) self.assertIn('Profile not found: c6b8b252', str(error)) + + +class TestProfileValidate(TestProfile): + + spec_path = 'senlinclient/tests/test_specs/nova_server.yaml' + response = {"profile": { + "created_at": None, + "domain": None, + "id": None, + "metadata": None, + "name": "validated_profile", + "project": "5f1cc92b578e4e25a3b284179cf20a9b", + "spec": {"properties": { + "flavor": 1, + "image": "cirros-0.3.4-x86_64-uec", + "name": "cirros_server"}, + "type": "os.nova.server", + "version": 1.0}, + "type": "os.nova.server-1.0", + "updated_at": None, + "user": "2d7aca950f3e465d8ef0c81720faf6ff"}} + + defaults = { + "spec": { + "version": 1.0, + "type": "os.nova.server", + "properties": { + "flavor": 1, + "name": "cirros_server", + "image": "cirros-0.3.4-x86_64-uec" + }, + } + } + + def setUp(self): + super(TestProfileValidate, self).setUp() + self.cmd = osc_profile.ValidateProfile(self.app, None) + self.mock_client.validate_profile = mock.Mock( + return_value=sdk_profile.Profile(**self.response['profile'])) + utils.get_dict_properties = mock.Mock(return_value='') + + def test_profile_validate(self): + arglist = ['--spec-file', self.spec_path] + parsed_args = self.check_parser(self.cmd, arglist, []) + self.cmd.take_action(parsed_args) + self.mock_client.validate_profile.assert_called_with(**self.defaults) diff --git a/senlinclient/tests/unit/v1/test_shell.py b/senlinclient/tests/unit/v1/test_shell.py index 8919d255..7c2e39f5 100644 --- a/senlinclient/tests/unit/v1/test_shell.py +++ b/senlinclient/tests/unit/v1/test_shell.py @@ -298,6 +298,48 @@ class ShellTest(testtools.TestCase): self.assertEqual(msg, six.text_type(ex)) service.delete_profile.assert_called_with('profile2', False) + @mock.patch.object(utils, 'process_stack_spec') + @mock.patch.object(utils, 'get_spec_content') + def test_do_profile_validate(self, mock_get, mock_proc): + args = self._make_args({'spec_file': mock.Mock()}) + spec = copy.deepcopy(self.profile_spec) + mock_get.return_value = spec + params = { + 'spec': spec, + } + service = mock.Mock() + profile = mock.Mock() + profile.to_dict.return_value = {} + service.validate_profile.return_value = profile + + sh.do_profile_validate(service, args) + + service.validate_profile.assert_called_once_with(**params) + + # Miss 'type' key in spec file + del spec['type'] + ex = self.assertRaises(exc.CommandError, + sh.do_profile_validate, + service, args) + self.assertEqual(_("Missing 'type' key in spec file."), + six.text_type(ex)) + # Miss 'version' key in spec file + spec['type'] = 'os.heat.stack' + del spec['version'] + ex = self.assertRaises(exc.CommandError, + sh.do_profile_validate, + service, args) + self.assertEqual(_("Missing 'version' key in spec file."), + six.text_type(ex)) + # Miss 'properties' key in spec file + spec['version'] = 1.0 + del spec['properties'] + ex = self.assertRaises(exc.CommandError, + sh.do_profile_validate, + service, args) + self.assertEqual(_("Missing 'properties' key in spec file."), + six.text_type(ex)) + @mock.patch.object(utils, 'print_list') def test_do_policy_type_list(self, mock_print): service = mock.Mock() diff --git a/senlinclient/v1/client.py b/senlinclient/v1/client.py index 50540144..2b89da27 100644 --- a/senlinclient/v1/client.py +++ b/senlinclient/v1/client.py @@ -84,6 +84,14 @@ class Client(object): """ return self.service.delete_profile(profile, ignore_missing) + def validate_profile(self, **attrs): + """Validate a profile spec + + Doc link: + http://developer.openstack.org/api-ref-clustering-v1.html#validateProfile + """ + return self.service.validate_profile(**attrs) + def policy_types(self, **query): """List policy types diff --git a/senlinclient/v1/profile.py b/senlinclient/v1/profile.py index db2500eb..4d8197e1 100644 --- a/senlinclient/v1/profile.py +++ b/senlinclient/v1/profile.py @@ -307,3 +307,65 @@ class UpdateProfile(command.ShowOne): parsed_args.profile) senlin_client.update_profile(profile.id, **params) return _show_profile(senlin_client, profile_id=profile.id) + + +class ValidateProfile(command.ShowOne): + """Validate a profile.""" + + log = logging.getLogger(__name__ + ".ValidateProfile") + + def get_parser(self, prog_name): + parser = super(ValidateProfile, self).get_parser(prog_name) + parser.add_argument( + '--spec-file', + metavar='', + required=True, + help=_('The spec file used to create the profile') + ) + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)", parsed_args) + senlin_client = self.app.client_manager.clustering + + spec = senlin_utils.get_spec_content(parsed_args.spec_file) + type_name = spec.get('type', None) + type_version = spec.get('version', None) + properties = spec.get('properties', None) + if type_name is None: + raise exc.CommandError(_("Missing 'type' key in spec file.")) + if type_version is None: + raise exc.CommandError(_("Missing 'version' key in spec file.")) + if properties is None: + raise exc.CommandError(_("Missing 'properties' key in spec file.")) + + if type_name == 'os.heat.stack': + stack_properties = senlin_utils.process_stack_spec(properties) + spec['properties'] = stack_properties + + params = { + 'spec': spec, + } + + profile = senlin_client.validate_profile(**params) + + formatters = {} + formatters['metadata'] = senlin_utils.json_formatter + formatters['spec'] = senlin_utils.nested_dict_formatter( + ['type', 'version', 'properties'], + ['property', 'value']) + + columns = [ + 'created_at', + 'domain', + 'id', + 'metadata', + 'name', + 'project', + 'spec', + 'type', + 'updated_at', + 'user' + ] + return columns, utils.get_dict_properties(profile.to_dict(), columns, + formatters=formatters) diff --git a/senlinclient/v1/shell.py b/senlinclient/v1/shell.py index 48152bf9..fcddd7fc 100644 --- a/senlinclient/v1/shell.py +++ b/senlinclient/v1/shell.py @@ -241,6 +241,44 @@ def do_profile_delete(service, args): print('Profile deleted: %s' % args.id) +@utils.arg('-s', '--spec-file', metavar='', required=True, + help=_('The spec file used to create the profile.')) +def do_profile_validate(service, args): + """Validate a profile.""" + show_deprecated('senlin profile-validate', + 'openstack cluster profile validate') + spec = utils.get_spec_content(args.spec_file) + type_name = spec.get('type', None) + type_version = spec.get('version', None) + properties = spec.get('properties', None) + if type_name is None: + raise exc.CommandError(_("Missing 'type' key in spec file.")) + if type_version is None: + raise exc.CommandError(_("Missing 'version' key in spec file.")) + if properties is None: + raise exc.CommandError(_("Missing 'properties' key in spec file.")) + + if type_name == 'os.heat.stack': + stack_properties = utils.process_stack_spec(properties) + spec['properties'] = stack_properties + + params = { + 'spec': spec, + } + + profile = service.validate_profile(**params) + + formatters = { + 'metadata': utils.json_formatter, + } + + formatters['spec'] = utils.nested_dict_formatter( + ['type', 'version', 'properties'], + ['property', 'value']) + + utils.print_dict(profile.to_dict(), formatters=formatters) + + # POLICY TYPES diff --git a/setup.cfg b/setup.cfg index 6947ec16..c89aa04c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -69,6 +69,7 @@ openstack.clustering.v1 = cluster_profile_type_list = senlinclient.v1.profile_type:ProfileTypeList cluster_profile_type_show = senlinclient.v1.profile_type:ProfileTypeShow cluster_profile_update = senlinclient.v1.profile:UpdateProfile + cluster_profile_validate = senlinclient.v1.profile:ValidateProfile cluster_receiver_create = senlinclient.v1.receiver:CreateReceiver cluster_receiver_delete = senlinclient.v1.receiver:DeleteReceiver cluster_receiver_list = senlinclient.v1.receiver:ListReceiver