create a Quantum port to reserve VIP address
fixes bug 1129672 The API previously allowed a VIP to be created without verifying that the tenant had access to the subnet and that the address was valid and available. This change modifies VIP creation behavior to create a Quantum port with the requested address. If an address is not provided, an address is allocated using the normal allocation process for the subnet. This change also renames the port attribute to protocol_port to remove the ambiguity about which type of port it represents. Additional tests were added to validate the change in behavior. Change-Id: Ib19ef653887da568364b4faa0d2c0fac30970b5f
This commit is contained in:
parent
3b2416b1f2
commit
8acc578147
@ -29,6 +29,7 @@ from quantum.db import model_base
|
||||
from quantum.db import models_v2
|
||||
from quantum.extensions import loadbalancer
|
||||
from quantum.extensions.loadbalancer import LoadBalancerPluginBase
|
||||
from quantum import manager
|
||||
from quantum.openstack.common import log as logging
|
||||
from quantum.openstack.common import uuidutils
|
||||
from quantum.plugins.common import constants
|
||||
@ -64,9 +65,8 @@ class Vip(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
|
||||
"""Represents a v2 quantum loadbalancer vip."""
|
||||
name = sa.Column(sa.String(255))
|
||||
description = sa.Column(sa.String(255))
|
||||
subnet_id = sa.Column(sa.String(36), nullable=False)
|
||||
address = sa.Column(sa.String(64))
|
||||
port = sa.Column(sa.Integer, nullable=False)
|
||||
port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id'))
|
||||
protocol_port = sa.Column(sa.Integer, nullable=False)
|
||||
protocol = sa.Column(sa.Enum("HTTP", "HTTPS", "TCP", name="lb_protocols"),
|
||||
nullable=False)
|
||||
pool_id = sa.Column(sa.String(36), nullable=False)
|
||||
@ -77,6 +77,7 @@ class Vip(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
|
||||
status = sa.Column(sa.String(16), nullable=False)
|
||||
admin_state_up = sa.Column(sa.Boolean(), nullable=False)
|
||||
connection_limit = sa.Column(sa.Integer)
|
||||
port = orm.relationship(models_v2.Port)
|
||||
|
||||
|
||||
class Member(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
|
||||
@ -84,7 +85,7 @@ class Member(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
|
||||
pool_id = sa.Column(sa.String(36), sa.ForeignKey("pools.id"),
|
||||
nullable=False)
|
||||
address = sa.Column(sa.String(64), nullable=False)
|
||||
port = sa.Column(sa.Integer, nullable=False)
|
||||
protocol_port = sa.Column(sa.Integer, nullable=False)
|
||||
weight = sa.Column(sa.Integer, nullable=False)
|
||||
status = sa.Column(sa.String(16), nullable=False)
|
||||
admin_state_up = sa.Column(sa.Boolean(), nullable=False)
|
||||
@ -151,6 +152,10 @@ class LoadBalancerPluginDb(LoadBalancerPluginBase):
|
||||
loadbalancer plugin database access interface using SQLAlchemy models.
|
||||
"""
|
||||
|
||||
@property
|
||||
def _core_plugin(self):
|
||||
return manager.QuantumManager.get_plugin()
|
||||
|
||||
# TODO(lcui):
|
||||
# A set of internal facility methods are borrowed from QuantumDbPluginV2
|
||||
# class and hence this is duplicate. We need to pull out those methods
|
||||
@ -237,18 +242,22 @@ class LoadBalancerPluginDb(LoadBalancerPluginBase):
|
||||
########################################################
|
||||
# VIP DB access
|
||||
def _make_vip_dict(self, vip, fields=None):
|
||||
fixed_ip = (vip.port.fixed_ips or [{}])[0]
|
||||
|
||||
res = {'id': vip['id'],
|
||||
'tenant_id': vip['tenant_id'],
|
||||
'name': vip['name'],
|
||||
'description': vip['description'],
|
||||
'subnet_id': vip['subnet_id'],
|
||||
'address': vip['address'],
|
||||
'port': vip['port'],
|
||||
'subnet_id': fixed_ip.get('subnet_id'),
|
||||
'address': fixed_ip.get('ip_address'),
|
||||
'port_id': vip['port_id'],
|
||||
'protocol_port': vip['protocol_port'],
|
||||
'protocol': vip['protocol'],
|
||||
'pool_id': vip['pool_id'],
|
||||
'connection_limit': vip['connection_limit'],
|
||||
'admin_state_up': vip['admin_state_up'],
|
||||
'status': vip['status']}
|
||||
|
||||
if vip['session_persistence']:
|
||||
s_p = {
|
||||
'type': vip['session_persistence']['type']
|
||||
@ -320,22 +329,38 @@ class LoadBalancerPluginDb(LoadBalancerPluginBase):
|
||||
sess_qry = context.session.query(SessionPersistence)
|
||||
sess_qry.filter_by(vip_id=vip_id).delete()
|
||||
|
||||
def _create_port_for_vip(self, context, vip_db, subnet_id, ip_address):
|
||||
# resolve subnet and create port
|
||||
subnet = self._core_plugin.get_subnet(context, subnet_id)
|
||||
fixed_ip = {'subnet_id': subnet['id']}
|
||||
if ip_address and ip_address != attributes.ATTR_NOT_SPECIFIED:
|
||||
fixed_ip['ip_address'] = ip_address
|
||||
|
||||
port_data = {
|
||||
'tenant_id': vip_db.tenant_id,
|
||||
'name': 'vip-' + vip_db.id,
|
||||
'network_id': subnet['network_id'],
|
||||
'mac_address': attributes.ATTR_NOT_SPECIFIED,
|
||||
'admin_state_up': False,
|
||||
'device_id': '',
|
||||
'device_owner': '',
|
||||
'fixed_ips': [fixed_ip]
|
||||
}
|
||||
|
||||
port = self._core_plugin.create_port(context, {'port': port_data})
|
||||
vip_db.port_id = port['id']
|
||||
|
||||
def create_vip(self, context, vip):
|
||||
v = vip['vip']
|
||||
tenant_id = self._get_tenant_id_for_create(context, v)
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
if v['address'] is attributes.ATTR_NOT_SPECIFIED:
|
||||
address = None
|
||||
else:
|
||||
address = v['address']
|
||||
vip_db = Vip(id=uuidutils.generate_uuid(),
|
||||
tenant_id=tenant_id,
|
||||
name=v['name'],
|
||||
description=v['description'],
|
||||
subnet_id=v['subnet_id'],
|
||||
address=address,
|
||||
port=v['port'],
|
||||
port_id=None,
|
||||
protocol_port=v['protocol_port'],
|
||||
protocol=v['protocol'],
|
||||
pool_id=v['pool_id'],
|
||||
connection_limit=v['connection_limit'],
|
||||
@ -350,9 +375,16 @@ class LoadBalancerPluginDb(LoadBalancerPluginBase):
|
||||
vip_db.session_persistence = s_p
|
||||
|
||||
context.session.add(vip_db)
|
||||
self._update_pool_vip_info(context, v['pool_id'], vip_id)
|
||||
context.session.flush()
|
||||
|
||||
vip_db = self._get_resource(context, Vip, vip_id)
|
||||
self._create_port_for_vip(
|
||||
context,
|
||||
vip_db,
|
||||
v['subnet_id'],
|
||||
v.get('address')
|
||||
)
|
||||
|
||||
self._update_pool_vip_info(context, v['pool_id'], vip_id)
|
||||
return self._make_vip_dict(vip_db)
|
||||
|
||||
def update_vip(self, context, id, vip):
|
||||
@ -383,7 +415,11 @@ class LoadBalancerPluginDb(LoadBalancerPluginBase):
|
||||
qry = context.session.query(Pool)
|
||||
for pool in qry.filter_by(vip_id=id).all():
|
||||
pool.update({"vip_id": None})
|
||||
|
||||
context.session.delete(vip)
|
||||
if vip.port: # this is a Quantum port
|
||||
self._core_plugin.delete_port(context, vip.port.id)
|
||||
context.session.flush()
|
||||
|
||||
def get_vip(self, context, id, fields=None):
|
||||
vip = self._get_resource(context, Vip, id)
|
||||
@ -574,7 +610,7 @@ class LoadBalancerPluginDb(LoadBalancerPluginBase):
|
||||
'tenant_id': member['tenant_id'],
|
||||
'pool_id': member['pool_id'],
|
||||
'address': member['address'],
|
||||
'port': member['port'],
|
||||
'protocol_port': member['protocol_port'],
|
||||
'weight': member['weight'],
|
||||
'admin_state_up': member['admin_state_up'],
|
||||
'status': member['status']}
|
||||
@ -596,7 +632,7 @@ class LoadBalancerPluginDb(LoadBalancerPluginBase):
|
||||
tenant_id=tenant_id,
|
||||
pool_id=v['pool_id'],
|
||||
address=v['address'],
|
||||
port=v['port'],
|
||||
protocol_port=v['protocol_port'],
|
||||
weight=v['weight'],
|
||||
admin_state_up=v['admin_state_up'],
|
||||
status=constants.PENDING_CREATE)
|
||||
|
@ -48,9 +48,8 @@ def upgrade(active_plugin=None, options=None):
|
||||
sa.Column(u'id', sa.String(36), nullable=False),
|
||||
sa.Column(u'name', sa.String(255), nullable=True),
|
||||
sa.Column(u'description', sa.String(255), nullable=True),
|
||||
sa.Column(u'subnet_id', sa.String(36), nullable=False),
|
||||
sa.Column(u'address', sa.String(64), nullable=True),
|
||||
sa.Column(u'port', sa.Integer(), nullable=False),
|
||||
sa.Column(u'port_id', sa.String(36), nullable=True),
|
||||
sa.Column(u'protocol_port', sa.Integer(), nullable=False),
|
||||
sa.Column(u'protocol',
|
||||
sa.Enum("HTTP", "HTTPS", "TCP", name="lb_protocols"),
|
||||
nullable=False),
|
||||
@ -58,6 +57,7 @@ def upgrade(active_plugin=None, options=None):
|
||||
sa.Column(u'status', sa.String(16), nullable=False),
|
||||
sa.Column(u'admin_state_up', sa.Boolean(), nullable=False),
|
||||
sa.Column(u'connection_limit', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['port_id'], ['ports.id'], ),
|
||||
sa.PrimaryKeyConstraint(u'id')
|
||||
)
|
||||
op.create_table(
|
||||
@ -130,7 +130,7 @@ def upgrade(active_plugin=None, options=None):
|
||||
sa.Column(u'id', sa.String(36), nullable=False),
|
||||
sa.Column(u'pool_id', sa.String(36), nullable=False),
|
||||
sa.Column(u'address', sa.String(64), nullable=False),
|
||||
sa.Column(u'port', sa.Integer(), nullable=False),
|
||||
sa.Column(u'protocol_port', sa.Integer(), nullable=False),
|
||||
sa.Column(u'weight', sa.Integer(), nullable=False),
|
||||
sa.Column(u'status', sa.String(16), nullable=False),
|
||||
sa.Column(u'admin_state_up', sa.Boolean(), nullable=False),
|
||||
|
@ -79,10 +79,13 @@ RESOURCE_ATTRIBUTE_MAP = {
|
||||
'default': attr.ATTR_NOT_SPECIFIED,
|
||||
'validate': {'type:ip_address_or_none': None},
|
||||
'is_visible': True},
|
||||
'port': {'allow_post': True, 'allow_put': False,
|
||||
'validate': {'type:range': [0, 65535]},
|
||||
'convert_to': attr.convert_to_int,
|
||||
'is_visible': True},
|
||||
'port_id': {'allow_post': False, 'allow_put': False,
|
||||
'validate': {'type:uuid': None},
|
||||
'is_visible': True},
|
||||
'protocol_port': {'allow_post': True, 'allow_put': False,
|
||||
'validate': {'type:range': [0, 65535]},
|
||||
'convert_to': attr.convert_to_int,
|
||||
'is_visible': True},
|
||||
'protocol': {'allow_post': True, 'allow_put': False,
|
||||
'validate': {'type:values': ['TCP', 'HTTP', 'HTTPS']},
|
||||
'is_visible': True},
|
||||
@ -167,10 +170,10 @@ RESOURCE_ATTRIBUTE_MAP = {
|
||||
'address': {'allow_post': True, 'allow_put': False,
|
||||
'validate': {'type:ip_address': None},
|
||||
'is_visible': True},
|
||||
'port': {'allow_post': True, 'allow_put': False,
|
||||
'validate': {'type:range': [0, 65535]},
|
||||
'convert_to': attr.convert_to_int,
|
||||
'is_visible': True},
|
||||
'protocol_port': {'allow_post': True, 'allow_put': False,
|
||||
'validate': {'type:range': [0, 65535]},
|
||||
'convert_to': attr.convert_to_int,
|
||||
'is_visible': True},
|
||||
'weight': {'allow_post': True, 'allow_put': True,
|
||||
'default': 1,
|
||||
'validate': {'type:range': [0, 256]},
|
||||
|
@ -15,7 +15,9 @@
|
||||
|
||||
import contextlib
|
||||
import logging
|
||||
import mock
|
||||
import os
|
||||
import testtools
|
||||
|
||||
from oslo.config import cfg
|
||||
import webob.exc
|
||||
@ -25,6 +27,7 @@ from quantum.api.extensions import PluginAwareExtensionManager
|
||||
from quantum.api.v2 import attributes
|
||||
from quantum.api.v2.router import APIRouter
|
||||
from quantum.common import config
|
||||
from quantum.common import exceptions as q_exc
|
||||
from quantum.common.test_lib import test_config
|
||||
from quantum.db import api as db
|
||||
import quantum.extensions
|
||||
@ -79,16 +82,15 @@ class LoadBalancerPluginDbTestCase(test_db_plugin.QuantumDbPluginV2TestCase):
|
||||
app = config.load_paste_app('extensions_test_app')
|
||||
self.ext_api = ExtensionMiddleware(app, ext_mgr=ext_mgr)
|
||||
|
||||
def _create_vip(self, fmt, name, pool_id, protocol, port, admin_state_up,
|
||||
expected_res_status=None, **kwargs):
|
||||
def _create_vip(self, fmt, name, pool_id, protocol, protocol_port,
|
||||
admin_state_up, expected_res_status=None, **kwargs):
|
||||
data = {'vip': {'name': name,
|
||||
'subnet_id': self._subnet_id,
|
||||
'pool_id': pool_id,
|
||||
'protocol': protocol,
|
||||
'port': port,
|
||||
'protocol_port': protocol_port,
|
||||
'admin_state_up': admin_state_up,
|
||||
'tenant_id': self._tenant_id}}
|
||||
for arg in ('description', 'address',
|
||||
for arg in ('description', 'subnet_id', 'address',
|
||||
'session_persistence', 'connection_limit'):
|
||||
if arg in kwargs and kwargs[arg] is not None:
|
||||
data['vip'][arg] = kwargs[arg]
|
||||
@ -119,10 +121,10 @@ class LoadBalancerPluginDbTestCase(test_db_plugin.QuantumDbPluginV2TestCase):
|
||||
|
||||
return pool_res
|
||||
|
||||
def _create_member(self, fmt, address, port, admin_state_up,
|
||||
def _create_member(self, fmt, address, protocol_port, admin_state_up,
|
||||
expected_res_status=None, **kwargs):
|
||||
data = {'member': {'address': address,
|
||||
'port': port,
|
||||
'protocol_port': protocol_port,
|
||||
'admin_state_up': admin_state_up,
|
||||
'tenant_id': self._tenant_id}}
|
||||
for arg in ('weight', 'pool_id'):
|
||||
@ -164,44 +166,31 @@ class LoadBalancerPluginDbTestCase(test_db_plugin.QuantumDbPluginV2TestCase):
|
||||
return self.ext_api
|
||||
|
||||
@contextlib.contextmanager
|
||||
def vip(self, fmt=None, name='vip1', pool=None,
|
||||
protocol='HTTP', port=80, admin_state_up=True, no_delete=False,
|
||||
address="172.16.1.123", **kwargs):
|
||||
def vip(self, fmt=None, name='vip1', pool=None, subnet=None,
|
||||
protocol='HTTP', protocol_port=80, admin_state_up=True,
|
||||
no_delete=False, **kwargs):
|
||||
if not fmt:
|
||||
fmt = self.fmt
|
||||
if not pool:
|
||||
with self.pool() as pool:
|
||||
pool_id = pool['pool']['id']
|
||||
|
||||
with test_db_plugin.optional_ctx(subnet, self.subnet) as tmp_subnet:
|
||||
with test_db_plugin.optional_ctx(pool, self.pool) as tmp_pool:
|
||||
pool_id = tmp_pool['pool']['id']
|
||||
res = self._create_vip(fmt,
|
||||
name,
|
||||
pool_id,
|
||||
protocol,
|
||||
port,
|
||||
protocol_port,
|
||||
admin_state_up,
|
||||
address=address,
|
||||
subnet_id=tmp_subnet['subnet']['id'],
|
||||
**kwargs)
|
||||
vip = self.deserialize(fmt or self.fmt, res)
|
||||
if res.status_int >= 400:
|
||||
raise webob.exc.HTTPClientError(code=res.status_int)
|
||||
yield vip
|
||||
if not no_delete:
|
||||
self._delete('vips', vip['vip']['id'])
|
||||
else:
|
||||
pool_id = pool['pool']['id']
|
||||
res = self._create_vip(fmt,
|
||||
name,
|
||||
pool_id,
|
||||
protocol,
|
||||
port,
|
||||
admin_state_up,
|
||||
address=address,
|
||||
**kwargs)
|
||||
vip = self.deserialize(fmt or self.fmt, res)
|
||||
if res.status_int >= 400:
|
||||
raise webob.exc.HTTPClientError(code=res.status_int)
|
||||
yield vip
|
||||
if not no_delete:
|
||||
self._delete('vips', vip['vip']['id'])
|
||||
try:
|
||||
yield vip
|
||||
finally:
|
||||
if not no_delete:
|
||||
self._delete('vips', vip['vip']['id'])
|
||||
|
||||
@contextlib.contextmanager
|
||||
def pool(self, fmt=None, name='pool1', lb_method='ROUND_ROBIN',
|
||||
@ -218,27 +207,30 @@ class LoadBalancerPluginDbTestCase(test_db_plugin.QuantumDbPluginV2TestCase):
|
||||
pool = self.deserialize(fmt or self.fmt, res)
|
||||
if res.status_int >= 400:
|
||||
raise webob.exc.HTTPClientError(code=res.status_int)
|
||||
yield pool
|
||||
if not no_delete:
|
||||
self._delete('pools', pool['pool']['id'])
|
||||
try:
|
||||
yield pool
|
||||
finally:
|
||||
if not no_delete:
|
||||
self._delete('pools', pool['pool']['id'])
|
||||
|
||||
@contextlib.contextmanager
|
||||
def member(self, fmt=None, address='192.168.1.100',
|
||||
port=80, admin_state_up=True, no_delete=False,
|
||||
**kwargs):
|
||||
def member(self, fmt=None, address='192.168.1.100', protocol_port=80,
|
||||
admin_state_up=True, no_delete=False, **kwargs):
|
||||
if not fmt:
|
||||
fmt = self.fmt
|
||||
res = self._create_member(fmt,
|
||||
address,
|
||||
port,
|
||||
protocol_port,
|
||||
admin_state_up,
|
||||
**kwargs)
|
||||
member = self.deserialize(fmt or self.fmt, res)
|
||||
if res.status_int >= 400:
|
||||
raise webob.exc.HTTPClientError(code=res.status_int)
|
||||
yield member
|
||||
if not no_delete:
|
||||
self._delete('members', member['member']['id'])
|
||||
try:
|
||||
yield member
|
||||
finally:
|
||||
if not no_delete:
|
||||
self._delete('members', member['member']['id'])
|
||||
|
||||
@contextlib.contextmanager
|
||||
def health_monitor(self, fmt=None, type='TCP',
|
||||
@ -270,99 +262,84 @@ class LoadBalancerPluginDbTestCase(test_db_plugin.QuantumDbPluginV2TestCase):
|
||||
else:
|
||||
for arg in http_related_attributes:
|
||||
self.assertIsNone(the_health_monitor.get(arg))
|
||||
yield health_monitor
|
||||
if not no_delete:
|
||||
self._delete('health_monitors', the_health_monitor['id'])
|
||||
try:
|
||||
yield health_monitor
|
||||
finally:
|
||||
if not no_delete:
|
||||
self._delete('health_monitors', the_health_monitor['id'])
|
||||
|
||||
|
||||
class TestLoadBalancer(LoadBalancerPluginDbTestCase):
|
||||
def test_create_vip(self):
|
||||
name = 'vip1'
|
||||
keys = [('name', name),
|
||||
('subnet_id', self._subnet_id),
|
||||
('address', "172.16.1.123"),
|
||||
('port', 80),
|
||||
('protocol', 'HTTP'),
|
||||
('connection_limit', -1),
|
||||
('admin_state_up', True),
|
||||
('status', 'PENDING_CREATE')]
|
||||
def test_create_vip(self, **extras):
|
||||
expected = {
|
||||
'name': 'vip1',
|
||||
'description': '',
|
||||
'protocol_port': 80,
|
||||
'protocol': 'HTTP',
|
||||
'connection_limit': -1,
|
||||
'admin_state_up': True,
|
||||
'status': 'PENDING_CREATE',
|
||||
'tenant_id': self._tenant_id,
|
||||
}
|
||||
|
||||
with self.vip(name=name) as vip:
|
||||
for k, v in keys:
|
||||
self.assertEqual(vip['vip'][k], v)
|
||||
expected.update(extras)
|
||||
|
||||
with self.subnet() as subnet:
|
||||
expected['subnet_id'] = subnet['subnet']['id']
|
||||
name = expected['name']
|
||||
|
||||
with self.vip(name=name, subnet=subnet, **extras) as vip:
|
||||
for k in ('id', 'address', 'port_id', 'pool_id'):
|
||||
self.assertTrue(vip['vip'].get(k, None))
|
||||
|
||||
self.assertEqual(
|
||||
dict((k, v)
|
||||
for k, v in vip['vip'].items() if k in expected),
|
||||
expected
|
||||
)
|
||||
return vip
|
||||
|
||||
def test_create_vip_with_invalid_values(self):
|
||||
name = 'vip3'
|
||||
invalid = {
|
||||
'protocol': 'UNSUPPORTED',
|
||||
'protocol_port': 'NOT_AN_INT',
|
||||
'protocol_port': 1000500,
|
||||
'subnet': {'subnet': {'id': 'invalid-subnet'}}
|
||||
}
|
||||
|
||||
vip = self.vip(name=name, protocol='UNSUPPORTED')
|
||||
self.assertRaises(webob.exc.HTTPClientError, vip.__enter__)
|
||||
for param, value in invalid.items():
|
||||
kwargs = {'name': 'the-vip', param: value}
|
||||
with testtools.ExpectedException(webob.exc.HTTPClientError):
|
||||
with self.vip(**kwargs):
|
||||
pass
|
||||
|
||||
vip = self.vip(name=name, port='NOT_AN_INT')
|
||||
self.assertRaises(webob.exc.HTTPClientError, vip.__enter__)
|
||||
def test_create_vip_with_address(self):
|
||||
self.test_create_vip(address='10.0.0.7')
|
||||
|
||||
# 100500 is not a valid port number
|
||||
vip = self.vip(name=name, port='100500')
|
||||
self.assertRaises(webob.exc.HTTPClientError, vip.__enter__)
|
||||
|
||||
# 192.168.130.130.130 is not a valid IP address
|
||||
vip = self.vip(name=name, address='192.168.130.130.130')
|
||||
self.assertRaises(webob.exc.HTTPClientError, vip.__enter__)
|
||||
def test_create_vip_with_address_outside_subnet(self):
|
||||
with testtools.ExpectedException(webob.exc.HTTPClientError):
|
||||
self.test_create_vip(address='9.9.9.9')
|
||||
|
||||
def test_create_vip_with_session_persistence(self):
|
||||
name = 'vip2'
|
||||
keys = [('name', name),
|
||||
('subnet_id', self._subnet_id),
|
||||
('address', "172.16.1.123"),
|
||||
('port', 80),
|
||||
('protocol', 'HTTP'),
|
||||
('session_persistence', {'type': "HTTP_COOKIE"}),
|
||||
('connection_limit', -1),
|
||||
('admin_state_up', True),
|
||||
('status', 'PENDING_CREATE')]
|
||||
|
||||
with self.vip(name=name,
|
||||
session_persistence={'type': "HTTP_COOKIE"}) as vip:
|
||||
for k, v in keys:
|
||||
self.assertEqual(vip['vip'][k], v)
|
||||
self.test_create_vip(session_persistence={'type': 'HTTP_COOKIE'})
|
||||
|
||||
def test_create_vip_with_session_persistence_with_app_cookie(self):
|
||||
name = 'vip7'
|
||||
keys = [('name', name),
|
||||
('subnet_id', self._subnet_id),
|
||||
('address', "172.16.1.123"),
|
||||
('port', 80),
|
||||
('protocol', 'HTTP'),
|
||||
('session_persistence', {'type': "APP_COOKIE",
|
||||
'cookie_name': 'sessionId'}),
|
||||
('connection_limit', -1),
|
||||
('admin_state_up', True),
|
||||
('status', 'PENDING_CREATE')]
|
||||
|
||||
with self.vip(name=name,
|
||||
session_persistence={'type': "APP_COOKIE",
|
||||
'cookie_name': 'sessionId'}) as vip:
|
||||
for k, v in keys:
|
||||
self.assertEqual(vip['vip'][k], v)
|
||||
sp = {'type': 'APP_COOKIE', 'cookie_name': 'sessionId'}
|
||||
self.test_create_vip(session_persistence=sp)
|
||||
|
||||
def test_create_vip_with_session_persistence_unsupported_type(self):
|
||||
name = 'vip5'
|
||||
|
||||
vip = self.vip(name=name, session_persistence={'type': "UNSUPPORTED"})
|
||||
self.assertRaises(webob.exc.HTTPClientError, vip.__enter__)
|
||||
with testtools.ExpectedException(webob.exc.HTTPClientError):
|
||||
self.test_create_vip(session_persistence={'type': 'UNSUPPORTED'})
|
||||
|
||||
def test_create_vip_with_unnecessary_cookie_name(self):
|
||||
name = 'vip8'
|
||||
|
||||
s_p = {'type': "SOURCE_IP", 'cookie_name': 'sessionId'}
|
||||
vip = self.vip(name=name, session_persistence=s_p)
|
||||
|
||||
self.assertRaises(webob.exc.HTTPClientError, vip.__enter__)
|
||||
sp = {'type': "SOURCE_IP", 'cookie_name': 'sessionId'}
|
||||
with testtools.ExpectedException(webob.exc.HTTPClientError):
|
||||
self.test_create_vip(session_persistence=sp)
|
||||
|
||||
def test_create_vip_with_session_persistence_without_cookie_name(self):
|
||||
name = 'vip6'
|
||||
|
||||
vip = self.vip(name=name, session_persistence={'type': "APP_COOKIE"})
|
||||
self.assertRaises(webob.exc.HTTPClientError, vip.__enter__)
|
||||
sp = {'type': "APP_COOKIE"}
|
||||
with testtools.ExpectedException(webob.exc.HTTPClientError):
|
||||
self.test_create_vip(session_persistence=sp)
|
||||
|
||||
def test_reset_session_persistence(self):
|
||||
name = 'vip4'
|
||||
@ -386,14 +363,14 @@ class TestLoadBalancer(LoadBalancerPluginDbTestCase):
|
||||
def test_update_vip(self):
|
||||
name = 'new_vip'
|
||||
keys = [('name', name),
|
||||
('subnet_id', self._subnet_id),
|
||||
('address', "172.16.1.123"),
|
||||
('port', 80),
|
||||
('address', "10.0.0.2"),
|
||||
('protocol_port', 80),
|
||||
('connection_limit', 100),
|
||||
('admin_state_up', False),
|
||||
('status', 'PENDING_UPDATE')]
|
||||
|
||||
with self.vip(name=name) as vip:
|
||||
keys.append(('subnet_id', vip['vip']['subnet_id']))
|
||||
data = {'vip': {'name': name,
|
||||
'connection_limit': 100,
|
||||
'session_persistence':
|
||||
@ -416,14 +393,13 @@ class TestLoadBalancer(LoadBalancerPluginDbTestCase):
|
||||
def test_show_vip(self):
|
||||
name = "vip_show"
|
||||
keys = [('name', name),
|
||||
('subnet_id', self._subnet_id),
|
||||
('address', "172.16.1.123"),
|
||||
('port', 80),
|
||||
('address', "10.0.0.10"),
|
||||
('protocol_port', 80),
|
||||
('protocol', 'HTTP'),
|
||||
('connection_limit', -1),
|
||||
('admin_state_up', True),
|
||||
('status', 'PENDING_CREATE')]
|
||||
with self.vip(name=name) as vip:
|
||||
with self.vip(name=name, address='10.0.0.10') as vip:
|
||||
req = self.new_show_request('vips',
|
||||
vip['vip']['id'])
|
||||
res = self.deserialize(self.fmt, req.get_response(self.ext_api))
|
||||
@ -433,44 +409,52 @@ class TestLoadBalancer(LoadBalancerPluginDbTestCase):
|
||||
def test_list_vips(self):
|
||||
name = "vips_list"
|
||||
keys = [('name', name),
|
||||
('subnet_id', self._subnet_id),
|
||||
('address', "172.16.1.123"),
|
||||
('port', 80),
|
||||
('address', "10.0.0.2"),
|
||||
('protocol_port', 80),
|
||||
('protocol', 'HTTP'),
|
||||
('connection_limit', -1),
|
||||
('admin_state_up', True),
|
||||
('status', 'PENDING_CREATE')]
|
||||
with self.vip(name=name):
|
||||
with self.vip(name=name) as vip:
|
||||
keys.append(('subnet_id', vip['vip']['subnet_id']))
|
||||
req = self.new_list_request('vips')
|
||||
res = self.deserialize(self.fmt, req.get_response(self.ext_api))
|
||||
self.assertEqual(len(res), 1)
|
||||
for k, v in keys:
|
||||
self.assertEqual(res['vips'][0][k], v)
|
||||
|
||||
def test_list_vips_with_sort_emulated(self):
|
||||
with contextlib.nested(self.vip(name='vip1', port=81),
|
||||
self.vip(name='vip2', port=82),
|
||||
self.vip(name='vip3', port=82)
|
||||
) as (vip1, vip2, vip3):
|
||||
self._test_list_with_sort('vip', (vip1, vip3, vip2),
|
||||
[('port', 'asc'), ('name', 'desc')])
|
||||
with self.subnet() as subnet:
|
||||
with contextlib.nested(
|
||||
self.vip(name='vip1', subnet=subnet, protocol_port=81),
|
||||
self.vip(name='vip2', subnet=subnet, protocol_port=82),
|
||||
self.vip(name='vip3', subnet=subnet, protocol_port=82)
|
||||
) as (vip1, vip2, vip3):
|
||||
self._test_list_with_sort(
|
||||
'vip',
|
||||
(vip1, vip3, vip2),
|
||||
[('protocol_port', 'asc'), ('name', 'desc')]
|
||||
)
|
||||
|
||||
def test_list_vips_with_pagination_emulated(self):
|
||||
with contextlib.nested(self.vip(name='vip1'),
|
||||
self.vip(name='vip2'),
|
||||
self.vip(name='vip3')
|
||||
) as (vip1, vip2, vip3):
|
||||
self._test_list_with_pagination('vip',
|
||||
(vip1, vip2, vip3),
|
||||
('name', 'asc'), 2, 2)
|
||||
with self.subnet() as subnet:
|
||||
with contextlib.nested(self.vip(name='vip1', subnet=subnet),
|
||||
self.vip(name='vip2', subnet=subnet),
|
||||
self.vip(name='vip3', subnet=subnet)
|
||||
) as (vip1, vip2, vip3):
|
||||
self._test_list_with_pagination('vip',
|
||||
(vip1, vip2, vip3),
|
||||
('name', 'asc'), 2, 2)
|
||||
|
||||
def test_list_vips_with_pagination_reverse_emulated(self):
|
||||
with contextlib.nested(self.vip(name='vip1'),
|
||||
self.vip(name='vip2'),
|
||||
self.vip(name='vip3')
|
||||
) as (vip1, vip2, vip3):
|
||||
self._test_list_with_pagination_reverse('vip',
|
||||
(vip1, vip2, vip3),
|
||||
('name', 'asc'), 2, 2)
|
||||
with self.subnet() as subnet:
|
||||
with contextlib.nested(self.vip(name='vip1', subnet=subnet),
|
||||
self.vip(name='vip2', subnet=subnet),
|
||||
self.vip(name='vip3', subnet=subnet)
|
||||
) as (vip1, vip2, vip3):
|
||||
self._test_list_with_pagination_reverse('vip',
|
||||
(vip1, vip2, vip3),
|
||||
('name', 'asc'), 2, 2)
|
||||
|
||||
def test_create_pool_with_invalid_values(self):
|
||||
name = 'pool3'
|
||||
@ -519,7 +503,7 @@ class TestLoadBalancer(LoadBalancerPluginDbTestCase):
|
||||
self.assertEqual(len(pool_updated['pool']['members']), 1)
|
||||
|
||||
keys = [('address', '192.168.1.100'),
|
||||
('port', 80),
|
||||
('protocol_port', 80),
|
||||
('weight', 1),
|
||||
('pool_id', pool_id),
|
||||
('admin_state_up', True),
|
||||
@ -584,10 +568,10 @@ class TestLoadBalancer(LoadBalancerPluginDbTestCase):
|
||||
with self.pool() as pool:
|
||||
pool_id = pool['pool']['id']
|
||||
with self.member(address='192.168.1.100',
|
||||
port=80,
|
||||
protocol_port=80,
|
||||
pool_id=pool_id) as member1:
|
||||
with self.member(address='192.168.1.101',
|
||||
port=80,
|
||||
protocol_port=80,
|
||||
pool_id=pool_id) as member2:
|
||||
req = self.new_show_request('pools',
|
||||
pool_id,
|
||||
@ -606,7 +590,7 @@ class TestLoadBalancer(LoadBalancerPluginDbTestCase):
|
||||
with self.pool(name="pool2") as pool2:
|
||||
keys = [('address', "192.168.1.100"),
|
||||
('tenant_id', self._tenant_id),
|
||||
('port', 80),
|
||||
('protocol_port', 80),
|
||||
('weight', 10),
|
||||
('pool_id', pool2['pool']['id']),
|
||||
('admin_state_up', False),
|
||||
@ -686,7 +670,7 @@ class TestLoadBalancer(LoadBalancerPluginDbTestCase):
|
||||
with self.pool() as pool:
|
||||
keys = [('address', "192.168.1.100"),
|
||||
('tenant_id', self._tenant_id),
|
||||
('port', 80),
|
||||
('protocol_port', 80),
|
||||
('weight', 1),
|
||||
('pool_id', pool['pool']['id']),
|
||||
('admin_state_up', True),
|
||||
@ -705,40 +689,40 @@ class TestLoadBalancer(LoadBalancerPluginDbTestCase):
|
||||
def test_list_members_with_sort_emulated(self):
|
||||
with self.pool() as pool:
|
||||
with contextlib.nested(self.member(pool_id=pool['pool']['id'],
|
||||
port=81),
|
||||
protocol_port=81),
|
||||
self.member(pool_id=pool['pool']['id'],
|
||||
port=82),
|
||||
protocol_port=82),
|
||||
self.member(pool_id=pool['pool']['id'],
|
||||
port=83)
|
||||
protocol_port=83)
|
||||
) as (m1, m2, m3):
|
||||
self._test_list_with_sort('member', (m3, m2, m1),
|
||||
[('port', 'desc')])
|
||||
[('protocol_port', 'desc')])
|
||||
|
||||
def test_list_members_with_pagination_emulated(self):
|
||||
with self.pool() as pool:
|
||||
with contextlib.nested(self.member(pool_id=pool['pool']['id'],
|
||||
port=81),
|
||||
protocol_port=81),
|
||||
self.member(pool_id=pool['pool']['id'],
|
||||
port=82),
|
||||
protocol_port=82),
|
||||
self.member(pool_id=pool['pool']['id'],
|
||||
port=83)
|
||||
protocol_port=83)
|
||||
) as (m1, m2, m3):
|
||||
self._test_list_with_pagination('member',
|
||||
(m1, m2, m3),
|
||||
('port', 'asc'), 2, 2)
|
||||
self._test_list_with_pagination(
|
||||
'member', (m1, m2, m3), ('protocol_port', 'asc'), 2, 2
|
||||
)
|
||||
|
||||
def test_list_members_with_pagination_reverse_emulated(self):
|
||||
with self.pool() as pool:
|
||||
with contextlib.nested(self.member(pool_id=pool['pool']['id'],
|
||||
port=81),
|
||||
protocol_port=81),
|
||||
self.member(pool_id=pool['pool']['id'],
|
||||
port=82),
|
||||
protocol_port=82),
|
||||
self.member(pool_id=pool['pool']['id'],
|
||||
port=83)
|
||||
protocol_port=83)
|
||||
) as (m1, m2, m3):
|
||||
self._test_list_with_pagination_reverse('member',
|
||||
(m1, m2, m3),
|
||||
('port', 'asc'), 2, 2)
|
||||
self._test_list_with_pagination_reverse(
|
||||
'member', (m1, m2, m3), ('protocol_port', 'asc'), 2, 2
|
||||
)
|
||||
|
||||
def test_create_healthmonitor(self):
|
||||
keys = [('type', "TCP"),
|
||||
|
@ -93,7 +93,7 @@ class LoadBalancerExtensionTestCase(testlib_api.WebTestCase):
|
||||
'description': 'descr_vip1',
|
||||
'subnet_id': _uuid(),
|
||||
'address': '127.0.0.1',
|
||||
'port': 80,
|
||||
'protocol_port': 80,
|
||||
'protocol': 'HTTP',
|
||||
'pool_id': _uuid(),
|
||||
'session_persistence': {'type': 'HTTP_COOKIE'},
|
||||
@ -293,7 +293,7 @@ class LoadBalancerExtensionTestCase(testlib_api.WebTestCase):
|
||||
member_id = _uuid()
|
||||
data = {'member': {'pool_id': _uuid(),
|
||||
'address': '127.0.0.1',
|
||||
'port': 80,
|
||||
'protocol_port': 80,
|
||||
'weight': 1,
|
||||
'admin_state_up': True,
|
||||
'tenant_id': _uuid()}}
|
||||
|
@ -19,8 +19,11 @@ import webob.exc as webexc
|
||||
|
||||
import quantum
|
||||
from quantum.api import extensions
|
||||
from quantum.api.v2 import attributes
|
||||
from quantum.api.v2 import router
|
||||
from quantum.common import config
|
||||
from quantum import context as q_context
|
||||
from quantum.db import api as db
|
||||
from quantum.db import db_base_plugin_v2
|
||||
from quantum.db import l3_db
|
||||
from quantum.db.loadbalancer import loadbalancer_db as lb_db
|
||||
@ -171,6 +174,10 @@ class RouterServiceInsertionTestCase(testtools.TestCase):
|
||||
|
||||
# Ensure 'stale' patched copies of the plugin are never returned
|
||||
quantum.manager.QuantumManager._instance = None
|
||||
|
||||
# Ensure the database is reset between tests
|
||||
db._ENGINE = None
|
||||
db._MAKER = None
|
||||
# Ensure existing ExtensionManager is not used
|
||||
|
||||
ext_mgr = extensions.PluginAwareExtensionManager(
|
||||
@ -188,6 +195,48 @@ class RouterServiceInsertionTestCase(testtools.TestCase):
|
||||
res = self._do_request('GET', _get_path('service-types'))
|
||||
self._service_type_id = res['service_types'][0]['id']
|
||||
|
||||
self._setup_core_resources()
|
||||
|
||||
# FIXME (markmcclain): The test setup makes it difficult to add core
|
||||
# via the api. In the interim we'll create directly using the plugin with
|
||||
# the side effect of polluting the fixture database until tearDown.
|
||||
|
||||
def _setup_core_resources(self):
|
||||
core_plugin = quantum.manager.QuantumManager.get_plugin()
|
||||
|
||||
self._network = core_plugin.create_network(
|
||||
q_context.get_admin_context(),
|
||||
{
|
||||
'network':
|
||||
{
|
||||
'tenant_id': self._tenant_id,
|
||||
'name': 'test net',
|
||||
'admin_state_up': True,
|
||||
'shared': False,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
self._subnet = core_plugin.create_subnet(
|
||||
q_context.get_admin_context(),
|
||||
{
|
||||
'subnet':
|
||||
{
|
||||
'network_id': self._network['id'],
|
||||
'name': 'test subnet',
|
||||
'cidr': '192.168.1.0/24',
|
||||
'ip_version': 4,
|
||||
'gateway_ip': '192.168.1.1',
|
||||
'allocation_pools': attributes.ATTR_NOT_SPECIFIED,
|
||||
'dns_nameservers': attributes.ATTR_NOT_SPECIFIED,
|
||||
'host_routes': attributes.ATTR_NOT_SPECIFIED,
|
||||
'enable_dhcp': True,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
self._subnet_id = self._subnet['id']
|
||||
|
||||
def _do_request(self, method, path, data=None, params=None, action=None):
|
||||
content_type = 'application/json'
|
||||
body = None
|
||||
@ -267,7 +316,6 @@ class RouterServiceInsertionTestCase(testtools.TestCase):
|
||||
'DELETE', _get_path('routers/{0}'.format(router['id'])))
|
||||
|
||||
def _test_lb_setup(self):
|
||||
self._subnet_id = _uuid()
|
||||
router = self._router_create(self._service_type_id)
|
||||
self._router_id = router['id']
|
||||
|
||||
@ -337,10 +385,10 @@ class RouterServiceInsertionTestCase(testtools.TestCase):
|
||||
"tenant_id": self._tenant_id,
|
||||
"name": "test",
|
||||
"protocol": "HTTP",
|
||||
"port": 80,
|
||||
"protocol_port": 80,
|
||||
"subnet_id": self._subnet_id,
|
||||
"pool_id": self._pool_id,
|
||||
"address": "192.168.1.101",
|
||||
"address": "192.168.1.102",
|
||||
"connection_limit": 100,
|
||||
"admin_state_up": True,
|
||||
"router_id": router_id
|
||||
@ -361,7 +409,6 @@ class RouterServiceInsertionTestCase(testtools.TestCase):
|
||||
|
||||
def _test_resource_create(self, res):
|
||||
getattr(self, "_test_{0}_setup".format(res))()
|
||||
obj = getattr(self, "_{0}_create".format(res))()
|
||||
obj = getattr(self, "_{0}_create".format(res))(self._router_id)
|
||||
self.assertEqual(obj['router_id'], self._router_id)
|
||||
|
||||
@ -389,12 +436,15 @@ class RouterServiceInsertionTestCase(testtools.TestCase):
|
||||
_get_path('lb/{0}s/{1}'.format(res, obj['id'])))
|
||||
self.assertEqual(updated[res][update_attr], update_value)
|
||||
|
||||
def _test_resource_delete(self, res):
|
||||
def _test_resource_delete(self, res, with_router_id):
|
||||
getattr(self, "_test_{0}_setup".format(res))()
|
||||
obj = getattr(self, "_{0}_create".format(res))()
|
||||
self._do_request(
|
||||
'DELETE', _get_path('lb/{0}s/{1}'.format(res, obj['id'])))
|
||||
obj = getattr(self, "_{0}_create".format(res))(self._router_id)
|
||||
|
||||
func = getattr(self, "_{0}_create".format(res))
|
||||
|
||||
if with_router_id:
|
||||
obj = func(self._router_id)
|
||||
else:
|
||||
obj = func()
|
||||
self._do_request(
|
||||
'DELETE', _get_path('lb/{0}s/{1}'.format(res, obj['id'])))
|
||||
|
||||
@ -407,8 +457,11 @@ class RouterServiceInsertionTestCase(testtools.TestCase):
|
||||
def test_pool_update_without_router_id(self):
|
||||
self._test_resource_update('pool', False, 'name', _uuid())
|
||||
|
||||
def test_pool_delete(self):
|
||||
self._test_resource_delete('pool')
|
||||
def test_pool_delete_with_router_id(self):
|
||||
self._test_resource_delete('pool', True)
|
||||
|
||||
def test_pool_delete_without_router_id(self):
|
||||
self._test_resource_delete('pool', False)
|
||||
|
||||
def test_health_monitor_create(self):
|
||||
self._test_resource_create('health_monitor')
|
||||
@ -419,8 +472,11 @@ class RouterServiceInsertionTestCase(testtools.TestCase):
|
||||
def test_health_monitor_update_without_router_id(self):
|
||||
self._test_resource_update('health_monitor', False, 'timeout', 2)
|
||||
|
||||
def test_health_monitor_delete(self):
|
||||
self._test_resource_delete('health_monitor')
|
||||
def test_health_monitor_delete_with_router_id(self):
|
||||
self._test_resource_delete('health_monitor', True)
|
||||
|
||||
def test_health_monitor_delete_without_router_id(self):
|
||||
self._test_resource_delete('health_monitor', False)
|
||||
|
||||
def test_vip_create(self):
|
||||
self._test_resource_create('vip')
|
||||
@ -431,5 +487,8 @@ class RouterServiceInsertionTestCase(testtools.TestCase):
|
||||
def test_vip_update_without_router_id(self):
|
||||
self._test_resource_update('vip', False, 'name', _uuid())
|
||||
|
||||
def test_vip_delete(self):
|
||||
self._test_resource_delete('vip')
|
||||
def test_vip_delete_with_router_id(self):
|
||||
self._test_resource_delete('vip', True)
|
||||
|
||||
def test_vip_delete_without_router_id(self):
|
||||
self._test_resource_delete('vip', False)
|
||||
|
Loading…
Reference in New Issue
Block a user