diff options
Diffstat (limited to 'openstackclient/tests/unit')
125 files changed, 44262 insertions, 0 deletions
diff --git a/openstackclient/tests/unit/__init__.py b/openstackclient/tests/unit/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/openstackclient/tests/unit/__init__.py diff --git a/openstackclient/tests/unit/api/__init__.py b/openstackclient/tests/unit/api/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/openstackclient/tests/unit/api/__init__.py diff --git a/openstackclient/tests/unit/api/fakes.py b/openstackclient/tests/unit/api/fakes.py new file mode 100644 index 00000000..26213a2f --- /dev/null +++ b/openstackclient/tests/unit/api/fakes.py @@ -0,0 +1,56 @@ +# 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. +# + +"""API Test Fakes""" + +from keystoneauth1 import session +from requests_mock.contrib import fixture + +from openstackclient.tests.unit import utils + + +RESP_ITEM_1 = { + 'id': '1', + 'name': 'alpha', + 'status': 'UP', + 'props': {'a': 1, 'b': 2}, +} +RESP_ITEM_2 = { + 'id': '2', + 'name': 'beta', + 'status': 'DOWN', + 'props': {'a': 2, 'b': 2}, +} +RESP_ITEM_3 = { + 'id': '3', + 'name': 'delta', + 'status': 'UP', + 'props': {'a': 3, 'b': 1}, +} + +LIST_RESP = [RESP_ITEM_1, RESP_ITEM_2] + +LIST_BODY = { + 'p1': 'xxx', + 'p2': 'yyy', +} + + +class TestSession(utils.TestCase): + + BASE_URL = 'https://api.example.com:1234/vX' + + def setUp(self): + super(TestSession, self).setUp() + self.sess = session.Session() + self.requests_mock = self.useFixture(fixture.Fixture()) diff --git a/openstackclient/tests/unit/api/test_api.py b/openstackclient/tests/unit/api/test_api.py new file mode 100644 index 00000000..5f4a0c1a --- /dev/null +++ b/openstackclient/tests/unit/api/test_api.py @@ -0,0 +1,326 @@ +# 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. +# + +"""Base API Library Tests""" + +from osc_lib import exceptions + +from openstackclient.api import api +from openstackclient.tests.unit.api import fakes as api_fakes + + +class TestKeystoneSession(api_fakes.TestSession): + + def setUp(self): + super(TestKeystoneSession, self).setUp() + self.api = api.KeystoneSession( + session=self.sess, + endpoint=self.BASE_URL, + ) + + def test_session_request(self): + self.requests_mock.register_uri( + 'GET', + self.BASE_URL + '/qaz', + json=api_fakes.RESP_ITEM_1, + status_code=200, + ) + ret = self.api._request('GET', '/qaz') + self.assertEqual(api_fakes.RESP_ITEM_1, ret.json()) + + +class TestBaseAPI(api_fakes.TestSession): + + def setUp(self): + super(TestBaseAPI, self).setUp() + self.api = api.BaseAPI( + session=self.sess, + endpoint=self.BASE_URL, + ) + + def test_create_post(self): + self.requests_mock.register_uri( + 'POST', + self.BASE_URL + '/qaz', + json=api_fakes.RESP_ITEM_1, + status_code=202, + ) + ret = self.api.create('qaz') + self.assertEqual(api_fakes.RESP_ITEM_1, ret) + + def test_create_put(self): + self.requests_mock.register_uri( + 'PUT', + self.BASE_URL + '/qaz', + json=api_fakes.RESP_ITEM_1, + status_code=202, + ) + ret = self.api.create('qaz', method='PUT') + self.assertEqual(api_fakes.RESP_ITEM_1, ret) + + def test_delete(self): + self.requests_mock.register_uri( + 'DELETE', + self.BASE_URL + '/qaz', + status_code=204, + ) + ret = self.api.delete('qaz') + self.assertEqual(204, ret.status_code) + + # find tests + + def test_find_attr_by_id(self): + + # All first requests (by name) will fail in this test + self.requests_mock.register_uri( + 'GET', + self.BASE_URL + '/qaz?name=1', + json={'qaz': []}, + status_code=200, + ) + self.requests_mock.register_uri( + 'GET', + self.BASE_URL + '/qaz?id=1', + json={'qaz': [api_fakes.RESP_ITEM_1]}, + status_code=200, + ) + ret = self.api.find_attr('qaz', '1') + self.assertEqual(api_fakes.RESP_ITEM_1, ret) + + # value not found + self.requests_mock.register_uri( + 'GET', + self.BASE_URL + '/qaz?name=0', + json={'qaz': []}, + status_code=200, + ) + self.requests_mock.register_uri( + 'GET', + self.BASE_URL + '/qaz?id=0', + json={'qaz': []}, + status_code=200, + ) + self.assertRaises( + exceptions.CommandError, + self.api.find_attr, + 'qaz', + '0', + ) + + # Attribute other than 'name' + self.requests_mock.register_uri( + 'GET', + self.BASE_URL + '/qaz?status=UP', + json={'qaz': [api_fakes.RESP_ITEM_1]}, + status_code=200, + ) + ret = self.api.find_attr('qaz', 'UP', attr='status') + self.assertEqual(api_fakes.RESP_ITEM_1, ret) + ret = self.api.find_attr('qaz', value='UP', attr='status') + self.assertEqual(api_fakes.RESP_ITEM_1, ret) + + def test_find_attr_by_name(self): + self.requests_mock.register_uri( + 'GET', + self.BASE_URL + '/qaz?name=alpha', + json={'qaz': [api_fakes.RESP_ITEM_1]}, + status_code=200, + ) + ret = self.api.find_attr('qaz', 'alpha') + self.assertEqual(api_fakes.RESP_ITEM_1, ret) + + # value not found + self.requests_mock.register_uri( + 'GET', + self.BASE_URL + '/qaz?name=0', + json={'qaz': []}, + status_code=200, + ) + self.requests_mock.register_uri( + 'GET', + self.BASE_URL + '/qaz?id=0', + json={'qaz': []}, + status_code=200, + ) + self.assertRaises( + exceptions.CommandError, + self.api.find_attr, + 'qaz', + '0', + ) + + # Attribute other than 'name' + self.requests_mock.register_uri( + 'GET', + self.BASE_URL + '/qaz?status=UP', + json={'qaz': [api_fakes.RESP_ITEM_1]}, + status_code=200, + ) + ret = self.api.find_attr('qaz', 'UP', attr='status') + self.assertEqual(api_fakes.RESP_ITEM_1, ret) + ret = self.api.find_attr('qaz', value='UP', attr='status') + self.assertEqual(api_fakes.RESP_ITEM_1, ret) + + def test_find_attr_path_resource(self): + + # Test resource different than path + self.requests_mock.register_uri( + 'GET', + self.BASE_URL + '/wsx?name=1', + json={'qaz': []}, + status_code=200, + ) + self.requests_mock.register_uri( + 'GET', + self.BASE_URL + '/wsx?id=1', + json={'qaz': [api_fakes.RESP_ITEM_1]}, + status_code=200, + ) + ret = self.api.find_attr('wsx', '1', resource='qaz') + self.assertEqual(api_fakes.RESP_ITEM_1, ret) + + def test_find_bulk_none(self): + self.requests_mock.register_uri( + 'GET', + self.BASE_URL + '/qaz', + json=api_fakes.LIST_RESP, + status_code=200, + ) + ret = self.api.find_bulk('qaz') + self.assertEqual(api_fakes.LIST_RESP, ret) + + def test_find_bulk_one(self): + self.requests_mock.register_uri( + 'GET', + self.BASE_URL + '/qaz', + json=api_fakes.LIST_RESP, + status_code=200, + ) + ret = self.api.find_bulk('qaz', id='1') + self.assertEqual([api_fakes.LIST_RESP[0]], ret) + + ret = self.api.find_bulk('qaz', id='0') + self.assertEqual([], ret) + + ret = self.api.find_bulk('qaz', name='beta') + self.assertEqual([api_fakes.LIST_RESP[1]], ret) + + ret = self.api.find_bulk('qaz', error='bogus') + self.assertEqual([], ret) + + def test_find_bulk_two(self): + self.requests_mock.register_uri( + 'GET', + self.BASE_URL + '/qaz', + json=api_fakes.LIST_RESP, + status_code=200, + ) + ret = self.api.find_bulk('qaz', id='1', name='alpha') + self.assertEqual([api_fakes.LIST_RESP[0]], ret) + + ret = self.api.find_bulk('qaz', id='1', name='beta') + self.assertEqual([], ret) + + ret = self.api.find_bulk('qaz', id='1', error='beta') + self.assertEqual([], ret) + + def test_find_bulk_dict(self): + self.requests_mock.register_uri( + 'GET', + self.BASE_URL + '/qaz', + json={'qaz': api_fakes.LIST_RESP}, + status_code=200, + ) + ret = self.api.find_bulk('qaz', id='1') + self.assertEqual([api_fakes.LIST_RESP[0]], ret) + + # list tests + + def test_list_no_body(self): + self.requests_mock.register_uri( + 'GET', + self.BASE_URL, + json=api_fakes.LIST_RESP, + status_code=200, + ) + ret = self.api.list('') + self.assertEqual(api_fakes.LIST_RESP, ret) + + self.requests_mock.register_uri( + 'GET', + self.BASE_URL + '/qaz', + json=api_fakes.LIST_RESP, + status_code=200, + ) + ret = self.api.list('qaz') + self.assertEqual(api_fakes.LIST_RESP, ret) + + def test_list_params(self): + params = {'format': 'json'} + self.requests_mock.register_uri( + 'GET', + self.BASE_URL + '?format=json', + json=api_fakes.LIST_RESP, + status_code=200, + ) + ret = self.api.list('', **params) + self.assertEqual(api_fakes.LIST_RESP, ret) + + self.requests_mock.register_uri( + 'GET', + self.BASE_URL + '/qaz?format=json', + json=api_fakes.LIST_RESP, + status_code=200, + ) + ret = self.api.list('qaz', **params) + self.assertEqual(api_fakes.LIST_RESP, ret) + + def test_list_body(self): + self.requests_mock.register_uri( + 'POST', + self.BASE_URL + '/qaz', + json=api_fakes.LIST_RESP, + status_code=200, + ) + ret = self.api.list('qaz', body=api_fakes.LIST_BODY) + self.assertEqual(api_fakes.LIST_RESP, ret) + + def test_list_detailed(self): + self.requests_mock.register_uri( + 'GET', + self.BASE_URL + '/qaz/details', + json=api_fakes.LIST_RESP, + status_code=200, + ) + ret = self.api.list('qaz', detailed=True) + self.assertEqual(api_fakes.LIST_RESP, ret) + + def test_list_filtered(self): + self.requests_mock.register_uri( + 'GET', + self.BASE_URL + '/qaz?attr=value', + json=api_fakes.LIST_RESP, + status_code=200, + ) + ret = self.api.list('qaz', attr='value') + self.assertEqual(api_fakes.LIST_RESP, ret) + + def test_list_wrapped(self): + self.requests_mock.register_uri( + 'GET', + self.BASE_URL + '/qaz?attr=value', + json={'responses': api_fakes.LIST_RESP}, + status_code=200, + ) + ret = self.api.list('qaz', attr='value') + self.assertEqual({'responses': api_fakes.LIST_RESP}, ret) diff --git a/openstackclient/tests/unit/api/test_image_v1.py b/openstackclient/tests/unit/api/test_image_v1.py new file mode 100644 index 00000000..e02ef381 --- /dev/null +++ b/openstackclient/tests/unit/api/test_image_v1.py @@ -0,0 +1,98 @@ +# 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. +# + +"""Image v1 API Library Tests""" + +from keystoneauth1 import session +from requests_mock.contrib import fixture + +from openstackclient.api import image_v1 +from openstackclient.tests.unit import utils + + +FAKE_PROJECT = 'xyzpdq' +FAKE_URL = 'http://gopher.com' + + +class TestImageAPIv1(utils.TestCase): + + def setUp(self): + super(TestImageAPIv1, self).setUp() + + sess = session.Session() + self.api = image_v1.APIv1(session=sess, endpoint=FAKE_URL) + self.requests_mock = self.useFixture(fixture.Fixture()) + + +class TestImage(TestImageAPIv1): + + PUB_PROT = { + 'id': '1', + 'name': 'pub1', + 'is_public': True, + 'protected': True, + } + PUB_NOPROT = { + 'id': '2', + 'name': 'pub2-noprot', + 'is_public': True, + 'protected': False, + } + NOPUB_PROT = { + 'id': '3', + 'name': 'priv3', + 'is_public': False, + 'protected': True, + } + NOPUB_NOPROT = { + 'id': '4', + 'name': 'priv4-noprot', + 'is_public': False, + 'protected': False, + } + LIST_IMAGE_RESP = [ + PUB_PROT, + PUB_NOPROT, + NOPUB_PROT, + NOPUB_NOPROT, + ] + + def test_image_list_no_options(self): + self.requests_mock.register_uri( + 'GET', + FAKE_URL + '/v1/images', + json={'images': self.LIST_IMAGE_RESP}, + status_code=200, + ) + ret = self.api.image_list() + self.assertEqual(self.LIST_IMAGE_RESP, ret) + + def test_image_list_public(self): + self.requests_mock.register_uri( + 'GET', + FAKE_URL + '/v1/images/detail', + json={'images': self.LIST_IMAGE_RESP}, + status_code=200, + ) + ret = self.api.image_list(public=True) + self.assertEqual([self.PUB_PROT, self.PUB_NOPROT], ret) + + def test_image_list_private(self): + self.requests_mock.register_uri( + 'GET', + FAKE_URL + '/v1/images/detail', + json={'images': self.LIST_IMAGE_RESP}, + status_code=200, + ) + ret = self.api.image_list(private=True) + self.assertEqual([self.NOPUB_PROT, self.NOPUB_NOPROT], ret) diff --git a/openstackclient/tests/unit/api/test_image_v2.py b/openstackclient/tests/unit/api/test_image_v2.py new file mode 100644 index 00000000..5dbb51e0 --- /dev/null +++ b/openstackclient/tests/unit/api/test_image_v2.py @@ -0,0 +1,98 @@ +# 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. +# + +"""Image v2 API Library Tests""" + +from keystoneauth1 import session +from requests_mock.contrib import fixture + +from openstackclient.api import image_v2 +from openstackclient.tests.unit import utils + + +FAKE_PROJECT = 'xyzpdq' +FAKE_URL = 'http://gopher.com' + + +class TestImageAPIv2(utils.TestCase): + + def setUp(self): + super(TestImageAPIv2, self).setUp() + + sess = session.Session() + self.api = image_v2.APIv2(session=sess, endpoint=FAKE_URL) + self.requests_mock = self.useFixture(fixture.Fixture()) + + +class TestImage(TestImageAPIv2): + + PUB_PROT = { + 'id': '1', + 'name': 'pub1', + 'visibility': 'public', + 'protected': True, + } + PUB_NOPROT = { + 'id': '2', + 'name': 'pub2-noprot', + 'visibility': 'public', + 'protected': False, + } + NOPUB_PROT = { + 'id': '3', + 'name': 'priv3', + 'visibility': 'private', + 'protected': True, + } + NOPUB_NOPROT = { + 'id': '4', + 'name': 'priv4-noprot', + 'visibility': 'private', + 'protected': False, + } + LIST_IMAGE_RESP = [ + PUB_PROT, + PUB_NOPROT, + NOPUB_PROT, + NOPUB_NOPROT, + ] + + def test_image_list_no_options(self): + self.requests_mock.register_uri( + 'GET', + FAKE_URL + '/v2/images', + json={'images': self.LIST_IMAGE_RESP}, + status_code=200, + ) + ret = self.api.image_list() + self.assertEqual(self.LIST_IMAGE_RESP, ret) + + def test_image_list_public(self): + self.requests_mock.register_uri( + 'GET', + FAKE_URL + '/v2/images', + json={'images': [self.PUB_PROT, self.PUB_NOPROT]}, + status_code=200, + ) + ret = self.api.image_list(public=True) + self.assertEqual([self.PUB_PROT, self.PUB_NOPROT], ret) + + def test_image_list_private(self): + self.requests_mock.register_uri( + 'GET', + FAKE_URL + '/v2/images', + json={'images': [self.NOPUB_PROT, self.NOPUB_NOPROT]}, + status_code=200, + ) + ret = self.api.image_list(public=True) + self.assertEqual([self.NOPUB_PROT, self.NOPUB_NOPROT], ret) diff --git a/openstackclient/tests/unit/api/test_object_store_v1.py b/openstackclient/tests/unit/api/test_object_store_v1.py new file mode 100644 index 00000000..acf95550 --- /dev/null +++ b/openstackclient/tests/unit/api/test_object_store_v1.py @@ -0,0 +1,337 @@ +# 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. +# + +"""Object Store v1 API Library Tests""" + +import mock + +from keystoneauth1 import session +from requests_mock.contrib import fixture + +from openstackclient.api import object_store_v1 as object_store +from openstackclient.tests.unit import utils + + +FAKE_ACCOUNT = 'q12we34r' +FAKE_AUTH = '11223344556677889900' +FAKE_URL = 'http://gopher.com/v1/' + FAKE_ACCOUNT + +FAKE_CONTAINER = 'rainbarrel' +FAKE_OBJECT = 'spigot' + +LIST_CONTAINER_RESP = [ + 'qaz', + 'fred', +] + +LIST_OBJECT_RESP = [ + {'name': 'fred', 'bytes': 1234, 'content_type': 'text'}, + {'name': 'wilma', 'bytes': 5678, 'content_type': 'text'}, +] + + +class TestObjectAPIv1(utils.TestCase): + + def setUp(self): + super(TestObjectAPIv1, self).setUp() + sess = session.Session() + self.api = object_store.APIv1(session=sess, endpoint=FAKE_URL) + self.requests_mock = self.useFixture(fixture.Fixture()) + + +class TestContainer(TestObjectAPIv1): + + def setUp(self): + super(TestContainer, self).setUp() + + def test_container_create(self): + headers = { + 'x-trans-id': '1qaz2wsx', + } + self.requests_mock.register_uri( + 'PUT', + FAKE_URL + '/qaz', + headers=headers, + status_code=201, + ) + ret = self.api.container_create(container='qaz') + data = { + 'account': FAKE_ACCOUNT, + 'container': 'qaz', + 'x-trans-id': '1qaz2wsx', + } + self.assertEqual(data, ret) + + def test_container_delete(self): + self.requests_mock.register_uri( + 'DELETE', + FAKE_URL + '/qaz', + status_code=204, + ) + ret = self.api.container_delete(container='qaz') + self.assertIsNone(ret) + + def test_container_list_no_options(self): + self.requests_mock.register_uri( + 'GET', + FAKE_URL, + json=LIST_CONTAINER_RESP, + status_code=200, + ) + ret = self.api.container_list() + self.assertEqual(LIST_CONTAINER_RESP, ret) + + def test_container_list_prefix(self): + self.requests_mock.register_uri( + 'GET', + FAKE_URL + '?prefix=foo%2f&format=json', + json=LIST_CONTAINER_RESP, + status_code=200, + ) + ret = self.api.container_list( + prefix='foo/', + ) + self.assertEqual(LIST_CONTAINER_RESP, ret) + + def test_container_list_marker_limit_end(self): + self.requests_mock.register_uri( + 'GET', + FAKE_URL + '?marker=next&limit=2&end_marker=stop&format=json', + json=LIST_CONTAINER_RESP, + status_code=200, + ) + ret = self.api.container_list( + marker='next', + limit=2, + end_marker='stop', + ) + self.assertEqual(LIST_CONTAINER_RESP, ret) + +# def test_container_list_full_listing(self): +# sess = self.app.client_manager.session +# +# def side_effect(*args, **kwargs): +# rv = sess.get().json.return_value +# sess.get().json.return_value = [] +# sess.get().json.side_effect = None +# return rv +# +# resp = [{'name': 'is-name'}] +# sess.get().json.return_value = resp +# sess.get().json.side_effect = side_effect +# +# data = lib_container.list_containers( +# self.app.client_manager.session, +# fake_url, +# full_listing=True, +# ) +# +# # Check expected values +# sess.get.assert_called_with( +# fake_url, +# params={ +# 'format': 'json', +# 'marker': 'is-name', +# } +# ) +# self.assertEqual(resp, data) + + def test_container_show(self): + headers = { + 'X-Container-Meta-Owner': FAKE_ACCOUNT, + 'x-container-object-count': '1', + 'x-container-bytes-used': '577', + } + resp = { + 'account': FAKE_ACCOUNT, + 'container': 'qaz', + 'object_count': '1', + 'bytes_used': '577', + 'properties': {'Owner': FAKE_ACCOUNT}, + } + self.requests_mock.register_uri( + 'HEAD', + FAKE_URL + '/qaz', + headers=headers, + status_code=204, + ) + ret = self.api.container_show(container='qaz') + self.assertEqual(resp, ret) + + +class TestObject(TestObjectAPIv1): + + def setUp(self): + super(TestObject, self).setUp() + + @mock.patch('openstackclient.api.object_store_v1.io.open') + def base_object_create(self, file_contents, mock_open): + mock_open.read.return_value = file_contents + + headers = { + 'etag': 'youreit', + 'x-trans-id': '1qaz2wsx', + } + # TODO(dtroyer): When requests_mock gains the ability to + # match against request.body add this check + # https://review.openstack.org/127316 + self.requests_mock.register_uri( + 'PUT', + FAKE_URL + '/qaz/counter.txt', + headers=headers, + # body=file_contents, + status_code=201, + ) + ret = self.api.object_create( + container='qaz', + object='counter.txt', + ) + data = { + 'account': FAKE_ACCOUNT, + 'container': 'qaz', + 'object': 'counter.txt', + 'etag': 'youreit', + 'x-trans-id': '1qaz2wsx', + } + self.assertEqual(data, ret) + + def test_object_create(self): + self.base_object_create('111\n222\n333\n') + self.base_object_create(bytes([0x31, 0x00, 0x0d, 0x0a, 0x7f, 0xff])) + + def test_object_delete(self): + self.requests_mock.register_uri( + 'DELETE', + FAKE_URL + '/qaz/wsx', + status_code=204, + ) + ret = self.api.object_delete( + container='qaz', + object='wsx', + ) + self.assertIsNone(ret) + + def test_object_list_no_options(self): + self.requests_mock.register_uri( + 'GET', + FAKE_URL + '/qaz', + json=LIST_OBJECT_RESP, + status_code=200, + ) + ret = self.api.object_list(container='qaz') + self.assertEqual(LIST_OBJECT_RESP, ret) + + def test_object_list_delimiter(self): + self.requests_mock.register_uri( + 'GET', + FAKE_URL + '/qaz?delimiter=%7C', + json=LIST_OBJECT_RESP, + status_code=200, + ) + ret = self.api.object_list( + container='qaz', + delimiter='|', + ) + self.assertEqual(LIST_OBJECT_RESP, ret) + + def test_object_list_prefix(self): + self.requests_mock.register_uri( + 'GET', + FAKE_URL + '/qaz?prefix=foo%2f', + json=LIST_OBJECT_RESP, + status_code=200, + ) + ret = self.api.object_list( + container='qaz', + prefix='foo/', + ) + self.assertEqual(LIST_OBJECT_RESP, ret) + + def test_object_list_marker_limit_end(self): + self.requests_mock.register_uri( + 'GET', + FAKE_URL + '/qaz?marker=next&limit=2&end_marker=stop', + json=LIST_CONTAINER_RESP, + status_code=200, + ) + ret = self.api.object_list( + container='qaz', + marker='next', + limit=2, + end_marker='stop', + ) + self.assertEqual(LIST_CONTAINER_RESP, ret) + +# def test_list_objects_full_listing(self): +# sess = self.app.client_manager.session +# +# def side_effect(*args, **kwargs): +# rv = sess.get().json.return_value +# sess.get().json.return_value = [] +# sess.get().json.side_effect = None +# return rv +# +# resp = [{'name': 'is-name'}] +# sess.get().json.return_value = resp +# sess.get().json.side_effect = side_effect +# +# data = lib_object.list_objects( +# sess, +# fake_url, +# fake_container, +# full_listing=True, +# ) +# +# # Check expected values +# sess.get.assert_called_with( +# fake_url + '/' + fake_container, +# params={ +# 'format': 'json', +# 'marker': 'is-name', +# } +# ) +# self.assertEqual(resp, data) + + def test_object_show(self): + headers = { + 'content-type': 'text/alpha', + 'content-length': '577', + 'last-modified': '20130101', + 'etag': 'qaz', + 'x-container-meta-owner': FAKE_ACCOUNT, + 'x-object-meta-wife': 'Wilma', + 'x-object-meta-Husband': 'fred', + 'x-tra-header': 'yabba-dabba-do', + } + resp = { + 'account': FAKE_ACCOUNT, + 'container': 'qaz', + 'object': FAKE_OBJECT, + 'content-type': 'text/alpha', + 'content-length': '577', + 'last-modified': '20130101', + 'etag': 'qaz', + 'properties': {'wife': 'Wilma', + 'Husband': 'fred'}, + } + self.requests_mock.register_uri( + 'HEAD', + FAKE_URL + '/qaz/' + FAKE_OBJECT, + headers=headers, + status_code=204, + ) + ret = self.api.object_show( + container='qaz', + object=FAKE_OBJECT, + ) + self.assertEqual(resp, ret) diff --git a/openstackclient/tests/unit/api/test_utils.py b/openstackclient/tests/unit/api/test_utils.py new file mode 100644 index 00000000..1f528558 --- /dev/null +++ b/openstackclient/tests/unit/api/test_utils.py @@ -0,0 +1,115 @@ +# 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. +# + +"""API Utilities Library Tests""" + +import copy + +from openstackclient.api import api +from openstackclient.api import utils as api_utils +from openstackclient.tests.unit.api import fakes as api_fakes + + +class TestBaseAPIFilter(api_fakes.TestSession): + """The filters can be tested independently""" + + def setUp(self): + super(TestBaseAPIFilter, self).setUp() + self.api = api.BaseAPI( + session=self.sess, + endpoint=self.BASE_URL, + ) + + self.input_list = [ + api_fakes.RESP_ITEM_1, + api_fakes.RESP_ITEM_2, + api_fakes.RESP_ITEM_3, + ] + + def test_simple_filter_none(self): + output = api_utils.simple_filter( + ) + self.assertIsNone(output) + + def test_simple_filter_no_attr(self): + output = api_utils.simple_filter( + copy.deepcopy(self.input_list), + ) + self.assertEqual(self.input_list, output) + + def test_simple_filter_attr_only(self): + output = api_utils.simple_filter( + copy.deepcopy(self.input_list), + attr='status', + ) + self.assertEqual(self.input_list, output) + + def test_simple_filter_attr_value(self): + output = api_utils.simple_filter( + copy.deepcopy(self.input_list), + attr='status', + value='', + ) + self.assertEqual([], output) + + output = api_utils.simple_filter( + copy.deepcopy(self.input_list), + attr='status', + value='UP', + ) + self.assertEqual( + [api_fakes.RESP_ITEM_1, api_fakes.RESP_ITEM_3], + output, + ) + + output = api_utils.simple_filter( + copy.deepcopy(self.input_list), + attr='fred', + value='UP', + ) + self.assertEqual([], output) + + def test_simple_filter_prop_attr_only(self): + output = api_utils.simple_filter( + copy.deepcopy(self.input_list), + attr='b', + property_field='props', + ) + self.assertEqual(self.input_list, output) + + output = api_utils.simple_filter( + copy.deepcopy(self.input_list), + attr='status', + property_field='props', + ) + self.assertEqual(self.input_list, output) + + def test_simple_filter_prop_attr_value(self): + output = api_utils.simple_filter( + copy.deepcopy(self.input_list), + attr='b', + value=2, + property_field='props', + ) + self.assertEqual( + [api_fakes.RESP_ITEM_1, api_fakes.RESP_ITEM_2], + output, + ) + + output = api_utils.simple_filter( + copy.deepcopy(self.input_list), + attr='b', + value=9, + property_field='props', + ) + self.assertEqual([], output) diff --git a/openstackclient/tests/unit/common/__init__.py b/openstackclient/tests/unit/common/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/openstackclient/tests/unit/common/__init__.py diff --git a/openstackclient/tests/unit/common/test_availability_zone.py b/openstackclient/tests/unit/common/test_availability_zone.py new file mode 100644 index 00000000..6c7adc43 --- /dev/null +++ b/openstackclient/tests/unit/common/test_availability_zone.py @@ -0,0 +1,268 @@ +# 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 + +import six + +from openstackclient.common import availability_zone +from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit.network.v2 import fakes as network_fakes +from openstackclient.tests.unit import utils +from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes + + +def _build_compute_az_datalist(compute_az, long_datalist=False): + datalist = () + if not long_datalist: + datalist = ( + compute_az.zoneName, + 'available', + ) + else: + for host, services in six.iteritems(compute_az.hosts): + for service, state in six.iteritems(services): + datalist += ( + compute_az.zoneName, + 'available', + '', + host, + service, + 'enabled :-) ' + state['updated_at'], + ) + return (datalist,) + + +def _build_volume_az_datalist(volume_az, long_datalist=False): + datalist = () + if not long_datalist: + datalist = ( + volume_az.zoneName, + 'available', + ) + else: + datalist = ( + volume_az.zoneName, + 'available', + '', '', '', '', + ) + return (datalist,) + + +def _build_network_az_datalist(network_az, long_datalist=False): + datalist = () + if not long_datalist: + datalist = ( + network_az.name, + network_az.state, + ) + else: + datalist = ( + network_az.name, + network_az.state, + network_az.resource, + '', '', '', + ) + return (datalist,) + + +class TestAvailabilityZone(utils.TestCommand): + + def setUp(self): + super(TestAvailabilityZone, self).setUp() + + compute_client = compute_fakes.FakeComputev2Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + self.app.client_manager.compute = compute_client + + self.compute_azs_mock = compute_client.availability_zones + self.compute_azs_mock.reset_mock() + + volume_client = volume_fakes.FakeVolumeClient( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + self.app.client_manager.volume = volume_client + + self.volume_azs_mock = volume_client.availability_zones + self.volume_azs_mock.reset_mock() + + network_client = network_fakes.FakeNetworkV2Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + self.app.client_manager.network = network_client + + network_client.availability_zones = mock.Mock() + network_client.find_extension = mock.Mock() + self.network_azs_mock = network_client.availability_zones + + +class TestAvailabilityZoneList(TestAvailabilityZone): + + compute_azs = \ + compute_fakes.FakeAvailabilityZone.create_availability_zones() + volume_azs = \ + volume_fakes.FakeAvailabilityZone.create_availability_zones(count=1) + network_azs = \ + network_fakes.FakeAvailabilityZone.create_availability_zones() + + short_columnslist = ('Zone Name', 'Zone Status') + long_columnslist = ( + 'Zone Name', + 'Zone Status', + 'Zone Resource', + 'Host Name', + 'Service Name', + 'Service Status', + ) + + def setUp(self): + super(TestAvailabilityZoneList, self).setUp() + + self.compute_azs_mock.list.return_value = self.compute_azs + self.volume_azs_mock.list.return_value = self.volume_azs + self.network_azs_mock.return_value = self.network_azs + + # Get the command object to test + self.cmd = availability_zone.ListAvailabilityZone(self.app, None) + + def test_availability_zone_list_no_options(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.compute_azs_mock.list.assert_called_with() + self.volume_azs_mock.list.assert_called_with() + self.network_azs_mock.assert_called_with() + + self.assertEqual(self.short_columnslist, columns) + datalist = () + for compute_az in self.compute_azs: + datalist += _build_compute_az_datalist(compute_az) + for volume_az in self.volume_azs: + datalist += _build_volume_az_datalist(volume_az) + for network_az in self.network_azs: + datalist += _build_network_az_datalist(network_az) + self.assertEqual(datalist, tuple(data)) + + def test_availability_zone_list_long(self): + arglist = [ + '--long', + ] + verifylist = [ + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.compute_azs_mock.list.assert_called_with() + self.volume_azs_mock.list.assert_called_with() + self.network_azs_mock.assert_called_with() + + self.assertEqual(self.long_columnslist, columns) + datalist = () + for compute_az in self.compute_azs: + datalist += _build_compute_az_datalist(compute_az, + long_datalist=True) + for volume_az in self.volume_azs: + datalist += _build_volume_az_datalist(volume_az, + long_datalist=True) + for network_az in self.network_azs: + datalist += _build_network_az_datalist(network_az, + long_datalist=True) + self.assertEqual(datalist, tuple(data)) + + def test_availability_zone_list_compute(self): + arglist = [ + '--compute', + ] + verifylist = [ + ('compute', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.compute_azs_mock.list.assert_called_with() + self.volume_azs_mock.list.assert_not_called() + self.network_azs_mock.assert_not_called() + + self.assertEqual(self.short_columnslist, columns) + datalist = () + for compute_az in self.compute_azs: + datalist += _build_compute_az_datalist(compute_az) + self.assertEqual(datalist, tuple(data)) + + def test_availability_zone_list_volume(self): + arglist = [ + '--volume', + ] + verifylist = [ + ('volume', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.compute_azs_mock.list.assert_not_called() + self.volume_azs_mock.list.assert_called_with() + self.network_azs_mock.assert_not_called() + + self.assertEqual(self.short_columnslist, columns) + datalist = () + for volume_az in self.volume_azs: + datalist += _build_volume_az_datalist(volume_az) + self.assertEqual(datalist, tuple(data)) + + def test_availability_zone_list_network(self): + arglist = [ + '--network', + ] + verifylist = [ + ('network', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.compute_azs_mock.list.assert_not_called() + self.volume_azs_mock.list.assert_not_called() + self.network_azs_mock.assert_called_with() + + self.assertEqual(self.short_columnslist, columns) + datalist = () + for network_az in self.network_azs: + datalist += _build_network_az_datalist(network_az) + self.assertEqual(datalist, tuple(data)) diff --git a/openstackclient/tests/unit/common/test_clientmanager.py b/openstackclient/tests/unit/common/test_clientmanager.py new file mode 100644 index 00000000..7f82c35d --- /dev/null +++ b/openstackclient/tests/unit/common/test_clientmanager.py @@ -0,0 +1,69 @@ +# Copyright 2012-2013 OpenStack Foundation +# +# 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 copy + +from keystoneauth1 import token_endpoint +from osc_lib.tests import utils as osc_lib_test_utils + +from openstackclient.common import clientmanager +from openstackclient.tests.unit import fakes + + +class TestClientManager(osc_lib_test_utils.TestClientManager): + + def _clientmanager_class(self): + """Allow subclasses to override the ClientManager class""" + return clientmanager.ClientManager + + def test_client_manager_token_endpoint(self): + token_auth = { + 'url': fakes.AUTH_URL, + 'token': fakes.AUTH_TOKEN, + } + client_manager = self._make_clientmanager( + auth_args=token_auth, + auth_plugin_name='token_endpoint', + ) + + self.assertEqual( + fakes.AUTH_URL, + client_manager._cli_options.config['auth']['url'], + ) + self.assertEqual( + fakes.AUTH_TOKEN, + client_manager.auth.get_token(None), + ) + self.assertIsInstance( + client_manager.auth, + token_endpoint.Token, + ) + self.assertTrue(client_manager.is_network_endpoint_enabled()) + + def test_client_manager_network_endpoint_disabled(self): + auth_args = copy.deepcopy(self.default_password_auth) + auth_args.update({ + 'user_domain_name': 'default', + 'project_domain_name': 'default', + }) + # v3 fake doesn't have network endpoint + client_manager = self._make_clientmanager( + auth_args=auth_args, + identity_api_version='3', + auth_plugin_name='v3password', + ) + + self.assertFalse(client_manager.is_service_available('network')) + self.assertFalse(client_manager.is_network_endpoint_enabled()) diff --git a/openstackclient/tests/unit/common/test_command.py b/openstackclient/tests/unit/common/test_command.py new file mode 100644 index 00000000..f24b290b --- /dev/null +++ b/openstackclient/tests/unit/common/test_command.py @@ -0,0 +1,49 @@ +# Copyright 2016 NEC Corporation +# +# 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 osc_lib import exceptions + +from openstackclient.common import command +from openstackclient.tests.unit import fakes as test_fakes +from openstackclient.tests.unit import utils as test_utils + + +class FakeCommand(command.Command): + + def take_action(self, parsed_args): + pass + + +class TestCommand(test_utils.TestCase): + + def test_command_has_logger(self): + cmd = FakeCommand(mock.Mock(), mock.Mock()) + self.assertTrue(hasattr(cmd, 'log')) + self.assertEqual('openstackclient.tests.unit.common.test_command.' + 'FakeCommand', cmd.log.name) + + def test_validate_os_beta_command_enabled(self): + cmd = FakeCommand(mock.Mock(), mock.Mock()) + cmd.app = mock.Mock() + cmd.app.options = test_fakes.FakeOptions() + + # No exception is raised when enabled. + cmd.app.options.os_beta_command = True + cmd.validate_os_beta_command_enabled() + + cmd.app.options.os_beta_command = False + self.assertRaises(exceptions.CommandError, + cmd.validate_os_beta_command_enabled) diff --git a/openstackclient/tests/unit/common/test_commandmanager.py b/openstackclient/tests/unit/common/test_commandmanager.py new file mode 100644 index 00000000..0c6c99c0 --- /dev/null +++ b/openstackclient/tests/unit/common/test_commandmanager.py @@ -0,0 +1,107 @@ +# Copyright 2012-2013 OpenStack Foundation +# +# 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 openstackclient.common import commandmanager +from openstackclient.tests.unit import utils + + +class FakeCommand(object): + + @classmethod + def load(cls): + return cls + + def __init__(self): + return + +FAKE_CMD_ONE = FakeCommand +FAKE_CMD_TWO = FakeCommand +FAKE_CMD_ALPHA = FakeCommand +FAKE_CMD_BETA = FakeCommand + + +class FakeCommandManager(commandmanager.CommandManager): + commands = {} + + def load_commands(self, namespace): + if namespace == 'test': + self.commands['one'] = FAKE_CMD_ONE + self.commands['two'] = FAKE_CMD_TWO + self.group_list.append(namespace) + elif namespace == 'greek': + self.commands['alpha'] = FAKE_CMD_ALPHA + self.commands['beta'] = FAKE_CMD_BETA + self.group_list.append(namespace) + + +class TestCommandManager(utils.TestCase): + + def test_add_command_group(self): + mgr = FakeCommandManager('test') + + # Make sure add_command() still functions + mock_cmd_one = mock.Mock() + mgr.add_command('mock', mock_cmd_one) + cmd_mock, name, args = mgr.find_command(['mock']) + self.assertEqual(mock_cmd_one, cmd_mock) + + # Find a command added in initialization + cmd_one, name, args = mgr.find_command(['one']) + self.assertEqual(FAKE_CMD_ONE, cmd_one) + + # Load another command group + mgr.add_command_group('greek') + + # Find a new command + cmd_alpha, name, args = mgr.find_command(['alpha']) + self.assertEqual(FAKE_CMD_ALPHA, cmd_alpha) + + # Ensure that the original commands were not overwritten + cmd_two, name, args = mgr.find_command(['two']) + self.assertEqual(FAKE_CMD_TWO, cmd_two) + + def test_get_command_groups(self): + mgr = FakeCommandManager('test') + + # Make sure add_command() still functions + mock_cmd_one = mock.Mock() + mgr.add_command('mock', mock_cmd_one) + cmd_mock, name, args = mgr.find_command(['mock']) + self.assertEqual(mock_cmd_one, cmd_mock) + + # Load another command group + mgr.add_command_group('greek') + + gl = mgr.get_command_groups() + self.assertEqual(['test', 'greek'], gl) + + def test_get_command_names(self): + mock_cmd_one = mock.Mock() + mock_cmd_one.name = 'one' + mock_cmd_two = mock.Mock() + mock_cmd_two.name = 'cmd two' + mock_pkg_resources = mock.Mock( + return_value=[mock_cmd_one, mock_cmd_two], + ) + with mock.patch( + 'pkg_resources.iter_entry_points', + mock_pkg_resources, + ) as iter_entry_points: + mgr = commandmanager.CommandManager('test') + iter_entry_points.assert_called_once_with('test') + cmds = mgr.get_command_names('test') + self.assertEqual(['one', 'cmd two'], cmds) diff --git a/openstackclient/tests/unit/common/test_configuration.py b/openstackclient/tests/unit/common/test_configuration.py new file mode 100644 index 00000000..e10522b9 --- /dev/null +++ b/openstackclient/tests/unit/common/test_configuration.py @@ -0,0 +1,85 @@ +# 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 openstackclient.common import configuration +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit import utils + + +class TestConfiguration(utils.TestCommand): + + columns = ( + 'auth.password', + 'auth.token', + 'auth.username', + 'identity_api_version', + 'region', + ) + datalist = ( + configuration.REDACTED, + configuration.REDACTED, + fakes.USERNAME, + fakes.VERSION, + fakes.REGION_NAME, + ) + + opts = [mock.Mock(secret=True, dest="password"), + mock.Mock(secret=True, dest="token")] + + @mock.patch("keystoneauth1.loading.base.get_plugin_options", + return_value=opts) + def test_show(self, m_get_plugin_opts): + arglist = [] + verifylist = [('mask', True)] + cmd = configuration.ShowConfiguration(self.app, None) + parsed_args = self.check_parser(cmd, arglist, verifylist) + + columns, data = cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + @mock.patch("keystoneauth1.loading.base.get_plugin_options", + return_value=opts) + def test_show_unmask(self, m_get_plugin_opts): + arglist = ['--unmask'] + verifylist = [('mask', False)] + cmd = configuration.ShowConfiguration(self.app, None) + parsed_args = self.check_parser(cmd, arglist, verifylist) + + columns, data = cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + datalist = ( + fakes.PASSWORD, + fakes.AUTH_TOKEN, + fakes.USERNAME, + fakes.VERSION, + fakes.REGION_NAME, + ) + self.assertEqual(datalist, data) + + @mock.patch("keystoneauth1.loading.base.get_plugin_options", + return_value=opts) + def test_show_mask(self, m_get_plugin_opts): + arglist = ['--mask'] + verifylist = [('mask', True)] + cmd = configuration.ShowConfiguration(self.app, None) + parsed_args = self.check_parser(cmd, arglist, verifylist) + + columns, data = cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) diff --git a/openstackclient/tests/unit/common/test_extension.py b/openstackclient/tests/unit/common/test_extension.py new file mode 100644 index 00000000..bf856ed1 --- /dev/null +++ b/openstackclient/tests/unit/common/test_extension.py @@ -0,0 +1,244 @@ +# 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 openstackclient.common import extension +from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes +from openstackclient.tests.unit.network.v2 import fakes as network_fakes +from openstackclient.tests.unit import utils +from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes + + +class TestExtension(utils.TestCommand): + + def setUp(self): + super(TestExtension, self).setUp() + + identity_client = identity_fakes.FakeIdentityv2Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + self.app.client_manager.identity = identity_client + self.identity_extensions_mock = identity_client.extensions + self.identity_extensions_mock.reset_mock() + + compute_client = compute_fakes.FakeComputev2Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + self.app.client_manager.compute = compute_client + compute_client.list_extensions = mock.Mock() + self.compute_extensions_mock = compute_client.list_extensions + self.compute_extensions_mock.reset_mock() + + volume_client = volume_fakes.FakeVolumeClient( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + self.app.client_manager.volume = volume_client + volume_client.list_extensions = mock.Mock() + self.volume_extensions_mock = volume_client.list_extensions + self.volume_extensions_mock.reset_mock() + + network_client = network_fakes.FakeNetworkV2Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + self.app.client_manager.network = network_client + network_client.extensions = mock.Mock() + self.network_extensions_mock = network_client.extensions + self.network_extensions_mock.reset_mock() + + +class TestExtensionList(TestExtension): + + columns = ('Name', 'Alias', 'Description') + long_columns = ('Name', 'Namespace', 'Description', 'Alias', 'Updated', + 'Links') + + volume_extension = volume_fakes.FakeExtension.create_one_extension() + identity_extension = identity_fakes.FakeExtension.create_one_extension() + compute_extension = compute_fakes.FakeExtension.create_one_extension() + network_extension = network_fakes.FakeExtension.create_one_extension() + + def setUp(self): + super(TestExtensionList, self).setUp() + + self.identity_extensions_mock.list.return_value = [ + self.identity_extension] + self.compute_extensions_mock.show_all.return_value = [ + self.compute_extension] + self.volume_extensions_mock.show_all.return_value = [ + self.volume_extension] + self.network_extensions_mock.return_value = [self.network_extension] + + # Get the command object to test + self.cmd = extension.ListExtension(self.app, None) + + def _test_extension_list_helper(self, arglist, verifylist, + expected_data, long=False): + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + if long: + self.assertEqual(self.long_columns, columns) + else: + self.assertEqual(self.columns, columns) + self.assertEqual(expected_data, tuple(data)) + + def test_extension_list_no_options(self): + arglist = [] + verifylist = [] + datalist = ( + ( + self.identity_extension.name, + self.identity_extension.alias, + self.identity_extension.description, + ), + ( + self.compute_extension.name, + self.compute_extension.alias, + self.compute_extension.description, + ), + ( + self.volume_extension.name, + self.volume_extension.alias, + self.volume_extension.description, + ), + ( + self.network_extension.name, + self.network_extension.alias, + self.network_extension.description, + ), + ) + self._test_extension_list_helper(arglist, verifylist, datalist) + self.identity_extensions_mock.list.assert_called_with() + self.compute_extensions_mock.show_all.assert_called_with() + self.volume_extensions_mock.show_all.assert_called_with() + self.network_extensions_mock.assert_called_with() + + def test_extension_list_long(self): + arglist = [ + '--long', + ] + verifylist = [ + ('long', True), + ] + datalist = ( + ( + self.identity_extension.name, + self.identity_extension.namespace, + self.identity_extension.description, + self.identity_extension.alias, + self.identity_extension.updated, + self.identity_extension.links, + ), + ( + self.compute_extension.name, + self.compute_extension.namespace, + self.compute_extension.description, + self.compute_extension.alias, + self.compute_extension.updated, + self.compute_extension.links, + ), + ( + self.volume_extension.name, + self.volume_extension.namespace, + self.volume_extension.description, + self.volume_extension.alias, + self.volume_extension.updated, + self.volume_extension.links, + ), + ( + self.network_extension.name, + self.network_extension.namespace, + self.network_extension.description, + self.network_extension.alias, + self.network_extension.updated, + self.network_extension.links, + ), + ) + self._test_extension_list_helper(arglist, verifylist, datalist, True) + self.identity_extensions_mock.list.assert_called_with() + self.compute_extensions_mock.show_all.assert_called_with() + self.volume_extensions_mock.show_all.assert_called_with() + self.network_extensions_mock.assert_called_with() + + def test_extension_list_identity(self): + arglist = [ + '--identity', + ] + verifylist = [ + ('identity', True), + ] + datalist = (( + self.identity_extension.name, + self.identity_extension.alias, + self.identity_extension.description, + ), ) + self._test_extension_list_helper(arglist, verifylist, datalist) + self.identity_extensions_mock.list.assert_called_with() + + def test_extension_list_network(self): + arglist = [ + '--network', + ] + verifylist = [ + ('network', True), + ] + datalist = ( + ( + self.network_extension.name, + self.network_extension.alias, + self.network_extension.description, + ), + ) + self._test_extension_list_helper(arglist, verifylist, datalist) + self.network_extensions_mock.assert_called_with() + + def test_extension_list_compute(self): + arglist = [ + '--compute', + ] + verifylist = [ + ('compute', True), + ] + datalist = (( + self.compute_extension.name, + self.compute_extension.alias, + self.compute_extension.description, + ), ) + self._test_extension_list_helper(arglist, verifylist, datalist) + self.compute_extensions_mock.show_all.assert_called_with() + + def test_extension_list_volume(self): + arglist = [ + '--volume', + ] + verifylist = [ + ('volume', True), + ] + datalist = (( + self.volume_extension.name, + self.volume_extension.alias, + self.volume_extension.description, + ), ) + self._test_extension_list_helper(arglist, verifylist, datalist) + self.volume_extensions_mock.show_all.assert_called_with() diff --git a/openstackclient/tests/unit/common/test_logs.py b/openstackclient/tests/unit/common/test_logs.py new file mode 100644 index 00000000..4842c8d4 --- /dev/null +++ b/openstackclient/tests/unit/common/test_logs.py @@ -0,0 +1,207 @@ +# 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. +# + +# NOTE(dtroyer): This file is deprecated in Jun 2016, remove after 4.x release +# or Jun 2017. + +import logging +import mock + +from openstackclient.common import logs +from openstackclient.tests.unit import utils + + +class TestContext(utils.TestCase): + + def test_log_level_from_options(self): + opts = mock.Mock() + opts.verbose_level = 0 + self.assertEqual(logging.ERROR, logs.log_level_from_options(opts)) + opts.verbose_level = 1 + self.assertEqual(logging.WARNING, logs.log_level_from_options(opts)) + opts.verbose_level = 2 + self.assertEqual(logging.INFO, logs.log_level_from_options(opts)) + opts.verbose_level = 3 + self.assertEqual(logging.DEBUG, logs.log_level_from_options(opts)) + + def test_log_level_from_config(self): + cfg = {'verbose_level': 0} + self.assertEqual(logging.ERROR, logs.log_level_from_config(cfg)) + cfg = {'verbose_level': 1} + self.assertEqual(logging.WARNING, logs.log_level_from_config(cfg)) + cfg = {'verbose_level': 2} + self.assertEqual(logging.INFO, logs.log_level_from_config(cfg)) + cfg = {'verbose_level': 3} + self.assertEqual(logging.DEBUG, logs.log_level_from_config(cfg)) + cfg = {'verbose_level': 1, 'log_level': 'critical'} + self.assertEqual(logging.CRITICAL, logs.log_level_from_config(cfg)) + cfg = {'verbose_level': 1, 'log_level': 'error'} + self.assertEqual(logging.ERROR, logs.log_level_from_config(cfg)) + cfg = {'verbose_level': 1, 'log_level': 'warning'} + self.assertEqual(logging.WARNING, logs.log_level_from_config(cfg)) + cfg = {'verbose_level': 1, 'log_level': 'info'} + self.assertEqual(logging.INFO, logs.log_level_from_config(cfg)) + cfg = {'verbose_level': 1, 'log_level': 'debug'} + self.assertEqual(logging.DEBUG, logs.log_level_from_config(cfg)) + cfg = {'verbose_level': 1, 'log_level': 'bogus'} + self.assertEqual(logging.WARNING, logs.log_level_from_config(cfg)) + cfg = {'verbose_level': 1, 'log_level': 'info', 'debug': True} + self.assertEqual(logging.DEBUG, logs.log_level_from_config(cfg)) + + @mock.patch('warnings.simplefilter') + def test_set_warning_filter(self, simplefilter): + logs.set_warning_filter(logging.ERROR) + simplefilter.assert_called_with("ignore") + logs.set_warning_filter(logging.WARNING) + simplefilter.assert_called_with("ignore") + logs.set_warning_filter(logging.INFO) + simplefilter.assert_called_with("once") + + +class TestFileFormatter(utils.TestCase): + + def test_nothing(self): + formatter = logs._FileFormatter() + self.assertEqual(('%(asctime)s.%(msecs)03d %(process)d %(levelname)s ' + '%(name)s %(message)s'), formatter.fmt) + + def test_options(self): + class Opts(object): + cloud = 'cloudy' + os_project_name = 'projecty' + username = 'usernamey' + options = Opts() + formatter = logs._FileFormatter(options=options) + self.assertEqual(('%(asctime)s.%(msecs)03d %(process)d %(levelname)s ' + '%(name)s [cloudy usernamey projecty] %(message)s'), + formatter.fmt) + + def test_config(self): + config = mock.Mock() + config.config = {'cloud': 'cloudy'} + config.auth = {'project_name': 'projecty', 'username': 'usernamey'} + formatter = logs._FileFormatter(config=config) + self.assertEqual(('%(asctime)s.%(msecs)03d %(process)d %(levelname)s ' + '%(name)s [cloudy usernamey projecty] %(message)s'), + formatter.fmt) + + +class TestLogConfigurator(utils.TestCase): + + def setUp(self): + super(TestLogConfigurator, self).setUp() + self.options = mock.Mock() + self.options.verbose_level = 1 + self.options.log_file = None + self.options.debug = False + self.root_logger = mock.Mock() + self.root_logger.setLevel = mock.Mock() + self.root_logger.addHandler = mock.Mock() + self.requests_log = mock.Mock() + self.requests_log.setLevel = mock.Mock() + self.cliff_log = mock.Mock() + self.cliff_log.setLevel = mock.Mock() + self.stevedore_log = mock.Mock() + self.stevedore_log.setLevel = mock.Mock() + self.iso8601_log = mock.Mock() + self.iso8601_log.setLevel = mock.Mock() + self.loggers = [ + self.root_logger, + self.requests_log, + self.cliff_log, + self.stevedore_log, + self.iso8601_log] + + @mock.patch('logging.StreamHandler') + @mock.patch('logging.getLogger') + @mock.patch('osc_lib.logs.set_warning_filter') + def test_init(self, warning_filter, getLogger, handle): + getLogger.side_effect = self.loggers + console_logger = mock.Mock() + console_logger.setFormatter = mock.Mock() + console_logger.setLevel = mock.Mock() + handle.return_value = console_logger + + configurator = logs.LogConfigurator(self.options) + + getLogger.assert_called_with('iso8601') # last call + warning_filter.assert_called_with(logging.WARNING) + self.root_logger.setLevel.assert_called_with(logging.DEBUG) + self.root_logger.addHandler.assert_called_with(console_logger) + self.requests_log.setLevel.assert_called_with(logging.ERROR) + self.cliff_log.setLevel.assert_called_with(logging.ERROR) + self.stevedore_log.setLevel.assert_called_with(logging.ERROR) + self.iso8601_log.setLevel.assert_called_with(logging.ERROR) + self.assertFalse(configurator.dump_trace) + + @mock.patch('logging.getLogger') + @mock.patch('osc_lib.logs.set_warning_filter') + def test_init_no_debug(self, warning_filter, getLogger): + getLogger.side_effect = self.loggers + self.options.debug = True + + configurator = logs.LogConfigurator(self.options) + + warning_filter.assert_called_with(logging.DEBUG) + self.requests_log.setLevel.assert_called_with(logging.DEBUG) + self.assertTrue(configurator.dump_trace) + + @mock.patch('logging.FileHandler') + @mock.patch('logging.getLogger') + @mock.patch('osc_lib.logs.set_warning_filter') + @mock.patch('osc_lib.logs._FileFormatter') + def test_init_log_file(self, formatter, warning_filter, getLogger, handle): + getLogger.side_effect = self.loggers + self.options.log_file = '/tmp/log_file' + file_logger = mock.Mock() + file_logger.setFormatter = mock.Mock() + file_logger.setLevel = mock.Mock() + handle.return_value = file_logger + mock_formatter = mock.Mock() + formatter.return_value = mock_formatter + + logs.LogConfigurator(self.options) + + handle.assert_called_with(filename=self.options.log_file) + self.root_logger.addHandler.assert_called_with(file_logger) + file_logger.setFormatter.assert_called_with(mock_formatter) + file_logger.setLevel.assert_called_with(logging.WARNING) + + @mock.patch('logging.FileHandler') + @mock.patch('logging.getLogger') + @mock.patch('osc_lib.logs.set_warning_filter') + @mock.patch('osc_lib.logs._FileFormatter') + def test_configure(self, formatter, warning_filter, getLogger, handle): + getLogger.side_effect = self.loggers + configurator = logs.LogConfigurator(self.options) + cloud_config = mock.Mock() + config_log = '/tmp/config_log' + cloud_config.config = { + 'log_file': config_log, + 'verbose_level': 1, + 'log_level': 'info'} + file_logger = mock.Mock() + file_logger.setFormatter = mock.Mock() + file_logger.setLevel = mock.Mock() + handle.return_value = file_logger + mock_formatter = mock.Mock() + formatter.return_value = mock_formatter + + configurator.configure(cloud_config) + + warning_filter.assert_called_with(logging.INFO) + handle.assert_called_with(filename=config_log) + self.root_logger.addHandler.assert_called_with(file_logger) + file_logger.setFormatter.assert_called_with(mock_formatter) + file_logger.setLevel.assert_called_with(logging.INFO) + self.assertFalse(configurator.dump_trace) diff --git a/openstackclient/tests/unit/common/test_module.py b/openstackclient/tests/unit/common/test_module.py new file mode 100644 index 00000000..eb54dbe0 --- /dev/null +++ b/openstackclient/tests/unit/common/test_module.py @@ -0,0 +1,130 @@ +# Copyright 2013 Nebula 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. +# + +"""Test module module""" + +import mock + +from openstackclient.common import module as osc_module +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit import utils + + +# NOTE(dtroyer): module_1 must match the version list filter (not --all) +# currently == '*client*' +module_name_1 = 'fakeclient' +module_version_1 = '0.1.2' +MODULE_1 = { + '__version__': module_version_1, +} + +module_name_2 = 'zlib' +module_version_2 = '1.1' +MODULE_2 = { + '__version__': module_version_2, +} + +MODULES = { + module_name_1: fakes.FakeModule(module_name_1, module_version_1), + module_name_2: fakes.FakeModule(module_name_2, module_version_2), +} + + +class TestCommandList(utils.TestCommand): + + def setUp(self): + super(TestCommandList, self).setUp() + + self.app.command_manager = mock.Mock() + self.app.command_manager.get_command_groups.return_value = [ + 'openstack.common' + ] + self.app.command_manager.get_command_names.return_value = [ + 'limits show\nextension list' + ] + + # Get the command object to test + self.cmd = osc_module.ListCommand(self.app, None) + + def test_command_list_no_options(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # TODO(bapalm): Adjust this when cliff properly supports + # handling the detection rather than using the hard-code below. + collist = ('Command Group', 'Commands') + self.assertEqual(collist, columns) + datalist = (( + 'openstack.common', + 'limits show\nextension list' + ),) + + self.assertEqual(datalist, tuple(data)) + + +@mock.patch.dict( + 'openstackclient.common.module.sys.modules', + values=MODULES, + clear=True, +) +class TestModuleList(utils.TestCommand): + + def setUp(self): + super(TestModuleList, self).setUp() + + # Get the command object to test + self.cmd = osc_module.ListModule(self.app, None) + + def test_module_list_no_options(self): + arglist = [] + verifylist = [ + ('all', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Additional modules may be present, just check our additions + self.assertIn(module_name_1, columns) + self.assertIn(module_version_1, data) + + def test_module_list_all(self): + arglist = [ + '--all', + ] + verifylist = [ + ('all', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Additional modules may be present, just check our additions + self.assertIn(module_name_1, columns) + self.assertIn(module_name_2, columns) + self.assertIn(module_version_1, data) + self.assertIn(module_version_2, data) diff --git a/openstackclient/tests/unit/common/test_parseractions.py b/openstackclient/tests/unit/common/test_parseractions.py new file mode 100644 index 00000000..1212ad23 --- /dev/null +++ b/openstackclient/tests/unit/common/test_parseractions.py @@ -0,0 +1,221 @@ +# Copyright 2012-2013 OpenStack Foundation +# +# 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. +# + +# NOTE(dtroyer): This file is deprecated in Jun 2016, remove after 4.x release +# or Jun 2017. + +import argparse + +from openstackclient.common import parseractions +from openstackclient.tests.unit import utils + + +class TestKeyValueAction(utils.TestCase): + + def setUp(self): + super(TestKeyValueAction, self).setUp() + + self.parser = argparse.ArgumentParser() + + # Set up our typical usage + self.parser.add_argument( + '--property', + metavar='<key=value>', + action=parseractions.KeyValueAction, + default={'green': '20%', 'format': '#rgb'}, + help='Property to store for this volume ' + '(repeat option to set multiple properties)', + ) + + def test_good_values(self): + results = self.parser.parse_args([ + '--property', 'red=', + '--property', 'green=100%', + '--property', 'blue=50%', + ]) + + actual = getattr(results, 'property', {}) + # All should pass through unmolested + expect = {'red': '', 'green': '100%', 'blue': '50%', 'format': '#rgb'} + self.assertEqual(expect, actual) + + def test_error_values(self): + self.assertRaises( + argparse.ArgumentTypeError, + self.parser.parse_args, + [ + '--property', 'red', + ] + ) + + +class TestMultiKeyValueAction(utils.TestCase): + + def setUp(self): + super(TestMultiKeyValueAction, self).setUp() + + self.parser = argparse.ArgumentParser() + + # Set up our typical usage + self.parser.add_argument( + '--test', + metavar='req1=xxx,req2=yyy', + action=parseractions.MultiKeyValueAction, + dest='test', + default=None, + required_keys=['req1', 'req2'], + optional_keys=['opt1', 'opt2'], + help='Test' + ) + + def test_good_values(self): + results = self.parser.parse_args([ + '--test', 'req1=aaa,req2=bbb', + '--test', 'req1=,req2=', + ]) + + actual = getattr(results, 'test', []) + expect = [ + {'req1': 'aaa', 'req2': 'bbb'}, + {'req1': '', 'req2': ''}, + ] + self.assertItemsEqual(expect, actual) + + def test_empty_required_optional(self): + self.parser.add_argument( + '--test-empty', + metavar='req1=xxx,req2=yyy', + action=parseractions.MultiKeyValueAction, + dest='test_empty', + default=None, + required_keys=[], + optional_keys=[], + help='Test' + ) + + results = self.parser.parse_args([ + '--test-empty', 'req1=aaa,req2=bbb', + '--test-empty', 'req1=,req2=', + ]) + + actual = getattr(results, 'test_empty', []) + expect = [ + {'req1': 'aaa', 'req2': 'bbb'}, + {'req1': '', 'req2': ''}, + ] + self.assertItemsEqual(expect, actual) + + def test_error_values_with_comma(self): + self.assertRaises( + argparse.ArgumentTypeError, + self.parser.parse_args, + [ + '--test', 'mmm,nnn=zzz', + ] + ) + + def test_error_values_without_comma(self): + self.assertRaises( + argparse.ArgumentTypeError, + self.parser.parse_args, + [ + '--test', 'mmmnnn', + ] + ) + + def test_missing_key(self): + self.assertRaises( + argparse.ArgumentTypeError, + self.parser.parse_args, + [ + '--test', 'req2=ddd', + ] + ) + + def test_invalid_key(self): + self.assertRaises( + argparse.ArgumentTypeError, + self.parser.parse_args, + [ + '--test', 'req1=aaa,req2=bbb,aaa=req1', + ] + ) + + def test_required_keys_not_list(self): + self.assertRaises( + TypeError, + self.parser.add_argument, + '--test-required-dict', + metavar='req1=xxx,req2=yyy', + action=parseractions.MultiKeyValueAction, + dest='test_required_dict', + default=None, + required_keys={'aaa': 'bbb'}, + optional_keys=['opt1', 'opt2'], + help='Test' + ) + + def test_optional_keys_not_list(self): + self.assertRaises( + TypeError, + self.parser.add_argument, + '--test-optional-dict', + metavar='req1=xxx,req2=yyy', + action=parseractions.MultiKeyValueAction, + dest='test_optional_dict', + default=None, + required_keys=['req1', 'req2'], + optional_keys={'aaa': 'bbb'}, + help='Test' + ) + + +class TestNonNegativeAction(utils.TestCase): + + def setUp(self): + super(TestNonNegativeAction, self).setUp() + + self.parser = argparse.ArgumentParser() + + # Set up our typical usage + self.parser.add_argument( + '--foo', + metavar='<foo>', + type=int, + action=parseractions.NonNegativeAction, + ) + + def test_negative_values(self): + self.assertRaises( + argparse.ArgumentTypeError, + self.parser.parse_args, + "--foo -1".split() + ) + + def test_zero_values(self): + results = self.parser.parse_args( + '--foo 0'.split() + ) + + actual = getattr(results, 'foo', None) + self.assertEqual(actual, 0) + + def test_positive_values(self): + results = self.parser.parse_args( + '--foo 1'.split() + ) + + actual = getattr(results, 'foo', None) + self.assertEqual(actual, 1) diff --git a/openstackclient/tests/unit/common/test_quota.py b/openstackclient/tests/unit/common/test_quota.py new file mode 100644 index 00000000..4a80a2b2 --- /dev/null +++ b/openstackclient/tests/unit/common/test_quota.py @@ -0,0 +1,381 @@ +# 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 copy +import mock + +from openstackclient.common import quota +from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes +from openstackclient.tests.unit.network.v2 import fakes as network_fakes + + +class FakeQuotaResource(fakes.FakeResource): + + _keys = {'property': 'value'} + + def set_keys(self, args): + self._keys.update(args) + + def unset_keys(self, keys): + for key in keys: + self._keys.pop(key, None) + + def get_keys(self): + return self._keys + + +class TestQuota(compute_fakes.TestComputev2): + + def setUp(self): + super(TestQuota, self).setUp() + self.quotas_mock = self.app.client_manager.compute.quotas + self.quotas_mock.reset_mock() + self.quotas_class_mock = self.app.client_manager.compute.quota_classes + self.quotas_class_mock.reset_mock() + volume_mock = mock.Mock() + volume_mock.quotas = mock.Mock() + self.app.client_manager.volume = volume_mock + self.volume_quotas_mock = volume_mock.quotas + self.volume_quotas_mock.reset_mock() + self.volume_quotas_class_mock = \ + self.app.client_manager.volume.quota_classes + self.volume_quotas_class_mock.reset_mock() + self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock.reset_mock() + self.app.client_manager.auth_ref = mock.Mock() + self.app.client_manager.auth_ref.service_catalog = mock.Mock() + self.service_catalog_mock = \ + self.app.client_manager.auth_ref.service_catalog + self.service_catalog_mock.reset_mock() + self.app.client_manager.auth_ref.project_id = identity_fakes.project_id + + +class TestQuotaSet(TestQuota): + + def setUp(self): + super(TestQuotaSet, self).setUp() + + self.quotas_mock.find.return_value = FakeQuotaResource( + None, + copy.deepcopy(compute_fakes.QUOTA), + loaded=True, + ) + + self.quotas_mock.update.return_value = FakeQuotaResource( + None, + copy.deepcopy(compute_fakes.QUOTA), + loaded=True, + ) + + self.volume_quotas_mock.find.return_value = FakeQuotaResource( + None, + copy.deepcopy(compute_fakes.QUOTA), + loaded=True, + ) + + self.volume_quotas_mock.update.return_value = FakeQuotaResource( + None, + copy.deepcopy(compute_fakes.QUOTA), + loaded=True, + ) + + self.projects_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.PROJECT), + loaded=True, + ) + + self.network_mock = self.app.client_manager.network + self.network_mock.update_quota = mock.Mock() + + self.cmd = quota.SetQuota(self.app, None) + + def test_quota_set(self): + arglist = [ + '--floating-ips', str(compute_fakes.floating_ip_num), + '--fixed-ips', str(compute_fakes.fix_ip_num), + '--injected-files', str(compute_fakes.injected_file_num), + '--injected-file-size', str(compute_fakes.injected_file_size_num), + '--injected-path-size', str(compute_fakes.injected_path_size_num), + '--key-pairs', str(compute_fakes.key_pair_num), + '--cores', str(compute_fakes.core_num), + '--ram', str(compute_fakes.ram_num), + '--instances', str(compute_fakes.instance_num), + '--properties', str(compute_fakes.property_num), + '--secgroup-rules', str(compute_fakes.secgroup_rule_num), + '--secgroups', str(compute_fakes.secgroup_num), + '--server-groups', str(compute_fakes.servgroup_num), + '--server-group-members', str(compute_fakes.servgroup_members_num), + identity_fakes.project_name, + ] + verifylist = [ + ('floating_ips', compute_fakes.floating_ip_num), + ('fixed_ips', compute_fakes.fix_ip_num), + ('injected_files', compute_fakes.injected_file_num), + ('injected_file_content_bytes', + compute_fakes.injected_file_size_num), + ('injected_file_path_bytes', compute_fakes.injected_path_size_num), + ('key_pairs', compute_fakes.key_pair_num), + ('cores', compute_fakes.core_num), + ('ram', compute_fakes.ram_num), + ('instances', compute_fakes.instance_num), + ('metadata_items', compute_fakes.property_num), + ('security_group_rules', compute_fakes.secgroup_rule_num), + ('security_groups', compute_fakes.secgroup_num), + ('server_groups', compute_fakes.servgroup_num), + ('server_group_members', compute_fakes.servgroup_members_num), + ('project', identity_fakes.project_name), + ] + + self.app.client_manager.network_endpoint_enabled = False + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + kwargs = { + 'floating_ips': compute_fakes.floating_ip_num, + 'fixed_ips': compute_fakes.fix_ip_num, + 'injected_files': compute_fakes.injected_file_num, + 'injected_file_content_bytes': + compute_fakes.injected_file_size_num, + 'injected_file_path_bytes': compute_fakes.injected_path_size_num, + 'key_pairs': compute_fakes.key_pair_num, + 'cores': compute_fakes.core_num, + 'ram': compute_fakes.ram_num, + 'instances': compute_fakes.instance_num, + 'metadata_items': compute_fakes.property_num, + 'security_group_rules': compute_fakes.secgroup_rule_num, + 'security_groups': compute_fakes.secgroup_num, + 'server_groups': compute_fakes.servgroup_num, + 'server_group_members': compute_fakes.servgroup_members_num, + } + + self.quotas_mock.update.assert_called_with( + identity_fakes.project_id, + **kwargs + ) + + def test_quota_set_volume(self): + arglist = [ + '--gigabytes', str(compute_fakes.floating_ip_num), + '--snapshots', str(compute_fakes.fix_ip_num), + '--volumes', str(compute_fakes.injected_file_num), + identity_fakes.project_name, + ] + verifylist = [ + ('gigabytes', compute_fakes.floating_ip_num), + ('snapshots', compute_fakes.fix_ip_num), + ('volumes', compute_fakes.injected_file_num), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + kwargs = { + 'gigabytes': compute_fakes.floating_ip_num, + 'snapshots': compute_fakes.fix_ip_num, + 'volumes': compute_fakes.injected_file_num, + } + + self.volume_quotas_mock.update.assert_called_with( + identity_fakes.project_id, + **kwargs + ) + + def test_quota_set_network(self): + arglist = [ + '--subnets', str(network_fakes.QUOTA['subnet']), + '--networks', str(network_fakes.QUOTA['network']), + '--floating-ips', str(network_fakes.QUOTA['floatingip']), + '--subnetpools', str(network_fakes.QUOTA['subnetpool']), + '--secgroup-rules', + str(network_fakes.QUOTA['security_group_rule']), + '--secgroups', str(network_fakes.QUOTA['security_group']), + '--routers', str(network_fakes.QUOTA['router']), + '--rbac-policies', str(network_fakes.QUOTA['rbac_policy']), + '--ports', str(network_fakes.QUOTA['port']), + '--vips', str(network_fakes.QUOTA['vip']), + '--members', str(network_fakes.QUOTA['member']), + '--health-monitors', str(network_fakes.QUOTA['health_monitor']), + identity_fakes.project_name, + ] + verifylist = [ + ('subnet', network_fakes.QUOTA['subnet']), + ('network', network_fakes.QUOTA['network']), + ('floatingip', network_fakes.QUOTA['floatingip']), + ('subnetpool', network_fakes.QUOTA['subnetpool']), + ('security_group_rule', + network_fakes.QUOTA['security_group_rule']), + ('security_group', network_fakes.QUOTA['security_group']), + ('router', network_fakes.QUOTA['router']), + ('rbac_policy', network_fakes.QUOTA['rbac_policy']), + ('port', network_fakes.QUOTA['port']), + ('vip', network_fakes.QUOTA['vip']), + ('member', network_fakes.QUOTA['member']), + ('health_monitor', network_fakes.QUOTA['health_monitor']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + kwargs = { + 'subnet': network_fakes.QUOTA['subnet'], + 'network': network_fakes.QUOTA['network'], + 'floatingip': network_fakes.QUOTA['floatingip'], + 'subnetpool': network_fakes.QUOTA['subnetpool'], + 'security_group_rule': + network_fakes.QUOTA['security_group_rule'], + 'security_group': network_fakes.QUOTA['security_group'], + 'router': network_fakes.QUOTA['router'], + 'rbac_policy': network_fakes.QUOTA['rbac_policy'], + 'port': network_fakes.QUOTA['port'], + 'vip': network_fakes.QUOTA['vip'], + 'member': network_fakes.QUOTA['member'], + 'health_monitor': network_fakes.QUOTA['health_monitor'], + } + self.network_mock.update_quota.assert_called_with( + identity_fakes.project_id, + **kwargs + ) + + +class TestQuotaShow(TestQuota): + + def setUp(self): + super(TestQuotaShow, self).setUp() + + self.quotas_mock.get.return_value = FakeQuotaResource( + None, + copy.deepcopy(compute_fakes.QUOTA), + loaded=True, + ) + + self.quotas_mock.defaults.return_value = FakeQuotaResource( + None, + copy.deepcopy(compute_fakes.QUOTA), + loaded=True, + ) + + self.volume_quotas_mock.get.return_value = FakeQuotaResource( + None, + copy.deepcopy(compute_fakes.QUOTA), + loaded=True, + ) + + self.volume_quotas_mock.defaults.return_value = FakeQuotaResource( + None, + copy.deepcopy(compute_fakes.QUOTA), + loaded=True, + ) + + fake_network_endpoint = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.ENDPOINT), + loaded=True, + ) + + self.service_catalog_mock.get_endpoints.return_value = { + 'network': fake_network_endpoint + } + + self.quotas_class_mock.get.return_value = FakeQuotaResource( + None, + copy.deepcopy(compute_fakes.QUOTA), + loaded=True, + ) + + self.volume_quotas_class_mock.get.return_value = FakeQuotaResource( + None, + copy.deepcopy(compute_fakes.QUOTA), + loaded=True, + ) + + self.projects_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.PROJECT), + loaded=True, + ) + + self.app.client_manager.network = network_fakes.FakeNetworkV2Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + self.network = self.app.client_manager.network + self.network.get_quota = mock.Mock(return_value=network_fakes.QUOTA) + + self.cmd = quota.ShowQuota(self.app, None) + + def test_quota_show(self): + arglist = [ + identity_fakes.project_name, + ] + verifylist = [ + ('project', identity_fakes.project_name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + self.quotas_mock.get.assert_called_with(identity_fakes.project_id) + self.volume_quotas_mock.get.assert_called_with( + identity_fakes.project_id) + self.network.get_quota.assert_called_with(identity_fakes.project_id) + + def test_quota_show_with_default(self): + arglist = [ + '--default', + identity_fakes.project_name, + ] + verifylist = [ + ('default', True), + ('project', identity_fakes.project_name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + self.quotas_mock.defaults.assert_called_with(identity_fakes.project_id) + self.volume_quotas_mock.defaults.assert_called_with( + identity_fakes.project_id) + + def test_quota_show_with_class(self): + arglist = [ + '--class', + identity_fakes.project_name, + ] + verifylist = [ + ('quota_class', True), + ('project', identity_fakes.project_name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + self.quotas_class_mock.get.assert_called_with( + identity_fakes.project_id) + self.volume_quotas_class_mock.get.assert_called_with( + identity_fakes.project_id) + + def test_quota_show_no_project(self): + parsed_args = self.check_parser(self.cmd, [], []) + + self.cmd.take_action(parsed_args) + + self.quotas_mock.get.assert_called_with(identity_fakes.project_id) + self.volume_quotas_mock.get.assert_called_with( + identity_fakes.project_id) + self.network.get_quota.assert_called_with(identity_fakes.project_id) diff --git a/openstackclient/tests/unit/compute/__init__.py b/openstackclient/tests/unit/compute/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/openstackclient/tests/unit/compute/__init__.py diff --git a/openstackclient/tests/unit/compute/v2/__init__.py b/openstackclient/tests/unit/compute/v2/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/openstackclient/tests/unit/compute/v2/__init__.py diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py new file mode 100644 index 00000000..0e3d47ba --- /dev/null +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -0,0 +1,1249 @@ +# Copyright 2013 Nebula 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 copy +import mock +import uuid + +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes +from openstackclient.tests.unit.image.v2 import fakes as image_fakes +from openstackclient.tests.unit.network.v2 import fakes as network_fakes +from openstackclient.tests.unit import utils +from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes + +floating_ip_num = 100 +fix_ip_num = 100 +injected_file_num = 100 +injected_file_size_num = 10240 +injected_path_size_num = 255 +key_pair_num = 100 +core_num = 20 +ram_num = 51200 +instance_num = 10 +property_num = 128 +secgroup_rule_num = 20 +secgroup_num = 10 +servgroup_num = 10 +servgroup_members_num = 10 +project_name = 'project_test' +QUOTA = { + 'project': project_name, + 'floating-ips': floating_ip_num, + 'fix-ips': fix_ip_num, + 'injected-files': injected_file_num, + 'injected-file-size': injected_file_size_num, + 'injected-path-size': injected_path_size_num, + 'key-pairs': key_pair_num, + 'cores': core_num, + 'ram': ram_num, + 'instances': instance_num, + 'properties': property_num, + 'secgroup_rules': secgroup_rule_num, + 'secgroups': secgroup_num, + 'server-groups': servgroup_num, + 'server-group-members': servgroup_members_num +} + +QUOTA_columns = tuple(sorted(QUOTA)) +QUOTA_data = tuple(QUOTA[x] for x in sorted(QUOTA)) + + +class FakeAggregate(object): + """Fake one aggregate.""" + + @staticmethod + def create_one_aggregate(attrs=None): + """Create a fake aggregate. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id and other attributes + """ + attrs = attrs or {} + + # Set default attribute + aggregate_info = { + "name": "aggregate-name-" + uuid.uuid4().hex, + "availability_zone": "ag_zone", + "hosts": [], + "id": "aggregate-id-" + uuid.uuid4().hex, + "metadata": { + "availability_zone": "ag_zone", + } + } + + # Overwrite default attributes. + aggregate_info.update(attrs) + + aggregate = fakes.FakeResource( + info=copy.deepcopy(aggregate_info), + loaded=True) + return aggregate + + @staticmethod + def create_aggregates(attrs=None, count=2): + """Create multiple fake aggregates. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of aggregates to fake + :return: + A list of FakeResource objects faking the aggregates + """ + aggregates = [] + for i in range(0, count): + aggregates.append(FakeAggregate.create_one_aggregate(attrs)) + + return aggregates + + @staticmethod + def get_aggregates(aggregates=None, count=2): + """Get an iterable MagicMock object with a list of faked aggregates. + + If aggregates list is provided, then initialize the Mock object + with the list. Otherwise create one. + + :param List aggregates: + A list of FakeResource objects faking aggregates + :param int count: + The number of aggregates to fake + :return: + An iterable Mock object with side_effect set to a list of faked + aggregates + """ + if aggregates is None: + aggregates = FakeAggregate.create_aggregates(count) + return mock.MagicMock(side_effect=aggregates) + + +class FakeComputev2Client(object): + + def __init__(self, **kwargs): + self.agents = mock.Mock() + self.agents.resource_class = fakes.FakeResource(None, {}) + + self.aggregates = mock.Mock() + self.aggregates.resource_class = fakes.FakeResource(None, {}) + + self.availability_zones = mock.Mock() + self.availability_zones.resource_class = fakes.FakeResource(None, {}) + + self.images = mock.Mock() + self.images.resource_class = fakes.FakeResource(None, {}) + + self.servers = mock.Mock() + self.servers.resource_class = fakes.FakeResource(None, {}) + + self.services = mock.Mock() + self.services.resource_class = fakes.FakeResource(None, {}) + + self.extensions = mock.Mock() + self.extensions.resource_class = fakes.FakeResource(None, {}) + + self.flavors = mock.Mock() + self.flavors.resource_class = fakes.FakeResource(None, {}) + + self.flavor_access = mock.Mock() + self.flavor_access.resource_class = fakes.FakeResource(None, {}) + + self.quotas = mock.Mock() + self.quotas.resource_class = fakes.FakeResource(None, {}) + + self.quota_classes = mock.Mock() + self.quota_classes.resource_class = fakes.FakeResource(None, {}) + + self.volumes = mock.Mock() + self.volumes.resource_class = fakes.FakeResource(None, {}) + + self.hypervisors = mock.Mock() + self.hypervisors.resource_class = fakes.FakeResource(None, {}) + + self.hypervisors_stats = mock.Mock() + self.hypervisors_stats.resource_class = fakes.FakeResource(None, {}) + + self.security_groups = mock.Mock() + self.security_groups.resource_class = fakes.FakeResource(None, {}) + + self.security_group_rules = mock.Mock() + self.security_group_rules.resource_class = fakes.FakeResource(None, {}) + + self.floating_ips = mock.Mock() + self.floating_ips.resource_class = fakes.FakeResource(None, {}) + + self.floating_ip_pools = mock.Mock() + self.floating_ip_pools.resource_class = fakes.FakeResource(None, {}) + + self.networks = mock.Mock() + self.networks.resource_class = fakes.FakeResource(None, {}) + + self.keypairs = mock.Mock() + self.keypairs.resource_class = fakes.FakeResource(None, {}) + + self.hosts = mock.Mock() + self.hosts.resource_class = fakes.FakeResource(None, {}) + + self.server_groups = mock.Mock() + self.server_groups.resource_class = fakes.FakeResource(None, {}) + + self.auth_token = kwargs['token'] + + self.management_url = kwargs['endpoint'] + + +class TestComputev2(utils.TestCommand): + + def setUp(self): + super(TestComputev2, self).setUp() + + self.app.client_manager.compute = FakeComputev2Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + + self.app.client_manager.identity = identity_fakes.FakeIdentityv2Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + + self.app.client_manager.image = image_fakes.FakeImagev2Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + + self.app.client_manager.network = network_fakes.FakeNetworkV2Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + + self.app.client_manager.volume = volume_fakes.FakeVolumeClient( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + + +class FakeAgent(object): + """Fake one or more agent.""" + + @staticmethod + def create_one_agent(attrs=None): + """Create a fake agent. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with agent_id, os, and so on + """ + + attrs = attrs or {} + + # set default attributes. + agent_info = { + 'agent_id': 'agent-id-' + uuid.uuid4().hex, + 'os': 'agent-os-' + uuid.uuid4().hex, + 'architecture': 'agent-architecture', + 'version': '8.0', + 'url': 'http://127.0.0.1', + 'md5hash': 'agent-md5hash', + 'hypervisor': 'hypervisor', + } + + # Overwrite default attributes. + agent_info.update(attrs) + + agent = fakes.FakeResource(info=copy.deepcopy(agent_info), + loaded=True) + return agent + + @staticmethod + def create_agents(attrs=None, count=2): + """Create multiple fake agents. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of agents to fake + :return: + A list of FakeResource objects faking the agents + """ + agents = [] + for i in range(0, count): + agents.append(FakeAgent.create_one_agent(attrs)) + + return agents + + +class FakeExtension(object): + """Fake one or more extension.""" + + @staticmethod + def create_one_extension(attrs=None): + """Create a fake extension. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object with name, namespace, etc. + """ + attrs = attrs or {} + + # Set default attributes. + extension_info = { + 'name': 'name-' + uuid.uuid4().hex, + 'namespace': ( + 'http://docs.openstack.org/compute/ext/multinic/api/v1.1'), + 'description': 'description-' + uuid.uuid4().hex, + 'updated': '2014-01-07T12:00:0-00:00', + 'alias': 'NMN', + 'links': ('[{"href":' + '"https://github.com/openstack/compute-api", "type":' + ' "text/html", "rel": "describedby"}]') + } + + # Overwrite default attributes. + extension_info.update(attrs) + + extension = fakes.FakeResource( + info=copy.deepcopy(extension_info), + loaded=True) + return extension + + +class FakeHypervisor(object): + """Fake one or more hypervisor.""" + + @staticmethod + def create_one_hypervisor(attrs=None): + """Create a fake hypervisor. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, hypervisor_hostname, and so on + """ + attrs = attrs or {} + + # Set default attributes. + hypervisor_info = { + 'id': 'hypervisor-id-' + uuid.uuid4().hex, + 'hypervisor_hostname': 'hypervisor-hostname-' + uuid.uuid4().hex, + 'status': 'enabled', + 'host_ip': '192.168.0.10', + 'cpu_info': { + 'aaa': 'aaa', + }, + 'free_disk_gb': 50, + 'hypervisor_version': 2004001, + 'disk_available_least': 50, + 'local_gb': 50, + 'free_ram_mb': 1024, + 'service': { + 'host': 'aaa', + 'disabled_reason': None, + 'id': 1, + }, + 'vcpus_used': 0, + 'hypervisor_type': 'QEMU', + 'local_gb_used': 0, + 'vcpus': 4, + 'memory_mb_used': 512, + 'memory_mb': 1024, + 'current_workload': 0, + 'state': 'up', + 'running_vms': 0, + } + + # Overwrite default attributes. + hypervisor_info.update(attrs) + + hypervisor = fakes.FakeResource(info=copy.deepcopy(hypervisor_info), + loaded=True) + return hypervisor + + @staticmethod + def create_hypervisors(attrs=None, count=2): + """Create multiple fake hypervisors. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of hypervisors to fake + :return: + A list of FakeResource objects faking the hypervisors + """ + hypervisors = [] + for i in range(0, count): + hypervisors.append(FakeHypervisor.create_one_hypervisor(attrs)) + + return hypervisors + + +class FakeHypervisorStats(object): + """Fake one or more hypervisor stats.""" + + @staticmethod + def create_one_hypervisor_stats(attrs=None): + """Create a fake hypervisor stats. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with count, current_workload, and so on + """ + attrs = attrs or {} + + # Set default attributes. + stats_info = { + 'count': 2, + 'current_workload': 0, + 'disk_available_least': 50, + 'free_disk_gb': 100, + 'free_ram_mb': 23000, + 'local_gb': 100, + 'local_gb_used': 0, + 'memory_mb': 23800, + 'memory_mb_used': 1400, + 'running_vms': 3, + 'vcpus': 8, + 'vcpus_used': 3, + } + + # Overwrite default attributes. + stats_info.update(attrs) + + # Set default method. + hypervisor_stats_method = {'to_dict': stats_info} + + hypervisor_stats = fakes.FakeResource( + info=copy.deepcopy(stats_info), + methods=copy.deepcopy(hypervisor_stats_method), + loaded=True) + return hypervisor_stats + + @staticmethod + def create_hypervisors_stats(attrs=None, count=2): + """Create multiple fake hypervisors stats. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of hypervisors to fake + :return: + A list of FakeResource objects faking the hypervisors + """ + hypervisors = [] + for i in range(0, count): + hypervisors.append( + FakeHypervisorStats.create_one_hypervisor_stats(attrs)) + + return hypervisors + + +class FakeSecurityGroup(object): + """Fake one or more security groups.""" + + @staticmethod + def create_one_security_group(attrs=None): + """Create a fake security group. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, name, etc. + """ + attrs = attrs or {} + + # Set default attributes. + security_group_attrs = { + 'id': 'security-group-id-' + uuid.uuid4().hex, + 'name': 'security-group-name-' + uuid.uuid4().hex, + 'description': 'security-group-description-' + uuid.uuid4().hex, + 'tenant_id': 'project-id-' + uuid.uuid4().hex, + 'rules': [], + } + + # Overwrite default attributes. + security_group_attrs.update(attrs) + + security_group = fakes.FakeResource( + info=copy.deepcopy(security_group_attrs), + loaded=True) + return security_group + + @staticmethod + def create_security_groups(attrs=None, count=2): + """Create multiple fake security groups. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of security groups to fake + :return: + A list of FakeResource objects faking the security groups + """ + security_groups = [] + for i in range(0, count): + security_groups.append( + FakeSecurityGroup.create_one_security_group(attrs)) + + return security_groups + + @staticmethod + def get_security_groups(security_groups=None, count=2): + """Get an iterable MagicMock object with a list of faked security groups. + + If security groups list is provided, then initialize the Mock object + with the list. Otherwise create one. + + :param List security groups: + A list of FakeResource objects faking security groups + :param int count: + The number of security groups to fake + :return: + An iterable Mock object with side_effect set to a list of faked + security groups + """ + if security_groups is None: + security_groups = FakeSecurityGroup.create_security_groups(count) + return mock.MagicMock(side_effect=security_groups) + + +class FakeSecurityGroupRule(object): + """Fake one or more security group rules.""" + + @staticmethod + def create_one_security_group_rule(attrs=None): + """Create a fake security group rule. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, etc. + """ + attrs = attrs or {} + + # Set default attributes. + security_group_rule_attrs = { + 'from_port': 0, + 'group': {}, + 'id': 'security-group-rule-id-' + uuid.uuid4().hex, + 'ip_protocol': 'tcp', + 'ip_range': {'cidr': '0.0.0.0/0'}, + 'parent_group_id': 'security-group-id-' + uuid.uuid4().hex, + 'to_port': 0, + } + + # Overwrite default attributes. + security_group_rule_attrs.update(attrs) + + security_group_rule = fakes.FakeResource( + info=copy.deepcopy(security_group_rule_attrs), + loaded=True) + return security_group_rule + + @staticmethod + def create_security_group_rules(attrs=None, count=2): + """Create multiple fake security group rules. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of security group rules to fake + :return: + A list of FakeResource objects faking the security group rules + """ + security_group_rules = [] + for i in range(0, count): + security_group_rules.append( + FakeSecurityGroupRule.create_one_security_group_rule(attrs)) + + return security_group_rules + + +class FakeServer(object): + """Fake one or more compute servers.""" + + @staticmethod + def create_one_server(attrs=None, methods=None): + """Create a fake server. + + :param Dictionary attrs: + A dictionary with all attributes + :param Dictionary methods: + A dictionary with all methods + :return: + A FakeResource object, with id, name, metadata, and so on + """ + attrs = attrs or {} + methods = methods or {} + + # Set default attributes. + server_info = { + 'id': 'server-id-' + uuid.uuid4().hex, + 'name': 'server-name-' + uuid.uuid4().hex, + 'metadata': {}, + 'image': { + 'id': 'image-id-' + uuid.uuid4().hex, + }, + 'flavor': { + 'id': 'flavor-id-' + uuid.uuid4().hex, + }, + 'OS-EXT-STS:power_state': 1, + } + + # Overwrite default attributes. + server_info.update(attrs) + + server = fakes.FakeResource(info=copy.deepcopy(server_info), + methods=methods, + loaded=True) + return server + + @staticmethod + def create_servers(attrs=None, methods=None, count=2): + """Create multiple fake servers. + + :param Dictionary attrs: + A dictionary with all attributes + :param Dictionary methods: + A dictionary with all methods + :param int count: + The number of servers to fake + :return: + A list of FakeResource objects faking the servers + """ + servers = [] + for i in range(0, count): + servers.append(FakeServer.create_one_server(attrs, methods)) + + return servers + + @staticmethod + def get_servers(servers=None, count=2): + """Get an iterable MagicMock object with a list of faked servers. + + If servers list is provided, then initialize the Mock object with the + list. Otherwise create one. + + :param List servers: + A list of FakeResource objects faking servers + :param int count: + The number of servers to fake + :return: + An iterable Mock object with side_effect set to a list of faked + servers + """ + if servers is None: + servers = FakeServer.create_servers(count) + return mock.MagicMock(side_effect=servers) + + +class FakeService(object): + """Fake one or more services.""" + + @staticmethod + def create_one_service(attrs=None): + """Create a fake service. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, host, binary, and so on + """ + attrs = attrs or {} + + # Set default attributes. + service_info = { + 'id': 'id-' + uuid.uuid4().hex, + 'host': 'host-' + uuid.uuid4().hex, + 'binary': 'binary-' + uuid.uuid4().hex, + 'status': 'enabled', + 'zone': 'zone-' + uuid.uuid4().hex, + 'state': 'state-' + uuid.uuid4().hex, + 'updated_at': 'time-' + uuid.uuid4().hex, + 'disabled_reason': 'earthquake', + } + + # Overwrite default attributes. + service_info.update(attrs) + + service = fakes.FakeResource(info=copy.deepcopy(service_info), + loaded=True) + + return service + + @staticmethod + def create_services(attrs=None, count=2): + """Create multiple fake services. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of services to fake + :return: + A list of FakeResource objects faking the services + """ + services = [] + for i in range(0, count): + services.append(FakeService.create_one_service(attrs)) + + return services + + +class FakeFlavor(object): + """Fake one or more flavors.""" + + @staticmethod + def create_one_flavor(attrs=None): + """Create a fake flavor. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, name, ram, vcpus, and so on + """ + attrs = attrs or {} + + # Set default attributes. + flavor_info = { + 'id': 'flavor-id-' + uuid.uuid4().hex, + 'name': 'flavor-name-' + uuid.uuid4().hex, + 'ram': 8192, + 'vcpus': 4, + 'disk': 128, + 'swap': 0, + 'rxtx_factor': 1.0, + 'OS-FLV-DISABLED:disabled': False, + 'os-flavor-access:is_public': True, + 'OS-FLV-EXT-DATA:ephemeral': 0, + 'properties': {'property': 'value'}, + } + + # Overwrite default attributes. + flavor_info.update(attrs) + + # Set default methods. + flavor_methods = { + 'set_keys': None, + 'unset_keys': None, + 'get_keys': {'property': 'value'}, + } + + flavor = fakes.FakeResource(info=copy.deepcopy(flavor_info), + methods=flavor_methods, + loaded=True) + + # Set attributes with special mappings in nova client. + flavor.disabled = flavor_info['OS-FLV-DISABLED:disabled'] + flavor.is_public = flavor_info['os-flavor-access:is_public'] + flavor.ephemeral = flavor_info['OS-FLV-EXT-DATA:ephemeral'] + + return flavor + + @staticmethod + def create_flavors(attrs=None, count=2): + """Create multiple fake flavors. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of flavors to fake + :return: + A list of FakeResource objects faking the flavors + """ + flavors = [] + for i in range(0, count): + flavors.append(FakeFlavor.create_one_flavor(attrs)) + + return flavors + + @staticmethod + def get_flavors(flavors=None, count=2): + """Get an iterable MagicMock object with a list of faked flavors. + + If flavors list is provided, then initialize the Mock object with the + list. Otherwise create one. + + :param List flavors: + A list of FakeResource objects faking flavors + :param int count: + The number of flavors to fake + :return: + An iterable Mock object with side_effect set to a list of faked + flavors + """ + if flavors is None: + flavors = FakeFlavor.create_flavors(count) + return mock.MagicMock(side_effect=flavors) + + +class FakeFlavorAccess(object): + """Fake one or more flavor accesses.""" + + @staticmethod + def create_one_flavor_access(attrs=None): + """Create a fake flavor access. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with flavor_id, tenat_id + """ + attrs = attrs or {} + + # Set default attributes. + flavor_access_info = { + 'flavor_id': 'flavor-id-' + uuid.uuid4().hex, + 'tenant_id': 'tenant-id-' + uuid.uuid4().hex, + } + + # Overwrite default attributes. + flavor_access_info.update(attrs) + + flavor_access = fakes.FakeResource( + info=copy.deepcopy(flavor_access_info), loaded=True) + + return flavor_access + + +class FakeKeypair(object): + """Fake one or more keypairs.""" + + @staticmethod + def create_one_keypair(attrs=None, no_pri=False): + """Create a fake keypair + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, name, fingerprint, and so on + """ + attrs = attrs or {} + + # Set default attributes. + keypair_info = { + 'name': 'keypair-name-' + uuid.uuid4().hex, + 'fingerprint': 'dummy', + 'public_key': 'dummy', + 'user_id': 'user' + } + if not no_pri: + keypair_info['private_key'] = 'private_key' + + # Overwrite default attributes. + keypair_info.update(attrs) + + keypair = fakes.FakeResource(info=copy.deepcopy(keypair_info), + loaded=True) + + return keypair + + @staticmethod + def create_keypairs(attrs=None, count=2): + """Create multiple fake keypairs. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of keypairs to fake + :return: + A list of FakeResource objects faking the keypairs + """ + + keypairs = [] + for i in range(0, count): + keypairs.append(FakeKeypair.create_one_keypair(attrs)) + + return keypairs + + @staticmethod + def get_keypairs(keypairs=None, count=2): + """Get an iterable MagicMock object with a list of faked keypairs. + + If keypairs list is provided, then initialize the Mock object with the + list. Otherwise create one. + + :param List keypairs: + A list of FakeResource objects faking keypairs + :param int count: + The number of keypairs to fake + :return: + An iterable Mock object with side_effect set to a list of faked + keypairs + """ + if keypairs is None: + keypairs = FakeKeypair.create_keypairs(count) + return mock.MagicMock(side_effect=keypairs) + + +class FakeAvailabilityZone(object): + """Fake one or more compute availability zones (AZs).""" + + @staticmethod + def create_one_availability_zone(attrs=None): + """Create a fake AZ. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object with zoneName, zoneState, etc. + """ + attrs = attrs or {} + + # Set default attributes. + host_name = uuid.uuid4().hex + service_name = uuid.uuid4().hex + service_updated_at = uuid.uuid4().hex + availability_zone = { + 'zoneName': uuid.uuid4().hex, + 'zoneState': {'available': True}, + 'hosts': {host_name: {service_name: { + 'available': True, + 'active': True, + 'updated_at': service_updated_at, + }}}, + } + + # Overwrite default attributes. + availability_zone.update(attrs) + + availability_zone = fakes.FakeResource( + info=copy.deepcopy(availability_zone), + loaded=True) + return availability_zone + + @staticmethod + def create_availability_zones(attrs=None, count=2): + """Create multiple fake AZs. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of AZs to fake + :return: + A list of FakeResource objects faking the AZs + """ + availability_zones = [] + for i in range(0, count): + availability_zone = \ + FakeAvailabilityZone.create_one_availability_zone(attrs) + availability_zones.append(availability_zone) + + return availability_zones + + +class FakeFloatingIP(object): + """Fake one or more floating ip.""" + + @staticmethod + def create_one_floating_ip(attrs=None): + """Create a fake floating ip. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, ip, and so on + """ + attrs = attrs or {} + + # Set default attributes. + floating_ip_attrs = { + 'id': 'floating-ip-id-' + uuid.uuid4().hex, + 'ip': '1.0.9.0', + 'fixed_ip': '2.0.9.0', + 'instance_id': 'server-id-' + uuid.uuid4().hex, + 'pool': 'public', + } + + # Overwrite default attributes. + floating_ip_attrs.update(attrs) + + floating_ip = fakes.FakeResource( + info=copy.deepcopy(floating_ip_attrs), + loaded=True) + + return floating_ip + + @staticmethod + def create_floating_ips(attrs=None, count=2): + """Create multiple fake floating ips. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of floating ips to fake + :return: + A list of FakeResource objects faking the floating ips + """ + floating_ips = [] + for i in range(0, count): + floating_ips.append(FakeFloatingIP.create_one_floating_ip(attrs)) + return floating_ips + + @staticmethod + def get_floating_ips(floating_ips=None, count=2): + """Get an iterable MagicMock object with a list of faked floating ips. + + If floating_ips list is provided, then initialize the Mock object + with the list. Otherwise create one. + + :param List floating ips: + A list of FakeResource objects faking floating ips + :param int count: + The number of floating ips to fake + :return: + An iterable Mock object with side_effect set to a list of faked + floating ips + """ + if floating_ips is None: + floating_ips = FakeFloatingIP.create_floating_ips(count) + return mock.MagicMock(side_effect=floating_ips) + + +class FakeFloatingIPPool(object): + """Fake one or more floating ip pools.""" + + @staticmethod + def create_one_floating_ip_pool(attrs=None): + """Create a fake floating ip pool. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with name, etc + """ + if attrs is None: + attrs = {} + + # Set default attributes. + floating_ip_pool_attrs = { + 'name': 'floating-ip-pool-name-' + uuid.uuid4().hex, + } + + # Overwrite default attributes. + floating_ip_pool_attrs.update(attrs) + + floating_ip_pool = fakes.FakeResource( + info=copy.deepcopy(floating_ip_pool_attrs), + loaded=True) + + return floating_ip_pool + + @staticmethod + def create_floating_ip_pools(attrs=None, count=2): + """Create multiple fake floating ip pools. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of floating ip pools to fake + :return: + A list of FakeResource objects faking the floating ip pools + """ + floating_ip_pools = [] + for i in range(0, count): + floating_ip_pools.append( + FakeFloatingIPPool.create_one_floating_ip_pool(attrs) + ) + return floating_ip_pools + + +class FakeNetwork(object): + """Fake one or more networks.""" + + @staticmethod + def create_one_network(attrs=None): + """Create a fake network. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, label, cidr and so on + """ + attrs = attrs or {} + + # Set default attributes. + network_attrs = { + 'bridge': 'br100', + 'bridge_interface': None, + 'broadcast': '10.0.0.255', + 'cidr': '10.0.0.0/24', + 'cidr_v6': None, + 'created_at': '2016-02-11T11:17:37.000000', + 'deleted': False, + 'deleted_at': None, + 'dhcp_server': '10.0.0.1', + 'dhcp_start': '10.0.0.2', + 'dns1': '8.8.4.4', + 'dns2': None, + 'enable_dhcp': True, + 'gateway': '10.0.0.1', + 'gateway_v6': None, + 'host': None, + 'id': 'network-id-' + uuid.uuid4().hex, + 'injected': False, + 'label': 'network-label-' + uuid.uuid4().hex, + 'mtu': None, + 'multi_host': False, + 'netmask': '255.255.255.0', + 'netmask_v6': None, + 'priority': None, + 'project_id': 'project-id-' + uuid.uuid4().hex, + 'rxtx_base': None, + 'share_address': False, + 'updated_at': None, + 'vlan': None, + 'vpn_private_address': None, + 'vpn_public_address': None, + 'vpn_public_port': None, + } + + # Overwrite default attributes. + network_attrs.update(attrs) + + network = fakes.FakeResource(info=copy.deepcopy(network_attrs), + loaded=True) + + return network + + @staticmethod + def create_networks(attrs=None, count=2): + """Create multiple fake networks. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of networks to fake + :return: + A list of FakeResource objects faking the networks + """ + networks = [] + for i in range(0, count): + networks.append(FakeNetwork.create_one_network(attrs)) + + return networks + + @staticmethod + def get_networks(networks=None, count=2): + """Get an iterable MagicMock object with a list of faked networks. + + If networks list is provided, then initialize the Mock object with the + list. Otherwise create one. + + :param List networks: + A list of FakeResource objects faking networks + :param int count: + The number of networks to fake + :return: + An iterable Mock object with side_effect set to a list of faked + networks + """ + if networks is None: + networks = FakeNetwork.create_networks(count=count) + return mock.Mock(side_effect=networks) + + +class FakeHost(object): + """Fake one host.""" + + @staticmethod + def create_one_host(attrs=None): + """Create a fake host. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with uuid and other attributes + """ + attrs = attrs or {} + + # Set default attributes. + host_info = { + "service_id": 1, + "host": "host1", + "uuid": 'host-id-' + uuid.uuid4().hex, + "vcpus": 10, + "memory_mb": 100, + "local_gb": 100, + "vcpus_used": 5, + "memory_mb_used": 50, + "local_gb_used": 10, + "hypervisor_type": "xen", + "hypervisor_version": 1, + "hypervisor_hostname": "devstack1", + "free_ram_mb": 50, + "free_disk_gb": 50, + "current_workload": 10, + "running_vms": 1, + "cpu_info": "", + "disk_available_least": 1, + "host_ip": "10.10.10.10", + "supported_instances": "", + "metrics": "", + "pci_stats": "", + "extra_resources": "", + "stats": "", + "numa_topology": "", + "ram_allocation_ratio": 1.0, + "cpu_allocation_ratio": 1.0, + "zone": 'zone-' + uuid.uuid4().hex, + "host_name": 'name-' + uuid.uuid4().hex, + "service": 'service-' + uuid.uuid4().hex, + "cpu": 4, + "disk_gb": 100, + 'project': 'project-' + uuid.uuid4().hex, + } + host_info.update(attrs) + host = fakes.FakeResource( + info=copy.deepcopy(host_info), + loaded=True) + return host + + +class FakeServerGroup(object): + """Fake one server group""" + + @staticmethod + def create_one_server_group(attrs=None): + """Create a fake server group + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id and other attributes + """ + if attrs is None: + attrs = {} + + # Set default attributes. + server_group_info = { + 'id': 'server-group-id-' + uuid.uuid4().hex, + 'members': [], + 'metadata': {}, + 'name': 'server-group-name-' + uuid.uuid4().hex, + 'policies': [], + 'project_id': 'server-group-project-id-' + uuid.uuid4().hex, + 'user_id': 'server-group-user-id-' + uuid.uuid4().hex, + } + + # Overwrite default attributes. + server_group_info.update(attrs) + + server_group = fakes.FakeResource( + info=copy.deepcopy(server_group_info), + loaded=True) + return server_group diff --git a/openstackclient/tests/unit/compute/v2/test_agent.py b/openstackclient/tests/unit/compute/v2/test_agent.py new file mode 100644 index 00000000..169940e2 --- /dev/null +++ b/openstackclient/tests/unit/compute/v2/test_agent.py @@ -0,0 +1,320 @@ +# Copyright 2016 Easystack. 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 mock +from mock import call + +from osc_lib import exceptions + +from openstackclient.compute.v2 import agent +from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes +from openstackclient.tests.unit import utils as tests_utils + + +class TestAgent(compute_fakes.TestComputev2): + + attr = {} + attr['agent_id'] = 1 + fake_agent = compute_fakes.FakeAgent.create_one_agent(attr) + + columns = ( + 'agent_id', + 'architecture', + 'hypervisor', + 'md5hash', + 'os', + 'url', + 'version', + ) + + data = ( + fake_agent.agent_id, + fake_agent.architecture, + fake_agent.hypervisor, + fake_agent.md5hash, + fake_agent.os, + fake_agent.url, + fake_agent.version, + ) + + def setUp(self): + super(TestAgent, self).setUp() + + self.agents_mock = self.app.client_manager.compute.agents + self.agents_mock.reset_mock() + + +class TestAgentCreate(TestAgent): + + def setUp(self): + super(TestAgentCreate, self).setUp() + + self.agents_mock.create.return_value = self.fake_agent + self.cmd = agent.CreateAgent(self.app, None) + + def test_agent_create(self): + arglist = [ + self.fake_agent.os, + self.fake_agent.architecture, + self.fake_agent.version, + self.fake_agent.url, + self.fake_agent.md5hash, + self.fake_agent.hypervisor, + ] + + verifylist = [ + ('os', self.fake_agent.os), + ('architecture', self.fake_agent.architecture), + ('version', self.fake_agent.version), + ('url', self.fake_agent.url), + ('md5hash', self.fake_agent.md5hash), + ('hypervisor', self.fake_agent.hypervisor), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.agents_mock.create.assert_called_with(parsed_args.os, + parsed_args.architecture, + parsed_args.version, + parsed_args.url, + parsed_args.md5hash, + parsed_args.hypervisor) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestAgentDelete(TestAgent): + + fake_agents = compute_fakes.FakeAgent.create_agents(count=2) + + def setUp(self): + super(TestAgentDelete, self).setUp() + + self.agents_mock.get.return_value = self.fake_agents + self.cmd = agent.DeleteAgent(self.app, None) + + def test_delete_one_agent(self): + arglist = [ + self.fake_agents[0].agent_id + ] + + verifylist = [ + ('id', [self.fake_agents[0].agent_id]), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.agents_mock.delete.assert_called_with( + self.fake_agents[0].agent_id) + self.assertIsNone(result) + + def test_delete_multiple_agents(self): + arglist = [] + for n in self.fake_agents: + arglist.append(n.agent_id) + verifylist = [ + ('id', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + calls = [] + for n in self.fake_agents: + calls.append(call(n.agent_id)) + self.agents_mock.delete.assert_has_calls(calls) + self.assertIsNone(result) + + def test_delete_multiple_agents_exception(self): + arglist = [ + self.fake_agents[0].agent_id, + self.fake_agents[1].agent_id, + 'x-y-z', + ] + verifylist = [ + ('id', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + ret_delete = [ + None, + None, + exceptions.NotFound('404') + ] + self.agents_mock.delete = mock.Mock(side_effect=ret_delete) + + self.assertRaises(exceptions.CommandError, self.cmd.take_action, + parsed_args) + calls = [ + call(self.fake_agents[0].agent_id), + call(self.fake_agents[1].agent_id), + ] + self.agents_mock.delete.assert_has_calls(calls) + + def test_agent_delete_no_input(self): + arglist = [] + verifylist = None + self.assertRaises(tests_utils.ParserException, + self.check_parser, + self.cmd, + arglist, + verifylist) + + +class TestAgentList(TestAgent): + + agents = compute_fakes.FakeAgent.create_agents(count=3) + list_columns = ( + "Agent ID", + "Hypervisor", + "OS", + "Architecture", + "Version", + "Md5Hash", + "URL", + ) + + list_data = [] + for _agent in agents: + list_data.append(( + _agent.agent_id, + _agent.hypervisor, + _agent.os, + _agent.architecture, + _agent.version, + _agent.md5hash, + _agent.url, + )) + + def setUp(self): + + super(TestAgentList, self).setUp() + + self.agents_mock.list.return_value = self.agents + self.cmd = agent.ListAgent(self.app, None) + + def test_agent_list(self): + + arglist = [] + verifylist = [] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.list_columns, columns) + self.assertEqual(self.list_data, list(data)) + + def test_agent_list_with_hypervisor(self): + + arglist = [ + '--hypervisor', + 'hypervisor', + ] + verifylist = [ + ('hypervisor', 'hypervisor'), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.list_columns, columns) + self.assertEqual(self.list_data, list(data)) + + +class TestAgentSet(TestAgent): + + def setUp(self): + super(TestAgentSet, self).setUp() + + self.agents_mock.update.return_value = self.fake_agent + self.agents_mock.list.return_value = [self.fake_agent] + self.cmd = agent.SetAgent(self.app, None) + + def test_agent_set_nothing(self): + arglist = [ + '1', + ] + verifylist = [ + ('id', '1'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.agents_mock.update.assert_called_with(parsed_args.id, + self.fake_agent.version, + self.fake_agent.url, + self.fake_agent.md5hash) + self.assertIsNone(result) + + def test_agent_set_version(self): + arglist = [ + '1', + '--agent-version', 'new-version', + ] + + verifylist = [ + ('id', '1'), + ('version', 'new-version'), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.agents_mock.update.assert_called_with(parsed_args.id, + parsed_args.version, + self.fake_agent.url, + self.fake_agent.md5hash) + self.assertIsNone(result) + + def test_agent_set_url(self): + arglist = [ + '1', + '--url', 'new-url', + ] + + verifylist = [ + ('id', '1'), + ('url', 'new-url'), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.agents_mock.update.assert_called_with(parsed_args.id, + self.fake_agent.version, + parsed_args.url, + self.fake_agent.md5hash) + self.assertIsNone(result) + + def test_agent_set_md5hash(self): + arglist = [ + '1', + '--md5hash', 'new-md5hash', + ] + + verifylist = [ + ('id', '1'), + ('md5hash', 'new-md5hash'), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.agents_mock.update.assert_called_with(parsed_args.id, + self.fake_agent.version, + self.fake_agent.url, + parsed_args.md5hash) + self.assertIsNone(result) diff --git a/openstackclient/tests/unit/compute/v2/test_aggregate.py b/openstackclient/tests/unit/compute/v2/test_aggregate.py new file mode 100644 index 00000000..c636d3de --- /dev/null +++ b/openstackclient/tests/unit/compute/v2/test_aggregate.py @@ -0,0 +1,447 @@ +# Copyright 2016 Huawei, 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 mock +from mock import call + +from osc_lib import exceptions +from osc_lib import utils + +from openstackclient.compute.v2 import aggregate +from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes +from openstackclient.tests.unit import utils as tests_utils + + +class TestAggregate(compute_fakes.TestComputev2): + + fake_ag = compute_fakes.FakeAggregate.create_one_aggregate() + + columns = ( + 'availability_zone', + 'hosts', + 'id', + 'metadata', + 'name', + ) + + data = ( + fake_ag.availability_zone, + fake_ag.hosts, + fake_ag.id, + fake_ag.metadata, + fake_ag.name, + ) + + def setUp(self): + super(TestAggregate, self).setUp() + + # Get a shortcut to the AggregateManager Mock + self.aggregate_mock = self.app.client_manager.compute.aggregates + self.aggregate_mock.reset_mock() + + +class TestAggregateAddHost(TestAggregate): + + def setUp(self): + super(TestAggregateAddHost, self).setUp() + + self.aggregate_mock.get.return_value = self.fake_ag + self.aggregate_mock.add_host.return_value = self.fake_ag + self.cmd = aggregate.AddAggregateHost(self.app, None) + + def test_aggregate_add_host(self): + arglist = [ + 'ag1', + 'host1', + ] + verifylist = [ + ('aggregate', 'ag1'), + ('host', 'host1'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate) + self.aggregate_mock.add_host.assert_called_once_with(self.fake_ag, + parsed_args.host) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestAggregateCreate(TestAggregate): + + def setUp(self): + super(TestAggregateCreate, self).setUp() + + self.aggregate_mock.create.return_value = self.fake_ag + self.aggregate_mock.set_metadata.return_value = self.fake_ag + self.cmd = aggregate.CreateAggregate(self.app, None) + + def test_aggregate_create(self): + arglist = [ + 'ag1', + ] + verifylist = [ + ('name', 'ag1'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.aggregate_mock.create.assert_called_once_with(parsed_args.name, + None) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_aggregate_create_with_zone(self): + arglist = [ + '--zone', 'zone1', + 'ag1', + ] + verifylist = [ + ('zone', 'zone1'), + ('name', 'ag1'), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.aggregate_mock.create.assert_called_once_with(parsed_args.name, + parsed_args.zone) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_aggregate_create_with_property(self): + arglist = [ + '--property', 'key1=value1', + '--property', 'key2=value2', + 'ag1', + ] + verifylist = [ + ('property', {'key1': 'value1', 'key2': 'value2'}), + ('name', 'ag1'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.aggregate_mock.create.assert_called_once_with(parsed_args.name, + None) + self.aggregate_mock.set_metadata.assert_called_once_with( + self.fake_ag, parsed_args.property) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestAggregateDelete(TestAggregate): + + fake_ags = compute_fakes.FakeAggregate.create_aggregates(count=2) + + def setUp(self): + super(TestAggregateDelete, self).setUp() + + self.aggregate_mock.get = ( + compute_fakes.FakeAggregate.get_aggregates(self.fake_ags)) + self.cmd = aggregate.DeleteAggregate(self.app, None) + + def test_aggregate_delete(self): + arglist = [ + self.fake_ags[0].id + ] + verifylist = [ + ('aggregate', [self.fake_ags[0].id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.aggregate_mock.get.assert_called_once_with(self.fake_ags[0].id) + self.aggregate_mock.delete.assert_called_once_with(self.fake_ags[0].id) + self.assertIsNone(result) + + def test_delete_multiple_aggregates(self): + arglist = [] + for a in self.fake_ags: + arglist.append(a.id) + verifylist = [ + ('aggregate', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + calls = [] + for a in self.fake_ags: + calls.append(call(a.id)) + self.aggregate_mock.delete.assert_has_calls(calls) + self.assertIsNone(result) + + def test_delete_multiple_agggregates_with_exception(self): + arglist = [ + self.fake_ags[0].id, + 'unexist_aggregate', + ] + verifylist = [ + ('aggregate', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + find_mock_result = [self.fake_ags[0], exceptions.CommandError] + with mock.patch.object(utils, 'find_resource', + side_effect=find_mock_result) as find_mock: + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 aggregates failed to delete.', + str(e)) + + find_mock.assert_any_call(self.aggregate_mock, self.fake_ags[0].id) + find_mock.assert_any_call(self.aggregate_mock, 'unexist_aggregate') + + self.assertEqual(2, find_mock.call_count) + self.aggregate_mock.delete.assert_called_once_with( + self.fake_ags[0].id + ) + + +class TestAggregateList(TestAggregate): + + list_columns = ( + "ID", + "Name", + "Availability Zone", + ) + + list_columns_long = ( + "ID", + "Name", + "Availability Zone", + "Properties", + ) + + list_data = (( + TestAggregate.fake_ag.id, + TestAggregate.fake_ag.name, + TestAggregate.fake_ag.availability_zone, + ), ) + + list_data_long = (( + TestAggregate.fake_ag.id, + TestAggregate.fake_ag.name, + TestAggregate.fake_ag.availability_zone, + {}, + ), ) + + def setUp(self): + super(TestAggregateList, self).setUp() + + self.aggregate_mock.list.return_value = [self.fake_ag] + self.cmd = aggregate.ListAggregate(self.app, None) + + def test_aggregate_list(self): + + parsed_args = self.check_parser(self.cmd, [], []) + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.list_columns, columns) + self.assertEqual(self.list_data, tuple(data)) + + def test_aggregate_list_with_long(self): + arglist = [ + '--long', + ] + vertifylist = [ + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, vertifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.list_columns_long, columns) + self.assertEqual(self.list_data_long, tuple(data)) + + +class TestAggregateRemoveHost(TestAggregate): + + def setUp(self): + super(TestAggregateRemoveHost, self).setUp() + + self.aggregate_mock.get.return_value = self.fake_ag + self.aggregate_mock.remove_host.return_value = self.fake_ag + self.cmd = aggregate.RemoveAggregateHost(self.app, None) + + def test_aggregate_add_host(self): + arglist = [ + 'ag1', + 'host1', + ] + verifylist = [ + ('aggregate', 'ag1'), + ('host', 'host1'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate) + self.aggregate_mock.remove_host.assert_called_once_with( + self.fake_ag, parsed_args.host) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestAggregateSet(TestAggregate): + + def setUp(self): + super(TestAggregateSet, self).setUp() + + self.aggregate_mock.get.return_value = self.fake_ag + self.cmd = aggregate.SetAggregate(self.app, None) + + def test_aggregate_set_no_option(self): + arglist = [ + 'ag1', + ] + verifylist = [ + ('aggregate', 'ag1'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate) + self.assertNotCalled(self.aggregate_mock.update) + self.assertNotCalled(self.aggregate_mock.set_metadata) + self.assertIsNone(result) + + def test_aggregate_set_with_name(self): + arglist = [ + '--name', 'new_name', + 'ag1', + ] + verifylist = [ + ('name', 'new_name'), + ('aggregate', 'ag1'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate) + self.aggregate_mock.update.assert_called_once_with( + self.fake_ag, {'name': parsed_args.name}) + self.assertNotCalled(self.aggregate_mock.set_metadata) + self.assertIsNone(result) + + def test_aggregate_set_with_zone(self): + arglist = [ + '--zone', 'new_zone', + 'ag1', + ] + verifylist = [ + ('zone', 'new_zone'), + ('aggregate', 'ag1'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate) + self.aggregate_mock.update.assert_called_once_with( + self.fake_ag, {'availability_zone': parsed_args.zone}) + self.assertNotCalled(self.aggregate_mock.set_metadata) + self.assertIsNone(result) + + def test_aggregate_set_with_property(self): + arglist = [ + '--property', 'key1=value1', + '--property', 'key2=value2', + 'ag1', + ] + verifylist = [ + ('property', {'key1': 'value1', 'key2': 'value2'}), + ('aggregate', 'ag1'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate) + self.assertNotCalled(self.aggregate_mock.update) + self.aggregate_mock.set_metadata.assert_called_once_with( + self.fake_ag, parsed_args.property) + self.assertIsNone(result) + + +class TestAggregateShow(TestAggregate): + + columns = ( + 'availability_zone', + 'hosts', + 'id', + 'name', + 'properties', + ) + + data = ( + TestAggregate.fake_ag.availability_zone, + TestAggregate.fake_ag.hosts, + TestAggregate.fake_ag.id, + TestAggregate.fake_ag.name, + '', + ) + + def setUp(self): + super(TestAggregateShow, self).setUp() + + self.aggregate_mock.get.return_value = self.fake_ag + self.cmd = aggregate.ShowAggregate(self.app, None) + + def test_aggregate_show(self): + arglist = [ + 'ag1', + ] + verifylist = [ + ('aggregate', 'ag1'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestAggregateUnset(TestAggregate): + + def setUp(self): + super(TestAggregateUnset, self).setUp() + + self.aggregate_mock.get.return_value = self.fake_ag + self.cmd = aggregate.UnsetAggregate(self.app, None) + + def test_aggregate_unset(self): + arglist = [ + '--property', 'unset_key', + 'ag1', + ] + verifylist = [ + ('property', ['unset_key']), + ('aggregate', 'ag1'), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.aggregate_mock.set_metadata.assert_called_once_with( + self.fake_ag, {'unset_key': None}) + self.assertIsNone(result) + + def test_aggregate_unset_no_property(self): + arglist = [ + 'ag1', + ] + verifylist = None + self.assertRaises(tests_utils.ParserException, + self.check_parser, + self.cmd, + arglist, + verifylist) diff --git a/openstackclient/tests/unit/compute/v2/test_console.py b/openstackclient/tests/unit/compute/v2/test_console.py new file mode 100644 index 00000000..d53d241e --- /dev/null +++ b/openstackclient/tests/unit/compute/v2/test_console.py @@ -0,0 +1,196 @@ +# Copyright 2016 Huawei, 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 mock + +from openstackclient.compute.v2 import console +from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes + + +class TestConsole(compute_fakes.TestComputev2): + + def setUp(self): + super(TestConsole, self).setUp() + self.servers_mock = self.app.client_manager.compute.servers + self.servers_mock.reset_mock() + + +class TestConsoleUrlShow(TestConsole): + + def setUp(self): + super(TestConsoleUrlShow, self).setUp() + fake_console_data = {'remote_console': {'url': 'http://localhost', + 'protocol': 'fake_protocol', + 'type': 'fake_type'}} + methods = { + 'get_vnc_console': fake_console_data, + 'get_spice_console': fake_console_data, + 'get_serial_console': fake_console_data, + 'get_rdp_console': fake_console_data, + 'get_mks_console': fake_console_data, + } + self.fake_server = compute_fakes.FakeServer.create_one_server( + methods=methods) + self.servers_mock.get.return_value = self.fake_server + + self.columns = ( + 'protocol', + 'type', + 'url', + ) + self.data = ( + fake_console_data['remote_console']['protocol'], + fake_console_data['remote_console']['type'], + fake_console_data['remote_console']['url'] + ) + + self.cmd = console.ShowConsoleURL(self.app, None) + + def test_console_url_show_by_default(self): + arglist = [ + 'foo_vm', + ] + verifylist = [ + ('url_type', 'novnc'), + ('server', 'foo_vm'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.fake_server.get_vnc_console.assert_called_once_with('novnc') + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_console_url_show_with_novnc(self): + arglist = [ + '--novnc', + 'foo_vm', + ] + verifylist = [ + ('url_type', 'novnc'), + ('server', 'foo_vm'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.fake_server.get_vnc_console.assert_called_once_with('novnc') + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_console_url_show_with_xvpvnc(self): + arglist = [ + '--xvpvnc', + 'foo_vm', + ] + verifylist = [ + ('url_type', 'xvpvnc'), + ('server', 'foo_vm'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.fake_server.get_vnc_console.assert_called_once_with('xvpvnc') + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_console_url_show_with_spice(self): + arglist = [ + '--spice', + 'foo_vm', + ] + verifylist = [ + ('url_type', 'spice-html5'), + ('server', 'foo_vm'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.fake_server.get_spice_console.assert_called_once_with( + 'spice-html5') + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_console_url_show_compatible(self): + methods = { + 'get_vnc_console': {'console': {'url': 'http://localhost', + 'type': 'fake_type'}}, + } + old_fake_server = compute_fakes.FakeServer.create_one_server( + methods=methods) + old_columns = ( + 'type', + 'url', + ) + old_data = ( + methods['get_vnc_console']['console']['type'], + methods['get_vnc_console']['console']['url'] + ) + arglist = [ + 'foo_vm', + ] + verifylist = [ + ('url_type', 'novnc'), + ('server', 'foo_vm'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + with mock.patch.object(self.servers_mock, 'get', + return_value=old_fake_server): + columns, data = self.cmd.take_action(parsed_args) + old_fake_server.get_vnc_console.assert_called_once_with('novnc') + self.assertEqual(old_columns, columns) + self.assertEqual(old_data, data) + + def test_console_url_show_with_rdp(self): + arglist = [ + '--rdp', + 'foo_vm', + ] + verifylist = [ + ('url_type', 'rdp-html5'), + ('server', 'foo_vm'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.fake_server.get_rdp_console.assert_called_once_with( + 'rdp-html5') + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_console_url_show_with_serial(self): + arglist = [ + '--serial', + 'foo_vm', + ] + verifylist = [ + ('url_type', 'serial'), + ('server', 'foo_vm'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.fake_server.get_serial_console.assert_called_once_with( + 'serial') + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_console_url_show_with_mks(self): + arglist = [ + '--mks', + 'foo_vm', + ] + verifylist = [ + ('url_type', 'webmks'), + ('server', 'foo_vm'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.fake_server.get_mks_console.assert_called_once_with() + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/compute/v2/test_flavor.py b/openstackclient/tests/unit/compute/v2/test_flavor.py new file mode 100644 index 00000000..ace650eb --- /dev/null +++ b/openstackclient/tests/unit/compute/v2/test_flavor.py @@ -0,0 +1,825 @@ +# Copyright 2015 Symantec Corporation +# +# 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 mock import call + +from osc_lib import exceptions +from osc_lib import utils + +from openstackclient.compute.v2 import flavor +from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes +from openstackclient.tests.unit import utils as tests_utils + + +class TestFlavor(compute_fakes.TestComputev2): + + def setUp(self): + super(TestFlavor, self).setUp() + + # Get a shortcut to the FlavorManager Mock + self.flavors_mock = self.app.client_manager.compute.flavors + self.flavors_mock.reset_mock() + + # Get a shortcut to the FlavorAccessManager Mock + self.flavor_access_mock = self.app.client_manager.compute.flavor_access + self.flavor_access_mock.reset_mock() + + self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock.reset_mock() + + +class TestFlavorCreate(TestFlavor): + + flavor = compute_fakes.FakeFlavor.create_one_flavor( + attrs={'links': 'flavor-links'}) + project = identity_fakes.FakeProject.create_one_project() + columns = ( + 'OS-FLV-DISABLED:disabled', + 'OS-FLV-EXT-DATA:ephemeral', + 'disk', + 'id', + 'name', + 'os-flavor-access:is_public', + 'properties', + 'ram', + 'rxtx_factor', + 'swap', + 'vcpus', + ) + data = ( + flavor.disabled, + flavor.ephemeral, + flavor.disk, + flavor.id, + flavor.name, + flavor.is_public, + utils.format_dict(flavor.properties), + flavor.ram, + flavor.rxtx_factor, + flavor.swap, + flavor.vcpus, + ) + + def setUp(self): + super(TestFlavorCreate, self).setUp() + + # Return a project + self.projects_mock.get.return_value = self.project + self.flavors_mock.create.return_value = self.flavor + self.cmd = flavor.CreateFlavor(self.app, None) + + def test_flavor_create_default_options(self): + + arglist = [ + self.flavor.name + ] + verifylist = [ + ('name', self.flavor.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + default_args = ( + self.flavor.name, + 256, + 1, + 0, + 'auto', + 0, + 0, + 1.0, + True + ) + columns, data = self.cmd.take_action(parsed_args) + self.flavors_mock.create.assert_called_once_with(*default_args) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_flavor_create_all_options(self): + + arglist = [ + '--id', self.flavor.id, + '--ram', str(self.flavor.ram), + '--disk', str(self.flavor.disk), + '--ephemeral', str(self.flavor.ephemeral), + '--swap', str(self.flavor.swap), + '--vcpus', str(self.flavor.vcpus), + '--rxtx-factor', str(self.flavor.rxtx_factor), + '--public', + '--property', 'property=value', + self.flavor.name, + ] + verifylist = [ + ('id', self.flavor.id), + ('ram', self.flavor.ram), + ('disk', self.flavor.disk), + ('ephemeral', self.flavor.ephemeral), + ('swap', self.flavor.swap), + ('vcpus', self.flavor.vcpus), + ('rxtx_factor', self.flavor.rxtx_factor), + ('public', True), + ('property', {'property': 'value'}), + ('name', self.flavor.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + args = ( + self.flavor.name, + self.flavor.ram, + self.flavor.vcpus, + self.flavor.disk, + self.flavor.id, + self.flavor.ephemeral, + self.flavor.swap, + self.flavor.rxtx_factor, + self.flavor.is_public, + ) + columns, data = self.cmd.take_action(parsed_args) + self.flavors_mock.create.assert_called_once_with(*args) + self.flavor.set_keys.assert_called_once_with({'property': 'value'}) + self.flavor.get_keys.assert_called_once_with() + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_flavor_create_other_options(self): + + self.flavor.is_public = False + arglist = [ + '--id', self.flavor.id, + '--ram', str(self.flavor.ram), + '--disk', str(self.flavor.disk), + '--ephemeral', str(self.flavor.ephemeral), + '--swap', str(self.flavor.swap), + '--vcpus', str(self.flavor.vcpus), + '--rxtx-factor', str(self.flavor.rxtx_factor), + '--private', + '--project', self.project.id, + '--property', 'key1=value1', + '--property', 'key2=value2', + self.flavor.name, + ] + verifylist = [ + ('id', self.flavor.id), + ('ram', self.flavor.ram), + ('disk', self.flavor.disk), + ('ephemeral', self.flavor.ephemeral), + ('swap', self.flavor.swap), + ('vcpus', self.flavor.vcpus), + ('rxtx_factor', self.flavor.rxtx_factor), + ('public', False), + ('project', self.project.id), + ('property', {'key1': 'value1', 'key2': 'value2'}), + ('name', self.flavor.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + args = ( + self.flavor.name, + self.flavor.ram, + self.flavor.vcpus, + self.flavor.disk, + self.flavor.id, + self.flavor.ephemeral, + self.flavor.swap, + self.flavor.rxtx_factor, + self.flavor.is_public, + ) + columns, data = self.cmd.take_action(parsed_args) + self.flavors_mock.create.assert_called_once_with(*args) + self.flavor_access_mock.add_tenant_access.assert_called_with( + self.flavor.id, + self.project.id, + ) + self.flavor.set_keys.assert_called_with( + {'key1': 'value1', 'key2': 'value2'}) + self.flavor.get_keys.assert_called_with() + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_public_flavor_create_with_project(self): + arglist = [ + '--project', self.project.id, + self.flavor.name, + ] + verifylist = [ + ('project', self.project.id), + ('name', self.flavor.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises(exceptions.CommandError, + self.cmd.take_action, + parsed_args) + + def test_flavor_create_no_options(self): + arglist = [] + verifylist = None + self.assertRaises(tests_utils.ParserException, + self.check_parser, + self.cmd, + arglist, + verifylist) + + +class TestFlavorDelete(TestFlavor): + + flavors = compute_fakes.FakeFlavor.create_flavors(count=2) + + def setUp(self): + super(TestFlavorDelete, self).setUp() + + self.flavors_mock.get = ( + compute_fakes.FakeFlavor.get_flavors(self.flavors)) + self.flavors_mock.delete.return_value = None + + self.cmd = flavor.DeleteFlavor(self.app, None) + + def test_flavor_delete(self): + arglist = [ + self.flavors[0].id + ] + verifylist = [ + ('flavor', [self.flavors[0].id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.flavors_mock.delete.assert_called_with(self.flavors[0].id) + self.assertIsNone(result) + + def test_delete_multiple_flavors(self): + arglist = [] + for f in self.flavors: + arglist.append(f.id) + verifylist = [ + ('flavor', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + calls = [] + for f in self.flavors: + calls.append(call(f.id)) + self.flavors_mock.delete.assert_has_calls(calls) + self.assertIsNone(result) + + def test_multi_flavors_delete_with_exception(self): + arglist = [ + self.flavors[0].id, + 'unexist_flavor', + ] + verifylist = [ + ('flavor', [self.flavors[0].id, 'unexist_flavor']) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + find_mock_result = [self.flavors[0], exceptions.CommandError] + self.flavors_mock.get = ( + mock.MagicMock(side_effect=find_mock_result) + ) + self.flavors_mock.find.side_effect = exceptions.NotFound(None) + + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 flavors failed to delete.', str(e)) + + self.flavors_mock.get.assert_any_call(self.flavors[0].id) + self.flavors_mock.get.assert_any_call('unexist_flavor') + self.flavors_mock.delete.assert_called_once_with(self.flavors[0].id) + + +class TestFlavorList(TestFlavor): + + # Return value of self.flavors_mock.list(). + flavors = compute_fakes.FakeFlavor.create_flavors(count=1) + + columns = ( + 'ID', + 'Name', + 'RAM', + 'Disk', + 'Ephemeral', + 'VCPUs', + 'Is Public', + ) + columns_long = columns + ( + 'Swap', + 'RXTX Factor', + 'Properties' + ) + + data = (( + flavors[0].id, + flavors[0].name, + flavors[0].ram, + flavors[0].disk, + flavors[0].ephemeral, + flavors[0].vcpus, + flavors[0].is_public, + ), ) + data_long = (data[0] + ( + flavors[0].swap, + flavors[0].rxtx_factor, + u'property=\'value\'' + ), ) + + def setUp(self): + super(TestFlavorList, self).setUp() + + self.flavors_mock.list.return_value = self.flavors + + # Get the command object to test + self.cmd = flavor.ListFlavor(self.app, None) + + def test_flavor_list_no_options(self): + arglist = [] + verifylist = [ + ('public', True), + ('all', False), + ('long', False), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'is_public': True, + 'limit': None, + 'marker': None + } + + self.flavors_mock.list.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(tuple(self.data), tuple(data)) + + def test_flavor_list_all_flavors(self): + arglist = [ + '--all', + ] + verifylist = [ + ('all', True), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'is_public': None, + 'limit': None, + 'marker': None + } + + self.flavors_mock.list.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(tuple(self.data), tuple(data)) + + def test_flavor_list_private_flavors(self): + arglist = [ + '--private', + ] + verifylist = [ + ('public', False), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'is_public': False, + 'limit': None, + 'marker': None + } + + self.flavors_mock.list.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(tuple(self.data), tuple(data)) + + def test_flavor_list_public_flavors(self): + arglist = [ + '--public', + ] + verifylist = [ + ('public', True), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'is_public': True, + 'limit': None, + 'marker': None + } + + self.flavors_mock.list.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(tuple(self.data), tuple(data)) + + def test_flavor_list_long(self): + arglist = [ + '--long', + ] + verifylist = [ + ('long', True), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'is_public': True, + 'limit': None, + 'marker': None + } + + self.flavors_mock.list.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns_long, columns) + self.assertEqual(tuple(self.data_long), tuple(data)) + + +class TestFlavorSet(TestFlavor): + + # Return value of self.flavors_mock.find(). + flavor = compute_fakes.FakeFlavor.create_one_flavor( + attrs={'os-flavor-access:is_public': False}) + project = identity_fakes.FakeProject.create_one_project() + + def setUp(self): + super(TestFlavorSet, self).setUp() + + self.flavors_mock.find.return_value = self.flavor + self.flavors_mock.get.side_effect = exceptions.NotFound(None) + # Return a project + self.projects_mock.get.return_value = self.project + self.cmd = flavor.SetFlavor(self.app, None) + + def test_flavor_set_property(self): + arglist = [ + '--property', 'FOO="B A R"', + 'baremetal' + ] + verifylist = [ + ('property', {'FOO': '"B A R"'}), + ('flavor', 'baremetal') + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.flavors_mock.find.assert_called_with(name=parsed_args.flavor, + is_public=None) + self.flavor.set_keys.assert_called_with({'FOO': '"B A R"'}) + self.assertIsNone(result) + + def test_flavor_set_project(self): + arglist = [ + '--project', self.project.id, + self.flavor.id, + ] + verifylist = [ + ('project', self.project.id), + ('flavor', self.flavor.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.flavors_mock.find.assert_called_with(name=parsed_args.flavor, + is_public=None) + self.flavor_access_mock.add_tenant_access.assert_called_with( + self.flavor.id, + self.project.id, + ) + self.flavor.set_keys.assert_not_called() + self.assertIsNone(result) + + def test_flavor_set_no_project(self): + arglist = [ + '--project', + self.flavor.id, + ] + verifylist = [ + ('project', None), + ('flavor', self.flavor.id), + ] + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_flavor_set_no_flavor(self): + arglist = [ + '--project', self.project.id, + ] + verifylist = [ + ('project', self.project.id), + ] + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_flavor_set_with_unexist_flavor(self): + self.flavors_mock.get.side_effect = exceptions.NotFound(None) + self.flavors_mock.find.side_effect = exceptions.NotFound(None) + + arglist = [ + '--project', self.project.id, + 'unexist_flavor', + ] + verifylist = [ + ('project', self.project.id), + ('flavor', 'unexist_flavor'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises(exceptions.CommandError, + self.cmd.take_action, + parsed_args) + + def test_flavor_set_nothing(self): + arglist = [ + self.flavor.id, + ] + verifylist = [ + ('flavor', self.flavor.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.flavors_mock.find.assert_called_with(name=parsed_args.flavor, + is_public=None) + self.flavor_access_mock.add_tenant_access.assert_not_called() + self.assertIsNone(result) + + +class TestFlavorShow(TestFlavor): + + # Return value of self.flavors_mock.find(). + flavor_access = compute_fakes.FakeFlavorAccess.create_one_flavor_access() + flavor = compute_fakes.FakeFlavor.create_one_flavor() + + columns = ( + 'OS-FLV-DISABLED:disabled', + 'OS-FLV-EXT-DATA:ephemeral', + 'access_project_ids', + 'disk', + 'id', + 'name', + 'os-flavor-access:is_public', + 'properties', + 'ram', + 'rxtx_factor', + 'swap', + 'vcpus', + ) + + data = ( + flavor.disabled, + flavor.ephemeral, + None, + flavor.disk, + flavor.id, + flavor.name, + flavor.is_public, + utils.format_dict(flavor.get_keys()), + flavor.ram, + flavor.rxtx_factor, + flavor.swap, + flavor.vcpus, + ) + + def setUp(self): + super(TestFlavorShow, self).setUp() + + # Return value of _find_resource() + self.flavors_mock.find.return_value = self.flavor + self.flavors_mock.get.side_effect = exceptions.NotFound(None) + self.flavor_access_mock.list.return_value = [self.flavor_access] + self.cmd = flavor.ShowFlavor(self.app, None) + + def test_show_no_options(self): + arglist = [] + verifylist = [] + + # Missing required args should boil here + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_public_flavor_show(self): + arglist = [ + self.flavor.name, + ] + verifylist = [ + ('flavor', self.flavor.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_private_flavor_show(self): + private_flavor = compute_fakes.FakeFlavor.create_one_flavor( + attrs={ + 'os-flavor-access:is_public': False, + } + ) + self.flavors_mock.find.return_value = private_flavor + + arglist = [ + private_flavor.name, + ] + verifylist = [ + ('flavor', private_flavor.name), + ] + + data_with_project = ( + private_flavor.disabled, + private_flavor.ephemeral, + self.flavor_access.tenant_id, + private_flavor.disk, + private_flavor.id, + private_flavor.name, + private_flavor.is_public, + utils.format_dict(private_flavor.get_keys()), + private_flavor.ram, + private_flavor.rxtx_factor, + private_flavor.swap, + private_flavor.vcpus, + ) + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.flavor_access_mock.list.assert_called_with( + flavor=private_flavor.id) + self.assertEqual(self.columns, columns) + self.assertEqual(data_with_project, data) + + +class TestFlavorUnset(TestFlavor): + + # Return value of self.flavors_mock.find(). + flavor = compute_fakes.FakeFlavor.create_one_flavor( + attrs={'os-flavor-access:is_public': False}) + project = identity_fakes.FakeProject.create_one_project() + + def setUp(self): + super(TestFlavorUnset, self).setUp() + + self.flavors_mock.find.return_value = self.flavor + self.flavors_mock.get.side_effect = exceptions.NotFound(None) + # Return a project + self.projects_mock.get.return_value = self.project + self.cmd = flavor.UnsetFlavor(self.app, None) + + def test_flavor_unset_property(self): + arglist = [ + '--property', 'property', + 'baremetal' + ] + verifylist = [ + ('property', ['property']), + ('flavor', 'baremetal'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.flavors_mock.find.assert_called_with(name=parsed_args.flavor, + is_public=None) + self.flavor.unset_keys.assert_called_with(['property']) + self.flavor_access_mock.remove_tenant_access.assert_not_called() + self.assertIsNone(result) + + def test_flavor_unset_project(self): + arglist = [ + '--project', self.project.id, + self.flavor.id, + ] + verifylist = [ + ('project', self.project.id), + ('flavor', self.flavor.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) + + self.flavors_mock.find.assert_called_with(name=parsed_args.flavor, + is_public=None) + self.flavor_access_mock.remove_tenant_access.assert_called_with( + self.flavor.id, + self.project.id, + ) + self.flavor.unset_keys.assert_not_called() + self.assertIsNone(result) + + def test_flavor_unset_no_project(self): + arglist = [ + '--project', + self.flavor.id, + ] + verifylist = [ + ('project', None), + ('flavor', self.flavor.id), + ] + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_flavor_unset_no_flavor(self): + arglist = [ + '--project', self.project.id, + ] + verifylist = [ + ('project', self.project.id), + ] + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_flavor_unset_with_unexist_flavor(self): + self.flavors_mock.get.side_effect = exceptions.NotFound(None) + self.flavors_mock.find.side_effect = exceptions.NotFound(None) + + arglist = [ + '--project', self.project.id, + 'unexist_flavor', + ] + verifylist = [ + ('project', self.project.id), + ('flavor', 'unexist_flavor'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises(exceptions.CommandError, self.cmd.take_action, + parsed_args) + + def test_flavor_unset_nothing(self): + arglist = [ + self.flavor.id, + ] + verifylist = [ + ('flavor', self.flavor.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) + + self.flavor_access_mock.remove_tenant_access.assert_not_called() diff --git a/openstackclient/tests/unit/compute/v2/test_host.py b/openstackclient/tests/unit/compute/v2/test_host.py new file mode 100644 index 00000000..a388172f --- /dev/null +++ b/openstackclient/tests/unit/compute/v2/test_host.py @@ -0,0 +1,179 @@ +# Copyright 2016 IBM Corporation +# +# 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 openstackclient.compute.v2 import host +from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes +from openstackclient.tests.unit import utils as tests_utils + + +class TestHost(compute_fakes.TestComputev2): + + def setUp(self): + super(TestHost, self).setUp() + + # Get a shortcut to the FlavorManager Mock + self.host_mock = self.app.client_manager.compute.hosts + self.host_mock.reset_mock() + + +class TestHostList(TestHost): + + host = compute_fakes.FakeHost.create_one_host() + + columns = ( + 'Host Name', + 'Service', + 'Zone', + ) + + data = [( + host.host_name, + host.service, + host.zone, + )] + + def setUp(self): + super(TestHostList, self).setUp() + + self.host_mock.list_all.return_value = [self.host] + + self.cmd = host.ListHost(self.app, None) + + def test_host_list_no_option(self): + arglist = [] + verifylist = [] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.host_mock.list_all.assert_called_with(None) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_host_list_with_option(self): + arglist = [ + '--zone', self.host.zone, + ] + verifylist = [ + ('zone', self.host.zone), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.host_mock.list_all.assert_called_with(self.host.zone) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + +class TestHostSet(TestHost): + + def setUp(self): + super(TestHostSet, self).setUp() + + self.host = compute_fakes.FakeHost.create_one_host() + self.host_mock.get.return_value = self.host + self.host_mock.update.return_value = None + + self.cmd = host.SetHost(self.app, None) + + def test_host_set_no_option(self): + arglist = [ + self.host.host + ] + verifylist = [ + ('host', self.host.host) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) + + body = {} + self.host_mock.update.assert_called_with(self.host.host, body) + + def test_host_set(self): + arglist = [ + '--enable', + '--disable-maintenance', + self.host.host + ] + verifylist = [ + ('enable', True), + ('enable_maintenance', False), + ('host', self.host.host) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) + + body = {'status': 'enable', 'maintenance_mode': 'disable'} + self.host_mock.update.assert_called_with(self.host.host, body) + + +class TestHostShow(TestHost): + + host = compute_fakes.FakeHost.create_one_host() + + columns = ( + 'Host', + 'Project', + 'CPU', + 'Memory MB', + 'Disk GB', + ) + data = [( + host.host, + host.project, + host.cpu, + host.memory_mb, + host.disk_gb, + )] + + def setUp(self): + super(TestHostShow, self).setUp() + + self.host_mock.get.return_value = [self.host] + + self.cmd = host.ShowHost(self.app, None) + + def test_host_show_no_option(self): + arglist = [] + verifylist = [] + + # Missing required args should bail here + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_host_show_with_option(self): + arglist = [ + self.host.host_name, + ] + verifylist = [ + ('host', self.host.host_name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.host_mock.get.assert_called_with(self.host.host_name) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) diff --git a/openstackclient/tests/unit/compute/v2/test_hypervisor.py b/openstackclient/tests/unit/compute/v2/test_hypervisor.py new file mode 100644 index 00000000..d94a107c --- /dev/null +++ b/openstackclient/tests/unit/compute/v2/test_hypervisor.py @@ -0,0 +1,221 @@ +# Copyright 2016 EasyStack Corporation +# +# 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 copy + +from osc_lib import exceptions + +from openstackclient.compute.v2 import hypervisor +from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes +from openstackclient.tests.unit import fakes + + +class TestHypervisor(compute_fakes.TestComputev2): + + def setUp(self): + super(TestHypervisor, self).setUp() + + # Get a shortcut to the compute client hypervisors mock + self.hypervisors_mock = self.app.client_manager.compute.hypervisors + self.hypervisors_mock.reset_mock() + + # Get a shortcut to the compute client aggregates mock + self.aggregates_mock = self.app.client_manager.compute.aggregates + self.aggregates_mock.reset_mock() + + +class TestHypervisorList(TestHypervisor): + + def setUp(self): + super(TestHypervisorList, self).setUp() + + # Fake hypervisors to be listed up + self.hypervisors = compute_fakes.FakeHypervisor.create_hypervisors() + self.hypervisors_mock.list.return_value = self.hypervisors + + self.columns = ( + "ID", + "Hypervisor Hostname" + ) + self.data = ( + ( + self.hypervisors[0].id, + self.hypervisors[0].hypervisor_hostname, + ), + ( + self.hypervisors[1].id, + self.hypervisors[1].hypervisor_hostname, + ), + ) + + # Get the command object to test + self.cmd = hypervisor.ListHypervisor(self.app, None) + + def test_hypervisor_list_no_option(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.hypervisors_mock.list.assert_called_with() + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, tuple(data)) + + def test_hypervisor_list_matching_option_found(self): + arglist = [ + '--matching', self.hypervisors[0].hypervisor_hostname, + ] + verifylist = [ + ('matching', self.hypervisors[0].hypervisor_hostname), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # Fake the return value of search() + self.hypervisors_mock.search.return_value = [self.hypervisors[0]] + self.data = ( + ( + self.hypervisors[0].id, + self.hypervisors[0].hypervisor_hostname, + ), + ) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.hypervisors_mock.search.assert_called_with( + self.hypervisors[0].hypervisor_hostname + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, tuple(data)) + + def test_hypervisor_list_matching_option_not_found(self): + arglist = [ + '--matching', 'xxx', + ] + verifylist = [ + ('matching', 'xxx'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # Fake exception raised from search() + self.hypervisors_mock.search.side_effect = exceptions.NotFound(None) + + self.assertRaises(exceptions.NotFound, + self.cmd.take_action, + parsed_args) + + +class TestHypervisorShow(TestHypervisor): + + def setUp(self): + super(TestHypervisorShow, self).setUp() + + # Fake hypervisors to be listed up + self.hypervisor = compute_fakes.FakeHypervisor.create_one_hypervisor() + + # Return value of utils.find_resource() + self.hypervisors_mock.get.return_value = self.hypervisor + + # Return value of compute_client.aggregates.list() + self.aggregates_mock.list.return_value = [] + + # Return value of compute_client.hypervisors.uptime() + uptime_info = { + 'status': self.hypervisor.status, + 'state': self.hypervisor.state, + 'id': self.hypervisor.id, + 'hypervisor_hostname': self.hypervisor.hypervisor_hostname, + 'uptime': ' 01:28:24 up 3 days, 11:15, 1 user, ' + ' load average: 0.94, 0.62, 0.50\n', + } + self.hypervisors_mock.uptime.return_value = fakes.FakeResource( + info=copy.deepcopy(uptime_info), + loaded=True + ) + + self.columns = ( + 'aggregates', + 'cpu_info', + 'current_workload', + 'disk_available_least', + 'free_disk_gb', + 'free_ram_mb', + 'host_ip', + 'hypervisor_hostname', + 'hypervisor_type', + 'hypervisor_version', + 'id', + 'local_gb', + 'local_gb_used', + 'memory_mb', + 'memory_mb_used', + 'running_vms', + 'service_host', + 'service_id', + 'state', + 'status', + 'vcpus', + 'vcpus_used', + ) + self.data = ( + [], + {'aaa': 'aaa'}, + 0, + 50, + 50, + 1024, + '192.168.0.10', + self.hypervisor.hypervisor_hostname, + 'QEMU', + 2004001, + self.hypervisor.id, + 50, + 0, + 1024, + 512, + 0, + 'aaa', + 1, + 'up', + 'enabled', + 4, + 0, + ) + + # Get the command object to test + self.cmd = hypervisor.ShowHypervisor(self.app, None) + + def test_hypervisor_show(self): + arglist = [ + self.hypervisor.hypervisor_hostname, + ] + verifylist = [ + ('hypervisor', self.hypervisor.hypervisor_hostname), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py b/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py new file mode 100644 index 00000000..40086f9b --- /dev/null +++ b/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py @@ -0,0 +1,79 @@ +# Copyright 2016 EasyStack Corporation +# +# 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 openstackclient.compute.v2 import hypervisor_stats +from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes + + +class TestHypervisorStats(compute_fakes.TestComputev2): + + def setUp(self): + super(TestHypervisorStats, self).setUp() + + # Get a shortcut to the compute client hypervisors mock + self.hypervisors_mock = self.app.client_manager.compute.hypervisors + self.hypervisors_mock.reset_mock() + + +class TestHypervisorStatsShow(TestHypervisorStats): + + def setUp(self): + super(TestHypervisorStatsShow, self).setUp() + + self.hypervisor_stats = \ + compute_fakes.FakeHypervisorStats.create_one_hypervisor_stats() + + self.hypervisors_mock.statistics.return_value =\ + self.hypervisor_stats + + self.cmd = hypervisor_stats.ShowHypervisorStats(self.app, None) + + self.columns = ( + 'count', + 'current_workload', + 'disk_available_least', + 'free_disk_gb', + 'free_ram_mb', + 'local_gb', + 'local_gb_used', + 'memory_mb', + 'memory_mb_used', + 'running_vms', + 'vcpus', + 'vcpus_used', + ) + + self.data = ( + 2, + 0, + 50, + 100, + 23000, + 100, + 0, + 23800, + 1400, + 3, + 8, + 3, + ) + + def test_hypervisor_show_stats(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/compute/v2/test_keypair.py b/openstackclient/tests/unit/compute/v2/test_keypair.py new file mode 100644 index 00000000..cb008545 --- /dev/null +++ b/openstackclient/tests/unit/compute/v2/test_keypair.py @@ -0,0 +1,312 @@ +# Copyright 2016 IBM +# +# 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 mock import call + +from osc_lib import exceptions +from osc_lib import utils + +from openstackclient.compute.v2 import keypair +from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes +from openstackclient.tests.unit import utils as tests_utils + + +class TestKeypair(compute_fakes.TestComputev2): + + def setUp(self): + super(TestKeypair, self).setUp() + + # Get a shortcut to the KeypairManager Mock + self.keypairs_mock = self.app.client_manager.compute.keypairs + self.keypairs_mock.reset_mock() + + +class TestKeypairCreate(TestKeypair): + + keypair = compute_fakes.FakeKeypair.create_one_keypair() + + def setUp(self): + super(TestKeypairCreate, self).setUp() + + self.columns = ( + 'fingerprint', + 'name', + 'user_id' + ) + self.data = ( + self.keypair.fingerprint, + self.keypair.name, + self.keypair.user_id + ) + + # Get the command object to test + self.cmd = keypair.CreateKeypair(self.app, None) + + self.keypairs_mock.create.return_value = self.keypair + + def test_key_pair_create_no_options(self): + + arglist = [ + self.keypair.name, + ] + verifylist = [ + ('name', self.keypair.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.keypairs_mock.create.assert_called_with( + self.keypair.name, + public_key=None + ) + + self.assertEqual({}, columns) + self.assertEqual({}, data) + + def test_keypair_create_public_key(self): + # overwrite the setup one because we want to omit private_key + self.keypair = compute_fakes.FakeKeypair.create_one_keypair( + no_pri=True) + self.keypairs_mock.create.return_value = self.keypair + + self.data = ( + self.keypair.fingerprint, + self.keypair.name, + self.keypair.user_id + ) + + arglist = [ + '--public-key', self.keypair.public_key, + self.keypair.name, + ] + verifylist = [ + ('public_key', self.keypair.public_key), + ('name', self.keypair.name) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + with mock.patch('io.open') as mock_open: + mock_open.return_value = mock.MagicMock() + m_file = mock_open.return_value.__enter__.return_value + m_file.read.return_value = 'dummy' + + columns, data = self.cmd.take_action(parsed_args) + + self.keypairs_mock.create.assert_called_with( + self.keypair.name, + public_key=self.keypair.public_key + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestKeypairDelete(TestKeypair): + + keypairs = compute_fakes.FakeKeypair.create_keypairs(count=2) + + def setUp(self): + super(TestKeypairDelete, self).setUp() + + self.keypairs_mock.get = compute_fakes.FakeKeypair.get_keypairs( + self.keypairs) + self.keypairs_mock.delete.return_value = None + + self.cmd = keypair.DeleteKeypair(self.app, None) + + def test_keypair_delete(self): + arglist = [ + self.keypairs[0].name + ] + verifylist = [ + ('name', [self.keypairs[0].name]), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + ret = self.cmd.take_action(parsed_args) + + self.assertIsNone(ret) + self.keypairs_mock.delete.assert_called_with(self.keypairs[0].name) + + def test_delete_multiple_keypairs(self): + arglist = [] + for k in self.keypairs: + arglist.append(k.name) + verifylist = [ + ('name', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + calls = [] + for k in self.keypairs: + calls.append(call(k.name)) + self.keypairs_mock.delete.assert_has_calls(calls) + self.assertIsNone(result) + + def test_delete_multiple_keypairs_with_exception(self): + arglist = [ + self.keypairs[0].name, + 'unexist_keypair', + ] + verifylist = [ + ('name', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + find_mock_result = [self.keypairs[0], exceptions.CommandError] + with mock.patch.object(utils, 'find_resource', + side_effect=find_mock_result) as find_mock: + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 public keys failed to delete.', + str(e)) + + find_mock.assert_any_call( + self.keypairs_mock, self.keypairs[0].name) + find_mock.assert_any_call(self.keypairs_mock, 'unexist_keypair') + + self.assertEqual(2, find_mock.call_count) + self.keypairs_mock.delete.assert_called_once_with( + self.keypairs[0].name + ) + + +class TestKeypairList(TestKeypair): + + # Return value of self.keypairs_mock.list(). + keypairs = compute_fakes.FakeKeypair.create_keypairs(count=1) + + columns = ( + "Name", + "Fingerprint" + ) + + data = (( + keypairs[0].name, + keypairs[0].fingerprint + ), ) + + def setUp(self): + super(TestKeypairList, self).setUp() + + self.keypairs_mock.list.return_value = self.keypairs + + # Get the command object to test + self.cmd = keypair.ListKeypair(self.app, None) + + def test_keypair_list_no_options(self): + arglist = [] + verifylist = [ + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + + self.keypairs_mock.list.assert_called_with() + + self.assertEqual(self.columns, columns) + self.assertEqual(tuple(self.data), tuple(data)) + + +class TestKeypairShow(TestKeypair): + + keypair = compute_fakes.FakeKeypair.create_one_keypair() + + def setUp(self): + super(TestKeypairShow, self).setUp() + + self.keypairs_mock.get.return_value = self.keypair + + self.cmd = keypair.ShowKeypair(self.app, None) + + self.columns = ( + "fingerprint", + "name", + "user_id" + ) + + self.data = ( + self.keypair.fingerprint, + self.keypair.name, + self.keypair.user_id + ) + + def test_show_no_options(self): + + arglist = [] + verifylist = [] + + # Missing required args should boil here + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_keypair_show(self): + # overwrite the setup one because we want to omit private_key + self.keypair = compute_fakes.FakeKeypair.create_one_keypair( + no_pri=True) + self.keypairs_mock.get.return_value = self.keypair + + self.data = ( + self.keypair.fingerprint, + self.keypair.name, + self.keypair.user_id + ) + + arglist = [ + self.keypair.name + ] + verifylist = [ + ('name', self.keypair.name) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_keypair_show_public(self): + + arglist = [ + '--public-key', + self.keypair.name + ] + verifylist = [ + ('public_key', True), + ('name', self.keypair.name) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual({}, columns) + self.assertEqual({}, data) diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py new file mode 100644 index 00000000..d4843f51 --- /dev/null +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -0,0 +1,1844 @@ +# Copyright 2013 Nebula 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 collections +import getpass +import mock +from mock import call + +from osc_lib import exceptions +from osc_lib import utils as common_utils + +from openstackclient.compute.v2 import server +from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes +from openstackclient.tests.unit.image.v2 import fakes as image_fakes +from openstackclient.tests.unit import utils +from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes + + +class TestServer(compute_fakes.TestComputev2): + + def setUp(self): + super(TestServer, self).setUp() + + # Get a shortcut to the compute client ServerManager Mock + self.servers_mock = self.app.client_manager.compute.servers + self.servers_mock.reset_mock() + + # Get a shortcut to the compute client ImageManager Mock + self.cimages_mock = self.app.client_manager.compute.images + self.cimages_mock.reset_mock() + + # Get a shortcut to the compute client FlavorManager Mock + self.flavors_mock = self.app.client_manager.compute.flavors + self.flavors_mock.reset_mock() + + # Get a shortcut to the compute client SecurityGroupManager Mock + self.security_groups_mock = \ + self.app.client_manager.compute.security_groups + self.security_groups_mock.reset_mock() + + # Get a shortcut to the image client ImageManager Mock + self.images_mock = self.app.client_manager.image.images + self.images_mock.reset_mock() + + # Get a shortcut to the volume client VolumeManager Mock + self.volumes_mock = self.app.client_manager.volume.volumes + self.volumes_mock.reset_mock() + + # Set object attributes to be tested. Could be overwritten in subclass. + self.attrs = {} + + # Set object methods to be tested. Could be overwritten in subclass. + self.methods = {} + + def setup_servers_mock(self, count): + servers = compute_fakes.FakeServer.create_servers(attrs=self.attrs, + methods=self.methods, + count=count) + + # This is the return value for utils.find_resource() + self.servers_mock.get = compute_fakes.FakeServer.get_servers(servers, + 0) + return servers + + def run_method_with_servers(self, method_name, server_count): + servers = self.setup_servers_mock(server_count) + + arglist = [] + verifylist = [] + + for s in servers: + arglist.append(s.id) + verifylist = [ + ('server', arglist), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + for s in servers: + method = getattr(s, method_name) + method.assert_called_with() + self.assertIsNone(result) + + +class TestServerAddFixedIP(TestServer): + + def setUp(self): + super(TestServerAddFixedIP, self).setUp() + + # Get a shortcut to the compute client ServerManager Mock + self.networks_mock = self.app.client_manager.compute.networks + + # Get the command object to test + self.cmd = server.AddFixedIP(self.app, None) + + # Set add_fixed_ip method to be tested. + self.methods = { + 'add_fixed_ip': None, + } + + def test_server_add_fixed_ip(self): + servers = self.setup_servers_mock(count=1) + network = compute_fakes.FakeNetwork.create_one_network() + self.networks_mock.get.return_value = network + + arglist = [ + servers[0].id, + network.id, + ] + verifylist = [ + ('server', servers[0].id), + ('network', network.id) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + servers[0].add_fixed_ip.assert_called_once_with( + network.id, + ) + self.assertIsNone(result) + + +class TestServerAddFloatingIP(TestServer): + + def setUp(self): + super(TestServerAddFloatingIP, self).setUp() + + # Get a shortcut to the compute client ServerManager Mock + self.networks_mock = self.app.client_manager.compute.networks + + # Get the command object to test + self.cmd = server.AddFloatingIP(self.app, None) + + # Set add_floating_ip method to be tested. + self.methods = { + 'add_floating_ip': None, + } + + def test_server_add_floating_ip(self): + servers = self.setup_servers_mock(count=1) + + arglist = [ + servers[0].id, + '1.2.3.4', + ] + verifylist = [ + ('server', servers[0].id), + ('ip_address', '1.2.3.4'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + servers[0].add_floating_ip.assert_called_once_with('1.2.3.4') + self.assertIsNone(result) + + +class TestServerAddSecurityGroup(TestServer): + + def setUp(self): + super(TestServerAddSecurityGroup, self).setUp() + + self.security_group = \ + compute_fakes.FakeSecurityGroup.create_one_security_group() + # This is the return value for utils.find_resource() for security group + self.security_groups_mock.get.return_value = self.security_group + + attrs = { + 'security_groups': [{'name': self.security_group.id}] + } + methods = { + 'add_security_group': None, + } + + self.server = compute_fakes.FakeServer.create_one_server( + attrs=attrs, + methods=methods + ) + # This is the return value for utils.find_resource() for server + self.servers_mock.get.return_value = self.server + + # Get the command object to test + self.cmd = server.AddServerSecurityGroup(self.app, None) + + def test_server_add_security_group(self): + arglist = [ + self.server.id, + self.security_group.id + ] + verifylist = [ + ('server', self.server.id), + ('group', self.security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.security_groups_mock.get.assert_called_with( + self.security_group.id, + ) + self.servers_mock.get.assert_called_with(self.server.id) + self.server.add_security_group.assert_called_with( + self.security_group.id, + ) + self.assertIsNone(result) + + +class TestServerCreate(TestServer): + + columns = ( + 'OS-EXT-STS:power_state', + 'addresses', + 'flavor', + 'id', + 'image', + 'name', + 'networks', + 'properties', + ) + + def datalist(self): + datalist = ( + server._format_servers_list_power_state( + getattr(self.new_server, 'OS-EXT-STS:power_state')), + '', + self.flavor.name + ' (' + self.new_server.flavor.get('id') + ')', + self.new_server.id, + self.image.name + ' (' + self.new_server.image.get('id') + ')', + self.new_server.name, + self.new_server.networks, + '', + ) + return datalist + + def setUp(self): + super(TestServerCreate, self).setUp() + + attrs = { + 'networks': {}, + } + self.new_server = compute_fakes.FakeServer.create_one_server( + attrs=attrs) + + # This is the return value for utils.find_resource(). + # This is for testing --wait option. + self.servers_mock.get.return_value = self.new_server + + self.servers_mock.create.return_value = self.new_server + + self.image = image_fakes.FakeImage.create_one_image() + self.cimages_mock.get.return_value = self.image + + self.flavor = compute_fakes.FakeFlavor.create_one_flavor() + self.flavors_mock.get.return_value = self.flavor + + self.volume = volume_fakes.FakeVolume.create_one_volume() + self.volumes_mock.get.return_value = self.volume + self.block_device_mapping = 'vda=' + self.volume.name + ':::0' + + # Get the command object to test + self.cmd = server.CreateServer(self.app, None) + + def test_server_create_no_options(self): + arglist = [ + self.new_server.name, + ] + verifylist = [ + ('server_name', self.new_server.name), + ] + + self.assertRaises(utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_server_create_minimal(self): + arglist = [ + '--image', 'image1', + '--flavor', 'flavor1', + self.new_server.name, + ] + verifylist = [ + ('image', 'image1'), + ('flavor', 'flavor1'), + ('config_drive', False), + ('server_name', self.new_server.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = dict( + meta=None, + files={}, + reservation_id=None, + min_count=1, + max_count=1, + security_groups=[], + userdata=None, + key_name=None, + availability_zone=None, + block_device_mapping={}, + nics=[], + scheduler_hints={}, + config_drive=None, + ) + # ServerManager.create(name, image, flavor, **kwargs) + self.servers_mock.create.assert_called_with( + self.new_server.name, + self.image, + self.flavor, + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist(), data) + + def test_server_create_with_network(self): + arglist = [ + '--image', 'image1', + '--flavor', 'flavor1', + '--nic', 'net-id=net1', + '--nic', 'port-id=port1', + self.new_server.name, + ] + verifylist = [ + ('image', 'image1'), + ('flavor', 'flavor1'), + ('nic', ['net-id=net1', 'port-id=port1']), + ('config_drive', False), + ('server_name', self.new_server.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + get_endpoints = mock.Mock() + get_endpoints.return_value = {'network': []} + self.app.client_manager.auth_ref = mock.Mock() + self.app.client_manager.auth_ref.service_catalog = mock.Mock() + self.app.client_manager.auth_ref.service_catalog.get_endpoints = ( + get_endpoints) + + find_network = mock.Mock() + find_port = mock.Mock() + network_client = self.app.client_manager.network + network_client.find_network = find_network + network_client.find_port = find_port + network_resource = mock.Mock() + network_resource.id = 'net1_uuid' + port_resource = mock.Mock() + port_resource.id = 'port1_uuid' + find_network.return_value = network_resource + find_port.return_value = port_resource + + # Mock sdk APIs. + _network = mock.Mock() + _network.id = 'net1_uuid' + _port = mock.Mock() + _port.id = 'port1_uuid' + find_network = mock.Mock() + find_port = mock.Mock() + find_network.return_value = _network + find_port.return_value = _port + self.app.client_manager.network.find_network = find_network + self.app.client_manager.network.find_port = find_port + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = dict( + meta=None, + files={}, + reservation_id=None, + min_count=1, + max_count=1, + security_groups=[], + userdata=None, + key_name=None, + availability_zone=None, + block_device_mapping={}, + nics=[{'net-id': 'net1_uuid', + 'v4-fixed-ip': '', + 'v6-fixed-ip': '', + 'port-id': ''}, + {'net-id': '', + 'v4-fixed-ip': '', + 'v6-fixed-ip': '', + 'port-id': 'port1_uuid'}], + scheduler_hints={}, + config_drive=None, + ) + # ServerManager.create(name, image, flavor, **kwargs) + self.servers_mock.create.assert_called_with( + self.new_server.name, + self.image, + self.flavor, + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist(), data) + + @mock.patch('openstackclient.compute.v2.server.io.open') + def test_server_create_userdata(self, mock_open): + mock_file = mock.MagicMock(name='File') + mock_open.return_value = mock_file + mock_open.read.return_value = '#!/bin/sh' + + arglist = [ + '--image', 'image1', + '--flavor', 'flavor1', + '--user-data', 'userdata.sh', + self.new_server.name, + ] + verifylist = [ + ('image', 'image1'), + ('flavor', 'flavor1'), + ('user_data', 'userdata.sh'), + ('config_drive', False), + ('server_name', self.new_server.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Ensure the userdata file is opened + mock_open.assert_called_with('userdata.sh') + + # Ensure the userdata file is closed + mock_file.close.assert_called_with() + + # Set expected values + kwargs = dict( + meta=None, + files={}, + reservation_id=None, + min_count=1, + max_count=1, + security_groups=[], + userdata=mock_file, + key_name=None, + availability_zone=None, + block_device_mapping={}, + nics=[], + scheduler_hints={}, + config_drive=None, + ) + # ServerManager.create(name, image, flavor, **kwargs) + self.servers_mock.create.assert_called_with( + self.new_server.name, + self.image, + self.flavor, + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist(), data) + + def test_server_create_with_block_device_mapping(self): + arglist = [ + '--image', 'image1', + '--flavor', self.flavor.id, + '--block-device-mapping', self.block_device_mapping, + self.new_server.name, + ] + verifylist = [ + ('image', 'image1'), + ('flavor', self.flavor.id), + ('block_device_mapping', [self.block_device_mapping]), + ('config_drive', False), + ('server_name', self.new_server.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # CreateServer.take_action() returns two tuples + columns, data = self.cmd.take_action(parsed_args) + + real_volume_mapping = ( + (self.block_device_mapping.split('=', 1)[1]).replace( + self.volume.name, + self.volume.id)) + + # Set expected values + kwargs = dict( + meta=None, + files={}, + reservation_id=None, + min_count=1, + max_count=1, + security_groups=[], + userdata=None, + key_name=None, + availability_zone=None, + block_device_mapping={ + 'vda': real_volume_mapping + }, + nics=[], + scheduler_hints={}, + config_drive=None, + ) + # ServerManager.create(name, image, flavor, **kwargs) + self.servers_mock.create.assert_called_with( + self.new_server.name, + self.image, + self.flavor, + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist(), data) + + +class TestServerDelete(TestServer): + + def setUp(self): + super(TestServerDelete, self).setUp() + + self.servers_mock.delete.return_value = None + + # Get the command object to test + self.cmd = server.DeleteServer(self.app, None) + + def test_server_delete_no_options(self): + servers = self.setup_servers_mock(count=1) + + arglist = [ + servers[0].id, + ] + verifylist = [ + ('server', [servers[0].id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.servers_mock.delete.assert_called_with(servers[0].id) + self.assertIsNone(result) + + def test_server_delete_multi_servers(self): + servers = self.setup_servers_mock(count=3) + + arglist = [] + verifylist = [] + + for s in servers: + arglist.append(s.id) + verifylist = [ + ('server', arglist), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + calls = [] + for s in servers: + calls.append(call(s.id)) + self.servers_mock.delete.assert_has_calls(calls) + self.assertIsNone(result) + + @mock.patch.object(common_utils, 'wait_for_delete', return_value=True) + def test_server_delete_wait_ok(self, mock_wait_for_delete): + servers = self.setup_servers_mock(count=1) + + arglist = [ + servers[0].id, '--wait' + ] + verifylist = [ + ('server', [servers[0].id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.servers_mock.delete.assert_called_with(servers[0].id) + mock_wait_for_delete.assert_called_once_with( + self.servers_mock, + servers[0].id, + callback=server._show_progress + ) + self.assertIsNone(result) + + @mock.patch.object(common_utils, 'wait_for_delete', return_value=False) + def test_server_delete_wait_fails(self, mock_wait_for_delete): + servers = self.setup_servers_mock(count=1) + + arglist = [ + servers[0].id, '--wait' + ] + verifylist = [ + ('server', [servers[0].id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises(SystemExit, self.cmd.take_action, parsed_args) + + self.servers_mock.delete.assert_called_with(servers[0].id) + mock_wait_for_delete.assert_called_once_with( + self.servers_mock, + servers[0].id, + callback=server._show_progress + ) + + +class TestServerDumpCreate(TestServer): + + def setUp(self): + super(TestServerDumpCreate, self).setUp() + + # Get the command object to test + self.cmd = server.CreateServerDump(self.app, None) + + # Set methods to be tested. + self.methods = { + 'trigger_crash_dump': None, + } + + def test_server_dump_one_server(self): + self.run_method_with_servers('trigger_crash_dump', 1) + + def test_server_dump_multi_servers(self): + self.run_method_with_servers('trigger_crash_dump', 3) + + +class TestServerList(TestServer): + + # Columns to be listed up. + columns = ( + 'ID', + 'Name', + 'Status', + 'Networks', + 'Image Name', + ) + columns_long = ( + 'ID', + 'Name', + 'Status', + 'Task State', + 'Power State', + 'Networks', + 'Image Name', + 'Image ID', + 'Availability Zone', + 'Host', + 'Properties', + ) + + def setUp(self): + super(TestServerList, self).setUp() + + self.search_opts = { + 'reservation_id': None, + 'ip': None, + 'ip6': None, + 'name': None, + 'instance_name': None, + 'status': None, + 'flavor': None, + 'image': None, + 'host': None, + 'tenant_id': None, + 'all_tenants': False, + 'user_id': None, + } + + # Default params of the core function of the command in the case of no + # commandline option specified. + self.kwargs = { + 'search_opts': self.search_opts, + 'marker': None, + 'limit': None, + } + + # The fake servers' attributes. Use the original attributes names in + # nova, not the ones printed by "server list" command. + self.attrs = { + 'status': 'ACTIVE', + 'OS-EXT-STS:task_state': 'None', + 'OS-EXT-STS:power_state': 0x01, # Running + 'networks': { + u'public': [u'10.20.30.40', u'2001:db8::5'] + }, + 'OS-EXT-AZ:availability_zone': 'availability-zone-xxx', + 'OS-EXT-SRV-ATTR:host': 'host-name-xxx', + 'Metadata': '', + } + + # The servers to be listed. + self.servers = self.setup_servers_mock(3) + self.servers_mock.list.return_value = self.servers + + self.image = image_fakes.FakeImage.create_one_image() + self.cimages_mock.get.return_value = self.image + + self.flavor = compute_fakes.FakeFlavor.create_one_flavor() + self.flavors_mock.get.return_value = self.flavor + + # Get the command object to test + self.cmd = server.ListServer(self.app, None) + + # Prepare data returned by fake Nova API. + self.data = [] + self.data_long = [] + + Image = collections.namedtuple('Image', 'id name') + self.images_mock.list.return_value = [ + Image(id=s.image['id'], name=self.image.name) + for s in self.servers + ] + + for s in self.servers: + self.data.append(( + s.id, + s.name, + s.status, + server._format_servers_list_networks(s.networks), + self.image.name, + )) + self.data_long.append(( + s.id, + s.name, + s.status, + getattr(s, 'OS-EXT-STS:task_state'), + server._format_servers_list_power_state( + getattr(s, 'OS-EXT-STS:power_state') + ), + server._format_servers_list_networks(s.networks), + self.image.name, + s.image['id'], + getattr(s, 'OS-EXT-AZ:availability_zone'), + getattr(s, 'OS-EXT-SRV-ATTR:host'), + s.Metadata, + )) + + def test_server_list_no_option(self): + arglist = [] + verifylist = [ + ('all_projects', False), + ('long', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.servers_mock.list.assert_called_with(**self.kwargs) + self.assertEqual(self.columns, columns) + self.assertEqual(tuple(self.data), tuple(data)) + + def test_server_list_long_option(self): + arglist = [ + '--long', + ] + verifylist = [ + ('all_projects', False), + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.servers_mock.list.assert_called_with(**self.kwargs) + self.assertEqual(self.columns_long, columns) + self.assertEqual(tuple(self.data_long), tuple(data)) + + def test_server_list_with_image(self): + + arglist = [ + '--image', self.image.id + ] + verifylist = [ + ('image', self.image.id) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.cimages_mock.get.assert_any_call(self.image.id) + + self.search_opts['image'] = self.image.id + self.servers_mock.list.assert_called_with(**self.kwargs) + + self.assertEqual(self.columns, columns) + self.assertEqual(tuple(self.data), tuple(data)) + + def test_server_list_with_flavor(self): + + arglist = [ + '--flavor', self.flavor.id + ] + verifylist = [ + ('flavor', self.flavor.id) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.flavors_mock.get.assert_called_with(self.flavor.id) + + self.search_opts['flavor'] = self.flavor.id + self.servers_mock.list.assert_called_with(**self.kwargs) + + self.assertEqual(self.columns, columns) + self.assertEqual(tuple(self.data), tuple(data)) + + +class TestServerLock(TestServer): + + def setUp(self): + super(TestServerLock, self).setUp() + + # Get the command object to test + self.cmd = server.LockServer(self.app, None) + + # Set methods to be tested. + self.methods = { + 'lock': None, + } + + def test_server_lock_one_server(self): + self.run_method_with_servers('lock', 1) + + def test_server_lock_multi_servers(self): + self.run_method_with_servers('lock', 3) + + +class TestServerPause(TestServer): + + def setUp(self): + super(TestServerPause, self).setUp() + + # Get the command object to test + self.cmd = server.PauseServer(self.app, None) + + # Set methods to be tested. + self.methods = { + 'pause': None, + } + + def test_server_pause_one_server(self): + self.run_method_with_servers('pause', 1) + + def test_server_pause_multi_servers(self): + self.run_method_with_servers('pause', 3) + + +class TestServerRebuild(TestServer): + + def setUp(self): + super(TestServerRebuild, self).setUp() + + # Return value for utils.find_resource for image + self.image = image_fakes.FakeImage.create_one_image() + self.cimages_mock.get.return_value = self.image + + # Fake the rebuilt new server. + new_server = compute_fakes.FakeServer.create_one_server() + + # Fake the server to be rebuilt. The IDs of them should be the same. + attrs = { + 'id': new_server.id, + 'image': { + 'id': self.image.id + }, + 'networks': {}, + 'adminPass': 'passw0rd', + } + methods = { + 'rebuild': new_server, + } + self.server = compute_fakes.FakeServer.create_one_server( + attrs=attrs, + methods=methods + ) + + # Return value for utils.find_resource for server. + self.servers_mock.get.return_value = self.server + + self.cmd = server.RebuildServer(self.app, None) + + def test_rebuild_with_current_image(self): + arglist = [ + self.server.id, + ] + verifylist = [ + ('server', self.server.id) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # Get the command object to test. + self.cmd.take_action(parsed_args) + + self.servers_mock.get.assert_called_with(self.server.id) + self.cimages_mock.get.assert_called_with(self.image.id) + self.server.rebuild.assert_called_with(self.image, None) + + def test_rebuild_with_current_image_and_password(self): + password = 'password-xxx' + arglist = [ + self.server.id, + '--password', password + ] + verifylist = [ + ('server', self.server.id), + ('password', password) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # Get the command object to test + self.cmd.take_action(parsed_args) + + self.servers_mock.get.assert_called_with(self.server.id) + self.cimages_mock.get.assert_called_with(self.image.id) + self.server.rebuild.assert_called_with(self.image, password) + + @mock.patch.object(common_utils, 'wait_for_status', return_value=True) + def test_rebuild_with_wait_ok(self, mock_wait_for_status): + arglist = [ + '--wait', + self.server.id, + ] + verifylist = [ + ('wait', True), + ('server', self.server.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # Get the command object to test. + self.cmd.take_action(parsed_args) + + # kwargs = dict(success_status=['active', 'verify_resize'],) + + mock_wait_for_status.assert_called_once_with( + self.servers_mock.get, + self.server.id, + callback=server._show_progress, + # **kwargs + ) + + self.servers_mock.get.assert_called_with(self.server.id) + self.cimages_mock.get.assert_called_with(self.image.id) + self.server.rebuild.assert_called_with(self.image, None) + + @mock.patch.object(common_utils, 'wait_for_status', return_value=False) + def test_rebuild_with_wait_fails(self, mock_wait_for_status): + arglist = [ + '--wait', + self.server.id, + ] + verifylist = [ + ('wait', True), + ('server', self.server.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises(SystemExit, self.cmd.take_action, parsed_args) + + mock_wait_for_status.assert_called_once_with( + self.servers_mock.get, + self.server.id, + callback=server._show_progress + ) + + self.servers_mock.get.assert_called_with(self.server.id) + self.cimages_mock.get.assert_called_with(self.image.id) + self.server.rebuild.assert_called_with(self.image, None) + + +class TestServerRemoveFixedIP(TestServer): + + def setUp(self): + super(TestServerRemoveFixedIP, self).setUp() + + # Get the command object to test + self.cmd = server.RemoveFixedIP(self.app, None) + + # Set unshelve method to be tested. + self.methods = { + 'remove_fixed_ip': None, + } + + def test_server_remove_fixed_ip(self): + servers = self.setup_servers_mock(count=1) + + arglist = [ + servers[0].id, + '1.2.3.4', + ] + verifylist = [ + ('server', servers[0].id), + ('ip_address', '1.2.3.4'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + servers[0].remove_fixed_ip.assert_called_once_with('1.2.3.4') + self.assertIsNone(result) + + +class TestServerRemoveFloatingIP(TestServer): + + def setUp(self): + super(TestServerRemoveFloatingIP, self).setUp() + + # Get the command object to test + self.cmd = server.RemoveFloatingIP(self.app, None) + + # Set unshelve method to be tested. + self.methods = { + 'remove_floating_ip': None, + } + + def test_server_remove_floating_ip(self): + servers = self.setup_servers_mock(count=1) + + arglist = [ + servers[0].id, + '1.2.3.4', + ] + verifylist = [ + ('server', servers[0].id), + ('ip_address', '1.2.3.4'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + servers[0].remove_floating_ip.assert_called_once_with('1.2.3.4') + self.assertIsNone(result) + + +class TestServerRemoveSecurityGroup(TestServer): + + def setUp(self): + super(TestServerRemoveSecurityGroup, self).setUp() + + self.security_group = \ + compute_fakes.FakeSecurityGroup.create_one_security_group() + # This is the return value for utils.find_resource() for security group + self.security_groups_mock.get.return_value = self.security_group + + attrs = { + 'security_groups': [{'name': self.security_group.id}] + } + methods = { + 'remove_security_group': None, + } + + self.server = compute_fakes.FakeServer.create_one_server( + attrs=attrs, + methods=methods + ) + # This is the return value for utils.find_resource() for server + self.servers_mock.get.return_value = self.server + + # Get the command object to test + self.cmd = server.RemoveServerSecurityGroup(self.app, None) + + def test_server_remove_security_group(self): + arglist = [ + self.server.id, + self.security_group.id + ] + verifylist = [ + ('server', self.server.id), + ('group', self.security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.security_groups_mock.get.assert_called_with( + self.security_group.id, + ) + self.servers_mock.get.assert_called_with(self.server.id) + self.server.remove_security_group.assert_called_with( + self.security_group.id, + ) + self.assertIsNone(result) + + +class TestServerResize(TestServer): + + def setUp(self): + super(TestServerResize, self).setUp() + + self.server = compute_fakes.FakeServer.create_one_server() + + # This is the return value for utils.find_resource() + self.servers_mock.get.return_value = self.server + + self.servers_mock.resize.return_value = None + self.servers_mock.confirm_resize.return_value = None + self.servers_mock.revert_resize.return_value = None + + # This is the return value for utils.find_resource() + self.flavors_get_return_value = \ + compute_fakes.FakeFlavor.create_one_flavor() + self.flavors_mock.get.return_value = self.flavors_get_return_value + + # Get the command object to test + self.cmd = server.ResizeServer(self.app, None) + + def test_server_resize_no_options(self): + arglist = [ + self.server.id, + ] + verifylist = [ + ('confirm', False), + ('revert', False), + ('server', self.server.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.servers_mock.get.assert_called_with(self.server.id) + + self.assertNotCalled(self.servers_mock.resize) + self.assertNotCalled(self.servers_mock.confirm_resize) + self.assertNotCalled(self.servers_mock.revert_resize) + self.assertIsNone(result) + + def test_server_resize(self): + arglist = [ + '--flavor', self.flavors_get_return_value.id, + self.server.id, + ] + verifylist = [ + ('flavor', self.flavors_get_return_value.id), + ('confirm', False), + ('revert', False), + ('server', self.server.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.servers_mock.get.assert_called_with(self.server.id) + self.flavors_mock.get.assert_called_with( + self.flavors_get_return_value.id, + ) + self.servers_mock.resize.assert_called_with( + self.server, + self.flavors_get_return_value, + ) + self.assertNotCalled(self.servers_mock.confirm_resize) + self.assertNotCalled(self.servers_mock.revert_resize) + self.assertIsNone(result) + + def test_server_resize_confirm(self): + arglist = [ + '--confirm', + self.server.id, + ] + verifylist = [ + ('confirm', True), + ('revert', False), + ('server', self.server.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.servers_mock.get.assert_called_with(self.server.id) + self.assertNotCalled(self.servers_mock.resize) + self.servers_mock.confirm_resize.assert_called_with(self.server) + self.assertNotCalled(self.servers_mock.revert_resize) + self.assertIsNone(result) + + def test_server_resize_revert(self): + arglist = [ + '--revert', + self.server.id, + ] + verifylist = [ + ('confirm', False), + ('revert', True), + ('server', self.server.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.servers_mock.get.assert_called_with(self.server.id) + self.assertNotCalled(self.servers_mock.resize) + self.assertNotCalled(self.servers_mock.confirm_resize) + self.servers_mock.revert_resize.assert_called_with(self.server) + self.assertIsNone(result) + + @mock.patch.object(common_utils, 'wait_for_status', return_value=True) + def test_server_resize_with_wait_ok(self, mock_wait_for_status): + + arglist = [ + '--flavor', self.flavors_get_return_value.id, + '--wait', + self.server.id, + ] + + verifylist = [ + ('flavor', self.flavors_get_return_value.id), + ('confirm', False), + ('revert', False), + ('wait', True), + ('server', self.server.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + self.servers_mock.get.assert_called_with( + self.server.id, + ) + + kwargs = dict(success_status=['active', 'verify_resize'],) + + mock_wait_for_status.assert_called_once_with( + self.servers_mock.get, + self.server.id, + callback=server._show_progress, + **kwargs + ) + + self.servers_mock.resize.assert_called_with( + self.server, + self.flavors_get_return_value + ) + self.assertNotCalled(self.servers_mock.confirm_resize) + self.assertNotCalled(self.servers_mock.revert_resize) + + @mock.patch.object(common_utils, 'wait_for_status', return_value=False) + def test_server_resize_with_wait_fails(self, mock_wait_for_status): + + arglist = [ + '--flavor', self.flavors_get_return_value.id, + '--wait', + self.server.id, + ] + + verifylist = [ + ('flavor', self.flavors_get_return_value.id), + ('confirm', False), + ('revert', False), + ('wait', True), + ('server', self.server.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises(SystemExit, self.cmd.take_action, parsed_args) + + self.servers_mock.get.assert_called_with( + self.server.id, + ) + + kwargs = dict(success_status=['active', 'verify_resize'],) + + mock_wait_for_status.assert_called_once_with( + self.servers_mock.get, + self.server.id, + callback=server._show_progress, + **kwargs + ) + + self.servers_mock.resize.assert_called_with( + self.server, + self.flavors_get_return_value + ) + + +class TestServerRestore(TestServer): + + def setUp(self): + super(TestServerRestore, self).setUp() + + # Get the command object to test + self.cmd = server.RestoreServer(self.app, None) + + # Set methods to be tested. + self.methods = { + 'restore': None, + } + + def test_server_restore_one_server(self): + self.run_method_with_servers('restore', 1) + + def test_server_restore_multi_servers(self): + self.run_method_with_servers('restore', 3) + + +class TestServerResume(TestServer): + + def setUp(self): + super(TestServerResume, self).setUp() + + # Get the command object to test + self.cmd = server.ResumeServer(self.app, None) + + # Set methods to be tested. + self.methods = { + 'resume': None, + } + + def test_server_resume_one_server(self): + self.run_method_with_servers('resume', 1) + + def test_server_resume_multi_servers(self): + self.run_method_with_servers('resume', 3) + + +class TestServerSet(TestServer): + + def setUp(self): + super(TestServerSet, self).setUp() + + self.methods = { + 'update': None, + 'reset_state': None, + 'change_password': None, + } + + self.fake_servers = self.setup_servers_mock(2) + + # Get the command object to test + self.cmd = server.SetServer(self.app, None) + + def test_server_set_no_option(self): + arglist = [ + 'foo_vm' + ] + verifylist = [ + ('server', 'foo_vm') + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.assertNotCalled(self.fake_servers[0].update) + self.assertNotCalled(self.fake_servers[0].reset_state) + self.assertNotCalled(self.fake_servers[0].change_password) + self.assertNotCalled(self.servers_mock.set_meta) + self.assertIsNone(result) + + def test_server_set_with_state(self): + for index, state in enumerate(['active', 'error']): + arglist = [ + '--state', state, + 'foo_vm', + ] + verifylist = [ + ('state', state), + ('server', 'foo_vm'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.fake_servers[index].reset_state.assert_called_once_with( + state=state) + self.assertIsNone(result) + + def test_server_set_with_invalid_state(self): + arglist = [ + '--state', 'foo_state', + 'foo_vm', + ] + verifylist = [ + ('state', 'foo_state'), + ('server', 'foo_vm'), + ] + self.assertRaises(utils.ParserException, + self.check_parser, + self.cmd, arglist, verifylist) + + def test_server_set_with_name(self): + arglist = [ + '--name', 'foo_name', + 'foo_vm', + ] + verifylist = [ + ('name', 'foo_name'), + ('server', 'foo_vm'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.fake_servers[0].update.assert_called_once_with(name='foo_name') + self.assertIsNone(result) + + def test_server_set_with_property(self): + arglist = [ + '--property', 'key1=value1', + '--property', 'key2=value2', + 'foo_vm', + ] + verifylist = [ + ('property', {'key1': 'value1', 'key2': 'value2'}), + ('server', 'foo_vm'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.servers_mock.set_meta.assert_called_once_with( + self.fake_servers[0], parsed_args.property) + self.assertIsNone(result) + + @mock.patch.object(getpass, 'getpass', + return_value=mock.sentinel.fake_pass) + def test_server_set_with_root_password(self, mock_getpass): + arglist = [ + '--root-password', + 'foo_vm', + ] + verifylist = [ + ('root_password', True), + ('server', 'foo_vm'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.fake_servers[0].change_password.assert_called_once_with( + mock.sentinel.fake_pass) + self.assertIsNone(result) + + +class TestServerShelve(TestServer): + + def setUp(self): + super(TestServerShelve, self).setUp() + + # Get the command object to test + self.cmd = server.ShelveServer(self.app, None) + + # Set shelve method to be tested. + self.methods = { + 'shelve': None, + } + + def test_shelve_one_server(self): + self.run_method_with_servers('shelve', 1) + + def test_shelve_multi_servers(self): + self.run_method_with_servers('shelve', 3) + + +class TestServerShow(TestServer): + + def setUp(self): + super(TestServerShow, self).setUp() + + self.image = image_fakes.FakeImage.create_one_image() + self.flavor = compute_fakes.FakeFlavor.create_one_flavor() + server_info = { + 'image': {'id': self.image.id}, + 'flavor': {'id': self.flavor.id}, + 'tenant_id': 'tenant-id-xxx', + 'networks': {'public': ['10.20.30.40', '2001:db8::f']}, + } + # Fake the server.diagnostics() method. The return value contains http + # response and data. The data is a dict. Sincce this method itself is + # faked, we don't need to fake everything of the return value exactly. + resp = mock.Mock() + resp.status_code = 200 + server_method = { + 'diagnostics': (resp, {'test': 'test'}), + } + self.server = compute_fakes.FakeServer.create_one_server( + attrs=server_info, methods=server_method) + + # This is the return value for utils.find_resource() + self.servers_mock.get.return_value = self.server + self.cimages_mock.get.return_value = self.image + self.flavors_mock.get.return_value = self.flavor + + # Get the command object to test + self.cmd = server.ShowServer(self.app, None) + + self.columns = ( + 'OS-EXT-STS:power_state', + 'addresses', + 'flavor', + 'id', + 'image', + 'name', + 'networks', + 'project_id', + 'properties', + ) + + self.data = ( + 'Running', + 'public=10.20.30.40, 2001:db8::f', + self.flavor.name + " (" + self.flavor.id + ")", + self.server.id, + self.image.name + " (" + self.image.id + ")", + self.server.name, + {'public': ['10.20.30.40', '2001:db8::f']}, + 'tenant-id-xxx', + '', + ) + + def test_show_no_options(self): + arglist = [] + verifylist = [] + + self.assertRaises(utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_show(self): + arglist = [ + self.server.name, + ] + verifylist = [ + ('diagnostics', False), + ('server', self.server.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_show_diagnostics(self): + arglist = [ + '--diagnostics', + self.server.name, + ] + verifylist = [ + ('diagnostics', True), + ('server', self.server.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(('test',), columns) + self.assertEqual(('test',), data) + + +class TestServerStart(TestServer): + + def setUp(self): + super(TestServerStart, self).setUp() + + # Get the command object to test + self.cmd = server.StartServer(self.app, None) + + # Set methods to be tested. + self.methods = { + 'start': None, + } + + def test_server_start_one_server(self): + self.run_method_with_servers('start', 1) + + def test_server_start_multi_servers(self): + self.run_method_with_servers('start', 3) + + +class TestServerStop(TestServer): + + def setUp(self): + super(TestServerStop, self).setUp() + + # Get the command object to test + self.cmd = server.StopServer(self.app, None) + + # Set methods to be tested. + self.methods = { + 'stop': None, + } + + def test_server_stop_one_server(self): + self.run_method_with_servers('stop', 1) + + def test_server_stop_multi_servers(self): + self.run_method_with_servers('stop', 3) + + +class TestServerSuspend(TestServer): + + def setUp(self): + super(TestServerSuspend, self).setUp() + + # Get the command object to test + self.cmd = server.SuspendServer(self.app, None) + + # Set methods to be tested. + self.methods = { + 'suspend': None, + } + + def test_server_suspend_one_server(self): + self.run_method_with_servers('suspend', 1) + + def test_server_suspend_multi_servers(self): + self.run_method_with_servers('suspend', 3) + + +class TestServerUnlock(TestServer): + + def setUp(self): + super(TestServerUnlock, self).setUp() + + # Get the command object to test + self.cmd = server.UnlockServer(self.app, None) + + # Set methods to be tested. + self.methods = { + 'unlock': None, + } + + def test_server_unlock_one_server(self): + self.run_method_with_servers('unlock', 1) + + def test_server_unlock_multi_servers(self): + self.run_method_with_servers('unlock', 3) + + +class TestServerUnpause(TestServer): + + def setUp(self): + super(TestServerUnpause, self).setUp() + + # Get the command object to test + self.cmd = server.UnpauseServer(self.app, None) + + # Set methods to be tested. + self.methods = { + 'unpause': None, + } + + def test_server_unpause_one_server(self): + self.run_method_with_servers('unpause', 1) + + def test_server_unpause_multi_servers(self): + self.run_method_with_servers('unpause', 3) + + +class TestServerUnset(TestServer): + + def setUp(self): + super(TestServerUnset, self).setUp() + + self.fake_server = self.setup_servers_mock(1)[0] + + # Get the command object to test + self.cmd = server.UnsetServer(self.app, None) + + def test_server_unset_no_option(self): + arglist = [ + 'foo_vm', + ] + verifylist = [ + ('server', 'foo_vm'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.assertNotCalled(self.servers_mock.delete_meta) + self.assertIsNone(result) + + def test_server_unset_with_property(self): + arglist = [ + '--property', 'key1', + '--property', 'key2', + 'foo_vm', + ] + verifylist = [ + ('property', ['key1', 'key2']), + ('server', 'foo_vm'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.servers_mock.delete_meta.assert_called_once_with( + self.fake_server, ['key1', 'key2']) + self.assertIsNone(result) + + +class TestServerUnshelve(TestServer): + + def setUp(self): + super(TestServerUnshelve, self).setUp() + + # Get the command object to test + self.cmd = server.UnshelveServer(self.app, None) + + # Set unshelve method to be tested. + self.methods = { + 'unshelve': None, + } + + def test_unshelve_one_server(self): + self.run_method_with_servers('unshelve', 1) + + def test_unshelve_multi_servers(self): + self.run_method_with_servers('unshelve', 3) + + +class TestServerGeneral(TestServer): + OLD = { + 'private': [ + { + 'addr': '192.168.0.3', + 'version': 4, + }, + ] + } + NEW = { + 'foo': [ + { + 'OS-EXT-IPS-MAC:mac_addr': 'fa:16:3e:93:b3:01', + 'version': 4, + 'addr': '10.10.1.2', + 'OS-EXT-IPS:type': 'fixed', + }, + { + 'OS-EXT-IPS-MAC:mac_addr': 'fa:16:3e:93:b3:02', + 'version': 6, + 'addr': '0:0:0:0:0:ffff:a0a:103', + 'OS-EXT-IPS:type': 'floating', + }, + ] + } + ODD = {'jenkins': ['10.3.3.18', '124.12.125.4']} + + def test_get_ip_address(self): + self.assertEqual("192.168.0.3", + server._get_ip_address(self.OLD, 'private', [4, 6])) + self.assertEqual("10.10.1.2", + server._get_ip_address(self.NEW, 'fixed', [4, 6])) + self.assertEqual("10.10.1.2", + server._get_ip_address(self.NEW, 'private', [4, 6])) + self.assertEqual("0:0:0:0:0:ffff:a0a:103", + server._get_ip_address(self.NEW, 'public', [6])) + self.assertEqual("0:0:0:0:0:ffff:a0a:103", + server._get_ip_address(self.NEW, 'floating', [6])) + self.assertEqual("124.12.125.4", + server._get_ip_address(self.ODD, 'public', [4, 6])) + self.assertEqual("10.3.3.18", + server._get_ip_address(self.ODD, 'private', [4, 6])) + self.assertRaises(exceptions.CommandError, + server._get_ip_address, self.NEW, 'public', [4]) + self.assertRaises(exceptions.CommandError, + server._get_ip_address, self.NEW, 'admin', [4]) + self.assertRaises(exceptions.CommandError, + server._get_ip_address, self.OLD, 'public', [4, 6]) + self.assertRaises(exceptions.CommandError, + server._get_ip_address, self.OLD, 'private', [6]) + + def test_format_servers_list_power_state(self): + self.assertEqual("NOSTATE", + server._format_servers_list_power_state(0x00)) + self.assertEqual("Running", + server._format_servers_list_power_state(0x01)) + self.assertEqual("", + server._format_servers_list_power_state(0x02)) + self.assertEqual("Paused", + server._format_servers_list_power_state(0x03)) + self.assertEqual("Shutdown", + server._format_servers_list_power_state(0x04)) + self.assertEqual("", + server._format_servers_list_power_state(0x05)) + self.assertEqual("Crashed", + server._format_servers_list_power_state(0x06)) + self.assertEqual("Suspended", + server._format_servers_list_power_state(0x07)) + self.assertEqual("N/A", + server._format_servers_list_power_state(0x08)) + + def test_format_servers_list_networks(self): + # Setup network info to test. + networks = { + u'public': [u'10.20.30.40', u'2001:db8::f'], + u'private': [u'2001:db8::f', u'10.20.30.40'], + } + + # Prepare expected data. + # Since networks is a dict, whose items are in random order, there + # could be two results after formatted. + data_1 = (u'private=2001:db8::f, 10.20.30.40; ' + u'public=10.20.30.40, 2001:db8::f') + data_2 = (u'public=10.20.30.40, 2001:db8::f; ' + u'private=2001:db8::f, 10.20.30.40') + + # Call _format_servers_list_networks(). + networks_format = server._format_servers_list_networks(networks) + + msg = ('Network string is not formatted correctly.\n' + 'reference = %s or %s\n' + 'actual = %s\n' % + (data_1, data_2, networks_format)) + self.assertIn(networks_format, (data_1, data_2), msg) + + @mock.patch('osc_lib.utils.find_resource') + def test_prep_server_detail(self, find_resource): + # Setup mock method return value. utils.find_resource() will be called + # three times in _prep_server_detail(): + # - The first time, return server info. + # - The second time, return image info. + # - The third time, return flavor info. + _image = image_fakes.FakeImage.create_one_image() + _flavor = compute_fakes.FakeFlavor.create_one_flavor() + server_info = { + 'image': {u'id': _image.id}, + 'flavor': {u'id': _flavor.id}, + 'tenant_id': u'tenant-id-xxx', + 'networks': {u'public': [u'10.20.30.40', u'2001:db8::f']}, + 'links': u'http://xxx.yyy.com', + } + _server = compute_fakes.FakeServer.create_one_server(attrs=server_info) + find_resource.side_effect = [_server, _image, _flavor] + + # Prepare result data. + info = { + 'id': _server.id, + 'name': _server.name, + 'addresses': u'public=10.20.30.40, 2001:db8::f', + 'flavor': u'%s (%s)' % (_flavor.name, _flavor.id), + 'image': u'%s (%s)' % (_image.name, _image.id), + 'project_id': u'tenant-id-xxx', + 'properties': '', + 'OS-EXT-STS:power_state': server._format_servers_list_power_state( + getattr(_server, 'OS-EXT-STS:power_state')), + } + + # Call _prep_server_detail(). + server_detail = server._prep_server_detail( + self.app.client_manager.compute, + _server + ) + # 'networks' is used to create _server. Remove it. + server_detail.pop('networks') + + # Check the results. + self.assertEqual(info, server_detail) diff --git a/openstackclient/tests/unit/compute/v2/test_server_backup.py b/openstackclient/tests/unit/compute/v2/test_server_backup.py new file mode 100644 index 00000000..9aa63fc7 --- /dev/null +++ b/openstackclient/tests/unit/compute/v2/test_server_backup.py @@ -0,0 +1,271 @@ +# 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 osc_lib import exceptions +from osc_lib import utils as common_utils + +from openstackclient.compute.v2 import server_backup +from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes +from openstackclient.tests.unit.image.v2 import fakes as image_fakes + + +class TestServerBackup(compute_fakes.TestComputev2): + + def setUp(self): + super(TestServerBackup, self).setUp() + + # Get a shortcut to the compute client ServerManager Mock + self.servers_mock = self.app.client_manager.compute.servers + self.servers_mock.reset_mock() + + # Get a shortcut to the image client ImageManager Mock + self.images_mock = self.app.client_manager.image.images + self.images_mock.reset_mock() + + # Set object attributes to be tested. Could be overwritten in subclass. + self.attrs = {} + + # Set object methods to be tested. Could be overwritten in subclass. + self.methods = {} + + def setup_servers_mock(self, count): + servers = compute_fakes.FakeServer.create_servers( + attrs=self.attrs, + methods=self.methods, + count=count, + ) + + # This is the return value for utils.find_resource() + self.servers_mock.get = compute_fakes.FakeServer.get_servers( + servers, + 0, + ) + return servers + + +class TestServerBackupCreate(TestServerBackup): + + # Just return whatever Image is testing with these days + def image_columns(self, image): + columnlist = tuple(sorted(image.keys())) + return columnlist + + def image_data(self, image): + datalist = ( + image['id'], + image['name'], + image['owner'], + image['protected'], + 'active', + common_utils.format_list(image.get('tags')), + image['visibility'], + ) + return datalist + + def setUp(self): + super(TestServerBackupCreate, self).setUp() + + # Get the command object to test + self.cmd = server_backup.CreateServerBackup(self.app, None) + + self.methods = { + 'backup': None, + } + + def setup_images_mock(self, count, servers=None): + if servers: + images = image_fakes.FakeImage.create_images( + attrs={ + 'name': servers[0].name, + 'status': 'active', + }, + count=count, + ) + else: + images = image_fakes.FakeImage.create_images( + attrs={ + 'status': 'active', + }, + count=count, + ) + + self.images_mock.get = mock.MagicMock(side_effect=images) + return images + + def test_server_backup_defaults(self): + servers = self.setup_servers_mock(count=1) + images = self.setup_images_mock(count=1, servers=servers) + + arglist = [ + servers[0].id, + ] + verifylist = [ + ('name', None), + ('type', None), + ('rotate', None), + ('wait', False), + ('server', servers[0].id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # ServerManager.backup(server, backup_name, backup_type, rotation) + self.servers_mock.backup.assert_called_with( + servers[0].id, + servers[0].name, + '', + 1, + ) + + self.assertEqual(self.image_columns(images[0]), columns) + self.assertEqual(self.image_data(images[0]), data) + + def test_server_backup_create_options(self): + servers = self.setup_servers_mock(count=1) + images = self.setup_images_mock(count=1, servers=servers) + + arglist = [ + '--name', 'image', + '--type', 'daily', + '--rotate', '2', + servers[0].id, + ] + verifylist = [ + ('name', 'image'), + ('type', 'daily'), + ('rotate', 2), + ('server', servers[0].id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # ServerManager.backup(server, backup_name, backup_type, rotation) + self.servers_mock.backup.assert_called_with( + servers[0].id, + 'image', + 'daily', + 2, + ) + + self.assertEqual(self.image_columns(images[0]), columns) + self.assertEqual(self.image_data(images[0]), data) + + @mock.patch.object(common_utils, 'wait_for_status', return_value=False) + def test_server_backup_wait_fail(self, mock_wait_for_status): + servers = self.setup_servers_mock(count=1) + images = image_fakes.FakeImage.create_images( + attrs={ + 'name': servers[0].name, + 'status': 'active', + }, + count=5, + ) + + self.images_mock.get = mock.MagicMock( + side_effect=images, + ) + + arglist = [ + '--name', 'image', + '--type', 'daily', + '--wait', + servers[0].id, + ] + verifylist = [ + ('name', 'image'), + ('type', 'daily'), + ('wait', True), + ('server', servers[0].id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + + # ServerManager.backup(server, backup_name, backup_type, rotation) + self.servers_mock.backup.assert_called_with( + servers[0].id, + 'image', + 'daily', + 1, + ) + + mock_wait_for_status.assert_called_once_with( + self.images_mock.get, + images[0].id, + callback=mock.ANY + ) + + @mock.patch.object(common_utils, 'wait_for_status', return_value=True) + def test_server_backup_wait_ok(self, mock_wait_for_status): + servers = self.setup_servers_mock(count=1) + images = image_fakes.FakeImage.create_images( + attrs={ + 'name': servers[0].name, + 'status': 'active', + }, + count=5, + ) + + self.images_mock.get = mock.MagicMock( + side_effect=images, + ) + + arglist = [ + '--name', 'image', + '--type', 'daily', + '--wait', + servers[0].id, + ] + verifylist = [ + ('name', 'image'), + ('type', 'daily'), + ('wait', True), + ('server', servers[0].id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # ServerManager.backup(server, backup_name, backup_type, rotation) + self.servers_mock.backup.assert_called_with( + servers[0].id, + 'image', + 'daily', + 1, + ) + + mock_wait_for_status.assert_called_once_with( + self.images_mock.get, + images[0].id, + callback=mock.ANY + ) + + self.assertEqual(self.image_columns(images[0]), columns) + self.assertEqual(self.image_data(images[0]), data) diff --git a/openstackclient/tests/unit/compute/v2/test_server_group.py b/openstackclient/tests/unit/compute/v2/test_server_group.py new file mode 100644 index 00000000..d474f41d --- /dev/null +++ b/openstackclient/tests/unit/compute/v2/test_server_group.py @@ -0,0 +1,284 @@ +# Copyright 2016 Huawei, 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 mock + +from osc_lib import exceptions +from osc_lib import utils + +from openstackclient.compute.v2 import server_group +from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes +from openstackclient.tests.unit import utils as tests_utils + + +class TestServerGroup(compute_fakes.TestComputev2): + + fake_server_group = compute_fakes.FakeServerGroup.create_one_server_group() + + columns = ( + 'id', + 'members', + 'name', + 'policies', + 'project_id', + 'user_id', + ) + + data = ( + fake_server_group.id, + utils.format_list(fake_server_group.members), + fake_server_group.name, + utils.format_list(fake_server_group.policies), + fake_server_group.project_id, + fake_server_group.user_id, + ) + + def setUp(self): + super(TestServerGroup, self).setUp() + + # Get a shortcut to the ServerGroupsManager Mock + self.server_groups_mock = self.app.client_manager.compute.server_groups + self.server_groups_mock.reset_mock() + + +class TestServerGroupCreate(TestServerGroup): + + def setUp(self): + super(TestServerGroupCreate, self).setUp() + + self.server_groups_mock.create.return_value = self.fake_server_group + self.cmd = server_group.CreateServerGroup(self.app, None) + + def test_server_group_create(self): + arglist = [ + '--policy', 'affinity', + 'affinity_group', + ] + verifylist = [ + ('policy', ['affinity']), + ('name', 'affinity_group'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.server_groups_mock.create.assert_called_once_with( + name=parsed_args.name, + policies=parsed_args.policy, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_server_group_create_with_multiple_policies(self): + arglist = [ + '--policy', 'affinity', + '--policy', 'soft-affinity', + 'affinity_group', + ] + verifylist = [ + ('policy', ['affinity', 'soft-affinity']), + ('name', 'affinity_group'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.server_groups_mock.create.assert_called_once_with( + name=parsed_args.name, + policies=parsed_args.policy, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_server_group_create_no_policy(self): + arglist = [ + 'affinity_group', + ] + verifylist = None + self.assertRaises(tests_utils.ParserException, + self.check_parser, + self.cmd, + arglist, + verifylist) + + +class TestServerGroupDelete(TestServerGroup): + + def setUp(self): + super(TestServerGroupDelete, self).setUp() + + self.server_groups_mock.get.return_value = self.fake_server_group + self.cmd = server_group.DeleteServerGroup(self.app, None) + + def test_server_group_delete(self): + arglist = [ + 'affinity_group', + ] + verifylist = [ + ('server_group', ['affinity_group']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.server_groups_mock.get.assert_called_once_with('affinity_group') + self.server_groups_mock.delete.assert_called_once_with( + self.fake_server_group.id + ) + self.assertIsNone(result) + + def test_server_group_multiple_delete(self): + arglist = [ + 'affinity_group', + 'anti_affinity_group' + ] + verifylist = [ + ('server_group', ['affinity_group', 'anti_affinity_group']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.server_groups_mock.get.assert_any_call('affinity_group') + self.server_groups_mock.get.assert_any_call('anti_affinity_group') + self.server_groups_mock.delete.assert_called_with( + self.fake_server_group.id + ) + self.assertEqual(2, self.server_groups_mock.get.call_count) + self.assertEqual(2, self.server_groups_mock.delete.call_count) + self.assertIsNone(result) + + def test_server_group_delete_no_input(self): + arglist = [] + verifylist = None + self.assertRaises(tests_utils.ParserException, + self.check_parser, + self.cmd, + arglist, + verifylist) + + def test_server_group_multiple_delete_with_exception(self): + arglist = [ + 'affinity_group', + 'anti_affinity_group' + ] + verifylist = [ + ('server_group', ['affinity_group', 'anti_affinity_group']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + find_mock_result = [self.fake_server_group, exceptions.CommandError] + with mock.patch.object(utils, 'find_resource', + side_effect=find_mock_result) as find_mock: + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 server groups failed to delete.', + str(e)) + + find_mock.assert_any_call(self.server_groups_mock, + 'affinity_group') + find_mock.assert_any_call(self.server_groups_mock, + 'anti_affinity_group') + + self.assertEqual(2, find_mock.call_count) + self.server_groups_mock.delete.assert_called_once_with( + self.fake_server_group.id + ) + + +class TestServerGroupList(TestServerGroup): + + list_columns = ( + 'ID', + 'Name', + 'Policies', + ) + + list_columns_long = ( + 'ID', + 'Name', + 'Policies', + 'Members', + 'Project Id', + 'User Id', + ) + + list_data = (( + TestServerGroup.fake_server_group.id, + TestServerGroup.fake_server_group.name, + utils.format_list(TestServerGroup.fake_server_group.policies), + ),) + + list_data_long = (( + TestServerGroup.fake_server_group.id, + TestServerGroup.fake_server_group.name, + utils.format_list(TestServerGroup.fake_server_group.policies), + utils.format_list(TestServerGroup.fake_server_group.members), + TestServerGroup.fake_server_group.project_id, + TestServerGroup.fake_server_group.user_id, + ),) + + def setUp(self): + super(TestServerGroupList, self).setUp() + + self.server_groups_mock.list.return_value = [self.fake_server_group] + self.cmd = server_group.ListServerGroup(self.app, None) + + def test_server_group_list(self): + arglist = [] + verifylist = [ + ('all_projects', False), + ('long', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.server_groups_mock.list.assert_called_once_with(False) + + self.assertEqual(self.list_columns, columns) + self.assertEqual(self.list_data, tuple(data)) + + def test_server_group_list_with_all_projects_and_long(self): + arglist = [ + '--all-projects', + '--long', + ] + verifylist = [ + ('all_projects', True), + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.server_groups_mock.list.assert_called_once_with(True) + + self.assertEqual(self.list_columns_long, columns) + self.assertEqual(self.list_data_long, tuple(data)) + + +class TestServerGroupShow(TestServerGroup): + + def setUp(self): + super(TestServerGroupShow, self).setUp() + + self.server_groups_mock.get.return_value = self.fake_server_group + self.cmd = server_group.ShowServerGroup(self.app, None) + + def test_server_group_show(self): + arglist = [ + 'affinity_group', + ] + verifylist = [ + ('server_group', 'affinity_group'), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/compute/v2/test_server_image.py b/openstackclient/tests/unit/compute/v2/test_server_image.py new file mode 100644 index 00000000..f53f08e6 --- /dev/null +++ b/openstackclient/tests/unit/compute/v2/test_server_image.py @@ -0,0 +1,228 @@ +# 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 osc_lib import exceptions +from osc_lib import utils as common_utils + +from openstackclient.compute.v2 import server_image +from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes +from openstackclient.tests.unit.image.v2 import fakes as image_fakes + + +class TestServerImage(compute_fakes.TestComputev2): + + def setUp(self): + super(TestServerImage, self).setUp() + + # Get a shortcut to the compute client ServerManager Mock + self.servers_mock = self.app.client_manager.compute.servers + self.servers_mock.reset_mock() + + # Get a shortcut to the image client ImageManager Mock + self.images_mock = self.app.client_manager.image.images + self.images_mock.reset_mock() + + # Set object attributes to be tested. Could be overwritten in subclass. + self.attrs = {} + + # Set object methods to be tested. Could be overwritten in subclass. + self.methods = {} + + def setup_servers_mock(self, count): + servers = compute_fakes.FakeServer.create_servers( + attrs=self.attrs, + methods=self.methods, + count=count, + ) + + # This is the return value for utils.find_resource() + self.servers_mock.get = compute_fakes.FakeServer.get_servers( + servers, + 0, + ) + return servers + + +class TestServerImageCreate(TestServerImage): + + def image_columns(self, image): + columnlist = tuple(sorted(image.keys())) + return columnlist + + def image_data(self, image): + datalist = ( + image['id'], + image['name'], + image['owner'], + image['protected'], + 'active', + common_utils.format_list(image.get('tags')), + image['visibility'], + ) + return datalist + + def setUp(self): + super(TestServerImageCreate, self).setUp() + + # Get the command object to test + self.cmd = server_image.CreateServerImage(self.app, None) + + self.methods = { + 'create_image': None, + } + + def setup_images_mock(self, count, servers=None): + if servers: + images = image_fakes.FakeImage.create_images( + attrs={ + 'name': servers[0].name, + 'status': 'active', + }, + count=count, + ) + else: + images = image_fakes.FakeImage.create_images( + attrs={ + 'status': 'active', + }, + count=count, + ) + + self.images_mock.get = mock.MagicMock(side_effect=images) + self.servers_mock.create_image = mock.MagicMock( + return_value=images[0].id, + ) + return images + + def test_server_image_create_defaults(self): + servers = self.setup_servers_mock(count=1) + images = self.setup_images_mock(count=1, servers=servers) + + arglist = [ + servers[0].id, + ] + verifylist = [ + ('server', servers[0].id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # ServerManager.create_image(server, image_name, metadata=) + self.servers_mock.create_image.assert_called_with( + servers[0].id, + servers[0].name, + ) + + self.assertEqual(self.image_columns(images[0]), columns) + self.assertEqual(self.image_data(images[0]), data) + + def test_server_image_create_options(self): + servers = self.setup_servers_mock(count=1) + images = self.setup_images_mock(count=1, servers=servers) + + arglist = [ + '--name', 'img-nam', + servers[0].id, + ] + verifylist = [ + ('name', 'img-nam'), + ('server', servers[0].id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # ServerManager.create_image(server, image_name, metadata=) + self.servers_mock.create_image.assert_called_with( + servers[0].id, + 'img-nam', + ) + + self.assertEqual(self.image_columns(images[0]), columns) + self.assertEqual(self.image_data(images[0]), data) + + @mock.patch.object(common_utils, 'wait_for_status', return_value=False) + def test_server_create_image_wait_fail(self, mock_wait_for_status): + servers = self.setup_servers_mock(count=1) + images = self.setup_images_mock(count=1, servers=servers) + + arglist = [ + '--wait', + servers[0].id, + ] + verifylist = [ + ('wait', True), + ('server', servers[0].id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + + # ServerManager.create_image(server, image_name, metadata=) + self.servers_mock.create_image.assert_called_with( + servers[0].id, + servers[0].name, + ) + + mock_wait_for_status.assert_called_once_with( + self.images_mock.get, + images[0].id, + callback=mock.ANY + ) + + @mock.patch.object(common_utils, 'wait_for_status', return_value=True) + def test_server_create_image_wait_ok(self, mock_wait_for_status): + servers = self.setup_servers_mock(count=1) + images = self.setup_images_mock(count=1, servers=servers) + + arglist = [ + '--wait', + servers[0].id, + ] + verifylist = [ + ('wait', True), + ('server', servers[0].id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # ServerManager.create_image(server, image_name, metadata=) + self.servers_mock.create_image.assert_called_with( + servers[0].id, + servers[0].name, + ) + + mock_wait_for_status.assert_called_once_with( + self.images_mock.get, + images[0].id, + callback=mock.ANY + ) + + self.assertEqual(self.image_columns(images[0]), columns) + self.assertEqual(self.image_data(images[0]), data) diff --git a/openstackclient/tests/unit/compute/v2/test_service.py b/openstackclient/tests/unit/compute/v2/test_service.py new file mode 100644 index 00000000..1fd3b7d5 --- /dev/null +++ b/openstackclient/tests/unit/compute/v2/test_service.py @@ -0,0 +1,410 @@ +# Copyright 2015 Mirantis, 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 mock +from mock import call + +from osc_lib import exceptions + +from openstackclient.compute.v2 import service +from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes + + +class TestService(compute_fakes.TestComputev2): + + def setUp(self): + super(TestService, self).setUp() + + # Get a shortcut to the ServiceManager Mock + self.service_mock = self.app.client_manager.compute.services + self.service_mock.reset_mock() + + +class TestServiceDelete(TestService): + + services = compute_fakes.FakeService.create_services(count=2) + + def setUp(self): + super(TestServiceDelete, self).setUp() + + self.service_mock.delete.return_value = None + + # Get the command object to test + self.cmd = service.DeleteService(self.app, None) + + def test_service_delete(self): + arglist = [ + self.services[0].binary, + ] + verifylist = [ + ('service', [self.services[0].binary]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.service_mock.delete.assert_called_with( + self.services[0].binary, + ) + self.assertIsNone(result) + + def test_multi_services_delete(self): + arglist = [] + for s in self.services: + arglist.append(s.binary) + verifylist = [ + ('service', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + calls = [] + for s in self.services: + calls.append(call(s.binary)) + self.service_mock.delete.assert_has_calls(calls) + self.assertIsNone(result) + + def test_multi_services_delete_with_exception(self): + arglist = [ + self.services[0].binary, + 'unexist_service', + ] + verifylist = [ + ('service', arglist) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + delete_mock_result = [None, exceptions.CommandError] + self.service_mock.delete = ( + mock.MagicMock(side_effect=delete_mock_result) + ) + + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual( + '1 of 2 compute services failed to delete.', str(e)) + + self.service_mock.delete.assert_any_call(self.services[0].binary) + self.service_mock.delete.assert_any_call('unexist_service') + + +class TestServiceList(TestService): + + service = compute_fakes.FakeService.create_one_service() + + columns = ( + 'ID', + 'Binary', + 'Host', + 'Zone', + 'Status', + 'State', + 'Updated At', + ) + columns_long = columns + ( + 'Disabled Reason', + ) + + data = [( + service.id, + service.binary, + service.host, + service.zone, + service.status, + service.state, + service.updated_at, + )] + data_long = [data[0] + (service.disabled_reason, )] + + def setUp(self): + super(TestServiceList, self).setUp() + + self.service_mock.list.return_value = [self.service] + + # Get the command object to test + self.cmd = service.ListService(self.app, None) + + def test_service_list(self): + arglist = [ + '--host', self.service.host, + '--service', self.service.binary, + ] + verifylist = [ + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.service_mock.list.assert_called_with( + self.service.host, + self.service.binary, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_service_list_with_long_option(self): + arglist = [ + '--host', self.service.host, + '--service', self.service.binary, + '--long' + ] + verifylist = [ + ('host', self.service.host), + ('service', self.service.binary), + ('long', True) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.service_mock.list.assert_called_with( + self.service.host, + self.service.binary, + ) + + self.assertEqual(self.columns_long, columns) + self.assertEqual(self.data_long, list(data)) + + +class TestServiceSet(TestService): + + def setUp(self): + super(TestServiceSet, self).setUp() + + self.service = compute_fakes.FakeService.create_one_service() + + self.service_mock.enable.return_value = self.service + self.service_mock.disable.return_value = self.service + + self.cmd = service.SetService(self.app, None) + + def test_set_nothing(self): + arglist = [ + self.service.host, + self.service.binary, + ] + verifylist = [ + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.service_mock.enable.assert_not_called() + self.service_mock.disable.assert_not_called() + self.service_mock.disable_log_reason.assert_not_called() + self.assertIsNone(result) + + def test_service_set_enable(self): + arglist = [ + '--enable', + self.service.host, + self.service.binary, + ] + verifylist = [ + ('enable', True), + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.service_mock.enable.assert_called_with( + self.service.host, + self.service.binary + ) + self.assertIsNone(result) + + def test_service_set_disable(self): + arglist = [ + '--disable', + self.service.host, + self.service.binary, + ] + verifylist = [ + ('disable', True), + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.service_mock.disable.assert_called_with( + self.service.host, + self.service.binary + ) + self.assertIsNone(result) + + def test_service_set_disable_with_reason(self): + reason = 'earthquake' + arglist = [ + '--disable', + '--disable-reason', reason, + self.service.host, + self.service.binary, + ] + verifylist = [ + ('disable', True), + ('disable_reason', reason), + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.service_mock.disable_log_reason.assert_called_with( + self.service.host, + self.service.binary, + reason + ) + self.assertIsNone(result) + + def test_service_set_only_with_disable_reason(self): + reason = 'earthquake' + arglist = [ + '--disable-reason', reason, + self.service.host, + self.service.binary, + ] + verifylist = [ + ('disable_reason', reason), + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + try: + self.cmd.take_action(parsed_args) + self.fail("CommandError should be raised.") + except exceptions.CommandError as e: + self.assertEqual("Cannot specify option --disable-reason without " + "--disable specified.", str(e)) + + def test_service_set_enable_with_disable_reason(self): + reason = 'earthquake' + arglist = [ + '--enable', + '--disable-reason', reason, + self.service.host, + self.service.binary, + ] + verifylist = [ + ('enable', True), + ('disable_reason', reason), + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + try: + self.cmd.take_action(parsed_args) + self.fail("CommandError should be raised.") + except exceptions.CommandError as e: + self.assertEqual("Cannot specify option --disable-reason without " + "--disable specified.", str(e)) + + def test_service_set_state_up(self): + arglist = [ + '--up', + self.service.host, + self.service.binary, + ] + verifylist = [ + ('up', True), + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.service_mock.force_down.assert_called_once_with( + self.service.host, self.service.binary, force_down=False) + self.assertNotCalled(self.service_mock.enable) + self.assertNotCalled(self.service_mock.disable) + self.assertIsNone(result) + + def test_service_set_state_down(self): + arglist = [ + '--down', + self.service.host, + self.service.binary, + ] + verifylist = [ + ('down', True), + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.service_mock.force_down.assert_called_once_with( + self.service.host, self.service.binary, force_down=True) + self.assertNotCalled(self.service_mock.enable) + self.assertNotCalled(self.service_mock.disable) + self.assertIsNone(result) + + def test_service_set_enable_and_state_down(self): + arglist = [ + '--enable', + '--down', + self.service.host, + self.service.binary, + ] + verifylist = [ + ('enable', True), + ('down', True), + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.service_mock.enable.assert_called_once_with( + self.service.host, self.service.binary) + self.service_mock.force_down.assert_called_once_with( + self.service.host, self.service.binary, force_down=True) + self.assertIsNone(result) + + def test_service_set_enable_and_state_down_with_exception(self): + arglist = [ + '--enable', + '--down', + self.service.host, + self.service.binary, + ] + verifylist = [ + ('enable', True), + ('down', True), + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + with mock.patch.object(self.service_mock, 'enable', + side_effect=Exception()): + self.assertRaises(exceptions.CommandError, + self.cmd.take_action, parsed_args) + self.service_mock.force_down.assert_called_once_with( + self.service.host, self.service.binary, force_down=True) diff --git a/openstackclient/tests/unit/fakes.py b/openstackclient/tests/unit/fakes.py new file mode 100644 index 00000000..786cd6d4 --- /dev/null +++ b/openstackclient/tests/unit/fakes.py @@ -0,0 +1,242 @@ +# Copyright 2013 Nebula 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 json +import mock +import sys + +from keystoneauth1 import fixture +import requests +import six + + +AUTH_TOKEN = "foobar" +AUTH_URL = "http://0.0.0.0" +USERNAME = "itchy" +PASSWORD = "scratchy" +PROJECT_NAME = "poochie" +REGION_NAME = "richie" +INTERFACE = "catchy" +VERSION = "3" + +TEST_RESPONSE_DICT = fixture.V2Token(token_id=AUTH_TOKEN, + user_name=USERNAME) +_s = TEST_RESPONSE_DICT.add_service('identity', name='keystone') +_s.add_endpoint(AUTH_URL + ':5000/v2.0') +_s = TEST_RESPONSE_DICT.add_service('network', name='neutron') +_s.add_endpoint(AUTH_URL + ':9696') +_s = TEST_RESPONSE_DICT.add_service('compute', name='nova') +_s.add_endpoint(AUTH_URL + ':8774/v2') +_s = TEST_RESPONSE_DICT.add_service('image', name='glance') +_s.add_endpoint(AUTH_URL + ':9292') +_s = TEST_RESPONSE_DICT.add_service('object', name='swift') +_s.add_endpoint(AUTH_URL + ':8080/v1') + +TEST_RESPONSE_DICT_V3 = fixture.V3Token(user_name=USERNAME) +TEST_RESPONSE_DICT_V3.set_project_scope() + +TEST_VERSIONS = fixture.DiscoveryList(href=AUTH_URL) + + +def to_unicode_dict(catalog_dict): + """Converts dict to unicode dict + + """ + if isinstance(catalog_dict, dict): + return {to_unicode_dict(key): to_unicode_dict(value) + for key, value in catalog_dict.items()} + elif isinstance(catalog_dict, list): + return [to_unicode_dict(element) for element in catalog_dict] + elif isinstance(catalog_dict, str): + return catalog_dict + u"" + else: + return catalog_dict + + +class FakeStdout(object): + + def __init__(self): + self.content = [] + + def write(self, text): + self.content.append(text) + + def make_string(self): + result = '' + for line in self.content: + result = result + line + return result + + +class FakeLog(object): + + def __init__(self): + self.messages = {} + + def debug(self, msg): + self.messages['debug'] = msg + + def info(self, msg): + self.messages['info'] = msg + + def warning(self, msg): + self.messages['warning'] = msg + + def error(self, msg): + self.messages['error'] = msg + + def critical(self, msg): + self.messages['critical'] = msg + + +class FakeApp(object): + + def __init__(self, _stdout, _log): + self.stdout = _stdout + self.client_manager = None + self.stdin = sys.stdin + self.stdout = _stdout or sys.stdout + self.stderr = sys.stderr + self.log = _log + + +class FakeOptions(object): + def __init__(self, **kwargs): + self.os_beta_command = False + + +class FakeClient(object): + + def __init__(self, **kwargs): + self.endpoint = kwargs['endpoint'] + self.token = kwargs['token'] + + +class FakeClientManager(object): + _api_version = { + 'image': '2', + } + + def __init__(self): + self.compute = None + self.identity = None + self.image = None + self.object_store = None + self.volume = None + self.network = None + self.session = None + self.auth_ref = None + self.auth_plugin_name = None + self.network_endpoint_enabled = True + + def get_configuration(self): + return { + 'auth': { + 'username': USERNAME, + 'password': PASSWORD, + 'token': AUTH_TOKEN, + }, + 'region': REGION_NAME, + 'identity_api_version': VERSION, + } + + def is_network_endpoint_enabled(self): + return self.network_endpoint_enabled + + +class FakeModule(object): + + def __init__(self, name, version): + self.name = name + self.__version__ = version + + +class FakeResource(object): + + def __init__(self, manager=None, info=None, loaded=False, methods=None): + """Set attributes and methods for a resource. + + :param manager: + The resource manager + :param Dictionary info: + A dictionary with all attributes + :param bool loaded: + True if the resource is loaded in memory + :param Dictionary methods: + A dictionary with all methods + """ + info = info or {} + methods = methods or {} + + self.__name__ = type(self).__name__ + self.manager = manager + self._info = info + self._add_details(info) + self._add_methods(methods) + self._loaded = loaded + + def _add_details(self, info): + for (k, v) in six.iteritems(info): + setattr(self, k, v) + + def _add_methods(self, methods): + """Fake methods with MagicMock objects. + + For each <@key, @value> pairs in methods, add an callable MagicMock + object named @key as an attribute, and set the mock's return_value to + @value. When users access the attribute with (), @value will be + returned, which looks like a function call. + """ + for (name, ret) in six.iteritems(methods): + method = mock.MagicMock(return_value=ret) + setattr(self, name, method) + + def __repr__(self): + reprkeys = sorted(k for k in self.__dict__.keys() if k[0] != '_' and + k != 'manager') + info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys) + return "<%s %s>" % (self.__class__.__name__, info) + + def keys(self): + return self._info.keys() + + @property + def info(self): + return self._info + + +class FakeResponse(requests.Response): + + def __init__(self, headers=None, status_code=200, + data=None, encoding=None): + super(FakeResponse, self).__init__() + + headers = headers or {} + + self.status_code = status_code + + self.headers.update(headers) + self._content = json.dumps(data) + if not isinstance(self._content, six.binary_type): + self._content = self._content.encode() + + +class FakeModel(dict): + + def __getattr__(self, key): + try: + return self[key] + except KeyError: + raise AttributeError(key) diff --git a/openstackclient/tests/unit/identity/__init__.py b/openstackclient/tests/unit/identity/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/openstackclient/tests/unit/identity/__init__.py diff --git a/openstackclient/tests/unit/identity/v2_0/__init__.py b/openstackclient/tests/unit/identity/v2_0/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/openstackclient/tests/unit/identity/v2_0/__init__.py diff --git a/openstackclient/tests/unit/identity/v2_0/fakes.py b/openstackclient/tests/unit/identity/v2_0/fakes.py new file mode 100644 index 00000000..3d25cadf --- /dev/null +++ b/openstackclient/tests/unit/identity/v2_0/fakes.py @@ -0,0 +1,515 @@ +# Copyright 2013 Nebula 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 copy +import mock +import uuid + +from keystoneauth1 import access +from keystoneauth1 import fixture + +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit import utils + + +project_id = '8-9-64' +project_name = 'beatles' +project_description = 'Fab Four' + +PROJECT = { + 'id': project_id, + 'name': project_name, + 'description': project_description, + 'enabled': True, +} + +PROJECT_2 = { + 'id': project_id + '-2222', + 'name': project_name + ' reprise', + 'description': project_description + 'plus four more', + 'enabled': True, +} + +role_id = '1' +role_name = 'boss' + +ROLE = { + 'id': role_id, + 'name': role_name, +} + +ROLE_2 = { + 'id': '2', + 'name': 'bigboss', +} + +service_id = '1925-10-11' +service_name = 'elmore' +service_description = 'Leonard, Elmore, rip' +service_type = 'author' + +SERVICE = { + 'id': service_id, + 'name': service_name, + 'description': service_description, + 'type': service_type, +} + +user_id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' +user_name = 'paul' +user_description = 'Sir Paul' +user_email = 'paul@applecorps.com' + +USER = { + 'id': user_id, + 'name': user_name, + 'tenantId': project_id, + 'email': user_email, + 'enabled': True, +} + +token_expires = '2016-09-05T18:04:52+0000' +token_id = 'token-id-' + uuid.uuid4().hex + +TOKEN = { + 'expires': token_expires, + 'id': token_id, + 'tenant_id': 'project-id', + 'user_id': 'user-id', +} + +UNSCOPED_TOKEN = { + 'expires': token_expires, + 'id': token_id, + 'user_id': 'user-id', +} + +endpoint_name = service_name +endpoint_adminurl = 'https://admin.example.com/v2/UUID' +endpoint_region = 'RegionOne' +endpoint_internalurl = 'https://internal.example.com/v2/UUID' +endpoint_type = service_type +endpoint_id = '11b41ee1b00841128b7333d4bf1a6140' +endpoint_publicurl = 'https://public.example.com/v2/UUID' +endpoint_service_id = service_id + +ENDPOINT = { + 'service_name': endpoint_name, + 'adminurl': endpoint_adminurl, + 'region': endpoint_region, + 'internalurl': endpoint_internalurl, + 'service_type': endpoint_type, + 'id': endpoint_id, + 'publicurl': endpoint_publicurl, + 'service_id': endpoint_service_id, +} + + +def fake_auth_ref(fake_token, fake_service=None): + """Create an auth_ref using keystoneauth's fixtures""" + token_copy = copy.deepcopy(fake_token) + token_copy['token_id'] = token_copy.pop('id') + token = fixture.V2Token(**token_copy) + # An auth_ref is actually an access info object + auth_ref = access.create(body=token) + + # Create a service catalog + if fake_service: + service = token.add_service( + fake_service.type, + fake_service.name, + ) + # TODO(dtroyer): Add an 'id' element to KSA's _Service fixure + service['id'] = fake_service.id + for e in fake_service.endpoints: + # KSA's _Service fixture copies publicURL to internalURL and + # adminURL if they do not exist. Soooo helpful... + internal = e.get('internalURL', None) + admin = e.get('adminURL', None) + region = e.get('region_id') or e.get('region', '<none>') + endpoint = service.add_endpoint( + public=e['publicURL'], + internal=internal, + admin=admin, + region=region, + ) + # ...so undo that helpfulness + if not internal: + endpoint['internalURL'] = None + if not admin: + endpoint['adminURL'] = None + + return auth_ref + + +class FakeIdentityv2Client(object): + + def __init__(self, **kwargs): + self.roles = mock.Mock() + self.roles.resource_class = fakes.FakeResource(None, {}) + self.services = mock.Mock() + self.services.resource_class = fakes.FakeResource(None, {}) + self.tenants = mock.Mock() + self.tenants.resource_class = fakes.FakeResource(None, {}) + self.tokens = mock.Mock() + self.tokens.resource_class = fakes.FakeResource(None, {}) + self.users = mock.Mock() + self.users.resource_class = fakes.FakeResource(None, {}) + self.ec2 = mock.Mock() + self.ec2.resource_class = fakes.FakeResource(None, {}) + self.endpoints = mock.Mock() + self.endpoints.resource_class = fakes.FakeResource(None, {}) + self.extensions = mock.Mock() + self.extensions.resource_class = fakes.FakeResource(None, {}) + self.auth_token = kwargs['token'] + self.management_url = kwargs['endpoint'] + + def __getattr__(self, name): + # Map v3 'projects' back to v2 'tenants' + if name == "projects": + return self.tenants + else: + raise AttributeError(name) + + +class TestIdentityv2(utils.TestCommand): + + def setUp(self): + super(TestIdentityv2, self).setUp() + + self.app.client_manager.identity = FakeIdentityv2Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + + +class FakeExtension(object): + """Fake one or more extension.""" + + @staticmethod + def create_one_extension(attrs=None): + """Create a fake extension. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object with name, namespace, etc. + """ + attrs = attrs or {} + + # Set default attributes. + extension_info = { + 'name': 'name-' + uuid.uuid4().hex, + 'namespace': ('http://docs.openstack.org/identity/' + 'api/ext/OS-KSCRUD/v1.0'), + 'description': 'description-' + uuid.uuid4().hex, + 'updated': '2013-07-07T12:00:0-00:00', + 'alias': 'OS-KSCRUD', + 'links': ('[{"href":' + '"https://github.com/openstack/identity-api", "type":' + ' "text/html", "rel": "describedby"}]') + } + + # Overwrite default attributes. + extension_info.update(attrs) + + extension = fakes.FakeResource( + info=copy.deepcopy(extension_info), + loaded=True) + return extension + + +class FakeCatalog(object): + """Fake one or more catalog.""" + + @staticmethod + def create_catalog(attrs=None): + """Create a fake catalog. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object with id, name, type and so on. + """ + attrs = attrs or {} + + # Set default attributes. + catalog_info = { + 'id': 'service-id-' + uuid.uuid4().hex, + 'type': 'compute', + 'name': 'supernova', + 'endpoints': [ + { + 'region': 'one', + 'publicURL': 'https://public.one.example.com', + 'internalURL': 'https://internal.one.example.com', + 'adminURL': 'https://admin.one.example.com', + }, + { + 'region': 'two', + 'publicURL': 'https://public.two.example.com', + 'internalURL': 'https://internal.two.example.com', + 'adminURL': 'https://admin.two.example.com', + }, + { + 'region': None, + 'publicURL': 'https://public.none.example.com', + 'internalURL': 'https://internal.none.example.com', + 'adminURL': 'https://admin.none.example.com', + }, + ], + } + # Overwrite default attributes. + catalog_info.update(attrs) + + catalog = fakes.FakeResource( + info=copy.deepcopy(catalog_info), + loaded=True) + + return catalog + + +class FakeProject(object): + """Fake one or more project.""" + + @staticmethod + def create_one_project(attrs=None): + """Create a fake project. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, name, and so on + """ + + attrs = attrs or {} + + # set default attributes. + project_info = { + 'id': 'project-id-' + uuid.uuid4().hex, + 'name': 'project-name-' + uuid.uuid4().hex, + 'description': 'project_description', + 'enabled': True, + } + project_info.update(attrs) + + project = fakes.FakeResource(info=copy.deepcopy(project_info), + loaded=True) + return project + + @staticmethod + def create_projects(attrs=None, count=2): + """Create multiple fake projects. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of projects to fake + :return: + A list of FakeResource objects faking the projects + """ + projects = [] + for i in range(0, count): + projects.append(FakeProject.create_one_project(attrs)) + + return projects + + +class FakeEndpoint(object): + """Fake one or more endpoint.""" + + @staticmethod + def create_one_endpoint(attrs=None): + """Create a fake agent. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, name, region, and so on + """ + + attrs = attrs or {} + + # set default attributes. + endpoint_info = { + 'service_name': 'service-name-' + uuid.uuid4().hex, + 'adminurl': 'http://endpoint_adminurl', + 'region': 'endpoint_region', + 'internalurl': 'http://endpoint_internalurl', + 'service_type': 'service_type', + 'id': 'endpoint-id-' + uuid.uuid4().hex, + 'publicurl': 'http://endpoint_publicurl', + 'service_id': 'service-name-' + uuid.uuid4().hex, + + } + endpoint_info.update(attrs) + + endpoint = fakes.FakeResource(info=copy.deepcopy(endpoint_info), + loaded=True) + return endpoint + + @staticmethod + def create_endpoints(attrs=None, count=2): + """Create multiple fake endpoints. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of endpoints to fake + :return: + A list of FakeResource objects faking the endpoints + """ + endpoints = [] + for i in range(0, count): + endpoints.append(FakeEndpoint.create_one_endpoint(attrs)) + + return endpoints + + +class FakeService(object): + """Fake one or more service.""" + + @staticmethod + def create_one_service(attrs=None): + """Create a fake service. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, name, type, and so on + """ + + attrs = attrs or {} + + # set default attributes. + service_info = { + 'id': 'service-id-' + uuid.uuid4().hex, + 'name': 'service-name-' + uuid.uuid4().hex, + 'description': 'service_description', + 'type': 'service_type', + + } + service_info.update(attrs) + + service = fakes.FakeResource(info=copy.deepcopy(service_info), + loaded=True) + return service + + @staticmethod + def create_services(attrs=None, count=2): + """Create multiple fake services. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of services to fake + :return: + A list of FakeResource objects faking the services + """ + services = [] + for i in range(0, count): + services.append(FakeService.create_one_service(attrs)) + + return services + + +class FakeRole(object): + """Fake one or more role.""" + + @staticmethod + def create_one_role(attrs=None): + """Create a fake role. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, name, and so on + """ + + attrs = attrs or {} + + # set default attributes. + role_info = { + 'id': 'role-id' + uuid.uuid4().hex, + 'name': 'role-name' + uuid.uuid4().hex, + } + role_info.update(attrs) + + role = fakes.FakeResource(info=copy.deepcopy(role_info), + loaded=True) + return role + + @staticmethod + def create_roles(attrs=None, count=2): + """Create multiple fake roles. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of roles to fake + :return: + A list of FakeResource objects faking the roles + """ + roles = [] + for i in range(0, count): + roles.append(FakeRole.create_one_role(attrs)) + + return roles + + +class FakeUser(object): + """Fake one or more user.""" + + @staticmethod + def create_one_user(attrs=None): + """Create a fake user. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, name, and so on + """ + attrs = attrs or {} + + # set default attributes. + user_info = { + 'id': 'user-id-' + uuid.uuid4().hex, + 'name': 'user-name-' + uuid.uuid4().hex, + 'tenantId': 'project-id-' + uuid.uuid4().hex, + 'email': 'admin@openstack.org', + 'enabled': True, + } + user_info.update(attrs) + + user = fakes.FakeResource(info=copy.deepcopy(user_info), + loaded=True) + return user + + @staticmethod + def create_users(attrs=None, count=2): + """Create multiple fake users. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of users to fake + :return: + A list of FakeResource objects faking the users + """ + users = [] + for i in range(0, count): + users.append(FakeUser.create_one_user(attrs)) + + return users diff --git a/openstackclient/tests/unit/identity/v2_0/test_catalog.py b/openstackclient/tests/unit/identity/v2_0/test_catalog.py new file mode 100644 index 00000000..c32f9fb8 --- /dev/null +++ b/openstackclient/tests/unit/identity/v2_0/test_catalog.py @@ -0,0 +1,176 @@ +# 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 openstackclient.identity.v2_0 import catalog +from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes +from openstackclient.tests.unit import utils + + +class TestCatalog(utils.TestCommand): + + service_catalog = identity_fakes.FakeCatalog.create_catalog() + + def setUp(self): + super(TestCatalog, self).setUp() + + self.sc_mock = mock.MagicMock() + self.sc_mock.service_catalog.catalog.return_value = [ + self.service_catalog, + ] + + self.auth_mock = mock.MagicMock() + self.app.client_manager.session = self.auth_mock + + self.auth_mock.auth.get_auth_ref.return_value = self.sc_mock + + +class TestCatalogList(TestCatalog): + + columns = ( + 'Name', + 'Type', + 'Endpoints', + ) + + def setUp(self): + super(TestCatalogList, self).setUp() + + # Get the command object to test + self.cmd = catalog.ListCatalog(self.app, None) + + def test_catalog_list(self): + auth_ref = identity_fakes.fake_auth_ref( + identity_fakes.TOKEN, + fake_service=self.service_catalog, + ) + self.ar_mock = mock.PropertyMock(return_value=auth_ref) + type(self.app.client_manager).auth_ref = self.ar_mock + + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + datalist = (( + 'supernova', + 'compute', + 'one\n publicURL: https://public.one.example.com\n ' + 'internalURL: https://internal.one.example.com\n ' + 'adminURL: https://admin.one.example.com\n' + 'two\n publicURL: https://public.two.example.com\n ' + 'internalURL: https://internal.two.example.com\n ' + 'adminURL: https://admin.two.example.com\n' + '<none>\n publicURL: https://public.none.example.com\n ' + 'internalURL: https://internal.none.example.com\n ' + 'adminURL: https://admin.none.example.com\n', + ), ) + self.assertEqual(datalist, tuple(data)) + + def test_catalog_list_with_endpoint_url(self): + attr = { + 'id': 'qwertyuiop', + 'type': 'compute', + 'name': 'supernova', + 'endpoints': [ + { + 'region': 'one', + 'publicURL': 'https://public.one.example.com', + }, + { + 'region': 'two', + 'publicURL': 'https://public.two.example.com', + 'internalURL': 'https://internal.two.example.com', + }, + ], + } + service_catalog = identity_fakes.FakeCatalog.create_catalog(attr) + auth_ref = identity_fakes.fake_auth_ref( + identity_fakes.TOKEN, + fake_service=service_catalog, + ) + self.ar_mock = mock.PropertyMock(return_value=auth_ref) + type(self.app.client_manager).auth_ref = self.ar_mock + + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + datalist = (( + 'supernova', + 'compute', + 'one\n publicURL: https://public.one.example.com\n' + 'two\n publicURL: https://public.two.example.com\n ' + 'internalURL: https://internal.two.example.com\n' + ), ) + self.assertEqual(datalist, tuple(data)) + + +class TestCatalogShow(TestCatalog): + + def setUp(self): + super(TestCatalogShow, self).setUp() + + # Get the command object to test + self.cmd = catalog.ShowCatalog(self.app, None) + + def test_catalog_show(self): + auth_ref = identity_fakes.fake_auth_ref( + identity_fakes.UNSCOPED_TOKEN, + fake_service=self.service_catalog, + ) + self.ar_mock = mock.PropertyMock(return_value=auth_ref) + type(self.app.client_manager).auth_ref = self.ar_mock + + arglist = [ + 'compute', + ] + verifylist = [ + ('service', 'compute'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + collist = ('endpoints', 'id', 'name', 'type') + self.assertEqual(collist, columns) + datalist = ( + 'one\n publicURL: https://public.one.example.com\n ' + 'internalURL: https://internal.one.example.com\n ' + 'adminURL: https://admin.one.example.com\n' + 'two\n publicURL: https://public.two.example.com\n ' + 'internalURL: https://internal.two.example.com\n ' + 'adminURL: https://admin.two.example.com\n' + '<none>\n publicURL: https://public.none.example.com\n ' + 'internalURL: https://internal.none.example.com\n ' + 'adminURL: https://admin.none.example.com\n', + self.service_catalog.id, + 'supernova', + 'compute', + ) + self.assertEqual(datalist, data) diff --git a/openstackclient/tests/unit/identity/v2_0/test_endpoint.py b/openstackclient/tests/unit/identity/v2_0/test_endpoint.py new file mode 100644 index 00000000..915e04a5 --- /dev/null +++ b/openstackclient/tests/unit/identity/v2_0/test_endpoint.py @@ -0,0 +1,239 @@ +# 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 openstackclient.identity.v2_0 import endpoint +from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes + + +class TestEndpoint(identity_fakes.TestIdentityv2): + + fake_service = identity_fakes.FakeService.create_one_service() + attr = { + 'service_name': fake_service.name, + 'service_id': fake_service.id, + } + fake_endpoint = identity_fakes.FakeEndpoint.create_one_endpoint(attr) + + def setUp(self): + super(TestEndpoint, self).setUp() + + # Get a shortcut to the EndpointManager Mock + self.endpoints_mock = self.app.client_manager.identity.endpoints + self.endpoints_mock.reset_mock() + + # Get a shortcut to the ServiceManager Mock + self.services_mock = self.app.client_manager.identity.services + self.services_mock.reset_mock() + + +class TestEndpointCreate(TestEndpoint): + + def setUp(self): + super(TestEndpointCreate, self).setUp() + + self.endpoints_mock.create.return_value = self.fake_endpoint + + self.services_mock.get.return_value = self.fake_service + + # Get the command object to test + self.cmd = endpoint.CreateEndpoint(self.app, None) + + def test_endpoint_create(self): + arglist = [ + '--publicurl', self.fake_endpoint.publicurl, + '--internalurl', self.fake_endpoint.internalurl, + '--adminurl', self.fake_endpoint.adminurl, + '--region', self.fake_endpoint.region, + self.fake_service.id, + ] + verifylist = [ + ('adminurl', self.fake_endpoint.adminurl), + ('internalurl', self.fake_endpoint.internalurl), + ('publicurl', self.fake_endpoint.publicurl), + ('region', self.fake_endpoint.region), + ('service', self.fake_service.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # EndpointManager.create(region, service_id, publicurl, adminurl, + # internalurl) + self.endpoints_mock.create.assert_called_with( + self.fake_endpoint.region, + self.fake_service.id, + self.fake_endpoint.publicurl, + self.fake_endpoint.adminurl, + self.fake_endpoint.internalurl, + ) + + collist = ('adminurl', 'id', 'internalurl', 'publicurl', + 'region', 'service_id', 'service_name', 'service_type') + self.assertEqual(collist, columns) + datalist = ( + self.fake_endpoint.adminurl, + self.fake_endpoint.id, + self.fake_endpoint.internalurl, + self.fake_endpoint.publicurl, + self.fake_endpoint.region, + self.fake_endpoint.service_id, + self.fake_endpoint.service_name, + self.fake_endpoint.service_type, + ) + + self.assertEqual(datalist, data) + + +class TestEndpointDelete(TestEndpoint): + + def setUp(self): + super(TestEndpointDelete, self).setUp() + + self.endpoints_mock.get.return_value = self.fake_endpoint + self.endpoints_mock.delete.return_value = None + + # Get the command object to test + self.cmd = endpoint.DeleteEndpoint(self.app, None) + + def test_endpoint_delete_no_options(self): + arglist = [ + self.fake_endpoint.id, + ] + verifylist = [ + ('endpoints', [self.fake_endpoint.id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.endpoints_mock.delete.assert_called_with( + self.fake_endpoint.id, + ) + self.assertIsNone(result) + + +class TestEndpointList(TestEndpoint): + + def setUp(self): + super(TestEndpointList, self).setUp() + + self.endpoints_mock.list.return_value = [self.fake_endpoint] + + self.services_mock.get.return_value = self.fake_service + + # Get the command object to test + self.cmd = endpoint.ListEndpoint(self.app, None) + + def test_endpoint_list_no_options(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.endpoints_mock.list.assert_called_with() + + collist = ('ID', 'Region', 'Service Name', 'Service Type') + self.assertEqual(collist, columns) + datalist = (( + self.fake_endpoint.id, + self.fake_endpoint.region, + self.fake_endpoint.service_name, + self.fake_endpoint.service_type, + ), ) + self.assertEqual(datalist, tuple(data)) + + def test_endpoint_list_long(self): + arglist = [ + '--long', + ] + verifylist = [ + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.endpoints_mock.list.assert_called_with() + + collist = ('ID', 'Region', 'Service Name', 'Service Type', + 'PublicURL', 'AdminURL', 'InternalURL') + self.assertEqual(collist, columns) + datalist = (( + self.fake_endpoint.id, + self.fake_endpoint.region, + self.fake_endpoint.service_name, + self.fake_endpoint.service_type, + self.fake_endpoint.publicurl, + self.fake_endpoint.adminurl, + self.fake_endpoint.internalurl, + ), ) + self.assertEqual(datalist, tuple(data)) + + +class TestEndpointShow(TestEndpoint): + + def setUp(self): + super(TestEndpointShow, self).setUp() + + self.endpoints_mock.list.return_value = [self.fake_endpoint] + + self.services_mock.get.return_value = self.fake_service + + # Get the command object to test + self.cmd = endpoint.ShowEndpoint(self.app, None) + + def test_endpoint_show(self): + arglist = [ + self.fake_endpoint.id, + ] + verifylist = [ + ('endpoint_or_service', self.fake_endpoint.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # EndpointManager.list() + self.endpoints_mock.list.assert_called_with() + # ServiceManager.get(name) + self.services_mock.get.assert_called_with( + self.fake_endpoint.service_id, + ) + + collist = ('adminurl', 'id', 'internalurl', 'publicurl', + 'region', 'service_id', 'service_name', 'service_type') + self.assertEqual(collist, columns) + datalist = ( + self.fake_endpoint.adminurl, + self.fake_endpoint.id, + self.fake_endpoint.internalurl, + self.fake_endpoint.publicurl, + self.fake_endpoint.region, + self.fake_endpoint.service_id, + self.fake_endpoint.service_name, + self.fake_endpoint.service_type, + ) + self.assertEqual(datalist, data) diff --git a/openstackclient/tests/unit/identity/v2_0/test_project.py b/openstackclient/tests/unit/identity/v2_0/test_project.py new file mode 100644 index 00000000..c1f00762 --- /dev/null +++ b/openstackclient/tests/unit/identity/v2_0/test_project.py @@ -0,0 +1,635 @@ +# Copyright 2013 Nebula 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. +# + +from keystoneauth1 import exceptions as ks_exc +from osc_lib import exceptions + +from openstackclient.identity.v2_0 import project +from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes + + +class TestProject(identity_fakes.TestIdentityv2): + + fake_project = identity_fakes.FakeProject.create_one_project() + + columns = ( + 'description', + 'enabled', + 'id', + 'name', + ) + datalist = ( + fake_project.description, + True, + fake_project.id, + fake_project.name, + ) + + def setUp(self): + super(TestProject, self).setUp() + + # Get a shortcut to the TenantManager Mock + self.projects_mock = self.app.client_manager.identity.tenants + self.projects_mock.reset_mock() + + +class TestProjectCreate(TestProject): + + def setUp(self): + super(TestProjectCreate, self).setUp() + + self.projects_mock.create.return_value = self.fake_project + + # Get the command object to test + self.cmd = project.CreateProject(self.app, None) + + def test_project_create_no_options(self): + arglist = [ + self.fake_project.name, + ] + verifylist = [ + ('enable', False), + ('disable', False), + ('name', self.fake_project.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'description': None, + 'enabled': True, + } + self.projects_mock.create.assert_called_with( + self.fake_project.name, + **kwargs + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_project_create_description(self): + arglist = [ + '--description', 'new desc', + self.fake_project.name, + ] + verifylist = [ + ('description', 'new desc'), + ('name', self.fake_project.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'description': 'new desc', + 'enabled': True, + } + self.projects_mock.create.assert_called_with( + self.fake_project.name, + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_project_create_enable(self): + arglist = [ + '--enable', + self.fake_project.name, + ] + verifylist = [ + ('enable', True), + ('disable', False), + ('name', self.fake_project.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'description': None, + 'enabled': True, + } + self.projects_mock.create.assert_called_with( + self.fake_project.name, + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_project_create_disable(self): + arglist = [ + '--disable', + self.fake_project.name, + ] + verifylist = [ + ('enable', False), + ('disable', True), + ('name', self.fake_project.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'description': None, + 'enabled': False, + } + self.projects_mock.create.assert_called_with( + self.fake_project.name, + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_project_create_property(self): + arglist = [ + '--property', 'fee=fi', + '--property', 'fo=fum', + self.fake_project.name, + ] + verifylist = [ + ('property', {'fee': 'fi', 'fo': 'fum'}), + ('name', self.fake_project.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'description': None, + 'enabled': True, + 'fee': 'fi', + 'fo': 'fum', + } + self.projects_mock.create.assert_called_with( + self.fake_project.name, + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_project_create_or_show_exists(self): + def _raise_conflict(*args, **kwargs): + raise ks_exc.Conflict(None) + + # need to make this throw an exception... + self.projects_mock.create.side_effect = _raise_conflict + + self.projects_mock.get.return_value = self.fake_project + + arglist = [ + '--or-show', + self.fake_project.name, + ] + verifylist = [ + ('or_show', True), + ('name', self.fake_project.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # ProjectManager.create(name, description, enabled) + self.projects_mock.get.assert_called_with(self.fake_project.name) + + # Set expected values + kwargs = { + 'description': None, + 'enabled': True, + } + self.projects_mock.create.assert_called_with( + self.fake_project.name, + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_project_create_or_show_not_exists(self): + arglist = [ + '--or-show', + self.fake_project.name, + ] + verifylist = [ + ('or_show', True), + ('name', self.fake_project.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'description': None, + 'enabled': True, + } + self.projects_mock.create.assert_called_with( + self.fake_project.name, + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + +class TestProjectDelete(TestProject): + + def setUp(self): + super(TestProjectDelete, self).setUp() + + # This is the return value for utils.find_resource() + self.projects_mock.get.return_value = self.fake_project + self.projects_mock.delete.return_value = None + + # Get the command object to test + self.cmd = project.DeleteProject(self.app, None) + + def test_project_delete_no_options(self): + arglist = [ + self.fake_project.id, + ] + verifylist = [ + ('projects', [self.fake_project.id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.projects_mock.delete.assert_called_with( + self.fake_project.id, + ) + self.assertIsNone(result) + + +class TestProjectList(TestProject): + + def setUp(self): + super(TestProjectList, self).setUp() + + self.projects_mock.list.return_value = [self.fake_project] + + # Get the command object to test + self.cmd = project.ListProject(self.app, None) + + def test_project_list_no_options(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + self.projects_mock.list.assert_called_with() + + collist = ('ID', 'Name') + self.assertEqual(collist, columns) + datalist = (( + self.fake_project.id, + self.fake_project.name, + ), ) + self.assertEqual(datalist, tuple(data)) + + def test_project_list_long(self): + arglist = [ + '--long', + ] + verifylist = [ + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + self.projects_mock.list.assert_called_with() + + collist = ('ID', 'Name', 'Description', 'Enabled') + self.assertEqual(collist, columns) + datalist = (( + self.fake_project.id, + self.fake_project.name, + self.fake_project.description, + True, + ), ) + self.assertEqual(datalist, tuple(data)) + + +class TestProjectSet(TestProject): + + def setUp(self): + super(TestProjectSet, self).setUp() + + self.projects_mock.get.return_value = self.fake_project + self.projects_mock.update.return_value = self.fake_project + + # Get the command object to test + self.cmd = project.SetProject(self.app, None) + + def test_project_set_no_options(self): + arglist = [ + self.fake_project.name, + ] + verifylist = [ + ('project', self.fake_project.name), + ('enable', False), + ('disable', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + + def test_project_set_unexist_project(self): + arglist = [ + "unexist-project", + ] + verifylist = [ + ('project', "unexist-project"), + ('name', None), + ('description', None), + ('enable', False), + ('disable', False), + ('property', None), + ] + self.projects_mock.get.side_effect = exceptions.NotFound(None) + self.projects_mock.find.side_effect = exceptions.NotFound(None) + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args) + + def test_project_set_name(self): + arglist = [ + '--name', self.fake_project.name, + self.fake_project.name, + ] + verifylist = [ + ('name', self.fake_project.name), + ('enable', False), + ('disable', False), + ('project', self.fake_project.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'description': self.fake_project.description, + 'enabled': True, + 'tenant_name': self.fake_project.name, + } + self.projects_mock.update.assert_called_with( + self.fake_project.id, + **kwargs + ) + self.assertIsNone(result) + + def test_project_set_description(self): + arglist = [ + '--description', self.fake_project.description, + self.fake_project.name, + ] + verifylist = [ + ('description', self.fake_project.description), + ('enable', False), + ('disable', False), + ('project', self.fake_project.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'description': self.fake_project.description, + 'enabled': True, + 'tenant_name': self.fake_project.name, + } + self.projects_mock.update.assert_called_with( + self.fake_project.id, + **kwargs + ) + self.assertIsNone(result) + + def test_project_set_enable(self): + arglist = [ + '--enable', + self.fake_project.name, + ] + verifylist = [ + ('enable', True), + ('disable', False), + ('project', self.fake_project.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'description': self.fake_project.description, + 'enabled': True, + 'tenant_name': self.fake_project.name, + } + self.projects_mock.update.assert_called_with( + self.fake_project.id, + **kwargs + ) + self.assertIsNone(result) + + def test_project_set_disable(self): + arglist = [ + '--disable', + self.fake_project.name, + ] + verifylist = [ + ('enable', False), + ('disable', True), + ('project', self.fake_project.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'description': self.fake_project.description, + 'enabled': False, + 'tenant_name': self.fake_project.name, + } + self.projects_mock.update.assert_called_with( + self.fake_project.id, + **kwargs + ) + self.assertIsNone(result) + + def test_project_set_property(self): + arglist = [ + '--property', 'fee=fi', + '--property', 'fo=fum', + self.fake_project.name, + ] + verifylist = [ + ('property', {'fee': 'fi', 'fo': 'fum'}), + ('project', self.fake_project.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'description': self.fake_project.description, + 'enabled': True, + 'tenant_name': self.fake_project.name, + 'fee': 'fi', + 'fo': 'fum', + } + self.projects_mock.update.assert_called_with( + self.fake_project.id, + **kwargs + ) + self.assertIsNone(result) + + +class TestProjectShow(TestProject): + + fake_proj_show = identity_fakes.FakeProject.create_one_project() + + def setUp(self): + super(TestProjectShow, self).setUp() + + self.projects_mock.get.return_value = self.fake_proj_show + + # Get the command object to test + self.cmd = project.ShowProject(self.app, None) + + def test_project_show(self): + arglist = [ + self.fake_proj_show.id, + ] + verifylist = [ + ('project', self.fake_proj_show.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + self.projects_mock.get.assert_called_with( + self.fake_proj_show.id, + ) + + collist = ('description', 'enabled', 'id', 'name', 'properties') + self.assertEqual(collist, columns) + datalist = ( + self.fake_proj_show.description, + True, + self.fake_proj_show.id, + self.fake_proj_show.name, + '', + ) + self.assertEqual(datalist, data) + + +class TestProjectUnset(TestProject): + + attr = {'fee': 'fi', 'fo': 'fum'} + fake_proj = identity_fakes.FakeProject.create_one_project(attr) + + def setUp(self): + super(TestProjectUnset, self).setUp() + + self.projects_mock.get.return_value = self.fake_proj + + # Get the command object to test + self.cmd = project.UnsetProject(self.app, None) + + def test_project_unset_no_options(self): + arglist = [ + self.fake_proj.name, + ] + verifylist = [ + ('project', self.fake_proj.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + + def test_project_unset_key(self): + arglist = [ + '--property', 'fee', + '--property', 'fo', + self.fake_proj.name, + ] + verifylist = [ + ('property', ['fee', 'fo']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + # Set expected values + kwargs = { + 'description': self.fake_proj.description, + 'enabled': True, + 'fee': None, + 'fo': None, + 'id': self.fake_proj.id, + 'name': self.fake_proj.name, + } + + self.projects_mock.update.assert_called_with( + self.fake_proj.id, + **kwargs + ) + self.assertIsNone(result) diff --git a/openstackclient/tests/unit/identity/v2_0/test_role.py b/openstackclient/tests/unit/identity/v2_0/test_role.py new file mode 100644 index 00000000..68ebf141 --- /dev/null +++ b/openstackclient/tests/unit/identity/v2_0/test_role.py @@ -0,0 +1,480 @@ +# Copyright 2013 Nebula 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 mock + +from keystoneauth1 import exceptions as ks_exc +from osc_lib import exceptions + +from openstackclient.identity.v2_0 import role +from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes + + +class TestRole(identity_fakes.TestIdentityv2): + + attr = {} + attr['endpoints'] = [ + { + 'publicURL': identity_fakes.ENDPOINT['publicurl'], + }, + ] + fake_service = identity_fakes.FakeService.create_one_service(attr) + fake_role = identity_fakes.FakeRole.create_one_role() + fake_project = identity_fakes.FakeProject.create_one_project() + attr = {} + attr = { + 'tenantId': fake_project.id, + } + fake_user = identity_fakes.FakeUser.create_one_user(attr) + + def setUp(self): + super(TestRole, self).setUp() + + # Get a shortcut to the TenantManager Mock + self.projects_mock = self.app.client_manager.identity.tenants + self.projects_mock.reset_mock() + + # Get a shortcut to the UserManager Mock + self.users_mock = self.app.client_manager.identity.users + self.users_mock.reset_mock() + + # Get a shortcut to the RoleManager Mock + self.roles_mock = self.app.client_manager.identity.roles + self.roles_mock.reset_mock() + + auth_ref = identity_fakes.fake_auth_ref( + identity_fakes.TOKEN, + fake_service=self.fake_service, + ) + self.ar_mock = mock.PropertyMock(return_value=auth_ref) + type(self.app.client_manager).auth_ref = self.ar_mock + + +class TestRoleAdd(TestRole): + + def setUp(self): + super(TestRoleAdd, self).setUp() + + self.projects_mock.get.return_value = self.fake_project + + self.users_mock.get.return_value = self.fake_user + + self.roles_mock.get.return_value = self.fake_role + self.roles_mock.add_user_role.return_value = self.fake_role + + # Get the command object to test + self.cmd = role.AddRole(self.app, None) + + def test_role_add(self): + arglist = [ + '--project', self.fake_project.name, + '--user', self.fake_user.name, + self.fake_role.name, + ] + verifylist = [ + ('project', self.fake_project.name), + ('user', self.fake_user.name), + ('role', self.fake_role.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # RoleManager.add_user_role(user, role, tenant=None) + self.roles_mock.add_user_role.assert_called_with( + self.fake_user.id, + self.fake_role.id, + self.fake_project.id, + ) + + collist = ('id', 'name') + self.assertEqual(collist, columns) + datalist = ( + self.fake_role.id, + self.fake_role.name, + ) + self.assertEqual(datalist, data) + + +class TestRoleCreate(TestRole): + + fake_role_c = identity_fakes.FakeRole.create_one_role() + columns = ( + 'id', + 'name' + ) + datalist = ( + fake_role_c.id, + fake_role_c.name, + ) + + def setUp(self): + super(TestRoleCreate, self).setUp() + + self.roles_mock.create.return_value = self.fake_role_c + + # Get the command object to test + self.cmd = role.CreateRole(self.app, None) + + def test_role_create_no_options(self): + arglist = [ + self.fake_role_c.name, + ] + verifylist = [ + ('role_name', self.fake_role_c.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # RoleManager.create(name) + self.roles_mock.create.assert_called_with( + self.fake_role_c.name, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_role_create_or_show_exists(self): + def _raise_conflict(*args, **kwargs): + raise ks_exc.Conflict(None) + + # need to make this throw an exception... + self.roles_mock.create.side_effect = _raise_conflict + + self.roles_mock.get.return_value = self.fake_role_c + + arglist = [ + '--or-show', + self.fake_role_c.name, + ] + verifylist = [ + ('role_name', self.fake_role_c.name), + ('or_show', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # RoleManager.get(name, description, enabled) + self.roles_mock.get.assert_called_with(self.fake_role_c.name) + + # RoleManager.create(name) + self.roles_mock.create.assert_called_with( + self.fake_role_c.name, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_role_create_or_show_not_exists(self): + arglist = [ + '--or-show', + self.fake_role_c.name, + ] + verifylist = [ + ('role_name', self.fake_role_c.name), + ('or_show', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # RoleManager.create(name) + self.roles_mock.create.assert_called_with( + self.fake_role_c.name, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + +class TestRoleDelete(TestRole): + + def setUp(self): + super(TestRoleDelete, self).setUp() + + self.roles_mock.get.return_value = self.fake_role + self.roles_mock.delete.return_value = None + + # Get the command object to test + self.cmd = role.DeleteRole(self.app, None) + + def test_role_delete_no_options(self): + arglist = [ + self.fake_role.name, + ] + verifylist = [ + ('roles', [self.fake_role.name]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.roles_mock.delete.assert_called_with( + self.fake_role.id, + ) + self.assertIsNone(result) + + +class TestRoleList(TestRole): + + def setUp(self): + super(TestRoleList, self).setUp() + + self.roles_mock.list.return_value = [self.fake_role] + + # Get the command object to test + self.cmd = role.ListRole(self.app, None) + + def test_role_list_no_options(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.roles_mock.list.assert_called_with() + + collist = ('ID', 'Name') + self.assertEqual(collist, columns) + datalist = (( + self.fake_role.id, + self.fake_role.name, + ), ) + self.assertEqual(datalist, tuple(data)) + + +class TestUserRoleList(TestRole): + + columns = ( + 'ID', + 'Name', + 'Project', + 'User' + ) + + def setUp(self): + super(TestUserRoleList, self).setUp() + + self.projects_mock.get.return_value = self.fake_project + + self.users_mock.get.return_value = self.fake_user + + self.roles_mock.roles_for_user.return_value = [self.fake_role] + + # Get the command object to test + self.cmd = role.ListUserRole(self.app, None) + + def test_user_role_list_no_options_unscoped_token(self): + auth_ref = identity_fakes.fake_auth_ref( + identity_fakes.UNSCOPED_TOKEN, + fake_service=self.fake_service, + ) + self.ar_mock = mock.PropertyMock(return_value=auth_ref) + type(self.app.client_manager).auth_ref = self.ar_mock + + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # This argument combination should raise a CommandError + self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + + def test_user_role_list_no_options_scoped_token(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.roles_mock.roles_for_user.assert_called_with( + self.fake_user.id, + self.fake_project.id, + ) + + collist = ('ID', 'Name', 'Project', 'User') + self.assertEqual(collist, columns) + datalist = (( + self.fake_role.id, + self.fake_role.name, + self.fake_project.name, + self.fake_user.name, + ), ) + self.assertEqual(datalist, tuple(data)) + + def test_user_role_list_project_unscoped_token(self): + auth_ref = identity_fakes.fake_auth_ref( + identity_fakes.UNSCOPED_TOKEN, + fake_service=self.fake_service, + ) + self.ar_mock = mock.PropertyMock(return_value=auth_ref) + type(self.app.client_manager).auth_ref = self.ar_mock + + self.projects_mock.get.return_value = self.fake_project + arglist = [ + '--project', self.fake_project.name, + ] + verifylist = [ + ('project', self.fake_project.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.roles_mock.roles_for_user.assert_called_with( + self.fake_user.id, + self.fake_project.id, + ) + + self.assertEqual(columns, columns) + datalist = (( + self.fake_role.id, + self.fake_role.name, + self.fake_project.name, + self.fake_user.name, + ), ) + self.assertEqual(datalist, tuple(data)) + + def test_user_role_list_project_scoped_token(self): + self.projects_mock.get.return_value = self.fake_project + arglist = [ + '--project', self.fake_project.name, + ] + verifylist = [ + ('project', self.fake_project.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.roles_mock.roles_for_user.assert_called_with( + self.fake_user.id, + self.fake_project.id, + ) + + self.assertEqual(columns, columns) + datalist = (( + self.fake_role.id, + self.fake_role.name, + self.fake_project.name, + self.fake_user.name, + ), ) + self.assertEqual(datalist, tuple(data)) + + +class TestRoleRemove(TestRole): + + def setUp(self): + super(TestRoleRemove, self).setUp() + + self.projects_mock.get.return_value = self.fake_project + + self.users_mock.get.return_value = self.fake_user + + self.roles_mock.get.return_value = self.fake_role + self.roles_mock.remove_user_role.return_value = None + + # Get the command object to test + self.cmd = role.RemoveRole(self.app, None) + + def test_role_remove(self): + arglist = [ + '--project', self.fake_project.name, + '--user', self.fake_user.name, + self.fake_role.name, + ] + verifylist = [ + ('role', self.fake_role.name), + ('project', self.fake_project.name), + ('user', self.fake_user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # RoleManager.remove_user_role(user, role, tenant=None) + self.roles_mock.remove_user_role.assert_called_with( + self.fake_user.id, + self.fake_role.id, + self.fake_project.id, + ) + self.assertIsNone(result) + + +class TestRoleShow(TestRole): + + def setUp(self): + super(TestRoleShow, self).setUp() + + self.roles_mock.get.return_value = self.fake_role + + # Get the command object to test + self.cmd = role.ShowRole(self.app, None) + + def test_service_show(self): + arglist = [ + self.fake_role.name, + ] + verifylist = [ + ('role', self.fake_role.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # RoleManager.get(role) + self.roles_mock.get.assert_called_with( + self.fake_role.name, + ) + + collist = ('id', 'name') + self.assertEqual(collist, columns) + datalist = ( + self.fake_role.id, + self.fake_role.name, + ) + self.assertEqual(datalist, data) diff --git a/openstackclient/tests/unit/identity/v2_0/test_role_assignment.py b/openstackclient/tests/unit/identity/v2_0/test_role_assignment.py new file mode 100644 index 00000000..27306959 --- /dev/null +++ b/openstackclient/tests/unit/identity/v2_0/test_role_assignment.py @@ -0,0 +1,271 @@ +# 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 copy +import mock + +from osc_lib import exceptions + +from openstackclient.identity.v2_0 import role_assignment +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes + + +class TestRoleAssignment(identity_fakes.TestIdentityv2): + + def setUp(self): + super(TestRoleAssignment, self).setUp() + + +class TestRoleAssignmentList(TestRoleAssignment): + + columns = ( + 'Role', + 'User', + 'Project', + ) + + def setUp(self): + super(TestRoleAssignment, self).setUp() + + # Get a shortcut to the UserManager Mock + self.users_mock = self.app.client_manager.identity.users + self.users_mock.reset_mock() + + # Get a shortcut to the ProjectManager Mock + self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock.reset_mock() + + # Get a shortcut to the RoleManager Mock + self.roles_mock = self.app.client_manager.identity.roles + self.roles_mock.reset_mock() + + self.projects_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.PROJECT), + loaded=True, + ) + + self.users_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.USER), + loaded=True, + ) + + self.roles_mock.roles_for_user.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.ROLE), + loaded=True, + ), + ] + + # Get the command object to test + self.cmd = role_assignment.ListRoleAssignment(self.app, None) + + def test_role_assignment_list_no_filters(self): + + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # This argument combination should raise a CommandError + self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + + def test_role_assignment_list_only_project_filter(self): + + arglist = [ + '--project', identity_fakes.project_name, + ] + verifylist = [ + ('project', identity_fakes.project_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # This argument combination should raise a CommandError + self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + + def test_role_assignment_list_only_user_filter(self): + + arglist = [ + '--user', identity_fakes.user_name, + ] + verifylist = [ + ('user', identity_fakes.user_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # This argument combination should raise a CommandError + self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + + def test_role_assignment_list_project_and_user(self): + + self.roles_mock.roles_for_user.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ROLE), + loaded=True, + ), + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ROLE_2), + loaded=True, + ), + ] + + arglist = [ + '--project', identity_fakes.project_name, + '--user', identity_fakes.user_name, + ] + verifylist = [ + ('user', identity_fakes.user_name), + ('project', identity_fakes.project_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.roles_mock.roles_for_user.assert_called_with( + identity_fakes.user_id, + identity_fakes.project_id, + ) + + self.assertEqual(self.columns, columns) + datalist = (( + identity_fakes.role_id, + identity_fakes.user_id, + identity_fakes.project_id, + ), (identity_fakes.ROLE_2['id'], + identity_fakes.user_id, + identity_fakes.project_id, + ),) + self.assertEqual(datalist, tuple(data)) + + def test_role_assignment_list_def_creds(self): + + auth_ref = self.app.client_manager.auth_ref = mock.MagicMock() + auth_ref.project_id.return_value = identity_fakes.project_id + auth_ref.user_id.return_value = identity_fakes.user_id + + self.roles_mock.roles_for_user.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ROLE), + loaded=True, + ), + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ROLE_2), + loaded=True, + ), + ] + + arglist = [ + '--auth-user', + '--auth-project', + ] + verifylist = [ + ('authuser', True), + ('authproject', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.roles_mock.roles_for_user.assert_called_with( + identity_fakes.user_id, + identity_fakes.project_id, + ) + + self.assertEqual(self.columns, columns) + datalist = (( + identity_fakes.role_id, + identity_fakes.user_id, + identity_fakes.project_id, + ), (identity_fakes.ROLE_2['id'], + identity_fakes.user_id, + identity_fakes.project_id, + ),) + self.assertEqual(datalist, tuple(data)) + + def test_role_assignment_list_by_name_project_and_user(self): + + self.roles_mock.roles_for_user.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ROLE), + loaded=True, + ), + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ROLE_2), + loaded=True, + ), + ] + + arglist = [ + '--project', identity_fakes.project_name, + '--user', identity_fakes.user_name, + '--names' + ] + verifylist = [ + ('user', identity_fakes.user_name), + ('project', identity_fakes.project_name), + ('names', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.roles_mock.roles_for_user.assert_called_with( + identity_fakes.user_id, + identity_fakes.project_id, + ) + + self.assertEqual(self.columns, columns) + datalist = (( + identity_fakes.role_name, + identity_fakes.user_name, + identity_fakes.project_name, + ), (identity_fakes.ROLE_2['name'], + identity_fakes.user_name, + identity_fakes.project_name, + ),) + self.assertEqual(datalist, tuple(data)) diff --git a/openstackclient/tests/unit/identity/v2_0/test_service.py b/openstackclient/tests/unit/identity/v2_0/test_service.py new file mode 100644 index 00000000..1948bf4a --- /dev/null +++ b/openstackclient/tests/unit/identity/v2_0/test_service.py @@ -0,0 +1,316 @@ +# Copyright 2013 Nebula 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. +# + +from keystoneclient import exceptions as identity_exc +from osc_lib import exceptions + +from openstackclient.identity.v2_0 import service +from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes + + +class TestService(identity_fakes.TestIdentityv2): + fake_service = identity_fakes.FakeService.create_one_service() + + def setUp(self): + super(TestService, self).setUp() + + # Get a shortcut to the ServiceManager Mock + self.services_mock = self.app.client_manager.identity.services + self.services_mock.reset_mock() + + +class TestServiceCreate(TestService): + + fake_service_c = identity_fakes.FakeService.create_one_service() + columns = ( + 'description', + 'id', + 'name', + 'type', + ) + datalist = ( + fake_service_c.description, + fake_service_c.id, + fake_service_c.name, + fake_service_c.type, + ) + + def setUp(self): + super(TestServiceCreate, self).setUp() + + self.services_mock.create.return_value = self.fake_service_c + + # Get the command object to test + self.cmd = service.CreateService(self.app, None) + + def test_service_create_with_type_positional(self): + arglist = [ + self.fake_service_c.type, + ] + verifylist = [ + ('type_or_name', self.fake_service_c.type), + ('type', None), + ('description', None), + ('name', None), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # ServiceManager.create(name, service_type, description) + self.services_mock.create.assert_called_with( + None, + self.fake_service_c.type, + None, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_service_create_with_type_option(self): + arglist = [ + '--type', self.fake_service_c.type, + self.fake_service_c.name, + ] + verifylist = [ + ('type_or_name', self.fake_service_c.name), + ('type', self.fake_service_c.type), + ('description', None), + ('name', None), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # ServiceManager.create(name, service_type, description) + self.services_mock.create.assert_called_with( + self.fake_service_c.name, + self.fake_service_c.type, + None, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_service_create_with_name_option(self): + arglist = [ + '--name', self.fake_service_c.name, + self.fake_service_c.type, + ] + verifylist = [ + ('type_or_name', self.fake_service_c.type), + ('type', None), + ('description', None), + ('name', self.fake_service_c.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # ServiceManager.create(name, service_type, description) + self.services_mock.create.assert_called_with( + self.fake_service_c.name, + self.fake_service_c.type, + None, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_service_create_description(self): + arglist = [ + '--name', self.fake_service_c.name, + '--description', self.fake_service_c.description, + self.fake_service_c.type, + ] + verifylist = [ + ('type_or_name', self.fake_service_c.type), + ('type', None), + ('description', self.fake_service_c.description), + ('name', self.fake_service_c.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # ServiceManager.create(name, service_type, description) + self.services_mock.create.assert_called_with( + self.fake_service_c.name, + self.fake_service_c.type, + self.fake_service_c.description, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + +class TestServiceDelete(TestService): + + def setUp(self): + super(TestServiceDelete, self).setUp() + + self.services_mock.get.side_effect = identity_exc.NotFound(None) + self.services_mock.find.return_value = self.fake_service + self.services_mock.delete.return_value = None + + # Get the command object to test + self.cmd = service.DeleteService(self.app, None) + + def test_service_delete_no_options(self): + arglist = [ + self.fake_service.name, + ] + verifylist = [ + ('services', [self.fake_service.name]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.services_mock.delete.assert_called_with( + self.fake_service.id, + ) + self.assertIsNone(result) + + +class TestServiceList(TestService): + + def setUp(self): + super(TestServiceList, self).setUp() + + self.services_mock.list.return_value = [self.fake_service] + + # Get the command object to test + self.cmd = service.ListService(self.app, None) + + def test_service_list_no_options(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.services_mock.list.assert_called_with() + + collist = ('ID', 'Name', 'Type') + self.assertEqual(collist, columns) + datalist = (( + self.fake_service.id, + self.fake_service.name, + self.fake_service.type, + ), ) + self.assertEqual(datalist, tuple(data)) + + def test_service_list_long(self): + arglist = [ + '--long', + ] + verifylist = [ + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.services_mock.list.assert_called_with() + + collist = ('ID', 'Name', 'Type', 'Description') + self.assertEqual(collist, columns) + datalist = (( + self.fake_service.id, + self.fake_service.name, + self.fake_service.type, + self.fake_service.description, + ), ) + self.assertEqual(datalist, tuple(data)) + + +class TestServiceShow(TestService): + + fake_service_s = identity_fakes.FakeService.create_one_service() + + def setUp(self): + super(TestServiceShow, self).setUp() + + self.services_mock.get.side_effect = identity_exc.NotFound(None) + self.services_mock.find.return_value = self.fake_service_s + + # Get the command object to test + self.cmd = service.ShowService(self.app, None) + + def test_service_show(self): + arglist = [ + self.fake_service_s.name, + ] + verifylist = [ + ('service', self.fake_service_s.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # ServiceManager.find(id) + self.services_mock.find.assert_called_with( + name=self.fake_service_s.name, + ) + + collist = ('description', 'id', 'name', 'type') + self.assertEqual(collist, columns) + datalist = ( + self.fake_service_s.description, + self.fake_service_s.id, + self.fake_service_s.name, + self.fake_service_s.type, + ) + self.assertEqual(datalist, data) + + def test_service_show_nounique(self): + self.services_mock.find.side_effect = identity_exc.NoUniqueMatch(None) + arglist = [ + 'nounique_service', + ] + verifylist = [ + ('service', 'nounique_service'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual( + "Multiple service matches found for 'nounique_service'," + " use an ID to be more specific.", str(e)) diff --git a/openstackclient/tests/unit/identity/v2_0/test_token.py b/openstackclient/tests/unit/identity/v2_0/test_token.py new file mode 100644 index 00000000..dd7f4f4a --- /dev/null +++ b/openstackclient/tests/unit/identity/v2_0/test_token.py @@ -0,0 +1,115 @@ +# Copyright 2014 eBay 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 mock + +from openstackclient.identity.v2_0 import token +from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes + + +class TestToken(identity_fakes.TestIdentityv2): + + fake_user = identity_fakes.FakeUser.create_one_user() + fake_project = identity_fakes.FakeProject.create_one_project() + + def setUp(self): + super(TestToken, self).setUp() + + # Get a shortcut to the Auth Ref Mock + self.ar_mock = mock.PropertyMock() + type(self.app.client_manager).auth_ref = self.ar_mock + + +class TestTokenIssue(TestToken): + + def setUp(self): + super(TestTokenIssue, self).setUp() + + self.cmd = token.IssueToken(self.app, None) + + def test_token_issue(self): + auth_ref = identity_fakes.fake_auth_ref( + identity_fakes.TOKEN, + ) + self.ar_mock = mock.PropertyMock(return_value=auth_ref) + type(self.app.client_manager).auth_ref = self.ar_mock + + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + collist = ('expires', 'id', 'project_id', 'user_id') + self.assertEqual(collist, columns) + datalist = ( + identity_fakes.token_expires, + identity_fakes.token_id, + 'project-id', + 'user-id', + ) + self.assertEqual(datalist, data) + + def test_token_issue_with_unscoped_token(self): + auth_ref = identity_fakes.fake_auth_ref( + identity_fakes.UNSCOPED_TOKEN, + ) + self.ar_mock = mock.PropertyMock(return_value=auth_ref) + type(self.app.client_manager).auth_ref = self.ar_mock + + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # DisplayCommandBase.take_action() returns two tuples + columns, data = self.cmd.take_action(parsed_args) + + collist = ( + 'expires', + 'id', + 'user_id', + ) + self.assertEqual(collist, columns) + datalist = ( + identity_fakes.token_expires, + identity_fakes.token_id, + 'user-id', + ) + self.assertEqual(datalist, data) + + +class TestTokenRevoke(TestToken): + + TOKEN = 'fob' + + def setUp(self): + super(TestTokenRevoke, self).setUp() + self.tokens_mock = self.app.client_manager.identity.tokens + self.tokens_mock.reset_mock() + self.tokens_mock.delete.return_value = True + self.cmd = token.RevokeToken(self.app, None) + + def test_token_revoke(self): + arglist = [self.TOKEN] + verifylist = [('token', self.TOKEN)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.tokens_mock.delete.assert_called_with(self.TOKEN) + self.assertIsNone(result) diff --git a/openstackclient/tests/unit/identity/v2_0/test_user.py b/openstackclient/tests/unit/identity/v2_0/test_user.py new file mode 100644 index 00000000..765f8559 --- /dev/null +++ b/openstackclient/tests/unit/identity/v2_0/test_user.py @@ -0,0 +1,793 @@ +# Copyright 2013 Nebula 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 mock + +from keystoneauth1 import exceptions as ks_exc +from osc_lib import exceptions + +from openstackclient.identity.v2_0 import user +from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes + + +class TestUser(identity_fakes.TestIdentityv2): + + fake_project = identity_fakes.FakeProject.create_one_project() + attr = { + 'tenantId': fake_project.id, + } + fake_user = identity_fakes.FakeUser.create_one_user(attr) + + def setUp(self): + super(TestUser, self).setUp() + + # Get a shortcut to the TenantManager Mock + self.projects_mock = self.app.client_manager.identity.tenants + self.projects_mock.reset_mock() + + # Get a shortcut to the UserManager Mock + self.users_mock = self.app.client_manager.identity.users + self.users_mock.reset_mock() + + +class TestUserCreate(TestUser): + + fake_project_c = identity_fakes.FakeProject.create_one_project() + attr = { + 'tenantId': fake_project_c.id, + } + fake_user_c = identity_fakes.FakeUser.create_one_user(attr) + + columns = ( + 'email', + 'enabled', + 'id', + 'name', + 'project_id', + ) + datalist = ( + fake_user_c.email, + True, + fake_user_c.id, + fake_user_c.name, + fake_project_c.id, + ) + + def setUp(self): + super(TestUserCreate, self).setUp() + + self.projects_mock.get.return_value = self.fake_project_c + + self.users_mock.create.return_value = self.fake_user_c + + # Get the command object to test + self.cmd = user.CreateUser(self.app, None) + + def test_user_create_no_options(self): + arglist = [ + self.fake_user_c.name, + ] + verifylist = [ + ('enable', False), + ('disable', False), + ('name', self.fake_user_c.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': True, + 'tenant_id': None, + } + # UserManager.create(name, password, email, tenant_id=, enabled=) + self.users_mock.create.assert_called_with( + self.fake_user_c.name, + None, + None, + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_user_create_password(self): + arglist = [ + '--password', 'secret', + self.fake_user_c.name, + ] + verifylist = [ + ('name', self.fake_user_c.name), + ('password_prompt', False), + ('password', 'secret') + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': True, + 'tenant_id': None, + } + # UserManager.create(name, password, email, tenant_id=, enabled=) + self.users_mock.create.assert_called_with( + self.fake_user_c.name, + 'secret', + None, + **kwargs + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_user_create_password_prompt(self): + arglist = [ + '--password-prompt', + self.fake_user_c.name, + ] + verifylist = [ + ('name', self.fake_user_c.name), + ('password_prompt', True) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + mocker = mock.Mock() + mocker.return_value = 'abc123' + with mock.patch("osc_lib.utils.get_password", mocker): + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': True, + 'tenant_id': None, + } + # UserManager.create(name, password, email, tenant_id=, enabled=) + self.users_mock.create.assert_called_with( + self.fake_user_c.name, + 'abc123', + None, + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_user_create_email(self): + arglist = [ + '--email', 'barney@example.com', + self.fake_user_c.name, + ] + verifylist = [ + ('name', self.fake_user_c.name), + ('email', 'barney@example.com'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': True, + 'tenant_id': None, + } + # UserManager.create(name, password, email, tenant_id=, enabled=) + self.users_mock.create.assert_called_with( + self.fake_user_c.name, + None, + 'barney@example.com', + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_user_create_project(self): + # Return the new project + self.projects_mock.get.return_value = self.fake_project_c + + # Set up to return an updated user + attr = { + 'tenantId': self.fake_project_c.id, + } + user_2 = identity_fakes.FakeUser.create_one_user(attr) + self.users_mock.create.return_value = user_2 + + arglist = [ + '--project', self.fake_project_c.name, + user_2.name, + ] + verifylist = [ + ('name', user_2.name), + ('project', self.fake_project_c.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': True, + 'tenant_id': self.fake_project_c.id, + } + # UserManager.create(name, password, email, tenant_id=, enabled=) + self.users_mock.create.assert_called_with( + user_2.name, + None, + None, + **kwargs + ) + + self.assertEqual(self.columns, columns) + datalist = ( + user_2.email, + True, + user_2.id, + user_2.name, + self.fake_project_c.id, + ) + self.assertEqual(datalist, data) + + def test_user_create_enable(self): + arglist = [ + '--enable', + self.fake_user_c.name, + ] + verifylist = [ + ('name', self.fake_user_c.name), + ('enable', True), + ('disable', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': True, + 'tenant_id': None, + } + # UserManager.create(name, password, email, tenant_id=, enabled=) + self.users_mock.create.assert_called_with( + self.fake_user_c.name, + None, + None, + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_user_create_disable(self): + arglist = [ + '--disable', + self.fake_user_c.name, + ] + verifylist = [ + ('name', self.fake_user_c.name), + ('enable', False), + ('disable', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': False, + 'tenant_id': None, + } + # UserManager.create(name, password, email, tenant_id=, enabled=) + self.users_mock.create.assert_called_with( + self.fake_user_c.name, + None, + None, + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_user_create_or_show_exists(self): + def _raise_conflict(*args, **kwargs): + raise ks_exc.Conflict(None) + + # need to make this throw an exception... + self.users_mock.create.side_effect = _raise_conflict + + self.users_mock.get.return_value = self.fake_user_c + + arglist = [ + '--or-show', + self.fake_user_c.name, + ] + verifylist = [ + ('name', self.fake_user_c.name), + ('or_show', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # UserManager.create(name, password, email, tenant_id=, enabled=) + self.users_mock.get.assert_called_with(self.fake_user_c.name) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_user_create_or_show_not_exists(self): + arglist = [ + '--or-show', + self.fake_user_c.name, + ] + verifylist = [ + ('name', self.fake_user_c.name), + ('or_show', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': True, + 'tenant_id': None, + } + # UserManager.create(name, password, email, tenant_id=, enabled=) + self.users_mock.create.assert_called_with( + self.fake_user_c.name, + None, + None, + **kwargs + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + +class TestUserDelete(TestUser): + + def setUp(self): + super(TestUserDelete, self).setUp() + + # This is the return value for utils.find_resource() + self.users_mock.get.return_value = self.fake_user + self.users_mock.delete.return_value = None + + # Get the command object to test + self.cmd = user.DeleteUser(self.app, None) + + def test_user_delete_no_options(self): + arglist = [ + self.fake_user.id, + ] + verifylist = [ + ('users', [self.fake_user.id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.users_mock.delete.assert_called_with( + self.fake_user.id, + ) + self.assertIsNone(result) + + +class TestUserList(TestUser): + + fake_project_l = identity_fakes.FakeProject.create_one_project() + attr = { + 'tenantId': fake_project_l.id, + } + fake_user_l = identity_fakes.FakeUser.create_one_user(attr) + + columns = ( + 'ID', + 'Name', + ) + datalist = ( + ( + fake_user_l.id, + fake_user_l.name, + ), + ) + + def setUp(self): + super(TestUserList, self).setUp() + + self.projects_mock.get.return_value = self.fake_project_l + self.projects_mock.list.return_value = [self.fake_project_l] + + self.users_mock.list.return_value = [self.fake_user_l] + + # Get the command object to test + self.cmd = user.ListUser(self.app, None) + + def test_user_list_no_options(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.users_mock.list.assert_called_with(tenant_id=None) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_user_list_project(self): + arglist = [ + '--project', self.fake_project_l.id, + ] + verifylist = [ + ('project', self.fake_project_l.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + project_id = self.fake_project_l.id + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.users_mock.list.assert_called_with(tenant_id=project_id) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_user_list_long(self): + arglist = [ + '--long', + ] + verifylist = [ + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.users_mock.list.assert_called_with(tenant_id=None) + + collist = ('ID', 'Name', 'Project', 'Email', 'Enabled') + self.assertEqual(collist, columns) + datalist = (( + self.fake_user_l.id, + self.fake_user_l.name, + self.fake_project_l.name, + self.fake_user_l.email, + True, + ), ) + self.assertEqual(datalist, tuple(data)) + + +class TestUserSet(TestUser): + + def setUp(self): + super(TestUserSet, self).setUp() + + self.projects_mock.get.return_value = self.fake_project + self.users_mock.get.return_value = self.fake_user + + # Get the command object to test + self.cmd = user.SetUser(self.app, None) + + def test_user_set_no_options(self): + arglist = [ + self.fake_user.name, + ] + verifylist = [ + ('name', None), + ('password', None), + ('email', None), + ('project', None), + ('enable', False), + ('disable', False), + ('user', self.fake_user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + + def test_user_set_unexist_user(self): + arglist = [ + "unexist-user", + ] + verifylist = [ + ('name', None), + ('password', None), + ('email', None), + ('project', None), + ('enable', False), + ('disable', False), + ('user', "unexist-user"), + ] + self.users_mock.get.side_effect = exceptions.NotFound(None) + self.users_mock.find.side_effect = exceptions.NotFound(None) + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args) + + def test_user_set_name(self): + arglist = [ + '--name', 'qwerty', + self.fake_user.name, + ] + verifylist = [ + ('name', 'qwerty'), + ('password', None), + ('email', None), + ('project', None), + ('enable', False), + ('disable', False), + ('user', self.fake_user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': True, + 'name': 'qwerty', + } + # UserManager.update(user, **kwargs) + self.users_mock.update.assert_called_with( + self.fake_user.id, + **kwargs + ) + self.assertIsNone(result) + + def test_user_set_password(self): + arglist = [ + '--password', 'secret', + self.fake_user.name, + ] + verifylist = [ + ('name', None), + ('password', 'secret'), + ('password_prompt', False), + ('email', None), + ('project', None), + ('enable', False), + ('disable', False), + ('user', self.fake_user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # UserManager.update_password(user, password) + self.users_mock.update_password.assert_called_with( + self.fake_user.id, + 'secret', + ) + self.assertIsNone(result) + + def test_user_set_password_prompt(self): + arglist = [ + '--password-prompt', + self.fake_user.name, + ] + verifylist = [ + ('name', None), + ('password', None), + ('password_prompt', True), + ('email', None), + ('project', None), + ('enable', False), + ('disable', False), + ('user', self.fake_user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + mocker = mock.Mock() + mocker.return_value = 'abc123' + with mock.patch("osc_lib.utils.get_password", mocker): + result = self.cmd.take_action(parsed_args) + + # UserManager.update_password(user, password) + self.users_mock.update_password.assert_called_with( + self.fake_user.id, + 'abc123', + ) + self.assertIsNone(result) + + def test_user_set_email(self): + arglist = [ + '--email', 'barney@example.com', + self.fake_user.name, + ] + verifylist = [ + ('name', None), + ('password', None), + ('email', 'barney@example.com'), + ('project', None), + ('enable', False), + ('disable', False), + ('user', self.fake_user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'email': 'barney@example.com', + 'enabled': True, + } + # UserManager.update(user, **kwargs) + self.users_mock.update.assert_called_with( + self.fake_user.id, + **kwargs + ) + self.assertIsNone(result) + + def test_user_set_project(self): + arglist = [ + '--project', self.fake_project.id, + self.fake_user.name, + ] + verifylist = [ + ('name', None), + ('password', None), + ('email', None), + ('project', self.fake_project.id), + ('enable', False), + ('disable', False), + ('user', self.fake_user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # UserManager.update_tenant(user, tenant) + self.users_mock.update_tenant.assert_called_with( + self.fake_user.id, + self.fake_project.id, + ) + self.assertIsNone(result) + + def test_user_set_enable(self): + arglist = [ + '--enable', + self.fake_user.name, + ] + verifylist = [ + ('name', None), + ('password', None), + ('email', None), + ('project', None), + ('enable', True), + ('disable', False), + ('user', self.fake_user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': True, + } + # UserManager.update(user, **kwargs) + self.users_mock.update.assert_called_with( + self.fake_user.id, + **kwargs + ) + self.assertIsNone(result) + + def test_user_set_disable(self): + arglist = [ + '--disable', + self.fake_user.name, + ] + verifylist = [ + ('name', None), + ('password', None), + ('email', None), + ('project', None), + ('enable', False), + ('disable', True), + ('user', self.fake_user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': False, + } + # UserManager.update(user, **kwargs) + self.users_mock.update.assert_called_with( + self.fake_user.id, + **kwargs + ) + self.assertIsNone(result) + + +class TestUserShow(TestUser): + + def setUp(self): + super(TestUserShow, self).setUp() + + self.users_mock.get.return_value = self.fake_user + + # Get the command object to test + self.cmd = user.ShowUser(self.app, None) + + def test_user_show(self): + arglist = [ + self.fake_user.id, + ] + verifylist = [ + ('user', self.fake_user.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + self.users_mock.get.assert_called_with(self.fake_user.id) + + collist = ('email', 'enabled', 'id', 'name', 'project_id') + self.assertEqual(collist, columns) + datalist = ( + self.fake_user.email, + True, + self.fake_user.id, + self.fake_user.name, + self.fake_project.id, + ) + self.assertEqual(datalist, data) diff --git a/openstackclient/tests/unit/identity/v3/__init__.py b/openstackclient/tests/unit/identity/v3/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/openstackclient/tests/unit/identity/v3/__init__.py diff --git a/openstackclient/tests/unit/identity/v3/fakes.py b/openstackclient/tests/unit/identity/v3/fakes.py new file mode 100644 index 00000000..7b76fa60 --- /dev/null +++ b/openstackclient/tests/unit/identity/v3/fakes.py @@ -0,0 +1,914 @@ +# Copyright 2013 Nebula 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 copy +import mock +import uuid + +from keystoneauth1 import access +from keystoneauth1 import fixture + +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit import utils + +base_url = 'http://identity:5000/v3/' + +domain_id = 'd1' +domain_name = 'oftheking' +domain_description = 'domain description' + +DOMAIN = { + 'id': domain_id, + 'name': domain_name, + 'description': domain_description, + 'enabled': True, + 'links': base_url + 'domains/' + domain_id, +} + +group_id = 'gr-010' +group_name = 'spencer davis' + +GROUP = { + 'id': group_id, + 'name': group_name, + 'links': base_url + 'groups/' + group_id, +} + +mapping_id = 'test_mapping' +mapping_rules_file_path = '/tmp/path/to/file' +# Copied from +# (https://github.com/openstack/keystone/blob\ +# master/keystone/tests/mapping_fixtures.py +EMPLOYEE_GROUP_ID = "0cd5e9" +DEVELOPER_GROUP_ID = "xyz" +MAPPING_RULES = [ + { + "local": [ + { + "group": { + "id": EMPLOYEE_GROUP_ID + } + } + ], + "remote": [ + { + "type": "orgPersonType", + "not_any_of": [ + "Contractor", + "Guest" + ] + } + ] + } +] + +MAPPING_RULES_2 = [ + { + "local": [ + { + "group": { + "id": DEVELOPER_GROUP_ID + } + } + ], + "remote": [ + { + "type": "orgPersonType", + "any_one_of": [ + "Contractor" + ] + } + ] + } +] + + +MAPPING_RESPONSE = { + "id": mapping_id, + "rules": MAPPING_RULES +} + +MAPPING_RESPONSE_2 = { + "id": mapping_id, + "rules": MAPPING_RULES_2 +} + +project_id = '8-9-64' +project_name = 'beatles' +project_description = 'Fab Four' + +PROJECT = { + 'id': project_id, + 'name': project_name, + 'description': project_description, + 'enabled': True, + 'domain_id': domain_id, + 'links': base_url + 'projects/' + project_id, +} + +PROJECT_2 = { + 'id': project_id + '-2222', + 'name': project_name + ' reprise', + 'description': project_description + 'plus four more', + 'enabled': True, + 'domain_id': domain_id, + 'links': base_url + 'projects/' + project_id, +} + +region_id = 'region_one' +region_parent_region_id = 'region_two' +region_description = 'region one' + +REGION = { + 'id': region_id, + 'description': region_description, + 'parent_region_id': region_parent_region_id, + 'links': base_url + 'regions/' + region_id, +} + +PROJECT_WITH_PARENT = { + 'id': project_id + '-with-parent', + 'name': project_name + ' and their parents', + 'description': project_description + ' plus another four', + 'enabled': True, + 'domain_id': domain_id, + 'parent_id': project_id, + 'links': base_url + 'projects/' + (project_id + '-with-parent'), +} + +PROJECT_WITH_GRANDPARENT = { + 'id': project_id + '-with-grandparent', + 'name': project_name + ', granny and grandpa', + 'description': project_description + ' plus another eight?', + 'enabled': True, + 'domain_id': domain_id, + 'parent_id': PROJECT_WITH_PARENT['id'], + 'links': base_url + 'projects/' + (project_id + '-with-grandparent'), +} + +parents = [{'project': PROJECT}] +grandparents = [{'project': PROJECT}, {'project': PROJECT_WITH_PARENT}] +ids_for_parents = [PROJECT['id']] +ids_for_parents_and_grandparents = [PROJECT['id'], PROJECT_WITH_PARENT['id']] + +children = [{'project': PROJECT_WITH_GRANDPARENT}] +ids_for_children = [PROJECT_WITH_GRANDPARENT['id']] + + +role_id = 'r1' +role_name = 'roller' + +ROLE = { + 'id': role_id, + 'name': role_name, + 'domain': None, + 'links': base_url + 'roles/' + role_id, +} + +ROLE_2 = { + 'id': 'r2', + 'name': 'Rolls Royce', + 'domain': domain_id, + 'links': base_url + 'roles/' + 'r2', +} + +service_id = 's-123' +service_name = 'Texaco' +service_type = 'gas' +service_description = 'oil brand' + +SERVICE = { + 'id': service_id, + 'name': service_name, + 'type': service_type, + 'description': service_description, + 'enabled': True, + 'links': base_url + 'services/' + service_id, +} + +SERVICE_WITHOUT_NAME = { + 'id': service_id, + 'type': service_type, + 'description': service_description, + 'enabled': True, + 'links': base_url + 'services/' + service_id, +} + +endpoint_id = 'e-123' +endpoint_url = 'http://127.0.0.1:35357' +endpoint_region = 'RegionOne' +endpoint_interface = 'admin' + +ENDPOINT = { + 'id': endpoint_id, + 'url': endpoint_url, + 'region': endpoint_region, + 'interface': endpoint_interface, + 'service_id': service_id, + 'enabled': True, + 'links': base_url + 'endpoints/' + endpoint_id, +} + +user_id = 'bbbbbbb-aaaa-aaaa-aaaa-bbbbbbbaaaa' +user_name = 'paul' +user_description = 'Sir Paul' +user_email = 'paul@applecorps.com' + +USER = { + 'id': user_id, + 'name': user_name, + 'default_project_id': project_id, + 'email': user_email, + 'enabled': True, + 'domain_id': domain_id, + 'links': base_url + 'users/' + user_id, +} + +trust_id = 't-456' +trust_expires = None +trust_impersonation = False +trust_roles = {"id": role_id, "name": role_name}, + +TRUST = { + 'expires_at': trust_expires, + 'id': trust_id, + 'impersonation': trust_impersonation, + 'links': base_url + 'trusts/' + trust_id, + 'project_id': project_id, + 'roles': trust_roles, + 'trustee_user_id': user_id, + 'trustor_user_id': user_id, +} + +token_expires = '2016-09-05T18:04:52+0000' +token_id = 'tttttttt-tttt-tttt-tttt-tttttttttttt' + +UNSCOPED_TOKEN = { + 'expires': token_expires, + 'id': token_id, + 'user_id': user_id, +} + +TOKEN_WITH_PROJECT_ID = { + 'expires': token_expires, + 'id': token_id, + 'project_id': project_id, + 'user_id': user_id, +} + +TOKEN_WITH_DOMAIN_ID = { + 'expires': token_expires, + 'id': token_id, + 'domain_id': domain_id, + 'user_id': user_id, +} + +idp_id = 'test_idp' +idp_description = 'super exciting IdP description' +idp_remote_ids = ['entity1', 'entity2'] +formatted_idp_remote_ids = 'entity1, entity2' + +IDENTITY_PROVIDER = { + 'id': idp_id, + 'remote_ids': idp_remote_ids, + 'enabled': True, + 'description': idp_description +} + +protocol_id = 'protocol' + +mapping_id = 'test_mapping' +mapping_id_updated = 'prod_mapping' + +sp_id = 'BETA' +sp_description = 'Service Provider to burst into' +service_provider_url = 'https://beta.example.com/Shibboleth.sso/POST/SAML' +sp_auth_url = ('https://beta.example.com/v3/OS-FEDERATION/identity_providers/' + 'idp/protocol/saml2/auth') + +SERVICE_PROVIDER = { + 'id': sp_id, + 'enabled': True, + 'description': sp_description, + 'sp_url': service_provider_url, + 'auth_url': sp_auth_url +} + +PROTOCOL_ID_MAPPING = { + 'id': protocol_id, + 'mapping': mapping_id +} + +PROTOCOL_OUTPUT = { + 'id': protocol_id, + 'mapping_id': mapping_id, + 'identity_provider': idp_id +} + +PROTOCOL_OUTPUT_UPDATED = { + 'id': protocol_id, + 'mapping_id': mapping_id_updated, + 'identity_provider': idp_id +} + +# Assignments + +ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID = { + 'scope': {'project': {'id': project_id}}, + 'user': {'id': user_id}, + 'role': {'id': role_id}, +} + +ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID_INCLUDE_NAMES = { + 'scope': { + 'project': { + 'domain': {'id': domain_id, + 'name': domain_name}, + 'id': project_id, + 'name': project_name}}, + 'user': { + 'domain': {'id': domain_id, + 'name': domain_name}, + 'id': user_id, + 'name': user_name}, + 'role': {'id': role_id, + 'name': role_name}, +} + +ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID_INHERITED = { + 'scope': {'project': {'id': project_id}, + 'OS-INHERIT:inherited_to': 'projects'}, + 'user': {'id': user_id}, + 'role': {'id': role_id}, +} + +ASSIGNMENT_WITH_PROJECT_ID_AND_GROUP_ID = { + 'scope': {'project': {'id': project_id}}, + 'group': {'id': group_id}, + 'role': {'id': role_id}, +} + +ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID = { + 'scope': {'domain': {'id': domain_id}}, + 'user': {'id': user_id}, + 'role': {'id': role_id}, +} + +ASSIGNMENT_WITH_DOMAIN_ROLE = { + 'scope': {'domain': {'id': domain_id}}, + 'user': {'id': user_id}, + 'role': {'id': ROLE_2['id']}, +} + +ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID_INCLUDE_NAMES = { + 'scope': { + 'domain': {'id': domain_id, + 'name': domain_name}}, + 'user': { + 'domain': {'id': domain_id, + 'name': domain_name}, + 'id': user_id, + 'name': user_name}, + 'role': {'id': role_id, + 'name': role_name}, +} + +ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID_INHERITED = { + 'scope': {'domain': {'id': domain_id}, + 'OS-INHERIT:inherited_to': 'projects'}, + 'user': {'id': user_id}, + 'role': {'id': role_id}, +} + +ASSIGNMENT_WITH_DOMAIN_ID_AND_GROUP_ID = { + 'scope': {'domain': {'id': domain_id}}, + 'group': {'id': group_id}, + 'role': {'id': role_id}, +} + +consumer_id = 'test consumer id' +consumer_description = 'someone we trust' +consumer_secret = 'test consumer secret' + +OAUTH_CONSUMER = { + 'id': consumer_id, + 'secret': consumer_secret, + 'description': consumer_description +} + +access_token_id = 'test access token id' +access_token_secret = 'test access token secret' +access_token_expires = '2014-05-18T03:13:18.152071Z' + +OAUTH_ACCESS_TOKEN = { + 'id': access_token_id, + 'expires': access_token_expires, + 'key': access_token_id, + 'secret': access_token_secret +} + +request_token_id = 'test request token id' +request_token_secret = 'test request token secret' +request_token_expires = '2014-05-17T11:10:51.511336Z' + +OAUTH_REQUEST_TOKEN = { + 'id': request_token_id, + 'expires': request_token_expires, + 'key': request_token_id, + 'secret': request_token_secret +} + +oauth_verifier_pin = '6d74XaDS' +OAUTH_VERIFIER = { + 'oauth_verifier': oauth_verifier_pin +} + + +def fake_auth_ref(fake_token, fake_service=None): + """Create an auth_ref using keystoneauth's fixtures""" + token_copy = copy.deepcopy(fake_token) + token_id = token_copy.pop('id') + token = fixture.V3Token(**token_copy) + # An auth_ref is actually an access info object + auth_ref = access.create( + body=token, + auth_token=token_id, + ) + + # Create a service catalog + if fake_service: + service = token.add_service( + fake_service['type'], + fake_service['name'], + ) + # TODO(dtroyer): Add an 'id' element to KSA's _Service fixure + service['id'] = fake_service['id'] + for e in fake_service['endpoints']: + region = e.get('region_id') or e.get('region', '<none>') + service.add_endpoint( + e['interface'], + e['url'], + region=region, + ) + + return auth_ref + + +class FakeAuth(object): + + def __init__(self, auth_method_class=None): + self._auth_method_class = auth_method_class + + def get_token(self, *args, **kwargs): + return token_id + + +class FakeSession(object): + + def __init__(self, **kwargs): + self.auth = FakeAuth() + + +class FakeIdentityv3Client(object): + + def __init__(self, **kwargs): + self.domains = mock.Mock() + self.domains.resource_class = fakes.FakeResource(None, {}) + self.credentials = mock.Mock() + self.credentials.resource_class = fakes.FakeResource(None, {}) + self.endpoints = mock.Mock() + self.endpoints.resource_class = fakes.FakeResource(None, {}) + self.groups = mock.Mock() + self.groups.resource_class = fakes.FakeResource(None, {}) + self.oauth1 = mock.Mock() + self.oauth1.resource_class = fakes.FakeResource(None, {}) + self.projects = mock.Mock() + self.projects.resource_class = fakes.FakeResource(None, {}) + self.regions = mock.Mock() + self.regions.resource_class = fakes.FakeResource(None, {}) + self.roles = mock.Mock() + self.roles.resource_class = fakes.FakeResource(None, {}) + self.services = mock.Mock() + self.services.resource_class = fakes.FakeResource(None, {}) + self.session = mock.Mock() + self.session.auth.auth_ref.service_catalog.resource_class = \ + fakes.FakeResource(None, {}) + self.tokens = mock.Mock() + self.tokens.resource_class = fakes.FakeResource(None, {}) + self.trusts = mock.Mock() + self.trusts.resource_class = fakes.FakeResource(None, {}) + self.users = mock.Mock() + self.users.resource_class = fakes.FakeResource(None, {}) + self.role_assignments = mock.Mock() + self.role_assignments.resource_class = fakes.FakeResource(None, {}) + self.auth_token = kwargs['token'] + self.management_url = kwargs['endpoint'] + self.auth = FakeAuth() + self.auth.client = mock.Mock() + self.auth.client.resource_class = fakes.FakeResource(None, {}) + + +class FakeFederationManager(object): + + def __init__(self, **kwargs): + self.identity_providers = mock.Mock() + self.identity_providers.resource_class = fakes.FakeResource(None, {}) + self.mappings = mock.Mock() + self.mappings.resource_class = fakes.FakeResource(None, {}) + self.protocols = mock.Mock() + self.protocols.resource_class = fakes.FakeResource(None, {}) + self.projects = mock.Mock() + self.projects.resource_class = fakes.FakeResource(None, {}) + self.domains = mock.Mock() + self.domains.resource_class = fakes.FakeResource(None, {}) + self.service_providers = mock.Mock() + self.service_providers.resource_class = fakes.FakeResource(None, {}) + + +class FakeFederatedClient(FakeIdentityv3Client): + + def __init__(self, **kwargs): + super(FakeFederatedClient, self).__init__(**kwargs) + self.federation = FakeFederationManager() + + +class FakeOAuth1Client(FakeIdentityv3Client): + + def __init__(self, **kwargs): + super(FakeOAuth1Client, self).__init__(**kwargs) + + self.access_tokens = mock.Mock() + self.access_tokens.resource_class = fakes.FakeResource(None, {}) + self.consumers = mock.Mock() + self.consumers.resource_class = fakes.FakeResource(None, {}) + self.request_tokens = mock.Mock() + self.request_tokens.resource_class = fakes.FakeResource(None, {}) + + +class TestIdentityv3(utils.TestCommand): + + def setUp(self): + super(TestIdentityv3, self).setUp() + + self.app.client_manager.identity = FakeIdentityv3Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + + +class TestFederatedIdentity(utils.TestCommand): + + def setUp(self): + super(TestFederatedIdentity, self).setUp() + + self.app.client_manager.identity = FakeFederatedClient( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN + ) + + +class TestOAuth1(utils.TestCommand): + + def setUp(self): + super(TestOAuth1, self).setUp() + + self.app.client_manager.identity = FakeOAuth1Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN + ) + + +class FakeProject(object): + """Fake one or more project.""" + + @staticmethod + def create_one_project(attrs=None): + """Create a fake project. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, name, and so on + """ + + attrs = attrs or {} + + # set default attributes. + project_info = { + 'id': 'project-id-' + uuid.uuid4().hex, + 'name': 'project-name-' + uuid.uuid4().hex, + 'description': 'project-description-' + uuid.uuid4().hex, + 'enabled': True, + 'is_domain': False, + 'domain_id': 'domain-id-' + uuid.uuid4().hex, + 'parent_id': 'parent-id-' + uuid.uuid4().hex, + 'links': 'links-' + uuid.uuid4().hex, + } + project_info.update(attrs) + + project = fakes.FakeResource(info=copy.deepcopy(project_info), + loaded=True) + return project + + +class FakeDomain(object): + """Fake one or more domain.""" + + @staticmethod + def create_one_domain(attrs=None): + """Create a fake domain. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, name, and so on + """ + + attrs = attrs or {} + + # set default attributes. + domain_info = { + 'id': 'domain-id-' + uuid.uuid4().hex, + 'name': 'domain-name-' + uuid.uuid4().hex, + 'description': 'domain-description-' + uuid.uuid4().hex, + 'enabled': True, + 'links': 'links-' + uuid.uuid4().hex, + } + domain_info.update(attrs) + + domain = fakes.FakeResource(info=copy.deepcopy(domain_info), + loaded=True) + return domain + + +class FakeCredential(object): + """Fake one or more credential.""" + + @staticmethod + def create_one_credential(attrs=None): + """Create a fake credential. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, type, and so on + """ + + attrs = attrs or {} + + # set default attributes. + credential_info = { + 'id': 'credential-id-' + uuid.uuid4().hex, + 'type': 'cert', + 'user_id': 'user-id-' + uuid.uuid4().hex, + 'blob': 'credential-data-' + uuid.uuid4().hex, + 'project_id': 'project-id-' + uuid.uuid4().hex, + 'links': 'links-' + uuid.uuid4().hex, + } + credential_info.update(attrs) + + credential = fakes.FakeResource( + info=copy.deepcopy(credential_info), loaded=True) + return credential + + @staticmethod + def create_credentials(attrs=None, count=2): + """Create multiple fake credentials. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of credentials to fake + :return: + A list of FakeResource objects faking the credentials + """ + credentials = [] + for i in range(0, count): + credential = FakeCredential.create_one_credential(attrs) + credentials.append(credential) + + return credentials + + @staticmethod + def get_credentials(credentials=None, count=2): + """Get an iterable MagicMock object with a list of faked credentials. + + If credentials list is provided, then initialize the Mock object with + the list. Otherwise create one. + + :param List credentials: + A list of FakeResource objects faking credentials + :param Integer count: + The number of credentials to be faked + :return + An iterable Mock object with side_effect set to a list of faked + credentials + """ + if credentials is None: + credentials = FakeCredential.create_credentials(count) + + return mock.MagicMock(side_effect=credentials) + + +class FakeUser(object): + """Fake one or more user.""" + + @staticmethod + def create_one_user(attrs=None): + """Create a fake user. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, name, and so on + """ + + attrs = attrs or {} + + # set default attributes. + user_info = { + 'id': 'user-id-' + uuid.uuid4().hex, + 'name': 'user-name-' + uuid.uuid4().hex, + 'default_project_id': 'project-' + uuid.uuid4().hex, + 'email': 'user-email-' + uuid.uuid4().hex, + 'enabled': True, + 'domain_id': 'domain-id-' + uuid.uuid4().hex, + 'links': 'links-' + uuid.uuid4().hex, + } + user_info.update(attrs) + + user = fakes.FakeResource(info=copy.deepcopy(user_info), + loaded=True) + return user + + +class FakeGroup(object): + """Fake one or more group.""" + + @staticmethod + def create_one_group(attrs=None): + """Create a fake group. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, name, and so on + """ + + attrs = attrs or {} + + # set default attributes. + group_info = { + 'id': 'group-id-' + uuid.uuid4().hex, + 'name': 'group-name-' + uuid.uuid4().hex, + 'links': 'links-' + uuid.uuid4().hex, + 'domain_id': 'domain-id-' + uuid.uuid4().hex, + 'description': 'group-description-' + uuid.uuid4().hex, + } + group_info.update(attrs) + + group = fakes.FakeResource(info=copy.deepcopy(group_info), + loaded=True) + return group + + @staticmethod + def create_groups(attrs=None, count=2): + """Create multiple fake groups. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of groups to fake + :return: + A list of FakeResource objects faking the groups + """ + groups = [] + for i in range(0, count): + group = FakeGroup.create_one_group(attrs) + groups.append(group) + + return groups + + @staticmethod + def get_groups(groups=None, count=2): + """Get an iterable MagicMock object with a list of faked groups. + + If groups list is provided, then initialize the Mock object with + the list. Otherwise create one. + + :param List groups: + A list of FakeResource objects faking groups + :param Integer count: + The number of groups to be faked + :return + An iterable Mock object with side_effect set to a list of faked + groups + """ + if groups is None: + groups = FakeGroup.create_groups(count) + + return mock.MagicMock(side_effect=groups) + + +class FakeEndpoint(object): + """Fake one or more endpoint.""" + + @staticmethod + def create_one_endpoint(attrs=None): + """Create a fake endpoint. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, url, and so on + """ + + attrs = attrs or {} + + # set default attributes. + endpoint_info = { + 'id': 'endpoint-id-' + uuid.uuid4().hex, + 'url': 'url-' + uuid.uuid4().hex, + 'region': 'endpoint-region-' + uuid.uuid4().hex, + 'interface': 'admin', + 'service_id': 'service-id-' + uuid.uuid4().hex, + 'enabled': True, + 'links': 'links-' + uuid.uuid4().hex, + } + endpoint_info.update(attrs) + + endpoint = fakes.FakeResource(info=copy.deepcopy(endpoint_info), + loaded=True) + return endpoint + + +class FakeService(object): + """Fake one or more service.""" + + @staticmethod + def create_one_service(attrs=None): + """Create a fake service. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, name, and so on + """ + + attrs = attrs or {} + + # set default attributes. + service_info = { + 'id': 'service-id-' + uuid.uuid4().hex, + 'name': 'service-name-' + uuid.uuid4().hex, + 'type': 'service-type-' + uuid.uuid4().hex, + 'description': 'service-description-' + uuid.uuid4().hex, + 'enabled': True, + 'links': 'links-' + uuid.uuid4().hex, + } + service_info.update(attrs) + + service = fakes.FakeResource(info=copy.deepcopy(service_info), + loaded=True) + return service + + +class FakeRoleAssignment(object): + """Fake one or more role assignment.""" + + @staticmethod + def create_one_role_assignment(attrs=None): + """Create a fake role assignment. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with scope, user, and so on + """ + + attrs = attrs or {} + + # set default attributes. + role_assignment_info = { + 'scope': {'project': {'id': 'project-id-' + uuid.uuid4().hex}}, + 'user': {'id': 'user-id-' + uuid.uuid4().hex}, + 'role': {'id': 'role-id-' + uuid.uuid4().hex}, + } + role_assignment_info.update(attrs) + + role_assignment = fakes.FakeResource( + info=copy.deepcopy(role_assignment_info), loaded=True) + + return role_assignment diff --git a/openstackclient/tests/unit/identity/v3/test_catalog.py b/openstackclient/tests/unit/identity/v3/test_catalog.py new file mode 100644 index 00000000..986c05f3 --- /dev/null +++ b/openstackclient/tests/unit/identity/v3/test_catalog.py @@ -0,0 +1,142 @@ +# 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 openstackclient.identity.v3 import catalog +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes +from openstackclient.tests.unit import utils + + +class TestCatalog(utils.TestCommand): + + fake_service = { + 'id': 'qwertyuiop', + 'type': 'compute', + 'name': 'supernova', + 'endpoints': [ + { + 'region': 'onlyone', + 'url': 'https://public.example.com', + 'interface': 'public', + }, + { + 'region_id': 'onlyone', + 'url': 'https://admin.example.com', + 'interface': 'admin', + }, + { + 'url': 'https://internal.example.com', + 'interface': 'internal', + }, + { + 'region': None, + 'url': 'https://none.example.com', + 'interface': 'none', + }, + ], + } + + def setUp(self): + super(TestCatalog, self).setUp() + + self.sc_mock = mock.MagicMock() + self.sc_mock.service_catalog.catalog.return_value = [ + self.fake_service, + ] + + self.auth_mock = mock.MagicMock() + self.app.client_manager.session = self.auth_mock + + self.auth_mock.auth.get_auth_ref.return_value = self.sc_mock + + +class TestCatalogList(TestCatalog): + + def setUp(self): + super(TestCatalogList, self).setUp() + + # Get the command object to test + self.cmd = catalog.ListCatalog(self.app, None) + + def test_catalog_list(self): + auth_ref = identity_fakes.fake_auth_ref( + identity_fakes.TOKEN_WITH_PROJECT_ID, + fake_service=self.fake_service, + ) + self.ar_mock = mock.PropertyMock(return_value=auth_ref) + type(self.app.client_manager).auth_ref = self.ar_mock + + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + collist = ('Name', 'Type', 'Endpoints') + self.assertEqual(collist, columns) + datalist = (( + 'supernova', + 'compute', + 'onlyone\n public: https://public.example.com\n' + 'onlyone\n admin: https://admin.example.com\n' + '<none>\n internal: https://internal.example.com\n' + '<none>\n none: https://none.example.com\n', + ), ) + self.assertEqual(datalist, tuple(data)) + + +class TestCatalogShow(TestCatalog): + + def setUp(self): + super(TestCatalogShow, self).setUp() + + # Get the command object to test + self.cmd = catalog.ShowCatalog(self.app, None) + + def test_catalog_show(self): + auth_ref = identity_fakes.fake_auth_ref( + identity_fakes.TOKEN_WITH_PROJECT_ID, + fake_service=self.fake_service, + ) + self.ar_mock = mock.PropertyMock(return_value=auth_ref) + type(self.app.client_manager).auth_ref = self.ar_mock + + arglist = [ + 'compute', + ] + verifylist = [ + ('service', 'compute'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + collist = ('endpoints', 'id', 'name', 'type') + self.assertEqual(collist, columns) + datalist = ( + 'onlyone\n public: https://public.example.com\nonlyone\n' + ' admin: https://admin.example.com\n' + '<none>\n internal: https://internal.example.com\n' + '<none>\n none: https://none.example.com\n', + 'qwertyuiop', + 'supernova', + 'compute', + ) + self.assertEqual(datalist, data) diff --git a/openstackclient/tests/unit/identity/v3/test_consumer.py b/openstackclient/tests/unit/identity/v3/test_consumer.py new file mode 100644 index 00000000..403250ef --- /dev/null +++ b/openstackclient/tests/unit/identity/v3/test_consumer.py @@ -0,0 +1,219 @@ +# 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 copy + +from openstackclient.identity.v3 import consumer +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes + + +class TestOAuth1(identity_fakes.TestOAuth1): + + def setUp(self): + super(TestOAuth1, self).setUp() + identity_client = self.app.client_manager.identity + self.consumers_mock = identity_client.oauth1.consumers + self.consumers_mock.reset_mock() + + +class TestConsumerCreate(TestOAuth1): + + def setUp(self): + super(TestConsumerCreate, self).setUp() + + self.consumers_mock.create.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.OAUTH_CONSUMER), + loaded=True, + ) + + self.cmd = consumer.CreateConsumer(self.app, None) + + def test_create_consumer(self): + arglist = [ + '--description', identity_fakes.consumer_description, + ] + verifylist = [ + ('description', identity_fakes.consumer_description), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.consumers_mock.create.assert_called_with( + identity_fakes.consumer_description, + ) + + collist = ('description', 'id', 'secret') + self.assertEqual(collist, columns) + datalist = ( + identity_fakes.consumer_description, + identity_fakes.consumer_id, + identity_fakes.consumer_secret, + ) + self.assertEqual(datalist, data) + + +class TestConsumerDelete(TestOAuth1): + + def setUp(self): + super(TestConsumerDelete, self).setUp() + + # This is the return value for utils.find_resource() + self.consumers_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.OAUTH_CONSUMER), + loaded=True, + ) + + self.consumers_mock.delete.return_value = None + self.cmd = consumer.DeleteConsumer(self.app, None) + + def test_delete_consumer(self): + arglist = [ + identity_fakes.consumer_id, + ] + verifylist = [ + ('consumer', [identity_fakes.consumer_id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.consumers_mock.delete.assert_called_with( + identity_fakes.consumer_id, + ) + self.assertIsNone(result) + + +class TestConsumerList(TestOAuth1): + + def setUp(self): + super(TestConsumerList, self).setUp() + + self.consumers_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.OAUTH_CONSUMER), + loaded=True, + ) + self.consumers_mock.list.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.OAUTH_CONSUMER), + loaded=True, + ), + ] + + # Get the command object to test + self.cmd = consumer.ListConsumer(self.app, None) + + def test_consumer_list(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + self.consumers_mock.list.assert_called_with() + + collist = ('ID', 'Description') + self.assertEqual(collist, columns) + datalist = (( + identity_fakes.consumer_id, + identity_fakes.consumer_description, + ), ) + self.assertEqual(datalist, tuple(data)) + + +class TestConsumerSet(TestOAuth1): + + def setUp(self): + super(TestConsumerSet, self).setUp() + + self.consumers_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.OAUTH_CONSUMER), + loaded=True, + ) + + consumer_updated = copy.deepcopy(identity_fakes.OAUTH_CONSUMER) + consumer_updated['description'] = "consumer new description" + self.consumers_mock.update.return_value = fakes.FakeResource( + None, + consumer_updated, + loaded=True, + ) + + self.cmd = consumer.SetConsumer(self.app, None) + + def test_consumer_update(self): + new_description = "consumer new description" + + arglist = [ + '--description', new_description, + identity_fakes.consumer_id, + ] + verifylist = [ + ('description', new_description), + ('consumer', identity_fakes.consumer_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + kwargs = {'description': new_description} + self.consumers_mock.update.assert_called_with( + identity_fakes.consumer_id, + **kwargs + ) + self.assertIsNone(result) + + +class TestConsumerShow(TestOAuth1): + + def setUp(self): + super(TestConsumerShow, self).setUp() + + consumer_no_secret = copy.deepcopy(identity_fakes.OAUTH_CONSUMER) + del consumer_no_secret['secret'] + self.consumers_mock.get.return_value = fakes.FakeResource( + None, + consumer_no_secret, + loaded=True, + ) + + # Get the command object to test + self.cmd = consumer.ShowConsumer(self.app, None) + + def test_consumer_show(self): + arglist = [ + identity_fakes.consumer_id, + ] + verifylist = [ + ('consumer', identity_fakes.consumer_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.consumers_mock.get.assert_called_with( + identity_fakes.consumer_id, + ) + + collist = ('description', 'id') + self.assertEqual(collist, columns) + datalist = ( + identity_fakes.consumer_description, + identity_fakes.consumer_id, + ) + self.assertEqual(datalist, data) diff --git a/openstackclient/tests/unit/identity/v3/test_credential.py b/openstackclient/tests/unit/identity/v3/test_credential.py new file mode 100644 index 00000000..fd3ae6b2 --- /dev/null +++ b/openstackclient/tests/unit/identity/v3/test_credential.py @@ -0,0 +1,357 @@ +# 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 mock import call + +from osc_lib import exceptions + +from openstackclient.identity.v3 import credential +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes +from openstackclient.tests.unit import utils + + +class TestCredential(identity_fakes.TestIdentityv3): + + def setUp(self): + super(TestCredential, self).setUp() + + # Get a shortcut to the CredentialManager Mock + self.credentials_mock = self.app.client_manager.identity.credentials + self.credentials_mock.reset_mock() + + # Get a shortcut to the UserManager Mock + self.users_mock = self.app.client_manager.identity.users + self.users_mock.reset_mock() + + # Get a shortcut to the ProjectManager Mock + self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock.reset_mock() + + +class TestCredentialCreate(TestCredential): + + user = identity_fakes.FakeUser.create_one_user() + project = identity_fakes.FakeProject.create_one_project() + columns = ( + 'blob', + 'id', + 'project_id', + 'type', + 'user_id', + ) + + def setUp(self): + super(TestCredentialCreate, self).setUp() + + self.credential = identity_fakes.FakeCredential.create_one_credential( + attrs={'user_id': self.user.id, 'project_id': self.project.id}) + self.credentials_mock.create.return_value = self.credential + self.users_mock.get.return_value = self.user + self.projects_mock.get.return_value = self.project + self.data = ( + self.credential.blob, + self.credential.id, + self.credential.project_id, + self.credential.type, + self.credential.user_id, + ) + + self.cmd = credential.CreateCredential(self.app, None) + + def test_credential_create_no_options(self): + arglist = [ + self.credential.user_id, + self.credential.blob, + ] + verifylist = [ + ('user', self.credential.user_id), + ('data', self.credential.blob), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + kwargs = { + 'user': self.credential.user_id, + 'type': self.credential.type, + 'blob': self.credential.blob, + 'project': None, + } + self.credentials_mock.create.assert_called_once_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_credential_create_with_options(self): + arglist = [ + self.credential.user_id, + self.credential.blob, + '--type', self.credential.type, + '--project', self.credential.project_id, + ] + verifylist = [ + ('user', self.credential.user_id), + ('data', self.credential.blob), + ('type', self.credential.type), + ('project', self.credential.project_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + kwargs = { + 'user': self.credential.user_id, + 'type': self.credential.type, + 'blob': self.credential.blob, + 'project': self.credential.project_id, + } + self.credentials_mock.create.assert_called_once_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_credential_create_with_invalid_type(self): + arglist = [ + self.credential.user_id, + self.credential.blob, + '--type', 'invalid_type', + ] + verifylist = [ + ('user', self.credential.user_id), + ('data', self.credential.blob), + ('type', 'invalid_type'), + ] + self.assertRaises(utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + +class TestCredentialDelete(TestCredential): + + credentials = identity_fakes.FakeCredential.create_credentials(count=2) + + def setUp(self): + super(TestCredentialDelete, self).setUp() + + self.credentials_mock.delete.return_value = None + + # Get the command object to test + self.cmd = credential.DeleteCredential(self.app, None) + + def test_credential_delete(self): + arglist = [ + self.credentials[0].id, + ] + verifylist = [ + ('credential', [self.credentials[0].id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.credentials_mock.delete.assert_called_with( + self.credentials[0].id, + ) + self.assertIsNone(result) + + def test_credential_multi_delete(self): + arglist = [] + for c in self.credentials: + arglist.append(c.id) + verifylist = [ + ('credential', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + calls = [] + for c in self.credentials: + calls.append(call(c.id)) + self.credentials_mock.delete.assert_has_calls(calls) + self.assertIsNone(result) + + def test_credential_multi_delete_with_exception(self): + arglist = [ + self.credentials[0].id, + 'unexist_credential', + ] + verifylist = [ + ('credential', [self.credentials[0].id, 'unexist_credential']) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + delete_mock_result = [None, exceptions.CommandError] + self.credentials_mock.delete = ( + mock.MagicMock(side_effect=delete_mock_result) + ) + + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 credential failed to delete.', str(e)) + + self.credentials_mock.delete.assert_any_call(self.credentials[0].id) + self.credentials_mock.delete.assert_any_call('unexist_credential') + + +class TestCredentialList(TestCredential): + + credential = identity_fakes.FakeCredential.create_one_credential() + + columns = ('ID', 'Type', 'User ID', 'Data', 'Project ID') + data = (( + credential.id, + credential.type, + credential.user_id, + credential.blob, + credential.project_id, + ), ) + + def setUp(self): + super(TestCredentialList, self).setUp() + + self.credentials_mock.list.return_value = [self.credential] + + # Get the command object to test + self.cmd = credential.ListCredential(self.app, None) + + def test_domain_list_no_options(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.credentials_mock.list.assert_called_with() + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, tuple(data)) + + +class TestCredentialSet(TestCredential): + + credential = identity_fakes.FakeCredential.create_one_credential() + + def setUp(self): + super(TestCredentialSet, self).setUp() + self.cmd = credential.SetCredential(self.app, None) + + def test_credential_set_no_options(self): + arglist = [ + self.credential.id, + ] + + self.assertRaises(utils.ParserException, + self.check_parser, self.cmd, arglist, []) + + def test_credential_set_missing_user(self): + arglist = [ + '--type', 'ec2', + '--data', self.credential.blob, + self.credential.id, + ] + + self.assertRaises(utils.ParserException, + self.check_parser, self.cmd, arglist, []) + + def test_credential_set_missing_type(self): + arglist = [ + '--user', self.credential.user_id, + '--data', self.credential.blob, + self.credential.id, + ] + + self.assertRaises(utils.ParserException, + self.check_parser, self.cmd, arglist, []) + + def test_credential_set_missing_data(self): + arglist = [ + '--user', self.credential.user_id, + '--type', 'ec2', + self.credential.id, + ] + + self.assertRaises(utils.ParserException, + self.check_parser, self.cmd, arglist, []) + + def test_credential_set_valid(self): + arglist = [ + '--user', self.credential.user_id, + '--type', 'ec2', + '--data', self.credential.blob, + self.credential.id, + ] + parsed_args = self.check_parser(self.cmd, arglist, []) + + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + + def test_credential_set_valid_with_project(self): + arglist = [ + '--user', self.credential.user_id, + '--type', 'ec2', + '--data', self.credential.blob, + '--project', self.credential.project_id, + self.credential.id, + ] + parsed_args = self.check_parser(self.cmd, arglist, []) + + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + + +class TestCredentialShow(TestCredential): + + columns = ( + 'blob', + 'id', + 'project_id', + 'type', + 'user_id', + ) + + def setUp(self): + super(TestCredentialShow, self).setUp() + + self.credential = identity_fakes.FakeCredential.create_one_credential() + self.credentials_mock.get.return_value = self.credential + self.data = ( + self.credential.blob, + self.credential.id, + self.credential.project_id, + self.credential.type, + self.credential.user_id, + ) + + self.cmd = credential.ShowCredential(self.app, None) + + def test_credential_show(self): + arglist = [ + self.credential.id, + ] + verifylist = [ + ('credential', self.credential.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.credentials_mock.get.assert_called_once_with(self.credential.id) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/identity/v3/test_domain.py b/openstackclient/tests/unit/identity/v3/test_domain.py new file mode 100644 index 00000000..36f13d33 --- /dev/null +++ b/openstackclient/tests/unit/identity/v3/test_domain.py @@ -0,0 +1,401 @@ +# 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 openstackclient.identity.v3 import domain +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes + + +class TestDomain(identity_fakes.TestIdentityv3): + + def setUp(self): + super(TestDomain, self).setUp() + + # Get a shortcut to the DomainManager Mock + self.domains_mock = self.app.client_manager.identity.domains + self.domains_mock.reset_mock() + + +class TestDomainCreate(TestDomain): + + columns = ( + 'description', + 'enabled', + 'id', + 'name', + ) + + def setUp(self): + super(TestDomainCreate, self).setUp() + + self.domain = identity_fakes.FakeDomain.create_one_domain() + self.domains_mock.create.return_value = self.domain + self.datalist = ( + self.domain.description, + True, + self.domain.id, + self.domain.name, + ) + + # Get the command object to test + self.cmd = domain.CreateDomain(self.app, None) + + def test_domain_create_no_options(self): + arglist = [ + self.domain.name, + ] + verifylist = [ + ('name', self.domain.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.domain.name, + 'description': None, + 'enabled': True, + } + self.domains_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_domain_create_description(self): + arglist = [ + '--description', 'new desc', + self.domain.name, + ] + verifylist = [ + ('description', 'new desc'), + ('name', self.domain.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.domain.name, + 'description': 'new desc', + 'enabled': True, + } + self.domains_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_domain_create_enable(self): + arglist = [ + '--enable', + self.domain.name, + ] + verifylist = [ + ('enable', True), + ('name', self.domain.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.domain.name, + 'description': None, + 'enabled': True, + } + self.domains_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_domain_create_disable(self): + arglist = [ + '--disable', + self.domain.name, + ] + verifylist = [ + ('disable', True), + ('name', self.domain.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.domain.name, + 'description': None, + 'enabled': False, + } + self.domains_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + +class TestDomainDelete(TestDomain): + + domain = identity_fakes.FakeDomain.create_one_domain() + + def setUp(self): + super(TestDomainDelete, self).setUp() + + # This is the return value for utils.find_resource() + self.domains_mock.get.return_value = self.domain + self.domains_mock.delete.return_value = None + + # Get the command object to test + self.cmd = domain.DeleteDomain(self.app, None) + + def test_domain_delete(self): + arglist = [ + self.domain.id, + ] + verifylist = [ + ('domain', [self.domain.id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.domains_mock.delete.assert_called_with( + self.domain.id, + ) + self.assertIsNone(result) + + +class TestDomainList(TestDomain): + + domain = identity_fakes.FakeDomain.create_one_domain() + + def setUp(self): + super(TestDomainList, self).setUp() + + self.domains_mock.list.return_value = [self.domain] + + # Get the command object to test + self.cmd = domain.ListDomain(self.app, None) + + def test_domain_list_no_options(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + self.domains_mock.list.assert_called_with() + + collist = ('ID', 'Name', 'Enabled', 'Description') + self.assertEqual(collist, columns) + datalist = (( + self.domain.id, + self.domain.name, + True, + self.domain.description, + ), ) + self.assertEqual(datalist, tuple(data)) + + +class TestDomainSet(TestDomain): + + domain = identity_fakes.FakeDomain.create_one_domain() + + def setUp(self): + super(TestDomainSet, self).setUp() + + self.domains_mock.get.return_value = self.domain + + self.domains_mock.update.return_value = self.domain + + # Get the command object to test + self.cmd = domain.SetDomain(self.app, None) + + def test_domain_set_no_options(self): + arglist = [ + self.domain.name, + ] + verifylist = [ + ('domain', self.domain.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + kwargs = {} + self.domains_mock.update.assert_called_with( + self.domain.id, + **kwargs + ) + self.assertIsNone(result) + + def test_domain_set_name(self): + arglist = [ + '--name', 'qwerty', + self.domain.id, + ] + verifylist = [ + ('name', 'qwerty'), + ('domain', self.domain.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': 'qwerty', + } + self.domains_mock.update.assert_called_with( + self.domain.id, + **kwargs + ) + self.assertIsNone(result) + + def test_domain_set_description(self): + arglist = [ + '--description', 'new desc', + self.domain.id, + ] + verifylist = [ + ('description', 'new desc'), + ('domain', self.domain.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'description': 'new desc', + } + self.domains_mock.update.assert_called_with( + self.domain.id, + **kwargs + ) + self.assertIsNone(result) + + def test_domain_set_enable(self): + arglist = [ + '--enable', + self.domain.id, + ] + verifylist = [ + ('enable', True), + ('domain', self.domain.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': True, + } + self.domains_mock.update.assert_called_with( + self.domain.id, + **kwargs + ) + self.assertIsNone(result) + + def test_domain_set_disable(self): + arglist = [ + '--disable', + self.domain.id, + ] + verifylist = [ + ('disable', True), + ('domain', self.domain.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': False, + } + self.domains_mock.update.assert_called_with( + self.domain.id, + **kwargs + ) + self.assertIsNone(result) + + +class TestDomainShow(TestDomain): + + def setUp(self): + super(TestDomainShow, self).setUp() + + self.domain = identity_fakes.FakeDomain.create_one_domain() + self.domains_mock.get.return_value = self.domain + # Get the command object to test + self.cmd = domain.ShowDomain(self.app, None) + + def test_domain_show(self): + arglist = [ + self.domain.id, + ] + verifylist = [ + ('domain', self.domain.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.app.client_manager.identity.tokens.get_token_data.return_value = \ + {'token': + {'project': + {'domain': + {'id': 'd1', + 'name': 'd1' + } + } + } + } + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + self.domains_mock.get.assert_called_with( + self.domain.id, + ) + + collist = ('description', 'enabled', 'id', 'name') + self.assertEqual(collist, columns) + datalist = ( + self.domain.description, + True, + self.domain.id, + self.domain.name, + ) + self.assertEqual(datalist, data) diff --git a/openstackclient/tests/unit/identity/v3/test_endpoint.py b/openstackclient/tests/unit/identity/v3/test_endpoint.py new file mode 100644 index 00000000..765fbedd --- /dev/null +++ b/openstackclient/tests/unit/identity/v3/test_endpoint.py @@ -0,0 +1,750 @@ +# 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 openstackclient.identity.v3 import endpoint +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes + + +class TestEndpoint(identity_fakes.TestIdentityv3): + + def setUp(self): + super(TestEndpoint, self).setUp() + + # Get a shortcut to the EndpointManager Mock + self.endpoints_mock = self.app.client_manager.identity.endpoints + self.endpoints_mock.reset_mock() + + # Get a shortcut to the ServiceManager Mock + self.services_mock = self.app.client_manager.identity.services + self.services_mock.reset_mock() + + +class TestEndpointCreate(TestEndpoint): + + service = identity_fakes.FakeService.create_one_service() + + columns = ( + 'enabled', + 'id', + 'interface', + 'region', + 'service_id', + 'service_name', + 'service_type', + 'url', + ) + + def setUp(self): + super(TestEndpointCreate, self).setUp() + + self.endpoint = identity_fakes.FakeEndpoint.create_one_endpoint( + attrs={'service_id': self.service.id}) + self.endpoints_mock.create.return_value = self.endpoint + + # This is the return value for common.find_resource(service) + self.services_mock.get.return_value = self.service + + # Get the command object to test + self.cmd = endpoint.CreateEndpoint(self.app, None) + + def test_endpoint_create_no_options(self): + arglist = [ + self.service.id, + self.endpoint.interface, + self.endpoint.url, + ] + verifylist = [ + ('enabled', True), + ('service', self.service.id), + ('interface', self.endpoint.interface), + ('url', self.endpoint.url), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'service': self.service.id, + 'url': self.endpoint.url, + 'interface': self.endpoint.interface, + 'enabled': True, + 'region': None, + } + + self.endpoints_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + datalist = ( + True, + self.endpoint.id, + self.endpoint.interface, + self.endpoint.region, + self.service.id, + self.service.name, + self.service.type, + self.endpoint.url, + ) + self.assertEqual(datalist, data) + + def test_endpoint_create_region(self): + arglist = [ + self.service.id, + self.endpoint.interface, + self.endpoint.url, + '--region', self.endpoint.region, + ] + verifylist = [ + ('enabled', True), + ('service', self.service.id), + ('interface', self.endpoint.interface), + ('url', self.endpoint.url), + ('region', self.endpoint.region), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'service': self.service.id, + 'url': self.endpoint.url, + 'interface': self.endpoint.interface, + 'enabled': True, + 'region': self.endpoint.region, + } + + self.endpoints_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + datalist = ( + True, + self.endpoint.id, + self.endpoint.interface, + self.endpoint.region, + self.service.id, + self.service.name, + self.service.type, + self.endpoint.url, + ) + self.assertEqual(datalist, data) + + def test_endpoint_create_enable(self): + arglist = [ + self.service.id, + self.endpoint.interface, + self.endpoint.url, + '--enable' + ] + verifylist = [ + ('enabled', True), + ('service', self.service.id), + ('interface', self.endpoint.interface), + ('url', self.endpoint.url), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'service': self.service.id, + 'url': self.endpoint.url, + 'interface': self.endpoint.interface, + 'enabled': True, + 'region': None, + } + + self.endpoints_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + datalist = ( + True, + self.endpoint.id, + self.endpoint.interface, + self.endpoint.region, + self.service.id, + self.service.name, + self.service.type, + self.endpoint.url, + ) + self.assertEqual(datalist, data) + + def test_endpoint_create_disable(self): + arglist = [ + self.service.id, + self.endpoint.interface, + self.endpoint.url, + '--disable', + ] + verifylist = [ + ('enabled', False), + ('service', self.service.id), + ('interface', self.endpoint.interface), + ('url', self.endpoint.url), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'service': self.service.id, + 'url': self.endpoint.url, + 'interface': self.endpoint.interface, + 'enabled': False, + 'region': None, + } + + self.endpoints_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + datalist = ( + True, + self.endpoint.id, + self.endpoint.interface, + self.endpoint.region, + self.service.id, + self.service.name, + self.service.type, + self.endpoint.url, + ) + self.assertEqual(datalist, data) + + +class TestEndpointDelete(TestEndpoint): + + endpoint = identity_fakes.FakeEndpoint.create_one_endpoint() + + def setUp(self): + super(TestEndpointDelete, self).setUp() + + # This is the return value for utils.find_resource(endpoint) + self.endpoints_mock.get.return_value = self.endpoint + self.endpoints_mock.delete.return_value = None + + # Get the command object to test + self.cmd = endpoint.DeleteEndpoint(self.app, None) + + def test_endpoint_delete(self): + arglist = [ + self.endpoint.id, + ] + verifylist = [ + ('endpoint', [self.endpoint.id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.endpoints_mock.delete.assert_called_with( + self.endpoint.id, + ) + self.assertIsNone(result) + + +class TestEndpointList(TestEndpoint): + + service = identity_fakes.FakeService.create_one_service() + endpoint = identity_fakes.FakeEndpoint.create_one_endpoint( + attrs={'service_id': service.id}) + + columns = ( + 'ID', + 'Region', + 'Service Name', + 'Service Type', + 'Enabled', + 'Interface', + 'URL', + ) + + def setUp(self): + super(TestEndpointList, self).setUp() + + self.endpoints_mock.list.return_value = [self.endpoint] + + # This is the return value for common.find_resource(service) + self.services_mock.get.return_value = self.service + + # Get the command object to test + self.cmd = endpoint.ListEndpoint(self.app, None) + + def test_endpoint_list_no_options(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + self.endpoints_mock.list.assert_called_with() + + self.assertEqual(self.columns, columns) + datalist = ( + ( + self.endpoint.id, + self.endpoint.region, + self.service.name, + self.service.type, + True, + self.endpoint.interface, + self.endpoint.url, + ), + ) + self.assertEqual(datalist, tuple(data)) + + def test_endpoint_list_service(self): + arglist = [ + '--service', self.service.id, + ] + verifylist = [ + ('service', self.service.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'service': self.service.id, + } + self.endpoints_mock.list.assert_called_with(**kwargs) + + self.assertEqual(self.columns, columns) + datalist = ( + ( + self.endpoint.id, + self.endpoint.region, + self.service.name, + self.service.type, + True, + self.endpoint.interface, + self.endpoint.url, + ), + ) + self.assertEqual(datalist, tuple(data)) + + def test_endpoint_list_interface(self): + arglist = [ + '--interface', self.endpoint.interface, + ] + verifylist = [ + ('interface', self.endpoint.interface), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'interface': self.endpoint.interface, + } + self.endpoints_mock.list.assert_called_with(**kwargs) + + self.assertEqual(self.columns, columns) + datalist = ( + ( + self.endpoint.id, + self.endpoint.region, + self.service.name, + self.service.type, + True, + self.endpoint.interface, + self.endpoint.url, + ), + ) + self.assertEqual(datalist, tuple(data)) + + def test_endpoint_list_region(self): + arglist = [ + '--region', self.endpoint.region, + ] + verifylist = [ + ('region', self.endpoint.region), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'region': self.endpoint.region, + } + self.endpoints_mock.list.assert_called_with(**kwargs) + + self.assertEqual(self.columns, columns) + datalist = ( + ( + self.endpoint.id, + self.endpoint.region, + self.service.name, + self.service.type, + True, + self.endpoint.interface, + self.endpoint.url, + ), + ) + self.assertEqual(datalist, tuple(data)) + + +class TestEndpointSet(TestEndpoint): + + service = identity_fakes.FakeService.create_one_service() + endpoint = identity_fakes.FakeEndpoint.create_one_endpoint( + attrs={'service_id': service.id}) + + def setUp(self): + super(TestEndpointSet, self).setUp() + + # This is the return value for utils.find_resource(endpoint) + self.endpoints_mock.get.return_value = self.endpoint + + self.endpoints_mock.update.return_value = self.endpoint + + # This is the return value for common.find_resource(service) + self.services_mock.get.return_value = self.service + + # Get the command object to test + self.cmd = endpoint.SetEndpoint(self.app, None) + + def test_endpoint_set_no_options(self): + arglist = [ + self.endpoint.id, + ] + verifylist = [ + ('endpoint', self.endpoint.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + kwargs = { + 'enabled': None, + 'interface': None, + 'region': None, + 'service': None, + 'url': None, + } + self.endpoints_mock.update.assert_called_with( + self.endpoint.id, + **kwargs + ) + self.assertIsNone(result) + + def test_endpoint_set_interface(self): + arglist = [ + '--interface', 'public', + self.endpoint.id + ] + verifylist = [ + ('interface', 'public'), + ('endpoint', self.endpoint.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': None, + 'interface': 'public', + 'url': None, + 'region': None, + 'service': None, + } + self.endpoints_mock.update.assert_called_with( + self.endpoint.id, + **kwargs + ) + self.assertIsNone(result) + + def test_endpoint_set_url(self): + arglist = [ + '--url', 'http://localhost:5000', + self.endpoint.id + ] + verifylist = [ + ('url', 'http://localhost:5000'), + ('endpoint', self.endpoint.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': None, + 'interface': None, + 'url': 'http://localhost:5000', + 'region': None, + 'service': None, + } + self.endpoints_mock.update.assert_called_with( + self.endpoint.id, + **kwargs + ) + self.assertIsNone(result) + + def test_endpoint_set_service(self): + arglist = [ + '--service', self.service.id, + self.endpoint.id + ] + verifylist = [ + ('service', self.service.id), + ('endpoint', self.endpoint.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': None, + 'interface': None, + 'url': None, + 'region': None, + 'service': self.service.id, + } + self.endpoints_mock.update.assert_called_with( + self.endpoint.id, + **kwargs + ) + self.assertIsNone(result) + + def test_endpoint_set_region(self): + arglist = [ + '--region', 'e-rzzz', + self.endpoint.id + ] + verifylist = [ + ('region', 'e-rzzz'), + ('endpoint', self.endpoint.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': None, + 'interface': None, + 'url': None, + 'region': 'e-rzzz', + 'service': None, + } + self.endpoints_mock.update.assert_called_with( + self.endpoint.id, + **kwargs + ) + self.assertIsNone(result) + + def test_endpoint_set_enable(self): + arglist = [ + '--enable', + self.endpoint.id + ] + verifylist = [ + ('enabled', True), + ('endpoint', self.endpoint.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': True, + 'interface': None, + 'url': None, + 'region': None, + 'service': None, + } + self.endpoints_mock.update.assert_called_with( + self.endpoint.id, + **kwargs + ) + self.assertIsNone(result) + + def test_endpoint_set_disable(self): + arglist = [ + '--disable', + self.endpoint.id + ] + verifylist = [ + ('disabled', True), + ('endpoint', self.endpoint.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': False, + 'interface': None, + 'url': None, + 'region': None, + 'service': None, + } + self.endpoints_mock.update.assert_called_with( + self.endpoint.id, + **kwargs + ) + self.assertIsNone(result) + + +class TestEndpointShow(TestEndpoint): + + service = identity_fakes.FakeService.create_one_service() + endpoint = identity_fakes.FakeEndpoint.create_one_endpoint( + attrs={'service_id': service.id}) + + def setUp(self): + super(TestEndpointShow, self).setUp() + + self.endpoints_mock.get.return_value = self.endpoint + + # This is the return value for common.find_resource(service) + self.services_mock.get.return_value = self.service + + # Get the command object to test + self.cmd = endpoint.ShowEndpoint(self.app, None) + + def test_endpoint_show(self): + arglist = [ + self.endpoint.id, + ] + verifylist = [ + ('endpoint', self.endpoint.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + self.endpoints_mock.get.assert_called_with( + self.endpoint.id, + ) + + collist = ( + 'enabled', + 'id', + 'interface', + 'region', + 'service_id', + 'service_name', + 'service_type', + 'url', + ) + self.assertEqual(collist, columns) + datalist = ( + True, + self.endpoint.id, + self.endpoint.interface, + self.endpoint.region, + self.service.id, + self.service.name, + self.service.type, + self.endpoint.url, + ) + self.assertEqual(datalist, data) + + +class TestEndpointCreateServiceWithoutName(TestEndpointCreate): + + service = identity_fakes.FakeService.create_one_service( + attrs={'service_name': ''}) + + def setUp(self): + super(TestEndpointCreate, self).setUp() + + self.endpoint = identity_fakes.FakeEndpoint.create_one_endpoint( + attrs={'service_id': self.service.id}) + + self.endpoints_mock.create.return_value = self.endpoint + + # This is the return value for common.find_resource(service) + self.services_mock.get.return_value = self.service + + # Get the command object to test + self.cmd = endpoint.CreateEndpoint(self.app, None) + + +class TestEndpointListServiceWithoutName(TestEndpointList): + + service = identity_fakes.FakeService.create_one_service( + attrs={'service_name': ''}) + endpoint = identity_fakes.FakeEndpoint.create_one_endpoint( + attrs={'service_id': service.id}) + + def setUp(self): + super(TestEndpointList, self).setUp() + + self.endpoints_mock.list.return_value = [self.endpoint] + + # This is the return value for common.find_resource(service) + self.services_mock.get.return_value = self.service + + # Get the command object to test + self.cmd = endpoint.ListEndpoint(self.app, None) + + +class TestEndpointShowServiceWithoutName(TestEndpointShow): + + service = identity_fakes.FakeService.create_one_service( + attrs={'service_name': ''}) + endpoint = identity_fakes.FakeEndpoint.create_one_endpoint( + attrs={'service_id': service.id}) + + def setUp(self): + super(TestEndpointShow, self).setUp() + + self.endpoints_mock.get.return_value = self.endpoint + + # This is the return value for common.find_resource(service) + self.services_mock.get.return_value = self.service + + # Get the command object to test + self.cmd = endpoint.ShowEndpoint(self.app, None) diff --git a/openstackclient/tests/unit/identity/v3/test_group.py b/openstackclient/tests/unit/identity/v3/test_group.py new file mode 100644 index 00000000..d35e98c6 --- /dev/null +++ b/openstackclient/tests/unit/identity/v3/test_group.py @@ -0,0 +1,569 @@ +# 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 mock import call + +from keystoneauth1 import exceptions as ks_exc +from osc_lib import exceptions + +from openstackclient.identity.v3 import group +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes + + +class TestGroup(identity_fakes.TestIdentityv3): + + def setUp(self): + super(TestGroup, self).setUp() + + # Get a shortcut to the DomainManager Mock + self.domains_mock = self.app.client_manager.identity.domains + self.domains_mock.reset_mock() + + # Get a shortcut to the GroupManager Mock + self.groups_mock = self.app.client_manager.identity.groups + self.groups_mock.reset_mock() + + # Get a shortcut to the UserManager Mock + self.users_mock = self.app.client_manager.identity.users + self.users_mock.reset_mock() + + +class TestGroupAddUser(TestGroup): + + group = identity_fakes.FakeGroup.create_one_group() + user = identity_fakes.FakeUser.create_one_user() + + def setUp(self): + super(TestGroupAddUser, self).setUp() + + self.groups_mock.get.return_value = self.group + self.users_mock.get.return_value = self.user + self.users_mock.add_to_group.return_value = None + + self.cmd = group.AddUserToGroup(self.app, None) + + def test_group_add_user(self): + arglist = [ + self.group.name, + self.user.name, + ] + verifylist = [ + ('group', self.group.name), + ('user', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.users_mock.add_to_group.assert_called_once_with( + self.user.id, self.group.id) + self.assertIsNone(result) + + +class TestGroupCheckUser(TestGroup): + + group = identity_fakes.FakeGroup.create_one_group() + user = identity_fakes.FakeUser.create_one_user() + + def setUp(self): + super(TestGroupCheckUser, self).setUp() + + self.groups_mock.get.return_value = self.group + self.users_mock.get.return_value = self.user + self.users_mock.check_in_group.return_value = None + + self.cmd = group.CheckUserInGroup(self.app, None) + + def test_group_check_user(self): + arglist = [ + self.group.name, + self.user.name, + ] + verifylist = [ + ('group', self.group.name), + ('user', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.users_mock.check_in_group.assert_called_once_with( + self.user.id, self.group.id) + self.assertIsNone(result) + + +class TestGroupCreate(TestGroup): + + domain = identity_fakes.FakeDomain.create_one_domain() + + columns = ( + 'description', + 'domain_id', + 'id', + 'name', + ) + + def setUp(self): + super(TestGroupCreate, self).setUp() + self.group = identity_fakes.FakeGroup.create_one_group( + attrs={'domain_id': self.domain.id}) + self.data = ( + self.group.description, + self.group.domain_id, + self.group.id, + self.group.name, + ) + + self.groups_mock.create.return_value = self.group + self.groups_mock.get.return_value = self.group + self.domains_mock.get.return_value = self.domain + + self.cmd = group.CreateGroup(self.app, None) + + def test_group_create(self): + arglist = [ + self.group.name, + ] + verifylist = [ + ('name', self.group.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.groups_mock.create.assert_called_once_with( + name=self.group.name, + domain=None, + description=None, + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_group_create_with_options(self): + arglist = [ + '--domain', self.domain.name, + '--description', self.group.description, + self.group.name, + ] + verifylist = [ + ('domain', self.domain.name), + ('description', self.group.description), + ('name', self.group.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.groups_mock.create.assert_called_once_with( + name=self.group.name, + domain=self.domain.id, + description=self.group.description, + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_group_create_or_show(self): + self.groups_mock.create.side_effect = ks_exc.Conflict() + arglist = [ + '--or-show', + self.group.name, + ] + verifylist = [ + ('or_show', True), + ('name', self.group.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.groups_mock.get.assert_called_once_with(self.group.name) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestGroupDelete(TestGroup): + + domain = identity_fakes.FakeDomain.create_one_domain() + groups = identity_fakes.FakeGroup.create_groups( + attrs={'domain_id': domain.id}, count=2) + + def setUp(self): + super(TestGroupDelete, self).setUp() + + self.groups_mock.get = ( + identity_fakes.FakeGroup.get_groups(self.groups)) + self.groups_mock.delete.return_value = None + self.domains_mock.get.return_value = self.domain + + self.cmd = group.DeleteGroup(self.app, None) + + def test_group_delete(self): + arglist = [ + self.groups[0].id, + ] + verifylist = [ + ('groups', [self.groups[0].id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.groups_mock.get.assert_called_once_with(self.groups[0].id) + self.groups_mock.delete.assert_called_once_with(self.groups[0].id) + self.assertIsNone(result) + + def test_group_multi_delete(self): + arglist = [] + verifylist = [] + + for g in self.groups: + arglist.append(g.id) + verifylist = [ + ('groups', arglist), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + calls = [] + for g in self.groups: + calls.append(call(g.id)) + self.groups_mock.delete.assert_has_calls(calls) + self.assertIsNone(result) + + def test_group_delete_with_domain(self): + get_mock_result = [exceptions.CommandError, self.groups[0]] + self.groups_mock.get = ( + mock.MagicMock(side_effect=get_mock_result)) + + arglist = [ + '--domain', self.domain.id, + self.groups[0].id, + ] + verifylist = [ + ('domain', self.groups[0].domain_id), + ('groups', [self.groups[0].id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.groups_mock.get.assert_any_call( + self.groups[0].id, domain_id=self.domain.id) + self.groups_mock.delete.assert_called_once_with(self.groups[0].id) + self.assertIsNone(result) + + +class TestGroupList(TestGroup): + + domain = identity_fakes.FakeDomain.create_one_domain() + group = identity_fakes.FakeGroup.create_one_group() + user = identity_fakes.FakeUser.create_one_user() + + columns = ( + 'ID', + 'Name', + ) + datalist = ( + ( + group.id, + group.name, + ), + ) + + def setUp(self): + super(TestGroupList, self).setUp() + + self.groups_mock.get.return_value = self.group + self.groups_mock.list.return_value = [self.group] + + self.domains_mock.get.return_value = self.domain + + self.users_mock.get.return_value = self.user + + # Get the command object to test + self.cmd = group.ListGroup(self.app, None) + + def test_group_list_no_options(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'domain': None, + 'user': None, + } + + self.groups_mock.list.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_group_list_domain(self): + arglist = [ + '--domain', self.domain.id, + ] + verifylist = [ + ('domain', self.domain.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'domain': self.domain.id, + 'user': None, + } + + self.groups_mock.list.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_group_list_user(self): + arglist = [ + '--user', self.user.name, + ] + verifylist = [ + ('user', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'domain': None, + 'user': self.user.id, + } + + self.groups_mock.list.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_group_list_long(self): + arglist = [ + '--long', + ] + verifylist = [ + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'domain': None, + 'user': None, + } + + self.groups_mock.list.assert_called_with( + **kwargs + ) + + columns = self.columns + ( + 'Domain ID', + 'Description', + ) + datalist = (( + self.group.id, + self.group.name, + self.group.domain_id, + self.group.description, + ), ) + self.assertEqual(columns, columns) + self.assertEqual(datalist, tuple(data)) + + +class TestGroupRemoveUser(TestGroup): + + group = identity_fakes.FakeGroup.create_one_group() + user = identity_fakes.FakeUser.create_one_user() + + def setUp(self): + super(TestGroupRemoveUser, self).setUp() + + self.groups_mock.get.return_value = self.group + self.users_mock.get.return_value = self.user + self.users_mock.remove_from_group.return_value = None + + self.cmd = group.RemoveUserFromGroup(self.app, None) + + def test_group_remove_user(self): + arglist = [ + self.group.id, + self.user.id, + ] + verifylist = [ + ('group', self.group.id), + ('user', self.user.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.users_mock.remove_from_group.assert_called_once_with( + self.user.id, self.group.id) + self.assertIsNone(result) + + +class TestGroupSet(TestGroup): + + domain = identity_fakes.FakeDomain.create_one_domain() + group = identity_fakes.FakeGroup.create_one_group( + attrs={'domain_id': domain.id}) + + def setUp(self): + super(TestGroupSet, self).setUp() + + self.groups_mock.get.return_value = self.group + self.domains_mock.get.return_value = self.domain + self.groups_mock.update.return_value = None + + self.cmd = group.SetGroup(self.app, None) + + def test_group_set_nothing(self): + arglist = [ + self.group.id, + ] + verifylist = [ + ('group', self.group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.groups_mock.update.assert_called_once_with(self.group.id) + self.assertIsNone(result) + + def test_group_set_name_and_description(self): + arglist = [ + '--name', 'new_name', + '--description', 'new_description', + self.group.id, + ] + verifylist = [ + ('name', 'new_name'), + ('description', 'new_description'), + ('group', self.group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + kwargs = { + 'name': 'new_name', + 'description': 'new_description', + } + self.groups_mock.update.assert_called_once_with( + self.group.id, **kwargs) + self.assertIsNone(result) + + def test_group_set_with_domain(self): + get_mock_result = [exceptions.CommandError, self.group] + self.groups_mock.get = ( + mock.MagicMock(side_effect=get_mock_result)) + + arglist = [ + '--domain', self.domain.id, + self.group.id, + ] + verifylist = [ + ('domain', self.domain.id), + ('group', self.group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.groups_mock.get.assert_any_call( + self.group.id, domain_id=self.domain.id) + self.groups_mock.update.assert_called_once_with(self.group.id) + self.assertIsNone(result) + + +class TestGroupShow(TestGroup): + + domain = identity_fakes.FakeDomain.create_one_domain() + + columns = ( + 'description', + 'domain_id', + 'id', + 'name', + ) + + def setUp(self): + super(TestGroupShow, self).setUp() + self.group = identity_fakes.FakeGroup.create_one_group( + attrs={'domain_id': self.domain.id}) + self.data = ( + self.group.description, + self.group.domain_id, + self.group.id, + self.group.name, + ) + + self.groups_mock.get.return_value = self.group + self.domains_mock.get.return_value = self.domain + + self.cmd = group.ShowGroup(self.app, None) + + def test_group_show(self): + arglist = [ + self.group.id, + ] + verifylist = [ + ('group', self.group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.groups_mock.get.assert_called_once_with(self.group.id) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_group_show_with_domain(self): + get_mock_result = [exceptions.CommandError, self.group] + self.groups_mock.get = ( + mock.MagicMock(side_effect=get_mock_result)) + + arglist = [ + '--domain', self.domain.id, + self.group.id, + ] + verifylist = [ + ('domain', self.domain.id), + ('group', self.group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.groups_mock.get.assert_any_call( + self.group.id, domain_id=self.domain.id) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/identity/v3/test_identity_provider.py b/openstackclient/tests/unit/identity/v3/test_identity_provider.py new file mode 100644 index 00000000..cb672a92 --- /dev/null +++ b/openstackclient/tests/unit/identity/v3/test_identity_provider.py @@ -0,0 +1,593 @@ +# Copyright 2014 CERN. +# +# 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 copy +import mock + +from openstackclient.identity.v3 import identity_provider +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes + + +class TestIdentityProvider(identity_fakes.TestFederatedIdentity): + + def setUp(self): + super(TestIdentityProvider, self).setUp() + + federation_lib = self.app.client_manager.identity.federation + self.identity_providers_mock = federation_lib.identity_providers + self.identity_providers_mock.reset_mock() + + +class TestIdentityProviderCreate(TestIdentityProvider): + + columns = ( + 'description', + 'enabled', + 'id', + 'remote_ids', + ) + datalist = ( + identity_fakes.idp_description, + True, + identity_fakes.idp_id, + identity_fakes.formatted_idp_remote_ids, + ) + + def setUp(self): + super(TestIdentityProviderCreate, self).setUp() + + copied_idp = copy.deepcopy(identity_fakes.IDENTITY_PROVIDER) + resource = fakes.FakeResource(None, copied_idp, loaded=True) + self.identity_providers_mock.create.return_value = resource + self.cmd = identity_provider.CreateIdentityProvider(self.app, None) + + def test_create_identity_provider_no_options(self): + arglist = [ + identity_fakes.idp_id, + ] + verifylist = [ + ('identity_provider_id', identity_fakes.idp_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'remote_ids': None, + 'enabled': True, + 'description': None, + } + + self.identity_providers_mock.create.assert_called_with( + id=identity_fakes.idp_id, + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_create_identity_provider_description(self): + arglist = [ + '--description', identity_fakes.idp_description, + identity_fakes.idp_id, + ] + verifylist = [ + ('identity_provider_id', identity_fakes.idp_id), + ('description', identity_fakes.idp_description), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'remote_ids': None, + 'description': identity_fakes.idp_description, + 'enabled': True, + } + + self.identity_providers_mock.create.assert_called_with( + id=identity_fakes.idp_id, + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_create_identity_provider_remote_id(self): + arglist = [ + identity_fakes.idp_id, + '--remote-id', identity_fakes.idp_remote_ids[0] + ] + verifylist = [ + ('identity_provider_id', identity_fakes.idp_id), + ('remote_id', identity_fakes.idp_remote_ids[:1]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'remote_ids': identity_fakes.idp_remote_ids[:1], + 'description': None, + 'enabled': True, + } + + self.identity_providers_mock.create.assert_called_with( + id=identity_fakes.idp_id, + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_create_identity_provider_remote_ids_multiple(self): + arglist = [ + '--remote-id', identity_fakes.idp_remote_ids[0], + '--remote-id', identity_fakes.idp_remote_ids[1], + identity_fakes.idp_id + ] + verifylist = [ + ('identity_provider_id', identity_fakes.idp_id), + ('remote_id', identity_fakes.idp_remote_ids), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'remote_ids': identity_fakes.idp_remote_ids, + 'description': None, + 'enabled': True, + } + + self.identity_providers_mock.create.assert_called_with( + id=identity_fakes.idp_id, + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_create_identity_provider_remote_ids_file(self): + arglist = [ + '--remote-id-file', '/tmp/file_name', + identity_fakes.idp_id, + ] + verifylist = [ + ('identity_provider_id', identity_fakes.idp_id), + ('remote_id_file', '/tmp/file_name'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + mocker = mock.Mock() + mocker.return_value = "\n".join(identity_fakes.idp_remote_ids) + with mock.patch("openstackclient.identity.v3.identity_provider." + "utils.read_blob_file_contents", mocker): + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'remote_ids': identity_fakes.idp_remote_ids, + 'description': None, + 'enabled': True, + } + + self.identity_providers_mock.create.assert_called_with( + id=identity_fakes.idp_id, + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_create_identity_provider_disabled(self): + + # Prepare FakeResource object + IDENTITY_PROVIDER = copy.deepcopy(identity_fakes.IDENTITY_PROVIDER) + IDENTITY_PROVIDER['enabled'] = False + IDENTITY_PROVIDER['description'] = None + + resource = fakes.FakeResource(None, IDENTITY_PROVIDER, loaded=True) + self.identity_providers_mock.create.return_value = resource + + arglist = [ + '--disable', + identity_fakes.idp_id, + ] + verifylist = [ + ('identity_provider_id', identity_fakes.idp_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'remote_ids': None, + 'enabled': False, + 'description': None, + } + + self.identity_providers_mock.create.assert_called_with( + id=identity_fakes.idp_id, + **kwargs + ) + + self.assertEqual(self.columns, columns) + datalist = ( + None, + False, + identity_fakes.idp_id, + identity_fakes.formatted_idp_remote_ids + ) + self.assertEqual(datalist, data) + + +class TestIdentityProviderDelete(TestIdentityProvider): + + def setUp(self): + super(TestIdentityProviderDelete, self).setUp() + + # This is the return value for utils.find_resource() + self.identity_providers_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.IDENTITY_PROVIDER), + loaded=True, + ) + + self.identity_providers_mock.delete.return_value = None + self.cmd = identity_provider.DeleteIdentityProvider(self.app, None) + + def test_delete_identity_provider(self): + arglist = [ + identity_fakes.idp_id, + ] + verifylist = [ + ('identity_provider', [identity_fakes.idp_id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.identity_providers_mock.delete.assert_called_with( + identity_fakes.idp_id, + ) + self.assertIsNone(result) + + +class TestIdentityProviderList(TestIdentityProvider): + + def setUp(self): + super(TestIdentityProviderList, self).setUp() + + self.identity_providers_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.IDENTITY_PROVIDER), + loaded=True, + ) + self.identity_providers_mock.list.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.IDENTITY_PROVIDER), + loaded=True, + ), + ] + + # Get the command object to test + self.cmd = identity_provider.ListIdentityProvider(self.app, None) + + def test_identity_provider_list_no_options(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.identity_providers_mock.list.assert_called_with() + + collist = ('ID', 'Enabled', 'Description') + self.assertEqual(collist, columns) + datalist = (( + identity_fakes.idp_id, + True, + identity_fakes.idp_description, + ), ) + self.assertEqual(datalist, tuple(data)) + + +class TestIdentityProviderSet(TestIdentityProvider): + + columns = ( + 'description', + 'enabled', + 'id', + 'remote_ids', + ) + datalist = ( + identity_fakes.idp_description, + True, + identity_fakes.idp_id, + identity_fakes.idp_remote_ids, + ) + + def setUp(self): + super(TestIdentityProviderSet, self).setUp() + self.cmd = identity_provider.SetIdentityProvider(self.app, None) + + def test_identity_provider_set_description(self): + """Set Identity Provider's description. """ + + def prepare(self): + """Prepare fake return objects before the test is executed""" + updated_idp = copy.deepcopy(identity_fakes.IDENTITY_PROVIDER) + updated_idp['enabled'] = False + resources = fakes.FakeResource( + None, + updated_idp, + loaded=True + ) + self.identity_providers_mock.update.return_value = resources + + prepare(self) + new_description = 'new desc' + arglist = [ + '--description', new_description, + identity_fakes.idp_id + ] + verifylist = [ + ('identity_provider', identity_fakes.idp_id), + ('description', new_description), + ('enable', False), + ('disable', False), + ('remote_id', None) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + self.identity_providers_mock.update.assert_called_with( + identity_fakes.idp_id, + description=new_description, + ) + + def test_identity_provider_disable(self): + """Disable Identity Provider + + Set Identity Provider's ``enabled`` attribute to False. + """ + + def prepare(self): + """Prepare fake return objects before the test is executed""" + updated_idp = copy.deepcopy(identity_fakes.IDENTITY_PROVIDER) + updated_idp['enabled'] = False + resources = fakes.FakeResource( + None, + updated_idp, + loaded=True + ) + self.identity_providers_mock.update.return_value = resources + + prepare(self) + arglist = [ + '--disable', identity_fakes.idp_id, + '--remote-id', identity_fakes.idp_remote_ids[0], + '--remote-id', identity_fakes.idp_remote_ids[1] + ] + verifylist = [ + ('identity_provider', identity_fakes.idp_id), + ('description', None), + ('enable', False), + ('disable', True), + ('remote_id', identity_fakes.idp_remote_ids) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + self.identity_providers_mock.update.assert_called_with( + identity_fakes.idp_id, + enabled=False, + remote_ids=identity_fakes.idp_remote_ids + ) + + def test_identity_provider_enable(self): + """Enable Identity Provider. + + Set Identity Provider's ``enabled`` attribute to True. + """ + + def prepare(self): + """Prepare fake return objects before the test is executed""" + resources = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.IDENTITY_PROVIDER), + loaded=True + ) + self.identity_providers_mock.update.return_value = resources + + prepare(self) + arglist = [ + '--enable', identity_fakes.idp_id, + '--remote-id', identity_fakes.idp_remote_ids[0], + '--remote-id', identity_fakes.idp_remote_ids[1] + ] + verifylist = [ + ('identity_provider', identity_fakes.idp_id), + ('description', None), + ('enable', True), + ('disable', False), + ('remote_id', identity_fakes.idp_remote_ids) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + self.identity_providers_mock.update.assert_called_with( + identity_fakes.idp_id, enabled=True, + remote_ids=identity_fakes.idp_remote_ids) + + def test_identity_provider_replace_remote_ids(self): + """Enable Identity Provider. + + Set Identity Provider's ``enabled`` attribute to True. + """ + + def prepare(self): + """Prepare fake return objects before the test is executed""" + self.new_remote_id = 'new_entity' + + updated_idp = copy.deepcopy(identity_fakes.IDENTITY_PROVIDER) + updated_idp['remote_ids'] = [self.new_remote_id] + resources = fakes.FakeResource( + None, + updated_idp, + loaded=True + ) + self.identity_providers_mock.update.return_value = resources + + prepare(self) + arglist = [ + '--enable', identity_fakes.idp_id, + '--remote-id', self.new_remote_id + ] + verifylist = [ + ('identity_provider', identity_fakes.idp_id), + ('description', None), + ('enable', True), + ('disable', False), + ('remote_id', [self.new_remote_id]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + self.identity_providers_mock.update.assert_called_with( + identity_fakes.idp_id, enabled=True, + remote_ids=[self.new_remote_id]) + + def test_identity_provider_replace_remote_ids_file(self): + """Enable Identity Provider. + + Set Identity Provider's ``enabled`` attribute to True. + """ + + def prepare(self): + """Prepare fake return objects before the test is executed""" + self.new_remote_id = 'new_entity' + + updated_idp = copy.deepcopy(identity_fakes.IDENTITY_PROVIDER) + updated_idp['remote_ids'] = [self.new_remote_id] + resources = fakes.FakeResource( + None, + updated_idp, + loaded=True + ) + self.identity_providers_mock.update.return_value = resources + + prepare(self) + arglist = [ + '--enable', identity_fakes.idp_id, + '--remote-id-file', self.new_remote_id, + ] + verifylist = [ + ('identity_provider', identity_fakes.idp_id), + ('description', None), + ('enable', True), + ('disable', False), + ('remote_id_file', self.new_remote_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + mocker = mock.Mock() + mocker.return_value = self.new_remote_id + with mock.patch("openstackclient.identity.v3.identity_provider." + "utils.read_blob_file_contents", mocker): + self.cmd.take_action(parsed_args) + self.identity_providers_mock.update.assert_called_with( + identity_fakes.idp_id, enabled=True, + remote_ids=[self.new_remote_id]) + + def test_identity_provider_no_options(self): + def prepare(self): + """Prepare fake return objects before the test is executed""" + resources = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.IDENTITY_PROVIDER), + loaded=True + ) + self.identity_providers_mock.get.return_value = resources + + resources = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.IDENTITY_PROVIDER), + loaded=True, + ) + self.identity_providers_mock.update.return_value = resources + + prepare(self) + arglist = [ + identity_fakes.idp_id, + ] + verifylist = [ + ('identity_provider', identity_fakes.idp_id), + ('enable', False), + ('disable', False), + ('remote_id', None) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + +class TestIdentityProviderShow(TestIdentityProvider): + + def setUp(self): + super(TestIdentityProviderShow, self).setUp() + + ret = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.IDENTITY_PROVIDER), + loaded=True, + ) + + self.identity_providers_mock.get.side_effect = [Exception("Not found"), + ret] + self.identity_providers_mock.get.return_value = ret + + # Get the command object to test + self.cmd = identity_provider.ShowIdentityProvider(self.app, None) + + def test_identity_provider_show(self): + arglist = [ + identity_fakes.idp_id, + ] + verifylist = [ + ('identity_provider', identity_fakes.idp_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.identity_providers_mock.get.assert_called_with( + identity_fakes.idp_id, + id='test_idp' + ) + + collist = ('description', 'enabled', 'id', 'remote_ids') + self.assertEqual(collist, columns) + datalist = ( + identity_fakes.idp_description, + True, + identity_fakes.idp_id, + identity_fakes.formatted_idp_remote_ids + ) + self.assertEqual(datalist, data) diff --git a/openstackclient/tests/unit/identity/v3/test_mappings.py b/openstackclient/tests/unit/identity/v3/test_mappings.py new file mode 100644 index 00000000..5086724c --- /dev/null +++ b/openstackclient/tests/unit/identity/v3/test_mappings.py @@ -0,0 +1,244 @@ +# Copyright 2014 CERN. +# +# 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 copy +import mock + +from osc_lib import exceptions + +from openstackclient.identity.v3 import mapping +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes + + +class TestMapping(identity_fakes.TestFederatedIdentity): + + def setUp(self): + super(TestMapping, self).setUp() + + federation_lib = self.app.client_manager.identity.federation + self.mapping_mock = federation_lib.mappings + self.mapping_mock.reset_mock() + + +class TestMappingCreate(TestMapping): + + def setUp(self): + super(TestMappingCreate, self).setUp() + self.mapping_mock.create.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.MAPPING_RESPONSE), + loaded=True + ) + self.cmd = mapping.CreateMapping(self.app, None) + + def test_create_mapping(self): + arglist = [ + '--rules', identity_fakes.mapping_rules_file_path, + identity_fakes.mapping_id + ] + verifylist = [ + ('mapping', identity_fakes.mapping_id), + ('rules', identity_fakes.mapping_rules_file_path) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + mocker = mock.Mock() + mocker.return_value = identity_fakes.MAPPING_RULES + with mock.patch("openstackclient.identity.v3.mapping." + "CreateMapping._read_rules", mocker): + columns, data = self.cmd.take_action(parsed_args) + + self.mapping_mock.create.assert_called_with( + mapping_id=identity_fakes.mapping_id, + rules=identity_fakes.MAPPING_RULES) + + collist = ('id', 'rules') + self.assertEqual(collist, columns) + + datalist = (identity_fakes.mapping_id, + identity_fakes.MAPPING_RULES) + self.assertEqual(datalist, data) + + +class TestMappingDelete(TestMapping): + + def setUp(self): + super(TestMappingDelete, self).setUp() + self.mapping_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.MAPPING_RESPONSE), + loaded=True) + + self.mapping_mock.delete.return_value = None + self.cmd = mapping.DeleteMapping(self.app, None) + + def test_delete_mapping(self): + arglist = [ + identity_fakes.mapping_id + ] + verifylist = [ + ('mapping', [identity_fakes.mapping_id]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.mapping_mock.delete.assert_called_with( + identity_fakes.mapping_id) + self.assertIsNone(result) + + +class TestMappingList(TestMapping): + + def setUp(self): + super(TestMappingList, self).setUp() + self.mapping_mock.get.return_value = fakes.FakeResource( + None, + {'id': identity_fakes.mapping_id}, + loaded=True) + # Pretend list command returns list of two mappings. + # NOTE(marek-denis): We are returning FakeResources with mapping id + # only as ShowMapping class is implemented in a way where rules will + # not be displayed, only mapping ids. + self.mapping_mock.list.return_value = [ + fakes.FakeResource( + None, + {'id': identity_fakes.mapping_id}, + loaded=True, + ), + fakes.FakeResource( + None, + {'id': 'extra_mapping'}, + loaded=True, + ), + ] + + # Get the command object to test + self.cmd = mapping.ListMapping(self.app, None) + + def test_mapping_list(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.mapping_mock.list.assert_called_with() + + collist = ('ID',) + self.assertEqual(collist, columns) + + datalist = [(identity_fakes.mapping_id,), ('extra_mapping',)] + self.assertEqual(datalist, data) + + +class TestMappingSet(TestMapping): + + def setUp(self): + super(TestMappingSet, self).setUp() + + self.mapping_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.MAPPING_RESPONSE), + loaded=True + ) + + self.mapping_mock.update.return_value = fakes.FakeResource( + None, + identity_fakes.MAPPING_RESPONSE_2, + loaded=True + ) + + # Get the command object to test + self.cmd = mapping.SetMapping(self.app, None) + + def test_set_new_rules(self): + arglist = [ + '--rules', identity_fakes.mapping_rules_file_path, + identity_fakes.mapping_id + ] + verifylist = [ + ('mapping', identity_fakes.mapping_id), + ('rules', identity_fakes.mapping_rules_file_path) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + mocker = mock.Mock() + mocker.return_value = identity_fakes.MAPPING_RULES_2 + with mock.patch("openstackclient.identity.v3.mapping." + "SetMapping._read_rules", mocker): + columns, data = self.cmd.take_action(parsed_args) + self.mapping_mock.update.assert_called_with( + mapping=identity_fakes.mapping_id, + rules=identity_fakes.MAPPING_RULES_2) + + collist = ('id', 'rules') + self.assertEqual(collist, columns) + datalist = (identity_fakes.mapping_id, + identity_fakes.MAPPING_RULES_2) + self.assertEqual(datalist, data) + + def test_set_rules_wrong_file_path(self): + arglist = [ + '--rules', identity_fakes.mapping_rules_file_path, + identity_fakes.mapping_id + ] + verifylist = [ + ('mapping', identity_fakes.mapping_id), + ('rules', identity_fakes.mapping_rules_file_path) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args) + + +class TestMappingShow(TestMapping): + + def setUp(self): + super(TestMappingShow, self).setUp() + + self.mapping_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.MAPPING_RESPONSE), + loaded=True + ) + + self.cmd = mapping.ShowMapping(self.app, None) + + def test_mapping_show(self): + arglist = [ + identity_fakes.mapping_id + ] + verifylist = [ + ('mapping', identity_fakes.mapping_id) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.mapping_mock.get.assert_called_with( + identity_fakes.mapping_id) + + collist = ('id', 'rules') + self.assertEqual(collist, columns) + + datalist = (identity_fakes.mapping_id, + identity_fakes.MAPPING_RULES) + self.assertEqual(datalist, data) diff --git a/openstackclient/tests/unit/identity/v3/test_oauth.py b/openstackclient/tests/unit/identity/v3/test_oauth.py new file mode 100644 index 00000000..3aabd9b8 --- /dev/null +++ b/openstackclient/tests/unit/identity/v3/test_oauth.py @@ -0,0 +1,173 @@ +# 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 copy + +from openstackclient.identity.v3 import token +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes + + +class TestOAuth1(identity_fakes.TestOAuth1): + + def setUp(self): + super(TestOAuth1, self).setUp() + identity_client = self.app.client_manager.identity + self.access_tokens_mock = identity_client.oauth1.access_tokens + self.access_tokens_mock.reset_mock() + self.request_tokens_mock = identity_client.oauth1.request_tokens + self.request_tokens_mock.reset_mock() + self.projects_mock = identity_client.projects + self.projects_mock.reset_mock() + self.roles_mock = identity_client.roles + self.roles_mock.reset_mock() + + +class TestAccessTokenCreate(TestOAuth1): + + def setUp(self): + super(TestAccessTokenCreate, self).setUp() + + self.access_tokens_mock.create.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.OAUTH_ACCESS_TOKEN), + loaded=True, + ) + + self.cmd = token.CreateAccessToken(self.app, None) + + def test_create_access_tokens(self): + arglist = [ + '--consumer-key', identity_fakes.consumer_id, + '--consumer-secret', identity_fakes.consumer_secret, + '--request-key', identity_fakes.request_token_id, + '--request-secret', identity_fakes.request_token_secret, + '--verifier', identity_fakes.oauth_verifier_pin, + ] + verifylist = [ + ('consumer_key', identity_fakes.consumer_id), + ('consumer_secret', identity_fakes.consumer_secret), + ('request_key', identity_fakes.request_token_id), + ('request_secret', identity_fakes.request_token_secret), + ('verifier', identity_fakes.oauth_verifier_pin), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.access_tokens_mock.create.assert_called_with( + identity_fakes.consumer_id, + identity_fakes.consumer_secret, + identity_fakes.request_token_id, + identity_fakes.request_token_secret, + identity_fakes.oauth_verifier_pin, + ) + + collist = ('expires', 'id', 'key', 'secret') + self.assertEqual(collist, columns) + datalist = ( + identity_fakes.access_token_expires, + identity_fakes.access_token_id, + identity_fakes.access_token_id, + identity_fakes.access_token_secret, + ) + self.assertEqual(datalist, data) + + +class TestRequestTokenAuthorize(TestOAuth1): + + def setUp(self): + super(TestRequestTokenAuthorize, self).setUp() + + self.roles_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.ROLE), + loaded=True, + ) + + copied_verifier = copy.deepcopy(identity_fakes.OAUTH_VERIFIER) + resource = fakes.FakeResource(None, copied_verifier, loaded=True) + self.request_tokens_mock.authorize.return_value = resource + self.cmd = token.AuthorizeRequestToken(self.app, None) + + def test_authorize_request_tokens(self): + arglist = [ + '--request-key', identity_fakes.request_token_id, + '--role', identity_fakes.role_name, + ] + verifylist = [ + ('request_key', identity_fakes.request_token_id), + ('role', [identity_fakes.role_name]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.request_tokens_mock.authorize.assert_called_with( + identity_fakes.request_token_id, + [identity_fakes.role_id], + ) + + collist = ('oauth_verifier',) + self.assertEqual(collist, columns) + datalist = ( + identity_fakes.oauth_verifier_pin, + ) + self.assertEqual(datalist, data) + + +class TestRequestTokenCreate(TestOAuth1): + + def setUp(self): + super(TestRequestTokenCreate, self).setUp() + + self.request_tokens_mock.create.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.OAUTH_REQUEST_TOKEN), + loaded=True, + ) + + self.projects_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.PROJECT), + loaded=True, + ) + + self.cmd = token.CreateRequestToken(self.app, None) + + def test_create_request_tokens(self): + arglist = [ + '--consumer-key', identity_fakes.consumer_id, + '--consumer-secret', identity_fakes.consumer_secret, + '--project', identity_fakes.project_id, + ] + verifylist = [ + ('consumer_key', identity_fakes.consumer_id), + ('consumer_secret', identity_fakes.consumer_secret), + ('project', identity_fakes.project_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.request_tokens_mock.create.assert_called_with( + identity_fakes.consumer_id, + identity_fakes.consumer_secret, + identity_fakes.project_id, + ) + + collist = ('expires', 'id', 'key', 'secret') + self.assertEqual(collist, columns) + datalist = ( + identity_fakes.request_token_expires, + identity_fakes.request_token_id, + identity_fakes.request_token_id, + identity_fakes.request_token_secret, + ) + self.assertEqual(datalist, data) diff --git a/openstackclient/tests/unit/identity/v3/test_project.py b/openstackclient/tests/unit/identity/v3/test_project.py new file mode 100644 index 00000000..702d9209 --- /dev/null +++ b/openstackclient/tests/unit/identity/v3/test_project.py @@ -0,0 +1,978 @@ +# Copyright 2013 Nebula 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 mock + +from osc_lib import exceptions + +from openstackclient.identity.v3 import project +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes + + +class TestProject(identity_fakes.TestIdentityv3): + + def setUp(self): + super(TestProject, self).setUp() + + # Get a shortcut to the DomainManager Mock + self.domains_mock = self.app.client_manager.identity.domains + self.domains_mock.reset_mock() + + # Get a shortcut to the ProjectManager Mock + self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock.reset_mock() + + +class TestProjectCreate(TestProject): + + domain = identity_fakes.FakeDomain.create_one_domain() + + columns = ( + 'description', + 'domain_id', + 'enabled', + 'id', + 'is_domain', + 'name', + 'parent_id', + ) + + def setUp(self): + super(TestProjectCreate, self).setUp() + + self.project = identity_fakes.FakeProject.create_one_project( + attrs={'domain_id': self.domain.id}) + self.domains_mock.get.return_value = self.domain + self.projects_mock.create.return_value = self.project + self.datalist = ( + self.project.description, + self.project.domain_id, + True, + self.project.id, + False, + self.project.name, + self.project.parent_id, + ) + # Get the command object to test + self.cmd = project.CreateProject(self.app, None) + + def test_project_create_no_options(self): + arglist = [ + self.project.name, + ] + verifylist = [ + ('parent', None), + ('enable', False), + ('disable', False), + ('name', self.project.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.project.name, + 'domain': None, + 'description': None, + 'enabled': True, + 'parent': None, + } + # ProjectManager.create(name=, domain=, description=, + # enabled=, **kwargs) + self.projects_mock.create.assert_called_with( + **kwargs + ) + + collist = ( + 'description', + 'domain_id', + 'enabled', + 'id', + 'is_domain', + 'name', + 'parent_id', + ) + self.assertEqual(collist, columns) + datalist = ( + self.project.description, + self.project.domain_id, + True, + self.project.id, + False, + self.project.name, + self.project.parent_id, + ) + self.assertEqual(datalist, data) + + def test_project_create_description(self): + arglist = [ + '--description', 'new desc', + self.project.name, + ] + verifylist = [ + ('description', 'new desc'), + ('enable', False), + ('disable', False), + ('name', self.project.name), + ('parent', None), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.project.name, + 'domain': None, + 'description': 'new desc', + 'enabled': True, + 'parent': None, + } + # ProjectManager.create(name=, domain=, description=, + # enabled=, **kwargs) + self.projects_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_project_create_domain(self): + arglist = [ + '--domain', self.project.domain_id, + self.project.name, + ] + verifylist = [ + ('domain', self.project.domain_id), + ('enable', False), + ('disable', False), + ('name', self.project.name), + ('parent', None), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.project.name, + 'domain': self.project.domain_id, + 'description': None, + 'enabled': True, + 'parent': None, + } + # ProjectManager.create(name=, domain=, description=, + # enabled=, **kwargs) + self.projects_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_project_create_domain_no_perms(self): + arglist = [ + '--domain', self.project.domain_id, + self.project.name, + ] + verifylist = [ + ('domain', self.project.domain_id), + ('enable', False), + ('disable', False), + ('name', self.project.name), + ('parent', None), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + mocker = mock.Mock() + mocker.return_value = None + + with mock.patch("osc_lib.utils.find_resource", mocker): + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.project.name, + 'domain': self.project.domain_id, + 'description': None, + 'enabled': True, + 'parent': None, + } + self.projects_mock.create.assert_called_with( + **kwargs + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_project_create_enable(self): + arglist = [ + '--enable', + self.project.name, + ] + verifylist = [ + ('enable', True), + ('disable', False), + ('name', self.project.name), + ('parent', None), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.project.name, + 'domain': None, + 'description': None, + 'enabled': True, + 'parent': None, + } + # ProjectManager.create(name=, domain=, description=, + # enabled=, **kwargs) + self.projects_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_project_create_disable(self): + arglist = [ + '--disable', + self.project.name, + ] + verifylist = [ + ('enable', False), + ('disable', True), + ('name', self.project.name), + ('parent', None), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.project.name, + 'domain': None, + 'description': None, + 'enabled': False, + 'parent': None, + } + # ProjectManager.create(name=, domain=, + # description=, enabled=, **kwargs) + self.projects_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_project_create_property(self): + arglist = [ + '--property', 'fee=fi', + '--property', 'fo=fum', + self.project.name, + ] + verifylist = [ + ('property', {'fee': 'fi', 'fo': 'fum'}), + ('name', self.project.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.project.name, + 'domain': None, + 'description': None, + 'enabled': True, + 'parent': None, + 'fee': 'fi', + 'fo': 'fum', + } + # ProjectManager.create(name=, domain=, description=, + # enabled=, **kwargs) + self.projects_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_project_create_parent(self): + self.parent = identity_fakes.FakeProject.create_one_project() + self.project = identity_fakes.FakeProject.create_one_project( + attrs={'domain_id': self.domain.id, 'parent_id': self.parent.id}) + self.projects_mock.get.return_value = self.parent + self.projects_mock.create.return_value = self.project + + arglist = [ + '--domain', self.project.domain_id, + '--parent', self.parent.name, + self.project.name, + ] + verifylist = [ + ('domain', self.project.domain_id), + ('parent', self.parent.name), + ('enable', False), + ('disable', False), + ('name', self.project.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + kwargs = { + 'name': self.project.name, + 'domain': self.project.domain_id, + 'parent': self.parent.id, + 'description': None, + 'enabled': True, + } + + self.projects_mock.create.assert_called_with( + **kwargs + ) + + collist = ( + 'description', + 'domain_id', + 'enabled', + 'id', + 'is_domain', + 'name', + 'parent_id', + ) + self.assertEqual(columns, collist) + datalist = ( + self.project.description, + self.project.domain_id, + self.project.enabled, + self.project.id, + self.project.is_domain, + self.project.name, + self.parent.id, + ) + self.assertEqual(data, datalist) + + def test_project_create_invalid_parent(self): + self.projects_mock.resource_class.__name__ = 'Project' + self.projects_mock.get.side_effect = exceptions.NotFound( + 'Invalid parent') + self.projects_mock.find.side_effect = exceptions.NotFound( + 'Invalid parent') + + arglist = [ + '--domain', self.project.domain_id, + '--parent', 'invalid', + self.project.name, + ] + verifylist = [ + ('domain', self.project.domain_id), + ('parent', 'invalid'), + ('enable', False), + ('disable', False), + ('name', self.project.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + + +class TestProjectDelete(TestProject): + + project = identity_fakes.FakeProject.create_one_project() + + def setUp(self): + super(TestProjectDelete, self).setUp() + + # This is the return value for utils.find_resource() + self.projects_mock.get.return_value = self.project + self.projects_mock.delete.return_value = None + + # Get the command object to test + self.cmd = project.DeleteProject(self.app, None) + + def test_project_delete_no_options(self): + arglist = [ + self.project.id, + ] + verifylist = [ + ('projects', [self.project.id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.projects_mock.delete.assert_called_with( + self.project.id, + ) + self.assertIsNone(result) + + +class TestProjectList(TestProject): + + domain = identity_fakes.FakeDomain.create_one_domain() + project = identity_fakes.FakeProject.create_one_project( + attrs={'domain_id': domain.id}) + + columns = ( + 'ID', + 'Name', + ) + datalist = ( + ( + project.id, + project.name, + ), + ) + + def setUp(self): + super(TestProjectList, self).setUp() + + self.projects_mock.list.return_value = [self.project] + + # Get the command object to test + self.cmd = project.ListProject(self.app, None) + + def test_project_list_no_options(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + self.projects_mock.list.assert_called_with() + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_project_list_long(self): + arglist = [ + '--long', + ] + verifylist = [ + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + self.projects_mock.list.assert_called_with() + + collist = ('ID', 'Name', 'Domain ID', 'Description', 'Enabled') + self.assertEqual(collist, columns) + datalist = (( + self.project.id, + self.project.name, + self.project.domain_id, + self.project.description, + True, + ), ) + self.assertEqual(datalist, tuple(data)) + + def test_project_list_domain(self): + arglist = [ + '--domain', self.project.domain_id, + ] + verifylist = [ + ('domain', self.project.domain_id), + ] + + self.domains_mock.get.return_value = self.domain + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + self.projects_mock.list.assert_called_with( + domain=self.project.domain_id) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_project_list_domain_no_perms(self): + arglist = [ + '--domain', self.project.domain_id, + ] + verifylist = [ + ('domain', self.project.domain_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + mocker = mock.Mock() + mocker.return_value = None + + with mock.patch("osc_lib.utils.find_resource", mocker): + columns, data = self.cmd.take_action(parsed_args) + + self.projects_mock.list.assert_called_with( + domain=self.project.domain_id) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + +class TestProjectSet(TestProject): + + domain = identity_fakes.FakeDomain.create_one_domain() + project = identity_fakes.FakeProject.create_one_project( + attrs={'domain_id': domain.id}) + + def setUp(self): + super(TestProjectSet, self).setUp() + + self.domains_mock.get.return_value = self.domain + + self.projects_mock.get.return_value = self.project + self.projects_mock.update.return_value = self.project + + # Get the command object to test + self.cmd = project.SetProject(self.app, None) + + def test_project_set_no_options(self): + arglist = [ + self.project.name, + ] + verifylist = [ + ('project', self.project.name), + ('enable', False), + ('disable', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + + def test_project_set_name(self): + arglist = [ + '--name', 'qwerty', + '--domain', self.project.domain_id, + self.project.name, + ] + verifylist = [ + ('name', 'qwerty'), + ('domain', self.project.domain_id), + ('enable', False), + ('disable', False), + ('project', self.project.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': 'qwerty', + } + # ProjectManager.update(project, name=, domain=, description=, + # enabled=, **kwargs) + self.projects_mock.update.assert_called_with( + self.project.id, + **kwargs + ) + self.assertIsNone(result) + + def test_project_set_description(self): + arglist = [ + '--domain', self.project.domain_id, + '--description', 'new desc', + self.project.name, + ] + verifylist = [ + ('domain', self.project.domain_id), + ('description', 'new desc'), + ('enable', False), + ('disable', False), + ('project', self.project.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'description': 'new desc', + } + self.projects_mock.update.assert_called_with( + self.project.id, + **kwargs + ) + self.assertIsNone(result) + + def test_project_set_enable(self): + arglist = [ + '--domain', self.project.domain_id, + '--enable', + self.project.name, + ] + verifylist = [ + ('domain', self.project.domain_id), + ('enable', True), + ('disable', False), + ('project', self.project.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': True, + } + self.projects_mock.update.assert_called_with( + self.project.id, + **kwargs + ) + self.assertIsNone(result) + + def test_project_set_disable(self): + arglist = [ + '--domain', self.project.domain_id, + '--disable', + self.project.name, + ] + verifylist = [ + ('domain', self.project.domain_id), + ('enable', False), + ('disable', True), + ('project', self.project.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': False, + } + self.projects_mock.update.assert_called_with( + self.project.id, + **kwargs + ) + self.assertIsNone(result) + + def test_project_set_property(self): + arglist = [ + '--domain', self.project.domain_id, + '--property', 'fee=fi', + '--property', 'fo=fum', + self.project.name, + ] + verifylist = [ + ('domain', self.project.domain_id), + ('property', {'fee': 'fi', 'fo': 'fum'}), + ('project', self.project.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'fee': 'fi', + 'fo': 'fum', + } + self.projects_mock.update.assert_called_with( + self.project.id, + **kwargs + ) + self.assertIsNone(result) + + +class TestProjectShow(TestProject): + + domain = identity_fakes.FakeDomain.create_one_domain() + + def setUp(self): + super(TestProjectShow, self).setUp() + + self.project = identity_fakes.FakeProject.create_one_project( + attrs={'domain_id': self.domain.id}) + + # Get the command object to test + self.cmd = project.ShowProject(self.app, None) + + def test_project_show(self): + + self.projects_mock.get.side_effect = [Exception("Not found"), + self.project] + self.projects_mock.get.return_value = self.project + + arglist = [ + self.project.id, + ] + verifylist = [ + ('project', self.project.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.app.client_manager.identity.tokens.get_token_data.return_value = \ + {'token': + {'project': + {'domain': {}, + 'name': parsed_args.project, + 'id': parsed_args.project + } + } + } + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + self.projects_mock.get.assert_called_with( + self.project.id, + parents_as_list=False, + subtree_as_list=False, + ) + + collist = ( + 'description', + 'domain_id', + 'enabled', + 'id', + 'is_domain', + 'name', + 'parent_id', + ) + self.assertEqual(collist, columns) + datalist = ( + self.project.description, + self.project.domain_id, + True, + self.project.id, + False, + self.project.name, + self.project.parent_id, + ) + self.assertEqual(datalist, data) + + def test_project_show_parents(self): + self.project = identity_fakes.FakeProject.create_one_project( + attrs={ + 'parent_id': self.project.parent_id, + 'parents': [{'project': {'id': self.project.parent_id}}] + } + ) + self.projects_mock.get.side_effect = [Exception("Not found"), + self.project] + self.projects_mock.get.return_value = self.project + + arglist = [ + self.project.id, + '--parents', + ] + verifylist = [ + ('project', self.project.id), + ('parents', True), + ('children', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.app.client_manager.identity.tokens.get_token_data.return_value = \ + {'token': + {'project': + {'domain': {}, + 'name': parsed_args.project, + 'id': parsed_args.project + } + } + } + + columns, data = self.cmd.take_action(parsed_args) + self.projects_mock.get.assert_called_with( + self.project.id, + parents_as_list=True, + subtree_as_list=False, + ) + + collist = ( + 'description', + 'domain_id', + 'enabled', + 'id', + 'is_domain', + 'name', + 'parent_id', + 'parents', + ) + self.assertEqual(columns, collist) + datalist = ( + self.project.description, + self.project.domain_id, + self.project.enabled, + self.project.id, + self.project.is_domain, + self.project.name, + self.project.parent_id, + [self.project.parent_id], + ) + self.assertEqual(data, datalist) + + def test_project_show_subtree(self): + self.project = identity_fakes.FakeProject.create_one_project( + attrs={ + 'parent_id': self.project.parent_id, + 'subtree': [{'project': {'id': 'children-id'}}] + } + ) + self.projects_mock.get.side_effect = [Exception("Not found"), + self.project] + self.projects_mock.get.return_value = self.project + + arglist = [ + self.project.id, + '--children', + ] + verifylist = [ + ('project', self.project.id), + ('parents', False), + ('children', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.app.client_manager.identity.tokens.get_token_data.return_value = \ + {'token': + {'project': + {'domain': {}, + 'name': parsed_args.project, + 'id': parsed_args.project + } + } + } + + columns, data = self.cmd.take_action(parsed_args) + self.projects_mock.get.assert_called_with( + self.project.id, + parents_as_list=False, + subtree_as_list=True, + ) + + collist = ( + 'description', + 'domain_id', + 'enabled', + 'id', + 'is_domain', + 'name', + 'parent_id', + 'subtree', + ) + self.assertEqual(columns, collist) + datalist = ( + self.project.description, + self.project.domain_id, + self.project.enabled, + self.project.id, + self.project.is_domain, + self.project.name, + self.project.parent_id, + ['children-id'], + ) + self.assertEqual(data, datalist) + + def test_project_show_parents_and_children(self): + self.project = identity_fakes.FakeProject.create_one_project( + attrs={ + 'parent_id': self.project.parent_id, + 'parents': [{'project': {'id': self.project.parent_id}}], + 'subtree': [{'project': {'id': 'children-id'}}] + } + ) + self.projects_mock.get.side_effect = [Exception("Not found"), + self.project] + self.projects_mock.get.return_value = self.project + + arglist = [ + self.project.id, + '--parents', + '--children', + ] + verifylist = [ + ('project', self.project.id), + ('parents', True), + ('children', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.app.client_manager.identity.tokens.get_token_data.return_value = \ + {'token': + {'project': + {'domain': {}, + 'name': parsed_args.project, + 'id': parsed_args.project + } + } + } + + columns, data = self.cmd.take_action(parsed_args) + self.projects_mock.get.assert_called_with( + self.project.id, + parents_as_list=True, + subtree_as_list=True, + ) + + collist = ( + 'description', + 'domain_id', + 'enabled', + 'id', + 'is_domain', + 'name', + 'parent_id', + 'parents', + 'subtree', + ) + self.assertEqual(columns, collist) + datalist = ( + self.project.description, + self.project.domain_id, + self.project.enabled, + self.project.id, + self.project.is_domain, + self.project.name, + self.project.parent_id, + [self.project.parent_id], + ['children-id'], + ) + self.assertEqual(data, datalist) diff --git a/openstackclient/tests/unit/identity/v3/test_protocol.py b/openstackclient/tests/unit/identity/v3/test_protocol.py new file mode 100644 index 00000000..30b4aa4a --- /dev/null +++ b/openstackclient/tests/unit/identity/v3/test_protocol.py @@ -0,0 +1,188 @@ +# Copyright 2014 CERN. +# +# 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 copy + +from openstackclient.identity.v3 import federation_protocol +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes + + +class TestProtocol(identity_fakes.TestFederatedIdentity): + + def setUp(self): + super(TestProtocol, self).setUp() + + federation_lib = self.app.client_manager.identity.federation + self.protocols_mock = federation_lib.protocols + self.protocols_mock.reset_mock() + + +class TestProtocolCreate(TestProtocol): + + def setUp(self): + super(TestProtocolCreate, self).setUp() + + proto = copy.deepcopy(identity_fakes.PROTOCOL_OUTPUT) + resource = fakes.FakeResource(None, proto, loaded=True) + self.protocols_mock.create.return_value = resource + self.cmd = federation_protocol.CreateProtocol(self.app, None) + + def test_create_protocol(self): + argslist = [ + identity_fakes.protocol_id, + '--identity-provider', identity_fakes.idp_id, + '--mapping', identity_fakes.mapping_id + ] + + verifylist = [ + ('federation_protocol', identity_fakes.protocol_id), + ('identity_provider', identity_fakes.idp_id), + ('mapping', identity_fakes.mapping_id) + ] + parsed_args = self.check_parser(self.cmd, argslist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.protocols_mock.create.assert_called_with( + protocol_id=identity_fakes.protocol_id, + identity_provider=identity_fakes.idp_id, + mapping=identity_fakes.mapping_id) + + collist = ('id', 'identity_provider', 'mapping') + self.assertEqual(collist, columns) + + datalist = (identity_fakes.protocol_id, + identity_fakes.idp_id, + identity_fakes.mapping_id) + self.assertEqual(datalist, data) + + +class TestProtocolDelete(TestProtocol): + + def setUp(self): + super(TestProtocolDelete, self).setUp() + + # This is the return value for utils.find_resource() + self.protocols_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.PROTOCOL_OUTPUT), + loaded=True, + ) + + self.protocols_mock.delete.return_value = None + self.cmd = federation_protocol.DeleteProtocol(self.app, None) + + def test_delete_identity_provider(self): + arglist = [ + '--identity-provider', identity_fakes.idp_id, + identity_fakes.protocol_id + ] + verifylist = [ + ('federation_protocol', [identity_fakes.protocol_id]), + ('identity_provider', identity_fakes.idp_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.protocols_mock.delete.assert_called_with( + identity_fakes.idp_id, identity_fakes.protocol_id) + self.assertIsNone(result) + + +class TestProtocolList(TestProtocol): + + def setUp(self): + super(TestProtocolList, self).setUp() + + self.protocols_mock.get.return_value = fakes.FakeResource( + None, identity_fakes.PROTOCOL_ID_MAPPING, loaded=True) + + self.protocols_mock.list.return_value = [fakes.FakeResource( + None, identity_fakes.PROTOCOL_ID_MAPPING, loaded=True)] + + self.cmd = federation_protocol.ListProtocols(self.app, None) + + def test_list_protocols(self): + arglist = ['--identity-provider', identity_fakes.idp_id] + verifylist = [('identity_provider', identity_fakes.idp_id)] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.protocols_mock.list.assert_called_with(identity_fakes.idp_id) + + +class TestProtocolSet(TestProtocol): + + def setUp(self): + super(TestProtocolSet, self).setUp() + self.protocols_mock.get.return_value = fakes.FakeResource( + None, identity_fakes.PROTOCOL_OUTPUT, loaded=True) + self.protocols_mock.update.return_value = fakes.FakeResource( + None, identity_fakes.PROTOCOL_OUTPUT_UPDATED, loaded=True) + + self.cmd = federation_protocol.SetProtocol(self.app, None) + + def test_set_new_mapping(self): + arglist = [ + identity_fakes.protocol_id, + '--identity-provider', identity_fakes.idp_id, + '--mapping', identity_fakes.mapping_id + ] + verifylist = [('identity_provider', identity_fakes.idp_id), + ('federation_protocol', identity_fakes.protocol_id), + ('mapping', identity_fakes.mapping_id)] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.protocols_mock.update.assert_called_with( + identity_fakes.idp_id, identity_fakes.protocol_id, + identity_fakes.mapping_id) + + collist = ('id', 'identity_provider', 'mapping') + self.assertEqual(collist, columns) + + datalist = (identity_fakes.protocol_id, identity_fakes.idp_id, + identity_fakes.mapping_id_updated) + self.assertEqual(datalist, data) + + +class TestProtocolShow(TestProtocol): + + def setUp(self): + super(TestProtocolShow, self).setUp() + self.protocols_mock.get.return_value = fakes.FakeResource( + None, identity_fakes.PROTOCOL_OUTPUT, loaded=False) + + self.cmd = federation_protocol.ShowProtocol(self.app, None) + + def test_show_protocol(self): + arglist = [identity_fakes.protocol_id, '--identity-provider', + identity_fakes.idp_id] + verifylist = [('federation_protocol', identity_fakes.protocol_id), + ('identity_provider', identity_fakes.idp_id)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.protocols_mock.get.assert_called_with(identity_fakes.idp_id, + identity_fakes.protocol_id) + + collist = ('id', 'identity_provider', 'mapping') + self.assertEqual(collist, columns) + + datalist = (identity_fakes.protocol_id, + identity_fakes.idp_id, + identity_fakes.mapping_id) + self.assertEqual(datalist, data) diff --git a/openstackclient/tests/unit/identity/v3/test_region.py b/openstackclient/tests/unit/identity/v3/test_region.py new file mode 100644 index 00000000..e83a4e9f --- /dev/null +++ b/openstackclient/tests/unit/identity/v3/test_region.py @@ -0,0 +1,348 @@ +# 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 copy + +from openstackclient.identity.v3 import region +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes + + +class TestRegion(identity_fakes.TestIdentityv3): + + def setUp(self): + super(TestRegion, self).setUp() + + # Get a shortcut to the RegionManager Mock + self.regions_mock = self.app.client_manager.identity.regions + self.regions_mock.reset_mock() + + +class TestRegionCreate(TestRegion): + + columns = ( + 'description', + 'parent_region', + 'region', + ) + datalist = ( + identity_fakes.region_description, + identity_fakes.region_parent_region_id, + identity_fakes.region_id, + ) + + def setUp(self): + super(TestRegionCreate, self).setUp() + + self.regions_mock.create.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.REGION), + loaded=True, + ) + + # Get the command object to test + self.cmd = region.CreateRegion(self.app, None) + + def test_region_create_description(self): + arglist = [ + identity_fakes.region_id, + '--description', identity_fakes.region_description, + ] + verifylist = [ + ('region', identity_fakes.region_id), + ('description', identity_fakes.region_description) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'description': identity_fakes.region_description, + 'id': identity_fakes.region_id, + 'parent_region': None, + } + self.regions_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_region_create_no_options(self): + arglist = [ + identity_fakes.region_id, + ] + verifylist = [ + ('region', identity_fakes.region_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'description': None, + 'id': identity_fakes.region_id, + 'parent_region': None, + } + self.regions_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_region_create_parent_region_id(self): + arglist = [ + identity_fakes.region_id, + '--parent-region', identity_fakes.region_parent_region_id, + ] + verifylist = [ + ('region', identity_fakes.region_id), + ('parent_region', identity_fakes.region_parent_region_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'description': None, + 'id': identity_fakes.region_id, + 'parent_region': identity_fakes.region_parent_region_id, + } + self.regions_mock.create.assert_called_with( + **kwargs + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + +class TestRegionDelete(TestRegion): + + def setUp(self): + super(TestRegionDelete, self).setUp() + + self.regions_mock.delete.return_value = None + + # Get the command object to test + self.cmd = region.DeleteRegion(self.app, None) + + def test_region_delete_no_options(self): + arglist = [ + identity_fakes.region_id, + ] + verifylist = [ + ('region', [identity_fakes.region_id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.regions_mock.delete.assert_called_with( + identity_fakes.region_id, + ) + self.assertIsNone(result) + + +class TestRegionList(TestRegion): + + columns = ( + 'Region', + 'Parent Region', + 'Description', + ) + datalist = ( + ( + identity_fakes.region_id, + identity_fakes.region_parent_region_id, + identity_fakes.region_description, + ), + ) + + def setUp(self): + super(TestRegionList, self).setUp() + + self.regions_mock.list.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.REGION), + loaded=True, + ), + ] + + # Get the command object to test + self.cmd = region.ListRegion(self.app, None) + + def test_region_list_no_options(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + self.regions_mock.list.assert_called_with() + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_region_list_parent_region_id(self): + arglist = [ + '--parent-region', identity_fakes.region_parent_region_id, + ] + verifylist = [ + ('parent_region', identity_fakes.region_parent_region_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + self.regions_mock.list.assert_called_with( + parent_region_id=identity_fakes.region_parent_region_id) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + +class TestRegionSet(TestRegion): + + def setUp(self): + super(TestRegionSet, self).setUp() + + self.regions_mock.update.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.REGION), + loaded=True, + ) + + # Get the command object to test + self.cmd = region.SetRegion(self.app, None) + + def test_region_set_no_options(self): + arglist = [ + identity_fakes.region_id, + ] + verifylist = [ + ('region', identity_fakes.region_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + kwargs = {} + self.regions_mock.update.assert_called_with( + identity_fakes.region_id, + **kwargs + ) + self.assertIsNone(result) + + def test_region_set_description(self): + arglist = [ + '--description', 'qwerty', + identity_fakes.region_id, + ] + verifylist = [ + ('description', 'qwerty'), + ('region', identity_fakes.region_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'description': 'qwerty', + } + self.regions_mock.update.assert_called_with( + identity_fakes.region_id, + **kwargs + ) + self.assertIsNone(result) + + def test_region_set_parent_region_id(self): + arglist = [ + '--parent-region', 'new_parent', + identity_fakes.region_id, + ] + verifylist = [ + ('parent_region', 'new_parent'), + ('region', identity_fakes.region_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'parent_region': 'new_parent', + } + self.regions_mock.update.assert_called_with( + identity_fakes.region_id, + **kwargs + ) + self.assertIsNone(result) + + +class TestRegionShow(TestRegion): + + def setUp(self): + super(TestRegionShow, self).setUp() + + self.regions_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.REGION), + loaded=True, + ) + + # Get the command object to test + self.cmd = region.ShowRegion(self.app, None) + + def test_region_show(self): + arglist = [ + identity_fakes.region_id, + ] + verifylist = [ + ('region', identity_fakes.region_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + self.regions_mock.get.assert_called_with( + identity_fakes.region_id, + ) + + collist = ('description', 'parent_region', 'region') + self.assertEqual(collist, columns) + datalist = ( + identity_fakes.region_description, + identity_fakes.region_parent_region_id, + identity_fakes.region_id, + ) + self.assertEqual(datalist, data) diff --git a/openstackclient/tests/unit/identity/v3/test_role.py b/openstackclient/tests/unit/identity/v3/test_role.py new file mode 100644 index 00000000..448e18d3 --- /dev/null +++ b/openstackclient/tests/unit/identity/v3/test_role.py @@ -0,0 +1,1105 @@ +# Copyright 2013 Nebula 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 copy + +from openstackclient.identity.v3 import role +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes + + +class TestRole(identity_fakes.TestIdentityv3): + + def setUp(self): + super(TestRole, self).setUp() + + # Get a shortcut to the UserManager Mock + self.users_mock = self.app.client_manager.identity.users + self.users_mock.reset_mock() + + # Get a shortcut to the UserManager Mock + self.groups_mock = self.app.client_manager.identity.groups + self.groups_mock.reset_mock() + + # Get a shortcut to the DomainManager Mock + self.domains_mock = self.app.client_manager.identity.domains + self.domains_mock.reset_mock() + + # Get a shortcut to the ProjectManager Mock + self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock.reset_mock() + + # Get a shortcut to the RoleManager Mock + self.roles_mock = self.app.client_manager.identity.roles + self.roles_mock.reset_mock() + + def _is_inheritance_testcase(self): + return False + + +class TestRoleInherited(TestRole): + + def _is_inheritance_testcase(self): + return True + + +class TestRoleAdd(TestRole): + + def setUp(self): + super(TestRoleAdd, self).setUp() + + self.users_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.USER), + loaded=True, + ) + + self.groups_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.GROUP), + loaded=True, + ) + + self.domains_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.DOMAIN), + loaded=True, + ) + + self.projects_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.PROJECT), + loaded=True, + ) + + self.roles_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.ROLE), + loaded=True, + ) + self.roles_mock.grant.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.ROLE), + loaded=True, + ) + + # Get the command object to test + self.cmd = role.AddRole(self.app, None) + + def test_role_add_user_domain(self): + arglist = [ + '--user', identity_fakes.user_name, + '--domain', identity_fakes.domain_name, + identity_fakes.role_name, + ] + if self._is_inheritance_testcase(): + arglist.append('--inherited') + verifylist = [ + ('user', identity_fakes.user_name), + ('group', None), + ('domain', identity_fakes.domain_name), + ('project', None), + ('role', identity_fakes.role_name), + ('inherited', self._is_inheritance_testcase()), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'user': identity_fakes.user_id, + 'domain': identity_fakes.domain_id, + 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + } + # RoleManager.grant(role, user=, group=, domain=, project=) + self.roles_mock.grant.assert_called_with( + identity_fakes.role_id, + **kwargs + ) + self.assertIsNone(result) + + def test_role_add_user_project(self): + arglist = [ + '--user', identity_fakes.user_name, + '--project', identity_fakes.project_name, + identity_fakes.role_name, + ] + if self._is_inheritance_testcase(): + arglist.append('--inherited') + verifylist = [ + ('user', identity_fakes.user_name), + ('group', None), + ('domain', None), + ('project', identity_fakes.project_name), + ('role', identity_fakes.role_name), + ('inherited', self._is_inheritance_testcase()), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'user': identity_fakes.user_id, + 'project': identity_fakes.project_id, + 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + } + # RoleManager.grant(role, user=, group=, domain=, project=) + self.roles_mock.grant.assert_called_with( + identity_fakes.role_id, + **kwargs + ) + self.assertIsNone(result) + + def test_role_add_group_domain(self): + arglist = [ + '--group', identity_fakes.group_name, + '--domain', identity_fakes.domain_name, + identity_fakes.role_name, + ] + if self._is_inheritance_testcase(): + arglist.append('--inherited') + verifylist = [ + ('user', None), + ('group', identity_fakes.group_name), + ('domain', identity_fakes.domain_name), + ('project', None), + ('role', identity_fakes.role_name), + ('inherited', self._is_inheritance_testcase()), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'group': identity_fakes.group_id, + 'domain': identity_fakes.domain_id, + 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + } + # RoleManager.grant(role, user=, group=, domain=, project=) + self.roles_mock.grant.assert_called_with( + identity_fakes.role_id, + **kwargs + ) + self.assertIsNone(result) + + def test_role_add_group_project(self): + arglist = [ + '--group', identity_fakes.group_name, + '--project', identity_fakes.project_name, + identity_fakes.role_name, + ] + if self._is_inheritance_testcase(): + arglist.append('--inherited') + verifylist = [ + ('user', None), + ('group', identity_fakes.group_name), + ('domain', None), + ('project', identity_fakes.project_name), + ('role', identity_fakes.role_name), + ('inherited', self._is_inheritance_testcase()), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'group': identity_fakes.group_id, + 'project': identity_fakes.project_id, + 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + } + # RoleManager.grant(role, user=, group=, domain=, project=) + self.roles_mock.grant.assert_called_with( + identity_fakes.role_id, + **kwargs + ) + self.assertIsNone(result) + + def test_role_add_domain_role_on_user_project(self): + self.roles_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.ROLE_2), + loaded=True, + ) + arglist = [ + '--user', identity_fakes.user_name, + '--project', identity_fakes.project_name, + '--role-domain', identity_fakes.domain_name, + identity_fakes.ROLE_2['name'], + ] + if self._is_inheritance_testcase(): + arglist.append('--inherited') + verifylist = [ + ('user', identity_fakes.user_name), + ('group', None), + ('domain', None), + ('project', identity_fakes.project_name), + ('role', identity_fakes.ROLE_2['name']), + ('inherited', self._is_inheritance_testcase()), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'user': identity_fakes.user_id, + 'project': identity_fakes.project_id, + 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + } + # RoleManager.grant(role, user=, group=, domain=, project=) + self.roles_mock.grant.assert_called_with( + identity_fakes.ROLE_2['id'], + **kwargs + ) + self.assertIsNone(result) + + +class TestRoleAddInherited(TestRoleAdd, TestRoleInherited): + pass + + +class TestRoleCreate(TestRole): + + def setUp(self): + super(TestRoleCreate, self).setUp() + + self.domains_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.DOMAIN), + loaded=True, + ) + + self.roles_mock.create.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.ROLE), + loaded=True, + ) + + # Get the command object to test + self.cmd = role.CreateRole(self.app, None) + + def test_role_create_no_options(self): + arglist = [ + identity_fakes.role_name, + ] + verifylist = [ + ('name', identity_fakes.role_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'domain': None, + 'name': identity_fakes.role_name, + } + + # RoleManager.create(name=, domain=) + self.roles_mock.create.assert_called_with( + **kwargs + ) + + collist = ('domain', 'id', 'name') + self.assertEqual(collist, columns) + datalist = ( + None, + identity_fakes.role_id, + identity_fakes.role_name, + ) + self.assertEqual(datalist, data) + + def test_role_create_with_domain(self): + + self.roles_mock.create.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.ROLE_2), + loaded=True, + ) + + arglist = [ + '--domain', identity_fakes.domain_name, + identity_fakes.ROLE_2['name'], + ] + verifylist = [ + ('domain', identity_fakes.domain_name), + ('name', identity_fakes.ROLE_2['name']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'domain': identity_fakes.domain_id, + 'name': identity_fakes.ROLE_2['name'], + } + + # RoleManager.create(name=, domain=) + self.roles_mock.create.assert_called_with( + **kwargs + ) + + collist = ('domain', 'id', 'name') + self.assertEqual(collist, columns) + datalist = ( + identity_fakes.domain_id, + identity_fakes.ROLE_2['id'], + identity_fakes.ROLE_2['name'], + ) + self.assertEqual(datalist, data) + + +class TestRoleDelete(TestRole): + + def setUp(self): + super(TestRoleDelete, self).setUp() + + self.roles_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.ROLE), + loaded=True, + ) + self.roles_mock.delete.return_value = None + + # Get the command object to test + self.cmd = role.DeleteRole(self.app, None) + + def test_role_delete_no_options(self): + arglist = [ + identity_fakes.role_name, + ] + verifylist = [ + ('roles', [identity_fakes.role_name]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.roles_mock.delete.assert_called_with( + identity_fakes.role_id, + ) + self.assertIsNone(result) + + def test_role_delete_with_domain(self): + self.roles_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.ROLE_2), + loaded=True, + ) + self.roles_mock.delete.return_value = None + + arglist = [ + '--domain', identity_fakes.domain_name, + identity_fakes.ROLE_2['name'], + ] + verifylist = [ + ('roles', [identity_fakes.ROLE_2['name']]), + ('domain', identity_fakes.domain_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.roles_mock.delete.assert_called_with( + identity_fakes.ROLE_2['id'], + ) + self.assertIsNone(result) + + +class TestRoleList(TestRole): + + columns = ( + 'ID', + 'Name', + ) + datalist = ( + ( + identity_fakes.role_id, + identity_fakes.role_name, + ), + ) + + def setUp(self): + super(TestRoleList, self).setUp() + + self.roles_mock.list.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.ROLE), + loaded=True, + ), + ] + + self.domains_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.DOMAIN), + loaded=True, + ) + self.projects_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.PROJECT), + loaded=True, + ) + self.users_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.USER), + loaded=True, + ) + self.groups_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.GROUP), + loaded=True, + ) + + # Get the command object to test + self.cmd = role.ListRole(self.app, None) + + def test_role_list_no_options(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.roles_mock.list.assert_called_with() + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_user_list_inherited(self): + arglist = [ + '--user', identity_fakes.user_id, + '--inherited', + ] + verifylist = [ + ('user', identity_fakes.user_id), + ('inherited', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'domain': 'default', + 'user': self.users_mock.get(), + 'os_inherit_extension_inherited': True, + } + # RoleManager.list(user=, group=, domain=, project=, **kwargs) + self.roles_mock.list.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_user_list_user(self): + arglist = [ + '--user', identity_fakes.user_id, + ] + verifylist = [ + ('user', identity_fakes.user_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'domain': 'default', + 'user': self.users_mock.get(), + 'os_inherit_extension_inherited': False + } + # RoleManager.list(user=, group=, domain=, project=, **kwargs) + self.roles_mock.list.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_role_list_domain_user(self): + arglist = [ + '--domain', identity_fakes.domain_name, + '--user', identity_fakes.user_id, + ] + verifylist = [ + ('domain', identity_fakes.domain_name), + ('user', identity_fakes.user_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'domain': self.domains_mock.get(), + 'user': self.users_mock.get(), + 'os_inherit_extension_inherited': False + } + # RoleManager.list(user=, group=, domain=, project=, **kwargs) + self.roles_mock.list.assert_called_with( + **kwargs + ) + + collist = ('ID', 'Name', 'Domain', 'User') + self.assertEqual(collist, columns) + datalist = (( + identity_fakes.role_id, + identity_fakes.role_name, + identity_fakes.domain_name, + identity_fakes.user_name, + ), ) + self.assertEqual(datalist, tuple(data)) + + def test_role_list_domain_group(self): + arglist = [ + '--domain', identity_fakes.domain_name, + '--group', identity_fakes.group_id, + ] + verifylist = [ + ('domain', identity_fakes.domain_name), + ('group', identity_fakes.group_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'domain': self.domains_mock.get(), + 'group': self.groups_mock.get(), + 'os_inherit_extension_inherited': False + } + # RoleManager.list(user=, group=, domain=, project=, **kwargs) + self.roles_mock.list.assert_called_with( + **kwargs + ) + + collist = ('ID', 'Name', 'Domain', 'Group') + self.assertEqual(collist, columns) + datalist = (( + identity_fakes.role_id, + identity_fakes.role_name, + identity_fakes.domain_name, + identity_fakes.group_name, + ), ) + self.assertEqual(datalist, tuple(data)) + + def test_role_list_project_user(self): + arglist = [ + '--project', identity_fakes.project_name, + '--user', identity_fakes.user_id, + ] + verifylist = [ + ('project', identity_fakes.project_name), + ('user', identity_fakes.user_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'project': self.projects_mock.get(), + 'user': self.users_mock.get(), + 'os_inherit_extension_inherited': False + } + # RoleManager.list(user=, group=, domain=, project=, **kwargs) + self.roles_mock.list.assert_called_with( + **kwargs + ) + + collist = ('ID', 'Name', 'Project', 'User') + self.assertEqual(collist, columns) + datalist = (( + identity_fakes.role_id, + identity_fakes.role_name, + identity_fakes.project_name, + identity_fakes.user_name, + ), ) + self.assertEqual(datalist, tuple(data)) + + def test_role_list_project_group(self): + arglist = [ + '--project', identity_fakes.project_name, + '--group', identity_fakes.group_id, + ] + verifylist = [ + ('project', identity_fakes.project_name), + ('group', identity_fakes.group_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'project': self.projects_mock.get(), + 'group': self.groups_mock.get(), + 'os_inherit_extension_inherited': False + } + # RoleManager.list(user=, group=, domain=, project=, **kwargs) + self.roles_mock.list.assert_called_with( + **kwargs + ) + + collist = ('ID', 'Name', 'Project', 'Group') + self.assertEqual(collist, columns) + datalist = (( + identity_fakes.role_id, + identity_fakes.role_name, + identity_fakes.project_name, + identity_fakes.group_name, + ), ) + self.assertEqual(datalist, tuple(data)) + + def test_role_list_domain_role(self): + self.roles_mock.list.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.ROLE_2), + loaded=True, + ), + ] + arglist = [ + '--domain', identity_fakes.domain_name, + ] + verifylist = [ + ('domain', identity_fakes.domain_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'domain_id': identity_fakes.domain_id + } + # RoleManager.list(user=, group=, domain=, project=, **kwargs) + self.roles_mock.list.assert_called_with( + **kwargs + ) + + collist = ('ID', 'Name', 'Domain') + self.assertEqual(collist, columns) + datalist = (( + identity_fakes.ROLE_2['id'], + identity_fakes.ROLE_2['name'], + identity_fakes.domain_name, + ), ) + self.assertEqual(datalist, tuple(data)) + + +class TestRoleRemove(TestRole): + + def setUp(self): + super(TestRoleRemove, self).setUp() + + self.users_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.USER), + loaded=True, + ) + + self.groups_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.GROUP), + loaded=True, + ) + + self.domains_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.DOMAIN), + loaded=True, + ) + + self.projects_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.PROJECT), + loaded=True, + ) + + self.roles_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.ROLE), + loaded=True, + ) + self.roles_mock.revoke.return_value = None + + # Get the command object to test + self.cmd = role.RemoveRole(self.app, None) + + def test_role_remove_user_domain(self): + arglist = [ + '--user', identity_fakes.user_name, + '--domain', identity_fakes.domain_name, + identity_fakes.role_name, + ] + if self._is_inheritance_testcase(): + arglist.append('--inherited') + verifylist = [ + ('user', identity_fakes.user_name), + ('group', None), + ('domain', identity_fakes.domain_name), + ('project', None), + ('role', identity_fakes.role_name), + ('inherited', self._is_inheritance_testcase()), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'user': identity_fakes.user_id, + 'domain': identity_fakes.domain_id, + 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + } + # RoleManager.revoke(role, user=, group=, domain=, project=) + self.roles_mock.revoke.assert_called_with( + identity_fakes.role_id, + **kwargs + ) + self.assertIsNone(result) + + def test_role_remove_user_project(self): + arglist = [ + '--user', identity_fakes.user_name, + '--project', identity_fakes.project_name, + identity_fakes.role_name, + ] + if self._is_inheritance_testcase(): + arglist.append('--inherited') + verifylist = [ + ('user', identity_fakes.user_name), + ('group', None), + ('domain', None), + ('project', identity_fakes.project_name), + ('role', identity_fakes.role_name), + ('inherited', self._is_inheritance_testcase()), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'user': identity_fakes.user_id, + 'project': identity_fakes.project_id, + 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + } + # RoleManager.revoke(role, user=, group=, domain=, project=) + self.roles_mock.revoke.assert_called_with( + identity_fakes.role_id, + **kwargs + ) + self.assertIsNone(result) + + def test_role_remove_group_domain(self): + arglist = [ + '--group', identity_fakes.group_name, + '--domain', identity_fakes.domain_name, + identity_fakes.role_name, + ] + if self._is_inheritance_testcase(): + arglist.append('--inherited') + verifylist = [ + ('user', None), + ('group', identity_fakes.group_name), + ('domain', identity_fakes.domain_name), + ('project', None), + ('role', identity_fakes.role_name), + ('role', identity_fakes.role_name), + ('inherited', self._is_inheritance_testcase()), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'group': identity_fakes.group_id, + 'domain': identity_fakes.domain_id, + 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + } + # RoleManager.revoke(role, user=, group=, domain=, project=) + self.roles_mock.revoke.assert_called_with( + identity_fakes.role_id, + **kwargs + ) + self.assertIsNone(result) + + def test_role_remove_group_project(self): + arglist = [ + '--group', identity_fakes.group_name, + '--project', identity_fakes.project_name, + identity_fakes.role_name, + ] + if self._is_inheritance_testcase(): + arglist.append('--inherited') + verifylist = [ + ('user', None), + ('group', identity_fakes.group_name), + ('domain', None), + ('project', identity_fakes.project_name), + ('role', identity_fakes.role_name), + ('inherited', self._is_inheritance_testcase()), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'group': identity_fakes.group_id, + 'project': identity_fakes.project_id, + 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + } + # RoleManager.revoke(role, user=, group=, domain=, project=) + self.roles_mock.revoke.assert_called_with( + identity_fakes.role_id, + **kwargs + ) + self.assertIsNone(result) + + def test_role_remove_domain_role_on_group_domain(self): + self.roles_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.ROLE_2), + loaded=True, + ) + arglist = [ + '--group', identity_fakes.group_name, + '--domain', identity_fakes.domain_name, + identity_fakes.ROLE_2['name'], + ] + if self._is_inheritance_testcase(): + arglist.append('--inherited') + verifylist = [ + ('user', None), + ('group', identity_fakes.group_name), + ('domain', identity_fakes.domain_name), + ('project', None), + ('role', identity_fakes.ROLE_2['name']), + ('inherited', self._is_inheritance_testcase()), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'group': identity_fakes.group_id, + 'domain': identity_fakes.domain_id, + 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + } + # RoleManager.revoke(role, user=, group=, domain=, project=) + self.roles_mock.revoke.assert_called_with( + identity_fakes.ROLE_2['id'], + **kwargs + ) + self.assertIsNone(result) + + +class TestRoleSet(TestRole): + + def setUp(self): + super(TestRoleSet, self).setUp() + + self.roles_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.ROLE), + loaded=True, + ) + self.roles_mock.update.return_value = None + + # Get the command object to test + self.cmd = role.SetRole(self.app, None) + + def test_role_set_no_options(self): + arglist = [ + '--name', 'over', + identity_fakes.role_name, + ] + verifylist = [ + ('name', 'over'), + ('role', identity_fakes.role_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': 'over', + } + # RoleManager.update(role, name=) + self.roles_mock.update.assert_called_with( + identity_fakes.role_id, + **kwargs + ) + self.assertIsNone(result) + + def test_role_set_domain_role(self): + self.roles_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.ROLE_2), + loaded=True, + ) + arglist = [ + '--name', 'over', + '--domain', identity_fakes.domain_name, + identity_fakes.ROLE_2['name'], + ] + verifylist = [ + ('name', 'over'), + ('domain', identity_fakes.domain_name), + ('role', identity_fakes.ROLE_2['name']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': 'over', + } + # RoleManager.update(role, name=) + self.roles_mock.update.assert_called_with( + identity_fakes.ROLE_2['id'], + **kwargs + ) + self.assertIsNone(result) + + +class TestRoleShow(TestRole): + + def setUp(self): + super(TestRoleShow, self).setUp() + + self.roles_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.ROLE), + loaded=True, + ) + + # Get the command object to test + self.cmd = role.ShowRole(self.app, None) + + def test_role_show(self): + arglist = [ + identity_fakes.role_name, + ] + verifylist = [ + ('role', identity_fakes.role_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # RoleManager.get(role) + self.roles_mock.get.assert_called_with( + identity_fakes.role_name, + ) + + collist = ('domain', 'id', 'name') + self.assertEqual(collist, columns) + datalist = ( + None, + identity_fakes.role_id, + identity_fakes.role_name, + ) + self.assertEqual(datalist, data) + + def test_role_show_domain_role(self): + self.roles_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.ROLE_2), + loaded=True, + ) + arglist = [ + '--domain', identity_fakes.domain_name, + identity_fakes.ROLE_2['name'], + ] + verifylist = [ + ('domain', identity_fakes.domain_name), + ('role', identity_fakes.ROLE_2['name']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # RoleManager.get(role). This is called from utils.find_resource(). + # In fact, the current implementation calls the get(role) first with + # just the name, then with the name+domain_id. So technically we should + # mock this out with a call list, with the first call returning None + # and the second returning the object. However, if we did that we are + # then just testing the current sequencing within the utils method, and + # would become brittle to changes within that method. Hence we just + # check for the first call which is always lookup by name. + self.roles_mock.get.assert_called_with( + identity_fakes.ROLE_2['name'], + ) + + collist = ('domain', 'id', 'name') + self.assertEqual(collist, columns) + datalist = ( + identity_fakes.domain_id, + identity_fakes.ROLE_2['id'], + identity_fakes.ROLE_2['name'], + ) + self.assertEqual(datalist, data) diff --git a/openstackclient/tests/unit/identity/v3/test_role_assignment.py b/openstackclient/tests/unit/identity/v3/test_role_assignment.py new file mode 100644 index 00000000..7102a0cd --- /dev/null +++ b/openstackclient/tests/unit/identity/v3/test_role_assignment.py @@ -0,0 +1,683 @@ +# 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 copy +import mock + +from openstackclient.identity.v3 import role_assignment +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes + + +class TestRoleAssignment(identity_fakes.TestIdentityv3): + + def setUp(self): + super(TestRoleAssignment, self).setUp() + + +class TestRoleAssignmentList(TestRoleAssignment): + + columns = ( + 'Role', + 'User', + 'Group', + 'Project', + 'Domain', + 'Inherited', + ) + + def setUp(self): + super(TestRoleAssignment, self).setUp() + + # Get a shortcut to the UserManager Mock + self.users_mock = self.app.client_manager.identity.users + self.users_mock.reset_mock() + + # Get a shortcut to the GroupManager Mock + self.groups_mock = self.app.client_manager.identity.groups + self.groups_mock.reset_mock() + + # Get a shortcut to the DomainManager Mock + self.domains_mock = self.app.client_manager.identity.domains + self.domains_mock.reset_mock() + + # Get a shortcut to the ProjectManager Mock + self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock.reset_mock() + + # Get a shortcut to the RoleManager Mock + self.roles_mock = self.app.client_manager.identity.roles + self.roles_mock.reset_mock() + + self.role_assignments_mock = self.app.client_manager.identity.\ + role_assignments + self.role_assignments_mock.reset_mock() + + # Get the command object to test + self.cmd = role_assignment.ListRoleAssignment(self.app, None) + + def test_role_assignment_list_no_filters(self): + + self.role_assignments_mock.list.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID), + loaded=True, + ), + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_GROUP_ID), + loaded=True, + ), + ] + + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.role_assignments_mock.list.assert_called_with( + domain=None, + group=None, + effective=False, + role=None, + user=None, + project=None, + os_inherit_extension_inherited_to=None, + include_names=False) + + self.assertEqual(self.columns, columns) + datalist = (( + identity_fakes.role_id, + identity_fakes.user_id, + '', + identity_fakes.project_id, + '', + False + ), (identity_fakes.role_id, + '', + identity_fakes.group_id, + identity_fakes.project_id, + '', + False + ),) + self.assertEqual(datalist, tuple(data)) + + def test_role_assignment_list_user(self): + + self.role_assignments_mock.list.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID), + loaded=True, + ), + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID), + loaded=True, + ), + ] + + arglist = [ + '--user', identity_fakes.user_name + ] + verifylist = [ + ('user', identity_fakes.user_name), + ('group', None), + ('domain', None), + ('project', None), + ('role', None), + ('effective', False), + ('inherited', False), + ('names', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.role_assignments_mock.list.assert_called_with( + domain=None, + user=self.users_mock.get(), + group=None, + project=None, + role=None, + effective=False, + os_inherit_extension_inherited_to=None, + include_names=False) + + self.assertEqual(self.columns, columns) + datalist = (( + identity_fakes.role_id, + identity_fakes.user_id, + '', + '', + identity_fakes.domain_id, + False + ), (identity_fakes.role_id, + identity_fakes.user_id, + '', + identity_fakes.project_id, + '', + False + ),) + self.assertEqual(datalist, tuple(data)) + + def test_role_assignment_list_group(self): + + self.role_assignments_mock.list.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ASSIGNMENT_WITH_DOMAIN_ID_AND_GROUP_ID), + loaded=True, + ), + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_GROUP_ID), + loaded=True, + ), + ] + + arglist = [ + '--group', identity_fakes.group_name + ] + verifylist = [ + ('user', None), + ('group', identity_fakes.group_name), + ('domain', None), + ('project', None), + ('role', None), + ('effective', False), + ('inherited', False), + ('names', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.role_assignments_mock.list.assert_called_with( + domain=None, + group=self.groups_mock.get(), + effective=False, + project=None, + role=None, + user=None, + os_inherit_extension_inherited_to=None, + include_names=False) + + self.assertEqual(self.columns, columns) + datalist = (( + identity_fakes.role_id, + '', + identity_fakes.group_id, + '', + identity_fakes.domain_id, + False + ), (identity_fakes.role_id, + '', + identity_fakes.group_id, + identity_fakes.project_id, + '', + False + ),) + self.assertEqual(datalist, tuple(data)) + + def test_role_assignment_list_domain(self): + + self.role_assignments_mock.list.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID), + loaded=True, + ), + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ASSIGNMENT_WITH_DOMAIN_ID_AND_GROUP_ID), + loaded=True, + ), + ] + + arglist = [ + '--domain', identity_fakes.domain_name + ] + verifylist = [ + ('user', None), + ('group', None), + ('domain', identity_fakes.domain_name), + ('project', None), + ('role', None), + ('effective', False), + ('inherited', False), + ('names', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.role_assignments_mock.list.assert_called_with( + domain=self.domains_mock.get(), + group=None, + effective=False, + project=None, + role=None, + user=None, + os_inherit_extension_inherited_to=None, + include_names=False) + + self.assertEqual(self.columns, columns) + datalist = (( + identity_fakes.role_id, + identity_fakes.user_id, + '', + '', + identity_fakes.domain_id, + False + ), (identity_fakes.role_id, + '', + identity_fakes.group_id, + '', + identity_fakes.domain_id, + False + ),) + self.assertEqual(datalist, tuple(data)) + + def test_role_assignment_list_project(self): + + self.role_assignments_mock.list.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID), + loaded=True, + ), + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_GROUP_ID), + loaded=True, + ), + ] + + arglist = [ + '--project', identity_fakes.project_name + ] + verifylist = [ + ('user', None), + ('group', None), + ('domain', None), + ('project', identity_fakes.project_name), + ('role', None), + ('effective', False), + ('inherited', False), + ('names', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.role_assignments_mock.list.assert_called_with( + domain=None, + group=None, + effective=False, + project=self.projects_mock.get(), + role=None, + user=None, + os_inherit_extension_inherited_to=None, + include_names=False) + + self.assertEqual(self.columns, columns) + datalist = (( + identity_fakes.role_id, + identity_fakes.user_id, + '', + identity_fakes.project_id, + '', + False + ), (identity_fakes.role_id, + '', + identity_fakes.group_id, + identity_fakes.project_id, + '', + False + ),) + self.assertEqual(datalist, tuple(data)) + + def test_role_assignment_list_def_creds(self): + + auth_ref = self.app.client_manager.auth_ref = mock.MagicMock() + auth_ref.project_id.return_value = identity_fakes.project_id + auth_ref.user_id.return_value = identity_fakes.user_id + + self.role_assignments_mock.list.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID), + loaded=True, + ), + ] + + arglist = [ + '--auth-user', + '--auth-project', + ] + verifylist = [ + ('user', None), + ('group', None), + ('domain', None), + ('project', None), + ('role', None), + ('effective', False), + ('inherited', False), + ('names', False), + ('authuser', True), + ('authproject', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.role_assignments_mock.list.assert_called_with( + domain=None, + user=self.users_mock.get(), + group=None, + project=self.projects_mock.get(), + role=None, + effective=False, + os_inherit_extension_inherited_to=None, + include_names=False) + + self.assertEqual(self.columns, columns) + datalist = (( + identity_fakes.role_id, + identity_fakes.user_id, + '', + identity_fakes.project_id, + '', + False + ),) + self.assertEqual(datalist, tuple(data)) + + def test_role_assignment_list_effective(self): + + self.role_assignments_mock.list.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID), + loaded=True, + ), + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID), + loaded=True, + ), + ] + + arglist = ['--effective'] + verifylist = [ + ('user', None), + ('group', None), + ('domain', None), + ('project', None), + ('role', None), + ('effective', True), + ('inherited', False), + ('names', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.role_assignments_mock.list.assert_called_with( + domain=None, + group=None, + effective=True, + project=None, + role=None, + user=None, + os_inherit_extension_inherited_to=None, + include_names=False) + + self.assertEqual(self.columns, columns) + datalist = (( + identity_fakes.role_id, + identity_fakes.user_id, + '', + identity_fakes.project_id, + '', + False + ), (identity_fakes.role_id, + identity_fakes.user_id, + '', + '', + identity_fakes.domain_id, + False + ),) + self.assertEqual(tuple(data), datalist) + + def test_role_assignment_list_inherited(self): + + self.role_assignments_mock.list.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy( + (identity_fakes. + ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID_INHERITED)), + loaded=True, + ), + fakes.FakeResource( + None, + copy.deepcopy( + (identity_fakes. + ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID_INHERITED)), + loaded=True, + ), + ] + + arglist = ['--inherited'] + verifylist = [ + ('user', None), + ('group', None), + ('domain', None), + ('project', None), + ('role', None), + ('effective', False), + ('inherited', True), + ('names', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.role_assignments_mock.list.assert_called_with( + domain=None, + group=None, + effective=False, + project=None, + role=None, + user=None, + os_inherit_extension_inherited_to='projects', + include_names=False) + + self.assertEqual(self.columns, columns) + datalist = (( + identity_fakes.role_id, + identity_fakes.user_id, + '', + identity_fakes.project_id, + '', + True + ), (identity_fakes.role_id, + identity_fakes.user_id, + '', + '', + identity_fakes.domain_id, + True + ),) + self.assertEqual(datalist, tuple(data)) + + def test_role_assignment_list_include_names(self): + + self.role_assignments_mock.list.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes + .ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID_INCLUDE_NAMES), + loaded=True, + ), + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes + .ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID_INCLUDE_NAMES), + loaded=True, + ), + ] + + arglist = ['--names'] + verifylist = [ + ('user', None), + ('group', None), + ('domain', None), + ('project', None), + ('role', None), + ('effective', False), + ('inherited', False), + ('names', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # DisplayCommandBase.take_action() returns two tuples + + # This test will not run correctly until the patch in the python + # client is merged. Once that is done 'data' should return the + # correct information + columns, data = self.cmd.take_action(parsed_args) + + self.role_assignments_mock.list.assert_called_with( + domain=None, + group=None, + effective=False, + project=None, + role=None, + user=None, + os_inherit_extension_inherited_to=None, + include_names=True) + + collist = ('Role', 'User', 'Group', 'Project', 'Domain', 'Inherited') + self.assertEqual(columns, collist) + + datalist1 = (( + identity_fakes.role_name, + '@'.join([identity_fakes.user_name, identity_fakes.domain_name]), + '', + '@'.join([identity_fakes.project_name, + identity_fakes.domain_name]), + '', + False + ), (identity_fakes.role_name, + '@'.join([identity_fakes.user_name, identity_fakes.domain_name]), + '', + '', + identity_fakes.domain_name, + False + ),) + self.assertEqual(tuple(data), datalist1) + + def test_role_assignment_list_domain_role(self): + + self.role_assignments_mock.list.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ASSIGNMENT_WITH_DOMAIN_ROLE), + loaded=True, + ), + ] + + arglist = [ + '--role', identity_fakes.ROLE_2['name'], + '--role-domain', identity_fakes.domain_name + ] + verifylist = [ + ('user', None), + ('group', None), + ('domain', None), + ('project', None), + ('role', identity_fakes.ROLE_2['name']), + ('effective', False), + ('inherited', False), + ('names', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.role_assignments_mock.list.assert_called_with( + domain=None, + user=None, + group=None, + project=None, + role=self.roles_mock.get(), + effective=False, + os_inherit_extension_inherited_to=None, + include_names=False) + + self.assertEqual(self.columns, columns) + datalist = (( + identity_fakes.ROLE_2['id'], + identity_fakes.user_id, + '', + '', + identity_fakes.domain_id, + False + ),) + self.assertEqual(datalist, tuple(data)) diff --git a/openstackclient/tests/unit/identity/v3/test_service.py b/openstackclient/tests/unit/identity/v3/test_service.py new file mode 100644 index 00000000..4cba445b --- /dev/null +++ b/openstackclient/tests/unit/identity/v3/test_service.py @@ -0,0 +1,504 @@ +# Copyright 2013 Nebula 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. +# + +from keystoneclient import exceptions as identity_exc +from osc_lib import exceptions + +from openstackclient.identity.v3 import service +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes + + +class TestService(identity_fakes.TestIdentityv3): + + def setUp(self): + super(TestService, self).setUp() + + # Get a shortcut to the ServiceManager Mock + self.services_mock = self.app.client_manager.identity.services + self.services_mock.reset_mock() + + +class TestServiceCreate(TestService): + + columns = ( + 'description', + 'enabled', + 'id', + 'name', + 'type', + ) + + def setUp(self): + super(TestServiceCreate, self).setUp() + + self.service = identity_fakes.FakeService.create_one_service() + self.datalist = ( + self.service.description, + True, + self.service.id, + self.service.name, + self.service.type, + ) + self.services_mock.create.return_value = self.service + + # Get the command object to test + self.cmd = service.CreateService(self.app, None) + + def test_service_create_name(self): + arglist = [ + '--name', self.service.name, + self.service.type, + ] + verifylist = [ + ('name', self.service.name), + ('description', None), + ('enable', False), + ('disable', False), + ('type', self.service.type), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # ServiceManager.create(name=, type=, enabled=, **kwargs) + self.services_mock.create.assert_called_with( + name=self.service.name, + type=self.service.type, + description=None, + enabled=True, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_service_create_description(self): + arglist = [ + '--description', self.service.description, + self.service.type, + ] + verifylist = [ + ('name', None), + ('description', self.service.description), + ('enable', False), + ('disable', False), + ('type', self.service.type), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # ServiceManager.create(name=, type=, enabled=, **kwargs) + self.services_mock.create.assert_called_with( + name=None, + type=self.service.type, + description=self.service.description, + enabled=True, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_service_create_enable(self): + arglist = [ + '--enable', + self.service.type, + ] + verifylist = [ + ('name', None), + ('description', None), + ('enable', True), + ('disable', False), + ('type', self.service.type), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # ServiceManager.create(name=, type=, enabled=, **kwargs) + self.services_mock.create.assert_called_with( + name=None, + type=self.service.type, + description=None, + enabled=True, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_service_create_disable(self): + arglist = [ + '--disable', + self.service.type, + ] + verifylist = [ + ('name', None), + ('description', None), + ('enable', False), + ('disable', True), + ('type', self.service.type), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # ServiceManager.create(name=, type=, enabled=, **kwargs) + self.services_mock.create.assert_called_with( + name=None, + type=self.service.type, + description=None, + enabled=False, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + +class TestServiceDelete(TestService): + + service = identity_fakes.FakeService.create_one_service() + + def setUp(self): + super(TestServiceDelete, self).setUp() + + self.services_mock.get.side_effect = identity_exc.NotFound(None) + self.services_mock.find.return_value = self.service + self.services_mock.delete.return_value = None + + # Get the command object to test + self.cmd = service.DeleteService(self.app, None) + + def test_service_delete_no_options(self): + arglist = [ + self.service.name, + ] + verifylist = [ + ('service', [self.service.name]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.services_mock.delete.assert_called_with( + self.service.id, + ) + self.assertIsNone(result) + + +class TestServiceList(TestService): + + service = identity_fakes.FakeService.create_one_service() + + def setUp(self): + super(TestServiceList, self).setUp() + + self.services_mock.list.return_value = [self.service] + + # Get the command object to test + self.cmd = service.ListService(self.app, None) + + def test_service_list_no_options(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.services_mock.list.assert_called_with() + + collist = ('ID', 'Name', 'Type') + self.assertEqual(collist, columns) + datalist = (( + self.service.id, + self.service.name, + self.service.type, + ), ) + self.assertEqual(datalist, tuple(data)) + + def test_service_list_long(self): + arglist = [ + '--long', + ] + verifylist = [ + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.services_mock.list.assert_called_with() + + collist = ('ID', 'Name', 'Type', 'Description', 'Enabled') + self.assertEqual(collist, columns) + datalist = (( + self.service.id, + self.service.name, + self.service.type, + self.service.description, + True, + ), ) + self.assertEqual(datalist, tuple(data)) + + +class TestServiceSet(TestService): + + service = identity_fakes.FakeService.create_one_service() + + def setUp(self): + super(TestServiceSet, self).setUp() + + self.services_mock.get.side_effect = identity_exc.NotFound(None) + self.services_mock.find.return_value = self.service + self.services_mock.update.return_value = self.service + + # Get the command object to test + self.cmd = service.SetService(self.app, None) + + def test_service_set_no_options(self): + arglist = [ + self.service.name, + ] + verifylist = [ + ('type', None), + ('name', None), + ('description', None), + ('enable', False), + ('disable', False), + ('service', self.service.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + + def test_service_set_type(self): + arglist = [ + '--type', self.service.type, + self.service.name, + ] + verifylist = [ + ('type', self.service.type), + ('name', None), + ('description', None), + ('enable', False), + ('disable', False), + ('service', self.service.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'type': self.service.type, + } + # ServiceManager.update(service, name=, type=, enabled=, **kwargs) + self.services_mock.update.assert_called_with( + self.service.id, + **kwargs + ) + self.assertIsNone(result) + + def test_service_set_name(self): + arglist = [ + '--name', self.service.name, + self.service.name, + ] + verifylist = [ + ('type', None), + ('name', self.service.name), + ('description', None), + ('enable', False), + ('disable', False), + ('service', self.service.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.service.name, + } + # ServiceManager.update(service, name=, type=, enabled=, **kwargs) + self.services_mock.update.assert_called_with( + self.service.id, + **kwargs + ) + self.assertIsNone(result) + + def test_service_set_description(self): + arglist = [ + '--description', self.service.description, + self.service.name, + ] + verifylist = [ + ('type', None), + ('name', None), + ('description', self.service.description), + ('enable', False), + ('disable', False), + ('service', self.service.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'description': self.service.description, + } + # ServiceManager.update(service, name=, type=, enabled=, **kwargs) + self.services_mock.update.assert_called_with( + self.service.id, + **kwargs + ) + self.assertIsNone(result) + + def test_service_set_enable(self): + arglist = [ + '--enable', + self.service.name, + ] + verifylist = [ + ('type', None), + ('name', None), + ('description', None), + ('enable', True), + ('disable', False), + ('service', self.service.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': True, + } + # ServiceManager.update(service, name=, type=, enabled=, **kwargs) + self.services_mock.update.assert_called_with( + self.service.id, + **kwargs + ) + self.assertIsNone(result) + + def test_service_set_disable(self): + arglist = [ + '--disable', + self.service.name, + ] + verifylist = [ + ('type', None), + ('name', None), + ('description', None), + ('enable', False), + ('disable', True), + ('service', self.service.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': False, + } + # ServiceManager.update(service, name=, type=, enabled=, **kwargs) + self.services_mock.update.assert_called_with( + self.service.id, + **kwargs + ) + self.assertIsNone(result) + + +class TestServiceShow(TestService): + + service = identity_fakes.FakeService.create_one_service() + + def setUp(self): + super(TestServiceShow, self).setUp() + + self.services_mock.get.side_effect = identity_exc.NotFound(None) + self.services_mock.find.return_value = self.service + + # Get the command object to test + self.cmd = service.ShowService(self.app, None) + + def test_service_show(self): + arglist = [ + self.service.name, + ] + verifylist = [ + ('service', self.service.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # ServiceManager.get(id) + self.services_mock.find.assert_called_with( + name=self.service.name + ) + + collist = ('description', 'enabled', 'id', 'name', 'type') + self.assertEqual(collist, columns) + datalist = ( + self.service.description, + True, + self.service.id, + self.service.name, + self.service.type, + ) + self.assertEqual(datalist, data) + + def test_service_show_nounique(self): + self.services_mock.find.side_effect = identity_exc.NoUniqueMatch(None) + arglist = [ + 'nounique_service', + ] + verifylist = [ + ('service', 'nounique_service'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual( + "Multiple service matches found for 'nounique_service'," + " use an ID to be more specific.", str(e)) diff --git a/openstackclient/tests/unit/identity/v3/test_service_provider.py b/openstackclient/tests/unit/identity/v3/test_service_provider.py new file mode 100644 index 00000000..57473ef9 --- /dev/null +++ b/openstackclient/tests/unit/identity/v3/test_service_provider.py @@ -0,0 +1,408 @@ +# Copyright 2014 CERN. +# +# 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 copy + +from openstackclient.identity.v3 import service_provider +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit.identity.v3 import fakes as service_fakes + + +class TestServiceProvider(service_fakes.TestFederatedIdentity): + + def setUp(self): + super(TestServiceProvider, self).setUp() + + federation_lib = self.app.client_manager.identity.federation + self.service_providers_mock = federation_lib.service_providers + self.service_providers_mock.reset_mock() + + +class TestServiceProviderCreate(TestServiceProvider): + + columns = ( + 'auth_url', + 'description', + 'enabled', + 'id', + 'sp_url', + ) + datalist = ( + service_fakes.sp_auth_url, + service_fakes.sp_description, + True, + service_fakes.sp_id, + service_fakes.service_provider_url + ) + + def setUp(self): + super(TestServiceProviderCreate, self).setUp() + + copied_sp = copy.deepcopy(service_fakes.SERVICE_PROVIDER) + resource = fakes.FakeResource(None, copied_sp, loaded=True) + self.service_providers_mock.create.return_value = resource + self.cmd = service_provider.CreateServiceProvider(self.app, None) + + def test_create_service_provider_required_options_only(self): + arglist = [ + '--auth-url', service_fakes.sp_auth_url, + '--service-provider-url', service_fakes.service_provider_url, + service_fakes.sp_id, + ] + verifylist = [ + ('auth_url', service_fakes.sp_auth_url), + ('service_provider_url', service_fakes.service_provider_url), + ('service_provider_id', service_fakes.sp_id), + + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': True, + 'description': None, + 'auth_url': service_fakes.sp_auth_url, + 'sp_url': service_fakes.service_provider_url + } + + self.service_providers_mock.create.assert_called_with( + id=service_fakes.sp_id, + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_create_service_provider_description(self): + + arglist = [ + '--description', service_fakes.sp_description, + '--auth-url', service_fakes.sp_auth_url, + '--service-provider-url', service_fakes.service_provider_url, + service_fakes.sp_id, + ] + verifylist = [ + ('description', service_fakes.sp_description), + ('auth_url', service_fakes.sp_auth_url), + ('service_provider_url', service_fakes.service_provider_url), + ('service_provider_id', service_fakes.sp_id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'description': service_fakes.sp_description, + 'auth_url': service_fakes.sp_auth_url, + 'sp_url': service_fakes.service_provider_url, + 'enabled': True, + } + + self.service_providers_mock.create.assert_called_with( + id=service_fakes.sp_id, + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_create_service_provider_disabled(self): + + # Prepare FakeResource object + service_provider = copy.deepcopy(service_fakes.SERVICE_PROVIDER) + service_provider['enabled'] = False + service_provider['description'] = None + + resource = fakes.FakeResource(None, service_provider, loaded=True) + self.service_providers_mock.create.return_value = resource + + arglist = [ + '--auth-url', service_fakes.sp_auth_url, + '--service-provider-url', service_fakes.service_provider_url, + '--disable', + service_fakes.sp_id, + ] + verifylist = [ + ('auth_url', service_fakes.sp_auth_url), + ('service_provider_url', service_fakes.service_provider_url), + ('service_provider_id', service_fakes.sp_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + # Set expected values + kwargs = { + 'auth_url': service_fakes.sp_auth_url, + 'sp_url': service_fakes.service_provider_url, + 'enabled': False, + 'description': None, + } + + self.service_providers_mock.create.assert_called_with( + id=service_fakes.sp_id, + **kwargs + ) + self.assertEqual(self.columns, columns) + datalist = ( + service_fakes.sp_auth_url, + None, + False, + service_fakes.sp_id, + service_fakes.service_provider_url + ) + self.assertEqual(datalist, data) + + +class TestServiceProviderDelete(TestServiceProvider): + + def setUp(self): + super(TestServiceProviderDelete, self).setUp() + + # This is the return value for utils.find_resource() + self.service_providers_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(service_fakes.SERVICE_PROVIDER), + loaded=True, + ) + + self.service_providers_mock.delete.return_value = None + self.cmd = service_provider.DeleteServiceProvider(self.app, None) + + def test_delete_service_provider(self): + arglist = [ + service_fakes.sp_id, + ] + verifylist = [ + ('service_provider', [service_fakes.sp_id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.service_providers_mock.delete.assert_called_with( + service_fakes.sp_id, + ) + self.assertIsNone(result) + + +class TestServiceProviderList(TestServiceProvider): + + def setUp(self): + super(TestServiceProviderList, self).setUp() + + self.service_providers_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(service_fakes.SERVICE_PROVIDER), + loaded=True, + ) + self.service_providers_mock.list.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy(service_fakes.SERVICE_PROVIDER), + loaded=True, + ), + ] + + # Get the command object to test + self.cmd = service_provider.ListServiceProvider(self.app, None) + + def test_service_provider_list_no_options(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.service_providers_mock.list.assert_called_with() + + collist = ('ID', 'Enabled', 'Description', 'Auth URL') + self.assertEqual(collist, columns) + datalist = (( + service_fakes.sp_id, + True, + service_fakes.sp_description, + service_fakes.sp_auth_url + ), ) + self.assertEqual(tuple(data), datalist) + + +class TestServiceProviderSet(TestServiceProvider): + + columns = ( + 'auth_url', + 'description', + 'enabled', + 'id', + 'sp_url', + ) + datalist = ( + service_fakes.sp_auth_url, + service_fakes.sp_description, + False, + service_fakes.sp_id, + service_fakes.service_provider_url, + ) + + def setUp(self): + super(TestServiceProviderSet, self).setUp() + self.cmd = service_provider.SetServiceProvider(self.app, None) + + def test_service_provider_disable(self): + """Disable Service Provider + + Set Service Provider's ``enabled`` attribute to False. + """ + + def prepare(self): + """Prepare fake return objects before the test is executed""" + updated_sp = copy.deepcopy(service_fakes.SERVICE_PROVIDER) + updated_sp['enabled'] = False + resources = fakes.FakeResource( + None, + updated_sp, + loaded=True + ) + self.service_providers_mock.update.return_value = resources + + prepare(self) + arglist = [ + '--disable', service_fakes.sp_id, + ] + verifylist = [ + ('service_provider', service_fakes.sp_id), + ('enable', False), + ('disable', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + self.service_providers_mock.update.assert_called_with( + service_fakes.sp_id, + enabled=False, + description=None, + auth_url=None, + sp_url=None + ) + + def test_service_provider_enable(self): + """Enable Service Provider. + + Set Service Provider's ``enabled`` attribute to True. + """ + + def prepare(self): + """Prepare fake return objects before the test is executed""" + resources = fakes.FakeResource( + None, + copy.deepcopy(service_fakes.SERVICE_PROVIDER), + loaded=True + ) + self.service_providers_mock.update.return_value = resources + + prepare(self) + arglist = [ + '--enable', service_fakes.sp_id, + ] + verifylist = [ + ('service_provider', service_fakes.sp_id), + ('enable', True), + ('disable', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + self.service_providers_mock.update.assert_called_with( + service_fakes.sp_id, enabled=True, description=None, + auth_url=None, sp_url=None) + + def test_service_provider_no_options(self): + def prepare(self): + """Prepare fake return objects before the test is executed""" + resources = fakes.FakeResource( + None, + copy.deepcopy(service_fakes.SERVICE_PROVIDER), + loaded=True + ) + self.service_providers_mock.get.return_value = resources + + resources = fakes.FakeResource( + None, + copy.deepcopy(service_fakes.SERVICE_PROVIDER), + loaded=True, + ) + self.service_providers_mock.update.return_value = resources + + prepare(self) + arglist = [ + service_fakes.sp_id, + ] + verifylist = [ + ('service_provider', service_fakes.sp_id), + ('description', None), + ('enable', False), + ('disable', False), + ('auth_url', None), + ('service_provider_url', None), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + +class TestServiceProviderShow(TestServiceProvider): + + def setUp(self): + super(TestServiceProviderShow, self).setUp() + + ret = fakes.FakeResource( + None, + copy.deepcopy(service_fakes.SERVICE_PROVIDER), + loaded=True, + ) + self.service_providers_mock.get.side_effect = [Exception("Not found"), + ret] + self.service_providers_mock.get.return_value = ret + + # Get the command object to test + self.cmd = service_provider.ShowServiceProvider(self.app, None) + + def test_service_provider_show(self): + arglist = [ + service_fakes.sp_id, + ] + verifylist = [ + ('service_provider', service_fakes.sp_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.service_providers_mock.get.assert_called_with( + service_fakes.sp_id, + id='BETA' + ) + + collist = ('auth_url', 'description', 'enabled', 'id', 'sp_url') + self.assertEqual(collist, columns) + datalist = ( + service_fakes.sp_auth_url, + service_fakes.sp_description, + True, + service_fakes.sp_id, + service_fakes.service_provider_url + ) + self.assertEqual(data, datalist) diff --git a/openstackclient/tests/unit/identity/v3/test_token.py b/openstackclient/tests/unit/identity/v3/test_token.py new file mode 100644 index 00000000..7321909f --- /dev/null +++ b/openstackclient/tests/unit/identity/v3/test_token.py @@ -0,0 +1,138 @@ +# Copyright 2014 eBay 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 mock + +from openstackclient.identity.v3 import token +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes + + +class TestToken(identity_fakes.TestIdentityv3): + + def setUp(self): + super(TestToken, self).setUp() + + # Get a shortcut to the Auth Ref Mock + self.ar_mock = mock.PropertyMock() + type(self.app.client_manager).auth_ref = self.ar_mock + + +class TestTokenIssue(TestToken): + + def setUp(self): + super(TestTokenIssue, self).setUp() + + self.cmd = token.IssueToken(self.app, None) + + def test_token_issue_with_project_id(self): + auth_ref = identity_fakes.fake_auth_ref( + identity_fakes.TOKEN_WITH_PROJECT_ID, + ) + self.ar_mock = mock.PropertyMock(return_value=auth_ref) + type(self.app.client_manager).auth_ref = self.ar_mock + + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + collist = ('expires', 'id', 'project_id', 'user_id') + self.assertEqual(collist, columns) + datalist = ( + identity_fakes.token_expires, + identity_fakes.token_id, + identity_fakes.project_id, + identity_fakes.user_id, + ) + self.assertEqual(datalist, data) + + def test_token_issue_with_domain_id(self): + auth_ref = identity_fakes.fake_auth_ref( + identity_fakes.TOKEN_WITH_DOMAIN_ID, + ) + self.ar_mock = mock.PropertyMock(return_value=auth_ref) + type(self.app.client_manager).auth_ref = self.ar_mock + + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + collist = ('domain_id', 'expires', 'id', 'user_id') + self.assertEqual(collist, columns) + datalist = ( + identity_fakes.domain_id, + identity_fakes.token_expires, + identity_fakes.token_id, + identity_fakes.user_id, + ) + self.assertEqual(datalist, data) + + def test_token_issue_with_unscoped(self): + auth_ref = identity_fakes.fake_auth_ref( + identity_fakes.UNSCOPED_TOKEN, + ) + self.ar_mock = mock.PropertyMock(return_value=auth_ref) + type(self.app.client_manager).auth_ref = self.ar_mock + + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # DisplayCommandBase.take_action() returns two tuples + columns, data = self.cmd.take_action(parsed_args) + + collist = ( + 'expires', + 'id', + 'user_id', + ) + self.assertEqual(collist, columns) + datalist = ( + identity_fakes.token_expires, + identity_fakes.token_id, + identity_fakes.user_id, + ) + self.assertEqual(datalist, data) + + +class TestTokenRevoke(TestToken): + + TOKEN = 'fob' + + def setUp(self): + super(TestTokenRevoke, self).setUp() + self.tokens_mock = self.app.client_manager.identity.tokens + self.tokens_mock.reset_mock() + self.tokens_mock.revoke_token.return_value = True + self.cmd = token.RevokeToken(self.app, None) + + def test_token_revoke(self): + arglist = [self.TOKEN] + verifylist = [('token', self.TOKEN)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.tokens_mock.revoke_token.assert_called_with(self.TOKEN) + self.assertIsNone(result) diff --git a/openstackclient/tests/unit/identity/v3/test_trust.py b/openstackclient/tests/unit/identity/v3/test_trust.py new file mode 100644 index 00000000..4eeb8bfe --- /dev/null +++ b/openstackclient/tests/unit/identity/v3/test_trust.py @@ -0,0 +1,236 @@ +# 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 copy + +from openstackclient.identity.v3 import trust +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes + + +class TestTrust(identity_fakes.TestIdentityv3): + + def setUp(self): + super(TestTrust, self).setUp() + + self.trusts_mock = self.app.client_manager.identity.trusts + self.trusts_mock.reset_mock() + self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock.reset_mock() + self.users_mock = self.app.client_manager.identity.users + self.users_mock.reset_mock() + self.roles_mock = self.app.client_manager.identity.roles + self.roles_mock.reset_mock() + + +class TestTrustCreate(TestTrust): + + def setUp(self): + super(TestTrustCreate, self).setUp() + + self.projects_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.PROJECT), + loaded=True, + ) + + self.users_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.USER), + loaded=True, + ) + + self.roles_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.ROLE), + loaded=True, + ) + + self.trusts_mock.create.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.TRUST), + loaded=True, + ) + + # Get the command object to test + self.cmd = trust.CreateTrust(self.app, None) + + def test_trust_create_basic(self): + arglist = [ + '--project', identity_fakes.project_id, + '--role', identity_fakes.role_id, + identity_fakes.user_id, + identity_fakes.user_id + ] + verifylist = [ + ('project', identity_fakes.project_id), + ('impersonate', False), + ('role', [identity_fakes.role_id]), + ('trustor', identity_fakes.user_id), + ('trustee', identity_fakes.user_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'impersonation': False, + 'project': identity_fakes.project_id, + 'role_names': [identity_fakes.role_name], + 'expires_at': None, + } + # TrustManager.create(trustee_id, trustor_id, impersonation=, + # project=, role_names=, expires_at=) + self.trusts_mock.create.assert_called_with( + identity_fakes.user_id, + identity_fakes.user_id, + **kwargs + ) + + collist = ('expires_at', 'id', 'impersonation', 'project_id', + 'roles', 'trustee_user_id', 'trustor_user_id') + self.assertEqual(collist, columns) + datalist = ( + identity_fakes.trust_expires, + identity_fakes.trust_id, + identity_fakes.trust_impersonation, + identity_fakes.project_id, + identity_fakes.role_name, + identity_fakes.user_id, + identity_fakes.user_id + ) + self.assertEqual(datalist, data) + + +class TestTrustDelete(TestTrust): + + def setUp(self): + super(TestTrustDelete, self).setUp() + + # This is the return value for utils.find_resource() + self.trusts_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.TRUST), + loaded=True, + ) + self.trusts_mock.delete.return_value = None + + # Get the command object to test + self.cmd = trust.DeleteTrust(self.app, None) + + def test_trust_delete(self): + arglist = [ + identity_fakes.trust_id, + ] + verifylist = [ + ('trust', [identity_fakes.trust_id]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.trusts_mock.delete.assert_called_with( + identity_fakes.trust_id, + ) + self.assertIsNone(result) + + +class TestTrustList(TestTrust): + + def setUp(self): + super(TestTrustList, self).setUp() + + self.trusts_mock.list.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.TRUST), + loaded=True, + ), + ] + + # Get the command object to test + self.cmd = trust.ListTrust(self.app, None) + + def test_trust_list_no_options(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.trusts_mock.list.assert_called_with() + + collist = ('ID', 'Expires At', 'Impersonation', 'Project ID', + 'Trustee User ID', 'Trustor User ID') + self.assertEqual(collist, columns) + datalist = (( + identity_fakes.trust_id, + identity_fakes.trust_expires, + identity_fakes.trust_impersonation, + identity_fakes.project_id, + identity_fakes.user_id, + identity_fakes.user_id + ), ) + self.assertEqual(datalist, tuple(data)) + + +class TestTrustShow(TestTrust): + + def setUp(self): + super(TestTrustShow, self).setUp() + + self.trusts_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.TRUST), + loaded=True, + ) + + # Get the command object to test + self.cmd = trust.ShowTrust(self.app, None) + + def test_trust_show(self): + arglist = [ + identity_fakes.trust_id, + ] + verifylist = [ + ('trust', identity_fakes.trust_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + self.trusts_mock.get.assert_called_with(identity_fakes.trust_id) + + collist = ('expires_at', 'id', 'impersonation', 'project_id', + 'roles', 'trustee_user_id', 'trustor_user_id') + self.assertEqual(collist, columns) + datalist = ( + identity_fakes.trust_expires, + identity_fakes.trust_id, + identity_fakes.trust_impersonation, + identity_fakes.project_id, + identity_fakes.role_name, + identity_fakes.user_id, + identity_fakes.user_id + ) + self.assertEqual(datalist, data) diff --git a/openstackclient/tests/unit/identity/v3/test_unscoped_saml.py b/openstackclient/tests/unit/identity/v3/test_unscoped_saml.py new file mode 100644 index 00000000..9e4e1876 --- /dev/null +++ b/openstackclient/tests/unit/identity/v3/test_unscoped_saml.py @@ -0,0 +1,133 @@ +# 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 copy + +from osc_lib import exceptions + +from openstackclient.identity.v3 import unscoped_saml +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes + + +class TestUnscopedSAML(identity_fakes.TestFederatedIdentity): + + def setUp(self): + super(TestUnscopedSAML, self).setUp() + + federation_lib = self.app.client_manager.identity.federation + self.projects_mock = federation_lib.projects + self.projects_mock.reset_mock() + self.domains_mock = federation_lib.domains + self.domains_mock.reset_mock() + + +class TestDomainList(TestUnscopedSAML): + + def setUp(self): + super(TestDomainList, self).setUp() + + self.domains_mock.list.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.DOMAIN), + loaded=True, + ), + ] + + # Get the command object to test + self.cmd = unscoped_saml.ListAccessibleDomains(self.app, None) + + def test_accessible_domains_list(self): + self.app.client_manager.auth_plugin_name = 'v3unscopedsaml' + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.domains_mock.list.assert_called_with() + + collist = ('ID', 'Enabled', 'Name', 'Description') + self.assertEqual(collist, columns) + datalist = (( + identity_fakes.domain_id, + True, + identity_fakes.domain_name, + identity_fakes.domain_description, + ), ) + self.assertEqual(datalist, tuple(data)) + + def test_accessible_domains_list_wrong_auth(self): + auth = identity_fakes.FakeAuth("wrong auth") + self.app.client_manager.identity.session.auth = auth + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises(exceptions.CommandError, + self.cmd.take_action, + parsed_args) + + +class TestProjectList(TestUnscopedSAML): + + def setUp(self): + super(TestProjectList, self).setUp() + + self.projects_mock.list.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.PROJECT), + loaded=True, + ), + ] + + # Get the command object to test + self.cmd = unscoped_saml.ListAccessibleProjects(self.app, None) + + def test_accessible_projects_list(self): + self.app.client_manager.auth_plugin_name = 'v3unscopedsaml' + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.projects_mock.list.assert_called_with() + + collist = ('ID', 'Domain ID', 'Enabled', 'Name') + self.assertEqual(collist, columns) + datalist = (( + identity_fakes.project_id, + identity_fakes.domain_id, + True, + identity_fakes.project_name, + ), ) + self.assertEqual(datalist, tuple(data)) + + def test_accessible_projects_list_wrong_auth(self): + auth = identity_fakes.FakeAuth("wrong auth") + self.app.client_manager.identity.session.auth = auth + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises(exceptions.CommandError, + self.cmd.take_action, + parsed_args) diff --git a/openstackclient/tests/unit/identity/v3/test_user.py b/openstackclient/tests/unit/identity/v3/test_user.py new file mode 100644 index 00000000..6150a5f3 --- /dev/null +++ b/openstackclient/tests/unit/identity/v3/test_user.py @@ -0,0 +1,1063 @@ +# Copyright 2013 Nebula 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 contextlib +import mock + +from openstackclient.identity.v3 import user +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes + + +class TestUser(identity_fakes.TestIdentityv3): + + def setUp(self): + super(TestUser, self).setUp() + + # Get a shortcut to the DomainManager Mock + self.domains_mock = self.app.client_manager.identity.domains + self.domains_mock.reset_mock() + + # Get a shortcut to the ProjectManager Mock + self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock.reset_mock() + + # Get a shortcut to the GroupManager Mock + self.groups_mock = self.app.client_manager.identity.groups + self.groups_mock.reset_mock() + + # Get a shortcut to the UserManager Mock + self.users_mock = self.app.client_manager.identity.users + self.users_mock.reset_mock() + + # Shortcut for RoleAssignmentManager Mock + self.role_assignments_mock = self.app.client_manager.identity.\ + role_assignments + self.role_assignments_mock.reset_mock() + + +class TestUserCreate(TestUser): + + domain = identity_fakes.FakeDomain.create_one_domain() + project = identity_fakes.FakeProject.create_one_project() + + columns = ( + 'default_project_id', + 'domain_id', + 'email', + 'enabled', + 'id', + 'name', + ) + + def setUp(self): + super(TestUserCreate, self).setUp() + + self.user = identity_fakes.FakeUser.create_one_user( + attrs={'domain_id': self.domain.id, + 'default_project_id': self.project.id} + ) + self.datalist = ( + self.project.id, + self.domain.id, + self.user.email, + True, + self.user.id, + self.user.name, + ) + + self.domains_mock.get.return_value = self.domain + self.projects_mock.get.return_value = self.project + self.users_mock.create.return_value = self.user + + # Get the command object to test + self.cmd = user.CreateUser(self.app, None) + + def test_user_create_no_options(self): + arglist = [ + self.user.name, + ] + verifylist = [ + ('enable', False), + ('disable', False), + ('name', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.user.name, + 'default_project': None, + 'description': None, + 'domain': None, + 'email': None, + 'enabled': True, + 'password': None, + } + + # UserManager.create(name=, domain=, project=, password=, email=, + # description=, enabled=, default_project=) + self.users_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_user_create_password(self): + arglist = [ + '--password', 'secret', + self.user.name, + ] + verifylist = [ + ('password', 'secret'), + ('password_prompt', False), + ('enable', False), + ('disable', False), + ('name', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.user.name, + 'default_project': None, + 'description': None, + 'domain': None, + 'email': None, + 'enabled': True, + 'password': 'secret', + } + # UserManager.create(name=, domain=, project=, password=, email=, + # description=, enabled=, default_project=) + self.users_mock.create.assert_called_with( + **kwargs + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_user_create_password_prompt(self): + arglist = [ + '--password-prompt', + self.user.name, + ] + verifylist = [ + ('password', None), + ('password_prompt', True), + ('enable', False), + ('disable', False), + ('name', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + mocker = mock.Mock() + mocker.return_value = 'abc123' + with mock.patch("osc_lib.utils.get_password", mocker): + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.user.name, + 'default_project': None, + 'description': None, + 'domain': None, + 'email': None, + 'enabled': True, + 'password': 'abc123', + } + # UserManager.create(name=, domain=, project=, password=, email=, + # description=, enabled=, default_project=) + self.users_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_user_create_email(self): + arglist = [ + '--email', 'barney@example.com', + self.user.name, + ] + verifylist = [ + ('email', 'barney@example.com'), + ('enable', False), + ('disable', False), + ('name', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.user.name, + 'default_project': None, + 'description': None, + 'domain': None, + 'email': 'barney@example.com', + 'enabled': True, + 'password': None, + } + # UserManager.create(name=, domain=, project=, password=, email=, + # description=, enabled=, default_project=) + self.users_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_user_create_project(self): + arglist = [ + '--project', self.project.name, + self.user.name, + ] + verifylist = [ + ('project', self.project.name), + ('enable', False), + ('disable', False), + ('name', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.user.name, + 'default_project': self.project.id, + 'description': None, + 'domain': None, + 'email': None, + 'enabled': True, + 'password': None, + } + # UserManager.create(name=, domain=, project=, password=, email=, + # description=, enabled=, default_project=) + self.users_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + datalist = ( + self.project.id, + self.domain.id, + self.user.email, + True, + self.user.id, + self.user.name, + ) + self.assertEqual(datalist, data) + + def test_user_create_project_domain(self): + arglist = [ + '--project', self.project.name, + '--project-domain', self.project.domain_id, + self.user.name, + ] + verifylist = [ + ('project', self.project.name), + ('project_domain', self.project.domain_id), + ('enable', False), + ('disable', False), + ('name', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.user.name, + 'default_project': self.project.id, + 'description': None, + 'domain': None, + 'email': None, + 'enabled': True, + 'password': None, + } + # UserManager.create(name=, domain=, project=, password=, email=, + # description=, enabled=, default_project=) + self.users_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + datalist = ( + self.project.id, + self.domain.id, + self.user.email, + True, + self.user.id, + self.user.name, + ) + self.assertEqual(datalist, data) + + def test_user_create_domain(self): + arglist = [ + '--domain', self.domain.name, + self.user.name, + ] + verifylist = [ + ('domain', self.domain.name), + ('enable', False), + ('disable', False), + ('name', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.user.name, + 'default_project': None, + 'description': None, + 'domain': self.domain.id, + 'email': None, + 'enabled': True, + 'password': None, + } + # UserManager.create(name=, domain=, project=, password=, email=, + # description=, enabled=, default_project=) + self.users_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_user_create_enable(self): + arglist = [ + '--enable', + self.user.name, + ] + verifylist = [ + ('enable', True), + ('disable', False), + ('name', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.user.name, + 'default_project': None, + 'description': None, + 'domain': None, + 'email': None, + 'enabled': True, + 'password': None, + } + # UserManager.create(name=, domain=, project=, password=, email=, + # description=, enabled=, default_project=) + self.users_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_user_create_disable(self): + arglist = [ + '--disable', + self.user.name, + ] + verifylist = [ + ('name', self.user.name), + ('enable', False), + ('disable', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.user.name, + 'default_project': None, + 'description': None, + 'domain': None, + 'email': None, + 'enabled': False, + 'password': None, + } + # users.create(name=, password, email, tenant_id=None, enabled=True) + self.users_mock.create.assert_called_with( + **kwargs + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + +class TestUserDelete(TestUser): + + user = identity_fakes.FakeUser.create_one_user() + + def setUp(self): + super(TestUserDelete, self).setUp() + + # This is the return value for utils.find_resource() + self.users_mock.get.return_value = self.user + self.users_mock.delete.return_value = None + + # Get the command object to test + self.cmd = user.DeleteUser(self.app, None) + + def test_user_delete_no_options(self): + arglist = [ + self.user.id, + ] + verifylist = [ + ('users', [self.user.id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.users_mock.delete.assert_called_with( + self.user.id, + ) + self.assertIsNone(result) + + +class TestUserList(TestUser): + + domain = identity_fakes.FakeDomain.create_one_domain() + project = identity_fakes.FakeProject.create_one_project() + user = identity_fakes.FakeUser.create_one_user( + attrs={'domain_id': domain.id, + 'default_project_id': project.id} + ) + group = identity_fakes.FakeGroup.create_one_group() + role_assignment = ( + identity_fakes.FakeRoleAssignment.create_one_role_assignment( + attrs={'user': {'id': user.id}})) + + columns = [ + 'ID', + 'Name' + ] + datalist = ( + ( + user.id, + user.name, + ), + ) + + def setUp(self): + super(TestUserList, self).setUp() + + self.users_mock.get.return_value = self.user + self.users_mock.list.return_value = [self.user] + self.domains_mock.get.return_value = self.domain + self.groups_mock.get.return_value = self.group + self.projects_mock.get.return_value = self.project + self.role_assignments_mock.list.return_value = [self.role_assignment] + + # Get the command object to test + self.cmd = user.ListUser(self.app, None) + + def test_user_list_no_options(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'domain': None, + 'group': None, + } + + self.users_mock.list.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_user_list_domain(self): + arglist = [ + '--domain', self.domain.id, + ] + verifylist = [ + ('domain', self.domain.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'domain': self.domain.id, + 'group': None, + } + + self.users_mock.list.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_user_list_group(self): + arglist = [ + '--group', self.group.name, + ] + verifylist = [ + ('group', self.group.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'domain': None, + 'group': self.group.id, + } + + self.users_mock.list.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_user_list_long(self): + arglist = [ + '--long', + ] + verifylist = [ + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'domain': None, + 'group': None, + } + + self.users_mock.list.assert_called_with( + **kwargs + ) + + collist = [ + 'ID', + 'Name', + 'Project', + 'Domain', + 'Description', + 'Email', + 'Enabled', + ] + self.assertEqual(collist, columns) + datalist = ( + ( + self.user.id, + self.user.name, + self.project.id, + self.domain.id, + '', + self.user.email, + True, + ), + ) + self.assertEqual(datalist, tuple(data)) + + def test_user_list_project(self): + arglist = [ + '--project', self.project.name, + ] + verifylist = [ + ('project', self.project.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + kwargs = { + 'project': self.project.id, + } + + self.role_assignments_mock.list.assert_called_with(**kwargs) + self.users_mock.get.assert_called_with(self.user.id) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + +class TestUserSet(TestUser): + + project = identity_fakes.FakeProject.create_one_project() + user = identity_fakes.FakeUser.create_one_user( + attrs={'default_project_id': project.id} + ) + + def setUp(self): + super(TestUserSet, self).setUp() + + self.projects_mock.get.return_value = self.project + self.users_mock.get.return_value = self.user + self.users_mock.update.return_value = self.user + + # Get the command object to test + self.cmd = user.SetUser(self.app, None) + + def test_user_set_no_options(self): + arglist = [ + self.user.name, + ] + verifylist = [ + ('name', None), + ('password', None), + ('email', None), + ('project', None), + ('enable', False), + ('disable', False), + ('user', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + + def test_user_set_name(self): + arglist = [ + '--name', 'qwerty', + self.user.name, + ] + verifylist = [ + ('name', 'qwerty'), + ('password', None), + ('email', None), + ('project', None), + ('enable', False), + ('disable', False), + ('user', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': True, + 'name': 'qwerty', + } + # UserManager.update(user, name=, domain=, project=, password=, + # email=, description=, enabled=, default_project=) + self.users_mock.update.assert_called_with( + self.user.id, + **kwargs + ) + self.assertIsNone(result) + + def test_user_set_password(self): + arglist = [ + '--password', 'secret', + self.user.name, + ] + verifylist = [ + ('name', None), + ('password', 'secret'), + ('password_prompt', False), + ('email', None), + ('project', None), + ('enable', False), + ('disable', False), + ('user', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': True, + 'password': 'secret', + } + # UserManager.update(user, name=, domain=, project=, password=, + # email=, description=, enabled=, default_project=) + self.users_mock.update.assert_called_with( + self.user.id, + **kwargs + ) + self.assertIsNone(result) + + def test_user_set_password_prompt(self): + arglist = [ + '--password-prompt', + self.user.name, + ] + verifylist = [ + ('name', None), + ('password', None), + ('password_prompt', True), + ('email', None), + ('project', None), + ('enable', False), + ('disable', False), + ('user', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + mocker = mock.Mock() + mocker.return_value = 'abc123' + with mock.patch("osc_lib.utils.get_password", mocker): + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': True, + 'password': 'abc123', + } + # UserManager.update(user, name=, domain=, project=, password=, + # email=, description=, enabled=, default_project=) + self.users_mock.update.assert_called_with( + self.user.id, + **kwargs + ) + self.assertIsNone(result) + + def test_user_set_email(self): + arglist = [ + '--email', 'barney@example.com', + self.user.name, + ] + verifylist = [ + ('name', None), + ('password', None), + ('email', 'barney@example.com'), + ('project', None), + ('enable', False), + ('disable', False), + ('user', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': True, + 'email': 'barney@example.com', + } + # UserManager.update(user, name=, domain=, project=, password=, + # email=, description=, enabled=, default_project=) + self.users_mock.update.assert_called_with( + self.user.id, + **kwargs + ) + self.assertIsNone(result) + + def test_user_set_project(self): + arglist = [ + '--project', self.project.id, + self.user.name, + ] + verifylist = [ + ('name', None), + ('password', None), + ('email', None), + ('project', self.project.id), + ('enable', False), + ('disable', False), + ('user', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': True, + 'default_project': self.project.id, + } + # UserManager.update(user, name=, domain=, project=, password=, + # email=, description=, enabled=, default_project=) + self.users_mock.update.assert_called_with( + self.user.id, + **kwargs + ) + self.assertIsNone(result) + + def test_user_set_project_domain(self): + arglist = [ + '--project', self.project.id, + '--project-domain', self.project.domain_id, + self.user.name, + ] + verifylist = [ + ('name', None), + ('password', None), + ('email', None), + ('project', self.project.id), + ('project_domain', self.project.domain_id), + ('enable', False), + ('disable', False), + ('user', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': True, + 'default_project': self.project.id, + } + # UserManager.update(user, name=, domain=, project=, password=, + # email=, description=, enabled=, default_project=) + self.users_mock.update.assert_called_with( + self.user.id, + **kwargs + ) + self.assertIsNone(result) + + def test_user_set_enable(self): + arglist = [ + '--enable', + self.user.name, + ] + verifylist = [ + ('name', None), + ('password', None), + ('email', None), + ('project', None), + ('enable', True), + ('disable', False), + ('user', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': True, + } + # UserManager.update(user, name=, domain=, project=, password=, + # email=, description=, enabled=, default_project=) + self.users_mock.update.assert_called_with( + self.user.id, + **kwargs + ) + self.assertIsNone(result) + + def test_user_set_disable(self): + arglist = [ + '--disable', + self.user.name, + ] + verifylist = [ + ('name', None), + ('password', None), + ('email', None), + ('project', None), + ('enable', False), + ('disable', True), + ('user', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'enabled': False, + } + # UserManager.update(user, name=, domain=, project=, password=, + # email=, description=, enabled=, default_project=) + self.users_mock.update.assert_called_with( + self.user.id, + **kwargs + ) + self.assertIsNone(result) + + +class TestUserSetPassword(TestUser): + + def setUp(self): + super(TestUserSetPassword, self).setUp() + self.cmd = user.SetPasswordUser(self.app, None) + + @staticmethod + @contextlib.contextmanager + def _mock_get_password(*passwords): + mocker = mock.Mock(side_effect=passwords) + with mock.patch("osc_lib.utils.get_password", mocker): + yield + + def test_user_password_change(self): + current_pass = 'old_pass' + new_pass = 'new_pass' + arglist = [ + '--password', new_pass, + ] + verifylist = [ + ('password', new_pass), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # Mock getting user current password. + with self._mock_get_password(current_pass): + result = self.cmd.take_action(parsed_args) + + self.users_mock.update_password.assert_called_with( + current_pass, new_pass + ) + self.assertIsNone(result) + + def test_user_create_password_prompt(self): + current_pass = 'old_pass' + new_pass = 'new_pass' + parsed_args = self.check_parser(self.cmd, [], []) + + # Mock getting user current and new password. + with self._mock_get_password(current_pass, new_pass): + result = self.cmd.take_action(parsed_args) + + self.users_mock.update_password.assert_called_with( + current_pass, new_pass + ) + self.assertIsNone(result) + + def test_user_password_change_no_prompt(self): + current_pass = 'old_pass' + new_pass = 'new_pass' + arglist = [ + '--password', new_pass, + '--original-password', current_pass, + ] + verifylist = [ + ('password', new_pass), + ('original_password', current_pass), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.users_mock.update_password.assert_called_with( + current_pass, new_pass + ) + self.assertIsNone(result) + + +class TestUserShow(TestUser): + + user = identity_fakes.FakeUser.create_one_user() + + def setUp(self): + super(TestUserShow, self).setUp() + + self.users_mock.get.return_value = self.user + + # Get the command object to test + self.cmd = user.ShowUser(self.app, None) + self.app.client_manager.identity.auth.client.get_user_id.\ + return_value = self.user.id + self.app.client_manager.identity.tokens.get_token_data.return_value = \ + {'token': + {'user': + {'domain': {}, + 'id': self.user.id, + 'name': self.user.name, + } + } + } + + def test_user_show(self): + arglist = [ + self.user.id, + ] + verifylist = [ + ('user', self.user.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + self.users_mock.get.assert_called_with(self.user.id) + + collist = ('default_project_id', 'domain_id', 'email', + 'enabled', 'id', 'name') + self.assertEqual(collist, columns) + datalist = ( + self.user.default_project_id, + self.user.domain_id, + self.user.email, + True, + self.user.id, + self.user.name, + ) + self.assertEqual(datalist, data) diff --git a/openstackclient/tests/unit/image/__init__.py b/openstackclient/tests/unit/image/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/openstackclient/tests/unit/image/__init__.py diff --git a/openstackclient/tests/unit/image/v1/__init__.py b/openstackclient/tests/unit/image/v1/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/openstackclient/tests/unit/image/v1/__init__.py diff --git a/openstackclient/tests/unit/image/v1/fakes.py b/openstackclient/tests/unit/image/v1/fakes.py new file mode 100644 index 00000000..a8e52fa3 --- /dev/null +++ b/openstackclient/tests/unit/image/v1/fakes.py @@ -0,0 +1,76 @@ +# Copyright 2013 Nebula 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 mock + +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit import utils +from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes + + +image_id = 'im1' +image_name = 'graven' +image_owner = 'baal' +image_protected = False +image_public = True +image_properties = { + 'Alpha': 'a', + 'Beta': 'b', + 'Gamma': 'g', +} +image_properties_str = "Alpha='a', Beta='b', Gamma='g'" +image_data = 'line 1\nline 2\n' + +IMAGE = { + 'id': image_id, + 'name': image_name, + 'container_format': '', + 'disk_format': '', + 'owner': image_owner, + 'min_disk': 0, + 'min_ram': 0, + 'is_public': image_public, + 'protected': image_protected, + 'properties': image_properties, +} + +IMAGE_columns = tuple(sorted(IMAGE)) +IMAGE_output = dict(IMAGE) +IMAGE_output['properties'] = image_properties_str +IMAGE_data = tuple((IMAGE_output[x] for x in sorted(IMAGE_output))) + + +class FakeImagev1Client(object): + + def __init__(self, **kwargs): + self.images = mock.Mock() + self.images.resource_class = fakes.FakeResource(None, {}) + self.auth_token = kwargs['token'] + self.management_url = kwargs['endpoint'] + + +class TestImagev1(utils.TestCommand): + + def setUp(self): + super(TestImagev1, self).setUp() + + self.app.client_manager.image = FakeImagev1Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + self.app.client_manager.volume = volume_fakes.FakeVolumev1Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) diff --git a/openstackclient/tests/unit/image/v1/test_image.py b/openstackclient/tests/unit/image/v1/test_image.py new file mode 100644 index 00000000..c62c1ff9 --- /dev/null +++ b/openstackclient/tests/unit/image/v1/test_image.py @@ -0,0 +1,701 @@ +# Copyright 2013 Nebula 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 copy +import mock + +from osc_lib import exceptions + +from openstackclient.image.v1 import image +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit.image.v1 import fakes as image_fakes + + +class TestImage(image_fakes.TestImagev1): + + def setUp(self): + super(TestImage, self).setUp() + + # Get a shortcut to the ServerManager Mock + self.images_mock = self.app.client_manager.image.images + self.images_mock.reset_mock() + + +class TestImageCreate(TestImage): + + def setUp(self): + super(TestImageCreate, self).setUp() + + self.images_mock.create.return_value = fakes.FakeResource( + None, + copy.deepcopy(image_fakes.IMAGE), + loaded=True, + ) + # This is the return value for utils.find_resource() + self.images_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(image_fakes.IMAGE), + loaded=True, + ) + self.images_mock.update.return_value = fakes.FakeResource( + None, + copy.deepcopy(image_fakes.IMAGE), + loaded=True, + ) + + # Get the command object to test + self.cmd = image.CreateImage(self.app, None) + + def test_image_reserve_no_options(self): + mock_exception = { + 'find.side_effect': exceptions.CommandError('x'), + 'get.side_effect': exceptions.CommandError('x'), + } + self.images_mock.configure_mock(**mock_exception) + arglist = [ + image_fakes.image_name, + ] + verifylist = [ + ('container_format', image.DEFAULT_CONTAINER_FORMAT), + ('disk_format', image.DEFAULT_DISK_FORMAT), + ('name', image_fakes.image_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # ImageManager.create(name=, **) + self.images_mock.create.assert_called_with( + name=image_fakes.image_name, + container_format=image.DEFAULT_CONTAINER_FORMAT, + disk_format=image.DEFAULT_DISK_FORMAT, + data=mock.ANY, + ) + + # Verify update() was not called, if it was show the args + self.assertEqual(self.images_mock.update.call_args_list, []) + + self.assertEqual(image_fakes.IMAGE_columns, columns) + self.assertEqual(image_fakes.IMAGE_data, data) + + def test_image_reserve_options(self): + mock_exception = { + 'find.side_effect': exceptions.CommandError('x'), + 'get.side_effect': exceptions.CommandError('x'), + } + self.images_mock.configure_mock(**mock_exception) + arglist = [ + '--container-format', 'ovf', + '--disk-format', 'fs', + '--min-disk', '10', + '--min-ram', '4', + '--protected', + '--private', + '--project', 'q', + image_fakes.image_name, + ] + verifylist = [ + ('container_format', 'ovf'), + ('disk_format', 'fs'), + ('min_disk', 10), + ('min_ram', 4), + ('protected', True), + ('unprotected', False), + ('public', False), + ('private', True), + ('project', 'q'), + ('name', image_fakes.image_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # ImageManager.create(name=, **) + self.images_mock.create.assert_called_with( + name=image_fakes.image_name, + container_format='ovf', + disk_format='fs', + min_disk=10, + min_ram=4, + protected=True, + is_public=False, + owner='q', + data=mock.ANY, + ) + + # Verify update() was not called, if it was show the args + self.assertEqual(self.images_mock.update.call_args_list, []) + + self.assertEqual(image_fakes.IMAGE_columns, columns) + self.assertEqual(image_fakes.IMAGE_data, data) + + @mock.patch('openstackclient.image.v1.image.io.open', name='Open') + def test_image_create_file(self, mock_open): + mock_file = mock.MagicMock(name='File') + mock_open.return_value = mock_file + mock_open.read.return_value = image_fakes.image_data + mock_exception = { + 'find.side_effect': exceptions.CommandError('x'), + 'get.side_effect': exceptions.CommandError('x'), + } + self.images_mock.configure_mock(**mock_exception) + + arglist = [ + '--file', 'filer', + '--unprotected', + '--public', + '--property', 'Alpha=1', + '--property', 'Beta=2', + image_fakes.image_name, + ] + verifylist = [ + ('file', 'filer'), + ('protected', False), + ('unprotected', True), + ('public', True), + ('private', False), + ('properties', {'Alpha': '1', 'Beta': '2'}), + ('name', image_fakes.image_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Ensure input file is opened + mock_open.assert_called_with('filer', 'rb') + + # Ensure the input file is closed + mock_file.close.assert_called_with() + + # ImageManager.get(name) not to be called since update action exists + self.images_mock.get.assert_not_called() + + # ImageManager.create(name=, **) + self.images_mock.create.assert_called_with( + name=image_fakes.image_name, + container_format=image.DEFAULT_CONTAINER_FORMAT, + disk_format=image.DEFAULT_DISK_FORMAT, + protected=False, + is_public=True, + properties={ + 'Alpha': '1', + 'Beta': '2', + }, + data=mock_file, + ) + + # Verify update() was not called, if it was show the args + self.assertEqual(self.images_mock.update.call_args_list, []) + + self.assertEqual(image_fakes.IMAGE_columns, columns) + self.assertEqual(image_fakes.IMAGE_data, data) + + +class TestImageDelete(TestImage): + + def setUp(self): + super(TestImageDelete, self).setUp() + + # This is the return value for utils.find_resource() + self.images_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(image_fakes.IMAGE), + loaded=True, + ) + self.images_mock.delete.return_value = None + + # Get the command object to test + self.cmd = image.DeleteImage(self.app, None) + + def test_image_delete_no_options(self): + arglist = [ + image_fakes.image_id, + ] + verifylist = [ + ('images', [image_fakes.image_id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.images_mock.delete.assert_called_with(image_fakes.image_id) + self.assertIsNone(result) + + +class TestImageList(TestImage): + + columns = ( + 'ID', + 'Name', + 'Status', + ) + datalist = ( + ( + image_fakes.image_id, + image_fakes.image_name, + '', + ), + ) + + def setUp(self): + super(TestImageList, self).setUp() + + self.api_mock = mock.Mock() + self.api_mock.image_list.side_effect = [ + [copy.deepcopy(image_fakes.IMAGE)], [], + ] + self.app.client_manager.image.api = self.api_mock + + # Get the command object to test + self.cmd = image.ListImage(self.app, None) + + def test_image_list_no_options(self): + arglist = [] + verifylist = [ + ('public', False), + ('private', False), + ('long', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + self.api_mock.image_list.assert_called_with( + detailed=True, + marker=image_fakes.image_id, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_image_list_public_option(self): + arglist = [ + '--public', + ] + verifylist = [ + ('public', True), + ('private', False), + ('long', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + self.api_mock.image_list.assert_called_with( + detailed=True, + public=True, + marker=image_fakes.image_id, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_image_list_private_option(self): + arglist = [ + '--private', + ] + verifylist = [ + ('public', False), + ('private', True), + ('long', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + self.api_mock.image_list.assert_called_with( + detailed=True, + private=True, + marker=image_fakes.image_id, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_image_list_long_option(self): + arglist = [ + '--long', + ] + verifylist = [ + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + self.api_mock.image_list.assert_called_with( + detailed=True, + marker=image_fakes.image_id, + ) + + collist = ( + 'ID', + 'Name', + 'Disk Format', + 'Container Format', + 'Size', + 'Checksum', + 'Status', + 'Visibility', + 'Protected', + 'Project', + 'Properties', + ) + + self.assertEqual(collist, columns) + datalist = (( + image_fakes.image_id, + image_fakes.image_name, + '', + '', + '', + '', + '', + 'public', + False, + image_fakes.image_owner, + "Alpha='a', Beta='b', Gamma='g'", + ), ) + self.assertEqual(datalist, tuple(data)) + + @mock.patch('openstackclient.api.utils.simple_filter') + def test_image_list_property_option(self, sf_mock): + sf_mock.side_effect = [ + [copy.deepcopy(image_fakes.IMAGE)], [], + ] + + arglist = [ + '--property', 'a=1', + ] + verifylist = [ + ('property', {'a': '1'}), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + self.api_mock.image_list.assert_called_with( + detailed=True, + marker=image_fakes.image_id, + ) + sf_mock.assert_called_with( + [image_fakes.IMAGE], + attr='a', + value='1', + property_field='properties', + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + @mock.patch('osc_lib.utils.sort_items') + def test_image_list_sort_option(self, si_mock): + si_mock.side_effect = [ + [copy.deepcopy(image_fakes.IMAGE)], [], + ] + + arglist = ['--sort', 'name:asc'] + verifylist = [('sort', 'name:asc')] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + self.api_mock.image_list.assert_called_with( + detailed=True, + marker=image_fakes.image_id, + ) + si_mock.assert_called_with( + [image_fakes.IMAGE], + 'name:asc' + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + +class TestImageSet(TestImage): + + def setUp(self): + super(TestImageSet, self).setUp() + + # This is the return value for utils.find_resource() + self.images_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(image_fakes.IMAGE), + loaded=True, + ) + self.images_mock.update.return_value = fakes.FakeResource( + None, + copy.deepcopy(image_fakes.IMAGE), + loaded=True, + ) + + # Get the command object to test + self.cmd = image.SetImage(self.app, None) + + def test_image_set_no_options(self): + arglist = [ + image_fakes.image_name, + ] + verifylist = [ + ('image', image_fakes.image_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.images_mock.update.assert_called_with(image_fakes.image_id, + **{}) + self.assertIsNone(result) + + def test_image_set_options(self): + arglist = [ + '--name', 'new-name', + '--min-disk', '2', + '--min-ram', '4', + '--container-format', 'ovf', + '--disk-format', 'vmdk', + '--size', '35165824', + '--project', 'new-owner', + image_fakes.image_name, + ] + verifylist = [ + ('name', 'new-name'), + ('min_disk', 2), + ('min_ram', 4), + ('container_format', 'ovf'), + ('disk_format', 'vmdk'), + ('size', 35165824), + ('project', 'new-owner'), + ('image', image_fakes.image_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + kwargs = { + 'name': 'new-name', + 'owner': 'new-owner', + 'min_disk': 2, + 'min_ram': 4, + 'container_format': 'ovf', + 'disk_format': 'vmdk', + 'size': 35165824 + } + # ImageManager.update(image, **kwargs) + self.images_mock.update.assert_called_with( + image_fakes.image_id, + **kwargs + ) + self.assertIsNone(result) + + def test_image_set_bools1(self): + arglist = [ + '--protected', + '--private', + image_fakes.image_name, + ] + verifylist = [ + ('protected', True), + ('unprotected', False), + ('public', False), + ('private', True), + ('image', image_fakes.image_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + kwargs = { + 'protected': True, + 'is_public': False, + } + # ImageManager.update(image, **kwargs) + self.images_mock.update.assert_called_with( + image_fakes.image_id, + **kwargs + ) + self.assertIsNone(result) + + def test_image_set_bools2(self): + arglist = [ + '--unprotected', + '--public', + image_fakes.image_name, + ] + verifylist = [ + ('protected', False), + ('unprotected', True), + ('public', True), + ('private', False), + ('image', image_fakes.image_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + kwargs = { + 'protected': False, + 'is_public': True, + } + # ImageManager.update(image, **kwargs) + self.images_mock.update.assert_called_with( + image_fakes.image_id, + **kwargs + ) + self.assertIsNone(result) + + def test_image_set_properties(self): + arglist = [ + '--property', 'Alpha=1', + '--property', 'Beta=2', + image_fakes.image_name, + ] + verifylist = [ + ('properties', {'Alpha': '1', 'Beta': '2'}), + ('image', image_fakes.image_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + kwargs = { + 'properties': { + 'Alpha': '1', + 'Beta': '2', + 'Gamma': 'g', + }, + } + # ImageManager.update(image, **kwargs) + self.images_mock.update.assert_called_with( + image_fakes.image_id, + **kwargs + ) + self.assertIsNone(result) + + def test_image_update_volume(self): + # Set up VolumeManager Mock + volumes_mock = self.app.client_manager.volume.volumes + volumes_mock.reset_mock() + volumes_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy({'id': 'vol1', 'name': 'volly'}), + loaded=True, + ) + response = { + "id": 'volume_id', + "updated_at": 'updated_at', + "status": 'uploading', + "display_description": 'desc', + "size": 'size', + "volume_type": 'volume_type', + "container_format": image.DEFAULT_CONTAINER_FORMAT, + "disk_format": image.DEFAULT_DISK_FORMAT, + "image": image_fakes.image_name, + } + full_response = {"os-volume_upload_image": response} + volumes_mock.upload_to_image.return_value = (201, full_response) + + arglist = [ + '--volume', 'volly', + '--name', 'updated_image', + image_fakes.image_name, + ] + verifylist = [ + ('private', False), + ('protected', False), + ('public', False), + ('unprotected', False), + ('volume', 'volly'), + ('force', False), + ('name', 'updated_image'), + ('image', image_fakes.image_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # VolumeManager.upload_to_image(volume, force, image_name, + # container_format, disk_format) + volumes_mock.upload_to_image.assert_called_with( + 'vol1', + False, + image_fakes.image_name, + '', + '', + ) + # ImageManager.update(image_id, remove_props=, **) + self.images_mock.update.assert_called_with( + image_fakes.image_id, + name='updated_image', + volume='volly', + ) + self.assertIsNone(result) + + +class TestImageShow(TestImage): + + def setUp(self): + super(TestImageShow, self).setUp() + + self.images_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(image_fakes.IMAGE), + loaded=True, + ) + + # Get the command object to test + self.cmd = image.ShowImage(self.app, None) + + def test_image_show(self): + arglist = [ + image_fakes.image_id, + ] + verifylist = [ + ('image', image_fakes.image_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + self.images_mock.get.assert_called_with( + image_fakes.image_id, + ) + + self.assertEqual(image_fakes.IMAGE_columns, columns) + self.assertEqual(image_fakes.IMAGE_data, data) diff --git a/openstackclient/tests/unit/image/v2/__init__.py b/openstackclient/tests/unit/image/v2/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/openstackclient/tests/unit/image/v2/__init__.py diff --git a/openstackclient/tests/unit/image/v2/fakes.py b/openstackclient/tests/unit/image/v2/fakes.py new file mode 100644 index 00000000..4d9f6458 --- /dev/null +++ b/openstackclient/tests/unit/image/v2/fakes.py @@ -0,0 +1,309 @@ +# Copyright 2013 Nebula 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 copy +import mock +import random +import uuid + +from glanceclient.v2 import schemas +from osc_lib import utils as common_utils +import warlock + +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes +from openstackclient.tests.unit import utils + +image_id = '0f41529e-7c12-4de8-be2d-181abb825b3c' +image_name = 'graven' +image_owner = 'baal' +image_protected = False +image_visibility = 'public' +image_tags = [] + +IMAGE = { + 'id': image_id, + 'name': image_name, + 'owner': image_owner, + 'protected': image_protected, + 'visibility': image_visibility, + 'tags': image_tags +} + +IMAGE_columns = tuple(sorted(IMAGE)) +IMAGE_data = tuple((IMAGE[x] for x in sorted(IMAGE))) + +IMAGE_SHOW = copy.copy(IMAGE) +IMAGE_SHOW['tags'] = '' +IMAGE_SHOW_data = tuple((IMAGE_SHOW[x] for x in sorted(IMAGE_SHOW))) + +# Just enough v2 schema to do some testing +IMAGE_schema = { + "additionalProperties": { + "type": "string" + }, + "name": "image", + "links": [ + { + "href": "{self}", + "rel": "self" + }, + { + "href": "{file}", + "rel": "enclosure" + }, + { + "href": "{schema}", + "rel": "describedby" + } + ], + "properties": { + "id": { + "pattern": "^([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}$", # noqa + "type": "string", + "description": "An identifier for the image" + }, + "name": { + "type": [ + "null", + "string" + ], + "description": "Descriptive name for the image", + "maxLength": 255 + }, + "owner": { + "type": [ + "null", + "string" + ], + "description": "Owner of the image", + "maxLength": 255 + }, + "protected": { + "type": "boolean", + "description": "If true, image will not be deletable." + }, + "self": { + "type": "string", + "description": "(READ-ONLY)" + }, + "schema": { + "type": "string", + "description": "(READ-ONLY)" + }, + "size": { + "type": [ + "null", + "integer" + ], + "description": "Size of image file in bytes (READ-ONLY)" + }, + "status": { + "enum": [ + "queued", + "saving", + "active", + "killed", + "deleted", + "pending_delete" + ], + "type": "string", + "description": "Status of the image (READ-ONLY)" + }, + "tags": { + "items": { + "type": "string", + "maxLength": 255 + }, + "type": "array", + "description": "List of strings related to the image" + }, + "visibility": { + "enum": [ + "public", + "private" + ], + "type": "string", + "description": "Scope of image accessibility" + }, + } +} + + +class FakeImagev2Client(object): + + def __init__(self, **kwargs): + self.images = mock.Mock() + self.images.resource_class = fakes.FakeResource(None, {}) + self.image_members = mock.Mock() + self.image_members.resource_class = fakes.FakeResource(None, {}) + self.image_tags = mock.Mock() + self.image_tags.resource_class = fakes.FakeResource(None, {}) + self.auth_token = kwargs['token'] + self.management_url = kwargs['endpoint'] + + +class TestImagev2(utils.TestCommand): + + def setUp(self): + super(TestImagev2, self).setUp() + + self.app.client_manager.image = FakeImagev2Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + + self.app.client_manager.identity = identity_fakes.FakeIdentityv3Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + + +class FakeImage(object): + """Fake one or more images. + + TODO(xiexs): Currently, only image API v2 is supported by this class. + """ + + @staticmethod + def create_one_image(attrs=None): + """Create a fake image. + + :param Dictionary attrs: + A dictionary with all attrbutes of image + :return: + A FakeResource object with id, name, owner, protected, + visibility and tags attrs + """ + attrs = attrs or {} + + # Set default attribute + image_info = { + 'id': str(uuid.uuid4()), + 'name': 'image-name' + uuid.uuid4().hex, + 'owner': 'image-owner' + uuid.uuid4().hex, + 'protected': bool(random.choice([0, 1])), + 'visibility': random.choice(['public', 'private']), + 'tags': [uuid.uuid4().hex for r in range(2)], + } + + # Overwrite default attributes if there are some attributes set + image_info.update(attrs) + + # Set up the schema + model = warlock.model_factory( + IMAGE_schema, + schemas.SchemaBasedModel, + ) + + return model(**image_info) + + @staticmethod + def create_images(attrs=None, count=2): + """Create multiple fake images. + + :param Dictionary attrs: + A dictionary with all attributes of image + :param Integer count: + The number of images to be faked + :return: + A list of FakeResource objects + """ + images = [] + for n in range(0, count): + images.append(FakeImage.create_one_image(attrs)) + + return images + + @staticmethod + def get_images(images=None, count=2): + """Get an iterable MagicMock object with a list of faked images. + + If images list is provided, then initialize the Mock object with the + list. Otherwise create one. + + :param List images: + A list of FakeResource objects faking images + :param Integer count: + The number of images to be faked + :return + An iterable Mock object with side_effect set to a list of faked + images + """ + if images is None: + images = FakeImage.create_images(count) + + return mock.MagicMock(side_effect=images) + + @staticmethod + def get_image_columns(image=None): + """Get the image columns from a faked image object. + + :param image: + A FakeResource objects faking image + :return + A tuple which may include the following keys: + ('id', 'name', 'owner', 'protected', 'visibility', 'tags') + """ + if image is not None: + return tuple(sorted(image)) + return IMAGE_columns + + @staticmethod + def get_image_data(image=None): + """Get the image data from a faked image object. + + :param image: + A FakeResource objects faking image + :return + A tuple which may include the following values: + ('image-123', 'image-foo', 'admin', False, 'public', 'bar, baz') + """ + data_list = [] + if image is not None: + for x in sorted(image.keys()): + if x == 'tags': + # The 'tags' should be format_list + data_list.append( + common_utils.format_list(getattr(image, x))) + else: + data_list.append(getattr(image, x)) + return tuple(data_list) + + @staticmethod + def create_one_image_member(attrs=None): + """Create a fake image member. + + :param Dictionary attrs: + A dictionary with all attrbutes of image member + :return: + A FakeResource object with member_id, image_id and so on + """ + attrs = attrs or {} + + # Set default attribute + image_member_info = { + 'member_id': 'member-id-' + uuid.uuid4().hex, + 'image_id': 'image-id-' + uuid.uuid4().hex, + 'status': 'pending', + } + + # Overwrite default attributes if there are some attributes set + image_member_info.update(attrs) + + image_member = fakes.FakeModel( + copy.deepcopy(image_member_info)) + + return image_member diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py new file mode 100644 index 00000000..ebc9c3a7 --- /dev/null +++ b/openstackclient/tests/unit/image/v2/test_image.py @@ -0,0 +1,1316 @@ +# Copyright 2013 Nebula 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 copy +import mock + +from glanceclient.v2 import schemas +from osc_lib import exceptions +from osc_lib import utils as common_utils +import warlock + +from openstackclient.image.v2 import image +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes +from openstackclient.tests.unit.image.v2 import fakes as image_fakes + + +class TestImage(image_fakes.TestImagev2): + + def setUp(self): + super(TestImage, self).setUp() + + # Get shortcuts to the Mocks in image client + self.images_mock = self.app.client_manager.image.images + self.images_mock.reset_mock() + self.image_members_mock = self.app.client_manager.image.image_members + self.image_members_mock.reset_mock() + self.image_tags_mock = self.app.client_manager.image.image_tags + self.image_tags_mock.reset_mock() + + # Get shortcut to the Mocks in identity client + self.project_mock = self.app.client_manager.identity.projects + self.project_mock.reset_mock() + self.domain_mock = self.app.client_manager.identity.domains + self.domain_mock.reset_mock() + + def setup_images_mock(self, count): + images = image_fakes.FakeImage.create_images(count=count) + + self.images_mock.get = image_fakes.FakeImage.get_images( + images, + 0) + return images + + +class TestImageCreate(TestImage): + + project = identity_fakes.FakeProject.create_one_project() + domain = identity_fakes.FakeDomain.create_one_domain() + + def setUp(self): + super(TestImageCreate, self).setUp() + + self.new_image = image_fakes.FakeImage.create_one_image() + self.images_mock.create.return_value = self.new_image + + self.project_mock.get.return_value = self.project + + self.domain_mock.get.return_value = self.domain + + # This is the return value for utils.find_resource() + self.images_mock.get.return_value = copy.deepcopy( + self.new_image + ) + self.images_mock.update.return_value = self.new_image + + # Get the command object to test + self.cmd = image.CreateImage(self.app, None) + + def test_image_reserve_no_options(self): + mock_exception = { + 'find.side_effect': exceptions.CommandError('x'), + } + self.images_mock.configure_mock(**mock_exception) + arglist = [ + self.new_image.name + ] + verifylist = [ + ('container_format', image.DEFAULT_CONTAINER_FORMAT), + ('disk_format', image.DEFAULT_DISK_FORMAT), + ('name', self.new_image.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # ImageManager.create(name=, **) + self.images_mock.create.assert_called_with( + name=self.new_image.name, + container_format=image.DEFAULT_CONTAINER_FORMAT, + disk_format=image.DEFAULT_DISK_FORMAT, + ) + + # Verify update() was not called, if it was show the args + self.assertEqual(self.images_mock.update.call_args_list, []) + + self.images_mock.upload.assert_called_with( + mock.ANY, mock.ANY, + ) + + self.assertEqual( + image_fakes.FakeImage.get_image_columns(self.new_image), + columns) + self.assertEqual( + image_fakes.FakeImage.get_image_data(self.new_image), + data) + + @mock.patch('glanceclient.common.utils.get_data_file', name='Open') + def test_image_reserve_options(self, mock_open): + mock_file = mock.MagicMock(name='File') + mock_open.return_value = mock_file + mock_open.read.return_value = None + mock_exception = { + 'find.side_effect': exceptions.CommandError('x'), + } + self.images_mock.configure_mock(**mock_exception) + arglist = [ + '--container-format', 'ovf', + '--disk-format', 'fs', + '--min-disk', '10', + '--min-ram', '4', + ('--protected' + if self.new_image.protected else '--unprotected'), + ('--private' + if self.new_image.visibility == 'private' else '--public'), + '--project', self.new_image.owner, + '--project-domain', self.domain.id, + self.new_image.name, + ] + verifylist = [ + ('container_format', 'ovf'), + ('disk_format', 'fs'), + ('min_disk', 10), + ('min_ram', 4), + ('protected', self.new_image.protected), + ('unprotected', not self.new_image.protected), + ('public', self.new_image.visibility == 'public'), + ('private', self.new_image.visibility == 'private'), + ('project', self.new_image.owner), + ('project_domain', self.domain.id), + ('name', self.new_image.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # ImageManager.create(name=, **) + self.images_mock.create.assert_called_with( + name=self.new_image.name, + container_format='ovf', + disk_format='fs', + min_disk=10, + min_ram=4, + owner=self.project.id, + protected=self.new_image.protected, + visibility=self.new_image.visibility, + ) + + # Verify update() was not called, if it was show the args + self.assertEqual(self.images_mock.update.call_args_list, []) + + self.images_mock.upload.assert_called_with( + mock.ANY, mock.ANY, + ) + + self.assertEqual( + image_fakes.FakeImage.get_image_columns(self.new_image), + columns) + self.assertEqual( + image_fakes.FakeImage.get_image_data(self.new_image), + data) + + def test_image_create_with_unexist_owner(self): + self.project_mock.get.side_effect = exceptions.NotFound(None) + self.project_mock.find.side_effect = exceptions.NotFound(None) + + arglist = [ + '--container-format', 'ovf', + '--disk-format', 'fs', + '--min-disk', '10', + '--min-ram', '4', + '--owner', 'unexist_owner', + '--protected', + '--private', + image_fakes.image_name, + ] + verifylist = [ + ('container_format', 'ovf'), + ('disk_format', 'fs'), + ('min_disk', 10), + ('min_ram', 4), + ('owner', 'unexist_owner'), + ('protected', True), + ('unprotected', False), + ('public', False), + ('private', True), + ('name', image_fakes.image_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + + def test_image_create_with_unexist_project(self): + self.project_mock.get.side_effect = exceptions.NotFound(None) + self.project_mock.find.side_effect = exceptions.NotFound(None) + + arglist = [ + '--container-format', 'ovf', + '--disk-format', 'fs', + '--min-disk', '10', + '--min-ram', '4', + '--protected', + '--private', + '--project', 'unexist_owner', + image_fakes.image_name, + ] + verifylist = [ + ('container_format', 'ovf'), + ('disk_format', 'fs'), + ('min_disk', 10), + ('min_ram', 4), + ('protected', True), + ('unprotected', False), + ('public', False), + ('private', True), + ('project', 'unexist_owner'), + ('name', image_fakes.image_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + + @mock.patch('glanceclient.common.utils.get_data_file', name='Open') + def test_image_create_file(self, mock_open): + mock_file = mock.MagicMock(name='File') + mock_open.return_value = mock_file + mock_open.read.return_value = ( + image_fakes.FakeImage.get_image_data(self.new_image)) + mock_exception = { + 'find.side_effect': exceptions.CommandError('x'), + } + self.images_mock.configure_mock(**mock_exception) + + arglist = [ + '--file', 'filer', + ('--unprotected' + if not self.new_image.protected else '--protected'), + ('--public' + if self.new_image.visibility == 'public' else '--private'), + '--property', 'Alpha=1', + '--property', 'Beta=2', + '--tag', self.new_image.tags[0], + '--tag', self.new_image.tags[1], + self.new_image.name, + ] + verifylist = [ + ('file', 'filer'), + ('protected', self.new_image.protected), + ('unprotected', not self.new_image.protected), + ('public', self.new_image.visibility == 'public'), + ('private', self.new_image.visibility == 'private'), + ('properties', {'Alpha': '1', 'Beta': '2'}), + ('tags', self.new_image.tags), + ('name', self.new_image.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # ImageManager.create(name=, **) + self.images_mock.create.assert_called_with( + name=self.new_image.name, + container_format=image.DEFAULT_CONTAINER_FORMAT, + disk_format=image.DEFAULT_DISK_FORMAT, + protected=self.new_image.protected, + visibility=self.new_image.visibility, + Alpha='1', + Beta='2', + tags=self.new_image.tags, + ) + + # Verify update() was not called, if it was show the args + self.assertEqual(self.images_mock.update.call_args_list, []) + + self.images_mock.upload.assert_called_with( + mock.ANY, mock.ANY, + ) + + self.assertEqual( + image_fakes.FakeImage.get_image_columns(self.new_image), + columns) + self.assertEqual( + image_fakes.FakeImage.get_image_data(self.new_image), + data) + + def test_image_create_dead_options(self): + + arglist = [ + '--store', 'somewhere', + self.new_image.name, + ] + verifylist = [ + ('name', self.new_image.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, parsed_args) + + +class TestAddProjectToImage(TestImage): + + project = identity_fakes.FakeProject.create_one_project() + domain = identity_fakes.FakeDomain.create_one_domain() + _image = image_fakes.FakeImage.create_one_image() + new_member = image_fakes.FakeImage.create_one_image_member( + attrs={'image_id': _image.id, + 'member_id': project.id} + ) + + columns = ( + 'image_id', + 'member_id', + 'status', + ) + + datalist = ( + _image.id, + new_member.member_id, + new_member.status, + ) + + def setUp(self): + super(TestAddProjectToImage, self).setUp() + + # This is the return value for utils.find_resource() + self.images_mock.get.return_value = self._image + + # Update the image_id in the MEMBER dict + self.image_members_mock.create.return_value = self.new_member + self.project_mock.get.return_value = self.project + self.domain_mock.get.return_value = self.domain + # Get the command object to test + self.cmd = image.AddProjectToImage(self.app, None) + + def test_add_project_to_image_no_option(self): + arglist = [ + self._image.id, + self.project.id, + ] + verifylist = [ + ('image', self._image.id), + ('project', self.project.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + self.image_members_mock.create.assert_called_with( + self._image.id, + self.project.id + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_add_project_to_image_with_option(self): + arglist = [ + self._image.id, + self.project.id, + '--project-domain', self.domain.id, + ] + verifylist = [ + ('image', self._image.id), + ('project', self.project.id), + ('project_domain', self.domain.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + self.image_members_mock.create.assert_called_with( + self._image.id, + self.project.id + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + +class TestImageDelete(TestImage): + + def setUp(self): + super(TestImageDelete, self).setUp() + + self.images_mock.delete.return_value = None + + # Get the command object to test + self.cmd = image.DeleteImage(self.app, None) + + def test_image_delete_no_options(self): + images = self.setup_images_mock(count=1) + + arglist = [ + images[0].id, + ] + verifylist = [ + ('images', [images[0].id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.images_mock.delete.assert_called_with(images[0].id) + self.assertIsNone(result) + + def test_image_delete_multi_images(self): + images = self.setup_images_mock(count=3) + + arglist = [i.id for i in images] + verifylist = [ + ('images', arglist), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + calls = [mock.call(i.id) for i in images] + self.images_mock.delete.assert_has_calls(calls) + self.assertIsNone(result) + + def test_image_delete_multi_images_exception(self): + + images = image_fakes.FakeImage.create_images(count=2) + arglist = [ + images[0].id, + images[1].id, + 'x-y-x', + ] + verifylist = [ + ('images', arglist) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # Fake exception in utils.find_resource() + # In image v2, we use utils.find_resource() to find a network. + # It calls get() several times, but find() only one time. So we + # choose to fake get() always raise exception, then pass through. + # And fake find() to find the real network or not. + ret_find = [ + images[0], + images[1], + exceptions.NotFound('404'), + ] + + self.images_mock.get = Exception() + self.images_mock.find.side_effect = ret_find + self.assertRaises(exceptions.CommandError, self.cmd.take_action, + parsed_args) + calls = [mock.call(i.id) for i in images] + self.images_mock.delete.assert_has_calls(calls) + + +class TestImageList(TestImage): + + _image = image_fakes.FakeImage.create_one_image() + + columns = ( + 'ID', + 'Name', + 'Status', + ) + + datalist = ( + _image.id, + _image.name, + '', + ), + + def setUp(self): + super(TestImageList, self).setUp() + + self.api_mock = mock.Mock() + self.api_mock.image_list.side_effect = [ + [self._image], [], + ] + self.app.client_manager.image.api = self.api_mock + + # Get the command object to test + self.cmd = image.ListImage(self.app, None) + + def test_image_list_no_options(self): + arglist = [] + verifylist = [ + ('public', False), + ('private', False), + ('shared', False), + ('long', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + self.api_mock.image_list.assert_called_with() + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_image_list_public_option(self): + arglist = [ + '--public', + ] + verifylist = [ + ('public', True), + ('private', False), + ('shared', False), + ('long', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + self.api_mock.image_list.assert_called_with( + public=True, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_image_list_private_option(self): + arglist = [ + '--private', + ] + verifylist = [ + ('public', False), + ('private', True), + ('shared', False), + ('long', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + self.api_mock.image_list.assert_called_with( + private=True, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_image_list_shared_option(self): + arglist = [ + '--shared', + ] + verifylist = [ + ('public', False), + ('private', False), + ('shared', True), + ('long', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + self.api_mock.image_list.assert_called_with( + shared=True, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_image_list_long_option(self): + arglist = [ + '--long', + ] + verifylist = [ + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + self.api_mock.image_list.assert_called_with() + + collist = ( + 'ID', + 'Name', + 'Disk Format', + 'Container Format', + 'Size', + 'Checksum', + 'Status', + 'Visibility', + 'Protected', + 'Project', + 'Tags', + ) + + self.assertEqual(collist, columns) + datalist = (( + self._image.id, + self._image.name, + '', + '', + '', + '', + '', + self._image.visibility, + self._image.protected, + self._image.owner, + common_utils.format_list(self._image.tags), + ), ) + self.assertEqual(datalist, tuple(data)) + + @mock.patch('openstackclient.api.utils.simple_filter') + def test_image_list_property_option(self, sf_mock): + sf_mock.return_value = [copy.deepcopy(self._image)] + + arglist = [ + '--property', 'a=1', + ] + verifylist = [ + ('property', {'a': '1'}), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + self.api_mock.image_list.assert_called_with() + sf_mock.assert_called_with( + [self._image], + attr='a', + value='1', + property_field='properties', + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + @mock.patch('osc_lib.utils.sort_items') + def test_image_list_sort_option(self, si_mock): + si_mock.return_value = [copy.deepcopy(self._image)] + + arglist = ['--sort', 'name:asc'] + verifylist = [('sort', 'name:asc')] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + self.api_mock.image_list.assert_called_with() + si_mock.assert_called_with( + [self._image], + 'name:asc' + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_image_list_limit_option(self): + arglist = [ + '--limit', str(1), + ] + verifylist = [ + ('limit', 1), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.api_mock.image_list.assert_called_with( + limit=1, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(len(self.datalist), len(tuple(data))) + + @mock.patch('osc_lib.utils.find_resource') + def test_image_list_marker_option(self, fr_mock): + # tangchen: Since image_fakes.IMAGE is a dict, it cannot offer a .id + # operation. Will fix this by using FakeImage class instead + # of IMAGE dict. + fr_mock.return_value = mock.Mock() + fr_mock.return_value.id = image_fakes.image_id + + arglist = [ + '--marker', image_fakes.image_name, + ] + verifylist = [ + ('marker', image_fakes.image_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.api_mock.image_list.assert_called_with( + marker=image_fakes.image_id, + ) + + +class TestRemoveProjectImage(TestImage): + + project = identity_fakes.FakeProject.create_one_project() + domain = identity_fakes.FakeDomain.create_one_domain() + + def setUp(self): + super(TestRemoveProjectImage, self).setUp() + + self._image = image_fakes.FakeImage.create_one_image() + # This is the return value for utils.find_resource() + self.images_mock.get.return_value = self._image + + self.project_mock.get.return_value = self.project + self.domain_mock.get.return_value = self.domain + self.image_members_mock.delete.return_value = None + # Get the command object to test + self.cmd = image.RemoveProjectImage(self.app, None) + + def test_remove_project_image_no_options(self): + arglist = [ + self._image.id, + self.project.id, + ] + verifylist = [ + ('image', self._image.id), + ('project', self.project.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.image_members_mock.delete.assert_called_with( + self._image.id, + self.project.id, + ) + self.assertIsNone(result) + + def test_remove_project_image_with_options(self): + arglist = [ + self._image.id, + self.project.id, + '--project-domain', self.domain.id, + ] + verifylist = [ + ('image', self._image.id), + ('project', self.project.id), + ('project_domain', self.domain.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.image_members_mock.delete.assert_called_with( + self._image.id, + self.project.id, + ) + self.assertIsNone(result) + + +class TestImageSet(TestImage): + + project = identity_fakes.FakeProject.create_one_project() + domain = identity_fakes.FakeDomain.create_one_domain() + + def setUp(self): + super(TestImageSet, self).setUp() + # Set up the schema + self.model = warlock.model_factory( + image_fakes.IMAGE_schema, + schemas.SchemaBasedModel, + ) + + self.project_mock.get.return_value = self.project + + self.domain_mock.get.return_value = self.domain + + self.images_mock.get.return_value = self.model(**image_fakes.IMAGE) + self.images_mock.update.return_value = self.model(**image_fakes.IMAGE) + # Get the command object to test + self.cmd = image.SetImage(self.app, None) + + def test_image_set_no_options(self): + arglist = [ + image_fakes.image_id, + ] + verifylist = [ + ('image', image_fakes.image_id) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + + def test_image_set_options(self): + arglist = [ + '--name', 'new-name', + '--min-disk', '2', + '--min-ram', '4', + '--container-format', 'ovf', + '--disk-format', 'vmdk', + '--project', self.project.name, + '--project-domain', self.domain.id, + image_fakes.image_id, + ] + verifylist = [ + ('name', 'new-name'), + ('min_disk', 2), + ('min_ram', 4), + ('container_format', 'ovf'), + ('disk_format', 'vmdk'), + ('project', self.project.name), + ('project_domain', self.domain.id), + ('image', image_fakes.image_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + kwargs = { + 'name': 'new-name', + 'owner': self.project.id, + 'min_disk': 2, + 'min_ram': 4, + 'container_format': 'ovf', + 'disk_format': 'vmdk', + } + # ImageManager.update(image, **kwargs) + self.images_mock.update.assert_called_with( + image_fakes.image_id, **kwargs) + self.assertIsNone(result) + + def test_image_set_with_unexist_owner(self): + self.project_mock.get.side_effect = exceptions.NotFound(None) + self.project_mock.find.side_effect = exceptions.NotFound(None) + + arglist = [ + '--owner', 'unexist_owner', + image_fakes.image_id, + ] + verifylist = [ + ('owner', 'unexist_owner'), + ('image', image_fakes.image_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, parsed_args) + + def test_image_set_with_unexist_project(self): + self.project_mock.get.side_effect = exceptions.NotFound(None) + self.project_mock.find.side_effect = exceptions.NotFound(None) + + arglist = [ + '--project', 'unexist_owner', + image_fakes.image_id, + ] + verifylist = [ + ('project', 'unexist_owner'), + ('image', image_fakes.image_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, parsed_args) + + def test_image_set_bools1(self): + arglist = [ + '--protected', + '--private', + image_fakes.image_name, + ] + verifylist = [ + ('protected', True), + ('unprotected', False), + ('public', False), + ('private', True), + ('image', image_fakes.image_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + kwargs = { + 'protected': True, + 'visibility': 'private', + } + # ImageManager.update(image, **kwargs) + self.images_mock.update.assert_called_with( + image_fakes.image_id, + **kwargs + ) + self.assertIsNone(result) + + def test_image_set_bools2(self): + arglist = [ + '--unprotected', + '--public', + image_fakes.image_name, + ] + verifylist = [ + ('protected', False), + ('unprotected', True), + ('public', True), + ('private', False), + ('image', image_fakes.image_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + kwargs = { + 'protected': False, + 'visibility': 'public', + } + # ImageManager.update(image, **kwargs) + self.images_mock.update.assert_called_with( + image_fakes.image_id, + **kwargs + ) + self.assertIsNone(result) + + def test_image_set_properties(self): + arglist = [ + '--property', 'Alpha=1', + '--property', 'Beta=2', + image_fakes.image_name, + ] + verifylist = [ + ('properties', {'Alpha': '1', 'Beta': '2'}), + ('image', image_fakes.image_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + kwargs = { + 'Alpha': '1', + 'Beta': '2', + } + # ImageManager.update(image, **kwargs) + self.images_mock.update.assert_called_with( + image_fakes.image_id, + **kwargs + ) + self.assertIsNone(result) + + def test_image_set_fake_properties(self): + arglist = [ + '--architecture', 'z80', + '--instance-id', '12345', + '--kernel-id', '67890', + '--os-distro', 'cpm', + '--os-version', '2.2H', + '--ramdisk-id', 'xyzpdq', + image_fakes.image_name, + ] + verifylist = [ + ('architecture', 'z80'), + ('instance_id', '12345'), + ('kernel_id', '67890'), + ('os_distro', 'cpm'), + ('os_version', '2.2H'), + ('ramdisk_id', 'xyzpdq'), + ('image', image_fakes.image_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + kwargs = { + 'architecture': 'z80', + 'instance_id': '12345', + 'kernel_id': '67890', + 'os_distro': 'cpm', + 'os_version': '2.2H', + 'ramdisk_id': 'xyzpdq', + } + # ImageManager.update(image, **kwargs) + self.images_mock.update.assert_called_with( + image_fakes.image_id, + **kwargs + ) + self.assertIsNone(result) + + def test_image_set_tag(self): + arglist = [ + '--tag', 'test-tag', + image_fakes.image_name, + ] + verifylist = [ + ('tags', ['test-tag']), + ('image', image_fakes.image_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + kwargs = { + 'tags': ['test-tag'], + } + # ImageManager.update(image, **kwargs) + self.images_mock.update.assert_called_with( + image_fakes.image_id, + **kwargs + ) + self.assertIsNone(result) + + def test_image_set_activate(self): + arglist = [ + '--tag', 'test-tag', + '--activate', + image_fakes.image_name, + ] + verifylist = [ + ('tags', ['test-tag']), + ('image', image_fakes.image_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + kwargs = { + 'tags': ['test-tag'], + } + + self.images_mock.reactivate.assert_called_with( + image_fakes.image_id, + ) + # ImageManager.update(image, **kwargs) + self.images_mock.update.assert_called_with( + image_fakes.image_id, + **kwargs + ) + self.assertIsNone(result) + + def test_image_set_deactivate(self): + arglist = [ + '--tag', 'test-tag', + '--deactivate', + image_fakes.image_name, + ] + verifylist = [ + ('tags', ['test-tag']), + ('image', image_fakes.image_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + kwargs = { + 'tags': ['test-tag'], + } + + self.images_mock.deactivate.assert_called_with( + image_fakes.image_id, + ) + # ImageManager.update(image, **kwargs) + self.images_mock.update.assert_called_with( + image_fakes.image_id, + **kwargs + ) + self.assertIsNone(result) + + def test_image_set_tag_merge(self): + old_image = copy.copy(image_fakes.IMAGE) + old_image['tags'] = ['old1', 'new2'] + self.images_mock.get.return_value = self.model(**old_image) + arglist = [ + '--tag', 'test-tag', + image_fakes.image_name, + ] + verifylist = [ + ('tags', ['test-tag']), + ('image', image_fakes.image_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + kwargs = { + 'tags': ['old1', 'new2', 'test-tag'], + } + # ImageManager.update(image, **kwargs) + a, k = self.images_mock.update.call_args + self.assertEqual(image_fakes.image_id, a[0]) + self.assertIn('tags', k) + self.assertEqual(set(kwargs['tags']), set(k['tags'])) + self.assertIsNone(result) + + def test_image_set_tag_merge_dupe(self): + old_image = copy.copy(image_fakes.IMAGE) + old_image['tags'] = ['old1', 'new2'] + self.images_mock.get.return_value = self.model(**old_image) + arglist = [ + '--tag', 'old1', + image_fakes.image_name, + ] + verifylist = [ + ('tags', ['old1']), + ('image', image_fakes.image_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + kwargs = { + 'tags': ['new2', 'old1'], + } + # ImageManager.update(image, **kwargs) + a, k = self.images_mock.update.call_args + self.assertEqual(image_fakes.image_id, a[0]) + self.assertIn('tags', k) + self.assertEqual(set(kwargs['tags']), set(k['tags'])) + self.assertIsNone(result) + + def test_image_set_dead_options(self): + + arglist = [ + '--visibility', '1-mile', + image_fakes.image_name, + ] + verifylist = [ + ('visibility', '1-mile'), + ('image', image_fakes.image_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, parsed_args) + + +class TestImageShow(TestImage): + + def setUp(self): + super(TestImageShow, self).setUp() + + # Set up the schema + self.model = warlock.model_factory( + image_fakes.IMAGE_schema, + schemas.SchemaBasedModel, + ) + + self.images_mock.get.return_value = self.model(**image_fakes.IMAGE) + + # Get the command object to test + self.cmd = image.ShowImage(self.app, None) + + def test_image_show(self): + arglist = [ + image_fakes.image_id, + ] + verifylist = [ + ('image', image_fakes.image_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + self.images_mock.get.assert_called_with( + image_fakes.image_id, + ) + + self.assertEqual(image_fakes.IMAGE_columns, columns) + self.assertEqual(image_fakes.IMAGE_SHOW_data, data) + + +class TestImageUnset(TestImage): + + attrs = {} + attrs['tags'] = ['test'] + attrs['prop'] = 'test' + image = image_fakes.FakeImage.create_one_image(attrs) + + def setUp(self): + super(TestImageUnset, self).setUp() + + # Set up the schema + self.model = warlock.model_factory( + image_fakes.IMAGE_schema, + schemas.SchemaBasedModel, + ) + + self.images_mock.get.return_value = self.image + self.image_tags_mock.delete.return_value = self.image + + # Get the command object to test + self.cmd = image.UnsetImage(self.app, None) + + def test_image_unset_no_options(self): + arglist = [ + image_fakes.image_id, + ] + verifylist = [ + ('image', image_fakes.image_id) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + + def test_image_unset_tag_option(self): + + arglist = [ + '--tag', 'test', + self.image.id, + ] + + verifylist = [ + ('tags', ['test']), + ('image', self.image.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.image_tags_mock.delete.assert_called_with( + self.image.id, 'test' + ) + self.assertIsNone(result) + + def test_image_unset_property_option(self): + + arglist = [ + '--property', 'prop', + self.image.id, + ] + + verifylist = [ + ('properties', ['prop']), + ('image', self.image.id) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + kwargs = {} + self.images_mock.update.assert_called_with( + self.image.id, + parsed_args.properties, + **kwargs) + + self.assertIsNone(result) + + def test_image_unset_mixed_option(self): + + arglist = [ + '--tag', 'test', + '--property', 'prop', + self.image.id, + ] + + verifylist = [ + ('tags', ['test']), + ('properties', ['prop']), + ('image', self.image.id) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + kwargs = {} + self.images_mock.update.assert_called_with( + self.image.id, + parsed_args.properties, + **kwargs) + + self.image_tags_mock.delete.assert_called_with( + self.image.id, 'test' + ) + self.assertIsNone(result) diff --git a/openstackclient/tests/unit/integ/__init__.py b/openstackclient/tests/unit/integ/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/openstackclient/tests/unit/integ/__init__.py diff --git a/openstackclient/tests/unit/integ/base.py b/openstackclient/tests/unit/integ/base.py new file mode 100644 index 00000000..caed4f89 --- /dev/null +++ b/openstackclient/tests/unit/integ/base.py @@ -0,0 +1,121 @@ +# 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 keystoneauth1 import fixture as ksa_fixture +from requests_mock.contrib import fixture + +from openstackclient.tests.unit import test_shell +from openstackclient.tests.unit import utils + + +HOST = "192.168.5.41" +URL_BASE = "http://%s/identity" % HOST + +V2_AUTH_URL = URL_BASE + "/v2.0/" +V2_VERSION_RESP = { + "version": { + "status": "stable", + "updated": "2014-04-17T00:00:00Z", + "media-types": [ + { + "base": "application/json", + "type": "application/vnd.openstack.identity-v2.0+json", + }, + ], + "id": "v2.0", + "links": [ + { + "href": V2_AUTH_URL, + "rel": "self", + }, + { + "href": "http://docs.openstack.org/", + "type": "text/html", + "rel": "describedby", + }, + ], + }, +} + +V3_AUTH_URL = URL_BASE + "/v3/" +V3_VERSION_RESP = { + "version": { + "status": "stable", + "updated": "2016-04-04T00:00:00Z", + "media-types": [{ + "base": "application/json", + "type": "application/vnd.openstack.identity-v3+json", + }], + "id": "v3.6", + "links": [{ + "href": V3_AUTH_URL, + "rel": "self", + }] + } +} + + +def make_v2_token(req_mock): + """Create an Identity v2 token and register the responses""" + + token = ksa_fixture.V2Token( + tenant_name=test_shell.DEFAULT_PROJECT_NAME, + user_name=test_shell.DEFAULT_USERNAME, + ) + + # Set up the v2 auth routes + req_mock.register_uri( + 'GET', + V2_AUTH_URL, + json=V2_VERSION_RESP, + status_code=200, + ) + req_mock.register_uri( + 'POST', + V2_AUTH_URL + 'tokens', + json=token, + status_code=200, + ) + return token + + +def make_v3_token(req_mock): + """Create an Identity v3 token and register the response""" + + token = ksa_fixture.V3Token( + # project_domain_id=test_shell.DEFAULT_PROJECT_DOMAIN_ID, + user_domain_id=test_shell.DEFAULT_USER_DOMAIN_ID, + user_name=test_shell.DEFAULT_USERNAME, + ) + + # Set up the v3 auth routes + req_mock.register_uri( + 'GET', + V3_AUTH_URL, + json=V3_VERSION_RESP, + status_code=200, + ) + req_mock.register_uri( + 'POST', + V3_AUTH_URL + 'auth/tokens', + json=token, + status_code=200, + ) + return token + + +class TestInteg(utils.TestCase): + + def setUp(self): + super(TestInteg, self).setUp() + + self.requests_mock = self.useFixture(fixture.Fixture()) diff --git a/openstackclient/tests/unit/integ/cli/__init__.py b/openstackclient/tests/unit/integ/cli/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/openstackclient/tests/unit/integ/cli/__init__.py diff --git a/openstackclient/tests/unit/integ/cli/test_project.py b/openstackclient/tests/unit/integ/cli/test_project.py new file mode 100644 index 00000000..6a7c6d1b --- /dev/null +++ b/openstackclient/tests/unit/integ/cli/test_project.py @@ -0,0 +1,257 @@ +# 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 copy + +from osc_lib.tests import utils as osc_lib_utils + +from openstackclient import shell +from openstackclient.tests.unit.integ import base as test_base +from openstackclient.tests.unit import test_shell + + +class TestIntegV2ProjectID(test_base.TestInteg): + + def setUp(self): + super(TestIntegV2ProjectID, self).setUp() + env = { + "OS_AUTH_URL": test_base.V2_AUTH_URL, + "OS_PROJECT_ID": test_shell.DEFAULT_PROJECT_ID, + "OS_USERNAME": test_shell.DEFAULT_USERNAME, + "OS_PASSWORD": test_shell.DEFAULT_PASSWORD, + "OS_IDENTITY_API_VERSION": "2", + } + self.useFixture(osc_lib_utils.EnvFixture(copy.deepcopy(env))) + + self.token = test_base.make_v2_token(self.requests_mock) + + def test_project_id_env(self): + _shell = shell.OpenStackShell() + _shell.run("configuration show".split()) + + # Check general calls + self.assertEqual(len(self.requests_mock.request_history), 2) + + # Check discovery request + self.assertEqual( + test_base.V2_AUTH_URL, + self.requests_mock.request_history[0].url, + ) + + # Check auth request + auth_req = self.requests_mock.request_history[1].json() + + self.assertEqual( + test_shell.DEFAULT_PROJECT_ID, + auth_req['auth']['tenantId'], + ) + + def test_project_id_arg(self): + _shell = shell.OpenStackShell() + _shell.run("--os-project-id wsx configuration show".split()) + + # Check general calls + self.assertEqual(len(self.requests_mock.request_history), 2) + + # Check discovery request + self.assertEqual( + test_base.V2_AUTH_URL, + self.requests_mock.request_history[0].url, + ) + + # Check auth request + auth_req = self.requests_mock.request_history[1].json() + + self.assertEqual( + "wsx", + auth_req['auth']['tenantId'], + ) + + +class TestIntegV2ProjectName(test_base.TestInteg): + + def setUp(self): + super(TestIntegV2ProjectName, self).setUp() + env = { + "OS_AUTH_URL": test_base.V2_AUTH_URL, + "OS_PROJECT_NAME": test_shell.DEFAULT_PROJECT_NAME, + "OS_USERNAME": test_shell.DEFAULT_USERNAME, + "OS_PASSWORD": test_shell.DEFAULT_PASSWORD, + "OS_IDENTITY_API_VERSION": "2", + } + self.useFixture(osc_lib_utils.EnvFixture(copy.deepcopy(env))) + + self.token = test_base.make_v2_token(self.requests_mock) + + def test_project_name_env(self): + _shell = shell.OpenStackShell() + _shell.run("configuration show".split()) + + # Check general calls + self.assertEqual(len(self.requests_mock.request_history), 2) + + # Check discovery request + self.assertEqual( + test_base.V2_AUTH_URL, + self.requests_mock.request_history[0].url, + ) + + # Check auth request + auth_req = self.requests_mock.request_history[1].json() + + self.assertEqual( + test_shell.DEFAULT_PROJECT_NAME, + auth_req['auth']['tenantName'], + ) + + def test_project_name_arg(self): + _shell = shell.OpenStackShell() + _shell.run("--os-project-name qaz configuration show".split()) + + # Check general calls + self.assertEqual(len(self.requests_mock.request_history), 2) + + # Check discovery request + self.assertEqual( + test_base.V2_AUTH_URL, + self.requests_mock.request_history[0].url, + ) + + # Check auth request + auth_req = self.requests_mock.request_history[1].json() + + self.assertEqual( + "qaz", + auth_req['auth']['tenantName'], + ) + + +class TestIntegV3ProjectID(test_base.TestInteg): + + def setUp(self): + super(TestIntegV3ProjectID, self).setUp() + env = { + "OS_AUTH_URL": test_base.V3_AUTH_URL, + "OS_PROJECT_ID": test_shell.DEFAULT_PROJECT_NAME, + # "OS_PROJECT_DOMAIN_ID": test_shell.DEFAULT_PROJECT_DOMAIN_ID, + # "OS_USER_DOMAIN_ID": test_shell.DEFAULT_USER_DOMAIN_ID, + "OS_USERNAME": test_shell.DEFAULT_USERNAME, + "OS_PASSWORD": test_shell.DEFAULT_PASSWORD, + "OS_IDENTITY_API_VERSION": "3", + } + self.useFixture(osc_lib_utils.EnvFixture(copy.deepcopy(env))) + + self.token = test_base.make_v3_token(self.requests_mock) + + def test_project_id_env(self): + _shell = shell.OpenStackShell() + _shell.run("configuration show".split()) + + # Check general calls + self.assertEqual(len(self.requests_mock.request_history), 2) + + # Check discovery request + self.assertEqual( + test_base.V3_AUTH_URL, + self.requests_mock.request_history[0].url, + ) + + # Check auth request + auth_req = self.requests_mock.request_history[1].json() + + self.assertIsNone(auth_req['auth'].get('tenantId', None)) + self.assertIsNone(auth_req['auth'].get('tenantName', None)) + + def test_project_id_arg(self): + _shell = shell.OpenStackShell() + _shell.run("--os-project-id wsx configuration show".split()) + + # Check general calls + self.assertEqual(len(self.requests_mock.request_history), 2) + + # Check discovery request + self.assertEqual( + test_base.V3_AUTH_URL, + self.requests_mock.request_history[0].url, + ) + + # Check auth request + auth_req = self.requests_mock.request_history[1].json() + + self.assertIsNone(auth_req['auth'].get('tenantId', None)) + self.assertIsNone(auth_req['auth'].get('tenantName', None)) + + +class TestIntegV3ProjectName(test_base.TestInteg): + + def setUp(self): + super(TestIntegV3ProjectName, self).setUp() + env = { + "OS_AUTH_URL": test_base.V3_AUTH_URL, + "OS_PROJECT_NAME": test_shell.DEFAULT_PROJECT_NAME, + # "OS_PROJECT_DOMAIN_ID": test_shell.DEFAULT_PROJECT_DOMAIN_ID, + # "OS_USER_DOMAIN_ID": test_shell.DEFAULT_USER_DOMAIN_ID, + "OS_USERNAME": test_shell.DEFAULT_USERNAME, + "OS_PASSWORD": test_shell.DEFAULT_PASSWORD, + "OS_IDENTITY_API_VERSION": "3", + } + self.useFixture(osc_lib_utils.EnvFixture(copy.deepcopy(env))) + + self.token = test_base.make_v3_token(self.requests_mock) + + def test_project_name_env(self): + _shell = shell.OpenStackShell() + _shell.run("configuration show".split()) + + # Check general calls + self.assertEqual(len(self.requests_mock.request_history), 2) + + # Check discovery request + self.assertEqual( + test_base.V3_AUTH_URL, + self.requests_mock.request_history[0].url, + ) + + # Check auth request + auth_req = self.requests_mock.request_history[1].json() + + self.assertEqual( + test_shell.DEFAULT_PROJECT_NAME, + auth_req['auth']['scope']['project']['name'], + ) + + self.assertIsNone(auth_req['auth'].get('tenantId', None)) + self.assertIsNone(auth_req['auth'].get('tenantName', None)) + + def test_project_name_arg(self): + _shell = shell.OpenStackShell() + _shell.run("--os-project-name wsx configuration show".split()) + + # Check general calls + self.assertEqual(len(self.requests_mock.request_history), 2) + + # Check discovery request + self.assertEqual( + test_base.V3_AUTH_URL, + self.requests_mock.request_history[0].url, + ) + + # Check auth request + auth_req = self.requests_mock.request_history[1].json() + + self.assertEqual( + "wsx", + auth_req['auth']['scope']['project']['name'], + ) + + self.assertIsNone(auth_req['auth'].get('tenantId', None)) + self.assertIsNone(auth_req['auth'].get('tenantName', None)) diff --git a/openstackclient/tests/unit/integ/cli/test_shell.py b/openstackclient/tests/unit/integ/cli/test_shell.py new file mode 100644 index 00000000..9d819ed2 --- /dev/null +++ b/openstackclient/tests/unit/integ/cli/test_shell.py @@ -0,0 +1,506 @@ +# 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 copy +import mock + +from osc_lib.tests import utils as osc_lib_utils + +from openstackclient import shell +from openstackclient.tests.unit.integ import base as test_base +from openstackclient.tests.unit import test_shell + + +class TestIntegShellCliV2(test_base.TestInteg): + + def setUp(self): + super(TestIntegShellCliV2, self).setUp() + env = { + "OS_AUTH_URL": test_base.V2_AUTH_URL, + "OS_PROJECT_NAME": test_shell.DEFAULT_PROJECT_NAME, + "OS_USERNAME": test_shell.DEFAULT_USERNAME, + "OS_PASSWORD": test_shell.DEFAULT_PASSWORD, + "OS_IDENTITY_API_VERSION": "2", + } + self.useFixture(osc_lib_utils.EnvFixture(copy.deepcopy(env))) + + self.token = test_base.make_v2_token(self.requests_mock) + + def test_shell_args_no_options(self): + _shell = shell.OpenStackShell() + _shell.run("configuration show".split()) + + # Check general calls + self.assertEqual(len(self.requests_mock.request_history), 2) + + # Check discovery request + self.assertEqual( + test_base.V2_AUTH_URL, + self.requests_mock.request_history[0].url, + ) + + # Check auth request + auth_req = self.requests_mock.request_history[1].json() + + self.assertEqual( + test_shell.DEFAULT_PROJECT_NAME, + auth_req['auth']['tenantName'], + ) + self.assertEqual( + test_shell.DEFAULT_USERNAME, + auth_req['auth']['passwordCredentials']['username'], + ) + self.assertEqual( + test_shell.DEFAULT_PASSWORD, + auth_req['auth']['passwordCredentials']['password'], + ) + + def test_shell_args_verify(self): + _shell = shell.OpenStackShell() + _shell.run("--verify configuration show".split()) + + # Check general calls + self.assertEqual(len(self.requests_mock.request_history), 2) + + # Check verify + self.assertTrue(self.requests_mock.request_history[0].verify) + + def test_shell_args_insecure(self): + _shell = shell.OpenStackShell() + _shell.run("--insecure configuration show".split()) + + # Check general calls + self.assertEqual(len(self.requests_mock.request_history), 2) + + # Check verify + self.assertFalse(self.requests_mock.request_history[0].verify) + + def test_shell_args_cacert(self): + _shell = shell.OpenStackShell() + _shell.run("--os-cacert xyzpdq configuration show".split()) + + # Check general calls + self.assertEqual(len(self.requests_mock.request_history), 2) + + # Check verify + self.assertEqual( + 'xyzpdq', + self.requests_mock.request_history[0].verify, + ) + + def test_shell_args_cacert_insecure(self): + _shell = shell.OpenStackShell() + _shell.run("--os-cacert xyzpdq --insecure configuration show".split()) + + # Check general calls + self.assertEqual(len(self.requests_mock.request_history), 2) + + # Check verify + self.assertFalse(self.requests_mock.request_history[0].verify) + + +class TestIntegShellCliV2Ignore(test_base.TestInteg): + + def setUp(self): + super(TestIntegShellCliV2Ignore, self).setUp() + env = { + "OS_AUTH_URL": test_base.V2_AUTH_URL, + "OS_PROJECT_NAME": test_shell.DEFAULT_PROJECT_NAME, + "OS_USERNAME": test_shell.DEFAULT_USERNAME, + "OS_PASSWORD": test_shell.DEFAULT_PASSWORD, + "OS_PROJECT_DOMAIN_ID": test_shell.DEFAULT_PROJECT_DOMAIN_ID, + "OS_USER_DOMAIN_ID": test_shell.DEFAULT_USER_DOMAIN_ID, + "OS_IDENTITY_API_VERSION": "2", + } + self.useFixture(osc_lib_utils.EnvFixture(copy.deepcopy(env))) + + self.token = test_base.make_v2_token(self.requests_mock) + + def test_shell_args_ignore_v3(self): + _shell = shell.OpenStackShell() + _shell.run("configuration show".split()) + + # Check general calls + self.assertEqual(len(self.requests_mock.request_history), 2) + + # Check discovery request + self.assertEqual( + test_base.V2_AUTH_URL, + self.requests_mock.request_history[0].url, + ) + + # Check auth request + auth_req = self.requests_mock.request_history[1].json() + + self.assertEqual( + test_shell.DEFAULT_PROJECT_NAME, + auth_req['auth']['tenantName'], + ) + self.assertEqual( + test_shell.DEFAULT_USERNAME, + auth_req['auth']['passwordCredentials']['username'], + ) + self.assertEqual( + test_shell.DEFAULT_PASSWORD, + auth_req['auth']['passwordCredentials']['password'], + ) + + +class TestIntegShellCliV3(test_base.TestInteg): + + def setUp(self): + super(TestIntegShellCliV3, self).setUp() + env = { + "OS_AUTH_URL": test_base.V3_AUTH_URL, + "OS_PROJECT_DOMAIN_ID": test_shell.DEFAULT_PROJECT_DOMAIN_ID, + "OS_USER_DOMAIN_ID": test_shell.DEFAULT_USER_DOMAIN_ID, + "OS_USERNAME": test_shell.DEFAULT_USERNAME, + "OS_PASSWORD": test_shell.DEFAULT_PASSWORD, + "OS_IDENTITY_API_VERSION": "3", + } + self.useFixture(osc_lib_utils.EnvFixture(copy.deepcopy(env))) + + self.token = test_base.make_v3_token(self.requests_mock) + + def test_shell_args_no_options(self): + _shell = shell.OpenStackShell() + _shell.run("configuration show".split()) + + # Check general calls + self.assertEqual(len(self.requests_mock.request_history), 2) + + # Check discovery request + self.assertEqual( + test_base.V3_AUTH_URL, + self.requests_mock.request_history[0].url, + ) + + # Check auth request + auth_req = self.requests_mock.request_history[1].json() + + self.assertEqual( + test_shell.DEFAULT_PROJECT_DOMAIN_ID, + auth_req['auth']['identity']['password']['user']['domain']['id'], + ) + self.assertEqual( + test_shell.DEFAULT_USERNAME, + auth_req['auth']['identity']['password']['user']['name'], + ) + self.assertEqual( + test_shell.DEFAULT_PASSWORD, + auth_req['auth']['identity']['password']['user']['password'], + ) + + def test_shell_args_verify(self): + _shell = shell.OpenStackShell() + _shell.run("--verify configuration show".split()) + + # Check general calls + self.assertEqual(len(self.requests_mock.request_history), 2) + + # Check verify + self.assertTrue(self.requests_mock.request_history[0].verify) + + def test_shell_args_insecure(self): + _shell = shell.OpenStackShell() + _shell.run("--insecure configuration show".split()) + + # Check general calls + self.assertEqual(len(self.requests_mock.request_history), 2) + + # Check verify + self.assertFalse(self.requests_mock.request_history[0].verify) + + def test_shell_args_cacert(self): + _shell = shell.OpenStackShell() + _shell.run("--os-cacert xyzpdq configuration show".split()) + + # Check general calls + self.assertEqual(len(self.requests_mock.request_history), 2) + + # Check verify + self.assertEqual( + 'xyzpdq', + self.requests_mock.request_history[0].verify, + ) + + def test_shell_args_cacert_insecure(self): + # This test verifies the outcome of bug 1447784 + # https://bugs.launchpad.net/python-openstackclient/+bug/1447784 + _shell = shell.OpenStackShell() + _shell.run("--os-cacert xyzpdq --insecure configuration show".split()) + + # Check general calls + self.assertEqual(len(self.requests_mock.request_history), 2) + + # Check verify + self.assertFalse(self.requests_mock.request_history[0].verify) + + +class TestIntegShellCliV3Prompt(test_base.TestInteg): + + def setUp(self): + super(TestIntegShellCliV3Prompt, self).setUp() + env = { + "OS_AUTH_URL": test_base.V3_AUTH_URL, + "OS_PROJECT_DOMAIN_ID": test_shell.DEFAULT_PROJECT_DOMAIN_ID, + "OS_USER_DOMAIN_ID": test_shell.DEFAULT_USER_DOMAIN_ID, + "OS_USERNAME": test_shell.DEFAULT_USERNAME, + "OS_IDENTITY_API_VERSION": "3", + } + self.useFixture(osc_lib_utils.EnvFixture(copy.deepcopy(env))) + + self.token = test_base.make_v3_token(self.requests_mock) + + @mock.patch("osc_lib.shell.prompt_for_password") + def test_shell_callback(self, mock_prompt): + mock_prompt.return_value = "qaz" + _shell = shell.OpenStackShell() + _shell.run("configuration show".split()) + + # Check general calls + self.assertEqual(len(self.requests_mock.request_history), 2) + + # Check password callback set correctly + self.assertEqual( + mock_prompt, + _shell.cloud._openstack_config._pw_callback + ) + + # Check auth request + auth_req = self.requests_mock.request_history[1].json() + + # Check returned password from prompt function + self.assertEqual( + "qaz", + auth_req['auth']['identity']['password']['user']['password'], + ) + + +class TestIntegShellCliPrecedence(test_base.TestInteg): + """Validate option precedence rules without clouds.yaml + + Global option values may be set in three places: + * command line options + * environment variables + * clouds.yaml + + Verify that the above order is the precedence used, + i.e. a command line option overrides all others, etc + """ + + def setUp(self): + super(TestIntegShellCliPrecedence, self).setUp() + env = { + "OS_AUTH_URL": test_base.V3_AUTH_URL, + "OS_PROJECT_DOMAIN_ID": test_shell.DEFAULT_PROJECT_DOMAIN_ID, + "OS_USER_DOMAIN_ID": test_shell.DEFAULT_USER_DOMAIN_ID, + "OS_USERNAME": test_shell.DEFAULT_USERNAME, + "OS_IDENTITY_API_VERSION": "3", + } + self.useFixture(osc_lib_utils.EnvFixture(copy.deepcopy(env))) + + self.token = test_base.make_v3_token(self.requests_mock) + + # Patch a v3 auth URL into the o-c-c data + test_shell.PUBLIC_1['public-clouds']['megadodo']['auth']['auth_url'] \ + = test_base.V3_AUTH_URL + + def test_shell_args_options(self): + """Verify command line options override environment variables""" + + _shell = shell.OpenStackShell() + _shell.run( + "--os-username zarquon --os-password qaz " + "configuration show".split(), + ) + + # Check general calls + self.assertEqual(len(self.requests_mock.request_history), 2) + + # Check discovery request + self.assertEqual( + test_base.V3_AUTH_URL, + self.requests_mock.request_history[0].url, + ) + + # Check auth request + auth_req = self.requests_mock.request_history[1].json() + + # -env, -cli + # No test, everything not specified tests this + + # -env, +cli + self.assertEqual( + 'qaz', + auth_req['auth']['identity']['password']['user']['password'], + ) + + # +env, -cli + self.assertEqual( + test_shell.DEFAULT_PROJECT_DOMAIN_ID, + auth_req['auth']['identity']['password']['user']['domain']['id'], + ) + + # +env, +cli + self.assertEqual( + 'zarquon', + auth_req['auth']['identity']['password']['user']['name'], + ) + + +class TestIntegShellCliPrecedenceOCC(test_base.TestInteg): + """Validate option precedence rules with clouds.yaml + + Global option values may be set in three places: + * command line options + * environment variables + * clouds.yaml + + Verify that the above order is the precedence used, + i.e. a command line option overrides all others, etc + """ + + def setUp(self): + super(TestIntegShellCliPrecedenceOCC, self).setUp() + env = { + "OS_CLOUD": "megacloud", + "OS_AUTH_URL": test_base.V3_AUTH_URL, + "OS_PROJECT_DOMAIN_ID": test_shell.DEFAULT_PROJECT_DOMAIN_ID, + "OS_USER_DOMAIN_ID": test_shell.DEFAULT_USER_DOMAIN_ID, + "OS_USERNAME": test_shell.DEFAULT_USERNAME, + "OS_IDENTITY_API_VERSION": "3", + "OS_CLOUD_NAME": "qaz", + } + self.useFixture(osc_lib_utils.EnvFixture(copy.deepcopy(env))) + + self.token = test_base.make_v3_token(self.requests_mock) + + # Patch a v3 auth URL into the o-c-c data + test_shell.PUBLIC_1['public-clouds']['megadodo']['auth']['auth_url'] \ + = test_base.V3_AUTH_URL + + @mock.patch("os_client_config.config.OpenStackConfig._load_vendor_file") + @mock.patch("os_client_config.config.OpenStackConfig._load_config_file") + def test_shell_args_precedence_1(self, config_mock, vendor_mock): + """Precedence run 1 + + Run 1 has --os-password on CLI + """ + + config_mock.return_value = ( + 'file.yaml', + copy.deepcopy(test_shell.CLOUD_2), + ) + vendor_mock.return_value = ( + 'file.yaml', + copy.deepcopy(test_shell.PUBLIC_1), + ) + _shell = shell.OpenStackShell() + _shell.run( + "--os-password qaz configuration show".split(), + ) + + # Check general calls + self.assertEqual(len(self.requests_mock.request_history), 2) + + # Check discovery request + self.assertEqual( + test_base.V3_AUTH_URL, + self.requests_mock.request_history[0].url, + ) + + # Check auth request + auth_req = self.requests_mock.request_history[1].json() + + # -env, -cli, -occ + # No test, everything not specified tests this + + # -env, -cli, +occ + self.assertEqual( + "heart-o-gold", + auth_req['auth']['scope']['project']['name'], + ) + + # -env, +cli, -occ + self.assertEqual( + 'qaz', + auth_req['auth']['identity']['password']['user']['password'], + ) + + # -env, +cli, +occ + + # +env, -cli, -occ + self.assertEqual( + test_shell.DEFAULT_USER_DOMAIN_ID, + auth_req['auth']['identity']['password']['user']['domain']['id'], + ) + + # +env, -cli, +occ + print("auth_req: %s" % auth_req['auth']) + self.assertEqual( + test_shell.DEFAULT_USERNAME, + auth_req['auth']['identity']['password']['user']['name'], + ) + + # +env, +cli, -occ + # see test_shell_args_precedence_2() + + # +env, +cli, +occ + # see test_shell_args_precedence_2() + + @mock.patch("os_client_config.config.OpenStackConfig._load_vendor_file") + @mock.patch("os_client_config.config.OpenStackConfig._load_config_file") + def test_shell_args_precedence_2(self, config_mock, vendor_mock): + """Precedence run 2 + + Run 2 has --os-username, --os-password, --os-project-domain-id on CLI + """ + + config_mock.return_value = ( + 'file.yaml', + copy.deepcopy(test_shell.CLOUD_2), + ) + vendor_mock.return_value = ( + 'file.yaml', + copy.deepcopy(test_shell.PUBLIC_1), + ) + _shell = shell.OpenStackShell() + _shell.run( + "--os-username zarquon --os-password qaz " + "--os-project-domain-id 5678 configuration show".split(), + ) + + # Check general calls + self.assertEqual(len(self.requests_mock.request_history), 2) + + # Check discovery request + self.assertEqual( + test_base.V3_AUTH_URL, + self.requests_mock.request_history[0].url, + ) + + # Check auth request + auth_req = self.requests_mock.request_history[1].json() + + # +env, +cli, -occ + self.assertEqual( + '5678', + auth_req['auth']['scope']['project']['domain']['id'], + ) + + # +env, +cli, +occ + print("auth_req: %s" % auth_req['auth']) + self.assertEqual( + 'zarquon', + auth_req['auth']['identity']['password']['user']['name'], + ) diff --git a/openstackclient/tests/unit/network/__init__.py b/openstackclient/tests/unit/network/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/openstackclient/tests/unit/network/__init__.py diff --git a/openstackclient/tests/unit/network/test_common.py b/openstackclient/tests/unit/network/test_common.py new file mode 100644 index 00000000..325aad2a --- /dev/null +++ b/openstackclient/tests/unit/network/test_common.py @@ -0,0 +1,174 @@ +# 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 argparse +import mock + +from openstackclient.network import common +from openstackclient.tests.unit import utils + + +def _add_common_argument(parser): + parser.add_argument( + 'common', + metavar='<common>', + help='Common argument', + ) + return parser + + +def _add_network_argument(parser): + parser.add_argument( + 'network', + metavar='<network>', + help='Network argument', + ) + return parser + + +def _add_compute_argument(parser): + parser.add_argument( + 'compute', + metavar='<compute>', + help='Compute argument', + ) + return parser + + +class FakeNetworkAndComputeCommand(common.NetworkAndComputeCommand): + + def update_parser_common(self, parser): + return _add_common_argument(parser) + + def update_parser_network(self, parser): + return _add_network_argument(parser) + + def update_parser_compute(self, parser): + return _add_compute_argument(parser) + + def take_action_network(self, client, parsed_args): + return client.network_action(parsed_args) + + def take_action_compute(self, client, parsed_args): + return client.compute_action(parsed_args) + + +class FakeNetworkAndComputeLister(common.NetworkAndComputeLister): + + def update_parser_common(self, parser): + return _add_common_argument(parser) + + def update_parser_network(self, parser): + return _add_network_argument(parser) + + def update_parser_compute(self, parser): + return _add_compute_argument(parser) + + def take_action_network(self, client, parsed_args): + return client.network_action(parsed_args) + + def take_action_compute(self, client, parsed_args): + return client.compute_action(parsed_args) + + +class FakeNetworkAndComputeShowOne(common.NetworkAndComputeShowOne): + + def update_parser_common(self, parser): + return _add_common_argument(parser) + + def update_parser_network(self, parser): + return _add_network_argument(parser) + + def update_parser_compute(self, parser): + return _add_compute_argument(parser) + + def take_action_network(self, client, parsed_args): + return client.network_action(parsed_args) + + def take_action_compute(self, client, parsed_args): + return client.compute_action(parsed_args) + + +class TestNetworkAndCompute(utils.TestCommand): + + def setUp(self): + super(TestNetworkAndCompute, self).setUp() + + self.namespace = argparse.Namespace() + + # Create network client mocks. + self.app.client_manager.network = mock.Mock() + self.network = self.app.client_manager.network + self.network.network_action = mock.Mock( + return_value='take_action_network') + + # Create compute client mocks. + self.app.client_manager.compute = mock.Mock() + self.compute = self.app.client_manager.compute + self.compute.compute_action = mock.Mock( + return_value='take_action_compute') + + # Subclasses can override the command object to test. + self.cmd = FakeNetworkAndComputeCommand(self.app, self.namespace) + + def test_take_action_network(self): + arglist = [ + 'common', + 'network' + ] + verifylist = [ + ('common', 'common'), + ('network', 'network') + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.network.network_action.assert_called_with(parsed_args) + self.assertEqual('take_action_network', result) + + def test_take_action_compute(self): + arglist = [ + 'common', + 'compute' + ] + verifylist = [ + ('common', 'common'), + ('compute', 'compute') + ] + + self.app.client_manager.network_endpoint_enabled = False + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.compute.compute_action.assert_called_with(parsed_args) + self.assertEqual('take_action_compute', result) + + +class TestNetworkAndComputeCommand(TestNetworkAndCompute): + + def setUp(self): + super(TestNetworkAndComputeCommand, self).setUp() + self.cmd = FakeNetworkAndComputeCommand(self.app, self.namespace) + + +class TestNetworkAndComputeLister(TestNetworkAndCompute): + + def setUp(self): + super(TestNetworkAndComputeLister, self).setUp() + self.cmd = FakeNetworkAndComputeLister(self.app, self.namespace) + + +class TestNetworkAndComputeShowOne(TestNetworkAndCompute): + + def setUp(self): + super(TestNetworkAndComputeShowOne, self).setUp() + self.cmd = FakeNetworkAndComputeShowOne(self.app, self.namespace) diff --git a/openstackclient/tests/unit/network/v2/__init__.py b/openstackclient/tests/unit/network/v2/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/openstackclient/tests/unit/network/v2/__init__.py diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py new file mode 100644 index 00000000..ed30bad3 --- /dev/null +++ b/openstackclient/tests/unit/network/v2/fakes.py @@ -0,0 +1,1100 @@ +# 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 argparse +import copy +import mock +import uuid + +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3 +from openstackclient.tests.unit import utils + + +QUOTA = { + "subnet": 10, + "network": 10, + "floatingip": 50, + "subnetpool": -1, + "security_group_rule": 100, + "security_group": 10, + "router": 10, + "rbac_policy": -1, + "port": 50, + "vip": 10, + "member": 10, + "health_monitor": 10, +} + + +class FakeNetworkV2Client(object): + + def __init__(self, **kwargs): + self.extensions = mock.Mock() + self.extensions.resource_class = fakes.FakeResource(None, {}) + + +class TestNetworkV2(utils.TestCommand): + + def setUp(self): + super(TestNetworkV2, self).setUp() + + self.namespace = argparse.Namespace() + + self.app.client_manager.session = mock.Mock() + + self.app.client_manager.network = FakeNetworkV2Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + + self.app.client_manager.identity = ( + identity_fakes_v3.FakeIdentityv3Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + ) + + +class FakeAddressScope(object): + """Fake one or more address scopes.""" + + @staticmethod + def create_one_address_scope(attrs=None): + """Create a fake address scope. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object with name, id, etc. + """ + attrs = attrs or {} + + # Set default attributes. + address_scope_attrs = { + 'name': 'address-scope-name-' + uuid.uuid4().hex, + 'id': 'address-scope-id-' + uuid.uuid4().hex, + 'tenant_id': 'project-id-' + uuid.uuid4().hex, + 'shared': False, + 'ip_version': 4, + } + + # Overwrite default attributes. + address_scope_attrs.update(attrs) + + address_scope = fakes.FakeResource( + info=copy.deepcopy(address_scope_attrs), + loaded=True) + + # Set attributes with special mapping in OpenStack SDK. + address_scope.project_id = address_scope_attrs['tenant_id'] + + return address_scope + + @staticmethod + def create_address_scopes(attrs=None, count=2): + """Create multiple fake address scopes. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of address scopes to fake + :return: + A list of FakeResource objects faking the address scopes + """ + address_scopes = [] + for i in range(0, count): + address_scopes.append( + FakeAddressScope.create_one_address_scope(attrs)) + + return address_scopes + + @staticmethod + def get_address_scopes(address_scopes=None, count=2): + """Get an iterable MagicMock object with a list of faked address scopes. + + If address scopes list is provided, then initialize the Mock object + with the list. Otherwise create one. + + :param List address scopes: + A list of FakeResource objects faking address scopes + :param int count: + The number of address scopes to fake + :return: + An iterable Mock object with side_effect set to a list of faked + address scopes + """ + if address_scopes is None: + address_scopes = FakeAddressScope.create_address_scopes(count) + return mock.MagicMock(side_effect=address_scopes) + + +class FakeAvailabilityZone(object): + """Fake one or more network availability zones (AZs).""" + + @staticmethod + def create_one_availability_zone(attrs=None): + """Create a fake AZ. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object with name, state, etc. + """ + attrs = attrs or {} + + # Set default attributes. + availability_zone = { + 'name': uuid.uuid4().hex, + 'state': 'available', + 'resource': 'network', + } + + # Overwrite default attributes. + availability_zone.update(attrs) + + availability_zone = fakes.FakeResource( + info=copy.deepcopy(availability_zone), + loaded=True) + return availability_zone + + @staticmethod + def create_availability_zones(attrs=None, count=2): + """Create multiple fake AZs. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of AZs to fake + :return: + A list of FakeResource objects faking the AZs + """ + availability_zones = [] + for i in range(0, count): + availability_zone = \ + FakeAvailabilityZone.create_one_availability_zone(attrs) + availability_zones.append(availability_zone) + + return availability_zones + + +class FakeIPAvailability(object): + """Fake one or more network ip availabilities.""" + + @staticmethod + def create_one_ip_availability(): + """Create a fake list with ip availability stats of a network. + + :return: + A FakeResource object with network_name, network_id, etc. + """ + + # Set default attributes. + network_ip_availability = { + 'network_id': 'network-id-' + uuid.uuid4().hex, + 'network_name': 'network-name-' + uuid.uuid4().hex, + 'tenant_id': '', + 'subnet_ip_availability': [], + 'total_ips': 254, + 'used_ips': 6, + } + + network_ip_availability = fakes.FakeResource( + info=copy.deepcopy(network_ip_availability), + loaded=True) + return network_ip_availability + + @staticmethod + def create_ip_availability(count=2): + """Create fake list of ip availability stats of multiple networks. + + :param int count: + The number of networks to fake + :return: + A list of FakeResource objects faking network ip availability stats + """ + network_ip_availabilities = [] + for i in range(0, count): + network_ip_availability = \ + FakeIPAvailability.create_one_ip_availability() + network_ip_availabilities.append(network_ip_availability) + + return network_ip_availabilities + + +class FakeExtension(object): + """Fake one or more extension.""" + + @staticmethod + def create_one_extension(attrs=None): + """Create a fake extension. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object with name, namespace, etc. + """ + attrs = attrs or {} + + # Set default attributes. + extension_info = { + 'name': 'name-' + uuid.uuid4().hex, + 'namespace': 'http://docs.openstack.org/network/', + 'description': 'description-' + uuid.uuid4().hex, + 'updated': '2013-07-09T12:00:0-00:00', + 'alias': 'Dystopian', + 'links': '[{"href":''"https://github.com/os/network", "type"}]', + } + + # Overwrite default attributes. + extension_info.update(attrs) + + extension = fakes.FakeResource( + info=copy.deepcopy(extension_info), + loaded=True) + return extension + + +class FakeNetwork(object): + """Fake one or more networks.""" + + @staticmethod + def create_one_network(attrs=None): + """Create a fake network. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, name, etc. + """ + attrs = attrs or {} + + # Set default attributes. + network_attrs = { + 'id': 'network-id-' + uuid.uuid4().hex, + 'name': 'network-name-' + uuid.uuid4().hex, + 'status': 'ACTIVE', + 'tenant_id': 'project-id-' + uuid.uuid4().hex, + 'admin_state_up': True, + 'shared': False, + 'subnets': ['a', 'b'], + 'provider_network_type': 'vlan', + 'router:external': True, + 'availability_zones': [], + 'availability_zone_hints': [], + 'is_default': False, + 'port_security_enabled': True, + } + + # Overwrite default attributes. + network_attrs.update(attrs) + + network = fakes.FakeResource(info=copy.deepcopy(network_attrs), + loaded=True) + + # Set attributes with special mapping in OpenStack SDK. + network.project_id = network_attrs['tenant_id'] + network.is_router_external = network_attrs['router:external'] + network.is_port_security_enabled = \ + network_attrs['port_security_enabled'] + + return network + + @staticmethod + def create_networks(attrs=None, count=2): + """Create multiple fake networks. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of networks to fake + :return: + A list of FakeResource objects faking the networks + """ + networks = [] + for i in range(0, count): + networks.append(FakeNetwork.create_one_network(attrs)) + + return networks + + @staticmethod + def get_networks(networks=None, count=2): + """Get an iterable MagicMock object with a list of faked networks. + + If networks list is provided, then initialize the Mock object with the + list. Otherwise create one. + + :param List networks: + A list of FakeResource objects faking networks + :param int count: + The number of networks to fake + :return: + An iterable Mock object with side_effect set to a list of faked + networks + """ + if networks is None: + networks = FakeNetwork.create_networks(count) + return mock.MagicMock(side_effect=networks) + + +class FakeNetworkSegment(object): + """Fake one or more network segments.""" + + @staticmethod + def create_one_network_segment(attrs=None): + """Create a fake network segment. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object faking the network segment + """ + attrs = attrs or {} + + # Set default attributes. + network_segment_attrs = { + 'id': 'network-segment-id-' + uuid.uuid4().hex, + 'network_id': 'network-id-' + uuid.uuid4().hex, + 'network_type': 'vlan', + 'physical_network': 'physical-network-name-' + uuid.uuid4().hex, + 'segmentation_id': 1024, + } + + # Overwrite default attributes. + network_segment_attrs.update(attrs) + + network_segment = fakes.FakeResource( + info=copy.deepcopy(network_segment_attrs), + loaded=True + ) + + return network_segment + + @staticmethod + def create_network_segments(attrs=None, count=2): + """Create multiple fake network segments. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of network segments to fake + :return: + A list of FakeResource objects faking the network segments + """ + network_segments = [] + for i in range(0, count): + network_segments.append( + FakeNetworkSegment.create_one_network_segment(attrs) + ) + return network_segments + + +class FakePort(object): + """Fake one or more ports.""" + + @staticmethod + def create_one_port(attrs=None): + """Create a fake port. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, name, etc. + """ + attrs = attrs or {} + + # Set default attributes. + port_attrs = { + 'admin_state_up': True, + 'allowed_address_pairs': [{}], + 'binding:host_id': 'binding-host-id-' + uuid.uuid4().hex, + 'binding:profile': {}, + 'binding:vif_details': {}, + 'binding:vif_type': 'ovs', + 'binding:vnic_type': 'normal', + 'device_id': 'device-id-' + uuid.uuid4().hex, + 'device_owner': 'compute:nova', + 'dns_assignment': [{}], + 'dns_name': 'dns-name-' + uuid.uuid4().hex, + 'extra_dhcp_opts': [{}], + 'fixed_ips': [{}], + 'id': 'port-id-' + uuid.uuid4().hex, + 'mac_address': 'fa:16:3e:a9:4e:72', + 'name': 'port-name-' + uuid.uuid4().hex, + 'network_id': 'network-id-' + uuid.uuid4().hex, + 'port_security_enabled': True, + 'security_groups': [], + 'status': 'ACTIVE', + 'tenant_id': 'project-id-' + uuid.uuid4().hex, + } + + # Overwrite default attributes. + port_attrs.update(attrs) + + port = fakes.FakeResource(info=copy.deepcopy(port_attrs), + loaded=True) + + # Set attributes with special mappings in OpenStack SDK. + port.project_id = port_attrs['tenant_id'] + port.binding_host_id = port_attrs['binding:host_id'] + port.binding_profile = port_attrs['binding:profile'] + port.binding_vif_details = port_attrs['binding:vif_details'] + port.binding_vif_type = port_attrs['binding:vif_type'] + port.binding_vnic_type = port_attrs['binding:vnic_type'] + + return port + + @staticmethod + def create_ports(attrs=None, count=2): + """Create multiple fake ports. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of ports to fake + :return: + A list of FakeResource objects faking the ports + """ + ports = [] + for i in range(0, count): + ports.append(FakePort.create_one_port(attrs)) + + return ports + + @staticmethod + def get_ports(ports=None, count=2): + """Get an iterable MagicMock object with a list of faked ports. + + If ports list is provided, then initialize the Mock object with the + list. Otherwise create one. + + :param List ports: + A list of FakeResource objects faking ports + :param int count: + The number of ports to fake + :return: + An iterable Mock object with side_effect set to a list of faked + ports + """ + if ports is None: + ports = FakePort.create_ports(count) + return mock.MagicMock(side_effect=ports) + + +class FakeNetworkAgent(object): + """Fake one or more network agents.""" + + @staticmethod + def create_one_network_agent(attrs=None): + """Create a fake network agent + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, agent_type, and so on. + """ + attrs = attrs or {} + + # Set default attributes + agent_attrs = { + 'id': 'agent-id-' + uuid.uuid4().hex, + 'agent_type': 'agent-type-' + uuid.uuid4().hex, + 'host': 'host-' + uuid.uuid4().hex, + 'availability_zone': 'zone-' + uuid.uuid4().hex, + 'alive': True, + 'admin_state_up': True, + 'binary': 'binary-' + uuid.uuid4().hex, + 'configurations': {'subnet': 2, 'networks': 1}, + } + agent_attrs.update(attrs) + agent = fakes.FakeResource(info=copy.deepcopy(agent_attrs), + loaded=True) + return agent + + @staticmethod + def create_network_agents(attrs=None, count=2): + """Create multiple fake network agents. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of network agents to fake + :return: + A list of FakeResource objects faking the network agents + """ + agents = [] + for i in range(0, count): + agents.append(FakeNetworkAgent.create_one_network_agent(attrs)) + + return agents + + @staticmethod + def get_network_agents(agents=None, count=2): + """Get an iterable MagicMock object with a list of faked network agents. + + If network agents list is provided, then initialize the Mock object + with the list. Otherwise create one. + + :param List agents: + A list of FakeResource objects faking network agents + :param int count: + The number of network agents to fake + :return: + An iterable Mock object with side_effect set to a list of faked + network agents + """ + if agents is None: + agents = FakeNetworkAgent.create_network_agents(count) + return mock.MagicMock(side_effect=agents) + + +class FakeNetworkRBAC(object): + """Fake one or more network rbac policies.""" + + @staticmethod + def create_one_network_rbac(attrs=None): + """Create a fake network rbac + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, action, target_tenant, + tenant_id, type + """ + attrs = attrs or {} + + # Set default attributes + rbac_attrs = { + 'id': 'rbac-id-' + uuid.uuid4().hex, + 'object_type': 'network', + 'object_id': 'object-id-' + uuid.uuid4().hex, + 'action': 'access_as_shared', + 'target_tenant': 'target-tenant-' + uuid.uuid4().hex, + 'tenant_id': 'tenant-id-' + uuid.uuid4().hex, + } + rbac_attrs.update(attrs) + rbac = fakes.FakeResource(info=copy.deepcopy(rbac_attrs), + loaded=True) + # Set attributes with special mapping in OpenStack SDK. + rbac.project_id = rbac_attrs['tenant_id'] + rbac.target_project_id = rbac_attrs['target_tenant'] + return rbac + + @staticmethod + def create_network_rbacs(attrs=None, count=2): + """Create multiple fake network rbac policies. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of rbac policies to fake + :return: + A list of FakeResource objects faking the rbac policies + """ + rbac_policies = [] + for i in range(0, count): + rbac_policies.append(FakeNetworkRBAC. + create_one_network_rbac(attrs)) + + return rbac_policies + + @staticmethod + def get_network_rbacs(rbac_policies=None, count=2): + """Get an iterable MagicMock object with a list of faked rbac policies. + + If rbac policies list is provided, then initialize the Mock object + with the list. Otherwise create one. + + :param List rbac_policies: + A list of FakeResource objects faking rbac policies + :param int count: + The number of rbac policies to fake + :return: + An iterable Mock object with side_effect set to a list of faked + rbac policies + """ + if rbac_policies is None: + rbac_policies = FakeNetworkRBAC.create_network_rbacs(count) + return mock.MagicMock(side_effect=rbac_policies) + + +class FakeRouter(object): + """Fake one or more routers.""" + + @staticmethod + def create_one_router(attrs=None): + """Create a fake router. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, name, admin_state_up, + status, tenant_id + """ + attrs = attrs or {} + + # Set default attributes. + router_attrs = { + 'id': 'router-id-' + uuid.uuid4().hex, + 'name': 'router-name-' + uuid.uuid4().hex, + 'status': 'ACTIVE', + 'admin_state_up': True, + 'distributed': False, + 'ha': False, + 'tenant_id': 'project-id-' + uuid.uuid4().hex, + 'routes': [], + 'external_gateway_info': {}, + 'availability_zone_hints': [], + 'availability_zones': [], + } + + # Overwrite default attributes. + router_attrs.update(attrs) + + router = fakes.FakeResource(info=copy.deepcopy(router_attrs), + loaded=True) + + # Set attributes with special mapping in OpenStack SDK. + router.project_id = router_attrs['tenant_id'] + + return router + + @staticmethod + def create_routers(attrs=None, count=2): + """Create multiple fake routers. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of routers to fake + :return: + A list of FakeResource objects faking the routers + """ + routers = [] + for i in range(0, count): + routers.append(FakeRouter.create_one_router(attrs)) + + return routers + + @staticmethod + def get_routers(routers=None, count=2): + """Get an iterable MagicMock object with a list of faked routers. + + If routers list is provided, then initialize the Mock object with the + list. Otherwise create one. + + :param List routers: + A list of FakeResource objects faking routers + :param int count: + The number of routers to fake + :return: + An iterable Mock object with side_effect set to a list of faked + routers + """ + if routers is None: + routers = FakeRouter.create_routers(count) + return mock.MagicMock(side_effect=routers) + + +class FakeSecurityGroup(object): + """Fake one or more security groups.""" + + @staticmethod + def create_one_security_group(attrs=None): + """Create a fake security group. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, name, etc. + """ + attrs = attrs or {} + + # Set default attributes. + security_group_attrs = { + 'id': 'security-group-id-' + uuid.uuid4().hex, + 'name': 'security-group-name-' + uuid.uuid4().hex, + 'description': 'security-group-description-' + uuid.uuid4().hex, + 'tenant_id': 'project-id-' + uuid.uuid4().hex, + 'security_group_rules': [], + } + + # Overwrite default attributes. + security_group_attrs.update(attrs) + + security_group = fakes.FakeResource( + info=copy.deepcopy(security_group_attrs), + loaded=True) + + # Set attributes with special mapping in OpenStack SDK. + security_group.project_id = security_group_attrs['tenant_id'] + + return security_group + + @staticmethod + def create_security_groups(attrs=None, count=2): + """Create multiple fake security groups. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of security groups to fake + :return: + A list of FakeResource objects faking the security groups + """ + security_groups = [] + for i in range(0, count): + security_groups.append( + FakeSecurityGroup.create_one_security_group(attrs)) + + return security_groups + + @staticmethod + def get_security_groups(security_groups=None, count=2): + """Get an iterable MagicMock object with a list of faked security groups. + + If security groups list is provided, then initialize the Mock object + with the list. Otherwise create one. + + :param List security groups: + A list of FakeResource objects faking security groups + :param int count: + The number of security groups to fake + :return: + An iterable Mock object with side_effect set to a list of faked + security groups + """ + if security_groups is None: + security_groups = FakeSecurityGroup.create_security_groups(count) + return mock.MagicMock(side_effect=security_groups) + + +class FakeSecurityGroupRule(object): + """Fake one or more security group rules.""" + + @staticmethod + def create_one_security_group_rule(attrs=None): + """Create a fake security group rule. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, etc. + """ + attrs = attrs or {} + + # Set default attributes. + security_group_rule_attrs = { + 'direction': 'ingress', + 'ethertype': 'IPv4', + 'id': 'security-group-rule-id-' + uuid.uuid4().hex, + 'port_range_max': None, + 'port_range_min': None, + 'protocol': 'tcp', + 'remote_group_id': None, + 'remote_ip_prefix': '0.0.0.0/0', + 'security_group_id': 'security-group-id-' + uuid.uuid4().hex, + 'tenant_id': 'project-id-' + uuid.uuid4().hex, + } + + # Overwrite default attributes. + security_group_rule_attrs.update(attrs) + + security_group_rule = fakes.FakeResource( + info=copy.deepcopy(security_group_rule_attrs), + loaded=True) + + # Set attributes with special mapping in OpenStack SDK. + security_group_rule.project_id = security_group_rule_attrs['tenant_id'] + + return security_group_rule + + @staticmethod + def create_security_group_rules(attrs=None, count=2): + """Create multiple fake security group rules. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of security group rules to fake + :return: + A list of FakeResource objects faking the security group rules + """ + security_group_rules = [] + for i in range(0, count): + security_group_rules.append( + FakeSecurityGroupRule.create_one_security_group_rule(attrs)) + + return security_group_rules + + @staticmethod + def get_security_group_rules(security_group_rules=None, count=2): + """Get an iterable MagicMock object with a list of faked security group rules. + + If security group rules list is provided, then initialize the Mock + object with the list. Otherwise create one. + + :param List security group rules: + A list of FakeResource objects faking security group rules + :param int count: + The number of security group rules to fake + :return: + An iterable Mock object with side_effect set to a list of faked + security group rules + """ + if security_group_rules is None: + security_group_rules = ( + FakeSecurityGroupRule.create_security_group_rules(count)) + return mock.MagicMock(side_effect=security_group_rules) + + +class FakeSubnet(object): + """Fake one or more subnets.""" + + @staticmethod + def create_one_subnet(attrs=None): + """Create a fake subnet. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object faking the subnet + """ + attrs = attrs or {} + + # Set default attributes. + project_id = 'project-id-' + uuid.uuid4().hex + subnet_attrs = { + 'id': 'subnet-id-' + uuid.uuid4().hex, + 'name': 'subnet-name-' + uuid.uuid4().hex, + 'network_id': 'network-id-' + uuid.uuid4().hex, + 'cidr': '10.10.10.0/24', + 'tenant_id': project_id, + 'enable_dhcp': True, + 'dns_nameservers': [], + 'allocation_pools': [], + 'host_routes': [], + 'ip_version': 4, + 'gateway_ip': '10.10.10.1', + 'ipv6_address_mode': None, + 'ipv6_ra_mode': None, + 'segment_id': None, + 'service_types': [], + 'subnetpool_id': None, + } + + # Overwrite default attributes. + subnet_attrs.update(attrs) + + subnet = fakes.FakeResource(info=copy.deepcopy(subnet_attrs), + loaded=True) + + # Set attributes with special mappings in OpenStack SDK. + subnet.project_id = subnet_attrs['tenant_id'] + + return subnet + + @staticmethod + def create_subnets(attrs=None, count=2): + """Create multiple fake subnets. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of subnets to fake + :return: + A list of FakeResource objects faking the subnets + """ + subnets = [] + for i in range(0, count): + subnets.append(FakeSubnet.create_one_subnet(attrs)) + + return subnets + + @staticmethod + def get_subnets(subnets=None, count=2): + """Get an iterable MagicMock object with a list of faked subnets. + + If subnets list is provided, then initialize the Mock object + with the list. Otherwise create one. + + :param List subnets: + A list of FakeResource objects faking subnets + :param int count: + The number of subnets to fake + :return: + An iterable Mock object with side_effect set to a list of faked + subnets + """ + if subnets is None: + subnets = FakeSubnet.create_subnets(count) + return mock.MagicMock(side_effect=subnets) + + +class FakeFloatingIP(object): + """Fake one or more floating ip.""" + + @staticmethod + def create_one_floating_ip(attrs=None): + """Create a fake floating ip. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, ip, and so on + """ + attrs = attrs or {} + + # Set default attributes. + floating_ip_attrs = { + 'id': 'floating-ip-id-' + uuid.uuid4().hex, + 'floating_ip_address': '1.0.9.0', + 'fixed_ip_address': '2.0.9.0', + 'dns_domain': None, + 'dns_name': None, + 'status': 'DOWN', + 'floating_network_id': 'network-id-' + uuid.uuid4().hex, + 'router_id': 'router-id-' + uuid.uuid4().hex, + 'port_id': 'port-id-' + uuid.uuid4().hex, + 'tenant_id': 'project-id-' + uuid.uuid4().hex, + } + + # Overwrite default attributes. + floating_ip_attrs.update(attrs) + + floating_ip = fakes.FakeResource( + info=copy.deepcopy(floating_ip_attrs), + loaded=True + ) + + # Set attributes with special mappings in OpenStack SDK. + floating_ip.project_id = floating_ip_attrs['tenant_id'] + + return floating_ip + + @staticmethod + def create_floating_ips(attrs=None, count=2): + """Create multiple fake floating ips. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of floating ips to fake + :return: + A list of FakeResource objects faking the floating ips + """ + floating_ips = [] + for i in range(0, count): + floating_ips.append(FakeFloatingIP.create_one_floating_ip(attrs)) + return floating_ips + + @staticmethod + def get_floating_ips(floating_ips=None, count=2): + """Get an iterable MagicMock object with a list of faked floating ips. + + If floating_ips list is provided, then initialize the Mock object + with the list. Otherwise create one. + + :param List floating ips: + A list of FakeResource objects faking floating ips + :param int count: + The number of floating ips to fake + :return: + An iterable Mock object with side_effect set to a list of faked + floating ips + """ + if floating_ips is None: + floating_ips = FakeFloatingIP.create_floating_ips(count) + return mock.MagicMock(side_effect=floating_ips) + + +class FakeSubnetPool(object): + """Fake one or more subnet pools.""" + + @staticmethod + def create_one_subnet_pool(attrs=None): + """Create a fake subnet pool. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object faking the subnet pool + """ + attrs = attrs or {} + + # Set default attributes. + subnet_pool_attrs = { + 'id': 'subnet-pool-id-' + uuid.uuid4().hex, + 'name': 'subnet-pool-name-' + uuid.uuid4().hex, + 'prefixes': ['10.0.0.0/24', '10.1.0.0/24'], + 'default_prefixlen': '8', + 'address_scope_id': 'address-scope-id-' + uuid.uuid4().hex, + 'tenant_id': 'project-id-' + uuid.uuid4().hex, + 'is_default': False, + 'shared': False, + 'max_prefixlen': '32', + 'min_prefixlen': '8', + 'default_quota': None, + 'ip_version': '4', + } + + # Overwrite default attributes. + subnet_pool_attrs.update(attrs) + + subnet_pool = fakes.FakeResource( + info=copy.deepcopy(subnet_pool_attrs), + loaded=True + ) + + # Set attributes with special mapping in OpenStack SDK. + subnet_pool.project_id = subnet_pool_attrs['tenant_id'] + + return subnet_pool + + @staticmethod + def create_subnet_pools(attrs=None, count=2): + """Create multiple fake subnet pools. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of subnet pools to fake + :return: + A list of FakeResource objects faking the subnet pools + """ + subnet_pools = [] + for i in range(0, count): + subnet_pools.append( + FakeSubnetPool.create_one_subnet_pool(attrs) + ) + + return subnet_pools + + @staticmethod + def get_subnet_pools(subnet_pools=None, count=2): + """Get an iterable MagicMock object with a list of faked subnet pools. + + If subnet_pools list is provided, then initialize the Mock object + with the list. Otherwise create one. + + :param List subnet pools: + A list of FakeResource objects faking subnet pools + :param int count: + The number of subnet pools to fake + :return: + An iterable Mock object with side_effect set to a list of faked + subnet pools + """ + if subnet_pools is None: + subnet_pools = FakeSubnetPool.create_subnet_pools(count) + return mock.MagicMock(side_effect=subnet_pools) diff --git a/openstackclient/tests/unit/network/v2/test_address_scope.py b/openstackclient/tests/unit/network/v2/test_address_scope.py new file mode 100644 index 00000000..6d3f4011 --- /dev/null +++ b/openstackclient/tests/unit/network/v2/test_address_scope.py @@ -0,0 +1,399 @@ +# 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 mock import call + +from osc_lib import exceptions + +from openstackclient.network.v2 import address_scope +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3 +from openstackclient.tests.unit.network.v2 import fakes as network_fakes +from openstackclient.tests.unit import utils as tests_utils + + +class TestAddressScope(network_fakes.TestNetworkV2): + + def setUp(self): + super(TestAddressScope, self).setUp() + + # Get a shortcut to the network client + self.network = self.app.client_manager.network + # Get a shortcut to the ProjectManager Mock + self.projects_mock = self.app.client_manager.identity.projects + # Get a shortcut to the DomainManager Mock + self.domains_mock = self.app.client_manager.identity.domains + + +class TestCreateAddressScope(TestAddressScope): + + project = identity_fakes_v3.FakeProject.create_one_project() + domain = identity_fakes_v3.FakeDomain.create_one_domain() + # The new address scope created. + new_address_scope = ( + network_fakes.FakeAddressScope.create_one_address_scope( + attrs={ + 'tenant_id': project.id, + } + )) + columns = ( + 'id', + 'ip_version', + 'name', + 'project_id', + 'shared' + ) + data = ( + new_address_scope.id, + new_address_scope.ip_version, + new_address_scope.name, + new_address_scope.project_id, + new_address_scope.shared, + ) + + def setUp(self): + super(TestCreateAddressScope, self).setUp() + self.network.create_address_scope = mock.Mock( + return_value=self.new_address_scope) + + # Get the command object to test + self.cmd = address_scope.CreateAddressScope(self.app, self.namespace) + + self.projects_mock.get.return_value = self.project + self.domains_mock.get.return_value = self.domain + + def test_create_no_options(self): + arglist = [] + verifylist = [] + + # Missing required args should bail here + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_create_default_options(self): + arglist = [ + self.new_address_scope.name, + ] + verifylist = [ + ('project', None), + ('ip_version', self.new_address_scope.ip_version), + ('name', self.new_address_scope.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = (self.cmd.take_action(parsed_args)) + + self.network.create_address_scope.assert_called_once_with(**{ + 'ip_version': self.new_address_scope.ip_version, + 'name': self.new_address_scope.name, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_create_all_options(self): + arglist = [ + '--ip-version', str(self.new_address_scope.ip_version), + '--share', + '--project', self.project.name, + '--project-domain', self.domain.name, + self.new_address_scope.name, + ] + verifylist = [ + ('ip_version', self.new_address_scope.ip_version), + ('share', True), + ('project', self.project.name), + ('project_domain', self.domain.name), + ('name', self.new_address_scope.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = (self.cmd.take_action(parsed_args)) + + self.network.create_address_scope.assert_called_once_with(**{ + 'ip_version': self.new_address_scope.ip_version, + 'shared': True, + 'tenant_id': self.project.id, + 'name': self.new_address_scope.name, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_create_no_share(self): + arglist = [ + '--no-share', + self.new_address_scope.name, + ] + verifylist = [ + ('no_share', True), + ('name', self.new_address_scope.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_address_scope.assert_called_once_with(**{ + 'ip_version': self.new_address_scope.ip_version, + 'shared': False, + 'name': self.new_address_scope.name, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestDeleteAddressScope(TestAddressScope): + + # The address scope to delete. + _address_scopes = ( + network_fakes.FakeAddressScope.create_address_scopes(count=2)) + + def setUp(self): + super(TestDeleteAddressScope, self).setUp() + self.network.delete_address_scope = mock.Mock(return_value=None) + self.network.find_address_scope = ( + network_fakes.FakeAddressScope.get_address_scopes( + address_scopes=self._address_scopes) + ) + + # Get the command object to test + self.cmd = address_scope.DeleteAddressScope(self.app, self.namespace) + + def test_address_scope_delete(self): + arglist = [ + self._address_scopes[0].name, + ] + verifylist = [ + ('address_scope', [self._address_scopes[0].name]), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.network.find_address_scope.assert_called_once_with( + self._address_scopes[0].name, ignore_missing=False) + self.network.delete_address_scope.assert_called_once_with( + self._address_scopes[0]) + self.assertIsNone(result) + + def test_multi_address_scopes_delete(self): + arglist = [] + verifylist = [] + + for a in self._address_scopes: + arglist.append(a.name) + verifylist = [ + ('address_scope', arglist), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + calls = [] + for a in self._address_scopes: + calls.append(call(a)) + self.network.delete_address_scope.assert_has_calls(calls) + self.assertIsNone(result) + + def test_multi_address_scopes_delete_with_exception(self): + arglist = [ + self._address_scopes[0].name, + 'unexist_address_scope', + ] + verifylist = [ + ('address_scope', + [self._address_scopes[0].name, 'unexist_address_scope']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + find_mock_result = [self._address_scopes[0], exceptions.CommandError] + self.network.find_address_scope = ( + mock.MagicMock(side_effect=find_mock_result) + ) + + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 address scopes failed to delete.', str(e)) + + self.network.find_address_scope.assert_any_call( + self._address_scopes[0].name, ignore_missing=False) + self.network.find_address_scope.assert_any_call( + 'unexist_address_scope', ignore_missing=False) + self.network.delete_address_scope.assert_called_once_with( + self._address_scopes[0] + ) + + +class TestListAddressScope(TestAddressScope): + + # The address scopes to list up. + address_scopes = ( + network_fakes.FakeAddressScope.create_address_scopes(count=3)) + columns = ( + 'ID', + 'Name', + 'IP Version', + 'Shared', + 'Project', + ) + data = [] + for scope in address_scopes: + data.append(( + scope.id, + scope.name, + scope.ip_version, + scope.shared, + scope.project_id, + )) + + def setUp(self): + super(TestListAddressScope, self).setUp() + self.network.address_scopes = mock.Mock( + return_value=self.address_scopes) + + # Get the command object to test + self.cmd = address_scope.ListAddressScope(self.app, self.namespace) + + def test_address_scope_list(self): + arglist = [] + verifylist = [] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.network.address_scopes.assert_called_once_with(**{}) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + +class TestSetAddressScope(TestAddressScope): + + # The address scope to set. + _address_scope = network_fakes.FakeAddressScope.create_one_address_scope() + + def setUp(self): + super(TestSetAddressScope, self).setUp() + self.network.update_address_scope = mock.Mock(return_value=None) + self.network.find_address_scope = mock.Mock( + return_value=self._address_scope) + + # Get the command object to test + self.cmd = address_scope.SetAddressScope(self.app, self.namespace) + + def test_set_nothing(self): + arglist = [self._address_scope.name, ] + verifylist = [ + ('address_scope', self._address_scope.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = {} + self.network.update_address_scope.assert_called_with( + self._address_scope, **attrs) + self.assertIsNone(result) + + def test_set_name_and_share(self): + arglist = [ + '--name', 'new_address_scope', + '--share', + self._address_scope.name, + ] + verifylist = [ + ('name', 'new_address_scope'), + ('share', True), + ('address_scope', self._address_scope.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + attrs = { + 'name': "new_address_scope", + 'shared': True, + } + self.network.update_address_scope.assert_called_with( + self._address_scope, **attrs) + self.assertIsNone(result) + + def test_set_no_share(self): + arglist = [ + '--no-share', + self._address_scope.name, + ] + verifylist = [ + ('no_share', True), + ('address_scope', self._address_scope.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + attrs = { + 'shared': False, + } + self.network.update_address_scope.assert_called_with( + self._address_scope, **attrs) + self.assertIsNone(result) + + +class TestShowAddressScope(TestAddressScope): + + # The address scope to show. + _address_scope = ( + network_fakes.FakeAddressScope.create_one_address_scope()) + columns = ( + 'id', + 'ip_version', + 'name', + 'project_id', + 'shared', + ) + data = ( + _address_scope.id, + _address_scope.ip_version, + _address_scope.name, + _address_scope.project_id, + _address_scope.shared, + ) + + def setUp(self): + super(TestShowAddressScope, self).setUp() + self.network.find_address_scope = mock.Mock( + return_value=self._address_scope) + + # Get the command object to test + self.cmd = address_scope.ShowAddressScope(self.app, self.namespace) + + def test_show_no_options(self): + arglist = [] + verifylist = [] + + # Missing required args should bail here + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_show_all_options(self): + arglist = [ + self._address_scope.name, + ] + verifylist = [ + ('address_scope', self._address_scope.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.network.find_address_scope.assert_called_once_with( + self._address_scope.name, ignore_missing=False) + self.assertEqual(self.columns, columns) + self.assertEqual(list(self.data), list(data)) diff --git a/openstackclient/tests/unit/network/v2/test_floating_ip.py b/openstackclient/tests/unit/network/v2/test_floating_ip.py new file mode 100644 index 00000000..a40e48f4 --- /dev/null +++ b/openstackclient/tests/unit/network/v2/test_floating_ip.py @@ -0,0 +1,565 @@ +# 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 mock import call + +from osc_lib import exceptions + +from openstackclient.network.v2 import floating_ip +from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes +from openstackclient.tests.unit.network.v2 import fakes as network_fakes +from openstackclient.tests.unit import utils as tests_utils + + +# Tests for Neutron network +# +class TestFloatingIPNetwork(network_fakes.TestNetworkV2): + + def setUp(self): + super(TestFloatingIPNetwork, self).setUp() + + # Get a shortcut to the network client + self.network = self.app.client_manager.network + + +class TestCreateFloatingIPNetwork(TestFloatingIPNetwork): + + # Fake data for option tests. + floating_network = network_fakes.FakeNetwork.create_one_network() + subnet = network_fakes.FakeSubnet.create_one_subnet() + port = network_fakes.FakePort.create_one_port() + + # The floating ip to be deleted. + floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip( + attrs={ + 'floating_network_id': floating_network.id, + 'port_id': port.id, + } + ) + + columns = ( + 'dns_domain', + 'dns_name', + 'fixed_ip_address', + 'floating_ip_address', + 'floating_network_id', + 'id', + 'port_id', + 'project_id', + 'router_id', + 'status', + ) + + data = ( + floating_ip.dns_domain, + floating_ip.dns_name, + floating_ip.fixed_ip_address, + floating_ip.floating_ip_address, + floating_ip.floating_network_id, + floating_ip.id, + floating_ip.port_id, + floating_ip.project_id, + floating_ip.router_id, + floating_ip.status, + ) + + def setUp(self): + super(TestCreateFloatingIPNetwork, self).setUp() + + self.network.create_ip = mock.Mock(return_value=self.floating_ip) + + self.network.find_network = mock.Mock( + return_value=self.floating_network) + self.network.find_subnet = mock.Mock(return_value=self.subnet) + self.network.find_port = mock.Mock(return_value=self.port) + + # Get the command object to test + self.cmd = floating_ip.CreateFloatingIP(self.app, self.namespace) + + def test_create_no_options(self): + arglist = [] + verifylist = [] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_create_default_options(self): + arglist = [ + self.floating_ip.floating_network_id, + ] + verifylist = [ + ('network', self.floating_ip.floating_network_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_ip.assert_called_once_with(**{ + 'floating_network_id': self.floating_ip.floating_network_id, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_create_all_options(self): + arglist = [ + '--subnet', self.subnet.id, + '--port', self.floating_ip.port_id, + '--floating-ip-address', self.floating_ip.floating_ip_address, + '--fixed-ip-address', self.floating_ip.fixed_ip_address, + self.floating_ip.floating_network_id, + ] + verifylist = [ + ('subnet', self.subnet.id), + ('port', self.floating_ip.port_id), + ('floating_ip_address', self.floating_ip.floating_ip_address), + ('fixed_ip_address', self.floating_ip.fixed_ip_address), + ('network', self.floating_ip.floating_network_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_ip.assert_called_once_with(**{ + 'subnet_id': self.subnet.id, + 'port_id': self.floating_ip.port_id, + 'floating_ip_address': self.floating_ip.floating_ip_address, + 'fixed_ip_address': self.floating_ip.fixed_ip_address, + 'floating_network_id': self.floating_ip.floating_network_id, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestDeleteFloatingIPNetwork(TestFloatingIPNetwork): + + # The floating ips to be deleted. + floating_ips = network_fakes.FakeFloatingIP.create_floating_ips(count=2) + + def setUp(self): + super(TestDeleteFloatingIPNetwork, self).setUp() + + self.network.delete_ip = mock.Mock(return_value=None) + self.network.find_ip = ( + network_fakes.FakeFloatingIP.get_floating_ips(self.floating_ips)) + + # Get the command object to test + self.cmd = floating_ip.DeleteFloatingIP(self.app, self.namespace) + + def test_floating_ip_delete(self): + arglist = [ + self.floating_ips[0].id, + ] + verifylist = [ + ('floating_ip', [self.floating_ips[0].id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.network.find_ip.assert_called_once_with( + self.floating_ips[0].id, ignore_missing=False) + self.network.delete_ip.assert_called_once_with(self.floating_ips[0]) + self.assertIsNone(result) + + def test_multi_floating_ips_delete(self): + arglist = [] + verifylist = [] + + for f in self.floating_ips: + arglist.append(f.id) + verifylist = [ + ('floating_ip', arglist), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + calls = [] + for f in self.floating_ips: + calls.append(call(f)) + self.network.delete_ip.assert_has_calls(calls) + self.assertIsNone(result) + + def test_multi_floating_ips_delete_with_exception(self): + arglist = [ + self.floating_ips[0].id, + 'unexist_floating_ip', + ] + verifylist = [ + ('floating_ip', + [self.floating_ips[0].id, 'unexist_floating_ip']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + find_mock_result = [self.floating_ips[0], exceptions.CommandError] + self.network.find_ip = ( + mock.MagicMock(side_effect=find_mock_result) + ) + + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 floating_ips failed to delete.', str(e)) + + self.network.find_ip.assert_any_call( + self.floating_ips[0].id, ignore_missing=False) + self.network.find_ip.assert_any_call( + 'unexist_floating_ip', ignore_missing=False) + self.network.delete_ip.assert_called_once_with( + self.floating_ips[0] + ) + + +class TestListFloatingIPNetwork(TestFloatingIPNetwork): + + # The floating ips to list up + floating_ips = network_fakes.FakeFloatingIP.create_floating_ips(count=3) + + columns = ( + 'ID', + 'Floating IP Address', + 'Fixed IP Address', + 'Port', + ) + + data = [] + for ip in floating_ips: + data.append(( + ip.id, + ip.floating_ip_address, + ip.fixed_ip_address, + ip.port_id, + )) + + def setUp(self): + super(TestListFloatingIPNetwork, self).setUp() + + self.network.ips = mock.Mock(return_value=self.floating_ips) + + # Get the command object to test + self.cmd = floating_ip.ListFloatingIP(self.app, self.namespace) + + def test_floating_ip_list(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.ips.assert_called_once_with(**{}) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + +class TestShowFloatingIPNetwork(TestFloatingIPNetwork): + + # The floating ip to display. + floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip() + + columns = ( + 'dns_domain', + 'dns_name', + 'fixed_ip_address', + 'floating_ip_address', + 'floating_network_id', + 'id', + 'port_id', + 'project_id', + 'router_id', + 'status', + ) + + data = ( + floating_ip.dns_domain, + floating_ip.dns_name, + floating_ip.fixed_ip_address, + floating_ip.floating_ip_address, + floating_ip.floating_network_id, + floating_ip.id, + floating_ip.port_id, + floating_ip.tenant_id, + floating_ip.router_id, + floating_ip.status, + ) + + def setUp(self): + super(TestShowFloatingIPNetwork, self).setUp() + + self.network.find_ip = mock.Mock(return_value=self.floating_ip) + + # Get the command object to test + self.cmd = floating_ip.ShowFloatingIP(self.app, self.namespace) + + def test_floating_ip_show(self): + arglist = [ + self.floating_ip.id, + ] + verifylist = [ + ('floating_ip', self.floating_ip.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.find_ip.assert_called_once_with( + self.floating_ip.id, + ignore_missing=False + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +# Tests for Nova network +# +class TestFloatingIPCompute(compute_fakes.TestComputev2): + + def setUp(self): + super(TestFloatingIPCompute, self).setUp() + + # Get a shortcut to the compute client + self.compute = self.app.client_manager.compute + + +class TestCreateFloatingIPCompute(TestFloatingIPCompute): + + # The floating ip to be deleted. + floating_ip = compute_fakes.FakeFloatingIP.create_one_floating_ip() + + columns = ( + 'fixed_ip', + 'id', + 'instance_id', + 'ip', + 'pool', + ) + + data = ( + floating_ip.fixed_ip, + floating_ip.id, + floating_ip.instance_id, + floating_ip.ip, + floating_ip.pool, + ) + + def setUp(self): + super(TestCreateFloatingIPCompute, self).setUp() + + self.app.client_manager.network_endpoint_enabled = False + + self.compute.floating_ips.create.return_value = self.floating_ip + + # Get the command object to test + self.cmd = floating_ip.CreateFloatingIP(self.app, None) + + def test_create_no_options(self): + arglist = [] + verifylist = [] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_create_default_options(self): + arglist = [ + self.floating_ip.pool, + ] + verifylist = [ + ('network', self.floating_ip.pool), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.compute.floating_ips.create.assert_called_once_with( + self.floating_ip.pool) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestDeleteFloatingIPCompute(TestFloatingIPCompute): + + # The floating ips to be deleted. + floating_ips = compute_fakes.FakeFloatingIP.create_floating_ips(count=2) + + def setUp(self): + super(TestDeleteFloatingIPCompute, self).setUp() + + self.app.client_manager.network_endpoint_enabled = False + + self.compute.floating_ips.delete.return_value = None + + # Return value of utils.find_resource() + self.compute.floating_ips.get = ( + compute_fakes.FakeFloatingIP.get_floating_ips(self.floating_ips)) + + # Get the command object to test + self.cmd = floating_ip.DeleteFloatingIP(self.app, None) + + def test_floating_ip_delete(self): + arglist = [ + self.floating_ips[0].id, + ] + verifylist = [ + ('floating_ip', [self.floating_ips[0].id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.compute.floating_ips.delete.assert_called_once_with( + self.floating_ips[0].id + ) + self.assertIsNone(result) + + def test_multi_floating_ips_delete(self): + arglist = [] + verifylist = [] + + for f in self.floating_ips: + arglist.append(f.id) + verifylist = [ + ('floating_ip', arglist), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + calls = [] + for f in self.floating_ips: + calls.append(call(f.id)) + self.compute.floating_ips.delete.assert_has_calls(calls) + self.assertIsNone(result) + + def test_multi_floating_ips_delete_with_exception(self): + arglist = [ + self.floating_ips[0].id, + 'unexist_floating_ip', + ] + verifylist = [ + ('floating_ip', + [self.floating_ips[0].id, 'unexist_floating_ip']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + find_mock_result = [self.floating_ips[0], exceptions.CommandError] + self.compute.floating_ips.get = ( + mock.MagicMock(side_effect=find_mock_result) + ) + self.compute.floating_ips.find.side_effect = exceptions.NotFound(None) + + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 floating_ips failed to delete.', str(e)) + + self.compute.floating_ips.get.assert_any_call( + self.floating_ips[0].id) + self.compute.floating_ips.get.assert_any_call( + 'unexist_floating_ip') + self.compute.floating_ips.delete.assert_called_once_with( + self.floating_ips[0].id + ) + + +class TestListFloatingIPCompute(TestFloatingIPCompute): + + # The floating ips to be list up + floating_ips = compute_fakes.FakeFloatingIP.create_floating_ips(count=3) + + columns = ( + 'ID', + 'Floating IP Address', + 'Fixed IP Address', + 'Server', + 'Pool', + ) + + data = [] + for ip in floating_ips: + data.append(( + ip.id, + ip.ip, + ip.fixed_ip, + ip.instance_id, + ip.pool, + )) + + def setUp(self): + super(TestListFloatingIPCompute, self).setUp() + + self.app.client_manager.network_endpoint_enabled = False + + self.compute.floating_ips.list.return_value = self.floating_ips + + # Get the command object to test + self.cmd = floating_ip.ListFloatingIP(self.app, None) + + def test_floating_ip_list(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.compute.floating_ips.list.assert_called_once_with() + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + +class TestShowFloatingIPCompute(TestFloatingIPCompute): + + # The floating ip to display. + floating_ip = compute_fakes.FakeFloatingIP.create_one_floating_ip() + + columns = ( + 'fixed_ip', + 'id', + 'instance_id', + 'ip', + 'pool', + ) + + data = ( + floating_ip.fixed_ip, + floating_ip.id, + floating_ip.instance_id, + floating_ip.ip, + floating_ip.pool, + ) + + def setUp(self): + super(TestShowFloatingIPCompute, self).setUp() + + self.app.client_manager.network_endpoint_enabled = False + + # Return value of utils.find_resource() + self.compute.floating_ips.get.return_value = self.floating_ip + + # Get the command object to test + self.cmd = floating_ip.ShowFloatingIP(self.app, None) + + def test_floating_ip_show(self): + arglist = [ + self.floating_ip.id, + ] + verifylist = [ + ('floating_ip', self.floating_ip.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/network/v2/test_floating_ip_pool.py b/openstackclient/tests/unit/network/v2/test_floating_ip_pool.py new file mode 100644 index 00000000..11d01d36 --- /dev/null +++ b/openstackclient/tests/unit/network/v2/test_floating_ip_pool.py @@ -0,0 +1,97 @@ +# 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 osc_lib import exceptions + +from openstackclient.network.v2 import floating_ip_pool +from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes +from openstackclient.tests.unit.network.v2 import fakes as network_fakes + + +# Tests for Network API v2 +# +class TestFloatingIPPoolNetwork(network_fakes.TestNetworkV2): + + def setUp(self): + super(TestFloatingIPPoolNetwork, self).setUp() + + # Get a shortcut to the network client + self.network = self.app.client_manager.network + + +class TestListFloatingIPPoolNetwork(TestFloatingIPPoolNetwork): + + def setUp(self): + super(TestListFloatingIPPoolNetwork, self).setUp() + + # Get the command object to test + self.cmd = floating_ip_pool.ListFloatingIPPool(self.app, + self.namespace) + + def test_floating_ip_list(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises(exceptions.CommandError, self.cmd.take_action, + parsed_args) + + +# Tests for Compute network +# +class TestFloatingIPPoolCompute(compute_fakes.TestComputev2): + + def setUp(self): + super(TestFloatingIPPoolCompute, self).setUp() + + # Get a shortcut to the compute client + self.compute = self.app.client_manager.compute + + +class TestListFloatingIPPoolCompute(TestFloatingIPPoolCompute): + + # The floating ip pools to list up + floating_ip_pools = \ + compute_fakes.FakeFloatingIPPool.create_floating_ip_pools(count=3) + + columns = ( + 'Name', + ) + + data = [] + for pool in floating_ip_pools: + data.append(( + pool.name, + )) + + def setUp(self): + super(TestListFloatingIPPoolCompute, self).setUp() + + self.app.client_manager.network_endpoint_enabled = False + + self.compute.floating_ip_pools.list.return_value = \ + self.floating_ip_pools + + # Get the command object to test + self.cmd = floating_ip_pool.ListFloatingIPPool(self.app, None) + + def test_floating_ip_list(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.compute.floating_ip_pools.list.assert_called_once_with() + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) diff --git a/openstackclient/tests/unit/network/v2/test_ip_availability.py b/openstackclient/tests/unit/network/v2/test_ip_availability.py new file mode 100644 index 00000000..c929ab82 --- /dev/null +++ b/openstackclient/tests/unit/network/v2/test_ip_availability.py @@ -0,0 +1,172 @@ +# 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 osc_lib import utils as common_utils + +from openstackclient.network.v2 import ip_availability +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes +from openstackclient.tests.unit.network.v2 import fakes as network_fakes +from openstackclient.tests.unit import utils as tests_utils + + +class TestIPAvailability(network_fakes.TestNetworkV2): + + def setUp(self): + super(TestIPAvailability, self).setUp() + + # Get a shortcut to the network client + self.network = self.app.client_manager.network + + # Get a shortcut to the ProjectManager Mock + self.projects_mock = self.app.client_manager.identity.projects + + self.project = identity_fakes.FakeProject.create_one_project() + self.projects_mock.get.return_value = self.project + + +class TestListIPAvailability(TestIPAvailability): + + _ip_availability = \ + network_fakes.FakeIPAvailability.create_ip_availability(count=3) + columns = ( + 'Network ID', + 'Network Name', + 'Total IPs', + 'Used IPs', + ) + data = [] + for net in _ip_availability: + data.append(( + net.network_id, + net.network_name, + net.total_ips, + net.used_ips, + )) + + def setUp(self): + super(TestListIPAvailability, self).setUp() + + self.cmd = ip_availability.ListIPAvailability( + self.app, self.namespace) + self.network.network_ip_availabilities = mock.Mock( + return_value=self._ip_availability) + + def test_list_no_options(self): + arglist = [] + verifylist = [] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + filters = {'ip_version': 4} + + self.network.network_ip_availabilities.assert_called_once_with( + **filters) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_list_ip_version(self): + arglist = [ + '--ip-version', str(4), + ] + verifylist = [ + ('ip_version', 4) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + filters = {'ip_version': 4} + + self.network.network_ip_availabilities.assert_called_once_with( + **filters) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_list_project(self): + arglist = [ + '--project', self.project.name + ] + verifylist = [ + ('project', self.project.name) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + filters = {'tenant_id': self.project.id, + 'ip_version': 4} + + self.network.network_ip_availabilities.assert_called_once_with( + **filters) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + +class TestShowIPAvailability(TestIPAvailability): + + _ip_availability = \ + network_fakes.FakeIPAvailability.create_one_ip_availability() + + columns = ( + 'network_id', + 'network_name', + 'project_id', + 'subnet_ip_availability', + 'total_ips', + 'used_ips', + ) + data = ( + _ip_availability.network_id, + _ip_availability.network_name, + _ip_availability.tenant_id, + common_utils.format_list( + _ip_availability.subnet_ip_availability), + _ip_availability.total_ips, + _ip_availability.used_ips, + ) + + def setUp(self): + super(TestShowIPAvailability, self).setUp() + + self.network.find_network_ip_availability = mock.Mock( + return_value=self._ip_availability) + + # Get the command object to test + self.cmd = ip_availability.ShowIPAvailability( + self.app, self.namespace) + + def test_show_no_option(self): + arglist = [] + verifylist = [] + + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_show_all_options(self): + arglist = [ + self._ip_availability.network_name, + ] + verifylist = [ + ('network', self._ip_availability.network_name) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.network.find_network_ip_availability.assert_called_once_with( + self._ip_availability.network_name, + ignore_missing=False) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/network/v2/test_network.py b/openstackclient/tests/unit/network/v2/test_network.py new file mode 100644 index 00000000..84ead093 --- /dev/null +++ b/openstackclient/tests/unit/network/v2/test_network.py @@ -0,0 +1,1059 @@ +# 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 mock import call + +from osc_lib import exceptions +from osc_lib import utils + +from openstackclient.network.v2 import network +from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes_v2 +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3 +from openstackclient.tests.unit.network.v2 import fakes as network_fakes +from openstackclient.tests.unit import utils as tests_utils + + +# Tests for Neutron network +# +class TestNetwork(network_fakes.TestNetworkV2): + + def setUp(self): + super(TestNetwork, self).setUp() + + # Get a shortcut to the network client + self.network = self.app.client_manager.network + # Get a shortcut to the ProjectManager Mock + self.projects_mock = self.app.client_manager.identity.projects + # Get a shortcut to the DomainManager Mock + self.domains_mock = self.app.client_manager.identity.domains + + +class TestCreateNetworkIdentityV3(TestNetwork): + + project = identity_fakes_v3.FakeProject.create_one_project() + domain = identity_fakes_v3.FakeDomain.create_one_domain() + # The new network created. + _network = network_fakes.FakeNetwork.create_one_network( + attrs={ + 'tenant_id': project.id, + 'availability_zone_hints': ["nova"], + } + ) + + columns = ( + 'admin_state_up', + 'availability_zone_hints', + 'availability_zones', + 'id', + 'is_default', + 'name', + 'port_security_enabled', + 'project_id', + 'provider_network_type', + 'router:external', + 'shared', + 'status', + 'subnets', + ) + + data = ( + network._format_admin_state(_network.admin_state_up), + utils.format_list(_network.availability_zone_hints), + utils.format_list(_network.availability_zones), + _network.id, + _network.is_default, + _network.name, + _network.is_port_security_enabled, + _network.project_id, + _network.provider_network_type, + network._format_router_external(_network.is_router_external), + _network.shared, + _network.status, + utils.format_list(_network.subnets), + ) + + def setUp(self): + super(TestCreateNetworkIdentityV3, self).setUp() + + self.network.create_network = mock.Mock(return_value=self._network) + + # Get the command object to test + self.cmd = network.CreateNetwork(self.app, self.namespace) + + self.projects_mock.get.return_value = self.project + self.domains_mock.get.return_value = self.domain + + def test_create_no_options(self): + arglist = [] + verifylist = [] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_create_default_options(self): + arglist = [ + self._network.name, + ] + verifylist = [ + ('name', self._network.name), + ('enable', True), + ('share', None), + ('project', None), + ('external', False), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_network.assert_called_once_with(**{ + 'admin_state_up': True, + 'name': self._network.name, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_create_all_options(self): + arglist = [ + "--disable", + "--share", + "--project", self.project.name, + "--project-domain", self.domain.name, + "--availability-zone-hint", "nova", + "--external", "--default", + "--provider-network-type", "vlan", + "--provider-physical-network", "physnet1", + "--provider-segment", "400", + "--transparent-vlan", + "--enable-port-security", + self._network.name, + ] + verifylist = [ + ('disable', True), + ('share', True), + ('project', self.project.name), + ('project_domain', self.domain.name), + ('availability_zone_hints', ["nova"]), + ('external', True), + ('default', True), + ('provider_network_type', 'vlan'), + ('physical_network', 'physnet1'), + ('segmentation_id', '400'), + ('transparent_vlan', True), + ('enable_port_security', True), + ('name', self._network.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = (self.cmd.take_action(parsed_args)) + + self.network.create_network.assert_called_once_with(**{ + 'admin_state_up': False, + 'availability_zone_hints': ["nova"], + 'name': self._network.name, + 'shared': True, + 'tenant_id': self.project.id, + 'is_default': True, + 'router:external': True, + 'provider:network_type': 'vlan', + 'provider:physical_network': 'physnet1', + 'provider:segmentation_id': '400', + 'vlan_transparent': True, + 'port_security_enabled': True, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_create_other_options(self): + arglist = [ + "--enable", + "--no-share", + "--disable-port-security", + self._network.name, + ] + verifylist = [ + ('enable', True), + ('no_share', True), + ('name', self._network.name), + ('external', False), + ('disable_port_security', True), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_network.assert_called_once_with(**{ + 'admin_state_up': True, + 'name': self._network.name, + 'shared': False, + 'port_security_enabled': False, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestCreateNetworkIdentityV2(TestNetwork): + + project = identity_fakes_v2.FakeProject.create_one_project() + # The new network created. + _network = network_fakes.FakeNetwork.create_one_network( + attrs={'tenant_id': project.id} + ) + + columns = ( + 'admin_state_up', + 'availability_zone_hints', + 'availability_zones', + 'id', + 'is_default', + 'name', + 'port_security_enabled', + 'project_id', + 'provider_network_type', + 'router:external', + 'shared', + 'status', + 'subnets', + ) + + data = ( + network._format_admin_state(_network.admin_state_up), + utils.format_list(_network.availability_zone_hints), + utils.format_list(_network.availability_zones), + _network.id, + _network.is_default, + _network.name, + _network.is_port_security_enabled, + _network.project_id, + _network.provider_network_type, + network._format_router_external(_network.is_router_external), + _network.shared, + _network.status, + utils.format_list(_network.subnets), + ) + + def setUp(self): + super(TestCreateNetworkIdentityV2, self).setUp() + + self.network.create_network = mock.Mock(return_value=self._network) + + # Get the command object to test + self.cmd = network.CreateNetwork(self.app, self.namespace) + + # Set identity client v2. And get a shortcut to Identity client. + identity_client = identity_fakes_v2.FakeIdentityv2Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + self.app.client_manager.identity = identity_client + self.identity = self.app.client_manager.identity + + # Get a shortcut to the ProjectManager Mock + self.projects_mock = self.identity.tenants + self.projects_mock.get.return_value = self.project + + # There is no DomainManager Mock in fake identity v2. + + def test_create_with_project_identityv2(self): + arglist = [ + "--project", self.project.name, + self._network.name, + ] + verifylist = [ + ('enable', True), + ('share', None), + ('name', self._network.name), + ('project', self.project.name), + ('external', False), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_network.assert_called_once_with(**{ + 'admin_state_up': True, + 'name': self._network.name, + 'tenant_id': self.project.id, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_create_with_domain_identityv2(self): + arglist = [ + "--project", self.project.name, + "--project-domain", "domain-name", + self._network.name, + ] + verifylist = [ + ('enable', True), + ('share', None), + ('project', self.project.name), + ('project_domain', "domain-name"), + ('name', self._network.name), + ('external', False), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises( + AttributeError, + self.cmd.take_action, + parsed_args, + ) + + +class TestDeleteNetwork(TestNetwork): + + def setUp(self): + super(TestDeleteNetwork, self).setUp() + + # The networks to delete + self._networks = network_fakes.FakeNetwork.create_networks(count=3) + + self.network.delete_network = mock.Mock(return_value=None) + + self.network.find_network = network_fakes.FakeNetwork.get_networks( + networks=self._networks) + + # Get the command object to test + self.cmd = network.DeleteNetwork(self.app, self.namespace) + + def test_delete_one_network(self): + arglist = [ + self._networks[0].name, + ] + verifylist = [ + ('network', [self._networks[0].name]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.network.delete_network.assert_called_once_with(self._networks[0]) + self.assertIsNone(result) + + def test_delete_multiple_networks(self): + arglist = [] + for n in self._networks: + arglist.append(n.id) + verifylist = [ + ('network', arglist), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + calls = [] + for n in self._networks: + calls.append(call(n)) + self.network.delete_network.assert_has_calls(calls) + self.assertIsNone(result) + + def test_delete_multiple_networks_exception(self): + arglist = [ + self._networks[0].id, + 'xxxx-yyyy-zzzz', + self._networks[1].id, + ] + verifylist = [ + ('network', arglist), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # Fake exception in find_network() + ret_find = [ + self._networks[0], + exceptions.NotFound('404'), + self._networks[1], + ] + self.network.find_network = mock.Mock(side_effect=ret_find) + + # Fake exception in delete_network() + ret_delete = [ + None, + exceptions.NotFound('404'), + ] + self.network.delete_network = mock.Mock(side_effect=ret_delete) + + self.assertRaises(exceptions.CommandError, self.cmd.take_action, + parsed_args) + + # The second call of find_network() should fail. So delete_network() + # was only called twice. + calls = [ + call(self._networks[0]), + call(self._networks[1]), + ] + self.network.delete_network.assert_has_calls(calls) + + +class TestListNetwork(TestNetwork): + + # The networks going to be listed up. + _network = network_fakes.FakeNetwork.create_networks(count=3) + + columns = ( + 'ID', + 'Name', + 'Subnets', + ) + columns_long = ( + 'ID', + 'Name', + 'Status', + 'Project', + 'State', + 'Shared', + 'Subnets', + 'Network Type', + 'Router Type', + 'Availability Zones', + ) + + data = [] + for net in _network: + data.append(( + net.id, + net.name, + utils.format_list(net.subnets), + )) + + data_long = [] + for net in _network: + data_long.append(( + net.id, + net.name, + net.status, + net.project_id, + network._format_admin_state(net.admin_state_up), + net.shared, + utils.format_list(net.subnets), + net.provider_network_type, + network._format_router_external(net.is_router_external), + utils.format_list(net.availability_zones), + )) + + def setUp(self): + super(TestListNetwork, self).setUp() + + # Get the command object to test + self.cmd = network.ListNetwork(self.app, self.namespace) + + self.network.networks = mock.Mock(return_value=self._network) + + def test_network_list_no_options(self): + arglist = [] + verifylist = [ + ('external', False), + ('long', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.network.networks.assert_called_once_with() + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_list_external(self): + arglist = [ + '--external', + ] + verifylist = [ + ('external', True), + ('long', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.network.networks.assert_called_once_with( + **{'router:external': True} + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_network_list_long(self): + arglist = [ + '--long', + ] + verifylist = [ + ('long', True), + ('external', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.network.networks.assert_called_once_with() + self.assertEqual(self.columns_long, columns) + self.assertEqual(self.data_long, list(data)) + + +class TestSetNetwork(TestNetwork): + + # The network to set. + _network = network_fakes.FakeNetwork.create_one_network() + + def setUp(self): + super(TestSetNetwork, self).setUp() + + self.network.update_network = mock.Mock(return_value=None) + + self.network.find_network = mock.Mock(return_value=self._network) + + # Get the command object to test + self.cmd = network.SetNetwork(self.app, self.namespace) + + def test_set_this(self): + arglist = [ + self._network.name, + '--enable', + '--name', 'noob', + '--share', + '--external', + '--default', + '--provider-network-type', 'vlan', + '--provider-physical-network', 'physnet1', + '--provider-segment', '400', + '--no-transparent-vlan', + '--enable-port-security', + ] + verifylist = [ + ('network', self._network.name), + ('enable', True), + ('name', 'noob'), + ('share', True), + ('external', True), + ('default', True), + ('provider_network_type', 'vlan'), + ('physical_network', 'physnet1'), + ('segmentation_id', '400'), + ('no_transparent_vlan', True), + ('enable_port_security', True), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = { + 'name': 'noob', + 'admin_state_up': True, + 'shared': True, + 'router:external': True, + 'is_default': True, + 'provider:network_type': 'vlan', + 'provider:physical_network': 'physnet1', + 'provider:segmentation_id': '400', + 'vlan_transparent': False, + 'port_security_enabled': True, + } + self.network.update_network.assert_called_once_with( + self._network, **attrs) + self.assertIsNone(result) + + def test_set_that(self): + arglist = [ + self._network.name, + '--disable', + '--no-share', + '--internal', + '--disable-port-security', + ] + verifylist = [ + ('network', self._network.name), + ('disable', True), + ('no_share', True), + ('internal', True), + ('disable_port_security', True), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = { + 'admin_state_up': False, + 'shared': False, + 'router:external': False, + 'port_security_enabled': False, + } + self.network.update_network.assert_called_once_with( + self._network, **attrs) + self.assertIsNone(result) + + def test_set_nothing(self): + arglist = [self._network.name, ] + verifylist = [('network', self._network.name), ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = {} + self.network.update_network.assert_called_once_with( + self._network, **attrs) + self.assertIsNone(result) + + +class TestShowNetwork(TestNetwork): + + # The network to show. + _network = network_fakes.FakeNetwork.create_one_network() + + columns = ( + 'admin_state_up', + 'availability_zone_hints', + 'availability_zones', + 'id', + 'is_default', + 'name', + 'port_security_enabled', + 'project_id', + 'provider_network_type', + 'router:external', + 'shared', + 'status', + 'subnets', + ) + + data = ( + network._format_admin_state(_network.admin_state_up), + utils.format_list(_network.availability_zone_hints), + utils.format_list(_network.availability_zones), + _network.id, + _network.is_default, + _network.name, + _network.is_port_security_enabled, + _network.project_id, + _network.provider_network_type, + network._format_router_external(_network.is_router_external), + _network.shared, + _network.status, + utils.format_list(_network.subnets), + ) + + def setUp(self): + super(TestShowNetwork, self).setUp() + + self.network.find_network = mock.Mock(return_value=self._network) + + # Get the command object to test + self.cmd = network.ShowNetwork(self.app, self.namespace) + + def test_show_no_options(self): + arglist = [] + verifylist = [] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_show_all_options(self): + arglist = [ + self._network.name, + ] + verifylist = [ + ('network', self._network.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.network.find_network.assert_called_once_with( + self._network.name, ignore_missing=False) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +# Tests for Nova network +# +class TestNetworkCompute(compute_fakes.TestComputev2): + + def setUp(self): + super(TestNetworkCompute, self).setUp() + + # Get a shortcut to the compute client + self.compute = self.app.client_manager.compute + + +class TestCreateNetworkCompute(TestNetworkCompute): + + # The network to create. + _network = compute_fakes.FakeNetwork.create_one_network() + + columns = ( + 'bridge', + 'bridge_interface', + 'broadcast', + 'cidr', + 'cidr_v6', + 'created_at', + 'deleted', + 'deleted_at', + 'dhcp_server', + 'dhcp_start', + 'dns1', + 'dns2', + 'enable_dhcp', + 'gateway', + 'gateway_v6', + 'host', + 'id', + 'injected', + 'label', + 'mtu', + 'multi_host', + 'netmask', + 'netmask_v6', + 'priority', + 'project_id', + 'rxtx_base', + 'share_address', + 'updated_at', + 'vlan', + 'vpn_private_address', + 'vpn_public_address', + 'vpn_public_port', + ) + + data = ( + _network.bridge, + _network.bridge_interface, + _network.broadcast, + _network.cidr, + _network.cidr_v6, + _network.created_at, + _network.deleted, + _network.deleted_at, + _network.dhcp_server, + _network.dhcp_start, + _network.dns1, + _network.dns2, + _network.enable_dhcp, + _network.gateway, + _network.gateway_v6, + _network.host, + _network.id, + _network.injected, + _network.label, + _network.mtu, + _network.multi_host, + _network.netmask, + _network.netmask_v6, + _network.priority, + _network.project_id, + _network.rxtx_base, + _network.share_address, + _network.updated_at, + _network.vlan, + _network.vpn_private_address, + _network.vpn_public_address, + _network.vpn_public_port, + ) + + def setUp(self): + super(TestCreateNetworkCompute, self).setUp() + + self.app.client_manager.network_endpoint_enabled = False + + self.compute.networks.create.return_value = self._network + + # Get the command object to test + self.cmd = network.CreateNetwork(self.app, None) + + def test_create_no_options(self): + arglist = [] + verifylist = [] + + # Missing required args should raise exception here + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_create_default_options(self): + arglist = [ + "--subnet", self._network.cidr, + self._network.label, + ] + verifylist = [ + ('subnet', self._network.cidr), + ('name', self._network.label), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.compute.networks.create.assert_called_once_with(**{ + 'cidr': self._network.cidr, + 'label': self._network.label, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestDeleteNetworkCompute(TestNetworkCompute): + + def setUp(self): + super(TestDeleteNetworkCompute, self).setUp() + + self.app.client_manager.network_endpoint_enabled = False + + # The networks to delete + self._networks = compute_fakes.FakeNetwork.create_networks(count=3) + + self.compute.networks.delete.return_value = None + + # Return value of utils.find_resource() + self.compute.networks.get = \ + compute_fakes.FakeNetwork.get_networks(networks=self._networks) + + # Get the command object to test + self.cmd = network.DeleteNetwork(self.app, None) + + def test_delete_one_network(self): + arglist = [ + self._networks[0].label, + ] + verifylist = [ + ('network', [self._networks[0].label]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.compute.networks.delete.assert_called_once_with( + self._networks[0].id) + self.assertIsNone(result) + + def test_delete_multiple_networks(self): + arglist = [] + for n in self._networks: + arglist.append(n.label) + verifylist = [ + ('network', arglist), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + calls = [] + for n in self._networks: + calls.append(call(n.id)) + self.compute.networks.delete.assert_has_calls(calls) + self.assertIsNone(result) + + def test_delete_multiple_networks_exception(self): + arglist = [ + self._networks[0].id, + 'xxxx-yyyy-zzzz', + self._networks[1].id, + ] + verifylist = [ + ('network', arglist), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # Fake exception in utils.find_resource() + # In compute v2, we use utils.find_resource() to find a network. + # It calls get() several times, but find() only one time. So we + # choose to fake get() always raise exception, then pass through. + # And fake find() to find the real network or not. + self.compute.networks.get.side_effect = Exception() + ret_find = [ + self._networks[0], + Exception(), + self._networks[1], + ] + self.compute.networks.find.side_effect = ret_find + + # Fake exception in delete() + ret_delete = [ + None, + Exception(), + ] + self.compute.networks.delete = mock.Mock(side_effect=ret_delete) + + self.assertRaises(exceptions.CommandError, self.cmd.take_action, + parsed_args) + + # The second call of utils.find_resource() should fail. So delete() + # was only called twice. + calls = [ + call(self._networks[0].id), + call(self._networks[1].id), + ] + self.compute.networks.delete.assert_has_calls(calls) + + +class TestListNetworkCompute(TestNetworkCompute): + + # The networks going to be listed up. + _networks = compute_fakes.FakeNetwork.create_networks(count=3) + + columns = ( + 'ID', + 'Name', + 'Subnet', + ) + + data = [] + for net in _networks: + data.append(( + net.id, + net.label, + net.cidr, + )) + + def setUp(self): + super(TestListNetworkCompute, self).setUp() + + self.app.client_manager.network_endpoint_enabled = False + + self.compute.networks.list.return_value = self._networks + + # Get the command object to test + self.cmd = network.ListNetwork(self.app, None) + + def test_network_list_no_options(self): + arglist = [] + verifylist = [ + ('external', False), + ('long', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.compute.networks.list.assert_called_once_with() + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + +class TestShowNetworkCompute(TestNetworkCompute): + + # The network to show. + _network = compute_fakes.FakeNetwork.create_one_network() + + columns = ( + 'bridge', + 'bridge_interface', + 'broadcast', + 'cidr', + 'cidr_v6', + 'created_at', + 'deleted', + 'deleted_at', + 'dhcp_server', + 'dhcp_start', + 'dns1', + 'dns2', + 'enable_dhcp', + 'gateway', + 'gateway_v6', + 'host', + 'id', + 'injected', + 'label', + 'mtu', + 'multi_host', + 'netmask', + 'netmask_v6', + 'priority', + 'project_id', + 'rxtx_base', + 'share_address', + 'updated_at', + 'vlan', + 'vpn_private_address', + 'vpn_public_address', + 'vpn_public_port', + ) + + data = ( + _network.bridge, + _network.bridge_interface, + _network.broadcast, + _network.cidr, + _network.cidr_v6, + _network.created_at, + _network.deleted, + _network.deleted_at, + _network.dhcp_server, + _network.dhcp_start, + _network.dns1, + _network.dns2, + _network.enable_dhcp, + _network.gateway, + _network.gateway_v6, + _network.host, + _network.id, + _network.injected, + _network.label, + _network.mtu, + _network.multi_host, + _network.netmask, + _network.netmask_v6, + _network.priority, + _network.project_id, + _network.rxtx_base, + _network.share_address, + _network.updated_at, + _network.vlan, + _network.vpn_private_address, + _network.vpn_public_address, + _network.vpn_public_port, + ) + + def setUp(self): + super(TestShowNetworkCompute, self).setUp() + + self.app.client_manager.network_endpoint_enabled = False + + # Return value of utils.find_resource() + self.compute.networks.get.return_value = self._network + + # Get the command object to test + self.cmd = network.ShowNetwork(self.app, None) + + def test_show_no_options(self): + arglist = [] + verifylist = [] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_show_all_options(self): + arglist = [ + self._network.label, + ] + verifylist = [ + ('network', self._network.label), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/network/v2/test_network_agent.py b/openstackclient/tests/unit/network/v2/test_network_agent.py new file mode 100644 index 00000000..2f17f41b --- /dev/null +++ b/openstackclient/tests/unit/network/v2/test_network_agent.py @@ -0,0 +1,294 @@ +# 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 mock import call + +from osc_lib import exceptions +from osc_lib import utils + +from openstackclient.network.v2 import network_agent +from openstackclient.tests.unit.network.v2 import fakes as network_fakes +from openstackclient.tests.unit import utils as tests_utils + + +class TestNetworkAgent(network_fakes.TestNetworkV2): + + def setUp(self): + super(TestNetworkAgent, self).setUp() + + # Get a shortcut to the network client + self.network = self.app.client_manager.network + + +class TestDeleteNetworkAgent(TestNetworkAgent): + + network_agents = ( + network_fakes.FakeNetworkAgent.create_network_agents(count=2)) + + def setUp(self): + super(TestDeleteNetworkAgent, self).setUp() + self.network.delete_agent = mock.Mock(return_value=None) + self.network.get_agent = ( + network_fakes.FakeNetworkAgent.get_network_agents( + agents=self.network_agents) + ) + + # Get the command object to test + self.cmd = network_agent.DeleteNetworkAgent(self.app, self.namespace) + + def test_network_agent_delete(self): + arglist = [ + self.network_agents[0].id, + ] + verifylist = [ + ('network_agent', [self.network_agents[0].id]), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.network.get_agent.assert_called_once_with( + self.network_agents[0].id, ignore_missing=False) + self.network.delete_agent.assert_called_once_with( + self.network_agents[0]) + self.assertIsNone(result) + + def test_multi_network_agents_delete(self): + arglist = [] + verifylist = [] + + for n in self.network_agents: + arglist.append(n.id) + verifylist = [ + ('network_agent', arglist), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + calls = [] + for n in self.network_agents: + calls.append(call(n)) + self.network.delete_agent.assert_has_calls(calls) + self.assertIsNone(result) + + def test_multi_network_agents_delete_with_exception(self): + arglist = [ + self.network_agents[0].id, + 'unexist_network_agent', + ] + verifylist = [ + ('network_agent', + [self.network_agents[0].id, 'unexist_network_agent']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + find_mock_result = [self.network_agents[0], exceptions.CommandError] + self.network.get_agent = ( + mock.MagicMock(side_effect=find_mock_result) + ) + + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 network agents failed to delete.', str(e)) + + self.network.get_agent.assert_any_call( + self.network_agents[0].id, ignore_missing=False) + self.network.get_agent.assert_any_call( + 'unexist_network_agent', ignore_missing=False) + self.network.delete_agent.assert_called_once_with( + self.network_agents[0] + ) + + +class TestListNetworkAgent(TestNetworkAgent): + + network_agents = ( + network_fakes.FakeNetworkAgent.create_network_agents(count=3)) + + columns = ( + 'ID', + 'Agent Type', + 'Host', + 'Availability Zone', + 'Alive', + 'State', + 'Binary' + ) + data = [] + for agent in network_agents: + data.append(( + agent.id, + agent.agent_type, + agent.host, + agent.availability_zone, + agent.alive, + network_agent._format_admin_state(agent.admin_state_up), + agent.binary, + )) + + def setUp(self): + super(TestListNetworkAgent, self).setUp() + self.network.agents = mock.Mock( + return_value=self.network_agents) + + # Get the command object to test + self.cmd = network_agent.ListNetworkAgent(self.app, self.namespace) + + def test_network_agents_list(self): + arglist = [] + verifylist = [] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.network.agents.assert_called_once_with(**{}) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + +class TestSetNetworkAgent(TestNetworkAgent): + + _network_agent = ( + network_fakes.FakeNetworkAgent.create_one_network_agent()) + + def setUp(self): + super(TestSetNetworkAgent, self).setUp() + self.network.update_agent = mock.Mock(return_value=None) + self.network.get_agent = mock.Mock(return_value=self._network_agent) + + # Get the command object to test + self.cmd = network_agent.SetNetworkAgent(self.app, self.namespace) + + def test_set_nothing(self): + arglist = [ + self._network_agent.id, + ] + verifylist = [ + ('network_agent', self._network_agent.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = {} + self.network.update_agent.assert_called_once_with( + self._network_agent, **attrs) + self.assertIsNone(result) + + def test_set_all(self): + arglist = [ + '--description', 'new_description', + '--enable', + self._network_agent.id, + ] + verifylist = [ + ('description', 'new_description'), + ('enable', True), + ('disable', False), + ('network_agent', self._network_agent.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = { + 'description': 'new_description', + 'admin_state_up': True, + } + self.network.update_agent.assert_called_once_with( + self._network_agent, **attrs) + self.assertIsNone(result) + + def test_set_with_disable(self): + arglist = [ + '--disable', + self._network_agent.id, + ] + verifylist = [ + ('enable', False), + ('disable', True), + ('network_agent', self._network_agent.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = { + 'admin_state_up': False, + } + self.network.update_agent.assert_called_once_with( + self._network_agent, **attrs) + self.assertIsNone(result) + + +class TestShowNetworkAgent(TestNetworkAgent): + + _network_agent = ( + network_fakes.FakeNetworkAgent.create_one_network_agent()) + + columns = ( + 'admin_state_up', + 'agent_type', + 'alive', + 'availability_zone', + 'binary', + 'configurations', + 'host', + 'id', + ) + data = ( + network_agent._format_admin_state(_network_agent.admin_state_up), + _network_agent.agent_type, + _network_agent.alive, + _network_agent.availability_zone, + _network_agent.binary, + utils.format_dict(_network_agent.configurations), + _network_agent.host, + _network_agent.id, + ) + + def setUp(self): + super(TestShowNetworkAgent, self).setUp() + self.network.get_agent = mock.Mock( + return_value=self._network_agent) + + # Get the command object to test + self.cmd = network_agent.ShowNetworkAgent(self.app, self.namespace) + + def test_show_no_options(self): + arglist = [] + verifylist = [] + + # Missing required args should bail here + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_show_all_options(self): + arglist = [ + self._network_agent.id, + ] + verifylist = [ + ('network_agent', self._network_agent.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.network.get_agent.assert_called_once_with( + self._network_agent.id, ignore_missing=False) + self.assertEqual(self.columns, columns) + self.assertEqual(list(self.data), list(data)) diff --git a/openstackclient/tests/unit/network/v2/test_network_rbac.py b/openstackclient/tests/unit/network/v2/test_network_rbac.py new file mode 100644 index 00000000..1cd18a09 --- /dev/null +++ b/openstackclient/tests/unit/network/v2/test_network_rbac.py @@ -0,0 +1,430 @@ +# 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 mock import call + +from osc_lib import exceptions + +from openstackclient.network.v2 import network_rbac +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3 +from openstackclient.tests.unit.network.v2 import fakes as network_fakes +from openstackclient.tests.unit import utils as tests_utils + + +class TestNetworkRBAC(network_fakes.TestNetworkV2): + + def setUp(self): + super(TestNetworkRBAC, self).setUp() + + # Get a shortcut to the network client + self.network = self.app.client_manager.network + # Get a shortcut to the ProjectManager Mock + self.projects_mock = self.app.client_manager.identity.projects + + +class TestCreateNetworkRBAC(TestNetworkRBAC): + + network_object = network_fakes.FakeNetwork.create_one_network() + project = identity_fakes_v3.FakeProject.create_one_project() + rbac_policy = network_fakes.FakeNetworkRBAC.create_one_network_rbac( + attrs={'tenant_id': project.id, + 'target_tenant': project.id, + 'object_id': network_object.id} + ) + + columns = ( + 'action', + 'id', + 'object_id', + 'object_type', + 'project_id', + 'target_project_id', + ) + + data = [ + rbac_policy.action, + rbac_policy.id, + rbac_policy.object_id, + rbac_policy.object_type, + rbac_policy.tenant_id, + rbac_policy.target_tenant, + ] + + def setUp(self): + super(TestCreateNetworkRBAC, self).setUp() + + # Get the command object to test + self.cmd = network_rbac.CreateNetworkRBAC(self.app, self.namespace) + + self.network.create_rbac_policy = mock.Mock( + return_value=self.rbac_policy) + self.network.find_network = mock.Mock( + return_value=self.network_object) + self.projects_mock.get.return_value = self.project + + def test_network_rbac_create_no_type(self): + arglist = [ + '--action', self.rbac_policy.action, + self.rbac_policy.object_id, + ] + verifylist = [ + ('action', self.rbac_policy.action), + ('rbac_policy', self.rbac_policy.id), + ] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_network_rbac_create_no_action(self): + arglist = [ + '--type', self.rbac_policy.object_type, + self.rbac_policy.object_id, + ] + verifylist = [ + ('type', self.rbac_policy.object_type), + ('rbac_policy', self.rbac_policy.id), + ] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_network_rbac_create_invalid_type(self): + arglist = [ + '--action', self.rbac_policy.action, + '--type', 'invalid_type', + '--target-project', self.rbac_policy.target_tenant, + self.rbac_policy.object_id, + ] + verifylist = [ + ('action', self.rbac_policy.action), + ('type', 'invalid_type'), + ('target-project', self.rbac_policy.target_tenant), + ('rbac_policy', self.rbac_policy.id), + ] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_network_rbac_create_invalid_action(self): + arglist = [ + '--type', self.rbac_policy.object_type, + '--action', 'invalid_action', + '--target-project', self.rbac_policy.target_tenant, + self.rbac_policy.object_id, + ] + verifylist = [ + ('type', self.rbac_policy.object_type), + ('action', 'invalid_action'), + ('target-project', self.rbac_policy.target_tenant), + ('rbac_policy', self.rbac_policy.id), + ] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_network_rbac_create(self): + arglist = [ + '--type', self.rbac_policy.object_type, + '--action', self.rbac_policy.action, + '--target-project', self.rbac_policy.target_tenant, + self.rbac_policy.object_id, + ] + verifylist = [ + ('type', self.rbac_policy.object_type), + ('action', self.rbac_policy.action), + ('target_project', self.rbac_policy.target_tenant), + ('rbac_object', self.rbac_policy.object_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # DisplayCommandBase.take_action() returns two tuples + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_rbac_policy.assert_called_with(**{ + 'object_id': self.rbac_policy.object_id, + 'object_type': self.rbac_policy.object_type, + 'action': self.rbac_policy.action, + 'target_tenant': self.rbac_policy.target_tenant, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_network_rbac_create_all_options(self): + arglist = [ + '--type', self.rbac_policy.object_type, + '--action', self.rbac_policy.action, + '--target-project', self.rbac_policy.target_tenant, + '--project', self.rbac_policy.tenant_id, + '--project-domain', self.project.domain_id, + '--target-project-domain', self.project.domain_id, + self.rbac_policy.object_id, + ] + verifylist = [ + ('type', self.rbac_policy.object_type), + ('action', self.rbac_policy.action), + ('target_project', self.rbac_policy.target_tenant), + ('project', self.rbac_policy.tenant_id), + ('project_domain', self.project.domain_id), + ('target_project_domain', self.project.domain_id), + ('rbac_object', self.rbac_policy.object_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # DisplayCommandBase.take_action() returns two tuples + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_rbac_policy.assert_called_with(**{ + 'object_id': self.rbac_policy.object_id, + 'object_type': self.rbac_policy.object_type, + 'action': self.rbac_policy.action, + 'target_tenant': self.rbac_policy.target_tenant, + 'tenant_id': self.rbac_policy.tenant_id, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + +class TestDeleteNetworkRBAC(TestNetworkRBAC): + + rbac_policies = network_fakes.FakeNetworkRBAC.create_network_rbacs(count=2) + + def setUp(self): + super(TestDeleteNetworkRBAC, self).setUp() + self.network.delete_rbac_policy = mock.Mock(return_value=None) + self.network.find_rbac_policy = ( + network_fakes.FakeNetworkRBAC.get_network_rbacs( + rbac_policies=self.rbac_policies) + ) + + # Get the command object to test + self.cmd = network_rbac.DeleteNetworkRBAC(self.app, self.namespace) + + def test_network_rbac_delete(self): + arglist = [ + self.rbac_policies[0].id, + ] + verifylist = [ + ('rbac_policy', [self.rbac_policies[0].id]), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.network.find_rbac_policy.assert_called_once_with( + self.rbac_policies[0].id, ignore_missing=False) + self.network.delete_rbac_policy.assert_called_once_with( + self.rbac_policies[0]) + self.assertIsNone(result) + + def test_multi_network_rbacs_delete(self): + arglist = [] + verifylist = [] + + for r in self.rbac_policies: + arglist.append(r.id) + verifylist = [ + ('rbac_policy', arglist), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + calls = [] + for r in self.rbac_policies: + calls.append(call(r)) + self.network.delete_rbac_policy.assert_has_calls(calls) + self.assertIsNone(result) + + def test_multi_network_policies_delete_with_exception(self): + arglist = [ + self.rbac_policies[0].id, + 'unexist_rbac_policy', + ] + verifylist = [ + ('rbac_policy', + [self.rbac_policies[0].id, 'unexist_rbac_policy']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + find_mock_result = [self.rbac_policies[0], exceptions.CommandError] + self.network.find_rbac_policy = ( + mock.MagicMock(side_effect=find_mock_result) + ) + + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 RBAC policies failed to delete.', str(e)) + + self.network.find_rbac_policy.assert_any_call( + self.rbac_policies[0].id, ignore_missing=False) + self.network.find_rbac_policy.assert_any_call( + 'unexist_rbac_policy', ignore_missing=False) + self.network.delete_rbac_policy.assert_called_once_with( + self.rbac_policies[0] + ) + + +class TestListNetworkRABC(TestNetworkRBAC): + + # The network rbac policies going to be listed up. + rbac_policies = network_fakes.FakeNetworkRBAC.create_network_rbacs(count=3) + + columns = ( + 'ID', + 'Object Type', + 'Object ID', + ) + + data = [] + for r in rbac_policies: + data.append(( + r.id, + r.object_type, + r.object_id, + )) + + def setUp(self): + super(TestListNetworkRABC, self).setUp() + + # Get the command object to test + self.cmd = network_rbac.ListNetworkRBAC(self.app, self.namespace) + + self.network.rbac_policies = mock.Mock(return_value=self.rbac_policies) + + def test_network_rbac_list(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # DisplayCommandBase.take_action() returns two tuples + columns, data = self.cmd.take_action(parsed_args) + + self.network.rbac_policies.assert_called_with() + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + +class TestSetNetworkRBAC(TestNetworkRBAC): + + project = identity_fakes_v3.FakeProject.create_one_project() + rbac_policy = network_fakes.FakeNetworkRBAC.create_one_network_rbac( + attrs={'target_tenant': project.id}) + + def setUp(self): + super(TestSetNetworkRBAC, self).setUp() + + # Get the command object to test + self.cmd = network_rbac.SetNetworkRBAC(self.app, self.namespace) + + self.network.find_rbac_policy = mock.Mock( + return_value=self.rbac_policy) + self.network.update_rbac_policy = mock.Mock(return_value=None) + self.projects_mock.get.return_value = self.project + + def test_network_rbac_set_nothing(self): + arglist = [ + self.rbac_policy.id, + ] + verifylist = [ + ('rbac_policy', self.rbac_policy.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.network.find_rbac_policy.assert_called_once_with( + self.rbac_policy.id, ignore_missing=False + ) + attrs = {} + self.network.update_rbac_policy.assert_called_once_with( + self.rbac_policy, **attrs) + self.assertIsNone(result) + + def test_network_rbac_set(self): + arglist = [ + '--target-project', self.project.id, + self.rbac_policy.id, + ] + verifylist = [ + ('target_project', self.project.id), + ('rbac_policy', self.rbac_policy.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.network.find_rbac_policy.assert_called_once_with( + self.rbac_policy.id, ignore_missing=False + ) + attrs = {'target_tenant': self.project.id} + self.network.update_rbac_policy.assert_called_once_with( + self.rbac_policy, **attrs) + self.assertIsNone(result) + + +class TestShowNetworkRBAC(TestNetworkRBAC): + + rbac_policy = network_fakes.FakeNetworkRBAC.create_one_network_rbac() + + columns = ( + 'action', + 'id', + 'object_id', + 'object_type', + 'project_id', + 'target_project_id', + ) + + data = [ + rbac_policy.action, + rbac_policy.id, + rbac_policy.object_id, + rbac_policy.object_type, + rbac_policy.tenant_id, + rbac_policy.target_tenant, + ] + + def setUp(self): + super(TestShowNetworkRBAC, self).setUp() + + # Get the command object to test + self.cmd = network_rbac.ShowNetworkRBAC(self.app, self.namespace) + + self.network.find_rbac_policy = mock.Mock( + return_value=self.rbac_policy) + + def test_show_no_options(self): + arglist = [] + verifylist = [] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_network_rbac_show_all_options(self): + arglist = [ + self.rbac_policy.id, + ] + verifylist = [ + ('rbac_policy', self.rbac_policy.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # DisplayCommandBase.take_action() returns two tuples + columns, data = self.cmd.take_action(parsed_args) + + self.network.find_rbac_policy.assert_called_with( + self.rbac_policy.id, ignore_missing=False + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) diff --git a/openstackclient/tests/unit/network/v2/test_network_segment.py b/openstackclient/tests/unit/network/v2/test_network_segment.py new file mode 100644 index 00000000..b9fce078 --- /dev/null +++ b/openstackclient/tests/unit/network/v2/test_network_segment.py @@ -0,0 +1,200 @@ +# 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 osc_lib import exceptions + +from openstackclient.network.v2 import network_segment +from openstackclient.tests.unit.network.v2 import fakes as network_fakes +from openstackclient.tests.unit import utils as tests_utils + + +class TestNetworkSegment(network_fakes.TestNetworkV2): + + def setUp(self): + super(TestNetworkSegment, self).setUp() + + # Enable beta commands. + self.app.options.os_beta_command = True + + # Get a shortcut to the network client + self.network = self.app.client_manager.network + + +class TestListNetworkSegment(TestNetworkSegment): + _network = network_fakes.FakeNetwork.create_one_network() + _network_segments = \ + network_fakes.FakeNetworkSegment.create_network_segments(count=3) + + columns = ( + 'ID', + 'Network', + 'Network Type', + 'Segment', + ) + columns_long = columns + ( + 'Physical Network', + ) + + data = [] + for _network_segment in _network_segments: + data.append(( + _network_segment.id, + _network_segment.network_id, + _network_segment.network_type, + _network_segment.segmentation_id, + )) + + data_long = [] + for _network_segment in _network_segments: + data_long.append(( + _network_segment.id, + _network_segment.network_id, + _network_segment.network_type, + _network_segment.segmentation_id, + _network_segment.physical_network, + )) + + def setUp(self): + super(TestListNetworkSegment, self).setUp() + + # Get the command object to test + self.cmd = network_segment.ListNetworkSegment(self.app, self.namespace) + + self.network.find_network = mock.Mock(return_value=self._network) + self.network.segments = mock.Mock(return_value=self._network_segments) + + def test_list_no_option(self): + arglist = [] + verifylist = [ + ('long', False), + ('network', None), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.segments.assert_called_once_with() + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_list_no_beta_commands(self): + self.app.options.os_beta_command = False + parsed_args = self.check_parser(self.cmd, [], []) + self.assertRaises(exceptions.CommandError, self.cmd.take_action, + parsed_args) + + def test_list_long(self): + arglist = [ + '--long', + ] + verifylist = [ + ('long', True), + ('network', None), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.segments.assert_called_once_with() + self.assertEqual(self.columns_long, columns) + self.assertEqual(self.data_long, list(data)) + + def test_list_network(self): + arglist = [ + '--network', + self._network.id, + ] + verifylist = [ + ('long', False), + ('network', self._network.id) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.segments.assert_called_once_with( + **{'network_id': self._network.id} + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + +class TestShowNetworkSegment(TestNetworkSegment): + + # The network segment to show. + _network_segment = \ + network_fakes.FakeNetworkSegment.create_one_network_segment() + + columns = ( + 'id', + 'network_id', + 'network_type', + 'physical_network', + 'segmentation_id', + ) + + data = ( + _network_segment.id, + _network_segment.network_id, + _network_segment.network_type, + _network_segment.physical_network, + _network_segment.segmentation_id, + ) + + def setUp(self): + super(TestShowNetworkSegment, self).setUp() + + self.network.find_segment = mock.Mock( + return_value=self._network_segment + ) + + # Get the command object to test + self.cmd = network_segment.ShowNetworkSegment(self.app, self.namespace) + + def test_show_no_options(self): + # Missing required args should bail here + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, [], []) + + def test_show_no_beta_commands(self): + arglist = [ + self._network_segment.id, + ] + verifylist = [ + ('network_segment', self._network_segment.id), + ] + self.app.options.os_beta_command = False + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises(exceptions.CommandError, self.cmd.take_action, + parsed_args) + + def test_show_all_options(self): + arglist = [ + self._network_segment.id, + ] + verifylist = [ + ('network_segment', self._network_segment.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.network.find_segment.assert_called_once_with( + self._network_segment.id, + ignore_missing=False + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py new file mode 100644 index 00000000..d5d7f330 --- /dev/null +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -0,0 +1,696 @@ +# 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 argparse +import mock + +from mock import call +from osc_lib import exceptions +from osc_lib import utils + +from openstackclient.network.v2 import port +from openstackclient.tests.unit.network.v2 import fakes as network_fakes +from openstackclient.tests.unit import utils as tests_utils + + +class TestPort(network_fakes.TestNetworkV2): + + def setUp(self): + super(TestPort, self).setUp() + + # Get a shortcut to the network client + self.network = self.app.client_manager.network + + def _get_common_cols_data(self, fake_port): + columns = ( + 'admin_state_up', + 'allowed_address_pairs', + 'binding_host_id', + 'binding_profile', + 'binding_vif_details', + 'binding_vif_type', + 'binding_vnic_type', + 'device_id', + 'device_owner', + 'dns_assignment', + 'dns_name', + 'extra_dhcp_opts', + 'fixed_ips', + 'id', + 'mac_address', + 'name', + 'network_id', + 'port_security_enabled', + 'project_id', + 'security_groups', + 'status', + ) + + data = ( + port._format_admin_state(fake_port.admin_state_up), + utils.format_list_of_dicts(fake_port.allowed_address_pairs), + fake_port.binding_host_id, + utils.format_dict(fake_port.binding_profile), + utils.format_dict(fake_port.binding_vif_details), + fake_port.binding_vif_type, + fake_port.binding_vnic_type, + fake_port.device_id, + fake_port.device_owner, + utils.format_list_of_dicts(fake_port.dns_assignment), + fake_port.dns_name, + utils.format_list_of_dicts(fake_port.extra_dhcp_opts), + utils.format_list_of_dicts(fake_port.fixed_ips), + fake_port.id, + fake_port.mac_address, + fake_port.name, + fake_port.network_id, + fake_port.port_security_enabled, + fake_port.project_id, + utils.format_list(fake_port.security_groups), + fake_port.status, + ) + + return columns, data + + +class TestCreatePort(TestPort): + + _port = network_fakes.FakePort.create_one_port() + + def setUp(self): + super(TestCreatePort, self).setUp() + + self.network.create_port = mock.Mock(return_value=self._port) + fake_net = network_fakes.FakeNetwork.create_one_network({ + 'id': self._port.network_id, + }) + self.network.find_network = mock.Mock(return_value=fake_net) + self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet() + self.network.find_subnet = mock.Mock(return_value=self.fake_subnet) + # Get the command object to test + self.cmd = port.CreatePort(self.app, self.namespace) + + def test_create_default_options(self): + arglist = [ + '--network', self._port.network_id, + 'test-port', + ] + verifylist = [ + ('network', self._port.network_id,), + ('enable', True), + ('name', 'test-port'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = (self.cmd.take_action(parsed_args)) + + self.network.create_port.assert_called_once_with(**{ + 'admin_state_up': True, + 'network_id': self._port.network_id, + 'name': 'test-port', + }) + + ref_columns, ref_data = self._get_common_cols_data(self._port) + self.assertEqual(ref_columns, columns) + self.assertEqual(ref_data, data) + + def test_create_full_options(self): + arglist = [ + '--mac-address', 'aa:aa:aa:aa:aa:aa', + '--fixed-ip', 'subnet=%s,ip-address=10.0.0.2' + % self.fake_subnet.id, + '--device', 'deviceid', + '--device-owner', 'fakeowner', + '--disable', + '--vnic-type', 'macvtap', + '--binding-profile', 'foo=bar', + '--binding-profile', 'foo2=bar2', + '--network', self._port.network_id, + 'test-port', + + ] + verifylist = [ + ('mac_address', 'aa:aa:aa:aa:aa:aa'), + ( + 'fixed_ip', + [{'subnet': self.fake_subnet.id, 'ip-address': '10.0.0.2'}] + ), + ('device', 'deviceid'), + ('device_owner', 'fakeowner'), + ('disable', True), + ('vnic_type', 'macvtap'), + ('binding_profile', {'foo': 'bar', 'foo2': 'bar2'}), + ('network', self._port.network_id), + ('name', 'test-port'), + + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = (self.cmd.take_action(parsed_args)) + + self.network.create_port.assert_called_once_with(**{ + 'mac_address': 'aa:aa:aa:aa:aa:aa', + 'fixed_ips': [{'subnet_id': self.fake_subnet.id, + 'ip_address': '10.0.0.2'}], + 'device_id': 'deviceid', + 'device_owner': 'fakeowner', + 'admin_state_up': False, + 'binding:vnic_type': 'macvtap', + 'binding:profile': {'foo': 'bar', 'foo2': 'bar2'}, + 'network_id': self._port.network_id, + 'name': 'test-port', + }) + + ref_columns, ref_data = self._get_common_cols_data(self._port) + self.assertEqual(ref_columns, columns) + self.assertEqual(ref_data, data) + + def test_create_invalid_json_binding_profile(self): + arglist = [ + '--network', self._port.network_id, + '--binding-profile', '{"parent_name":"fake_parent"', + 'test-port', + ] + self.assertRaises(argparse.ArgumentTypeError, + self.check_parser, + self.cmd, + arglist, + None) + + def test_create_invalid_key_value_binding_profile(self): + arglist = [ + '--network', self._port.network_id, + '--binding-profile', 'key', + 'test-port', + ] + self.assertRaises(argparse.ArgumentTypeError, + self.check_parser, + self.cmd, + arglist, + None) + + def test_create_json_binding_profile(self): + arglist = [ + '--network', self._port.network_id, + '--binding-profile', '{"parent_name":"fake_parent"}', + '--binding-profile', '{"tag":42}', + 'test-port', + ] + verifylist = [ + ('network', self._port.network_id,), + ('enable', True), + ('binding_profile', {'parent_name': 'fake_parent', 'tag': 42}), + ('name', 'test-port'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = (self.cmd.take_action(parsed_args)) + + self.network.create_port.assert_called_once_with(**{ + 'admin_state_up': True, + 'network_id': self._port.network_id, + 'binding:profile': {'parent_name': 'fake_parent', 'tag': 42}, + 'name': 'test-port', + }) + + ref_columns, ref_data = self._get_common_cols_data(self._port) + self.assertEqual(ref_columns, columns) + self.assertEqual(ref_data, data) + + +class TestDeletePort(TestPort): + + # Ports to delete. + _ports = network_fakes.FakePort.create_ports(count=2) + + def setUp(self): + super(TestDeletePort, self).setUp() + + self.network.delete_port = mock.Mock(return_value=None) + self.network.find_port = network_fakes.FakePort.get_ports( + ports=self._ports) + # Get the command object to test + self.cmd = port.DeletePort(self.app, self.namespace) + + def test_port_delete(self): + arglist = [ + self._ports[0].name, + ] + verifylist = [ + ('port', [self._ports[0].name]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.network.find_port.assert_called_once_with( + self._ports[0].name, ignore_missing=False) + self.network.delete_port.assert_called_once_with(self._ports[0]) + self.assertIsNone(result) + + def test_multi_ports_delete(self): + arglist = [] + verifylist = [] + + for p in self._ports: + arglist.append(p.name) + verifylist = [ + ('port', arglist), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + calls = [] + for p in self._ports: + calls.append(call(p)) + self.network.delete_port.assert_has_calls(calls) + self.assertIsNone(result) + + def test_multi_ports_delete_with_exception(self): + arglist = [ + self._ports[0].name, + 'unexist_port', + ] + verifylist = [ + ('port', + [self._ports[0].name, 'unexist_port']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + find_mock_result = [self._ports[0], exceptions.CommandError] + self.network.find_port = ( + mock.MagicMock(side_effect=find_mock_result) + ) + + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 ports failed to delete.', str(e)) + + self.network.find_port.assert_any_call( + self._ports[0].name, ignore_missing=False) + self.network.find_port.assert_any_call( + 'unexist_port', ignore_missing=False) + self.network.delete_port.assert_called_once_with( + self._ports[0] + ) + + +class TestListPort(TestPort): + + _ports = network_fakes.FakePort.create_ports(count=3) + + columns = ( + 'ID', + 'Name', + 'MAC Address', + 'Fixed IP Addresses', + ) + + data = [] + for prt in _ports: + data.append(( + prt.id, + prt.name, + prt.mac_address, + utils.format_list_of_dicts(prt.fixed_ips), + )) + + def setUp(self): + super(TestListPort, self).setUp() + + # Get the command object to test + self.cmd = port.ListPort(self.app, self.namespace) + self.network.ports = mock.Mock(return_value=self._ports) + fake_router = network_fakes.FakeRouter.create_one_router({ + 'id': 'fake-router-id', + }) + self.network.find_router = mock.Mock(return_value=fake_router) + + def test_port_list_no_options(self): + arglist = [] + verifylist = [] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.ports.assert_called_once_with() + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_port_list_router_opt(self): + arglist = [ + '--router', 'fake-router-name', + ] + + verifylist = [ + ('router', 'fake-router-name') + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.ports.assert_called_once_with(**{ + 'device_id': 'fake-router-id' + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_port_list_device_owner_opt(self): + arglist = [ + '--device-owner', self._ports[0].device_owner, + ] + + verifylist = [ + ('device_owner', self._ports[0].device_owner) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.ports.assert_called_once_with(**{ + 'device_owner': self._ports[0].device_owner + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_port_list_all_opt(self): + arglist = [ + '--device-owner', self._ports[0].device_owner, + '--router', 'fake-router-name', + ] + + verifylist = [ + ('device_owner', self._ports[0].device_owner), + ('router', 'fake-router-name') + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.ports.assert_called_once_with(**{ + 'device_owner': self._ports[0].device_owner, + 'device_id': 'fake-router-id' + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + +class TestSetPort(TestPort): + + _port = network_fakes.FakePort.create_one_port() + + def setUp(self): + super(TestSetPort, self).setUp() + self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet() + self.network.find_subnet = mock.Mock(return_value=self.fake_subnet) + self.network.find_port = mock.Mock(return_value=self._port) + self.network.update_port = mock.Mock(return_value=None) + + # Get the command object to test + self.cmd = port.SetPort(self.app, self.namespace) + + def test_set_fixed_ip(self): + arglist = [ + '--fixed-ip', 'ip-address=10.0.0.11', + self._port.name, + ] + verifylist = [ + ('fixed_ip', [{'ip-address': '10.0.0.11'}]), + ('port', self._port.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = { + 'fixed_ips': [{'ip_address': '10.0.0.11'}], + } + self.network.update_port.assert_called_once_with(self._port, **attrs) + self.assertIsNone(result) + + def test_append_fixed_ip(self): + _testport = network_fakes.FakePort.create_one_port( + {'fixed_ips': [{'ip_address': '0.0.0.1'}]}) + self.network.find_port = mock.Mock(return_value=_testport) + arglist = [ + '--fixed-ip', 'ip-address=10.0.0.12', + _testport.name, + ] + verifylist = [ + ('fixed_ip', [{'ip-address': '10.0.0.12'}]), + ('port', _testport.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + attrs = { + 'fixed_ips': [ + {'ip_address': '10.0.0.12'}, {'ip_address': '0.0.0.1'}], + } + self.network.update_port.assert_called_once_with(_testport, **attrs) + self.assertIsNone(result) + + def test_set_this(self): + arglist = [ + '--disable', + '--no-fixed-ip', + '--no-binding-profile', + self._port.name, + ] + verifylist = [ + ('disable', True), + ('no_binding_profile', True), + ('no_fixed_ip', True), + ('port', self._port.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = { + 'admin_state_up': False, + 'binding:profile': {}, + 'fixed_ips': [], + } + self.network.update_port.assert_called_once_with(self._port, **attrs) + self.assertIsNone(result) + + def test_set_that(self): + arglist = [ + '--enable', + '--vnic-type', 'macvtap', + '--binding-profile', 'foo=bar', + '--host', 'binding-host-id-xxxx', + '--name', 'newName', + self._port.name, + ] + verifylist = [ + ('enable', True), + ('vnic_type', 'macvtap'), + ('binding_profile', {'foo': 'bar'}), + ('host', 'binding-host-id-xxxx'), + ('name', 'newName'), + ('port', self._port.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = { + 'admin_state_up': True, + 'binding:vnic_type': 'macvtap', + 'binding:profile': {'foo': 'bar'}, + 'binding:host_id': 'binding-host-id-xxxx', + 'name': 'newName', + } + self.network.update_port.assert_called_once_with(self._port, **attrs) + self.assertIsNone(result) + + def test_set_nothing(self): + arglist = [ + self._port.name, + ] + verifylist = [ + ('port', self._port.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = {} + self.network.update_port.assert_called_once_with(self._port, **attrs) + self.assertIsNone(result) + + def test_set_invalid_json_binding_profile(self): + arglist = [ + '--binding-profile', '{"parent_name"}', + 'test-port', + ] + self.assertRaises(argparse.ArgumentTypeError, + self.check_parser, + self.cmd, + arglist, + None) + + def test_set_invalid_key_value_binding_profile(self): + arglist = [ + '--binding-profile', 'key', + 'test-port', + ] + self.assertRaises(argparse.ArgumentTypeError, + self.check_parser, + self.cmd, + arglist, + None) + + def test_set_mixed_binding_profile(self): + arglist = [ + '--binding-profile', 'foo=bar', + '--binding-profile', '{"foo2": "bar2"}', + self._port.name, + ] + verifylist = [ + ('binding_profile', {'foo': 'bar', 'foo2': 'bar2'}), + ('port', self._port.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = { + 'binding:profile': {'foo': 'bar', 'foo2': 'bar2'}, + } + self.network.update_port.assert_called_once_with(self._port, **attrs) + self.assertIsNone(result) + + +class TestShowPort(TestPort): + + # The port to show. + _port = network_fakes.FakePort.create_one_port() + + def setUp(self): + super(TestShowPort, self).setUp() + + self.network.find_port = mock.Mock(return_value=self._port) + + # Get the command object to test + self.cmd = port.ShowPort(self.app, self.namespace) + + def test_show_no_options(self): + arglist = [] + verifylist = [] + + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_show_all_options(self): + arglist = [ + self._port.name, + ] + verifylist = [ + ('port', self._port.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.find_port.assert_called_once_with( + self._port.name, ignore_missing=False) + + ref_columns, ref_data = self._get_common_cols_data(self._port) + self.assertEqual(ref_columns, columns) + self.assertEqual(ref_data, data) + + +class TestUnsetPort(TestPort): + + def setUp(self): + super(TestUnsetPort, self).setUp() + self._testport = network_fakes.FakePort.create_one_port( + {'fixed_ips': [{'subnet_id': '042eb10a-3a18-4658-ab-cf47c8d03152', + 'ip_address': '0.0.0.1'}, + {'subnet_id': '042eb10a-3a18-4658-ab-cf47c8d03152', + 'ip_address': '1.0.0.0'}], + 'binding:profile': {'batman': 'Joker', 'Superman': 'LexLuthor'}}) + self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet( + {'id': '042eb10a-3a18-4658-ab-cf47c8d03152'}) + self.network.find_subnet = mock.Mock(return_value=self.fake_subnet) + self.network.find_port = mock.Mock(return_value=self._testport) + self.network.update_port = mock.Mock(return_value=None) + # Get the command object to test + self.cmd = port.UnsetPort(self.app, self.namespace) + + def test_unset_port_parameters(self): + arglist = [ + '--fixed-ip', + 'subnet=042eb10a-3a18-4658-ab-cf47c8d03152,ip-address=1.0.0.0', + '--binding-profile', 'Superman', + self._testport.name, + ] + verifylist = [ + ('fixed_ip', [{ + 'subnet': '042eb10a-3a18-4658-ab-cf47c8d03152', + 'ip-address': '1.0.0.0'}]), + ('binding_profile', ['Superman']), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = { + 'fixed_ips': [{ + 'subnet_id': '042eb10a-3a18-4658-ab-cf47c8d03152', + 'ip_address': '0.0.0.1'}], + 'binding:profile': {'batman': 'Joker'} + } + self.network.update_port.assert_called_once_with( + self._testport, **attrs) + self.assertIsNone(result) + + def test_unset_port_fixed_ip_not_existent(self): + arglist = [ + '--fixed-ip', 'ip-address=1.0.0.1', + '--binding-profile', 'Superman', + self._testport.name, + ] + verifylist = [ + ('fixed_ip', [{'ip-address': '1.0.0.1'}]), + ('binding_profile', ['Superman']), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises(exceptions.CommandError, + self.cmd.take_action, + parsed_args) + + def test_unset_port_binding_profile_not_existent(self): + arglist = [ + '--fixed-ip', 'ip-address=1.0.0.0', + '--binding-profile', 'Neo', + self._testport.name, + ] + verifylist = [ + ('fixed_ip', [{'ip-address': '1.0.0.0'}]), + ('binding_profile', ['Neo']), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises(exceptions.CommandError, + self.cmd.take_action, + parsed_args) diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py new file mode 100644 index 00000000..26fe655e --- /dev/null +++ b/openstackclient/tests/unit/network/v2/test_router.py @@ -0,0 +1,751 @@ +# 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 mock import call + +from osc_lib import exceptions +from osc_lib import utils as osc_utils + +from openstackclient.network.v2 import router +from openstackclient.tests.unit.network.v2 import fakes as network_fakes +from openstackclient.tests.unit import utils as tests_utils + + +class TestRouter(network_fakes.TestNetworkV2): + + def setUp(self): + super(TestRouter, self).setUp() + + # Get a shortcut to the network client + self.network = self.app.client_manager.network + + +class TestAddPortToRouter(TestRouter): + '''Add port to Router ''' + + _port = network_fakes.FakePort.create_one_port() + _router = network_fakes.FakeRouter.create_one_router( + attrs={'port': _port.id}) + + def setUp(self): + super(TestAddPortToRouter, self).setUp() + self.network.router_add_interface = mock.Mock() + self.cmd = router.AddPortToRouter(self.app, self.namespace) + self.network.find_router = mock.Mock(return_value=self._router) + self.network.find_port = mock.Mock(return_value=self._port) + + def test_add_port_no_option(self): + arglist = [] + verifylist = [] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_add_port_required_options(self): + arglist = [ + self._router.id, + self._router.port, + ] + verifylist = [ + ('router', self._router.id), + ('port', self._router.port), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.network.router_add_interface.assert_called_with(self._router, **{ + 'port_id': self._router.port, + }) + self.assertIsNone(result) + + +class TestAddSubnetToRouter(TestRouter): + '''Add subnet to Router ''' + + _subnet = network_fakes.FakeSubnet.create_one_subnet() + _router = network_fakes.FakeRouter.create_one_router( + attrs={'subnet': _subnet.id}) + + def setUp(self): + super(TestAddSubnetToRouter, self).setUp() + self.network.router_add_interface = mock.Mock() + self.cmd = router.AddSubnetToRouter(self.app, self.namespace) + self.network.find_router = mock.Mock(return_value=self._router) + self.network.find_subnet = mock.Mock(return_value=self._subnet) + + def test_add_subnet_no_option(self): + arglist = [] + verifylist = [] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_add_subnet_required_options(self): + arglist = [ + self._router.id, + self._router.subnet, + ] + verifylist = [ + ('router', self._router.id), + ('subnet', self._router.subnet), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.network.router_add_interface.assert_called_with( + self._router, **{'subnet_id': self._router.subnet}) + + self.assertIsNone(result) + + +class TestCreateRouter(TestRouter): + + # The new router created. + new_router = network_fakes.FakeRouter.create_one_router() + + columns = ( + 'admin_state_up', + 'availability_zone_hints', + 'availability_zones', + 'distributed', + 'external_gateway_info', + 'ha', + 'id', + 'name', + 'project_id', + 'routes', + 'status', + ) + data = ( + router._format_admin_state(new_router.admin_state_up), + osc_utils.format_list(new_router.availability_zone_hints), + osc_utils.format_list(new_router.availability_zones), + new_router.distributed, + router._format_external_gateway_info(new_router.external_gateway_info), + new_router.ha, + new_router.id, + new_router.name, + new_router.tenant_id, + router._format_routes(new_router.routes), + new_router.status, + ) + + def setUp(self): + super(TestCreateRouter, self).setUp() + + self.network.create_router = mock.Mock(return_value=self.new_router) + + # Get the command object to test + self.cmd = router.CreateRouter(self.app, self.namespace) + + def test_create_no_options(self): + arglist = [] + verifylist = [] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_create_default_options(self): + arglist = [ + self.new_router.name, + ] + verifylist = [ + ('name', self.new_router.name), + ('enable', True), + ('distributed', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = (self.cmd.take_action(parsed_args)) + + self.network.create_router.assert_called_once_with(**{ + 'admin_state_up': True, + 'name': self.new_router.name, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_create_with_AZ_hints(self): + arglist = [ + self.new_router.name, + '--availability-zone-hint', 'fake-az', + '--availability-zone-hint', 'fake-az2', + ] + verifylist = [ + ('name', self.new_router.name), + ('availability_zone_hints', ['fake-az', 'fake-az2']), + ('enable', True), + ('distributed', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = (self.cmd.take_action(parsed_args)) + self.network.create_router.assert_called_once_with(**{ + 'admin_state_up': True, + 'name': self.new_router.name, + 'availability_zone_hints': ['fake-az', 'fake-az2'], + }) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestDeleteRouter(TestRouter): + + # The routers to delete. + _routers = network_fakes.FakeRouter.create_routers(count=2) + + def setUp(self): + super(TestDeleteRouter, self).setUp() + + self.network.delete_router = mock.Mock(return_value=None) + + self.network.find_router = ( + network_fakes.FakeRouter.get_routers(self._routers)) + + # Get the command object to test + self.cmd = router.DeleteRouter(self.app, self.namespace) + + def test_router_delete(self): + arglist = [ + self._routers[0].name, + ] + verifylist = [ + ('router', [self._routers[0].name]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.network.delete_router.assert_called_once_with(self._routers[0]) + self.assertIsNone(result) + + def test_multi_routers_delete(self): + arglist = [] + verifylist = [] + + for r in self._routers: + arglist.append(r.name) + verifylist = [ + ('router', arglist), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + calls = [] + for r in self._routers: + calls.append(call(r)) + self.network.delete_router.assert_has_calls(calls) + self.assertIsNone(result) + + def test_multi_routers_delete_with_exception(self): + arglist = [ + self._routers[0].name, + 'unexist_router', + ] + verifylist = [ + ('router', + [self._routers[0].name, 'unexist_router']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + find_mock_result = [self._routers[0], exceptions.CommandError] + self.network.find_router = ( + mock.MagicMock(side_effect=find_mock_result) + ) + + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 routers failed to delete.', str(e)) + + self.network.find_router.assert_any_call( + self._routers[0].name, ignore_missing=False) + self.network.find_router.assert_any_call( + 'unexist_router', ignore_missing=False) + self.network.delete_router.assert_called_once_with( + self._routers[0] + ) + + +class TestListRouter(TestRouter): + + # The routers going to be listed up. + routers = network_fakes.FakeRouter.create_routers(count=3) + + columns = ( + 'ID', + 'Name', + 'Status', + 'State', + 'Distributed', + 'HA', + 'Project', + ) + columns_long = columns + ( + 'Routes', + 'External gateway info', + 'Availability zones' + ) + + data = [] + for r in routers: + data.append(( + r.id, + r.name, + r.status, + router._format_admin_state(r.admin_state_up), + r.distributed, + r.ha, + r.tenant_id, + )) + data_long = [] + for i in range(0, len(routers)): + r = routers[i] + data_long.append( + data[i] + ( + router._format_routes(r.routes), + router._format_external_gateway_info(r.external_gateway_info), + osc_utils.format_list(r.availability_zones), + ) + ) + + def setUp(self): + super(TestListRouter, self).setUp() + + # Get the command object to test + self.cmd = router.ListRouter(self.app, self.namespace) + + self.network.routers = mock.Mock(return_value=self.routers) + + def test_router_list_no_options(self): + arglist = [] + verifylist = [ + ('long', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.network.routers.assert_called_once_with() + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_router_list_long(self): + arglist = [ + '--long', + ] + verifylist = [ + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.network.routers.assert_called_once_with() + self.assertEqual(self.columns_long, columns) + self.assertEqual(self.data_long, list(data)) + + +class TestRemovePortFromRouter(TestRouter): + '''Remove port from a Router ''' + + _port = network_fakes.FakePort.create_one_port() + _router = network_fakes.FakeRouter.create_one_router( + attrs={'port': _port.id}) + + def setUp(self): + super(TestRemovePortFromRouter, self).setUp() + self.network.router_remove_interface = mock.Mock() + self.cmd = router.RemovePortFromRouter(self.app, self.namespace) + self.network.find_router = mock.Mock(return_value=self._router) + self.network.find_port = mock.Mock(return_value=self._port) + + def test_remove_port_no_option(self): + arglist = [] + verifylist = [] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_remove_port_required_options(self): + arglist = [ + self._router.id, + self._router.port, + ] + verifylist = [ + ('router', self._router.id), + ('port', self._router.port), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.network.router_remove_interface.assert_called_with( + self._router, **{'port_id': self._router.port}) + self.assertIsNone(result) + + +class TestRemoveSubnetFromRouter(TestRouter): + '''Remove subnet from Router ''' + + _subnet = network_fakes.FakeSubnet.create_one_subnet() + _router = network_fakes.FakeRouter.create_one_router( + attrs={'subnet': _subnet.id}) + + def setUp(self): + super(TestRemoveSubnetFromRouter, self).setUp() + self.network.router_remove_interface = mock.Mock() + self.cmd = router.RemoveSubnetFromRouter(self.app, self.namespace) + self.network.find_router = mock.Mock(return_value=self._router) + self.network.find_subnet = mock.Mock(return_value=self._subnet) + + def test_remove_subnet_no_option(self): + arglist = [] + verifylist = [] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_remove_subnet_required_options(self): + arglist = [ + self._router.id, + self._router.subnet, + ] + verifylist = [ + ('subnet', self._router.subnet), + ('router', self._router.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.network.router_remove_interface.assert_called_with( + self._router, **{'subnet_id': self._router.subnet}) + self.assertIsNone(result) + + +class TestSetRouter(TestRouter): + + # The router to set. + _default_route = {'destination': '10.20.20.0/24', 'nexthop': '10.20.30.1'} + _router = network_fakes.FakeRouter.create_one_router( + attrs={'routes': [_default_route]} + ) + + def setUp(self): + super(TestSetRouter, self).setUp() + + self.network.update_router = mock.Mock(return_value=None) + + self.network.find_router = mock.Mock(return_value=self._router) + + # Get the command object to test + self.cmd = router.SetRouter(self.app, self.namespace) + + def test_set_this(self): + arglist = [ + self._router.name, + '--enable', + '--distributed', + '--name', 'noob', + ] + verifylist = [ + ('router', self._router.name), + ('enable', True), + ('distributed', True), + ('name', 'noob'), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = { + 'admin_state_up': True, + 'distributed': True, + 'name': 'noob', + } + self.network.update_router.assert_called_once_with( + self._router, **attrs) + self.assertIsNone(result) + + def test_set_that(self): + arglist = [ + self._router.name, + '--disable', + '--centralized', + ] + verifylist = [ + ('router', self._router.name), + ('disable', True), + ('centralized', True), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = { + 'admin_state_up': False, + 'distributed': False, + } + self.network.update_router.assert_called_once_with( + self._router, **attrs) + self.assertIsNone(result) + + def test_set_distributed_centralized(self): + arglist = [ + self._router.name, + '--distributed', + '--centralized', + ] + verifylist = [ + ('router', self._router.name), + ('distributed', True), + ('distributed', False), + ] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_set_route(self): + arglist = [ + self._router.name, + '--route', 'destination=10.20.30.0/24,gateway=10.20.30.1', + ] + verifylist = [ + ('router', self._router.name), + ('routes', [{'destination': '10.20.30.0/24', + 'gateway': '10.20.30.1'}]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + attrs = { + 'routes': self._router.routes + [{'destination': '10.20.30.0/24', + 'nexthop': '10.20.30.1'}], + } + self.network.update_router.assert_called_once_with( + self._router, **attrs) + self.assertIsNone(result) + + def test_set_no_route(self): + arglist = [ + self._router.name, + '--no-route', + ] + verifylist = [ + ('router', self._router.name), + ('no_route', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + attrs = { + 'routes': [], + } + self.network.update_router.assert_called_once_with( + self._router, **attrs) + self.assertIsNone(result) + + def test_set_route_no_route(self): + arglist = [ + self._router.name, + '--route', 'destination=10.20.30.0/24,gateway=10.20.30.1', + '--no-route', + ] + verifylist = [ + ('router', self._router.name), + ('routes', [{'destination': '10.20.30.0/24', + 'gateway': '10.20.30.1'}]), + ('no_route', True), + ] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_set_clear_routes(self): + arglist = [ + self._router.name, + '--clear-routes', + ] + verifylist = [ + ('router', self._router.name), + ('clear_routes', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + attrs = { + 'routes': [], + } + self.network.update_router.assert_called_once_with( + self._router, **attrs) + self.assertIsNone(result) + + def test_set_route_clear_routes(self): + arglist = [ + self._router.name, + '--route', 'destination=10.20.30.0/24,gateway=10.20.30.1', + '--clear-routes', + ] + verifylist = [ + ('router', self._router.name), + ('routes', [{'destination': '10.20.30.0/24', + 'gateway': '10.20.30.1'}]), + ('clear_routes', True), + ] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_set_nothing(self): + arglist = [ + self._router.name, + ] + verifylist = [ + ('router', self._router.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = {} + self.network.update_router.assert_called_once_with( + self._router, **attrs) + self.assertIsNone(result) + + +class TestShowRouter(TestRouter): + + # The router to set. + _router = network_fakes.FakeRouter.create_one_router() + + columns = ( + 'admin_state_up', + 'availability_zone_hints', + 'availability_zones', + 'distributed', + 'external_gateway_info', + 'ha', + 'id', + 'name', + 'project_id', + 'routes', + 'status', + ) + data = ( + router._format_admin_state(_router.admin_state_up), + osc_utils.format_list(_router.availability_zone_hints), + osc_utils.format_list(_router.availability_zones), + _router.distributed, + router._format_external_gateway_info(_router.external_gateway_info), + _router.ha, + _router.id, + _router.name, + _router.tenant_id, + router._format_routes(_router.routes), + _router.status, + ) + + def setUp(self): + super(TestShowRouter, self).setUp() + + self.network.find_router = mock.Mock(return_value=self._router) + + # Get the command object to test + self.cmd = router.ShowRouter(self.app, self.namespace) + + def test_show_no_options(self): + arglist = [] + verifylist = [] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_show_all_options(self): + arglist = [ + self._router.name, + ] + verifylist = [ + ('router', self._router.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.find_router.assert_called_once_with( + self._router.name, ignore_missing=False) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestUnsetRouter(TestRouter): + + def setUp(self): + super(TestUnsetRouter, self).setUp() + self._testrouter = network_fakes.FakeRouter.create_one_router( + {'routes': [{"destination": "192.168.101.1/24", + "gateway": "172.24.4.3"}, + {"destination": "192.168.101.2/24", + "gateway": "172.24.4.3"}], }) + self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet() + self.network.find_router = mock.Mock(return_value=self._testrouter) + self.network.update_router = mock.Mock(return_value=None) + # Get the command object to test + self.cmd = router.UnsetRouter(self.app, self.namespace) + + def test_unset_router_params(self): + arglist = [ + '--route', 'destination=192.168.101.1/24,gateway=172.24.4.3', + self._testrouter.name, + ] + verifylist = [ + ('routes', [ + {"destination": "192.168.101.1/24", "gateway": "172.24.4.3"}]), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = { + 'routes': [{"destination": "192.168.101.2/24", + "nexthop": "172.24.4.3"}], + } + self.network.update_router.assert_called_once_with( + self._testrouter, **attrs) + self.assertIsNone(result) + + def test_unset_router_wrong_routes(self): + arglist = [ + '--route', 'destination=192.168.101.1/24,gateway=172.24.4.2', + self._testrouter.name, + ] + verifylist = [ + ('routes', [ + {"destination": "192.168.101.1/24", "gateway": "172.24.4.2"}]), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises(exceptions.CommandError, + self.cmd.take_action, parsed_args) diff --git a/openstackclient/tests/unit/network/v2/test_security_group.py b/openstackclient/tests/unit/network/v2/test_security_group.py new file mode 100644 index 00000000..4c5b2972 --- /dev/null +++ b/openstackclient/tests/unit/network/v2/test_security_group.py @@ -0,0 +1,770 @@ +# 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 mock import call + +from osc_lib import exceptions + +from openstackclient.network.v2 import security_group +from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes +from openstackclient.tests.unit.network.v2 import fakes as network_fakes +from openstackclient.tests.unit import utils as tests_utils + + +class TestSecurityGroupNetwork(network_fakes.TestNetworkV2): + + def setUp(self): + super(TestSecurityGroupNetwork, self).setUp() + + # Get a shortcut to the network client + self.network = self.app.client_manager.network + # Get a shortcut to the ProjectManager Mock + self.projects_mock = self.app.client_manager.identity.projects + # Get a shortcut to the DomainManager Mock + self.domains_mock = self.app.client_manager.identity.domains + + +class TestSecurityGroupCompute(compute_fakes.TestComputev2): + + def setUp(self): + super(TestSecurityGroupCompute, self).setUp() + + # Get a shortcut to the compute client + self.compute = self.app.client_manager.compute + + +class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork): + + project = identity_fakes.FakeProject.create_one_project() + domain = identity_fakes.FakeDomain.create_one_domain() + # The security group to be created. + _security_group = \ + network_fakes.FakeSecurityGroup.create_one_security_group() + + columns = ( + 'description', + 'id', + 'name', + 'project_id', + 'rules', + ) + + data = ( + _security_group.description, + _security_group.id, + _security_group.name, + _security_group.project_id, + '', + ) + + def setUp(self): + super(TestCreateSecurityGroupNetwork, self).setUp() + + self.network.create_security_group = mock.Mock( + return_value=self._security_group) + + self.projects_mock.get.return_value = self.project + self.domains_mock.get.return_value = self.domain + + # Get the command object to test + self.cmd = security_group.CreateSecurityGroup(self.app, self.namespace) + + def test_create_no_options(self): + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, [], []) + + def test_create_min_options(self): + arglist = [ + self._security_group.name, + ] + verifylist = [ + ('name', self._security_group.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_security_group.assert_called_once_with(**{ + 'description': self._security_group.name, + 'name': self._security_group.name, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_create_all_options(self): + arglist = [ + '--description', self._security_group.description, + '--project', self.project.name, + '--project-domain', self.domain.name, + self._security_group.name, + ] + verifylist = [ + ('description', self._security_group.description), + ('name', self._security_group.name), + ('project', self.project.name), + ('project_domain', self.domain.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_security_group.assert_called_once_with(**{ + 'description': self._security_group.description, + 'name': self._security_group.name, + 'tenant_id': self.project.id, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestCreateSecurityGroupCompute(TestSecurityGroupCompute): + + project = identity_fakes.FakeProject.create_one_project() + domain = identity_fakes.FakeDomain.create_one_domain() + # The security group to be shown. + _security_group = \ + compute_fakes.FakeSecurityGroup.create_one_security_group() + + columns = ( + 'description', + 'id', + 'name', + 'project_id', + 'rules', + ) + + data = ( + _security_group.description, + _security_group.id, + _security_group.name, + _security_group.tenant_id, + '', + ) + + def setUp(self): + super(TestCreateSecurityGroupCompute, self).setUp() + + self.app.client_manager.network_endpoint_enabled = False + + self.compute.security_groups.create.return_value = self._security_group + + # Get the command object to test + self.cmd = security_group.CreateSecurityGroup(self.app, None) + + def test_create_no_options(self): + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, [], []) + + def test_create_network_options(self): + arglist = [ + '--project', self.project.name, + '--project-domain', self.domain.name, + self._security_group.name, + ] + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, arglist, []) + + def test_create_min_options(self): + arglist = [ + self._security_group.name, + ] + verifylist = [ + ('name', self._security_group.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.compute.security_groups.create.assert_called_once_with( + self._security_group.name, + self._security_group.name) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_create_all_options(self): + arglist = [ + '--description', self._security_group.description, + self._security_group.name, + ] + verifylist = [ + ('description', self._security_group.description), + ('name', self._security_group.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.compute.security_groups.create.assert_called_once_with( + self._security_group.name, + self._security_group.description) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestDeleteSecurityGroupNetwork(TestSecurityGroupNetwork): + + # The security groups to be deleted. + _security_groups = \ + network_fakes.FakeSecurityGroup.create_security_groups() + + def setUp(self): + super(TestDeleteSecurityGroupNetwork, self).setUp() + + self.network.delete_security_group = mock.Mock(return_value=None) + + self.network.find_security_group = ( + network_fakes.FakeSecurityGroup.get_security_groups( + self._security_groups) + ) + + # Get the command object to test + self.cmd = security_group.DeleteSecurityGroup(self.app, self.namespace) + + def test_security_group_delete(self): + arglist = [ + self._security_groups[0].name, + ] + verifylist = [ + ('group', [self._security_groups[0].name]), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.network.delete_security_group.assert_called_once_with( + self._security_groups[0]) + self.assertIsNone(result) + + def test_multi_security_groups_delete(self): + arglist = [] + verifylist = [] + + for s in self._security_groups: + arglist.append(s.name) + verifylist = [ + ('group', arglist), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + calls = [] + for s in self._security_groups: + calls.append(call(s)) + self.network.delete_security_group.assert_has_calls(calls) + self.assertIsNone(result) + + def test_multi_security_groups_delete_with_exception(self): + arglist = [ + self._security_groups[0].name, + 'unexist_security_group', + ] + verifylist = [ + ('group', + [self._security_groups[0].name, 'unexist_security_group']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + find_mock_result = [self._security_groups[0], exceptions.CommandError] + self.network.find_security_group = ( + mock.MagicMock(side_effect=find_mock_result) + ) + + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 groups failed to delete.', str(e)) + + self.network.find_security_group.assert_any_call( + self._security_groups[0].name, ignore_missing=False) + self.network.find_security_group.assert_any_call( + 'unexist_security_group', ignore_missing=False) + self.network.delete_security_group.assert_called_once_with( + self._security_groups[0] + ) + + +class TestDeleteSecurityGroupCompute(TestSecurityGroupCompute): + + # The security groups to be deleted. + _security_groups = \ + compute_fakes.FakeSecurityGroup.create_security_groups() + + def setUp(self): + super(TestDeleteSecurityGroupCompute, self).setUp() + + self.app.client_manager.network_endpoint_enabled = False + + self.compute.security_groups.delete = mock.Mock(return_value=None) + + self.compute.security_groups.get = ( + compute_fakes.FakeSecurityGroup.get_security_groups( + self._security_groups) + ) + + # Get the command object to test + self.cmd = security_group.DeleteSecurityGroup(self.app, None) + + def test_security_group_delete(self): + arglist = [ + self._security_groups[0].id, + ] + verifylist = [ + ('group', [self._security_groups[0].id]), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.compute.security_groups.delete.assert_called_once_with( + self._security_groups[0].id) + self.assertIsNone(result) + + def test_multi_security_groups_delete(self): + arglist = [] + verifylist = [] + + for s in self._security_groups: + arglist.append(s.id) + verifylist = [ + ('group', arglist), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + calls = [] + for s in self._security_groups: + calls.append(call(s.id)) + self.compute.security_groups.delete.assert_has_calls(calls) + self.assertIsNone(result) + + def test_multi_security_groups_delete_with_exception(self): + arglist = [ + self._security_groups[0].id, + 'unexist_security_group', + ] + verifylist = [ + ('group', + [self._security_groups[0].id, 'unexist_security_group']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + find_mock_result = [self._security_groups[0], exceptions.CommandError] + self.compute.security_groups.get = ( + mock.MagicMock(side_effect=find_mock_result) + ) + self.compute.security_groups.find.side_effect = ( + exceptions.NotFound(None)) + + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 groups failed to delete.', str(e)) + + self.compute.security_groups.get.assert_any_call( + self._security_groups[0].id) + self.compute.security_groups.get.assert_any_call( + 'unexist_security_group') + self.compute.security_groups.delete.assert_called_once_with( + self._security_groups[0].id + ) + + +class TestListSecurityGroupNetwork(TestSecurityGroupNetwork): + + # The security group to be listed. + _security_groups = \ + network_fakes.FakeSecurityGroup.create_security_groups(count=3) + + columns = ( + 'ID', + 'Name', + 'Description', + 'Project', + ) + + data = [] + for grp in _security_groups: + data.append(( + grp.id, + grp.name, + grp.description, + grp.tenant_id, + )) + + def setUp(self): + super(TestListSecurityGroupNetwork, self).setUp() + + self.network.security_groups = mock.Mock( + return_value=self._security_groups) + + # Get the command object to test + self.cmd = security_group.ListSecurityGroup(self.app, self.namespace) + + def test_security_group_list_no_options(self): + arglist = [] + verifylist = [ + ('all_projects', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.security_groups.assert_called_once_with() + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_security_group_list_all_projects(self): + arglist = [ + '--all-projects', + ] + verifylist = [ + ('all_projects', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.security_groups.assert_called_once_with() + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + +class TestListSecurityGroupCompute(TestSecurityGroupCompute): + + # The security group to be listed. + _security_groups = \ + compute_fakes.FakeSecurityGroup.create_security_groups(count=3) + + columns = ( + 'ID', + 'Name', + 'Description', + ) + columns_all_projects = ( + 'ID', + 'Name', + 'Description', + 'Project', + ) + + data = [] + for grp in _security_groups: + data.append(( + grp.id, + grp.name, + grp.description, + )) + data_all_projects = [] + for grp in _security_groups: + data_all_projects.append(( + grp.id, + grp.name, + grp.description, + grp.tenant_id, + )) + + def setUp(self): + super(TestListSecurityGroupCompute, self).setUp() + + self.app.client_manager.network_endpoint_enabled = False + self.compute.security_groups.list.return_value = self._security_groups + + # Get the command object to test + self.cmd = security_group.ListSecurityGroup(self.app, None) + + def test_security_group_list_no_options(self): + arglist = [] + verifylist = [ + ('all_projects', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + kwargs = {'search_opts': {'all_tenants': False}} + self.compute.security_groups.list.assert_called_once_with(**kwargs) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_security_group_list_all_projects(self): + arglist = [ + '--all-projects', + ] + verifylist = [ + ('all_projects', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + kwargs = {'search_opts': {'all_tenants': True}} + self.compute.security_groups.list.assert_called_once_with(**kwargs) + self.assertEqual(self.columns_all_projects, columns) + self.assertEqual(self.data_all_projects, list(data)) + + +class TestSetSecurityGroupNetwork(TestSecurityGroupNetwork): + + # The security group to be set. + _security_group = \ + network_fakes.FakeSecurityGroup.create_one_security_group() + + def setUp(self): + super(TestSetSecurityGroupNetwork, self).setUp() + + self.network.update_security_group = mock.Mock(return_value=None) + + self.network.find_security_group = mock.Mock( + return_value=self._security_group) + + # Get the command object to test + self.cmd = security_group.SetSecurityGroup(self.app, self.namespace) + + def test_set_no_options(self): + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, [], []) + + def test_set_no_updates(self): + arglist = [ + self._security_group.name, + ] + verifylist = [ + ('group', self._security_group.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.network.update_security_group.assert_called_once_with( + self._security_group, + **{} + ) + self.assertIsNone(result) + + def test_set_all_options(self): + new_name = 'new-' + self._security_group.name + new_description = 'new-' + self._security_group.description + arglist = [ + '--name', new_name, + '--description', new_description, + self._security_group.name, + ] + verifylist = [ + ('description', new_description), + ('group', self._security_group.name), + ('name', new_name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = { + 'description': new_description, + 'name': new_name, + } + self.network.update_security_group.assert_called_once_with( + self._security_group, + **attrs + ) + self.assertIsNone(result) + + +class TestSetSecurityGroupCompute(TestSecurityGroupCompute): + + # The security group to be set. + _security_group = \ + compute_fakes.FakeSecurityGroup.create_one_security_group() + + def setUp(self): + super(TestSetSecurityGroupCompute, self).setUp() + + self.app.client_manager.network_endpoint_enabled = False + + self.compute.security_groups.update = mock.Mock(return_value=None) + + self.compute.security_groups.get = mock.Mock( + return_value=self._security_group) + + # Get the command object to test + self.cmd = security_group.SetSecurityGroup(self.app, None) + + def test_set_no_options(self): + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, [], []) + + def test_set_no_updates(self): + arglist = [ + self._security_group.name, + ] + verifylist = [ + ('group', self._security_group.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.compute.security_groups.update.assert_called_once_with( + self._security_group, + self._security_group.name, + self._security_group.description + ) + self.assertIsNone(result) + + def test_set_all_options(self): + new_name = 'new-' + self._security_group.name + new_description = 'new-' + self._security_group.description + arglist = [ + '--name', new_name, + '--description', new_description, + self._security_group.name, + ] + verifylist = [ + ('description', new_description), + ('group', self._security_group.name), + ('name', new_name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.compute.security_groups.update.assert_called_once_with( + self._security_group, + new_name, + new_description + ) + self.assertIsNone(result) + + +class TestShowSecurityGroupNetwork(TestSecurityGroupNetwork): + + # The security group rule to be shown with the group. + _security_group_rule = \ + network_fakes.FakeSecurityGroupRule.create_one_security_group_rule() + + # The security group to be shown. + _security_group = \ + network_fakes.FakeSecurityGroup.create_one_security_group( + attrs={'security_group_rules': [_security_group_rule._info]} + ) + + columns = ( + 'description', + 'id', + 'name', + 'project_id', + 'rules', + ) + + data = ( + _security_group.description, + _security_group.id, + _security_group.name, + _security_group.project_id, + security_group._format_network_security_group_rules( + [_security_group_rule._info]), + ) + + def setUp(self): + super(TestShowSecurityGroupNetwork, self).setUp() + + self.network.find_security_group = mock.Mock( + return_value=self._security_group) + + # Get the command object to test + self.cmd = security_group.ShowSecurityGroup(self.app, self.namespace) + + def test_show_no_options(self): + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, [], []) + + def test_show_all_options(self): + arglist = [ + self._security_group.id, + ] + verifylist = [ + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.find_security_group.assert_called_once_with( + self._security_group.id, ignore_missing=False) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestShowSecurityGroupCompute(TestSecurityGroupCompute): + + # The security group rule to be shown with the group. + _security_group_rule = \ + compute_fakes.FakeSecurityGroupRule.create_one_security_group_rule() + + # The security group to be shown. + _security_group = \ + compute_fakes.FakeSecurityGroup.create_one_security_group( + attrs={'rules': [_security_group_rule._info]} + ) + + columns = ( + 'description', + 'id', + 'name', + 'project_id', + 'rules', + ) + + data = ( + _security_group.description, + _security_group.id, + _security_group.name, + _security_group.tenant_id, + security_group._format_compute_security_group_rules( + [_security_group_rule._info]), + ) + + def setUp(self): + super(TestShowSecurityGroupCompute, self).setUp() + + self.app.client_manager.network_endpoint_enabled = False + + self.compute.security_groups.get.return_value = self._security_group + + # Get the command object to test + self.cmd = security_group.ShowSecurityGroup(self.app, None) + + def test_show_no_options(self): + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, [], []) + + def test_show_all_options(self): + arglist = [ + self._security_group.id, + ] + verifylist = [ + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.compute.security_groups.get.assert_called_once_with( + self._security_group.id) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/network/v2/test_security_group_rule.py b/openstackclient/tests/unit/network/v2/test_security_group_rule.py new file mode 100644 index 00000000..51e18a65 --- /dev/null +++ b/openstackclient/tests/unit/network/v2/test_security_group_rule.py @@ -0,0 +1,1177 @@ +# 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 copy +import mock +from mock import call + +from osc_lib import exceptions + +from openstackclient.network import utils as network_utils +from openstackclient.network.v2 import security_group_rule +from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes +from openstackclient.tests.unit.network.v2 import fakes as network_fakes +from openstackclient.tests.unit import utils as tests_utils + + +class TestSecurityGroupRuleNetwork(network_fakes.TestNetworkV2): + + def setUp(self): + super(TestSecurityGroupRuleNetwork, self).setUp() + + # Get a shortcut to the network client + self.network = self.app.client_manager.network + # Get a shortcut to the ProjectManager Mock + self.projects_mock = self.app.client_manager.identity.projects + # Get a shortcut to the DomainManager Mock + self.domains_mock = self.app.client_manager.identity.domains + + +class TestSecurityGroupRuleCompute(compute_fakes.TestComputev2): + + def setUp(self): + super(TestSecurityGroupRuleCompute, self).setUp() + + # Get a shortcut to the network client + self.compute = self.app.client_manager.compute + + +class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): + + project = identity_fakes.FakeProject.create_one_project() + domain = identity_fakes.FakeDomain.create_one_domain() + # The security group rule to be created. + _security_group_rule = None + + # The security group that will contain the rule created. + _security_group = \ + network_fakes.FakeSecurityGroup.create_one_security_group() + + expected_columns = ( + 'direction', + 'ethertype', + 'id', + 'port_range_max', + 'port_range_min', + 'project_id', + 'protocol', + 'remote_group_id', + 'remote_ip_prefix', + 'security_group_id', + ) + + expected_data = None + + def _setup_security_group_rule(self, attrs=None): + self._security_group_rule = \ + network_fakes.FakeSecurityGroupRule.create_one_security_group_rule( + attrs) + self.network.create_security_group_rule = mock.Mock( + return_value=self._security_group_rule) + self.expected_data = ( + self._security_group_rule.direction, + self._security_group_rule.ethertype, + self._security_group_rule.id, + self._security_group_rule.port_range_max, + self._security_group_rule.port_range_min, + self._security_group_rule.project_id, + self._security_group_rule.protocol, + self._security_group_rule.remote_group_id, + self._security_group_rule.remote_ip_prefix, + self._security_group_rule.security_group_id, + ) + + def setUp(self): + super(TestCreateSecurityGroupRuleNetwork, self).setUp() + + self.network.find_security_group = mock.Mock( + return_value=self._security_group) + + self.projects_mock.get.return_value = self.project + self.domains_mock.get.return_value = self.domain + + # Get the command object to test + self.cmd = security_group_rule.CreateSecurityGroupRule( + self.app, self.namespace) + + def test_create_no_options(self): + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, [], []) + + def test_create_all_source_options(self): + arglist = [ + '--src-ip', '10.10.0.0/24', + '--src-group', self._security_group.id, + self._security_group.id, + ] + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, arglist, []) + + def test_create_bad_ethertype(self): + arglist = [ + '--ethertype', 'foo', + self._security_group.id, + ] + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, arglist, []) + + def test_create_all_protocol_options(self): + arglist = [ + '--protocol', 'tcp', + '--proto', 'tcp', + self._security_group.id, + ] + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, arglist, []) + + def test_create_all_port_range_options(self): + arglist = [ + '--dst-port', '80:80', + '--icmp-type', '3', + '--icmp-code', '1', + self._security_group.id, + ] + verifylist = [ + ('dst_port', (80, 80)), + ('icmp_type', 3), + ('icmp_code', 1), + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises(exceptions.CommandError, self.cmd.take_action, + parsed_args) + + def test_create_default_rule(self): + self._setup_security_group_rule({ + 'port_range_max': 443, + 'port_range_min': 443, + }) + arglist = [ + '--dst-port', str(self._security_group_rule.port_range_min), + self._security_group.id, + ] + verifylist = [ + ('dst_port', (self._security_group_rule.port_range_min, + self._security_group_rule.port_range_max)), + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_security_group_rule.assert_called_once_with(**{ + 'direction': self._security_group_rule.direction, + 'ethertype': self._security_group_rule.ethertype, + 'port_range_max': self._security_group_rule.port_range_max, + 'port_range_min': self._security_group_rule.port_range_min, + 'protocol': self._security_group_rule.protocol, + 'remote_ip_prefix': self._security_group_rule.remote_ip_prefix, + 'security_group_id': self._security_group.id, + }) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + + def test_create_proto_option(self): + self._setup_security_group_rule({ + 'protocol': 'icmp', + 'remote_ip_prefix': '10.0.2.0/24', + }) + arglist = [ + '--proto', self._security_group_rule.protocol, + '--src-ip', self._security_group_rule.remote_ip_prefix, + self._security_group.id, + ] + verifylist = [ + ('proto', self._security_group_rule.protocol), + ('protocol', None), + ('src_ip', self._security_group_rule.remote_ip_prefix), + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_security_group_rule.assert_called_once_with(**{ + 'direction': self._security_group_rule.direction, + 'ethertype': self._security_group_rule.ethertype, + 'protocol': self._security_group_rule.protocol, + 'remote_ip_prefix': self._security_group_rule.remote_ip_prefix, + 'security_group_id': self._security_group.id, + }) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + + def test_create_source_group(self): + self._setup_security_group_rule({ + 'port_range_max': 22, + 'port_range_min': 22, + 'remote_group_id': self._security_group.id, + }) + arglist = [ + '--dst-port', str(self._security_group_rule.port_range_min), + '--ingress', + '--src-group', self._security_group.name, + self._security_group.id, + ] + verifylist = [ + ('dst_port', (self._security_group_rule.port_range_min, + self._security_group_rule.port_range_max)), + ('ingress', True), + ('src_group', self._security_group.name), + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_security_group_rule.assert_called_once_with(**{ + 'direction': self._security_group_rule.direction, + 'ethertype': self._security_group_rule.ethertype, + 'port_range_max': self._security_group_rule.port_range_max, + 'port_range_min': self._security_group_rule.port_range_min, + 'protocol': self._security_group_rule.protocol, + 'remote_group_id': self._security_group_rule.remote_group_id, + 'security_group_id': self._security_group.id, + }) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + + def test_create_source_ip(self): + self._setup_security_group_rule({ + 'protocol': 'icmp', + 'remote_ip_prefix': '10.0.2.0/24', + }) + arglist = [ + '--protocol', self._security_group_rule.protocol, + '--src-ip', self._security_group_rule.remote_ip_prefix, + self._security_group.id, + ] + verifylist = [ + ('protocol', self._security_group_rule.protocol), + ('src_ip', self._security_group_rule.remote_ip_prefix), + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_security_group_rule.assert_called_once_with(**{ + 'direction': self._security_group_rule.direction, + 'ethertype': self._security_group_rule.ethertype, + 'protocol': self._security_group_rule.protocol, + 'remote_ip_prefix': self._security_group_rule.remote_ip_prefix, + 'security_group_id': self._security_group.id, + }) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + + def test_create_network_options(self): + self._setup_security_group_rule({ + 'direction': 'egress', + 'ethertype': 'IPv6', + 'port_range_max': 443, + 'port_range_min': 443, + 'protocol': '6', + 'remote_group_id': None, + 'remote_ip_prefix': None, + }) + arglist = [ + '--dst-port', str(self._security_group_rule.port_range_min), + '--egress', + '--ethertype', self._security_group_rule.ethertype, + '--project', self.project.name, + '--project-domain', self.domain.name, + '--protocol', self._security_group_rule.protocol, + self._security_group.id, + ] + verifylist = [ + ('dst_port', (self._security_group_rule.port_range_min, + self._security_group_rule.port_range_max)), + ('egress', True), + ('ethertype', self._security_group_rule.ethertype), + ('project', self.project.name), + ('project_domain', self.domain.name), + ('protocol', self._security_group_rule.protocol), + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_security_group_rule.assert_called_once_with(**{ + 'direction': self._security_group_rule.direction, + 'ethertype': self._security_group_rule.ethertype, + 'port_range_max': self._security_group_rule.port_range_max, + 'port_range_min': self._security_group_rule.port_range_min, + 'protocol': self._security_group_rule.protocol, + 'security_group_id': self._security_group.id, + 'tenant_id': self.project.id, + }) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + + def test_create_tcp_with_icmp_type(self): + arglist = [ + '--protocol', 'tcp', + '--icmp-type', '15', + self._security_group.id, + ] + verifylist = [ + ('protocol', 'tcp'), + ('icmp_type', 15), + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises(exceptions.CommandError, self.cmd.take_action, + parsed_args) + + def test_create_icmp_code(self): + arglist = [ + '--protocol', '1', + '--icmp-code', '1', + self._security_group.id, + ] + verifylist = [ + ('protocol', '1'), + ('icmp_code', 1), + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises(exceptions.CommandError, self.cmd.take_action, + parsed_args) + + def test_create_icmp_type(self): + self._setup_security_group_rule({ + 'port_range_min': 15, + 'protocol': 'icmp', + 'remote_ip_prefix': '0.0.0.0/0', + }) + arglist = [ + '--icmp-type', str(self._security_group_rule.port_range_min), + '--protocol', self._security_group_rule.protocol, + self._security_group.id, + ] + verifylist = [ + ('dst_port', None), + ('icmp_type', self._security_group_rule.port_range_min), + ('icmp_code', None), + ('protocol', self._security_group_rule.protocol), + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_security_group_rule.assert_called_once_with(**{ + 'direction': self._security_group_rule.direction, + 'ethertype': self._security_group_rule.ethertype, + 'port_range_min': self._security_group_rule.port_range_min, + 'protocol': self._security_group_rule.protocol, + 'remote_ip_prefix': self._security_group_rule.remote_ip_prefix, + 'security_group_id': self._security_group.id, + }) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + + def test_create_ipv6_icmp_type_code(self): + self._setup_security_group_rule({ + 'ethertype': 'IPv6', + 'port_range_min': 139, + 'port_range_max': 2, + 'protocol': 'ipv6-icmp', + }) + arglist = [ + '--icmp-type', str(self._security_group_rule.port_range_min), + '--icmp-code', str(self._security_group_rule.port_range_max), + '--protocol', self._security_group_rule.protocol, + self._security_group.id, + ] + verifylist = [ + ('dst_port', None), + ('icmp_type', self._security_group_rule.port_range_min), + ('icmp_code', self._security_group_rule.port_range_max), + ('protocol', self._security_group_rule.protocol), + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_security_group_rule.assert_called_once_with(**{ + 'direction': self._security_group_rule.direction, + 'ethertype': self._security_group_rule.ethertype, + 'port_range_min': self._security_group_rule.port_range_min, + 'port_range_max': self._security_group_rule.port_range_max, + 'protocol': self._security_group_rule.protocol, + 'security_group_id': self._security_group.id, + }) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + + def test_create_icmpv6_type(self): + self._setup_security_group_rule({ + 'ethertype': 'IPv6', + 'port_range_min': 139, + 'protocol': 'icmpv6', + }) + arglist = [ + '--icmp-type', str(self._security_group_rule.port_range_min), + '--protocol', self._security_group_rule.protocol, + self._security_group.id, + ] + verifylist = [ + ('dst_port', None), + ('icmp_type', self._security_group_rule.port_range_min), + ('icmp_code', None), + ('protocol', self._security_group_rule.protocol), + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_security_group_rule.assert_called_once_with(**{ + 'direction': self._security_group_rule.direction, + 'ethertype': self._security_group_rule.ethertype, + 'port_range_min': self._security_group_rule.port_range_min, + 'protocol': self._security_group_rule.protocol, + 'security_group_id': self._security_group.id, + }) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + + +class TestCreateSecurityGroupRuleCompute(TestSecurityGroupRuleCompute): + + project = identity_fakes.FakeProject.create_one_project() + domain = identity_fakes.FakeDomain.create_one_domain() + # The security group rule to be created. + _security_group_rule = None + + # The security group that will contain the rule created. + _security_group = \ + compute_fakes.FakeSecurityGroup.create_one_security_group() + + def _setup_security_group_rule(self, attrs=None): + self._security_group_rule = \ + compute_fakes.FakeSecurityGroupRule.create_one_security_group_rule( + attrs) + self.compute.security_group_rules.create.return_value = \ + self._security_group_rule + expected_columns, expected_data = \ + security_group_rule._format_security_group_rule_show( + self._security_group_rule._info) + return expected_columns, expected_data + + def setUp(self): + super(TestCreateSecurityGroupRuleCompute, self).setUp() + + self.app.client_manager.network_endpoint_enabled = False + + self.compute.security_groups.get.return_value = self._security_group + + # Get the command object to test + self.cmd = security_group_rule.CreateSecurityGroupRule(self.app, None) + + def test_create_no_options(self): + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, [], []) + + def test_create_all_source_options(self): + arglist = [ + '--src-ip', '10.10.0.0/24', + '--src-group', self._security_group.id, + self._security_group.id, + ] + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, arglist, []) + + def test_create_bad_protocol(self): + arglist = [ + '--protocol', 'foo', + self._security_group.id, + ] + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, arglist, []) + + def test_create_all_protocol_options(self): + arglist = [ + '--protocol', 'tcp', + '--proto', 'tcp', + self._security_group.id, + ] + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, arglist, []) + + def test_create_network_options(self): + arglist = [ + '--ingress', + '--ethertype', 'IPv4', + '--icmp-type', '3', + '--icmp-code', '11', + '--project', self.project.name, + '--project-domain', self.domain.name, + self._security_group.id, + ] + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, arglist, []) + + def test_create_default_rule(self): + expected_columns, expected_data = self._setup_security_group_rule() + dst_port = str(self._security_group_rule.from_port) + ':' + \ + str(self._security_group_rule.to_port) + arglist = [ + '--dst-port', dst_port, + self._security_group.id, + ] + verifylist = [ + ('dst_port', (self._security_group_rule.from_port, + self._security_group_rule.to_port)), + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.compute.security_group_rules.create.assert_called_once_with( + self._security_group.id, + self._security_group_rule.ip_protocol, + self._security_group_rule.from_port, + self._security_group_rule.to_port, + self._security_group_rule.ip_range['cidr'], + None, + ) + self.assertEqual(expected_columns, columns) + self.assertEqual(expected_data, data) + + def test_create_source_group(self): + expected_columns, expected_data = self._setup_security_group_rule({ + 'from_port': 22, + 'to_port': 22, + 'group': {'name': self._security_group.name}, + }) + arglist = [ + '--dst-port', str(self._security_group_rule.from_port), + '--src-group', self._security_group.name, + self._security_group.id, + ] + verifylist = [ + ('dst_port', (self._security_group_rule.from_port, + self._security_group_rule.to_port)), + ('src_group', self._security_group.name), + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.compute.security_group_rules.create.assert_called_once_with( + self._security_group.id, + self._security_group_rule.ip_protocol, + self._security_group_rule.from_port, + self._security_group_rule.to_port, + self._security_group_rule.ip_range['cidr'], + self._security_group.id, + ) + self.assertEqual(expected_columns, columns) + self.assertEqual(expected_data, data) + + def test_create_source_ip(self): + expected_columns, expected_data = self._setup_security_group_rule({ + 'ip_protocol': 'icmp', + 'from_port': -1, + 'to_port': -1, + 'ip_range': {'cidr': '10.0.2.0/24'}, + }) + arglist = [ + '--protocol', self._security_group_rule.ip_protocol, + '--src-ip', self._security_group_rule.ip_range['cidr'], + self._security_group.id, + ] + verifylist = [ + ('protocol', self._security_group_rule.ip_protocol), + ('src_ip', self._security_group_rule.ip_range['cidr']), + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.compute.security_group_rules.create.assert_called_once_with( + self._security_group.id, + self._security_group_rule.ip_protocol, + self._security_group_rule.from_port, + self._security_group_rule.to_port, + self._security_group_rule.ip_range['cidr'], + None, + ) + self.assertEqual(expected_columns, columns) + self.assertEqual(expected_data, data) + + def test_create_proto_option(self): + expected_columns, expected_data = self._setup_security_group_rule({ + 'ip_protocol': 'icmp', + 'from_port': -1, + 'to_port': -1, + 'ip_range': {'cidr': '10.0.2.0/24'}, + }) + arglist = [ + '--proto', self._security_group_rule.ip_protocol, + '--src-ip', self._security_group_rule.ip_range['cidr'], + self._security_group.id, + ] + verifylist = [ + ('proto', self._security_group_rule.ip_protocol), + ('protocol', None), + ('src_ip', self._security_group_rule.ip_range['cidr']), + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.compute.security_group_rules.create.assert_called_once_with( + self._security_group.id, + self._security_group_rule.ip_protocol, + self._security_group_rule.from_port, + self._security_group_rule.to_port, + self._security_group_rule.ip_range['cidr'], + None, + ) + self.assertEqual(expected_columns, columns) + self.assertEqual(expected_data, data) + + +class TestDeleteSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): + + # The security group rules to be deleted. + _security_group_rules = \ + network_fakes.FakeSecurityGroupRule.create_security_group_rules( + count=2) + + def setUp(self): + super(TestDeleteSecurityGroupRuleNetwork, self).setUp() + + self.network.delete_security_group_rule = mock.Mock(return_value=None) + + self.network.find_security_group_rule = ( + network_fakes.FakeSecurityGroupRule.get_security_group_rules( + self._security_group_rules) + ) + + # Get the command object to test + self.cmd = security_group_rule.DeleteSecurityGroupRule( + self.app, self.namespace) + + def test_security_group_rule_delete(self): + arglist = [ + self._security_group_rules[0].id, + ] + verifylist = [ + ('rule', [self._security_group_rules[0].id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.network.delete_security_group_rule.assert_called_once_with( + self._security_group_rules[0]) + self.assertIsNone(result) + + def test_multi_security_group_rules_delete(self): + arglist = [] + verifylist = [] + + for s in self._security_group_rules: + arglist.append(s.id) + verifylist = [ + ('rule', arglist), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + calls = [] + for s in self._security_group_rules: + calls.append(call(s)) + self.network.delete_security_group_rule.assert_has_calls(calls) + self.assertIsNone(result) + + def test_multi_security_group_rules_delete_with_exception(self): + arglist = [ + self._security_group_rules[0].id, + 'unexist_rule', + ] + verifylist = [ + ('rule', + [self._security_group_rules[0].id, 'unexist_rule']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + find_mock_result = [ + self._security_group_rules[0], exceptions.CommandError] + self.network.find_security_group_rule = ( + mock.MagicMock(side_effect=find_mock_result) + ) + + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 rules failed to delete.', str(e)) + + self.network.find_security_group_rule.assert_any_call( + self._security_group_rules[0].id, ignore_missing=False) + self.network.find_security_group_rule.assert_any_call( + 'unexist_rule', ignore_missing=False) + self.network.delete_security_group_rule.assert_called_once_with( + self._security_group_rules[0] + ) + + +class TestDeleteSecurityGroupRuleCompute(TestSecurityGroupRuleCompute): + + # The security group rule to be deleted. + _security_group_rules = \ + compute_fakes.FakeSecurityGroupRule.create_security_group_rules( + count=2) + + def setUp(self): + super(TestDeleteSecurityGroupRuleCompute, self).setUp() + + self.app.client_manager.network_endpoint_enabled = False + + # Get the command object to test + self.cmd = security_group_rule.DeleteSecurityGroupRule(self.app, None) + + def test_security_group_rule_delete(self): + arglist = [ + self._security_group_rules[0].id, + ] + verifylist = [ + ('rule', [self._security_group_rules[0].id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.compute.security_group_rules.delete.assert_called_once_with( + self._security_group_rules[0].id) + self.assertIsNone(result) + + def test_multi_security_group_rules_delete(self): + arglist = [] + verifylist = [] + + for s in self._security_group_rules: + arglist.append(s.id) + verifylist = [ + ('rule', arglist), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + calls = [] + for s in self._security_group_rules: + calls.append(call(s.id)) + self.compute.security_group_rules.delete.assert_has_calls(calls) + self.assertIsNone(result) + + def test_multi_security_group_rules_delete_with_exception(self): + arglist = [ + self._security_group_rules[0].id, + 'unexist_rule', + ] + verifylist = [ + ('rule', + [self._security_group_rules[0].id, 'unexist_rule']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + find_mock_result = [None, exceptions.CommandError] + self.compute.security_group_rules.delete = ( + mock.MagicMock(side_effect=find_mock_result) + ) + + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 rules failed to delete.', str(e)) + + self.compute.security_group_rules.delete.assert_any_call( + self._security_group_rules[0].id) + self.compute.security_group_rules.delete.assert_any_call( + 'unexist_rule') + + +class TestListSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): + + # The security group to hold the rules. + _security_group = \ + network_fakes.FakeSecurityGroup.create_one_security_group() + + # The security group rule to be listed. + _security_group_rule_tcp = \ + network_fakes.FakeSecurityGroupRule.create_one_security_group_rule({ + 'protocol': 'tcp', + 'port_range_max': 80, + 'port_range_min': 80, + 'security_group_id': _security_group.id, + }) + _security_group_rule_icmp = \ + network_fakes.FakeSecurityGroupRule.create_one_security_group_rule({ + 'protocol': 'icmp', + 'remote_ip_prefix': '10.0.2.0/24', + 'security_group_id': _security_group.id, + }) + _security_group.security_group_rules = [_security_group_rule_tcp._info, + _security_group_rule_icmp._info] + _security_group_rules = [_security_group_rule_tcp, + _security_group_rule_icmp] + + expected_columns_with_group_and_long = ( + 'ID', + 'IP Protocol', + 'IP Range', + 'Port Range', + 'Direction', + 'Ethertype', + 'Remote Security Group', + ) + expected_columns_no_group = ( + 'ID', + 'IP Protocol', + 'IP Range', + 'Port Range', + 'Remote Security Group', + 'Security Group', + ) + + expected_data_with_group_and_long = [] + expected_data_no_group = [] + for _security_group_rule in _security_group_rules: + expected_data_with_group_and_long.append(( + _security_group_rule.id, + _security_group_rule.protocol, + _security_group_rule.remote_ip_prefix, + security_group_rule._format_network_port_range( + _security_group_rule), + _security_group_rule.direction, + _security_group_rule.ethertype, + _security_group_rule.remote_group_id, + )) + expected_data_no_group.append(( + _security_group_rule.id, + _security_group_rule.protocol, + _security_group_rule.remote_ip_prefix, + security_group_rule._format_network_port_range( + _security_group_rule), + _security_group_rule.remote_group_id, + _security_group_rule.security_group_id, + )) + + def setUp(self): + super(TestListSecurityGroupRuleNetwork, self).setUp() + + self.network.find_security_group = mock.Mock( + return_value=self._security_group) + self.network.security_group_rules = mock.Mock( + return_value=self._security_group_rules) + + # Get the command object to test + self.cmd = security_group_rule.ListSecurityGroupRule( + self.app, self.namespace) + + def test_list_default(self): + self._security_group_rule_tcp.port_range_min = 80 + parsed_args = self.check_parser(self.cmd, [], []) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.security_group_rules.assert_called_once_with(**{}) + self.assertEqual(self.expected_columns_no_group, columns) + self.assertEqual(self.expected_data_no_group, list(data)) + + def test_list_with_group_and_long(self): + self._security_group_rule_tcp.port_range_min = 80 + arglist = [ + '--long', + self._security_group.id, + ] + verifylist = [ + ('long', True), + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.security_group_rules.assert_called_once_with(**{ + 'security_group_id': self._security_group.id, + }) + self.assertEqual(self.expected_columns_with_group_and_long, columns) + self.assertEqual(self.expected_data_with_group_and_long, list(data)) + + def test_list_with_ignored_options(self): + self._security_group_rule_tcp.port_range_min = 80 + arglist = [ + '--all-projects', + ] + verifylist = [ + ('all_projects', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.security_group_rules.assert_called_once_with(**{}) + self.assertEqual(self.expected_columns_no_group, columns) + self.assertEqual(self.expected_data_no_group, list(data)) + + +class TestListSecurityGroupRuleCompute(TestSecurityGroupRuleCompute): + + # The security group to hold the rules. + _security_group = \ + compute_fakes.FakeSecurityGroup.create_one_security_group() + + # The security group rule to be listed. + _security_group_rule_tcp = \ + compute_fakes.FakeSecurityGroupRule.create_one_security_group_rule({ + 'ip_protocol': 'tcp', + 'from_port': 80, + 'to_port': 80, + 'group': {'name': _security_group.name}, + }) + _security_group_rule_icmp = \ + compute_fakes.FakeSecurityGroupRule.create_one_security_group_rule({ + 'ip_protocol': 'icmp', + 'from_port': -1, + 'to_port': -1, + 'ip_range': {'cidr': '10.0.2.0/24'}, + 'group': {'name': _security_group.name}, + }) + _security_group.rules = [_security_group_rule_tcp._info, + _security_group_rule_icmp._info] + + expected_columns_with_group = ( + 'ID', + 'IP Protocol', + 'IP Range', + 'Port Range', + 'Remote Security Group', + ) + expected_columns_no_group = \ + expected_columns_with_group + ('Security Group',) + + expected_data_with_group = [] + expected_data_no_group = [] + for _security_group_rule in _security_group.rules: + rule = network_utils.transform_compute_security_group_rule( + _security_group_rule + ) + expected_rule_with_group = ( + rule['id'], + rule['ip_protocol'], + rule['ip_range'], + rule['port_range'], + rule['remote_security_group'], + ) + expected_rule_no_group = expected_rule_with_group + \ + (_security_group_rule['parent_group_id'],) + expected_data_with_group.append(expected_rule_with_group) + expected_data_no_group.append(expected_rule_no_group) + + def setUp(self): + super(TestListSecurityGroupRuleCompute, self).setUp() + + self.app.client_manager.network_endpoint_enabled = False + + self.compute.security_groups.get.return_value = \ + self._security_group + self.compute.security_groups.list.return_value = \ + [self._security_group] + + # Get the command object to test + self.cmd = security_group_rule.ListSecurityGroupRule(self.app, None) + + def test_list_default(self): + parsed_args = self.check_parser(self.cmd, [], []) + + columns, data = self.cmd.take_action(parsed_args) + self.compute.security_groups.list.assert_called_once_with( + search_opts={'all_tenants': False} + ) + self.assertEqual(self.expected_columns_no_group, columns) + self.assertEqual(self.expected_data_no_group, list(data)) + + def test_list_with_group(self): + arglist = [ + self._security_group.id, + ] + verifylist = [ + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.compute.security_groups.get.assert_called_once_with( + self._security_group.id + ) + self.assertEqual(self.expected_columns_with_group, columns) + self.assertEqual(self.expected_data_with_group, list(data)) + + def test_list_all_projects(self): + arglist = [ + '--all-projects', + ] + verifylist = [ + ('all_projects', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.compute.security_groups.list.assert_called_once_with( + search_opts={'all_tenants': True} + ) + self.assertEqual(self.expected_columns_no_group, columns) + self.assertEqual(self.expected_data_no_group, list(data)) + + def test_list_with_ignored_options(self): + arglist = [ + '--long', + ] + verifylist = [ + ('long', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.compute.security_groups.list.assert_called_once_with( + search_opts={'all_tenants': False} + ) + self.assertEqual(self.expected_columns_no_group, columns) + self.assertEqual(self.expected_data_no_group, list(data)) + + +class TestShowSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): + + # The security group rule to be shown. + _security_group_rule = \ + network_fakes.FakeSecurityGroupRule.create_one_security_group_rule() + + columns = ( + 'direction', + 'ethertype', + 'id', + 'port_range_max', + 'port_range_min', + 'project_id', + 'protocol', + 'remote_group_id', + 'remote_ip_prefix', + 'security_group_id', + ) + + data = ( + _security_group_rule.direction, + _security_group_rule.ethertype, + _security_group_rule.id, + _security_group_rule.port_range_max, + _security_group_rule.port_range_min, + _security_group_rule.project_id, + _security_group_rule.protocol, + _security_group_rule.remote_group_id, + _security_group_rule.remote_ip_prefix, + _security_group_rule.security_group_id, + ) + + def setUp(self): + super(TestShowSecurityGroupRuleNetwork, self).setUp() + + self.network.find_security_group_rule = mock.Mock( + return_value=self._security_group_rule) + + # Get the command object to test + self.cmd = security_group_rule.ShowSecurityGroupRule( + self.app, self.namespace) + + def test_show_no_options(self): + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, [], []) + + def test_show_all_options(self): + arglist = [ + self._security_group_rule.id, + ] + verifylist = [ + ('rule', self._security_group_rule.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.find_security_group_rule.assert_called_once_with( + self._security_group_rule.id, ignore_missing=False) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestShowSecurityGroupRuleCompute(TestSecurityGroupRuleCompute): + + # The security group rule to be shown. + _security_group_rule = \ + compute_fakes.FakeSecurityGroupRule.create_one_security_group_rule() + + columns, data = \ + security_group_rule._format_security_group_rule_show( + _security_group_rule._info) + + def setUp(self): + super(TestShowSecurityGroupRuleCompute, self).setUp() + + self.app.client_manager.network_endpoint_enabled = False + + # Build a security group fake customized for this test. + security_group_rules = [self._security_group_rule._info] + security_group = fakes.FakeResource( + info=copy.deepcopy({'rules': security_group_rules}), + loaded=True) + security_group.rules = security_group_rules + self.compute.security_groups.list.return_value = [security_group] + + # Get the command object to test + self.cmd = security_group_rule.ShowSecurityGroupRule(self.app, None) + + def test_show_no_options(self): + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, [], []) + + def test_show_all_options(self): + arglist = [ + self._security_group_rule.id, + ] + verifylist = [ + ('rule', self._security_group_rule.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.compute.security_groups.list.assert_called_once_with() + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/network/v2/test_subnet.py b/openstackclient/tests/unit/network/v2/test_subnet.py new file mode 100644 index 00000000..e31db469 --- /dev/null +++ b/openstackclient/tests/unit/network/v2/test_subnet.py @@ -0,0 +1,978 @@ +# 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 mock import call + +from osc_lib import exceptions +from osc_lib import utils + +from openstackclient.network.v2 import subnet as subnet_v2 +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3 +from openstackclient.tests.unit.network.v2 import fakes as network_fakes +from openstackclient.tests.unit import utils as tests_utils + + +class TestSubnet(network_fakes.TestNetworkV2): + + def setUp(self): + super(TestSubnet, self).setUp() + + # Get a shortcut to the network client + self.network = self.app.client_manager.network + # Get a shortcut to the ProjectManager Mock + self.projects_mock = self.app.client_manager.identity.projects + # Get a shortcut to the DomainManager Mock + self.domains_mock = self.app.client_manager.identity.domains + + +class TestCreateSubnet(TestSubnet): + + project = identity_fakes_v3.FakeProject.create_one_project() + domain = identity_fakes_v3.FakeDomain.create_one_domain() + # An IPv4 subnet to be created with mostly default values + _subnet = network_fakes.FakeSubnet.create_one_subnet( + attrs={ + 'tenant_id': project.id, + } + ) + + # Subnet pool to be used to create a subnet from a pool + _subnet_pool = network_fakes.FakeSubnetPool.create_one_subnet_pool() + + # An IPv4 subnet to be created using a specific subnet pool + _subnet_from_pool = network_fakes.FakeSubnet.create_one_subnet( + attrs={ + 'tenant_id': project.id, + 'subnetpool_id': _subnet_pool.id, + 'dns_nameservers': ['8.8.8.8', + '8.8.4.4'], + 'host_routes': [{'destination': '10.20.20.0/24', + 'nexthop': '10.20.20.1'}, + {'destination': '10.30.30.0/24', + 'nexthop': '10.30.30.1'}], + 'service_types': ['network:router_gateway', + 'network:floatingip_agent_gateway'], + } + ) + + # An IPv6 subnet to be created with most options specified + _subnet_ipv6 = network_fakes.FakeSubnet.create_one_subnet( + attrs={ + 'tenant_id': project.id, + 'cidr': 'fe80:0:0:a00a::/64', + 'enable_dhcp': True, + 'dns_nameservers': ['fe80:27ff:a00a:f00f::ffff', + 'fe80:37ff:a00a:f00f::ffff'], + 'allocation_pools': [{'start': 'fe80::a00a:0:c0de:0:100', + 'end': 'fe80::a00a:0:c0de:0:f000'}, + {'start': 'fe80::a00a:0:c0de:1:100', + 'end': 'fe80::a00a:0:c0de:1:f000'}], + 'host_routes': [{'destination': 'fe80:27ff:a00a:f00f::/64', + 'nexthop': 'fe80:27ff:a00a:f00f::1'}, + {'destination': 'fe80:37ff:a00a:f00f::/64', + 'nexthop': 'fe80:37ff:a00a:f00f::1'}], + 'ip_version': 6, + 'gateway_ip': 'fe80::a00a:0:c0de:0:1', + 'ipv6_address_mode': 'slaac', + 'ipv6_ra_mode': 'slaac', + 'subnetpool_id': 'None', + 'service_types': ['network:router_gateway', + 'network:floatingip_agent_gateway'], + } + ) + + # The network to be returned from find_network + _network = network_fakes.FakeNetwork.create_one_network( + attrs={ + 'id': _subnet.network_id, + } + ) + + # The network segment to be returned from find_segment + _network_segment = \ + network_fakes.FakeNetworkSegment.create_one_network_segment( + attrs={ + 'network_id': _subnet.network_id, + } + ) + + columns = ( + 'allocation_pools', + 'cidr', + 'dns_nameservers', + 'enable_dhcp', + 'gateway_ip', + 'host_routes', + 'id', + 'ip_version', + 'ipv6_address_mode', + 'ipv6_ra_mode', + 'name', + 'network_id', + 'project_id', + 'segment_id', + 'service_types', + 'subnetpool_id', + ) + + data = ( + subnet_v2._format_allocation_pools(_subnet.allocation_pools), + _subnet.cidr, + utils.format_list(_subnet.dns_nameservers), + _subnet.enable_dhcp, + _subnet.gateway_ip, + subnet_v2._format_host_routes(_subnet.host_routes), + _subnet.id, + _subnet.ip_version, + _subnet.ipv6_address_mode, + _subnet.ipv6_ra_mode, + _subnet.name, + _subnet.network_id, + _subnet.project_id, + _subnet.segment_id, + utils.format_list(_subnet.service_types), + _subnet.subnetpool_id, + ) + + data_subnet_pool = ( + subnet_v2._format_allocation_pools(_subnet_from_pool.allocation_pools), + _subnet_from_pool.cidr, + utils.format_list(_subnet_from_pool.dns_nameservers), + _subnet_from_pool.enable_dhcp, + _subnet_from_pool.gateway_ip, + subnet_v2._format_host_routes(_subnet_from_pool.host_routes), + _subnet_from_pool.id, + _subnet_from_pool.ip_version, + _subnet_from_pool.ipv6_address_mode, + _subnet_from_pool.ipv6_ra_mode, + _subnet_from_pool.name, + _subnet_from_pool.network_id, + _subnet_from_pool.project_id, + _subnet_from_pool.segment_id, + utils.format_list(_subnet_from_pool.service_types), + _subnet_from_pool.subnetpool_id, + ) + + data_ipv6 = ( + subnet_v2._format_allocation_pools(_subnet_ipv6.allocation_pools), + _subnet_ipv6.cidr, + utils.format_list(_subnet_ipv6.dns_nameservers), + _subnet_ipv6.enable_dhcp, + _subnet_ipv6.gateway_ip, + subnet_v2._format_host_routes(_subnet_ipv6.host_routes), + _subnet_ipv6.id, + _subnet_ipv6.ip_version, + _subnet_ipv6.ipv6_address_mode, + _subnet_ipv6.ipv6_ra_mode, + _subnet_ipv6.name, + _subnet_ipv6.network_id, + _subnet_ipv6.project_id, + _subnet_ipv6.segment_id, + utils.format_list(_subnet_ipv6.service_types), + _subnet_ipv6.subnetpool_id, + ) + + def setUp(self): + super(TestCreateSubnet, self).setUp() + + # Get the command object to test + self.cmd = subnet_v2.CreateSubnet(self.app, self.namespace) + + self.projects_mock.get.return_value = self.project + self.domains_mock.get.return_value = self.domain + + # Mock SDK calls for all tests. + self.network.find_network = mock.Mock(return_value=self._network) + self.network.find_segment = mock.Mock( + return_value=self._network_segment + ) + self.network.find_subnet_pool = mock.Mock( + return_value=self._subnet_pool + ) + + def test_create_no_options(self): + arglist = [] + verifylist = [] + + # Testing that a call without the required argument will fail and + # throw a "ParserExecption" + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_create_default_options(self): + # Mock SDK calls for this test. + self.network.create_subnet = mock.Mock(return_value=self._subnet) + self._network.id = self._subnet.network_id + + arglist = [ + "--subnet-range", self._subnet.cidr, + "--network", self._subnet.network_id, + self._subnet.name, + ] + verifylist = [ + ('name', self._subnet.name), + ('subnet_range', self._subnet.cidr), + ('network', self._subnet.network_id), + ('ip_version', self._subnet.ip_version), + ('gateway', 'auto'), + + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_subnet.assert_called_once_with(**{ + 'cidr': self._subnet.cidr, + 'ip_version': self._subnet.ip_version, + 'name': self._subnet.name, + 'network_id': self._subnet.network_id, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_create_from_subnet_pool_options(self): + # Mock SDK calls for this test. + self.network.create_subnet = \ + mock.Mock(return_value=self._subnet_from_pool) + self._network.id = self._subnet_from_pool.network_id + + arglist = [ + self._subnet_from_pool.name, + "--subnet-pool", self._subnet_from_pool.subnetpool_id, + "--prefix-length", '24', + "--network", self._subnet_from_pool.network_id, + "--ip-version", str(self._subnet_from_pool.ip_version), + "--gateway", self._subnet_from_pool.gateway_ip, + "--dhcp", + ] + + for dns_addr in self._subnet_from_pool.dns_nameservers: + arglist.append('--dns-nameserver') + arglist.append(dns_addr) + + for host_route in self._subnet_from_pool.host_routes: + arglist.append('--host-route') + value = 'gateway=' + host_route.get('nexthop', '') + \ + ',destination=' + host_route.get('destination', '') + arglist.append(value) + + for service_type in self._subnet_from_pool.service_types: + arglist.append('--service-type') + arglist.append(service_type) + + verifylist = [ + ('name', self._subnet_from_pool.name), + ('prefix_length', '24'), + ('network', self._subnet_from_pool.network_id), + ('ip_version', self._subnet_from_pool.ip_version), + ('gateway', self._subnet_from_pool.gateway_ip), + ('dns_nameservers', self._subnet_from_pool.dns_nameservers), + ('dhcp', self._subnet_from_pool.enable_dhcp), + ('host_routes', subnet_v2.convert_entries_to_gateway( + self._subnet_from_pool.host_routes)), + ('subnet_pool', self._subnet_from_pool.subnetpool_id), + ('service_types', self._subnet_from_pool.service_types), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_subnet.assert_called_once_with(**{ + 'dns_nameservers': self._subnet_from_pool.dns_nameservers, + 'enable_dhcp': self._subnet_from_pool.enable_dhcp, + 'gateway_ip': self._subnet_from_pool.gateway_ip, + 'host_routes': self._subnet_from_pool.host_routes, + 'ip_version': self._subnet_from_pool.ip_version, + 'name': self._subnet_from_pool.name, + 'network_id': self._subnet_from_pool.network_id, + 'prefixlen': '24', + 'subnetpool_id': self._subnet_from_pool.subnetpool_id, + 'service_types': self._subnet_from_pool.service_types, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data_subnet_pool, data) + + def test_create_options_subnet_range_ipv6(self): + # Mock SDK calls for this test. + self.network.create_subnet = mock.Mock(return_value=self._subnet_ipv6) + self._network.id = self._subnet_ipv6.network_id + + arglist = [ + self._subnet_ipv6.name, + "--subnet-range", self._subnet_ipv6.cidr, + "--network", self._subnet_ipv6.network_id, + "--ip-version", str(self._subnet_ipv6.ip_version), + "--ipv6-ra-mode", self._subnet_ipv6.ipv6_ra_mode, + "--ipv6-address-mode", self._subnet_ipv6.ipv6_address_mode, + "--gateway", self._subnet_ipv6.gateway_ip, + "--dhcp", + ] + + for dns_addr in self._subnet_ipv6.dns_nameservers: + arglist.append('--dns-nameserver') + arglist.append(dns_addr) + + for host_route in self._subnet_ipv6.host_routes: + arglist.append('--host-route') + value = 'gateway=' + host_route.get('nexthop', '') + \ + ',destination=' + host_route.get('destination', '') + arglist.append(value) + + for pool in self._subnet_ipv6.allocation_pools: + arglist.append('--allocation-pool') + value = 'start=' + pool.get('start', '') + \ + ',end=' + pool.get('end', '') + arglist.append(value) + + for service_type in self._subnet_ipv6.service_types: + arglist.append('--service-type') + arglist.append(service_type) + + verifylist = [ + ('name', self._subnet_ipv6.name), + ('subnet_range', self._subnet_ipv6.cidr), + ('network', self._subnet_ipv6.network_id), + ('ip_version', self._subnet_ipv6.ip_version), + ('ipv6_ra_mode', self._subnet_ipv6.ipv6_ra_mode), + ('ipv6_address_mode', self._subnet_ipv6.ipv6_address_mode), + ('gateway', self._subnet_ipv6.gateway_ip), + ('dns_nameservers', self._subnet_ipv6.dns_nameservers), + ('dhcp', self._subnet_ipv6.enable_dhcp), + ('host_routes', subnet_v2.convert_entries_to_gateway( + self._subnet_ipv6.host_routes)), + ('allocation_pools', self._subnet_ipv6.allocation_pools), + ('service_types', self._subnet_ipv6.service_types), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_subnet.assert_called_once_with(**{ + 'cidr': self._subnet_ipv6.cidr, + 'dns_nameservers': self._subnet_ipv6.dns_nameservers, + 'enable_dhcp': self._subnet_ipv6.enable_dhcp, + 'gateway_ip': self._subnet_ipv6.gateway_ip, + 'host_routes': self._subnet_ipv6.host_routes, + 'ip_version': self._subnet_ipv6.ip_version, + 'ipv6_address_mode': self._subnet_ipv6.ipv6_address_mode, + 'ipv6_ra_mode': self._subnet_ipv6.ipv6_ra_mode, + 'name': self._subnet_ipv6.name, + 'network_id': self._subnet_ipv6.network_id, + 'allocation_pools': self._subnet_ipv6.allocation_pools, + 'service_types': self._subnet_ipv6.service_types, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data_ipv6, data) + + def test_create_no_beta_command_options(self): + arglist = [ + "--subnet-range", self._subnet.cidr, + "--network-segment", self._network_segment.id, + "--network", self._subnet.network_id, + self._subnet.name, + ] + verifylist = [ + ('name', self._subnet.name), + ('subnet_range', self._subnet.cidr), + ('network-segment', self._network_segment.id), + ('network', self._subnet.network_id), + ] + self.app.options.os_beta_command = False + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_create_with_network_segment(self): + # Mock SDK calls for this test. + self.network.create_subnet = mock.Mock(return_value=self._subnet) + self._network.id = self._subnet.network_id + + arglist = [ + "--subnet-range", self._subnet.cidr, + "--network-segment", self._network_segment.id, + "--network", self._subnet.network_id, + self._subnet.name, + ] + verifylist = [ + ('name', self._subnet.name), + ('subnet_range', self._subnet.cidr), + ('network_segment', self._network_segment.id), + ('network', self._subnet.network_id), + ('ip_version', self._subnet.ip_version), + ('gateway', 'auto'), + + ] + + self.app.options.os_beta_command = True + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_subnet.assert_called_once_with(**{ + 'cidr': self._subnet.cidr, + 'ip_version': self._subnet.ip_version, + 'name': self._subnet.name, + 'network_id': self._subnet.network_id, + 'segment_id': self._network_segment.id, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestDeleteSubnet(TestSubnet): + + # The subnets to delete. + _subnets = network_fakes.FakeSubnet.create_subnets(count=2) + + def setUp(self): + super(TestDeleteSubnet, self).setUp() + + self.network.delete_subnet = mock.Mock(return_value=None) + + self.network.find_subnet = ( + network_fakes.FakeSubnet.get_subnets(self._subnets)) + + # Get the command object to test + self.cmd = subnet_v2.DeleteSubnet(self.app, self.namespace) + + def test_subnet_delete(self): + arglist = [ + self._subnets[0].name, + ] + verifylist = [ + ('subnet', [self._subnets[0].name]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.network.delete_subnet.assert_called_once_with(self._subnets[0]) + self.assertIsNone(result) + + def test_multi_subnets_delete(self): + arglist = [] + verifylist = [] + + for s in self._subnets: + arglist.append(s.name) + verifylist = [ + ('subnet', arglist), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + calls = [] + for s in self._subnets: + calls.append(call(s)) + self.network.delete_subnet.assert_has_calls(calls) + self.assertIsNone(result) + + def test_multi_subnets_delete_with_exception(self): + arglist = [ + self._subnets[0].name, + 'unexist_subnet', + ] + verifylist = [ + ('subnet', + [self._subnets[0].name, 'unexist_subnet']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + find_mock_result = [self._subnets[0], exceptions.CommandError] + self.network.find_subnet = ( + mock.MagicMock(side_effect=find_mock_result) + ) + + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 subnets failed to delete.', str(e)) + + self.network.find_subnet.assert_any_call( + self._subnets[0].name, ignore_missing=False) + self.network.find_subnet.assert_any_call( + 'unexist_subnet', ignore_missing=False) + self.network.delete_subnet.assert_called_once_with( + self._subnets[0] + ) + + +class TestListSubnet(TestSubnet): + # The subnets going to be listed up. + _subnet = network_fakes.FakeSubnet.create_subnets(count=3) + + columns = ( + 'ID', + 'Name', + 'Network', + 'Subnet', + ) + columns_long = columns + ( + 'Project', + 'DHCP', + 'Name Servers', + 'Allocation Pools', + 'Host Routes', + 'IP Version', + 'Gateway', + 'Service Types', + ) + + data = [] + for subnet in _subnet: + data.append(( + subnet.id, + subnet.name, + subnet.network_id, + subnet.cidr, + )) + + data_long = [] + for subnet in _subnet: + data_long.append(( + subnet.id, + subnet.name, + subnet.network_id, + subnet.cidr, + subnet.tenant_id, + subnet.enable_dhcp, + utils.format_list(subnet.dns_nameservers), + subnet_v2._format_allocation_pools(subnet.allocation_pools), + utils.format_list(subnet.host_routes), + subnet.ip_version, + subnet.gateway_ip, + utils.format_list(subnet.service_types), + )) + + def setUp(self): + super(TestListSubnet, self).setUp() + + # Get the command object to test + self.cmd = subnet_v2.ListSubnet(self.app, self.namespace) + + self.network.subnets = mock.Mock(return_value=self._subnet) + + def test_subnet_list_no_options(self): + arglist = [] + verifylist = [ + ('long', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.subnets.assert_called_once_with() + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_subnet_list_long(self): + arglist = [ + '--long', + ] + verifylist = [ + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.subnets.assert_called_once_with() + self.assertEqual(self.columns_long, columns) + self.assertEqual(self.data_long, list(data)) + + def test_subnet_list_ip_version(self): + arglist = [ + '--ip-version', str(4), + ] + verifylist = [ + ('ip_version', 4), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + filters = {'ip_version': 4} + + self.network.subnets.assert_called_once_with(**filters) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_subnet_list_dhcp(self): + arglist = [ + '--dhcp', + ] + verifylist = [ + ('dhcp', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + filters = {'enable_dhcp': True} + + self.network.subnets.assert_called_once_with(**filters) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_subnet_list_no_dhcp(self): + arglist = [ + '--no-dhcp', + ] + verifylist = [ + ('no_dhcp', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + filters = {'enable_dhcp': False} + + self.network.subnets.assert_called_once_with(**filters) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_subnet_list_service_type(self): + arglist = [ + '--service-type', 'network:router_gateway', + ] + verifylist = [ + ('service_types', ['network:router_gateway']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + filters = {'service_types': ['network:router_gateway']} + + self.network.subnets.assert_called_once_with(**filters) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_subnet_list_service_type_multiple(self): + arglist = [ + '--service-type', 'network:router_gateway', + '--service-type', 'network:floatingip_agent_gateway', + ] + verifylist = [ + ('service_types', ['network:router_gateway', + 'network:floatingip_agent_gateway']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + filters = {'service_types': ['network:router_gateway', + 'network:floatingip_agent_gateway']} + + self.network.subnets.assert_called_once_with(**filters) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + +class TestSetSubnet(TestSubnet): + + _subnet = network_fakes.FakeSubnet.create_one_subnet() + + def setUp(self): + super(TestSetSubnet, self).setUp() + self.network.update_subnet = mock.Mock(return_value=None) + self.network.find_subnet = mock.Mock(return_value=self._subnet) + self.cmd = subnet_v2.SetSubnet(self.app, self.namespace) + + def test_set_this(self): + arglist = [ + "--name", "new_subnet", + "--dhcp", + "--gateway", self._subnet.gateway_ip, + self._subnet.name, + ] + verifylist = [ + ('name', "new_subnet"), + ('dhcp', True), + ('gateway', self._subnet.gateway_ip), + ('subnet', self._subnet.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + attrs = { + 'enable_dhcp': True, + 'gateway_ip': self._subnet.gateway_ip, + 'name': "new_subnet", + } + self.network.update_subnet.assert_called_with(self._subnet, **attrs) + self.assertIsNone(result) + + def test_set_that(self): + arglist = [ + "--name", "new_subnet", + "--no-dhcp", + "--gateway", "none", + self._subnet.name, + ] + verifylist = [ + ('name', "new_subnet"), + ('no_dhcp', True), + ('gateway', "none"), + ('subnet', self._subnet.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + attrs = { + 'enable_dhcp': False, + 'gateway_ip': None, + 'name': "new_subnet", + } + self.network.update_subnet.assert_called_with(self._subnet, **attrs) + self.assertIsNone(result) + + def test_set_nothing(self): + arglist = [self._subnet.name, ] + verifylist = [('subnet', self._subnet.name)] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = {} + self.network.update_subnet.assert_called_with(self._subnet, **attrs) + self.assertIsNone(result) + + def test_append_options(self): + _testsubnet = network_fakes.FakeSubnet.create_one_subnet( + {'dns_nameservers': ["10.0.0.1"], + 'service_types': ["network:router_gateway"]}) + self.network.find_subnet = mock.Mock(return_value=_testsubnet) + arglist = [ + '--dns-nameserver', '10.0.0.2', + '--service-type', 'network:floatingip_agent_gateway', + _testsubnet.name, + ] + verifylist = [ + ('dns_nameservers', ['10.0.0.2']), + ('service_types', ['network:floatingip_agent_gateway']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + attrs = { + 'dns_nameservers': ['10.0.0.2', '10.0.0.1'], + 'service_types': ['network:floatingip_agent_gateway', + 'network:router_gateway'], + } + self.network.update_subnet.assert_called_once_with( + _testsubnet, **attrs) + self.assertIsNone(result) + + +class TestShowSubnet(TestSubnet): + # The subnets to be shown + _subnet = network_fakes.FakeSubnet.create_one_subnet() + + columns = ( + 'allocation_pools', + 'cidr', + 'dns_nameservers', + 'enable_dhcp', + 'gateway_ip', + 'host_routes', + 'id', + 'ip_version', + 'ipv6_address_mode', + 'ipv6_ra_mode', + 'name', + 'network_id', + 'project_id', + 'segment_id', + 'service_types', + 'subnetpool_id', + ) + + data = ( + subnet_v2._format_allocation_pools(_subnet.allocation_pools), + _subnet.cidr, + utils.format_list(_subnet.dns_nameservers), + _subnet.enable_dhcp, + _subnet.gateway_ip, + utils.format_list(_subnet.host_routes), + _subnet.id, + _subnet.ip_version, + _subnet.ipv6_address_mode, + _subnet.ipv6_ra_mode, + _subnet.name, + _subnet.network_id, + _subnet.tenant_id, + _subnet.segment_id, + utils.format_list(_subnet.service_types), + _subnet.subnetpool_id, + ) + + def setUp(self): + super(TestShowSubnet, self).setUp() + + # Get the command object to test + self.cmd = subnet_v2.ShowSubnet(self.app, self.namespace) + + self.network.find_subnet = mock.Mock(return_value=self._subnet) + + def test_show_no_options(self): + arglist = [] + verifylist = [] + + # Testing that a call without the required argument will fail and + # throw a "ParserExecption" + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_show_all_options(self): + arglist = [ + self._subnet.name, + ] + verifylist = [ + ('subnet', self._subnet.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.network.find_subnet.assert_called_once_with( + self._subnet.name, ignore_missing=False) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestUnsetSubnet(TestSubnet): + + def setUp(self): + super(TestUnsetSubnet, self).setUp() + self._testsubnet = network_fakes.FakeSubnet.create_one_subnet( + {'dns_nameservers': ['8.8.8.8', + '8.8.8.4'], + 'host_routes': [{'destination': '10.20.20.0/24', + 'nexthop': '10.20.20.1'}, + {'destination': '10.30.30.30/24', + 'nexthop': '10.30.30.1'}], + 'allocation_pools': [{'start': '8.8.8.100', + 'end': '8.8.8.150'}, + {'start': '8.8.8.160', + 'end': '8.8.8.170'}], + 'service_types': ['network:router_gateway', + 'network:floatingip_agent_gateway'], }) + self.network.find_subnet = mock.Mock(return_value=self._testsubnet) + self.network.update_subnet = mock.Mock(return_value=None) + # Get the command object to test + self.cmd = subnet_v2.UnsetSubnet(self.app, self.namespace) + + def test_unset_subnet_params(self): + arglist = [ + '--dns-nameserver', '8.8.8.8', + '--host-route', 'destination=10.30.30.30/24,gateway=10.30.30.1', + '--allocation-pool', 'start=8.8.8.100,end=8.8.8.150', + '--service-type', 'network:router_gateway', + self._testsubnet.name, + ] + verifylist = [ + ('dns_nameservers', ['8.8.8.8']), + ('host_routes', [{ + "destination": "10.30.30.30/24", "gateway": "10.30.30.1"}]), + ('allocation_pools', [{ + 'start': '8.8.8.100', 'end': '8.8.8.150'}]), + ('service_types', ['network:router_gateway']), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = { + 'dns_nameservers': ['8.8.8.4'], + 'host_routes': [{ + "destination": "10.20.20.0/24", "nexthop": "10.20.20.1"}], + 'allocation_pools': [{'start': '8.8.8.160', 'end': '8.8.8.170'}], + 'service_types': ['network:floatingip_agent_gateway'], + } + self.network.update_subnet.assert_called_once_with( + self._testsubnet, **attrs) + self.assertIsNone(result) + + def test_unset_subnet_wrong_host_routes(self): + arglist = [ + '--dns-nameserver', '8.8.8.8', + '--host-route', 'destination=10.30.30.30/24,gateway=10.30.30.2', + '--allocation-pool', 'start=8.8.8.100,end=8.8.8.150', + self._testsubnet.name, + ] + verifylist = [ + ('dns_nameservers', ['8.8.8.8']), + ('host_routes', [{ + "destination": "10.30.30.30/24", "gateway": "10.30.30.2"}]), + ('allocation_pools', [{ + 'start': '8.8.8.100', 'end': '8.8.8.150'}]), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises(exceptions.CommandError, + self.cmd.take_action, parsed_args) + + def test_unset_subnet_wrong_allocation_pool(self): + arglist = [ + '--dns-nameserver', '8.8.8.8', + '--host-route', 'destination=10.30.30.30/24,gateway=10.30.30.1', + '--allocation-pool', 'start=8.8.8.100,end=8.8.8.156', + self._testsubnet.name, + ] + verifylist = [ + ('dns_nameservers', ['8.8.8.8']), + ('host_routes', [{ + "destination": "10.30.30.30/24", "gateway": "10.30.30.1"}]), + ('allocation_pools', [{ + 'start': '8.8.8.100', 'end': '8.8.8.156'}]), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises(exceptions.CommandError, + self.cmd.take_action, parsed_args) + + def test_unset_subnet_wrong_dns_nameservers(self): + arglist = [ + '--dns-nameserver', '8.8.8.1', + '--host-route', 'destination=10.30.30.30/24,gateway=10.30.30.1', + '--allocation-pool', 'start=8.8.8.100,end=8.8.8.150', + self._testsubnet.name, + ] + verifylist = [ + ('dns_nameservers', ['8.8.8.1']), + ('host_routes', [{ + "destination": "10.30.30.30/24", "gateway": "10.30.30.1"}]), + ('allocation_pools', [{ + 'start': '8.8.8.100', 'end': '8.8.8.150'}]), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises(exceptions.CommandError, + self.cmd.take_action, parsed_args) + + def test_unset_subnet_wrong_service_type(self): + arglist = [ + '--dns-nameserver', '8.8.8.8', + '--host-route', 'destination=10.30.30.30/24,gateway=10.30.30.1', + '--allocation-pool', 'start=8.8.8.100,end=8.8.8.150', + '--service-type', 'network:dhcp', + self._testsubnet.name, + ] + verifylist = [ + ('dns_nameservers', ['8.8.8.8']), + ('host_routes', [{ + "destination": "10.30.30.30/24", "gateway": "10.30.30.1"}]), + ('allocation_pools', [{ + 'start': '8.8.8.100', 'end': '8.8.8.150'}]), + ('service_types', ['network:dhcp']), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises(exceptions.CommandError, + self.cmd.take_action, parsed_args) diff --git a/openstackclient/tests/unit/network/v2/test_subnet_pool.py b/openstackclient/tests/unit/network/v2/test_subnet_pool.py new file mode 100644 index 00000000..0d9494c0 --- /dev/null +++ b/openstackclient/tests/unit/network/v2/test_subnet_pool.py @@ -0,0 +1,722 @@ +# 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 argparse +import mock +from mock import call + +from osc_lib import exceptions +from osc_lib import utils + +from openstackclient.network.v2 import subnet_pool +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3 +from openstackclient.tests.unit.network.v2 import fakes as network_fakes +from openstackclient.tests.unit import utils as tests_utils + + +class TestSubnetPool(network_fakes.TestNetworkV2): + + def setUp(self): + super(TestSubnetPool, self).setUp() + + # Get a shortcut to the network client + self.network = self.app.client_manager.network + # Get a shortcut to the ProjectManager Mock + self.projects_mock = self.app.client_manager.identity.projects + # Get a shortcut to the DomainManager Mock + self.domains_mock = self.app.client_manager.identity.domains + + +class TestCreateSubnetPool(TestSubnetPool): + + project = identity_fakes_v3.FakeProject.create_one_project() + domain = identity_fakes_v3.FakeDomain.create_one_domain() + # The new subnet pool to create. + _subnet_pool = network_fakes.FakeSubnetPool.create_one_subnet_pool() + + _address_scope = network_fakes.FakeAddressScope.create_one_address_scope() + + columns = ( + 'address_scope_id', + 'default_prefixlen', + 'default_quota', + 'id', + 'ip_version', + 'is_default', + 'max_prefixlen', + 'min_prefixlen', + 'name', + 'prefixes', + 'project_id', + 'shared', + ) + data = ( + _subnet_pool.address_scope_id, + _subnet_pool.default_prefixlen, + _subnet_pool.default_quota, + _subnet_pool.id, + _subnet_pool.ip_version, + _subnet_pool.is_default, + _subnet_pool.max_prefixlen, + _subnet_pool.min_prefixlen, + _subnet_pool.name, + utils.format_list(_subnet_pool.prefixes), + _subnet_pool.project_id, + _subnet_pool.shared, + ) + + def setUp(self): + super(TestCreateSubnetPool, self).setUp() + + self.network.create_subnet_pool = mock.Mock( + return_value=self._subnet_pool) + + # Get the command object to test + self.cmd = subnet_pool.CreateSubnetPool(self.app, self.namespace) + + self.network.find_address_scope = mock.Mock( + return_value=self._address_scope) + + self.projects_mock.get.return_value = self.project + self.domains_mock.get.return_value = self.domain + + def test_create_no_options(self): + arglist = [] + verifylist = [] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_create_no_pool_prefix(self): + """Make sure --pool-prefix is a required argument""" + arglist = [ + self._subnet_pool.name, + ] + verifylist = [ + ('name', self._subnet_pool.name), + ] + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_create_default_options(self): + arglist = [ + '--pool-prefix', '10.0.10.0/24', + self._subnet_pool.name, + ] + verifylist = [ + ('prefixes', ['10.0.10.0/24']), + ('name', self._subnet_pool.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = (self.cmd.take_action(parsed_args)) + + self.network.create_subnet_pool.assert_called_once_with(**{ + 'prefixes': ['10.0.10.0/24'], + 'name': self._subnet_pool.name, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_create_prefixlen_options(self): + arglist = [ + '--default-prefix-length', self._subnet_pool.default_prefixlen, + '--max-prefix-length', self._subnet_pool.max_prefixlen, + '--min-prefix-length', self._subnet_pool.min_prefixlen, + '--pool-prefix', '10.0.10.0/24', + self._subnet_pool.name, + ] + verifylist = [ + ('default_prefix_length', + int(self._subnet_pool.default_prefixlen)), + ('max_prefix_length', int(self._subnet_pool.max_prefixlen)), + ('min_prefix_length', int(self._subnet_pool.min_prefixlen)), + ('name', self._subnet_pool.name), + ('prefixes', ['10.0.10.0/24']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = (self.cmd.take_action(parsed_args)) + + self.network.create_subnet_pool.assert_called_once_with(**{ + 'default_prefixlen': int(self._subnet_pool.default_prefixlen), + 'max_prefixlen': int(self._subnet_pool.max_prefixlen), + 'min_prefixlen': int(self._subnet_pool.min_prefixlen), + 'prefixes': ['10.0.10.0/24'], + 'name': self._subnet_pool.name, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_create_len_negative(self): + arglist = [ + self._subnet_pool.name, + '--min-prefix-length', '-16', + ] + verifylist = [ + ('subnet_pool', self._subnet_pool.name), + ('min_prefix_length', '-16'), + ] + + self.assertRaises(argparse.ArgumentTypeError, self.check_parser, + self.cmd, arglist, verifylist) + + def test_create_project_domain(self): + arglist = [ + '--pool-prefix', '10.0.10.0/24', + "--project", self.project.name, + "--project-domain", self.domain.name, + self._subnet_pool.name, + ] + verifylist = [ + ('prefixes', ['10.0.10.0/24']), + ('project', self.project.name), + ('project_domain', self.domain.name), + ('name', self._subnet_pool.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = (self.cmd.take_action(parsed_args)) + + self.network.create_subnet_pool.assert_called_once_with(**{ + 'prefixes': ['10.0.10.0/24'], + 'tenant_id': self.project.id, + 'name': self._subnet_pool.name, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_create_address_scope_option(self): + arglist = [ + '--pool-prefix', '10.0.10.0/24', + '--address-scope', self._address_scope.id, + self._subnet_pool.name, + ] + verifylist = [ + ('prefixes', ['10.0.10.0/24']), + ('address_scope', self._address_scope.id), + ('name', self._subnet_pool.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = (self.cmd.take_action(parsed_args)) + + self.network.create_subnet_pool.assert_called_once_with(**{ + 'prefixes': ['10.0.10.0/24'], + 'address_scope_id': self._address_scope.id, + 'name': self._subnet_pool.name, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_create_default_and_shared_options(self): + arglist = [ + '--pool-prefix', '10.0.10.0/24', + '--default', + '--share', + self._subnet_pool.name, + ] + verifylist = [ + ('prefixes', ['10.0.10.0/24']), + ('default', True), + ('share', True), + ('name', self._subnet_pool.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = (self.cmd.take_action(parsed_args)) + + self.network.create_subnet_pool.assert_called_once_with(**{ + 'is_default': True, + 'name': self._subnet_pool.name, + 'prefixes': ['10.0.10.0/24'], + 'shared': True, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestDeleteSubnetPool(TestSubnetPool): + + # The subnet pools to delete. + _subnet_pools = network_fakes.FakeSubnetPool.create_subnet_pools(count=2) + + def setUp(self): + super(TestDeleteSubnetPool, self).setUp() + + self.network.delete_subnet_pool = mock.Mock(return_value=None) + + self.network.find_subnet_pool = ( + network_fakes.FakeSubnetPool.get_subnet_pools(self._subnet_pools) + ) + + # Get the command object to test + self.cmd = subnet_pool.DeleteSubnetPool(self.app, self.namespace) + + def test_subnet_pool_delete(self): + arglist = [ + self._subnet_pools[0].name, + ] + verifylist = [ + ('subnet_pool', [self._subnet_pools[0].name]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.network.delete_subnet_pool.assert_called_once_with( + self._subnet_pools[0]) + self.assertIsNone(result) + + def test_multi_subnet_pools_delete(self): + arglist = [] + verifylist = [] + + for s in self._subnet_pools: + arglist.append(s.name) + verifylist = [ + ('subnet_pool', arglist), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + calls = [] + for s in self._subnet_pools: + calls.append(call(s)) + self.network.delete_subnet_pool.assert_has_calls(calls) + self.assertIsNone(result) + + def test_multi_subnet_pools_delete_with_exception(self): + arglist = [ + self._subnet_pools[0].name, + 'unexist_subnet_pool', + ] + verifylist = [ + ('subnet_pool', + [self._subnet_pools[0].name, 'unexist_subnet_pool']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + find_mock_result = [self._subnet_pools[0], exceptions.CommandError] + self.network.find_subnet_pool = ( + mock.MagicMock(side_effect=find_mock_result) + ) + + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 subnet pools failed to delete.', str(e)) + + self.network.find_subnet_pool.assert_any_call( + self._subnet_pools[0].name, ignore_missing=False) + self.network.find_subnet_pool.assert_any_call( + 'unexist_subnet_pool', ignore_missing=False) + self.network.delete_subnet_pool.assert_called_once_with( + self._subnet_pools[0] + ) + + +class TestListSubnetPool(TestSubnetPool): + # The subnet pools going to be listed up. + _subnet_pools = network_fakes.FakeSubnetPool.create_subnet_pools(count=3) + + columns = ( + 'ID', + 'Name', + 'Prefixes', + ) + columns_long = columns + ( + 'Default Prefix Length', + 'Address Scope', + 'Default Subnet Pool', + 'Shared', + ) + + data = [] + for pool in _subnet_pools: + data.append(( + pool.id, + pool.name, + utils.format_list(pool.prefixes), + )) + + data_long = [] + for pool in _subnet_pools: + data_long.append(( + pool.id, + pool.name, + utils.format_list(pool.prefixes), + pool.default_prefixlen, + pool.address_scope_id, + pool.is_default, + pool.shared, + )) + + def setUp(self): + super(TestListSubnetPool, self).setUp() + + # Get the command object to test + self.cmd = subnet_pool.ListSubnetPool(self.app, self.namespace) + + self.network.subnet_pools = mock.Mock(return_value=self._subnet_pools) + + def test_subnet_pool_list_no_option(self): + arglist = [] + verifylist = [ + ('long', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.subnet_pools.assert_called_once_with() + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_subnet_pool_list_long(self): + arglist = [ + '--long', + ] + verifylist = [ + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.subnet_pools.assert_called_once_with() + self.assertEqual(self.columns_long, columns) + self.assertEqual(self.data_long, list(data)) + + +class TestSetSubnetPool(TestSubnetPool): + + # The subnet_pool to set. + _subnet_pool = network_fakes.FakeSubnetPool.create_one_subnet_pool() + + _address_scope = network_fakes.FakeAddressScope.create_one_address_scope() + + def setUp(self): + super(TestSetSubnetPool, self).setUp() + + self.network.update_subnet_pool = mock.Mock(return_value=None) + + self.network.find_subnet_pool = mock.Mock( + return_value=self._subnet_pool) + + self.network.find_address_scope = mock.Mock( + return_value=self._address_scope) + + # Get the command object to test + self.cmd = subnet_pool.SetSubnetPool(self.app, self.namespace) + + def test_set_this(self): + arglist = [ + '--name', 'noob', + '--default-prefix-length', '8', + '--min-prefix-length', '8', + self._subnet_pool.name, + ] + verifylist = [ + ('name', 'noob'), + ('default_prefix_length', 8), + ('min_prefix_length', 8), + ('subnet_pool', self._subnet_pool.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + attrs = { + 'name': 'noob', + 'default_prefixlen': 8, + 'min_prefixlen': 8, + } + self.network.update_subnet_pool.assert_called_once_with( + self._subnet_pool, **attrs) + self.assertIsNone(result) + + def test_set_that(self): + arglist = [ + '--pool-prefix', '10.0.1.0/24', + '--pool-prefix', '10.0.2.0/24', + '--max-prefix-length', '16', + self._subnet_pool.name, + ] + verifylist = [ + ('prefixes', ['10.0.1.0/24', '10.0.2.0/24']), + ('max_prefix_length', 16), + ('subnet_pool', self._subnet_pool.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + prefixes = ['10.0.1.0/24', '10.0.2.0/24'] + prefixes.extend(self._subnet_pool.prefixes) + attrs = { + 'prefixes': prefixes, + 'max_prefixlen': 16, + } + self.network.update_subnet_pool.assert_called_once_with( + self._subnet_pool, **attrs) + self.assertIsNone(result) + + def test_set_nothing(self): + arglist = [self._subnet_pool.name, ] + verifylist = [('subnet_pool', self._subnet_pool.name), ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = {} + self.network.update_subnet_pool.assert_called_once_with( + self._subnet_pool, **attrs) + self.assertIsNone(result) + + def test_set_len_negative(self): + arglist = [ + '--max-prefix-length', '-16', + self._subnet_pool.name, + ] + verifylist = [ + ('max_prefix_length', '-16'), + ('subnet_pool', self._subnet_pool.name), + ] + + self.assertRaises(argparse.ArgumentTypeError, self.check_parser, + self.cmd, arglist, verifylist) + + def test_set_address_scope(self): + arglist = [ + '--address-scope', self._address_scope.id, + self._subnet_pool.name, + ] + verifylist = [ + ('address_scope', self._address_scope.id), + ('subnet_pool', self._subnet_pool.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + attrs = { + 'address_scope_id': self._address_scope.id, + } + self.network.update_subnet_pool.assert_called_once_with( + self._subnet_pool, **attrs) + self.assertIsNone(result) + + def test_set_no_address_scope(self): + arglist = [ + '--no-address-scope', + self._subnet_pool.name, + ] + verifylist = [ + ('no_address_scope', True), + ('subnet_pool', self._subnet_pool.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + attrs = { + 'address_scope_id': None, + } + self.network.update_subnet_pool.assert_called_once_with( + self._subnet_pool, **attrs) + self.assertIsNone(result) + + def test_set_no_address_scope_conflict(self): + arglist = [ + '--address-scope', self._address_scope.id, + '--no-address-scope', + self._subnet_pool.name, + ] + verifylist = [ + ('address_scope', self._address_scope.id), + ('no_address_scope', True), + ('subnet_pool', self._subnet_pool.name), + ] + + # Exclusive arguments will conflict here. + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_set_default(self): + arglist = [ + '--default', + self._subnet_pool.name, + ] + verifylist = [ + ('default', True), + ('subnet_pool', self._subnet_pool.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + attrs = { + 'is_default': True + } + self.network.update_subnet_pool.assert_called_once_with( + self._subnet_pool, **attrs) + self.assertIsNone(result) + + def test_set_no_default(self): + arglist = [ + '--no-default', + self._subnet_pool.name, + ] + verifylist = [ + ('no_default', True), + ('subnet_pool', self._subnet_pool.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + attrs = { + 'is_default': False, + } + self.network.update_subnet_pool.assert_called_once_with( + self._subnet_pool, **attrs) + self.assertIsNone(result) + + def test_set_no_default_conflict(self): + arglist = [ + '--default', + '--no-default', + self._subnet_pool.name, + ] + verifylist = [ + ('default', True), + ('no_default', True), + ('subnet_pool', self._subnet_pool.name), + ] + + # Exclusive arguments will conflict here. + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + +class TestShowSubnetPool(TestSubnetPool): + + # The subnet_pool to set. + _subnet_pool = network_fakes.FakeSubnetPool.create_one_subnet_pool() + + columns = ( + 'address_scope_id', + 'default_prefixlen', + 'default_quota', + 'id', + 'ip_version', + 'is_default', + 'max_prefixlen', + 'min_prefixlen', + 'name', + 'prefixes', + 'project_id', + 'shared', + ) + + data = ( + _subnet_pool.address_scope_id, + _subnet_pool.default_prefixlen, + _subnet_pool.default_quota, + _subnet_pool.id, + _subnet_pool.ip_version, + _subnet_pool.is_default, + _subnet_pool.max_prefixlen, + _subnet_pool.min_prefixlen, + _subnet_pool.name, + utils.format_list(_subnet_pool.prefixes), + _subnet_pool.tenant_id, + _subnet_pool.shared, + ) + + def setUp(self): + super(TestShowSubnetPool, self).setUp() + + self.network.find_subnet_pool = mock.Mock( + return_value=self._subnet_pool + ) + + # Get the command object to test + self.cmd = subnet_pool.ShowSubnetPool(self.app, self.namespace) + + def test_show_no_options(self): + arglist = [] + verifylist = [] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_show_all_options(self): + arglist = [ + self._subnet_pool.name, + ] + verifylist = [ + ('subnet_pool', self._subnet_pool.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.find_subnet_pool.assert_called_once_with( + self._subnet_pool.name, + ignore_missing=False + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestUnsetSubnetPool(TestSubnetPool): + + def setUp(self): + super(TestUnsetSubnetPool, self).setUp() + self._subnetpool = network_fakes.FakeSubnetPool.create_one_subnet_pool( + {'prefixes': ['10.0.10.0/24', '10.1.10.0/24', + '10.2.10.0/24'], }) + self.network.find_subnet_pool = mock.Mock( + return_value=self._subnetpool) + self.network.update_subnet_pool = mock.Mock(return_value=None) + # Get the command object to test + self.cmd = subnet_pool.UnsetSubnetPool(self.app, self.namespace) + + def test_unset_subnet_pool(self): + arglist = [ + '--pool-prefix', '10.0.10.0/24', + '--pool-prefix', '10.1.10.0/24', + self._subnetpool.name, + ] + verifylist = [('prefixes', ['10.0.10.0/24', '10.1.10.0/24'])] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + attrs = {'prefixes': ['10.2.10.0/24']} + self.network.update_subnet_pool.assert_called_once_with( + self._subnetpool, **attrs) + self.assertIsNone(result) + + def test_unset_subnet_pool_prefix_not_existent(self): + arglist = [ + '--pool-prefix', '10.100.1.1/25', + self._subnetpool.name, + ] + verifylist = [('prefixes', ['10.100.1.1/25'])] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises(exceptions.CommandError, + self.cmd.take_action, + parsed_args) diff --git a/openstackclient/tests/unit/object/__init__.py b/openstackclient/tests/unit/object/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/openstackclient/tests/unit/object/__init__.py diff --git a/openstackclient/tests/unit/object/v1/__init__.py b/openstackclient/tests/unit/object/v1/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/openstackclient/tests/unit/object/v1/__init__.py diff --git a/openstackclient/tests/unit/object/v1/fakes.py b/openstackclient/tests/unit/object/v1/fakes.py new file mode 100644 index 00000000..0ff594bc --- /dev/null +++ b/openstackclient/tests/unit/object/v1/fakes.py @@ -0,0 +1,88 @@ +# Copyright 2013 Nebula 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. +# + +from keystoneauth1 import session + +from openstackclient.api import object_store_v1 as object_store +from openstackclient.tests.unit import utils + + +ACCOUNT_ID = 'tqbfjotld' +ENDPOINT = 'https://0.0.0.0:6482/v1/' + ACCOUNT_ID + +container_name = 'bit-bucket' +container_bytes = 1024 +container_count = 1 + +container_name_2 = 'archive' +container_name_3 = 'bit-blit' + +CONTAINER = { + 'name': container_name, + 'bytes': container_bytes, + 'count': container_count, +} + +CONTAINER_2 = { + 'name': container_name_2, + 'bytes': container_bytes * 2, + 'count': container_count * 2, +} + +CONTAINER_3 = { + 'name': container_name_3, + 'bytes': container_bytes * 3, + 'count': container_count * 3, +} + +object_name_1 = 'punch-card' +object_bytes_1 = 80 +object_hash_1 = '1234567890' +object_content_type_1 = 'text' +object_modified_1 = 'today' + +object_name_2 = 'floppy-disk' +object_bytes_2 = 1440000 +object_hash_2 = '0987654321' +object_content_type_2 = 'text' +object_modified_2 = 'today' + +OBJECT = { + 'name': object_name_1, + 'bytes': object_bytes_1, + 'hash': object_hash_1, + 'content_type': object_content_type_1, + 'last_modified': object_modified_1, +} + +OBJECT_2 = { + 'name': object_name_2, + 'bytes': object_bytes_2, + 'hash': object_hash_2, + 'content_type': object_content_type_2, + 'last_modified': object_modified_2, +} + + +class TestObjectv1(utils.TestCommand): + + def setUp(self): + super(TestObjectv1, self).setUp() + + self.app.client_manager.session = session.Session() + self.app.client_manager.object_store = object_store.APIv1( + session=self.app.client_manager.session, + endpoint=ENDPOINT, + ) diff --git a/openstackclient/tests/unit/object/v1/test_container.py b/openstackclient/tests/unit/object/v1/test_container.py new file mode 100644 index 00000000..37b8c705 --- /dev/null +++ b/openstackclient/tests/unit/object/v1/test_container.py @@ -0,0 +1,407 @@ +# Copyright 2013 OpenStack Foundation +# +# 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 copy +import mock + +from openstackclient.api import object_store_v1 as object_store +from openstackclient.object.v1 import container +from openstackclient.tests.unit.object.v1 import fakes as object_fakes + + +AUTH_TOKEN = "foobar" +AUTH_URL = "http://0.0.0.0" + + +class FakeClient(object): + + def __init__(self, endpoint=None, **kwargs): + self.endpoint = AUTH_URL + self.token = AUTH_TOKEN + + +class TestContainer(object_fakes.TestObjectv1): + + columns = ('Name',) + + def setUp(self): + super(TestContainer, self).setUp() + self.app.client_manager.object_store = object_store.APIv1( + session=mock.Mock(), + service_type="object-store", + ) + self.api = self.app.client_manager.object_store + + +@mock.patch('openstackclient.api.object_store_v1.APIv1.object_delete') +@mock.patch('openstackclient.api.object_store_v1.APIv1.object_list') +@mock.patch('openstackclient.api.object_store_v1.APIv1.container_delete') +class TestContainerDelete(TestContainer): + + def setUp(self): + super(TestContainerDelete, self).setUp() + + # Get the command object to test + self.cmd = container.DeleteContainer(self.app, None) + + def test_container_delete(self, c_mock, o_list_mock, o_delete_mock): + c_mock.return_value = None + + arglist = [ + object_fakes.container_name, + ] + verifylist = [ + ('containers', [object_fakes.container_name]), + ('recursive', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertIsNone(self.cmd.take_action(parsed_args)) + + kwargs = {} + c_mock.assert_called_with( + container=object_fakes.container_name, + **kwargs + ) + self.assertFalse(o_list_mock.called) + self.assertFalse(o_delete_mock.called) + + def test_recursive_delete(self, c_mock, o_list_mock, o_delete_mock): + c_mock.return_value = None + o_list_mock.return_value = [object_fakes.OBJECT] + o_delete_mock.return_value = None + + arglist = [ + '--recursive', + object_fakes.container_name, + ] + verifylist = [ + ('containers', [object_fakes.container_name]), + ('recursive', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertIsNone(self.cmd.take_action(parsed_args)) + + kwargs = {} + c_mock.assert_called_with( + container=object_fakes.container_name, + **kwargs + ) + o_list_mock.assert_called_with(container=object_fakes.container_name) + o_delete_mock.assert_called_with( + container=object_fakes.container_name, + object=object_fakes.OBJECT['name'], + ) + + def test_r_delete(self, c_mock, o_list_mock, o_delete_mock): + c_mock.return_value = None + o_list_mock.return_value = [object_fakes.OBJECT] + o_delete_mock.return_value = None + + arglist = [ + '-r', + object_fakes.container_name, + ] + verifylist = [ + ('containers', [object_fakes.container_name]), + ('recursive', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertIsNone(self.cmd.take_action(parsed_args)) + + kwargs = {} + c_mock.assert_called_with( + container=object_fakes.container_name, + **kwargs + ) + o_list_mock.assert_called_with(container=object_fakes.container_name) + o_delete_mock.assert_called_with( + container=object_fakes.container_name, + object=object_fakes.OBJECT['name'], + ) + + +@mock.patch( + 'openstackclient.api.object_store_v1.APIv1.container_list' +) +class TestContainerList(TestContainer): + + def setUp(self): + super(TestContainerList, self).setUp() + + # Get the command object to test + self.cmd = container.ListContainer(self.app, None) + + def test_object_list_containers_no_options(self, c_mock): + c_mock.return_value = [ + copy.deepcopy(object_fakes.CONTAINER), + copy.deepcopy(object_fakes.CONTAINER_3), + copy.deepcopy(object_fakes.CONTAINER_2), + ] + + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + } + c_mock.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + datalist = ( + (object_fakes.container_name, ), + (object_fakes.container_name_3, ), + (object_fakes.container_name_2, ), + ) + self.assertEqual(datalist, tuple(data)) + + def test_object_list_containers_prefix(self, c_mock): + c_mock.return_value = [ + copy.deepcopy(object_fakes.CONTAINER), + copy.deepcopy(object_fakes.CONTAINER_3), + ] + + arglist = [ + '--prefix', 'bit', + ] + verifylist = [ + ('prefix', 'bit'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'prefix': 'bit', + } + c_mock.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + datalist = ( + (object_fakes.container_name, ), + (object_fakes.container_name_3, ), + ) + self.assertEqual(datalist, tuple(data)) + + def test_object_list_containers_marker(self, c_mock): + c_mock.return_value = [ + copy.deepcopy(object_fakes.CONTAINER), + copy.deepcopy(object_fakes.CONTAINER_3), + ] + + arglist = [ + '--marker', object_fakes.container_name, + '--end-marker', object_fakes.container_name_3, + ] + verifylist = [ + ('marker', object_fakes.container_name), + ('end_marker', object_fakes.container_name_3), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'marker': object_fakes.container_name, + 'end_marker': object_fakes.container_name_3, + } + c_mock.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + datalist = ( + (object_fakes.container_name, ), + (object_fakes.container_name_3, ), + ) + self.assertEqual(datalist, tuple(data)) + + def test_object_list_containers_limit(self, c_mock): + c_mock.return_value = [ + copy.deepcopy(object_fakes.CONTAINER), + copy.deepcopy(object_fakes.CONTAINER_3), + ] + + arglist = [ + '--limit', '2', + ] + verifylist = [ + ('limit', 2), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'limit': 2, + } + c_mock.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + datalist = ( + (object_fakes.container_name, ), + (object_fakes.container_name_3, ), + ) + self.assertEqual(datalist, tuple(data)) + + def test_object_list_containers_long(self, c_mock): + c_mock.return_value = [ + copy.deepcopy(object_fakes.CONTAINER), + copy.deepcopy(object_fakes.CONTAINER_3), + ] + + arglist = [ + '--long', + ] + verifylist = [ + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + } + c_mock.assert_called_with( + **kwargs + ) + + collist = ('Name', 'Bytes', 'Count') + self.assertEqual(collist, columns) + datalist = ( + ( + object_fakes.container_name, + object_fakes.container_bytes, + object_fakes.container_count, + ), + ( + object_fakes.container_name_3, + object_fakes.container_bytes * 3, + object_fakes.container_count * 3, + ), + ) + self.assertEqual(datalist, tuple(data)) + + def test_object_list_containers_all(self, c_mock): + c_mock.return_value = [ + copy.deepcopy(object_fakes.CONTAINER), + copy.deepcopy(object_fakes.CONTAINER_2), + copy.deepcopy(object_fakes.CONTAINER_3), + ] + + arglist = [ + '--all', + ] + verifylist = [ + ('all', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'full_listing': True, + } + c_mock.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + datalist = ( + (object_fakes.container_name, ), + (object_fakes.container_name_2, ), + (object_fakes.container_name_3, ), + ) + self.assertEqual(datalist, tuple(data)) + + +@mock.patch( + 'openstackclient.api.object_store_v1.APIv1.container_show' +) +class TestContainerShow(TestContainer): + + def setUp(self): + super(TestContainerShow, self).setUp() + + # Get the command object to test + self.cmd = container.ShowContainer(self.app, None) + + def test_container_show(self, c_mock): + c_mock.return_value = copy.deepcopy(object_fakes.CONTAINER) + + arglist = [ + object_fakes.container_name, + ] + verifylist = [ + ('container', object_fakes.container_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + } + # lib.container.show_container(api, url, container) + c_mock.assert_called_with( + container=object_fakes.container_name, + **kwargs + ) + + collist = ('bytes', 'count', 'name') + self.assertEqual(collist, columns) + datalist = ( + object_fakes.container_bytes, + object_fakes.container_count, + object_fakes.container_name, + ) + self.assertEqual(datalist, data) diff --git a/openstackclient/tests/unit/object/v1/test_container_all.py b/openstackclient/tests/unit/object/v1/test_container_all.py new file mode 100644 index 00000000..58c90e36 --- /dev/null +++ b/openstackclient/tests/unit/object/v1/test_container_all.py @@ -0,0 +1,345 @@ +# 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 copy + +from requests_mock.contrib import fixture + +from openstackclient.object.v1 import container as container_cmds +from openstackclient.tests.unit.object.v1 import fakes as object_fakes + + +class TestContainerAll(object_fakes.TestObjectv1): + + def setUp(self): + super(TestContainerAll, self).setUp() + + self.requests_mock = self.useFixture(fixture.Fixture()) + + +class TestContainerCreate(TestContainerAll): + + columns = ( + 'account', + 'container', + 'x-trans-id', + ) + + def setUp(self): + super(TestContainerCreate, self).setUp() + + # Get the command object to test + self.cmd = container_cmds.CreateContainer(self.app, None) + + def test_object_create_container_single(self): + self.requests_mock.register_uri( + 'PUT', + object_fakes.ENDPOINT + '/ernie', + headers={'x-trans-id': '314159'}, + status_code=200, + ) + + arglist = [ + 'ernie', + ] + verifylist = [( + 'containers', ['ernie'], + )] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + datalist = [( + object_fakes.ACCOUNT_ID, + 'ernie', + '314159', + )] + self.assertEqual(datalist, list(data)) + + def test_object_create_container_more(self): + self.requests_mock.register_uri( + 'PUT', + object_fakes.ENDPOINT + '/ernie', + headers={'x-trans-id': '314159'}, + status_code=200, + ) + self.requests_mock.register_uri( + 'PUT', + object_fakes.ENDPOINT + '/bert', + headers={'x-trans-id': '42'}, + status_code=200, + ) + + arglist = [ + 'ernie', + 'bert', + ] + verifylist = [( + 'containers', ['ernie', 'bert'], + )] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + datalist = [ + ( + object_fakes.ACCOUNT_ID, + 'ernie', + '314159', + ), + ( + object_fakes.ACCOUNT_ID, + 'bert', + '42', + ), + ] + self.assertEqual(datalist, list(data)) + + +class TestContainerDelete(TestContainerAll): + + def setUp(self): + super(TestContainerDelete, self).setUp() + + # Get the command object to test + self.cmd = container_cmds.DeleteContainer(self.app, None) + + def test_object_delete_container_single(self): + self.requests_mock.register_uri( + 'DELETE', + object_fakes.ENDPOINT + '/ernie', + status_code=200, + ) + + arglist = [ + 'ernie', + ] + verifylist = [( + 'containers', ['ernie'], + )] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # Command.take_action() returns None + ret = self.cmd.take_action(parsed_args) + self.assertIsNone(ret) + + def test_object_delete_container_more(self): + self.requests_mock.register_uri( + 'DELETE', + object_fakes.ENDPOINT + '/ernie', + status_code=200, + ) + self.requests_mock.register_uri( + 'DELETE', + object_fakes.ENDPOINT + '/bert', + status_code=200, + ) + + arglist = [ + 'ernie', + 'bert', + ] + verifylist = [( + 'containers', ['ernie', 'bert'], + )] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # Command.take_action() returns None + ret = self.cmd.take_action(parsed_args) + self.assertIsNone(ret) + + +class TestContainerList(TestContainerAll): + + columns = ('Name',) + + def setUp(self): + super(TestContainerList, self).setUp() + + # Get the command object to test + self.cmd = container_cmds.ListContainer(self.app, None) + + def test_object_list_containers_no_options(self): + return_body = [ + copy.deepcopy(object_fakes.CONTAINER), + copy.deepcopy(object_fakes.CONTAINER_3), + copy.deepcopy(object_fakes.CONTAINER_2), + ] + self.requests_mock.register_uri( + 'GET', + object_fakes.ENDPOINT + '?format=json', + json=return_body, + status_code=200, + ) + + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # Lister.take_action() returns two tuples + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + datalist = [ + (object_fakes.container_name, ), + (object_fakes.container_name_3, ), + (object_fakes.container_name_2, ), + ] + self.assertEqual(datalist, list(data)) + + def test_object_list_containers_prefix(self): + return_body = [ + copy.deepcopy(object_fakes.CONTAINER), + copy.deepcopy(object_fakes.CONTAINER_3), + ] + self.requests_mock.register_uri( + 'GET', + object_fakes.ENDPOINT + '?format=json&prefix=bit', + json=return_body, + status_code=200, + ) + + arglist = [ + '--prefix', 'bit', + ] + verifylist = [ + ('prefix', 'bit'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # Lister.take_action() returns two tuples + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + datalist = [ + (object_fakes.container_name, ), + (object_fakes.container_name_3, ), + ] + self.assertEqual(datalist, list(data)) + + +class TestContainerSave(TestContainerAll): + + def setUp(self): + super(TestContainerSave, self).setUp() + + # Get the command object to test + self.cmd = container_cmds.SaveContainer(self.app, None) + +# TODO(dtroyer): need to mock out object_lib.save_object() to test this +# def test_object_save_container(self): +# return_body = [ +# copy.deepcopy(object_fakes.OBJECT), +# copy.deepcopy(object_fakes.OBJECT_2), +# ] +# # Initial container list request +# self.requests_mock.register_uri( +# 'GET', +# object_fakes.ENDPOINT + '/oscar?format=json', +# json=return_body, +# status_code=200, +# ) +# # Individual object save requests +# self.requests_mock.register_uri( +# 'GET', +# object_fakes.ENDPOINT + '/oscar/' + object_fakes.object_name_1, +# json=object_fakes.OBJECT, +# status_code=200, +# ) +# self.requests_mock.register_uri( +# 'GET', +# object_fakes.ENDPOINT + '/oscar/' + object_fakes.object_name_2, +# json=object_fakes.OBJECT_2, +# status_code=200, +# ) +# +# arglist = [ +# 'oscar', +# ] +# verifylist = [( +# 'container', 'oscar', +# )] +# parsed_args = self.check_parser(self.cmd, arglist, verifylist) +# +# # Command.take_action() returns None +# ret = self.cmd.take_action(parsed_args) +# self.assertIsNone(ret) + + +class TestContainerShow(TestContainerAll): + + def setUp(self): + super(TestContainerShow, self).setUp() + + # Get the command object to test + self.cmd = container_cmds.ShowContainer(self.app, None) + + def test_object_show_container(self): + headers = { + 'x-container-object-count': '42', + 'x-container-bytes-used': '123', + 'x-container-read': 'qaz', + 'x-container-write': 'wsx', + 'x-container-sync-to': 'edc', + 'x-container-sync-key': 'rfv', + } + self.requests_mock.register_uri( + 'HEAD', + object_fakes.ENDPOINT + '/ernie', + headers=headers, + status_code=200, + ) + + arglist = [ + 'ernie', + ] + verifylist = [( + 'container', 'ernie', + )] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + collist = ( + 'account', + 'bytes_used', + 'container', + 'object_count', + 'read_acl', + 'sync_key', + 'sync_to', + 'write_acl', + ) + self.assertEqual(collist, columns) + datalist = [ + object_fakes.ACCOUNT_ID, + '123', + 'ernie', + '42', + 'qaz', + 'rfv', + 'edc', + 'wsx', + ] + self.assertEqual(datalist, list(data)) diff --git a/openstackclient/tests/unit/object/v1/test_object.py b/openstackclient/tests/unit/object/v1/test_object.py new file mode 100644 index 00000000..c0ac204d --- /dev/null +++ b/openstackclient/tests/unit/object/v1/test_object.py @@ -0,0 +1,382 @@ +# Copyright 2013 OpenStack Foundation +# +# 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 copy +import mock + +from openstackclient.api import object_store_v1 as object_store +from openstackclient.object.v1 import object as obj +from openstackclient.tests.unit.object.v1 import fakes as object_fakes + + +AUTH_TOKEN = "foobar" +AUTH_URL = "http://0.0.0.0" + + +class TestObject(object_fakes.TestObjectv1): + + def setUp(self): + super(TestObject, self).setUp() + self.app.client_manager.object_store = object_store.APIv1( + session=mock.Mock(), + service_type="object-store", + ) + self.api = self.app.client_manager.object_store + + +@mock.patch( + 'openstackclient.api.object_store_v1.APIv1.object_list' +) +class TestObjectList(TestObject): + + columns = ('Name',) + datalist = ( + ( + object_fakes.object_name_2, + ), + ) + + def setUp(self): + super(TestObjectList, self).setUp() + + # Get the command object to test + self.cmd = obj.ListObject(self.app, None) + + def test_object_list_objects_no_options(self, o_mock): + o_mock.return_value = [ + copy.deepcopy(object_fakes.OBJECT), + copy.deepcopy(object_fakes.OBJECT_2), + ] + + arglist = [ + object_fakes.container_name, + ] + verifylist = [ + ('container', object_fakes.container_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + o_mock.assert_called_with( + container=object_fakes.container_name, + ) + + self.assertEqual(self.columns, columns) + datalist = ( + (object_fakes.object_name_1, ), + (object_fakes.object_name_2, ), + ) + self.assertEqual(datalist, tuple(data)) + + def test_object_list_objects_prefix(self, o_mock): + o_mock.return_value = [ + copy.deepcopy(object_fakes.OBJECT_2), + ] + + arglist = [ + '--prefix', 'floppy', + object_fakes.container_name_2, + ] + verifylist = [ + ('prefix', 'floppy'), + ('container', object_fakes.container_name_2), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'prefix': 'floppy', + } + o_mock.assert_called_with( + container=object_fakes.container_name_2, + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_object_list_objects_delimiter(self, o_mock): + o_mock.return_value = [ + copy.deepcopy(object_fakes.OBJECT_2), + ] + + arglist = [ + '--delimiter', '=', + object_fakes.container_name_2, + ] + verifylist = [ + ('delimiter', '='), + ('container', object_fakes.container_name_2), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'delimiter': '=', + } + o_mock.assert_called_with( + container=object_fakes.container_name_2, + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_object_list_objects_marker(self, o_mock): + o_mock.return_value = [ + copy.deepcopy(object_fakes.OBJECT_2), + ] + + arglist = [ + '--marker', object_fakes.object_name_2, + object_fakes.container_name_2, + ] + verifylist = [ + ('marker', object_fakes.object_name_2), + ('container', object_fakes.container_name_2), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'marker': object_fakes.object_name_2, + } + o_mock.assert_called_with( + container=object_fakes.container_name_2, + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_object_list_objects_end_marker(self, o_mock): + o_mock.return_value = [ + copy.deepcopy(object_fakes.OBJECT_2), + ] + + arglist = [ + '--end-marker', object_fakes.object_name_2, + object_fakes.container_name_2, + ] + verifylist = [ + ('end_marker', object_fakes.object_name_2), + ('container', object_fakes.container_name_2), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'end_marker': object_fakes.object_name_2, + } + o_mock.assert_called_with( + container=object_fakes.container_name_2, + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_object_list_objects_limit(self, o_mock): + o_mock.return_value = [ + copy.deepcopy(object_fakes.OBJECT_2), + ] + + arglist = [ + '--limit', '2', + object_fakes.container_name_2, + ] + verifylist = [ + ('limit', 2), + ('container', object_fakes.container_name_2), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'limit': 2, + } + o_mock.assert_called_with( + container=object_fakes.container_name_2, + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_object_list_objects_long(self, o_mock): + o_mock.return_value = [ + copy.deepcopy(object_fakes.OBJECT), + copy.deepcopy(object_fakes.OBJECT_2), + ] + + arglist = [ + '--long', + object_fakes.container_name, + ] + verifylist = [ + ('long', True), + ('container', object_fakes.container_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + } + o_mock.assert_called_with( + container=object_fakes.container_name, + **kwargs + ) + + collist = ('Name', 'Bytes', 'Hash', 'Content Type', 'Last Modified') + self.assertEqual(collist, columns) + datalist = ( + ( + object_fakes.object_name_1, + object_fakes.object_bytes_1, + object_fakes.object_hash_1, + object_fakes.object_content_type_1, + object_fakes.object_modified_1, + ), + ( + object_fakes.object_name_2, + object_fakes.object_bytes_2, + object_fakes.object_hash_2, + object_fakes.object_content_type_2, + object_fakes.object_modified_2, + ), + ) + self.assertEqual(datalist, tuple(data)) + + def test_object_list_objects_all(self, o_mock): + o_mock.return_value = [ + copy.deepcopy(object_fakes.OBJECT), + copy.deepcopy(object_fakes.OBJECT_2), + ] + + arglist = [ + '--all', + object_fakes.container_name, + ] + verifylist = [ + ('all', True), + ('container', object_fakes.container_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'full_listing': True, + } + o_mock.assert_called_with( + container=object_fakes.container_name, + **kwargs + ) + + self.assertEqual(self.columns, columns) + datalist = ( + (object_fakes.object_name_1, ), + (object_fakes.object_name_2, ), + ) + self.assertEqual(datalist, tuple(data)) + + +@mock.patch( + 'openstackclient.api.object_store_v1.APIv1.object_show' +) +class TestObjectShow(TestObject): + + def setUp(self): + super(TestObjectShow, self).setUp() + + # Get the command object to test + self.cmd = obj.ShowObject(self.app, None) + + def test_object_show(self, c_mock): + c_mock.return_value = copy.deepcopy(object_fakes.OBJECT) + + arglist = [ + object_fakes.container_name, + object_fakes.object_name_1, + ] + verifylist = [ + ('container', object_fakes.container_name), + ('object', object_fakes.object_name_1), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + } + # lib.container.show_container(api, url, container) + c_mock.assert_called_with( + container=object_fakes.container_name, + object=object_fakes.object_name_1, + **kwargs + ) + + collist = ('bytes', 'content_type', 'hash', 'last_modified', 'name') + self.assertEqual(collist, columns) + datalist = ( + object_fakes.object_bytes_1, + object_fakes.object_content_type_1, + object_fakes.object_hash_1, + object_fakes.object_modified_1, + object_fakes.object_name_1, + ) + self.assertEqual(datalist, data) diff --git a/openstackclient/tests/unit/object/v1/test_object_all.py b/openstackclient/tests/unit/object/v1/test_object_all.py new file mode 100644 index 00000000..a0948b1b --- /dev/null +++ b/openstackclient/tests/unit/object/v1/test_object_all.py @@ -0,0 +1,182 @@ +# 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 copy + +from requests_mock.contrib import fixture + +from openstackclient.object.v1 import object as object_cmds +from openstackclient.tests.unit.object.v1 import fakes as object_fakes + + +class TestObjectAll(object_fakes.TestObjectv1): + + def setUp(self): + super(TestObjectAll, self).setUp() + + self.requests_mock = self.useFixture(fixture.Fixture()) + + +class TestObjectCreate(TestObjectAll): + + def setUp(self): + super(TestObjectCreate, self).setUp() + + # Get the command object to test + self.cmd = object_cmds.CreateObject(self.app, None) + + +class TestObjectList(TestObjectAll): + + columns = ('Name',) + + def setUp(self): + super(TestObjectList, self).setUp() + + # Get the command object to test + self.cmd = object_cmds.ListObject(self.app, None) + + def test_object_list_objects_no_options(self): + return_body = [ + copy.deepcopy(object_fakes.OBJECT), + copy.deepcopy(object_fakes.OBJECT_2), + ] + self.requests_mock.register_uri( + 'GET', + object_fakes.ENDPOINT + + '/' + + object_fakes.container_name + + '?format=json', + json=return_body, + status_code=200, + ) + + arglist = [ + object_fakes.container_name, + ] + verifylist = [ + ('container', object_fakes.container_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # Lister.take_action() returns two tuples + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + datalist = [ + (object_fakes.object_name_1, ), + (object_fakes.object_name_2, ), + ] + self.assertEqual(datalist, list(data)) + + def test_object_list_objects_prefix(self): + return_body = [ + copy.deepcopy(object_fakes.OBJECT_2), + ] + self.requests_mock.register_uri( + 'GET', + object_fakes.ENDPOINT + + '/' + + object_fakes.container_name_2 + + '?prefix=floppy&format=json', + json=return_body, + status_code=200, + ) + + arglist = [ + '--prefix', 'floppy', + object_fakes.container_name_2, + ] + verifylist = [ + ('prefix', 'floppy'), + ('container', object_fakes.container_name_2), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + datalist = ( + (object_fakes.object_name_2, ), + ) + self.assertEqual(datalist, tuple(data)) + + +class TestObjectShow(TestObjectAll): + + def setUp(self): + super(TestObjectShow, self).setUp() + + # Get the command object to test + self.cmd = object_cmds.ShowObject(self.app, None) + + def test_object_show(self): + headers = { + 'content-type': 'text/plain', + 'content-length': '20', + 'last-modified': 'yesterday', + 'etag': '4c4e39a763d58392724bccf76a58783a', + 'x-container-meta-owner': object_fakes.ACCOUNT_ID, + 'x-object-manifest': 'manifest', + } + self.requests_mock.register_uri( + 'HEAD', + '/'.join([ + object_fakes.ENDPOINT, + object_fakes.container_name, + object_fakes.object_name_1, + ]), + headers=headers, + status_code=200, + ) + + arglist = [ + object_fakes.container_name, + object_fakes.object_name_1, + ] + verifylist = [ + ('container', object_fakes.container_name), + ('object', object_fakes.object_name_1), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + collist = ( + 'account', + 'container', + 'content-length', + 'content-type', + 'etag', + 'last-modified', + 'object', + 'x-object-manifest', + ) + self.assertEqual(collist, columns) + datalist = ( + object_fakes.ACCOUNT_ID, + object_fakes.container_name, + '20', + 'text/plain', + '4c4e39a763d58392724bccf76a58783a', + 'yesterday', + object_fakes.object_name_1, + 'manifest', + ) + self.assertEqual(datalist, data) diff --git a/openstackclient/tests/unit/test_shell.py b/openstackclient/tests/unit/test_shell.py new file mode 100644 index 00000000..87cd7f51 --- /dev/null +++ b/openstackclient/tests/unit/test_shell.py @@ -0,0 +1,442 @@ +# Copyright 2012-2013 OpenStack Foundation +# +# 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 +import os +import sys + +from osc_lib.tests import utils as osc_lib_test_utils +from oslo_utils import importutils +import wrapt + +from openstackclient import shell + + +DEFAULT_AUTH_URL = "http://127.0.0.1:5000/v2.0/" +DEFAULT_PROJECT_ID = "xxxx-yyyy-zzzz" +DEFAULT_PROJECT_NAME = "project" +DEFAULT_DOMAIN_ID = "aaaa-bbbb-cccc" +DEFAULT_DOMAIN_NAME = "default" +DEFAULT_USER_DOMAIN_ID = "aaaa-bbbb-cccc" +DEFAULT_USER_DOMAIN_NAME = "domain" +DEFAULT_PROJECT_DOMAIN_ID = "aaaa-bbbb-cccc" +DEFAULT_PROJECT_DOMAIN_NAME = "domain" +DEFAULT_USERNAME = "username" +DEFAULT_PASSWORD = "password" + +DEFAULT_CLOUD = "altocumulus" +DEFAULT_REGION_NAME = "ZZ9_Plural_Z_Alpha" +DEFAULT_TOKEN = "token" +DEFAULT_SERVICE_URL = "http://127.0.0.1:8771/v3.0/" +DEFAULT_AUTH_PLUGIN = "v2password" +DEFAULT_INTERFACE = "internal" + +DEFAULT_COMPUTE_API_VERSION = "" +DEFAULT_IDENTITY_API_VERSION = "" +DEFAULT_IMAGE_API_VERSION = "" +DEFAULT_VOLUME_API_VERSION = "" +DEFAULT_NETWORK_API_VERSION = "" + +LIB_COMPUTE_API_VERSION = "" +LIB_IDENTITY_API_VERSION = "" +LIB_IMAGE_API_VERSION = "" +LIB_VOLUME_API_VERSION = "" +LIB_NETWORK_API_VERSION = "" + +CLOUD_1 = { + 'clouds': { + 'scc': { + 'auth': { + 'auth_url': DEFAULT_AUTH_URL, + 'project_name': DEFAULT_PROJECT_NAME, + 'username': 'zaphod', + }, + 'region_name': 'occ-cloud', + 'donut': 'glazed', + 'interface': 'public', + } + } +} + +CLOUD_2 = { + 'clouds': { + 'megacloud': { + 'cloud': 'megadodo', + 'auth': { + 'project_name': 'heart-o-gold', + 'username': 'zaphod', + }, + 'region_name': 'occ-cloud,krikkit,occ-env', + 'log_file': '/tmp/test_log_file', + 'log_level': 'debug', + 'cert': 'mycert', + 'key': 'mickey', + } + } +} + +PUBLIC_1 = { + 'public-clouds': { + 'megadodo': { + 'auth': { + 'auth_url': DEFAULT_AUTH_URL, + 'project_name': DEFAULT_PROJECT_NAME, + }, + 'region_name': 'occ-public', + 'donut': 'cake', + } + } +} + + +# The option table values is a tuple of (<value>, <test-opt>, <test-env>) +# where <value> is the test value to use, <test-opt> is True if this option +# should be tested as a CLI option and <test-env> is True of this option +# should be tested as an environment variable. + +# Global options that should be parsed before shell.initialize_app() is called +global_options = { + '--os-cloud': (DEFAULT_CLOUD, True, True), + '--os-region-name': (DEFAULT_REGION_NAME, True, True), + '--os-default-domain': (DEFAULT_DOMAIN_NAME, True, True), + '--os-cacert': ('/dev/null', True, True), + '--timing': (True, True, False), + '--os-profile': ('SECRET_KEY', True, False), + '--os-interface': (DEFAULT_INTERFACE, True, True) +} + + +# Wrap the osc_lib make_shell() function to set the shell class since +# osc-lib's TestShell class doesn't allow us to specify it yet. +# TODO(dtroyer): remove this once the shell_class_patch patch is released +# in osc-lib +def make_shell_wrapper(func, inst, args, kwargs): + if 'shell_class' not in kwargs: + kwargs['shell_class'] = shell.OpenStackShell + return func(*args, **kwargs) + + +wrapt.wrap_function_wrapper( + osc_lib_test_utils, + 'make_shell', + make_shell_wrapper, +) + + +class TestShell(osc_lib_test_utils.TestShell): + + # Full name of the OpenStackShell class to test (cliff.app.App subclass) + shell_class_name = "openstackclient.shell.OpenStackShell" + + # TODO(dtroyer): remove this once the shell_class_patch patch is released + # in osc-lib + app_patch = shell_class_name + + def setUp(self): + super(TestShell, self).setUp() + # TODO(dtroyer): remove this once the shell_class_patch patch is + # released in osc-lib + self.shell_class = importutils.import_class(self.shell_class_name) + + def _assert_token_endpoint_auth(self, cmd_options, default_args): + with mock.patch( + self.shell_class_name + ".initialize_app", + self.app, + ): + _shell = osc_lib_test_utils.make_shell( + shell_class=self.shell_class, + ) + _cmd = cmd_options + " list role" + osc_lib_test_utils.fake_execute(_shell, _cmd) + print("_shell: %s" % _shell) + + self.app.assert_called_with(["list", "role"]) + self.assertEqual( + default_args.get("token", ''), + _shell.options.token, + "token", + ) + self.assertEqual( + default_args.get("url", ''), + _shell.options.url, + "url", + ) + + def _assert_token_auth(self, cmd_options, default_args): + with mock.patch( + self.app_patch + ".initialize_app", + self.app, + ): + _shell = osc_lib_test_utils.make_shell( + shell_class=self.shell_class, + ) + _cmd = cmd_options + " list role" + osc_lib_test_utils.fake_execute(_shell, _cmd) + print("_shell: %s" % _shell) + + self.app.assert_called_with(["list", "role"]) + self.assertEqual( + default_args.get("token", ''), + _shell.options.token, + "token" + ) + self.assertEqual( + default_args.get("auth_url", ''), + _shell.options.auth_url, + "auth_url" + ) + + def _assert_cli(self, cmd_options, default_args): + with mock.patch( + self.shell_class_name + ".initialize_app", + self.app, + ): + _shell = osc_lib_test_utils.make_shell( + shell_class=self.shell_class, + ) + _cmd = cmd_options + " list server" + osc_lib_test_utils.fake_execute(_shell, _cmd) + + self.app.assert_called_with(["list", "server"]) + self.assertEqual(default_args["compute_api_version"], + _shell.options.os_compute_api_version) + self.assertEqual(default_args["identity_api_version"], + _shell.options.os_identity_api_version) + self.assertEqual(default_args["image_api_version"], + _shell.options.os_image_api_version) + self.assertEqual(default_args["volume_api_version"], + _shell.options.os_volume_api_version) + self.assertEqual(default_args["network_api_version"], + _shell.options.os_network_api_version) + + +class TestShellOptions(TestShell): + + def setUp(self): + super(TestShellOptions, self).setUp() + self.useFixture(osc_lib_test_utils.EnvFixture()) + + def _test_options_init_app(self, test_opts): + for opt in test_opts.keys(): + if not test_opts[opt][1]: + continue + key = osc_lib_test_utils.opt2attr(opt) + if isinstance(test_opts[opt][0], str): + cmd = opt + " " + test_opts[opt][0] + else: + cmd = opt + kwargs = { + key: test_opts[opt][0], + } + self._assert_initialize_app_arg(cmd, kwargs) + + def _test_options_get_one_cloud(self, test_opts): + for opt in test_opts.keys(): + if not test_opts[opt][1]: + continue + key = osc_lib_test_utils.opt2attr(opt) + if isinstance(test_opts[opt][0], str): + cmd = opt + " " + test_opts[opt][0] + else: + cmd = opt + kwargs = { + key: test_opts[opt][0], + } + self._assert_cloud_config_arg(cmd, kwargs) + + def _test_env_init_app(self, test_opts): + for opt in test_opts.keys(): + if not test_opts[opt][2]: + continue + key = osc_lib_test_utils.opt2attr(opt) + kwargs = { + key: test_opts[opt][0], + } + env = { + osc_lib_test_utils.opt2env(opt): test_opts[opt][0], + } + os.environ = env.copy() + self._assert_initialize_app_arg("", kwargs) + + def _test_env_get_one_cloud(self, test_opts): + for opt in test_opts.keys(): + if not test_opts[opt][2]: + continue + key = osc_lib_test_utils.opt2attr(opt) + kwargs = { + key: test_opts[opt][0], + } + env = { + osc_lib_test_utils.opt2env(opt): test_opts[opt][0], + } + os.environ = env.copy() + self._assert_cloud_config_arg("", kwargs) + + +class TestShellTokenAuthEnv(TestShell): + + def setUp(self): + super(TestShellTokenAuthEnv, self).setUp() + env = { + "OS_TOKEN": DEFAULT_TOKEN, + "OS_AUTH_URL": DEFAULT_AUTH_URL, + } + self.useFixture(osc_lib_test_utils.EnvFixture(env.copy())) + + def test_env(self): + flag = "" + kwargs = { + "token": DEFAULT_TOKEN, + "auth_url": DEFAULT_AUTH_URL, + } + self._assert_token_auth(flag, kwargs) + + def test_only_token(self): + flag = "--os-token xyzpdq" + kwargs = { + "token": "xyzpdq", + "auth_url": DEFAULT_AUTH_URL, + } + self._assert_token_auth(flag, kwargs) + + def test_only_auth_url(self): + flag = "--os-auth-url http://cloud.local:555" + kwargs = { + "token": DEFAULT_TOKEN, + "auth_url": "http://cloud.local:555", + } + self._assert_token_auth(flag, kwargs) + + def test_empty_auth(self): + os.environ = {} + flag = "" + kwargs = { + "token": '', + "auth_url": '', + } + self._assert_token_auth(flag, kwargs) + + +class TestShellTokenEndpointAuthEnv(TestShell): + + def setUp(self): + super(TestShellTokenEndpointAuthEnv, self).setUp() + env = { + "OS_TOKEN": DEFAULT_TOKEN, + "OS_URL": DEFAULT_SERVICE_URL, + } + self.useFixture(osc_lib_test_utils.EnvFixture(env.copy())) + + def test_env(self): + flag = "" + kwargs = { + "token": DEFAULT_TOKEN, + "url": DEFAULT_SERVICE_URL, + } + self._assert_token_endpoint_auth(flag, kwargs) + + def test_only_token(self): + flag = "--os-token xyzpdq" + kwargs = { + "token": "xyzpdq", + "url": DEFAULT_SERVICE_URL, + } + self._assert_token_auth(flag, kwargs) + + def test_only_url(self): + flag = "--os-url http://cloud.local:555" + kwargs = { + "token": DEFAULT_TOKEN, + "url": "http://cloud.local:555", + } + self._assert_token_auth(flag, kwargs) + + def test_empty_auth(self): + os.environ = {} + flag = "" + kwargs = { + "token": '', + "url": '', + } + self._assert_token_auth(flag, kwargs) + + +class TestShellCli(TestShell): + + def setUp(self): + super(TestShellCli, self).setUp() + env = { + "OS_COMPUTE_API_VERSION": DEFAULT_COMPUTE_API_VERSION, + "OS_IDENTITY_API_VERSION": DEFAULT_IDENTITY_API_VERSION, + "OS_IMAGE_API_VERSION": DEFAULT_IMAGE_API_VERSION, + "OS_VOLUME_API_VERSION": DEFAULT_VOLUME_API_VERSION, + "OS_NETWORK_API_VERSION": DEFAULT_NETWORK_API_VERSION, + } + self.useFixture(osc_lib_test_utils.EnvFixture(env.copy())) + + def test_default_env(self): + flag = "" + kwargs = { + "compute_api_version": DEFAULT_COMPUTE_API_VERSION, + "identity_api_version": DEFAULT_IDENTITY_API_VERSION, + "image_api_version": DEFAULT_IMAGE_API_VERSION, + "volume_api_version": DEFAULT_VOLUME_API_VERSION, + "network_api_version": DEFAULT_NETWORK_API_VERSION, + } + self._assert_cli(flag, kwargs) + + def test_empty_env(self): + os.environ = {} + flag = "" + kwargs = { + "compute_api_version": LIB_COMPUTE_API_VERSION, + "identity_api_version": LIB_IDENTITY_API_VERSION, + "image_api_version": LIB_IMAGE_API_VERSION, + "volume_api_version": LIB_VOLUME_API_VERSION, + "network_api_version": LIB_NETWORK_API_VERSION + } + self._assert_cli(flag, kwargs) + + +class TestShellArgV(TestShell): + """Test the deferred help flag""" + + def setUp(self): + super(TestShellArgV, self).setUp() + + def test_shell_argv(self): + """Test argv decoding + + Python 2 does nothing with argv while Python 3 decodes it into + Unicode before we ever see it. We manually decode when running + under Python 2 so verify that we get the right argv types. + + Use the argv supplied by the test runner so we get actual Python + runtime behaviour; we only need to check the type of argv[0] + which will alwyas be present. + """ + + with mock.patch( + self.shell_class_name + ".run", + self.app, + ): + # Ensure type gets through unmolested through shell.main() + argv = sys.argv + shell.main(sys.argv) + self.assertEqual(type(argv[0]), type(self.app.call_args[0][0][0])) + + # When shell.main() gets sys.argv itself it should be decoded + shell.main() + self.assertEqual(type(u'x'), type(self.app.call_args[0][0][0])) diff --git a/openstackclient/tests/unit/utils.py b/openstackclient/tests/unit/utils.py new file mode 100644 index 00000000..3c5c8683 --- /dev/null +++ b/openstackclient/tests/unit/utils.py @@ -0,0 +1,75 @@ +# Copyright 2012-2013 OpenStack Foundation +# Copyright 2013 Nebula 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 fixtures +import os +import testtools + +from openstackclient.tests.unit import fakes + + +class ParserException(Exception): + pass + + +class TestCase(testtools.TestCase): + + def setUp(self): + testtools.TestCase.setUp(self) + + if (os.environ.get("OS_STDOUT_CAPTURE") == "True" or + os.environ.get("OS_STDOUT_CAPTURE") == "1"): + stdout = self.useFixture(fixtures.StringStream("stdout")).stream + self.useFixture(fixtures.MonkeyPatch("sys.stdout", stdout)) + + if (os.environ.get("OS_STDERR_CAPTURE") == "True" or + os.environ.get("OS_STDERR_CAPTURE") == "1"): + stderr = self.useFixture(fixtures.StringStream("stderr")).stream + self.useFixture(fixtures.MonkeyPatch("sys.stderr", stderr)) + + def assertNotCalled(self, m, msg=None): + """Assert a function was not called""" + + if m.called: + if not msg: + msg = 'method %s should not have been called' % m + self.fail(msg) + + +class TestCommand(TestCase): + """Test cliff command classes""" + + def setUp(self): + super(TestCommand, self).setUp() + # Build up a fake app + self.fake_stdout = fakes.FakeStdout() + self.fake_log = fakes.FakeLog() + self.app = fakes.FakeApp(self.fake_stdout, self.fake_log) + self.app.client_manager = fakes.FakeClientManager() + self.app.options = fakes.FakeOptions() + + def check_parser(self, cmd, args, verify_args): + cmd_parser = cmd.get_parser('check_parser') + try: + parsed_args = cmd_parser.parse_args(args) + except SystemExit: + raise ParserException("Argument parse failed") + for av in verify_args: + attr, value = av + if attr: + self.assertIn(attr, parsed_args) + self.assertEqual(value, getattr(parsed_args, attr)) + return parsed_args diff --git a/openstackclient/tests/unit/volume/__init__.py b/openstackclient/tests/unit/volume/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/openstackclient/tests/unit/volume/__init__.py diff --git a/openstackclient/tests/unit/volume/test_find_resource.py b/openstackclient/tests/unit/volume/test_find_resource.py new file mode 100644 index 00000000..d2509315 --- /dev/null +++ b/openstackclient/tests/unit/volume/test_find_resource.py @@ -0,0 +1,83 @@ +# Copyright 2013 Nebula 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 mock + +from cinderclient.v1 import volume_snapshots +from cinderclient.v1 import volumes +from osc_lib import exceptions +from osc_lib import utils + +from openstackclient.tests.unit import utils as test_utils +from openstackclient.volume import client # noqa + + +# Monkey patch for v1 cinderclient +# NOTE(dtroyer): Do here because openstackclient.volume.client +# doesn't do it until the client object is created now. +volumes.Volume.NAME_ATTR = 'display_name' +volume_snapshots.Snapshot.NAME_ATTR = 'display_name' + + +ID = '1after909' +NAME = 'PhilSpector' + + +class TestFindResourceVolumes(test_utils.TestCase): + + def setUp(self): + super(TestFindResourceVolumes, self).setUp() + api = mock.Mock() + api.client = mock.Mock() + api.client.get = mock.Mock() + resp = mock.Mock() + body = {"volumes": [{"id": ID, 'display_name': NAME}]} + api.client.get.side_effect = [Exception("Not found"), + Exception("Not found"), + (resp, body)] + self.manager = volumes.VolumeManager(api) + + def test_find(self): + result = utils.find_resource(self.manager, NAME) + self.assertEqual(ID, result.id) + self.assertEqual(NAME, result.display_name) + + def test_not_find(self): + self.assertRaises(exceptions.CommandError, utils.find_resource, + self.manager, 'GeorgeMartin') + + +class TestFindResourceVolumeSnapshots(test_utils.TestCase): + + def setUp(self): + super(TestFindResourceVolumeSnapshots, self).setUp() + api = mock.Mock() + api.client = mock.Mock() + api.client.get = mock.Mock() + resp = mock.Mock() + body = {"snapshots": [{"id": ID, 'display_name': NAME}]} + api.client.get.side_effect = [Exception("Not found"), + Exception("Not found"), + (resp, body)] + self.manager = volume_snapshots.SnapshotManager(api) + + def test_find(self): + result = utils.find_resource(self.manager, NAME) + self.assertEqual(ID, result.id) + self.assertEqual(NAME, result.display_name) + + def test_not_find(self): + self.assertRaises(exceptions.CommandError, utils.find_resource, + self.manager, 'GeorgeMartin') diff --git a/openstackclient/tests/unit/volume/v1/__init__.py b/openstackclient/tests/unit/volume/v1/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/openstackclient/tests/unit/volume/v1/__init__.py diff --git a/openstackclient/tests/unit/volume/v1/fakes.py b/openstackclient/tests/unit/volume/v1/fakes.py new file mode 100644 index 00000000..c6fee7d1 --- /dev/null +++ b/openstackclient/tests/unit/volume/v1/fakes.py @@ -0,0 +1,280 @@ +# Copyright 2013 Nebula 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 mock + +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes +from openstackclient.tests.unit import utils + + +volume_id = 'vvvvvvvv-vvvv-vvvv-vvvvvvvv' +volume_name = 'nigel' +volume_description = 'Nigel Tufnel' +volume_status = 'available' +volume_size = 120 +volume_type = 'to-eleven' +volume_zone = 'stonehenge' +volume_metadata = { + 'Alpha': 'a', + 'Beta': 'b', + 'Gamma': 'g', +} +volume_metadata_str = "Alpha='a', Beta='b', Gamma='g'" + +VOLUME = { + 'id': volume_id, + 'display_name': volume_name, + 'display_description': volume_description, + 'size': volume_size, + 'status': volume_status, + 'attach_status': 'detached', + 'availability_zone': volume_zone, + 'volume_type': volume_type, + 'metadata': volume_metadata, +} + +extension_name = 'SchedulerHints' +extension_namespace = 'http://docs.openstack.org/'\ + 'block-service/ext/scheduler-hints/api/v2' +extension_description = 'Pass arbitrary key/value'\ + 'pairs to the scheduler.' +extension_updated = '2014-02-07T12:00:0-00:00' +extension_alias = 'OS-SCH-HNT' +extension_links = '[{"href":'\ + '"https://github.com/openstack/block-api", "type":'\ + ' "text/html", "rel": "describedby"}]' + +EXTENSION = { + 'name': extension_name, + 'namespace': extension_namespace, + 'description': extension_description, + 'updated': extension_updated, + 'alias': extension_alias, + 'links': extension_links, +} + +# NOTE(dtroyer): duplicating here the minimum image info needed to test +# volume create --image until circular references can be +# avoided by refactoring the test fakes. + +image_id = 'im1' +image_name = 'graven' + + +IMAGE = { + 'id': image_id, + 'name': image_name, +} + +type_id = "5520dc9e-6f9b-4378-a719-729911c0f407" +type_name = "fake-lvmdriver-1" + +TYPE = { + 'id': type_id, + 'name': type_name +} + +qos_id = '6f2be1de-997b-4230-b76c-a3633b59e8fb' +qos_consumer = 'front-end' +qos_default_consumer = 'both' +qos_name = "fake-qos-specs" +qos_specs = { + 'foo': 'bar', + 'iops': '9001' +} +qos_association = { + 'association_type': 'volume_type', + 'name': type_name, + 'id': type_id +} + +QOS = { + 'id': qos_id, + 'consumer': qos_consumer, + 'name': qos_name +} + +QOS_DEFAULT_CONSUMER = { + 'id': qos_id, + 'consumer': qos_default_consumer, + 'name': qos_name +} + +QOS_WITH_SPECS = { + 'id': qos_id, + 'consumer': qos_consumer, + 'name': qos_name, + 'specs': qos_specs +} + +QOS_WITH_ASSOCIATIONS = { + 'id': qos_id, + 'consumer': qos_consumer, + 'name': qos_name, + 'specs': qos_specs, + 'associations': [qos_association] +} + + +class FakeTransfer(object): + """Fake one or more Transfer.""" + + @staticmethod + def create_one_transfer(attrs=None): + """Create a fake transfer. + + :param Dictionary attrs: + A dictionary with all attributes of Transfer Request + :return: + A FakeResource object with volume_id, name, id. + """ + # Set default attribute + transfer_info = { + 'volume_id': 'ce26708d-a7f8-4b4b-9861-4a80256615a7', + 'name': 'fake_transfer_name', + 'id': '731a7f53-aa92-4fbd-9de3-6f7d729c926b' + } + + # Overwrite default attributes if there are some attributes set + attrs = attrs or {} + + transfer_info.update(attrs) + + transfer = fakes.FakeResource( + None, + transfer_info, + loaded=True) + + return transfer + + +class FakeService(object): + """Fake one or more Services.""" + + @staticmethod + def create_one_service(attrs=None): + """Create a fake service. + + :param Dictionary attrs: + A dictionary with all attributes of service + :return: + A FakeResource object with host, status, etc. + """ + # Set default attribute + service_info = { + 'host': 'host_test', + 'binary': 'cinder_test', + 'status': 'enabled', + 'disabled_reason': 'LongHoliday-GoldenWeek', + 'zone': 'fake_zone', + 'updated_at': 'fake_date', + 'state': 'fake_state', + } + + # Overwrite default attributes if there are some attributes set + attrs = attrs or {} + + service_info.update(attrs) + + service = fakes.FakeResource( + None, + service_info, + loaded=True) + + return service + + @staticmethod + def create_services(attrs=None, count=2): + """Create multiple fake services. + + :param Dictionary attrs: + A dictionary with all attributes of service + :param Integer count: + The number of services to be faked + :return: + A list of FakeResource objects + """ + services = [] + for n in range(0, count): + services.append(FakeService.create_one_service(attrs)) + + return services + + @staticmethod + def get_services(services=None, count=2): + """Get an iterable MagicMock object with a list of faked services. + + If services list is provided, then initialize the Mock object with the + list. Otherwise create one. + + :param List services: + A list of FakeResource objects faking services + :param Integer count: + The number of services to be faked + :return + An iterable Mock object with side_effect set to a list of faked + services + """ + if services is None: + services = FakeService.create_services(count) + + return mock.MagicMock(side_effect=services) + + +class FakeImagev1Client(object): + + def __init__(self, **kwargs): + self.images = mock.Mock() + + +class FakeVolumev1Client(object): + + def __init__(self, **kwargs): + self.volumes = mock.Mock() + self.volumes.resource_class = fakes.FakeResource(None, {}) + self.services = mock.Mock() + self.services.resource_class = fakes.FakeResource(None, {}) + self.extensions = mock.Mock() + self.extensions.resource_class = fakes.FakeResource(None, {}) + self.qos_specs = mock.Mock() + self.qos_specs.resource_class = fakes.FakeResource(None, {}) + self.volume_types = mock.Mock() + self.volume_types.resource_class = fakes.FakeResource(None, {}) + self.transfers = mock.Mock() + self.transfers.resource_class = fakes.FakeResource(None, {}) + self.auth_token = kwargs['token'] + self.management_url = kwargs['endpoint'] + + +class TestVolumev1(utils.TestCommand): + + def setUp(self): + super(TestVolumev1, self).setUp() + + self.app.client_manager.volume = FakeVolumev1Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + + self.app.client_manager.identity = identity_fakes.FakeIdentityv2Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + + self.app.client_manager.image = FakeImagev1Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) diff --git a/openstackclient/tests/unit/volume/v1/test_qos_specs.py b/openstackclient/tests/unit/volume/v1/test_qos_specs.py new file mode 100644 index 00000000..7b87ccb3 --- /dev/null +++ b/openstackclient/tests/unit/volume/v1/test_qos_specs.py @@ -0,0 +1,472 @@ +# Copyright 2015 iWeb Technologies 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 copy + +from osc_lib import utils + +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes +from openstackclient.volume.v1 import qos_specs + + +class TestQos(volume_fakes.TestVolumev1): + + def setUp(self): + super(TestQos, self).setUp() + + self.qos_mock = self.app.client_manager.volume.qos_specs + self.qos_mock.reset_mock() + + self.types_mock = self.app.client_manager.volume.volume_types + self.types_mock.reset_mock() + + +class TestQosAssociate(TestQos): + + def setUp(self): + super(TestQosAssociate, self).setUp() + + # Get the command object to test + self.cmd = qos_specs.AssociateQos(self.app, None) + + def test_qos_associate(self): + self.qos_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(volume_fakes.QOS), + loaded=True + ) + self.types_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(volume_fakes.TYPE), + loaded=True + ) + arglist = [ + volume_fakes.qos_id, + volume_fakes.type_id + ] + verifylist = [ + ('qos_spec', volume_fakes.qos_id), + ('volume_type', volume_fakes.type_id) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.qos_mock.associate.assert_called_with( + volume_fakes.qos_id, + volume_fakes.type_id + ) + self.assertIsNone(result) + + +class TestQosCreate(TestQos): + + columns = ( + 'consumer', + 'id', + 'name', + ) + datalist = ( + volume_fakes.qos_consumer, + volume_fakes.qos_id, + volume_fakes.qos_name + ) + + def setUp(self): + super(TestQosCreate, self).setUp() + + # Get the command object to test + self.cmd = qos_specs.CreateQos(self.app, None) + + def test_qos_create_without_properties(self): + self.qos_mock.create.return_value = fakes.FakeResource( + None, + copy.deepcopy(volume_fakes.QOS_DEFAULT_CONSUMER), + loaded=True + ) + + arglist = [ + volume_fakes.qos_name, + ] + verifylist = [ + ('name', volume_fakes.qos_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.qos_mock.create.assert_called_with( + volume_fakes.qos_name, + {'consumer': volume_fakes.qos_default_consumer} + ) + + self.assertEqual(self.columns, columns) + datalist = ( + volume_fakes.qos_default_consumer, + volume_fakes.qos_id, + volume_fakes.qos_name + ) + self.assertEqual(datalist, data) + + def test_qos_create_with_consumer(self): + self.qos_mock.create.return_value = fakes.FakeResource( + None, + copy.deepcopy(volume_fakes.QOS), + loaded=True + ) + + arglist = [ + volume_fakes.qos_name, + '--consumer', volume_fakes.qos_consumer + ] + verifylist = [ + ('name', volume_fakes.qos_name), + ('consumer', volume_fakes.qos_consumer) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.qos_mock.create.assert_called_with( + volume_fakes.qos_name, + {'consumer': volume_fakes.qos_consumer} + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_qos_create_with_properties(self): + self.qos_mock.create.return_value = fakes.FakeResource( + None, + copy.deepcopy(volume_fakes.QOS_WITH_SPECS), + loaded=True + ) + + arglist = [ + volume_fakes.qos_name, + '--consumer', volume_fakes.qos_consumer, + '--property', 'foo=bar', + '--property', 'iops=9001' + ] + verifylist = [ + ('name', volume_fakes.qos_name), + ('consumer', volume_fakes.qos_consumer), + ('property', volume_fakes.qos_specs) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + specs = volume_fakes.qos_specs.copy() + specs.update({'consumer': volume_fakes.qos_consumer}) + self.qos_mock.create.assert_called_with( + volume_fakes.qos_name, + specs + ) + + columns = self.columns + ( + 'specs', + ) + self.assertEqual(columns, columns) + datalist = self.datalist + ( + volume_fakes.qos_specs, + ) + self.assertEqual(datalist, data) + + +class TestQosDelete(TestQos): + + def setUp(self): + super(TestQosDelete, self).setUp() + + self.qos_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(volume_fakes.QOS), + loaded=True, + ) + + # Get the command object to test + self.cmd = qos_specs.DeleteQos(self.app, None) + + def test_qos_delete_with_id(self): + arglist = [ + volume_fakes.qos_id + ] + verifylist = [ + ('qos_specs', [volume_fakes.qos_id]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.qos_mock.delete.assert_called_with(volume_fakes.qos_id, False) + self.assertIsNone(result) + + def test_qos_delete_with_name(self): + arglist = [ + volume_fakes.qos_name + ] + verifylist = [ + ('qos_specs', [volume_fakes.qos_name]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.qos_mock.delete.assert_called_with(volume_fakes.qos_id, False) + self.assertIsNone(result) + + def test_qos_delete_with_force(self): + arglist = [ + '--force', + volume_fakes.qos_id + ] + verifylist = [ + ('force', True), + ('qos_specs', [volume_fakes.qos_id]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.qos_mock.delete.assert_called_with(volume_fakes.qos_id, True) + self.assertIsNone(result) + + +class TestQosDisassociate(TestQos): + + def setUp(self): + super(TestQosDisassociate, self).setUp() + + # Get the command object to test + self.cmd = qos_specs.DisassociateQos(self.app, None) + + def test_qos_disassociate_with_volume_type(self): + self.qos_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(volume_fakes.QOS), + loaded=True + ) + self.types_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(volume_fakes.TYPE), + loaded=True + ) + arglist = [ + volume_fakes.qos_id, + '--volume-type', volume_fakes.type_id + ] + verifylist = [ + ('qos_spec', volume_fakes.qos_id), + ('volume_type', volume_fakes.type_id) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.qos_mock.disassociate.assert_called_with( + volume_fakes.qos_id, + volume_fakes.type_id + ) + self.assertIsNone(result) + + def test_qos_disassociate_with_all_volume_types(self): + self.qos_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(volume_fakes.QOS), + loaded=True + ) + + arglist = [ + volume_fakes.qos_id, + '--all' + ] + verifylist = [ + ('qos_spec', volume_fakes.qos_id) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.qos_mock.disassociate_all.assert_called_with(volume_fakes.qos_id) + self.assertIsNone(result) + + +class TestQosList(TestQos): + + def setUp(self): + super(TestQosList, self).setUp() + + self.qos_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(volume_fakes.QOS_WITH_ASSOCIATIONS), + loaded=True, + ) + self.qos_mock.list.return_value = [self.qos_mock.get.return_value] + self.qos_mock.get_associations.return_value = [fakes.FakeResource( + None, + copy.deepcopy(volume_fakes.qos_association), + loaded=True, + )] + + # Get the command object to test + self.cmd = qos_specs.ListQos(self.app, None) + + def test_qos_list(self): + arglist = [] + verifylist = [] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.qos_mock.list.assert_called_with() + + collist = ( + 'ID', + 'Name', + 'Consumer', + 'Associations', + 'Specs', + ) + self.assertEqual(collist, columns) + datalist = (( + volume_fakes.qos_id, + volume_fakes.qos_name, + volume_fakes.qos_consumer, + volume_fakes.type_name, + utils.format_dict(volume_fakes.qos_specs), + ), ) + self.assertEqual(datalist, tuple(data)) + + +class TestQosSet(TestQos): + + def setUp(self): + super(TestQosSet, self).setUp() + + # Get the command object to test + self.cmd = qos_specs.SetQos(self.app, None) + + def test_qos_set_with_properties_with_id(self): + self.qos_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(volume_fakes.QOS_WITH_SPECS), + loaded=True + ) + arglist = [ + volume_fakes.qos_id, + '--property', 'foo=bar', + '--property', 'iops=9001' + ] + verifylist = [ + ('qos_spec', volume_fakes.qos_id), + ('property', volume_fakes.qos_specs) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.qos_mock.set_keys.assert_called_with( + volume_fakes.qos_id, + volume_fakes.qos_specs + ) + self.assertIsNone(result) + + +class TestQosShow(TestQos): + + def setUp(self): + super(TestQosShow, self).setUp() + + self.qos_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(volume_fakes.QOS_WITH_ASSOCIATIONS), + loaded=True, + ) + self.qos_mock.get_associations.return_value = [fakes.FakeResource( + None, + copy.deepcopy(volume_fakes.qos_association), + loaded=True, + )] + + # Get the command object to test + self.cmd = qos_specs.ShowQos(self.app, None) + + def test_qos_show(self): + arglist = [ + volume_fakes.qos_id + ] + verifylist = [ + ('qos_spec', volume_fakes.qos_id) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.qos_mock.get.assert_called_with( + volume_fakes.qos_id + ) + + collist = ( + 'associations', + 'consumer', + 'id', + 'name', + 'specs' + ) + self.assertEqual(collist, columns) + datalist = ( + volume_fakes.type_name, + volume_fakes.qos_consumer, + volume_fakes.qos_id, + volume_fakes.qos_name, + utils.format_dict(volume_fakes.qos_specs), + ) + self.assertEqual(datalist, tuple(data)) + + +class TestQosUnset(TestQos): + + def setUp(self): + super(TestQosUnset, self).setUp() + + # Get the command object to test + self.cmd = qos_specs.UnsetQos(self.app, None) + + def test_qos_unset_with_properties(self): + self.qos_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(volume_fakes.QOS), + loaded=True + ) + arglist = [ + volume_fakes.qos_id, + '--property', 'iops', + '--property', 'foo' + ] + + verifylist = [ + ('qos_spec', volume_fakes.qos_id), + ('property', ['iops', 'foo']) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.qos_mock.unset_keys.assert_called_with( + volume_fakes.qos_id, + ['iops', 'foo'] + ) + self.assertIsNone(result) diff --git a/openstackclient/tests/unit/volume/v1/test_service.py b/openstackclient/tests/unit/volume/v1/test_service.py new file mode 100644 index 00000000..82d21bfc --- /dev/null +++ b/openstackclient/tests/unit/volume/v1/test_service.py @@ -0,0 +1,286 @@ +# +# 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 osc_lib import exceptions + +from openstackclient.tests.unit.volume.v1 import fakes as service_fakes +from openstackclient.volume.v1 import service + + +class TestService(service_fakes.TestVolumev1): + + def setUp(self): + super(TestService, self).setUp() + + # Get a shortcut to the ServiceManager Mock + self.service_mock = self.app.client_manager.volume.services + self.service_mock.reset_mock() + + +class TestServiceList(TestService): + + # The service to be listed + services = service_fakes.FakeService.create_one_service() + + def setUp(self): + super(TestServiceList, self).setUp() + + self.service_mock.list.return_value = [self.services] + + # Get the command object to test + self.cmd = service.ListService(self.app, None) + + def test_service_list(self): + arglist = [ + '--host', self.services.host, + '--service', self.services.binary, + ] + verifylist = [ + ('host', self.services.host), + ('service', self.services.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + expected_columns = [ + 'Binary', + 'Host', + 'Zone', + 'Status', + 'State', + 'Updated At', + ] + + # confirming if all expected columns are present in the result. + self.assertEqual(expected_columns, columns) + + datalist = (( + self.services.binary, + self.services.host, + self.services.zone, + self.services.status, + self.services.state, + self.services.updated_at, + ), ) + + # confirming if all expected values are present in the result. + self.assertEqual(datalist, tuple(data)) + + # checking if proper call was made to list services + self.service_mock.list.assert_called_with( + self.services.host, + self.services.binary, + ) + + # checking if prohibited columns are present in output + self.assertNotIn("Disabled Reason", columns) + self.assertNotIn(self.services.disabled_reason, + tuple(data)) + + def test_service_list_with_long_option(self): + arglist = [ + '--host', self.services.host, + '--service', self.services.binary, + '--long' + ] + verifylist = [ + ('host', self.services.host), + ('service', self.services.binary), + ('long', True) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + expected_columns = [ + 'Binary', + 'Host', + 'Zone', + 'Status', + 'State', + 'Updated At', + 'Disabled Reason' + ] + + # confirming if all expected columns are present in the result. + self.assertEqual(expected_columns, columns) + + datalist = (( + self.services.binary, + self.services.host, + self.services.zone, + self.services.status, + self.services.state, + self.services.updated_at, + self.services.disabled_reason, + ), ) + + # confirming if all expected values are present in the result. + self.assertEqual(datalist, tuple(data)) + + self.service_mock.list.assert_called_with( + self.services.host, + self.services.binary, + ) + + +class TestServiceSet(TestService): + + service = service_fakes.FakeService.create_one_service() + + def setUp(self): + super(TestServiceSet, self).setUp() + + self.service_mock.enable.return_value = self.service + self.service_mock.disable.return_value = self.service + self.service_mock.disable_log_reason.return_value = self.service + + self.cmd = service.SetService(self.app, None) + + def test_service_set_nothing(self): + arglist = [ + self.service.host, + self.service.binary, + ] + verifylist = [ + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.service_mock.enable.assert_not_called() + self.service_mock.disable.assert_not_called() + self.service_mock.disable_log_reason.assert_not_called() + self.assertIsNone(result) + + def test_service_set_enable(self): + arglist = [ + '--enable', + self.service.host, + self.service.binary, + ] + verifylist = [ + ('enable', True), + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.service_mock.enable.assert_called_with( + self.service.host, + self.service.binary + ) + self.service_mock.disable.assert_not_called() + self.service_mock.disable_log_reason.assert_not_called() + self.assertIsNone(result) + + def test_service_set_disable(self): + arglist = [ + '--disable', + self.service.host, + self.service.binary, + ] + verifylist = [ + ('disable', True), + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.service_mock.disable.assert_called_with( + self.service.host, + self.service.binary + ) + self.service_mock.enable.assert_not_called() + self.service_mock.disable_log_reason.assert_not_called() + self.assertIsNone(result) + + def test_service_set_disable_with_reason(self): + reason = 'earthquake' + arglist = [ + '--disable', + '--disable-reason', reason, + self.service.host, + self.service.binary, + ] + verifylist = [ + ('disable', True), + ('disable_reason', reason), + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.service_mock.disable_log_reason.assert_called_with( + self.service.host, + self.service.binary, + reason + ) + self.assertIsNone(result) + + def test_service_set_only_with_disable_reason(self): + reason = 'earthquake' + arglist = [ + '--disable-reason', reason, + self.service.host, + self.service.binary, + ] + verifylist = [ + ('disable_reason', reason), + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + try: + self.cmd.take_action(parsed_args) + self.fail("CommandError should be raised.") + except exceptions.CommandError as e: + self.assertEqual("Cannot specify option --disable-reason without " + "--disable specified.", str(e)) + + def test_service_set_enable_with_disable_reason(self): + reason = 'earthquake' + arglist = [ + '--enable', + '--disable-reason', reason, + self.service.host, + self.service.binary, + ] + verifylist = [ + ('enable', True), + ('disable_reason', reason), + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + try: + self.cmd.take_action(parsed_args) + self.fail("CommandError should be raised.") + except exceptions.CommandError as e: + self.assertEqual("Cannot specify option --disable-reason without " + "--disable specified.", str(e)) diff --git a/openstackclient/tests/unit/volume/v1/test_transfer_request.py b/openstackclient/tests/unit/volume/v1/test_transfer_request.py new file mode 100644 index 00000000..f7980c34 --- /dev/null +++ b/openstackclient/tests/unit/volume/v1/test_transfer_request.py @@ -0,0 +1,114 @@ +# +# 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 openstackclient.tests.unit.volume.v1 import fakes as transfer_fakes +from openstackclient.volume.v1 import volume_transfer_request + + +class TestTransfer(transfer_fakes.TestVolumev1): + + def setUp(self): + super(TestTransfer, self).setUp() + + # Get a shortcut to the TransferManager Mock + self.transfer_mock = self.app.client_manager.volume.transfers + self.transfer_mock.reset_mock() + + +class TestTransferList(TestTransfer): + + # The Transfers to be listed + volume_transfers = transfer_fakes.FakeTransfer.create_one_transfer() + + def setUp(self): + super(TestTransferList, self).setUp() + + self.transfer_mock.list.return_value = [self.volume_transfers] + + # Get the command object to test + self.cmd = volume_transfer_request.ListTransferRequests(self.app, None) + + def test_transfer_list_without_argument(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + expected_columns = [ + 'ID', + 'Volume', + 'Name', + ] + + # confirming if all expected columns are present in the result. + self.assertEqual(expected_columns, columns) + + datalist = (( + self.volume_transfers.id, + self.volume_transfers.volume_id, + self.volume_transfers.name, + ), ) + + # confirming if all expected values are present in the result. + self.assertEqual(datalist, tuple(data)) + + # checking if proper call was made to list volume_transfers + self.transfer_mock.list.assert_called_with( + detailed=True, + search_opts={'all_tenants': 0} + ) + + def test_transfer_list_with_argument(self): + arglist = [ + "--all-projects" + ] + verifylist = [ + ("all_projects", True) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + expected_columns = [ + 'ID', + 'Volume', + 'Name', + ] + + # confirming if all expected columns are present in the result. + self.assertEqual(expected_columns, columns) + + datalist = (( + self.volume_transfers.id, + self.volume_transfers.volume_id, + self.volume_transfers.name, + ), ) + + # confirming if all expected values are present in the result. + self.assertEqual(datalist, tuple(data)) + + # checking if proper call was made to list volume_transfers + self.transfer_mock.list.assert_called_with( + detailed=True, + search_opts={'all_tenants': 1} + ) diff --git a/openstackclient/tests/unit/volume/v1/test_volume.py b/openstackclient/tests/unit/volume/v1/test_volume.py new file mode 100644 index 00000000..f90566fd --- /dev/null +++ b/openstackclient/tests/unit/volume/v1/test_volume.py @@ -0,0 +1,716 @@ +# Copyright 2013 Nebula 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 copy +import mock + +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes +from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes +from openstackclient.volume.v1 import volume + + +class TestVolume(volume_fakes.TestVolumev1): + + def setUp(self): + super(TestVolume, self).setUp() + + # Get a shortcut to the VolumeManager Mock + self.volumes_mock = self.app.client_manager.volume.volumes + self.volumes_mock.reset_mock() + + # Get a shortcut to the TenantManager Mock + self.projects_mock = self.app.client_manager.identity.tenants + self.projects_mock.reset_mock() + + # Get a shortcut to the UserManager Mock + self.users_mock = self.app.client_manager.identity.users + self.users_mock.reset_mock() + + # Get a shortcut to the ImageManager Mock + self.images_mock = self.app.client_manager.image.images + self.images_mock.reset_mock() + + +# TODO(dtroyer): The volume create tests are incomplete, only the minimal +# options and the options that require additional processing +# are implemented at this time. + +class TestVolumeCreate(TestVolume): + + project = identity_fakes.FakeProject.create_one_project() + user = identity_fakes.FakeUser.create_one_user() + + columns = ( + 'attach_status', + 'availability_zone', + 'display_description', + 'display_name', + 'id', + 'properties', + 'size', + 'status', + 'type', + ) + datalist = ( + 'detached', + volume_fakes.volume_zone, + volume_fakes.volume_description, + volume_fakes.volume_name, + volume_fakes.volume_id, + volume_fakes.volume_metadata_str, + volume_fakes.volume_size, + volume_fakes.volume_status, + volume_fakes.volume_type, + ) + + def setUp(self): + super(TestVolumeCreate, self).setUp() + + self.volumes_mock.create.return_value = fakes.FakeResource( + None, + copy.deepcopy(volume_fakes.VOLUME), + loaded=True, + ) + + # Get the command object to test + self.cmd = volume.CreateVolume(self.app, None) + + def test_volume_create_min_options(self): + arglist = [ + '--size', str(volume_fakes.volume_size), + volume_fakes.volume_name, + ] + verifylist = [ + ('size', volume_fakes.volume_size), + ('name', volume_fakes.volume_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # VolumeManager.create(size, snapshot_id=, source_volid=, + # display_name=, display_description=, + # volume_type=, user_id=, + # project_id=, availability_zone=, + # metadata=, imageRef=) + self.volumes_mock.create.assert_called_with( + volume_fakes.volume_size, + None, + None, + volume_fakes.volume_name, + None, + None, + None, + None, + None, + None, + None, + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_volume_create_options(self): + arglist = [ + '--size', str(volume_fakes.volume_size), + '--description', volume_fakes.volume_description, + '--type', volume_fakes.volume_type, + '--availability-zone', volume_fakes.volume_zone, + volume_fakes.volume_name, + ] + verifylist = [ + ('size', volume_fakes.volume_size), + ('description', volume_fakes.volume_description), + ('type', volume_fakes.volume_type), + ('availability_zone', volume_fakes.volume_zone), + ('name', volume_fakes.volume_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # VolumeManager.create(size, snapshot_id=, source_volid=, + # display_name=, display_description=, + # volume_type=, user_id=, + # project_id=, availability_zone=, + # metadata=, imageRef=) + self.volumes_mock.create.assert_called_with( + volume_fakes.volume_size, + None, + None, + volume_fakes.volume_name, + volume_fakes.volume_description, + volume_fakes.volume_type, + None, + None, + volume_fakes.volume_zone, + None, + None, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_volume_create_user_project_id(self): + # Return a project + self.projects_mock.get.return_value = self.project + # Return a user + self.users_mock.get.return_value = self.user + + arglist = [ + '--size', str(volume_fakes.volume_size), + '--project', self.project.id, + '--user', self.user.id, + volume_fakes.volume_name, + ] + verifylist = [ + ('size', volume_fakes.volume_size), + ('project', self.project.id), + ('user', self.user.id), + ('name', volume_fakes.volume_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # VolumeManager.create(size, snapshot_id=, source_volid=, + # display_name=, display_description=, + # volume_type=, user_id=, + # project_id=, availability_zone=, + # metadata=, imageRef=) + self.volumes_mock.create.assert_called_with( + volume_fakes.volume_size, + None, + None, + volume_fakes.volume_name, + None, + None, + self.user.id, + self.project.id, + None, + None, + None, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_volume_create_user_project_name(self): + # Return a project + self.projects_mock.get.return_value = self.project + # Return a user + self.users_mock.get.return_value = self.user + + arglist = [ + '--size', str(volume_fakes.volume_size), + '--project', self.project.name, + '--user', self.user.name, + volume_fakes.volume_name, + ] + verifylist = [ + ('size', volume_fakes.volume_size), + ('project', self.project.name), + ('user', self.user.name), + ('name', volume_fakes.volume_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # VolumeManager.create(size, snapshot_id=, source_volid=, + # display_name=, display_description=, + # volume_type=, user_id=, + # project_id=, availability_zone=, + # metadata=, imageRef=) + self.volumes_mock.create.assert_called_with( + volume_fakes.volume_size, + None, + None, + volume_fakes.volume_name, + None, + None, + self.user.id, + self.project.id, + None, + None, + None, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_volume_create_properties(self): + arglist = [ + '--property', 'Alpha=a', + '--property', 'Beta=b', + '--size', str(volume_fakes.volume_size), + volume_fakes.volume_name, + ] + verifylist = [ + ('property', {'Alpha': 'a', 'Beta': 'b'}), + ('size', volume_fakes.volume_size), + ('name', volume_fakes.volume_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # VolumeManager.create(size, snapshot_id=, source_volid=, + # display_name=, display_description=, + # volume_type=, user_id=, + # project_id=, availability_zone=, + # metadata=, imageRef=) + self.volumes_mock.create.assert_called_with( + volume_fakes.volume_size, + None, + None, + volume_fakes.volume_name, + None, + None, + None, + None, + None, + {'Alpha': 'a', 'Beta': 'b'}, + None, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_volume_create_image_id(self): + self.images_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(volume_fakes.IMAGE), + loaded=True, + ) + + arglist = [ + '--image', volume_fakes.image_id, + '--size', str(volume_fakes.volume_size), + volume_fakes.volume_name, + ] + verifylist = [ + ('image', volume_fakes.image_id), + ('size', volume_fakes.volume_size), + ('name', volume_fakes.volume_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # VolumeManager.create(size, snapshot_id=, source_volid=, + # display_name=, display_description=, + # volume_type=, user_id=, + # project_id=, availability_zone=, + # metadata=, imageRef=) + self.volumes_mock.create.assert_called_with( + volume_fakes.volume_size, + None, + None, + volume_fakes.volume_name, + None, + None, + None, + None, + None, + None, + volume_fakes.image_id, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_volume_create_image_name(self): + self.images_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(volume_fakes.IMAGE), + loaded=True, + ) + + arglist = [ + '--image', volume_fakes.image_name, + '--size', str(volume_fakes.volume_size), + volume_fakes.volume_name, + ] + verifylist = [ + ('image', volume_fakes.image_name), + ('size', volume_fakes.volume_size), + ('name', volume_fakes.volume_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # VolumeManager.create(size, snapshot_id=, source_volid=, + # display_name=, display_description=, + # volume_type=, user_id=, + # project_id=, availability_zone=, + # metadata=, imageRef=) + self.volumes_mock.create.assert_called_with( + volume_fakes.volume_size, + None, + None, + volume_fakes.volume_name, + None, + None, + None, + None, + None, + None, + volume_fakes.image_id, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + +class TestVolumeList(TestVolume): + + columns = ( + 'ID', + 'Display Name', + 'Status', + 'Size', + 'Attached to', + ) + datalist = ( + ( + volume_fakes.volume_id, + volume_fakes.volume_name, + volume_fakes.volume_status, + volume_fakes.volume_size, + '', + ), + ) + + def setUp(self): + super(TestVolumeList, self).setUp() + + self.volumes_mock.list.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy(volume_fakes.VOLUME), + loaded=True, + ), + ] + + # Get the command object to test + self.cmd = volume.ListVolume(self.app, None) + + def test_volume_list_no_options(self): + arglist = [] + verifylist = [ + ('long', False), + ('all_projects', False), + ('name', None), + ('status', None), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_volume_list_name(self): + arglist = [ + '--name', volume_fakes.volume_name, + ] + verifylist = [ + ('long', False), + ('all_projects', False), + ('name', volume_fakes.volume_name), + ('status', None), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.assertEqual(self.columns, tuple(columns)) + self.assertEqual(self.datalist, tuple(data)) + + def test_volume_list_status(self): + arglist = [ + '--status', volume_fakes.volume_status, + ] + verifylist = [ + ('long', False), + ('all_projects', False), + ('name', None), + ('status', volume_fakes.volume_status), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.assertEqual(self.columns, tuple(columns)) + self.assertEqual(self.datalist, tuple(data)) + + def test_volume_list_all_projects(self): + arglist = [ + '--all-projects', + ] + verifylist = [ + ('long', False), + ('all_projects', True), + ('name', None), + ('status', None), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.assertEqual(self.columns, tuple(columns)) + self.assertEqual(self.datalist, tuple(data)) + + def test_volume_list_long(self): + arglist = [ + '--long', + ] + verifylist = [ + ('long', True), + ('all_projects', False), + ('name', None), + ('status', None), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + collist = ( + 'ID', + 'Display Name', + 'Status', + 'Size', + 'Type', + 'Bootable', + 'Attached to', + 'Properties', + ) + self.assertEqual(collist, columns) + + datalist = (( + volume_fakes.volume_id, + volume_fakes.volume_name, + volume_fakes.volume_status, + volume_fakes.volume_size, + volume_fakes.volume_type, + '', + '', + "Alpha='a', Beta='b', Gamma='g'", + ), ) + self.assertEqual(datalist, tuple(data)) + + +class TestVolumeSet(TestVolume): + + def setUp(self): + super(TestVolumeSet, self).setUp() + + self.volumes_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(volume_fakes.VOLUME), + loaded=True, + ) + + self.volumes_mock.update.return_value = fakes.FakeResource( + None, + copy.deepcopy(volume_fakes.VOLUME), + loaded=True, + ) + # Get the command object to test + self.cmd = volume.SetVolume(self.app, None) + + def test_volume_set_no_options(self): + arglist = [ + volume_fakes.volume_name, + ] + verifylist = [ + ('name', None), + ('description', None), + ('size', None), + ('property', None), + ('volume', volume_fakes.volume_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) + + def test_volume_set_name(self): + arglist = [ + '--name', 'qwerty', + volume_fakes.volume_name, + ] + verifylist = [ + ('name', 'qwerty'), + ('description', None), + ('size', None), + ('property', None), + ('volume', volume_fakes.volume_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'display_name': 'qwerty', + } + self.volumes_mock.update.assert_called_with( + volume_fakes.volume_id, + **kwargs + ) + self.assertIsNone(result) + + def test_volume_set_description(self): + arglist = [ + '--description', 'new desc', + volume_fakes.volume_name, + ] + verifylist = [ + ('name', None), + ('description', 'new desc'), + ('size', None), + ('property', None), + ('volume', volume_fakes.volume_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'display_description': 'new desc', + } + self.volumes_mock.update.assert_called_with( + volume_fakes.volume_id, + **kwargs + ) + self.assertIsNone(result) + + def test_volume_set_size(self): + arglist = [ + '--size', '130', + volume_fakes.volume_name, + ] + verifylist = [ + ('name', None), + ('description', None), + ('size', 130), + ('property', None), + ('volume', volume_fakes.volume_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + size = 130 + self.volumes_mock.extend.assert_called_with( + volume_fakes.volume_id, + size + ) + self.assertIsNone(result) + + @mock.patch.object(volume.LOG, 'error') + def test_volume_set_size_smaller(self, mock_log_error): + arglist = [ + '--size', '100', + volume_fakes.volume_name, + ] + verifylist = [ + ('name', None), + ('description', None), + ('size', 100), + ('property', None), + ('volume', volume_fakes.volume_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + mock_log_error.assert_called_with("New size must be greater " + "than %s GB", + volume_fakes.volume_size) + self.assertIsNone(result) + + @mock.patch.object(volume.LOG, 'error') + def test_volume_set_size_not_available(self, mock_log_error): + self.volumes_mock.get.return_value.status = 'error' + arglist = [ + '--size', '130', + volume_fakes.volume_name, + ] + verifylist = [ + ('name', None), + ('description', None), + ('size', 130), + ('property', None), + ('volume', volume_fakes.volume_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + mock_log_error.assert_called_with("Volume is in %s state, it must be " + "available before size can be " + "extended", 'error') + self.assertIsNone(result) + + def test_volume_set_property(self): + arglist = [ + '--property', 'myprop=myvalue', + volume_fakes.volume_name, + ] + verifylist = [ + ('name', None), + ('description', None), + ('size', None), + ('property', {'myprop': 'myvalue'}), + ('volume', volume_fakes.volume_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + metadata = { + 'myprop': 'myvalue' + } + self.volumes_mock.set_metadata.assert_called_with( + volume_fakes.volume_id, + metadata + ) + self.assertIsNone(result) diff --git a/openstackclient/tests/unit/volume/v2/__init__.py b/openstackclient/tests/unit/volume/v2/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/openstackclient/tests/unit/volume/v2/__init__.py diff --git a/openstackclient/tests/unit/volume/v2/fakes.py b/openstackclient/tests/unit/volume/v2/fakes.py new file mode 100644 index 00000000..a958c468 --- /dev/null +++ b/openstackclient/tests/unit/volume/v2/fakes.py @@ -0,0 +1,696 @@ +# +# 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 copy +import mock +import random +import uuid + +from osc_lib import utils as common_utils + +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes +from openstackclient.tests.unit.image.v2 import fakes as image_fakes +from openstackclient.tests.unit import utils + + +class FakeTransfer(object): + """Fake one or more Transfer.""" + + @staticmethod + def create_one_transfer(attrs=None): + """Create a fake transfer. + + :param Dictionary attrs: + A dictionary with all attributes of Transfer Request + :return: + A FakeResource object with volume_id, name, id. + """ + # Set default attribute + transfer_info = { + 'volume_id': 'ce26708d-a7f8-4b4b-9861-4a80256615a7', + 'name': 'fake_transfer_name', + 'id': '731a7f53-aa92-4fbd-9de3-6f7d729c926b' + } + + # Overwrite default attributes if there are some attributes set + attrs = attrs or {} + + transfer_info.update(attrs) + + transfer = fakes.FakeResource( + None, + transfer_info, + loaded=True) + + return transfer + + +class FakeTypeAccess(object): + """Fake one or more volume type access.""" + + @staticmethod + def create_one_type_access(attrs=None): + """Create a fake volume type access for project. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with Volume_type_ID and Project_ID. + """ + if attrs is None: + attrs = {} + + # Set default attributes. + type_access_attrs = { + 'volume_type_id': 'volume-type-id-' + uuid.uuid4().hex, + 'project_id': 'project-id-' + uuid.uuid4().hex, + } + + # Overwrite default attributes. + type_access_attrs.update(attrs) + + type_access = fakes.FakeResource( + None, + type_access_attrs, + loaded=True) + + return type_access + + +class FakeService(object): + """Fake one or more Services.""" + + @staticmethod + def create_one_service(attrs=None): + """Create a fake service. + + :param Dictionary attrs: + A dictionary with all attributes of service + :return: + A FakeResource object with host, status, etc. + """ + # Set default attribute + service_info = { + 'host': 'host_test', + 'binary': 'cinder_test', + 'status': 'enabled', + 'disabled_reason': 'LongHoliday-GoldenWeek', + 'zone': 'fake_zone', + 'updated_at': 'fake_date', + 'state': 'fake_state', + } + + # Overwrite default attributes if there are some attributes set + attrs = attrs or {} + + service_info.update(attrs) + + service = fakes.FakeResource( + None, + service_info, + loaded=True) + + return service + + @staticmethod + def create_services(attrs=None, count=2): + """Create multiple fake services. + + :param Dictionary attrs: + A dictionary with all attributes of service + :param Integer count: + The number of services to be faked + :return: + A list of FakeResource objects + """ + services = [] + for n in range(0, count): + services.append(FakeService.create_one_service(attrs)) + + return services + + +class FakeVolumeClient(object): + + def __init__(self, **kwargs): + self.volumes = mock.Mock() + self.volumes.resource_class = fakes.FakeResource(None, {}) + self.extensions = mock.Mock() + self.extensions.resource_class = fakes.FakeResource(None, {}) + self.volume_snapshots = mock.Mock() + self.volume_snapshots.resource_class = fakes.FakeResource(None, {}) + self.backups = mock.Mock() + self.backups.resource_class = fakes.FakeResource(None, {}) + self.volume_types = mock.Mock() + self.volume_types.resource_class = fakes.FakeResource(None, {}) + self.volume_type_access = mock.Mock() + self.volume_type_access.resource_class = fakes.FakeResource(None, {}) + self.restores = mock.Mock() + self.restores.resource_class = fakes.FakeResource(None, {}) + self.qos_specs = mock.Mock() + self.qos_specs.resource_class = fakes.FakeResource(None, {}) + self.availability_zones = mock.Mock() + self.availability_zones.resource_class = fakes.FakeResource(None, {}) + self.transfers = mock.Mock() + self.transfers.resource_class = fakes.FakeResource(None, {}) + self.services = mock.Mock() + self.services.resource_class = fakes.FakeResource(None, {}) + self.auth_token = kwargs['token'] + self.management_url = kwargs['endpoint'] + + +class TestVolume(utils.TestCommand): + + def setUp(self): + super(TestVolume, self).setUp() + + self.app.client_manager.volume = FakeVolumeClient( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN + ) + self.app.client_manager.identity = identity_fakes.FakeIdentityv3Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN + ) + self.app.client_manager.image = image_fakes.FakeImagev2Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN + ) + + +class FakeVolume(object): + """Fake one or more volumes. + + TODO(xiexs): Currently, only volume API v2 is supported by this class. + """ + + @staticmethod + def create_one_volume(attrs=None): + """Create a fake volume. + + :param Dictionary attrs: + A dictionary with all attributes of volume + :return: + A FakeResource object with id, name, status, etc. + """ + attrs = attrs or {} + + # Set default attribute + volume_info = { + 'id': 'volume-id' + uuid.uuid4().hex, + 'name': 'volume-name' + uuid.uuid4().hex, + 'description': 'description' + uuid.uuid4().hex, + 'status': random.choice(['available', 'in_use']), + 'size': random.randint(1, 20), + 'volume_type': + random.choice(['fake_lvmdriver-1', 'fake_lvmdriver-2']), + 'bootable': + random.randint(0, 1), + 'metadata': { + 'key' + uuid.uuid4().hex: 'val' + uuid.uuid4().hex, + 'key' + uuid.uuid4().hex: 'val' + uuid.uuid4().hex, + 'key' + uuid.uuid4().hex: 'val' + uuid.uuid4().hex}, + 'snapshot_id': random.randint(1, 5), + 'availability_zone': 'zone' + uuid.uuid4().hex, + 'attachments': [{ + 'device': '/dev/' + uuid.uuid4().hex, + 'server_id': uuid.uuid4().hex, + }, ], + } + + # Overwrite default attributes if there are some attributes set + volume_info.update(attrs) + + volume = fakes.FakeResource( + None, + volume_info, + loaded=True) + return volume + + @staticmethod + def create_volumes(attrs=None, count=2): + """Create multiple fake volumes. + + :param Dictionary attrs: + A dictionary with all attributes of volume + :param Integer count: + The number of volumes to be faked + :return: + A list of FakeResource objects + """ + volumes = [] + for n in range(0, count): + volumes.append(FakeVolume.create_one_volume(attrs)) + + return volumes + + @staticmethod + def get_volumes(volumes=None, count=2): + """Get an iterable MagicMock object with a list of faked volumes. + + If volumes list is provided, then initialize the Mock object with the + list. Otherwise create one. + + :param List volumes: + A list of FakeResource objects faking volumes + :param Integer count: + The number of volumes to be faked + :return + An iterable Mock object with side_effect set to a list of faked + volumes + """ + if volumes is None: + volumes = FakeVolume.create_volumes(count) + + return mock.MagicMock(side_effect=volumes) + + @staticmethod + def get_volume_columns(volume=None): + """Get the volume columns from a faked volume object. + + :param volume: + A FakeResource objects faking volume + :return + A tuple which may include the following keys: + ('id', 'name', 'description', 'status', 'size', 'volume_type', + 'metadata', 'snapshot', 'availability_zone', 'attachments') + """ + if volume is not None: + return tuple(k for k in sorted(volume.keys())) + return tuple([]) + + @staticmethod + def get_volume_data(volume=None): + """Get the volume data from a faked volume object. + + :param volume: + A FakeResource objects faking volume + :return + A tuple which may include the following values: + ('ce26708d', 'fake_volume', 'fake description', 'available', + 20, 'fake_lvmdriver-1', "Alpha='a', Beta='b', Gamma='g'", + 1, 'nova', [{'device': '/dev/ice', 'server_id': '1233'}]) + """ + data_list = [] + if volume is not None: + for x in sorted(volume.keys()): + if x == 'tags': + # The 'tags' should be format_list + data_list.append( + common_utils.format_list(volume.info.get(x))) + else: + data_list.append(volume.info.get(x)) + return tuple(data_list) + + +class FakeAvailabilityZone(object): + """Fake one or more volume availability zones (AZs).""" + + @staticmethod + def create_one_availability_zone(attrs=None): + """Create a fake AZ. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object with zoneName, zoneState, etc. + """ + attrs = attrs or {} + + # Set default attributes. + availability_zone = { + 'zoneName': uuid.uuid4().hex, + 'zoneState': {'available': True}, + } + + # Overwrite default attributes. + availability_zone.update(attrs) + + availability_zone = fakes.FakeResource( + info=copy.deepcopy(availability_zone), + loaded=True) + return availability_zone + + @staticmethod + def create_availability_zones(attrs=None, count=2): + """Create multiple fake AZs. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of AZs to fake + :return: + A list of FakeResource objects faking the AZs + """ + availability_zones = [] + for i in range(0, count): + availability_zone = \ + FakeAvailabilityZone.create_one_availability_zone(attrs) + availability_zones.append(availability_zone) + + return availability_zones + + +class FakeBackup(object): + """Fake one or more backup.""" + + @staticmethod + def create_one_backup(attrs=None): + """Create a fake backup. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object with id, name, volume_id, etc. + """ + attrs = attrs or {} + + # Set default attributes. + backup_info = { + "id": 'backup-id-' + uuid.uuid4().hex, + "name": 'backup-name-' + uuid.uuid4().hex, + "volume_id": 'volume-id-' + uuid.uuid4().hex, + "snapshot_id": 'snapshot-id' + uuid.uuid4().hex, + "description": 'description-' + uuid.uuid4().hex, + "object_count": None, + "container": 'container-' + uuid.uuid4().hex, + "size": random.randint(1, 20), + "status": "error", + "availability_zone": 'zone' + uuid.uuid4().hex, + } + + # Overwrite default attributes. + backup_info.update(attrs) + + backup = fakes.FakeResource( + info=copy.deepcopy(backup_info), + loaded=True) + return backup + + @staticmethod + def create_backups(attrs=None, count=2): + """Create multiple fake backups. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of backups to fake + :return: + A list of FakeResource objects faking the backups + """ + backups = [] + for i in range(0, count): + backup = FakeBackup.create_one_backup(attrs) + backups.append(backup) + + return backups + + @staticmethod + def get_backups(backups=None, count=2): + """Get an iterable MagicMock object with a list of faked backups. + + If backups list is provided, then initialize the Mock object with the + list. Otherwise create one. + + :param List volumes: + A list of FakeResource objects faking backups + :param Integer count: + The number of backups to be faked + :return + An iterable Mock object with side_effect set to a list of faked + backups + """ + if backups is None: + backups = FakeBackup.create_backups(count) + + return mock.MagicMock(side_effect=backups) + + +class FakeExtension(object): + """Fake one or more extension.""" + + @staticmethod + def create_one_extension(attrs=None): + """Create a fake extension. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object with name, namespace, etc. + """ + attrs = attrs or {} + + # Set default attributes. + extension_info = { + 'name': 'name-' + uuid.uuid4().hex, + 'namespace': ('http://docs.openstack.org/' + 'block-service/ext/scheduler-hints/api/v2'), + 'description': 'description-' + uuid.uuid4().hex, + 'updated': '2013-04-18T00:00:00+00:00', + 'alias': 'OS-SCH-HNT', + 'links': ('[{"href":' + '"https://github.com/openstack/block-api", "type":' + ' "text/html", "rel": "describedby"}]'), + } + + # Overwrite default attributes. + extension_info.update(attrs) + + extension = fakes.FakeResource( + info=copy.deepcopy(extension_info), + loaded=True) + return extension + + +class FakeQos(object): + """Fake one or more Qos specification.""" + + @staticmethod + def create_one_qos(attrs=None): + """Create a fake Qos specification. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object with id, name, consumer, etc. + """ + attrs = attrs or {} + + # Set default attributes. + qos_info = { + "id": 'qos-id-' + uuid.uuid4().hex, + "name": 'qos-name-' + uuid.uuid4().hex, + "consumer": 'front-end', + "specs": {"foo": "bar", "iops": "9001"}, + } + + # Overwrite default attributes. + qos_info.update(attrs) + + qos = fakes.FakeResource( + info=copy.deepcopy(qos_info), + loaded=True) + return qos + + @staticmethod + def create_one_qos_association(attrs=None): + """Create a fake Qos specification association. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object with id, name, association_type, etc. + """ + attrs = attrs or {} + + # Set default attributes. + qos_association_info = { + "id": 'type-id-' + uuid.uuid4().hex, + "name": 'type-name-' + uuid.uuid4().hex, + "association_type": 'volume_type', + } + + # Overwrite default attributes. + qos_association_info.update(attrs) + + qos_association = fakes.FakeResource( + info=copy.deepcopy(qos_association_info), + loaded=True) + return qos_association + + @staticmethod + def create_qoses(attrs=None, count=2): + """Create multiple fake Qos specifications. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of Qos specifications to fake + :return: + A list of FakeResource objects faking the Qos specifications + """ + qoses = [] + for i in range(0, count): + qos = FakeQos.create_one_qos(attrs) + qoses.append(qos) + + return qoses + + @staticmethod + def get_qoses(qoses=None, count=2): + """Get an iterable MagicMock object with a list of faked qoses. + + If qoses list is provided, then initialize the Mock object with the + list. Otherwise create one. + + :param List volumes: + A list of FakeResource objects faking qoses + :param Integer count: + The number of qoses to be faked + :return + An iterable Mock object with side_effect set to a list of faked + qoses + """ + if qoses is None: + qoses = FakeQos.create_qoses(count) + + return mock.MagicMock(side_effect=qoses) + + +class FakeSnapshot(object): + """Fake one or more snapshot.""" + + @staticmethod + def create_one_snapshot(attrs=None): + """Create a fake snapshot. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object with id, name, description, etc. + """ + attrs = attrs or {} + + # Set default attributes. + snapshot_info = { + "id": 'snapshot-id-' + uuid.uuid4().hex, + "name": 'snapshot-name-' + uuid.uuid4().hex, + "description": 'snapshot-description-' + uuid.uuid4().hex, + "size": 10, + "status": "available", + "metadata": {"foo": "bar"}, + "created_at": "2015-06-03T18:49:19.000000", + "volume_id": 'vloume-id-' + uuid.uuid4().hex, + } + + # Overwrite default attributes. + snapshot_info.update(attrs) + + snapshot = fakes.FakeResource( + info=copy.deepcopy(snapshot_info), + loaded=True) + return snapshot + + @staticmethod + def create_snapshots(attrs=None, count=2): + """Create multiple fake snapshots. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of snapshots to fake + :return: + A list of FakeResource objects faking the snapshots + """ + snapshots = [] + for i in range(0, count): + snapshot = FakeSnapshot.create_one_snapshot(attrs) + snapshots.append(snapshot) + + return snapshots + + @staticmethod + def get_snapshots(snapshots=None, count=2): + """Get an iterable MagicMock object with a list of faked snapshots. + + If snapshots list is provided, then initialize the Mock object with the + list. Otherwise create one. + + :param List volumes: + A list of FakeResource objects faking snapshots + :param Integer count: + The number of snapshots to be faked + :return + An iterable Mock object with side_effect set to a list of faked + snapshots + """ + if snapshots is None: + snapshots = FakeSnapshot.create_snapshots(count) + + return mock.MagicMock(side_effect=snapshots) + + +class FakeType(object): + """Fake one or more type.""" + + @staticmethod + def create_one_type(attrs=None, methods=None): + """Create a fake type. + + :param Dictionary attrs: + A dictionary with all attributes + :param Dictionary methods: + A dictionary with all methods + :return: + A FakeResource object with id, name, description, etc. + """ + attrs = attrs or {} + methods = methods or {} + + # Set default attributes. + type_info = { + "id": 'type-id-' + uuid.uuid4().hex, + "name": 'type-name-' + uuid.uuid4().hex, + "description": 'type-description-' + uuid.uuid4().hex, + "extra_specs": {"foo": "bar"}, + "is_public": True, + } + + # Overwrite default attributes. + type_info.update(attrs) + + volume_type = fakes.FakeResource( + info=copy.deepcopy(type_info), + methods=methods, + loaded=True) + return volume_type + + @staticmethod + def create_types(attrs=None, count=2): + """Create multiple fake types. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of types to fake + :return: + A list of FakeResource objects faking the types + """ + volume_types = [] + for i in range(0, count): + volume_type = FakeType.create_one_type(attrs) + volume_types.append(volume_type) + + return volume_types diff --git a/openstackclient/tests/unit/volume/v2/test_backup.py b/openstackclient/tests/unit/volume/v2/test_backup.py new file mode 100644 index 00000000..45633870 --- /dev/null +++ b/openstackclient/tests/unit/volume/v2/test_backup.py @@ -0,0 +1,388 @@ +# +# 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 mock import call + +from osc_lib import exceptions +from osc_lib import utils + +from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes +from openstackclient.volume.v2 import backup + + +class TestBackup(volume_fakes.TestVolume): + + def setUp(self): + super(TestBackup, self).setUp() + + self.backups_mock = self.app.client_manager.volume.backups + self.backups_mock.reset_mock() + self.volumes_mock = self.app.client_manager.volume.volumes + self.volumes_mock.reset_mock() + self.snapshots_mock = self.app.client_manager.volume.volume_snapshots + self.snapshots_mock.reset_mock() + self.restores_mock = self.app.client_manager.volume.restores + self.restores_mock.reset_mock() + + +class TestBackupCreate(TestBackup): + + volume = volume_fakes.FakeVolume.create_one_volume() + snapshot = volume_fakes.FakeSnapshot.create_one_snapshot() + new_backup = volume_fakes.FakeBackup.create_one_backup( + attrs={'volume_id': volume.id, 'snapshot_id': snapshot.id}) + + columns = ( + 'availability_zone', + 'container', + 'description', + 'id', + 'name', + 'object_count', + 'size', + 'snapshot_id', + 'status', + 'volume_id', + ) + data = ( + new_backup.availability_zone, + new_backup.container, + new_backup.description, + new_backup.id, + new_backup.name, + new_backup.object_count, + new_backup.size, + new_backup.snapshot_id, + new_backup.status, + new_backup.volume_id, + ) + + def setUp(self): + super(TestBackupCreate, self).setUp() + + self.volumes_mock.get.return_value = self.volume + self.snapshots_mock.get.return_value = self.snapshot + self.backups_mock.create.return_value = self.new_backup + + # Get the command object to test + self.cmd = backup.CreateVolumeBackup(self.app, None) + + def test_backup_create(self): + arglist = [ + "--name", self.new_backup.name, + "--description", self.new_backup.description, + "--container", self.new_backup.container, + "--force", + "--incremental", + "--snapshot", self.new_backup.snapshot_id, + self.new_backup.volume_id, + ] + verifylist = [ + ("name", self.new_backup.name), + ("description", self.new_backup.description), + ("container", self.new_backup.container), + ("force", True), + ("incremental", True), + ("snapshot", self.new_backup.snapshot_id), + ("volume", self.new_backup.volume_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.backups_mock.create.assert_called_with( + self.new_backup.volume_id, + container=self.new_backup.container, + name=self.new_backup.name, + description=self.new_backup.description, + force=True, + incremental=True, + snapshot_id=self.new_backup.snapshot_id, + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_backup_create_without_name(self): + arglist = [ + "--description", self.new_backup.description, + "--container", self.new_backup.container, + self.new_backup.volume_id, + ] + verifylist = [ + ("description", self.new_backup.description), + ("container", self.new_backup.container), + ("volume", self.new_backup.volume_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.backups_mock.create.assert_called_with( + self.new_backup.volume_id, + container=self.new_backup.container, + name=None, + description=self.new_backup.description, + force=False, + incremental=False, + snapshot_id=None, + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestBackupDelete(TestBackup): + + backups = volume_fakes.FakeBackup.create_backups(count=2) + + def setUp(self): + super(TestBackupDelete, self).setUp() + + self.backups_mock.get = ( + volume_fakes.FakeBackup.get_backups(self.backups)) + self.backups_mock.delete.return_value = None + + # Get the command object to mock + self.cmd = backup.DeleteVolumeBackup(self.app, None) + + def test_backup_delete(self): + arglist = [ + self.backups[0].id + ] + verifylist = [ + ("backups", [self.backups[0].id]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.backups_mock.delete.assert_called_with( + self.backups[0].id, False) + self.assertIsNone(result) + + def test_backup_delete_with_force(self): + arglist = [ + '--force', + self.backups[0].id, + ] + verifylist = [ + ('force', True), + ("backups", [self.backups[0].id]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.backups_mock.delete.assert_called_with(self.backups[0].id, True) + self.assertIsNone(result) + + def test_delete_multiple_backups(self): + arglist = [] + for b in self.backups: + arglist.append(b.id) + verifylist = [ + ('backups', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + calls = [] + for b in self.backups: + calls.append(call(b.id, False)) + self.backups_mock.delete.assert_has_calls(calls) + self.assertIsNone(result) + + def test_delete_multiple_backups_with_exception(self): + arglist = [ + self.backups[0].id, + 'unexist_backup', + ] + verifylist = [ + ('backups', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + find_mock_result = [self.backups[0], exceptions.CommandError] + with mock.patch.object(utils, 'find_resource', + side_effect=find_mock_result) as find_mock: + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 backups failed to delete.', + str(e)) + + find_mock.assert_any_call(self.backups_mock, self.backups[0].id) + find_mock.assert_any_call(self.backups_mock, 'unexist_backup') + + self.assertEqual(2, find_mock.call_count) + self.backups_mock.delete.assert_called_once_with( + self.backups[0].id, False + ) + + +class TestBackupList(TestBackup): + + volume = volume_fakes.FakeVolume.create_one_volume() + backups = volume_fakes.FakeBackup.create_backups( + attrs={'volume_id': volume.name}, count=3) + + columns = [ + 'ID', + 'Name', + 'Description', + 'Status', + 'Size', + ] + columns_long = columns + [ + 'Availability Zone', + 'Volume', + 'Container', + ] + + data = [] + for b in backups: + data.append(( + b.id, + b.name, + b.description, + b.status, + b.size, + )) + data_long = [] + for b in backups: + data_long.append(( + b.id, + b.name, + b.description, + b.status, + b.size, + b.availability_zone, + b.volume_id, + b.container, + )) + + def setUp(self): + super(TestBackupList, self).setUp() + + self.volumes_mock.list.return_value = [self.volume] + self.backups_mock.list.return_value = self.backups + # Get the command to test + self.cmd = backup.ListVolumeBackup(self.app, None) + + def test_backup_list_without_options(self): + arglist = [] + verifylist = [("long", False)] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_backup_list_with_options(self): + arglist = ["--long"] + verifylist = [("long", True)] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns_long, columns) + self.assertEqual(self.data_long, list(data)) + + +class TestBackupRestore(TestBackup): + + volume = volume_fakes.FakeVolume.create_one_volume() + backup = volume_fakes.FakeBackup.create_one_backup( + attrs={'volume_id': volume.id}) + + def setUp(self): + super(TestBackupRestore, self).setUp() + + self.backups_mock.get.return_value = self.backup + self.volumes_mock.get.return_value = self.volume + self.restores_mock.restore.return_value = None + # Get the command object to mock + self.cmd = backup.RestoreVolumeBackup(self.app, None) + + def test_backup_restore(self): + arglist = [ + self.backup.id, + self.backup.volume_id + ] + verifylist = [ + ("backup", self.backup.id), + ("volume", self.backup.volume_id) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.restores_mock.restore.assert_called_with(self.backup.id, + self.backup.volume_id) + self.assertIsNone(result) + + +class TestBackupShow(TestBackup): + + backup = volume_fakes.FakeBackup.create_one_backup() + + columns = ( + 'availability_zone', + 'container', + 'description', + 'id', + 'name', + 'object_count', + 'size', + 'snapshot_id', + 'status', + 'volume_id', + ) + data = ( + backup.availability_zone, + backup.container, + backup.description, + backup.id, + backup.name, + backup.object_count, + backup.size, + backup.snapshot_id, + backup.status, + backup.volume_id, + ) + + def setUp(self): + super(TestBackupShow, self).setUp() + + self.backups_mock.get.return_value = self.backup + # Get the command object to test + self.cmd = backup.ShowVolumeBackup(self.app, None) + + def test_backup_show(self): + arglist = [ + self.backup.id + ] + verifylist = [ + ("backup", self.backup.id) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.backups_mock.get.assert_called_with(self.backup.id) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/volume/v2/test_qos_specs.py b/openstackclient/tests/unit/volume/v2/test_qos_specs.py new file mode 100644 index 00000000..7597e852 --- /dev/null +++ b/openstackclient/tests/unit/volume/v2/test_qos_specs.py @@ -0,0 +1,453 @@ +# Copyright 2015 iWeb Technologies 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 mock +from mock import call + +from osc_lib import exceptions +from osc_lib import utils + +from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes +from openstackclient.volume.v2 import qos_specs + + +class TestQos(volume_fakes.TestVolume): + + def setUp(self): + super(TestQos, self).setUp() + + self.qos_mock = self.app.client_manager.volume.qos_specs + self.qos_mock.reset_mock() + + self.types_mock = self.app.client_manager.volume.volume_types + self.types_mock.reset_mock() + + +class TestQosAssociate(TestQos): + + volume_type = volume_fakes.FakeType.create_one_type() + qos_spec = volume_fakes.FakeQos.create_one_qos() + + def setUp(self): + super(TestQosAssociate, self).setUp() + + self.qos_mock.get.return_value = self.qos_spec + self.types_mock.get.return_value = self.volume_type + # Get the command object to test + self.cmd = qos_specs.AssociateQos(self.app, None) + + def test_qos_associate(self): + arglist = [ + self.qos_spec.id, + self.volume_type.id + ] + verifylist = [ + ('qos_spec', self.qos_spec.id), + ('volume_type', self.volume_type.id) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.qos_mock.associate.assert_called_with( + self.qos_spec.id, + self.volume_type.id + ) + self.assertIsNone(result) + + +class TestQosCreate(TestQos): + + new_qos_spec = volume_fakes.FakeQos.create_one_qos() + columns = ( + 'consumer', + 'id', + 'name', + 'specs' + ) + data = ( + new_qos_spec.consumer, + new_qos_spec.id, + new_qos_spec.name, + new_qos_spec.specs + ) + + def setUp(self): + super(TestQosCreate, self).setUp() + + self.qos_mock.create.return_value = self.new_qos_spec + # Get the command object to test + self.cmd = qos_specs.CreateQos(self.app, None) + + def test_qos_create_without_properties(self): + arglist = [ + self.new_qos_spec.name, + ] + verifylist = [ + ('name', self.new_qos_spec.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.qos_mock.create.assert_called_with( + self.new_qos_spec.name, + {'consumer': 'both'} + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_qos_create_with_consumer(self): + arglist = [ + '--consumer', self.new_qos_spec.consumer, + self.new_qos_spec.name, + ] + verifylist = [ + ('consumer', self.new_qos_spec.consumer), + ('name', self.new_qos_spec.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.qos_mock.create.assert_called_with( + self.new_qos_spec.name, + {'consumer': self.new_qos_spec.consumer} + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_qos_create_with_properties(self): + arglist = [ + '--consumer', self.new_qos_spec.consumer, + '--property', 'foo=bar', + '--property', 'iops=9001', + self.new_qos_spec.name, + ] + verifylist = [ + ('consumer', self.new_qos_spec.consumer), + ('property', self.new_qos_spec.specs), + ('name', self.new_qos_spec.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.new_qos_spec.specs.update( + {'consumer': self.new_qos_spec.consumer}) + self.qos_mock.create.assert_called_with( + self.new_qos_spec.name, + self.new_qos_spec.specs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestQosDelete(TestQos): + + qos_specs = volume_fakes.FakeQos.create_qoses(count=2) + + def setUp(self): + super(TestQosDelete, self).setUp() + + self.qos_mock.get = ( + volume_fakes.FakeQos.get_qoses(self.qos_specs)) + # Get the command object to test + self.cmd = qos_specs.DeleteQos(self.app, None) + + def test_qos_delete(self): + arglist = [ + self.qos_specs[0].id + ] + verifylist = [ + ('qos_specs', [self.qos_specs[0].id]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.qos_mock.delete.assert_called_with( + self.qos_specs[0].id, False) + self.assertIsNone(result) + + def test_qos_delete_with_force(self): + arglist = [ + '--force', + self.qos_specs[0].id + ] + verifylist = [ + ('force', True), + ('qos_specs', [self.qos_specs[0].id]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.qos_mock.delete.assert_called_with( + self.qos_specs[0].id, True) + self.assertIsNone(result) + + def test_delete_multiple_qoses(self): + arglist = [] + for q in self.qos_specs: + arglist.append(q.id) + verifylist = [ + ('qos_specs', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + calls = [] + for q in self.qos_specs: + calls.append(call(q.id, False)) + self.qos_mock.delete.assert_has_calls(calls) + self.assertIsNone(result) + + def test_delete_multiple_qoses_with_exception(self): + arglist = [ + self.qos_specs[0].id, + 'unexist_qos', + ] + verifylist = [ + ('qos_specs', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + find_mock_result = [self.qos_specs[0], exceptions.CommandError] + with mock.patch.object(utils, 'find_resource', + side_effect=find_mock_result) as find_mock: + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual( + '1 of 2 QoS specifications failed to delete.', str(e)) + + find_mock.assert_any_call(self.qos_mock, self.qos_specs[0].id) + find_mock.assert_any_call(self.qos_mock, 'unexist_qos') + + self.assertEqual(2, find_mock.call_count) + self.qos_mock.delete.assert_called_once_with( + self.qos_specs[0].id, False + ) + + +class TestQosDisassociate(TestQos): + + volume_type = volume_fakes.FakeType.create_one_type() + qos_spec = volume_fakes.FakeQos.create_one_qos() + + def setUp(self): + super(TestQosDisassociate, self).setUp() + + self.qos_mock.get.return_value = self.qos_spec + self.types_mock.get.return_value = self.volume_type + # Get the command object to test + self.cmd = qos_specs.DisassociateQos(self.app, None) + + def test_qos_disassociate_with_volume_type(self): + arglist = [ + '--volume-type', self.volume_type.id, + self.qos_spec.id, + ] + verifylist = [ + ('volume_type', self.volume_type.id), + ('qos_spec', self.qos_spec.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.qos_mock.disassociate.assert_called_with( + self.qos_spec.id, + self.volume_type.id + ) + self.assertIsNone(result) + + def test_qos_disassociate_with_all_volume_types(self): + arglist = [ + '--all', + self.qos_spec.id, + ] + verifylist = [ + ('qos_spec', self.qos_spec.id) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.qos_mock.disassociate_all.assert_called_with(self.qos_spec.id) + self.assertIsNone(result) + + +class TestQosList(TestQos): + + qos_specs = volume_fakes.FakeQos.create_qoses(count=2) + qos_association = volume_fakes.FakeQos.create_one_qos_association() + + columns = ( + 'ID', + 'Name', + 'Consumer', + 'Associations', + 'Specs', + ) + data = [] + for q in qos_specs: + data.append(( + q.id, + q.name, + q.consumer, + qos_association.name, + utils.format_dict(q.specs), + )) + + def setUp(self): + super(TestQosList, self).setUp() + + self.qos_mock.list.return_value = self.qos_specs + self.qos_mock.get_associations.return_value = [self.qos_association] + + # Get the command object to test + self.cmd = qos_specs.ListQos(self.app, None) + + def test_qos_list(self): + arglist = [] + verifylist = [] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.qos_mock.list.assert_called_with() + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + +class TestQosSet(TestQos): + + qos_spec = volume_fakes.FakeQos.create_one_qos() + + def setUp(self): + super(TestQosSet, self).setUp() + + self.qos_mock.get.return_value = self.qos_spec + # Get the command object to test + self.cmd = qos_specs.SetQos(self.app, None) + + def test_qos_set_with_properties_with_id(self): + arglist = [ + '--property', 'foo=bar', + '--property', 'iops=9001', + self.qos_spec.id, + ] + verifylist = [ + ('property', self.qos_spec.specs), + ('qos_spec', self.qos_spec.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.qos_mock.set_keys.assert_called_with( + self.qos_spec.id, + self.qos_spec.specs + ) + self.assertIsNone(result) + + +class TestQosShow(TestQos): + + qos_spec = volume_fakes.FakeQos.create_one_qos() + qos_association = volume_fakes.FakeQos.create_one_qos_association() + + columns = ( + 'associations', + 'consumer', + 'id', + 'name', + 'specs' + ) + data = ( + qos_association.name, + qos_spec.consumer, + qos_spec.id, + qos_spec.name, + utils.format_dict(qos_spec.specs), + ) + + def setUp(self): + super(TestQosShow, self).setUp() + + self.qos_mock.get.return_value = self.qos_spec + self.qos_mock.get_associations.return_value = [self.qos_association] + + # Get the command object to test + self.cmd = qos_specs.ShowQos(self.app, None) + + def test_qos_show(self): + arglist = [ + self.qos_spec.id + ] + verifylist = [ + ('qos_spec', self.qos_spec.id) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.qos_mock.get.assert_called_with( + self.qos_spec.id + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, tuple(data)) + + +class TestQosUnset(TestQos): + + qos_spec = volume_fakes.FakeQos.create_one_qos() + + def setUp(self): + super(TestQosUnset, self).setUp() + + self.qos_mock.get.return_value = self.qos_spec + # Get the command object to test + self.cmd = qos_specs.UnsetQos(self.app, None) + + def test_qos_unset_with_properties(self): + arglist = [ + '--property', 'iops', + '--property', 'foo', + self.qos_spec.id, + ] + verifylist = [ + ('property', ['iops', 'foo']), + ('qos_spec', self.qos_spec.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.qos_mock.unset_keys.assert_called_with( + self.qos_spec.id, + ['iops', 'foo'] + ) + self.assertIsNone(result) diff --git a/openstackclient/tests/unit/volume/v2/test_service.py b/openstackclient/tests/unit/volume/v2/test_service.py new file mode 100644 index 00000000..3e9b2df9 --- /dev/null +++ b/openstackclient/tests/unit/volume/v2/test_service.py @@ -0,0 +1,286 @@ +# +# 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 osc_lib import exceptions + +from openstackclient.tests.unit.volume.v2 import fakes as service_fakes +from openstackclient.volume.v2 import service + + +class TestService(service_fakes.TestVolume): + + def setUp(self): + super(TestService, self).setUp() + + # Get a shortcut to the ServiceManager Mock + self.service_mock = self.app.client_manager.volume.services + self.service_mock.reset_mock() + + +class TestServiceList(TestService): + + # The service to be listed + services = service_fakes.FakeService.create_one_service() + + def setUp(self): + super(TestServiceList, self).setUp() + + self.service_mock.list.return_value = [self.services] + + # Get the command object to test + self.cmd = service.ListService(self.app, None) + + def test_service_list(self): + arglist = [ + '--host', self.services.host, + '--service', self.services.binary, + ] + verifylist = [ + ('host', self.services.host), + ('service', self.services.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + expected_columns = [ + 'Binary', + 'Host', + 'Zone', + 'Status', + 'State', + 'Updated At', + ] + + # confirming if all expected columns are present in the result. + self.assertEqual(expected_columns, columns) + + datalist = (( + self.services.binary, + self.services.host, + self.services.zone, + self.services.status, + self.services.state, + self.services.updated_at, + ), ) + + # confirming if all expected values are present in the result. + self.assertEqual(datalist, tuple(data)) + + # checking if proper call was made to list services + self.service_mock.list.assert_called_with( + self.services.host, + self.services.binary, + ) + + # checking if prohibited columns are present in output + self.assertNotIn("Disabled Reason", columns) + self.assertNotIn(self.services.disabled_reason, + tuple(data)) + + def test_service_list_with_long_option(self): + arglist = [ + '--host', self.services.host, + '--service', self.services.binary, + '--long' + ] + verifylist = [ + ('host', self.services.host), + ('service', self.services.binary), + ('long', True) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + expected_columns = [ + 'Binary', + 'Host', + 'Zone', + 'Status', + 'State', + 'Updated At', + 'Disabled Reason' + ] + + # confirming if all expected columns are present in the result. + self.assertEqual(expected_columns, columns) + + datalist = (( + self.services.binary, + self.services.host, + self.services.zone, + self.services.status, + self.services.state, + self.services.updated_at, + self.services.disabled_reason, + ), ) + + # confirming if all expected values are present in the result. + self.assertEqual(datalist, tuple(data)) + + self.service_mock.list.assert_called_with( + self.services.host, + self.services.binary, + ) + + +class TestServiceSet(TestService): + + service = service_fakes.FakeService.create_one_service() + + def setUp(self): + super(TestServiceSet, self).setUp() + + self.service_mock.enable.return_value = self.service + self.service_mock.disable.return_value = self.service + self.service_mock.disable_log_reason.return_value = self.service + + self.cmd = service.SetService(self.app, None) + + def test_service_set_nothing(self): + arglist = [ + self.service.host, + self.service.binary, + ] + verifylist = [ + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.service_mock.enable.assert_not_called() + self.service_mock.disable.assert_not_called() + self.service_mock.disable_log_reason.assert_not_called() + self.assertIsNone(result) + + def test_service_set_enable(self): + arglist = [ + '--enable', + self.service.host, + self.service.binary, + ] + verifylist = [ + ('enable', True), + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.service_mock.enable.assert_called_with( + self.service.host, + self.service.binary + ) + self.service_mock.disable.assert_not_called() + self.service_mock.disable_log_reason.assert_not_called() + self.assertIsNone(result) + + def test_service_set_disable(self): + arglist = [ + '--disable', + self.service.host, + self.service.binary, + ] + verifylist = [ + ('disable', True), + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.service_mock.disable.assert_called_with( + self.service.host, + self.service.binary + ) + self.service_mock.enable.assert_not_called() + self.service_mock.disable_log_reason.assert_not_called() + self.assertIsNone(result) + + def test_service_set_disable_with_reason(self): + reason = 'earthquake' + arglist = [ + '--disable', + '--disable-reason', reason, + self.service.host, + self.service.binary, + ] + verifylist = [ + ('disable', True), + ('disable_reason', reason), + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.service_mock.disable_log_reason.assert_called_with( + self.service.host, + self.service.binary, + reason + ) + self.assertIsNone(result) + + def test_service_set_only_with_disable_reason(self): + reason = 'earthquake' + arglist = [ + '--disable-reason', reason, + self.service.host, + self.service.binary, + ] + verifylist = [ + ('disable_reason', reason), + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + try: + self.cmd.take_action(parsed_args) + self.fail("CommandError should be raised.") + except exceptions.CommandError as e: + self.assertEqual("Cannot specify option --disable-reason without " + "--disable specified.", str(e)) + + def test_service_set_enable_with_disable_reason(self): + reason = 'earthquake' + arglist = [ + '--enable', + '--disable-reason', reason, + self.service.host, + self.service.binary, + ] + verifylist = [ + ('enable', True), + ('disable_reason', reason), + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + try: + self.cmd.take_action(parsed_args) + self.fail("CommandError should be raised.") + except exceptions.CommandError as e: + self.assertEqual("Cannot specify option --disable-reason without " + "--disable specified.", str(e)) diff --git a/openstackclient/tests/unit/volume/v2/test_snapshot.py b/openstackclient/tests/unit/volume/v2/test_snapshot.py new file mode 100644 index 00000000..333d8d72 --- /dev/null +++ b/openstackclient/tests/unit/volume/v2/test_snapshot.py @@ -0,0 +1,458 @@ +# +# 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 argparse +import mock +from mock import call + +from osc_lib import exceptions +from osc_lib import utils + +from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes +from openstackclient.volume.v2 import snapshot + + +class TestSnapshot(volume_fakes.TestVolume): + + def setUp(self): + super(TestSnapshot, self).setUp() + + self.snapshots_mock = self.app.client_manager.volume.volume_snapshots + self.snapshots_mock.reset_mock() + self.volumes_mock = self.app.client_manager.volume.volumes + self.volumes_mock.reset_mock() + + +class TestSnapshotCreate(TestSnapshot): + + columns = ( + 'created_at', + 'description', + 'id', + 'name', + 'properties', + 'size', + 'status', + 'volume_id', + ) + + def setUp(self): + super(TestSnapshotCreate, self).setUp() + + self.volume = volume_fakes.FakeVolume.create_one_volume() + self.new_snapshot = volume_fakes.FakeSnapshot.create_one_snapshot( + attrs={'volume_id': self.volume.id}) + + self.data = ( + self.new_snapshot.created_at, + self.new_snapshot.description, + self.new_snapshot.id, + self.new_snapshot.name, + utils.format_dict(self.new_snapshot.metadata), + self.new_snapshot.size, + self.new_snapshot.status, + self.new_snapshot.volume_id, + ) + + self.volumes_mock.get.return_value = self.volume + self.snapshots_mock.create.return_value = self.new_snapshot + # Get the command object to test + self.cmd = snapshot.CreateSnapshot(self.app, None) + + def test_snapshot_create(self): + arglist = [ + "--name", self.new_snapshot.name, + "--description", self.new_snapshot.description, + "--force", + '--property', 'Alpha=a', + '--property', 'Beta=b', + self.new_snapshot.volume_id, + ] + verifylist = [ + ("name", self.new_snapshot.name), + ("description", self.new_snapshot.description), + ("force", True), + ('property', {'Alpha': 'a', 'Beta': 'b'}), + ("volume", self.new_snapshot.volume_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.snapshots_mock.create.assert_called_with( + self.new_snapshot.volume_id, + force=True, + name=self.new_snapshot.name, + description=self.new_snapshot.description, + metadata={'Alpha': 'a', 'Beta': 'b'}, + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_snapshot_create_without_name(self): + arglist = [ + self.new_snapshot.volume_id, + "--description", self.new_snapshot.description, + "--force" + ] + verifylist = [ + ("volume", self.new_snapshot.volume_id), + ("description", self.new_snapshot.description), + ("force", True) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.snapshots_mock.create.assert_called_with( + self.new_snapshot.volume_id, + force=True, + name=None, + description=self.new_snapshot.description, + metadata=None, + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestSnapshotDelete(TestSnapshot): + + snapshots = volume_fakes.FakeSnapshot.create_snapshots(count=2) + + def setUp(self): + super(TestSnapshotDelete, self).setUp() + + self.snapshots_mock.get = ( + volume_fakes.FakeSnapshot.get_snapshots(self.snapshots)) + self.snapshots_mock.delete.return_value = None + + # Get the command object to mock + self.cmd = snapshot.DeleteSnapshot(self.app, None) + + def test_snapshot_delete(self): + arglist = [ + self.snapshots[0].id + ] + verifylist = [ + ("snapshots", [self.snapshots[0].id]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.snapshots_mock.delete.assert_called_with( + self.snapshots[0].id) + self.assertIsNone(result) + + def test_delete_multiple_snapshots(self): + arglist = [] + for s in self.snapshots: + arglist.append(s.id) + verifylist = [ + ('snapshots', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + calls = [] + for s in self.snapshots: + calls.append(call(s.id)) + self.snapshots_mock.delete.assert_has_calls(calls) + self.assertIsNone(result) + + def test_delete_multiple_snapshots_with_exception(self): + arglist = [ + self.snapshots[0].id, + 'unexist_snapshot', + ] + verifylist = [ + ('snapshots', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + find_mock_result = [self.snapshots[0], exceptions.CommandError] + with mock.patch.object(utils, 'find_resource', + side_effect=find_mock_result) as find_mock: + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 snapshots failed to delete.', + str(e)) + + find_mock.assert_any_call( + self.snapshots_mock, self.snapshots[0].id) + find_mock.assert_any_call(self.snapshots_mock, 'unexist_snapshot') + + self.assertEqual(2, find_mock.call_count) + self.snapshots_mock.delete.assert_called_once_with( + self.snapshots[0].id + ) + + +class TestSnapshotList(TestSnapshot): + + volume = volume_fakes.FakeVolume.create_one_volume() + snapshots = volume_fakes.FakeSnapshot.create_snapshots( + attrs={'volume_id': volume.name}, count=3) + + columns = [ + "ID", + "Name", + "Description", + "Status", + "Size" + ] + columns_long = columns + [ + "Created At", + "Volume", + "Properties" + ] + + data = [] + for s in snapshots: + data.append(( + s.id, + s.name, + s.description, + s.status, + s.size, + )) + data_long = [] + for s in snapshots: + data_long.append(( + s.id, + s.name, + s.description, + s.status, + s.size, + s.created_at, + s.volume_id, + utils.format_dict(s.metadata), + )) + + def setUp(self): + super(TestSnapshotList, self).setUp() + + self.volumes_mock.list.return_value = [self.volume] + self.snapshots_mock.list.return_value = self.snapshots + # Get the command to test + self.cmd = snapshot.ListSnapshot(self.app, None) + + def test_snapshot_list_without_options(self): + arglist = [] + verifylist = [ + ('all_projects', False), + ("long", False) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.snapshots_mock.list.assert_called_once_with( + limit=None, marker=None, search_opts={'all_tenants': False}) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_snapshot_list_with_options(self): + arglist = [ + "--long", + "--limit", "2", + "--marker", self.snapshots[0].id, + ] + verifylist = [ + ("long", True), + ("limit", 2), + ("marker", self.snapshots[0].id), + ('all_projects', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.snapshots_mock.list.assert_called_once_with( + limit=2, + marker=self.snapshots[0].id, + search_opts={'all_tenants': False} + ) + self.assertEqual(self.columns_long, columns) + self.assertEqual(self.data_long, list(data)) + + def test_snapshot_list_all_projects(self): + arglist = [ + '--all-projects', + ] + verifylist = [ + ('long', False), + ('all_projects', True) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.snapshots_mock.list.assert_called_once_with( + limit=None, marker=None, search_opts={'all_tenants': True}) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_snapshot_list_negative_limit(self): + arglist = [ + "--limit", "-2", + ] + verifylist = [ + ("limit", -2), + ] + self.assertRaises(argparse.ArgumentTypeError, self.check_parser, + self.cmd, arglist, verifylist) + + +class TestSnapshotSet(TestSnapshot): + + snapshot = volume_fakes.FakeSnapshot.create_one_snapshot() + + def setUp(self): + super(TestSnapshotSet, self).setUp() + + self.snapshots_mock.get.return_value = self.snapshot + self.snapshots_mock.set_metadata.return_value = None + self.snapshots_mock.update.return_value = None + # Get the command object to mock + self.cmd = snapshot.SetSnapshot(self.app, None) + + def test_snapshot_set(self): + arglist = [ + "--name", "new_snapshot", + "--property", "x=y", + "--property", "foo=foo", + self.snapshot.id, + ] + new_property = {"x": "y", "foo": "foo"} + verifylist = [ + ("name", "new_snapshot"), + ("property", new_property), + ("snapshot", self.snapshot.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + kwargs = { + "name": "new_snapshot", + } + self.snapshots_mock.update.assert_called_with( + self.snapshot.id, **kwargs) + self.snapshots_mock.set_metadata.assert_called_with( + self.snapshot.id, new_property + ) + self.assertIsNone(result) + + def test_snapshot_set_state_to_error(self): + arglist = [ + "--state", "error", + self.snapshot.id + ] + verifylist = [ + ("state", "error"), + ("snapshot", self.snapshot.id) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.snapshots_mock.reset_state.assert_called_with( + self.snapshot.id, "error") + self.assertIsNone(result) + + +class TestSnapshotShow(TestSnapshot): + + columns = ( + 'created_at', + 'description', + 'id', + 'name', + 'properties', + 'size', + 'status', + 'volume_id', + ) + + def setUp(self): + super(TestSnapshotShow, self).setUp() + + self.snapshot = volume_fakes.FakeSnapshot.create_one_snapshot() + + self.data = ( + self.snapshot.created_at, + self.snapshot.description, + self.snapshot.id, + self.snapshot.name, + utils.format_dict(self.snapshot.metadata), + self.snapshot.size, + self.snapshot.status, + self.snapshot.volume_id, + ) + + self.snapshots_mock.get.return_value = self.snapshot + # Get the command object to test + self.cmd = snapshot.ShowSnapshot(self.app, None) + + def test_snapshot_show(self): + arglist = [ + self.snapshot.id + ] + verifylist = [ + ("snapshot", self.snapshot.id) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.snapshots_mock.get.assert_called_with(self.snapshot.id) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestSnapshotUnset(TestSnapshot): + + snapshot = volume_fakes.FakeSnapshot.create_one_snapshot() + + def setUp(self): + super(TestSnapshotUnset, self).setUp() + + self.snapshots_mock.get.return_value = self.snapshot + self.snapshots_mock.delete_metadata.return_value = None + # Get the command object to mock + self.cmd = snapshot.UnsetSnapshot(self.app, None) + + def test_snapshot_unset(self): + arglist = [ + "--property", "foo", + self.snapshot.id, + ] + verifylist = [ + ("property", ["foo"]), + ("snapshot", self.snapshot.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.snapshots_mock.delete_metadata.assert_called_with( + self.snapshot.id, ["foo"] + ) + self.assertIsNone(result) diff --git a/openstackclient/tests/unit/volume/v2/test_transfer_request.py b/openstackclient/tests/unit/volume/v2/test_transfer_request.py new file mode 100644 index 00000000..32108c02 --- /dev/null +++ b/openstackclient/tests/unit/volume/v2/test_transfer_request.py @@ -0,0 +1,114 @@ +# +# 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 openstackclient.tests.unit.volume.v2 import fakes as transfer_fakes +from openstackclient.volume.v2 import volume_transfer_request + + +class TestTransfer(transfer_fakes.TestVolume): + + def setUp(self): + super(TestTransfer, self).setUp() + + # Get a shortcut to the TransferManager Mock + self.transfer_mock = self.app.client_manager.volume.transfers + self.transfer_mock.reset_mock() + + +class TestTransferList(TestTransfer): + + # The Transfers to be listed + volume_transfers = transfer_fakes.FakeTransfer.create_one_transfer() + + def setUp(self): + super(TestTransferList, self).setUp() + + self.transfer_mock.list.return_value = [self.volume_transfers] + + # Get the command object to test + self.cmd = volume_transfer_request.ListTransferRequests(self.app, None) + + def test_transfer_list_without_argument(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + expected_columns = [ + 'ID', + 'Volume', + 'Name', + ] + + # confirming if all expected columns are present in the result. + self.assertEqual(expected_columns, columns) + + datalist = (( + self.volume_transfers.id, + self.volume_transfers.volume_id, + self.volume_transfers.name, + ), ) + + # confirming if all expected values are present in the result. + self.assertEqual(datalist, tuple(data)) + + # checking if proper call was made to list volume_transfers + self.transfer_mock.list.assert_called_with( + detailed=True, + search_opts={'all_tenants': 0} + ) + + def test_transfer_list_with_argument(self): + arglist = [ + "--all-projects" + ] + verifylist = [ + ("all_projects", True) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + expected_columns = [ + 'ID', + 'Volume', + 'Name', + ] + + # confirming if all expected columns are present in the result. + self.assertEqual(expected_columns, columns) + + datalist = (( + self.volume_transfers.id, + self.volume_transfers.volume_id, + self.volume_transfers.name, + ), ) + + # confirming if all expected values are present in the result. + self.assertEqual(datalist, tuple(data)) + + # checking if proper call was made to list volume_transfers + self.transfer_mock.list.assert_called_with( + detailed=True, + search_opts={'all_tenants': 1} + ) diff --git a/openstackclient/tests/unit/volume/v2/test_type.py b/openstackclient/tests/unit/volume/v2/test_type.py new file mode 100644 index 00000000..84f87e3b --- /dev/null +++ b/openstackclient/tests/unit/volume/v2/test_type.py @@ -0,0 +1,575 @@ +# +# 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 osc_lib import exceptions +from osc_lib import utils + +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes +from openstackclient.tests.unit import utils as tests_utils +from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes +from openstackclient.volume.v2 import volume_type + + +class TestType(volume_fakes.TestVolume): + + def setUp(self): + super(TestType, self).setUp() + + self.types_mock = self.app.client_manager.volume.volume_types + self.types_mock.reset_mock() + + self.types_access_mock = ( + self.app.client_manager.volume.volume_type_access) + self.types_access_mock.reset_mock() + + self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock.reset_mock() + + +class TestTypeCreate(TestType): + + project = identity_fakes.FakeProject.create_one_project() + columns = ( + 'description', + 'id', + 'is_public', + 'name', + ) + + def setUp(self): + super(TestTypeCreate, self).setUp() + + self.new_volume_type = volume_fakes.FakeType.create_one_type() + self.data = ( + self.new_volume_type.description, + self.new_volume_type.id, + True, + self.new_volume_type.name, + ) + + self.types_mock.create.return_value = self.new_volume_type + self.projects_mock.get.return_value = self.project + # Get the command object to test + self.cmd = volume_type.CreateVolumeType(self.app, None) + + def test_type_create_public(self): + arglist = [ + "--description", self.new_volume_type.description, + "--public", + self.new_volume_type.name, + ] + verifylist = [ + ("description", self.new_volume_type.description), + ("public", True), + ("private", False), + ("name", self.new_volume_type.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.types_mock.create.assert_called_with( + self.new_volume_type.name, + description=self.new_volume_type.description, + is_public=True, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_type_create_private(self): + arglist = [ + "--description", self.new_volume_type.description, + "--private", + "--project", self.project.id, + self.new_volume_type.name, + ] + verifylist = [ + ("description", self.new_volume_type.description), + ("public", False), + ("private", True), + ("project", self.project.id), + ("name", self.new_volume_type.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.types_mock.create.assert_called_with( + self.new_volume_type.name, + description=self.new_volume_type.description, + is_public=False, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_public_type_create_with_project(self): + arglist = [ + '--project', self.project.id, + self.new_volume_type.name, + ] + verifylist = [ + ('project', self.project.id), + ('name', self.new_volume_type.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises(exceptions.CommandError, + self.cmd.take_action, + parsed_args) + + +class TestTypeDelete(TestType): + + volume_type = volume_fakes.FakeType.create_one_type() + + def setUp(self): + super(TestTypeDelete, self).setUp() + + self.types_mock.get.return_value = self.volume_type + self.types_mock.delete.return_value = None + + # Get the command object to mock + self.cmd = volume_type.DeleteVolumeType(self.app, None) + + def test_type_delete(self): + arglist = [ + self.volume_type.id + ] + verifylist = [ + ("volume_types", [self.volume_type.id]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.types_mock.delete.assert_called_with(self.volume_type) + self.assertIsNone(result) + + +class TestTypeList(TestType): + + volume_types = volume_fakes.FakeType.create_types() + + columns = [ + "ID", + "Name" + ] + columns_long = columns + [ + "Description", + "Properties" + ] + + data = [] + for t in volume_types: + data.append(( + t.id, + t.name, + )) + data_long = [] + for t in volume_types: + data_long.append(( + t.id, + t.name, + t.description, + utils.format_dict(t.extra_specs), + )) + + def setUp(self): + super(TestTypeList, self).setUp() + + self.types_mock.list.return_value = self.volume_types + # get the command to test + self.cmd = volume_type.ListVolumeType(self.app, None) + + def test_type_list_without_options(self): + arglist = [] + verifylist = [ + ("long", False), + ("private", False), + ("public", False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.types_mock.list.assert_called_once_with(is_public=None) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_type_list_with_options(self): + arglist = [ + "--long", + "--public", + ] + verifylist = [ + ("long", True), + ("private", False), + ("public", True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.types_mock.list.assert_called_once_with(is_public=True) + self.assertEqual(self.columns_long, columns) + self.assertEqual(self.data_long, list(data)) + + def test_type_list_with_private_option(self): + arglist = [ + "--private", + ] + verifylist = [ + ("long", False), + ("private", True), + ("public", False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.types_mock.list.assert_called_once_with(is_public=False) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + +class TestTypeSet(TestType): + + project = identity_fakes.FakeProject.create_one_project() + volume_type = volume_fakes.FakeType.create_one_type( + methods={'set_keys': None}) + + def setUp(self): + super(TestTypeSet, self).setUp() + + self.types_mock.get.return_value = self.volume_type + + # Return a project + self.projects_mock.get.return_value = self.project + # Get the command object to test + self.cmd = volume_type.SetVolumeType(self.app, None) + + def test_type_set_name(self): + new_name = 'new_name' + arglist = [ + '--name', new_name, + self.volume_type.id, + ] + verifylist = [ + ('name', new_name), + ('description', None), + ('property', None), + ('volume_type', self.volume_type.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': new_name, + } + self.types_mock.update.assert_called_with( + self.volume_type.id, + **kwargs + ) + self.assertIsNone(result) + + def test_type_set_description(self): + new_desc = 'new_desc' + arglist = [ + '--description', new_desc, + self.volume_type.id, + ] + verifylist = [ + ('name', None), + ('description', new_desc), + ('property', None), + ('volume_type', self.volume_type.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'description': new_desc, + } + self.types_mock.update.assert_called_with( + self.volume_type.id, + **kwargs + ) + self.assertIsNone(result) + + def test_type_set_property(self): + arglist = [ + '--property', 'myprop=myvalue', + self.volume_type.id, + ] + verifylist = [ + ('name', None), + ('description', None), + ('property', {'myprop': 'myvalue'}), + ('volume_type', self.volume_type.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.volume_type.set_keys.assert_called_once_with( + {'myprop': 'myvalue'}) + self.assertIsNone(result) + + def test_type_set_not_called_without_project_argument(self): + arglist = [ + '--project', '', + self.volume_type.id, + ] + verifylist = [ + ('project', ''), + ('volume_type', self.volume_type.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) + + self.assertFalse(self.types_access_mock.add_project_access.called) + + def test_type_set_failed_with_missing_volume_type_argument(self): + arglist = [ + '--project', 'identity_fakes.project_id', + ] + verifylist = [ + ('project', 'identity_fakes.project_id'), + ] + + self.assertRaises(tests_utils.ParserException, + self.check_parser, + self.cmd, + arglist, + verifylist) + + def test_type_set_project_access(self): + arglist = [ + '--project', self.project.id, + self.volume_type.id, + ] + verifylist = [ + ('project', self.project.id), + ('volume_type', self.volume_type.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) + + self.types_access_mock.add_project_access.assert_called_with( + self.volume_type.id, + self.project.id, + ) + + +class TestTypeShow(TestType): + + columns = ( + 'access_project_ids', + 'description', + 'id', + 'is_public', + 'name', + 'properties', + ) + + def setUp(self): + super(TestTypeShow, self).setUp() + + self.volume_type = volume_fakes.FakeType.create_one_type() + self.data = ( + None, + self.volume_type.description, + self.volume_type.id, + True, + self.volume_type.name, + utils.format_dict(self.volume_type.extra_specs) + ) + + self.types_mock.get.return_value = self.volume_type + + # Get the command object to test + self.cmd = volume_type.ShowVolumeType(self.app, None) + + def test_type_show(self): + arglist = [ + self.volume_type.id + ] + verifylist = [ + ("volume_type", self.volume_type.id) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.types_mock.get.assert_called_with(self.volume_type.id) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_type_show_with_access(self): + arglist = [ + self.volume_type.id + ] + verifylist = [ + ("volume_type", self.volume_type.id) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + private_type = volume_fakes.FakeType.create_one_type( + attrs={'is_public': False}) + type_access_list = volume_fakes.FakeTypeAccess.create_one_type_access() + with mock.patch.object(self.types_mock, 'get', + return_value=private_type): + with mock.patch.object(self.types_access_mock, 'list', + return_value=[type_access_list]): + columns, data = self.cmd.take_action(parsed_args) + self.types_mock.get.assert_called_once_with( + self.volume_type.id) + self.types_access_mock.list.assert_called_once_with( + private_type.id) + + self.assertEqual(self.columns, columns) + private_type_data = ( + utils.format_list([type_access_list.project_id]), + private_type.description, + private_type.id, + private_type.is_public, + private_type.name, + utils.format_dict(private_type.extra_specs) + ) + self.assertEqual(private_type_data, data) + + def test_type_show_with_list_access_exec(self): + arglist = [ + self.volume_type.id + ] + verifylist = [ + ("volume_type", self.volume_type.id) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + private_type = volume_fakes.FakeType.create_one_type( + attrs={'is_public': False}) + with mock.patch.object(self.types_mock, 'get', + return_value=private_type): + with mock.patch.object(self.types_access_mock, 'list', + side_effect=Exception()): + columns, data = self.cmd.take_action(parsed_args) + self.types_mock.get.assert_called_once_with( + self.volume_type.id) + self.types_access_mock.list.assert_called_once_with( + private_type.id) + + self.assertEqual(self.columns, columns) + private_type_data = ( + None, + private_type.description, + private_type.id, + private_type.is_public, + private_type.name, + utils.format_dict(private_type.extra_specs) + ) + self.assertEqual(private_type_data, data) + + +class TestTypeUnset(TestType): + + project = identity_fakes.FakeProject.create_one_project() + volume_type = volume_fakes.FakeType.create_one_type( + methods={'unset_keys': None}) + + def setUp(self): + super(TestTypeUnset, self).setUp() + + self.types_mock.get.return_value = self.volume_type + + # Return a project + self.projects_mock.get.return_value = self.project + + # Get the command object to test + self.cmd = volume_type.UnsetVolumeType(self.app, None) + + def test_type_unset(self): + arglist = [ + '--property', 'property', + '--property', 'multi_property', + self.volume_type.id, + ] + verifylist = [ + ('property', ['property', 'multi_property']), + ('volume_type', self.volume_type.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.volume_type.unset_keys.assert_called_once_with( + ['property', 'multi_property']) + self.assertIsNone(result) + + def test_type_unset_project_access(self): + arglist = [ + '--project', self.project.id, + self.volume_type.id, + ] + verifylist = [ + ('project', self.project.id), + ('volume_type', self.volume_type.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) + + self.types_access_mock.remove_project_access.assert_called_with( + self.volume_type.id, + self.project.id, + ) + + def test_type_unset_not_called_without_project_argument(self): + arglist = [ + '--project', '', + self.volume_type.id, + ] + verifylist = [ + ('project', ''), + ('volume_type', self.volume_type.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) + + self.assertFalse(self.types_access_mock.remove_project_access.called) + + def test_type_unset_failed_with_missing_volume_type_argument(self): + arglist = [ + '--project', 'identity_fakes.project_id', + ] + verifylist = [ + ('project', 'identity_fakes.project_id'), + ] + + self.assertRaises(tests_utils.ParserException, + self.check_parser, + self.cmd, + arglist, + verifylist) diff --git a/openstackclient/tests/unit/volume/v2/test_volume.py b/openstackclient/tests/unit/volume/v2/test_volume.py new file mode 100644 index 00000000..66f8f74d --- /dev/null +++ b/openstackclient/tests/unit/volume/v2/test_volume.py @@ -0,0 +1,966 @@ +# +# 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 mock import call + +from osc_lib import exceptions +from osc_lib import utils + +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes +from openstackclient.tests.unit.image.v2 import fakes as image_fakes +from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes +from openstackclient.volume.v2 import volume + + +class TestVolume(volume_fakes.TestVolume): + + def setUp(self): + super(TestVolume, self).setUp() + + self.volumes_mock = self.app.client_manager.volume.volumes + self.volumes_mock.reset_mock() + + self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock.reset_mock() + + self.users_mock = self.app.client_manager.identity.users + self.users_mock.reset_mock() + + self.images_mock = self.app.client_manager.image.images + self.images_mock.reset_mock() + + self.snapshots_mock = self.app.client_manager.volume.volume_snapshots + self.snapshots_mock.reset_mock() + + def setup_volumes_mock(self, count): + volumes = volume_fakes.FakeVolume.create_volumes(count=count) + + self.volumes_mock.get = volume_fakes.FakeVolume.get_volumes( + volumes, + 0) + return volumes + + +class TestVolumeCreate(TestVolume): + + project = identity_fakes.FakeProject.create_one_project() + user = identity_fakes.FakeUser.create_one_user() + + columns = ( + 'attachments', + 'availability_zone', + 'bootable', + 'description', + 'id', + 'name', + 'properties', + 'size', + 'snapshot_id', + 'status', + 'type', + ) + + def setUp(self): + super(TestVolumeCreate, self).setUp() + + self.new_volume = volume_fakes.FakeVolume.create_one_volume() + self.volumes_mock.create.return_value = self.new_volume + + self.datalist = ( + self.new_volume.attachments, + self.new_volume.availability_zone, + self.new_volume.bootable, + self.new_volume.description, + self.new_volume.id, + self.new_volume.name, + utils.format_dict(self.new_volume.metadata), + self.new_volume.size, + self.new_volume.snapshot_id, + self.new_volume.status, + self.new_volume.volume_type, + ) + + # Get the command object to test + self.cmd = volume.CreateVolume(self.app, None) + + def test_volume_create_min_options(self): + arglist = [ + '--size', str(self.new_volume.size), + self.new_volume.name, + ] + verifylist = [ + ('size', self.new_volume.size), + ('name', self.new_volume.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + self.volumes_mock.create.assert_called_with( + size=self.new_volume.size, + snapshot_id=None, + name=self.new_volume.name, + description=None, + volume_type=None, + user_id=None, + project_id=None, + availability_zone=None, + metadata=None, + imageRef=None, + source_volid=None + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_volume_create_options(self): + arglist = [ + '--size', str(self.new_volume.size), + '--description', self.new_volume.description, + '--type', self.new_volume.volume_type, + '--availability-zone', self.new_volume.availability_zone, + self.new_volume.name, + ] + verifylist = [ + ('size', self.new_volume.size), + ('description', self.new_volume.description), + ('type', self.new_volume.volume_type), + ('availability_zone', self.new_volume.availability_zone), + ('name', self.new_volume.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + self.volumes_mock.create.assert_called_with( + size=self.new_volume.size, + snapshot_id=None, + name=self.new_volume.name, + description=self.new_volume.description, + volume_type=self.new_volume.volume_type, + user_id=None, + project_id=None, + availability_zone=self.new_volume.availability_zone, + metadata=None, + imageRef=None, + source_volid=None + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_volume_create_user_project_id(self): + # Return a project + self.projects_mock.get.return_value = self.project + # Return a user + self.users_mock.get.return_value = self.user + + arglist = [ + '--size', str(self.new_volume.size), + '--project', self.project.id, + '--user', self.user.id, + self.new_volume.name, + ] + verifylist = [ + ('size', self.new_volume.size), + ('project', self.project.id), + ('user', self.user.id), + ('name', self.new_volume.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + self.volumes_mock.create.assert_called_with( + size=self.new_volume.size, + snapshot_id=None, + name=self.new_volume.name, + description=None, + volume_type=None, + user_id=self.user.id, + project_id=self.project.id, + availability_zone=None, + metadata=None, + imageRef=None, + source_volid=None + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_volume_create_user_project_name(self): + # Return a project + self.projects_mock.get.return_value = self.project + # Return a user + self.users_mock.get.return_value = self.user + + arglist = [ + '--size', str(self.new_volume.size), + '--project', self.project.name, + '--user', self.user.name, + self.new_volume.name, + ] + verifylist = [ + ('size', self.new_volume.size), + ('project', self.project.name), + ('user', self.user.name), + ('name', self.new_volume.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + self.volumes_mock.create.assert_called_with( + size=self.new_volume.size, + snapshot_id=None, + name=self.new_volume.name, + description=None, + volume_type=None, + user_id=self.user.id, + project_id=self.project.id, + availability_zone=None, + metadata=None, + imageRef=None, + source_volid=None + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_volume_create_properties(self): + arglist = [ + '--property', 'Alpha=a', + '--property', 'Beta=b', + '--size', str(self.new_volume.size), + self.new_volume.name, + ] + verifylist = [ + ('property', {'Alpha': 'a', 'Beta': 'b'}), + ('size', self.new_volume.size), + ('name', self.new_volume.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + self.volumes_mock.create.assert_called_with( + size=self.new_volume.size, + snapshot_id=None, + name=self.new_volume.name, + description=None, + volume_type=None, + user_id=None, + project_id=None, + availability_zone=None, + metadata={'Alpha': 'a', 'Beta': 'b'}, + imageRef=None, + source_volid=None + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_volume_create_image_id(self): + image = image_fakes.FakeImage.create_one_image() + self.images_mock.get.return_value = image + + arglist = [ + '--image', image.id, + '--size', str(self.new_volume.size), + self.new_volume.name, + ] + verifylist = [ + ('image', image.id), + ('size', self.new_volume.size), + ('name', self.new_volume.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + self.volumes_mock.create.assert_called_with( + size=self.new_volume.size, + snapshot_id=None, + name=self.new_volume.name, + description=None, + volume_type=None, + user_id=None, + project_id=None, + availability_zone=None, + metadata=None, + imageRef=image.id, + source_volid=None, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_volume_create_image_name(self): + image = image_fakes.FakeImage.create_one_image() + self.images_mock.get.return_value = image + + arglist = [ + '--image', image.name, + '--size', str(self.new_volume.size), + self.new_volume.name, + ] + verifylist = [ + ('image', image.name), + ('size', self.new_volume.size), + ('name', self.new_volume.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + self.volumes_mock.create.assert_called_with( + size=self.new_volume.size, + snapshot_id=None, + name=self.new_volume.name, + description=None, + volume_type=None, + user_id=None, + project_id=None, + availability_zone=None, + metadata=None, + imageRef=image.id, + source_volid=None + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_volume_create_with_snapshot(self): + snapshot = volume_fakes.FakeSnapshot.create_one_snapshot() + self.new_volume.snapshot_id = snapshot.id + arglist = [ + '--size', str(self.new_volume.size), + '--snapshot', self.new_volume.snapshot_id, + self.new_volume.name, + ] + verifylist = [ + ('size', self.new_volume.size), + ('snapshot', self.new_volume.snapshot_id), + ('name', self.new_volume.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.snapshots_mock.get.return_value = snapshot + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + self.volumes_mock.create.assert_called_once_with( + size=self.new_volume.size, + snapshot_id=snapshot.id, + name=self.new_volume.name, + description=None, + volume_type=None, + user_id=None, + project_id=None, + availability_zone=None, + metadata=None, + imageRef=None, + source_volid=None + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + +class TestVolumeDelete(TestVolume): + + def setUp(self): + super(TestVolumeDelete, self).setUp() + + self.volumes_mock.delete.return_value = None + + # Get the command object to mock + self.cmd = volume.DeleteVolume(self.app, None) + + def test_volume_delete_one_volume(self): + volumes = self.setup_volumes_mock(count=1) + + arglist = [ + volumes[0].id + ] + verifylist = [ + ("force", False), + ("purge", False), + ("volumes", [volumes[0].id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.volumes_mock.delete.assert_called_once_with( + volumes[0].id, cascade=False) + self.assertIsNone(result) + + def test_volume_delete_multi_volumes(self): + volumes = self.setup_volumes_mock(count=3) + + arglist = [v.id for v in volumes] + verifylist = [ + ('force', False), + ('purge', False), + ('volumes', arglist), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + calls = [call(v.id, cascade=False) for v in volumes] + self.volumes_mock.delete.assert_has_calls(calls) + self.assertIsNone(result) + + def test_volume_delete_multi_volumes_with_exception(self): + volumes = self.setup_volumes_mock(count=2) + + arglist = [ + volumes[0].id, + 'unexist_volume', + ] + verifylist = [ + ('force', False), + ('purge', False), + ('volumes', arglist), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + find_mock_result = [volumes[0], exceptions.CommandError] + with mock.patch.object(utils, 'find_resource', + side_effect=find_mock_result) as find_mock: + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 volumes failed to delete.', + str(e)) + + find_mock.assert_any_call(self.volumes_mock, volumes[0].id) + find_mock.assert_any_call(self.volumes_mock, 'unexist_volume') + + self.assertEqual(2, find_mock.call_count) + self.volumes_mock.delete.assert_called_once_with( + volumes[0].id, cascade=False) + + def test_volume_delete_with_purge(self): + volumes = self.setup_volumes_mock(count=1) + + arglist = [ + '--purge', + volumes[0].id, + ] + verifylist = [ + ('force', False), + ('purge', True), + ('volumes', [volumes[0].id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.volumes_mock.delete.assert_called_once_with( + volumes[0].id, cascade=True) + self.assertIsNone(result) + + def test_volume_delete_with_force(self): + volumes = self.setup_volumes_mock(count=1) + + arglist = [ + '--force', + volumes[0].id, + ] + verifylist = [ + ('force', True), + ('purge', False), + ('volumes', [volumes[0].id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.volumes_mock.force_delete.assert_called_once_with(volumes[0].id) + self.assertIsNone(result) + + +class TestVolumeList(TestVolume): + + project = identity_fakes.FakeProject.create_one_project() + user = identity_fakes.FakeUser.create_one_user() + + columns = [ + 'ID', + 'Display Name', + 'Status', + 'Size', + 'Attached to', + ] + + def setUp(self): + super(TestVolumeList, self).setUp() + + self.mock_volume = volume_fakes.FakeVolume.create_one_volume() + self.volumes_mock.list.return_value = [self.mock_volume] + + self.users_mock.get.return_value = self.user + + self.projects_mock.get.return_value = self.project + + # Get the command object to test + self.cmd = volume.ListVolume(self.app, None) + + def test_volume_list_no_options(self): + arglist = [] + verifylist = [ + ('long', False), + ('all_projects', False), + ('name', None), + ('status', None), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + + server = self.mock_volume.attachments[0]['server_id'] + device = self.mock_volume.attachments[0]['device'] + msg = 'Attached to %s on %s ' % (server, device) + datalist = (( + self.mock_volume.id, + self.mock_volume.name, + self.mock_volume.status, + self.mock_volume.size, + msg, + ), ) + self.assertEqual(datalist, tuple(data)) + + def test_volume_list_project(self): + arglist = [ + '--project', self.project.name, + ] + verifylist = [ + ('project', self.project.name), + ('long', False), + ('all_projects', False), + ('status', None), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + + server = self.mock_volume.attachments[0]['server_id'] + device = self.mock_volume.attachments[0]['device'] + msg = 'Attached to %s on %s ' % (server, device) + datalist = (( + self.mock_volume.id, + self.mock_volume.name, + self.mock_volume.status, + self.mock_volume.size, + msg, + ), ) + self.assertEqual(datalist, tuple(data)) + + def test_volume_list_project_domain(self): + arglist = [ + '--project', self.project.name, + '--project-domain', self.project.domain_id, + ] + verifylist = [ + ('project', self.project.name), + ('project_domain', self.project.domain_id), + ('long', False), + ('all_projects', False), + ('status', None), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + + server = self.mock_volume.attachments[0]['server_id'] + device = self.mock_volume.attachments[0]['device'] + msg = 'Attached to %s on %s ' % (server, device) + datalist = (( + self.mock_volume.id, + self.mock_volume.name, + self.mock_volume.status, + self.mock_volume.size, + msg, + ), ) + self.assertEqual(datalist, tuple(data)) + + def test_volume_list_user(self): + arglist = [ + '--user', self.user.name, + ] + verifylist = [ + ('user', self.user.name), + ('long', False), + ('all_projects', False), + ('status', None), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + server = self.mock_volume.attachments[0]['server_id'] + device = self.mock_volume.attachments[0]['device'] + msg = 'Attached to %s on %s ' % (server, device) + datalist = (( + self.mock_volume.id, + self.mock_volume.name, + self.mock_volume.status, + self.mock_volume.size, + msg, + ), ) + self.assertEqual(datalist, tuple(data)) + + def test_volume_list_user_domain(self): + arglist = [ + '--user', self.user.name, + '--user-domain', self.user.domain_id, + ] + verifylist = [ + ('user', self.user.name), + ('user_domain', self.user.domain_id), + ('long', False), + ('all_projects', False), + ('status', None), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + + server = self.mock_volume.attachments[0]['server_id'] + device = self.mock_volume.attachments[0]['device'] + msg = 'Attached to %s on %s ' % (server, device) + datalist = (( + self.mock_volume.id, + self.mock_volume.name, + self.mock_volume.status, + self.mock_volume.size, + msg, + ), ) + self.assertEqual(datalist, tuple(data)) + + def test_volume_list_name(self): + arglist = [ + '--name', self.mock_volume.name, + ] + verifylist = [ + ('long', False), + ('all_projects', False), + ('name', self.mock_volume.name), + ('status', None), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + + server = self.mock_volume.attachments[0]['server_id'] + device = self.mock_volume.attachments[0]['device'] + msg = 'Attached to %s on %s ' % (server, device) + datalist = (( + self.mock_volume.id, + self.mock_volume.name, + self.mock_volume.status, + self.mock_volume.size, + msg, + ), ) + self.assertEqual(datalist, tuple(data)) + + def test_volume_list_status(self): + arglist = [ + '--status', self.mock_volume.status, + ] + verifylist = [ + ('long', False), + ('all_projects', False), + ('name', None), + ('status', self.mock_volume.status), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + + server = self.mock_volume.attachments[0]['server_id'] + device = self.mock_volume.attachments[0]['device'] + msg = 'Attached to %s on %s ' % (server, device) + datalist = (( + self.mock_volume.id, + self.mock_volume.name, + self.mock_volume.status, + self.mock_volume.size, + msg, + ), ) + self.assertEqual(datalist, tuple(data)) + + def test_volume_list_all_projects(self): + arglist = [ + '--all-projects', + ] + verifylist = [ + ('long', False), + ('all_projects', True), + ('name', None), + ('status', None), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + + server = self.mock_volume.attachments[0]['server_id'] + device = self.mock_volume.attachments[0]['device'] + msg = 'Attached to %s on %s ' % (server, device) + datalist = (( + self.mock_volume.id, + self.mock_volume.name, + self.mock_volume.status, + self.mock_volume.size, + msg, + ), ) + self.assertEqual(datalist, tuple(data)) + + def test_volume_list_long(self): + arglist = [ + '--long', + ] + verifylist = [ + ('long', True), + ('all_projects', False), + ('name', None), + ('status', None), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + collist = [ + 'ID', + 'Display Name', + 'Status', + 'Size', + 'Type', + 'Bootable', + 'Attached to', + 'Properties', + ] + self.assertEqual(collist, columns) + + server = self.mock_volume.attachments[0]['server_id'] + device = self.mock_volume.attachments[0]['device'] + msg = 'Attached to %s on %s ' % (server, device) + datalist = (( + self.mock_volume.id, + self.mock_volume.name, + self.mock_volume.status, + self.mock_volume.size, + self.mock_volume.volume_type, + self.mock_volume.bootable, + msg, + utils.format_dict(self.mock_volume.metadata), + ), ) + self.assertEqual(datalist, tuple(data)) + + +class TestVolumeSet(TestVolume): + + def setUp(self): + super(TestVolumeSet, self).setUp() + + self.new_volume = volume_fakes.FakeVolume.create_one_volume() + self.volumes_mock.get.return_value = self.new_volume + + # Get the command object to test + self.cmd = volume.SetVolume(self.app, None) + + def test_volume_set_image_property(self): + arglist = [ + '--image-property', 'Alpha=a', + '--image-property', 'Beta=b', + self.new_volume.id, + ] + verifylist = [ + ('image_property', {'Alpha': 'a', 'Beta': 'b'}), + ('volume', self.new_volume.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns nothing + self.cmd.take_action(parsed_args) + self.volumes_mock.set_image_metadata.assert_called_with( + self.new_volume.id, parsed_args.image_property) + + def test_volume_set_state(self): + arglist = [ + '--state', 'error', + self.new_volume.id + ] + verifylist = [ + ('state', 'error'), + ('volume', self.new_volume.id) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.volumes_mock.reset_state.assert_called_with( + self.new_volume.id, 'error') + self.assertIsNone(result) + + def test_volume_set_state_failed(self): + self.volumes_mock.reset_state.side_effect = exceptions.CommandError() + arglist = [ + '--state', 'error', + self.new_volume.id + ] + verifylist = [ + ('state', 'error'), + ('volume', self.new_volume.id) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('One or more of the set operations failed', + str(e)) + self.volumes_mock.reset_state.assert_called_with( + self.new_volume.id, 'error') + + +class TestVolumeShow(TestVolume): + + def setUp(self): + super(TestVolumeShow, self).setUp() + + self._volume = volume_fakes.FakeVolume.create_one_volume() + self.volumes_mock.get.return_value = self._volume + # Get the command object to test + self.cmd = volume.ShowVolume(self.app, None) + + def test_volume_show(self): + arglist = [ + self._volume.id + ] + verifylist = [ + ("volume", self._volume.id) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.volumes_mock.get.assert_called_with(self._volume.id) + + self.assertEqual( + volume_fakes.FakeVolume.get_volume_columns(self._volume), + columns) + + self.assertEqual( + volume_fakes.FakeVolume.get_volume_data(self._volume), + data) + + +class TestVolumeUnset(TestVolume): + + def setUp(self): + super(TestVolumeUnset, self).setUp() + + self.new_volume = volume_fakes.FakeVolume.create_one_volume() + self.volumes_mock.get.return_value = self.new_volume + + # Get the command object to set property + self.cmd_set = volume.SetVolume(self.app, None) + + # Get the command object to unset property + self.cmd_unset = volume.UnsetVolume(self.app, None) + + def test_volume_unset_image_property(self): + + # Arguments for setting image properties + arglist = [ + '--image-property', 'Alpha=a', + '--image-property', 'Beta=b', + self.new_volume.id, + ] + verifylist = [ + ('image_property', {'Alpha': 'a', 'Beta': 'b'}), + ('volume', self.new_volume.id), + ] + parsed_args = self.check_parser(self.cmd_set, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns nothing + self.cmd_set.take_action(parsed_args) + + # Arguments for unsetting image properties + arglist_unset = [ + '--image-property', 'Alpha', + self.new_volume.id, + ] + verifylist_unset = [ + ('image_property', ['Alpha']), + ('volume', self.new_volume.id), + ] + parsed_args_unset = self.check_parser(self.cmd_unset, + arglist_unset, + verifylist_unset) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns nothing + self.cmd_unset.take_action(parsed_args_unset) + + self.volumes_mock.delete_image_metadata.assert_called_with( + self.new_volume.id, parsed_args_unset.image_property) |
