Plug vip and networks by port mac address
Co-Authored-By: German Eichberger <german.eichberger@hp.com> Change-Id: Ic6206a97ad67f6364d850e033760ee60d7161f6f
This commit is contained in:
parent
0bc2612952
commit
3b23de32b8
@ -1088,6 +1088,7 @@ Plug VIP
|
||||
|
||||
* *subnet_cidr*: The vip subnet in cidr notation
|
||||
* *gateway*: The vip subnet gateway address
|
||||
* *mac_address*: The mac address of the interface to plug
|
||||
|
||||
* **Success Response:**
|
||||
|
||||
@ -1137,7 +1138,8 @@ Plug VIP
|
||||
JSON POST parameters:
|
||||
{
|
||||
'subnet_cidr': '203.0.113.0/24',
|
||||
'gateway': '203.0.113.1'
|
||||
'gateway': '203.0.113.1',
|
||||
'mac_address': '78:31:c1:ce:0b:3c'
|
||||
}
|
||||
|
||||
JSON Response:
|
||||
@ -1176,7 +1178,10 @@ Plug Network
|
||||
* **Method:** POST
|
||||
* **URL params:** none
|
||||
|
||||
* **Data params:** none
|
||||
* **Data params:**
|
||||
|
||||
* *mac_address*: The mac address of the interface to plug
|
||||
|
||||
* **Success Response:**
|
||||
|
||||
* Code: 202
|
||||
@ -1212,6 +1217,11 @@ Plug Network
|
||||
POST URL:
|
||||
https://octavia-haproxy-img-00328.local/v0.1/plug/network/
|
||||
|
||||
JSON POST parameters:
|
||||
{
|
||||
'mac_address': '78:31:c1:ce:0b:3c'
|
||||
}
|
||||
|
||||
JSON Response:
|
||||
{
|
||||
'message': 'OK',
|
||||
|
@ -38,7 +38,7 @@ template_port = j2_env.get_template(ETH_X_VIP_CONF)
|
||||
template_vip = j2_env.get_template(ETH_PORT_CONF)
|
||||
|
||||
|
||||
def plug_vip(vip, subnet_cidr, gateway):
|
||||
def plug_vip(vip, subnet_cidr, gateway, mac_address):
|
||||
# validate vip
|
||||
try:
|
||||
socket.inet_aton(vip)
|
||||
@ -46,7 +46,7 @@ def plug_vip(vip, subnet_cidr, gateway):
|
||||
return flask.make_response(flask.jsonify(dict(
|
||||
message="Invalid VIP")), 400)
|
||||
|
||||
interface = _interface_down()
|
||||
interface = _interface_by_mac(mac_address)
|
||||
|
||||
# assume for now only a fixed subnet size
|
||||
sections = vip.split('.')[:3]
|
||||
@ -110,8 +110,8 @@ def plug_vip(vip, subnet_cidr, gateway):
|
||||
vip=vip, interface=interface))), 202)
|
||||
|
||||
|
||||
def plug_network():
|
||||
interface = _interface_down()
|
||||
def plug_network(mac_address):
|
||||
interface = _interface_by_mac(mac_address)
|
||||
|
||||
# write interface file
|
||||
with open(util.get_network_interface_file(interface), 'w') as text_file:
|
||||
@ -127,17 +127,15 @@ def plug_network():
|
||||
interface=interface))), 202)
|
||||
|
||||
|
||||
def _interface_down():
|
||||
# Find the interface which is down
|
||||
down = [interface for interface in netifaces.interfaces() if
|
||||
netifaces.AF_INET not in netifaces.ifaddresses(interface)]
|
||||
if len(down) != 1:
|
||||
# There should only be ONE interface being plugged; if there is
|
||||
# none down or more than one we have a problem...
|
||||
raise exceptions.HTTPException(
|
||||
response=flask.make_response(flask.jsonify(dict(
|
||||
details="No suitable network interface found")), 404))
|
||||
return down[0]
|
||||
def _interface_by_mac(mac):
|
||||
for interface in netifaces.interfaces():
|
||||
if netifaces.AF_LINK in netifaces.ifaddresses(interface):
|
||||
for link in netifaces.ifaddresses(interface)[netifaces.AF_LINK]:
|
||||
if link.get('addr') == mac:
|
||||
return interface
|
||||
raise exceptions.HTTPException(
|
||||
response=flask.make_response(flask.jsonify(dict(
|
||||
details="No suitable network interface found")), 404))
|
||||
|
||||
|
||||
def _bring_if_up(params, what):
|
||||
|
@ -115,20 +115,28 @@ def delete_certificate(listener_id, filename):
|
||||
def plug_vip(vip):
|
||||
# Catch any issues with the subnet info json
|
||||
try:
|
||||
subnet_info = flask.request.get_json()
|
||||
assert type(subnet_info) is dict
|
||||
assert 'subnet_cidr' in subnet_info
|
||||
assert 'gateway' in subnet_info
|
||||
net_info = flask.request.get_json()
|
||||
assert type(net_info) is dict
|
||||
assert 'subnet_cidr' in net_info
|
||||
assert 'gateway' in net_info
|
||||
assert 'mac_address' in net_info
|
||||
except Exception:
|
||||
raise exceptions.BadRequest(description='Invalid subnet information')
|
||||
return plug.plug_vip(vip,
|
||||
subnet_info['subnet_cidr'],
|
||||
subnet_info['gateway'])
|
||||
net_info['subnet_cidr'],
|
||||
net_info['gateway'],
|
||||
net_info['mac_address'])
|
||||
|
||||
|
||||
@app.route('/' + api_server.VERSION + '/plug/network', methods=['POST'])
|
||||
def plug_network():
|
||||
return plug.plug_network()
|
||||
try:
|
||||
port_info = flask.request.get_json()
|
||||
assert type(port_info) is dict
|
||||
assert 'mac_address' in port_info
|
||||
except Exception:
|
||||
raise exceptions.BadRequest(description='Invalid port information')
|
||||
return plug.plug_network(port_info['mac_address'])
|
||||
|
||||
|
||||
@app.route('/' + api_server.VERSION + '/certificate', methods=['PUT'])
|
||||
|
@ -149,11 +149,13 @@ class AmphoraLoadBalancerDriver(object):
|
||||
"""
|
||||
pass
|
||||
|
||||
def post_network_plug(self, amphora):
|
||||
def post_network_plug(self, amphora, port):
|
||||
"""Called after amphora added to network
|
||||
|
||||
:param amphora: amphora object, needs id and network ip(s)
|
||||
:type amphora: object
|
||||
:param port: contains information of the plugged port
|
||||
:type port: octavia.network.data_models.Port
|
||||
|
||||
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
|
||||
|
@ -102,14 +102,22 @@ class HaproxyAmphoraLoadBalancerDriver(driver_base.AmphoraLoadBalancerDriver):
|
||||
def post_vip_plug(self, load_balancer, amphorae_network_config):
|
||||
for amp in load_balancer.amphorae:
|
||||
subnet = amphorae_network_config.get(amp.id).vip_subnet
|
||||
subnet_info = {'subnet_cidr': subnet.cidr,
|
||||
'gateway': subnet.gateway_ip}
|
||||
# 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.
|
||||
port = amphorae_network_config.get(amp.id).vrrp_port
|
||||
net_info = {'subnet_cidr': subnet.cidr,
|
||||
'gateway': subnet.gateway_ip,
|
||||
'mac_address': port.mac_address}
|
||||
self.client.plug_vip(amp,
|
||||
load_balancer.vip.ip_address,
|
||||
subnet_info)
|
||||
net_info)
|
||||
|
||||
def post_network_plug(self, amphora):
|
||||
self.client.plug_network(amphora)
|
||||
def post_network_plug(self, amphora, port):
|
||||
port_info = {'mac_address': port.mac_address}
|
||||
self.client.plug_network(amphora, port_info)
|
||||
|
||||
def _process_tls_certificates(self, listener):
|
||||
"""Processes TLS data from the listener.
|
||||
@ -297,12 +305,13 @@ class AmphoraAPIClient(object):
|
||||
listener_id=listener_id, filename=pem_filename))
|
||||
return exc.check_exception(r)
|
||||
|
||||
def plug_network(self, amp):
|
||||
r = self.post(amp, 'plug/network')
|
||||
def plug_network(self, amp, port):
|
||||
r = self.post(amp, 'plug/network',
|
||||
json=port)
|
||||
return exc.check_exception(r)
|
||||
|
||||
def plug_vip(self, amp, vip, subnet_info):
|
||||
def plug_vip(self, amp, vip, net_info):
|
||||
r = self.post(amp,
|
||||
'plug/vip/{vip}'.format(vip=vip),
|
||||
json=subnet_info)
|
||||
json=net_info)
|
||||
return exc.check_exception(r)
|
||||
|
@ -36,7 +36,8 @@ VIP_ROUTE_TABLE = 'vip'
|
||||
CMD_DHCLIENT = "dhclient {0}"
|
||||
CMD_ADD_IP_ADDR = "ip addr add {0}/24 dev {1}"
|
||||
CMD_SHOW_IP_ADDR = "ip addr show {0}"
|
||||
CMD_GREP_DOWN_LINKS = "ip link | grep DOWN -m 1 | awk '{print $2}'"
|
||||
CMD_GREP_LINK_BY_MAC = ("ip link | grep {mac_address} -m 1 -B 1 "
|
||||
"| awk 'NR==1{{print $2}}'")
|
||||
CMD_CREATE_VIP_ROUTE_TABLE = (
|
||||
"su -c 'echo \"1 {0}\" >> /etc/iproute2/rt_tables'"
|
||||
)
|
||||
@ -184,7 +185,9 @@ class HaproxyManager(driver_base.AmphoraLoadBalancerDriver):
|
||||
# Connect to amphora
|
||||
self._connect(hostname=amp.lb_network_ip)
|
||||
|
||||
stdout, _ = self._execute_command(CMD_GREP_DOWN_LINKS)
|
||||
mac = amphorae_network_config.get(amp.id).vrrp_port.mac_address
|
||||
stdout, _ = self._execute_command(
|
||||
CMD_GREP_LINK_BY_MAC.format(mac_address=mac))
|
||||
iface = stdout[:-2]
|
||||
if not iface:
|
||||
self.client.close()
|
||||
@ -195,9 +198,10 @@ class HaproxyManager(driver_base.AmphoraLoadBalancerDriver):
|
||||
iface, amphorae_network_config.get(amp.id))
|
||||
self.client.close()
|
||||
|
||||
def post_network_plug(self, amphora):
|
||||
def post_network_plug(self, amphora, port):
|
||||
self._connect(hostname=amphora.lb_network_ip)
|
||||
stdout, _ = self._execute_command(CMD_GREP_DOWN_LINKS)
|
||||
stdout, _ = self._execute_command(
|
||||
CMD_GREP_LINK_BY_MAC.format(mac_address=port.mac_address))
|
||||
iface = stdout[:-2]
|
||||
if not iface:
|
||||
self.client.close()
|
||||
|
@ -98,6 +98,7 @@ OBJECT = 'object'
|
||||
SERVER_PEM = 'server_pem'
|
||||
VIP_NETWORK = 'vip_network'
|
||||
AMPHORAE_NETWORK_CONFIG = 'amphorae_network_config'
|
||||
ADDED_PORTS = 'added_ports'
|
||||
|
||||
CREATE_AMPHORA_FLOW = 'octavia-create-amphora-flow'
|
||||
CREATE_AMPHORA_FOR_LB_FLOW = 'octavia-create-amp-for-lb-flow'
|
||||
|
@ -34,9 +34,9 @@ class MemberFlows(object):
|
||||
requires=constants.LOADBALANCER,
|
||||
provides=constants.DELTAS))
|
||||
create_member_flow.add(network_tasks.HandleNetworkDeltas(
|
||||
requires=constants.DELTAS))
|
||||
requires=constants.DELTAS, provides=constants.ADDED_PORTS))
|
||||
create_member_flow.add(amphora_driver_tasks.AmphoraePostNetworkPlug(
|
||||
requires=(constants.LOADBALANCER, constants.DELTAS)
|
||||
requires=(constants.LOADBALANCER, constants.ADDED_PORTS)
|
||||
))
|
||||
create_member_flow.add(amphora_driver_tasks.ListenerUpdate(
|
||||
requires=(constants.LISTENER, constants.VIP)))
|
||||
|
@ -165,12 +165,16 @@ class AmphoraPostNetworkPlug(BaseAmphoraTask):
|
||||
class AmphoraePostNetworkPlug(BaseAmphoraTask):
|
||||
"""Task to notify the amphorae post network plug."""
|
||||
|
||||
def execute(self, loadbalancer, deltas):
|
||||
def execute(self, loadbalancer, added_ports):
|
||||
"""Execute post_network_plug routine."""
|
||||
for amphora in loadbalancer.amphorae:
|
||||
if amphora.id in deltas and deltas[amphora.id].add_nics:
|
||||
self.amphora_driver.post_network_plug(amphora)
|
||||
LOG.debug("Posted network plug for the compute instance")
|
||||
if amphora.id in added_ports:
|
||||
for port in added_ports[amphora.id]:
|
||||
self.amphora_driver.post_network_plug(amphora, port)
|
||||
LOG.debug(
|
||||
"post_network_plug called on compute instance "
|
||||
"{compute_id} for port {port_id}".format(
|
||||
compute_id=amphora.compute_id, port_id=port.id))
|
||||
|
||||
def revert(self, result, loadbalancer, deltas, *args, **kwargs):
|
||||
"""Handle a failed post network plug."""
|
||||
|
@ -200,10 +200,18 @@ class HandleNetworkDeltas(BaseNetworkTask):
|
||||
def execute(self, deltas):
|
||||
"""Handle network plugging based off deltas."""
|
||||
|
||||
added_ports = {}
|
||||
for amp_id, delta in six.iteritems(deltas):
|
||||
added_ports[amp_id] = []
|
||||
for nic in delta.add_nics:
|
||||
self.network_driver.plug_network(delta.compute_id,
|
||||
nic.network_id)
|
||||
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,
|
||||
@ -213,6 +221,7 @@ class HandleNetworkDeltas(BaseNetworkTask):
|
||||
except Exception as e:
|
||||
LOG.error(
|
||||
_LE("Unable to unplug network - exception: %s"), e)
|
||||
return added_ports
|
||||
|
||||
def revert(self, result, deltas):
|
||||
"""Handle a network plug or unplug failures."""
|
||||
|
@ -73,7 +73,8 @@ class Port(data_models.BaseDataModel):
|
||||
|
||||
def __init__(self, id=None, name=None, device_id=None, device_owner=None,
|
||||
mac_address=None, network_id=None, status=None,
|
||||
tenant_id=None, admin_state_up=None, fixed_ips=None):
|
||||
tenant_id=None, admin_state_up=None, fixed_ips=None,
|
||||
network=None):
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.device_id = device_id
|
||||
@ -84,6 +85,7 @@ class Port(data_models.BaseDataModel):
|
||||
self.tenant_id = tenant_id
|
||||
self.admin_state_up = admin_state_up
|
||||
self.fixed_ips = fixed_ips or []
|
||||
self.network = network
|
||||
|
||||
def get_subnet_id(self, fixed_ip_address):
|
||||
for fixed_ip in self.fixed_ips:
|
||||
@ -93,9 +95,10 @@ class Port(data_models.BaseDataModel):
|
||||
|
||||
class FixedIP(data_models.BaseDataModel):
|
||||
|
||||
def __init__(self, subnet_id=None, ip_address=None):
|
||||
def __init__(self, subnet_id=None, ip_address=None, subnet=None):
|
||||
self.subnet_id = subnet_id
|
||||
self.ip_address = ip_address
|
||||
self.subnet = subnet
|
||||
|
||||
|
||||
class AmphoraNetworkConfig(data_models.BaseDataModel):
|
||||
|
@ -474,9 +474,13 @@ class ServerTestCase(base.TestCase):
|
||||
@mock.patch('subprocess.check_output')
|
||||
def test_plug_network(self, mock_check_output, mock_ifaddress,
|
||||
mock_interfaces):
|
||||
port_info = {'mac_address': '123'}
|
||||
|
||||
# No interface at all
|
||||
mock_interfaces.side_effect = [[]]
|
||||
rv = self.app.post('/' + api_server.VERSION + "/plug/network")
|
||||
rv = self.app.post('/' + api_server.VERSION + "/plug/network",
|
||||
content_type='application/json',
|
||||
data=json.dumps(port_info))
|
||||
self.assertEqual(404, rv.status_code)
|
||||
self.assertEqual(dict(details="No suitable network interface found"),
|
||||
json.loads(rv.data.decode('utf-8')))
|
||||
@ -484,7 +488,9 @@ class ServerTestCase(base.TestCase):
|
||||
# No interface down
|
||||
mock_interfaces.side_effect = [['blah']]
|
||||
mock_ifaddress.side_effect = [[netifaces.AF_INET]]
|
||||
rv = self.app.post('/' + api_server.VERSION + "/plug/network")
|
||||
rv = self.app.post('/' + api_server.VERSION + "/plug/network",
|
||||
content_type='application/json',
|
||||
data=json.dumps(port_info))
|
||||
self.assertEqual(404, rv.status_code)
|
||||
self.assertEqual(dict(details="No suitable network interface found"),
|
||||
json.loads(rv.data.decode('utf-8')))
|
||||
@ -492,10 +498,13 @@ class ServerTestCase(base.TestCase):
|
||||
|
||||
# One Interface down, Happy Path
|
||||
mock_interfaces.side_effect = [['blah']]
|
||||
mock_ifaddress.side_effect = [['bla']]
|
||||
mock_ifaddress.side_effect = [[netifaces.AF_LINK],
|
||||
{netifaces.AF_LINK: [{'addr': '123'}]}]
|
||||
m = mock.mock_open()
|
||||
with mock.patch('%s.open' % BUILTINS, m, create=True):
|
||||
rv = self.app.post('/' + api_server.VERSION + "/plug/network")
|
||||
rv = self.app.post('/' + api_server.VERSION + "/plug/network",
|
||||
content_type='application/json',
|
||||
data=json.dumps(port_info))
|
||||
self.assertEqual(202, rv.status_code)
|
||||
m.assert_called_once_with(
|
||||
'/etc/network/interfaces.d/blah.cfg', 'w')
|
||||
@ -509,13 +518,16 @@ class ServerTestCase(base.TestCase):
|
||||
|
||||
# same as above but ifup fails
|
||||
mock_interfaces.side_effect = [['blah']]
|
||||
mock_ifaddress.side_effect = [['bla']]
|
||||
mock_ifaddress.side_effect = [[netifaces.AF_LINK],
|
||||
{netifaces.AF_LINK: [{'addr': '123'}]}]
|
||||
mock_check_output.side_effect = [subprocess.CalledProcessError(
|
||||
7, 'test', RANDOM_ERROR), subprocess.CalledProcessError(
|
||||
7, 'test', RANDOM_ERROR)]
|
||||
m = mock.mock_open()
|
||||
with mock.patch('%s.open' % BUILTINS, m, create=True):
|
||||
rv = self.app.post('/' + api_server.VERSION + "/plug/network")
|
||||
rv = self.app.post('/' + api_server.VERSION + "/plug/network",
|
||||
content_type='application/json',
|
||||
data=json.dumps(port_info))
|
||||
self.assertEqual(500, rv.status_code)
|
||||
self.assertEqual(
|
||||
{'details': RANDOM_ERROR,
|
||||
@ -529,7 +541,9 @@ class ServerTestCase(base.TestCase):
|
||||
def test_plug_VIP(self, mock_pyroute2, mock_check_output, mock_ifaddress,
|
||||
mock_interfaces):
|
||||
|
||||
subnet_info = {'subnet_cidr': '10.0.0.0/24', 'gateway': '10.0.0.1'}
|
||||
subnet_info = {'subnet_cidr': '10.0.0.0/24',
|
||||
'gateway': '10.0.0.1',
|
||||
'mac_address': '123'}
|
||||
|
||||
# malformated ip
|
||||
rv = self.app.post('/' + api_server.VERSION + '/plug/vip/error',
|
||||
@ -562,7 +576,8 @@ class ServerTestCase(base.TestCase):
|
||||
|
||||
# One Interface down, Happy Path
|
||||
mock_interfaces.side_effect = [['blah']]
|
||||
mock_ifaddress.side_effect = [['bla']]
|
||||
mock_ifaddress.side_effect = [[netifaces.AF_LINK],
|
||||
{netifaces.AF_LINK: [{'addr': '123'}]}]
|
||||
m = mock.mock_open()
|
||||
with mock.patch('%s.open' % BUILTINS, m, create=True):
|
||||
rv = self.app.post('/' + api_server.VERSION +
|
||||
@ -585,7 +600,8 @@ class ServerTestCase(base.TestCase):
|
||||
['ifup', 'blah:0'], stderr=-2)
|
||||
|
||||
mock_interfaces.side_effect = [['blah']]
|
||||
mock_ifaddress.side_effect = [['blah']]
|
||||
mock_ifaddress.side_effect = [[netifaces.AF_LINK],
|
||||
{netifaces.AF_LINK: [{'addr': '123'}]}]
|
||||
mock_check_output.side_effect = [
|
||||
'unplug1',
|
||||
subprocess.CalledProcessError(
|
||||
|
@ -21,6 +21,7 @@ import six
|
||||
from octavia.amphorae.drivers.haproxy import exceptions as exc
|
||||
from octavia.amphorae.drivers.haproxy import rest_api_driver as driver
|
||||
from octavia.db import models
|
||||
from octavia.network import data_models as network_models
|
||||
from octavia.tests.unit import base as base
|
||||
from octavia.tests.unit.common.sample_configs import sample_configs
|
||||
|
||||
@ -29,7 +30,8 @@ FAKE_GATEWAY = '10.0.0.1'
|
||||
FAKE_IP = 'fake'
|
||||
FAKE_PEM_FILENAME = "file_name"
|
||||
FAKE_SUBNET_INFO = {'subnet_cidr': FAKE_CIDR,
|
||||
'gateway': FAKE_GATEWAY}
|
||||
'gateway': FAKE_GATEWAY,
|
||||
'mac_address': '123'}
|
||||
FAKE_UUID_1 = uuidutils.generate_uuid()
|
||||
|
||||
|
||||
@ -48,6 +50,7 @@ class HaproxyAmphoraLoadBalancerDriverTest(base.TestCase):
|
||||
self.amp = self.sl.load_balancer.amphorae[0]
|
||||
self.sv = sample_configs.sample_vip_tuple()
|
||||
self.lb = self.sl.load_balancer
|
||||
self.port = network_models.Port(mac_address='123')
|
||||
|
||||
@mock.patch('octavia.common.tls_utils.cert_parser.get_host_names')
|
||||
def test_update(self, mock_cert):
|
||||
@ -119,13 +122,15 @@ class HaproxyAmphoraLoadBalancerDriverTest(base.TestCase):
|
||||
amphorae_network_config = mock.MagicMock()
|
||||
amphorae_network_config.get().vip_subnet.cidr = FAKE_CIDR
|
||||
amphorae_network_config.get().vip_subnet.gateway_ip = FAKE_GATEWAY
|
||||
amphorae_network_config.get().vrrp_port = self.port
|
||||
self.driver.post_vip_plug(self.lb, amphorae_network_config)
|
||||
self.driver.client.plug_vip.assert_called_once_with(
|
||||
self.amp, self.lb.vip.ip_address, FAKE_SUBNET_INFO)
|
||||
|
||||
def test_post_network_plug(self):
|
||||
self.driver.post_network_plug(self.amp)
|
||||
self.driver.client.plug_network.assert_called_once_with(self.amp)
|
||||
self.driver.post_network_plug(self.amp, self.port)
|
||||
self.driver.client.plug_network.assert_called_once_with(
|
||||
self.amp, dict(mac_address='123'))
|
||||
|
||||
|
||||
class AmphoraAPIClientTest(base.TestCase):
|
||||
@ -135,6 +140,7 @@ class AmphoraAPIClientTest(base.TestCase):
|
||||
self.driver = driver.AmphoraAPIClient()
|
||||
self.base_url = "https://127.0.0.1:8443/0.5"
|
||||
self.amp = models.Amphora(lb_network_ip='127.0.0.1', compute_id='123')
|
||||
self.port_info = dict(mac_address='123')
|
||||
|
||||
@requests_mock.mock()
|
||||
def test_get_info(self, m):
|
||||
@ -605,5 +611,5 @@ class AmphoraAPIClientTest(base.TestCase):
|
||||
m.post("{base}/plug/network".format(
|
||||
base=self.base_url)
|
||||
)
|
||||
self.driver.plug_network(self.amp)
|
||||
self.driver.plug_network(self.amp, self.port_info)
|
||||
self.assertTrue(m.called)
|
||||
|
@ -65,6 +65,7 @@ class TestSshDriver(base.TestCase):
|
||||
self.driver.client.exec_command.return_value = (
|
||||
mock.Mock(), mock.Mock(), mock.Mock())
|
||||
self.driver.amp_config = mock.MagicMock()
|
||||
self.port = network_models.Port(mac_address='123')
|
||||
|
||||
def test_update(self):
|
||||
with mock.patch.object(
|
||||
@ -232,10 +233,14 @@ class TestSshDriver(base.TestCase):
|
||||
lb_network_ip=MOCK_IP_ADDRESS)]
|
||||
vip = data_models.Vip(ip_address=MOCK_IP_ADDRESS)
|
||||
lb = data_models.LoadBalancer(amphorae=amps, vip=vip)
|
||||
vip_network = network_models.Network(id=MOCK_NETWORK_ID)
|
||||
amphorae_net_config = {amps[0].id: network_models.AmphoraNetworkConfig(
|
||||
amphora=amps[0],
|
||||
vrrp_port=self.port
|
||||
)}
|
||||
exec_command.return_value = ('', '')
|
||||
self.driver.post_vip_plug(lb, vip_network)
|
||||
exec_command.assert_called_once_with(ssh_driver.CMD_GREP_DOWN_LINKS)
|
||||
self.driver.post_vip_plug(lb, amphorae_net_config)
|
||||
exec_command.assert_called_once_with(
|
||||
ssh_driver.CMD_GREP_LINK_BY_MAC.format(mac_address='123'))
|
||||
|
||||
@mock.patch.object(ssh_driver.HaproxyManager, '_execute_command')
|
||||
def test_post_vip_plug(self, exec_command):
|
||||
@ -251,12 +256,14 @@ class TestSshDriver(base.TestCase):
|
||||
amphorae_net_config = {amps[0].id: network_models.AmphoraNetworkConfig(
|
||||
amphora=amps[0],
|
||||
vip_subnet=vip_subnet,
|
||||
vip_port=vip_port
|
||||
vip_port=vip_port,
|
||||
vrrp_port=self.port
|
||||
)}
|
||||
iface = 'eth1'
|
||||
exec_command.return_value = ('{0}: '.format(iface), '')
|
||||
self.driver.post_vip_plug(lb, amphorae_net_config)
|
||||
grep_call = mock.call(ssh_driver.CMD_GREP_DOWN_LINKS)
|
||||
grep_call = mock.call(
|
||||
ssh_driver.CMD_GREP_LINK_BY_MAC.format(mac_address='123'))
|
||||
dhclient_call = mock.call(ssh_driver.CMD_DHCLIENT.format(iface),
|
||||
run_as_root=True)
|
||||
add_ip_call = mock.call(ssh_driver.CMD_ADD_IP_ADDR.format(
|
||||
@ -298,8 +305,9 @@ class TestSshDriver(base.TestCase):
|
||||
amp = data_models.Amphora(id=MOCK_AMP_ID, compute_id=MOCK_COMPUTE_ID,
|
||||
lb_network_ip=MOCK_IP_ADDRESS)
|
||||
exec_command.return_value = ('', '')
|
||||
self.driver.post_network_plug(amp)
|
||||
exec_command.assert_called_once_with(ssh_driver.CMD_GREP_DOWN_LINKS)
|
||||
self.driver.post_network_plug(amp, self.port)
|
||||
exec_command.assert_called_once_with(
|
||||
ssh_driver.CMD_GREP_LINK_BY_MAC.format(mac_address='123'))
|
||||
|
||||
@mock.patch.object(ssh_driver.HaproxyManager, '_execute_command')
|
||||
def test_post_network_plug(self, exec_command):
|
||||
@ -307,8 +315,9 @@ class TestSshDriver(base.TestCase):
|
||||
lb_network_ip=MOCK_IP_ADDRESS)
|
||||
iface = 'eth1'
|
||||
exec_command.return_value = ('{0}: '.format(iface), '')
|
||||
self.driver.post_network_plug(amp)
|
||||
grep_call = mock.call(ssh_driver.CMD_GREP_DOWN_LINKS)
|
||||
self.driver.post_network_plug(amp, self.port)
|
||||
grep_call = mock.call(
|
||||
ssh_driver.CMD_GREP_LINK_BY_MAC.format(mac_address='123'))
|
||||
dhclient_call = mock.call(ssh_driver.CMD_DHCLIENT.format(iface),
|
||||
run_as_root=True)
|
||||
show_ip_call = mock.call(ssh_driver.CMD_SHOW_IP_ADDR.format(iface))
|
||||
|
@ -37,7 +37,7 @@ class TestMemberFlows(base.TestCase):
|
||||
self.assertIn('vip', member_flow.requires)
|
||||
|
||||
self.assertEqual(len(member_flow.requires), 3)
|
||||
self.assertEqual(len(member_flow.provides), 1)
|
||||
self.assertEqual(len(member_flow.provides), 2)
|
||||
|
||||
def test_get_delete_member_flow(self):
|
||||
|
||||
|
@ -230,11 +230,12 @@ class TestAmphoraDriverTasks(base.TestCase):
|
||||
_LB_mock.amphorae = [_amphora_mock]
|
||||
amphora_post_network_plug_obj = (amphora_driver_tasks.
|
||||
AmphoraePostNetworkPlug())
|
||||
_deltas_mock = {_amphora_mock.id: mock.Mock()}
|
||||
port_mock = mock.Mock()
|
||||
_deltas_mock = {_amphora_mock.id: [port_mock]}
|
||||
amphora_post_network_plug_obj.execute(_LB_mock, _deltas_mock)
|
||||
|
||||
(mock_driver.post_network_plug.
|
||||
assert_called_once_with(_amphora_mock))
|
||||
assert_called_once_with(_amphora_mock, port_mock))
|
||||
|
||||
# Test revert
|
||||
amp = amphora_post_network_plug_obj.revert(None, _LB_mock,
|
||||
|
@ -82,11 +82,15 @@ Establish a base class to model the desire functionality:
|
||||
"""
|
||||
pass
|
||||
|
||||
def post_network_plug(self, amphora):
|
||||
def post_network_plug(self, amphora, port):
|
||||
"""OPTIONAL - called after adding a compute instance to a network.
|
||||
|
||||
This will perform any necessary actions to allow for connectivity
|
||||
for that network on that instance.
|
||||
|
||||
port is an instance of octavia.network.data_models.Port. It
|
||||
contains information about the port, subnet, and network that
|
||||
was just plugged.
|
||||
"""
|
||||
|
||||
def post_vip_plug(self, load_balancer, amphorae_network_config):
|
||||
|
Loading…
Reference in New Issue
Block a user