Create Amphora floating IP using a stack

Change-Id: I7ea1e775476fcd097ed6b56c574abf01e53d4954
This commit is contained in:
Federico Ressi 2022-04-25 15:51:58 +02:00
parent bbf99d35be
commit 87ec31f52a
7 changed files with 159 additions and 19 deletions

View File

@ -65,6 +65,7 @@ skip_if_missing_networking_extensions = (
create_floating_ip = _floating_ip.create_floating_ip
delete_floating_ip = _floating_ip.delete_floating_ip
ensure_floating_ip = _floating_ip.ensure_floating_ip
get_floating_ip = _floating_ip.get_floating_ip
get_floating_ip_id = _floating_ip.get_floating_ip_id
find_floating_ip = _floating_ip.find_floating_ip
@ -108,6 +109,7 @@ NetworkIdType = _network.NetworkIdType
add_router_interface = _router.add_router_interface
create_router = _router.create_router
delete_router = _router.delete_router
ensure_router_interface = _router.ensure_router_interface
get_router = _router.get_router
get_router_id = _router.get_router_id
remove_router_interface = _router.remove_router_interface

View File

@ -18,6 +18,9 @@ import typing
import tobiko
from tobiko.openstack.neutron import _client
from tobiko.openstack.neutron import _network
from tobiko.openstack.neutron import _port
from tobiko.openstack.neutron import _router
FloatingIpType = typing.Dict[str, typing.Any]
FloatingIpIdType = typing.Union[str, FloatingIpType]
@ -58,25 +61,28 @@ def find_floating_ip(client: _client.NeutronClientType = None,
def list_floating_ips(client: _client.NeutronClientType = None,
retrieve_all=True,
port: _port.PortIdType = None,
**params) -> tobiko.Selection[FloatingIpType]:
if port is not None:
params['port_id'] = _port.get_port_id(port)
floating_ips = _client.neutron_client(client).list_floatingips(
retrieve_all=retrieve_all, **params)['floatingips']
return tobiko.select(floating_ips)
def create_floating_ip(network: _network.NetworkIdType = None,
port: _port.PortIdType = None,
client: _client.NeutronClientType = None,
add_cleanup=True,
**params) -> FloatingIpType:
if 'floating_network_id' not in params:
if network is None:
network = params.get('floating_network_id')
if network is None:
from tobiko.openstack import stacks
network_id = stacks.get_floating_network_id()
else:
network_id = _network.get_network_id(network)
if network_id is not None:
params['floating_network_id'] = network_id
network = stacks.get_floating_network_id()
params['floating_network_id'] = _network.get_network_id(network)
if port is not None:
params['port_id'] = _port.get_port_id(port)
floating_ip: FloatingIpType = _client.neutron_client(
client).create_floatingip(body={'floatingip': params})['floatingip']
if add_cleanup:
@ -115,5 +121,21 @@ def update_floating_ip(floating_ip: FloatingIpIdType,
raise NoSuchFloatingIp(id=floating_ip_id) from ex
def ensure_floating_ip(fixed_ip_address: str,
device_id: str = None) \
-> FloatingIpType:
port = _port.find_port(device_id=device_id,
fixed_ips=[f'ip_address={fixed_ip_address}'])
try:
return find_floating_ip(port=port)
except tobiko.ObjectNotFound:
from tobiko.openstack import stacks
subnet = tobiko.select(port['fixed_ips']).with_attributes(
ip_address=fixed_ip_address).unique['subnet_id']
_router.ensure_router_interface(subnet=subnet)
fixture = stacks.FloatingIpStackFixture(port=port)
return tobiko.setup_fixture(fixture).floating_ip_details
class NoSuchFloatingIp(tobiko.ObjectNotFound):
message = "No such floating IP found for {id!r}"

View File

@ -182,5 +182,25 @@ def remove_router_interface(router: RouterIdType,
raise tobiko.ObjectNotFound() from ex
def ensure_router_interface(subnet: _subnet.SubnetIdType,
router: RouterIdType = None,
client: _client.NeutronClientType = None):
if isinstance(subnet, str):
subnet = _subnet.get_subnet(subnet=subnet, client=client)
if 'gateway_ip' not in subnet:
if router is None:
from tobiko.openstack import stacks
router = stacks.get_router_id()
LOG.debug("Add router interface: subnet={subnet['id']}")
interface = add_router_interface(router=router,
subnet=subnet,
add_cleanup=False,
client=client)
interface_dump = json.dumps(interface, sort_keys=True, indent=4)
LOG.info(f"Added router interface:\n{interface_dump}")
subnet = _subnet.get_subnet(subnet=subnet, client=client)
assert subnet['gateway_ip']
class NoSuchRouter(tobiko.ObjectNotFound):
message = "No such router found for {id!r}"

View File

@ -69,7 +69,7 @@ RouterStackFixture = _neutron.RouterStackFixture
NetworkWithNetMtuWriteStackFixture = (
_neutron.NetworkWithNetMtuWriteStackFixture)
SecurityGroupsFixture = _neutron.SecurityGroupsFixture
FloatingIpStackFixture = _neutron.FloatingIpStackFixture
get_external_network = _neutron.get_external_network
has_external_network = _neutron.has_external_network
skip_unless_has_external_network = _neutron.skip_unless_has_external_network

View File

@ -504,3 +504,50 @@ def default_nameservers(
if ip_version is not None:
nameservers = nameservers.with_attributes(version=ip_version)
return nameservers
class FloatingIpStackFixture(heat.HeatStackFixture):
#: Heat template file
template = _hot.heat_template_file('neutron/floating_ip.yaml')
router_stack = tobiko.required_fixture(RouterStackFixture)
def __init__(self,
stack_name: str = None,
network: neutron.NetworkIdType = None,
port: neutron.PortIdType = None):
self._network = network
self._port = port
if port is not None and stack_name is None:
stack_name = (f"{tobiko.get_object_name(self)}-"
f"{neutron.get_port_id(port)}")
super(FloatingIpStackFixture, self).__init__(stack_name=stack_name)
@property
def network(self) -> str:
network = self._network
if network is None:
return self.router_stack.network_id
else:
return neutron.get_network_id(network)
@property
def port(self) -> str:
port = self._port
if port is None:
raise ValueError(f"Undefined floating IP port ID for stack"
f" {self.stack_name}")
return neutron.get_port_id(port)
@property
def network_details(self) -> neutron.NetworkType:
return neutron.get_network(self.network_id)
@property
def router_details(self) -> neutron.RouterType:
return neutron.get_network(self.router_id)
@property
def floating_ip_details(self) -> neutron.FloatingIpType:
return neutron.get_floating_ip(self.floating_ip_id)

View File

@ -0,0 +1,57 @@
heat_template_version: newton
description: |
Creates a floating IP connected to an existing Neutron port
parameters:
network:
type: string
description: |
provisioner network where floating IP addresses will be allocated
constraints:
- custom_constraint: neutron.network
port:
type: string
description: |
port where fixed IP is being allocated
constraints:
- custom_constraint: neutron.port
resources:
_network:
type: OS::Neutron::Network
external_id: network
_port:
type: OS::Neutron::Port
external_id: port
_floating_ip:
type: OS::Neutron::FloatingIP
description: Floating IP address to be connected to server
properties:
floating_network: {get_resource: _network}
port_id: {get_resource: _port}
outputs:
fixed_ips:
description: fixed IP addresses of server
value: {get_attr: [_port, fixed_ips]}
floating_ip_address:
description: floating IP address of server in public network
value: { get_attr: [_floating_ip, floating_ip_address] }
netowkr_id:
value: {get_resource: _network}
port_id:
value: {get_resource: _port}

View File

@ -258,14 +258,6 @@ class OctaviaBasicFaultTest(testtools.TestCase):
ip_address=self.loadbalancer_stack.floating_ip_address,
lb_port=self.listener_stack.lb_port,
lb_protocol=self.listener_stack.lb_protocol)
# Finding the loadbalancer's management port
lb_network_ip = amphora['lb_network_ip']
port = neutron.find_port(device_id=amphora['compute_id'],
fixed_ips=[f'ip_address={lb_network_ip}'])
# Ensure there is a floating IP which is set to the LB management port
try:
floating_ip = neutron.find_floating_ip(port_id=port['id'])
except tobiko.ObjectNotFound:
floating_ip = neutron.create_floating_ip(port_id=port['id'])
return floating_ip
return neutron.ensure_floating_ip(
fixed_ip_address=amphora['lb_network_ip'],
device_id=amphora['compute_id'])