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:
Brandon Logan 2015-03-24 18:21:41 -05:00
parent 7e11665bc5
commit 85838dbb4c
7 changed files with 703 additions and 2 deletions

View File

@ -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:

View File

@ -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

View File

@ -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)

View File

View File

@ -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

View File

@ -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)