Fix plugging member subnets on existing networks
CalculateAmphoraDelta uses now subnets 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. Stable branches: Also includes a follow up patch for amphorav1 [0] [0] Ibbfca939ff580fd13c094c86721f703a711ffc46 Co-Authored-By: Gregory Thiemonge <gthiemon@redhat.com> Story: 2004112 Task: 27533 Change-Id: I1384c6f52eec99e6573a8e83fe5a80a632804083 (cherry picked from commit 4b0d4c1b591e41d42fba87ce4a50a899abee6fcf) (cherry picked from commit cade09e72af8361479f883fc08bed9dc105024dd)
This commit is contained in:
parent
c513c9e77a
commit
16fa7f8eb9
@ -164,10 +164,10 @@ class AmphoraInfo(object):
|
||||
for interface in netns.get_links():
|
||||
interface_name = None
|
||||
for item in interface['attrs']:
|
||||
if (item[0] == 'IFLA_IFNAME' and
|
||||
if (item[0] == consts.IFLA_IFNAME and
|
||||
not item[1].startswith('eth')):
|
||||
break
|
||||
if item[0] == 'IFLA_IFNAME':
|
||||
if item[0] == consts.IFLA_IFNAME:
|
||||
interface_name = item[1]
|
||||
if item[0] == 'IFLA_STATS64':
|
||||
networks[interface_name] = dict(
|
||||
|
@ -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)
|
||||
|
@ -132,23 +132,61 @@ 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):
|
||||
# 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
|
||||
if self._netns_interface_exists(mac_address):
|
||||
return webob.Response(json=dict(
|
||||
message="Interface already exists"), status=409)
|
||||
|
||||
# This is the interface as it was initially plugged into the
|
||||
# default network namespace, this will likely always be eth1
|
||||
|
||||
def plug_network(self, mac_address, fixed_ips, mtu=None,
|
||||
vip_net_info=None):
|
||||
try:
|
||||
self._check_ip_addresses(fixed_ips=fixed_ips)
|
||||
except socket.error:
|
||||
return webob.Response(json=dict(
|
||||
message="Invalid network port"), status=400)
|
||||
|
||||
# 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, just ensure all fixed_ips are up
|
||||
if self._netns_interface_exists(mac_address):
|
||||
# Get the existing interface name and path
|
||||
existing_interface = self._netns_interface_by_mac(mac_address)
|
||||
|
||||
# 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)
|
||||
|
||||
# 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
|
||||
@ -192,7 +230,7 @@ class Plug(object):
|
||||
idx = ipr.link_lookup(address=mac)[0]
|
||||
addr = ipr.get_links(idx)[0]
|
||||
for attr in addr['attrs']:
|
||||
if attr[0] == 'IFLA_IFNAME':
|
||||
if attr[0] == consts.IFLA_IFNAME:
|
||||
return attr[1]
|
||||
except Exception as e:
|
||||
LOG.info('Unable to find interface with MAC: %s, rescanning '
|
||||
@ -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(consts.IFLA_ADDRESS) == mac_address:
|
||||
return attr_dict.get(consts.IFLA_IFNAME)
|
||||
return None
|
||||
|
||||
def _netns_interface_exists(self, mac_address):
|
||||
return self._netns_interface_by_mac(mac_address) is not None
|
||||
|
@ -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()
|
||||
|
@ -129,13 +129,14 @@ class VIPInterfaceFile(InterfaceFile):
|
||||
consts.GATEWAY: gateway,
|
||||
consts.FLAGS: [consts.ONLINK]
|
||||
})
|
||||
self.routes.append({
|
||||
consts.DST: (
|
||||
"::/0" if ip_version == 6 else "0.0.0.0/0"),
|
||||
consts.GATEWAY: gateway,
|
||||
consts.FLAGS: [consts.ONLINK],
|
||||
consts.TABLE: 1,
|
||||
})
|
||||
if topology != consts.TOPOLOGY_ACTIVE_STANDBY:
|
||||
self.routes.append({
|
||||
consts.DST: (
|
||||
"::/0" if ip_version == 6 else "0.0.0.0/0"),
|
||||
consts.GATEWAY: gateway,
|
||||
consts.FLAGS: [consts.ONLINK],
|
||||
consts.TABLE: 1,
|
||||
})
|
||||
|
||||
# In ACTIVE_STANDBY topology, keepalived configures the VIP address.
|
||||
# Keep track of it in the interface file but mark it with a special
|
||||
@ -148,9 +149,13 @@ class VIPInterfaceFile(InterfaceFile):
|
||||
# tool (keepalived)
|
||||
consts.OCTAVIA_OWNED: topology != consts.TOPOLOGY_ACTIVE_STANDBY
|
||||
})
|
||||
vip_cidr = ipaddress.ip_network(
|
||||
"{}/{}".format(vip, prefixlen), strict=False)
|
||||
self.routes.append({
|
||||
consts.DST: vip_cidr.exploded,
|
||||
consts.SCOPE: 'link',
|
||||
})
|
||||
if topology != consts.TOPOLOGY_ACTIVE_STANDBY:
|
||||
vip_cidr = ipaddress.ip_network(
|
||||
"{}/{}".format(vip, prefixlen), strict=False)
|
||||
self.routes.append({
|
||||
consts.DST: vip_cidr.exploded,
|
||||
consts.PREFSRC: vip,
|
||||
@ -169,23 +174,40 @@ class VIPInterfaceFile(InterfaceFile):
|
||||
|
||||
ip_versions = {ip_version}
|
||||
|
||||
if fixed_ips:
|
||||
for fixed_ip in fixed_ips:
|
||||
ip_addr = fixed_ip['ip_address']
|
||||
cidr = fixed_ip['subnet_cidr']
|
||||
ip = ipaddress.ip_address(ip_addr)
|
||||
network = ipaddress.ip_network(cidr)
|
||||
prefixlen = network.prefixlen
|
||||
self.addresses.append({
|
||||
consts.ADDRESS: fixed_ip['ip_address'],
|
||||
consts.PREFIXLEN: prefixlen,
|
||||
for fixed_ip in fixed_ips or ():
|
||||
ip_addr = fixed_ip['ip_address']
|
||||
cidr = fixed_ip['subnet_cidr']
|
||||
ip = ipaddress.ip_address(ip_addr)
|
||||
network = ipaddress.ip_network(cidr)
|
||||
prefixlen = network.prefixlen
|
||||
self.addresses.append({
|
||||
consts.ADDRESS: fixed_ip['ip_address'],
|
||||
consts.PREFIXLEN: prefixlen,
|
||||
})
|
||||
|
||||
ip_versions.add(ip.version)
|
||||
|
||||
gateway = fixed_ip.get('gateway')
|
||||
if gateway:
|
||||
# Add default routes if there's a gateway
|
||||
self.routes.append({
|
||||
consts.DST: (
|
||||
"::/0" if ip.version == 6 else "0.0.0.0/0"),
|
||||
consts.GATEWAY: gateway,
|
||||
consts.FLAGS: [consts.ONLINK]
|
||||
})
|
||||
if topology != consts.TOPOLOGY_ACTIVE_STANDBY:
|
||||
self.routes.append({
|
||||
consts.DST: (
|
||||
"::/0" if ip.version == 6 else "0.0.0.0/0"),
|
||||
consts.GATEWAY: gateway,
|
||||
consts.FLAGS: [consts.ONLINK],
|
||||
consts.TABLE: 1,
|
||||
})
|
||||
|
||||
ip_versions.add(ip.version)
|
||||
|
||||
host_routes = self.get_host_routes(
|
||||
fixed_ip.get('host_routes', []))
|
||||
self.routes.extend(host_routes)
|
||||
host_routes = self.get_host_routes(
|
||||
fixed_ip.get('host_routes', []))
|
||||
self.routes.extend(host_routes)
|
||||
|
||||
for ip_v in ip_versions:
|
||||
self.scripts[consts.IFACE_UP].append({
|
||||
|
@ -15,6 +15,7 @@ import ipaddress
|
||||
|
||||
import pyroute2
|
||||
|
||||
from octavia.common import constants as consts
|
||||
from octavia.common import exceptions
|
||||
|
||||
|
||||
@ -47,7 +48,7 @@ def _find_interface(ip_address, rtnl_api, normalized_addr):
|
||||
for int_attr in lookup_int[0]['attrs']:
|
||||
# Look for the attribute name/value pair that includes
|
||||
# the interface name
|
||||
if int_attr[0] == 'IFLA_IFNAME':
|
||||
if int_attr[0] == consts.IFLA_IFNAME:
|
||||
# Return the matching interface name that is in
|
||||
# int_attr[1] for the matching interface attribute
|
||||
# name
|
||||
|
@ -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
|
||||
|
@ -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__)
|
||||
@ -390,23 +391,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
|
||||
@ -415,15 +426,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)
|
||||
@ -432,7 +437,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,
|
||||
@ -440,11 +445,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)
|
||||
|
@ -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):
|
||||
|
@ -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'
|
||||
@ -925,5 +933,8 @@ IFACE_DOWN = 'down'
|
||||
|
||||
COMMAND = 'command'
|
||||
|
||||
IFLA_ADDRESS = 'IFLA_ADDRESS'
|
||||
IFLA_IFNAME = 'IFLA_IFNAME'
|
||||
|
||||
# Amphora network directory
|
||||
AMP_NET_DIR_TEMPLATE = '/etc/octavia/interfaces/'
|
||||
|
@ -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] = (
|
||||
@ -512,6 +514,7 @@ class ControllerWorker(base_taskflow.BaseTaskFlowEngine):
|
||||
store = {
|
||||
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] = (
|
||||
|
@ -436,18 +436,17 @@ class AmphoraFlows(object):
|
||||
amp_for_failover_flow.add(network_tasks.CalculateAmphoraDelta(
|
||||
name=prefix + '-' + constants.CALCULATE_AMPHORA_DELTA,
|
||||
requires=(constants.LOADBALANCER, constants.AMPHORA,
|
||||
constants.AVAILABILITY_ZONE, constants.VRRP_PORT),
|
||||
rebind={constants.VRRP_PORT: constants.BASE_PORT},
|
||||
constants.AVAILABILITY_ZONE),
|
||||
provides=constants.DELTA))
|
||||
|
||||
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
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
@ -53,42 +53,101 @@ class CalculateAmphoraDelta(BaseNetworkTask):
|
||||
|
||||
default_provides = constants.DELTA
|
||||
|
||||
def execute(self, loadbalancer, amphora, availability_zone,
|
||||
vrrp_port=None):
|
||||
def execute(self, loadbalancer, amphora, availability_zone):
|
||||
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)
|
||||
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)]
|
||||
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 = {}
|
||||
for mgmt_net_id in management_nets:
|
||||
for subnet_id in self.network_driver.get_network(
|
||||
mgmt_net_id).subnets:
|
||||
desired_subnet_to_net_map[subnet_id] = mgmt_net_id
|
||||
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)
|
||||
# assume we don't have two nics in the same network
|
||||
actual_network_nics = dict((nic.network_id, nic) for nic in nics)
|
||||
desired_network_ids = set(desired_subnet_to_net_map.values())
|
||||
desired_subnet_ids = set(desired_subnet_to_net_map)
|
||||
|
||||
del_ids = set(actual_network_nics) - desired_network_ids
|
||||
delete_nics = list(
|
||||
actual_network_nics[net_id] for net_id in del_ids)
|
||||
# Calculate Network deltas
|
||||
nics = self.network_driver.get_plugged_networks(
|
||||
amphora.compute_id)
|
||||
# we don't have two nics in the same network
|
||||
network_to_nic_map = {nic.network_id: nic for nic in nics}
|
||||
|
||||
plugged_network_ids = set(network_to_nic_map)
|
||||
|
||||
del_ids = plugged_network_ids - 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 - plugged_network_ids
|
||||
add_nics = [n_data_models.Interface(
|
||||
network_id=add_net_id,
|
||||
fixed_ips=[
|
||||
n_data_models.FixedIP(
|
||||
subnet_id=subnet_id)
|
||||
for subnet_id, net_id in desired_subnet_to_net_map.items()
|
||||
if net_id == add_net_id])
|
||||
for add_net_id in add_ids]
|
||||
|
||||
# Calculate member Subnet deltas
|
||||
plugged_subnets = {}
|
||||
for nic in network_to_nic_map.values():
|
||||
for fixed_ip in nic.fixed_ips or []:
|
||||
plugged_subnets[fixed_ip.subnet_id] = nic.network_id
|
||||
|
||||
plugged_subnet_ids = set(plugged_subnets)
|
||||
del_subnet_ids = plugged_subnet_ids - desired_subnet_ids
|
||||
add_subnet_ids = desired_subnet_ids - plugged_subnet_ids
|
||||
|
||||
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 +293,87 @@ 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 = nic.fixed_ips[0].subnet_id
|
||||
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)
|
||||
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)
|
||||
# nova may plugged undesired subnets (it plugs one of the subnets
|
||||
# of the network), we can safely unplug the subnets we don't need,
|
||||
# the desired subnet will be added in the 'ADD_SUBNETS' loop.
|
||||
extra_subnets = [
|
||||
fixed_ip.subnet_id
|
||||
for fixed_ip in port.fixed_ips
|
||||
if fixed_ip.subnet_id != subnet_id]
|
||||
for subnet_id in extra_subnets:
|
||||
port = self.network_driver.unplug_fixed_ip(
|
||||
port_id=interface.port_id, subnet_id=subnet_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:
|
||||
has_subnet = any(
|
||||
fixed_ip.subnet_id == subnet_id
|
||||
for fixed_ip in updated_ports[network_id].fixed_ips)
|
||||
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 = any(
|
||||
fixed_ip.subnet_id == subnet_id
|
||||
for fixed_ip in port.fixed_ips)
|
||||
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."""
|
||||
@ -274,7 +392,14 @@ class HandleNetworkDelta(BaseNetworkTask):
|
||||
self.network_driver.unplug_network(delta.compute_id,
|
||||
nic.network_id)
|
||||
except Exception:
|
||||
pass
|
||||
LOG.exception("Unable to unplug network %s",
|
||||
nic.network_id)
|
||||
|
||||
port_id = nic.port_id
|
||||
try:
|
||||
self.network_driver.delete_port(port_id)
|
||||
except Exception:
|
||||
LOG.exception("Unable to delete port %s", port_id)
|
||||
|
||||
|
||||
class HandleNetworkDeltas(BaseNetworkTask):
|
||||
@ -284,35 +409,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,8 +441,15 @@ class HandleNetworkDeltas(BaseNetworkTask):
|
||||
try:
|
||||
self.network_driver.unplug_network(delta.compute_id,
|
||||
nic.network_id)
|
||||
except base.NetworkNotFound:
|
||||
pass
|
||||
except Exception:
|
||||
LOG.exception("Unable to unplug network %s",
|
||||
nic.network_id)
|
||||
|
||||
port_id = nic.port_id
|
||||
try:
|
||||
self.network_driver.delete_port(port_id)
|
||||
except Exception:
|
||||
LOG.exception("Unable to delete port %s", port_id)
|
||||
|
||||
|
||||
class PlugVIP(BaseNetworkTask):
|
||||
|
@ -403,18 +403,17 @@ class AmphoraFlows(object):
|
||||
amp_for_failover_flow.add(network_tasks.CalculateAmphoraDelta(
|
||||
name=prefix + '-' + constants.CALCULATE_AMPHORA_DELTA,
|
||||
requires=(constants.LOADBALANCER, constants.AMPHORA,
|
||||
constants.AVAILABILITY_ZONE, constants.VRRP_PORT),
|
||||
rebind={constants.VRRP_PORT: constants.BASE_PORT},
|
||||
constants.AVAILABILITY_ZONE),
|
||||
provides=constants.DELTA))
|
||||
|
||||
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
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
@ -55,17 +55,15 @@ class CalculateAmphoraDelta(BaseNetworkTask):
|
||||
|
||||
default_provides = constants.DELTA
|
||||
|
||||
def execute(self, loadbalancer, amphora, availability_zone,
|
||||
vrrp_port=None):
|
||||
# TODO(gthiemonge) ensure we no longer need vrrp_port
|
||||
def execute(self, loadbalancer, amphora, availability_zone):
|
||||
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 +73,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 = {}
|
||||
for mgmt_net_id in management_nets:
|
||||
for subnet_id in self.network_driver.get_network(
|
||||
mgmt_net_id).subnets:
|
||||
desired_subnet_to_net_map[subnet_id] = mgmt_net_id
|
||||
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)
|
||||
# we don't have two nics in the same network
|
||||
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)
|
||||
plugged_network_ids = set(network_to_nic_map)
|
||||
|
||||
del_ids = plugged_network_ids - 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 - plugged_network_ids
|
||||
add_nics = [n_data_models.Interface(
|
||||
network_id=add_net_id,
|
||||
fixed_ips=[
|
||||
n_data_models.FixedIP(
|
||||
subnet_id=subnet_id)
|
||||
for subnet_id, net_id in desired_subnet_to_net_map.items()
|
||||
if net_id == add_net_id])
|
||||
for add_net_id in add_ids]
|
||||
|
||||
# Calculate member Subnet deltas
|
||||
plugged_subnets = {}
|
||||
for nic in network_to_nic_map.values():
|
||||
for fixed_ip in nic.fixed_ips or []:
|
||||
plugged_subnets[fixed_ip.subnet_id] = nic.network_id
|
||||
|
||||
plugged_subnet_ids = set(plugged_subnets)
|
||||
del_subnet_ids = plugged_subnet_ids - desired_subnet_ids
|
||||
add_subnet_ids = desired_subnet_ids - plugged_subnet_ids
|
||||
|
||||
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 +309,92 @@ 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 = nic[constants.FIXED_IPS][0][constants.SUBNET_ID]
|
||||
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)
|
||||
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))
|
||||
# nova may plugged undesired subnets (it plugs one of the subnets
|
||||
# of the network), we can safely unplug the subnets we don't need,
|
||||
# the desired subnet will be added in the 'ADD_SUBNETS' loop.
|
||||
extra_subnets = [
|
||||
fixed_ip.subnet_id
|
||||
for fixed_ip in port.fixed_ips
|
||||
if fixed_ip.subnet_id != subnet_id]
|
||||
for subnet_id in extra_subnets:
|
||||
port = self.network_driver.unplug_fixed_ip(
|
||||
port_id=interface.port_id, subnet_id=subnet_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:
|
||||
has_subnet = any(
|
||||
fixed_ip[constants.SUBNET_ID] == subnet_id
|
||||
for fixed_ip in updated_ports[network_id][
|
||||
constants.FIXED_IPS])
|
||||
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 = any(
|
||||
fixed_ip.subnet_id == subnet_id
|
||||
for fixed_ip in port.fixed_ips)
|
||||
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."""
|
||||
@ -297,7 +413,14 @@ class HandleNetworkDelta(BaseNetworkTask):
|
||||
self.network_driver.unplug_network(delta[constants.COMPUTE_ID],
|
||||
nic[constants.NETWORK_ID])
|
||||
except Exception:
|
||||
pass
|
||||
LOG.exception("Unable to unplug network %s",
|
||||
nic[constants.NETWORK_ID])
|
||||
|
||||
port_id = nic[constants.PORT_ID]
|
||||
try:
|
||||
self.network_driver.delete_port(port_id)
|
||||
except Exception:
|
||||
LOG.exception("Unable to delete port %s", port_id)
|
||||
|
||||
|
||||
class HandleNetworkDeltas(BaseNetworkTask):
|
||||
@ -307,50 +430,47 @@ 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:
|
||||
pass
|
||||
except Exception:
|
||||
LOG.exception("Unable to unplug network %s",
|
||||
nic[constants.NETWORK_ID])
|
||||
|
||||
port_id = nic[constants.PORT_ID]
|
||||
try:
|
||||
self.network_driver.delete_port(port_id)
|
||||
except Exception:
|
||||
LOG.exception("Unable to delete port %s", port_id)
|
||||
|
||||
|
||||
class PlugVIP(BaseNetworkTask):
|
||||
|
@ -169,31 +169,52 @@ class AbstractNetworkDriver(object, metaclass=abc.ABCMeta):
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def plug_network(self, compute_id, network_id, ip_address=None):
|
||||
def plug_network(self, compute_id, network_id):
|
||||
"""Connects an existing amphora to an existing network.
|
||||
|
||||
:param compute_id: id of an amphora in the compute service
|
||||
:param network_id: id of a network
|
||||
:param ip_address: ip address to attempt to be assigned to interface
|
||||
:return: octavia.network.data_models.Interface instance
|
||||
:raises: PlugNetworkException, AmphoraNotFound, NetworkNotFound
|
||||
"""
|
||||
|
||||
@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
|
||||
:param network_id: id of a network
|
||||
:param ip_address: specific ip_address to unplug
|
||||
:return: None
|
||||
:raises: UnplugNetworkException, AmphoraNotFound, NetworkNotFound,
|
||||
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.
|
||||
|
@ -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):
|
||||
|
@ -589,11 +589,10 @@ class AllowedAddressPairsDriver(neutron_base.BaseNeutronDriver):
|
||||
load_balancer.amphorae):
|
||||
self.unplug_aap_port(vip, amphora, subnet)
|
||||
|
||||
def plug_network(self, compute_id, network_id, ip_address=None):
|
||||
def plug_network(self, compute_id, network_id):
|
||||
try:
|
||||
interface = self.compute.attach_network_or_port(
|
||||
compute_id=compute_id, network_id=network_id,
|
||||
ip_address=ip_address)
|
||||
compute_id=compute_id, network_id=network_id)
|
||||
except exceptions.NotFound as e:
|
||||
if 'Instance' in str(e):
|
||||
raise base.AmphoraNotFound(str(e))
|
||||
@ -610,15 +609,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)
|
||||
removed_port_ids = set()
|
||||
for index, unplugger in enumerate(unpluggers):
|
||||
self.compute.detach_port(
|
||||
|
@ -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_dict = {'subnet_id': subnet_id}
|
||||
if ip_address:
|
||||
new_fixed_ip_dict['ip_address'] = ip_address
|
||||
|
||||
fixed_ips.append(new_fixed_ip_dict)
|
||||
|
||||
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))
|
||||
|
@ -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,40 @@ 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):
|
||||
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", self.__class__.__name__, compute_id,
|
||||
network_id)
|
||||
self.networkconfigconfig[(compute_id, network_id)] = (
|
||||
compute_id, network_id, 'plug_network')
|
||||
interface = network_models.Interface(
|
||||
id=uuidutils.generate_uuid(),
|
||||
compute_id=compute_id,
|
||||
network_id=network_id,
|
||||
fixed_ips=[],
|
||||
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 +186,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 +351,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.pop(port_id, None)
|
||||
|
||||
|
||||
class NoopNetworkDriver(driver_base.AbstractNetworkDriver):
|
||||
def __init__(self):
|
||||
@ -355,15 +390,14 @@ 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):
|
||||
return self.driver.plug_network(compute_id, network_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 +471,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)
|
||||
|
@ -969,10 +969,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 = [
|
||||
@ -991,24 +993,9 @@ class TestServerTestCase(base.TestCase):
|
||||
netns_handle.get_links.return_value = [0] * test_int_num
|
||||
mock_isfile.return_value = True
|
||||
|
||||
test_int_num = str(test_int_num)
|
||||
mock_check_output.return_value = b"1\n2\n3\n"
|
||||
|
||||
# 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
|
||||
test_int_num = str(test_int_num)
|
||||
|
||||
# No interface at all
|
||||
file_name = '/sys/bus/pci/rescan'
|
||||
@ -1674,6 +1661,9 @@ class TestServerTestCase(base.TestCase):
|
||||
consts.GATEWAY: '203.0.113.1',
|
||||
consts.TABLE: 1,
|
||||
consts.FLAGS: [consts.ONLINK]
|
||||
}, {
|
||||
consts.DST: '203.0.113.0/24',
|
||||
consts.SCOPE: 'link'
|
||||
}, {
|
||||
consts.DST: '203.0.113.0/24',
|
||||
consts.PREFSRC: '203.0.113.2',
|
||||
@ -1798,6 +1788,9 @@ class TestServerTestCase(base.TestCase):
|
||||
consts.GATEWAY: '203.0.113.1',
|
||||
consts.FLAGS: [consts.ONLINK],
|
||||
consts.TABLE: 1
|
||||
}, {
|
||||
consts.DST: '203.0.113.0/24',
|
||||
consts.SCOPE: 'link'
|
||||
}, {
|
||||
consts.DST: '203.0.113.0/24',
|
||||
consts.PREFSRC: '203.0.113.2',
|
||||
@ -2038,6 +2031,9 @@ class TestServerTestCase(base.TestCase):
|
||||
consts.GATEWAY: '2001:db8::1',
|
||||
consts.FLAGS: [consts.ONLINK],
|
||||
consts.TABLE: 1
|
||||
}, {
|
||||
consts.DST: '2001:0db8::/32',
|
||||
consts.SCOPE: 'link'
|
||||
}, {
|
||||
consts.DST: '2001:0db8::/32',
|
||||
consts.PREFSRC: '2001:0db8::2',
|
||||
@ -2159,6 +2155,9 @@ class TestServerTestCase(base.TestCase):
|
||||
consts.GATEWAY: '2001:db8::1',
|
||||
consts.FLAGS: [consts.ONLINK],
|
||||
consts.TABLE: 1
|
||||
}, {
|
||||
consts.DST: '2001:db8::/32',
|
||||
consts.SCOPE: 'link'
|
||||
}, {
|
||||
consts.DST: '2001:db8::/32',
|
||||
consts.PREFSRC: '2001:db8::2',
|
||||
|
@ -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',
|
||||
'192.0.2.2', 16)
|
||||
|
||||
mock_interface_file.assert_called_once_with(
|
||||
name='eth1',
|
||||
addresses=[{"address": "192.0.2.2", "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.'
|
||||
|
@ -193,16 +193,166 @@ 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=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_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': '192.0.2.0/24',
|
||||
'nexthop': '192.0.2.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': '192.0.2.0/24',
|
||||
'nexthop': '192.0.2.254'}]
|
||||
}, {'ip_address': FAKE_IP_IPV6,
|
||||
'subnet_cidr': FAKE_CIDR_IPV6,
|
||||
'gateway': FAKE_GATEWAY_IPV6,
|
||||
'host_routes': [
|
||||
{'destination': '2001:db8::/64',
|
||||
'nexthop': '2001:db8::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': '192.0.2.128/25',
|
||||
'nexthop': '192.0.2.100'}]
|
||||
}, {'ip_address': FAKE_IP_IPV6,
|
||||
'subnet_cidr': FAKE_CIDR_IPV6,
|
||||
'gateway': FAKE_GATEWAY_IPV6,
|
||||
'host_routes': [
|
||||
{'destination': '2001:db8::/64',
|
||||
'nexthop': '2001:db8::ffff'}]
|
||||
}]
|
||||
mtu = 1400
|
||||
vip_net_info = {
|
||||
'vip': '192.0.2.10',
|
||||
'subnet_cidr': '192.0.2.0/25',
|
||||
'vrrp_ip': '192.0.2.11',
|
||||
'gateway': '192.0.2.1',
|
||||
'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=25,
|
||||
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)
|
||||
|
@ -74,6 +74,10 @@ class TestInterfaceFile(base.TestCase):
|
||||
consts.FLAGS: [consts.ONLINK],
|
||||
consts.TABLE: 1
|
||||
},
|
||||
{
|
||||
consts.DST: cidr.exploded,
|
||||
consts.SCOPE: 'link'
|
||||
},
|
||||
{
|
||||
consts.DST: cidr.exploded,
|
||||
consts.PREFSRC: VIP_ADDRESS,
|
||||
@ -192,6 +196,10 @@ class TestInterfaceFile(base.TestCase):
|
||||
consts.FLAGS: [consts.ONLINK],
|
||||
consts.TABLE: 1
|
||||
},
|
||||
{
|
||||
consts.DST: cidr.exploded,
|
||||
consts.SCOPE: 'link'
|
||||
},
|
||||
{
|
||||
consts.DST: cidr.exploded,
|
||||
consts.PREFSRC: VIP_ADDRESS,
|
||||
@ -281,6 +289,9 @@ class TestInterfaceFile(base.TestCase):
|
||||
],
|
||||
consts.ROUTES: [
|
||||
{
|
||||
consts.DST: cidr.exploded,
|
||||
consts.SCOPE: 'link',
|
||||
}, {
|
||||
consts.DST: cidr.exploded,
|
||||
consts.PREFSRC: VIP_ADDRESS,
|
||||
consts.SCOPE: 'link',
|
||||
@ -360,13 +371,10 @@ class TestInterfaceFile(base.TestCase):
|
||||
{
|
||||
consts.DST: "0.0.0.0/0",
|
||||
consts.GATEWAY: GATEWAY,
|
||||
consts.FLAGS: [consts.ONLINK],
|
||||
},
|
||||
{
|
||||
consts.DST: "0.0.0.0/0",
|
||||
consts.GATEWAY: GATEWAY,
|
||||
consts.FLAGS: [consts.ONLINK],
|
||||
consts.TABLE: 1
|
||||
consts.FLAGS: [consts.ONLINK]
|
||||
}, {
|
||||
consts.DST: SUBNET_CIDR,
|
||||
consts.SCOPE: 'link'
|
||||
}
|
||||
],
|
||||
consts.RULES: [],
|
||||
@ -447,6 +455,10 @@ class TestInterfaceFile(base.TestCase):
|
||||
consts.TABLE: 1,
|
||||
consts.FLAGS: [consts.ONLINK]
|
||||
},
|
||||
{
|
||||
consts.DST: cidr.exploded,
|
||||
consts.SCOPE: 'link',
|
||||
},
|
||||
{
|
||||
consts.DST: cidr.exploded,
|
||||
consts.PREFSRC: VIP_ADDRESS,
|
||||
|
@ -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')
|
||||
@ -599,6 +603,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(
|
||||
@ -609,7 +620,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=[],
|
||||
@ -618,12 +629,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):
|
||||
@ -639,6 +651,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,
|
||||
@ -649,12 +662,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}]}
|
||||
]
|
||||
|
@ -42,7 +42,8 @@ FAKE_IPV6 = '2001:db8::cafe'
|
||||
FAKE_IPV6_LLA = 'fe80::00ff:fe00:cafe'
|
||||
FAKE_PEM_FILENAME = "file_name"
|
||||
FAKE_UUID_1 = uuidutils.generate_uuid()
|
||||
FAKE_VRRP_IP = '10.1.0.1'
|
||||
FAKE_VRRP_IP = '192.0.2.5'
|
||||
FAKE_VIP_SUBNET = '192.0.2.0/24'
|
||||
FAKE_MAC_ADDRESS = '123'
|
||||
FAKE_MTU = 1450
|
||||
FAKE_MEMBER_IP_PORT_NAME_1 = "10.0.0.10:1003"
|
||||
@ -94,6 +95,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 +118,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=FAKE_VIP_SUBNET,
|
||||
host_routes=[]))
|
||||
|
||||
@mock.patch('octavia.amphorae.drivers.haproxy.rest_api_driver.'
|
||||
'HaproxyAmphoraLoadBalancerDriver._process_secret')
|
||||
@ -694,6 +701,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(
|
||||
@ -704,7 +718,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=[],
|
||||
@ -713,14 +727,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=FAKE_VIP_SUBNET,
|
||||
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'
|
||||
@ -734,6 +774,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,
|
||||
@ -744,12 +785,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}]}
|
||||
]
|
||||
|
@ -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)])
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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: {}}))
|
||||
@ -849,6 +852,8 @@ class TestControllerWorker(base.TestCase):
|
||||
constants.LISTENERS: [_listener_mock],
|
||||
constants.LOADBALANCER:
|
||||
_load_balancer_mock,
|
||||
constants.LOADBALANCER_ID:
|
||||
_load_balancer_mock.id,
|
||||
constants.POOL: _pool_mock,
|
||||
constants.AVAILABILITY_ZONE: {}}))
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -871,7 +871,10 @@ class TestAllowedAddressPairsDriver(base.TestCase):
|
||||
|
||||
@mock.patch("time.time")
|
||||
@mock.patch("time.sleep")
|
||||
def test_unplug_aap_port(self, mock_time_sleep, mock_time_time):
|
||||
@mock.patch("octavia.network.drivers.neutron.allowed_address_pairs."
|
||||
"AllowedAddressPairsDriver.unplug_network")
|
||||
def test_unplug_aap_port(self, mock_unplug_network,
|
||||
mock_time_sleep, mock_time_time):
|
||||
lb = dmh.generate_load_balancer_tree()
|
||||
update_port = self.driver.neutron_client.update_port
|
||||
port1 = t_constants.MOCK_NEUTRON_PORT['port']
|
||||
@ -894,6 +897,8 @@ class TestAllowedAddressPairsDriver(base.TestCase):
|
||||
self.driver.unplug_aap_port(lb.vip, lb.amphorae[0], subnet)
|
||||
clear_aap = {'port': {'allowed_address_pairs': []}}
|
||||
update_port.assert_called_once_with(port2.get('id'), clear_aap)
|
||||
mock_unplug_network.assert_called_once_with(
|
||||
lb.amphorae[0].compute_id, subnet.network_id)
|
||||
|
||||
def test_plug_network_when_compute_instance_cant_be_found(self):
|
||||
net_id = t_constants.MOCK_NOVA_INTERFACE.net_id
|
||||
|
@ -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)
|
||||
|
@ -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,51 @@ 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,
|
||||
'plug_network'),
|
||||
self.driver.plug_network(self.compute_id, self.network_id)
|
||||
self.assertEqual((self.compute_id, self.network_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.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 +321,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]
|
||||
)
|
||||
|
@ -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.
|
Loading…
x
Reference in New Issue
Block a user