diff --git a/doc/source/cli/index.rst b/doc/source/cli/index.rst index 942a01be4..751a800cd 100644 --- a/doc/source/cli/index.rst +++ b/doc/source/cli/index.rst @@ -43,6 +43,7 @@ documentation of these services: modelarts nat obs + privatenat rds_v3 sdrs smn diff --git a/doc/source/cli/privatenat.rst b/doc/source/cli/privatenat.rst new file mode 100644 index 000000000..f140cd65d --- /dev/null +++ b/doc/source/cli/privatenat.rst @@ -0,0 +1,19 @@ +Private Network Address Translation (Private NAT) +================================================= + +The Private NAT client is the command-line interface (CLI) for +the Private NAT API. + +For help on a specific `privatenat` command, enter: + +.. code-block:: console + + $ openstack privatenat help SUBCOMMAND + +.. _private_gateway: + +Private NAT Gateway Operations +------------------------------ + +.. autoprogram-cliff:: openstack.privatenat.v3 + :command: privatenat gateway * diff --git a/doc/source/sdk/proxies/index.rst b/doc/source/sdk/proxies/index.rst index 459e7310e..1cd5bfeae 100644 --- a/doc/source/sdk/proxies/index.rst +++ b/doc/source/sdk/proxies/index.rst @@ -29,6 +29,7 @@ Service Proxies MapReduce Service (MRS) Modelarts Service (ModelArts) Network Address Translation (NAT) + Private Network Address Translation (Private NAT) Object Block Storage (OBS) Relational Database Service RDS V1 (RDSv1) Relational Database Service RDS V3 (RDS) diff --git a/doc/source/sdk/proxies/privatenat.rst b/doc/source/sdk/proxies/privatenat.rst new file mode 100644 index 000000000..47f368514 --- /dev/null +++ b/doc/source/sdk/proxies/privatenat.rst @@ -0,0 +1,19 @@ +Private NAT API +=============== + +.. automodule:: otcextensions.sdk.natv3.v3._proxy + +The Private NAT high-level interface +------------------------------------ + +The private NAT high-level interface is available through the ``natv3`` +member of a :class:`~openstack.connection.Connection` object. The +``natv3`` member will only be added if the +``otcextensions.sdk.register_otc_extensions(conn)`` method is called. + +Private NAT Gateway Operations +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. autoclass:: otcextensions.sdk.natv3.v3._proxy.Proxy + :noindex: + :members: private_nat_gateways diff --git a/doc/source/sdk/resources/index.rst b/doc/source/sdk/resources/index.rst index fd763dfdd..3c52e163a 100644 --- a/doc/source/sdk/resources/index.rst +++ b/doc/source/sdk/resources/index.rst @@ -30,6 +30,7 @@ Open Telekom Cloud Resources MapReduce Service (MRS) ModelArts Service (MA) Network Address Translation (NAT) + Private Network Address Translation (Private NAT) Object Block Storage (OBS) Relational Database Service (RDS) Shared File System Turbo (SFS Turbo) diff --git a/doc/source/sdk/resources/privatenat/index.rst b/doc/source/sdk/resources/privatenat/index.rst new file mode 100644 index 000000000..a8d43428a --- /dev/null +++ b/doc/source/sdk/resources/privatenat/index.rst @@ -0,0 +1,7 @@ +Private NAT Resources +===================== + +.. toctree:: + :maxdepth: 1 + + v3/private_gateway diff --git a/doc/source/sdk/resources/privatenat/v3/private_gateway.rst b/doc/source/sdk/resources/privatenat/v3/private_gateway.rst new file mode 100644 index 000000000..062e88f0e --- /dev/null +++ b/doc/source/sdk/resources/privatenat/v3/private_gateway.rst @@ -0,0 +1,13 @@ +otcextensions.sdk.natv3.v3.gateway +================================== + +.. automodule:: otcextensions.sdk.natv3.v3.gateway + +The Private NAT Gateway Class +----------------------------- + +The ``PrivateNatGateway`` class inherits from +:class:`~openstack.resource.Resource`. + +.. autoclass:: otcextensions.sdk.natv3.v3.gateway.PrivateNatGateway + :members: diff --git a/examples/natv3/list_private_gateways.py b/examples/natv3/list_private_gateways.py new file mode 100644 index 000000000..6c4ef5548 --- /dev/null +++ b/examples/natv3/list_private_gateways.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# 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. +""" +List all Private NAT Gateways +""" +import openstack + + +openstack.enable_logging(True) +conn = openstack.connect(cloud='otc') + +for gateway in conn.natv3.private_nat_gateways(): + print(gateway) diff --git a/otcextensions/osclient/privatenat/__init__.py b/otcextensions/osclient/privatenat/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/otcextensions/osclient/privatenat/client.py b/otcextensions/osclient/privatenat/client.py new file mode 100644 index 000000000..aa93d7438 --- /dev/null +++ b/otcextensions/osclient/privatenat/client.py @@ -0,0 +1,52 @@ +# 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 logging + +from osc_lib import utils + +from otcextensions import sdk +from otcextensions.i18n import _ + +LOG = logging.getLogger(__name__) + +DEFAULT_API_VERSION = '3' +API_VERSION_OPTION = 'os_privatenat_api_version' +API_NAME = "privatenat" +API_VERSIONS = { + "3": "openstack.connection.Connection" +} + + +def make_client(instance): + """Returns a private NAT proxy""" + conn = instance.sdk_connection + + if getattr(conn, 'natv3', None) is None: + LOG.debug('OTC extensions are not registered. Do that now') + sdk.register_otc_extensions(conn) + + LOG.debug('Private NAT client initialized using OpenStack OTC SDK: %s', + conn.natv3) + return conn.natv3 + + +def build_option_parser(parser): + """Hook to add global options""" + parser.add_argument( + '--os-privatenat-api-version', + metavar='', + default=utils.env('OS_PRIVATENAT_API_VERSION'), + help=_("Private NAT API version, default=%s " + "(Env: OS_PRIVATENAT_API_VERSION)") % DEFAULT_API_VERSION + ) + return parser diff --git a/otcextensions/osclient/privatenat/v3/__init__.py b/otcextensions/osclient/privatenat/v3/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/otcextensions/osclient/privatenat/v3/private_nat_gateway.py b/otcextensions/osclient/privatenat/v3/private_nat_gateway.py new file mode 100644 index 000000000..fb4fc8866 --- /dev/null +++ b/otcextensions/osclient/privatenat/v3/private_nat_gateway.py @@ -0,0 +1,153 @@ +# 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. +# +"""Private NAT Gateway v3 action implementations""" + +import logging + +from osc_lib import utils +from osc_lib.command import command + +from otcextensions.i18n import _ + + +LOG = logging.getLogger(__name__) + + +class ListPrivateNatGateways(command.Lister): + + _description = _("List Private NAT Gateways.") + columns = ( + 'Id', + 'Name', + 'Spec', + 'Status', + 'Project Id', + 'Enterprise Project Id', + ) + + def get_parser(self, prog_name): + parser = super(ListPrivateNatGateways, self).get_parser(prog_name) + + parser.add_argument( + '--limit', + metavar='', + type=int, + help=_("Specifies the number of records displayed on each page."), + ) + parser.add_argument( + '--marker', + metavar='', + help=_("Specifies the start resource ID of pagination query."), + ) + parser.add_argument( + '--page-reverse', + action='store_true', + default=False, + help=_("Specifies whether to query resources" + " on the previous page."), + ) + parser.add_argument( + '--id', + metavar='', + nargs='+', + help=_("Specifies the private NAT gateway IDs."), + ) + parser.add_argument( + '--name', + metavar='', + nargs='+', + help=_("Specifies the private NAT gateway names."), + ) + parser.add_argument( + '--description', + metavar='', + nargs='+', + help=_("Provides supplementary information" + " about the private NAT gateway."), + ) + parser.add_argument( + '--spec', + metavar='', + nargs='+', + help=_("Specifies the private NAT gateway specifications. "), + ) + parser.add_argument( + '--project-id', + metavar='', + nargs='+', + help=_("Specifies the project ID. Repeat for multiple values."), + ) + parser.add_argument( + '--status', + metavar='', + nargs='+', + help=_("Specifies the private NAT gateway status."), + ) + parser.add_argument( + '--vpc-id', + metavar='', + nargs='+', + help=_("Specifies the ID of the VPC."), + ) + parser.add_argument( + '--virsubnet-id', + metavar='', + nargs='+', + help=_("Specifies the ID of the subnet."), + ) + parser.add_argument( + '--enterprise-project-id', + metavar='', + nargs='+', + help=_("Specifies the enterprise project ID " + "associated with the private NAT gateway."), + ) + + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.privatenat + + attrs = {} + args_list = [ + 'limit', + 'marker', + 'page_reverse', + 'id', + 'name', + 'description', + 'spec', + 'project_id', + 'status', + 'vpc_id', + 'virsubnet_id', + 'enterprise_project_id', + ] + for arg in args_list: + val = getattr(parsed_args, arg) + if arg == 'page_reverse': + # Only send flag if explicitly requested + if val: + attrs[arg] = val + elif val is not None and val != [] and val != '': + attrs[arg] = val + + data = client.private_nat_gateways(**attrs) + + return ( + self.columns, + ( + utils.get_item_properties(s, self.columns) + for s in data + ) + ) diff --git a/otcextensions/sdk/__init__.py b/otcextensions/sdk/__init__.py index 17cb6c155..9c9fb89c2 100644 --- a/otcextensions/sdk/__init__.py +++ b/otcextensions/sdk/__init__.py @@ -269,6 +269,7 @@ }, 'natv3': { 'service_type': 'natv3', + 'append_project_id': True, 'endpoint_service_type': 'natv3' }, 'obs': { diff --git a/otcextensions/sdk/natv3/v3/_proxy.py b/otcextensions/sdk/natv3/v3/_proxy.py index 1b3ec9e89..feece5abc 100644 --- a/otcextensions/sdk/natv3/v3/_proxy.py +++ b/otcextensions/sdk/natv3/v3/_proxy.py @@ -12,6 +12,7 @@ from openstack import proxy from otcextensions.common.utils import extract_url_parts +from otcextensions.sdk.natv3.v3 import gateway as _gateway class Proxy(proxy.Proxy): @@ -20,3 +21,13 @@ class Proxy(proxy.Proxy): def _extract_name(self, url, service_type=None, project_id=None): return extract_url_parts(url, project_id) + + def private_nat_gateways(self, **query): + """Return a generator of private NAT gateways. + + :param dict query: Optional query parameters to be sent to limit + the resources being returned. + + :returns: A generator of private NAT gateway objects. + """ + return self._list(_gateway.PrivateNatGateway, **query) diff --git a/otcextensions/sdk/natv3/v3/gateway.py b/otcextensions/sdk/natv3/v3/gateway.py new file mode 100644 index 000000000..459f3adb1 --- /dev/null +++ b/otcextensions/sdk/natv3/v3/gateway.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 openstack import resource + + +class DownlinkVpc(resource.Resource): + #: Specifies the ID of the VPC where the private NAT gateway works. + vpc_id = resource.Body('vpc_id') + + #: Specifies the ID of the subnet where the private NAT gateway works. + virsubnet_id = resource.Body('virsubnet_id') + + #: Specifies the private IP address of the private NAT gateway. + ngport_ip_address = resource.Body('ngport_ip_address') + + +class Tag(resource.Resource): + #: Specifies the tag key. Up to 128 Unicode characters. Cannot be blank. + key = resource.Body('key') + + #: Specifies the tag value. Up to 255 Unicode characters. + value = resource.Body('value') + + +class PrivateNatGateway(resource.Resource): + resources_key = 'gateways' + resource_key = 'gateway' + base_path = '/private-nat/gateways' + + allow_list = True + + _query_mapping = resource.QueryParameters( + 'description', + 'enterprise_project_id', + 'id', + 'limit', + 'marker', + 'name', + 'page_reverse', + 'project_id', + 'spec', + 'status', + 'virsubnet_id', + 'vpc_id', + ) + + # Properties + #: Specifies the time when the private NAT gateway was created. + #: It is a UTC time in yyyy-mm-ddThh:mm:ssZ format. + created_at = resource.Body('created_at') + #: Provides supplementary information about the private NAT gateway. + #: The description can contain up to 255 characters. + #: Cannot contain angle brackets (<>). + description = resource.Body('description') + #: Specifies the VPC where the private NAT gateway works. + #: Each item contains: + #: * vpc_id: Specifies the ID of the VPC. + #: * virsubnet_id: Specifies the ID of the subnet. + #: * ngport_ip_address: Specifies the private IP address of the gateway. + downlink_vpcs = resource.Body('downlink_vpcs', type=list, + list_type=DownlinkVpc) + #: Specifies the ID of the enterprise project that is associated + #: with the private NAT gateway when the private NAT gateway is created. + enterprise_project_id = resource.Body('enterprise_project_id') + #: Specifies the private NAT gateway ID. + id = resource.Body('id') + #: Specifies the private NAT gateway name. + name = resource.Body('name') + #: Specifies the project ID. + project_id = resource.Body('project_id') + #: Specifies the maximum number of rules. + #: Value range: 0–65535. + rule_max = resource.Body('rule_max', type=int) + #: Specifies the private NAT gateway specifications. + #: Enumeration values: + #: * Small (default) + #: * Medium + #: * Large + #: * Extra-large + spec = resource.Body('spec') + #: Specifies the private NAT gateway status. + #: Enumeration values: + #: * ACTIVE: The private NAT gateway is running properly. + #: * FROZEN: The private NAT gateway is frozen. + status = resource.Body('status') + #: Specifies the list of tags. + #: Each tag contains: + #: * key: Specifies the tag key. Up to 128 Unicode characters. + #: * value: Specifies the tag value. Up to 255 Unicode characters. + tags = resource.Body('tags', type=list, list_type=Tag) + #: Specifies the maximum number of transit IP addresses + #: in a transit IP address pool. + #: Value range: 1–100. + transit_ip_pool_size_max = resource.Body('transit_ip_pool_size_max', + type=int) + #: Specifies the time when the private NAT gateway was updated. + #: It is a UTC time in yyyy-mm-ddThh:mm:ssZ format. + updated_at = resource.Body('updated_at') + + +class PageInfo(resource.Resource): + next_marker = resource.Body('next_marker') + previous_marker = resource.Body('previous_marker') + current_count = resource.Body('current_count', type=int) diff --git a/otcextensions/sdk/obs/v1/_proxy.py b/otcextensions/sdk/obs/v1/_proxy.py index 5702db391..018f88c54 100644 --- a/otcextensions/sdk/obs/v1/_proxy.py +++ b/otcextensions/sdk/obs/v1/_proxy.py @@ -794,10 +794,12 @@ def wait_for_delete_object(self, obj, container=None, obj = self._get_resource(_obj.Object, obj) container = self._get_container_name(obj, container) for _ in os_utils.iterate_timeout( - timeout=wait, - message=f"Timeout waiting for {obj.name} " - f"in {container} to delete", - wait=interval, + timeout=wait, + message=( + f"Timeout waiting for {obj.name} " + f"in {container} to delete" + ), + wait=interval, ): try: obj.fetch(self, container=container, skip_cache=True) @@ -825,10 +827,12 @@ def wait_for_delete_container(self, container, interval=2, wait=180): container = self._get_resource(_container.Container, container) for _ in os_utils.iterate_timeout( - timeout=wait, - message=f"Timeout waiting for container" - f" {container.name} to delete", - wait=interval, + timeout=wait, + message=( + f"Timeout waiting for container" + f" {container.name} to delete" + ), + wait=interval, ): try: self.get_container(container.name) diff --git a/otcextensions/tests/functional/osclient/privatenat/__init__.py b/otcextensions/tests/functional/osclient/privatenat/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/otcextensions/tests/functional/osclient/privatenat/v3/__init__.py b/otcextensions/tests/functional/osclient/privatenat/v3/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/otcextensions/tests/functional/osclient/privatenat/v3/test_private_nat_gateway.py b/otcextensions/tests/functional/osclient/privatenat/v3/test_private_nat_gateway.py new file mode 100644 index 000000000..aed48e4ff --- /dev/null +++ b/otcextensions/tests/functional/osclient/privatenat/v3/test_private_nat_gateway.py @@ -0,0 +1,28 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import json + +from openstackclient.tests.functional import base + + +class TestPrivateNatGateway(base.TestCase): + """Functional Tests for Private NAT Gateway""" + + def setUp(self): + super(TestPrivateNatGateway, self).setUp() + + def test_private_nat_gateway_list(self): + json_output = json.loads(self.openstack( + 'privatenat gateway list -f json ' + )) + self.assertIsNotNone(json_output) diff --git a/otcextensions/tests/functional/sdk/natv3/__init__.py b/otcextensions/tests/functional/sdk/natv3/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/otcextensions/tests/functional/sdk/natv3/v3/__init__.py b/otcextensions/tests/functional/sdk/natv3/v3/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/otcextensions/tests/functional/sdk/natv3/v3/test_gateway.py b/otcextensions/tests/functional/sdk/natv3/v3/test_gateway.py new file mode 100644 index 000000000..b417a63f1 --- /dev/null +++ b/otcextensions/tests/functional/sdk/natv3/v3/test_gateway.py @@ -0,0 +1,24 @@ +# 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 openstack +import uuid + +from otcextensions.tests.functional import base + +_logger = openstack._log.setup_logging('openstack') + + +class TestGateway(base.BaseFunctionalTest): + + def test_01_list_gateways(self): + self.gateways = list(self.conn.natv3.private_nat_gateways()) + self.assertGreaterEqual(len(self.gateways), 0) diff --git a/otcextensions/tests/unit/osclient/privatenat/__init__.py b/otcextensions/tests/unit/osclient/privatenat/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/otcextensions/tests/unit/osclient/privatenat/v3/fakes.py b/otcextensions/tests/unit/osclient/privatenat/v3/fakes.py new file mode 100644 index 000000000..995cd8150 --- /dev/null +++ b/otcextensions/tests/unit/osclient/privatenat/v3/fakes.py @@ -0,0 +1,48 @@ +# 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 uuid + +import mock + +from openstackclient.tests.unit import utils + +from otcextensions.sdk.natv3.v3 import gateway +from otcextensions.tests.unit.osclient import test_base + + +def gen_data(data, columns): + return tuple(getattr(data, attr, '') for attr in columns) + + +class TestPrivateNat(utils.TestCommand): + def setUp(self): + super(TestPrivateNat, self).setUp() + + self.app.client_manager.privatenat = mock.Mock() + + self.client = self.app.client_manager.privatenat + + +class FakePrivateNatGateway(test_base.Fake): + @classmethod + def generate(cls): + object_info = { + 'id': 'id-' + uuid.uuid4().hex, + 'name': 'name-' + uuid.uuid4().hex, + 'spec': 'Small', + 'status': 'ACTIVE', + 'project_id': 'project-' + uuid.uuid4().hex, + 'enterprise_project_id': 'ep-' + uuid.uuid4().hex, + } + + return gateway.PrivateNatGateway(**object_info) diff --git a/otcextensions/tests/unit/osclient/privatenat/v3/test_private_gateway.py b/otcextensions/tests/unit/osclient/privatenat/v3/test_private_gateway.py new file mode 100644 index 000000000..5470afed2 --- /dev/null +++ b/otcextensions/tests/unit/osclient/privatenat/v3/test_private_gateway.py @@ -0,0 +1,126 @@ +# 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 otcextensions.osclient.privatenat.v3 import private_nat_gateway +from otcextensions.tests.unit.osclient.privatenat.v3 import fakes + + +class TestListPrivateNatGateways(fakes.TestPrivateNat): + + objects = fakes.FakePrivateNatGateway.create_multiple(3) + + column_list_headers = ( + 'Id', + 'Name', + 'Spec', + 'Status', + 'Project Id', + 'Enterprise Project Id', + ) + + columns = ( + 'id', + 'name', + 'spec', + 'status', + 'project_id', + 'enterprise_project_id', + ) + + data = [] + + for s in objects: + data.append(( + s.id, + s.name, + s.spec, + s.status, + s.project_id, + s.enterprise_project_id, + )) + + def setUp(self): + super(TestListPrivateNatGateways, self).setUp() + + self.cmd = private_nat_gateway.ListPrivateNatGateways(self.app, None) + + self.client.private_nat_gateways = mock.Mock() + self.client.api_mock = self.client.private_nat_gateways + + def test_list(self): + arglist = [] + verifylist = [] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.client.api_mock.side_effect = [self.objects] + + columns, data = self.cmd.take_action(parsed_args) + + self.client.api_mock.assert_called_with() + + self.assertEqual(self.column_list_headers, columns) + self.assertEqual(self.data, list(data)) + + def test_list_args(self): + arglist = [ + '--limit', '1', + '--marker', 'm1', + '--page-reverse', + '--id', 'id1', 'id2', + '--name', 'n1', + '--description', 'd1', + '--spec', 'Small', + '--project-id', 'p1', + '--status', 'ACTIVE', + '--vpc-id', 'v1', + '--virsubnet-id', 's1', + '--enterprise-project-id', 'ep1', + ] + + verifylist = [ + ('limit', 1), + ('marker', 'm1'), + ('page_reverse', True), + ('id', ['id1', 'id2']), + ('name', ['n1']), + ('description', ['d1']), + ('spec', ['Small']), + ('project_id', ['p1']), + ('status', ['ACTIVE']), + ('vpc_id', ['v1']), + ('virsubnet_id', ['s1']), + ('enterprise_project_id', ['ep1']), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.client.api_mock.side_effect = [self.objects] + + self.cmd.take_action(parsed_args) + + self.client.api_mock.assert_called_with( + limit=1, + marker='m1', + page_reverse=True, + id=['id1', 'id2'], + name=['n1'], + description=['d1'], + spec=['Small'], + project_id=['p1'], + status=['ACTIVE'], + vpc_id=['v1'], + virsubnet_id=['s1'], + enterprise_project_id=['ep1'], + ) diff --git a/otcextensions/tests/unit/sdk/natv3/__init__.py b/otcextensions/tests/unit/sdk/natv3/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/otcextensions/tests/unit/sdk/natv3/v3/__init__.py b/otcextensions/tests/unit/sdk/natv3/v3/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/otcextensions/tests/unit/sdk/natv3/v3/test_gateway.py b/otcextensions/tests/unit/sdk/natv3/v3/test_gateway.py new file mode 100644 index 000000000..39b08ce67 --- /dev/null +++ b/otcextensions/tests/unit/sdk/natv3/v3/test_gateway.py @@ -0,0 +1,78 @@ +# 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 openstack.tests.unit import base + +from otcextensions.sdk.natv3.v3 import gateway + + +INSTANCE_ID = '14338426-6afe-4019-996b-3a9525296e11' +PROJECT_ID = '70505c941b9b4dfd82fd351932328a2f' +DOWNLINK_VPC = { + 'vpc_id': '3cb66d44-9f75-4237-bfff-e37b14d23ad2', + 'virsubnet_id': '373979ee-f4f0-46c5-80e3-0fbf72646b70', + 'ngport_ip_address': '10.0.0.17' +} +TAGS = {'key': 'key1', 'value': 'value1'} +EXAMPLE = { + 'id': INSTANCE_ID, + 'name': 'private-nat-gateway-name1', + 'description': 'private-nat-gateway-description1', + 'spec': 'Small', + 'project_id': PROJECT_ID, + 'enterprise_project_id': '2759da7b-8015-404c-ae0a-a389007b0e2a', + 'status': 'ACTIVE', + 'created_at': '2019-04-22T08:47:13', + 'updated_at': '2019-04-22T08:47:13', + 'tags': [TAGS], + 'downlink_vpcs': [DOWNLINK_VPC], + 'transit_ip_pool_size_max': 1, + 'rule_max': 20 +} + + +class TestPrivateNatGateway(base.TestCase): + + def test_basic(self): + sot = gateway.PrivateNatGateway() + self.assertEqual('gateways', sot.resources_key) + self.assertEqual('gateway', sot.resource_key) + self.assertEqual('/v3/%(project_id)s/private-nat/gateways', + sot.base_path) + self.assertTrue(sot.allow_list) + + def test_make_it(self): + sot = gateway.PrivateNatGateway(**EXAMPLE) + self.assertEqual(EXAMPLE['id'], sot.id) + self.assertEqual(EXAMPLE['project_id'], sot.project_id) + self.assertEqual(EXAMPLE['name'], sot.name) + self.assertEqual(EXAMPLE['description'], sot.description) + self.assertEqual(EXAMPLE['spec'], sot.spec) + self.assertEqual(EXAMPLE['status'], sot.status) + self.assertEqual(EXAMPLE['created_at'], sot.created_at) + self.assertEqual(EXAMPLE['updated_at'], sot.updated_at) + self.assertEqual(1, len(sot.downlink_vpcs)) + self.assertIsInstance(sot.downlink_vpcs[0], gateway.DownlinkVpc) + self.assertEqual(DOWNLINK_VPC['vpc_id'], sot.downlink_vpcs[0].vpc_id) + self.assertEqual(DOWNLINK_VPC['virsubnet_id'], + sot.downlink_vpcs[0].virsubnet_id) + self.assertEqual(DOWNLINK_VPC['ngport_ip_address'], + sot.downlink_vpcs[0].ngport_ip_address) + self.assertEqual(1, len(sot.tags)) + self.assertIsInstance(sot.tags[0], gateway.Tag) + self.assertEqual(TAGS['key'], sot.tags[0].key) + self.assertEqual(TAGS['value'], sot.tags[0].value) + self.assertEqual(EXAMPLE['enterprise_project_id'], + sot.enterprise_project_id) + self.assertEqual(EXAMPLE['rule_max'], sot.rule_max) + self.assertEqual(EXAMPLE['transit_ip_pool_size_max'], + sot.transit_ip_pool_size_max) diff --git a/otcextensions/tests/unit/sdk/natv3/v3/test_proxy.py b/otcextensions/tests/unit/sdk/natv3/v3/test_proxy.py new file mode 100644 index 000000000..efc49e49b --- /dev/null +++ b/otcextensions/tests/unit/sdk/natv3/v3/test_proxy.py @@ -0,0 +1,28 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from otcextensions.sdk.natv3.v3 import _proxy +from otcextensions.sdk.natv3.v3 import gateway + +from openstack.tests.unit import test_proxy_base + + +class TestNatv3Proxy(test_proxy_base.TestProxyBase): + def setUp(self): + super(TestNatv3Proxy, self).setUp() + self.proxy = _proxy.Proxy(self.session) + + +class TestPrivateNatGateway(TestNatv3Proxy): + def test_private_nat_gateways(self): + self.verify_list(self.proxy.private_nat_gateways, + gateway.PrivateNatGateway) diff --git a/setup.cfg b/setup.cfg index 07e96bc35..ccac91f6b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -50,6 +50,7 @@ openstack.cli.extension = deh = otcextensions.osclient.deh.client mrs = otcextensions.osclient.mrs.client nat = otcextensions.osclient.nat.client + privatenat = otcextensions.osclient.privatenat.client vpc = otcextensions.osclient.vpc.client vpcep = otcextensions.osclient.vpcep.client dlb = otcextensions.osclient.vlb.client @@ -179,6 +180,9 @@ openstack.nat.v2 = nat_dnat_rule_create = otcextensions.osclient.nat.v2.dnat:CreateDnatRule nat_dnat_rule_delete = otcextensions.osclient.nat.v2.dnat:DeleteDnatRule +openstack.privatenat.v3 = + privatenat_gateway_list = otcextensions.osclient.privatenat.v3.private_nat_gateway:ListPrivateNatGateways + openstack.auto_scaling.v1 = as_group_list = otcextensions.osclient.auto_scaling.v1.group:ListAutoScalingGroup as_group_show = otcextensions.osclient.auto_scaling.v1.group:ShowAutoScalingGroup diff --git a/tox.ini b/tox.ini index 67bc0535a..0ecc08c57 100644 --- a/tox.ini +++ b/tox.ini @@ -87,7 +87,7 @@ commands = # W503 Is supposed to be off by default but in the latest pycodestyle isn't. # Also, both openstacksdk and Donald Knuth disagree with the rule. Line # breaks should occur before the binary operator for readability. -ignore = H306,H4,W503,E231 +ignore = H306,H4,W503,E231,E713 show-source = True exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,openstack/_services_mixin.py