Merge "create a Quantum port to reserve VIP address"

This commit is contained in:
Jenkins 2013-02-27 23:49:44 +00:00 committed by Gerrit Code Review
commit ce7f38cd40
6 changed files with 299 additions and 217 deletions

View File

@ -29,6 +29,7 @@ from quantum.db import model_base
from quantum.db import models_v2 from quantum.db import models_v2
from quantum.extensions import loadbalancer from quantum.extensions import loadbalancer
from quantum.extensions.loadbalancer import LoadBalancerPluginBase from quantum.extensions.loadbalancer import LoadBalancerPluginBase
from quantum import manager
from quantum.openstack.common import log as logging from quantum.openstack.common import log as logging
from quantum.openstack.common import uuidutils from quantum.openstack.common import uuidutils
from quantum.plugins.common import constants 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.""" """Represents a v2 quantum loadbalancer vip."""
name = sa.Column(sa.String(255)) name = sa.Column(sa.String(255))
description = sa.Column(sa.String(255)) description = sa.Column(sa.String(255))
subnet_id = sa.Column(sa.String(36), nullable=False) port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id'))
address = sa.Column(sa.String(64)) protocol_port = sa.Column(sa.Integer, nullable=False)
port = sa.Column(sa.Integer, nullable=False)
protocol = sa.Column(sa.Enum("HTTP", "HTTPS", "TCP", name="lb_protocols"), protocol = sa.Column(sa.Enum("HTTP", "HTTPS", "TCP", name="lb_protocols"),
nullable=False) nullable=False)
pool_id = sa.Column(sa.String(36), 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) status = sa.Column(sa.String(16), nullable=False)
admin_state_up = sa.Column(sa.Boolean(), nullable=False) admin_state_up = sa.Column(sa.Boolean(), nullable=False)
connection_limit = sa.Column(sa.Integer) connection_limit = sa.Column(sa.Integer)
port = orm.relationship(models_v2.Port)
class Member(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant): 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"), pool_id = sa.Column(sa.String(36), sa.ForeignKey("pools.id"),
nullable=False) nullable=False)
address = sa.Column(sa.String(64), 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) weight = sa.Column(sa.Integer, nullable=False)
status = sa.Column(sa.String(16), nullable=False) status = sa.Column(sa.String(16), nullable=False)
admin_state_up = sa.Column(sa.Boolean(), 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. loadbalancer plugin database access interface using SQLAlchemy models.
""" """
@property
def _core_plugin(self):
return manager.QuantumManager.get_plugin()
# TODO(lcui): # TODO(lcui):
# A set of internal facility methods are borrowed from QuantumDbPluginV2 # A set of internal facility methods are borrowed from QuantumDbPluginV2
# class and hence this is duplicate. We need to pull out those methods # class and hence this is duplicate. We need to pull out those methods
@ -237,18 +242,22 @@ class LoadBalancerPluginDb(LoadBalancerPluginBase):
######################################################## ########################################################
# VIP DB access # VIP DB access
def _make_vip_dict(self, vip, fields=None): def _make_vip_dict(self, vip, fields=None):
fixed_ip = (vip.port.fixed_ips or [{}])[0]
res = {'id': vip['id'], res = {'id': vip['id'],
'tenant_id': vip['tenant_id'], 'tenant_id': vip['tenant_id'],
'name': vip['name'], 'name': vip['name'],
'description': vip['description'], 'description': vip['description'],
'subnet_id': vip['subnet_id'], 'subnet_id': fixed_ip.get('subnet_id'),
'address': vip['address'], 'address': fixed_ip.get('ip_address'),
'port': vip['port'], 'port_id': vip['port_id'],
'protocol_port': vip['protocol_port'],
'protocol': vip['protocol'], 'protocol': vip['protocol'],
'pool_id': vip['pool_id'], 'pool_id': vip['pool_id'],
'connection_limit': vip['connection_limit'], 'connection_limit': vip['connection_limit'],
'admin_state_up': vip['admin_state_up'], 'admin_state_up': vip['admin_state_up'],
'status': vip['status']} 'status': vip['status']}
if vip['session_persistence']: if vip['session_persistence']:
s_p = { s_p = {
'type': vip['session_persistence']['type'] 'type': vip['session_persistence']['type']
@ -320,22 +329,38 @@ class LoadBalancerPluginDb(LoadBalancerPluginBase):
sess_qry = context.session.query(SessionPersistence) sess_qry = context.session.query(SessionPersistence)
sess_qry.filter_by(vip_id=vip_id).delete() 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): def create_vip(self, context, vip):
v = vip['vip'] v = vip['vip']
tenant_id = self._get_tenant_id_for_create(context, v) tenant_id = self._get_tenant_id_for_create(context, v)
with context.session.begin(subtransactions=True): 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(), vip_db = Vip(id=uuidutils.generate_uuid(),
tenant_id=tenant_id, tenant_id=tenant_id,
name=v['name'], name=v['name'],
description=v['description'], description=v['description'],
subnet_id=v['subnet_id'], port_id=None,
address=address, protocol_port=v['protocol_port'],
port=v['port'],
protocol=v['protocol'], protocol=v['protocol'],
pool_id=v['pool_id'], pool_id=v['pool_id'],
connection_limit=v['connection_limit'], connection_limit=v['connection_limit'],
@ -350,9 +375,16 @@ class LoadBalancerPluginDb(LoadBalancerPluginBase):
vip_db.session_persistence = s_p vip_db.session_persistence = s_p
context.session.add(vip_db) 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) return self._make_vip_dict(vip_db)
def update_vip(self, context, id, vip): def update_vip(self, context, id, vip):
@ -383,7 +415,11 @@ class LoadBalancerPluginDb(LoadBalancerPluginBase):
qry = context.session.query(Pool) qry = context.session.query(Pool)
for pool in qry.filter_by(vip_id=id).all(): for pool in qry.filter_by(vip_id=id).all():
pool.update({"vip_id": None}) pool.update({"vip_id": None})
context.session.delete(vip) 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): def get_vip(self, context, id, fields=None):
vip = self._get_resource(context, Vip, id) vip = self._get_resource(context, Vip, id)
@ -574,7 +610,7 @@ class LoadBalancerPluginDb(LoadBalancerPluginBase):
'tenant_id': member['tenant_id'], 'tenant_id': member['tenant_id'],
'pool_id': member['pool_id'], 'pool_id': member['pool_id'],
'address': member['address'], 'address': member['address'],
'port': member['port'], 'protocol_port': member['protocol_port'],
'weight': member['weight'], 'weight': member['weight'],
'admin_state_up': member['admin_state_up'], 'admin_state_up': member['admin_state_up'],
'status': member['status']} 'status': member['status']}
@ -596,7 +632,7 @@ class LoadBalancerPluginDb(LoadBalancerPluginBase):
tenant_id=tenant_id, tenant_id=tenant_id,
pool_id=v['pool_id'], pool_id=v['pool_id'],
address=v['address'], address=v['address'],
port=v['port'], protocol_port=v['protocol_port'],
weight=v['weight'], weight=v['weight'],
admin_state_up=v['admin_state_up'], admin_state_up=v['admin_state_up'],
status=constants.PENDING_CREATE) status=constants.PENDING_CREATE)

View File

@ -48,9 +48,8 @@ def upgrade(active_plugin=None, options=None):
sa.Column(u'id', sa.String(36), nullable=False), sa.Column(u'id', sa.String(36), nullable=False),
sa.Column(u'name', sa.String(255), nullable=True), sa.Column(u'name', sa.String(255), nullable=True),
sa.Column(u'description', 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'port_id', sa.String(36), nullable=True),
sa.Column(u'address', sa.String(64), nullable=True), sa.Column(u'protocol_port', sa.Integer(), nullable=False),
sa.Column(u'port', sa.Integer(), nullable=False),
sa.Column(u'protocol', sa.Column(u'protocol',
sa.Enum("HTTP", "HTTPS", "TCP", name="lb_protocols"), sa.Enum("HTTP", "HTTPS", "TCP", name="lb_protocols"),
nullable=False), 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'status', sa.String(16), nullable=False),
sa.Column(u'admin_state_up', sa.Boolean(), nullable=False), sa.Column(u'admin_state_up', sa.Boolean(), nullable=False),
sa.Column(u'connection_limit', sa.Integer(), nullable=True), sa.Column(u'connection_limit', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['port_id'], ['ports.id'], ),
sa.PrimaryKeyConstraint(u'id') sa.PrimaryKeyConstraint(u'id')
) )
op.create_table( 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'id', sa.String(36), nullable=False),
sa.Column(u'pool_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'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'weight', sa.Integer(), nullable=False),
sa.Column(u'status', sa.String(16), nullable=False), sa.Column(u'status', sa.String(16), nullable=False),
sa.Column(u'admin_state_up', sa.Boolean(), nullable=False), sa.Column(u'admin_state_up', sa.Boolean(), nullable=False),

View File

@ -79,10 +79,13 @@ RESOURCE_ATTRIBUTE_MAP = {
'default': attr.ATTR_NOT_SPECIFIED, 'default': attr.ATTR_NOT_SPECIFIED,
'validate': {'type:ip_address_or_none': None}, 'validate': {'type:ip_address_or_none': None},
'is_visible': True}, 'is_visible': True},
'port': {'allow_post': True, 'allow_put': False, 'port_id': {'allow_post': False, 'allow_put': False,
'validate': {'type:range': [0, 65535]}, 'validate': {'type:uuid': None},
'convert_to': attr.convert_to_int, 'is_visible': True},
'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, 'protocol': {'allow_post': True, 'allow_put': False,
'validate': {'type:values': ['TCP', 'HTTP', 'HTTPS']}, 'validate': {'type:values': ['TCP', 'HTTP', 'HTTPS']},
'is_visible': True}, 'is_visible': True},
@ -167,10 +170,10 @@ RESOURCE_ATTRIBUTE_MAP = {
'address': {'allow_post': True, 'allow_put': False, 'address': {'allow_post': True, 'allow_put': False,
'validate': {'type:ip_address': None}, 'validate': {'type:ip_address': None},
'is_visible': True}, 'is_visible': True},
'port': {'allow_post': True, 'allow_put': False, 'protocol_port': {'allow_post': True, 'allow_put': False,
'validate': {'type:range': [0, 65535]}, 'validate': {'type:range': [0, 65535]},
'convert_to': attr.convert_to_int, 'convert_to': attr.convert_to_int,
'is_visible': True}, 'is_visible': True},
'weight': {'allow_post': True, 'allow_put': True, 'weight': {'allow_post': True, 'allow_put': True,
'default': 1, 'default': 1,
'validate': {'type:range': [0, 256]}, 'validate': {'type:range': [0, 256]},

View File

@ -15,7 +15,9 @@
import contextlib import contextlib
import logging import logging
import mock
import os import os
import testtools
from oslo.config import cfg from oslo.config import cfg
import webob.exc import webob.exc
@ -25,6 +27,7 @@ from quantum.api.extensions import PluginAwareExtensionManager
from quantum.api.v2 import attributes from quantum.api.v2 import attributes
from quantum.api.v2.router import APIRouter from quantum.api.v2.router import APIRouter
from quantum.common import config from quantum.common import config
from quantum.common import exceptions as q_exc
from quantum.common.test_lib import test_config from quantum.common.test_lib import test_config
from quantum.db import api as db from quantum.db import api as db
import quantum.extensions import quantum.extensions
@ -79,16 +82,15 @@ class LoadBalancerPluginDbTestCase(test_db_plugin.QuantumDbPluginV2TestCase):
app = config.load_paste_app('extensions_test_app') app = config.load_paste_app('extensions_test_app')
self.ext_api = ExtensionMiddleware(app, ext_mgr=ext_mgr) self.ext_api = ExtensionMiddleware(app, ext_mgr=ext_mgr)
def _create_vip(self, fmt, name, pool_id, protocol, port, admin_state_up, def _create_vip(self, fmt, name, pool_id, protocol, protocol_port,
expected_res_status=None, **kwargs): admin_state_up, expected_res_status=None, **kwargs):
data = {'vip': {'name': name, data = {'vip': {'name': name,
'subnet_id': self._subnet_id,
'pool_id': pool_id, 'pool_id': pool_id,
'protocol': protocol, 'protocol': protocol,
'port': port, 'protocol_port': protocol_port,
'admin_state_up': admin_state_up, 'admin_state_up': admin_state_up,
'tenant_id': self._tenant_id}} 'tenant_id': self._tenant_id}}
for arg in ('description', 'address', for arg in ('description', 'subnet_id', 'address',
'session_persistence', 'connection_limit'): 'session_persistence', 'connection_limit'):
if arg in kwargs and kwargs[arg] is not None: if arg in kwargs and kwargs[arg] is not None:
data['vip'][arg] = kwargs[arg] data['vip'][arg] = kwargs[arg]
@ -119,10 +121,10 @@ class LoadBalancerPluginDbTestCase(test_db_plugin.QuantumDbPluginV2TestCase):
return pool_res 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): expected_res_status=None, **kwargs):
data = {'member': {'address': address, data = {'member': {'address': address,
'port': port, 'protocol_port': protocol_port,
'admin_state_up': admin_state_up, 'admin_state_up': admin_state_up,
'tenant_id': self._tenant_id}} 'tenant_id': self._tenant_id}}
for arg in ('weight', 'pool_id'): for arg in ('weight', 'pool_id'):
@ -164,44 +166,31 @@ class LoadBalancerPluginDbTestCase(test_db_plugin.QuantumDbPluginV2TestCase):
return self.ext_api return self.ext_api
@contextlib.contextmanager @contextlib.contextmanager
def vip(self, fmt=None, name='vip1', pool=None, def vip(self, fmt=None, name='vip1', pool=None, subnet=None,
protocol='HTTP', port=80, admin_state_up=True, no_delete=False, protocol='HTTP', protocol_port=80, admin_state_up=True,
address="172.16.1.123", **kwargs): no_delete=False, **kwargs):
if not fmt: if not fmt:
fmt = self.fmt fmt = self.fmt
if not pool:
with self.pool() as pool: with test_db_plugin.optional_ctx(subnet, self.subnet) as tmp_subnet:
pool_id = pool['pool']['id'] with test_db_plugin.optional_ctx(pool, self.pool) as tmp_pool:
pool_id = tmp_pool['pool']['id']
res = self._create_vip(fmt, res = self._create_vip(fmt,
name, name,
pool_id, pool_id,
protocol, protocol,
port, protocol_port,
admin_state_up, admin_state_up,
address=address, subnet_id=tmp_subnet['subnet']['id'],
**kwargs) **kwargs)
vip = self.deserialize(fmt or self.fmt, res) vip = self.deserialize(fmt or self.fmt, res)
if res.status_int >= 400: if res.status_int >= 400:
raise webob.exc.HTTPClientError(code=res.status_int) raise webob.exc.HTTPClientError(code=res.status_int)
yield vip try:
if not no_delete: yield vip
self._delete('vips', vip['vip']['id']) finally:
else: if not no_delete:
pool_id = pool['pool']['id'] self._delete('vips', vip['vip']['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'])
@contextlib.contextmanager @contextlib.contextmanager
def pool(self, fmt=None, name='pool1', lb_method='ROUND_ROBIN', 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) pool = self.deserialize(fmt or self.fmt, res)
if res.status_int >= 400: if res.status_int >= 400:
raise webob.exc.HTTPClientError(code=res.status_int) raise webob.exc.HTTPClientError(code=res.status_int)
yield pool try:
if not no_delete: yield pool
self._delete('pools', pool['pool']['id']) finally:
if not no_delete:
self._delete('pools', pool['pool']['id'])
@contextlib.contextmanager @contextlib.contextmanager
def member(self, fmt=None, address='192.168.1.100', def member(self, fmt=None, address='192.168.1.100', protocol_port=80,
port=80, admin_state_up=True, no_delete=False, admin_state_up=True, no_delete=False, **kwargs):
**kwargs):
if not fmt: if not fmt:
fmt = self.fmt fmt = self.fmt
res = self._create_member(fmt, res = self._create_member(fmt,
address, address,
port, protocol_port,
admin_state_up, admin_state_up,
**kwargs) **kwargs)
member = self.deserialize(fmt or self.fmt, res) member = self.deserialize(fmt or self.fmt, res)
if res.status_int >= 400: if res.status_int >= 400:
raise webob.exc.HTTPClientError(code=res.status_int) raise webob.exc.HTTPClientError(code=res.status_int)
yield member try:
if not no_delete: yield member
self._delete('members', member['member']['id']) finally:
if not no_delete:
self._delete('members', member['member']['id'])
@contextlib.contextmanager @contextlib.contextmanager
def health_monitor(self, fmt=None, type='TCP', def health_monitor(self, fmt=None, type='TCP',
@ -270,99 +262,84 @@ class LoadBalancerPluginDbTestCase(test_db_plugin.QuantumDbPluginV2TestCase):
else: else:
for arg in http_related_attributes: for arg in http_related_attributes:
self.assertIsNone(the_health_monitor.get(arg)) self.assertIsNone(the_health_monitor.get(arg))
yield health_monitor try:
if not no_delete: yield health_monitor
self._delete('health_monitors', the_health_monitor['id']) finally:
if not no_delete:
self._delete('health_monitors', the_health_monitor['id'])
class TestLoadBalancer(LoadBalancerPluginDbTestCase): class TestLoadBalancer(LoadBalancerPluginDbTestCase):
def test_create_vip(self): def test_create_vip(self, **extras):
name = 'vip1' expected = {
keys = [('name', name), 'name': 'vip1',
('subnet_id', self._subnet_id), 'description': '',
('address', "172.16.1.123"), 'protocol_port': 80,
('port', 80), 'protocol': 'HTTP',
('protocol', 'HTTP'), 'connection_limit': -1,
('connection_limit', -1), 'admin_state_up': True,
('admin_state_up', True), 'status': 'PENDING_CREATE',
('status', 'PENDING_CREATE')] 'tenant_id': self._tenant_id,
}
with self.vip(name=name) as vip: expected.update(extras)
for k, v in keys:
self.assertEqual(vip['vip'][k], v) 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): 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') for param, value in invalid.items():
self.assertRaises(webob.exc.HTTPClientError, vip.__enter__) 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') def test_create_vip_with_address(self):
self.assertRaises(webob.exc.HTTPClientError, vip.__enter__) self.test_create_vip(address='10.0.0.7')
# 100500 is not a valid port number def test_create_vip_with_address_outside_subnet(self):
vip = self.vip(name=name, port='100500') with testtools.ExpectedException(webob.exc.HTTPClientError):
self.assertRaises(webob.exc.HTTPClientError, vip.__enter__) self.test_create_vip(address='9.9.9.9')
# 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_session_persistence(self): def test_create_vip_with_session_persistence(self):
name = 'vip2' self.test_create_vip(session_persistence={'type': 'HTTP_COOKIE'})
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)
def test_create_vip_with_session_persistence_with_app_cookie(self): def test_create_vip_with_session_persistence_with_app_cookie(self):
name = 'vip7' sp = {'type': 'APP_COOKIE', 'cookie_name': 'sessionId'}
keys = [('name', name), self.test_create_vip(session_persistence=sp)
('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)
def test_create_vip_with_session_persistence_unsupported_type(self): def test_create_vip_with_session_persistence_unsupported_type(self):
name = 'vip5' with testtools.ExpectedException(webob.exc.HTTPClientError):
self.test_create_vip(session_persistence={'type': 'UNSUPPORTED'})
vip = self.vip(name=name, session_persistence={'type': "UNSUPPORTED"})
self.assertRaises(webob.exc.HTTPClientError, vip.__enter__)
def test_create_vip_with_unnecessary_cookie_name(self): def test_create_vip_with_unnecessary_cookie_name(self):
name = 'vip8' sp = {'type': "SOURCE_IP", 'cookie_name': 'sessionId'}
with testtools.ExpectedException(webob.exc.HTTPClientError):
s_p = {'type': "SOURCE_IP", 'cookie_name': 'sessionId'} self.test_create_vip(session_persistence=sp)
vip = self.vip(name=name, session_persistence=s_p)
self.assertRaises(webob.exc.HTTPClientError, vip.__enter__)
def test_create_vip_with_session_persistence_without_cookie_name(self): def test_create_vip_with_session_persistence_without_cookie_name(self):
name = 'vip6' sp = {'type': "APP_COOKIE"}
with testtools.ExpectedException(webob.exc.HTTPClientError):
vip = self.vip(name=name, session_persistence={'type': "APP_COOKIE"}) self.test_create_vip(session_persistence=sp)
self.assertRaises(webob.exc.HTTPClientError, vip.__enter__)
def test_reset_session_persistence(self): def test_reset_session_persistence(self):
name = 'vip4' name = 'vip4'
@ -386,14 +363,14 @@ class TestLoadBalancer(LoadBalancerPluginDbTestCase):
def test_update_vip(self): def test_update_vip(self):
name = 'new_vip' name = 'new_vip'
keys = [('name', name), keys = [('name', name),
('subnet_id', self._subnet_id), ('address', "10.0.0.2"),
('address', "172.16.1.123"), ('protocol_port', 80),
('port', 80),
('connection_limit', 100), ('connection_limit', 100),
('admin_state_up', False), ('admin_state_up', False),
('status', 'PENDING_UPDATE')] ('status', 'PENDING_UPDATE')]
with self.vip(name=name) as vip: with self.vip(name=name) as vip:
keys.append(('subnet_id', vip['vip']['subnet_id']))
data = {'vip': {'name': name, data = {'vip': {'name': name,
'connection_limit': 100, 'connection_limit': 100,
'session_persistence': 'session_persistence':
@ -416,14 +393,13 @@ class TestLoadBalancer(LoadBalancerPluginDbTestCase):
def test_show_vip(self): def test_show_vip(self):
name = "vip_show" name = "vip_show"
keys = [('name', name), keys = [('name', name),
('subnet_id', self._subnet_id), ('address', "10.0.0.10"),
('address', "172.16.1.123"), ('protocol_port', 80),
('port', 80),
('protocol', 'HTTP'), ('protocol', 'HTTP'),
('connection_limit', -1), ('connection_limit', -1),
('admin_state_up', True), ('admin_state_up', True),
('status', 'PENDING_CREATE')] ('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', req = self.new_show_request('vips',
vip['vip']['id']) vip['vip']['id'])
res = self.deserialize(self.fmt, req.get_response(self.ext_api)) res = self.deserialize(self.fmt, req.get_response(self.ext_api))
@ -433,44 +409,52 @@ class TestLoadBalancer(LoadBalancerPluginDbTestCase):
def test_list_vips(self): def test_list_vips(self):
name = "vips_list" name = "vips_list"
keys = [('name', name), keys = [('name', name),
('subnet_id', self._subnet_id), ('address', "10.0.0.2"),
('address', "172.16.1.123"), ('protocol_port', 80),
('port', 80),
('protocol', 'HTTP'), ('protocol', 'HTTP'),
('connection_limit', -1), ('connection_limit', -1),
('admin_state_up', True), ('admin_state_up', True),
('status', 'PENDING_CREATE')] ('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') req = self.new_list_request('vips')
res = self.deserialize(self.fmt, req.get_response(self.ext_api)) res = self.deserialize(self.fmt, req.get_response(self.ext_api))
self.assertEqual(len(res), 1)
for k, v in keys: for k, v in keys:
self.assertEqual(res['vips'][0][k], v) self.assertEqual(res['vips'][0][k], v)
def test_list_vips_with_sort_emulated(self): def test_list_vips_with_sort_emulated(self):
with contextlib.nested(self.vip(name='vip1', port=81), with self.subnet() as subnet:
self.vip(name='vip2', port=82), with contextlib.nested(
self.vip(name='vip3', port=82) self.vip(name='vip1', subnet=subnet, protocol_port=81),
) as (vip1, vip2, vip3): self.vip(name='vip2', subnet=subnet, protocol_port=82),
self._test_list_with_sort('vip', (vip1, vip3, vip2), self.vip(name='vip3', subnet=subnet, protocol_port=82)
[('port', 'asc'), ('name', 'desc')]) ) 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): def test_list_vips_with_pagination_emulated(self):
with contextlib.nested(self.vip(name='vip1'), with self.subnet() as subnet:
self.vip(name='vip2'), with contextlib.nested(self.vip(name='vip1', subnet=subnet),
self.vip(name='vip3') self.vip(name='vip2', subnet=subnet),
) as (vip1, vip2, vip3): self.vip(name='vip3', subnet=subnet)
self._test_list_with_pagination('vip', ) as (vip1, vip2, vip3):
(vip1, vip2, vip3), self._test_list_with_pagination('vip',
('name', 'asc'), 2, 2) (vip1, vip2, vip3),
('name', 'asc'), 2, 2)
def test_list_vips_with_pagination_reverse_emulated(self): def test_list_vips_with_pagination_reverse_emulated(self):
with contextlib.nested(self.vip(name='vip1'), with self.subnet() as subnet:
self.vip(name='vip2'), with contextlib.nested(self.vip(name='vip1', subnet=subnet),
self.vip(name='vip3') self.vip(name='vip2', subnet=subnet),
) as (vip1, vip2, vip3): self.vip(name='vip3', subnet=subnet)
self._test_list_with_pagination_reverse('vip', ) as (vip1, vip2, vip3):
(vip1, vip2, vip3), self._test_list_with_pagination_reverse('vip',
('name', 'asc'), 2, 2) (vip1, vip2, vip3),
('name', 'asc'), 2, 2)
def test_create_pool_with_invalid_values(self): def test_create_pool_with_invalid_values(self):
name = 'pool3' name = 'pool3'
@ -519,7 +503,7 @@ class TestLoadBalancer(LoadBalancerPluginDbTestCase):
self.assertEqual(len(pool_updated['pool']['members']), 1) self.assertEqual(len(pool_updated['pool']['members']), 1)
keys = [('address', '192.168.1.100'), keys = [('address', '192.168.1.100'),
('port', 80), ('protocol_port', 80),
('weight', 1), ('weight', 1),
('pool_id', pool_id), ('pool_id', pool_id),
('admin_state_up', True), ('admin_state_up', True),
@ -584,10 +568,10 @@ class TestLoadBalancer(LoadBalancerPluginDbTestCase):
with self.pool() as pool: with self.pool() as pool:
pool_id = pool['pool']['id'] pool_id = pool['pool']['id']
with self.member(address='192.168.1.100', with self.member(address='192.168.1.100',
port=80, protocol_port=80,
pool_id=pool_id) as member1: pool_id=pool_id) as member1:
with self.member(address='192.168.1.101', with self.member(address='192.168.1.101',
port=80, protocol_port=80,
pool_id=pool_id) as member2: pool_id=pool_id) as member2:
req = self.new_show_request('pools', req = self.new_show_request('pools',
pool_id, pool_id,
@ -606,7 +590,7 @@ class TestLoadBalancer(LoadBalancerPluginDbTestCase):
with self.pool(name="pool2") as pool2: with self.pool(name="pool2") as pool2:
keys = [('address', "192.168.1.100"), keys = [('address', "192.168.1.100"),
('tenant_id', self._tenant_id), ('tenant_id', self._tenant_id),
('port', 80), ('protocol_port', 80),
('weight', 10), ('weight', 10),
('pool_id', pool2['pool']['id']), ('pool_id', pool2['pool']['id']),
('admin_state_up', False), ('admin_state_up', False),
@ -686,7 +670,7 @@ class TestLoadBalancer(LoadBalancerPluginDbTestCase):
with self.pool() as pool: with self.pool() as pool:
keys = [('address', "192.168.1.100"), keys = [('address', "192.168.1.100"),
('tenant_id', self._tenant_id), ('tenant_id', self._tenant_id),
('port', 80), ('protocol_port', 80),
('weight', 1), ('weight', 1),
('pool_id', pool['pool']['id']), ('pool_id', pool['pool']['id']),
('admin_state_up', True), ('admin_state_up', True),
@ -705,40 +689,40 @@ class TestLoadBalancer(LoadBalancerPluginDbTestCase):
def test_list_members_with_sort_emulated(self): def test_list_members_with_sort_emulated(self):
with self.pool() as pool: with self.pool() as pool:
with contextlib.nested(self.member(pool_id=pool['pool']['id'], with contextlib.nested(self.member(pool_id=pool['pool']['id'],
port=81), protocol_port=81),
self.member(pool_id=pool['pool']['id'], self.member(pool_id=pool['pool']['id'],
port=82), protocol_port=82),
self.member(pool_id=pool['pool']['id'], self.member(pool_id=pool['pool']['id'],
port=83) protocol_port=83)
) as (m1, m2, m3): ) as (m1, m2, m3):
self._test_list_with_sort('member', (m3, m2, m1), self._test_list_with_sort('member', (m3, m2, m1),
[('port', 'desc')]) [('protocol_port', 'desc')])
def test_list_members_with_pagination_emulated(self): def test_list_members_with_pagination_emulated(self):
with self.pool() as pool: with self.pool() as pool:
with contextlib.nested(self.member(pool_id=pool['pool']['id'], with contextlib.nested(self.member(pool_id=pool['pool']['id'],
port=81), protocol_port=81),
self.member(pool_id=pool['pool']['id'], self.member(pool_id=pool['pool']['id'],
port=82), protocol_port=82),
self.member(pool_id=pool['pool']['id'], self.member(pool_id=pool['pool']['id'],
port=83) protocol_port=83)
) as (m1, m2, m3): ) as (m1, m2, m3):
self._test_list_with_pagination('member', self._test_list_with_pagination(
(m1, m2, m3), 'member', (m1, m2, m3), ('protocol_port', 'asc'), 2, 2
('port', 'asc'), 2, 2) )
def test_list_members_with_pagination_reverse_emulated(self): def test_list_members_with_pagination_reverse_emulated(self):
with self.pool() as pool: with self.pool() as pool:
with contextlib.nested(self.member(pool_id=pool['pool']['id'], with contextlib.nested(self.member(pool_id=pool['pool']['id'],
port=81), protocol_port=81),
self.member(pool_id=pool['pool']['id'], self.member(pool_id=pool['pool']['id'],
port=82), protocol_port=82),
self.member(pool_id=pool['pool']['id'], self.member(pool_id=pool['pool']['id'],
port=83) protocol_port=83)
) as (m1, m2, m3): ) as (m1, m2, m3):
self._test_list_with_pagination_reverse('member', self._test_list_with_pagination_reverse(
(m1, m2, m3), 'member', (m1, m2, m3), ('protocol_port', 'asc'), 2, 2
('port', 'asc'), 2, 2) )
def test_create_healthmonitor(self): def test_create_healthmonitor(self):
keys = [('type', "TCP"), keys = [('type', "TCP"),

View File

@ -93,7 +93,7 @@ class LoadBalancerExtensionTestCase(testlib_api.WebTestCase):
'description': 'descr_vip1', 'description': 'descr_vip1',
'subnet_id': _uuid(), 'subnet_id': _uuid(),
'address': '127.0.0.1', 'address': '127.0.0.1',
'port': 80, 'protocol_port': 80,
'protocol': 'HTTP', 'protocol': 'HTTP',
'pool_id': _uuid(), 'pool_id': _uuid(),
'session_persistence': {'type': 'HTTP_COOKIE'}, 'session_persistence': {'type': 'HTTP_COOKIE'},
@ -293,7 +293,7 @@ class LoadBalancerExtensionTestCase(testlib_api.WebTestCase):
member_id = _uuid() member_id = _uuid()
data = {'member': {'pool_id': _uuid(), data = {'member': {'pool_id': _uuid(),
'address': '127.0.0.1', 'address': '127.0.0.1',
'port': 80, 'protocol_port': 80,
'weight': 1, 'weight': 1,
'admin_state_up': True, 'admin_state_up': True,
'tenant_id': _uuid()}} 'tenant_id': _uuid()}}

View File

@ -19,8 +19,11 @@ import webob.exc as webexc
import quantum import quantum
from quantum.api import extensions from quantum.api import extensions
from quantum.api.v2 import attributes
from quantum.api.v2 import router from quantum.api.v2 import router
from quantum.common import config 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 db_base_plugin_v2
from quantum.db import l3_db from quantum.db import l3_db
from quantum.db.loadbalancer import loadbalancer_db as lb_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 # Ensure 'stale' patched copies of the plugin are never returned
quantum.manager.QuantumManager._instance = None quantum.manager.QuantumManager._instance = None
# Ensure the database is reset between tests
db._ENGINE = None
db._MAKER = None
# Ensure existing ExtensionManager is not used # Ensure existing ExtensionManager is not used
ext_mgr = extensions.PluginAwareExtensionManager( ext_mgr = extensions.PluginAwareExtensionManager(
@ -188,6 +195,48 @@ class RouterServiceInsertionTestCase(testtools.TestCase):
res = self._do_request('GET', _get_path('service-types')) res = self._do_request('GET', _get_path('service-types'))
self._service_type_id = res['service_types'][0]['id'] 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): def _do_request(self, method, path, data=None, params=None, action=None):
content_type = 'application/json' content_type = 'application/json'
body = None body = None
@ -267,7 +316,6 @@ class RouterServiceInsertionTestCase(testtools.TestCase):
'DELETE', _get_path('routers/{0}'.format(router['id']))) 'DELETE', _get_path('routers/{0}'.format(router['id'])))
def _test_lb_setup(self): def _test_lb_setup(self):
self._subnet_id = _uuid()
router = self._router_create(self._service_type_id) router = self._router_create(self._service_type_id)
self._router_id = router['id'] self._router_id = router['id']
@ -337,10 +385,10 @@ class RouterServiceInsertionTestCase(testtools.TestCase):
"tenant_id": self._tenant_id, "tenant_id": self._tenant_id,
"name": "test", "name": "test",
"protocol": "HTTP", "protocol": "HTTP",
"port": 80, "protocol_port": 80,
"subnet_id": self._subnet_id, "subnet_id": self._subnet_id,
"pool_id": self._pool_id, "pool_id": self._pool_id,
"address": "192.168.1.101", "address": "192.168.1.102",
"connection_limit": 100, "connection_limit": 100,
"admin_state_up": True, "admin_state_up": True,
"router_id": router_id "router_id": router_id
@ -361,7 +409,6 @@ class RouterServiceInsertionTestCase(testtools.TestCase):
def _test_resource_create(self, res): def _test_resource_create(self, res):
getattr(self, "_test_{0}_setup".format(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) obj = getattr(self, "_{0}_create".format(res))(self._router_id)
self.assertEqual(obj['router_id'], 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']))) _get_path('lb/{0}s/{1}'.format(res, obj['id'])))
self.assertEqual(updated[res][update_attr], update_value) 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))() getattr(self, "_test_{0}_setup".format(res))()
obj = getattr(self, "_{0}_create".format(res))()
self._do_request( func = getattr(self, "_{0}_create".format(res))
'DELETE', _get_path('lb/{0}s/{1}'.format(res, obj['id'])))
obj = getattr(self, "_{0}_create".format(res))(self._router_id) if with_router_id:
obj = func(self._router_id)
else:
obj = func()
self._do_request( self._do_request(
'DELETE', _get_path('lb/{0}s/{1}'.format(res, obj['id']))) '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): def test_pool_update_without_router_id(self):
self._test_resource_update('pool', False, 'name', _uuid()) self._test_resource_update('pool', False, 'name', _uuid())
def test_pool_delete(self): def test_pool_delete_with_router_id(self):
self._test_resource_delete('pool') 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): def test_health_monitor_create(self):
self._test_resource_create('health_monitor') self._test_resource_create('health_monitor')
@ -419,8 +472,11 @@ class RouterServiceInsertionTestCase(testtools.TestCase):
def test_health_monitor_update_without_router_id(self): def test_health_monitor_update_without_router_id(self):
self._test_resource_update('health_monitor', False, 'timeout', 2) self._test_resource_update('health_monitor', False, 'timeout', 2)
def test_health_monitor_delete(self): def test_health_monitor_delete_with_router_id(self):
self._test_resource_delete('health_monitor') 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): def test_vip_create(self):
self._test_resource_create('vip') self._test_resource_create('vip')
@ -431,5 +487,8 @@ class RouterServiceInsertionTestCase(testtools.TestCase):
def test_vip_update_without_router_id(self): def test_vip_update_without_router_id(self):
self._test_resource_update('vip', False, 'name', _uuid()) self._test_resource_update('vip', False, 'name', _uuid())
def test_vip_delete(self): def test_vip_delete_with_router_id(self):
self._test_resource_delete('vip') self._test_resource_delete('vip', True)
def test_vip_delete_without_router_id(self):
self._test_resource_delete('vip', False)