Adds policy based routing for the amphora

1. Creates a new element for pyroute2
2. Adds this element to the amphora image
3. Updates the amphora REST interface to pass additional network information
4. Creates the policy based routes and rules on the amp during plug vip
5. Updates the REST API spec

Change-Id: Ibd622ec302cf78c12ae2bd5d76d012ab619939a6
This commit is contained in:
Michael Johnson 2015-08-03 16:39:09 +00:00
parent adfc6977c6
commit fcc5dcf6e0
12 changed files with 226 additions and 121 deletions

View File

@ -330,6 +330,9 @@ fi
# Add the Octavia Amphora agent.py element # Add the Octavia Amphora agent.py element
AMP_element_sequence="$AMP_element_sequence amphora-agent" AMP_element_sequence="$AMP_element_sequence amphora-agent"
# Add pyroute2 element
AMP_element_sequence="$AMP_element_sequence pyroute2"
# Add the vrrp Octacia element # Add the vrrp Octacia element
AMP_element_sequence="$AMP_element_sequence vrrp-octavia" AMP_element_sequence="$AMP_element_sequence vrrp-octavia"

View File

@ -35,6 +35,7 @@ virt-df -a $AMP_IMAGE_LOCATION | \
grep -q "amphora-x64-haproxy.qcow2:/dev/sda1[ \t]*5015940[ \t]*.*" grep -q "amphora-x64-haproxy.qcow2:/dev/sda1[ \t]*5015940[ \t]*.*"
if [ $? != 0 ]; then if [ $? != 0 ]; then
echo "ERROR: Amphora image did not pass the default size test" echo "ERROR: Amphora image did not pass the default size test"
echo "On Ubuntu you may need to run 'sudo chmod 0644 /boot/vmlinuz*' for libguestfs"
exit 1 exit 1
else else
echo "Amphora image size is correct" echo "Amphora image size is correct"

View File

@ -1,3 +1,4 @@
Babel>=1.3 Babel>=1.3
dib-utils dib-utils
PyYAML PyYAML
six>=1.9.0

View File

@ -1084,7 +1084,11 @@ Plug VIP
* *:ip* = the vip's ip address * *:ip* = the vip's ip address
* **Data params:** none * **Data params:**
* *subnet_cidr*: The vip subnet in cidr notation
* *gateway*: The vip subnet gateway address
* **Success Response:** * **Success Response:**
* Code: 202 * Code: 202
@ -1095,6 +1099,7 @@ Plug VIP
* Code: 400 * Code: 400
* Content: Invalid IP * Content: Invalid IP
* Content: Invalid subnet information
* Code: 404 * Code: 404
@ -1129,6 +1134,12 @@ Plug VIP
POST URL: POST URL:
https://octavia-haproxy-img-00328.local/v0.1/plug/vip/203.0.113.2 https://octavia-haproxy-img-00328.local/v0.1/plug/vip/203.0.113.2
JSON POST parameters:
{
'subnet_cidr': '203.0.113.0/24',
'gateway': '203.0.113.1'
}
JSON Response: JSON Response:
{ {
'message': 'OK', 'message': 'OK',

View File

@ -0,0 +1,3 @@
This element installs the pyroute2 python library.
Pyroute2 is a pure Python netlink and Linux network configuration library.

View File

@ -0,0 +1,5 @@
#!/bin/bash
set -eux
pip install -U 'pyroute2>=0.3.10'

View File

@ -20,6 +20,7 @@ import subprocess
import flask import flask
import jinja2 import jinja2
import netifaces import netifaces
import pyroute2
from werkzeug import exceptions from werkzeug import exceptions
from octavia.amphorae.backends.agent.api_server import util from octavia.amphorae.backends.agent.api_server import util
@ -37,7 +38,7 @@ template_port = j2_env.get_template(ETH_X_VIP_CONF)
template_vip = j2_env.get_template(ETH_PORT_CONF) template_vip = j2_env.get_template(ETH_PORT_CONF)
def plug_vip(vip): def plug_vip(vip, subnet_cidr, gateway):
# validate vip # validate vip
try: try:
socket.inet_aton(vip) socket.inet_aton(vip)
@ -68,6 +69,41 @@ def plug_vip(vip):
_bring_if_up("{interface}".format(interface=interface), 'VIP') _bring_if_up("{interface}".format(interface=interface), 'VIP')
_bring_if_up("{interface}:0".format(interface=interface), 'VIP') _bring_if_up("{interface}:0".format(interface=interface), 'VIP')
# Setup policy based routes for the amphora
ip = pyroute2.IPRoute()
cidr_split = subnet_cidr.split('/')
num_interface = ip.link_lookup(ifname=interface)
ip.route('add',
dst=cidr_split[0],
mask=int(cidr_split[1]),
oif=num_interface,
table=1,
rtproto='RTPROT_BOOT',
rtscope='RT_SCOPE_LINK')
ip.route('add',
dst='0.0.0.0',
gateway=gateway,
oif=num_interface,
table=1,
rtproto='RTPROT_BOOT')
ip.rule('add',
table=1,
action='FR_ACT_TO_TBL',
src=cidr_split[0],
src_len=int(cidr_split[1]))
ip.rule('add',
table=1,
action='FR_ACT_TO_TBL',
dst=cidr_split[0],
dst_len=int(cidr_split[1]))
return flask.make_response(flask.jsonify(dict( return flask.make_response(flask.jsonify(dict(
message="OK", message="OK",
details="VIP {vip} plugged on interface {interface}".format( details="VIP {vip} plugged on interface {interface}".format(

View File

@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import json
import logging import logging
import flask import flask
@ -111,7 +112,17 @@ def delete_certificate(listener_id, filename):
@app.route('/' + api_server.VERSION + '/plug/vip/<vip>', methods=['POST']) @app.route('/' + api_server.VERSION + '/plug/vip/<vip>', methods=['POST'])
def plug_vip(vip): def plug_vip(vip):
return plug.plug_vip(vip) # Catch any issues with the subnet info json
try:
request_json = flask.request.data.decode('utf8')
subnet_info = json.loads(request_json)
assert 'subnet_cidr' in subnet_info
assert 'gateway' in subnet_info
except Exception:
raise exceptions.BadRequest(description='Invalid subnet information')
return plug.plug_vip(vip,
subnet_info['subnet_cidr'],
subnet_info['gateway'])
@app.route('/' + api_server.VERSION + '/plug/network', methods=['POST']) @app.route('/' + api_server.VERSION + '/plug/network', methods=['POST'])

View File

@ -15,6 +15,7 @@
import functools import functools
import hashlib import hashlib
import json
import time import time
from oslo_log import log as logging from oslo_log import log as logging
@ -101,7 +102,12 @@ class HaproxyAmphoraLoadBalancerDriver(driver_base.AmphoraLoadBalancerDriver):
def post_vip_plug(self, load_balancer, amphorae_network_config): def post_vip_plug(self, load_balancer, amphorae_network_config):
for amp in load_balancer.amphorae: for amp in load_balancer.amphorae:
self.client.plug_vip(amp, load_balancer.vip.ip_address) subnet = amphorae_network_config.get(amp.id).vip_subnet
subnet_info = {'subnet_cidr': subnet.cidr,
'gateway': subnet.gateway_ip}
self.client.plug_vip(amp,
load_balancer.vip.ip_address,
subnet_info)
def post_network_plug(self, amphora): def post_network_plug(self, amphora):
self.client.plug_network(amphora) self.client.plug_network(amphora)
@ -296,6 +302,8 @@ class AmphoraAPIClient(object):
r = self.post(amp, 'plug/network') r = self.post(amp, 'plug/network')
return exc.check_exception(r) return exc.check_exception(r)
def plug_vip(self, amp, vip): def plug_vip(self, amp, vip, subnet_info):
r = self.post(amp, 'plug/vip/{vip}'.format(vip=vip)) r = self.post(amp,
'plug/vip/{vip}'.format(vip=vip),
json=json.dumps(subnet_info))
return exc.check_exception(r) return exc.check_exception(r)

View File

@ -508,15 +508,27 @@ class ServerTestCase(base.TestCase):
@mock.patch('netifaces.interfaces') @mock.patch('netifaces.interfaces')
@mock.patch('netifaces.ifaddresses') @mock.patch('netifaces.ifaddresses')
@mock.patch('subprocess.check_output') @mock.patch('subprocess.check_output')
def test_plug_VIP(self, mock_check_output, mock_ifaddress, @mock.patch('pyroute2.IPRoute')
def test_plug_VIP(self, mock_pyroute2, mock_check_output, mock_ifaddress,
mock_interfaces): mock_interfaces):
subnet_info = {'subnet_cidr': '10.0.0.0/24', 'gateway': '10.0.0.1'}
# malformated ip # malformated ip
rv = self.app.post('/' + api_server.VERSION + '/plug/vip/error',
data=json.dumps(subnet_info),
content_type='application/json')
self.assertEqual(400, rv.status_code)
# No subnet info
rv = self.app.post('/' + api_server.VERSION + '/plug/vip/error') rv = self.app.post('/' + api_server.VERSION + '/plug/vip/error')
self.assertEqual(400, rv.status_code) self.assertEqual(400, rv.status_code)
# No interface at all # No interface at all
mock_interfaces.side_effect = [[]] mock_interfaces.side_effect = [[]]
rv = self.app.post('/' + api_server.VERSION + "/plug/vip/203.0.113.2") rv = self.app.post('/' + api_server.VERSION + "/plug/vip/203.0.113.2",
content_type='application/json',
data=json.dumps(subnet_info))
self.assertEqual(404, rv.status_code) self.assertEqual(404, rv.status_code)
self.assertEqual(dict(details="No suitable network interface found"), self.assertEqual(dict(details="No suitable network interface found"),
json.loads(rv.data.decode('utf-8'))) json.loads(rv.data.decode('utf-8')))
@ -524,7 +536,9 @@ class ServerTestCase(base.TestCase):
# Two interfaces down # Two interfaces down
mock_interfaces.side_effect = [['blah', 'blah2']] mock_interfaces.side_effect = [['blah', 'blah2']]
mock_ifaddress.side_effect = [['blabla'], ['blabla']] mock_ifaddress.side_effect = [['blabla'], ['blabla']]
rv = self.app.post('/' + api_server.VERSION + "/plug/vip/203.0.113.2") rv = self.app.post('/' + api_server.VERSION + "/plug/vip/203.0.113.2",
content_type='application/json',
data=json.dumps(subnet_info))
self.assertEqual(404, rv.status_code) self.assertEqual(404, rv.status_code)
self.assertEqual(dict(details="No suitable network interface found"), self.assertEqual(dict(details="No suitable network interface found"),
json.loads(rv.data.decode('utf-8'))) json.loads(rv.data.decode('utf-8')))
@ -535,7 +549,9 @@ class ServerTestCase(base.TestCase):
m = mock.mock_open() m = mock.mock_open()
with mock.patch('%s.open' % BUILTINS, m, create=True): with mock.patch('%s.open' % BUILTINS, m, create=True):
rv = self.app.post('/' + api_server.VERSION + rv = self.app.post('/' + api_server.VERSION +
"/plug/vip/203.0.113.2") "/plug/vip/203.0.113.2",
content_type='application/json',
data=json.dumps(subnet_info))
self.assertEqual(202, rv.status_code) self.assertEqual(202, rv.status_code)
m.assert_called_once_with( m.assert_called_once_with(
'/etc/network/interfaces.d/blah.cfg', 'w') '/etc/network/interfaces.d/blah.cfg', 'w')
@ -561,7 +577,9 @@ class ServerTestCase(base.TestCase):
m = mock.mock_open() m = mock.mock_open()
with mock.patch('%s.open' % BUILTINS, m, create=True): with mock.patch('%s.open' % BUILTINS, m, create=True):
rv = self.app.post('/' + api_server.VERSION + rv = self.app.post('/' + api_server.VERSION +
"/plug/vip/203.0.113.2") "/plug/vip/203.0.113.2",
content_type='application/json',
data=json.dumps(subnet_info))
self.assertEqual(500, rv.status_code) self.assertEqual(500, rv.status_code)
self.assertEqual( self.assertEqual(
{'details': RANDOM_ERROR, {'details': RANDOM_ERROR,

View File

@ -24,9 +24,16 @@ from octavia.db import models
from octavia.tests.unit import base as base from octavia.tests.unit import base as base
from octavia.tests.unit.common.sample_configs import sample_configs from octavia.tests.unit.common.sample_configs import sample_configs
FAKE_CIDR = '10.0.0.0/24'
FAKE_GATEWAY = '10.0.0.1'
FAKE_IP = 'fake'
FAKE_PEM_FILENAME = "file_name"
FAKE_SUBNET_INFO = {'subnet_cidr': FAKE_CIDR,
'gateway': FAKE_GATEWAY}
FAKE_UUID_1 = uuidutils.generate_uuid()
class HaproxyAmphoraLoadBalancerDriverTest(base.TestCase): class HaproxyAmphoraLoadBalancerDriverTest(base.TestCase):
FAKE_UUID_1 = uuidutils.generate_uuid()
def setUp(self): def setUp(self):
super(HaproxyAmphoraLoadBalancerDriverTest, self).setUp() super(HaproxyAmphoraLoadBalancerDriverTest, self).setUp()
@ -109,9 +116,12 @@ class HaproxyAmphoraLoadBalancerDriverTest(base.TestCase):
pass pass
def test_post_vip_plug(self): def test_post_vip_plug(self):
self.driver.post_vip_plug(self.lb, mock.Mock()) amphorae_network_config = mock.MagicMock()
amphorae_network_config.get().vip_subnet.cidr = FAKE_CIDR
amphorae_network_config.get().vip_subnet.gateway_ip = FAKE_GATEWAY
self.driver.post_vip_plug(self.lb, amphorae_network_config)
self.driver.client.plug_vip.assert_called_once_with( self.driver.client.plug_vip.assert_called_once_with(
self.amp, self.lb.vip.ip_address) self.amp, self.lb.vip.ip_address, FAKE_SUBNET_INFO)
def test_post_network_plug(self): def test_post_network_plug(self):
self.driver.post_network_plug(self.amp) self.driver.post_network_plug(self.amp)
@ -119,8 +129,6 @@ class HaproxyAmphoraLoadBalancerDriverTest(base.TestCase):
class AmphoraAPIClientTest(base.TestCase): class AmphoraAPIClientTest(base.TestCase):
FAKE_UUID_1 = uuidutils.generate_uuid()
FAKE_PEM_FILENAME = "file_name"
def setUp(self): def setUp(self):
super(AmphoraAPIClientTest, self).setUp() super(AmphoraAPIClientTest, self).setUp()
@ -131,7 +139,7 @@ class AmphoraAPIClientTest(base.TestCase):
@requests_mock.mock() @requests_mock.mock()
def test_get_info(self, m): def test_get_info(self, m):
info = {"hostname": "some_hostname", "version": "some_version", info = {"hostname": "some_hostname", "version": "some_version",
"api_version": "0.5", "uuid": self.FAKE_UUID_1} "api_version": "0.5", "uuid": FAKE_UUID_1}
m.get("{base}/info".format(base=self.base_url), m.get("{base}/info".format(base=self.base_url),
json=info) json=info)
information = self.driver.get_info(self.amp) information = self.driver.get_info(self.amp)
@ -166,7 +174,7 @@ class AmphoraAPIClientTest(base.TestCase):
@requests_mock.mock() @requests_mock.mock()
def test_get_details(self, m): def test_get_details(self, m):
details = {"hostname": "some_hostname", "version": "some_version", details = {"hostname": "some_hostname", "version": "some_version",
"api_version": "0.5", "uuid": self.FAKE_UUID_1, "api_version": "0.5", "uuid": FAKE_UUID_1,
"network_tx": "some_tx", "network_rx": "some_rx", "network_tx": "some_tx", "network_rx": "some_rx",
"active": True, "haproxy_count": 10} "active": True, "haproxy_count": 10}
m.get("{base}/details".format(base=self.base_url), m.get("{base}/details".format(base=self.base_url),
@ -203,7 +211,7 @@ class AmphoraAPIClientTest(base.TestCase):
@requests_mock.mock() @requests_mock.mock()
def test_get_all_listeners(self, m): def test_get_all_listeners(self, m):
listeners = [{"status": "ONLINE", "provisioning_status": "ACTIVE", listeners = [{"status": "ONLINE", "provisioning_status": "ACTIVE",
"type": "PASSIVE", "uuid": self.FAKE_UUID_1}] "type": "PASSIVE", "uuid": FAKE_UUID_1}]
m.get("{base}/listeners".format(base=self.base_url), m.get("{base}/listeners".format(base=self.base_url),
json=listeners) json=listeners)
all_listeners = self.driver.get_all_listeners(self.amp) all_listeners = self.driver.get_all_listeners(self.amp)
@ -240,308 +248,308 @@ class AmphoraAPIClientTest(base.TestCase):
@requests_mock.mock() @requests_mock.mock()
def test_get_listener_status(self, m): def test_get_listener_status(self, m):
listener = {"status": "ONLINE", "provisioning_status": "ACTIVE", listener = {"status": "ONLINE", "provisioning_status": "ACTIVE",
"type": "PASSIVE", "uuid": self.FAKE_UUID_1} "type": "PASSIVE", "uuid": FAKE_UUID_1}
m.get("{base}/listeners/{listener_id}".format( m.get("{base}/listeners/{listener_id}".format(
base=self.base_url, listener_id=self.FAKE_UUID_1), base=self.base_url, listener_id=FAKE_UUID_1),
json=listener) json=listener)
status = self.driver.get_listener_status(self.amp, self.FAKE_UUID_1) status = self.driver.get_listener_status(self.amp, FAKE_UUID_1)
self.assertEqual(listener, status) self.assertEqual(listener, status)
@requests_mock.mock() @requests_mock.mock()
def test_get_listener_status_unauthorized(self, m): def test_get_listener_status_unauthorized(self, m):
m.get("{base}/listeners/{listener_id}".format( m.get("{base}/listeners/{listener_id}".format(
base=self.base_url, listener_id=self.FAKE_UUID_1), base=self.base_url, listener_id=FAKE_UUID_1),
status_code=401) status_code=401)
self.assertRaises(exc.Unauthorized, self.assertRaises(exc.Unauthorized,
self.driver.get_listener_status, self.amp, self.driver.get_listener_status, self.amp,
self.FAKE_UUID_1) FAKE_UUID_1)
@requests_mock.mock() @requests_mock.mock()
def test_get_listener_status_missing(self, m): def test_get_listener_status_missing(self, m):
m.get("{base}/listeners/{listener_id}".format( m.get("{base}/listeners/{listener_id}".format(
base=self.base_url, listener_id=self.FAKE_UUID_1), base=self.base_url, listener_id=FAKE_UUID_1),
status_code=404) status_code=404)
self.assertRaises(exc.NotFound, self.assertRaises(exc.NotFound,
self.driver.get_listener_status, self.amp, self.driver.get_listener_status, self.amp,
self.FAKE_UUID_1) FAKE_UUID_1)
@requests_mock.mock() @requests_mock.mock()
def test_get_listener_status_server_error(self, m): def test_get_listener_status_server_error(self, m):
m.get("{base}/listeners/{listener_id}".format( m.get("{base}/listeners/{listener_id}".format(
base=self.base_url, listener_id=self.FAKE_UUID_1), base=self.base_url, listener_id=FAKE_UUID_1),
status_code=500) status_code=500)
self.assertRaises(exc.InternalServerError, self.assertRaises(exc.InternalServerError,
self.driver.get_listener_status, self.amp, self.driver.get_listener_status, self.amp,
self.FAKE_UUID_1) FAKE_UUID_1)
@requests_mock.mock() @requests_mock.mock()
def test_get_listener_status_service_unavailable(self, m): def test_get_listener_status_service_unavailable(self, m):
m.get("{base}/listeners/{listener_id}".format( m.get("{base}/listeners/{listener_id}".format(
base=self.base_url, listener_id=self.FAKE_UUID_1), base=self.base_url, listener_id=FAKE_UUID_1),
status_code=503) status_code=503)
self.assertRaises(exc.ServiceUnavailable, self.assertRaises(exc.ServiceUnavailable,
self.driver.get_listener_status, self.amp, self.driver.get_listener_status, self.amp,
self.FAKE_UUID_1) FAKE_UUID_1)
@requests_mock.mock() @requests_mock.mock()
def test_start_listener(self, m): def test_start_listener(self, m):
m.put("{base}/listeners/{listener_id}/start".format( m.put("{base}/listeners/{listener_id}/start".format(
base=self.base_url, listener_id=self.FAKE_UUID_1)) base=self.base_url, listener_id=FAKE_UUID_1))
self.driver.start_listener(self.amp, self.FAKE_UUID_1) self.driver.start_listener(self.amp, FAKE_UUID_1)
self.assertTrue(m.called) self.assertTrue(m.called)
@requests_mock.mock() @requests_mock.mock()
def test_start_listener_missing(self, m): def test_start_listener_missing(self, m):
m.put("{base}/listeners/{listener_id}/start".format( m.put("{base}/listeners/{listener_id}/start".format(
base=self.base_url, listener_id=self.FAKE_UUID_1), base=self.base_url, listener_id=FAKE_UUID_1),
status_code=404) status_code=404)
self.assertRaises(exc.NotFound, self.driver.start_listener, self.assertRaises(exc.NotFound, self.driver.start_listener,
self.amp, self.FAKE_UUID_1) self.amp, FAKE_UUID_1)
@requests_mock.mock() @requests_mock.mock()
def test_start_listener_unauthorized(self, m): def test_start_listener_unauthorized(self, m):
m.put("{base}/listeners/{listener_id}/start".format( m.put("{base}/listeners/{listener_id}/start".format(
base=self.base_url, listener_id=self.FAKE_UUID_1), base=self.base_url, listener_id=FAKE_UUID_1),
status_code=401) status_code=401)
self.assertRaises(exc.Unauthorized, self.driver.start_listener, self.assertRaises(exc.Unauthorized, self.driver.start_listener,
self.amp, self.FAKE_UUID_1) self.amp, FAKE_UUID_1)
@requests_mock.mock() @requests_mock.mock()
def test_start_listener_server_error(self, m): def test_start_listener_server_error(self, m):
m.put("{base}/listeners/{listener_id}/start".format( m.put("{base}/listeners/{listener_id}/start".format(
base=self.base_url, listener_id=self.FAKE_UUID_1), base=self.base_url, listener_id=FAKE_UUID_1),
status_code=500) status_code=500)
self.assertRaises(exc.InternalServerError, self.driver.start_listener, self.assertRaises(exc.InternalServerError, self.driver.start_listener,
self.amp, self.FAKE_UUID_1) self.amp, FAKE_UUID_1)
@requests_mock.mock() @requests_mock.mock()
def test_start_listener_service_unavailable(self, m): def test_start_listener_service_unavailable(self, m):
m.put("{base}/listeners/{listener_id}/start".format( m.put("{base}/listeners/{listener_id}/start".format(
base=self.base_url, listener_id=self.FAKE_UUID_1), base=self.base_url, listener_id=FAKE_UUID_1),
status_code=503) status_code=503)
self.assertRaises(exc.ServiceUnavailable, self.driver.start_listener, self.assertRaises(exc.ServiceUnavailable, self.driver.start_listener,
self.amp, self.FAKE_UUID_1) self.amp, FAKE_UUID_1)
@requests_mock.mock() @requests_mock.mock()
def test_stop_listener(self, m): def test_stop_listener(self, m):
m.put("{base}/listeners/{listener_id}/stop".format( m.put("{base}/listeners/{listener_id}/stop".format(
base=self.base_url, listener_id=self.FAKE_UUID_1)) base=self.base_url, listener_id=FAKE_UUID_1))
self.driver.stop_listener(self.amp, self.FAKE_UUID_1) self.driver.stop_listener(self.amp, FAKE_UUID_1)
self.assertTrue(m.called) self.assertTrue(m.called)
@requests_mock.mock() @requests_mock.mock()
def test_stop_listener_missing(self, m): def test_stop_listener_missing(self, m):
m.put("{base}/listeners/{listener_id}/stop".format( m.put("{base}/listeners/{listener_id}/stop".format(
base=self.base_url, listener_id=self.FAKE_UUID_1), base=self.base_url, listener_id=FAKE_UUID_1),
status_code=404) status_code=404)
self.assertRaises(exc.NotFound, self.driver.stop_listener, self.assertRaises(exc.NotFound, self.driver.stop_listener,
self.amp, self.FAKE_UUID_1) self.amp, FAKE_UUID_1)
@requests_mock.mock() @requests_mock.mock()
def test_stop_listener_unauthorized(self, m): def test_stop_listener_unauthorized(self, m):
m.put("{base}/listeners/{listener_id}/stop".format( m.put("{base}/listeners/{listener_id}/stop".format(
base=self.base_url, listener_id=self.FAKE_UUID_1), base=self.base_url, listener_id=FAKE_UUID_1),
status_code=401) status_code=401)
self.assertRaises(exc.Unauthorized, self.driver.stop_listener, self.assertRaises(exc.Unauthorized, self.driver.stop_listener,
self.amp, self.FAKE_UUID_1) self.amp, FAKE_UUID_1)
@requests_mock.mock() @requests_mock.mock()
def test_stop_listener_server_error(self, m): def test_stop_listener_server_error(self, m):
m.put("{base}/listeners/{listener_id}/stop".format( m.put("{base}/listeners/{listener_id}/stop".format(
base=self.base_url, listener_id=self.FAKE_UUID_1), base=self.base_url, listener_id=FAKE_UUID_1),
status_code=500) status_code=500)
self.assertRaises(exc.InternalServerError, self.driver.stop_listener, self.assertRaises(exc.InternalServerError, self.driver.stop_listener,
self.amp, self.FAKE_UUID_1) self.amp, FAKE_UUID_1)
@requests_mock.mock() @requests_mock.mock()
def test_stop_listener_service_unavailable(self, m): def test_stop_listener_service_unavailable(self, m):
m.put("{base}/listeners/{listener_id}/stop".format( m.put("{base}/listeners/{listener_id}/stop".format(
base=self.base_url, listener_id=self.FAKE_UUID_1), base=self.base_url, listener_id=FAKE_UUID_1),
status_code=503) status_code=503)
self.assertRaises(exc.ServiceUnavailable, self.driver.stop_listener, self.assertRaises(exc.ServiceUnavailable, self.driver.stop_listener,
self.amp, self.FAKE_UUID_1) self.amp, FAKE_UUID_1)
@requests_mock.mock() @requests_mock.mock()
def test_delete_listener(self, m): def test_delete_listener(self, m):
m.delete("{base}/listeners/{listener_id}".format( m.delete("{base}/listeners/{listener_id}".format(
base=self.base_url, listener_id=self.FAKE_UUID_1), json={}) base=self.base_url, listener_id=FAKE_UUID_1), json={})
self.driver.delete_listener(self.amp, self.FAKE_UUID_1) self.driver.delete_listener(self.amp, FAKE_UUID_1)
self.assertTrue(m.called) self.assertTrue(m.called)
@requests_mock.mock() @requests_mock.mock()
def test_delete_listener_missing(self, m): def test_delete_listener_missing(self, m):
m.delete("{base}/listeners/{listener_id}".format( m.delete("{base}/listeners/{listener_id}".format(
base=self.base_url, listener_id=self.FAKE_UUID_1), base=self.base_url, listener_id=FAKE_UUID_1),
status_code=404) status_code=404)
self.assertRaises(exc.NotFound, self.driver.delete_listener, self.assertRaises(exc.NotFound, self.driver.delete_listener,
self.amp, self.FAKE_UUID_1) self.amp, FAKE_UUID_1)
@requests_mock.mock() @requests_mock.mock()
def test_delete_listener_unauthorized(self, m): def test_delete_listener_unauthorized(self, m):
m.delete("{base}/listeners/{listener_id}".format( m.delete("{base}/listeners/{listener_id}".format(
base=self.base_url, listener_id=self.FAKE_UUID_1), base=self.base_url, listener_id=FAKE_UUID_1),
status_code=401) status_code=401)
self.assertRaises(exc.Unauthorized, self.driver.delete_listener, self.assertRaises(exc.Unauthorized, self.driver.delete_listener,
self.amp, self.FAKE_UUID_1) self.amp, FAKE_UUID_1)
@requests_mock.mock() @requests_mock.mock()
def test_delete_listener_server_error(self, m): def test_delete_listener_server_error(self, m):
m.delete("{base}/listeners/{listener_id}".format( m.delete("{base}/listeners/{listener_id}".format(
base=self.base_url, listener_id=self.FAKE_UUID_1), base=self.base_url, listener_id=FAKE_UUID_1),
status_code=500) status_code=500)
self.assertRaises(exc.InternalServerError, self.driver.delete_listener, self.assertRaises(exc.InternalServerError, self.driver.delete_listener,
self.amp, self.FAKE_UUID_1) self.amp, FAKE_UUID_1)
@requests_mock.mock() @requests_mock.mock()
def test_delete_listener_service_unavailable(self, m): def test_delete_listener_service_unavailable(self, m):
m.delete("{base}/listeners/{listener_id}".format( m.delete("{base}/listeners/{listener_id}".format(
base=self.base_url, listener_id=self.FAKE_UUID_1), base=self.base_url, listener_id=FAKE_UUID_1),
status_code=503) status_code=503)
self.assertRaises(exc.ServiceUnavailable, self.driver.delete_listener, self.assertRaises(exc.ServiceUnavailable, self.driver.delete_listener,
self.amp, self.FAKE_UUID_1) self.amp, FAKE_UUID_1)
@requests_mock.mock() @requests_mock.mock()
def test_upload_cert_pem(self, m): def test_upload_cert_pem(self, m):
m.put("{base}/listeners/{listener_id}/certificates/{filename}".format( m.put("{base}/listeners/{listener_id}/certificates/{filename}".format(
base=self.base_url, listener_id=self.FAKE_UUID_1, base=self.base_url, listener_id=FAKE_UUID_1,
filename=self.FAKE_PEM_FILENAME)) filename=FAKE_PEM_FILENAME))
self.driver.upload_cert_pem(self.amp, self.FAKE_UUID_1, self.driver.upload_cert_pem(self.amp, FAKE_UUID_1,
self.FAKE_PEM_FILENAME, FAKE_PEM_FILENAME,
"some_file") "some_file")
self.assertTrue(m.called) self.assertTrue(m.called)
@requests_mock.mock() @requests_mock.mock()
def test_upload_invalid_cert_pem(self, m): def test_upload_invalid_cert_pem(self, m):
m.put("{base}/listeners/{listener_id}/certificates/{filename}".format( m.put("{base}/listeners/{listener_id}/certificates/{filename}".format(
base=self.base_url, listener_id=self.FAKE_UUID_1, base=self.base_url, listener_id=FAKE_UUID_1,
filename=self.FAKE_PEM_FILENAME), status_code=403) filename=FAKE_PEM_FILENAME), status_code=403)
self.assertRaises(exc.InvalidRequest, self.driver.upload_cert_pem, self.assertRaises(exc.InvalidRequest, self.driver.upload_cert_pem,
self.amp, self.FAKE_UUID_1, self.FAKE_PEM_FILENAME, self.amp, FAKE_UUID_1, FAKE_PEM_FILENAME,
"some_file") "some_file")
@requests_mock.mock() @requests_mock.mock()
def test_upload_cert_pem_unauthorized(self, m): def test_upload_cert_pem_unauthorized(self, m):
m.put("{base}/listeners/{listener_id}/certificates/{filename}".format( m.put("{base}/listeners/{listener_id}/certificates/{filename}".format(
base=self.base_url, listener_id=self.FAKE_UUID_1, base=self.base_url, listener_id=FAKE_UUID_1,
filename=self.FAKE_PEM_FILENAME), status_code=401) filename=FAKE_PEM_FILENAME), status_code=401)
self.assertRaises(exc.Unauthorized, self.driver.upload_cert_pem, self.assertRaises(exc.Unauthorized, self.driver.upload_cert_pem,
self.amp, self.FAKE_UUID_1, self.FAKE_PEM_FILENAME, self.amp, FAKE_UUID_1, FAKE_PEM_FILENAME,
"some_file") "some_file")
@requests_mock.mock() @requests_mock.mock()
def test_upload_cert_pem_server_error(self, m): def test_upload_cert_pem_server_error(self, m):
m.put("{base}/listeners/{listener_id}/certificates/{filename}".format( m.put("{base}/listeners/{listener_id}/certificates/{filename}".format(
base=self.base_url, listener_id=self.FAKE_UUID_1, base=self.base_url, listener_id=FAKE_UUID_1,
filename=self.FAKE_PEM_FILENAME), status_code=500) filename=FAKE_PEM_FILENAME), status_code=500)
self.assertRaises(exc.InternalServerError, self.driver.upload_cert_pem, self.assertRaises(exc.InternalServerError, self.driver.upload_cert_pem,
self.amp, self.FAKE_UUID_1, self.FAKE_PEM_FILENAME, self.amp, FAKE_UUID_1, FAKE_PEM_FILENAME,
"some_file") "some_file")
@requests_mock.mock() @requests_mock.mock()
def test_upload_cert_pem_service_unavailable(self, m): def test_upload_cert_pem_service_unavailable(self, m):
m.put("{base}/listeners/{listener_id}/certificates/{filename}".format( m.put("{base}/listeners/{listener_id}/certificates/{filename}".format(
base=self.base_url, listener_id=self.FAKE_UUID_1, base=self.base_url, listener_id=FAKE_UUID_1,
filename=self.FAKE_PEM_FILENAME), status_code=503) filename=FAKE_PEM_FILENAME), status_code=503)
self.assertRaises(exc.ServiceUnavailable, self.driver.upload_cert_pem, self.assertRaises(exc.ServiceUnavailable, self.driver.upload_cert_pem,
self.amp, self.FAKE_UUID_1, self.FAKE_PEM_FILENAME, self.amp, FAKE_UUID_1, FAKE_PEM_FILENAME,
"some_file") "some_file")
@requests_mock.mock() @requests_mock.mock()
def test_get_cert_5sum(self, m): def test_get_cert_5sum(self, m):
md5sum = {"md5sum": "some_real_sum"} md5sum = {"md5sum": "some_real_sum"}
m.get("{base}/listeners/{listener_id}/certificates/{filename}".format( m.get("{base}/listeners/{listener_id}/certificates/{filename}".format(
base=self.base_url, listener_id=self.FAKE_UUID_1, base=self.base_url, listener_id=FAKE_UUID_1,
filename=self.FAKE_PEM_FILENAME), json=md5sum) filename=FAKE_PEM_FILENAME), json=md5sum)
sum_test = self.driver.get_cert_md5sum(self.amp, self.FAKE_UUID_1, sum_test = self.driver.get_cert_md5sum(self.amp, FAKE_UUID_1,
self.FAKE_PEM_FILENAME) FAKE_PEM_FILENAME)
self.assertIsNotNone(sum_test) self.assertIsNotNone(sum_test)
@requests_mock.mock() @requests_mock.mock()
def test_get_cert_5sum_missing(self, m): def test_get_cert_5sum_missing(self, m):
m.get("{base}/listeners/{listener_id}/certificates/{filename}".format( m.get("{base}/listeners/{listener_id}/certificates/{filename}".format(
base=self.base_url, listener_id=self.FAKE_UUID_1, base=self.base_url, listener_id=FAKE_UUID_1,
filename=self.FAKE_PEM_FILENAME), status_code=404) filename=FAKE_PEM_FILENAME), status_code=404)
self.assertRaises(exc.NotFound, self.driver.get_cert_md5sum, self.assertRaises(exc.NotFound, self.driver.get_cert_md5sum,
self.amp, self.FAKE_UUID_1, self.FAKE_PEM_FILENAME) self.amp, FAKE_UUID_1, FAKE_PEM_FILENAME)
@requests_mock.mock() @requests_mock.mock()
def test_get_cert_5sum_unauthorized(self, m): def test_get_cert_5sum_unauthorized(self, m):
m.get("{base}/listeners/{listener_id}/certificates/{filename}".format( m.get("{base}/listeners/{listener_id}/certificates/{filename}".format(
base=self.base_url, listener_id=self.FAKE_UUID_1, base=self.base_url, listener_id=FAKE_UUID_1,
filename=self.FAKE_PEM_FILENAME), status_code=401) filename=FAKE_PEM_FILENAME), status_code=401)
self.assertRaises(exc.Unauthorized, self.driver.get_cert_md5sum, self.assertRaises(exc.Unauthorized, self.driver.get_cert_md5sum,
self.amp, self.FAKE_UUID_1, self.FAKE_PEM_FILENAME) self.amp, FAKE_UUID_1, FAKE_PEM_FILENAME)
@requests_mock.mock() @requests_mock.mock()
def test_get_cert_5sum_server_error(self, m): def test_get_cert_5sum_server_error(self, m):
m.get("{base}/listeners/{listener_id}/certificates/{filename}".format( m.get("{base}/listeners/{listener_id}/certificates/{filename}".format(
base=self.base_url, listener_id=self.FAKE_UUID_1, base=self.base_url, listener_id=FAKE_UUID_1,
filename=self.FAKE_PEM_FILENAME), status_code=500) filename=FAKE_PEM_FILENAME), status_code=500)
self.assertRaises(exc.InternalServerError, self.driver.get_cert_md5sum, self.assertRaises(exc.InternalServerError, self.driver.get_cert_md5sum,
self.amp, self.FAKE_UUID_1, self.FAKE_PEM_FILENAME) self.amp, FAKE_UUID_1, FAKE_PEM_FILENAME)
@requests_mock.mock() @requests_mock.mock()
def test_get_cert_5sum_service_unavailable(self, m): def test_get_cert_5sum_service_unavailable(self, m):
m.get("{base}/listeners/{listener_id}/certificates/{filename}".format( m.get("{base}/listeners/{listener_id}/certificates/{filename}".format(
base=self.base_url, listener_id=self.FAKE_UUID_1, base=self.base_url, listener_id=FAKE_UUID_1,
filename=self.FAKE_PEM_FILENAME), status_code=503) filename=FAKE_PEM_FILENAME), status_code=503)
self.assertRaises(exc.ServiceUnavailable, self.driver.get_cert_md5sum, self.assertRaises(exc.ServiceUnavailable, self.driver.get_cert_md5sum,
self.amp, self.FAKE_UUID_1, self.FAKE_PEM_FILENAME) self.amp, FAKE_UUID_1, FAKE_PEM_FILENAME)
@requests_mock.mock() @requests_mock.mock()
def test_delete_cert_pem(self, m): def test_delete_cert_pem(self, m):
m.delete( m.delete(
"{base}/listeners/{listener_id}/certificates/{filename}".format( "{base}/listeners/{listener_id}/certificates/{filename}".format(
base=self.base_url, listener_id=self.FAKE_UUID_1, base=self.base_url, listener_id=FAKE_UUID_1,
filename=self.FAKE_PEM_FILENAME)) filename=FAKE_PEM_FILENAME))
self.driver.delete_cert_pem(self.amp, self.FAKE_UUID_1, self.driver.delete_cert_pem(self.amp, FAKE_UUID_1,
self.FAKE_PEM_FILENAME) FAKE_PEM_FILENAME)
self.assertTrue(m.called) self.assertTrue(m.called)
@requests_mock.mock() @requests_mock.mock()
def test_delete_cert_pem_missing(self, m): def test_delete_cert_pem_missing(self, m):
m.delete( m.delete(
"{base}/listeners/{listener_id}/certificates/{filename}".format( "{base}/listeners/{listener_id}/certificates/{filename}".format(
base=self.base_url, listener_id=self.FAKE_UUID_1, base=self.base_url, listener_id=FAKE_UUID_1,
filename=self.FAKE_PEM_FILENAME), status_code=404) filename=FAKE_PEM_FILENAME), status_code=404)
self.assertRaises(exc.NotFound, self.driver.delete_cert_pem, self.amp, self.assertRaises(exc.NotFound, self.driver.delete_cert_pem, self.amp,
self.FAKE_UUID_1, self.FAKE_PEM_FILENAME) FAKE_UUID_1, FAKE_PEM_FILENAME)
@requests_mock.mock() @requests_mock.mock()
def test_delete_cert_pem_unauthorized(self, m): def test_delete_cert_pem_unauthorized(self, m):
m.delete( m.delete(
"{base}/listeners/{listener_id}/certificates/{filename}".format( "{base}/listeners/{listener_id}/certificates/{filename}".format(
base=self.base_url, listener_id=self.FAKE_UUID_1, base=self.base_url, listener_id=FAKE_UUID_1,
filename=self.FAKE_PEM_FILENAME), status_code=401) filename=FAKE_PEM_FILENAME), status_code=401)
self.assertRaises(exc.Unauthorized, self.driver.delete_cert_pem, self.assertRaises(exc.Unauthorized, self.driver.delete_cert_pem,
self.amp, self.FAKE_UUID_1, self.FAKE_PEM_FILENAME) self.amp, FAKE_UUID_1, FAKE_PEM_FILENAME)
@requests_mock.mock() @requests_mock.mock()
def test_delete_cert_pem_server_error(self, m): def test_delete_cert_pem_server_error(self, m):
m.delete( m.delete(
"{base}/listeners/{listener_id}/certificates/{filename}".format( "{base}/listeners/{listener_id}/certificates/{filename}".format(
base=self.base_url, listener_id=self.FAKE_UUID_1, base=self.base_url, listener_id=FAKE_UUID_1,
filename=self.FAKE_PEM_FILENAME), status_code=500) filename=FAKE_PEM_FILENAME), status_code=500)
self.assertRaises(exc.InternalServerError, self.driver.delete_cert_pem, self.assertRaises(exc.InternalServerError, self.driver.delete_cert_pem,
self.amp, self.FAKE_UUID_1, self.FAKE_PEM_FILENAME) self.amp, FAKE_UUID_1, FAKE_PEM_FILENAME)
@requests_mock.mock() @requests_mock.mock()
def test_delete_cert_pem_service_unavailable(self, m): def test_delete_cert_pem_service_unavailable(self, m):
m.delete( m.delete(
"{base}/listeners/{listener_id}/certificates/{filename}".format( "{base}/listeners/{listener_id}/certificates/{filename}".format(
base=self.base_url, listener_id=self.FAKE_UUID_1, base=self.base_url, listener_id=FAKE_UUID_1,
filename=self.FAKE_PEM_FILENAME), status_code=503) filename=FAKE_PEM_FILENAME), status_code=503)
self.assertRaises(exc.ServiceUnavailable, self.driver.delete_cert_pem, self.assertRaises(exc.ServiceUnavailable, self.driver.delete_cert_pem,
self.amp, self.FAKE_UUID_1, self.FAKE_PEM_FILENAME) self.amp, FAKE_UUID_1, FAKE_PEM_FILENAME)
@requests_mock.mock() @requests_mock.mock()
def test_upload_config(self, m): def test_upload_config(self, m):
config = {"name": "fake_config"} config = {"name": "fake_config"}
m.put( m.put(
"{base}/listeners/{listener_id}/haproxy".format( "{base}/listeners/{listener_id}/haproxy".format(
base=self.base_url, listener_id=self.FAKE_UUID_1), base=self.base_url, listener_id=FAKE_UUID_1),
json=config) json=config)
self.driver.upload_config(self.amp, self.FAKE_UUID_1, config) self.driver.upload_config(self.amp, FAKE_UUID_1, config)
self.assertTrue(m.called) self.assertTrue(m.called)
@requests_mock.mock() @requests_mock.mock()
@ -549,48 +557,47 @@ class AmphoraAPIClientTest(base.TestCase):
config = '{"name": "bad_config"}' config = '{"name": "bad_config"}'
m.put( m.put(
"{base}/listeners/{listener_id}/haproxy".format( "{base}/listeners/{listener_id}/haproxy".format(
base=self.base_url, listener_id=self.FAKE_UUID_1), base=self.base_url, listener_id=FAKE_UUID_1),
status_code=403) status_code=403)
self.assertRaises(exc.InvalidRequest, self.driver.upload_config, self.assertRaises(exc.InvalidRequest, self.driver.upload_config,
self.amp, self.FAKE_UUID_1, config) self.amp, FAKE_UUID_1, config)
@requests_mock.mock() @requests_mock.mock()
def test_upload_config_unauthorized(self, m): def test_upload_config_unauthorized(self, m):
config = '{"name": "bad_config"}' config = '{"name": "bad_config"}'
m.put( m.put(
"{base}/listeners/{listener_id}/haproxy".format( "{base}/listeners/{listener_id}/haproxy".format(
base=self.base_url, listener_id=self.FAKE_UUID_1), base=self.base_url, listener_id=FAKE_UUID_1),
status_code=401) status_code=401)
self.assertRaises(exc.Unauthorized, self.driver.upload_config, self.assertRaises(exc.Unauthorized, self.driver.upload_config,
self.amp, self.FAKE_UUID_1, config) self.amp, FAKE_UUID_1, config)
@requests_mock.mock() @requests_mock.mock()
def test_upload_config_server_error(self, m): def test_upload_config_server_error(self, m):
config = '{"name": "bad_config"}' config = '{"name": "bad_config"}'
m.put( m.put(
"{base}/listeners/{listener_id}/haproxy".format( "{base}/listeners/{listener_id}/haproxy".format(
base=self.base_url, listener_id=self.FAKE_UUID_1), base=self.base_url, listener_id=FAKE_UUID_1),
status_code=500) status_code=500)
self.assertRaises(exc.InternalServerError, self.driver.upload_config, self.assertRaises(exc.InternalServerError, self.driver.upload_config,
self.amp, self.FAKE_UUID_1, config) self.amp, FAKE_UUID_1, config)
@requests_mock.mock() @requests_mock.mock()
def test_upload_config_service_unavailable(self, m): def test_upload_config_service_unavailable(self, m):
config = '{"name": "bad_config"}' config = '{"name": "bad_config"}'
m.put( m.put(
"{base}/listeners/{listener_id}/haproxy".format( "{base}/listeners/{listener_id}/haproxy".format(
base=self.base_url, listener_id=self.FAKE_UUID_1), base=self.base_url, listener_id=FAKE_UUID_1),
status_code=503) status_code=503)
self.assertRaises(exc.ServiceUnavailable, self.driver.upload_config, self.assertRaises(exc.ServiceUnavailable, self.driver.upload_config,
self.amp, self.FAKE_UUID_1, config) self.amp, FAKE_UUID_1, config)
@requests_mock.mock() @requests_mock.mock()
def test_plug_vip(self, m): def test_plug_vip(self, m):
FAKE_IP = 'fake'
m.post("{base}/plug/vip/{vip}".format( m.post("{base}/plug/vip/{vip}".format(
base=self.base_url, vip=FAKE_IP) base=self.base_url, vip=FAKE_IP)
) )
self.driver.plug_vip(self.amp, FAKE_IP) self.driver.plug_vip(self.amp, FAKE_IP, FAKE_SUBNET_INFO)
self.assertTrue(m.called) self.assertTrue(m.called)
@requests_mock.mock() @requests_mock.mock()

View File

@ -8,6 +8,7 @@ coverage>=3.6
discover discover
fixtures>=1.3.1 fixtures>=1.3.1
mock>=1.2 mock>=1.2
pyroute2>=0.3.10 # Apache-2.0
python-subunit>=0.0.18 python-subunit>=0.0.18
ordereddict ordereddict
oslotest>=1.9.0 # Apache-2.0 oslotest>=1.9.0 # Apache-2.0