diff --git a/magnumclient/tests/v1/test_stats.py b/magnumclient/tests/v1/test_stats.py new file mode 100644 index 00000000..d1455402 --- /dev/null +++ b/magnumclient/tests/v1/test_stats.py @@ -0,0 +1,91 @@ +# 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 magnumclient.tests import utils +from magnumclient.v1 import stats + + +CLUSTER1 = {'id': 123, + 'uuid': '66666666-7777-8888-9999-000000000001', + 'name': 'cluster1', + 'cluster_template_id': 'e74c40e0-d825-11e2-a28f-0800200c9a61', + 'stack_id': '5d12f6fd-a196-4bf0-ae4c-1f639a523a51', + 'api_address': '172.17.2.1', + 'node_addresses': ['172.17.2.3'], + 'node_count': 2, + 'master_count': 1, + 'project_id': 'abc' + } +CLUSTER2 = {'id': 124, + 'uuid': '66666666-7777-8888-9999-000000000002', + 'name': 'cluster2', + 'cluster_template_id': 'e74c40e0-d825-11e2-a28f-0800200c9a62', + 'stack_id': '5d12f6fd-a196-4bf0-ae4c-1f639a523a52', + 'api_address': '172.17.2.2', + 'node_addresses': ['172.17.2.4'], + 'node_count': 2, + 'master_count': 1, + 'project_id': 'bcd' + } + + +nc = 'node_count' +mc = 'master_count' +C1 = CLUSTER1 +C2 = CLUSTER2 +fake_responses = { + '/v1/stats': + { + 'GET': ( + {}, + {'clusters': 2, 'nodes': C1[nc] + C1[mc] + C2[nc] + C2[mc]}, + ) + }, + '/v1/stats?project_id=%s' % C2['project_id']: + { + 'GET': ( + {}, + {'clusters': 1, 'nodes': C2[nc] + C2[mc]}, + ) + }, +} + + +class StatsManagerTest(testtools.TestCase): + + def setUp(self): + super(StatsManagerTest, self).setUp() + self.api = utils.FakeAPI(fake_responses) + self.mgr = stats.StatsManager(self.api) + + def test_stats(self): + stats = self.mgr.list() + expect = [ + ('GET', '/v1/stats', {}, None), + ] + self.assertEqual(expect, self.api.calls) + expected_stats = {'clusters': 2, + 'nodes': C1[nc] + C1[mc] + C2[nc] + C2[mc]} + self.assertEqual(expected_stats, stats._info) + + def test_stats_with_project_id(self): + expect = [ + ('GET', '/v1/stats?project_id=%s' % CLUSTER2['project_id'], {}, + None), + ] + stats = self.mgr.list(project_id=CLUSTER2['project_id']) + self.assertEqual(expect, self.api.calls) + expected_stats = {'clusters': 1, + 'nodes': C2[nc] + C2[mc]} + self.assertEqual(expected_stats, stats._info) diff --git a/magnumclient/tests/v1/test_stats_shell.py b/magnumclient/tests/v1/test_stats_shell.py new file mode 100644 index 00000000..1d6ccc7f --- /dev/null +++ b/magnumclient/tests/v1/test_stats_shell.py @@ -0,0 +1,53 @@ +# 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 magnumclient.tests.v1 import shell_test_base +from magnumclient.v1.stats import Stats + + +class FakeStats(Stats): + def __init__(self, manager=None, info={}, **kwargs): + Stats.__init__(self, manager=manager, info=info) + self.clusters = kwargs.get('clusters', 0) + self.nodes = kwargs.get('nodes', 0) + + +class ShellTest(shell_test_base.TestCommandLineArgument): + + def _get_expected_args_list(self, project_id=None): + expected_args = {} + expected_args['project_id'] = project_id + return expected_args + + @mock.patch( + 'magnumclient.v1.stats.StatsManager.list') + def test_stats_get_success(self, mock_list): + self._test_arg_success('stats-list') + expected_args = self._get_expected_args_list() + mock_list.assert_called_once_with(**expected_args) + + @mock.patch( + 'magnumclient.v1.stats.StatsManager.list') + def test_stats_get_success_with_arg(self, mock_list): + self._test_arg_success('stats-list ' + '--project-id 111 ') + expected_args = self._get_expected_args_list('111') + mock_list.assert_called_once_with(**expected_args) + + @mock.patch( + 'magnumclient.v1.stats.StatsManager.list') + def test_stats_get_failure(self, mock_list): + self._test_arg_failure('stats-list --wrong', + self._unrecognized_arg_error) + mock_list.assert_not_called() diff --git a/magnumclient/v1/client.py b/magnumclient/v1/client.py index 60288e1d..15e9f5a5 100644 --- a/magnumclient/v1/client.py +++ b/magnumclient/v1/client.py @@ -25,6 +25,7 @@ from magnumclient.v1 import certificates from magnumclient.v1 import cluster_templates from magnumclient.v1 import clusters from magnumclient.v1 import mservices +from magnumclient.v1 import stats profiler = importutils.try_import("osprofiler.profiler") @@ -211,3 +212,4 @@ class Client(object): # will fail to check the request signature and will skip # initialization of osprofiler on the server side. profiler.init(profile) + self.stats = stats.StatsManager(self.http_client) diff --git a/magnumclient/v1/shell.py b/magnumclient/v1/shell.py index 2380f768..a095d407 100644 --- a/magnumclient/v1/shell.py +++ b/magnumclient/v1/shell.py @@ -19,6 +19,7 @@ from magnumclient.v1 import certificates_shell from magnumclient.v1 import cluster_templates_shell from magnumclient.v1 import clusters_shell from magnumclient.v1 import mservices_shell +from magnumclient.v1 import stats_shell COMMAND_MODULES = [ baymodels_shell, @@ -27,4 +28,5 @@ COMMAND_MODULES = [ clusters_shell, cluster_templates_shell, mservices_shell, + stats_shell, ] diff --git a/magnumclient/v1/stats.py b/magnumclient/v1/stats.py new file mode 100644 index 00000000..7db55110 --- /dev/null +++ b/magnumclient/v1/stats.py @@ -0,0 +1,32 @@ +# 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 magnumclient.common import base + + +class Stats(base.Resource): + def __repr__(self): + return "" % self._info + + +class StatsManager(base.Manager): + resource_class = Stats + + @staticmethod + def _path(id=None): + return '/v1/stats?project_id=%s' % id if id else '/v1/stats' + + def list(self, project_id=None): + try: + return self._list(self._path(project_id))[0] + except IndexError: + return None diff --git a/magnumclient/v1/stats_shell.py b/magnumclient/v1/stats_shell.py new file mode 100644 index 00000000..a5f25d00 --- /dev/null +++ b/magnumclient/v1/stats_shell.py @@ -0,0 +1,28 @@ +# 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 magnumclient.common import cliutils as utils +from magnumclient.i18n import _ + + +@utils.arg('--project-id', + required=False, + metavar='', + help=_('Project ID')) +def do_stats_list(cs, args): + """Show stats for the given project_id""" + opts = { + 'project_id': args.project_id + } + + stats = cs.stats.list(**opts) + utils.print_dict(stats._info)