Merge "Fix plugging member subnets on existing networks" into stable/yoga
This commit is contained in:
commit
8fa8d8f071
@ -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):
|
||||
|
@ -301,7 +301,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'
|
||||
@ -333,9 +333,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'
|
||||
@ -345,6 +347,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'
|
||||
@ -352,6 +355,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'
|
||||
@ -370,6 +374,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'
|
||||
@ -379,6 +384,7 @@ MESSAGE = 'message'
|
||||
NAME = 'name'
|
||||
NETWORK = 'network'
|
||||
NETWORK_ID = 'network_id'
|
||||
NEXTHOP = 'nexthop'
|
||||
NICS = 'nics'
|
||||
OBJECT = 'object'
|
||||
ORIGINAL_HEALTH_MONITOR = 'original_health_monitor'
|
||||
@ -426,10 +432,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'
|
||||
@ -930,5 +938,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 = {
|
||||
@ -650,9 +655,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…
Reference in New Issue
Block a user