From 743898d75a8f747a8909f9689e2dacb07c9f5cce Mon Sep 17 00:00:00 2001 From: Chris Spencer Date: Thu, 18 Aug 2016 15:07:09 -0700 Subject: [PATCH] Adding support for creating a region. Adding ability to create region in both python and CLI clients. Adding doctests and unit tests for region creation. Change-Id: I59bcf4e727d3fd7ed6d55a58560fcf48eed8cb3d Implements: blueprint craton-client-access-inventory (partial) Closes-Bug: #1611418 --- cratonclient/session.py | 2 +- cratonclient/shell/v1/regions_shell.py | 31 ++++++++ cratonclient/shell/v1/shell.py | 5 +- cratonclient/tests/unit/test_regions_shell.py | 72 +++++++++++++++++++ cratonclient/v1/regions.py | 34 +++++++++ 5 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 cratonclient/shell/v1/regions_shell.py create mode 100644 cratonclient/tests/unit/test_regions_shell.py diff --git a/cratonclient/session.py b/cratonclient/session.py index 666b14d..8bd94d8 100644 --- a/cratonclient/session.py +++ b/cratonclient/session.py @@ -51,7 +51,7 @@ class Session(object): """ if session is None: session = requests.Session() - + self.project_id = project_id self._session = session self._session.headers['X-Auth-User'] = username self._session.headers['X-Auth-Project'] = str(project_id) diff --git a/cratonclient/shell/v1/regions_shell.py b/cratonclient/shell/v1/regions_shell.py new file mode 100644 index 0000000..9b46c54 --- /dev/null +++ b/cratonclient/shell/v1/regions_shell.py @@ -0,0 +1,31 @@ +# 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. +"""Hosts resource and resource shell wrapper.""" + +from cratonclient.common import cliutils +from cratonclient.v1.regions import REGION_FIELDS as r_fields + + +@cliutils.arg('-n', '--name', + metavar='', + required=True, + help='Name of the host.') +@cliutils.arg('--note', + help='Note about the host.') +def do_region_create(cc, args): + """Register a new region with the Craton service.""" + fields = {k: v for (k, v) in vars(args).items() + if k in r_fields and not (v is None)} + + region = cc.regions.create(**fields) + data = {f: getattr(region, f, '') for f in r_fields} + cliutils.print_dict(data, wrap=72) diff --git a/cratonclient/shell/v1/shell.py b/cratonclient/shell/v1/shell.py index 3c253ba..a1b991d 100644 --- a/cratonclient/shell/v1/shell.py +++ b/cratonclient/shell/v1/shell.py @@ -11,9 +11,10 @@ # limitations under the License. """Command-line interface to the OpenStack Craton API V1.""" from cratonclient.shell.v1 import hosts_shell +from cratonclient.shell.v1 import regions_shell COMMAND_MODULES = [ - # TODO(cmspence): project_shell, regions_shell, - # cell_shell, device_shell, user_shell, etc. + # TODO(cmspence): project_shell, cell_shell, device_shell, user_shell, etc. + regions_shell, hosts_shell, ] diff --git a/cratonclient/tests/unit/test_regions_shell.py b/cratonclient/tests/unit/test_regions_shell.py new file mode 100644 index 0000000..4cd6381 --- /dev/null +++ b/cratonclient/tests/unit/test_regions_shell.py @@ -0,0 +1,72 @@ +# 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. +"""Tests for `cratonclient.shell.v1.regions_shell` module.""" +import mock +import re + +from argparse import Namespace +from testtools import matchers + +from cratonclient.shell.v1 import regions_shell +from cratonclient.tests import base +from cratonclient.v1 import regions + + +class TestRegionsShell(base.ShellTestCase): + """Test craton regions shell commands.""" + + re_options = re.DOTALL | re.MULTILINE + region_valid_fields = None + region_invalid_field = None + + def setUp(self): + """Setup required test fixtures.""" + super(TestRegionsShell, self).setUp() + self.region_valid_fields = Namespace(project_id=1, + id=1, + name='mock_region') + self.region_invalid_field = Namespace(project_id=1, + id=1, + name='mock_region', + invalid_foo='ignored') + + def test_region_create_missing_required_args(self): + """Verify that missing required args results in error message.""" + expected_responses = [ + '.*?^usage: craton region-create', + '.*?^craton region-create: error:.*$' + ] + stdout, stderr = self.shell('region-create') + actual_output = stdout + stderr + for r in expected_responses: + self.assertThat(actual_output, + matchers.MatchesRegex(r, self.re_options)) + + @mock.patch('cratonclient.v1.regions.RegionManager.create') + def test_do_region_create_calls_host_manager(self, mock_create): + """Verify that do region create calls RegionManager create.""" + client = mock.Mock() + session = mock.Mock() + session.project_id = 1 + client.regions = regions.RegionManager(session, 'http://127.0.0.1/') + regions_shell.do_region_create(client, self.region_valid_fields) + mock_create.assert_called_once_with(**vars(self.region_valid_fields)) + + @mock.patch('cratonclient.v1.regions.RegionManager.create') + def test_do_region_create_ignores_unknown_fields(self, mock_create): + """Verify that do host create ignores unknown field.""" + client = mock.Mock() + session = mock.Mock() + session.project_id = 1 + client.regions = regions.RegionManager(session, 'http://127.0.0.1/') + regions_shell.do_region_create(client, self.region_invalid_field) + mock_create.assert_called_once_with(**vars(self.region_valid_fields)) diff --git a/cratonclient/v1/regions.py b/cratonclient/v1/regions.py index 9a11e4c..72c8bad 100644 --- a/cratonclient/v1/regions.py +++ b/cratonclient/v1/regions.py @@ -27,3 +27,37 @@ class RegionManager(crud.CRUDClient): key = 'region' base_path = '/regions' resource_class = Region + project_id = 0 + + def __init__(self, session, url): + """Initialize our RegionManager with project, session, and url.""" + super(RegionManager, self).__init__(session, url) + self.project_id = session.project_id + + def create(self, **kwargs): + """Create a region. + + .. code-block:: python + + >>> from cratonclient.v1 import client + >>> from cratonclient import session + >>> session = session.Session( + ... username='demo', + ... token='password', + ... project_id=1 + ... ) + >>> client = client.Client(session, 'http://example.com') + >>> client.regions.create(name='region') + """ + kwargs['project_id'] = self.project_id + return super(RegionManager, self).create(**kwargs) + + +REGION_FIELDS = { + 'id': 'ID', + 'project_id': 'Project ID', + 'name': 'Name', + 'note': 'Note', + 'created_at': 'Created At', + 'update_at': 'Updated At' +}