Skip to content

Commit 48ad9ff

Browse files
committed
Add transit ip delte endpoint
1 parent 9801ea2 commit 48ad9ff

13 files changed

Lines changed: 252 additions & 70 deletions

File tree

doc/source/sdk/guides/privatenat.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,3 +199,12 @@ This interface is used to assign a transit IP address.
199199

200200
.. literalinclude:: ../examples/natv3/create_private_transit_ip.py
201201
:lines: 16-28
202+
203+
Delete Private Transit IP Address
204+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
205+
206+
This interface is used to delete a transit IP address.
207+
:class:`~otcextensions.sdk.natv3.v3.transit_ip.PrivateTransitIp`.
208+
209+
.. literalinclude:: ../examples/natv3/delete_private_transit_ip.py
210+
:lines: 16-22

doc/source/sdk/proxies/privatenat.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,7 @@ Private NAT Gateway Operations
1616

1717
.. autoclass:: otcextensions.sdk.natv3.v3._proxy.Proxy
1818
:noindex:
19-
:members: private_nat_gateways, get_private_nat_gateway, private_dnat_rules, create_private_dnat_rule, update_private_dnat_rule, delete_private_dnat_rule, private_snat_rules, create_private_snat_rule, update_private_snat_rule, delete_private_snat_rule, private_transit_ips, get_private_transit_ip, create_private_transit_ip
19+
:members: private_nat_gateways, get_private_nat_gateway, private_dnat_rules, create_private_dnat_rule,
20+
update_private_dnat_rule, delete_private_dnat_rule, private_snat_rules, create_private_snat_rule,
21+
update_private_snat_rule, delete_private_snat_rule, private_transit_ips, get_private_transit_ip,
22+
create_private_transit_ip, delete_private_transit_ip
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/usr/bin/env python3
2+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
3+
# not use this file except in compliance with the License. You may obtain
4+
# a copy of the License at
5+
#
6+
# http://www.apache.org/licenses/LICENSE-2.0
7+
#
8+
# Unless required by applicable law or agreed to in writing, software
9+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11+
# License for the specific language governing permissions and limitations
12+
# under the License.
13+
"""
14+
Delete a private transit IP address.
15+
"""
16+
17+
import openstack
18+
19+
openstack.enable_logging(True)
20+
conn = openstack.connect(cloud="otc")
21+
22+
conn.natv3.delete_private_transit_ip("a2845109-3b2f-4627-b08f-09a726c0a6e7")

otcextensions/osclient/privatenat/v3/transit_ip.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import logging
1616

17+
from osc_lib import exceptions
1718
from osc_lib import utils
1819
from osc_lib.command import command
1920

@@ -262,3 +263,27 @@ def take_action(self, parsed_args):
262263
data = utils.get_item_properties(obj, columns)
263264

264265
return display_columns, data
266+
267+
268+
class DeletePrivateTransitIp(command.Command):
269+
_description = _("Delete a private transit IP address.")
270+
271+
def get_parser(self, prog_name):
272+
parser = super(DeletePrivateTransitIp, self).get_parser(prog_name)
273+
parser.add_argument(
274+
"transit_ip",
275+
metavar="<transit_ip>",
276+
help=_("Specifies the transit IP address ID."),
277+
)
278+
return parser
279+
280+
def take_action(self, parsed_args):
281+
client = self.app.client_manager.privatenat
282+
283+
try:
284+
client.delete_private_transit_ip(parsed_args.transit_ip)
285+
except Exception as e:
286+
msg = _(
287+
"Failed to delete private transit IP address with ID '%(id)s': %(e)s"
288+
) % {"id": parsed_args.transit_ip, "e": e}
289+
raise exceptions.CommandError(msg)

otcextensions/sdk/natv3/v3/_proxy.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,3 +277,21 @@ def create_private_transit_ip(self, **attrs):
277277
:rtype: :class:`~otcextensions.sdk.natv3.v3.transit_ip.PrivateTransitIp`
278278
"""
279279
return self._create(_transit_ip.PrivateTransitIp, **attrs)
280+
281+
def delete_private_transit_ip(self, transit_ip, ignore_missing=True):
282+
"""Delete a private NAT transit IP address.
283+
284+
:param transit_ip: The value can be the ID of a transit IP address or a
285+
:class:`~otcextensions.sdk.natv3.v3.transit_ip.PrivateTransitIp`
286+
instance.
287+
:param bool ignore_missing: When set to ``False``,
288+
:class:`~openstack.exceptions.ResourceNotFound` will be raised when
289+
the transit IP address does not exist. When set to ``True``, no
290+
exception will be raised when attempting to delete a nonexistent
291+
transit IP address.
292+
293+
:returns: ``None``
294+
"""
295+
return self._delete(
296+
_transit_ip.PrivateTransitIp, transit_ip, ignore_missing=ignore_missing
297+
)

otcextensions/sdk/natv3/v3/transit_ip.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ class PrivateTransitIp(resource.Resource):
2323
base_path = "/private-nat/transit-ips"
2424

2525
# capabilities
26-
allow_fetch = True
2726
allow_list = True
27+
allow_fetch = True
2828
allow_create = True
29+
allow_delete = True
2930

3031
_query_mapping = resource.QueryParameters(
3132
"limit",

otcextensions/tests/functional/osclient/privatenat/v3/test_private_transit_ip.py

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,76 @@
1414

1515
from openstackclient.tests.functional import base
1616

17+
from openstack import connection
18+
from openstack import exceptions as sdk_exceptions
19+
from otcextensions import sdk
20+
from otcextensions.tests.functional import base as sdk_base
21+
from otcextensions.tests.functional.privatenat import PrivateNatEnvironmentMixin
1722

18-
class TestPrivateTransitIp(base.TestCase):
23+
24+
class TestPrivateTransitIp(PrivateNatEnvironmentMixin, base.TestCase):
1925
"""Functional Tests for private NAT transit IP addresses"""
2026

2127
def setUp(self):
2228
super(TestPrivateTransitIp, self).setUp()
29+
self.conn = connection.Connection(config=sdk_base.TEST_CLOUD_REGION)
30+
sdk.register_otc_extensions(self.conn)
2331

2432
def test_privatenat_transit_ip_list(self):
2533
json_output = json.loads(self.openstack("privatenat transit ip list -f json "))
2634
self.assertIsNotNone(json_output)
35+
36+
def test_privatenat_transit_ip_create(self):
37+
env = self._prepare_private_nat_subnet_environment(
38+
"cli-private-transit-ip-create"
39+
)
40+
41+
created = json.loads(
42+
self.openstack(
43+
"privatenat transit ip create "
44+
"--virsubnet-id {virsubnet_id} "
45+
"--tags test=cli "
46+
"-f json".format(virsubnet_id=env["subnet"].id)
47+
)
48+
)
49+
self.addCleanup(self._delete_private_transit_ip, created["id"])
50+
51+
self.assertIsNotNone(created)
52+
self.assertEqual("ACTIVE", created["status"])
53+
54+
def test_privatenat_transit_ip_show(self):
55+
env = self._prepare_private_nat_subnet_environment(
56+
"cli-private-transit-ip-show"
57+
)
58+
59+
created = json.loads(
60+
self.openstack(
61+
"privatenat transit ip create "
62+
"--virsubnet-id {virsubnet_id} "
63+
"-f json".format(virsubnet_id=env["subnet"].id)
64+
)
65+
)
66+
self.addCleanup(self._delete_private_transit_ip, created["id"])
67+
68+
shown = json.loads(
69+
self.openstack("privatenat transit ip show -f json " + created["id"])
70+
)
71+
self.assertEqual(created["id"], shown["id"])
72+
73+
def test_privatenat_transit_ip_delete(self):
74+
env = self._prepare_private_nat_subnet_environment(
75+
"cli-private-transit-ip-delete"
76+
)
77+
78+
created = json.loads(
79+
self.openstack(
80+
"privatenat transit ip create "
81+
"--virsubnet-id {virsubnet_id} "
82+
"-f json".format(virsubnet_id=env["subnet"].id)
83+
)
84+
)
85+
86+
self.openstack("privatenat transit ip delete " + created["id"])
87+
88+
with self.assertRaises(sdk_exceptions.ResourceNotFound):
89+
self.conn.natv3.get_private_transit_ip(created["id"])

otcextensions/tests/functional/privatenat.py

Lines changed: 43 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,28 @@
1414

1515
from openstack import exceptions as sdk_exceptions
1616
from openstack import resource
17-
from otcextensions.tests.functional import base
1817

1918

2019
class PrivateNatEnvironmentMixin(object):
21-
def _private_transit_ip_id(self):
22-
transit_ip_id = base._get_resource_value("private_transit_ip_id", None)
23-
if not transit_ip_id:
24-
self.skipTest("functional.private_transit_ip_id is required")
25-
return transit_ip_id
20+
def _delete_private_transit_ip(self, transit_ip_id):
21+
self.conn.natv3.delete_private_transit_ip(transit_ip_id, ignore_missing=True)
2622

27-
def _create_private_nat_network_stack(self, prefix):
28-
cidr = "192.168.0.0/16"
29-
suffix = uuid.uuid4().hex[:8]
23+
def _create_private_nat_vpc(self, prefix, suffix):
3024
vpc_name = "{prefix}-vpc-{suffix}".format(prefix=prefix, suffix=suffix)
31-
subnet_name = "{prefix}-subnet-{suffix}".format(prefix=prefix, suffix=suffix)
25+
return self.conn.vpc.create_vpc(name=vpc_name, cidr="192.168.0.0/16")
26+
27+
def _delete_private_nat_vpc(self, vpc):
28+
try:
29+
vpc = self.conn.vpc.get_vpc(vpc.id)
30+
except sdk_exceptions.ResourceNotFound:
31+
return
32+
33+
self.conn.vpc.delete_vpc(vpc, ignore_missing=True)
34+
resource.wait_for_delete(self.conn.vpc, vpc, 2, 120)
3235

33-
vpc = self.conn.vpc.create_vpc(name=vpc_name, cidr=cidr)
34-
gw, _ = cidr.split("/")
36+
def _create_private_nat_subnet(self, vpc, prefix, suffix):
37+
subnet_name = "{prefix}-subnet-{suffix}".format(prefix=prefix, suffix=suffix)
38+
gw, _ = vpc.cidr.split("/")
3539
subnet = self.conn.vpc.create_subnet(
3640
name=subnet_name,
3741
vpc_id=vpc.id,
@@ -40,6 +44,22 @@ def _create_private_nat_network_stack(self, prefix):
4044
dns_list=["100.125.4.25", "100.125.129.199"],
4145
)
4246
resource.wait_for_status(self.conn.vpc, subnet, "ACTIVE", None, 2, 60)
47+
return subnet
48+
49+
def _delete_private_nat_subnet(self, subnet):
50+
try:
51+
subnet = self.conn.vpc.get_subnet(subnet.id)
52+
except sdk_exceptions.ResourceNotFound:
53+
return
54+
55+
resource.wait_for_status(self.conn.vpc, subnet, "ACTIVE", None, 2, 60)
56+
self.conn.vpc.delete_subnet(subnet, ignore_missing=True)
57+
resource.wait_for_delete(self.conn.vpc, subnet, 2, 120)
58+
59+
def _create_private_nat_network_stack(self, prefix):
60+
suffix = uuid.uuid4().hex[:8]
61+
vpc = self._create_private_nat_vpc(prefix, suffix)
62+
subnet = self._create_private_nat_subnet(vpc, prefix, suffix)
4363

4464
return {
4565
"vpc": vpc,
@@ -52,23 +72,19 @@ def _cleanup_private_nat_network_stack(self, stack):
5272
vpc = stack.get("vpc")
5373

5474
if subnet:
55-
try:
56-
subnet = self.conn.vpc.get_subnet(subnet.id)
57-
except sdk_exceptions.ResourceNotFound:
58-
subnet = None
59-
if subnet:
60-
resource.wait_for_status(self.conn.vpc, subnet, "ACTIVE", None, 2, 60)
61-
self.conn.vpc.delete_subnet(subnet, ignore_missing=True)
62-
resource.wait_for_delete(self.conn.vpc, subnet, 2, 120)
75+
self._delete_private_nat_subnet(subnet)
6376

6477
if vpc:
65-
try:
66-
vpc = self.conn.vpc.get_vpc(vpc.id)
67-
except sdk_exceptions.ResourceNotFound:
68-
vpc = None
69-
if vpc:
70-
self.conn.vpc.delete_vpc(vpc, ignore_missing=True)
71-
resource.wait_for_delete(self.conn.vpc, vpc, 2, 120)
78+
self._delete_private_nat_vpc(vpc)
79+
80+
def _prepare_private_nat_subnet_environment(self, prefix):
81+
suffix = uuid.uuid4().hex[:8]
82+
vpc = self._create_private_nat_vpc(prefix, suffix)
83+
self.addCleanup(self._delete_private_nat_vpc, vpc)
84+
85+
subnet = self._create_private_nat_subnet(vpc, prefix, suffix)
86+
self.addCleanup(self._delete_private_nat_subnet, subnet)
87+
return {"vpc": vpc, "subnet": subnet}
7288

7389
def _create_private_nat_gateway(self, subnet_id, prefix):
7490
gateway = self.conn.natv3.create_private_nat_gateway(
@@ -90,39 +106,6 @@ def _delete_private_nat_gateway(self, gateway):
90106
self.conn.natv3.delete_private_nat_gateway(gateway, ignore_missing=True)
91107
resource.wait_for_delete(self.conn.natv3, gateway, 2, 120)
92108

93-
def _create_private_nat_port(self, network_id, prefix):
94-
if hasattr(self, "create_port"):
95-
return self.create_port(network_id, prefix=prefix)
96-
return self.conn.network.create_port(
97-
name="{prefix}-port-{suffix}".format(
98-
prefix=prefix, suffix=uuid.uuid4().hex[:8]
99-
),
100-
network_id=network_id,
101-
)
102-
103-
def _delete_private_nat_port(self, port):
104-
if hasattr(self, "destroy_port"):
105-
try:
106-
self.destroy_port(port)
107-
except sdk_exceptions.ResourceNotFound:
108-
return
109-
return
110-
self.conn.network.delete_port(port, ignore_missing=True)
111-
112-
def _prepare_private_nat_environment(self, prefix):
113-
env = {
114-
"transit_ip_id": self._private_transit_ip_id(),
115-
}
116-
117-
stack = self._prepare_private_nat_gateway_environment(prefix)
118-
env["stack"] = stack["stack"]
119-
env["gateway"] = stack["gateway"]
120-
121-
port = self._create_private_nat_port(stack["stack"]["network_id"], prefix)
122-
self.addCleanup(self._delete_private_nat_port, port)
123-
env["port"] = port
124-
return env
125-
126109
def _prepare_private_nat_gateway_environment(self, prefix):
127110
stack = self._create_private_nat_network_stack(prefix)
128111
self.addCleanup(self._cleanup_private_nat_network_stack, stack)

otcextensions/tests/functional/sdk/natv3/v3/test_private_transit_ip.py

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,53 @@
1111
# under the License.
1212

1313
import openstack
14+
from openstack import exceptions as sdk_exceptions
1415
from otcextensions.tests.functional import base
16+
from otcextensions.tests.functional.privatenat import PrivateNatEnvironmentMixin
1517

1618
_logger = openstack._log.setup_logging("openstack")
1719

1820

19-
class TestPrivateTransitIp(base.BaseFunctionalTest):
20-
21+
class TestPrivateTransitIp(PrivateNatEnvironmentMixin, base.BaseFunctionalTest):
2122
def test_list_transit_ips(self):
2223
transit_ips = list(self.conn.natv3.private_transit_ips())
2324
self.assertGreaterEqual(len(transit_ips), 0)
2425

2526
def test_get_private_transit_ip(self):
26-
transit_ip_id = base._get_resource_value("private_transit_ip_id", None)
27-
if not transit_ip_id:
28-
self.skipTest("functional.private_transit_ip_id is required")
27+
env = self._prepare_private_nat_subnet_environment("sdk-private-transit-ip-get")
28+
29+
created = self.conn.natv3.create_private_transit_ip(
30+
virsubnet_id=env["subnet"].id
31+
)
32+
self.addCleanup(self._delete_private_transit_ip, created.id)
33+
34+
transit_ip = self.conn.natv3.get_private_transit_ip(created.id)
35+
self.assertEqual(created.id, transit_ip.id)
36+
37+
def test_create_private_transit_ip(self):
38+
env = self._prepare_private_nat_subnet_environment(
39+
"sdk-private-transit-ip-create"
40+
)
41+
42+
transit_ip = self.conn.natv3.create_private_transit_ip(
43+
virsubnet_id=env["subnet"].id,
44+
tags=[{"key": "test", "value": "sdk"}],
45+
)
46+
self.addCleanup(self._delete_private_transit_ip, transit_ip.id)
47+
48+
self.assertIsNotNone(transit_ip.id)
49+
self.assertEqual("ACTIVE", transit_ip.status)
50+
51+
def test_delete_private_transit_ip(self):
52+
env = self._prepare_private_nat_subnet_environment(
53+
"sdk-private-transit-ip-delete"
54+
)
55+
56+
transit_ip = self.conn.natv3.create_private_transit_ip(
57+
virsubnet_id=env["subnet"].id
58+
)
59+
60+
self.conn.natv3.delete_private_transit_ip(transit_ip, ignore_missing=False)
2961

30-
transit_ip = self.conn.natv3.get_private_transit_ip(transit_ip_id)
31-
self.assertEqual(transit_ip_id, transit_ip.id)
62+
with self.assertRaises(sdk_exceptions.ResourceNotFound):
63+
self.conn.natv3.get_private_transit_ip(transit_ip.id)

0 commit comments

Comments
 (0)