From fcc5dcf6e0a84c791cc79cea28eb7279a08c4914 Mon Sep 17 00:00:00 2001 From: Michael Johnson Date: Mon, 3 Aug 2015 16:39:09 +0000 Subject: [PATCH] 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 --- diskimage-create/diskimage-create.sh | 3 + diskimage-create/image-tests.sh | 1 + diskimage-create/requirements.txt | 1 + doc/source/main/api/haproxy-amphora-api.rst | 13 +- elements/pyroute2/README.rst | 3 + elements/pyroute2/install.d/10-pyroute2 | 5 + .../backends/agent/api_server/plug.py | 38 ++- .../backends/agent/api_server/server.py | 15 +- .../drivers/haproxy/rest_api_driver.py | 14 +- .../backend/agent/api_server/test_server.py | 28 ++- .../drivers/haproxy/test_rest_api_driver.py | 225 +++++++++--------- test-requirements.txt | 1 + 12 files changed, 226 insertions(+), 121 deletions(-) create mode 100644 elements/pyroute2/README.rst create mode 100755 elements/pyroute2/install.d/10-pyroute2 diff --git a/diskimage-create/diskimage-create.sh b/diskimage-create/diskimage-create.sh index a526973c6f..4c94e1ee3e 100755 --- a/diskimage-create/diskimage-create.sh +++ b/diskimage-create/diskimage-create.sh @@ -330,6 +330,9 @@ fi # Add the Octavia Amphora agent.py element AMP_element_sequence="$AMP_element_sequence amphora-agent" +# Add pyroute2 element +AMP_element_sequence="$AMP_element_sequence pyroute2" + # Add the vrrp Octacia element AMP_element_sequence="$AMP_element_sequence vrrp-octavia" diff --git a/diskimage-create/image-tests.sh b/diskimage-create/image-tests.sh index 8b672c9551..2889d3199c 100755 --- a/diskimage-create/image-tests.sh +++ b/diskimage-create/image-tests.sh @@ -35,6 +35,7 @@ virt-df -a $AMP_IMAGE_LOCATION | \ grep -q "amphora-x64-haproxy.qcow2:/dev/sda1[ \t]*5015940[ \t]*.*" if [ $? != 0 ]; then 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 else echo "Amphora image size is correct" diff --git a/diskimage-create/requirements.txt b/diskimage-create/requirements.txt index 24ae17be15..7930ee859b 100644 --- a/diskimage-create/requirements.txt +++ b/diskimage-create/requirements.txt @@ -1,3 +1,4 @@ Babel>=1.3 dib-utils PyYAML +six>=1.9.0 diff --git a/doc/source/main/api/haproxy-amphora-api.rst b/doc/source/main/api/haproxy-amphora-api.rst index 0db976248f..2796ba009a 100644 --- a/doc/source/main/api/haproxy-amphora-api.rst +++ b/doc/source/main/api/haproxy-amphora-api.rst @@ -1084,7 +1084,11 @@ Plug VIP * *: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:** * Code: 202 @@ -1095,6 +1099,7 @@ Plug VIP * Code: 400 * Content: Invalid IP + * Content: Invalid subnet information * Code: 404 @@ -1129,6 +1134,12 @@ Plug VIP POST URL: 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: { 'message': 'OK', diff --git a/elements/pyroute2/README.rst b/elements/pyroute2/README.rst new file mode 100644 index 0000000000..39d9947737 --- /dev/null +++ b/elements/pyroute2/README.rst @@ -0,0 +1,3 @@ +This element installs the pyroute2 python library. + +Pyroute2 is a pure Python netlink and Linux network configuration library. diff --git a/elements/pyroute2/install.d/10-pyroute2 b/elements/pyroute2/install.d/10-pyroute2 new file mode 100755 index 0000000000..623fbd8246 --- /dev/null +++ b/elements/pyroute2/install.d/10-pyroute2 @@ -0,0 +1,5 @@ +#!/bin/bash + +set -eux + +pip install -U 'pyroute2>=0.3.10' diff --git a/octavia/amphorae/backends/agent/api_server/plug.py b/octavia/amphorae/backends/agent/api_server/plug.py index bfeab62d52..394010681b 100644 --- a/octavia/amphorae/backends/agent/api_server/plug.py +++ b/octavia/amphorae/backends/agent/api_server/plug.py @@ -20,6 +20,7 @@ import subprocess import flask import jinja2 import netifaces +import pyroute2 from werkzeug import exceptions 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) -def plug_vip(vip): +def plug_vip(vip, subnet_cidr, gateway): # validate vip try: socket.inet_aton(vip) @@ -68,6 +69,41 @@ def plug_vip(vip): _bring_if_up("{interface}".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( message="OK", details="VIP {vip} plugged on interface {interface}".format( diff --git a/octavia/amphorae/backends/agent/api_server/server.py b/octavia/amphorae/backends/agent/api_server/server.py index 1361256301..86621d603f 100644 --- a/octavia/amphorae/backends/agent/api_server/server.py +++ b/octavia/amphorae/backends/agent/api_server/server.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +import json import logging import flask @@ -111,9 +112,19 @@ def delete_certificate(listener_id, filename): @app.route('/' + api_server.VERSION + '/plug/vip/', methods=['POST']) 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']) def plug_network(): - return plug.plug_network() \ No newline at end of file + return plug.plug_network() diff --git a/octavia/amphorae/drivers/haproxy/rest_api_driver.py b/octavia/amphorae/drivers/haproxy/rest_api_driver.py index 580116c668..ccf9c1895c 100644 --- a/octavia/amphorae/drivers/haproxy/rest_api_driver.py +++ b/octavia/amphorae/drivers/haproxy/rest_api_driver.py @@ -15,6 +15,7 @@ import functools import hashlib +import json import time 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): 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): self.client.plug_network(amphora) @@ -296,6 +302,8 @@ class AmphoraAPIClient(object): r = self.post(amp, 'plug/network') return exc.check_exception(r) - def plug_vip(self, amp, vip): - r = self.post(amp, 'plug/vip/{vip}'.format(vip=vip)) + def plug_vip(self, amp, vip, subnet_info): + r = self.post(amp, + 'plug/vip/{vip}'.format(vip=vip), + json=json.dumps(subnet_info)) return exc.check_exception(r) diff --git a/octavia/tests/functional/amphorae/backend/agent/api_server/test_server.py b/octavia/tests/functional/amphorae/backend/agent/api_server/test_server.py index 4e3ae405f8..d9b47af75e 100644 --- a/octavia/tests/functional/amphorae/backend/agent/api_server/test_server.py +++ b/octavia/tests/functional/amphorae/backend/agent/api_server/test_server.py @@ -508,15 +508,27 @@ class ServerTestCase(base.TestCase): @mock.patch('netifaces.interfaces') @mock.patch('netifaces.ifaddresses') @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): + + subnet_info = {'subnet_cidr': '10.0.0.0/24', 'gateway': '10.0.0.1'} + # 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') self.assertEqual(400, rv.status_code) # No interface at all 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(dict(details="No suitable network interface found"), json.loads(rv.data.decode('utf-8'))) @@ -524,7 +536,9 @@ class ServerTestCase(base.TestCase): # Two interfaces down mock_interfaces.side_effect = [['blah', 'blah2']] 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(dict(details="No suitable network interface found"), json.loads(rv.data.decode('utf-8'))) @@ -535,7 +549,9 @@ class ServerTestCase(base.TestCase): m = mock.mock_open() with mock.patch('%s.open' % BUILTINS, m, create=True): 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) m.assert_called_once_with( '/etc/network/interfaces.d/blah.cfg', 'w') @@ -561,7 +577,9 @@ class ServerTestCase(base.TestCase): m = mock.mock_open() with mock.patch('%s.open' % BUILTINS, m, create=True): 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( {'details': RANDOM_ERROR, diff --git a/octavia/tests/unit/amphorae/drivers/haproxy/test_rest_api_driver.py b/octavia/tests/unit/amphorae/drivers/haproxy/test_rest_api_driver.py index 76a28e6000..950f67dfee 100644 --- a/octavia/tests/unit/amphorae/drivers/haproxy/test_rest_api_driver.py +++ b/octavia/tests/unit/amphorae/drivers/haproxy/test_rest_api_driver.py @@ -24,9 +24,16 @@ from octavia.db import models from octavia.tests.unit import base as base 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): - FAKE_UUID_1 = uuidutils.generate_uuid() def setUp(self): super(HaproxyAmphoraLoadBalancerDriverTest, self).setUp() @@ -109,9 +116,12 @@ class HaproxyAmphoraLoadBalancerDriverTest(base.TestCase): pass 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.amp, self.lb.vip.ip_address) + self.amp, self.lb.vip.ip_address, FAKE_SUBNET_INFO) def test_post_network_plug(self): self.driver.post_network_plug(self.amp) @@ -119,8 +129,6 @@ class HaproxyAmphoraLoadBalancerDriverTest(base.TestCase): class AmphoraAPIClientTest(base.TestCase): - FAKE_UUID_1 = uuidutils.generate_uuid() - FAKE_PEM_FILENAME = "file_name" def setUp(self): super(AmphoraAPIClientTest, self).setUp() @@ -131,7 +139,7 @@ class AmphoraAPIClientTest(base.TestCase): @requests_mock.mock() def test_get_info(self, m): 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), json=info) information = self.driver.get_info(self.amp) @@ -166,7 +174,7 @@ class AmphoraAPIClientTest(base.TestCase): @requests_mock.mock() def test_get_details(self, m): 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", "active": True, "haproxy_count": 10} m.get("{base}/details".format(base=self.base_url), @@ -203,7 +211,7 @@ class AmphoraAPIClientTest(base.TestCase): @requests_mock.mock() def test_get_all_listeners(self, m): 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), json=listeners) all_listeners = self.driver.get_all_listeners(self.amp) @@ -240,308 +248,308 @@ class AmphoraAPIClientTest(base.TestCase): @requests_mock.mock() def test_get_listener_status(self, m): 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( - base=self.base_url, listener_id=self.FAKE_UUID_1), + base=self.base_url, listener_id=FAKE_UUID_1), 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) @requests_mock.mock() def test_get_listener_status_unauthorized(self, m): 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) self.assertRaises(exc.Unauthorized, self.driver.get_listener_status, self.amp, - self.FAKE_UUID_1) + FAKE_UUID_1) @requests_mock.mock() def test_get_listener_status_missing(self, m): 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) self.assertRaises(exc.NotFound, self.driver.get_listener_status, self.amp, - self.FAKE_UUID_1) + FAKE_UUID_1) @requests_mock.mock() def test_get_listener_status_server_error(self, m): 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) self.assertRaises(exc.InternalServerError, self.driver.get_listener_status, self.amp, - self.FAKE_UUID_1) + FAKE_UUID_1) @requests_mock.mock() def test_get_listener_status_service_unavailable(self, m): 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) self.assertRaises(exc.ServiceUnavailable, self.driver.get_listener_status, self.amp, - self.FAKE_UUID_1) + FAKE_UUID_1) @requests_mock.mock() def test_start_listener(self, m): m.put("{base}/listeners/{listener_id}/start".format( - base=self.base_url, listener_id=self.FAKE_UUID_1)) - self.driver.start_listener(self.amp, self.FAKE_UUID_1) + base=self.base_url, listener_id=FAKE_UUID_1)) + self.driver.start_listener(self.amp, FAKE_UUID_1) self.assertTrue(m.called) @requests_mock.mock() def test_start_listener_missing(self, m): 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) self.assertRaises(exc.NotFound, self.driver.start_listener, - self.amp, self.FAKE_UUID_1) + self.amp, FAKE_UUID_1) @requests_mock.mock() def test_start_listener_unauthorized(self, m): 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) self.assertRaises(exc.Unauthorized, self.driver.start_listener, - self.amp, self.FAKE_UUID_1) + self.amp, FAKE_UUID_1) @requests_mock.mock() def test_start_listener_server_error(self, m): 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) self.assertRaises(exc.InternalServerError, self.driver.start_listener, - self.amp, self.FAKE_UUID_1) + self.amp, FAKE_UUID_1) @requests_mock.mock() def test_start_listener_service_unavailable(self, m): 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) self.assertRaises(exc.ServiceUnavailable, self.driver.start_listener, - self.amp, self.FAKE_UUID_1) + self.amp, FAKE_UUID_1) @requests_mock.mock() def test_stop_listener(self, m): m.put("{base}/listeners/{listener_id}/stop".format( - base=self.base_url, listener_id=self.FAKE_UUID_1)) - self.driver.stop_listener(self.amp, self.FAKE_UUID_1) + base=self.base_url, listener_id=FAKE_UUID_1)) + self.driver.stop_listener(self.amp, FAKE_UUID_1) self.assertTrue(m.called) @requests_mock.mock() def test_stop_listener_missing(self, m): 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) self.assertRaises(exc.NotFound, self.driver.stop_listener, - self.amp, self.FAKE_UUID_1) + self.amp, FAKE_UUID_1) @requests_mock.mock() def test_stop_listener_unauthorized(self, m): 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) self.assertRaises(exc.Unauthorized, self.driver.stop_listener, - self.amp, self.FAKE_UUID_1) + self.amp, FAKE_UUID_1) @requests_mock.mock() def test_stop_listener_server_error(self, m): 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) self.assertRaises(exc.InternalServerError, self.driver.stop_listener, - self.amp, self.FAKE_UUID_1) + self.amp, FAKE_UUID_1) @requests_mock.mock() def test_stop_listener_service_unavailable(self, m): 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) self.assertRaises(exc.ServiceUnavailable, self.driver.stop_listener, - self.amp, self.FAKE_UUID_1) + self.amp, FAKE_UUID_1) @requests_mock.mock() def test_delete_listener(self, m): m.delete("{base}/listeners/{listener_id}".format( - base=self.base_url, listener_id=self.FAKE_UUID_1), json={}) - self.driver.delete_listener(self.amp, self.FAKE_UUID_1) + base=self.base_url, listener_id=FAKE_UUID_1), json={}) + self.driver.delete_listener(self.amp, FAKE_UUID_1) self.assertTrue(m.called) @requests_mock.mock() def test_delete_listener_missing(self, m): 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) self.assertRaises(exc.NotFound, self.driver.delete_listener, - self.amp, self.FAKE_UUID_1) + self.amp, FAKE_UUID_1) @requests_mock.mock() def test_delete_listener_unauthorized(self, m): 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) self.assertRaises(exc.Unauthorized, self.driver.delete_listener, - self.amp, self.FAKE_UUID_1) + self.amp, FAKE_UUID_1) @requests_mock.mock() def test_delete_listener_server_error(self, m): 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) self.assertRaises(exc.InternalServerError, self.driver.delete_listener, - self.amp, self.FAKE_UUID_1) + self.amp, FAKE_UUID_1) @requests_mock.mock() def test_delete_listener_service_unavailable(self, m): 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) self.assertRaises(exc.ServiceUnavailable, self.driver.delete_listener, - self.amp, self.FAKE_UUID_1) + self.amp, FAKE_UUID_1) @requests_mock.mock() def test_upload_cert_pem(self, m): m.put("{base}/listeners/{listener_id}/certificates/{filename}".format( - base=self.base_url, listener_id=self.FAKE_UUID_1, - filename=self.FAKE_PEM_FILENAME)) - self.driver.upload_cert_pem(self.amp, self.FAKE_UUID_1, - self.FAKE_PEM_FILENAME, + base=self.base_url, listener_id=FAKE_UUID_1, + filename=FAKE_PEM_FILENAME)) + self.driver.upload_cert_pem(self.amp, FAKE_UUID_1, + FAKE_PEM_FILENAME, "some_file") self.assertTrue(m.called) @requests_mock.mock() def test_upload_invalid_cert_pem(self, m): m.put("{base}/listeners/{listener_id}/certificates/{filename}".format( - base=self.base_url, listener_id=self.FAKE_UUID_1, - filename=self.FAKE_PEM_FILENAME), status_code=403) + base=self.base_url, listener_id=FAKE_UUID_1, + filename=FAKE_PEM_FILENAME), status_code=403) 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") @requests_mock.mock() def test_upload_cert_pem_unauthorized(self, m): m.put("{base}/listeners/{listener_id}/certificates/{filename}".format( - base=self.base_url, listener_id=self.FAKE_UUID_1, - filename=self.FAKE_PEM_FILENAME), status_code=401) + base=self.base_url, listener_id=FAKE_UUID_1, + filename=FAKE_PEM_FILENAME), status_code=401) 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") @requests_mock.mock() def test_upload_cert_pem_server_error(self, m): m.put("{base}/listeners/{listener_id}/certificates/{filename}".format( - base=self.base_url, listener_id=self.FAKE_UUID_1, - filename=self.FAKE_PEM_FILENAME), status_code=500) + base=self.base_url, listener_id=FAKE_UUID_1, + filename=FAKE_PEM_FILENAME), status_code=500) 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") @requests_mock.mock() def test_upload_cert_pem_service_unavailable(self, m): m.put("{base}/listeners/{listener_id}/certificates/{filename}".format( - base=self.base_url, listener_id=self.FAKE_UUID_1, - filename=self.FAKE_PEM_FILENAME), status_code=503) + base=self.base_url, listener_id=FAKE_UUID_1, + filename=FAKE_PEM_FILENAME), status_code=503) 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") @requests_mock.mock() def test_get_cert_5sum(self, m): md5sum = {"md5sum": "some_real_sum"} m.get("{base}/listeners/{listener_id}/certificates/{filename}".format( - base=self.base_url, listener_id=self.FAKE_UUID_1, - filename=self.FAKE_PEM_FILENAME), json=md5sum) - sum_test = self.driver.get_cert_md5sum(self.amp, self.FAKE_UUID_1, - self.FAKE_PEM_FILENAME) + base=self.base_url, listener_id=FAKE_UUID_1, + filename=FAKE_PEM_FILENAME), json=md5sum) + sum_test = self.driver.get_cert_md5sum(self.amp, FAKE_UUID_1, + FAKE_PEM_FILENAME) self.assertIsNotNone(sum_test) @requests_mock.mock() def test_get_cert_5sum_missing(self, m): m.get("{base}/listeners/{listener_id}/certificates/{filename}".format( - base=self.base_url, listener_id=self.FAKE_UUID_1, - filename=self.FAKE_PEM_FILENAME), status_code=404) + base=self.base_url, listener_id=FAKE_UUID_1, + filename=FAKE_PEM_FILENAME), status_code=404) 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() def test_get_cert_5sum_unauthorized(self, m): m.get("{base}/listeners/{listener_id}/certificates/{filename}".format( - base=self.base_url, listener_id=self.FAKE_UUID_1, - filename=self.FAKE_PEM_FILENAME), status_code=401) + base=self.base_url, listener_id=FAKE_UUID_1, + filename=FAKE_PEM_FILENAME), status_code=401) 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() def test_get_cert_5sum_server_error(self, m): m.get("{base}/listeners/{listener_id}/certificates/{filename}".format( - base=self.base_url, listener_id=self.FAKE_UUID_1, - filename=self.FAKE_PEM_FILENAME), status_code=500) + base=self.base_url, listener_id=FAKE_UUID_1, + filename=FAKE_PEM_FILENAME), status_code=500) 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() def test_get_cert_5sum_service_unavailable(self, m): m.get("{base}/listeners/{listener_id}/certificates/{filename}".format( - base=self.base_url, listener_id=self.FAKE_UUID_1, - filename=self.FAKE_PEM_FILENAME), status_code=503) + base=self.base_url, listener_id=FAKE_UUID_1, + filename=FAKE_PEM_FILENAME), status_code=503) 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() def test_delete_cert_pem(self, m): m.delete( "{base}/listeners/{listener_id}/certificates/{filename}".format( - base=self.base_url, listener_id=self.FAKE_UUID_1, - filename=self.FAKE_PEM_FILENAME)) - self.driver.delete_cert_pem(self.amp, self.FAKE_UUID_1, - self.FAKE_PEM_FILENAME) + base=self.base_url, listener_id=FAKE_UUID_1, + filename=FAKE_PEM_FILENAME)) + self.driver.delete_cert_pem(self.amp, FAKE_UUID_1, + FAKE_PEM_FILENAME) self.assertTrue(m.called) @requests_mock.mock() def test_delete_cert_pem_missing(self, m): m.delete( "{base}/listeners/{listener_id}/certificates/{filename}".format( - base=self.base_url, listener_id=self.FAKE_UUID_1, - filename=self.FAKE_PEM_FILENAME), status_code=404) + base=self.base_url, listener_id=FAKE_UUID_1, + filename=FAKE_PEM_FILENAME), status_code=404) 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() def test_delete_cert_pem_unauthorized(self, m): m.delete( "{base}/listeners/{listener_id}/certificates/{filename}".format( - base=self.base_url, listener_id=self.FAKE_UUID_1, - filename=self.FAKE_PEM_FILENAME), status_code=401) + base=self.base_url, listener_id=FAKE_UUID_1, + filename=FAKE_PEM_FILENAME), status_code=401) 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() def test_delete_cert_pem_server_error(self, m): m.delete( "{base}/listeners/{listener_id}/certificates/{filename}".format( - base=self.base_url, listener_id=self.FAKE_UUID_1, - filename=self.FAKE_PEM_FILENAME), status_code=500) + base=self.base_url, listener_id=FAKE_UUID_1, + filename=FAKE_PEM_FILENAME), status_code=500) 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() def test_delete_cert_pem_service_unavailable(self, m): m.delete( "{base}/listeners/{listener_id}/certificates/{filename}".format( - base=self.base_url, listener_id=self.FAKE_UUID_1, - filename=self.FAKE_PEM_FILENAME), status_code=503) + base=self.base_url, listener_id=FAKE_UUID_1, + filename=FAKE_PEM_FILENAME), status_code=503) 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() def test_upload_config(self, m): config = {"name": "fake_config"} m.put( "{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) - 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) @requests_mock.mock() @@ -549,48 +557,47 @@ class AmphoraAPIClientTest(base.TestCase): config = '{"name": "bad_config"}' m.put( "{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) self.assertRaises(exc.InvalidRequest, self.driver.upload_config, - self.amp, self.FAKE_UUID_1, config) + self.amp, FAKE_UUID_1, config) @requests_mock.mock() def test_upload_config_unauthorized(self, m): config = '{"name": "bad_config"}' m.put( "{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) self.assertRaises(exc.Unauthorized, self.driver.upload_config, - self.amp, self.FAKE_UUID_1, config) + self.amp, FAKE_UUID_1, config) @requests_mock.mock() def test_upload_config_server_error(self, m): config = '{"name": "bad_config"}' m.put( "{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) self.assertRaises(exc.InternalServerError, self.driver.upload_config, - self.amp, self.FAKE_UUID_1, config) + self.amp, FAKE_UUID_1, config) @requests_mock.mock() def test_upload_config_service_unavailable(self, m): config = '{"name": "bad_config"}' m.put( "{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) self.assertRaises(exc.ServiceUnavailable, self.driver.upload_config, - self.amp, self.FAKE_UUID_1, config) + self.amp, FAKE_UUID_1, config) @requests_mock.mock() def test_plug_vip(self, m): - FAKE_IP = 'fake' m.post("{base}/plug/vip/{vip}".format( 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) @requests_mock.mock() diff --git a/test-requirements.txt b/test-requirements.txt index 987723fd35..838c77add2 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -8,6 +8,7 @@ coverage>=3.6 discover fixtures>=1.3.1 mock>=1.2 +pyroute2>=0.3.10 # Apache-2.0 python-subunit>=0.0.18 ordereddict oslotest>=1.9.0 # Apache-2.0