From c5a5b7dad7caa590be4aafc1c6bb4e0251b79432 Mon Sep 17 00:00:00 2001 From: Vincent Francoise Date: Wed, 20 Apr 2016 17:29:08 +0200 Subject: [PATCH] Added Strategy support in Watcher CLI In this changeset, I add the support for the /strategies endpoint. Partially Implements: blueprint get-goal-from-strategy Change-Id: I63dbbaefee18e7ae6db180478aae89ac4c09d2cc --- watcherclient/tests/test_shell.py | 2 + watcherclient/tests/v1/test_strategy.py | 167 ++++++++++++++++++ watcherclient/tests/v1/test_strategy_shell.py | 34 ++++ watcherclient/v1/client.py | 2 + watcherclient/v1/resource_fields.py | 11 ++ watcherclient/v1/shell.py | 4 +- watcherclient/v1/strategy.py | 85 +++++++++ watcherclient/v1/strategy_shell.py | 87 +++++++++ 8 files changed, 391 insertions(+), 1 deletion(-) create mode 100644 watcherclient/tests/v1/test_strategy.py create mode 100644 watcherclient/tests/v1/test_strategy_shell.py create mode 100644 watcherclient/v1/strategy.py create mode 100644 watcherclient/v1/strategy_shell.py diff --git a/watcherclient/tests/test_shell.py b/watcherclient/tests/test_shell.py index ca24792..43bcbf6 100644 --- a/watcherclient/tests/test_shell.py +++ b/watcherclient/tests/test_shell.py @@ -150,6 +150,8 @@ class ShellTest(utils.BaseTestCase): '.*action-plan-update', '.*goal-list', '.*goal-show', + '.*strategy-list', + '.*strategy-show', ] for r in required: self.assertThat(stdout, diff --git a/watcherclient/tests/v1/test_strategy.py b/watcherclient/tests/v1/test_strategy.py new file mode 100644 index 0000000..6137e23 --- /dev/null +++ b/watcherclient/tests/v1/test_strategy.py @@ -0,0 +1,167 @@ +# -*- coding: utf-8 -*- + +# Copyright 2013 Red Hat, Inc. +# 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 testtools +from testtools.matchers import HasLength + +from watcherclient.tests import utils +import watcherclient.v1.strategy + +STRATEGY1 = { + 'id': "basic", + 'display_name': 'Basic consolidation', + 'strategy_id': "SERVER_CONSOLIDATION", +} + +STRATEGY2 = { + 'id': "dummy", + 'display_name': 'Dummy', + 'strategy_id': "DUMMY", +} + +fake_responses = { + '/v1/strategies': + { + 'GET': ( + {}, + {"strategies": [STRATEGY1]}, + ), + }, + '/v1/strategies/detail': + { + 'GET': ( + {}, + {"strategies": [STRATEGY1]}, + ) + }, + '/v1/strategies/%s' % STRATEGY1['id']: + { + 'GET': ( + {}, + STRATEGY1, + ), + }, +} + +fake_responses_pagination = { + '/v1/strategies': + { + 'GET': ( + {}, + {"strategies": [STRATEGY1], + "next": "http://127.0.0.1:6385/v1/strategies/?limit=1"} + ), + }, + '/v1/strategies/?limit=1': + { + 'GET': ( + {}, + {"strategies": [STRATEGY2]} + ), + }, +} + +fake_responses_sorting = { + '/v1/strategies/?sort_key=id': + { + 'GET': ( + {}, + {"strategies": [STRATEGY1, STRATEGY2]} + ), + }, + '/v1/strategies/?sort_dir=desc': + { + 'GET': ( + {}, + {"strategies": [STRATEGY2, STRATEGY1]} + ), + }, +} + + +class StrategyManagerTest(testtools.TestCase): + + def setUp(self): + super(StrategyManagerTest, self).setUp() + self.api = utils.FakeAPI(fake_responses) + self.mgr = watcherclient.v1.strategy.StrategyManager(self.api) + + def test_strategies_list(self): + strategies = self.mgr.list() + expect = [ + ('GET', '/v1/strategies', {}, None), + ] + self.assertEqual(expect, self.api.calls) + self.assertEqual(1, len(strategies)) + + def test_strategies_list_detail(self): + strategies = self.mgr.list(detail=True) + expect = [ + ('GET', '/v1/strategies/detail', {}, None), + ] + self.assertEqual(expect, self.api.calls) + self.assertEqual(1, len(strategies)) + + def test_strategies_list_limit(self): + self.api = utils.FakeAPI(fake_responses_pagination) + self.mgr = watcherclient.v1.strategy.StrategyManager(self.api) + strategies = self.mgr.list(limit=1) + expect = [ + ('GET', '/v1/strategies/?limit=1', {}, None), + ] + self.assertEqual(expect, self.api.calls) + self.assertThat(strategies, HasLength(1)) + + def test_strategies_list_pagination_no_limit(self): + self.api = utils.FakeAPI(fake_responses_pagination) + self.mgr = watcherclient.v1.strategy.StrategyManager(self.api) + strategies = self.mgr.list(limit=0) + expect = [ + ('GET', '/v1/strategies', {}, None), + ('GET', '/v1/strategies/?limit=1', {}, None) + ] + self.assertEqual(expect, self.api.calls) + self.assertThat(strategies, HasLength(2)) + + def test_strategies_list_sort_key(self): + self.api = utils.FakeAPI(fake_responses_sorting) + self.mgr = watcherclient.v1.strategy.StrategyManager(self.api) + strategies = self.mgr.list(sort_key='id') + expect = [ + ('GET', '/v1/strategies/?sort_key=id', {}, None) + ] + self.assertEqual(expect, self.api.calls) + self.assertEqual(2, len(strategies)) + + def test_strategies_list_sort_dir(self): + self.api = utils.FakeAPI(fake_responses_sorting) + self.mgr = watcherclient.v1.strategy.StrategyManager(self.api) + strategies = self.mgr.list(sort_dir='desc') + expect = [ + ('GET', '/v1/strategies/?sort_dir=desc', {}, None) + ] + self.assertEqual(expect, self.api.calls) + self.assertEqual(2, len(strategies)) + + def test_strategies_show(self): + strategy = self.mgr.get(STRATEGY1['id']) + expect = [ + ('GET', '/v1/strategies/%s' % STRATEGY1['id'], {}, None), + ] + self.assertEqual(expect, self.api.calls) + self.assertEqual(STRATEGY1['id'], strategy.id) diff --git a/watcherclient/tests/v1/test_strategy_shell.py b/watcherclient/tests/v1/test_strategy_shell.py new file mode 100644 index 0000000..8092a89 --- /dev/null +++ b/watcherclient/tests/v1/test_strategy_shell.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2013 IBM Corp +# +# 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 mock + +from watcherclient.common import cliutils +from watcherclient.tests import utils +from watcherclient.v1 import strategy_shell + + +class StrategyShellTest(utils.BaseTestCase): + + def test_do_strategy_show(self): + actual = {} + fake_print_dict = lambda data, *args, **kwargs: actual.update(data) + with mock.patch.object(cliutils, 'print_dict', fake_print_dict): + strategy = object() + strategy_shell._print_strategy_show(strategy) + exp = ['uuid', 'name', 'display_name', 'goal_uuid'] + act = actual.keys() + self.assertEqual(sorted(exp), sorted(act)) diff --git a/watcherclient/v1/client.py b/watcherclient/v1/client.py index e7262b2..8c5a4e2 100644 --- a/watcherclient/v1/client.py +++ b/watcherclient/v1/client.py @@ -22,6 +22,7 @@ from watcherclient.v1 import audit from watcherclient.v1 import audit_template from watcherclient.v1 import goal from watcherclient.v1 import metric_collector +from watcherclient.v1 import strategy class Client(object): @@ -46,3 +47,4 @@ class Client(object): self.metric_collector = metric_collector.MetricCollectorManager( self.http_client ) + self.strategy = strategy.StrategyManager(self.http_client) diff --git a/watcherclient/v1/resource_fields.py b/watcherclient/v1/resource_fields.py index 060b425..a14501b 100644 --- a/watcherclient/v1/resource_fields.py +++ b/watcherclient/v1/resource_fields.py @@ -80,6 +80,17 @@ GOAL_SHORT_LIST_FIELDS = ['uuid', 'name', 'display_name'] GOAL_SHORT_LIST_FIELD_LABELS = ['UUID', 'Name', 'Display name'] +# Strategies + +STRATEGY_FIELDS = ['uuid', 'name', 'display_name', 'goal_uuid'] + +STRATEGY_FIELD_LABELS = ['UUID', 'Name', 'Display name', 'Goal UUID'] + +STRATEGY_SHORT_LIST_FIELDS = ['uuid', 'name', 'display_name', 'goal_uuid'] + +STRATEGY_SHORT_LIST_FIELD_LABELS = ['UUID', 'Name', 'Display name', + 'Goal UUID'] + # Metric Collector METRIC_COLLECTOR_FIELDS = ['uuid', 'created_at', 'updated_at', 'deleted_at', 'endpoint', 'category'] diff --git a/watcherclient/v1/shell.py b/watcherclient/v1/shell.py index 76b61eb..20bfdf4 100644 --- a/watcherclient/v1/shell.py +++ b/watcherclient/v1/shell.py @@ -19,6 +19,7 @@ from watcherclient.v1 import action_shell from watcherclient.v1 import audit_shell from watcherclient.v1 import audit_template_shell from watcherclient.v1 import goal_shell +from watcherclient.v1 import strategy_shell # from watcherclient.v1 import metric_collector_shell COMMAND_MODULES = [ @@ -27,7 +28,8 @@ COMMAND_MODULES = [ action_plan_shell, action_shell, # metric_collector_shell, - goal_shell + goal_shell, + strategy_shell, ] diff --git a/watcherclient/v1/strategy.py b/watcherclient/v1/strategy.py new file mode 100644 index 0000000..f3d2764 --- /dev/null +++ b/watcherclient/v1/strategy.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2013 Red Hat, Inc. +# +# 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 six.moves.urllib.parse as parse + +from watcherclient.common import base +from watcherclient.common import utils + + +class Strategy(base.Resource): + def __repr__(self): + return "" % self._info + + +class StrategyManager(base.Manager): + resource_class = Strategy + + @staticmethod + def _path(strategy=None): + return ('/v1/strategies/%s' % strategy + if strategy else '/v1/strategies') + + def list(self, goal_uuid=None, limit=None, sort_key=None, + sort_dir=None, detail=False): + """Retrieve a list of strategy. + + :param goal_uuid: The UUID of the goal to filter by + :param limit: The maximum number of results to return per + request, if: + + 1) limit > 0, the maximum number of audits to return. + 2) limit == 0, return the entire list of audits. + 3) limit param is NOT specified (None), the number of items + returned respect the maximum imposed by the Watcher API + (see Watcher'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 audits. + + :returns: A list of audits. + + """ + if limit is not None: + limit = int(limit) + + filters = utils.common_filters(limit, sort_key, sort_dir) + + if goal_uuid: + filters.append(parse.urlencode(dict(goal_uuid=goal_uuid))) + + path = '' + if detail: + path += 'detail' + if filters: + path += '?' + '&'.join(filters) + + if limit is None: + return self._list(self._path(path), "strategies") + else: + return self._list_pagination(self._path(path), "strategies", + limit=limit) + + def get(self, strategy): + try: + return self._list(self._path(strategy))[0] + except IndexError: + return None diff --git a/watcherclient/v1/strategy_shell.py b/watcherclient/v1/strategy_shell.py new file mode 100644 index 0000000..5670cb1 --- /dev/null +++ b/watcherclient/v1/strategy_shell.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2013 Red Hat, Inc. +# 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. + +from watcherclient.common import cliutils +from watcherclient.common import utils +from watcherclient.v1 import resource_fields as res_fields + + +def _print_strategy_show(strategy): + fields = res_fields.STRATEGY_FIELDS + data = dict([(f, getattr(strategy, f, '')) for f in fields]) + cliutils.print_dict(data, wrap=72) + + +@cliutils.arg( + 'strategy', + metavar='', + help="UUID or name of the strategy") +def do_strategy_show(cc, args): + """Show detailed information about a _print_strategy_show.""" + strategy = cc.strategy.get(args.strategy) + _print_strategy_show(strategy) + + +@cliutils.arg( + '--goal-uuid', + metavar='', + dest='goal_uuid', + help='UUID of the goal') +@cliutils.arg( + '--detail', + dest='detail', + action='store_true', + default=False, + help="Show detailed information about each strategy.") +@cliutils.arg( + '--limit', + metavar='', + type=int, + help='Maximum number of strategies to return per request, ' + '0 for no limit. Default is the maximum number used ' + 'by the Watcher API Service.') +@cliutils.arg( + '--sort-key', + metavar='', + help='Goal field that will be used for sorting.') +@cliutils.arg( + '--sort-dir', + metavar='', + choices=['asc', 'desc'], + help='Sort direction: "asc" (the default) or "desc".') +def do_strategy_list(cc, args): + """List the strategies.""" + params = {} + + if args.detail: + fields = res_fields.STRATEGY_FIELDS + field_labels = res_fields.STRATEGY_FIELD_LABELS + else: + fields = res_fields.STRATEGY_SHORT_LIST_FIELDS + field_labels = res_fields.STRATEGY_SHORT_LIST_FIELD_LABELS + + if args.goal_uuid: + params["goal_uuid"] = args.goal_uuid + + params.update(utils.common_params_for_list(args, + fields, + field_labels)) + + strategy = cc.strategy.list(**params) + cliutils.print_list(strategy, fields, + field_labels=field_labels, + sortby_index=None)