Octavia v2 API for listeners
GET one - /v2.0/lbaas/listeners/<listener-id> GET all - /v2.0/lbaas/listeners POST - /v2.0/lbaas/listeners {<body>} PUT - /v2.0/lbaas/listeners/<listener_id> {<body>} DELETE - /v2.0/lbaas/listener/listener_id Partially-Implements: #1616640 Co-Authored-By: Adam Harwell <flux.adam@gmail.com> Co-Authored-By: Ankur Gupta <ankur.gupta@intel.com> Change-Id: Ia4effb6e37df3ae562b9b25976440f6eb6b0044a
This commit is contained in:
parent
c3d03cc6db
commit
3eedc728f1
@ -16,11 +16,13 @@ from wsme import types as wtypes
|
||||
from wsmeext import pecan as wsme_pecan
|
||||
|
||||
from octavia.api.v2.controllers import base
|
||||
from octavia.api.v2.controllers import listener
|
||||
from octavia.api.v2.controllers import load_balancer
|
||||
|
||||
|
||||
class BaseV2Controller(base.BaseController):
|
||||
loadbalancers = load_balancer.LoadBalancersController()
|
||||
listeners = listener.ListenersController()
|
||||
|
||||
@wsme_pecan.wsexpose(wtypes.text)
|
||||
def get(self):
|
||||
|
@ -96,3 +96,9 @@ class BaseController(rest.RestController):
|
||||
"""Get a L7 Rule from the database."""
|
||||
return self._get_db_obj(session, self.repositories.l7rule,
|
||||
data_models.L7Rule, id)
|
||||
|
||||
def _get_lb_project_id(self, session, id):
|
||||
"""Get the project_id of the load balancer from the database."""
|
||||
lb = self._get_db_obj(session, self.repositories.load_balancer,
|
||||
data_models.LoadBalancer, id)
|
||||
return lb.project_id
|
||||
|
256
octavia/api/v2/controllers/listener.py
Normal file
256
octavia/api/v2/controllers/listener.py
Normal file
@ -0,0 +1,256 @@
|
||||
# Copyright 2014 Rackspace
|
||||
# Copyright 2016 Blue Box, an IBM Company
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_db import exception as odb_exceptions
|
||||
from oslo_utils import excutils
|
||||
import pecan
|
||||
from wsme import types as wtypes
|
||||
from wsmeext import pecan as wsme_pecan
|
||||
|
||||
from octavia.api.v2.controllers import base
|
||||
from octavia.api.v2.types import listener as listener_types
|
||||
from octavia.common import constants
|
||||
from octavia.common import data_models
|
||||
from octavia.common import exceptions
|
||||
from octavia.db import prepare as db_prepare
|
||||
from octavia.i18n import _LI
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ListenersController(base.BaseController):
|
||||
|
||||
def __init__(self):
|
||||
super(ListenersController, self).__init__()
|
||||
self.handler = self.handler.listener
|
||||
|
||||
def _get_db_listener(self, session, id):
|
||||
"""Gets a listener object from the database."""
|
||||
listener = super(ListenersController, self)._get_db_listener(
|
||||
session, id)
|
||||
load_balancer_id = listener.load_balancer_id
|
||||
db_listener = self.repositories.listener.get(
|
||||
session, load_balancer_id=load_balancer_id, id=id)
|
||||
if not db_listener:
|
||||
LOG.info(_LI("Listener %s not found."), id)
|
||||
raise exceptions.NotFound(
|
||||
resource=data_models.Listener._name(), id=id)
|
||||
return db_listener
|
||||
|
||||
@wsme_pecan.wsexpose(listener_types.ListenerRootResponse, wtypes.text)
|
||||
def get_one(self, id):
|
||||
"""Gets a single listener's details."""
|
||||
context = pecan.request.context.get('octavia_context')
|
||||
db_listener = self._get_db_listener(context.session, id)
|
||||
result = self._convert_db_to_type(db_listener,
|
||||
listener_types.ListenerResponse)
|
||||
return listener_types.ListenerRootResponse(listener=result)
|
||||
|
||||
@wsme_pecan.wsexpose(listener_types.ListenersRootResponse,
|
||||
wtypes.text, wtypes.text)
|
||||
def get_all(self, tenant_id=None, project_id=None):
|
||||
"""Lists all listeners."""
|
||||
context = pecan.request.context.get('octavia_context')
|
||||
if context.is_admin or CONF.auth_strategy == constants.NOAUTH:
|
||||
if project_id or tenant_id:
|
||||
project_id = {'project_id': project_id or tenant_id}
|
||||
else:
|
||||
project_id = {}
|
||||
else:
|
||||
project_id = {'project_id': context.project_id}
|
||||
db_listeners = self.repositories.listener.get_all(
|
||||
context.session, **project_id)
|
||||
result = self._convert_db_to_type(db_listeners,
|
||||
[listener_types.ListenerResponse])
|
||||
return listener_types.ListenersRootResponse(listeners=result)
|
||||
|
||||
def _test_lb_and_listener_statuses(
|
||||
self, session, lb_id, id=None,
|
||||
listener_status=constants.PENDING_UPDATE):
|
||||
"""Verify load balancer is in a mutable state."""
|
||||
lb_repo = self.repositories.load_balancer
|
||||
if id:
|
||||
if not self.repositories.test_and_set_lb_and_listeners_prov_status(
|
||||
session, lb_id, constants.PENDING_UPDATE,
|
||||
listener_status, listener_ids=[id]):
|
||||
LOG.info(_LI("Load Balancer %s is immutable."),
|
||||
lb_id)
|
||||
db_lb = lb_repo.get(session, id=lb_id)
|
||||
raise exceptions.ImmutableObject(resource=db_lb._name(),
|
||||
id=lb_id)
|
||||
else:
|
||||
if not lb_repo.test_and_set_provisioning_status(
|
||||
session, lb_id, constants.PENDING_UPDATE):
|
||||
db_lb = lb_repo.get(session, id=lb_id)
|
||||
LOG.info(_LI("Load Balancer %s is immutable."), db_lb.id)
|
||||
raise exceptions.ImmutableObject(resource=db_lb._name(),
|
||||
id=lb_id)
|
||||
|
||||
def _validate_pool(self, session, lb_id, pool_id):
|
||||
"""Validate pool given exists on same load balancer as listener."""
|
||||
db_pool = self.repositories.pool.get(
|
||||
session, load_balancer_id=lb_id, id=pool_id)
|
||||
if not db_pool:
|
||||
raise exceptions.NotFound(
|
||||
resource=data_models.Pool._name(), id=pool_id)
|
||||
|
||||
def _validate_listener(self, session, lb_id, listener_dict):
|
||||
"""Validate listener for wrong protocol or duplicate listeners
|
||||
|
||||
Update the load balancer db when provisioning status changes.
|
||||
"""
|
||||
lb_repo = self.repositories.load_balancer
|
||||
if (listener_dict and
|
||||
listener_dict.get('insert_headers') and
|
||||
list(set(listener_dict['insert_headers'].keys()) -
|
||||
set(constants.SUPPORTED_HTTP_HEADERS))):
|
||||
raise exceptions.InvalidOption(
|
||||
value=listener_dict.get('insert_headers'),
|
||||
option='insert_headers')
|
||||
|
||||
try:
|
||||
sni_containers = listener_dict.pop('sni_containers', [])
|
||||
db_listener = self.repositories.listener.create(
|
||||
session, **listener_dict)
|
||||
if sni_containers:
|
||||
for container in sni_containers:
|
||||
sni_dict = {'listener_id': db_listener.id,
|
||||
'tls_container_id': container.get(
|
||||
'tls_container_id')}
|
||||
self.repositories.sni.create(session, **sni_dict)
|
||||
db_listener = self.repositories.listener.get(session,
|
||||
id=db_listener.id)
|
||||
return db_listener
|
||||
except odb_exceptions.DBDuplicateEntry as de:
|
||||
# Setting LB back to active because this is just a validation
|
||||
# failure
|
||||
lb_repo.update(session, lb_id,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
column_list = ['load_balancer_id', 'protocol_port']
|
||||
constraint_list = ['uq_listener_load_balancer_id_protocol_port']
|
||||
if ['id'] == de.columns:
|
||||
raise exceptions.IDAlreadyExists()
|
||||
elif (set(column_list) == set(de.columns) or
|
||||
set(constraint_list) == set(de.columns)):
|
||||
raise exceptions.DuplicateListenerEntry(
|
||||
port=listener_dict.get('protocol_port'))
|
||||
except odb_exceptions.DBError:
|
||||
# Setting LB back to active because this is just a validation
|
||||
# failure
|
||||
lb_repo.update(session, lb_id,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
raise exceptions.InvalidOption(value=listener_dict.get('protocol'),
|
||||
option='protocol')
|
||||
|
||||
def _send_listener_to_handler(self, session, db_listener):
|
||||
try:
|
||||
LOG.info(_LI("Sending Creation of Listener %s to handler"),
|
||||
db_listener.id)
|
||||
self.handler.create(db_listener)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception(reraise=False):
|
||||
self.repositories.listener.update(
|
||||
session, db_listener.id,
|
||||
provisioning_status=constants.ERROR)
|
||||
db_listener = self._get_db_listener(session, db_listener.id)
|
||||
result = self._convert_db_to_type(db_listener,
|
||||
listener_types.ListenerResponse)
|
||||
return listener_types.ListenerRootResponse(listener=result)
|
||||
|
||||
@wsme_pecan.wsexpose(listener_types.ListenerRootResponse,
|
||||
body=listener_types.ListenerRootPOST, status_code=201)
|
||||
def post(self, listener_):
|
||||
"""Creates a listener on a load balancer."""
|
||||
listener = listener_.listener
|
||||
context = pecan.request.context.get('octavia_context')
|
||||
|
||||
listener_dict = db_prepare.create_listener(
|
||||
listener.to_dict(render_unsets=True), None)
|
||||
load_balancer_id = listener_dict['load_balancer_id']
|
||||
listener_dict['project_id'] = self._get_lb_project_id(
|
||||
context.session, load_balancer_id)
|
||||
|
||||
if listener_dict['default_pool_id']:
|
||||
self._validate_pool(context.session, load_balancer_id,
|
||||
listener_dict['default_pool_id'])
|
||||
self._test_lb_and_listener_statuses(context.session, load_balancer_id)
|
||||
# This is the extra validation layer for wrong protocol or duplicate
|
||||
# listeners on the same load balancer.
|
||||
db_listener = self._validate_listener(
|
||||
context.session, load_balancer_id, listener_dict)
|
||||
return self._send_listener_to_handler(context.session, db_listener)
|
||||
|
||||
@wsme_pecan.wsexpose(listener_types.ListenerRootResponse, wtypes.text,
|
||||
body=listener_types.ListenerRootPUT, status_code=200)
|
||||
def put(self, id, listener_):
|
||||
"""Updates a listener on a load balancer."""
|
||||
listener = listener_.listener
|
||||
context = pecan.request.context.get('octavia_context')
|
||||
db_listener = self._get_db_listener(context.session, id)
|
||||
load_balancer_id = db_listener.load_balancer_id
|
||||
|
||||
# TODO(rm_work): Do we need something like this? What do we do on an
|
||||
# empty body for a PUT?
|
||||
if not listener:
|
||||
raise exceptions.ValidationException(
|
||||
detail='No listener object supplied.')
|
||||
|
||||
if listener.default_pool_id:
|
||||
self._validate_pool(context.session, load_balancer_id,
|
||||
listener.default_pool_id)
|
||||
self._test_lb_and_listener_statuses(context.session, load_balancer_id,
|
||||
id=id)
|
||||
|
||||
try:
|
||||
LOG.info(_LI("Sending Update of Listener %s to handler"), id)
|
||||
self.handler.update(db_listener, listener)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception(reraise=False):
|
||||
self.repositories.listener.update(
|
||||
context.session, id, provisioning_status=constants.ERROR)
|
||||
db_listener = self._get_db_listener(context.session, id)
|
||||
result = self._convert_db_to_type(db_listener,
|
||||
listener_types.ListenerResponse)
|
||||
return listener_types.ListenerRootResponse(listener=result)
|
||||
|
||||
@wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
|
||||
def delete(self, id):
|
||||
"""Deletes a listener from a load balancer."""
|
||||
context = pecan.request.context.get('octavia_context')
|
||||
db_listener = self._get_db_listener(context.session, id)
|
||||
load_balancer_id = db_listener.load_balancer_id
|
||||
self._test_lb_and_listener_statuses(
|
||||
context.session, load_balancer_id,
|
||||
id=id, listener_status=constants.PENDING_DELETE)
|
||||
|
||||
try:
|
||||
LOG.info(_LI("Sending Deletion of Listener %s to handler"),
|
||||
db_listener.id)
|
||||
self.handler.delete(db_listener)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception(reraise=False):
|
||||
self.repositories.listener.update(
|
||||
context.session, db_listener.id,
|
||||
provisioning_status=constants.ERROR)
|
||||
db_listener = self.repositories.listener.get(
|
||||
context.session, id=db_listener.id)
|
||||
result = self._convert_db_to_type(db_listener,
|
||||
listener_types.ListenerResponse)
|
||||
return listener_types.ListenerRootResponse(listener=result)
|
149
octavia/api/v2/types/listener.py
Normal file
149
octavia/api/v2/types/listener.py
Normal file
@ -0,0 +1,149 @@
|
||||
# Copyright 2014 Rackspace
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from wsme import types as wtypes
|
||||
|
||||
from octavia.api.common import types
|
||||
from octavia.api.v1.types import l7policy
|
||||
from octavia.api.v1.types import pool
|
||||
from octavia.common import constants
|
||||
|
||||
|
||||
class BaseListenerType(types.BaseType):
|
||||
_type_to_model_map = {'admin_state_up': 'enabled'}
|
||||
|
||||
|
||||
class MinimalLoadBalancer(types.BaseType):
|
||||
id = wtypes.wsattr(wtypes.UuidType())
|
||||
|
||||
|
||||
class ListenerResponse(BaseListenerType):
|
||||
"""Defines which attributes are to be shown on any response."""
|
||||
id = wtypes.wsattr(wtypes.UuidType())
|
||||
name = wtypes.wsattr(wtypes.StringType())
|
||||
description = wtypes.wsattr(wtypes.StringType())
|
||||
provisioning_status = wtypes.wsattr(wtypes.StringType())
|
||||
operating_status = wtypes.wsattr(wtypes.StringType())
|
||||
admin_state_up = wtypes.wsattr(bool)
|
||||
protocol = wtypes.wsattr(wtypes.text)
|
||||
protocol_port = wtypes.wsattr(wtypes.IntegerType())
|
||||
connection_limit = wtypes.wsattr(wtypes.IntegerType())
|
||||
default_tls_container_ref = wtypes.wsattr(wtypes.StringType())
|
||||
sni_container_refs = [wtypes.StringType()]
|
||||
# TODO(johnsom) Remove after deprecation (R series)
|
||||
project_id = wtypes.wsattr(wtypes.StringType())
|
||||
# TODO(johnsom) Remove after deprecation (R series)
|
||||
tenant_id = wtypes.wsattr(wtypes.StringType())
|
||||
default_pool_id = wtypes.wsattr(wtypes.UuidType())
|
||||
default_pool = wtypes.wsattr(pool.PoolResponse)
|
||||
l7policies = wtypes.wsattr([l7policy.L7PolicyResponse])
|
||||
insert_headers = wtypes.wsattr(wtypes.DictType(str, str))
|
||||
created_at = wtypes.wsattr(wtypes.datetime.datetime)
|
||||
updated_at = wtypes.wsattr(wtypes.datetime.datetime)
|
||||
loadbalancers = wtypes.wsattr([MinimalLoadBalancer])
|
||||
|
||||
@classmethod
|
||||
def from_data_model(cls, data_model, children=False):
|
||||
listener = super(ListenerResponse, cls).from_data_model(
|
||||
data_model, children=children)
|
||||
listener.tenant_id = data_model.project_id
|
||||
listener.sni_container_refs = [sni_c.tls_container_id
|
||||
for sni_c in data_model.sni_containers]
|
||||
if data_model.tls_certificate_id:
|
||||
listener.default_tls_container_ref = data_model.tls_certificate_id
|
||||
listener.loadbalancers = [
|
||||
MinimalLoadBalancer.from_data_model(data_model.load_balancer)
|
||||
]
|
||||
if not listener.description:
|
||||
listener.description = ""
|
||||
if not listener.name:
|
||||
listener.name = ""
|
||||
|
||||
if not children:
|
||||
# NOTE(blogan): do not show default_pool if the request does not
|
||||
# want to see children
|
||||
del listener.default_pool
|
||||
del listener.l7policies
|
||||
return listener
|
||||
if data_model.default_pool:
|
||||
listener.default_pool = pool.PoolResponse.from_data_model(
|
||||
data_model.default_pool, children=children)
|
||||
if data_model.l7policies:
|
||||
listener.l7policies = [l7policy.L7PolicyResponse.from_data_model(
|
||||
policy, children=children) for policy in data_model.l7policies]
|
||||
if not listener.default_pool:
|
||||
del listener.default_pool
|
||||
del listener.default_pool_id
|
||||
if not listener.l7policies or len(listener.l7policies) <= 0:
|
||||
del listener.l7policies
|
||||
|
||||
return listener
|
||||
|
||||
|
||||
class ListenerRootResponse(types.BaseType):
|
||||
listener = wtypes.wsattr(ListenerResponse)
|
||||
|
||||
|
||||
class ListenersRootResponse(types.BaseType):
|
||||
listeners = wtypes.wsattr([ListenerResponse])
|
||||
|
||||
|
||||
class ListenerPOST(BaseListenerType):
|
||||
"""Defines mandatory and optional attributes of a POST request."""
|
||||
name = wtypes.wsattr(wtypes.StringType(max_length=255))
|
||||
description = wtypes.wsattr(wtypes.StringType(max_length=255))
|
||||
admin_state_up = wtypes.wsattr(bool, default=True)
|
||||
protocol = wtypes.wsattr(wtypes.Enum(str, *constants.SUPPORTED_PROTOCOLS),
|
||||
mandatory=True)
|
||||
protocol_port = wtypes.wsattr(
|
||||
wtypes.IntegerType(minimum=constants.MIN_PORT_NUMBER,
|
||||
maximum=constants.MAX_PORT_NUMBER), mandatory=True)
|
||||
connection_limit = wtypes.wsattr(
|
||||
wtypes.IntegerType(minimum=constants.MIN_CONNECTION_LIMIT), default=-1)
|
||||
default_tls_container_ref = wtypes.wsattr(
|
||||
wtypes.StringType(max_length=255))
|
||||
sni_container_refs = [wtypes.StringType(max_length=255)]
|
||||
# TODO(johnsom) Remove after deprecation (R series)
|
||||
project_id = wtypes.wsattr(wtypes.StringType(max_length=36))
|
||||
# TODO(johnsom) Remove after deprecation (R series)
|
||||
tenant_id = wtypes.wsattr(wtypes.StringType(max_length=36))
|
||||
default_pool_id = wtypes.wsattr(wtypes.UuidType())
|
||||
default_pool = wtypes.wsattr(pool.PoolPOST)
|
||||
l7policies = wtypes.wsattr([l7policy.L7PolicyPOST], default=[])
|
||||
insert_headers = wtypes.wsattr(
|
||||
wtypes.DictType(str, wtypes.StringType(max_length=255)))
|
||||
loadbalancer_id = wtypes.wsattr(wtypes.UuidType(), mandatory=True)
|
||||
|
||||
|
||||
class ListenerRootPOST(types.BaseType):
|
||||
listener = wtypes.wsattr(ListenerPOST)
|
||||
|
||||
|
||||
class ListenerPUT(BaseListenerType):
|
||||
"""Defines attributes that are acceptable of a PUT request."""
|
||||
name = wtypes.wsattr(wtypes.StringType(max_length=255))
|
||||
description = wtypes.wsattr(wtypes.StringType(max_length=255))
|
||||
admin_state_up = wtypes.wsattr(bool)
|
||||
connection_limit = wtypes.wsattr(
|
||||
wtypes.IntegerType(minimum=constants.MIN_CONNECTION_LIMIT))
|
||||
default_tls_container_ref = wtypes.wsattr(
|
||||
wtypes.StringType(max_length=255))
|
||||
sni_container_refs = [wtypes.StringType(max_length=255)]
|
||||
default_pool_id = wtypes.wsattr(wtypes.UuidType())
|
||||
insert_headers = wtypes.wsattr(
|
||||
wtypes.DictType(str, wtypes.StringType(max_length=255)))
|
||||
|
||||
|
||||
class ListenerRootPUT(types.BaseType):
|
||||
listener = wtypes.wsattr(ListenerPUT)
|
@ -15,6 +15,7 @@
|
||||
from wsme import types as wtypes
|
||||
|
||||
from octavia.api.common import types
|
||||
from octavia.api.v2.types import listener
|
||||
|
||||
|
||||
class BaseLoadBalancerType(types.BaseType):
|
||||
@ -25,6 +26,10 @@ class BaseLoadBalancerType(types.BaseType):
|
||||
'admin_state_up': 'enabled'}
|
||||
|
||||
|
||||
class MinimalListener(types.BaseType):
|
||||
id = wtypes.wsattr(wtypes.UuidType())
|
||||
|
||||
|
||||
class LoadBalancerResponse(BaseLoadBalancerType):
|
||||
"""Defines which attributes are to be shown on any response."""
|
||||
id = wtypes.wsattr(wtypes.UuidType())
|
||||
@ -41,7 +46,7 @@ class LoadBalancerResponse(BaseLoadBalancerType):
|
||||
vip_port_id = wtypes.wsattr(wtypes.UuidType())
|
||||
vip_subnet_id = wtypes.wsattr(wtypes.UuidType())
|
||||
vip_network_id = wtypes.wsattr(wtypes.UuidType())
|
||||
# TODO(blogan): add listeners once that has been merged
|
||||
listeners = wtypes.wsattr([MinimalListener])
|
||||
# TODO(ankur-gupta-f): add pools once that has been merged
|
||||
|
||||
@classmethod
|
||||
@ -53,6 +58,8 @@ class LoadBalancerResponse(BaseLoadBalancerType):
|
||||
result.vip_port_id = data_model.vip.port_id
|
||||
result.vip_address = data_model.vip.ip_address
|
||||
result.vip_network_id = data_model.vip.network_id
|
||||
result.listeners = [
|
||||
MinimalListener.from_data_model(i) for i in data_model.listeners]
|
||||
result.tenant_id = data_model.project_id
|
||||
if not result.description:
|
||||
result.description = ""
|
||||
@ -82,6 +89,7 @@ class LoadBalancerPOST(BaseLoadBalancerType):
|
||||
vip_network_id = wtypes.wsattr(wtypes.UuidType())
|
||||
project_id = wtypes.wsattr(wtypes.StringType(max_length=36))
|
||||
tenant_id = wtypes.wsattr(wtypes.StringType(max_length=36))
|
||||
listeners = wtypes.wsattr([listener.ListenerPOST], default=[])
|
||||
|
||||
|
||||
class LoadBalancerRootPOST(types.BaseType):
|
||||
|
@ -46,6 +46,12 @@ PROTOCOL_TERMINATED_HTTPS = 'TERMINATED_HTTPS'
|
||||
SUPPORTED_PROTOCOLS = (PROTOCOL_TCP, PROTOCOL_HTTPS, PROTOCOL_HTTP,
|
||||
PROTOCOL_TERMINATED_HTTPS)
|
||||
|
||||
# API Integer Ranges
|
||||
MIN_PORT_NUMBER = 1
|
||||
MAX_PORT_NUMBER = 65535
|
||||
|
||||
MIN_CONNECTION_LIMIT = -1
|
||||
|
||||
# Note: The database Amphora table has a foreign key constraint against
|
||||
# the provisioning_status table
|
||||
# Amphora has been allocated to a load balancer
|
||||
|
@ -70,14 +70,29 @@ def create_load_balancer(lb_dict):
|
||||
def create_listener(listener_dict, lb_id):
|
||||
if not listener_dict.get('id'):
|
||||
listener_dict['id'] = uuidutils.generate_uuid()
|
||||
if 'loadbalancer_id' in listener_dict.keys():
|
||||
listener_dict['load_balancer_id'] = listener_dict.pop(
|
||||
'loadbalancer_id')
|
||||
else:
|
||||
listener_dict['load_balancer_id'] = lb_id
|
||||
|
||||
listener_dict['provisioning_status'] = constants.PENDING_CREATE
|
||||
listener_dict['operating_status'] = constants.OFFLINE
|
||||
# NOTE(blogan): Throwing away because we should not store secure data
|
||||
# in the database nor should we send it to a handler.
|
||||
if 'tls_termination' in listener_dict:
|
||||
del listener_dict['tls_termination']
|
||||
|
||||
if 'default_tls_container_ref' in listener_dict:
|
||||
listener_dict['tls_certificate_id'] = (
|
||||
listener_dict.pop('default_tls_container_ref'))
|
||||
|
||||
if 'sni_containers' in listener_dict:
|
||||
sni_container_ids = listener_dict.pop('sni_containers') or []
|
||||
elif 'sni_container_refs' in listener_dict:
|
||||
sni_container_ids = listener_dict.pop('sni_container_refs') or []
|
||||
else:
|
||||
sni_container_ids = []
|
||||
sni_containers = [{'listener_id': listener_dict.get('id'),
|
||||
'tls_container_id': sni_container_id}
|
||||
for sni_container_id in sni_container_ids]
|
||||
|
@ -61,9 +61,9 @@ class BaseAPITest(base_db_test.OctaviaDBTestBase):
|
||||
|
||||
def setUp(self):
|
||||
super(BaseAPITest, self).setUp()
|
||||
conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
|
||||
conf.config(api_handler='simulated_handler')
|
||||
conf.config(group="controller_worker",
|
||||
self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
|
||||
self.conf.config(api_handler='simulated_handler')
|
||||
self.conf.config(group="controller_worker",
|
||||
network_driver='network_noop_driver')
|
||||
self.lb_repo = repositories.LoadBalancerRepository()
|
||||
self.listener_repo = repositories.ListenerRepository()
|
||||
@ -74,6 +74,7 @@ class BaseAPITest(base_db_test.OctaviaDBTestBase):
|
||||
patcher = mock.patch('octavia.api.handlers.controller_simulator.'
|
||||
'handler.SimulatedControllerHandler')
|
||||
self.handler_mock = patcher.start()
|
||||
self.addCleanup(self.handler_mock.stop)
|
||||
self.app = self._make_app()
|
||||
self.project_id = uuidutils.generate_uuid()
|
||||
|
||||
@ -131,20 +132,22 @@ class BaseAPITest(base_db_test.OctaviaDBTestBase):
|
||||
|
||||
def create_load_balancer(self, vip_subnet_id,
|
||||
**optionals):
|
||||
req_dict = {'vip_subnet_id': vip_subnet_id}
|
||||
req_dict = {'vip_subnet_id': vip_subnet_id,
|
||||
'project_id': self.project_id}
|
||||
req_dict.update(optionals)
|
||||
body = {'loadbalancer': req_dict}
|
||||
response = self.post(self.LBS_PATH, body)
|
||||
return response.json
|
||||
|
||||
def create_listener(self, protocol, protocol_port, lb_id,
|
||||
**optionals):
|
||||
status=None, **optionals):
|
||||
req_dict = {'protocol': protocol, 'protocol_port': protocol_port,
|
||||
'load_balancer_id': lb_id}
|
||||
'loadbalancer_id': lb_id}
|
||||
req_dict.update(optionals)
|
||||
path = self.LISTENERS_PATH
|
||||
body = {'listener': req_dict}
|
||||
response = self.post(path, body)
|
||||
status = {'status': status} if status else {}
|
||||
response = self.post(path, body, **status)
|
||||
return response.json
|
||||
|
||||
def create_listener_stats(self, listener_id, amphora_id):
|
||||
|
574
octavia/tests/functional/api/v2/test_listener.py
Normal file
574
octavia/tests/functional/api/v2/test_listener.py
Normal file
@ -0,0 +1,574 @@
|
||||
# Copyright 2014 Rackspace
|
||||
# Copyright 2016 Blue Box, an IBM Company
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from octavia.common import constants
|
||||
import octavia.common.context
|
||||
from octavia.tests.functional.api.v2 import base
|
||||
|
||||
import testtools
|
||||
|
||||
|
||||
class TestListener(base.BaseAPITest):
|
||||
|
||||
root_tag = 'listener'
|
||||
root_tag_list = 'listeners'
|
||||
|
||||
def setUp(self):
|
||||
super(TestListener, self).setUp()
|
||||
self.lb = self.create_load_balancer(uuidutils.generate_uuid())
|
||||
self.set_lb_status(self.lb['loadbalancer']['id'])
|
||||
self.listeners_path = self.LISTENERS_PATH
|
||||
self.listener_path = self.LISTENERS_PATH + '/{listener_id}'
|
||||
# self.pool = self.create_pool(
|
||||
# self.lb['loadbalancer']['id'], constants.PROTOCOL_HTTP,
|
||||
# constants.LB_ALGORITHM_ROUND_ROBIN, 'pool')
|
||||
# self.set_lb_status(self.lb['loadbalancer']['id'])
|
||||
|
||||
def _build_body(self, json):
|
||||
return {self.root_tag: json}
|
||||
|
||||
def test_get_all_admin(self):
|
||||
project_id = uuidutils.generate_uuid()
|
||||
lb1 = self.create_load_balancer(uuidutils.generate_uuid(),
|
||||
name='lb1', project_id=project_id)
|
||||
self.set_lb_status(lb1['loadbalancer']['id'])
|
||||
listener1 = self.create_listener(constants.PROTOCOL_HTTP, 80,
|
||||
lb1['loadbalancer']['id'])
|
||||
self.set_lb_status(lb1['loadbalancer']['id'])
|
||||
listener2 = self.create_listener(constants.PROTOCOL_HTTP, 81,
|
||||
lb1['loadbalancer']['id'])
|
||||
self.set_lb_status(lb1['loadbalancer']['id'])
|
||||
listener3 = self.create_listener(constants.PROTOCOL_HTTP, 82,
|
||||
lb1['loadbalancer']['id'])
|
||||
self.set_lb_status(lb1['loadbalancer']['id'])
|
||||
response = self.get(self.listeners_path)
|
||||
listeners = response.json.get(self.root_tag_list)
|
||||
self.assertEqual(3, len(listeners))
|
||||
listener_id_names = [(l.get('id'), l.get('name')) for l in listeners]
|
||||
listener1 = listener1.get(self.root_tag)
|
||||
listener2 = listener2.get(self.root_tag)
|
||||
listener3 = listener3.get(self.root_tag)
|
||||
self.assertIn((listener1.get('id'), listener1.get('name')),
|
||||
listener_id_names)
|
||||
self.assertIn((listener2.get('id'), listener2.get('name')),
|
||||
listener_id_names)
|
||||
self.assertIn((listener3.get('id'), listener3.get('name')),
|
||||
listener_id_names)
|
||||
|
||||
def test_get_all_non_admin(self):
|
||||
project_id = uuidutils.generate_uuid()
|
||||
lb1 = self.create_load_balancer(uuidutils.generate_uuid(),
|
||||
name='lb1', project_id=project_id)
|
||||
self.set_lb_status(lb1['loadbalancer']['id'])
|
||||
self.create_listener(constants.PROTOCOL_HTTP, 80,
|
||||
lb1['loadbalancer']['id'])
|
||||
self.set_lb_status(lb1['loadbalancer']['id'])
|
||||
self.create_listener(constants.PROTOCOL_HTTP, 81,
|
||||
lb1['loadbalancer']['id'])
|
||||
self.set_lb_status(self.lb['loadbalancer']['id'])
|
||||
listener3 = self.create_listener(constants.PROTOCOL_HTTP, 82,
|
||||
self.lb['loadbalancer']['id'])
|
||||
listener3 = listener3.get(self.root_tag)
|
||||
self.set_lb_status(self.lb['loadbalancer']['id'])
|
||||
|
||||
auth_strategy = self.conf.conf.get('auth_strategy')
|
||||
self.conf.config(auth_strategy=constants.KEYSTONE)
|
||||
with mock.patch.object(octavia.common.context.Context, 'project_id',
|
||||
listener3['project_id']):
|
||||
response = self.get(self.LISTENERS_PATH)
|
||||
self.conf.config(auth_strategy=auth_strategy)
|
||||
|
||||
listeners = response.json.get(self.root_tag_list)
|
||||
self.assertEqual(1, len(listeners))
|
||||
listener_id_names = [(l.get('id'), l.get('name')) for l in listeners]
|
||||
self.assertIn((listener3.get('id'), listener3.get('name')),
|
||||
listener_id_names)
|
||||
|
||||
def test_get_all_by_project_id(self):
|
||||
project1_id = uuidutils.generate_uuid()
|
||||
project2_id = uuidutils.generate_uuid()
|
||||
lb1 = self.create_load_balancer(uuidutils.generate_uuid(),
|
||||
name='lb1',
|
||||
project_id=project1_id)
|
||||
self.set_lb_status(lb1['loadbalancer']['id'])
|
||||
lb2 = self.create_load_balancer(uuidutils.generate_uuid(),
|
||||
name='lb2',
|
||||
project_id=project2_id)
|
||||
self.set_lb_status(lb2['loadbalancer']['id'])
|
||||
listener1 = self.create_listener(constants.PROTOCOL_HTTP, 80,
|
||||
lb1['loadbalancer']['id'],
|
||||
name='listener1')
|
||||
self.set_lb_status(lb1['loadbalancer']['id'])
|
||||
listener2 = self.create_listener(constants.PROTOCOL_HTTP, 81,
|
||||
lb1['loadbalancer']['id'],
|
||||
name='listener2')
|
||||
self.set_lb_status(lb1['loadbalancer']['id'])
|
||||
listener3 = self.create_listener(constants.PROTOCOL_HTTP, 82,
|
||||
lb2['loadbalancer']['id'],
|
||||
name='listener3')
|
||||
self.set_lb_status(lb2['loadbalancer']['id'])
|
||||
response = self.get(self.LISTENERS_PATH,
|
||||
params={'project_id': project1_id})
|
||||
listeners = response.json.get(self.root_tag_list)
|
||||
|
||||
self.assertEqual(2, len(listeners))
|
||||
|
||||
listener_id_names = [(l.get('id'), l.get('name')) for l in listeners]
|
||||
listener1 = listener1.get(self.root_tag)
|
||||
listener2 = listener2.get(self.root_tag)
|
||||
listener3 = listener3.get(self.root_tag)
|
||||
self.assertIn((listener1.get('id'), listener1.get('name')),
|
||||
listener_id_names)
|
||||
self.assertIn((listener2.get('id'), listener2.get('name')),
|
||||
listener_id_names)
|
||||
response = self.get(self.LISTENERS_PATH,
|
||||
params={'project_id': project2_id})
|
||||
listeners = response.json.get(self.root_tag_list)
|
||||
listener_id_names = [(l.get('id'), l.get('name')) for l in listeners]
|
||||
self.assertEqual(1, len(listeners))
|
||||
self.assertIn((listener3.get('id'), listener3.get('name')),
|
||||
listener_id_names)
|
||||
|
||||
def test_get(self):
|
||||
listener = self.create_listener(constants.PROTOCOL_HTTP, 80,
|
||||
self.lb['loadbalancer']['id'])
|
||||
listener_path = self.listener_path
|
||||
response = self.get(listener_path.format(
|
||||
listener_id=listener['listener']['id']))
|
||||
api_lb = response.json['listener']
|
||||
expected = {'name': None, 'description': None, 'admin_state_up': True,
|
||||
'operating_status': constants.OFFLINE,
|
||||
'provisioning_status': constants.PENDING_CREATE,
|
||||
'connection_limit': None}
|
||||
listener.update(expected)
|
||||
self.assertEqual(listener['listener'], api_lb)
|
||||
|
||||
def test_get_bad_listener_id(self):
|
||||
listener_path = self.listener_path
|
||||
self.get(listener_path.format(listener_id='SEAN-CONNERY'), status=404)
|
||||
|
||||
def test_create(self, **optionals):
|
||||
sni1 = uuidutils.generate_uuid()
|
||||
sni2 = uuidutils.generate_uuid()
|
||||
lb_listener = {'name': 'listener1', 'default_pool_id': None,
|
||||
'description': 'desc1',
|
||||
'admin_state_up': False,
|
||||
'protocol': constants.PROTOCOL_HTTP,
|
||||
'protocol_port': 80, 'connection_limit': 10,
|
||||
'default_tls_container_ref': uuidutils.generate_uuid(),
|
||||
'sni_container_refs': [sni1, sni2],
|
||||
'insert_headers': {},
|
||||
'project_id': self.project_id,
|
||||
'loadbalancer_id': self.lb['loadbalancer']['id']}
|
||||
lb_listener.update(optionals)
|
||||
body = self._build_body(lb_listener)
|
||||
response = self.post(self.listeners_path, body)
|
||||
listener_api = response.json['listener']
|
||||
extra_expects = {'provisioning_status': constants.PENDING_CREATE,
|
||||
'operating_status': constants.OFFLINE}
|
||||
lb_listener.update(extra_expects)
|
||||
self.assertTrue(uuidutils.is_uuid_like(listener_api.get('id')))
|
||||
for key, value in optionals.items():
|
||||
self.assertEqual(value, lb_listener.get(key))
|
||||
lb_listener['id'] = listener_api.get('id')
|
||||
lb_listener.pop('sni_container_refs')
|
||||
sni_ex = [sni1, sni2]
|
||||
sni_resp = listener_api.pop('sni_container_refs')
|
||||
self.assertEqual(2, len(sni_resp))
|
||||
for sni in sni_resp:
|
||||
self.assertIn(sni, sni_ex)
|
||||
self.assertIsNotNone(listener_api.pop('created_at'))
|
||||
self.assertIsNone(listener_api.pop('updated_at'))
|
||||
self.assertNotEqual(lb_listener, listener_api)
|
||||
self.assert_correct_lb_status(self.lb['loadbalancer']['id'],
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.assert_final_lb_statuses(self.lb['loadbalancer']['id'])
|
||||
self.assert_final_listener_statuses(self.lb['loadbalancer']['id'],
|
||||
listener_api.get('id'))
|
||||
|
||||
def test_create_duplicate_fails(self):
|
||||
self.create_listener(constants.PROTOCOL_HTTP, 80,
|
||||
self.lb['loadbalancer']['id'])
|
||||
self.set_lb_status(self.lb['loadbalancer']['id'])
|
||||
self.create_listener(constants.PROTOCOL_HTTP, 80,
|
||||
self.lb['loadbalancer']['id'],
|
||||
status=409)
|
||||
|
||||
@testtools.skip('Skip until complete v2 merge')
|
||||
def test_create_with_default_pool_id(self):
|
||||
lb_listener = {'name': 'listener1',
|
||||
'default_pool_id': self.pool.get('id'),
|
||||
'description': 'desc1',
|
||||
'admin_state_up': False,
|
||||
'protocol': constants.PROTOCOL_HTTP,
|
||||
'protocol_port': 80}
|
||||
response = self.post(self.listeners_path, lb_listener)
|
||||
api_listener = response.json
|
||||
self.assertEqual(api_listener.get('default_pool_id'),
|
||||
self.pool.get('id'))
|
||||
|
||||
def test_create_with_bad_default_pool_id(self):
|
||||
lb_listener = {'name': 'listener1',
|
||||
'default_pool_id': uuidutils.generate_uuid(),
|
||||
'description': 'desc1',
|
||||
'admin_state_up': False,
|
||||
'protocol': constants.PROTOCOL_HTTP,
|
||||
'protocol_port': 80,
|
||||
'loadbalancer_id': self.lb['loadbalancer']['id']}
|
||||
body = self._build_body(lb_listener)
|
||||
self.post(self.listeners_path, body, status=404)
|
||||
|
||||
@testtools.skip('Skip until complete v2 merge')
|
||||
def test_create_with_shared_default_pool_id(self):
|
||||
lb_listener1 = {'name': 'listener1',
|
||||
'default_pool_id': self.pool.get('id'),
|
||||
'description': 'desc1',
|
||||
'admin_state_up': False,
|
||||
'protocol': constants.PROTOCOL_HTTP,
|
||||
'protocol_port': 80}
|
||||
lb_listener2 = {'name': 'listener2',
|
||||
'default_pool_id': self.pool.get('id'),
|
||||
'description': 'desc2',
|
||||
'admin_state_up': False,
|
||||
'protocol': constants.PROTOCOL_HTTP,
|
||||
'protocol_port': 81}
|
||||
body1 = self._build_body(lb_listener1)
|
||||
body2 = self._build_body(lb_listener2)
|
||||
listener1 = self.post(self.listeners_path, body1).json
|
||||
self.set_lb_status(self.lb.get('id'), constants.ACTIVE)
|
||||
listener2 = self.post(self.listeners_path, body2).json
|
||||
self.assertEqual(listener1['default_pool_id'], self.pool.get('id'))
|
||||
self.assertEqual(listener1['default_pool_id'],
|
||||
listener2['default_pool_id'])
|
||||
|
||||
def test_create_with_project_id(self):
|
||||
self.test_create(project_id=self.project_id)
|
||||
|
||||
def test_create_defaults(self):
|
||||
defaults = {'name': None, 'default_pool_id': None,
|
||||
'description': None, 'admin_state_up': True,
|
||||
'connection_limit': None,
|
||||
'default_tls_container_ref': None,
|
||||
'sni_container_refs': [], 'project_id': None,
|
||||
'insert_headers': {}}
|
||||
lb_listener = {'protocol': constants.PROTOCOL_HTTP,
|
||||
'protocol_port': 80,
|
||||
'loadbalancer_id': self.lb['loadbalancer']['id']}
|
||||
body = self._build_body(lb_listener)
|
||||
response = self.post(self.listeners_path, body)
|
||||
listener_api = response.json['listener']
|
||||
extra_expects = {'provisioning_status': constants.PENDING_CREATE,
|
||||
'operating_status': constants.OFFLINE}
|
||||
lb_listener.update(extra_expects)
|
||||
lb_listener.update(defaults)
|
||||
self.assertTrue(uuidutils.is_uuid_like(listener_api.get('id')))
|
||||
lb_listener['id'] = listener_api.get('id')
|
||||
self.assertIsNotNone(listener_api.pop('created_at'))
|
||||
self.assertIsNone(listener_api.pop('updated_at'))
|
||||
self.assertNotEqual(lb_listener, listener_api)
|
||||
self.assert_correct_lb_status(self.lb['loadbalancer']['id'],
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.assert_final_lb_statuses(self.lb['loadbalancer']['id'])
|
||||
self.assert_final_listener_statuses(self.lb['loadbalancer']['id'],
|
||||
listener_api['id'])
|
||||
|
||||
@testtools.skip('Skip until complete v2 merge')
|
||||
def test_update(self):
|
||||
tls_uuid = uuidutils.generate_uuid()
|
||||
listener = self.create_listener(self.lb['loadbalancer']['id'],
|
||||
constants.PROTOCOL_TCP, 80,
|
||||
name='listener1', description='desc1',
|
||||
enabled=False, connection_limit=10,
|
||||
default_tls_container_ref=tls_uuid,
|
||||
default_pool_id=None)
|
||||
self.set_lb_status(self.lb['loadbalancer']['id'])
|
||||
new_listener = {'name': 'listener2', 'admin_state_up': True,
|
||||
'default_pool_id': self.pool.get('id')}
|
||||
listener_path = self.LISTENER_PATH.format(listener_id=listener['id'])
|
||||
api_listener = self.put(listener_path, new_listener).json
|
||||
update_expect = {'name': 'listener2', 'admin_state_up': True,
|
||||
'default_pool_id': self.pool.get('id'),
|
||||
'provisioning_status': constants.PENDING_UPDATE,
|
||||
'operating_status': constants.ONLINE}
|
||||
listener.update(update_expect)
|
||||
self.assertEqual(listener.pop('created_at'),
|
||||
api_listener.pop('created_at'))
|
||||
self.assertNotEqual(listener.pop('updated_at'),
|
||||
api_listener.pop('updated_at'))
|
||||
self.assertNotEqual(listener, api_listener)
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.assert_final_listener_statuses(self.lb.get('id'),
|
||||
api_listener.get('id'))
|
||||
|
||||
def test_update_bad_listener_id(self):
|
||||
self.put(self.listener_path.format(listener_id='SEAN-CONNERY'),
|
||||
body={}, status=404)
|
||||
|
||||
@testtools.skip('Skip until complete v2 merge')
|
||||
def test_update_with_bad_default_pool_id(self):
|
||||
bad_pool_uuid = uuidutils.generate_uuid()
|
||||
listener = self.create_listener(self.lb.get('id'),
|
||||
constants.PROTOCOL_TCP, 80,
|
||||
name='listener1', description='desc1',
|
||||
enabled=False, connection_limit=10,
|
||||
default_pool_id=self.pool.get('id'))
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
new_listener = {'name': 'listener2', 'admin_state_up': True,
|
||||
'default_pool_id': bad_pool_uuid}
|
||||
listener_path = self.LISTENER_PATH.format(
|
||||
lb_id=self.lb.get('id'), listener_id=listener.get('id'))
|
||||
self.put(listener_path, new_listener, status=404)
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.ACTIVE,
|
||||
constants.ONLINE)
|
||||
self.assert_final_listener_statuses(self.lb.get('id'),
|
||||
listener.get('id'))
|
||||
|
||||
def test_create_listeners_same_port(self):
|
||||
listener1 = self.create_listener(constants.PROTOCOL_TCP, 80,
|
||||
self.lb['loadbalancer']['id'])
|
||||
self.set_lb_status(self.lb['loadbalancer']['id'])
|
||||
listener2_post = {'protocol': listener1['listener']['protocol'],
|
||||
'protocol_port':
|
||||
listener1['listener']['protocol_port'],
|
||||
'loadbalancer_id': self.lb['loadbalancer']['id']}
|
||||
body = self._build_body(listener2_post)
|
||||
self.post(self.listeners_path, body, status=409)
|
||||
|
||||
def test_delete(self):
|
||||
listener = self.create_listener(constants.PROTOCOL_HTTP, 80,
|
||||
self.lb['loadbalancer']['id'])
|
||||
self.set_lb_status(self.lb['loadbalancer']['id'])
|
||||
listener_path = self.LISTENER_PATH.format(
|
||||
listener_id=listener['listener']['id'])
|
||||
self.delete(listener_path)
|
||||
response = self.get(listener_path)
|
||||
api_listener = response.json['listener']
|
||||
expected = {'name': None, 'default_pool_id': None,
|
||||
'description': None, 'admin_state_up': True,
|
||||
'operating_status': constants.ONLINE,
|
||||
'provisioning_status': constants.PENDING_DELETE,
|
||||
'connection_limit': None}
|
||||
listener['listener'].update(expected)
|
||||
|
||||
self.assertIsNone(listener['listener'].pop('updated_at'))
|
||||
self.assertIsNotNone(api_listener.pop('updated_at'))
|
||||
self.assertNotEqual(listener, api_listener)
|
||||
self.assert_correct_lb_status(self.lb['loadbalancer']['id'],
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.assert_final_lb_statuses(self.lb['loadbalancer']['id'])
|
||||
self.assert_final_listener_statuses(self.lb['loadbalancer']['id'],
|
||||
api_listener['id'],
|
||||
delete=True)
|
||||
|
||||
def test_delete_bad_listener_id(self):
|
||||
listener_path = self.LISTENER_PATH.format(listener_id='SEAN-CONNERY')
|
||||
self.delete(listener_path, status=404)
|
||||
|
||||
def test_create_listener_bad_protocol(self):
|
||||
lb_listener = {'protocol': 'SEAN_CONNERY',
|
||||
'protocol_port': 80}
|
||||
self.post(self.listeners_path, lb_listener, status=400)
|
||||
|
||||
def test_update_listener_bad_protocol(self):
|
||||
listener = self.create_listener(constants.PROTOCOL_TCP, 80,
|
||||
self.lb['loadbalancer']['id'])
|
||||
self.set_lb_status(self.lb['loadbalancer']['id'])
|
||||
new_listener = {'protocol': 'SEAN_CONNERY',
|
||||
'protocol_port': 80}
|
||||
listener_path = self.LISTENER_PATH.format(
|
||||
listener_id=listener['listener'].get('id'))
|
||||
self.put(listener_path, new_listener, status=400)
|
||||
|
||||
def test_update_pending_create(self):
|
||||
lb = self.create_load_balancer(uuidutils.generate_uuid())
|
||||
optionals = {'name': 'lb1', 'description': 'desc1',
|
||||
'admin_state_up': False}
|
||||
lb.update(optionals)
|
||||
|
||||
lb_listener = {'name': 'listener1', 'description': 'desc1',
|
||||
'admin_state_up': False,
|
||||
'protocol': constants.PROTOCOL_HTTP,
|
||||
'protocol_port': 80, 'connection_limit': 10,
|
||||
'loadbalancer_id': lb['loadbalancer']['id']}
|
||||
body = self._build_body(lb_listener)
|
||||
self.post(self.LISTENERS_PATH, body, status=409)
|
||||
|
||||
def test_delete_pending_update(self):
|
||||
lb = self.create_load_balancer(uuidutils.generate_uuid())
|
||||
optionals = {'name': 'lb1', 'description': 'desc1',
|
||||
'admin_state_up': False}
|
||||
lb.update(optionals)
|
||||
|
||||
self.set_lb_status(lb['loadbalancer']['id'])
|
||||
lb_listener = {'name': 'listener1', 'description': 'desc1',
|
||||
'admin_state_up': False,
|
||||
'protocol': constants.PROTOCOL_HTTP,
|
||||
'protocol_port': 80, 'connection_limit': 10,
|
||||
'loadbalancer_id': lb['loadbalancer']['id']}
|
||||
body = self._build_body(lb_listener)
|
||||
api_listener = self.post(
|
||||
self.LISTENERS_PATH, body).json['listener']
|
||||
listener_path = self.LISTENER_PATH.format(
|
||||
listener_id=api_listener['id'])
|
||||
self.delete(listener_path, status=409)
|
||||
|
||||
def test_update_empty_body(self):
|
||||
listener = self.create_listener(constants.PROTOCOL_TCP, 80,
|
||||
self.lb['loadbalancer']['id'])
|
||||
self.set_lb_status(self.lb['loadbalancer']['id'])
|
||||
listener_path = self.LISTENER_PATH.format(
|
||||
listener_id=listener['listener'].get('id'))
|
||||
self.put(listener_path, {}, status=400)
|
||||
|
||||
def test_update_pending_update(self):
|
||||
lb = self.create_load_balancer(uuidutils.generate_uuid())
|
||||
optionals = {'name': 'lb1', 'description': 'desc1',
|
||||
'admin_state_up': False}
|
||||
lb.update(optionals)
|
||||
self.set_lb_status(lb['loadbalancer']['id'])
|
||||
lb_listener = {'name': 'listener1', 'description': 'desc1',
|
||||
'admin_state_up': False,
|
||||
'protocol': constants.PROTOCOL_HTTP,
|
||||
'protocol_port': 80, 'connection_limit': 10,
|
||||
'loadbalancer_id': lb['loadbalancer']['id']}
|
||||
body = self._build_body(lb_listener)
|
||||
api_listener = self.post(
|
||||
self.LISTENERS_PATH, body).json['listener']
|
||||
self.set_lb_status(lb['loadbalancer']['id'])
|
||||
self.put(self.LB_PATH.format(lb_id=lb['loadbalancer']['id']),
|
||||
{'loadbalancer': {'name': 'hi'}})
|
||||
lb_listener_put = {'name': 'listener1_updated'}
|
||||
body = self._build_body(lb_listener_put)
|
||||
listener_path = self.LISTENER_PATH.format(
|
||||
listener_id=api_listener['id'])
|
||||
self.put(listener_path, body, status=409)
|
||||
|
||||
def test_update_pending_delete(self):
|
||||
lb = self.create_load_balancer(uuidutils.generate_uuid(),
|
||||
name='lb1', description='desc1',
|
||||
admin_state_up=False)
|
||||
self.set_lb_status(lb['loadbalancer']['id'])
|
||||
lb_listener = {'name': 'listener1', 'description': 'desc1',
|
||||
'admin_state_up': False,
|
||||
'protocol': constants.PROTOCOL_HTTP,
|
||||
'protocol_port': 80, 'connection_limit': 10,
|
||||
'loadbalancer_id': lb['loadbalancer']['id']}
|
||||
body = self._build_body(lb_listener)
|
||||
api_listener = self.post(
|
||||
self.LISTENERS_PATH, body).json['listener']
|
||||
self.set_lb_status(lb['loadbalancer']['id'])
|
||||
self.delete(self.LB_PATH.format(lb_id=lb['loadbalancer']['id']))
|
||||
lb_listener_put = {'name': 'listener1_updated'}
|
||||
body = self._build_body(lb_listener_put)
|
||||
listener_path = self.LISTENER_PATH.format(
|
||||
listener_id=api_listener['id'])
|
||||
self.put(listener_path, body, status=409)
|
||||
|
||||
def test_delete_pending_delete(self):
|
||||
lb = self.create_load_balancer(uuidutils.generate_uuid(),
|
||||
name='lb1', description='desc1',
|
||||
admin_state_up=False)
|
||||
self.set_lb_status(lb['loadbalancer']['id'])
|
||||
lb_listener = {'name': 'listener1', 'description': 'desc1',
|
||||
'admin_state_up': False,
|
||||
'protocol': constants.PROTOCOL_HTTP,
|
||||
'protocol_port': 80, 'connection_limit': 10,
|
||||
'loadbalancer_id': lb['loadbalancer']['id']}
|
||||
body = self._build_body(lb_listener)
|
||||
api_listener = self.post(
|
||||
self.LISTENERS_PATH, body).json['listener']
|
||||
self.set_lb_status(lb['loadbalancer']['id'])
|
||||
self.delete(self.LB_PATH.format(lb_id=lb['loadbalancer']['id']))
|
||||
listener_path = self.LISTENER_PATH.format(
|
||||
listener_id=api_listener['id'])
|
||||
self.delete(listener_path, status=409)
|
||||
|
||||
def test_create_with_tls_termination_data(self):
|
||||
cert_id = uuidutils.generate_uuid()
|
||||
listener = self.create_listener(constants.PROTOCOL_HTTP, 80,
|
||||
self.lb['loadbalancer']['id'],
|
||||
default_tls_container_ref=cert_id)
|
||||
listener_path = self.LISTENER_PATH.format(
|
||||
listener_id=listener['listener']['id'])
|
||||
get_listener = self.get(listener_path).json['listener']
|
||||
self.assertEqual(cert_id, get_listener['default_tls_container_ref'])
|
||||
|
||||
def test_update_with_tls_termination_data(self):
|
||||
cert_id = uuidutils.generate_uuid()
|
||||
listener = self.create_listener(constants.PROTOCOL_HTTP, 80,
|
||||
self.lb['loadbalancer']['id'])
|
||||
self.set_lb_status(self.lb['loadbalancer']['id'])
|
||||
listener_path = self.LISTENER_PATH.format(
|
||||
listener_id=listener['listener']['id'])
|
||||
get_listener = self.get(listener_path).json['listener']
|
||||
self.assertIsNone(get_listener.get('default_tls_container_ref'))
|
||||
self.put(listener_path,
|
||||
self._build_body({'default_tls_container_ref': cert_id}))
|
||||
get_listener = self.get(listener_path).json['listener']
|
||||
self.assertIsNone(get_listener.get('default_tls_container_ref'))
|
||||
|
||||
def test_create_with_sni_data(self):
|
||||
sni_id1 = uuidutils.generate_uuid()
|
||||
sni_id2 = uuidutils.generate_uuid()
|
||||
listener = self.create_listener(constants.PROTOCOL_HTTP, 80,
|
||||
self.lb['loadbalancer']['id'],
|
||||
sni_container_refs=[sni_id1, sni_id2])
|
||||
listener_path = self.LISTENER_PATH.format(
|
||||
listener_id=listener['listener']['id'])
|
||||
get_listener = self.get(listener_path).json['listener']
|
||||
self.assertItemsEqual([sni_id1, sni_id2],
|
||||
get_listener['sni_container_refs'])
|
||||
|
||||
def test_update_with_sni_data(self):
|
||||
sni_id1 = uuidutils.generate_uuid()
|
||||
sni_id2 = uuidutils.generate_uuid()
|
||||
listener = self.create_listener(constants.PROTOCOL_HTTP, 80,
|
||||
self.lb['loadbalancer']['id'])
|
||||
self.set_lb_status(self.lb['loadbalancer']['id'])
|
||||
listener_path = self.LISTENER_PATH.format(
|
||||
listener_id=listener['listener']['id'])
|
||||
get_listener = self.get(listener_path).json['listener']
|
||||
self.assertEqual([], get_listener.get('sni_container_refs'))
|
||||
self.put(listener_path,
|
||||
self._build_body({'sni_container_refs': [sni_id1, sni_id2]}))
|
||||
get_listener = self.get(listener_path).json['listener']
|
||||
self.assertEqual([], get_listener.get('sni_container_refs'))
|
||||
|
||||
def test_create_with_valid_insert_headers(self):
|
||||
lb_listener = {'protocol': 'HTTP',
|
||||
'protocol_port': 80,
|
||||
'loadbalancer_id': self.lb['loadbalancer']['id'],
|
||||
'insert_headers': {'X-Forwarded-For': 'true'}}
|
||||
body = self._build_body(lb_listener)
|
||||
self.post(self.listeners_path, body, status=201)
|
||||
|
||||
def test_create_with_bad_insert_headers(self):
|
||||
lb_listener = {'protocol': 'HTTP',
|
||||
'protocol_port': 80,
|
||||
'loadbalancer_id': self.lb['loadbalancer']['id'],
|
||||
# 'insert_headers': {'x': 'x'}}
|
||||
'insert_headers': {'X-Forwarded-Four': 'true'}}
|
||||
body = self._build_body(lb_listener)
|
||||
self.post(self.listeners_path, body, status=400)
|
@ -555,19 +555,19 @@ class TestLoadBalancerGraph(base.BaseAPITest):
|
||||
create_lb = {
|
||||
'name': 'lb1',
|
||||
'project_id': self._project_id,
|
||||
'vip': {},
|
||||
'vip_subnet_id': None,
|
||||
'listeners': create_listeners
|
||||
}
|
||||
expected_lb = {
|
||||
'description': None,
|
||||
'enabled': True,
|
||||
'provisioning_status': constants.PENDING_CREATE,
|
||||
'operating_status': constants.OFFLINE
|
||||
'operating_status': constants.OFFLINE,
|
||||
'vip_subnet_id': None,
|
||||
'vip_address': None,
|
||||
}
|
||||
expected_lb.update(create_lb)
|
||||
expected_lb['listeners'] = expected_listeners
|
||||
expected_lb['vip'] = {'ip_address': None, 'port_id': None,
|
||||
'subnet_id': None}
|
||||
return create_lb, expected_lb
|
||||
|
||||
def _get_listener_bodies(self, name='listener1', protocol_port=80,
|
||||
|
137
octavia/tests/unit/api/v2/types/test_listeners.py
Normal file
137
octavia/tests/unit/api/v2/types/test_listeners.py
Normal file
@ -0,0 +1,137 @@
|
||||
# Copyright 2014 Rackspace
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_utils import uuidutils
|
||||
from wsme import exc
|
||||
from wsme.rest import json as wsme_json
|
||||
from wsme import types as wsme_types
|
||||
|
||||
from octavia.api.v2.types import listener as lis_type
|
||||
from octavia.common import constants
|
||||
from octavia.tests.unit.api.common import base
|
||||
|
||||
|
||||
class TestListener(object):
|
||||
|
||||
_type = None
|
||||
|
||||
def test_invalid_name(self):
|
||||
body = {"name": 0}
|
||||
if self._type is lis_type.ListenerPOST:
|
||||
body.update({"protocol": constants.PROTOCOL_HTTP,
|
||||
"protocol_port": 80,
|
||||
"loadbalancer_id": uuidutils.generate_uuid()})
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_description(self):
|
||||
body = {"description": 0}
|
||||
if self._type is lis_type.ListenerPOST:
|
||||
body.update({"protocol": constants.PROTOCOL_HTTP,
|
||||
"protocol_port": 80,
|
||||
"loadbalancer_id": uuidutils.generate_uuid()})
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_admin_state_up(self):
|
||||
body = {"admin_state_up": "notvalid"}
|
||||
if self._type is lis_type.ListenerPOST:
|
||||
body.update({"protocol": constants.PROTOCOL_HTTP,
|
||||
"protocol_port": 80,
|
||||
"loadbalancer_id": uuidutils.generate_uuid()})
|
||||
self.assertRaises(ValueError, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_connection_limit(self):
|
||||
body = {"connection_limit": "test"}
|
||||
if self._type is lis_type.ListenerPOST:
|
||||
body.update({"protocol": constants.PROTOCOL_HTTP,
|
||||
"protocol_port": 80,
|
||||
"loadbalancer_id": uuidutils.generate_uuid()})
|
||||
self.assertRaises(ValueError, wsme_json.fromjson, self._type, body)
|
||||
|
||||
|
||||
class TestListenerPOST(base.BaseTypesTest, TestListener):
|
||||
|
||||
_type = lis_type.ListenerPOST
|
||||
|
||||
def test_listener(self):
|
||||
body = {"name": "test", "description": "test", "connection_limit": 10,
|
||||
"protocol": constants.PROTOCOL_HTTP, "protocol_port": 80,
|
||||
"default_pool_id": uuidutils.generate_uuid(),
|
||||
"loadbalancer_id": uuidutils.generate_uuid()}
|
||||
listener = wsme_json.fromjson(self._type, body)
|
||||
self.assertTrue(listener.admin_state_up)
|
||||
|
||||
def test_protocol_mandatory(self):
|
||||
body = {"protocol_port": 80,
|
||||
"loadbalancer_id": uuidutils.generate_uuid()}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_protocol_port_mandatory(self):
|
||||
body = {"protocol": constants.PROTOCOL_HTTP,
|
||||
"loadbalancer_id": uuidutils.generate_uuid()}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_protocol(self):
|
||||
body = {"protocol": "http", "protocol_port": 80}
|
||||
if self._type is lis_type.ListenerPOST:
|
||||
body.update({"loadbalancer_id": uuidutils.generate_uuid()})
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_protocol_port(self):
|
||||
body = {"protocol": constants.PROTOCOL_HTTP, "protocol_port": "test"}
|
||||
if self._type is lis_type.ListenerPOST:
|
||||
body.update({"loadbalancer_id": uuidutils.generate_uuid()})
|
||||
self.assertRaises(ValueError, wsme_json.fromjson, self._type, body)
|
||||
|
||||
def test_loadbalancer_id_mandatory(self):
|
||||
body = {"protocol": constants.PROTOCOL_HTTP,
|
||||
"protocol_port": 80}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_loadbalancer_id(self):
|
||||
body = {"protocol": constants.PROTOCOL_HTTP, "protocol_port": 80,
|
||||
"loadbalancer_id": "a"}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_non_uuid_project_id(self):
|
||||
body = {"name": "test", "description": "test", "connection_limit": 10,
|
||||
"protocol": constants.PROTOCOL_HTTP, "protocol_port": 80,
|
||||
"default_pool_id": uuidutils.generate_uuid(),
|
||||
"loadbalancer_id": uuidutils.generate_uuid(),
|
||||
"project_id": "non-uuid"}
|
||||
listener = wsme_json.fromjson(self._type, body)
|
||||
self.assertEqual(listener.project_id, body['project_id'])
|
||||
|
||||
|
||||
class TestListenerPUT(base.BaseTypesTest, TestListener):
|
||||
|
||||
_type = lis_type.ListenerPUT
|
||||
|
||||
def test_listener(self):
|
||||
body = {"name": "test", "description": "test",
|
||||
"connection_limit": 10,
|
||||
"default_tls_container_ref": uuidutils.generate_uuid(),
|
||||
"sni_container_refs": [uuidutils.generate_uuid(),
|
||||
uuidutils.generate_uuid()],
|
||||
"default_pool_id": uuidutils.generate_uuid(),
|
||||
"insert_headers": {"a": "1", "b": "2"}}
|
||||
listener = wsme_json.fromjson(self._type, body)
|
||||
self.assertEqual(wsme_types.Unset, listener.admin_state_up)
|
Loading…
Reference in New Issue
Block a user