Add CRUD methods for Neutron Port Forwarding
Floating IP port forwarding API was implemented in Neutron in Rocky. This patch adds support for it in SDK. Depends-On: https://review.openstack.org/617045 Change-Id: Ib3f8b45e1534198f1e03223c20348fb604a91a59
This commit is contained in:
parent
19852d1578
commit
9b29b88719
|
@ -107,6 +107,7 @@
|
|||
devstack_services:
|
||||
neutron-qos: true
|
||||
neutron-trunk: true
|
||||
neutron-port-forwarding: true
|
||||
|
||||
- job:
|
||||
name: openstacksdk-functional-devstack-networking
|
||||
|
|
|
@ -32,6 +32,7 @@ from openstack.network.v2 import network_ip_availability
|
|||
from openstack.network.v2 import pool as _pool
|
||||
from openstack.network.v2 import pool_member as _pool_member
|
||||
from openstack.network.v2 import port as _port
|
||||
from openstack.network.v2 import port_forwarding as _port_forwarding
|
||||
from openstack.network.v2 import qos_bandwidth_limit_rule as \
|
||||
_qos_bandwidth_limit_rule
|
||||
from openstack.network.v2 import qos_dscp_marking_rule as \
|
||||
|
@ -589,6 +590,125 @@ class Proxy(proxy.Proxy):
|
|||
"""
|
||||
return self._update(_floating_ip.FloatingIP, floating_ip, **attrs)
|
||||
|
||||
def create_port_forwarding(self, **attrs):
|
||||
"""Create a new floating ip port forwarding from attributes
|
||||
|
||||
:param dict attrs: Keyword arguments which will be used to create
|
||||
a :class:`~openstack.network.v2.port_forwarding.PortForwarding`,
|
||||
comprised of the properties on the PortForwarding class.
|
||||
|
||||
:returns: The results of port forwarding creation
|
||||
:rtype: :class:`~openstack.network.v2.port_forwarding.PortForwarding`
|
||||
"""
|
||||
return self._create(_port_forwarding.PortForwarding, **attrs)
|
||||
|
||||
def get_port_forwarding(self, port_forwarding, floating_ip):
|
||||
"""Get a single port forwarding
|
||||
|
||||
:param port_forwarding: The value can be the ID of a port forwarding
|
||||
or a :class:`~openstack.network.v2.port_forwarding.PortForwarding`
|
||||
instance.
|
||||
:param floating_ip: The value can be the ID of a Floating IP or a
|
||||
:class:`~openstack.network.v2.floating_ip.FloatingIP`
|
||||
instance.
|
||||
|
||||
:returns: One
|
||||
:class:`~openstack.network.v2.port_forwarding.PortForwarding`
|
||||
:raises: :class:`~openstack.exceptions.ResourceNotFound`
|
||||
when no resource can be found.
|
||||
"""
|
||||
floating_ip = self._get_resource(_floating_ip.FloatingIP, floating_ip)
|
||||
return self._get(_port_forwarding.PortForwarding, port_forwarding,
|
||||
floatingip_id=floating_ip.id)
|
||||
|
||||
def find_port_forwarding(self, pf_id, floating_ip, ignore_missing=True,
|
||||
**args):
|
||||
"""Find a single port forwarding
|
||||
|
||||
:param pf_id: The ID of a port forwarding.
|
||||
:param floating_ip: The value can be the ID of a Floating IP or a
|
||||
:class:`~openstack.network.v2.floating_ip.FloatingIP`
|
||||
instance.
|
||||
:param bool ignore_missing: When set to ``False``
|
||||
:class:`~openstack.exceptions.ResourceNotFound` will be
|
||||
raised when the resource does not exist.
|
||||
When set to ``True``, None will be returned when
|
||||
attempting to find a nonexistent resource.
|
||||
:param dict args: Any additional parameters to be passed into
|
||||
underlying methods. such as query filters.
|
||||
:returns:
|
||||
One :class:`~openstack.network.v2.port_forwarding.PortForwarding`
|
||||
or None
|
||||
"""
|
||||
floating_ip = self._get_resource(_floating_ip.FloatingIP, floating_ip)
|
||||
return self._find(_port_forwarding.PortForwarding, pf_id,
|
||||
floatingip_id=floating_ip.id,
|
||||
ignore_missing=ignore_missing, **args)
|
||||
|
||||
def delete_port_forwarding(self, port_forwarding, floating_ip,
|
||||
ignore_missing=True):
|
||||
"""Delete a port forwarding
|
||||
|
||||
:param port_forwarding: The value can be the ID of a port forwarding
|
||||
or a :class:`~openstack.network.v2.port_forwarding.PortForwarding`
|
||||
instance.
|
||||
:param floating_ip: The value can be the ID of a Floating IP or a
|
||||
:class:`~openstack.network.v2.floating_ip.FloatingIP`
|
||||
instance.
|
||||
:param bool ignore_missing: When set to ``False``
|
||||
:class:`~openstack.exceptions.ResourceNotFound` will be
|
||||
raised when the floating ip does not exist.
|
||||
When set to ``True``, no exception will be set when
|
||||
attempting to delete a nonexistent ip.
|
||||
|
||||
:returns: ``None``
|
||||
"""
|
||||
fip = self._get_resource(_floating_ip.FloatingIP, floating_ip)
|
||||
self._delete(_port_forwarding.PortForwarding, port_forwarding,
|
||||
floatingip_id=fip.id,
|
||||
ignore_missing=ignore_missing)
|
||||
|
||||
def port_forwardings(self, floating_ip, **query):
|
||||
"""Return a generator of port forwardings
|
||||
|
||||
:param floating_ip: The value can be the ID of a Floating IP or a
|
||||
:class:`~openstack.network.v2.floating_ip.FloatingIP`
|
||||
instance.
|
||||
:param dict query: Optional query parameters to be sent to limit
|
||||
the resources being returned. Valid parameters are:
|
||||
|
||||
* ``internal_port_id``: The ID of internal port.
|
||||
* ``internal_ip_address``: The internal IP address
|
||||
* ``internal_port``: The internal TCP/UDP/other port number
|
||||
* ``external_port``: The external TCP/UDP/other port number
|
||||
* ``protocol``: TCP/UDP/other protocol
|
||||
|
||||
:returns: A generator of port forwarding objects
|
||||
:rtype: :class:`~openstack.network.v2.port_forwarding.PortForwarding`
|
||||
"""
|
||||
fip = self._get_resource(_floating_ip.FloatingIP, floating_ip)
|
||||
return self._list(_port_forwarding.PortForwarding, paginated=False,
|
||||
floatingip_id=fip.id, **query)
|
||||
|
||||
def update_port_forwarding(self, port_forwarding, floating_ip, **attrs):
|
||||
"""Update a port forwarding
|
||||
|
||||
:param port_forwarding: The value can be the ID of a port forwarding
|
||||
or a :class:`~openstack.network.v2.port_forwarding.PortForwarding`
|
||||
instance.
|
||||
:param floating_ip: The value can be the ID of a Floating IP or a
|
||||
:class:`~openstack.network.v2.floating_ip.FloatingIP`
|
||||
instance.
|
||||
:param dict attrs: The attributes to update on the ip represented
|
||||
by ``value``.
|
||||
|
||||
:returns: The updated port_forwarding
|
||||
:rtype: :class:`~openstack.network.v2.port_forwarding.PortForwarding`
|
||||
"""
|
||||
fip = self._get_resource(_floating_ip.FloatingIP, floating_ip)
|
||||
return self._update(_port_forwarding.PortForwarding,
|
||||
port_forwarding, floatingip_id=fip.id, **attrs)
|
||||
|
||||
def create_health_monitor(self, **attrs):
|
||||
"""Create a new health monitor from attributes
|
||||
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
# 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 PortForwarding(resource.Resource):
|
||||
name_attribute = "floating_ip_port_forwarding"
|
||||
resource_name = "port forwarding"
|
||||
resource_key = 'port_forwarding'
|
||||
resources_key = 'port_forwardings'
|
||||
base_path = '/floatingips/%(floatingip_id)s/port_forwardings'
|
||||
|
||||
# capabilities
|
||||
allow_create = True
|
||||
allow_fetch = True
|
||||
allow_commit = True
|
||||
allow_delete = True
|
||||
allow_list = True
|
||||
|
||||
# Properties
|
||||
#: The ID of Floating IP address
|
||||
floatingip_id = resource.URI('floatingip_id')
|
||||
#: The ID of internal port
|
||||
internal_port_id = resource.Body('internal_port_id')
|
||||
#: The internal IP address
|
||||
internal_ip_address = resource.Body('internal_ip_address')
|
||||
#: The internal TCP/UDP/other port number
|
||||
internal_port = resource.Body('internal_port', type=int)
|
||||
#: The external TCP/UDP/other port number
|
||||
external_port = resource.Body('external_port', type=int)
|
||||
#: The protocol
|
||||
protocol = resource.Body('protocol')
|
|
@ -0,0 +1,175 @@
|
|||
# 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.network.v2 import floating_ip
|
||||
from openstack.network.v2 import network
|
||||
from openstack.network.v2 import port_forwarding as _port_forwarding
|
||||
from openstack.network.v2 import port
|
||||
from openstack.network.v2 import router
|
||||
from openstack.network.v2 import subnet
|
||||
from openstack.tests.functional import base
|
||||
|
||||
|
||||
class TestPortForwarding(base.BaseFunctionalTest):
|
||||
|
||||
IPV4 = 4
|
||||
FIP_ID = None
|
||||
EXT_CIDR = "10.100.0.0/24"
|
||||
INT_CIDR = "10.101.0.0/24"
|
||||
EXT_NET_ID = None
|
||||
INT_NET_ID = None
|
||||
EXT_SUB_ID = None
|
||||
INT_SUB_ID = None
|
||||
ROT_ID = None
|
||||
|
||||
INTERNAL_PORT_ID = None
|
||||
INTERNAL_IP_ADDRESS = None
|
||||
INTERNAL_PORT = 8080
|
||||
EXTERNAL_PORT = 80
|
||||
PROTOCOL = "tcp"
|
||||
|
||||
def setUp(self):
|
||||
super(TestPortForwarding, self).setUp()
|
||||
|
||||
if not self.conn.network.find_extension('floating-ip-port-forwarding'):
|
||||
self.skipTest('Floating IP Port Forwarding extension disabled')
|
||||
|
||||
self.ROT_NAME = self.getUniqueString()
|
||||
self.EXT_NET_NAME = self.getUniqueString()
|
||||
self.EXT_SUB_NAME = self.getUniqueString()
|
||||
self.INT_NET_NAME = self.getUniqueString()
|
||||
self.INT_SUB_NAME = self.getUniqueString()
|
||||
# Create Exeternal Network
|
||||
args = {'router:external': True}
|
||||
net = self._create_network(self.EXT_NET_NAME, **args)
|
||||
self.EXT_NET_ID = net.id
|
||||
sub = self._create_subnet(
|
||||
self.EXT_SUB_NAME, self.EXT_NET_ID, self.EXT_CIDR)
|
||||
self.EXT_SUB_ID = sub.id
|
||||
# Create Internal Network
|
||||
net = self._create_network(self.INT_NET_NAME)
|
||||
self.INT_NET_ID = net.id
|
||||
sub = self._create_subnet(
|
||||
self.INT_SUB_NAME, self.INT_NET_ID, self.INT_CIDR)
|
||||
self.INT_SUB_ID = sub.id
|
||||
# Create Router
|
||||
args = {'external_gateway_info': {'network_id': self.EXT_NET_ID}}
|
||||
sot = self.conn.network.create_router(name=self.ROT_NAME, **args)
|
||||
assert isinstance(sot, router.Router)
|
||||
self.assertEqual(self.ROT_NAME, sot.name)
|
||||
self.ROT_ID = sot.id
|
||||
self.ROT = sot
|
||||
# Add Router's Interface to Internal Network
|
||||
sot = self.ROT.add_interface(
|
||||
self.conn.network, subnet_id=self.INT_SUB_ID)
|
||||
self.assertEqual(sot['subnet_id'], self.INT_SUB_ID)
|
||||
# Create Port in Internal Network
|
||||
prt = self.conn.network.create_port(network_id=self.INT_NET_ID)
|
||||
assert isinstance(prt, port.Port)
|
||||
self.INTERNAL_PORT_ID = prt.id
|
||||
self.INTERNAL_IP_ADDRESS = prt.fixed_ips[0]['ip_address']
|
||||
# Create Floating IP.
|
||||
fip = self.conn.network.create_ip(
|
||||
floating_network_id=self.EXT_NET_ID)
|
||||
assert isinstance(fip, floating_ip.FloatingIP)
|
||||
self.FIP_ID = fip.id
|
||||
# Create Port Forwarding
|
||||
pf = self.conn.network.create_port_forwarding(
|
||||
floatingip_id=self.FIP_ID,
|
||||
internal_port_id=self.INTERNAL_PORT_ID,
|
||||
internal_ip_address=self.INTERNAL_IP_ADDRESS,
|
||||
internal_port=self.INTERNAL_PORT,
|
||||
external_port=self.EXTERNAL_PORT,
|
||||
protocol=self.PROTOCOL)
|
||||
assert isinstance(pf, _port_forwarding.PortForwarding)
|
||||
self.PF = pf
|
||||
|
||||
def tearDown(self):
|
||||
sot = self.conn.network.delete_port_forwarding(
|
||||
self.PF, self.FIP_ID, ignore_missing=False)
|
||||
self.assertIsNone(sot)
|
||||
sot = self.conn.network.delete_ip(self.FIP_ID, ignore_missing=False)
|
||||
self.assertIsNone(sot)
|
||||
sot = self.conn.network.delete_port(
|
||||
self.INTERNAL_PORT_ID, ignore_missing=False)
|
||||
self.assertIsNone(sot)
|
||||
sot = self.ROT.remove_interface(
|
||||
self.conn.network, subnet_id=self.INT_SUB_ID)
|
||||
self.assertEqual(sot['subnet_id'], self.INT_SUB_ID)
|
||||
sot = self.conn.network.delete_router(
|
||||
self.ROT_ID, ignore_missing=False)
|
||||
self.assertIsNone(sot)
|
||||
sot = self.conn.network.delete_subnet(
|
||||
self.EXT_SUB_ID, ignore_missing=False)
|
||||
self.assertIsNone(sot)
|
||||
sot = self.conn.network.delete_network(
|
||||
self.EXT_NET_ID, ignore_missing=False)
|
||||
self.assertIsNone(sot)
|
||||
sot = self.conn.network.delete_subnet(
|
||||
self.INT_SUB_ID, ignore_missing=False)
|
||||
self.assertIsNone(sot)
|
||||
sot = self.conn.network.delete_network(
|
||||
self.INT_NET_ID, ignore_missing=False)
|
||||
self.assertIsNone(sot)
|
||||
super(TestPortForwarding, self).tearDown()
|
||||
|
||||
def _create_network(self, name, **args):
|
||||
self.name = name
|
||||
net = self.conn.network.create_network(name=name, **args)
|
||||
assert isinstance(net, network.Network)
|
||||
self.assertEqual(self.name, net.name)
|
||||
return net
|
||||
|
||||
def _create_subnet(self, name, net_id, cidr):
|
||||
self.name = name
|
||||
self.net_id = net_id
|
||||
self.cidr = cidr
|
||||
sub = self.conn.network.create_subnet(
|
||||
name=self.name,
|
||||
ip_version=self.IPV4,
|
||||
network_id=self.net_id,
|
||||
cidr=self.cidr)
|
||||
assert isinstance(sub, subnet.Subnet)
|
||||
self.assertEqual(self.name, sub.name)
|
||||
return sub
|
||||
|
||||
def test_find(self):
|
||||
sot = self.conn.network.find_port_forwarding(
|
||||
self.PF.id, self.FIP_ID)
|
||||
self.assertEqual(self.INTERNAL_PORT_ID, sot.internal_port_id)
|
||||
self.assertEqual(self.INTERNAL_IP_ADDRESS, sot.internal_ip_address)
|
||||
self.assertEqual(self.INTERNAL_PORT, sot.internal_port)
|
||||
self.assertEqual(self.EXTERNAL_PORT, sot.external_port)
|
||||
self.assertEqual(self.PROTOCOL, sot.protocol)
|
||||
|
||||
def test_get(self):
|
||||
sot = self.conn.network.get_port_forwarding(
|
||||
self.PF, self.FIP_ID)
|
||||
self.assertEqual(self.INTERNAL_PORT_ID, sot.internal_port_id)
|
||||
self.assertEqual(self.INTERNAL_IP_ADDRESS, sot.internal_ip_address)
|
||||
self.assertEqual(self.INTERNAL_PORT, sot.internal_port)
|
||||
self.assertEqual(self.EXTERNAL_PORT, sot.external_port)
|
||||
self.assertEqual(self.PROTOCOL, sot.protocol)
|
||||
|
||||
def test_list(self):
|
||||
pf_ids = [o.id for o in
|
||||
self.conn.network.port_forwardings(self.FIP_ID)]
|
||||
self.assertIn(self.PF.id, pf_ids)
|
||||
|
||||
def test_update(self):
|
||||
NEW_EXTERNAL_PORT = 90
|
||||
sot = self.conn.network.update_port_forwarding(
|
||||
self.PF.id,
|
||||
self.FIP_ID,
|
||||
external_port=NEW_EXTERNAL_PORT)
|
||||
self.assertEqual(NEW_EXTERNAL_PORT, sot.external_port)
|
|
@ -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.
|
||||
|
||||
from openstack.tests.unit import base
|
||||
|
||||
from openstack.network.v2 import port_forwarding
|
||||
|
||||
EXAMPLE = {
|
||||
'id': 'pf_id',
|
||||
'protocol': 'tcp',
|
||||
'internal_ip_address': '1.2.3.4',
|
||||
'floatingip_id': 'floating-ip-uuid',
|
||||
'internal_port': 80,
|
||||
'internal_port_id': 'internal-port-uuid',
|
||||
'external_port': 8080,
|
||||
}
|
||||
|
||||
|
||||
class TestFloatingIP(base.TestCase):
|
||||
|
||||
def test_basic(self):
|
||||
sot = port_forwarding.PortForwarding()
|
||||
self.assertEqual('port_forwarding', sot.resource_key)
|
||||
self.assertEqual('port_forwardings', sot.resources_key)
|
||||
self.assertEqual(
|
||||
'/floatingips/%(floatingip_id)s/port_forwardings',
|
||||
sot.base_path)
|
||||
self.assertTrue(sot.allow_create)
|
||||
self.assertTrue(sot.allow_fetch)
|
||||
self.assertTrue(sot.allow_commit)
|
||||
self.assertTrue(sot.allow_delete)
|
||||
self.assertTrue(sot.allow_list)
|
||||
|
||||
def test_make_it(self):
|
||||
sot = port_forwarding.PortForwarding(**EXAMPLE)
|
||||
self.assertEqual(EXAMPLE['id'], sot.id)
|
||||
self.assertEqual(EXAMPLE['floatingip_id'], sot.floatingip_id)
|
||||
self.assertEqual(EXAMPLE['protocol'], sot.protocol)
|
||||
self.assertEqual(EXAMPLE['internal_ip_address'],
|
||||
sot.internal_ip_address)
|
||||
self.assertEqual(EXAMPLE['internal_port'], sot.internal_port)
|
||||
self.assertEqual(EXAMPLE['internal_port_id'], sot.internal_port_id)
|
||||
self.assertEqual(EXAMPLE['external_port'], sot.external_port)
|
Loading…
Reference in New Issue