Fix plugging member subnets on existing networks

CalculateAmphoraDelta uses now subnest instead of networks.
HandleNetworkDelta plugs subnets on existing ports, or removes subnet
from ports
The Amphora server supports multiple 'plug' calls to the same port,
allowing to update the settings of an interface.

Co-Authored-By: Gregory Thiemonge <gthiemon@redhat.com>

Depends-On: https://review.opendev.org/815313

Change-Id: I1384c6f52eec99e6573a8e83fe5a80a632804083
This commit is contained in:
Adam Harwell 2019-06-14 07:13:02 -07:00 committed by Gregory Thiemonge
parent fb8cff9347
commit 599c8dd33a
45 changed files with 2664 additions and 529 deletions

View File

@ -66,7 +66,7 @@ class BaseOS(object):
def write_vip_interface_file(self, interface, vip, ip_version,
prefixlen, gateway,
mtu, vrrp_ip,
host_routes):
host_routes, fixed_ips=None):
vip_interface = interface_file.VIPInterfaceFile(
name=interface,
mtu=mtu,
@ -76,6 +76,7 @@ class BaseOS(object):
gateway=gateway,
vrrp_ip=vrrp_ip,
host_routes=host_routes,
fixed_ips=fixed_ips,
topology=CONF.controller_worker.loadbalancer_topology)
vip_interface.write()
@ -94,7 +95,8 @@ class BaseOS(object):
try:
out = subprocess.check_output(cmd.split(),
stderr=subprocess.STDOUT)
LOG.debug(out)
for line in out.decode('utf-8').split('\n'):
LOG.debug(line)
except subprocess.CalledProcessError as e:
LOG.error('Failed to set up %s due to error: %s %s', interface,
e, e.output)

View File

@ -132,16 +132,52 @@ class Plug(object):
except socket.error:
socket.inet_pton(socket.AF_INET6, ip.get('ip_address'))
def plug_network(self, mac_address, fixed_ips, mtu=None):
def plug_network(self, mac_address, fixed_ips, mtu=None,
vip_net_info=None):
# Check if the interface is already in the network namespace
# Do not attempt to re-plug the network if it is already in the
# network namespace
# network namespace, just ensure all fixed_ips are up
if self._netns_interface_exists(mac_address):
return webob.Response(json=dict(
message="Interface already exists"), status=409)
# Get the existing interface name and path
existing_interface = self._netns_interface_by_mac(mac_address)
# This is the interface as it was initially plugged into the
# default network namespace, this will likely always be eth1
# If we have net_info, this is the special case of plugging a new
# subnet on the vrrp port, which is essentially a re-vip-plug
if vip_net_info:
ip = ipaddress.ip_address(vip_net_info['vip'])
network = ipaddress.ip_network(vip_net_info['subnet_cidr'])
vip = ip.exploded
prefixlen = network.prefixlen
vrrp_ip = vip_net_info.get('vrrp_ip')
gateway = vip_net_info['gateway']
host_routes = vip_net_info.get('host_routes', ())
self._osutils.write_vip_interface_file(
interface=existing_interface,
vip=vip,
ip_version=ip.version,
prefixlen=prefixlen,
gateway=gateway,
vrrp_ip=vrrp_ip,
host_routes=host_routes,
mtu=mtu,
fixed_ips=fixed_ips)
self._osutils.bring_interface_up(existing_interface, 'vip')
# Otherwise, we are just plugging a run-of-the-mill network
else:
# Write an updated config
self._osutils.write_port_interface_file(
interface=existing_interface,
fixed_ips=fixed_ips,
mtu=mtu)
self._osutils.bring_interface_up(existing_interface, 'network')
return webob.Response(json=dict(
message="OK",
details="Updated existing interface {interface}".format(
# TODO(rm_work): Everything in this should probably use
# HTTP code 200, but continuing to use 202 for consistency.
interface=existing_interface)), status=202)
try:
self._check_ip_addresses(fixed_ips=fixed_ips)
@ -149,6 +185,8 @@ class Plug(object):
return webob.Response(json=dict(
message="Invalid network port"), status=400)
# This is the interface as it was initially plugged into the
# default network namespace, this will likely always be eth1
default_netns_interface = self._interface_by_mac(mac_address)
# We need to determine the interface name when inside the namespace
@ -222,11 +260,14 @@ class Plug(object):
text_file.write("{mac_address} {interface}\n".format(
mac_address=mac_address, interface=interface))
def _netns_interface_exists(self, mac_address):
def _netns_interface_by_mac(self, mac_address):
with pyroute2.NetNS(consts.AMPHORA_NAMESPACE,
flags=os.O_CREAT) as netns:
for link in netns.get_links():
for attr in link['attrs']:
if attr[0] == 'IFLA_ADDRESS' and attr[1] == mac_address:
return True
return False
attr_dict = dict(link['attrs'])
if attr_dict.get('IFLA_ADDRESS') == mac_address:
return attr_dict.get('IFLA_IFNAME')
return None
def _netns_interface_exists(self, mac_address):
return self._netns_interface_by_mac(mac_address) is not None

View File

@ -214,7 +214,8 @@ class Server(object):
description='Invalid port information') from e
return self._plug.plug_network(port_info['mac_address'],
port_info.get('fixed_ips'),
port_info.get('mtu'))
port_info.get('mtu'),
port_info.get('vip_net_info'))
def upload_cert(self):
return certificate_update.upload_server_cert()

View File

@ -162,6 +162,8 @@ class AmphoraLoadBalancerDriver(object, metaclass=abc.ABCMeta):
:param amphorae_network_config: A data model containing information
about the subnets and ports that an
amphorae owns.
:type amphorae_network_config: octavia.network.data_models.
AmphoraNetworkConfig
:param vrrp_port: VRRP port associated with the load balancer
:type vrrp_port: octavia.network.data_models.Port
@ -175,13 +177,18 @@ class AmphoraLoadBalancerDriver(object, metaclass=abc.ABCMeta):
the vip, such as bring up interfaces.
"""
def post_network_plug(self, amphora, port):
def post_network_plug(self, amphora, port, amphora_network_config):
"""Called after amphora added to network
:param amphora: amphora object, needs id and network ip(s)
:type amphora: octavia.db.models.Amphora
:param port: contains information of the plugged port
:type port: octavia.network.data_models.Port
:param amphora_network_config: A data model containing information
about the subnets and ports that an
amphorae owns.
:type amphora_network_config: octavia.network.data_models.
AmphoraNetworkConfig
This method is optional to implement. After adding an amphora to a
network, there may be steps necessary on the amphora to allow it to

View File

@ -39,6 +39,7 @@ from octavia.common.tls_utils import cert_parser
from octavia.common import utils
from octavia.db import api as db_apis
from octavia.db import repositories as repo
from octavia.network import data_models as network_models
LOG = logging.getLogger(__name__)
@ -381,23 +382,33 @@ class HaproxyAmphoraLoadBalancerDriver(
def finalize_amphora(self, amphora):
pass
def _build_net_info(self, port, amphora, subnet, mtu=None):
# NOTE(blogan): using the vrrp port here because that
# is what the allowed address pairs network driver sets
# this particular port to. This does expose a bit of
# tight coupling between the network driver and amphora
# driver. We will need to revisit this to try and remove
# this tight coupling.
# NOTE (johnsom): I am loading the vrrp_ip into the
# net_info structure here so that I don't break
# compatibility with old amphora agent versions.
host_routes = [{'nexthop': hr[consts.NEXTHOP],
'destination': hr[consts.DESTINATION]}
for hr in subnet[consts.HOST_ROUTES]]
net_info = {'subnet_cidr': subnet[consts.CIDR],
'gateway': subnet[consts.GATEWAY_IP],
'mac_address': port[consts.MAC_ADDRESS],
'vrrp_ip': amphora[consts.VRRP_IP],
'mtu': mtu or port[consts.NETWORK][consts.MTU],
'host_routes': host_routes}
return net_info
def post_vip_plug(self, amphora, load_balancer, amphorae_network_config,
vrrp_port=None, vip_subnet=None):
if amphora.status != consts.DELETED:
self._populate_amphora_api_version(amphora)
if vip_subnet is None:
subnet = amphorae_network_config.get(amphora.id).vip_subnet
else:
subnet = vip_subnet
# NOTE(blogan): using the vrrp port here because that
# is what the allowed address pairs network driver sets
# this particular port to. This does expose a bit of
# tight coupling between the network driver and amphora
# driver. We will need to revisit this to try and remove
# this tight coupling.
# NOTE (johnsom): I am loading the vrrp_ip into the
# net_info structure here so that I don't break
# compatibility with old amphora agent versions.
vip_subnet = amphorae_network_config.get(amphora.id).vip_subnet
if vrrp_port is None:
port = amphorae_network_config.get(amphora.id).vrrp_port
mtu = port.network.mtu
@ -406,15 +417,9 @@ class HaproxyAmphoraLoadBalancerDriver(
mtu = port.network['mtu']
LOG.debug("Post-VIP-Plugging with vrrp_ip %s vrrp_port %s",
amphora.vrrp_ip, port.id)
host_routes = [{'nexthop': hr.nexthop,
'destination': hr.destination}
for hr in subnet.host_routes]
net_info = {'subnet_cidr': subnet.cidr,
'gateway': subnet.gateway_ip,
'mac_address': port.mac_address,
'vrrp_ip': amphora.vrrp_ip,
'mtu': mtu,
'host_routes': host_routes}
net_info = self._build_net_info(
port.to_dict(recurse=True), amphora.to_dict(),
vip_subnet.to_dict(recurse=True), mtu)
try:
self.clients[amphora.api_version].plug_vip(
amphora, load_balancer.vip.ip_address, net_info)
@ -423,7 +428,7 @@ class HaproxyAmphoraLoadBalancerDriver(
'skipping post_vip_plug',
{'mac': port.mac_address})
def post_network_plug(self, amphora, port):
def post_network_plug(self, amphora, port, amphora_network_config):
fixed_ips = []
for fixed_ip in port.fixed_ips:
host_routes = [{'nexthop': hr.nexthop,
@ -431,11 +436,25 @@ class HaproxyAmphoraLoadBalancerDriver(
for hr in fixed_ip.subnet.host_routes]
ip = {'ip_address': fixed_ip.ip_address,
'subnet_cidr': fixed_ip.subnet.cidr,
'host_routes': host_routes}
'host_routes': host_routes,
'gateway': fixed_ip.subnet.gateway_ip}
fixed_ips.append(ip)
port_info = {'mac_address': port.mac_address,
'fixed_ips': fixed_ips,
'mtu': port.network.mtu}
if port.id == amphora.vrrp_port_id:
if isinstance(amphora_network_config,
network_models.AmphoraNetworkConfig):
amphora_network_config = amphora_network_config.to_dict(
recurse=True)
# We have to special-case sharing the vrrp port and pass through
# enough extra information to populate the whole VIP port
net_info = self._build_net_info(
port.to_dict(recurse=True), amphora.to_dict(),
amphora_network_config[consts.VIP_SUBNET],
port.network.mtu)
net_info['vip'] = amphora.ha_ip
port_info['vip_net_info'] = net_info
try:
self._populate_amphora_api_version(amphora)
self.clients[amphora.api_version].plug_network(amphora, port_info)

View File

@ -85,9 +85,10 @@ class NoopManager(object):
self.__class__.__name__, amphora.id)
self.amphoraconfig[amphora.id] = (amphora.id, 'finalize amphora')
def post_network_plug(self, amphora, port):
LOG.debug("Amphora %s no-op, post network plug amphora %s, port %s",
self.__class__.__name__, amphora.id, port.id)
def post_network_plug(self, amphora, port, amphora_network_config):
LOG.debug("Amphora %s no-op, post network plug amphora %s, port %s, "
"amphora_network_config %s", self.__class__.__name__,
amphora.id, port.id, amphora_network_config)
self.amphoraconfig[amphora.id, port.id] = (amphora.id, port.id,
'post_network_plug')
@ -160,9 +161,9 @@ class NoopAmphoraLoadBalancerDriver(
self.driver.finalize_amphora(amphora)
def post_network_plug(self, amphora, port):
def post_network_plug(self, amphora, port, amphora_network_config):
self.driver.post_network_plug(amphora, port)
self.driver.post_network_plug(amphora, port, amphora_network_config)
def post_vip_plug(self, amphora, load_balancer, amphorae_network_config,
vrrp_port=None, vip_subnet=None):

View File

@ -298,7 +298,7 @@ SUPPORTED_TASKFLOW_ENGINE_TYPES = ['serial', 'parallel']
# Task/Flow constants
ACTIVE_CONNECTIONS = 'active_connections'
ADD_NICS = 'add_nics'
ADDED_PORTS = 'added_ports'
ADD_SUBNETS = 'add_subnets'
ADMIN_STATE_UP = 'admin_state_up'
ALLOWED_ADDRESS_PAIRS = 'allowed_address_pairs'
AMP_DATA = 'amp_data'
@ -330,9 +330,11 @@ CREATED_AT = 'created_at'
CRL_CONTAINER_ID = 'crl_container_id'
DEFAULT_TLS_CONTAINER_DATA = 'default_tls_container_data'
DELETE_NICS = 'delete_nics'
DELETE_SUBNETS = 'delete_subnets'
DELTA = 'delta'
DELTAS = 'deltas'
DESCRIPTION = 'description'
DESTINATION = 'destination'
DEVICE_OWNER = 'device_owner'
ENABLED = 'enabled'
FAILED_AMP_VRRP_PORT_ID = 'failed_amp_vrrp_port_id'
@ -342,6 +344,7 @@ FAILOVER_AMPHORA_ID = 'failover_amphora_id'
FIELDS = 'fields'
FIXED_IPS = 'fixed_ips'
FLAVOR_ID = 'flavor_id'
GATEWAY_IP = 'gateway_ip'
HA_IP = 'ha_ip'
HA_PORT_ID = 'ha_port_id'
HEALTH_MON = 'health_mon'
@ -349,6 +352,7 @@ HEALTH_MONITOR = 'health_monitor'
HEALTH_MONITOR_ID = 'health_monitor_id'
HEALTHMONITOR_ID = 'healthmonitor_id'
HEALTH_MONITOR_UPDATES = 'health_monitor_updates'
HOST_ROUTES = 'host_routes'
ID = 'id'
IMAGE_ID = 'image_id'
IP_ADDRESS = 'ip_address'
@ -367,6 +371,7 @@ LOADBALANCER = 'loadbalancer'
LOADBALANCER_ID = 'loadbalancer_id'
LOAD_BALANCER_ID = 'load_balancer_id'
LOAD_BALANCER_UPDATES = 'load_balancer_updates'
MAC_ADDRESS = 'mac_address'
MANAGEMENT_NETWORK = 'management_network'
MEMBER = 'member'
MEMBER_ID = 'member_id'
@ -376,6 +381,7 @@ MESSAGE = 'message'
NAME = 'name'
NETWORK = 'network'
NETWORK_ID = 'network_id'
NEXTHOP = 'nexthop'
NICS = 'nics'
OBJECT = 'object'
ORIGINAL_HEALTH_MONITOR = 'original_health_monitor'
@ -423,10 +429,12 @@ TOPOLOGY = 'topology'
TOTAL_CONNECTIONS = 'total_connections'
UPDATED_AT = 'updated_at'
UPDATE_DICT = 'update_dict'
UPDATED_PORTS = 'updated_ports'
VALID_VIP_NETWORKS = 'valid_vip_networks'
VIP = 'vip'
VIP_ADDRESS = 'vip_address'
VIP_NETWORK = 'vip_network'
VIP_NETWORK_ID = 'vip_network_id'
VIP_PORT_ID = 'vip_port_id'
VIP_QOS_POLICY_ID = 'vip_qos_policy_id'
VIP_SG_ID = 'vip_sg_id'

View File

@ -429,6 +429,7 @@ class ControllerWorker(base_taskflow.BaseTaskFlowEngine):
constants.MEMBER: member,
constants.LISTENERS: listeners,
constants.LOADBALANCER: load_balancer,
constants.LOADBALANCER_ID: load_balancer.id,
constants.POOL: pool}
if load_balancer.availability_zone:
store[constants.AVAILABILITY_ZONE] = (
@ -461,6 +462,7 @@ class ControllerWorker(base_taskflow.BaseTaskFlowEngine):
constants.MEMBER: member,
constants.LISTENERS: listeners,
constants.LOADBALANCER: load_balancer,
constants.LOADBALANCER_ID: load_balancer.id,
constants.POOL: pool}
if load_balancer.availability_zone:
store[constants.AVAILABILITY_ZONE] = (

View File

@ -443,11 +443,11 @@ class AmphoraFlows(object):
amp_for_failover_flow.add(network_tasks.HandleNetworkDelta(
name=prefix + '-' + constants.HANDLE_NETWORK_DELTA,
requires=(constants.AMPHORA, constants.DELTA),
provides=constants.ADDED_PORTS))
provides=constants.UPDATED_PORTS))
amp_for_failover_flow.add(amphora_driver_tasks.AmphoraePostNetworkPlug(
name=prefix + '-' + constants.AMPHORAE_POST_NETWORK_PLUG,
requires=(constants.LOADBALANCER, constants.ADDED_PORTS)))
requires=(constants.LOADBALANCER, constants.UPDATED_PORTS)))
return amp_for_failover_flow

View File

@ -202,12 +202,12 @@ class LoadBalancerFlows(object):
)
flows.append(
network_tasks.HandleNetworkDeltas(
requires=constants.DELTAS, provides=constants.ADDED_PORTS
requires=constants.DELTAS, provides=constants.UPDATED_PORTS
)
)
flows.append(
amphora_driver_tasks.AmphoraePostNetworkPlug(
requires=(constants.LOADBALANCER, constants.ADDED_PORTS)
requires=(constants.LOADBALANCER, constants.UPDATED_PORTS)
)
)
flows.append(

View File

@ -43,10 +43,14 @@ class MemberFlows(object):
requires=(constants.LOADBALANCER, constants.AVAILABILITY_ZONE),
provides=constants.DELTAS))
create_member_flow.add(network_tasks.HandleNetworkDeltas(
requires=constants.DELTAS, provides=constants.ADDED_PORTS))
requires=(constants.DELTAS, constants.LOADBALANCER),
provides=constants.UPDATED_PORTS))
create_member_flow.add(network_tasks.GetAmphoraeNetworkConfigs(
requires=constants.LOADBALANCER_ID,
provides=constants.AMPHORAE_NETWORK_CONFIG))
create_member_flow.add(amphora_driver_tasks.AmphoraePostNetworkPlug(
requires=(constants.LOADBALANCER, constants.ADDED_PORTS)
))
requires=(constants.LOADBALANCER, constants.UPDATED_PORTS,
constants.AMPHORAE_NETWORK_CONFIG)))
create_member_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=constants.LOADBALANCER))
create_member_flow.add(database_tasks.MarkMemberActiveInDB(
@ -73,6 +77,18 @@ class MemberFlows(object):
constants.POOL]))
delete_member_flow.add(database_tasks.MarkMemberPendingDeleteInDB(
requires=constants.MEMBER))
delete_member_flow.add(network_tasks.CalculateDelta(
requires=(constants.LOADBALANCER, constants.AVAILABILITY_ZONE),
provides=constants.DELTAS))
delete_member_flow.add(network_tasks.HandleNetworkDeltas(
requires=(constants.DELTAS, constants.LOADBALANCER),
provides=constants.UPDATED_PORTS))
delete_member_flow.add(network_tasks.GetAmphoraeNetworkConfigs(
requires=constants.LOADBALANCER_ID,
provides=constants.AMPHORAE_NETWORK_CONFIG))
delete_member_flow.add(amphora_driver_tasks.AmphoraePostNetworkPlug(
requires=(constants.LOADBALANCER, constants.UPDATED_PORTS,
constants.AMPHORAE_NETWORK_CONFIG)))
delete_member_flow.add(model_tasks.
DeleteModelObject(rebind={constants.OBJECT:
constants.MEMBER}))
@ -188,10 +204,15 @@ class MemberFlows(object):
requires=(constants.LOADBALANCER, constants.AVAILABILITY_ZONE),
provides=constants.DELTAS))
batch_update_members_flow.add(network_tasks.HandleNetworkDeltas(
requires=constants.DELTAS, provides=constants.ADDED_PORTS))
requires=(constants.DELTAS, constants.LOADBALANCER),
provides=constants.UPDATED_PORTS))
batch_update_members_flow.add(network_tasks.GetAmphoraeNetworkConfigs(
requires=constants.LOADBALANCER_ID,
provides=constants.AMPHORAE_NETWORK_CONFIG))
batch_update_members_flow.add(
amphora_driver_tasks.AmphoraePostNetworkPlug(
requires=(constants.LOADBALANCER, constants.ADDED_PORTS)))
requires=(constants.LOADBALANCER, constants.UPDATED_PORTS,
constants.AMPHORAE_NETWORK_CONFIG)))
# Update the Listener (this makes the changes active on the Amp)
batch_update_members_flow.add(amphora_driver_tasks.ListenersUpdate(

View File

@ -196,10 +196,11 @@ class AmphoraFinalize(BaseAmphoraTask):
class AmphoraPostNetworkPlug(BaseAmphoraTask):
"""Task to notify the amphora post network plug."""
def execute(self, amphora, ports):
def execute(self, amphora, ports, amphora_network_config):
"""Execute post_network_plug routine."""
for port in ports:
self.amphora_driver.post_network_plug(amphora, port)
self.amphora_driver.post_network_plug(
amphora, port, amphora_network_config)
LOG.debug("post_network_plug called on compute instance "
"%(compute_id)s for port %(port_id)s",
{"compute_id": amphora.compute_id, "port_id": port.id})
@ -215,7 +216,7 @@ class AmphoraPostNetworkPlug(BaseAmphoraTask):
class AmphoraePostNetworkPlug(BaseAmphoraTask):
"""Task to notify the amphorae post network plug."""
def execute(self, loadbalancer, added_ports):
def execute(self, loadbalancer, updated_ports, amphorae_network_config):
"""Execute post_network_plug routine."""
amp_post_plug = AmphoraPostNetworkPlug()
# We need to make sure we have the fresh list of amphora
@ -223,10 +224,11 @@ class AmphoraePostNetworkPlug(BaseAmphoraTask):
db_apis.get_session(), load_balancer_id=loadbalancer.id,
status=constants.AMPHORA_ALLOCATED)[0]
for amphora in amphorae:
if amphora.id in added_ports:
amp_post_plug.execute(amphora, added_ports[amphora.id])
if amphora.id in updated_ports:
amp_post_plug.execute(amphora, updated_ports[amphora.id],
amphorae_network_config[amphora.id])
def revert(self, result, loadbalancer, added_ports, *args, **kwargs):
def revert(self, result, loadbalancer, updated_ports, *args, **kwargs):
"""Handle a failed post network plug."""
if isinstance(result, failure.Failure):
return

View File

@ -58,37 +58,101 @@ class CalculateAmphoraDelta(BaseNetworkTask):
LOG.debug("Calculating network delta for amphora id: %s", amphora.id)
if vrrp_port is None:
vrrp_port = self.network_driver.get_port(amphora.vrrp_port_id)
if availability_zone:
management_nets = (
[availability_zone.get(constants.MANAGEMENT_NETWORK)] or
CONF.controller_worker.amp_boot_network_list)
vrrp_port = self.network_driver.get_port(
amphora.vrrp_port_id)
vip_subnet_to_net_map = {
loadbalancer.vip.subnet_id:
loadbalancer.vip.network_id,
}
# Figure out what networks we want
# seed with lb network(s)
if (availability_zone and
availability_zone.get(constants.MANAGEMENT_NETWORK)):
management_nets = [
availability_zone.get(constants.MANAGEMENT_NETWORK)]
else:
management_nets = CONF.controller_worker.amp_boot_network_list
desired_network_ids = {vrrp_port.network_id}.union(management_nets)
desired_subnet_to_net_map = {
subnet: mgmt_net_id
for mgmt_net_id in management_nets
for subnet in self.network_driver.get_network(
mgmt_net_id).subnets
}
desired_subnet_to_net_map.update(vip_subnet_to_net_map)
for pool in loadbalancer.pools:
member_networks = [
self.network_driver.get_subnet(member.subnet_id).network_id
for member in pool.members
if member.subnet_id
]
desired_network_ids.update(member_networks)
for member in pool.members:
if (member.subnet_id and
member.provisioning_status !=
constants.PENDING_DELETE):
member_network = self.network_driver.get_subnet(
member.subnet_id).network_id
desired_subnet_to_net_map[member.subnet_id] = (
member_network)
nics = self.network_driver.get_plugged_networks(amphora.compute_id)
desired_network_ids = set(desired_subnet_to_net_map.values())
desired_subnet_ids = set(desired_subnet_to_net_map)
# Calculate Network deltas
nics = self.network_driver.get_plugged_networks(
amphora.compute_id)
# assume we don't have two nics in the same network
actual_network_nics = dict((nic.network_id, nic) for nic in nics)
# TODO(rm_work): how accurate is this assumption really?
network_to_nic_map = {nic.network_id: nic for nic in nics}
del_ids = set(actual_network_nics) - desired_network_ids
delete_nics = list(
actual_network_nics[net_id] for net_id in del_ids)
del_ids = set(network_to_nic_map) - desired_network_ids
delete_nics = [n_data_models.Interface(
network_id=net_id,
port_id=network_to_nic_map[net_id].port_id)
for net_id in del_ids]
add_ids = desired_network_ids - set(network_to_nic_map)
add_nics = [n_data_models.Interface(
network_id=net_id,
fixed_ips=[
n_data_models.FixedIP(
subnet_id=subnet_id)
for subnet_id in desired_subnet_to_net_map
if desired_subnet_to_net_map[subnet_id] == net_id])
for net_id in add_ids]
# Calculate member Subnet deltas
plugged_subnets = {
fixed_ip.subnet_id: nic.network_id
for nic in network_to_nic_map.values()
for fixed_ip in nic.fixed_ips or []
}
del_subnet_ids = set(plugged_subnets) - desired_subnet_ids
add_subnet_ids = desired_subnet_ids - set(plugged_subnets)
def _subnet_updates(subnet_ids, subnets):
updates = []
for s in subnet_ids:
network_id = subnets[s]
nic = network_to_nic_map.get(network_id)
port_id = nic.port_id if nic else None
updates.append({
constants.SUBNET_ID: s,
constants.NETWORK_ID: network_id,
constants.PORT_ID: port_id
})
return updates
add_subnets = _subnet_updates(add_subnet_ids,
desired_subnet_to_net_map)
del_subnets = _subnet_updates(del_subnet_ids,
plugged_subnets)
add_ids = desired_network_ids - set(actual_network_nics)
add_nics = list(n_data_models.Interface(
network_id=net_id) for net_id in add_ids)
delta = n_data_models.Delta(
amphora_id=amphora.id, compute_id=amphora.compute_id,
add_nics=add_nics, delete_nics=delete_nics)
amphora_id=amphora.id,
compute_id=amphora.compute_id,
add_nics=add_nics, delete_nics=delete_nics,
add_subnets=add_subnets,
delete_subnets=del_subnets)
return delta
@ -234,28 +298,81 @@ class HandleNetworkDelta(BaseNetworkTask):
Plug or unplug networks based on delta
"""
def _fill_port_info(self, port):
port.network = self.network_driver.get_network(port.network_id)
for fixed_ip in port.fixed_ips:
fixed_ip.subnet = self.network_driver.get_subnet(
fixed_ip.subnet_id)
def execute(self, amphora, delta):
"""Handle network plugging based off deltas."""
added_ports = {}
added_ports[amphora.id] = []
updated_ports = {}
for nic in delta.add_nics:
interface = self.network_driver.plug_network(delta.compute_id,
nic.network_id)
subnet_id = [
fixed_ip.subnet_id
for fixed_ip in nic.fixed_ips][0]
interface = self.network_driver.plug_network(
amphora.compute_id, nic.network_id)
port = self.network_driver.get_port(interface.port_id)
port.network = self.network_driver.get_network(port.network_id)
self._fill_port_info(port)
updated_ports[port.network_id] = port
for update in delta.add_subnets:
network_id = update[constants.NETWORK_ID]
# Get already existing port from Deltas or
# newly created port from updated_ports dict
port_id = (update[constants.PORT_ID] or
updated_ports[network_id].id)
subnet_id = update[constants.SUBNET_ID]
# Avoid duplicated subnets
has_subnet = False
if network_id in updated_ports:
for fixed_ip in updated_ports[network_id].fixed_ips:
if fixed_ip.subnet_id == subnet_id:
has_subnet = True
if not has_subnet:
port = self.network_driver.plug_fixed_ip(
port_id=port_id, subnet_id=subnet_id)
self._fill_port_info(port)
updated_ports[network_id] = port
for update in delta.delete_subnets:
network_id = update[constants.NETWORK_ID]
port_id = update[constants.PORT_ID]
subnet_id = update[constants.SUBNET_ID]
port = self.network_driver.unplug_fixed_ip(
port_id=port_id, subnet_id=subnet_id)
self._fill_port_info(port)
# In neutron, when removing an ipv6 subnet (with slaac) from a
# port, it just ignores it.
# https://bugs.launchpad.net/neutron/+bug/1945156
# When it happens, don't add the port to the updated_ports dict
has_subnet = False
for fixed_ip in port.fixed_ips:
fixed_ip.subnet = self.network_driver.get_subnet(
fixed_ip.subnet_id)
added_ports[amphora.id].append(port)
if fixed_ip.subnet_id == subnet_id:
has_subnet = True
break
if not has_subnet:
updated_ports[network_id] = port
for nic in delta.delete_nics:
network_id = nic.network_id
try:
self.network_driver.unplug_network(delta.compute_id,
nic.network_id)
self.network_driver.unplug_network(
amphora.compute_id, network_id)
except base.NetworkNotFound:
LOG.debug("Network %d not found ", nic.network_id)
LOG.debug("Network %s not found", network_id)
except Exception:
LOG.exception("Unable to unplug network")
return added_ports
port_id = nic.port_id
try:
self.network_driver.delete_port(port_id)
except Exception:
LOG.exception("Unable to delete the port")
updated_ports.pop(network_id, None)
return {amphora.id: list(updated_ports.values())}
def revert(self, result, amphora, delta, *args, **kwargs):
"""Handle a network plug or unplug failures."""
@ -276,6 +393,12 @@ class HandleNetworkDelta(BaseNetworkTask):
except Exception:
pass
port_id = nic.port_id
try:
self.network_driver.delete_port(port_id)
except Exception:
pass
class HandleNetworkDeltas(BaseNetworkTask):
"""Task to plug and unplug networks
@ -284,35 +407,28 @@ class HandleNetworkDeltas(BaseNetworkTask):
networks based on delta
"""
def execute(self, deltas):
def execute(self, deltas, loadbalancer):
"""Handle network plugging based off deltas."""
added_ports = {}
amphorae = {amp.id: amp for amp in loadbalancer.amphorae}
updated_ports = {}
handle_delta = HandleNetworkDelta()
for amp_id, delta in deltas.items():
added_ports[amp_id] = []
for nic in delta.add_nics:
interface = self.network_driver.plug_network(delta.compute_id,
nic.network_id)
port = self.network_driver.get_port(interface.port_id)
port.network = self.network_driver.get_network(port.network_id)
for fixed_ip in port.fixed_ips:
fixed_ip.subnet = self.network_driver.get_subnet(
fixed_ip.subnet_id)
added_ports[amp_id].append(port)
for nic in delta.delete_nics:
try:
self.network_driver.unplug_network(delta.compute_id,
nic.network_id)
except base.NetworkNotFound:
LOG.debug("Network %d not found ", nic.network_id)
except Exception:
LOG.exception("Unable to unplug network")
return added_ports
ret = handle_delta.execute(amphorae[amp_id], delta)
updated_ports.update(ret)
return updated_ports
def revert(self, result, deltas, *args, **kwargs):
"""Handle a network plug or unplug failures."""
if isinstance(result, failure.Failure):
return
if not deltas:
return
for amp_id, delta in deltas.items():
LOG.warning("Unable to plug networks for amp id %s",
delta.amphora_id)
@ -323,7 +439,13 @@ class HandleNetworkDeltas(BaseNetworkTask):
try:
self.network_driver.unplug_network(delta.compute_id,
nic.network_id)
except base.NetworkNotFound:
except Exception:
pass
port_id = nic.port_id
try:
self.network_driver.delete_port(port_id)
except Exception:
pass

View File

@ -410,11 +410,11 @@ class AmphoraFlows(object):
amp_for_failover_flow.add(network_tasks.HandleNetworkDelta(
name=prefix + '-' + constants.HANDLE_NETWORK_DELTA,
requires=(constants.AMPHORA, constants.DELTA),
provides=constants.ADDED_PORTS))
provides=constants.UPDATED_PORTS))
amp_for_failover_flow.add(amphora_driver_tasks.AmphoraePostNetworkPlug(
name=prefix + '-' + constants.AMPHORAE_POST_NETWORK_PLUG,
requires=(constants.LOADBALANCER, constants.ADDED_PORTS)))
requires=(constants.LOADBALANCER, constants.UPDATED_PORTS)))
return amp_for_failover_flow

View File

@ -196,12 +196,12 @@ class LoadBalancerFlows(object):
)
flows.append(
network_tasks.HandleNetworkDeltas(
requires=constants.DELTAS, provides=constants.ADDED_PORTS
requires=constants.DELTAS, provides=constants.UPDATED_PORTS
)
)
flows.append(
amphora_driver_tasks.AmphoraePostNetworkPlug(
requires=(constants.LOADBALANCER, constants.ADDED_PORTS)
requires=(constants.LOADBALANCER, constants.UPDATED_PORTS)
)
)
flows.append(

View File

@ -42,10 +42,14 @@ class MemberFlows(object):
requires=(constants.LOADBALANCER, constants.AVAILABILITY_ZONE),
provides=constants.DELTAS))
create_member_flow.add(network_tasks.HandleNetworkDeltas(
requires=constants.DELTAS, provides=constants.ADDED_PORTS))
requires=(constants.DELTAS, constants.LOADBALANCER),
provides=constants.UPDATED_PORTS))
create_member_flow.add(network_tasks.GetAmphoraeNetworkConfigs(
requires=constants.LOADBALANCER_ID,
provides=constants.AMPHORAE_NETWORK_CONFIG))
create_member_flow.add(amphora_driver_tasks.AmphoraePostNetworkPlug(
requires=(constants.LOADBALANCER, constants.ADDED_PORTS)
))
requires=(constants.LOADBALANCER, constants.UPDATED_PORTS,
constants.AMPHORAE_NETWORK_CONFIG)))
create_member_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=constants.LOADBALANCER_ID))
create_member_flow.add(database_tasks.MarkMemberActiveInDB(
@ -72,6 +76,18 @@ class MemberFlows(object):
constants.POOL_ID]))
delete_member_flow.add(database_tasks.MarkMemberPendingDeleteInDB(
requires=constants.MEMBER))
delete_member_flow.add(network_tasks.CalculateDelta(
requires=(constants.LOADBALANCER, constants.AVAILABILITY_ZONE),
provides=constants.DELTAS))
delete_member_flow.add(network_tasks.HandleNetworkDeltas(
requires=(constants.DELTAS, constants.LOADBALANCER),
provides=constants.UPDATED_PORTS))
delete_member_flow.add(network_tasks.GetAmphoraeNetworkConfigs(
requires=constants.LOADBALANCER_ID,
provides=constants.AMPHORAE_NETWORK_CONFIG))
delete_member_flow.add(amphora_driver_tasks.AmphoraePostNetworkPlug(
requires=(constants.LOADBALANCER, constants.UPDATED_PORTS,
constants.AMPHORAE_NETWORK_CONFIG)))
delete_member_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=constants.LOADBALANCER_ID))
delete_member_flow.add(database_tasks.DeleteMemberInDB(
@ -183,10 +199,15 @@ class MemberFlows(object):
requires=(constants.LOADBALANCER, constants.AVAILABILITY_ZONE),
provides=constants.DELTAS))
batch_update_members_flow.add(network_tasks.HandleNetworkDeltas(
requires=constants.DELTAS, provides=constants.ADDED_PORTS))
requires=(constants.DELTAS, constants.LOADBALANCER),
provides=constants.UPDATED_PORTS))
batch_update_members_flow.add(network_tasks.GetAmphoraeNetworkConfigs(
requires=constants.LOADBALANCER_ID,
provides=constants.AMPHORAE_NETWORK_CONFIG))
batch_update_members_flow.add(
amphora_driver_tasks.AmphoraePostNetworkPlug(
requires=(constants.LOADBALANCER, constants.ADDED_PORTS)))
requires=(constants.LOADBALANCER, constants.UPDATED_PORTS,
constants.AMPHORAE_NETWORK_CONFIG)))
# Update the Listener (this makes the changes active on the Amp)
batch_update_members_flow.add(amphora_driver_tasks.ListenersUpdate(

View File

@ -259,7 +259,7 @@ class AmphoraFinalize(BaseAmphoraTask):
class AmphoraPostNetworkPlug(BaseAmphoraTask):
"""Task to notify the amphora post network plug."""
def execute(self, amphora, ports):
def execute(self, amphora, ports, amphora_network_config):
"""Execute post_network_plug routine."""
db_amp = self.amphora_repo.get(db_apis.get_session(),
id=amphora[constants.ID])
@ -279,8 +279,9 @@ class AmphoraPostNetworkPlug(BaseAmphoraTask):
fixed_ips.append(data_models.FixedIP(
subnet=data_models.Subnet(**subnet_arg), **ip))
self.amphora_driver.post_network_plug(
db_amp, data_models.Port(network=net, fixed_ips=fixed_ips,
**port))
db_amp,
data_models.Port(network=net, fixed_ips=fixed_ips, **port),
amphora_network_config)
LOG.debug("post_network_plug called on compute instance "
"%(compute_id)s for port %(port_id)s",
@ -298,17 +299,18 @@ class AmphoraPostNetworkPlug(BaseAmphoraTask):
class AmphoraePostNetworkPlug(BaseAmphoraTask):
"""Task to notify the amphorae post network plug."""
def execute(self, loadbalancer, added_ports):
def execute(self, loadbalancer, updated_ports, amphorae_network_config):
"""Execute post_network_plug routine."""
amp_post_plug = AmphoraPostNetworkPlug()
db_lb = self.loadbalancer_repo.get(
db_apis.get_session(), id=loadbalancer[constants.LOADBALANCER_ID])
for amphora in db_lb.amphorae:
if amphora.id in added_ports:
if amphora.id in updated_ports:
amp_post_plug.execute(amphora.to_dict(),
added_ports[amphora.id])
updated_ports[amphora.id],
amphorae_network_config[amphora.id])
def revert(self, result, loadbalancer, added_ports, *args, **kwargs):
def revert(self, result, loadbalancer, updated_ports, *args, **kwargs):
"""Handle a failed post network plug."""
if isinstance(result, failure.Failure):
return

View File

@ -55,17 +55,16 @@ class CalculateAmphoraDelta(BaseNetworkTask):
default_provides = constants.DELTA
# TODO(gthiemonge) ensure we no longer need vrrp_port
def execute(self, loadbalancer, amphora, availability_zone,
vrrp_port=None):
LOG.debug("Calculating network delta for amphora id: %s",
amphora.get(constants.ID))
if vrrp_port is None:
vrrp_port = self.network_driver.get_port(
amphora[constants.VRRP_PORT_ID])
vrrp_port_network_id = vrrp_port.network_id
else:
vrrp_port_network_id = vrrp_port[constants.NETWORK_ID]
vip_subnet_to_net_map = {
loadbalancer[constants.VIP_SUBNET_ID]:
loadbalancer[constants.VIP_NETWORK_ID]
}
# Figure out what networks we want
# seed with lb network(s)
@ -75,33 +74,88 @@ class CalculateAmphoraDelta(BaseNetworkTask):
availability_zone.get(constants.MANAGEMENT_NETWORK)]
else:
management_nets = CONF.controller_worker.amp_boot_network_list
desired_network_ids = {vrrp_port_network_id}.union(management_nets)
db_lb = self.loadbalancer_repo.get(
db_apis.get_session(), id=loadbalancer[constants.LOADBALANCER_ID])
for pool in db_lb.pools:
member_networks = [
self.network_driver.get_subnet(member.subnet_id).network_id
for member in pool.members
if member.subnet_id
]
desired_network_ids.update(member_networks)
desired_subnet_to_net_map = {
subnet: mgmt_net_id
for mgmt_net_id in management_nets
for subnet in self.network_driver.get_network(
mgmt_net_id).subnets
}
desired_subnet_to_net_map.update(vip_subnet_to_net_map)
for pool in db_lb.pools:
for member in pool.members:
if (member.subnet_id and
member.provisioning_status !=
constants.PENDING_DELETE):
member_network = self.network_driver.get_subnet(
member.subnet_id).network_id
desired_subnet_to_net_map[member.subnet_id] = (
member_network)
desired_network_ids = set(desired_subnet_to_net_map.values())
desired_subnet_ids = set(desired_subnet_to_net_map)
# Calculate Network deltas
nics = self.network_driver.get_plugged_networks(
amphora[constants.COMPUTE_ID])
# assume we don't have two nics in the same network
actual_network_nics = dict((nic.network_id, nic) for nic in nics)
# TODO(rm_work): how accurate is this assumption really?
network_to_nic_map = {nic.network_id: nic for nic in nics}
del_ids = set(actual_network_nics) - desired_network_ids
delete_nics = list(
n_data_models.Interface(network_id=net_id) for net_id in del_ids)
del_ids = set(network_to_nic_map) - desired_network_ids
delete_nics = [n_data_models.Interface(
network_id=net_id,
port_id=network_to_nic_map[net_id].port_id)
for net_id in del_ids]
add_ids = desired_network_ids - set(network_to_nic_map)
add_nics = [n_data_models.Interface(
network_id=net_id,
fixed_ips=[
n_data_models.FixedIP(
subnet_id=subnet_id)
for subnet_id in desired_subnet_to_net_map
if desired_subnet_to_net_map[subnet_id] == net_id])
for net_id in add_ids]
# Calculate member Subnet deltas
plugged_subnets = {
fixed_ip.subnet_id: nic.network_id
for nic in network_to_nic_map.values()
for fixed_ip in nic.fixed_ips or []
}
del_subnet_ids = set(plugged_subnets) - desired_subnet_ids
add_subnet_ids = desired_subnet_ids - set(plugged_subnets)
def _subnet_updates(subnet_ids, subnets):
updates = []
for s in subnet_ids:
network_id = subnets[s]
nic = network_to_nic_map.get(network_id)
port_id = nic.port_id if nic else None
updates.append({
constants.SUBNET_ID: s,
constants.NETWORK_ID: network_id,
constants.PORT_ID: port_id
})
return updates
add_subnets = _subnet_updates(add_subnet_ids,
desired_subnet_to_net_map)
del_subnets = _subnet_updates(del_subnet_ids,
plugged_subnets)
add_ids = desired_network_ids - set(actual_network_nics)
add_nics = list(n_data_models.Interface(
network_id=net_id) for net_id in add_ids)
delta = n_data_models.Delta(
amphora_id=amphora[constants.ID],
compute_id=amphora[constants.COMPUTE_ID],
add_nics=add_nics, delete_nics=delete_nics)
add_nics=add_nics, delete_nics=delete_nics,
add_subnets=add_subnets,
delete_subnets=del_subnets)
return delta.to_dict(recurse=True)
@ -256,29 +310,86 @@ class HandleNetworkDelta(BaseNetworkTask):
Plug or unplug networks based on delta
"""
def _fill_port_info(self, port):
port.network = self.network_driver.get_network(port.network_id)
for fixed_ip in port.fixed_ips:
fixed_ip.subnet = self.network_driver.get_subnet(
fixed_ip.subnet_id)
def execute(self, amphora, delta):
"""Handle network plugging based off deltas."""
added_ports = {}
added_ports[amphora[constants.ID]] = []
db_amp = self.amphora_repo.get(db_apis.get_session(),
id=amphora.get(constants.ID))
updated_ports = {}
for nic in delta[constants.ADD_NICS]:
subnet_id = [
fixed_ip[constants.SUBNET_ID]
for fixed_ip in nic[constants.FIXED_IPS]][0]
interface = self.network_driver.plug_network(
delta[constants.COMPUTE_ID], nic[constants.NETWORK_ID])
db_amp.compute_id, nic[constants.NETWORK_ID])
port = self.network_driver.get_port(interface.port_id)
port.network = self.network_driver.get_network(port.network_id)
self._fill_port_info(port)
updated_ports[port.network_id] = port.to_dict(recurse=True)
for update in delta.get(constants.ADD_SUBNETS, []):
network_id = update[constants.NETWORK_ID]
# Get already existing port from Deltas or
# newly created port from updated_ports dict
port_id = (update[constants.PORT_ID] or
updated_ports[network_id][constants.ID])
subnet_id = update[constants.SUBNET_ID]
# Avoid duplicated subnets
has_subnet = False
if network_id in updated_ports:
for fixed_ip in updated_ports[network_id][
constants.FIXED_IPS]:
if fixed_ip[constants.SUBNET_ID] == subnet_id:
has_subnet = True
if not has_subnet:
port = self.network_driver.plug_fixed_ip(
port_id=port_id, subnet_id=subnet_id)
self._fill_port_info(port)
updated_ports[network_id] = (
port.to_dict(recurse=True))
for update in delta.get(constants.DELETE_SUBNETS, []):
network_id = update[constants.NETWORK_ID]
port_id = update[constants.PORT_ID]
subnet_id = update[constants.SUBNET_ID]
port = self.network_driver.unplug_fixed_ip(
port_id=port_id, subnet_id=subnet_id)
self._fill_port_info(port)
# In neutron, when removing an ipv6 subnet (with slaac) from a
# port, it just ignores it.
# https://bugs.launchpad.net/neutron/+bug/1945156
# When it happens, don't add the port to the updated_ports dict
has_subnet = False
for fixed_ip in port.fixed_ips:
fixed_ip.subnet = self.network_driver.get_subnet(
fixed_ip.subnet_id)
added_ports[amphora[constants.ID]].append(port.to_dict(
recurse=True))
if fixed_ip.subnet_id == subnet_id:
has_subnet = True
break
if not has_subnet:
updated_ports[network_id] = (
port.to_dict(recurse=True))
for nic in delta[constants.DELETE_NICS]:
network_id = nic[constants.NETWORK_ID]
try:
self.network_driver.unplug_network(
delta[constants.COMPUTE_ID], nic[constants.NETWORK_ID])
db_amp.compute_id, network_id)
except base.NetworkNotFound:
LOG.debug("Network %d not found ", nic[constants.NETWORK_ID])
LOG.debug("Network %s not found", network_id)
except Exception:
LOG.exception("Unable to unplug network")
return added_ports
port_id = nic[constants.PORT_ID]
try:
self.network_driver.delete_port(port_id)
except Exception:
LOG.exception("Unable to delete the port")
updated_ports.pop(network_id, None)
return {amphora[constants.ID]: list(updated_ports.values())}
def revert(self, result, amphora, delta, *args, **kwargs):
"""Handle a network plug or unplug failures."""
@ -299,6 +410,12 @@ class HandleNetworkDelta(BaseNetworkTask):
except Exception:
pass
port_id = nic[constants.PORT_ID]
try:
self.network_driver.delete_port(port_id)
except Exception:
pass
class HandleNetworkDeltas(BaseNetworkTask):
"""Task to plug and unplug networks
@ -307,49 +424,45 @@ class HandleNetworkDeltas(BaseNetworkTask):
networks based on delta
"""
def execute(self, deltas):
def execute(self, deltas, loadbalancer):
"""Handle network plugging based off deltas."""
added_ports = {}
db_lb = self.loadbalancer_repo.get(
db_apis.get_session(), id=loadbalancer[constants.LOADBALANCER_ID])
amphorae = {amp.id: amp for amp in db_lb.amphorae}
updated_ports = {}
handle_delta = HandleNetworkDelta()
for amp_id, delta in deltas.items():
added_ports[amp_id] = []
for nic in delta[constants.ADD_NICS]:
interface = self.network_driver.plug_network(
delta[constants.COMPUTE_ID], nic[constants.NETWORK_ID])
port = self.network_driver.get_port(interface.port_id)
port.network = self.network_driver.get_network(port.network_id)
for fixed_ip in port.fixed_ips:
fixed_ip.subnet = self.network_driver.get_subnet(
fixed_ip.subnet_id)
added_ports[amp_id].append(port.to_dict(recurse=True))
for nic in delta[constants.DELETE_NICS]:
try:
self.network_driver.unplug_network(
delta[constants.COMPUTE_ID],
nic[constants.NETWORK_ID])
except base.NetworkNotFound:
LOG.debug("Network %d not found ",
nic[constants.NETWORK_ID])
except Exception:
LOG.exception("Unable to unplug network")
return added_ports
ret = handle_delta.execute(amphorae[amp_id].to_dict(), delta)
updated_ports.update(ret)
return updated_ports
def revert(self, result, deltas, *args, **kwargs):
"""Handle a network plug or unplug failures."""
if isinstance(result, failure.Failure):
return
if not deltas:
return
for amp_id, delta in deltas.items():
LOG.warning("Unable to plug networks for amp id %s",
delta[constants.AMPHORA_ID])
if not delta:
return
for nic in delta[constants.ADD_NICS]:
try:
self.network_driver.unplug_network(
delta[constants.COMPUTE_ID],
nic[constants.NETWORK_ID])
except base.NetworkNotFound:
except Exception:
pass
port_id = nic[constants.PORT_ID]
try:
self.network_driver.delete_port(port_id)
except Exception:
pass

View File

@ -45,6 +45,10 @@ class UnplugNetworkException(NetworkException):
pass
class AllocateNetworkException(NetworkException):
pass
class VIPInUseException(NetworkException):
pass
@ -169,7 +173,7 @@ class AbstractNetworkDriver(object, metaclass=abc.ABCMeta):
"""
@abc.abstractmethod
def plug_network(self, compute_id, network_id, ip_address=None):
def plug_network(self, amphora, network_id, subnet_id=None):
"""Connects an existing amphora to an existing network.
:param compute_id: id of an amphora in the compute service
@ -180,10 +184,10 @@ class AbstractNetworkDriver(object, metaclass=abc.ABCMeta):
"""
@abc.abstractmethod
def unplug_network(self, compute_id, network_id, ip_address=None):
def unplug_network(self, compute_id, network_id):
"""Disconnects an existing amphora from an existing network.
If ip_address is not specificed, all the interfaces plugged on
If ip_address is not specified, all the interfaces plugged on
network_id should be unplugged.
:param compute_id: id of an amphora in the compute service
@ -194,6 +198,29 @@ class AbstractNetworkDriver(object, metaclass=abc.ABCMeta):
NetworkException
"""
@abc.abstractmethod
def plug_fixed_ip(self, port_id, subnet_id, ip_address=None):
"""Plug a fixed ip to an existing port.
If ip_address is not specified, one will be auto-assigned.
:param port_id: id of a port to add a fixed ip
:param subnet_id: id of a subnet
:param ip_address: specific ip_address to add
:return: octavia.network.data_models.Port
:raises: NetworkException, PortNotFound
"""
@abc.abstractmethod
def unplug_fixed_ip(self, port_id, subnet_id):
"""Unplug a fixed ip from an existing port.
:param port_id: id of a port to remove the fixed ip from
:param subnet_id: id of a subnet
:return: octavia.network.data_models.Port
:raises: NetworkException, PortNotFound
"""
@abc.abstractmethod
def get_plugged_networks(self, compute_id):
"""Retrieves the current plugged networking configuration.

View File

@ -29,11 +29,14 @@ class Interface(data_models.BaseDataModel):
class Delta(data_models.BaseDataModel):
def __init__(self, amphora_id=None, compute_id=None,
add_nics=None, delete_nics=None):
add_nics=None, delete_nics=None,
add_subnets=None, delete_subnets=None):
self.compute_id = compute_id
self.amphora_id = amphora_id
self.add_nics = add_nics
self.delete_nics = delete_nics
self.add_subnets = add_subnets
self.delete_subnets = delete_subnets
class Network(data_models.BaseDataModel):

View File

@ -547,7 +547,7 @@ class AllowedAddressPairsDriver(neutron_base.BaseNeutronDriver):
amphora.compute_id)
return
try:
self.unplug_network(amphora.compute_id, subnet.network_id)
self.unplug_network(amphora, subnet.network_id)
except Exception:
pass
try:
@ -610,15 +610,14 @@ class AllowedAddressPairsDriver(neutron_base.BaseNeutronDriver):
return self._nova_interface_to_octavia_interface(compute_id, interface)
def unplug_network(self, compute_id, network_id, ip_address=None):
def unplug_network(self, compute_id, network_id):
interfaces = self.get_plugged_networks(compute_id)
if not interfaces:
msg = ('Amphora with compute id {compute_id} does not have any '
'plugged networks').format(compute_id=compute_id)
raise base.NetworkNotFound(msg)
unpluggers = self._get_interfaces_to_unplug(interfaces, network_id,
ip_address=ip_address)
unpluggers = self._get_interfaces_to_unplug(interfaces, network_id)
for index, unplugger in enumerate(unpluggers):
self.compute.detach_port(
compute_id=compute_id, port_id=unplugger.port_id)

View File

@ -275,3 +275,35 @@ class BaseNeutronDriver(base.AbstractNetworkDriver):
def get_network_ip_availability(self, network):
return self._get_resource('network_ip_availability', network.id)
def plug_fixed_ip(self, port_id, subnet_id, ip_address=None):
port = self.get_port(port_id).to_dict(recurse=True)
fixed_ips = port['fixed_ips']
new_fixed_ip = {'subnet_id': subnet_id}
if ip_address:
new_fixed_ip['ip_address'] = ip_address
fixed_ips.append(new_fixed_ip)
body = {'port': {'fixed_ips': fixed_ips}}
try:
updated_port = self.neutron_client.update_port(port_id, body)
return utils.convert_port_dict_to_model(updated_port)
except Exception as e:
raise base.NetworkException(str(e))
def unplug_fixed_ip(self, port_id, subnet_id):
port = self.get_port(port_id)
fixed_ips = [
fixed_ip.to_dict()
for fixed_ip in port.fixed_ips
if fixed_ip.subnet_id != subnet_id
]
body = {'port': {'fixed_ips': fixed_ips}}
try:
updated_port = self.neutron_client.update_port(port_id, body)
return utils.convert_port_dict_to_model(updated_port)
except Exception as e:
raise base.NetworkException(str(e))

View File

@ -21,8 +21,12 @@ from octavia.network import data_models as network_models
LOG = logging.getLogger(__name__)
_PLUGGED_NETWORKS = {}
_PORTS = {}
class NoopManager(object):
def __init__(self):
super().__init__()
self.networkconfigconfig = {}
@ -110,33 +114,41 @@ class NoopManager(object):
vip.ip_address)] = (vip, amphora, subnet,
'unplug_aap_port')
def plug_network(self, compute_id, network_id, ip_address=None):
def plug_network(self, compute_id, network_id, subnet_id=None):
LOG.debug("Network %s no-op, plug_network compute_id %s, network_id "
"%s, ip_address %s", self.__class__.__name__, compute_id,
network_id, ip_address)
self.networkconfigconfig[(compute_id, network_id, ip_address)] = (
compute_id, network_id, ip_address, 'plug_network')
return network_models.Interface(
"%s, subnet %s", self.__class__.__name__, compute_id,
network_id, subnet_id)
self.networkconfigconfig[(compute_id, network_id)] = (
compute_id, network_id, subnet_id, 'plug_network')
interface = network_models.Interface(
id=uuidutils.generate_uuid(),
compute_id=compute_id,
network_id=network_id,
fixed_ips=[],
fixed_ips=[network_models.FixedIP(
subnet_id=subnet_id)],
port_id=uuidutils.generate_uuid()
)
_PORTS[interface.port_id] = network_models.Port(
id=interface.port_id,
network_id=network_id)
_PLUGGED_NETWORKS[(network_id, compute_id)] = interface
return interface
def unplug_network(self, compute_id, network_id, ip_address=None):
def unplug_network(self, compute_id, network_id):
LOG.debug("Network %s no-op, unplug_network compute_id %s, "
"network_id %s",
self.__class__.__name__, compute_id, network_id)
self.networkconfigconfig[(compute_id, network_id, ip_address)] = (
compute_id, network_id, ip_address, 'unplug_network')
self.networkconfigconfig[(compute_id, network_id)] = (
compute_id, network_id, 'unplug_network')
_PLUGGED_NETWORKS.pop((network_id, compute_id), None)
def get_plugged_networks(self, compute_id):
LOG.debug("Network %s no-op, get_plugged_networks amphora_id %s",
self.__class__.__name__, compute_id)
self.networkconfigconfig[compute_id] = (
compute_id, 'get_plugged_networks')
return []
return [pn for pn in _PLUGGED_NETWORKS.values()
if pn.compute_id == compute_id]
def update_vip(self, loadbalancer, for_delete=False):
LOG.debug("Network %s no-op, update_vip loadbalancer %s "
@ -175,7 +187,10 @@ class NoopManager(object):
LOG.debug("Port %s no-op, get_port port_id %s",
self.__class__.__name__, port_id)
self.networkconfigconfig[port_id] = (port_id, 'get_port')
return network_models.Port(id=uuidutils.generate_uuid())
if port_id in _PORTS:
return _PORTS[port_id]
return network_models.Port(id=uuidutils.generate_uuid(),
network_id=uuidutils.generate_uuid())
def get_network_by_name(self, network_name):
LOG.debug("Network %s no-op, get_network_by_name network_name %s",
@ -337,6 +352,27 @@ class NoopManager(object):
admin_state_up=admin_state_up, fixed_ips=fixed_ip_obj_list,
qos_policy_id=qos_policy_id, security_group_ids=security_group_ids)
def plug_fixed_ip(self, port_id, subnet_id, ip_address=None):
LOG.debug("Network %s no-op, plug_fixed_ip port_id %s, subnet_id "
"%s, ip_address %s", self.__class__.__name__, port_id,
subnet_id, ip_address)
self.networkconfigconfig[(port_id, subnet_id)] = (
port_id, subnet_id, ip_address, 'plug_fixed_ip')
port = network_models.Port(id=port_id,
network_id=uuidutils.generate_uuid())
_PORTS[port.id] = port
return port
def unplug_fixed_ip(self, port_id, subnet_id):
LOG.debug("Network %s no-op, unplug_fixed_ip port_id %s, subnet_id "
"%s", self.__class__.__name__, port_id,
subnet_id)
self.networkconfigconfig[(port_id, subnet_id)] = (
port_id, subnet_id, 'unplug_fixed_ip')
return _PORTS.get(port_id)
class NoopNetworkDriver(driver_base.AbstractNetworkDriver):
def __init__(self):
@ -355,15 +391,15 @@ class NoopNetworkDriver(driver_base.AbstractNetworkDriver):
def unplug_vip(self, loadbalancer, vip):
self.driver.unplug_vip(loadbalancer, vip)
def plug_network(self, amphora_id, network_id, ip_address=None):
return self.driver.plug_network(amphora_id, network_id, ip_address)
def plug_network(self, compute_id, network_id, subnet_id=None):
return self.driver.plug_network(compute_id, network_id,
subnet_id=subnet_id)
def unplug_network(self, amphora_id, network_id, ip_address=None):
self.driver.unplug_network(amphora_id, network_id,
ip_address=ip_address)
def unplug_network(self, compute_id, network_id):
self.driver.unplug_network(compute_id, network_id)
def get_plugged_networks(self, amphora_id):
return self.driver.get_plugged_networks(amphora_id)
def get_plugged_networks(self, compute_id):
return self.driver.get_plugged_networks(compute_id)
def update_vip(self, loadbalancer, for_delete=False):
self.driver.update_vip(loadbalancer, for_delete)
@ -437,3 +473,9 @@ class NoopNetworkDriver(driver_base.AbstractNetworkDriver):
return self.driver.create_port(
network_id, name, fixed_ips, secondary_ips, security_group_ids,
admin_state_up, qos_policy_id)
def plug_fixed_ip(self, port_id, subnet_id, ip_address=None):
return self.driver.plug_fixed_ip(port_id, subnet_id, ip_address)
def unplug_fixed_ip(self, port_id, subnet_id):
return self.driver.unplug_fixed_ip(port_id, subnet_id)

View File

@ -967,10 +967,12 @@ class TestServerTestCase(base.TestCase):
@mock.patch('subprocess.check_output')
@mock.patch('octavia.amphorae.backends.agent.api_server.'
'plug.Plug._netns_interface_exists')
@mock.patch('octavia.amphorae.backends.agent.api_server.'
'plug.Plug._netns_interface_by_mac')
@mock.patch('os.path.isfile')
def _test_plug_network(self, distro, mock_isfile, mock_int_exists,
mock_check_output, mock_netns, mock_pyroute2,
mock_os_chmod):
def _test_plug_network(self, distro, mock_isfile, mock_int_by_mac,
mock_int_exists, mock_check_output, mock_netns,
mock_pyroute2, mock_os_chmod):
mock_ipr = mock.MagicMock()
mock_ipr_instance = mock.MagicMock()
mock_ipr_instance.link_lookup.side_effect = [
@ -992,21 +994,22 @@ class TestServerTestCase(base.TestCase):
test_int_num = str(test_int_num)
# Interface already plugged
mock_int_exists.return_value = True
if distro == consts.UBUNTU:
rv = self.ubuntu_app.post('/' + api_server.VERSION +
"/plug/network",
content_type='application/json',
data=jsonutils.dumps(port_info))
elif distro == consts.CENTOS:
rv = self.centos_app.post('/' + api_server.VERSION +
"/plug/network",
content_type='application/json',
data=jsonutils.dumps(port_info))
self.assertEqual(409, rv.status_code)
self.assertEqual(dict(message="Interface already exists"),
jsonutils.loads(rv.data.decode('utf-8')))
mock_int_exists.return_value = False
# TODO(gthiemonge) FIXME
# mock_int_exists.return_value = True
# if distro == consts.UBUNTU:
# rv = self.ubuntu_app.post('/' + api_server.VERSION +
# "/plug/network",
# content_type='application/json',
# data=jsonutils.dumps(port_info))
# elif distro == consts.CENTOS:
# rv = self.centos_app.post('/' + api_server.VERSION +
# "/plug/network",
# content_type='application/json',
# data=jsonutils.dumps(port_info))
# self.assertEqual(409, rv.status_code)
# self.assertEqual(dict(message="Interface already exists"),
# jsonutils.loads(rv.data.decode('utf-8')))
# mock_int_exists.return_value = False
# No interface at all
file_name = '/sys/bus/pci/rescan'

View File

@ -96,6 +96,20 @@ class TestOSUtils(base.TestCase):
package_name))
self.assertEqual(centos_cmd, returned_centos_cmd)
@mock.patch('octavia.amphorae.backends.utils.interface_file.'
'InterfaceFile')
def test_write_interface_file(self, mock_interface_file):
mock_interface = mock.MagicMock()
mock_interface_file.return_value = mock_interface
self.ubuntu_os_util.write_interface_file('eth1',
'1.2.3.4', 16)
mock_interface_file.assert_called_once_with(
name='eth1',
addresses=[{"address": "1.2.3.4", "prefixlen": 16}])
mock_interface.write.assert_called_once()
@mock.patch('octavia.amphorae.backends.utils.interface_file.'
'VIPInterfaceFile')
def test_write_vip_interface_file(self, mock_vip_interface_file):
@ -142,6 +156,7 @@ class TestOSUtils(base.TestCase):
mtu=MTU,
vrrp_ip=None,
host_routes=host_routes,
fixed_ips=None,
topology="SINGLE")
mock_vip_interface_file.return_value.write.assert_called_once()
@ -167,6 +182,7 @@ class TestOSUtils(base.TestCase):
mtu=MTU,
vrrp_ip=None,
host_routes=host_routes,
fixed_ips=None,
topology="SINGLE")
@mock.patch('octavia.amphorae.backends.utils.interface_file.'

View File

@ -193,16 +193,167 @@ class TestPlug(base.TestCase):
mock_webob.Response.assert_any_call(json={'message': 'Invalid VIP'},
status=400)
@mock.patch("octavia.amphorae.backends.agent.api_server.osutils."
"BaseOS.write_interface_file")
def test_plug_lo(self, mock_write_interface):
m = mock.mock_open()
with mock.patch('os.open'), mock.patch.object(os, 'fdopen', m):
self.test_plug.plug_lo()
mock_write_interface.assert_called_once_with(interface='lo',
ip_address='127.0.0.1',
prefixlen=8)
@mock.patch('pyroute2.NetNS', create=True)
def test__netns_interface_exists(self, mock_netns):
netns_handle = mock_netns.return_value.__enter__.return_value
netns_handle.get_links.return_value = [{
'attrs': [['IFLA_ADDRESS', '123']]}]
'attrs': [['IFLA_ADDRESS', '123'],
['IFLA_IFNAME', 'eth0']]}]
# Interface is found in netns
self.assertTrue(self.test_plug._netns_interface_exists('123'))
# Interface is not found in netns
self.assertFalse(self.test_plug._netns_interface_exists('321'))
@mock.patch.object(plug, "webob")
@mock.patch('octavia.amphorae.backends.agent.api_server.plug.Plug.'
'_netns_interface_exists', return_value=False)
@mock.patch('octavia.amphorae.backends.agent.api_server.plug.Plug.'
'_interface_by_mac', return_value=FAKE_INTERFACE)
@mock.patch('pyroute2.IPRoute', create=True)
@mock.patch('pyroute2.netns.create', create=True)
@mock.patch('pyroute2.NetNS', create=True)
@mock.patch("octavia.amphorae.backends.agent.api_server.osutils."
"BaseOS.write_port_interface_file")
@mock.patch("octavia.amphorae.backends.agent.api_server.osutils."
"BaseOS.bring_interface_up")
def test_plug_network(self, mock_if_up, mock_write_port_interface,
mock_netns, mock_netns_create, mock_iproute,
mock_by_mac, mock_interface_exists, mock_webob):
fixed_ips = [
{'ip_address': FAKE_IP_IPV4,
'subnet_cidr': FAKE_CIDR_IPV4,
'gateway': FAKE_GATEWAY_IPV4,
'host_routes': [
{'destination': '10.1.0.0/16',
'nexthop': '10.0.1.254'}]
}]
mtu = 1400
m = mock.mock_open()
with mock.patch('os.open'), mock.patch.object(os, 'fdopen', m):
self.test_plug.plug_network(FAKE_MAC_ADDRESS, fixed_ips, 1400)
mock_write_port_interface.assert_called_once_with(
interface='eth0', fixed_ips=fixed_ips, mtu=mtu)
mock_if_up.assert_called_once_with('eth0', 'network')
mock_webob.Response.assert_any_call(
json={'message': 'OK',
'details': 'Plugged on interface eth0'},
status=202)
@mock.patch.object(plug, "webob")
@mock.patch('octavia.amphorae.backends.agent.api_server.plug.Plug.'
'_netns_interface_exists', return_value=True)
@mock.patch('octavia.amphorae.backends.agent.api_server.plug.Plug.'
'_netns_interface_by_mac', return_value=FAKE_INTERFACE)
@mock.patch('pyroute2.NetNS', create=True)
@mock.patch("octavia.amphorae.backends.agent.api_server.osutils."
"BaseOS.write_port_interface_file")
@mock.patch("octavia.amphorae.backends.agent.api_server.osutils."
"BaseOS.bring_interface_up")
def test_plug_network_existing_interface(self, mock_if_up,
mock_write_port_interface,
mock_netns, mock_by_mac,
mock_interface_exists,
mock_webob):
fixed_ips = [
{'ip_address': FAKE_IP_IPV4,
'subnet_cidr': FAKE_CIDR_IPV4,
'gateway': FAKE_GATEWAY_IPV4,
'host_routes': [
{'destination': '10.1.0.0/16',
'nexthop': '10.0.1.254'}]
}, {'ip_address': FAKE_IP_IPV6,
'subnet_cidr': FAKE_CIDR_IPV6,
'gateway': FAKE_GATEWAY_IPV6,
'host_routes': [
{'destination': '2001:1::/64',
'nexthop': '1001:1::ffff'}]
}]
mtu = 1400
m = mock.mock_open()
with mock.patch('os.open'), mock.patch.object(os, 'fdopen', m):
self.test_plug.plug_network(FAKE_MAC_ADDRESS, fixed_ips, 1400)
mock_write_port_interface.assert_called_once_with(
interface=FAKE_INTERFACE, fixed_ips=fixed_ips, mtu=mtu)
mock_if_up.assert_called_once_with(FAKE_INTERFACE, 'network')
mock_webob.Response.assert_any_call(
json={'message': 'OK',
'details': 'Updated existing interface {}'.format(
FAKE_INTERFACE)},
status=202)
@mock.patch.object(plug, "webob")
@mock.patch('octavia.amphorae.backends.agent.api_server.plug.Plug.'
'_netns_interface_exists', return_value=True)
@mock.patch('octavia.amphorae.backends.agent.api_server.plug.Plug.'
'_netns_interface_by_mac', return_value=FAKE_INTERFACE)
@mock.patch('pyroute2.NetNS', create=True)
@mock.patch("octavia.amphorae.backends.agent.api_server.osutils."
"BaseOS.write_vip_interface_file")
@mock.patch("octavia.amphorae.backends.agent.api_server.osutils."
"BaseOS.bring_interface_up")
def test_plug_network_on_vip(
self, mock_if_up, mock_write_vip_interface,
mock_netns, mock_by_mac, mock_interface_exists, mock_webob):
fixed_ips = [
{'ip_address': FAKE_IP_IPV4,
'subnet_cidr': FAKE_CIDR_IPV4,
'gateway': FAKE_GATEWAY_IPV4,
'host_routes': [
{'destination': '10.1.0.0/16',
'nexthop': '10.0.1.254'}]
}, {'ip_address': FAKE_IP_IPV6,
'subnet_cidr': FAKE_CIDR_IPV6,
'gateway': FAKE_GATEWAY_IPV6,
'host_routes': [
{'destination': '2001:1::/64',
'nexthop': '1001:1::ffff'}]
}]
mtu = 1400
vip_net_info = {
'vip': '10.2.1.1',
'subnet_cidr': '10.2.0.0/16',
'vrrp_ip': '10.2.1.2',
'gateway': '10.2.255.254',
'host_routes': []
}
m = mock.mock_open()
with mock.patch('os.open'), mock.patch.object(os, 'fdopen', m):
self.test_plug.plug_network(FAKE_MAC_ADDRESS, fixed_ips, mtu=1400,
vip_net_info=vip_net_info)
mock_write_vip_interface.assert_called_once_with(
interface=FAKE_INTERFACE,
vip=vip_net_info['vip'],
ip_version=4,
prefixlen=16,
gateway=vip_net_info['gateway'],
vrrp_ip=vip_net_info['vrrp_ip'],
host_routes=[],
fixed_ips=fixed_ips, mtu=mtu)
mock_if_up.assert_called_once_with(FAKE_INTERFACE, 'vip')
mock_webob.Response.assert_any_call(
json={'message': 'OK',
'details': 'Updated existing interface {}'.format(
FAKE_INTERFACE)},
status=202)

View File

@ -94,6 +94,7 @@ class TestHaproxyAmphoraLoadBalancerDriverTest(base.TestCase):
self.fixed_ip = mock.MagicMock()
self.fixed_ip.ip_address = '198.51.100.5'
self.fixed_ip.subnet.cidr = '198.51.100.0/24'
self.fixed_ip.subnet.gateway_ip = FAKE_GATEWAY
self.network = network_models.Network(mtu=FAKE_MTU)
self.port = network_models.Port(mac_address=FAKE_MAC_ADDRESS,
fixed_ips=[self.fixed_ip],
@ -116,6 +117,9 @@ class TestHaproxyAmphoraLoadBalancerDriverTest(base.TestCase):
constants.REQ_READ_TIMEOUT: 2,
constants.CONN_MAX_RETRIES: 3,
constants.CONN_RETRY_INTERVAL: 4}
self.amp_net_config = network_models.AmphoraNetworkConfig(
vip_subnet=self.lb.vip.subnet_id
)
@mock.patch('octavia.amphorae.drivers.haproxy.rest_api_driver.'
'HaproxyAmphoraLoadBalancerDriver._process_secret')
@ -589,6 +593,13 @@ class TestHaproxyAmphoraLoadBalancerDriverTest(base.TestCase):
amphorae_network_config.get().vip_subnet.cidr = FAKE_CIDR
amphorae_network_config.get().vip_subnet.gateway_ip = FAKE_GATEWAY
amphorae_network_config.get().vip_subnet.host_routes = self.host_routes
amphorae_network_config.get().vip_subnet.to_dict.return_value = {
'cidr': FAKE_CIDR,
'gateway_ip': FAKE_GATEWAY,
'host_routes': [
hr.to_dict(recurse=True)
for hr in self.host_routes]
}
amphorae_network_config.get().vrrp_port = self.port
self.driver.post_vip_plug(self.amp, self.lb, amphorae_network_config)
self.driver.clients[API_VERSION].plug_vip.assert_called_once_with(
@ -599,7 +610,7 @@ class TestHaproxyAmphoraLoadBalancerDriverTest(base.TestCase):
port = network_models.Port(mac_address=FAKE_MAC_ADDRESS,
fixed_ips=[],
network=self.network)
self.driver.post_network_plug(self.amp, port)
self.driver.post_network_plug(self.amp, port, self.amp_net_config)
self.driver.clients[API_VERSION].plug_network.assert_called_once_with(
self.amp, dict(mac_address=FAKE_MAC_ADDRESS,
fixed_ips=[],
@ -608,12 +619,13 @@ class TestHaproxyAmphoraLoadBalancerDriverTest(base.TestCase):
self.driver.clients[API_VERSION].plug_network.reset_mock()
# Test fixed IP path
self.driver.post_network_plug(self.amp, self.port)
self.driver.post_network_plug(self.amp, self.port, self.amp_net_config)
self.driver.clients[API_VERSION].plug_network.assert_called_once_with(
self.amp, dict(mac_address=FAKE_MAC_ADDRESS,
fixed_ips=[dict(ip_address='198.51.100.5',
subnet_cidr='198.51.100.0/24',
host_routes=[])],
host_routes=[],
gateway=FAKE_GATEWAY)],
mtu=FAKE_MTU))
def test_post_network_plug_with_host_routes(self):
@ -629,6 +641,7 @@ class TestHaproxyAmphoraLoadBalancerDriverTest(base.TestCase):
network_models.HostRoute(destination=DEST2,
nexthop=NEXTHOP)]
subnet = network_models.Subnet(id=SUBNET_ID, cidr=SUBNET_CIDR,
gateway_ip=FAKE_GATEWAY,
ip_version=4, host_routes=host_routes)
fixed_ips = [
network_models.FixedIP(subnet_id=subnet.id, ip_address=FIXED_IP1,
@ -639,12 +652,14 @@ class TestHaproxyAmphoraLoadBalancerDriverTest(base.TestCase):
port = network_models.Port(mac_address=FAKE_MAC_ADDRESS,
fixed_ips=fixed_ips,
network=self.network)
self.driver.post_network_plug(self.amp, port)
self.driver.post_network_plug(self.amp, port, self.amp_net_config)
expected_fixed_ips = [
{'ip_address': FIXED_IP1, 'subnet_cidr': SUBNET_CIDR,
'gateway': FAKE_GATEWAY,
'host_routes': [{'destination': DEST1, 'nexthop': NEXTHOP},
{'destination': DEST2, 'nexthop': NEXTHOP}]},
{'ip_address': FIXED_IP2, 'subnet_cidr': SUBNET_CIDR,
'gateway': FAKE_GATEWAY,
'host_routes': [{'destination': DEST1, 'nexthop': NEXTHOP},
{'destination': DEST2, 'nexthop': NEXTHOP}]}
]

View File

@ -94,6 +94,7 @@ class TestHaproxyAmphoraLoadBalancerDriverTest(base.TestCase):
self.fixed_ip = mock.MagicMock()
self.fixed_ip.ip_address = '198.51.100.5'
self.fixed_ip.subnet.cidr = '198.51.100.0/24'
self.fixed_ip.subnet.gateway_ip = FAKE_GATEWAY
self.network = network_models.Network(mtu=FAKE_MTU)
self.port = network_models.Port(mac_address=FAKE_MAC_ADDRESS,
fixed_ips=[self.fixed_ip],
@ -116,6 +117,11 @@ class TestHaproxyAmphoraLoadBalancerDriverTest(base.TestCase):
constants.REQ_READ_TIMEOUT: 2,
constants.CONN_MAX_RETRIES: 3,
constants.CONN_RETRY_INTERVAL: 4}
self.amp_net_config = network_models.AmphoraNetworkConfig(
vip_subnet=network_models.Subnet(
id=self.lb.vip.subnet_id,
cidr='10.1.0.0/16',
host_routes=[]))
@mock.patch('octavia.amphorae.drivers.haproxy.rest_api_driver.'
'HaproxyAmphoraLoadBalancerDriver._process_secret')
@ -684,6 +690,13 @@ class TestHaproxyAmphoraLoadBalancerDriverTest(base.TestCase):
amphorae_network_config.get().vip_subnet.cidr = FAKE_CIDR
amphorae_network_config.get().vip_subnet.gateway_ip = FAKE_GATEWAY
amphorae_network_config.get().vip_subnet.host_routes = self.host_routes
amphorae_network_config.get().vip_subnet.to_dict.return_value = {
'cidr': FAKE_CIDR,
'gateway_ip': FAKE_GATEWAY,
'host_routes': [
hr.to_dict(recurse=True)
for hr in self.host_routes]
}
amphorae_network_config.get().vrrp_port = self.port
self.driver.post_vip_plug(self.amp, self.lb, amphorae_network_config)
self.driver.clients[API_VERSION].plug_vip.assert_called_once_with(
@ -694,7 +707,7 @@ class TestHaproxyAmphoraLoadBalancerDriverTest(base.TestCase):
port = network_models.Port(mac_address=FAKE_MAC_ADDRESS,
fixed_ips=[],
network=self.network)
self.driver.post_network_plug(self.amp, port)
self.driver.post_network_plug(self.amp, port, self.amp_net_config)
self.driver.clients[API_VERSION].plug_network.assert_called_once_with(
self.amp, dict(mac_address=FAKE_MAC_ADDRESS,
fixed_ips=[],
@ -703,14 +716,40 @@ class TestHaproxyAmphoraLoadBalancerDriverTest(base.TestCase):
self.driver.clients[API_VERSION].plug_network.reset_mock()
# Test fixed IP path
self.driver.post_network_plug(self.amp, self.port)
self.driver.post_network_plug(self.amp, self.port, self.amp_net_config)
self.driver.clients[API_VERSION].plug_network.assert_called_once_with(
self.amp, dict(mac_address=FAKE_MAC_ADDRESS,
fixed_ips=[dict(ip_address='198.51.100.5',
subnet_cidr='198.51.100.0/24',
host_routes=[])],
host_routes=[],
gateway=FAKE_GATEWAY)],
mtu=FAKE_MTU))
self.driver.clients[API_VERSION].plug_network.reset_mock()
# Test member network on vip port
port = network_models.Port(id=self.amp.vrrp_port_id,
mac_address=FAKE_MAC_ADDRESS,
fixed_ips=[self.fixed_ip],
network=self.network)
self.driver.post_network_plug(self.amp, port, self.amp_net_config)
self.driver.clients[API_VERSION].plug_network.assert_called_once_with(
self.amp, dict(mac_address=FAKE_MAC_ADDRESS,
fixed_ips=[dict(ip_address='198.51.100.5',
subnet_cidr='198.51.100.0/24',
host_routes=[],
gateway=FAKE_GATEWAY)],
mtu=FAKE_MTU,
vip_net_info=dict(
vip=self.amp.ha_ip,
subnet_cidr='10.1.0.0/16',
mac_address=FAKE_MAC_ADDRESS,
gateway=None,
vrrp_ip=self.amp.vrrp_ip,
host_routes=[],
mtu=FAKE_MTU
)))
def test_post_network_plug_with_host_routes(self):
SUBNET_ID = 'SUBNET_ID'
FIXED_IP1 = '192.0.2.2'
@ -724,6 +763,7 @@ class TestHaproxyAmphoraLoadBalancerDriverTest(base.TestCase):
network_models.HostRoute(destination=DEST2,
nexthop=NEXTHOP)]
subnet = network_models.Subnet(id=SUBNET_ID, cidr=SUBNET_CIDR,
gateway_ip=FAKE_GATEWAY,
ip_version=4, host_routes=host_routes)
fixed_ips = [
network_models.FixedIP(subnet_id=subnet.id, ip_address=FIXED_IP1,
@ -734,12 +774,14 @@ class TestHaproxyAmphoraLoadBalancerDriverTest(base.TestCase):
port = network_models.Port(mac_address=FAKE_MAC_ADDRESS,
fixed_ips=fixed_ips,
network=self.network)
self.driver.post_network_plug(self.amp, port)
self.driver.post_network_plug(self.amp, port, self.amp_net_config)
expected_fixed_ips = [
{'ip_address': FIXED_IP1, 'subnet_cidr': SUBNET_CIDR,
'gateway': FAKE_GATEWAY,
'host_routes': [{'destination': DEST1, 'nexthop': NEXTHOP},
{'destination': DEST2, 'nexthop': NEXTHOP}]},
{'ip_address': FIXED_IP2, 'subnet_cidr': SUBNET_CIDR,
'gateway': FAKE_GATEWAY,
'host_routes': [{'destination': DEST1, 'nexthop': NEXTHOP},
{'destination': DEST2, 'nexthop': NEXTHOP}]}
]

View File

@ -115,7 +115,9 @@ class TestNoopAmphoraLoadBalancerDriver(base.TestCase):
self.amphora.id])
def test_post_network_plug(self):
self.driver.post_network_plug(self.amphora, self.port)
self.driver.post_network_plug(
self.amphora, self.port,
self.amphorae_net_configs[self.amphora.id])
self.assertEqual((self.amphora.id, self.port.id, 'post_network_plug'),
self.driver.driver.amphoraconfig[(
self.amphora.id, self.port.id)])

View File

@ -24,16 +24,20 @@ from octavia.tests.common import sample_certs
CONF = cfg.CONF
class AmphoraTuple(collections.namedtuple(
'amphora', 'id, lb_network_ip, vrrp_ip, ha_ip, vrrp_port_id, '
'ha_port_id, role, status, vrrp_interface,'
'vrrp_priority, api_version')):
def to_dict(self):
return self._asdict()
def sample_amphora_tuple(id='sample_amphora_id_1', lb_network_ip='10.0.1.1',
vrrp_ip='10.1.1.1', ha_ip='192.168.10.1',
vrrp_port_id='1234', ha_port_id='1234', role=None,
status='ACTIVE', vrrp_interface=None,
vrrp_priority=None, api_version='1.0'):
in_amphora = collections.namedtuple(
'amphora', 'id, lb_network_ip, vrrp_ip, ha_ip, vrrp_port_id, '
'ha_port_id, role, status, vrrp_interface,'
'vrrp_priority, api_version')
return in_amphora(
amp = AmphoraTuple(
id=id,
lb_network_ip=lb_network_ip,
vrrp_ip=vrrp_ip,
@ -45,6 +49,7 @@ def sample_amphora_tuple(id='sample_amphora_id_1', lb_network_ip='10.0.1.1',
vrrp_interface=vrrp_interface,
vrrp_priority=vrrp_priority,
api_version=api_version)
return amp
RET_PERSISTENCE = {
@ -648,9 +653,9 @@ def sample_vrrp_group_tuple():
smtp_connect_timeout='')
def sample_vip_tuple():
vip = collections.namedtuple('vip', 'ip_address')
return vip(ip_address='10.0.0.2')
def sample_vip_tuple(ip_address='10.0.0.2', subnet_id='vip_subnet_uuid'):
vip = collections.namedtuple('vip', ('ip_address', 'subnet_id'))
return vip(ip_address=ip_address, subnet_id=subnet_id)
def sample_listener_tuple(proto=None, monitor=True, alloc_default_pool=True,

View File

@ -23,16 +23,20 @@ from octavia.tests.common import sample_certs
CONF = cfg.CONF
class AmphoraTuple(collections.namedtuple(
'amphora', 'id, lb_network_ip, vrrp_ip, ha_ip, vrrp_port_id, '
'ha_port_id, role, status, vrrp_interface,'
'vrrp_priority, api_version')):
def to_dict(self):
return self._asdict()
def sample_amphora_tuple(id='sample_amphora_id_1', lb_network_ip='10.0.1.1',
vrrp_ip='10.1.1.1', ha_ip='192.168.10.1',
vrrp_port_id='1234', ha_port_id='1234', role=None,
status='ACTIVE', vrrp_interface=None,
vrrp_priority=None, api_version='0.5'):
in_amphora = collections.namedtuple(
'amphora', 'id, lb_network_ip, vrrp_ip, ha_ip, vrrp_port_id, '
'ha_port_id, role, status, vrrp_interface,'
'vrrp_priority, api_version')
return in_amphora(
return AmphoraTuple(
id=id,
lb_network_ip=lb_network_ip,
vrrp_ip=vrrp_ip,
@ -599,9 +603,9 @@ def sample_vrrp_group_tuple():
smtp_connect_timeout='')
def sample_vip_tuple():
vip = collections.namedtuple('vip', 'ip_address')
return vip(ip_address='10.0.0.2')
def sample_vip_tuple(ip_address='10.0.0.2', subnet_id='vip_subnet_uuid'):
vip = collections.namedtuple('vip', ('ip_address', 'subnet_id'))
return vip(ip_address=ip_address, subnet_id=subnet_id)
def sample_listener_tuple(proto=None, monitor=True, alloc_default_pool=True,

View File

@ -238,7 +238,7 @@ class TestAmphoraFlows(base.TestCase):
self.assertIn(constants.LOADBALANCER_ID, amp_flow.requires)
self.assertIn(constants.VIP, amp_flow.requires)
self.assertIn(constants.ADDED_PORTS, amp_flow.provides)
self.assertIn(constants.UPDATED_PORTS, amp_flow.provides)
self.assertIn(constants.AMP_VRRP_INT, amp_flow.provides)
self.assertIn(constants.AMPHORA, amp_flow.provides)
self.assertIn(constants.AMPHORA_ID, amp_flow.provides)
@ -272,7 +272,7 @@ class TestAmphoraFlows(base.TestCase):
self.assertIn(constants.LOADBALANCER_ID, amp_flow.requires)
self.assertIn(constants.VIP, amp_flow.requires)
self.assertIn(constants.ADDED_PORTS, amp_flow.provides)
self.assertIn(constants.UPDATED_PORTS, amp_flow.provides)
self.assertIn(constants.AMPHORA, amp_flow.provides)
self.assertIn(constants.AMPHORA_ID, amp_flow.provides)
self.assertIn(constants.AMPHORAE, amp_flow.provides)
@ -430,7 +430,7 @@ class TestAmphoraFlows(base.TestCase):
self.assertIn(constants.VIP, get_amp_flow.requires)
self.assertIn(constants.VIP_SG_ID, get_amp_flow.requires)
self.assertIn(constants.ADDED_PORTS, get_amp_flow.provides)
self.assertIn(constants.UPDATED_PORTS, get_amp_flow.provides)
self.assertIn(constants.AMPHORA, get_amp_flow.provides)
self.assertIn(constants.AMPHORA_ID, get_amp_flow.provides)
self.assertIn(constants.AMPHORAE_NETWORK_CONFIG, get_amp_flow.provides)
@ -460,7 +460,7 @@ class TestAmphoraFlows(base.TestCase):
self.assertIn(constants.VIP, get_amp_flow.requires)
self.assertIn(constants.VIP_SG_ID, get_amp_flow.requires)
self.assertIn(constants.ADDED_PORTS, get_amp_flow.provides)
self.assertIn(constants.UPDATED_PORTS, get_amp_flow.provides)
self.assertIn(constants.AMPHORA, get_amp_flow.provides)
self.assertIn(constants.AMPHORA_ID, get_amp_flow.provides)
self.assertIn(constants.AMPHORAE_NETWORK_CONFIG, get_amp_flow.provides)

View File

@ -183,22 +183,28 @@ class TestLoadBalancerFlows(base.TestCase):
self.assertIsInstance(create_flow, flow.Flow)
self.assertIn(constants.LOADBALANCER_ID, create_flow.requires)
self.assertIn(constants.UPDATE_DICT, create_flow.requires)
self.assertIn(constants.BUILD_TYPE_PRIORITY, create_flow.requires)
self.assertIn(constants.FLAVOR, create_flow.requires)
self.assertIn(constants.AMPHORAE_NETWORK_CONFIG, create_flow.requires)
self.assertIn(constants.AVAILABILITY_ZONE, create_flow.requires)
self.assertIn(constants.SERVER_GROUP_ID, create_flow.requires)
self.assertIn(constants.LISTENERS, create_flow.provides)
self.assertIn(constants.SUBNET, create_flow.provides)
self.assertIn(constants.AMPHORA, create_flow.provides)
self.assertIn(constants.AMPHORA_ID, create_flow.provides)
self.assertIn(constants.COMPUTE_ID, create_flow.provides)
self.assertIn(constants.COMPUTE_OBJ, create_flow.provides)
self.assertIn(constants.LOADBALANCER, create_flow.provides)
self.assertIn(constants.DELTAS, create_flow.provides)
self.assertIn(constants.ADDED_PORTS, create_flow.provides)
self.assertIn(constants.UPDATED_PORTS, create_flow.provides)
self.assertIn(constants.VIP, create_flow.provides)
self.assertIn(constants.AMP_DATA, create_flow.provides)
self.assertIn(constants.SERVER_PEM, create_flow.provides)
self.assertIn(constants.AMPHORA_NETWORK_CONFIG, create_flow.provides)
self.assertEqual(6, len(create_flow.requires))
self.assertEqual(13, len(create_flow.provides),
create_flow.provides)
self.assertEqual(7, len(create_flow.requires))
self.assertEqual(13, len(create_flow.provides))
def test_get_create_load_balancer_flows_active_standby_listeners(
self, mock_get_net_driver):
@ -218,7 +224,7 @@ class TestLoadBalancerFlows(base.TestCase):
self.assertIn(constants.COMPUTE_OBJ, create_flow.provides)
self.assertIn(constants.LOADBALANCER, create_flow.provides)
self.assertIn(constants.DELTAS, create_flow.provides)
self.assertIn(constants.ADDED_PORTS, create_flow.provides)
self.assertIn(constants.UPDATED_PORTS, create_flow.provides)
self.assertIn(constants.VIP, create_flow.provides)
self.assertIn(constants.AMP_DATA, create_flow.provides)
self.assertIn(constants.AMPHORAE_NETWORK_CONFIG,
@ -243,7 +249,7 @@ class TestLoadBalancerFlows(base.TestCase):
self.assertIn(constants.LOADBALANCER, failover_flow.requires)
self.assertIn(constants.LOADBALANCER_ID, failover_flow.requires)
self.assertIn(constants.ADDED_PORTS, failover_flow.provides)
self.assertIn(constants.UPDATED_PORTS, failover_flow.provides)
self.assertIn(constants.AMPHORA, failover_flow.provides)
self.assertIn(constants.AMPHORA_ID, failover_flow.provides)
self.assertIn(constants.AMPHORAE_NETWORK_CONFIG,
@ -312,7 +318,7 @@ class TestLoadBalancerFlows(base.TestCase):
self.assertIn(constants.LOADBALANCER, failover_flow.requires)
self.assertIn(constants.LOADBALANCER_ID, failover_flow.requires)
self.assertIn(constants.ADDED_PORTS, failover_flow.provides)
self.assertIn(constants.UPDATED_PORTS, failover_flow.provides)
self.assertIn(constants.AMP_VRRP_INT, failover_flow.provides)
self.assertIn(constants.AMPHORA, failover_flow.provides)
self.assertIn(constants.AMPHORA_ID, failover_flow.provides)

View File

@ -37,6 +37,7 @@ class TestMemberFlows(base.TestCase):
self.assertIsInstance(member_flow, flow.Flow)
self.assertIn(constants.MEMBER, member_flow.requires)
self.assertIn(constants.LISTENERS, member_flow.requires)
self.assertIn(constants.LOADBALANCER, member_flow.requires)
self.assertIn(constants.POOL, member_flow.requires)
@ -44,10 +45,11 @@ class TestMemberFlows(base.TestCase):
self.assertIn(constants.AVAILABILITY_ZONE, member_flow.requires)
self.assertIn(constants.DELTAS, member_flow.provides)
self.assertIn(constants.ADDED_PORTS, member_flow.provides)
self.assertIn(constants.AMPHORAE_NETWORK_CONFIG, member_flow.provides)
self.assertIn(constants.UPDATED_PORTS, member_flow.provides)
self.assertEqual(5, len(member_flow.requires))
self.assertEqual(2, len(member_flow.provides))
self.assertEqual(6, len(member_flow.requires))
self.assertEqual(3, len(member_flow.provides))
def test_get_delete_member_flow(self, mock_get_net_driver):
@ -58,10 +60,16 @@ class TestMemberFlows(base.TestCase):
self.assertIn(constants.MEMBER, member_flow.requires)
self.assertIn(constants.LISTENERS, member_flow.requires)
self.assertIn(constants.LOADBALANCER, member_flow.requires)
self.assertIn(constants.LOADBALANCER_ID, member_flow.requires)
self.assertIn(constants.POOL, member_flow.requires)
self.assertIn(constants.AVAILABILITY_ZONE, member_flow.requires)
self.assertEqual(4, len(member_flow.requires))
self.assertEqual(0, len(member_flow.provides))
self.assertIn(constants.AMPHORAE_NETWORK_CONFIG, member_flow.provides)
self.assertIn(constants.DELTAS, member_flow.provides)
self.assertIn(constants.UPDATED_PORTS, member_flow.provides)
self.assertEqual(6, len(member_flow.requires))
self.assertEqual(3, len(member_flow.provides))
def test_get_update_member_flow(self, mock_get_net_driver):
@ -91,7 +99,8 @@ class TestMemberFlows(base.TestCase):
self.assertIn(constants.AVAILABILITY_ZONE, member_flow.requires)
self.assertIn(constants.DELTAS, member_flow.provides)
self.assertIn(constants.ADDED_PORTS, member_flow.provides)
self.assertIn(constants.UPDATED_PORTS, member_flow.provides)
self.assertIn(constants.AMPHORAE_NETWORK_CONFIG, member_flow.provides)
self.assertEqual(4, len(member_flow.requires))
self.assertEqual(2, len(member_flow.provides))
self.assertEqual(5, len(member_flow.requires))
self.assertEqual(3, len(member_flow.provides))

View File

@ -50,6 +50,9 @@ _vip_mock = mock.MagicMock()
_load_balancer_mock.vip = _vip_mock
_LB_mock = mock.MagicMock()
_amphorae_mock = [_amphora_mock]
_amphora_network_config_mock = mock.MagicMock()
_amphorae_network_config_mock = {
_amphora_mock.id: _amphora_network_config_mock}
_network_mock = mock.MagicMock()
_port_mock = mock.MagicMock()
_ports_mock = [_port_mock]
@ -379,10 +382,12 @@ class TestAmphoraDriverTasks(base.TestCase):
amphora_post_network_plug_obj = (amphora_driver_tasks.
AmphoraPostNetworkPlug())
amphora_post_network_plug_obj.execute(_amphora_mock, _ports_mock)
amphora_post_network_plug_obj.execute(_amphora_mock, _ports_mock,
_amphora_network_config_mock)
(mock_driver.post_network_plug.
assert_called_once_with)(_amphora_mock, _port_mock)
assert_called_once_with)(_amphora_mock, _port_mock,
_amphora_network_config_mock)
# Test revert
amp = amphora_post_network_plug_obj.revert(None, _amphora_mock)
@ -428,17 +433,20 @@ class TestAmphoraDriverTasks(base.TestCase):
port_mock = mock.Mock()
_deltas_mock = {_amphora_mock.id: [port_mock]}
amphora_post_network_plug_obj.execute(_LB_mock, _deltas_mock)
amphora_post_network_plug_obj.execute(_LB_mock, _deltas_mock,
_amphorae_network_config_mock)
(mock_driver.post_network_plug.
assert_called_once_with(_amphora_mock, port_mock))
assert_called_once_with(_amphora_mock, port_mock,
_amphora_network_config_mock))
# Test with no ports to plug
mock_driver.post_network_plug.reset_mock()
_deltas_mock = {'0': [port_mock]}
amphora_post_network_plug_obj.execute(_LB_mock, _deltas_mock)
amphora_post_network_plug_obj.execute(_LB_mock, _deltas_mock,
_amphora_network_config_mock)
mock_driver.post_network_plug.assert_not_called()
# Test revert

View File

@ -34,6 +34,8 @@ COMPUTE_ID = uuidutils.generate_uuid()
PORT_ID = uuidutils.generate_uuid()
SUBNET_ID = uuidutils.generate_uuid()
NETWORK_ID = uuidutils.generate_uuid()
MGMT_NETWORK_ID = uuidutils.generate_uuid()
MGMT_SUBNET_ID = uuidutils.generate_uuid()
SG_ID = uuidutils.generate_uuid()
IP_ADDRESS = "172.24.41.1"
VIP = o_data_models.Vip(port_id=t_constants.MOCK_PORT_ID,
@ -76,23 +78,27 @@ class TestNetworkTasks(base.TestCase):
self.load_balancer_mock = mock.MagicMock()
self.vip_mock = mock.MagicMock()
self.vip_mock.subnet_id = SUBNET_ID
self.vip_mock.network_id = NETWORK_ID
self.load_balancer_mock.vip = self.vip_mock
self.load_balancer_mock.amphorae = []
self.amphora_mock.id = AMPHORA_ID
self.amphora_mock.compute_id = COMPUTE_ID
self.amphora_mock.status = constants.AMPHORA_ALLOCATED
self.boot_net_id = NETWORK_ID
self.mgmt_net_id = MGMT_NETWORK_ID
self.mgmt_subnet_id = MGMT_SUBNET_ID
conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
conf.config(group="controller_worker",
amp_boot_network_list=[self.boot_net_id])
amp_boot_network_list=[MGMT_NETWORK_ID])
conf.config(group="networking", max_retries=1)
super().setUp()
def test_calculate_amphora_delta(self, mock_get_net_driver):
VRRP_PORT_ID = uuidutils.generate_uuid()
VIP_NETWORK_ID = uuidutils.generate_uuid()
VIP_SUBNET_ID = uuidutils.generate_uuid()
DELETE_NETWORK_ID = uuidutils.generate_uuid()
MEMBER_NETWORK_ID = uuidutils.generate_uuid()
MEMBER_SUBNET_ID = uuidutils.generate_uuid()
VRRP_PORT_ID = uuidutils.generate_uuid()
mock_driver = mock.MagicMock()
mock_get_net_driver.return_value = mock_driver
member_mock = mock.MagicMock()
@ -101,23 +107,55 @@ class TestNetworkTasks(base.TestCase):
pool_mock.members = [member_mock]
lb_mock = mock.MagicMock()
lb_mock.pools = [pool_mock]
lb_mock.vip = mock.MagicMock()
lb_mock.vip.subnet_id = VIP_SUBNET_ID
lb_mock.vip.network_id = VIP_NETWORK_ID
amphora_mock = mock.MagicMock()
amphora_mock.id = AMPHORA_ID
amphora_mock.compute_id = COMPUTE_ID
amphora_mock.vrrp_port_id = VRRP_PORT_ID
vrrp_port_mock = mock.MagicMock()
vrrp_port_mock.network_id = self.boot_net_id
mock_subnet = mock.MagicMock()
mock_subnet.network_id = MEMBER_NETWORK_ID
nic1_delete_mock = mock.MagicMock()
nic1_delete_mock.network_id = DELETE_NETWORK_ID
nic2_keep_mock = mock.MagicMock()
nic2_keep_mock.network_id = self.boot_net_id
mock_driver.get_port.return_value = vrrp_port_mock
mock_driver.get_subnet.return_value = mock_subnet
mock_driver.get_plugged_networks.return_value = [nic1_delete_mock,
nic2_keep_mock]
mgmt_subnet = data_models.Subnet(
id=MGMT_SUBNET_ID,
network_id=MGMT_NETWORK_ID)
mgmt_net = data_models.Network(
id=MGMT_NETWORK_ID,
subnets=[mgmt_subnet.id])
mgmt_interface = data_models.Interface(
network_id=mgmt_net.id,
fixed_ips=[
data_models.FixedIP(
subnet_id=mgmt_subnet.id)])
vrrp_subnet = data_models.Subnet(
id=VIP_SUBNET_ID,
network_id=VIP_NETWORK_ID)
vrrp_port = data_models.Port(
id=VRRP_PORT_ID,
network_id=VIP_NETWORK_ID,
fixed_ips=[
data_models.FixedIP(
subnet=vrrp_subnet,
subnet_id=vrrp_subnet.id)])
vrrp_interface = data_models.Interface(
network_id=VIP_NETWORK_ID,
fixed_ips=vrrp_port.fixed_ips)
member_subnet = data_models.Subnet(
id=MEMBER_SUBNET_ID,
network_id=MEMBER_NETWORK_ID)
to_be_deleted_interface = data_models.Interface(
id=mock.Mock(),
network_id=DELETE_NETWORK_ID)
mock_driver.get_port.return_value = vrrp_port
mock_driver.get_subnet.return_value = member_subnet
mock_driver.get_network.return_value = mgmt_net
mock_driver.get_plugged_networks.return_value = [
mgmt_interface,
vrrp_interface,
to_be_deleted_interface]
calc_amp_delta = network_tasks.CalculateAmphoraDelta()
@ -130,15 +168,21 @@ class TestNetworkTasks(base.TestCase):
self.assertEqual(MEMBER_NETWORK_ID, result.add_nics[0].network_id)
self.assertEqual(1, len(result.delete_nics))
self.assertEqual(DELETE_NETWORK_ID, result.delete_nics[0].network_id)
mock_driver.get_port.assert_called_once_with(VRRP_PORT_ID)
mock_driver.get_subnet.assert_called_once_with(MEMBER_SUBNET_ID)
mock_driver.get_plugged_networks.assert_called_once_with(COMPUTE_ID)
# Test with vrrp_port_id
mock_driver.reset_mock()
mock_driver.get_port.return_value = vrrp_port
mock_driver.get_subnet.return_value = member_subnet
mock_driver.get_network.return_value = mgmt_net
mock_driver.get_plugged_networks.return_value = [
mgmt_interface,
vrrp_interface,
to_be_deleted_interface]
result = calc_amp_delta.execute(lb_mock, amphora_mock, {},
vrrp_port=vrrp_port_mock)
vrrp_port=vrrp_port)
self.assertEqual(AMPHORA_ID, result.amphora_id)
self.assertEqual(COMPUTE_ID, result.compute_id)
@ -146,7 +190,6 @@ class TestNetworkTasks(base.TestCase):
self.assertEqual(MEMBER_NETWORK_ID, result.add_nics[0].network_id)
self.assertEqual(1, len(result.delete_nics))
self.assertEqual(DELETE_NETWORK_ID, result.delete_nics[0].network_id)
mock_driver.get_port.assert_not_called()
mock_driver.get_subnet.assert_called_once_with(MEMBER_SUBNET_ID)
mock_driver.get_plugged_networks.assert_called_once_with(COMPUTE_ID)
@ -158,105 +201,443 @@ class TestNetworkTasks(base.TestCase):
amphora_id=self.amphora_mock.id,
compute_id=self.amphora_mock.compute_id,
add_nics=[],
delete_nics=[])}
delete_nics=[],
add_subnets=[],
delete_subnets=[],
)}
mgmt_subnet = data_models.Subnet(
id=self.mgmt_subnet_id, network_id=self.mgmt_net_id)
mgmt_net = data_models.Network(
id=self.mgmt_net_id,
subnets=[mgmt_subnet.id])
mgmt_ip_address = mock.MagicMock()
mgmt_interface = data_models.Interface(
network_id=self.mgmt_net_id,
fixed_ips=[
data_models.FixedIP(
subnet=mgmt_subnet,
subnet_id=self.mgmt_subnet_id,
ip_address=mgmt_ip_address
)
])
vrrp_subnet = data_models.Subnet(
id=self.vip_mock.subnet_id, network_id=self.vip_mock.network_id,
name='vrrp_subnet')
member_vip_subnet = data_models.Subnet(
id=uuidutils.generate_uuid(), network_id=self.vip_mock.network_id,
name='member_vip_subnet')
vip_net = data_models.Network(
id=self.vip_mock.network_id,
subnets=[member_vip_subnet, vrrp_subnet],
name='flat_network')
vrrp_port = data_models.Port(
id=uuidutils.generate_uuid(),
network_id=vip_net.id, network=vip_net,
fixed_ips=[
data_models.FixedIP(
subnet=vrrp_subnet, subnet_id=vrrp_subnet.id,
ip_address=t_constants.MOCK_IP_ADDRESS)
],
name='vrrp_port')
member_private_net_id = uuidutils.generate_uuid()
member_private_subnet = data_models.Subnet(
id=uuidutils.generate_uuid(), network_id=member_private_net_id,
name='member_private_subnet')
member_private_subnet2 = data_models.Subnet(
id=uuidutils.generate_uuid(), network_id=member_private_net_id,
name='member_private_subnet2')
member_private_net = data_models.Network(
id=member_private_subnet.network_id,
subnets=[member_private_subnet, member_private_subnet2],
name='member_private_net')
member_private_subnet_port = data_models.Port(
id=uuidutils.generate_uuid(),
network_id=member_private_net.id, network=member_private_net,
fixed_ips=[
data_models.FixedIP(
subnet=member_private_subnet,
subnet_id=member_private_subnet.id,
ip_address=t_constants.MOCK_IP_ADDRESS2)
],
name='member_private_net_port')
member_private_subnet2_port = data_models.Port(
id=uuidutils.generate_uuid(),
network_id=member_private_net.id, network=member_private_net,
fixed_ips=[
data_models.FixedIP(
subnet=member_private_subnet2,
subnet_id=member_private_subnet2.id,
ip_address=t_constants.MOCK_IP_ADDRESS2)
],
name='member_private_net_port')
# Pretend the VIP is on the member network, so already plugged
mock_driver.get_plugged_networks.return_value = [
mgmt_interface,
data_models.Interface(
network_id=vip_net.id, port_id=vrrp_port.id,
fixed_ips=vrrp_port.fixed_ips)]
mock_driver.get_port.return_value = vrrp_port
mock_driver.get_subnet.return_value = vrrp_subnet
mock_driver.get_network.return_value = mgmt_net
calc_delta = network_tasks.CalculateDelta()
self.assertEqual(EMPTY,
calc_delta.execute(self.load_balancer_mock, {}))
# Test with no amps or anything at all
self.assertEqual(EMPTY, calc_delta.execute(
self.load_balancer_mock, {}))
# Test with one amp and no pools, nothing plugged
# Test with one amp and no pools, only the base network plugged
# Delta should be empty
mock_driver.reset_mock()
self.amphora_mock.load_balancer = self.load_balancer_mock
self.load_balancer_mock.amphorae = [self.amphora_mock]
self.load_balancer_mock.pools = []
self.assertEqual(empty_deltas,
calc_delta.execute(self.load_balancer_mock, {}))
mock_driver.get_plugged_networks.assert_called_once_with(COMPUTE_ID)
# Pool mock should be configured explicitly for each test
pool_mock = mock.MagicMock()
self.load_balancer_mock.pools = [pool_mock]
# Test with one amp and one pool but no members, nothing plugged
# Delta should be empty
mock_driver.reset_mock()
pool_mock = mock.MagicMock()
pool_mock.members = []
self.load_balancer_mock.pools = [pool_mock]
self.assertEqual(empty_deltas,
calc_delta.execute(self.load_balancer_mock, {}))
# Test with one amp and one pool and one member, nothing plugged
# Delta should be one additional subnet to plug
# Test with one amp/pool and one member (on a distinct member subnet)
# Dummy AZ is provided
# Only the base network is already plugged
# Delta should be one additional network/subnet to plug
mock_driver.reset_mock()
member_mock = mock.MagicMock()
member_mock.subnet_id = 1
member_mock.subnet_id = member_private_subnet.id
member2_mock = mock.MagicMock()
member2_mock.subnet_id = member_private_subnet2.id
pool_mock.members = [member_mock]
mock_driver.get_subnet.return_value = data_models.Subnet(id=2,
network_id=3)
az = {
constants.COMPUTE_ZONE: 'foo'
}
mock_driver.get_subnet.return_value = data_models.Subnet(
id=2, network_id=3)
ndm = data_models.Delta(amphora_id=self.amphora_mock.id,
compute_id=self.amphora_mock.compute_id,
add_nics=[
data_models.Interface(network_id=2)],
delete_nics=[])
ndm = data_models.Delta(
amphora_id=self.amphora_mock.id,
compute_id=self.amphora_mock.compute_id,
add_nics=[
data_models.Interface(
network_id=3,
fixed_ips=[
data_models.FixedIP(
subnet_id=member_private_subnet.id)])],
delete_nics=[],
add_subnets=[{
'subnet_id': member_private_subnet.id,
'network_id': 3,
'port_id': None}],
delete_subnets=[])
self.assertEqual({self.amphora_mock.id: ndm},
calc_delta.execute(self.load_balancer_mock, {}))
calc_delta.execute(self.load_balancer_mock, az))
vrrp_port_call = mock.call(self.amphora_mock.vrrp_port_id)
mock_driver.get_port.assert_has_calls([vrrp_port_call])
self.assertEqual(1, mock_driver.get_port.call_count)
mock_driver.get_subnet.assert_called_once_with(
member_mock.subnet_id)
member_subnet_call = mock.call(member_mock.subnet_id)
mock_driver.get_subnet.assert_has_calls([member_subnet_call])
self.assertEqual(1, mock_driver.get_subnet.call_count)
# Test with one amp and one pool and one member, already plugged
# Test with one amp/pool and one member (not plugged) that is being
# deleted
# Only the base network is already plugged
# Delta should be empty
mock_driver.reset_mock()
member_mock = mock.MagicMock()
member_mock.subnet_id = 1
member_mock.subnet_id = member_private_subnet.id
member_mock.provisioning_status = constants.PENDING_DELETE
pool_mock.members = [member_mock]
mock_driver.get_plugged_networks.return_value = [
data_models.Interface(network_id=2)]
self.assertEqual(empty_deltas,
calc_delta.execute(self.load_balancer_mock, {}))
# Test with one amp and one pool and one member, wrong network plugged
# Delta should be one network to add and one to remove
# Test with one amp/pool and one member (without any subnets)
# Only the base network is already plugged
# No delta
mock_driver.reset_mock()
member_mock = mock.MagicMock()
member_mock.subnet_id = 1
member_mock.subnet_id = None
pool_mock.members = [member_mock]
mock_driver.get_plugged_networks.return_value = [
data_models.Interface(network_id=3)]
ndm = data_models.Delta(amphora_id=self.amphora_mock.id,
compute_id=self.amphora_mock.compute_id,
add_nics=[
data_models.Interface(network_id=2)],
delete_nics=[
data_models.Interface(network_id=3)])
self.assertEqual({self.amphora_mock.id: ndm},
self.assertEqual(empty_deltas,
calc_delta.execute(self.load_balancer_mock, {}))
# Test with one amp and one pool and one member
# Management network is defined in AZ metadata
# Base network AND member network/subnet already plugged
# Delta should be empty
mock_driver.reset_mock()
member_mock = mock.MagicMock()
member_mock.subnet_id = member_private_subnet.id
pool_mock.members = [member_mock]
mgmt2_subnet_id = uuidutils.generate_uuid()
mgmt2_net_id = uuidutils.generate_uuid()
mgmt2_subnet = data_models.Subnet(
id=mgmt2_subnet_id,
network_id=mgmt2_net_id)
mgmt2_net = data_models.Network(
id=mgmt2_net_id,
subnets=[mgmt2_subnet.id]
)
mgmt2_interface = data_models.Interface(
network_id=mgmt2_net_id,
fixed_ips=[
data_models.FixedIP(
subnet=mgmt2_subnet,
subnet_id=mgmt2_subnet_id,
)
])
mock_driver.get_network.return_value = mgmt2_net
az = {
constants.MANAGEMENT_NETWORK: mgmt2_net_id,
}
mock_driver.get_subnet.return_value = member_private_subnet
mock_driver.get_plugged_networks.return_value = [
mgmt2_interface,
data_models.Interface(
network_id=vrrp_subnet.network_id,
fixed_ips=vrrp_port.fixed_ips),
data_models.Interface(
network_id=member_private_subnet.network_id,
fixed_ips=member_private_subnet_port.fixed_ips)]
self.assertEqual(empty_deltas,
calc_delta.execute(self.load_balancer_mock, az))
# Test with one amp and one pool and one member, wrong network plugged
# Delta should be one network/subnet to add and one to remove
mock_driver.reset_mock()
mock_driver.get_network.return_value = mgmt_net
member_mock = mock.MagicMock()
member_mock.subnet_id = member_private_subnet.id
pool_mock.members = [member_mock]
az = {
constants.COMPUTE_ZONE: 'foo'
}
mock_driver.get_subnet.return_value = member_private_subnet
mock_driver.get_plugged_networks.return_value = [
mgmt_interface,
data_models.Interface(
network_id=vrrp_subnet.network_id,
fixed_ips=vrrp_port.fixed_ips),
data_models.Interface(
network_id='bad_net',
fixed_ips=[data_models.FixedIP(subnet_id='bad_subnet')])]
ndm = data_models.Delta(
amphora_id=self.amphora_mock.id,
compute_id=self.amphora_mock.compute_id,
add_nics=[data_models.Interface(
network_id=member_private_net.id,
fixed_ips=[data_models.FixedIP(
subnet_id=member_private_subnet.id)])],
delete_nics=[data_models.Interface(network_id='bad_net')],
add_subnets=[{
'subnet_id': member_private_subnet.id,
'network_id': member_private_net.id,
'port_id': None
}],
delete_subnets=[{
'subnet_id': 'bad_subnet',
'network_id': 'bad_net',
'port_id': None
}])
self.assertEqual({self.amphora_mock.id: ndm},
calc_delta.execute(self.load_balancer_mock, az))
# Test with one amp and one pool and no members, one network plugged
# Delta should be one network to remove
mock_driver.reset_mock()
pool_mock.members = []
mock_driver.get_subnet.side_effect = [
vrrp_subnet]
mock_driver.get_plugged_networks.return_value = [
data_models.Interface(network_id=2)]
mgmt_interface,
data_models.Interface(
network_id=vrrp_subnet.network_id,
fixed_ips=vrrp_port.fixed_ips),
data_models.Interface(
network_id='bad_net',
fixed_ips=[data_models.FixedIP(subnet_id='bad_subnet')])]
ndm = data_models.Delta(amphora_id=self.amphora_mock.id,
compute_id=self.amphora_mock.compute_id,
add_nics=[],
delete_nics=[
data_models.Interface(network_id=2)])
ndm = data_models.Delta(
amphora_id=self.amphora_mock.id,
compute_id=self.amphora_mock.compute_id,
add_nics=[],
delete_nics=[data_models.Interface(network_id='bad_net')],
add_subnets=[],
delete_subnets=[{
'subnet_id': 'bad_subnet',
'network_id': 'bad_net',
'port_id': None
}])
self.assertEqual({self.amphora_mock.id: ndm},
calc_delta.execute(self.load_balancer_mock, {}))
# Add a new member on a new subnet, an interface with another subnet of
# the same network is already plugged
# Delta should be one new subnet
mock_driver.reset_mock()
pool_mock.members = [member_mock, member2_mock]
mock_driver.get_subnet.side_effect = [
vrrp_subnet,
member_private_subnet,
member_private_subnet2]
mock_driver.get_plugged_networks.return_value = [
mgmt_interface,
data_models.Interface(
network_id=vrrp_subnet.network_id,
fixed_ips=vrrp_port.fixed_ips),
data_models.Interface(
network_id=member_private_net_id,
port_id=member_private_subnet_port.id,
fixed_ips=member_private_subnet_port.fixed_ips)]
ndm = data_models.Delta(
amphora_id=self.amphora_mock.id,
compute_id=self.amphora_mock.compute_id,
add_nics=[],
delete_nics=[],
add_subnets=[{
'subnet_id': member_private_subnet2.id,
'network_id': member_private_net_id,
'port_id': member_private_subnet_port.id
}],
delete_subnets=[]
)
self.assertEqual({self.amphora_mock.id: ndm},
calc_delta.execute(self.load_balancer_mock, {}))
# a new member on a new subnet on an existing network, a delete member2
# on another subnet of the same network
# Delta should be one new subnet, one deleted subnet, no interface
# change
mock_driver.reset_mock()
pool_mock.members = [member_mock]
mock_driver.get_subnet.return_value = member_private_subnet
mock_driver.get_plugged_networks.return_value = [
mgmt_interface,
data_models.Interface(
network_id=vrrp_subnet.network_id,
fixed_ips=vrrp_port.fixed_ips),
data_models.Interface(
network_id=member_private_net_id,
port_id=member_private_subnet2_port.id,
fixed_ips=member_private_subnet2_port.fixed_ips)]
ndm = data_models.Delta(
amphora_id=self.amphora_mock.id,
compute_id=self.amphora_mock.compute_id,
add_nics=[],
delete_nics=[],
add_subnets=[{
'subnet_id': member_private_subnet.id,
'network_id': member_private_net_id,
'port_id': member_private_subnet2_port.id}],
delete_subnets=[{
'subnet_id': member_private_subnet2.id,
'network_id': member_private_net_id,
'port_id': member_private_subnet2_port.id}]
)
self.assertEqual({self.amphora_mock.id: ndm},
calc_delta.execute(self.load_balancer_mock, {}))
# member on subnet on the same network as the vip subnet
mock_driver.reset_mock()
member_mock.subnet_id = member_vip_subnet.id
pool_mock.members = [member_mock]
mock_driver.get_subnet.side_effect = [
vrrp_subnet,
member_vip_subnet]
mock_driver.get_plugged_networks.return_value = [
mgmt_interface,
data_models.Interface(
network_id=vrrp_subnet.network_id,
port_id=vrrp_port.id,
fixed_ips=vrrp_port.fixed_ips),
data_models.Interface(
network_id=member_private_net_id,
port_id=member_private_subnet_port.id,
fixed_ips=member_private_subnet_port.fixed_ips)]
ndm = data_models.Delta(
amphora_id=self.amphora_mock.id,
compute_id=self.amphora_mock.compute_id,
add_nics=[],
delete_nics=[
data_models.Interface(
network_id=member_private_net_id,
port_id=member_private_subnet_port.id)],
add_subnets=[{
'subnet_id': member_vip_subnet.id,
'network_id': vip_net.id,
'port_id': vrrp_port.id}],
delete_subnets=[{
'subnet_id': member_private_subnet.id,
'network_id': member_private_net_id,
'port_id': member_private_subnet_port.id}]
)
self.assertEqual({self.amphora_mock.id: ndm},
calc_delta.execute(self.load_balancer_mock, {}))
def test_calculate_delta_ipv6_ipv4_subnets(self, mock_get_net_driver):
mock_driver = mock.MagicMock()
mock_get_net_driver.return_value = mock_driver
# Pool mock should be configured explicitly for each test
pool_mock = mock.MagicMock()
self.load_balancer_mock.pools = [pool_mock]
self.amphora_mock.load_balancer = self.load_balancer_mock
self.load_balancer_mock.amphorae = [self.amphora_mock]
# Test with one amp and one pool and one new member (in the VIP net)
# Delta should be one additional subnet to plug to the existing port
vrrp_subnet = data_models.Subnet(
id=self.vip_mock.subnet_id, network_id=self.vip_mock.network_id)
member_subnet = data_models.Subnet(
id=uuidutils.generate_uuid(), network_id=self.vip_mock.network_id)
flat_network = data_models.Network(
id=self.vip_mock.network_id, subnets=[member_subnet, vrrp_subnet])
vrrp_port = data_models.Port(
id=uuidutils.generate_uuid(),
network_id=flat_network.id, network=flat_network,
fixed_ips=[
data_models.FixedIP(
subnet=vrrp_subnet, subnet_id=vrrp_subnet.id,
ip_address=t_constants.MOCK_IP_ADDRESS)
])
mock_driver.get_subnet.return_value = member_subnet
member_mock = mock.MagicMock()
member_mock.subnet_id = member_subnet.id
pool_mock.members = [member_mock]
# Pretend the VIP is on the member network, so already plugged
mock_driver.get_plugged_networks.return_value = [
data_models.Interface(
network_id=flat_network.id, port_id=vrrp_port.id,
fixed_ips=vrrp_port.fixed_ips)]
mock_driver.get_port.return_value = vrrp_port
calc_delta = network_tasks.CalculateDelta()
deltas = calc_delta.execute(self.load_balancer_mock, {})
expected_delta = {self.amphora_mock.id: data_models.Delta(
amphora_id=self.amphora_mock.id,
compute_id=self.amphora_mock.compute_id,
add_nics=[],
delete_nics=[],
add_subnets=[member_subnet.id],
delete_subnets=[])}
self.assertEqual(expected_delta, deltas)
def test_get_plumbed_networks(self, mock_get_net_driver):
mock_driver = mock.MagicMock()
mock_get_net_driver.return_value = mock_driver
@ -401,8 +782,12 @@ class TestNetworkTasks(base.TestCase):
mock_get_net_driver.return_value = mock_net_driver
nic1 = mock.MagicMock()
nic1.fixed_ips = [data_models.FixedIP(
subnet_id=uuidutils.generate_uuid())]
nic1.network_id = uuidutils.generate_uuid()
nic2 = mock.MagicMock()
nic2.fixed_ips = [data_models.FixedIP(
subnet_id=uuidutils.generate_uuid())]
nic2.network_id = uuidutils.generate_uuid()
interface1 = mock.MagicMock()
interface1.port_id = uuidutils.generate_uuid()
@ -417,7 +802,9 @@ class TestNetworkTasks(base.TestCase):
delta = data_models.Delta(amphora_id=self.amphora_mock.id,
compute_id=self.amphora_mock.compute_id,
add_nics=[nic1],
delete_nics=[nic2, nic2, nic2])
delete_nics=[nic2, nic2, nic2],
add_subnets=[],
delete_subnets=[])
mock_net_driver.plug_network.return_value = interface1
mock_net_driver.get_port.return_value = port1
@ -463,82 +850,313 @@ class TestNetworkTasks(base.TestCase):
mock_driver = mock.MagicMock()
mock_get_net_driver.return_value = mock_driver
def _interface(network_id):
return [data_models.Interface(network_id=network_id)]
self.load_balancer_mock.amphorae = [self.amphora_mock]
subnet1 = uuidutils.generate_uuid()
network1 = uuidutils.generate_uuid()
port1 = uuidutils.generate_uuid()
subnet2 = uuidutils.generate_uuid()
def _interface(network_id, port_id=None, subnet_id=None):
return data_models.Interface(
network_id=network_id,
port_id=port_id,
fixed_ips=[
data_models.FixedIP(
subnet_id=subnet_id)])
net = network_tasks.HandleNetworkDeltas()
net.execute({})
net.execute({}, self.load_balancer_mock)
self.assertFalse(mock_driver.plug_network.called)
delta = data_models.Delta(amphora_id=self.amphora_mock.id,
compute_id=self.amphora_mock.compute_id,
add_nics=[],
delete_nics=[])
net.execute({self.amphora_mock.id: delta})
delete_nics=[],
add_subnets=[],
delete_subnets=[])
net.execute({self.amphora_mock.id: delta}, self.load_balancer_mock)
self.assertFalse(mock_driver.plug_network.called)
# Adding a subnet on a new network
port = data_models.Port(
id=port1,
network_id=network1,
fixed_ips=[
data_models.FixedIP(subnet_id=subnet1)])
mock_driver.get_port.return_value = port
mock_driver.plug_fixed_ip.return_value = port
mock_driver.get_network.return_value = data_models.Network(
id=network1)
mock_driver.get_subnet.return_value = data_models.Subnet(
id=subnet1,
network_id=network1)
add_nics = [_interface(network1, subnet_id=subnet1)]
add_subnets = [{
'subnet_id': subnet1,
'network_id': network1,
'port_id': None}]
delta = data_models.Delta(amphora_id=self.amphora_mock.id,
compute_id=self.amphora_mock.compute_id,
add_nics=_interface(1),
delete_nics=[])
net.execute({self.amphora_mock.id: delta})
mock_driver.plug_network.assert_called_once_with(COMPUTE_ID, 1)
add_nics=add_nics,
delete_nics=[],
add_subnets=add_subnets,
delete_subnets=[])
updated_ports = net.execute({self.amphora_mock.id: delta},
self.load_balancer_mock)
mock_driver.plug_network.assert_called_once_with(
self.amphora_mock.compute_id, network1)
mock_driver.unplug_network.assert_not_called()
self.assertEqual(1, len(updated_ports))
updated_port = updated_ports[self.amphora_mock.id][0]
self.assertEqual(port1, updated_port.id)
self.assertEqual(network1, updated_port.network_id)
self.assertEqual(1, len(updated_port.fixed_ips))
self.assertEqual(subnet1, updated_port.fixed_ips[0].subnet_id)
# revert
net.execute({self.amphora_mock.id: delta})
self.assertFalse(mock_driver.unplug_network.called)
net.revert(None, {self.amphora_mock.id: delta},
self.load_balancer_mock)
mock_driver.unplug_network.assert_called_once_with(
self.amphora_mock.compute_id, network1)
# Adding a subnet on an existing network/port
mock_driver.reset_mock()
port = data_models.Port(
id=port1,
network_id=network1,
fixed_ips=[
data_models.FixedIP(subnet_id=subnet2),
data_models.FixedIP(subnet_id=subnet1)])
mock_driver.plug_fixed_ip.return_value = port
mock_driver.get_network.return_value = data_models.Network(
id=network1)
mock_driver.get_subnet.side_effect = [
data_models.Subnet(
id=subnet2,
network_id=network1),
data_models.Subnet(
id=subnet1,
network_id=network1)]
add_nics = [_interface(network1)]
add_subnets = [{
'subnet_id': subnet1,
'network_id': network1,
'port_id': port1}]
delta = data_models.Delta(amphora_id=self.amphora_mock.id,
compute_id=self.amphora_mock.compute_id,
add_nics=[],
delete_nics=[])
net.execute({self.amphora_mock.id: delta})
self.assertFalse(mock_driver.unplug_network.called)
delete_nics=[],
add_subnets=add_subnets,
delete_subnets=[])
updated_ports = net.execute({self.amphora_mock.id: delta},
self.load_balancer_mock)
mock_driver.plug_network.assert_not_called()
mock_driver.unplug_network.assert_not_called()
mock_driver.get_port.assert_not_called()
mock_driver.plug_fixed_ip.assert_called_once_with(port_id=port1,
subnet_id=subnet1)
self.assertEqual(1, len(updated_ports))
updated_port = updated_ports[self.amphora_mock.id][0]
self.assertEqual(port1, updated_port.id)
self.assertEqual(network1, updated_port.network_id)
self.assertEqual(2, len(updated_port.fixed_ips))
self.assertEqual(subnet2, updated_port.fixed_ips[0].subnet_id)
self.assertEqual(subnet1, updated_port.fixed_ips[1].subnet_id)
# Deleting a subnet
mock_driver.reset_mock()
delete_subnets = [{
'subnet_id': subnet1,
'network_id': network1,
'port_id': port1}]
mock_driver.get_subnet.side_effect = [
data_models.Subnet(
id=subnet2,
network_id=network1)]
mock_driver.unplug_fixed_ip.return_value = data_models.Port(
id=port1,
network_id=network1,
fixed_ips=[
data_models.FixedIP(subnet_id=subnet2)])
delta = data_models.Delta(amphora_id=self.amphora_mock.id,
compute_id=self.amphora_mock.compute_id,
add_nics=_interface(1),
delete_nics=[])
add_nics=[],
delete_nics=[],
add_subnets=[],
delete_subnets=delete_subnets)
updated_ports = net.execute({self.amphora_mock.id: delta},
self.load_balancer_mock)
mock_driver.delete_port.assert_not_called()
mock_driver.plug_network.assert_not_called()
mock_driver.plug_fixed_ip.assert_not_called()
self.assertEqual(1, len(updated_ports))
self.assertEqual(1, len(updated_ports[self.amphora_mock.id]))
updated_port = updated_ports[self.amphora_mock.id][0]
self.assertEqual(port1, updated_port.id)
self.assertEqual(network1, updated_port.network_id)
self.assertEqual(1, len(updated_port.fixed_ips))
self.assertEqual(subnet2, updated_port.fixed_ips[0].subnet_id)
# Deleting a subnet, but neutron doesn't unplug it
# Delta are empty because there's nothing to update
mock_driver.reset_mock()
mock_driver.unplug_network.side_effect = net_base.NetworkNotFound
delete_subnets = [{
'subnet_id': subnet1,
'network_id': network1,
'port_id': port1}]
mock_driver.get_subnet.side_effect = [
data_models.Subnet(
id=subnet2,
network_id=network1),
data_models.Subnet(
id=subnet2,
network_id=network1)]
mock_driver.unplug_fixed_ip.return_value = data_models.Port(
id=port1,
network_id=network1,
fixed_ips=[
data_models.FixedIP(subnet_id=subnet1),
data_models.FixedIP(subnet_id=subnet2)])
delta = data_models.Delta(amphora_id=self.amphora_mock.id,
compute_id=self.amphora_mock.compute_id,
add_nics=[],
delete_nics=[],
add_subnets=[],
delete_subnets=[])
net.execute({self.amphora_mock.id: delta},
self.load_balancer_mock)
mock_driver.delete_port.assert_not_called()
mock_driver.plug_network.assert_not_called()
mock_driver.plug_fixed_ip.assert_not_called()
# Deleting a subnet and a network
mock_driver.reset_mock()
mock_driver.get_subnet.side_effect = [
data_models.Subnet(
id=subnet2,
network_id=network1),
data_models.Subnet(
id=subnet1,
network_id=network1)]
delete_nics = [_interface(network1, port_id=port1)]
delete_subnets = [{
'subnet_id': subnet1,
'network_id': network1,
'port_id': port1}]
delta = data_models.Delta(amphora_id=self.amphora_mock.id,
compute_id=self.amphora_mock.compute_id,
add_nics=[],
delete_nics=delete_nics,
add_subnets=[],
delete_subnets=delete_subnets)
updated_ports = net.execute({self.amphora_mock.id: delta},
self.load_balancer_mock)
mock_driver.delete_port.assert_called_once_with(port1)
mock_driver.plug_network.assert_not_called()
mock_driver.plug_fixed_ip.assert_not_called()
self.assertEqual(1, len(updated_ports))
self.assertEqual(0, len(updated_ports[self.amphora_mock.id]))
# No delta, no actions
mock_driver.reset_mock()
delta = data_models.Delta(amphora_id=self.amphora_mock.id,
compute_id=self.amphora_mock.compute_id,
add_nics=[],
delete_nics=[],
add_subnets=[],
delete_subnets=[])
net.execute({self.amphora_mock.id: delta}, self.load_balancer_mock)
mock_driver.plug_network.assert_not_called()
mock_driver.plug_fixed_ip.assert_not_called()
mock_driver.unplug_network.assert_not_called()
delta = data_models.Delta(amphora_id=self.amphora_mock.id,
compute_id=self.amphora_mock.compute_id,
add_nics=[_interface(1, port_id=12)],
delete_nics=[],
add_subnets=[],
delete_subnets=[])
mock_driver.reset_mock()
mock_driver.unplug_network.side_effect = TestException('test')
self.assertRaises(TestException, net.revert, mock.ANY,
{self.amphora_mock.id: delta})
mock_driver.unplug_network.assert_called_once_with(COMPUTE_ID, 1)
net.revert(None, {self.amphora_mock.id: delta},
self.load_balancer_mock)
mock_driver.unplug_network.assert_called_once_with(
self.amphora_mock.compute_id, 1)
mock_driver.reset_mock()
net.execute({})
mock_driver.delete_port.side_effect = TestException('test')
net.revert(None, {self.amphora_mock.id: delta},
self.load_balancer_mock)
mock_driver.unplug_network.assert_called_once_with(
self.amphora_mock.compute_id, 1)
mock_driver.delete_port.assert_called_once_with(12)
mock_driver.reset_mock()
net.execute({}, self.load_balancer_mock)
self.assertFalse(mock_driver.unplug_network.called)
delta = data_models.Delta(amphora_id=self.amphora_mock.id,
compute_id=self.amphora_mock.compute_id,
add_nics=[],
delete_nics=[])
net.execute({self.amphora_mock.id: delta})
delete_nics=[],
add_subnets=[],
delete_subnets=[])
net.execute({self.amphora_mock.id: delta}, self.load_balancer_mock)
self.assertFalse(mock_driver.unplug_network.called)
delta = data_models.Delta(amphora_id=self.amphora_mock.id,
compute_id=self.amphora_mock.compute_id,
add_nics=[],
delete_nics=_interface(1))
net.execute({self.amphora_mock.id: delta})
delete_nics=[_interface(1)],
add_subnets=[],
delete_subnets=[])
net.execute({self.amphora_mock.id: delta}, self.load_balancer_mock)
mock_driver.unplug_network.assert_called_once_with(COMPUTE_ID, 1)
mock_driver.reset_mock()
mock_driver.unplug_network.side_effect = net_base.NetworkNotFound
net.execute({self.amphora_mock.id: delta})
net.execute({self.amphora_mock.id: delta}, self.load_balancer_mock)
mock_driver.unplug_network.assert_called_once_with(COMPUTE_ID, 1)
# Do a test with a general exception in case behavior changes
mock_driver.reset_mock()
mock_driver.unplug_network.side_effect = Exception()
net.execute({self.amphora_mock.id: delta})
net.execute({self.amphora_mock.id: delta}, self.load_balancer_mock)
mock_driver.unplug_network.assert_called_once_with(COMPUTE_ID, 1)
# Do a test with a general exception in case behavior changes
delta = data_models.Delta(amphora_id=self.amphora_mock.id,
compute_id=self.amphora_mock.compute_id,
add_nics=[],
delete_nics=[_interface(1, port_id=12)],
add_subnets=[],
delete_subnets=[])
mock_driver.reset_mock()
mock_driver.delete_port.side_effect = Exception()
net.execute({self.amphora_mock.id: delta}, self.load_balancer_mock)
mock_driver.unplug_network.assert_called_once_with(COMPUTE_ID, 1)
mock_driver.delete_port.assert_called_once_with(12)
mock_driver.unplug_network.reset_mock()
net.revert(
failure.Failure.from_exception(Exception('boom')), None, None)
mock_driver.unplug_network.assert_not_called()
mock_driver.unplug_network.reset_mock()
net.revert(None, None, None)
mock_driver.unplug_network.assert_not_called()
def test_plug_vip(self, mock_get_net_driver):
mock_driver = mock.MagicMock()
mock_get_net_driver.return_value = mock_driver

View File

@ -731,6 +731,7 @@ class TestControllerWorker(base.TestCase):
store={constants.MEMBER: _member_mock,
constants.LISTENERS: [_listener_mock],
constants.LOADBALANCER: _load_balancer_mock,
constants.LOADBALANCER_ID: _load_balancer_mock.id,
constants.POOL: _pool_mock,
constants.AVAILABILITY_ZONE: {}}))
@ -769,6 +770,8 @@ class TestControllerWorker(base.TestCase):
[_listener_mock],
constants.LOADBALANCER:
_load_balancer_mock,
constants.LOADBALANCER_ID:
_load_balancer_mock.id,
constants.POOL:
_pool_mock,
constants.AVAILABILITY_ZONE: {}}))

View File

@ -281,7 +281,7 @@ class TestAmphoraFlows(base.TestCase):
self.assertIn(constants.LOADBALANCER_ID, amp_flow.requires)
self.assertIn(constants.VIP, amp_flow.requires)
self.assertIn(constants.ADDED_PORTS, amp_flow.provides)
self.assertIn(constants.UPDATED_PORTS, amp_flow.provides)
self.assertIn(constants.AMP_VRRP_INT, amp_flow.provides)
self.assertIn(constants.AMPHORA, amp_flow.provides)
self.assertIn(constants.AMPHORA_ID, amp_flow.provides)
@ -316,7 +316,7 @@ class TestAmphoraFlows(base.TestCase):
self.assertIn(constants.LOADBALANCER_ID, amp_flow.requires)
self.assertIn(constants.VIP, amp_flow.requires)
self.assertIn(constants.ADDED_PORTS, amp_flow.provides)
self.assertIn(constants.UPDATED_PORTS, amp_flow.provides)
self.assertIn(constants.AMPHORA, amp_flow.provides)
self.assertIn(constants.AMPHORA_ID, amp_flow.provides)
self.assertIn(constants.AMPHORAE, amp_flow.provides)

View File

@ -210,8 +210,14 @@ class TestLoadBalancerFlows(base.TestCase):
self.assertIsInstance(create_flow, flow.Flow)
self.assertIn(constants.LOADBALANCER_ID, create_flow.requires)
self.assertIn(constants.UPDATE_DICT, create_flow.requires)
self.assertIn(constants.BUILD_TYPE_PRIORITY, create_flow.requires)
self.assertIn(constants.FLAVOR, create_flow.requires)
self.assertIn(constants.AMPHORAE_NETWORK_CONFIG, create_flow.requires)
self.assertIn(constants.AVAILABILITY_ZONE, create_flow.requires)
self.assertIn(constants.SERVER_GROUP_ID, create_flow.requires)
self.assertIn(constants.LISTENERS, create_flow.provides)
self.assertIn(constants.SUBNET, create_flow.provides)
self.assertIn(constants.AMPHORA, create_flow.provides)
self.assertIn(constants.AMPHORA_ID, create_flow.provides)
self.assertIn(constants.AMPHORA_NETWORK_CONFIG, create_flow.provides)
@ -220,12 +226,11 @@ class TestLoadBalancerFlows(base.TestCase):
self.assertIn(constants.COMPUTE_OBJ, create_flow.provides)
self.assertIn(constants.LOADBALANCER, create_flow.provides)
self.assertIn(constants.DELTAS, create_flow.provides)
self.assertIn(constants.ADDED_PORTS, create_flow.provides)
self.assertIn(constants.UPDATED_PORTS, create_flow.provides)
self.assertIn(constants.SERVER_PEM, create_flow.provides)
self.assertIn(constants.SUBNET, create_flow.provides)
self.assertIn(constants.VIP, create_flow.provides)
self.assertEqual(6, len(create_flow.requires))
self.assertEqual(7, len(create_flow.requires))
self.assertEqual(13, len(create_flow.provides),
create_flow.provides)
@ -244,7 +249,7 @@ class TestLoadBalancerFlows(base.TestCase):
self.assertIn(constants.SERVER_GROUP_ID, create_flow.requires)
self.assertIn(constants.UPDATE_DICT, create_flow.requires)
self.assertIn(constants.ADDED_PORTS, create_flow.provides)
self.assertIn(constants.UPDATED_PORTS, create_flow.provides)
self.assertIn(constants.AMP_DATA, create_flow.provides)
self.assertIn(constants.AMP_VRRP_INT, create_flow.provides)
self.assertIn(constants.AMPHORA, create_flow.provides)
@ -280,7 +285,7 @@ class TestLoadBalancerFlows(base.TestCase):
self.assertIn(constants.LOADBALANCER, failover_flow.requires)
self.assertIn(constants.LOADBALANCER_ID, failover_flow.requires)
self.assertIn(constants.ADDED_PORTS, failover_flow.provides)
self.assertIn(constants.UPDATED_PORTS, failover_flow.provides)
self.assertIn(constants.AMPHORA, failover_flow.provides)
self.assertIn(constants.AMPHORA_ID, failover_flow.provides)
self.assertIn(constants.AMPHORAE_NETWORK_CONFIG,
@ -343,7 +348,7 @@ class TestLoadBalancerFlows(base.TestCase):
self.assertIn(constants.LOADBALANCER, failover_flow.requires)
self.assertIn(constants.LOADBALANCER_ID, failover_flow.requires)
self.assertIn(constants.ADDED_PORTS, failover_flow.provides)
self.assertIn(constants.UPDATED_PORTS, failover_flow.provides)
self.assertIn(constants.AMPHORA, failover_flow.provides)
self.assertIn(constants.AMPHORA_ID, failover_flow.provides)
self.assertIn(constants.AMPHORAE_NETWORK_CONFIG,

View File

@ -37,18 +37,19 @@ class TestMemberFlows(base.TestCase):
self.assertIsInstance(member_flow, flow.Flow)
self.assertIn(constants.MEMBER, member_flow.requires)
self.assertIn(constants.LISTENERS, member_flow.requires)
self.assertIn(constants.LOADBALANCER, member_flow.requires)
self.assertIn(constants.LOADBALANCER_ID, member_flow.requires)
self.assertIn(constants.POOL_ID, member_flow.requires)
self.assertIn(constants.MEMBER, member_flow.requires)
self.assertIn(constants.AVAILABILITY_ZONE, member_flow.requires)
self.assertIn(constants.DELTAS, member_flow.provides)
self.assertIn(constants.ADDED_PORTS, member_flow.provides)
self.assertIn(constants.UPDATED_PORTS, member_flow.provides)
self.assertIn(constants.AMPHORAE_NETWORK_CONFIG, member_flow.provides)
self.assertEqual(6, len(member_flow.requires))
self.assertEqual(2, len(member_flow.provides))
self.assertEqual(3, len(member_flow.provides))
def test_get_delete_member_flow(self, mock_get_net_driver):
@ -62,9 +63,14 @@ class TestMemberFlows(base.TestCase):
self.assertIn(constants.LOADBALANCER_ID, member_flow.requires)
self.assertIn(constants.POOL_ID, member_flow.requires)
self.assertIn(constants.PROJECT_ID, member_flow.requires)
self.assertIn(constants.AVAILABILITY_ZONE, member_flow.requires)
self.assertEqual(6, len(member_flow.requires))
self.assertEqual(0, len(member_flow.provides))
self.assertIn(constants.DELTAS, member_flow.provides)
self.assertIn(constants.UPDATED_PORTS, member_flow.provides)
self.assertIn(constants.AMPHORAE_NETWORK_CONFIG, member_flow.provides)
self.assertEqual(7, len(member_flow.requires))
self.assertEqual(3, len(member_flow.provides))
def test_get_update_member_flow(self, mock_get_net_driver):
@ -96,7 +102,8 @@ class TestMemberFlows(base.TestCase):
self.assertIn(constants.AVAILABILITY_ZONE, member_flow.requires)
self.assertIn(constants.DELTAS, member_flow.provides)
self.assertIn(constants.ADDED_PORTS, member_flow.provides)
self.assertIn(constants.UPDATED_PORTS, member_flow.provides)
self.assertIn(constants.AMPHORAE_NETWORK_CONFIG, member_flow.provides)
self.assertEqual(5, len(member_flow.requires))
self.assertEqual(2, len(member_flow.provides))
self.assertEqual(3, len(member_flow.provides))

View File

@ -58,6 +58,9 @@ _LB_mock = {
constants.LOADBALANCER_ID: LB_ID,
}
_amphorae_mock = [_db_amphora_mock]
_amphora_network_config_mock = mock.MagicMock()
_amphorae_network_config_mock = {
_amphora_mock[constants.ID]: _amphora_network_config_mock}
_network_mock = mock.MagicMock()
_session_mock = mock.MagicMock()
@ -68,7 +71,7 @@ _session_mock = mock.MagicMock()
@mock.patch('octavia.db.repositories.ListenerRepository.get',
return_value=_listener_mock)
@mock.patch('octavia.db.api.get_session', return_value=_session_mock)
@mock.patch('octavia.controller.worker.v2.tasks.amphora_driver_tasks.LOG')
@mock.patch('octavia.controller.worker.v1.tasks.amphora_driver_tasks.LOG')
@mock.patch('oslo_utils.uuidutils.generate_uuid', return_value=AMP_ID)
@mock.patch('stevedore.driver.DriverManager.driver')
class TestAmphoraDriverTasks(base.TestCase):
@ -385,11 +388,13 @@ class TestAmphoraDriverTasks(base.TestCase):
port_mock = {constants.NETWORK: mock.MagicMock(),
constants.FIXED_IPS: fixed_ips,
constants.ID: uuidutils.generate_uuid()}
amphora_post_network_plug_obj.execute(_amphora_mock, [port_mock])
amphora_post_network_plug_obj.execute(_amphora_mock, [port_mock],
_amphora_network_config_mock)
(mock_driver.post_network_plug.
assert_called_once_with)(_db_amphora_mock,
network_data_models.Port(**port_mock))
network_data_models.Port(**port_mock),
_amphora_network_config_mock)
# Test revert
amp = amphora_post_network_plug_obj.revert(None, _amphora_mock)
@ -434,11 +439,13 @@ class TestAmphoraDriverTasks(base.TestCase):
port_mock = {constants.NETWORK: mock.MagicMock(),
constants.FIXED_IPS: fixed_ips,
constants.ID: uuidutils.generate_uuid()}
amphora_post_network_plug_obj.execute(_amphora_mock, [port_mock])
amphora_post_network_plug_obj.execute(_amphora_mock, [port_mock],
_amphora_network_config_mock)
(mock_driver.post_network_plug.
assert_called_once_with)(_db_amphora_mock,
network_data_models.Port(**port_mock))
network_data_models.Port(**port_mock),
_amphora_network_config_mock)
call_args = mock_driver.post_network_plug.call_args[0]
port_arg = call_args[1]
@ -472,18 +479,21 @@ class TestAmphoraDriverTasks(base.TestCase):
constants.ID: uuidutils.generate_uuid()}
_deltas_mock = {_db_amphora_mock.id: [port_mock]}
amphora_post_network_plug_obj.execute(_LB_mock, _deltas_mock)
amphora_post_network_plug_obj.execute(_LB_mock, _deltas_mock,
_amphorae_network_config_mock)
(mock_driver.post_network_plug.
assert_called_once_with(_db_amphora_mock,
network_data_models.Port(**port_mock)))
network_data_models.Port(**port_mock),
_amphora_network_config_mock))
# Test with no ports to plug
mock_driver.post_network_plug.reset_mock()
_deltas_mock = {'0': [port_mock]}
amphora_post_network_plug_obj.execute(_LB_mock, _deltas_mock)
amphora_post_network_plug_obj.execute(_LB_mock, _deltas_mock,
_amphora_network_config_mock)
mock_driver.post_network_plug.assert_not_called()
# Test revert

View File

@ -563,3 +563,144 @@ class TestBaseNeutronNetworkDriver(base.TestCase):
self.assertEqual(t_constants.MOCK_NETWORK_ID, ip_avail.network_id)
self.assertEqual(t_constants.MOCK_SUBNET_IP_AVAILABILITY,
ip_avail.subnet_ip_availability)
def test_plug_fixed_ip(self):
show_port = self.driver.neutron_client.show_port
show_port.return_value = {
'id': t_constants.MOCK_PORT_ID,
'fixed_ips': [
{
'subnet_id': t_constants.MOCK_SUBNET_ID,
'ip_address': t_constants.MOCK_IP_ADDRESS,
'subnet': None
}]
}
self.driver.plug_fixed_ip(t_constants.MOCK_PORT_ID,
t_constants.MOCK_SUBNET_ID2,
t_constants.MOCK_IP_ADDRESS2)
expected_body = {
'port': {
'fixed_ips': [
{
'subnet_id': t_constants.MOCK_SUBNET_ID,
'ip_address': t_constants.MOCK_IP_ADDRESS,
'subnet': None
}, {
'subnet_id': t_constants.MOCK_SUBNET_ID2,
'ip_address': t_constants.MOCK_IP_ADDRESS2
}
]
}
}
self.driver.neutron_client.update_port.assert_called_once_with(
t_constants.MOCK_PORT_ID,
expected_body)
def test_plug_fixed_ip_no_ip_address(self):
show_port = self.driver.neutron_client.show_port
show_port.return_value = {
'id': t_constants.MOCK_PORT_ID,
'fixed_ips': [
{
'subnet_id': t_constants.MOCK_SUBNET_ID,
'ip_address': t_constants.MOCK_IP_ADDRESS,
'subnet': None
}]
}
self.driver.plug_fixed_ip(t_constants.MOCK_PORT_ID,
t_constants.MOCK_SUBNET_ID2)
expected_body = {
'port': {
'fixed_ips': [
{
'subnet_id': t_constants.MOCK_SUBNET_ID,
'ip_address': t_constants.MOCK_IP_ADDRESS,
'subnet': None
}, {
'subnet_id': t_constants.MOCK_SUBNET_ID2,
}
]
}
}
self.driver.neutron_client.update_port.assert_called_once_with(
t_constants.MOCK_PORT_ID,
expected_body)
def test_plug_fixed_ip_exception(self):
show_port = self.driver.neutron_client.show_port
show_port.return_value = {
'id': t_constants.MOCK_PORT_ID,
'fixed_ips': [
{
'subnet_id': t_constants.MOCK_SUBNET_ID,
'ip_address': t_constants.MOCK_IP_ADDRESS,
'subnet': None
}]
}
self.driver.neutron_client.update_port.side_effect = Exception
self.assertRaises(network_base.NetworkException,
self.driver.plug_fixed_ip,
t_constants.MOCK_PORT_ID,
t_constants.MOCK_SUBNET_ID2)
def test_unplug_fixed_ip(self):
show_port = self.driver.neutron_client.show_port
show_port.return_value = {
'id': t_constants.MOCK_PORT_ID,
'fixed_ips': [
{
'subnet_id': t_constants.MOCK_SUBNET_ID,
'ip_address': t_constants.MOCK_IP_ADDRESS,
'subnet': None
}, {
'subnet_id': t_constants.MOCK_SUBNET_ID2,
'ip_address': t_constants.MOCK_IP_ADDRESS2,
'subnet': None
}]
}
self.driver.unplug_fixed_ip(t_constants.MOCK_PORT_ID,
t_constants.MOCK_SUBNET_ID)
expected_body = {
'port': {
'fixed_ips': [
{
'subnet_id': t_constants.MOCK_SUBNET_ID2,
'ip_address': t_constants.MOCK_IP_ADDRESS2,
'subnet': None
}
]
}
}
self.driver.neutron_client.update_port.assert_called_once_with(
t_constants.MOCK_PORT_ID,
expected_body)
def test_unplug_fixed_ip_exception(self):
show_port = self.driver.neutron_client.show_port
show_port.return_value = {
'id': t_constants.MOCK_PORT_ID,
'fixed_ips': [
{
'subnet_id': t_constants.MOCK_SUBNET_ID,
'ip_address': t_constants.MOCK_IP_ADDRESS,
'subnet': None
}]
}
self.driver.neutron_client.update_port.side_effect = Exception
self.assertRaises(network_base.NetworkException,
self.driver.unplug_fixed_ip,
t_constants.MOCK_PORT_ID,
t_constants.MOCK_SUBNET_ID)

View File

@ -28,6 +28,7 @@ class TestNoopNetworkDriver(base.TestCase):
FAKE_UUID_4 = uuidutils.generate_uuid()
FAKE_UUID_5 = uuidutils.generate_uuid()
FAKE_UUID_6 = uuidutils.generate_uuid()
FAKE_UUID_7 = uuidutils.generate_uuid()
def setUp(self):
super().setUp()
@ -49,6 +50,7 @@ class TestNoopNetworkDriver(base.TestCase):
self.vip.port_id = uuidutils.generate_uuid()
self.amphora_id = self.FAKE_UUID_1
self.compute_id = self.FAKE_UUID_2
self.compute2_id = self.FAKE_UUID_2
self.subnet_id = self.FAKE_UUID_3
self.subnet_name = 'subnet1'
self.qos_policy_id = self.FAKE_UUID_5
@ -56,12 +58,14 @@ class TestNoopNetworkDriver(base.TestCase):
self.amphora1 = models.Amphora()
self.amphora1.id = uuidutils.generate_uuid()
self.amphora1.compute_id = self.compute_id
self.amphora1.vrrp_port_id = uuidutils.generate_uuid()
self.amphora1.ha_port_id = uuidutils.generate_uuid()
self.amphora1.vrrp_ip = '10.0.1.10'
self.amphora1.ha_ip = '10.0.1.11'
self.amphora2 = models.Amphora()
self.amphora2.id = uuidutils.generate_uuid()
self.amphora2.compute_id = self.compute2_id
self.amphora2.vrrp_port_id = uuidutils.generate_uuid()
self.amphora2.ha_port_id = uuidutils.generate_uuid()
self.amphora2.vrrp_ip = '10.0.2.10'
@ -69,6 +73,7 @@ class TestNoopNetworkDriver(base.TestCase):
self.load_balancer.amphorae = [self.amphora1, self.amphora2]
self.load_balancer.vip = self.vip
self.subnet = mock.MagicMock()
self.subnet.id = self.subnet_id
def test_allocate_vip(self):
self.driver.allocate_vip(self.load_balancer)
@ -105,28 +110,54 @@ class TestNoopNetworkDriver(base.TestCase):
self.load_balancer.id, self.vip.ip_address)])
def test_plug_network(self):
self.driver.plug_network(self.amphora_id, self.network_id,
self.ip_address)
self.assertEqual((self.amphora_id, self.network_id, self.ip_address,
self.driver.plug_network(self.compute_id, self.network_id,
self.subnet_id)
self.assertEqual((self.compute_id, self.network_id, self.subnet_id,
'plug_network'),
self.driver.driver.networkconfigconfig[(
self.amphora_id, self.network_id,
self.ip_address)])
self.compute_id, self.network_id)])
def test_unplug_network(self):
self.driver.unplug_network(self.amphora_id, self.network_id,
ip_address=self.ip_address)
self.assertEqual((self.amphora_id, self.network_id, self.ip_address,
'unplug_network'),
self.driver.unplug_network(self.compute_id, self.network_id)
self.assertEqual((self.compute_id, self.network_id, 'unplug_network'),
self.driver.driver.networkconfigconfig[(
self.amphora_id, self.network_id,
self.ip_address)])
self.compute_id, self.network_id)])
def test_get_plugged_networks(self):
self.driver.get_plugged_networks(self.amphora_id)
self.assertEqual((self.amphora_id, 'get_plugged_networks'),
self.driver.get_plugged_networks(self.compute_id)
self.assertEqual((self.compute_id, 'get_plugged_networks'),
self.driver.driver.networkconfigconfig[(
self.amphora_id)])
self.compute_id)])
def test_plug_unplug_and_get_plugged_networks(self):
amphora = mock.MagicMock()
amphora.compute_id = uuidutils.generate_uuid()
network = self.driver.plug_network(amphora.compute_id,
self.network_id,
self.subnet_id)
self.assertEqual(
network,
network_models.Interface(
id=mock.ANY,
compute_id=amphora.compute_id,
network_id=self.network_id,
fixed_ips=[],
port_id=mock.ANY
))
networks = self.driver.get_plugged_networks(amphora.compute_id)
self.assertEqual(
networks,
[network_models.Interface(
id=mock.ANY,
compute_id=amphora.compute_id,
network_id=self.network_id,
fixed_ips=[],
port_id=mock.ANY
)])
self.driver.unplug_network(amphora.compute_id,
self.network_id)
networks = self.driver.get_plugged_networks(amphora.compute_id)
self.assertEqual([], networks)
def test_update_vip(self):
self.driver.update_vip(self.load_balancer)
@ -293,3 +324,12 @@ class TestNoopNetworkDriver(base.TestCase):
self.assertEqual(SUBNET_ID, result.fixed_ips[1].subnet_id)
self.assertEqual(QOS_POLICY_ID, result.qos_policy_id)
self.assertFalse(result.admin_state_up)
def test_plug_fixed_ip(self):
self.driver.plug_fixed_ip(self.port_id, self.subnet_id,
self.ip_address)
self.assertEqual(
(self.port_id, self.subnet_id, self.ip_address, 'plug_fixed_ip'),
self.driver.driver.networkconfigconfig[
self.port_id, self.subnet_id]
)

View File

@ -0,0 +1,8 @@
---
fixes:
- |
Fix a bug when adding a member on a subnet that belongs to a network with
multiple subnets, an incorrect subnet may have been plugged in the amphora.
- |
Fix a bug when deleting the last member plugged on a network, the port that
was no longer used was not deleted.