Added neutron allowed address pairs network driver
This driver will allocate a neutron port and fixed_ip on the VIP's network. Upon plugging the VIP, it will use neutron's allowed address pair to allow traffic destined for the ha_ip to pass to raised on amphora interfaces. Change-Id: I7bce4c2bbb9b35905c21caf79cb865e0ca146dac
This commit is contained in:
parent
7e11665bc5
commit
85838dbb4c
|
@ -32,8 +32,10 @@ class OctaviaException(Exception):
|
|||
"""
|
||||
message = _("An unknown exception occurred.")
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
def __init__(self, *args, **kwargs):
|
||||
try:
|
||||
if len(args) > 0:
|
||||
self.message = args[0]
|
||||
super(OctaviaException, self).__init__(self.message % kwargs)
|
||||
self.msg = self.message % kwargs
|
||||
except Exception:
|
||||
|
|
|
@ -18,8 +18,9 @@ from octavia.common import data_models
|
|||
class Interface(data_models.BaseDataModel):
|
||||
|
||||
def __init__(self, id=None, amphora_id=None, network_id=None,
|
||||
ip_address=None):
|
||||
ip_address=None, port_id=None):
|
||||
self.id = id
|
||||
self.amphora_id = amphora_id
|
||||
self.network_id = network_id
|
||||
self.port_id = port_id
|
||||
self.ip_address = ip_address
|
||||
|
|
|
@ -0,0 +1,331 @@
|
|||
# Copyright 2014 Rackspace
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutronclient.common import exceptions as neutron_client_exceptions
|
||||
from neutronclient.neutron import client as neutron_client
|
||||
from novaclient import client as nova_client
|
||||
from novaclient import exceptions as nova_client_exceptions
|
||||
from oslo_log import log as logging
|
||||
|
||||
from octavia.common import data_models
|
||||
from octavia.common import keystone
|
||||
from octavia.i18n import _LE, _LI
|
||||
from octavia.network import base
|
||||
from octavia.network import data_models as network_models
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
NEUTRON_VERSION = '2.0'
|
||||
NOVA_VERSION = '2'
|
||||
AAP_EXT_ALIAS = 'allowed-address-pairs'
|
||||
SEC_GRP_EXT_ALIAS = 'security-group'
|
||||
VIP_SECURITY_GRP_PREFIX = 'lb-'
|
||||
|
||||
|
||||
class AllowedAddressPairsDriver(base.AbstractNetworkDriver):
|
||||
|
||||
def __init__(self):
|
||||
self.sec_grp_enabled = True
|
||||
self.neutron_client = neutron_client.Client(
|
||||
NEUTRON_VERSION, session=keystone.get_session())
|
||||
self._check_extensions_loaded()
|
||||
self.nova_client = nova_client.Client(
|
||||
NOVA_VERSION, session=keystone.get_session())
|
||||
|
||||
def _check_extensions_loaded(self):
|
||||
extensions = self.neutron_client.list_extensions()
|
||||
extensions = extensions.get('extensions')
|
||||
aliases = [ext.get('alias') for ext in extensions]
|
||||
if AAP_EXT_ALIAS not in aliases:
|
||||
raise base.NetworkException(
|
||||
'The {alias} extension is not enabled in neutron. This '
|
||||
'driver cannot be used with the {alias} extension '
|
||||
'disabled.'.format(alias=AAP_EXT_ALIAS))
|
||||
if SEC_GRP_EXT_ALIAS not in aliases:
|
||||
LOG.info(_LI('Neutron security groups are disabled. This driver'
|
||||
'will not manage any security groups.'))
|
||||
self.sec_grp_enabled = False
|
||||
|
||||
def _port_to_vip(self, port, load_balancer_id=None):
|
||||
port = port['port']
|
||||
ip_address = port['fixed_ips'][0]['ip_address']
|
||||
network_id = port['network_id']
|
||||
port_id = port['id']
|
||||
return data_models.Vip(ip_address=ip_address,
|
||||
network_id=network_id,
|
||||
port_id=port_id,
|
||||
load_balancer_id=load_balancer_id)
|
||||
|
||||
def _nova_interface_to_octavia_interface(self, amphora_id, nova_interface):
|
||||
ip_address = nova_interface.fixed_ips[0]['ip_address']
|
||||
return network_models.Interface(amphora_id=amphora_id,
|
||||
network_id=nova_interface.net_id,
|
||||
port_id=nova_interface.port_id,
|
||||
ip_address=ip_address)
|
||||
|
||||
def _get_interfaces_to_unplug(self, interfaces, network_id,
|
||||
ip_address=None):
|
||||
ret = []
|
||||
for interface_ in interfaces:
|
||||
if interface_.net_id == network_id:
|
||||
if ip_address:
|
||||
for fixed_ip in interface_.fixed_ips:
|
||||
if ip_address == fixed_ip.get('ip_address'):
|
||||
ret.append(interface_)
|
||||
else:
|
||||
ret.append(interface_)
|
||||
return ret
|
||||
|
||||
def _get_plugged_interface(self, compute_id, network_id):
|
||||
interfaces = self.get_plugged_networks(compute_id)
|
||||
for interface in interfaces:
|
||||
if interface.network_id == network_id:
|
||||
return interface
|
||||
|
||||
def _plug_amphora_vip(self, compute_id, network_id):
|
||||
try:
|
||||
interface = self.plug_network(compute_id, network_id)
|
||||
except Exception:
|
||||
message = _LE('Error plugging amphora (compute_id: {compute_id}) '
|
||||
'into vip network {network_id}.').format(
|
||||
compute_id=compute_id,
|
||||
network_id=network_id)
|
||||
LOG.exception(message)
|
||||
raise base.PlugVIPException(message)
|
||||
return interface
|
||||
|
||||
def _add_vip_address_pair(self, port_id, vip_address):
|
||||
try:
|
||||
aap = {
|
||||
'port': {
|
||||
'allowed_address_pairs': [
|
||||
{'ip_address': vip_address}
|
||||
]
|
||||
}
|
||||
}
|
||||
self.neutron_client.update_port(port_id, aap)
|
||||
except neutron_client_exceptions.PortNotFoundClient as e:
|
||||
raise base.PortNotFound(e.message)
|
||||
except Exception:
|
||||
message = _LE('Error adding allowed address pair {ip} '
|
||||
'to port {port_id}.').format(ip=vip_address,
|
||||
port_id=port_id)
|
||||
LOG.exception(message)
|
||||
raise base.PlugVIPException(message)
|
||||
|
||||
def _get_lb_security_group(self, load_balancer_id):
|
||||
sec_grp_name = VIP_SECURITY_GRP_PREFIX + load_balancer_id
|
||||
sec_grps = self.neutron_client.list_security_groups(name=sec_grp_name)
|
||||
if len(sec_grps.get('security_groups')):
|
||||
return sec_grps.get('security_groups')[0]
|
||||
|
||||
def _update_vip_security_group(self, load_balancer, vip):
|
||||
sec_grp = self._get_lb_security_group(load_balancer.id)
|
||||
if not sec_grp:
|
||||
sec_grp_name = VIP_SECURITY_GRP_PREFIX + load_balancer.id
|
||||
new_sec_grp = {'security_group': {'name': sec_grp_name}}
|
||||
sec_grp = self.neutron_client.create_security_group(new_sec_grp)
|
||||
sec_grp = sec_grp['security_group']
|
||||
for listener in load_balancer.listeners:
|
||||
rule = {
|
||||
'security_group_rule': {
|
||||
'security_group_id': sec_grp.get('id'),
|
||||
'direction': 'ingress',
|
||||
'protocol': 'TCP',
|
||||
'port_range_min': listener.protocol_port,
|
||||
'port_range_max': listener.protocol_port
|
||||
}
|
||||
}
|
||||
try:
|
||||
self.neutron_client.create_security_group_rule(rule)
|
||||
except neutron_client_exceptions.Conflict as conflict_e:
|
||||
if 'already exists' not in conflict_e.message.lower():
|
||||
raise conflict_e
|
||||
port_update = {'port': {'security_groups': [sec_grp.get('id')]}}
|
||||
try:
|
||||
self.neutron_client.update_port(vip.port_id, port_update)
|
||||
except neutron_client_exceptions.PortNotFoundClient as e:
|
||||
raise base.PortNotFound(e.message)
|
||||
except Exception as e:
|
||||
raise base.PlugVIPException(e.message)
|
||||
|
||||
def _add_vip_security_group_to_amphorae(self, load_balancer_id, amphora):
|
||||
sec_grp = self._get_lb_security_group(load_balancer_id)
|
||||
self.nova_client.servers.add_security_group(
|
||||
amphora.compute_id, sec_grp.get('id'))
|
||||
|
||||
def deallocate_vip(self, vip):
|
||||
try:
|
||||
self.neutron_client.delete_port(vip.port_id)
|
||||
except neutron_client_exceptions.PortNotFoundClient as e:
|
||||
raise base.VIPConfigurationNotFound(e.message)
|
||||
except Exception:
|
||||
message = _LE('Error deleting VIP port_id {port_id} from '
|
||||
'neutron').format(port_id=vip.port_id)
|
||||
LOG.exception(message)
|
||||
raise base.DeallocateVIPException(message)
|
||||
|
||||
def plug_vip(self, load_balancer, vip):
|
||||
if self.sec_grp_enabled:
|
||||
self._update_vip_security_group(load_balancer, vip)
|
||||
plugged_amphorae = []
|
||||
for amphora in load_balancer.amphorae:
|
||||
interface = self._get_plugged_interface(amphora.compute_id,
|
||||
vip.network_id)
|
||||
if not interface:
|
||||
interface = self._plug_amphora_vip(amphora.compute_id,
|
||||
vip.network_id)
|
||||
self._add_vip_address_pair(interface.port_id, vip.ip_address)
|
||||
if self.sec_grp_enabled:
|
||||
self._add_vip_security_group_to_amphorae(
|
||||
load_balancer.id, amphora)
|
||||
plugged_amphorae.append(data_models.Amphora(
|
||||
id=amphora.id,
|
||||
compute_id=amphora.compute_id,
|
||||
vrrp_ip=interface.ip_address,
|
||||
ha_ip=vip.ip_address))
|
||||
return plugged_amphorae
|
||||
|
||||
def allocate_vip(self, port_id=None, network_id=None, ip_address=None):
|
||||
if not port_id and not network_id:
|
||||
raise base.AllocateVIPException('Cannot allocate a vip '
|
||||
'without a port_id or '
|
||||
'a network_id.')
|
||||
if port_id:
|
||||
LOG.info(_LI('Port {port_id} already exists. Nothing to be '
|
||||
'done.').format(port_id=port_id))
|
||||
try:
|
||||
port = self.neutron_client.show_port(port_id)
|
||||
except neutron_client_exceptions.PortNotFoundClient as e:
|
||||
raise base.PortNotFound(e.message)
|
||||
except Exception:
|
||||
message = _LE('Error retrieving info about port '
|
||||
'{port_id}.').format(port_id=port_id)
|
||||
LOG.exception(message)
|
||||
raise base.AllocateVIPException(message)
|
||||
return self._port_to_vip(port)
|
||||
|
||||
# It can be assumed that network_id exists
|
||||
port = {'port': {'name': 'octavia-port',
|
||||
'network_id': network_id,
|
||||
'admin_state_up': False,
|
||||
'device_id': '',
|
||||
'device_owner': ''}}
|
||||
try:
|
||||
new_port = self.neutron_client.create_port(port)
|
||||
except neutron_client_exceptions.NetworkNotFoundClient as e:
|
||||
raise base.NetworkNotFound(e.message)
|
||||
except Exception:
|
||||
message = _LE('Error creating neutron port on network '
|
||||
'{network_id}.').format(network_id=network_id)
|
||||
LOG.exception(message)
|
||||
raise base.AllocateVIPException(message)
|
||||
return self._port_to_vip(new_port)
|
||||
|
||||
def unplug_vip(self, load_balancer, vip):
|
||||
for amphora in load_balancer.amphorae:
|
||||
interface = self._get_plugged_interface(amphora.compute_id,
|
||||
vip.network_id)
|
||||
if not interface:
|
||||
# Thought about raising PluggedVIPNotFound exception but
|
||||
# then that wouldn't evaluate all amphorae, so just continue
|
||||
continue
|
||||
try:
|
||||
self.unplug_network(amphora.compute_id, vip.network_id)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
aap_update = {'port': {
|
||||
'allowed_address_pairs': []
|
||||
}}
|
||||
self.neutron_client.update_port(interface.port_id,
|
||||
aap_update)
|
||||
except Exception:
|
||||
message = _LE('Error unplugging VIP. Could not clear '
|
||||
'allowed address pairs from port '
|
||||
'{port_id}.').format(port_id=vip.port_id)
|
||||
LOG.exception(message)
|
||||
raise base.UnplugVIPException(message)
|
||||
|
||||
def plug_network(self, compute_id, network_id, ip_address=None):
|
||||
try:
|
||||
interface_ = self.nova_client.servers.interface_attach(
|
||||
server=compute_id, net_id=network_id, fixed_ip=ip_address,
|
||||
port_id=None)
|
||||
except nova_client_exceptions.NotFound as e:
|
||||
if 'Instance' in e.message:
|
||||
raise base.AmphoraNotFound(e.message)
|
||||
if 'Network' in e.message:
|
||||
raise base.NetworkNotFound(e.message)
|
||||
except Exception:
|
||||
message = _LE('Error plugging amphora (compute_id: {compute_id}) '
|
||||
'into network {network_id}.').format(
|
||||
compute_id=compute_id,
|
||||
network_id=network_id)
|
||||
LOG.exception(message)
|
||||
raise base.PlugNetworkException(message)
|
||||
|
||||
return self._nova_interface_to_octavia_interface(compute_id,
|
||||
interface_)
|
||||
|
||||
def get_plugged_networks(self, amphora_id):
|
||||
try:
|
||||
interfaces = self.nova_client.servers.interface_list(
|
||||
server=amphora_id)
|
||||
except Exception:
|
||||
message = _LE('Error retrieving plugged networks for amphora '
|
||||
'{amphora_id}.').format(amphora_id=amphora_id)
|
||||
LOG.exception(message)
|
||||
raise base.NetworkException(message)
|
||||
return [self._nova_interface_to_octavia_interface(
|
||||
amphora_id, interface_) for interface_ in interfaces]
|
||||
|
||||
def unplug_network(self, compute_id, network_id, ip_address=None):
|
||||
try:
|
||||
interfaces = self.nova_client.servers.interface_list(
|
||||
server=compute_id)
|
||||
except nova_client_exceptions.NotFound as e:
|
||||
raise base.AmphoraNotFound(e.message)
|
||||
except Exception:
|
||||
message = _LE('Error retrieving nova interfaces for amphora '
|
||||
'(compute_id: {compute_id}) on network {network_id} '
|
||||
'with ip {ip_address}.').format(
|
||||
compute_id=compute_id,
|
||||
network_id=network_id,
|
||||
ip_address=ip_address)
|
||||
LOG.exception(message)
|
||||
raise base.NetworkException(message)
|
||||
unpluggers = self._get_interfaces_to_unplug(interfaces, network_id,
|
||||
ip_address=ip_address)
|
||||
try:
|
||||
for index, unplugger in enumerate(unpluggers):
|
||||
self.nova_client.servers.interface_detach(
|
||||
server=compute_id, port_id=unplugger.port_id)
|
||||
except Exception:
|
||||
message = _LE('Error unplugging amphora {amphora_id} from network '
|
||||
'{network_id}.').format(amphora_id=compute_id,
|
||||
network_id=network_id)
|
||||
if len(unpluggers) > 1:
|
||||
message = _LE('{base} Other interfaces have been successfully '
|
||||
'unplugged: ').format(base=message)
|
||||
unpluggeds = unpluggers[:index]
|
||||
for unplugged in unpluggeds:
|
||||
message = _LE('{base} neutron port '
|
||||
'{port_id} ').format(
|
||||
base=message, port_id=unplugged.port_id)
|
||||
else:
|
||||
message = _LE('{base} No other networks were '
|
||||
'unplugged.').format(base=message)
|
||||
LOG.exception(message)
|
||||
raise base.UnplugNetworkException(message)
|
|
@ -0,0 +1,76 @@
|
|||
# Copyright 2014 Rackspace
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from octavia.common import data_models
|
||||
|
||||
|
||||
def generate_load_balancer_tree():
|
||||
vip = generate_vip()
|
||||
amps = [generate_amphora(), generate_amphora()]
|
||||
lb = generate_load_balancer(vip=vip, amphorae=amps)
|
||||
return lb
|
||||
|
||||
|
||||
LB_SEED = 0
|
||||
|
||||
|
||||
def generate_load_balancer(vip=None, amphorae=None):
|
||||
amphorae = amphorae or []
|
||||
global LB_SEED
|
||||
LB_SEED += 1
|
||||
lb = data_models.LoadBalancer(id='lb{0}-id'.format(LB_SEED),
|
||||
tenant_id='2',
|
||||
name='lb{0}'.format(LB_SEED),
|
||||
description='lb{0}'.format(LB_SEED),
|
||||
vip=vip,
|
||||
amphorae=amphorae)
|
||||
for amp in lb.amphorae:
|
||||
amp.load_balancer = lb
|
||||
amp.load_balancer_id = lb.id
|
||||
if vip:
|
||||
vip.load_balancer = lb
|
||||
vip.load_balancer_id = lb.id
|
||||
return lb
|
||||
|
||||
|
||||
VIP_SEED = 0
|
||||
|
||||
|
||||
def generate_vip(load_balancer=None):
|
||||
global VIP_SEED
|
||||
VIP_SEED += 1
|
||||
vip = data_models.Vip(ip_address='10.0.0.{0}'.format(VIP_SEED),
|
||||
network_id='net{0}-id'.format(VIP_SEED),
|
||||
port_id='port{0}-id'.format(VIP_SEED),
|
||||
load_balancer=load_balancer)
|
||||
if load_balancer:
|
||||
vip.load_balancer_id = load_balancer.id
|
||||
return vip
|
||||
|
||||
|
||||
AMP_SEED = 0
|
||||
|
||||
|
||||
def generate_amphora(load_balancer=None):
|
||||
global AMP_SEED
|
||||
AMP_SEED += 1
|
||||
amp = data_models.Amphora(id='amp{0}-id'.format(AMP_SEED),
|
||||
compute_id='compute{0}-id'.format(AMP_SEED),
|
||||
status='ACTIVE',
|
||||
lb_network_ip='11.0.0.{0}'.format(AMP_SEED),
|
||||
vrrp_ip='12.0.0.{0}'.format(AMP_SEED),
|
||||
load_balancer=load_balancer)
|
||||
if load_balancer:
|
||||
amp.load_balancer_id = load_balancer.id
|
||||
return amp
|
|
@ -0,0 +1,291 @@
|
|||
# Copyright 2015 Rackspace
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
from neutronclient.common import exceptions as neutron_exceptions
|
||||
from novaclient.client import exceptions as nova_exceptions
|
||||
|
||||
from octavia.common import data_models
|
||||
from octavia.network import base as network_base
|
||||
from octavia.network.drivers.neutron import allowed_address_pairs
|
||||
from octavia.tests.common import data_model_helpers as dmh
|
||||
from octavia.tests.unit import base
|
||||
|
||||
|
||||
class MockNovaInterface(object):
|
||||
net_id = None
|
||||
port_id = None
|
||||
fixed_ips = []
|
||||
|
||||
|
||||
MOCK_NOVA_INTERFACE = MockNovaInterface()
|
||||
MOCK_NOVA_INTERFACE.net_id = '1'
|
||||
MOCK_NOVA_INTERFACE.port_id = '2'
|
||||
MOCK_NOVA_INTERFACE.fixed_ips = [{'ip_address': '10.0.0.1'}]
|
||||
|
||||
MOCK_NEUTRON_PORT = {'port': {'network_id': '1',
|
||||
'id': '2',
|
||||
'fixed_ips': [{'ip_address': '10.0.0.1'}]}}
|
||||
|
||||
|
||||
class TestAllowedAddressPairsDriver(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestAllowedAddressPairsDriver, self).setUp()
|
||||
neutron_patcher = mock.patch('neutronclient.neutron.client.Client',
|
||||
autospec=True)
|
||||
mock.patch('novaclient.client.Client', autospec=True).start()
|
||||
neutron_client = neutron_patcher.start()
|
||||
client = neutron_client(allowed_address_pairs.NEUTRON_VERSION)
|
||||
client.list_extensions.return_value = {
|
||||
'extensions': [{'alias': allowed_address_pairs.AAP_EXT_ALIAS},
|
||||
{'alias': allowed_address_pairs.SEC_GRP_EXT_ALIAS}]}
|
||||
mock.patch('octavia.common.keystone.get_session').start()
|
||||
self.driver = allowed_address_pairs.AllowedAddressPairsDriver()
|
||||
|
||||
def test_check_extensions_loaded(self):
|
||||
list_extensions = self.driver.neutron_client.list_extensions
|
||||
list_extensions.return_value = {
|
||||
'extensions': [{'alias': 'blah'}]}
|
||||
self.assertRaises(network_base.NetworkException,
|
||||
self.driver._check_extensions_loaded)
|
||||
|
||||
def test_port_to_vip(self):
|
||||
fake_lb_id = '4'
|
||||
vip = self.driver._port_to_vip(MOCK_NEUTRON_PORT, fake_lb_id)
|
||||
self.assertIsInstance(vip, data_models.Vip)
|
||||
self.assertEqual(
|
||||
MOCK_NEUTRON_PORT['port']['fixed_ips'][0]['ip_address'],
|
||||
vip.ip_address)
|
||||
self.assertEqual(MOCK_NEUTRON_PORT['port']['network_id'],
|
||||
vip.network_id)
|
||||
self.assertEqual(MOCK_NEUTRON_PORT['port']['id'], vip.port_id)
|
||||
self.assertEqual(fake_lb_id, vip.load_balancer_id)
|
||||
|
||||
def test_nova_interface_to_octavia_interface(self):
|
||||
nova_interface = MockNovaInterface()
|
||||
nova_interface.net_id = '1'
|
||||
nova_interface.port_id = '2'
|
||||
nova_interface.fixed_ips = [{'ip_address': '10.0.0.1'}]
|
||||
interface = self.driver._nova_interface_to_octavia_interface(
|
||||
'3', nova_interface)
|
||||
self.assertEqual('1', interface.network_id)
|
||||
self.assertEqual('2', interface.port_id)
|
||||
self.assertEqual('10.0.0.1', interface.ip_address)
|
||||
|
||||
def test_get_interfaces_to_unplug(self):
|
||||
if1 = MockNovaInterface()
|
||||
if1.net_id = 'if1-net'
|
||||
if1.port_id = 'if1-port'
|
||||
if1.fixed_ips = [{'ip_address': '10.0.0.1'}]
|
||||
if2 = MockNovaInterface()
|
||||
if2.net_id = 'if2-net'
|
||||
if2.port_id = 'if2-port'
|
||||
if2.fixed_ips = [{'ip_address': '11.0.0.1'}]
|
||||
interfaces = [if1, if2]
|
||||
unpluggers = self.driver._get_interfaces_to_unplug(
|
||||
interfaces, 'if1-net')
|
||||
self.assertEqual([if1], unpluggers)
|
||||
unpluggers = self.driver._get_interfaces_to_unplug(
|
||||
interfaces, 'if1-net', ip_address='10.0.0.1')
|
||||
self.assertEqual([if1], unpluggers)
|
||||
unpluggers = self.driver._get_interfaces_to_unplug(
|
||||
interfaces, 'if1-net', ip_address='11.0.0.1')
|
||||
self.assertEqual([], unpluggers)
|
||||
unpluggers = self.driver._get_interfaces_to_unplug(
|
||||
interfaces, 'if3-net')
|
||||
self.assertEqual([], unpluggers)
|
||||
|
||||
def test_deallocate_vip(self):
|
||||
vip = data_models.Vip(port_id='1')
|
||||
self.driver.deallocate_vip(vip)
|
||||
delete_port = self.driver.neutron_client.delete_port
|
||||
delete_port.side_effect = neutron_exceptions.PortNotFoundClient
|
||||
self.assertRaises(network_base.VIPConfigurationNotFound,
|
||||
self.driver.deallocate_vip, vip)
|
||||
delete_port.side_effect = TypeError
|
||||
self.assertRaises(network_base.DeallocateVIPException,
|
||||
self.driver.deallocate_vip, vip)
|
||||
|
||||
def test_plug_vip(self):
|
||||
interface_attach = self.driver.nova_client.servers.interface_attach
|
||||
interface_attach.side_effect = nova_exceptions.NotFound
|
||||
lb = dmh.generate_load_balancer_tree()
|
||||
self.assertRaises(network_base.PlugVIPException,
|
||||
self.driver.plug_vip, lb, lb.vip)
|
||||
interface_attach.side_effect = None
|
||||
interface_attach.return_value = MOCK_NOVA_INTERFACE
|
||||
update_port = self.driver.neutron_client.update_port
|
||||
update_port.side_effect = neutron_exceptions.PortNotFoundClient
|
||||
self.assertRaises(network_base.PortNotFound,
|
||||
self.driver.plug_vip, lb, lb.vip)
|
||||
update_port.side_effect = TypeError
|
||||
self.assertRaises(network_base.PlugVIPException,
|
||||
self.driver.plug_vip, lb, lb.vip)
|
||||
update_port.side_effect = None
|
||||
update_port.reset_mock()
|
||||
list_security_groups = self.driver.neutron_client.list_security_groups
|
||||
list_security_groups.return_value = {
|
||||
'security_groups': [
|
||||
{'id': 'lb-sec-grp1'}
|
||||
]
|
||||
}
|
||||
mock_ip = MOCK_NOVA_INTERFACE.fixed_ips[0].get('ip_address')
|
||||
expected_aap = {'port': {'allowed_address_pairs':
|
||||
[{'ip_address': lb.vip.ip_address}]}}
|
||||
interface_list = self.driver.nova_client.servers.interface_list
|
||||
if1 = MOCK_NOVA_INTERFACE
|
||||
if1.net_id = lb.vip.network_id
|
||||
if2 = MockNovaInterface()
|
||||
if2.net_id = '3'
|
||||
if2.port_id = '4'
|
||||
if2.fixed_ips = [{'ip_address': '10.0.0.2'}]
|
||||
interface_list.return_value = [if1, if2]
|
||||
amps = self.driver.plug_vip(lb, lb.vip)
|
||||
self.assertEqual(3, update_port.call_count)
|
||||
update_port.assert_any_call(if1.port_id, expected_aap)
|
||||
for amp in amps:
|
||||
self.assertEqual(mock_ip, amp.vrrp_ip)
|
||||
self.assertEqual(lb.vip.ip_address, amp.ha_ip)
|
||||
self.assertIn(amp.id, [lb.amphorae[0].id, lb.amphorae[1].id])
|
||||
|
||||
def test_allocate_vip(self):
|
||||
self.assertRaises(network_base.AllocateVIPException,
|
||||
self.driver.allocate_vip, port_id=None,
|
||||
network_id=None)
|
||||
show_port = self.driver.neutron_client.show_port
|
||||
show_port.return_value = MOCK_NEUTRON_PORT
|
||||
vip = self.driver.allocate_vip(port_id=MOCK_NEUTRON_PORT['port']['id'])
|
||||
self.assertIsInstance(vip, data_models.Vip)
|
||||
self.assertEqual(
|
||||
MOCK_NEUTRON_PORT['port']['fixed_ips'][0]['ip_address'],
|
||||
vip.ip_address)
|
||||
self.assertEqual(MOCK_NEUTRON_PORT['port']['network_id'],
|
||||
vip.network_id)
|
||||
self.assertEqual(MOCK_NEUTRON_PORT['port']['id'], vip.port_id)
|
||||
self.assertIsNone(vip.load_balancer_id)
|
||||
|
||||
create_port = self.driver.neutron_client.create_port
|
||||
create_port.return_value = MOCK_NEUTRON_PORT
|
||||
vip = self.driver.allocate_vip(
|
||||
network_id=MOCK_NEUTRON_PORT['port']['network_id'])
|
||||
self.assertIsInstance(vip, data_models.Vip)
|
||||
self.assertEqual(
|
||||
MOCK_NEUTRON_PORT['port']['fixed_ips'][0]['ip_address'],
|
||||
vip.ip_address)
|
||||
self.assertEqual(MOCK_NEUTRON_PORT['port']['network_id'],
|
||||
vip.network_id)
|
||||
self.assertEqual(MOCK_NEUTRON_PORT['port']['id'], vip.port_id)
|
||||
self.assertIsNone(vip.load_balancer_id)
|
||||
|
||||
def test_unplug_vip(self):
|
||||
lb = dmh.generate_load_balancer_tree()
|
||||
interface_list = self.driver.nova_client.servers.interface_list
|
||||
interface_list.reset_mock()
|
||||
if1 = MOCK_NOVA_INTERFACE
|
||||
if1.net_id = lb.vip.network_id
|
||||
if2 = MockNovaInterface()
|
||||
if2.net_id = '3'
|
||||
if2.port_id = '4'
|
||||
if2.fixed_ips = [{'ip_address': '10.0.0.2'}]
|
||||
interface_list.return_value = [if1, if2]
|
||||
update_port = self.driver.neutron_client.update_port
|
||||
update_port.side_effect = neutron_exceptions.PortNotFoundClient
|
||||
self.assertRaises(network_base.UnplugVIPException,
|
||||
self.driver.unplug_vip, lb, lb.vip)
|
||||
update_port.side_effect = TypeError
|
||||
self.assertRaises(network_base.UnplugVIPException,
|
||||
self.driver.unplug_vip, lb, lb.vip)
|
||||
update_port.side_effect = None
|
||||
update_port.reset_mock()
|
||||
self.driver.unplug_vip(lb, lb.vip)
|
||||
self.assertEqual(len(lb.amphorae), update_port.call_count)
|
||||
clear_aap = {'port': {'allowed_address_pairs': []}}
|
||||
update_port.assert_has_calls([mock.call(if1.port_id, clear_aap),
|
||||
mock.call(if1.port_id, clear_aap)])
|
||||
|
||||
def test_plug_network(self):
|
||||
amp_id = '1'
|
||||
net_id = MOCK_NOVA_INTERFACE.net_id
|
||||
interface_attach = self.driver.nova_client.servers.interface_attach
|
||||
interface_attach.side_effect = nova_exceptions.NotFound(
|
||||
404, message='Instance not found')
|
||||
self.assertRaises(network_base.AmphoraNotFound,
|
||||
self.driver.plug_network, amp_id, net_id)
|
||||
interface_attach.side_effect = nova_exceptions.NotFound(
|
||||
404, message='Network not found')
|
||||
self.assertRaises(network_base.NetworkException,
|
||||
self.driver.plug_network, amp_id, net_id)
|
||||
interface_attach.side_effect = TypeError
|
||||
self.assertRaises(network_base.PlugNetworkException,
|
||||
self.driver.plug_network, amp_id, net_id)
|
||||
interface_attach.side_effect = None
|
||||
interface_attach.return_value = MOCK_NOVA_INTERFACE
|
||||
oct_interface = self.driver.plug_network(amp_id, net_id)
|
||||
self.assertEqual(MOCK_NOVA_INTERFACE.fixed_ips[0].get('ip_address'),
|
||||
oct_interface.ip_address)
|
||||
self.assertEqual(amp_id, oct_interface.amphora_id)
|
||||
self.assertEqual(net_id, oct_interface.network_id)
|
||||
|
||||
def test_get_plugged_networks(self):
|
||||
amp_id = '1'
|
||||
interface_list = self.driver.nova_client.servers.interface_list
|
||||
interface_list.side_effect = TypeError
|
||||
self.assertRaises(network_base.NetworkException,
|
||||
self.driver.get_plugged_networks, amp_id)
|
||||
interface_list.side_effect = None
|
||||
interface_list.reset_mock()
|
||||
if1 = MOCK_NOVA_INTERFACE
|
||||
if2 = MockNovaInterface()
|
||||
if2.net_id = '3'
|
||||
if2.port_id = '4'
|
||||
if2.fixed_ips = [{'ip_address': '10.0.0.2'}]
|
||||
interface_list.return_value = [if1, if2]
|
||||
plugged_networks = self.driver.get_plugged_networks(amp_id)
|
||||
for pn in plugged_networks:
|
||||
self.assertIn(pn.port_id, [if1.port_id, if2.port_id])
|
||||
self.assertIn(pn.network_id, [if1.net_id, if2.net_id])
|
||||
self.assertIn(pn.ip_address, [if1.fixed_ips[0]['ip_address'],
|
||||
if2.fixed_ips[0]['ip_address']])
|
||||
|
||||
def test_unplug_network(self):
|
||||
amp_id = '1'
|
||||
net_id = MOCK_NOVA_INTERFACE.net_id
|
||||
interface_list = self.driver.nova_client.servers.interface_list
|
||||
interface_list.side_effect = nova_exceptions.NotFound(404)
|
||||
self.assertRaises(network_base.AmphoraNotFound,
|
||||
self.driver.unplug_network, amp_id, net_id)
|
||||
interface_list.side_effect = Exception
|
||||
self.assertRaises(network_base.NetworkException,
|
||||
self.driver.unplug_network, amp_id, net_id)
|
||||
interface_list.side_effect = None
|
||||
interface_list.reset_mock()
|
||||
if1 = MockNovaInterface()
|
||||
if1.net_id = 'if1-net'
|
||||
if1.port_id = 'if1-port'
|
||||
if1.fixed_ips = [{'ip_address': '10.0.0.1'}]
|
||||
if2 = MockNovaInterface()
|
||||
if2.net_id = 'if2-net'
|
||||
if2.port_id = 'if2-port'
|
||||
if2.fixed_ips = [{'ip_address': '11.0.0.1'}]
|
||||
interface_list.return_value = [if1, if2]
|
||||
interface_detach = self.driver.nova_client.servers.interface_detach
|
||||
interface_detach.side_effect = Exception
|
||||
self.assertRaises(network_base.UnplugNetworkException,
|
||||
self.driver.unplug_network, amp_id, if2.net_id)
|
||||
interface_detach.side_effect = None
|
||||
interface_detach.reset_mock()
|
||||
self.driver.unplug_network(amp_id, if2.net_id)
|
||||
interface_detach.assert_called_once_with(server=amp_id,
|
||||
port_id=if2.port_id)
|
Loading…
Reference in New Issue