Implementing simple operator API
Setup executable to use wsgiref.simple_server Simple setup of pecan Defined controllers that define the API resources Implemented all controllers to manipulate database and send to handler Added repository methods required for creating multiple items in one transaction Defined a few API exceptions based of wsme exceptions Defined the wsme types that define the resource response and request bodies Defined an abstract handler that all handlers should subclass Defined a simple handler that is responsible for sending to controller Added some wsme type tests Implements: bp/operator-api Change-Id: I0d91934db47a6e45f0c9ac22089f8689957bd239
This commit is contained in:
parent
9df9ff9137
commit
9786021205
0
bin/__init__.py
Normal file
0
bin/__init__.py
Normal file
39
bin/octavia-api.py
Executable file
39
bin/octavia-api.py
Executable file
@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# 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.
|
||||
|
||||
import sys
|
||||
from wsgiref import simple_server
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
from octavia.api import app as api_app
|
||||
from octavia.common import service as octavia_service
|
||||
from octavia.i18n import _LI
|
||||
from octavia.openstack.common import log as logging
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
if __name__ == '__main__':
|
||||
octavia_service.prepare_service(sys.argv)
|
||||
app = api_app.setup_app()
|
||||
|
||||
host, port = cfg.CONF.bind_host, cfg.CONF.bind_port
|
||||
LOG.info(_LI("Starting API server on %(host)s:%(port)s") %
|
||||
{"host": host, "port": port})
|
||||
srv = simple_server.make_server(host, port, app)
|
||||
|
||||
srv.serve_forever()
|
@ -3,6 +3,8 @@
|
||||
# verbose = False
|
||||
# Print debugging output (set logging level to DEBUG instead of default WARNING level).
|
||||
# debug = False
|
||||
# bind_host = 0.0.0.0
|
||||
# bind_port = 9191
|
||||
|
||||
[database]
|
||||
# This line MUST be changed to actually run the plugin.
|
||||
|
0
octavia/api/__init__.py
Normal file
0
octavia/api/__init__.py
Normal file
40
octavia/api/app.py
Normal file
40
octavia/api/app.py
Normal file
@ -0,0 +1,40 @@
|
||||
# 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.
|
||||
|
||||
import pecan
|
||||
|
||||
from octavia.api import config as app_config
|
||||
from octavia.api.v1 import hooks
|
||||
|
||||
|
||||
def get_pecan_config():
|
||||
"""Returns the pecan config."""
|
||||
filename = app_config.__file__.replace('.pyc', '.py')
|
||||
return pecan.configuration.conf_from_file(filename)
|
||||
|
||||
|
||||
def setup_app(pecan_config=None, debug=False):
|
||||
"""Creates and returns a pecan wsgi app."""
|
||||
app_hooks = [hooks.ContextHook()]
|
||||
|
||||
if not pecan_config:
|
||||
pecan_config = get_pecan_config()
|
||||
pecan.configuration.set_config(dict(pecan_config), overwrite=True)
|
||||
|
||||
return pecan.make_app(
|
||||
pecan_config.app.root,
|
||||
debug=debug,
|
||||
hooks=app_hooks,
|
||||
wsme=pecan_config.wsme
|
||||
)
|
28
octavia/api/config.py
Normal file
28
octavia/api/config.py
Normal file
@ -0,0 +1,28 @@
|
||||
# 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.
|
||||
|
||||
# Pecan Application Configurations
|
||||
# See https://pecan.readthedocs.org/en/latest/configuration.html#application-configuration # noqa
|
||||
app = {
|
||||
'root': 'octavia.api.root_controller.RootController',
|
||||
'modules': ['octavia.api'],
|
||||
'debug': False
|
||||
}
|
||||
|
||||
# WSME Configurations
|
||||
# See https://wsme.readthedocs.org/en/latest/integrate.html#configuration
|
||||
wsme = {
|
||||
# Keeping debug True for now so we can easily troubleshoot.
|
||||
'debug': True
|
||||
}
|
32
octavia/api/root_controller.py
Normal file
32
octavia/api/root_controller.py
Normal file
@ -0,0 +1,32 @@
|
||||
# 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 pecan import rest
|
||||
from wsme import types as wtypes
|
||||
from wsmeext import pecan as wsme_pecan
|
||||
|
||||
from octavia.api.v1 import controllers
|
||||
|
||||
|
||||
class RootController(rest.RestController):
|
||||
"""The controller in which the pecan wsgi app should be created with."""
|
||||
v1 = controllers.V1Controller()
|
||||
|
||||
@wsme_pecan.wsexpose(wtypes.text)
|
||||
def get(self):
|
||||
# TODO(blogan): once a decision is made on how to do versions, do that
|
||||
# here
|
||||
return {'versions': [{'status': 'CURRENT',
|
||||
'updated': '2014-12-11T00:00:00Z',
|
||||
'id': 'v1'}]}
|
0
octavia/api/v1/__init__.py
Normal file
0
octavia/api/v1/__init__.py
Normal file
29
octavia/api/v1/controllers/__init__.py
Normal file
29
octavia/api/v1/controllers/__init__.py
Normal file
@ -0,0 +1,29 @@
|
||||
# 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 wsmeext import pecan as wsme_pecan
|
||||
|
||||
from octavia.api.v1.controllers import base
|
||||
from octavia.api.v1.controllers import load_balancer
|
||||
|
||||
|
||||
class V1Controller(base.BaseController):
|
||||
|
||||
loadbalancers = load_balancer.LoadBalancersController()
|
||||
|
||||
@wsme_pecan.wsexpose(wtypes.text)
|
||||
def get(self):
|
||||
# TODO(blogan): decide what exactly should be here, if anything
|
||||
return "V1"
|
52
octavia/api/v1/controllers/base.py
Normal file
52
octavia/api/v1/controllers/base.py
Normal file
@ -0,0 +1,52 @@
|
||||
# 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 pecan import rest
|
||||
|
||||
from octavia.api.v1.handlers.controller_simulator import handler
|
||||
from octavia.api.v1.types import load_balancer as lb_types
|
||||
from octavia.api.v1.types import pool as pool_types
|
||||
from octavia.db import repositories
|
||||
|
||||
|
||||
class BaseController(rest.RestController):
|
||||
|
||||
def __init__(self):
|
||||
self.handler = handler.SimulatedControllerHandler()
|
||||
self.repositories = repositories.Repositories()
|
||||
|
||||
def _convert_db_to_type(self, db_entity, to_type):
|
||||
"""Converts a data model into a Octavia WSME type
|
||||
|
||||
:param db_entity: data model to convert
|
||||
:param to_type: converts db_entity to this time
|
||||
"""
|
||||
if isinstance(to_type, list):
|
||||
to_type = to_type[0]
|
||||
|
||||
def _convert(db_obj):
|
||||
api_type = to_type.from_data_model(db_obj)
|
||||
if to_type == lb_types.LoadBalancerResponse:
|
||||
api_type.vip = lb_types.VIP.from_data_model(db_obj.vip)
|
||||
elif (to_type == pool_types.PoolResponse
|
||||
and db_obj.session_persistence):
|
||||
api_type.session_persistence = (
|
||||
pool_types.SessionPersistenceResponse.from_data_model(
|
||||
db_obj.session_persistence))
|
||||
return api_type
|
||||
if isinstance(db_entity, list):
|
||||
converted = [_convert(db_obj) for db_obj in db_entity]
|
||||
else:
|
||||
converted = _convert(db_entity)
|
||||
return converted
|
205
octavia/api/v1/controllers/health_monitor.py
Normal file
205
octavia/api/v1/controllers/health_monitor.py
Normal file
@ -0,0 +1,205 @@
|
||||
# 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.
|
||||
|
||||
import logging
|
||||
|
||||
from oslo.db import exception as odb_exceptions
|
||||
from oslo.utils import excutils
|
||||
from wsmeext import pecan as wsme_pecan
|
||||
|
||||
from octavia.api.v1.controllers import base
|
||||
from octavia.api.v1.types import health_monitor as hm_types
|
||||
from octavia.common import constants
|
||||
from octavia.common import data_models
|
||||
from octavia.common import exceptions
|
||||
from octavia.db import api as db_api
|
||||
from octavia.i18n import _LI
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HealthMonitorController(base.BaseController):
|
||||
|
||||
def __init__(self, load_balancer_id, listener_id, pool_id):
|
||||
super(HealthMonitorController, self).__init__()
|
||||
self.load_balancer_id = load_balancer_id
|
||||
self.listener_id = listener_id
|
||||
self.pool_id = pool_id
|
||||
self.handler = self.handler.health_monitor
|
||||
|
||||
@wsme_pecan.wsexpose(hm_types.HealthMonitorResponse)
|
||||
def get_all(self):
|
||||
"""Gets a single health monitor's details."""
|
||||
# NOTE(blogan): since a pool can only have one health monitor
|
||||
# we are using the get_all method to only get the single health monitor
|
||||
session = db_api.get_session()
|
||||
db_hm = self.repositories.health_monitor.get(
|
||||
session, pool_id=self.pool_id)
|
||||
if not db_hm:
|
||||
LOG.info(_LI("Health Monitor for Pool %s was not found") %
|
||||
self.pool_id)
|
||||
raise exceptions.NotFound(
|
||||
resource=data_models.HealthMonitor._name(), id=id)
|
||||
return self._convert_db_to_type(db_hm, hm_types.HealthMonitorResponse)
|
||||
|
||||
@wsme_pecan.wsexpose(hm_types.HealthMonitorResponse,
|
||||
body=hm_types.HealthMonitorPOST, status_code=202)
|
||||
def post(self, health_monitor):
|
||||
"""Creates a health monitor on a pool."""
|
||||
session = db_api.get_session()
|
||||
try:
|
||||
db_hm = self.repositories.health_monitor.get(
|
||||
session, pool_id=self.pool_id)
|
||||
if db_hm:
|
||||
raise exceptions.DuplicateHealthMonitor()
|
||||
except exceptions.NotFound:
|
||||
pass
|
||||
hm_dict = health_monitor.to_dict()
|
||||
hm_dict['pool_id'] = self.pool_id
|
||||
# Verify load balancer is in a mutable status. If so it can be assumed
|
||||
# that the listener is also in a mutable status because a load balancer
|
||||
# will only be ACTIVE when all it's listeners as ACTIVE.
|
||||
if not self.repositories.test_and_set_lb_and_listener_prov_status(
|
||||
session, self.load_balancer_id, self.listener_id,
|
||||
constants.PENDING_UPDATE, constants.PENDING_UPDATE):
|
||||
LOG.info(_LI("Health Monitor for Pool %s cannot be updated "
|
||||
"because the Load Balancer is immutable.") %
|
||||
self.pool_id)
|
||||
lb_repo = self.repositories.load_balancer
|
||||
db_lb = lb_repo.get(session, id=self.load_balancer_id)
|
||||
raise exceptions.ImmutableObject(resource=db_lb._name(),
|
||||
id=self.load_balancer_id)
|
||||
try:
|
||||
db_hm = self.repositories.health_monitor.create(session, **hm_dict)
|
||||
except odb_exceptions.DBError:
|
||||
# Setting LB and Listener back to active because this is just a
|
||||
# validation failure
|
||||
self.repositories.load_balancer.update(
|
||||
session, self.load_balancer_id,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
self.repositories.listener.update(
|
||||
session, self.listener_id,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
raise exceptions.InvalidOption(value=hm_dict.get('type'),
|
||||
option='type')
|
||||
try:
|
||||
LOG.info(_LI("Sending Creation of Health Monitor for Pool %s to "
|
||||
"handler") % self.pool_id)
|
||||
self.handler.create(db_hm)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception(reraise=False):
|
||||
self.repositories.listener.update(
|
||||
session, self.listener_id,
|
||||
operating_status=constants.ERROR)
|
||||
db_hm = self.repositories.health_monitor.get(
|
||||
session, pool_id=self.pool_id)
|
||||
return self._convert_db_to_type(db_hm, hm_types.HealthMonitorResponse)
|
||||
|
||||
@wsme_pecan.wsexpose(hm_types.HealthMonitorResponse,
|
||||
body=hm_types.HealthMonitorPUT, status_code=202)
|
||||
def put(self, health_monitor):
|
||||
"""Updates a health monitor.
|
||||
|
||||
Updates a health monitor on a pool if it exists. Only one health
|
||||
monitor is allowed per pool so there is no need for a health monitor
|
||||
id.
|
||||
"""
|
||||
session = db_api.get_session()
|
||||
db_hm = self.repositories.health_monitor.get(
|
||||
session, pool_id=self.pool_id)
|
||||
if not db_hm:
|
||||
LOG.info(_LI("Health Monitor for Pool %s was not found") %
|
||||
self.pool_id)
|
||||
raise exceptions.NotFound(
|
||||
resource=data_models.HealthMonitor._name(), id=id)
|
||||
hm_dict = health_monitor.to_dict(render_unsets=False)
|
||||
# Verify load balancer is in a mutable status. If so it can be assumed
|
||||
# that the listener is also in a mutable status because a load balancer
|
||||
# will only be ACTIVE when all it's listeners as ACTIVE.
|
||||
if not self.repositories.test_and_set_lb_and_listener_prov_status(
|
||||
session, self.load_balancer_id, self.listener_id,
|
||||
constants.PENDING_UPDATE, constants.PENDING_UPDATE):
|
||||
LOG.info(_LI("Health Monitor for Pool %s cannot be updated "
|
||||
"because the Load Balancer is immutable.") %
|
||||
self.pool_id)
|
||||
lb_repo = self.repositories.load_balancer
|
||||
db_lb = lb_repo.get(session, id=self.load_balancer_id)
|
||||
raise exceptions.ImmutableObject(resource=db_lb._name(),
|
||||
id=self.load_balancer_id)
|
||||
try:
|
||||
self.repositories.health_monitor.update(
|
||||
session, self.pool_id, **hm_dict)
|
||||
except odb_exceptions.DBError:
|
||||
# Setting LB and Listener back to active because this is just a
|
||||
# validation failure
|
||||
self.repositories.load_balancer.update(
|
||||
session, self.load_balancer_id,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
self.repositories.listener.update(
|
||||
session, self.listener_id,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
raise exceptions.InvalidOption(value=hm_dict.get('type'),
|
||||
option='type')
|
||||
db_hm = self.repositories.health_monitor.get(
|
||||
session, pool_id=self.pool_id)
|
||||
try:
|
||||
LOG.info(_LI("Sending Update of Health Monitor for Pool %s to "
|
||||
"handler") % self.pool_id)
|
||||
self.handler.update(db_hm)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception(reraise=False):
|
||||
self.repositories.listener.update(
|
||||
session, self.listener_id,
|
||||
operating_status=constants.ERROR)
|
||||
db_hm = self.repositories.health_monitor.get(
|
||||
session, pool_id=self.pool_id)
|
||||
return self._convert_db_to_type(db_hm, hm_types.HealthMonitorResponse)
|
||||
|
||||
@wsme_pecan.wsexpose(None, status_code=202)
|
||||
def delete(self):
|
||||
"""Deletes a health monitor."""
|
||||
session = db_api.get_session()
|
||||
db_hm = self.repositories.health_monitor.get(
|
||||
session, pool_id=self.pool_id)
|
||||
if not db_hm:
|
||||
LOG.info(_LI("Health Monitor for Pool %s cannot be updated "
|
||||
"because the Load Balancer is immutable.") %
|
||||
self.pool_id)
|
||||
raise exceptions.NotFound(
|
||||
resource=data_models.HealthMonitor._name(), id=id)
|
||||
# Verify load balancer is in a mutable status. If so it can be assumed
|
||||
# that the listener is also in a mutable status because a load balancer
|
||||
# will only be ACTIVE when all it's listeners as ACTIVE.
|
||||
if not self.repositories.test_and_set_lb_and_listener_prov_status(
|
||||
session, self.load_balancer_id, self.listener_id,
|
||||
constants.PENDING_UPDATE, constants.PENDING_UPDATE):
|
||||
lb_repo = self.repositories.load_balancer
|
||||
db_lb = lb_repo.get(session, id=self.load_balancer_id)
|
||||
raise exceptions.ImmutableObject(resource=db_lb._name(),
|
||||
id=self.load_balancer_id)
|
||||
db_hm = self.repositories.health_monitor.get(session,
|
||||
pool_id=self.pool_id)
|
||||
try:
|
||||
LOG.info(_LI("Sending Deletion of Health Monitor for Pool %s to "
|
||||
"handler") % self.pool_id)
|
||||
self.handler.delete(db_hm)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception(reraise=False):
|
||||
self.repositories.listener.update(
|
||||
session, self.listener_id,
|
||||
operating_status=constants.ERROR)
|
||||
db_hm = self.repositories.health_monitor.get(
|
||||
session, pool_id=self.pool_id)
|
||||
return self._convert_db_to_type(db_hm, hm_types.HealthMonitorResponse)
|
240
octavia/api/v1/controllers/listener.py
Normal file
240
octavia/api/v1/controllers/listener.py
Normal file
@ -0,0 +1,240 @@
|
||||
# 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.
|
||||
|
||||
import logging
|
||||
|
||||
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.v1.controllers import base
|
||||
from octavia.api.v1.controllers import pool
|
||||
from octavia.api.v1.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 api as db_api
|
||||
from octavia.i18n import _LI
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ListenersController(base.BaseController):
|
||||
|
||||
def __init__(self, load_balancer_id):
|
||||
super(ListenersController, self).__init__()
|
||||
self.load_balancer_id = load_balancer_id
|
||||
self.handler = self.handler.listener
|
||||
|
||||
def _secure_data(self, listener):
|
||||
# TODO(blogan): Handle this data when certificate management code is
|
||||
# available
|
||||
listener.tls_termination = wtypes.Unset
|
||||
|
||||
@wsme_pecan.wsexpose(listener_types.ListenerResponse, wtypes.text)
|
||||
def get_one(self, id):
|
||||
"""Gets a single listener's details."""
|
||||
session = db_api.get_session()
|
||||
db_listener = self.repositories.listener.get(
|
||||
session, load_balancer_id=self.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 self._convert_db_to_type(db_listener,
|
||||
listener_types.ListenerResponse)
|
||||
|
||||
@wsme_pecan.wsexpose([listener_types.ListenerResponse])
|
||||
def get_all(self):
|
||||
"""Lists all listeners on a load balancer."""
|
||||
session = db_api.get_session()
|
||||
db_listeners = self.repositories.listener.get_all(
|
||||
session, load_balancer_id=self.load_balancer_id)
|
||||
return self._convert_db_to_type(db_listeners,
|
||||
[listener_types.ListenerResponse])
|
||||
|
||||
@wsme_pecan.wsexpose(listener_types.ListenerResponse,
|
||||
body=listener_types.ListenerPOST, status_code=202)
|
||||
def post(self, listener):
|
||||
"""Creates a listener on a load balancer."""
|
||||
self._secure_data(listener)
|
||||
session = db_api.get_session()
|
||||
lb_repo = self.repositories.load_balancer
|
||||
if not lb_repo.test_and_set_provisioning_status(
|
||||
session, self.load_balancer_id, constants.PENDING_UPDATE):
|
||||
db_lb = lb_repo.get(session, id=self.load_balancer_id)
|
||||
LOG.info(_LI("Load Balancer %s is immutable.") % db_lb.id)
|
||||
raise exceptions.ImmutableObject(resource=db_lb._name(),
|
||||
id=self.load_balancer_id)
|
||||
listener_dict = listener.to_dict()
|
||||
listener_dict['load_balancer_id'] = self.load_balancer_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']
|
||||
# This is the extra validation layer for wrong protocol or duplicate
|
||||
# listeners on the same load balancer.
|
||||
try:
|
||||
db_listener = self.repositories.listener.create(
|
||||
session, **listener_dict)
|
||||
except odb_exceptions.DBDuplicateEntry:
|
||||
# Setting LB back to active because this is just a validation
|
||||
# failure
|
||||
lb_repo.update(session, self.load_balancer_id,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
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, self.load_balancer_id,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
raise exceptions.InvalidOption(value=listener_dict.get('protocol'),
|
||||
option='protocol')
|
||||
# Handler will be responsible for sending to controller
|
||||
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.repositories.listener.get(
|
||||
session, id=db_listener.id)
|
||||
return self._convert_db_to_type(db_listener,
|
||||
listener_types.ListenerResponse)
|
||||
|
||||
@wsme_pecan.wsexpose(listener_types.ListenerResponse, wtypes.text,
|
||||
body=listener_types.ListenerPUT, status_code=202)
|
||||
def put(self, id, listener):
|
||||
"""Updates a listener on a load balancer."""
|
||||
self._secure_data(listener)
|
||||
session = db_api.get_session()
|
||||
old_db_listener = self.repositories.listener.get(session, id=id)
|
||||
if not old_db_listener:
|
||||
LOG.info(_LI("Listener %s not found.") % id)
|
||||
raise exceptions.NotFound(
|
||||
resource=data_models.Listener._name(), id=id)
|
||||
# Verify load balancer is in a mutable status. If so it can be assumed
|
||||
# that the listener is also in a mutable status because a load balancer
|
||||
# will only be ACTIVE when all it's listeners as ACTIVE.
|
||||
if not self.repositories.test_and_set_lb_and_listener_prov_status(
|
||||
session, self.load_balancer_id, id, constants.PENDING_UPDATE,
|
||||
constants.PENDING_UPDATE):
|
||||
LOG.info(_LI("Load Balancer %s is immutable.") %
|
||||
self.load_balancer_id)
|
||||
lb_repo = self.repositories.load_balancer
|
||||
db_lb = lb_repo.get(session, id=self.load_balancer_id)
|
||||
raise exceptions.ImmutableObject(resource=db_lb._name(),
|
||||
id=self.load_balancer_id)
|
||||
listener_dict = listener.to_dict(render_unsets=False)
|
||||
listener_dict['operating_status'] = old_db_listener.operating_status
|
||||
# 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']
|
||||
try:
|
||||
self.repositories.listener.update(session, id, **listener_dict)
|
||||
except odb_exceptions.DBDuplicateEntry:
|
||||
# Setting LB and Listener back to active because this is just a
|
||||
# validation failure
|
||||
self.repositories.load_balancer.update(
|
||||
session, self.load_balancer_id,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
self.repositories.listener.update(
|
||||
session, id, provisioning_status=constants.ACTIVE)
|
||||
raise exceptions.DuplicateListenerEntry(
|
||||
port=listener_dict.get('protocol_port'))
|
||||
except odb_exceptions.DBError:
|
||||
# Setting LB and Listener back to active because this is just a
|
||||
# validation failure
|
||||
self.repositories.load_balancer.update(
|
||||
session, self.load_balancer_id,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
self.repositories.listener.update(
|
||||
session, id, provisioning_status=constants.ACTIVE)
|
||||
raise exceptions.InvalidOption(value=listener_dict.get('protocol'),
|
||||
option='protocol')
|
||||
db_listener = self.repositories.listener.get(session, id=id)
|
||||
try:
|
||||
LOG.info(_LI("Sending Update of Listener %s to handler") %
|
||||
db_listener.id)
|
||||
self.handler.update(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_lb = self.repositories.listener.get(session, id=db_listener.id)
|
||||
return self._convert_db_to_type(db_lb, listener_types.ListenerResponse)
|
||||
|
||||
@wsme_pecan.wsexpose(None, wtypes.text, status_code=202)
|
||||
def delete(self, id):
|
||||
"""Deletes a listener from a load balancer."""
|
||||
session = db_api.get_session()
|
||||
db_listener = self.repositories.listener.get(session, id=id)
|
||||
if not db_listener:
|
||||
LOG.info(_LI("Listener %s not found.") % id)
|
||||
raise exceptions.NotFound(
|
||||
resource=data_models.Listener._name(), id=id)
|
||||
# Verify load balancer is in a mutable status. If so it can be assumed
|
||||
# that the listener is also in a mutable status because a load balancer
|
||||
# will only be ACTIVE when all it's listeners as ACTIVE.
|
||||
if not self.repositories.test_and_set_lb_and_listener_prov_status(
|
||||
session, self.load_balancer_id, id, constants.PENDING_UPDATE,
|
||||
constants.PENDING_DELETE):
|
||||
lb_repo = self.repositories.load_balancer
|
||||
db_lb = lb_repo.get(session, id=self.load_balancer_id)
|
||||
raise exceptions.ImmutableObject(resource=db_lb._name(),
|
||||
id=self.load_balancer_id)
|
||||
db_listener = self.repositories.listener.get(session, id=id)
|
||||
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(
|
||||
session, db_listener.id,
|
||||
provisioning_status=constants.ERROR)
|
||||
db_listener = self.repositories.listener.get(
|
||||
session, id=db_listener.id)
|
||||
return self._convert_db_to_type(db_listener,
|
||||
listener_types.ListenerResponse)
|
||||
|
||||
@pecan.expose()
|
||||
def _lookup(self, listener_id, *remainder):
|
||||
"""Overriden pecan _lookup method for custom routing.
|
||||
|
||||
Verifies that the listener passed in the url exists, and if so decides
|
||||
which controller, if any, should control be passed.
|
||||
"""
|
||||
session = db_api.get_session()
|
||||
if listener_id and len(remainder) and remainder[0] == 'pools':
|
||||
remainder = remainder[1:]
|
||||
db_listener = self.repositories.listener.get(
|
||||
session, id=listener_id)
|
||||
if not db_listener:
|
||||
LOG.info(_LI("Listener %s not found.") % listener_id)
|
||||
raise exceptions.NotFound(
|
||||
resource=data_models.Listener._name(), id=listener_id)
|
||||
return pool.PoolsController(load_balancer_id=self.load_balancer_id,
|
||||
listener_id=db_listener.id), remainder
|
163
octavia/api/v1/controllers/load_balancer.py
Normal file
163
octavia/api/v1/controllers/load_balancer.py
Normal file
@ -0,0 +1,163 @@
|
||||
# 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 excutils
|
||||
import pecan
|
||||
from wsme import types as wtypes
|
||||
from wsmeext import pecan as wsme_pecan
|
||||
|
||||
from octavia.api.v1.controllers import base
|
||||
from octavia.api.v1.controllers import listener
|
||||
from octavia.api.v1.types import load_balancer as lb_types
|
||||
from octavia.common import constants
|
||||
from octavia.common import data_models
|
||||
from octavia.common import exceptions
|
||||
from octavia.db import api as db_api
|
||||
from octavia.i18n import _LI
|
||||
from octavia.openstack.common import log as logging
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LoadBalancersController(base.BaseController):
|
||||
|
||||
def __init__(self):
|
||||
super(LoadBalancersController, self).__init__()
|
||||
self.handler = self.handler.load_balancer
|
||||
|
||||
@wsme_pecan.wsexpose(lb_types.LoadBalancerResponse, wtypes.text)
|
||||
def get_one(self, id):
|
||||
"""Gets a single load balancer's details."""
|
||||
session = db_api.get_session()
|
||||
load_balancer = self.repositories.load_balancer.get(
|
||||
session, id=id)
|
||||
if not load_balancer:
|
||||
LOG.info(_LI("Load Balancer %s was not found.") % id)
|
||||
raise exceptions.NotFound(
|
||||
resource=data_models.LoadBalancer._name(), id=id)
|
||||
return self._convert_db_to_type(load_balancer,
|
||||
lb_types.LoadBalancerResponse)
|
||||
|
||||
@wsme_pecan.wsexpose([lb_types.LoadBalancerResponse], wtypes.text)
|
||||
def get_all(self, tenant_id=None):
|
||||
"""Lists all listeners on a load balancer."""
|
||||
# tenant_id is an optional query parameter
|
||||
session = db_api.get_session()
|
||||
load_balancers = self.repositories.load_balancer.get_all(
|
||||
session, tenant_id=tenant_id)
|
||||
return self._convert_db_to_type(load_balancers,
|
||||
[lb_types.LoadBalancerResponse])
|
||||
|
||||
@wsme_pecan.wsexpose(lb_types.LoadBalancerResponse,
|
||||
body=lb_types.LoadBalancerPOST, status_code=202)
|
||||
def post(self, load_balancer):
|
||||
"""Creates a load balancer."""
|
||||
session = db_api.get_session()
|
||||
lb_dict = load_balancer.to_dict()
|
||||
vip_dict = lb_dict.pop('vip')
|
||||
lb_dict['provisioning_status'] = constants.PENDING_CREATE
|
||||
lb_dict['operating_status'] = constants.OFFLINE
|
||||
db_lb = self.repositories.create_load_balancer_and_vip(
|
||||
session, lb_dict, vip_dict)
|
||||
# Handler will be responsible for sending to controller
|
||||
try:
|
||||
LOG.info(_LI("Sending created Load Balancer %s to the handler") %
|
||||
db_lb.id)
|
||||
self.handler.create(db_lb)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception(reraise=False):
|
||||
self.repositories.load_balancer.update(
|
||||
session, db_lb.id, provisioning_status=constants.ERROR)
|
||||
return self._convert_db_to_type(db_lb, lb_types.LoadBalancerResponse)
|
||||
|
||||
@wsme_pecan.wsexpose(lb_types.LoadBalancerResponse,
|
||||
wtypes.text, status_code=202,
|
||||
body=lb_types.LoadBalancerPUT)
|
||||
def put(self, id, load_balancer):
|
||||
"""Updates a load balancer."""
|
||||
session = db_api.get_session()
|
||||
# Purely to make lines smaller length
|
||||
lb_repo = self.repositories.load_balancer
|
||||
old_db_lb = self.repositories.load_balancer.get(session, id=id)
|
||||
if not old_db_lb:
|
||||
LOG.info(_LI("Load Balancer %s was not found.") % id)
|
||||
raise exceptions.NotFound(
|
||||
resource=data_models.LoadBalancer._name(), id=id)
|
||||
# Check load balancer is in a mutable status
|
||||
if not lb_repo.test_and_set_provisioning_status(
|
||||
session, id, constants.PENDING_UPDATE):
|
||||
LOG.info(_LI("Load Balancer %s is immutable.") % id)
|
||||
raise exceptions.ImmutableObject(resource=old_db_lb._name(),
|
||||
id=id)
|
||||
lb_dict = load_balancer.to_dict(render_unsets=False)
|
||||
lb_dict['operating_status'] = old_db_lb.operating_status
|
||||
self.repositories.load_balancer.update(
|
||||
session, id, **lb_dict)
|
||||
db_lb = self.repositories.load_balancer.get(session, id=id)
|
||||
try:
|
||||
LOG.info(_LI("Sending updated Load Balancer %s to the handler") %
|
||||
db_lb.id)
|
||||
self.handler.update(db_lb)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception(reraise=False):
|
||||
self.repositories.load_balancer.update(
|
||||
session, db_lb.id, provisioning_status=constants.ERROR)
|
||||
return self._convert_db_to_type(db_lb, lb_types.LoadBalancerResponse)
|
||||
|
||||
@wsme_pecan.wsexpose(None, wtypes.text, status_code=202)
|
||||
def delete(self, id):
|
||||
"""Deletes a load balancer."""
|
||||
session = db_api.get_session()
|
||||
# Purely to make lines smaller length
|
||||
lb_repo = self.repositories.load_balancer
|
||||
db_lb = self.repositories.load_balancer.get(session, id=id)
|
||||
if not db_lb:
|
||||
LOG.info(_LI("Load Balancer %s was not found.") % id)
|
||||
raise exceptions.NotFound(
|
||||
resource=data_models.LoadBalancer._name(), id=id)
|
||||
# Check load balancer is in a mutable status
|
||||
if not lb_repo.test_and_set_provisioning_status(
|
||||
session, id, constants.PENDING_DELETE):
|
||||
LOG.info(_LI("Load Balancer %s is immutable.") % id)
|
||||
raise exceptions.ImmutableObject(resource=db_lb._name(),
|
||||
id=id)
|
||||
db_lb = self.repositories.load_balancer.get(session, id=id)
|
||||
try:
|
||||
LOG.info(_LI("Sending deleted Load Balancer %s to the handler") %
|
||||
db_lb.id)
|
||||
self.handler.delete(db_lb)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception(reraise=False):
|
||||
self.repositories.load_balancer.update(
|
||||
session, db_lb.id, provisioning_status=constants.ERROR)
|
||||
return self._convert_db_to_type(db_lb, lb_types.LoadBalancerResponse)
|
||||
|
||||
@pecan.expose()
|
||||
def _lookup(self, lb_id, *remainder):
|
||||
"""Overriden pecan _lookup method for custom routing.
|
||||
|
||||
Verifies that the load balancer passed in the url exists, and if so
|
||||
decides which controller, if any, should control be passed.
|
||||
"""
|
||||
session = db_api.get_session()
|
||||
if lb_id and len(remainder) and remainder[0] == 'listeners':
|
||||
remainder = remainder[1:]
|
||||
db_lb = self.repositories.load_balancer.get(session, id=lb_id)
|
||||
if not db_lb:
|
||||
LOG.info(_LI("Load Balancer %s was not found.") % lb_id)
|
||||
raise exceptions.NotFound(
|
||||
resource=data_models.LoadBalancer._name(), id=lb_id)
|
||||
return listener.ListenersController(
|
||||
load_balancer_id=db_lb.id), remainder
|
193
octavia/api/v1/controllers/member.py
Normal file
193
octavia/api/v1/controllers/member.py
Normal file
@ -0,0 +1,193 @@
|
||||
# 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.
|
||||
|
||||
import logging
|
||||
|
||||
import oslo.db.exception as oslo_exc
|
||||
from oslo.utils import excutils
|
||||
from wsme import types as wtypes
|
||||
from wsmeext import pecan as wsme_pecan
|
||||
|
||||
from octavia.api.v1.controllers import base
|
||||
from octavia.api.v1.types import member as member_types
|
||||
from octavia.common import constants
|
||||
from octavia.common import data_models
|
||||
from octavia.common import exceptions
|
||||
from octavia.db import api as db_api
|
||||
from octavia.i18n import _LI
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MembersController(base.BaseController):
|
||||
|
||||
def __init__(self, load_balancer_id, listener_id, pool_id):
|
||||
super(MembersController, self).__init__()
|
||||
self.load_balancer_id = load_balancer_id
|
||||
self.listener_id = listener_id
|
||||
self.pool_id = pool_id
|
||||
self.handler = self.handler.member
|
||||
|
||||
@wsme_pecan.wsexpose(member_types.MemberResponse, wtypes.text)
|
||||
def get(self, id):
|
||||
"""Gets a single pool member's details."""
|
||||
session = db_api.get_session()
|
||||
db_member = self.repositories.member.get(session, id=id)
|
||||
if not db_member:
|
||||
LOG.info(_LI("Member %s not found") % id)
|
||||
raise exceptions.NotFound(
|
||||
resource=data_models.Member._name(), id=id)
|
||||
return self._convert_db_to_type(db_member, member_types.MemberResponse)
|
||||
|
||||
@wsme_pecan.wsexpose([member_types.MemberResponse])
|
||||
def get_all(self):
|
||||
"""Lists all pool members of a pool."""
|
||||
session = db_api.get_session()
|
||||
db_members = self.repositories.member.get_all(
|
||||
session, pool_id=self.pool_id)
|
||||
return self._convert_db_to_type(db_members,
|
||||
[member_types.MemberResponse])
|
||||
|
||||
@wsme_pecan.wsexpose(member_types.MemberResponse,
|
||||
body=member_types.MemberPOST, status_code=202)
|
||||
def post(self, member):
|
||||
"""Creates a pool member on a pool."""
|
||||
session = db_api.get_session()
|
||||
member_dict = member.to_dict()
|
||||
member_dict['pool_id'] = self.pool_id
|
||||
member_dict['operating_status'] = constants.OFFLINE
|
||||
# Verify load balancer is in a mutable status. If so it can be assumed
|
||||
# that the listener is also in a mutable status because a load balancer
|
||||
# will only be ACTIVE when all it's listeners as ACTIVE.
|
||||
if not self.repositories.test_and_set_lb_and_listener_prov_status(
|
||||
session, self.load_balancer_id, self.listener_id,
|
||||
constants.PENDING_UPDATE, constants.PENDING_UPDATE):
|
||||
LOG.info(_LI("Member cannot be created because it's Load "
|
||||
"Balancer is in an immutable state."))
|
||||
lb_repo = self.repositories.load_balancer
|
||||
db_lb = lb_repo.get(session, id=self.load_balancer_id)
|
||||
raise exceptions.ImmutableObject(resource=db_lb._name(),
|
||||
id=self.load_balancer_id)
|
||||
try:
|
||||
db_member = self.repositories.member.create(session, **member_dict)
|
||||
except oslo_exc.DBDuplicateEntry:
|
||||
# Setting LB and Listener back to active because this is just a
|
||||
# validation failure
|
||||
self.repositories.load_balancer.update(
|
||||
session, self.load_balancer_id,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
self.repositories.listener.update(
|
||||
session, self.listener_id,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
raise exceptions.DuplicateMemberEntry(
|
||||
ip_address=member_dict.get('ip_address'),
|
||||
port=member_dict.get('protocol_port'))
|
||||
try:
|
||||
LOG.info(_LI("Sending Creation of Member %s to handler") %
|
||||
db_member.id)
|
||||
self.handler.create(db_member)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception(reraise=False):
|
||||
self.repositories.listener.update(
|
||||
session, self.listener_id,
|
||||
operating_status=constants.ERROR)
|
||||
db_member = self.repositories.member.get(session, id=db_member.id)
|
||||
return self._convert_db_to_type(db_member, member_types.MemberResponse)
|
||||
|
||||
@wsme_pecan.wsexpose(member_types.MemberResponse,
|
||||
wtypes.text, body=member_types.MemberPUT,
|
||||
status_code=202)
|
||||
def put(self, id, member):
|
||||
"""Updates a pool member."""
|
||||
session = db_api.get_session()
|
||||
old_db_member = self.repositories.member.get(session, id=id)
|
||||
if not old_db_member:
|
||||
LOG.info(_LI("Member %s cannot be updated because it's Load "
|
||||
"Balancer is in an immutable state.") % id)
|
||||
LOG.info(_LI("Member %s not found") % id)
|
||||
raise exceptions.NotFound(
|
||||
resource=data_models.Member._name(), id=id)
|
||||
member_dict = member.to_dict(render_unsets=False)
|
||||
member_dict['operating_status'] = old_db_member.operating_status
|
||||
# Verify load balancer is in a mutable status. If so it can be assumed
|
||||
# that the listener is also in a mutable status because a load balancer
|
||||
# will only be ACTIVE when all it's listeners as ACTIVE.
|
||||
if not self.repositories.test_and_set_lb_and_listener_prov_status(
|
||||
session, self.load_balancer_id, self.listener_id,
|
||||
constants.PENDING_UPDATE, constants.PENDING_UPDATE):
|
||||
lb_repo = self.repositories.load_balancer
|
||||
db_lb = lb_repo.get(session, id=self.load_balancer_id)
|
||||
raise exceptions.ImmutableObject(resource=db_lb._name(),
|
||||
id=self.load_balancer_id)
|
||||
try:
|
||||
self.repositories.member.update(session, id, **member_dict)
|
||||
except oslo_exc.DBDuplicateEntry:
|
||||
# Setting LB and Listener back to active because this is just a
|
||||
# validation failure
|
||||
self.repositories.load_balancer.update(
|
||||
session, self.load_balancer_id,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
self.repositories.listener.update(
|
||||
session, self.listener_id,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
raise exceptions.DuplicateMemberEntry(
|
||||
ip_address=member_dict.get('ip_address'),
|
||||
port=member_dict.get('protocol_port'))
|
||||
db_member = self.repositories.member.get(session, id=id)
|
||||
try:
|
||||
LOG.info(_LI("Sending Update of Member %s to handler") %
|
||||
db_member.id)
|
||||
self.handler.update(db_member)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception(reraise=False):
|
||||
self.repositories.listener.update(
|
||||
session, self.listener_id,
|
||||
operating_status=constants.ERROR)
|
||||
db_member = self.repositories.member.get(session, id=db_member.id)
|
||||
return self._convert_db_to_type(db_member, member_types.MemberResponse)
|
||||
|
||||
@wsme_pecan.wsexpose(None, wtypes.text, status_code=202)
|
||||
def delete(self, id):
|
||||
"""Deletes a pool member."""
|
||||
session = db_api.get_session()
|
||||
db_member = self.repositories.member.get(session, id=id)
|
||||
if not db_member:
|
||||
LOG.info(_LI("Member %s not found") % id)
|
||||
raise exceptions.NotFound(
|
||||
resource=data_models.Member._name(), id=id)
|
||||
# Verify load balancer is in a mutable status. If so it can be assumed
|
||||
# that the listener is also in a mutable status because a load balancer
|
||||
# will only be ACTIVE when all it's listeners as ACTIVE.
|
||||
if not self.repositories.test_and_set_lb_and_listener_prov_status(
|
||||
session, self.load_balancer_id, self.listener_id,
|
||||
constants.PENDING_UPDATE, constants.PENDING_UPDATE):
|
||||
LOG.info(_LI("Member %s cannot be deleted because it's Load "
|
||||
"Balancer is in an immutable state.") % id)
|
||||
lb_repo = self.repositories.load_balancer
|
||||
db_lb = lb_repo.get(session, id=self.load_balancer_id)
|
||||
raise exceptions.ImmutableObject(resource=db_lb._name(),
|
||||
id=self.load_balancer_id)
|
||||
db_member = self.repositories.member.get(session, id=id)
|
||||
try:
|
||||
LOG.info(_LI("Sending Deletion of Member %s to handler") %
|
||||
db_member.id)
|
||||
self.handler.delete(db_member)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception(reraise=False):
|
||||
self.repositories.listener.update(
|
||||
session, self.listener_id,
|
||||
operating_status=constants.ERROR)
|
||||
db_member = self.repositories.member.get(session, id=id)
|
||||
return self._convert_db_to_type(db_member, member_types.MemberResponse)
|
241
octavia/api/v1/controllers/pool.py
Normal file
241
octavia/api/v1/controllers/pool.py
Normal file
@ -0,0 +1,241 @@
|
||||
# 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.
|
||||
|
||||
import logging
|
||||
|
||||
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.v1.controllers import base
|
||||
from octavia.api.v1.controllers import health_monitor
|
||||
from octavia.api.v1.controllers import member
|
||||
from octavia.api.v1.types import pool as pool_types
|
||||
from octavia.common import constants
|
||||
from octavia.common import data_models
|
||||
from octavia.common import exceptions
|
||||
from octavia.db import api as db_api
|
||||
from octavia.i18n import _LI
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PoolsController(base.BaseController):
|
||||
|
||||
def __init__(self, load_balancer_id, listener_id):
|
||||
super(PoolsController, self).__init__()
|
||||
self.load_balancer_id = load_balancer_id
|
||||
self.listener_id = listener_id
|
||||
self.handler = self.handler.pool
|
||||
|
||||
@wsme_pecan.wsexpose(pool_types.PoolResponse, wtypes.text)
|
||||
def get(self, id):
|
||||
"""Gets a pool's details."""
|
||||
session = db_api.get_session()
|
||||
db_pool = self.repositories.pool.get(session, id=id)
|
||||
if not db_pool:
|
||||
LOG.info(_LI("Pool %s not found.") % id)
|
||||
raise exceptions.NotFound(resource=data_models.Pool._name(), id=id)
|
||||
return self._convert_db_to_type(db_pool, pool_types.PoolResponse)
|
||||
|
||||
@wsme_pecan.wsexpose([pool_types.PoolResponse])
|
||||
def get_all(self):
|
||||
"""Lists all pools on a listener."""
|
||||
session = db_api.get_session()
|
||||
default_pool = self.repositories.listener.get(
|
||||
session, id=self.listener_id).default_pool
|
||||
if default_pool:
|
||||
default_pool = [default_pool]
|
||||
else:
|
||||
default_pool = []
|
||||
return self._convert_db_to_type(default_pool,
|
||||
[pool_types.PoolResponse])
|
||||
|
||||
@wsme_pecan.wsexpose(pool_types.PoolResponse, body=pool_types.PoolPOST,
|
||||
status_code=202)
|
||||
def post(self, pool):
|
||||
"""Creates a pool on a listener.
|
||||
|
||||
This does not allow more than one pool to be on a listener so once one
|
||||
is created, another cannot be created until the first one has been
|
||||
deleted.
|
||||
"""
|
||||
session = db_api.get_session()
|
||||
if self.repositories.listener.has_pool(session, self.listener_id):
|
||||
raise exceptions.DuplicatePoolEntry()
|
||||
# Verify load balancer is in a mutable status. If so it can be assumed
|
||||
# that the listener is also in a mutable status because a load balancer
|
||||
# will only be ACTIVE when all it's listeners as ACTIVE.
|
||||
if not self.repositories.test_and_set_lb_and_listener_prov_status(
|
||||
session, self.load_balancer_id, self.listener_id,
|
||||
constants.PENDING_UPDATE, constants.PENDING_UPDATE):
|
||||
LOG.info(_LI("Pool cannot be created because the Load "
|
||||
"Balancer is in an immutable state"))
|
||||
lb_repo = self.repositories.load_balancer
|
||||
db_lb = lb_repo.get(session, id=self.load_balancer_id)
|
||||
raise exceptions.ImmutableObject(resource=db_lb._name(),
|
||||
id=self.load_balancer_id)
|
||||
pool_dict = pool.to_dict()
|
||||
sp_dict = pool_dict.pop('session_persistence', None)
|
||||
pool_dict['operating_status'] = constants.OFFLINE
|
||||
try:
|
||||
db_pool = self.repositories.create_pool_on_listener(
|
||||
session, self.listener_id, pool_dict, sp_dict=sp_dict)
|
||||
except odb_exceptions.DBError:
|
||||
# Setting LB and Listener back to active because this is just a
|
||||
# validation failure
|
||||
self.repositories.load_balancer.update(
|
||||
session, self.load_balancer_id,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
self.repositories.listener.update(
|
||||
session, self.listener_id,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
# TODO(blogan): will have to do separate validation protocol
|
||||
# before creation or update since the exception messages
|
||||
# do not give any information as to what constraint failed
|
||||
raise exceptions.InvalidOption(value='', option='')
|
||||
try:
|
||||
LOG.info(_LI("Sending Creation of Pool %s to handler") %
|
||||
db_pool.id)
|
||||
self.handler.create(db_pool)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception(reraise=False):
|
||||
self.repositories.listener.update(
|
||||
session, self.listener_id,
|
||||
operating_status=constants.ERROR)
|
||||
db_pool = self.repositories.pool.get(session, id=db_pool.id)
|
||||
return self._convert_db_to_type(db_pool, pool_types.PoolResponse)
|
||||
|
||||
@wsme_pecan.wsexpose(pool_types.PoolResponse, wtypes.text,
|
||||
body=pool_types.PoolPUT, status_code=202)
|
||||
def put(self, id, pool):
|
||||
"""Updates a pool on a listener."""
|
||||
session = db_api.get_session()
|
||||
old_db_pool = self.repositories.pool.get(session, id=id)
|
||||
if not old_db_pool:
|
||||
LOG.info(_LI("Pool %s not found.") % id)
|
||||
raise exceptions.NotFound(resource=data_models.Pool._name(), id=id)
|
||||
# Verify load balancer is in a mutable status. If so it can be assumed
|
||||
# that the listener is also in a mutable status because a load balancer
|
||||
# will only be ACTIVE when all it's listeners as ACTIVE.
|
||||
if not self.repositories.test_and_set_lb_and_listener_prov_status(
|
||||
session, self.load_balancer_id, self.listener_id,
|
||||
constants.PENDING_UPDATE, constants.PENDING_UPDATE):
|
||||
LOG.info(_LI("Pool %s cannot be updated because the Load "
|
||||
"Balancer is in an immutable state") % id)
|
||||
lb_repo = self.repositories.load_balancer
|
||||
db_lb = lb_repo.get(session, id=self.load_balancer_id)
|
||||
raise exceptions.ImmutableObject(resource=db_lb._name(),
|
||||
id=self.load_balancer_id)
|
||||
pool_dict = pool.to_dict(render_unsets=False)
|
||||
pool_dict['operating_status'] = old_db_pool.operating_status
|
||||
sp_dict = pool_dict.pop('session_persistence', None)
|
||||
try:
|
||||
self.repositories.update_pool_on_listener(session, id, pool_dict,
|
||||
sp_dict)
|
||||
except odb_exceptions.DBError:
|
||||
# Setting LB and Listener back to active because this is just a
|
||||
# validation failure
|
||||
self.repositories.load_balancer.update(
|
||||
session, self.load_balancer_id,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
self.repositories.listener.update(
|
||||
session, self.listener_id,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
# TODO(blogan): will have to do separate validation protocol
|
||||
# before creation or update since the exception messages
|
||||
# do not give any information as to what constraint failed
|
||||
raise exceptions.InvalidOption(value='', option='')
|
||||
db_pool = self.repositories.pool.get(session, id=id)
|
||||
try:
|
||||
LOG.info(_LI("Sending Update of Pool %s to handler") %
|
||||
db_pool.id)
|
||||
self.handler.update(db_pool)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception(reraise=False):
|
||||
self.repositories.listener.update(
|
||||
session, self.listener_id,
|
||||
operating_status=constants.ERROR)
|
||||
db_pool = self.repositories.pool.get(session, id=db_pool.id)
|
||||
return self._convert_db_to_type(db_pool, pool_types.PoolResponse)
|
||||
|
||||
@wsme_pecan.wsexpose(None, wtypes.text, status_code=202)
|
||||
def delete(self, id):
|
||||
"""Deletes a pool from a listener."""
|
||||
session = db_api.get_session()
|
||||
db_pool = self.repositories.pool.get(session, id=id)
|
||||
if not db_pool:
|
||||
LOG.info(_LI("Pool %s not found.") % id)
|
||||
raise exceptions.NotFound(resource=data_models.Pool._name(), id=id)
|
||||
# Verify load balancer is in a mutable status. If so it can be assumed
|
||||
# that the listener is also in a mutable status because a load balancer
|
||||
# will only be ACTIVE when all it's listeners as ACTIVE.
|
||||
if not self.repositories.test_and_set_lb_and_listener_prov_status(
|
||||
session, self.load_balancer_id, self.listener_id,
|
||||
constants.PENDING_UPDATE, constants.PENDING_UPDATE):
|
||||
LOG.info(_LI("Pool %s cannot be deleted because the Load "
|
||||
"Balancer is in an immutable state") % id)
|
||||
lb_repo = self.repositories.load_balancer
|
||||
db_lb = lb_repo.get(session, id=self.load_balancer_id)
|
||||
raise exceptions.ImmutableObject(resource=db_lb._name(),
|
||||
id=self.load_balancer_id)
|
||||
db_pool = self.repositories.pool.get(session, id=id)
|
||||
try:
|
||||
LOG.info(_LI("Sending Deletion of Pool %s to handler") %
|
||||
db_pool.id)
|
||||
self.handler.delete(db_pool)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception(reraise=False):
|
||||
self.repositories.listener.update(
|
||||
session, self.listener_id,
|
||||
operating_status=constants.ERROR)
|
||||
self.repositories.pool.update(
|
||||
session, db_pool.id,
|
||||
operating_status=constants.ERROR)
|
||||
db_pool = self.repositories.pool.get(session, id=db_pool.id)
|
||||
return self._convert_db_to_type(db_pool, pool_types.PoolResponse)
|
||||
|
||||
@pecan.expose()
|
||||
def _lookup(self, pool_id, *remainder):
|
||||
"""Overriden pecan _lookup method for custom routing.
|
||||
|
||||
Verifies that the pool passed in the url exists, and if so decides
|
||||
which controller, if any, should control be passed.
|
||||
"""
|
||||
session = db_api.get_session()
|
||||
if pool_id and len(remainder) and remainder[0] == 'members':
|
||||
remainder = remainder[1:]
|
||||
db_pool = self.repositories.pool.get(session, id=pool_id)
|
||||
if not db_pool:
|
||||
LOG.info(_LI("Pool %s not found.") % pool_id)
|
||||
raise exceptions.NotFound(resource=data_models.Pool._name(),
|
||||
id=pool_id)
|
||||
return member.MembersController(
|
||||
load_balancer_id=self.load_balancer_id,
|
||||
listener_id=self.listener_id,
|
||||
pool_id=db_pool.id), remainder
|
||||
if pool_id and len(remainder) and remainder[0] == 'healthmonitor':
|
||||
remainder = remainder[1:]
|
||||
db_pool = self.repositories.pool.get(session, id=pool_id)
|
||||
if not db_pool:
|
||||
LOG.info(_LI("Pool %s not found.") % pool_id)
|
||||
raise exceptions.NotFound(resource=data_models.Pool._name(),
|
||||
id=pool_id)
|
||||
return health_monitor.HealthMonitorController(
|
||||
load_balancer_id=self.load_balancer_id,
|
||||
listener_id=self.listener_id,
|
||||
pool_id=db_pool.id), remainder
|
0
octavia/api/v1/handlers/__init__.py
Normal file
0
octavia/api/v1/handlers/__init__.py
Normal file
62
octavia/api/v1/handlers/abstract_handler.py
Normal file
62
octavia/api/v1/handlers/abstract_handler.py
Normal file
@ -0,0 +1,62 @@
|
||||
# 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.
|
||||
|
||||
import abc
|
||||
|
||||
import six
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class BaseObjectHandler(object):
|
||||
"""Base class for any object handler."""
|
||||
@abc.abstractmethod
|
||||
def create(self, data_model):
|
||||
"""Begins process of actually creating data_model."""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def update(self, data_model):
|
||||
"""Begins process of actually updating data_model."""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete(self, data_model):
|
||||
"""Begins process of actually deleting data_model."""
|
||||
pass
|
||||
|
||||
|
||||
class NotImplementedObjectHandler(BaseObjectHandler):
|
||||
"""Default Object Handler to force implementation of subclasses.
|
||||
|
||||
Helper class to make any subclass of AbstractHandler explode if it
|
||||
is missing any of the required object managers.
|
||||
"""
|
||||
def update(self, data_model):
|
||||
raise NotImplementedError()
|
||||
|
||||
def delete(self, data_model):
|
||||
raise NotImplementedError()
|
||||
|
||||
def create(self, data_model):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class BaseHandler(object):
|
||||
"""Base class for all handlers."""
|
||||
load_balancer = NotImplementedObjectHandler()
|
||||
listener = NotImplementedObjectHandler()
|
||||
pool = NotImplementedObjectHandler()
|
||||
health_monitor = NotImplementedObjectHandler()
|
||||
member = NotImplementedObjectHandler()
|
249
octavia/api/v1/handlers/controller_simulator/handler.py
Normal file
249
octavia/api/v1/handlers/controller_simulator/handler.py
Normal file
@ -0,0 +1,249 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
This is just a handler that will simulate successful operations a controller
|
||||
should perform. There is nothing useful about this other than database
|
||||
entity status management.
|
||||
"""
|
||||
|
||||
import threading
|
||||
import time
|
||||
|
||||
from octavia.api.v1.handlers import abstract_handler
|
||||
from octavia.common import constants
|
||||
from octavia.common import data_models
|
||||
from octavia.db import api as db_api
|
||||
import octavia.db.repositories as repos
|
||||
from octavia.i18n import _LI
|
||||
from octavia.openstack.common import log as logging
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
ASYNC_TIME = 1
|
||||
|
||||
|
||||
def validate_input(expected, actual):
|
||||
if not isinstance(actual, expected):
|
||||
raise InvalidHandlerInputObject(obj_type=actual.__class__)
|
||||
|
||||
|
||||
def simulate_controller(data_model, delete=False):
|
||||
"""Simulates a successful controller operator for a data model.
|
||||
|
||||
:param data_model: data model to simulate controller operation
|
||||
:param delete: deletes from the database
|
||||
"""
|
||||
repo = repos.Repositories()
|
||||
|
||||
def controller(model, delete):
|
||||
|
||||
def session():
|
||||
return db_api.get_session()
|
||||
|
||||
time.sleep(ASYNC_TIME)
|
||||
LOG.info(_LI("Simulating controller operation for %(entity)s...") %
|
||||
{"entity": model.__class__.__name__})
|
||||
if isinstance(model, data_models.Member):
|
||||
if delete:
|
||||
repo.member.delete(session(), id=model.id)
|
||||
else:
|
||||
repo.member.update(session(), model.id,
|
||||
operating_status=constants.ONLINE)
|
||||
repo.listener.update(session(), model.pool.listener.id,
|
||||
operating_status=constants.ONLINE,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
repo.load_balancer.update(session(),
|
||||
model.pool.listener.load_balancer.id,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
elif isinstance(model, data_models.Pool):
|
||||
if delete:
|
||||
repo.pool.delete(session(), id=model.id)
|
||||
else:
|
||||
repo.pool.update(session(), model.id,
|
||||
operating_status=constants.ONLINE)
|
||||
repo.listener.update(session(), model.listener.id,
|
||||
operating_status=constants.ONLINE,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
repo.load_balancer.update(session(),
|
||||
model.listener.load_balancer.id,
|
||||
operating_status=constants.ONLINE,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
elif isinstance(model, data_models.Listener):
|
||||
if delete:
|
||||
repo.listener.update(session(), model.id,
|
||||
operating_status=constants.OFFLINE,
|
||||
provisioning_status=constants.DELETED)
|
||||
else:
|
||||
repo.listener.update(session(), model.id,
|
||||
operating_status=constants.ONLINE,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
repo.load_balancer.update(session(),
|
||||
model.load_balancer.id,
|
||||
operating_status=constants.ONLINE,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
elif isinstance(model, data_models.LoadBalancer):
|
||||
if delete:
|
||||
repo.load_balancer.update(
|
||||
session(), id=model.id, operating_status=constants.OFFLINE,
|
||||
provisioning_status=constants.DELETED)
|
||||
else:
|
||||
repo.load_balancer.update(session(), id=model.id,
|
||||
operating_status=constants.ONLINE,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
else:
|
||||
if delete:
|
||||
repo.health_monitor.delete(session(), pool_id=model.pool.id)
|
||||
repo.listener.update(session(), model.pool.listener.id,
|
||||
operating_status=constants.ONLINE,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
repo.load_balancer.update(session(),
|
||||
model.pool.listener.load_balancer.id,
|
||||
operating_status=constants.ONLINE,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
LOG.info(_LI("Simulated Controller Handler Thread Complete"))
|
||||
|
||||
thread = threading.Thread(target=controller, args=(data_model, delete))
|
||||
thread.start()
|
||||
|
||||
|
||||
class InvalidHandlerInputObject(Exception):
|
||||
message = "Invalid Input Object %(obj_type)"
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
message = self.message % kwargs
|
||||
super(InvalidHandlerInputObject, self).__init__(message=message)
|
||||
|
||||
|
||||
class LoadBalancerHandler(abstract_handler.BaseObjectHandler):
|
||||
|
||||
def create(self, load_balancer):
|
||||
validate_input(data_models.LoadBalancer, load_balancer)
|
||||
LOG.info(_LI("%(entity)s handling the creation of "
|
||||
"load balancer %(id)s") %
|
||||
{"entity": self.__class__.__name__, "id": load_balancer.id})
|
||||
simulate_controller(load_balancer)
|
||||
|
||||
def update(self, load_balancer):
|
||||
validate_input(data_models.LoadBalancer, load_balancer)
|
||||
LOG.info(_LI("%(entity)s handling the update of "
|
||||
"load balancer %(id)s") %
|
||||
{"entity": self.__class__.__name__, "id": load_balancer.id})
|
||||
simulate_controller(load_balancer)
|
||||
|
||||
def delete(self, load_balancer):
|
||||
validate_input(data_models.LoadBalancer, load_balancer)
|
||||
LOG.info(_LI("%(entity)s handling the deletion of "
|
||||
"load balancer %(id)s") %
|
||||
{"entity": self.__class__.__name__, "id": load_balancer.id})
|
||||
simulate_controller(load_balancer, delete=True)
|
||||
|
||||
|
||||
class ListenerHandler(abstract_handler.BaseObjectHandler):
|
||||
|
||||
def create(self, listener):
|
||||
validate_input(data_models.Listener, listener)
|
||||
LOG.info(_LI("%(entity)s handling the creation of listener %(id)s") %
|
||||
{"entity": self.__class__.__name__, "id": listener.id})
|
||||
simulate_controller(listener)
|
||||
|
||||
def update(self, listener):
|
||||
validate_input(data_models.Listener, listener)
|
||||
LOG.info(_LI("%(entity)s handling the update of listener %(id)s") %
|
||||
{"entity": self.__class__.__name__, "id": listener.id})
|
||||
simulate_controller(listener)
|
||||
|
||||
def delete(self, listener):
|
||||
validate_input(data_models.Listener, listener)
|
||||
LOG.info(_LI("%(entity)s handling the deletion of listener %(id)s") %
|
||||
{"entity": self.__class__.__name__, "id": listener.id})
|
||||
simulate_controller(listener, delete=True)
|
||||
|
||||
|
||||
class PoolHandler(abstract_handler.BaseObjectHandler):
|
||||
|
||||
def create(self, pool):
|
||||
validate_input(data_models.Pool, pool)
|
||||
LOG.info(_LI("%(entity)s handling the creation of pool %(id)s") %
|
||||
{"entity": self.__class__.__name__, "id": pool.id})
|
||||
simulate_controller(pool)
|
||||
|
||||
def update(self, pool):
|
||||
validate_input(data_models.Pool, pool)
|
||||
LOG.info(_LI("%(entity)s handling the update of pool %(id)s") %
|
||||
{"entity": self.__class__.__name__, "id": pool.id})
|
||||
simulate_controller(pool)
|
||||
|
||||
def delete(self, pool):
|
||||
validate_input(data_models.Pool, pool)
|
||||
LOG.info(_LI("%(entity)s handling the deletion of pool %(id)s") %
|
||||
{"entity": self.__class__.__name__, "id": pool.id})
|
||||
simulate_controller(pool, delete=True)
|
||||
|
||||
|
||||
class HealthMonitorHandler(abstract_handler.BaseObjectHandler):
|
||||
|
||||
def create(self, health_monitor):
|
||||
validate_input(data_models.HealthMonitor, health_monitor)
|
||||
LOG.info(_LI("%(entity)s handling the creation of health monitor "
|
||||
"on pool %(id)s") %
|
||||
{"entity": self.__class__.__name__,
|
||||
"id": health_monitor.pool_id})
|
||||
simulate_controller(health_monitor)
|
||||
|
||||
def update(self, health_monitor):
|
||||
validate_input(data_models.HealthMonitor, health_monitor)
|
||||
LOG.info(_LI("%(entity)s handling the update of health monitor "
|
||||
"on pool %(id)s") %
|
||||
{"entity": self.__class__.__name__,
|
||||
"id": health_monitor.pool_id})
|
||||
simulate_controller(health_monitor)
|
||||
|
||||
def delete(self, health_monitor):
|
||||
validate_input(data_models.HealthMonitor, health_monitor)
|
||||
LOG.info(_LI("%(entity)s handling the deletion of health monitor "
|
||||
"on pool %(id)s") %
|
||||
{"entity": self.__class__.__name__,
|
||||
"id": health_monitor.pool_id})
|
||||
simulate_controller(health_monitor, delete=True)
|
||||
|
||||
|
||||
class MemberHandler(abstract_handler.BaseObjectHandler):
|
||||
|
||||
def create(self, member):
|
||||
validate_input(data_models.Member, member)
|
||||
LOG.info(_LI("%(entity)s handling the creation of member %(id)s") %
|
||||
{"entity": self.__class__.__name__, "id": member.id})
|
||||
simulate_controller(member)
|
||||
|
||||
def update(self, member):
|
||||
validate_input(data_models.Member, member)
|
||||
LOG.info(_LI("%(entity)s handling the update of member %(id)s") %
|
||||
{"entity": self.__class__.__name__, "id": member.id})
|
||||
simulate_controller(member)
|
||||
|
||||
def delete(self, member):
|
||||
validate_input(data_models.Member, member)
|
||||
LOG.info(_LI("%(entity)s handling the deletion of member %(id)s") %
|
||||
{"entity": self.__class__.__name__, "id": member.id})
|
||||
simulate_controller(member, delete=True)
|
||||
|
||||
|
||||
class SimulatedControllerHandler(abstract_handler.BaseHandler):
|
||||
"""Handler that simulates database calls of a successful controller."""
|
||||
load_balancer = LoadBalancerHandler()
|
||||
listener = ListenerHandler()
|
||||
pool = PoolHandler()
|
||||
member = MemberHandler()
|
||||
health_monitor = HealthMonitorHandler()
|
30
octavia/api/v1/hooks.py
Normal file
30
octavia/api/v1/hooks.py
Normal file
@ -0,0 +1,30 @@
|
||||
# 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 pecan import hooks
|
||||
|
||||
from octavia.common import context
|
||||
|
||||
|
||||
class ContextHook(hooks.PecanHook):
|
||||
def on_route(self, state):
|
||||
user_id = state.request.headers.get('X-User-Id')
|
||||
user_id = state.request.headers.get('X-User', user_id)
|
||||
tenant = state.request.headers.get('X-Tenant-Id')
|
||||
tenant = state.request.headers.get('X-Tenant', tenant)
|
||||
auth_token = state.request.headers.get('X-Auth-Token')
|
||||
|
||||
state.request.context = context.Context(user_id=user_id,
|
||||
tenant_id=tenant,
|
||||
auth_token=auth_token)
|
0
octavia/api/v1/types/__init__.py
Normal file
0
octavia/api/v1/types/__init__.py
Normal file
67
octavia/api/v1/types/base.py
Normal file
67
octavia/api/v1/types/base.py
Normal file
@ -0,0 +1,67 @@
|
||||
# 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
|
||||
|
||||
|
||||
class IPAddressType(wtypes.UserType):
|
||||
basetype = str
|
||||
name = 'ipaddress'
|
||||
|
||||
@staticmethod
|
||||
def validate(value):
|
||||
"""Validates whether value is an IPv4 or IPv6 address."""
|
||||
try:
|
||||
wtypes.IPv4AddressType.validate(value)
|
||||
return value
|
||||
except ValueError:
|
||||
try:
|
||||
wtypes.IPv6AddressType.validate(value)
|
||||
return value
|
||||
except ValueError:
|
||||
error = 'Value should be IPv4 or IPv6 format'
|
||||
raise ValueError(error)
|
||||
|
||||
|
||||
class BaseType(wtypes.Base):
|
||||
@classmethod
|
||||
def from_data_model(cls, data_model):
|
||||
"""Converts data_model to Octavia WSME type.
|
||||
|
||||
:param data_model: data model to convert from
|
||||
"""
|
||||
return cls(**data_model.to_dict())
|
||||
|
||||
def to_dict(self, render_unsets=True):
|
||||
"""Converts Octavia WSME type to dictionary.
|
||||
|
||||
:param render_unsets: If True, will convert items that are WSME Unset
|
||||
types to None. If False, does not add the item
|
||||
"""
|
||||
ret_dict = {}
|
||||
for attr in dir(self):
|
||||
if attr.startswith('_'):
|
||||
continue
|
||||
value = getattr(self, attr, None)
|
||||
if value and callable(value):
|
||||
continue
|
||||
if value and isinstance(value, BaseType):
|
||||
value = value.to_dict()
|
||||
if isinstance(value, wtypes.UnsetType):
|
||||
if render_unsets:
|
||||
value = None
|
||||
else:
|
||||
continue
|
||||
ret_dict[attr] = value
|
||||
return ret_dict
|
56
octavia/api/v1/types/health_monitor.py
Normal file
56
octavia/api/v1/types/health_monitor.py
Normal file
@ -0,0 +1,56 @@
|
||||
# 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.v1.types import base
|
||||
|
||||
|
||||
class HealthMonitorResponse(base.BaseType):
|
||||
"""Defines which attributes are to be shown on any response."""
|
||||
type = wtypes.wsattr(wtypes.text)
|
||||
delay = wtypes.wsattr(wtypes.IntegerType())
|
||||
timeout = wtypes.wsattr(wtypes.IntegerType())
|
||||
fall_threshold = wtypes.wsattr(wtypes.IntegerType())
|
||||
rise_threshold = wtypes.wsattr(wtypes.IntegerType())
|
||||
http_method = wtypes.wsattr(wtypes.text)
|
||||
url_path = wtypes.wsattr(wtypes.text)
|
||||
expected_codes = wtypes.wsattr(wtypes.text)
|
||||
enabled = wtypes.wsattr(bool)
|
||||
|
||||
|
||||
class HealthMonitorPOST(base.BaseType):
|
||||
"""Defines mandatory and optional attributes of a POST request."""
|
||||
type = wtypes.wsattr(wtypes.text, mandatory=True)
|
||||
delay = wtypes.wsattr(wtypes.IntegerType(), mandatory=True)
|
||||
timeout = wtypes.wsattr(wtypes.IntegerType(), mandatory=True)
|
||||
fall_threshold = wtypes.wsattr(wtypes.IntegerType(), mandatory=True)
|
||||
rise_threshold = wtypes.wsattr(wtypes.IntegerType(), mandatory=True)
|
||||
http_method = wtypes.wsattr(wtypes.text)
|
||||
url_path = wtypes.wsattr(wtypes.text)
|
||||
expected_codes = wtypes.wsattr(wtypes.text)
|
||||
enabled = wtypes.wsattr(bool, default=True)
|
||||
|
||||
|
||||
class HealthMonitorPUT(base.BaseType):
|
||||
"""Defines attributes that are acceptable of a PUT request."""
|
||||
type = wtypes.wsattr(wtypes.text)
|
||||
delay = wtypes.wsattr(wtypes.IntegerType())
|
||||
timeout = wtypes.wsattr(wtypes.IntegerType())
|
||||
fall_threshold = wtypes.wsattr(wtypes.IntegerType())
|
||||
rise_threshold = wtypes.wsattr(wtypes.IntegerType())
|
||||
http_method = wtypes.wsattr(wtypes.text)
|
||||
url_path = wtypes.wsattr(wtypes.text)
|
||||
expected_codes = wtypes.wsattr(wtypes.text)
|
||||
enabled = wtypes.wsattr(bool)
|
62
octavia/api/v1/types/listener.py
Normal file
62
octavia/api/v1/types/listener.py
Normal file
@ -0,0 +1,62 @@
|
||||
# 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.v1.types import base
|
||||
|
||||
|
||||
class TLSTermination(base.BaseType):
|
||||
certificate = wtypes.wsattr(wtypes.StringType())
|
||||
intermediate_certificate = wtypes.wsattr(wtypes.StringType())
|
||||
private_key = wtypes.wsattr(wtypes.StringType())
|
||||
passphrase = wtypes.wsattr(wtypes.StringType())
|
||||
|
||||
|
||||
class ListenerResponse(base.BaseType):
|
||||
"""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())
|
||||
enabled = wtypes.wsattr(bool)
|
||||
protocol = wtypes.wsattr(wtypes.text)
|
||||
protocol_port = wtypes.wsattr(wtypes.IntegerType())
|
||||
connection_limit = wtypes.wsattr(wtypes.IntegerType())
|
||||
tls_certificate_id = wtypes.wsattr(wtypes.UuidType())
|
||||
|
||||
|
||||
class ListenerPOST(base.BaseType):
|
||||
"""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))
|
||||
enabled = wtypes.wsattr(bool, default=True)
|
||||
protocol = wtypes.wsattr(wtypes.StringType(), mandatory=True)
|
||||
protocol_port = wtypes.wsattr(wtypes.IntegerType(), mandatory=True)
|
||||
connection_limit = wtypes.wsattr(wtypes.IntegerType())
|
||||
tls_certificate_id = wtypes.wsattr(wtypes.UuidType())
|
||||
tls_termination = wtypes.wsattr(TLSTermination)
|
||||
|
||||
|
||||
class ListenerPUT(base.BaseType):
|
||||
"""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))
|
||||
enabled = wtypes.wsattr(bool)
|
||||
protocol = wtypes.wsattr(wtypes.StringType())
|
||||
protocol_port = wtypes.wsattr(wtypes.IntegerType())
|
||||
connection_limit = wtypes.wsattr(wtypes.IntegerType())
|
||||
tls_certificate_id = wtypes.wsattr(wtypes.UuidType())
|
||||
tls_termination = wtypes.wsattr(TLSTermination)
|
52
octavia/api/v1/types/load_balancer.py
Normal file
52
octavia/api/v1/types/load_balancer.py
Normal file
@ -0,0 +1,52 @@
|
||||
# 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.v1.types import base
|
||||
|
||||
|
||||
class VIP(base.BaseType):
|
||||
"""Defines the response and acceptable POST request attributes."""
|
||||
ip_address = wtypes.wsattr(base.IPAddressType())
|
||||
net_port_id = wtypes.wsattr(wtypes.UuidType())
|
||||
subnet_id = wtypes.wsattr(wtypes.UuidType())
|
||||
floating_ip_id = wtypes.wsattr(wtypes.UuidType())
|
||||
floating_ip_network_id = wtypes.wsattr(wtypes.UuidType())
|
||||
|
||||
|
||||
class LoadBalancerResponse(base.BaseType):
|
||||
"""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())
|
||||
enabled = wtypes.wsattr(bool)
|
||||
vip = wtypes.wsattr(VIP)
|
||||
|
||||
|
||||
class LoadBalancerPOST(base.BaseType):
|
||||
"""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))
|
||||
enabled = wtypes.wsattr(bool, default=True)
|
||||
vip = wtypes.wsattr(VIP, mandatory=True)
|
||||
|
||||
|
||||
class LoadBalancerPUT(base.BaseType):
|
||||
"""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))
|
||||
enabled = wtypes.wsattr(bool)
|
44
octavia/api/v1/types/member.py
Normal file
44
octavia/api/v1/types/member.py
Normal file
@ -0,0 +1,44 @@
|
||||
# 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.v1.types import base
|
||||
|
||||
|
||||
class MemberResponse(base.BaseType):
|
||||
"""Defines which attributes are to be shown on any response."""
|
||||
id = wtypes.wsattr(wtypes.UuidType())
|
||||
operating_status = wtypes.wsattr(wtypes.StringType())
|
||||
enabled = wtypes.wsattr(bool)
|
||||
ip_address = wtypes.wsattr(base.IPAddressType())
|
||||
protocol_port = wtypes.wsattr(wtypes.IntegerType())
|
||||
weight = wtypes.wsattr(wtypes.IntegerType())
|
||||
subnet_id = wtypes.wsattr(wtypes.UuidType())
|
||||
|
||||
|
||||
class MemberPOST(base.BaseType):
|
||||
"""Defines mandatory and optional attributes of a POST request."""
|
||||
enabled = wtypes.wsattr(bool, default=True)
|
||||
ip_address = wtypes.wsattr(base.IPAddressType(), mandatory=True)
|
||||
protocol_port = wtypes.wsattr(wtypes.IntegerType(), mandatory=True)
|
||||
weight = wtypes.wsattr(wtypes.IntegerType(), default=1)
|
||||
subnet_id = wtypes.wsattr(wtypes.UuidType())
|
||||
|
||||
|
||||
class MemberPUT(base.BaseType):
|
||||
"""Defines attributes that are acceptable of a PUT request."""
|
||||
protocol_port = wtypes.wsattr(wtypes.IntegerType())
|
||||
enabled = wtypes.wsattr(bool)
|
||||
weight = wtypes.wsattr(wtypes.IntegerType())
|
67
octavia/api/v1/types/pool.py
Normal file
67
octavia/api/v1/types/pool.py
Normal file
@ -0,0 +1,67 @@
|
||||
# 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.v1.types import base
|
||||
|
||||
|
||||
class SessionPersistenceResponse(base.BaseType):
|
||||
"""Defines which attributes are to be shown on any response."""
|
||||
type = wtypes.wsattr(wtypes.text)
|
||||
cookie_name = wtypes.wsattr(wtypes.text)
|
||||
|
||||
|
||||
class SessionPersistencePOST(base.BaseType):
|
||||
"""Defines mandatory and optional attributes of a POST request."""
|
||||
type = wtypes.wsattr(wtypes.text, mandatory=True)
|
||||
cookie_name = wtypes.wsattr(wtypes.text)
|
||||
|
||||
|
||||
class SessionPersistencePUT(base.BaseType):
|
||||
"""Defines attributes that are acceptable of a PUT request."""
|
||||
type = wtypes.wsattr(wtypes.text)
|
||||
cookie_name = wtypes.wsattr(wtypes.text)
|
||||
|
||||
|
||||
class PoolResponse(base.BaseType):
|
||||
"""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())
|
||||
operating_status = wtypes.wsattr(wtypes.StringType())
|
||||
enabled = wtypes.wsattr(bool)
|
||||
protocol = wtypes.wsattr(wtypes.text)
|
||||
lb_algorithm = wtypes.wsattr(wtypes.text)
|
||||
session_persistence = wtypes.wsattr(SessionPersistenceResponse)
|
||||
|
||||
|
||||
class PoolPOST(base.BaseType):
|
||||
"""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))
|
||||
enabled = wtypes.wsattr(bool, default=True)
|
||||
protocol = wtypes.wsattr(wtypes.text, mandatory=True)
|
||||
lb_algorithm = wtypes.wsattr(wtypes.text, mandatory=True)
|
||||
session_persistence = wtypes.wsattr(SessionPersistencePOST)
|
||||
|
||||
|
||||
class PoolPUT(base.BaseType):
|
||||
"""Defines attributes that are acceptable of a PUT request."""
|
||||
name = wtypes.wsattr(wtypes.StringType())
|
||||
description = wtypes.wsattr(wtypes.StringType())
|
||||
enabled = wtypes.wsattr(bool)
|
||||
protocol = wtypes.wsattr(wtypes.text)
|
||||
lb_algorithm = wtypes.wsattr(wtypes.text)
|
||||
session_persistence = wtypes.wsattr(SessionPersistencePUT)
|
@ -44,6 +44,7 @@ DELETED = 'DELETED'
|
||||
ERROR = 'ERROR'
|
||||
SUPPORTED_PROVISIONING_STATUSES = (ACTIVE, PENDING_DELETE, PENDING_CREATE,
|
||||
PENDING_UPDATE, DELETED, ERROR)
|
||||
MUTABLE_STATUSES = (ACTIVE)
|
||||
|
||||
ONLINE = 'ONLINE'
|
||||
OFFLINE = 'OFFLINE'
|
||||
|
29
octavia/common/context.py
Normal file
29
octavia/common/context.py
Normal file
@ -0,0 +1,29 @@
|
||||
# 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 octavia.db import api as db_api
|
||||
from octavia.openstack.common import context as common_context
|
||||
|
||||
|
||||
class Context(common_context.RequestContext):
|
||||
def __init__(self, user_id, tenant_id, is_admin=False, auth_token=None):
|
||||
super(Context, self).__init__(tenant=tenant_id, auth_token=auth_token,
|
||||
is_admin=is_admin, user=user_id)
|
||||
self._session = None
|
||||
|
||||
@property
|
||||
def session(self):
|
||||
if self._session is None:
|
||||
self._session = db_api.get_session()
|
||||
return self._session
|
@ -13,12 +13,15 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import re
|
||||
|
||||
|
||||
class BaseDataModel(object):
|
||||
|
||||
# NOTE(brandon-logan) This does not discover dicts for relationship
|
||||
# attributes.
|
||||
def to_dict(self):
|
||||
"""Converts a data model to a dictionary."""
|
||||
ret = {}
|
||||
for attr in self.__dict__:
|
||||
if attr.startswith('_'):
|
||||
@ -34,6 +37,12 @@ class BaseDataModel(object):
|
||||
return self.to_dict() == other.to_dict()
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def _name(cls):
|
||||
"""Returns class name in a more human readable form."""
|
||||
# Split the class name up by capitalized words
|
||||
return ' '.join(re.findall('[A-Z][^A-Z]*', cls.__name__))
|
||||
|
||||
|
||||
class SessionPersistence(BaseDataModel):
|
||||
|
||||
@ -122,7 +131,7 @@ class Listener(BaseDataModel):
|
||||
default_pool_id=None, load_balancer_id=None, protocol=None,
|
||||
protocol_port=None, connection_limit=None,
|
||||
enabled=None, provisioning_status=None, operating_status=None,
|
||||
default_tls_container_id=None, stats=None, default_pool=None,
|
||||
tls_certificate_id=None, stats=None, default_pool=None,
|
||||
load_balancer=None, sni_containers=None):
|
||||
self.id = id
|
||||
self.tenant_id = tenant_id
|
||||
@ -136,7 +145,7 @@ class Listener(BaseDataModel):
|
||||
self.enabled = enabled
|
||||
self.provisioning_status = provisioning_status
|
||||
self.operating_status = operating_status
|
||||
self.default_tls_container_id = default_tls_container_id
|
||||
self.tls_certificate_id = tls_certificate_id
|
||||
self.stats = stats
|
||||
self.default_pool = default_pool
|
||||
self.load_balancer = load_balancer
|
||||
|
@ -18,6 +18,7 @@ Octavia base exception handling.
|
||||
"""
|
||||
|
||||
from oslo.utils import excutils
|
||||
from webob import exc
|
||||
|
||||
|
||||
class OctaviaException(Exception):
|
||||
@ -47,16 +48,31 @@ class OctaviaException(Exception):
|
||||
return False
|
||||
|
||||
|
||||
class BadRequest(OctaviaException):
|
||||
message = _('Bad %(resource)s request: %(msg)s')
|
||||
# NOTE(blogan) Using webob exceptions here because WSME exceptions a very
|
||||
# limited at this point and they do not work well in _lookup methods in the
|
||||
# controllers
|
||||
class APIException(exc.HTTPClientError):
|
||||
msg = "Something unknown went wrong"
|
||||
code = 500
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.msg = self.msg % kwargs
|
||||
super(APIException, self).__init__(detail=self.msg)
|
||||
|
||||
|
||||
class NotFound(OctaviaException):
|
||||
message = _('%(resource)s not found.')
|
||||
class NotFound(APIException):
|
||||
msg = _('%(resource)s %(id)s not found.')
|
||||
code = 404
|
||||
|
||||
|
||||
class NotAuthorized(OctaviaException):
|
||||
message = _("Not authorized.")
|
||||
class NotAuthorized(APIException):
|
||||
msg = _("Not authorized.")
|
||||
code = 401
|
||||
|
||||
|
||||
class InvalidOption(APIException):
|
||||
msg = _("%(value)s is not a valid option for %(option)s")
|
||||
code = 400
|
||||
|
||||
|
||||
class MissingArguments(OctaviaException):
|
||||
@ -69,3 +85,30 @@ class CertificateStorageException(OctaviaException):
|
||||
|
||||
class CertificateGenerationException(OctaviaException):
|
||||
message = _('Could not sign the certificate request: %(msg)s')
|
||||
|
||||
|
||||
class DuplicateListenerEntry(APIException):
|
||||
msg = _("Another Listener on this Load Balancer "
|
||||
"is already using protocol_port %(port)d")
|
||||
code = 409
|
||||
|
||||
|
||||
class DuplicateMemberEntry(APIException):
|
||||
msg = _("Another member on this pool is already using ip %(ip_address)s "
|
||||
"on protocol_port %(port)d")
|
||||
code = 409
|
||||
|
||||
|
||||
class DuplicateHealthMonitor(APIException):
|
||||
msg = _("This pool already has a health monitor")
|
||||
code = 409
|
||||
|
||||
|
||||
class DuplicatePoolEntry(APIException):
|
||||
msg = _("This listener already has a default pool")
|
||||
code = 409
|
||||
|
||||
|
||||
class ImmutableObject(APIException):
|
||||
msg = _("%(resource)s %(id)s is immutable and cannot be updated.")
|
||||
code = 409
|
||||
|
42
octavia/common/service.py
Normal file
42
octavia/common/service.py
Normal file
@ -0,0 +1,42 @@
|
||||
# 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.config import cfg
|
||||
|
||||
from octavia.common import config
|
||||
from octavia.i18n import _LI
|
||||
from octavia.openstack.common import log
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
def prepare_service(argv=None):
|
||||
"""Sets global config from config file and sets up logging."""
|
||||
argv = argv or []
|
||||
config.init(argv[1:])
|
||||
LOG.info(_LI('Starting Octavia API server'))
|
||||
cfg.set_defaults(log.log_opts,
|
||||
default_log_levels=['amqp=WARN',
|
||||
'amqplib=WARN',
|
||||
'qpid.messaging=INFO',
|
||||
'sqlalchemy=WARN',
|
||||
'keystoneclient=INFO',
|
||||
'stevedore=INFO',
|
||||
'eventlet.wsgi.server=WARN',
|
||||
'iso8601=WARN',
|
||||
'paramiko=WARN',
|
||||
'requests=WARN',
|
||||
'ironic.openstack.common=WARN',
|
||||
])
|
||||
config.setup_logging(cfg.CONF)
|
@ -21,7 +21,7 @@ _FACADE = None
|
||||
def _create_facade_lazily():
|
||||
global _FACADE
|
||||
if _FACADE is None:
|
||||
_FACADE = db_session.EngineFacade.from_config(cfg.CONF)
|
||||
_FACADE = db_session.EngineFacade.from_config(cfg.CONF, sqlite_fk=True)
|
||||
return _FACADE
|
||||
|
||||
|
||||
@ -30,6 +30,7 @@ def get_engine():
|
||||
return facade.get_engine()
|
||||
|
||||
|
||||
def get_session(**kwargs):
|
||||
def get_session(expire_on_commit=True):
|
||||
"""Helper method to grab session."""
|
||||
facade = _create_facade_lazily()
|
||||
return facade.get_session(**kwargs)
|
||||
return facade.get_session(expire_on_commit=expire_on_commit)
|
||||
|
@ -12,19 +12,24 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from octavia.openstack.common import uuidutils
|
||||
|
||||
from oslo.db.sqlalchemy import models
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.ext import declarative
|
||||
from sqlalchemy.orm import collections
|
||||
|
||||
from octavia.openstack.common import uuidutils
|
||||
|
||||
|
||||
class OctaviaBase(models.ModelBase):
|
||||
|
||||
__data_model__ = None
|
||||
|
||||
def to_data_model(self, calling_cls=None):
|
||||
def to_data_model(self, _calling_cls=None):
|
||||
"""Converts to a data model.
|
||||
|
||||
:param _calling_cls: Used only for internal recursion of this method.
|
||||
Should not be called from the outside.
|
||||
"""
|
||||
if not self.__data_model__:
|
||||
raise NotImplementedError
|
||||
dm_kwargs = {}
|
||||
@ -35,16 +40,17 @@ class OctaviaBase(models.ModelBase):
|
||||
for attr_name in attr_names:
|
||||
attr = getattr(self, attr_name)
|
||||
if isinstance(attr, OctaviaBase):
|
||||
if attr.__class__ != calling_cls:
|
||||
if attr.__class__ != _calling_cls:
|
||||
dm_kwargs[attr_name] = attr.to_data_model(
|
||||
calling_cls=self.__class__)
|
||||
_calling_cls=self.__class__)
|
||||
elif isinstance(attr, collections.InstrumentedList):
|
||||
dm_kwargs[attr_name] = []
|
||||
for item in attr:
|
||||
if isinstance(item, OctaviaBase):
|
||||
if attr.__class__ != calling_cls:
|
||||
if attr.__class__ != _calling_cls:
|
||||
dm_kwargs[attr_name].append(
|
||||
item.to_data_model(calling_cls=self.__class__))
|
||||
item.to_data_model(
|
||||
_calling_cls=self.__class__))
|
||||
else:
|
||||
dm_kwargs[attr_name].append(item)
|
||||
return self.__data_model__(**dm_kwargs)
|
||||
|
@ -279,7 +279,7 @@ def upgrade():
|
||||
sa.Column(u'protocol_port', sa.Integer(), nullable=False),
|
||||
sa.Column(u'connection_limit', sa.Integer(), nullable=True),
|
||||
sa.Column(u'load_balancer_id', sa.String(36), nullable=True),
|
||||
sa.Column(u'default_tls_container_id', sa.String(36), nullable=True),
|
||||
sa.Column(u'tls_certificate_id', sa.String(36), nullable=True),
|
||||
sa.Column(u'default_pool_id', sa.String(36), nullable=True),
|
||||
sa.Column(u'provisioning_status', sa.String(16), nullable=False),
|
||||
sa.Column(u'operating_status', sa.String(16), nullable=False),
|
||||
|
@ -0,0 +1,38 @@
|
||||
# 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.
|
||||
|
||||
"""update member address column
|
||||
|
||||
Revision ID: 4faaa983e7a9
|
||||
Revises: 13500e2e978d
|
||||
Create Date: 2014-09-29 11:22:16.565071
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '4faaa983e7a9'
|
||||
down_revision = '13500e2e978d'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.alter_column(u'member', u'address', new_column_name=u'ip_address',
|
||||
existing_type=sa.String(64))
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.alter_column(u'member', u'ip_address', new_column_name=u'address',
|
||||
existing_type=sa.String(64))
|
@ -115,7 +115,7 @@ class Member(base_models.BASE, base_models.IdMixin, base_models.TenantMixin):
|
||||
__tablename__ = "member"
|
||||
__table_args__ = (
|
||||
sa.UniqueConstraint('pool_id', 'ip_address', 'protocol_port',
|
||||
name='uq_member_pool_id_ip_address_protocol_port'),
|
||||
name='uq_member_pool_id_address_protocol_port'),
|
||||
)
|
||||
|
||||
pool_id = sa.Column(
|
||||
@ -123,7 +123,7 @@ class Member(base_models.BASE, base_models.IdMixin, base_models.TenantMixin):
|
||||
sa.ForeignKey("pool.id", name="fk_member_pool_id"),
|
||||
nullable=False)
|
||||
subnet_id = sa.Column(sa.String(36), nullable=True)
|
||||
ip_address = sa.Column(sa.String(64), nullable=False)
|
||||
ip_address = sa.Column('ip_address', sa.String(64), nullable=False)
|
||||
protocol_port = sa.Column(sa.Integer, nullable=False)
|
||||
weight = sa.Column(sa.Integer, nullable=True)
|
||||
operating_status = sa.Column(
|
||||
@ -137,8 +137,7 @@ class Member(base_models.BASE, base_models.IdMixin, base_models.TenantMixin):
|
||||
cascade="delete"))
|
||||
|
||||
|
||||
class HealthMonitor(base_models.BASE, base_models.IdMixin,
|
||||
base_models.TenantMixin):
|
||||
class HealthMonitor(base_models.BASE):
|
||||
|
||||
__data_model__ = data_models.HealthMonitor
|
||||
|
||||
@ -261,7 +260,7 @@ class Listener(base_models.BASE, base_models.IdMixin, base_models.TenantMixin):
|
||||
sa.String(36),
|
||||
sa.ForeignKey("load_balancer.id", name="fk_listener_load_balancer_id"),
|
||||
nullable=True)
|
||||
default_tls_container_id = sa.Column(sa.String(36), nullable=True)
|
||||
tls_certificate_id = sa.Column(sa.String(36), nullable=True)
|
||||
default_pool_id = sa.Column(
|
||||
sa.String(36),
|
||||
sa.ForeignKey("pool.id", name="fk_listener_pool_id"),
|
||||
|
@ -17,8 +17,10 @@ Defines interface for DB access that Resource or Octavia Controllers may
|
||||
reference
|
||||
"""
|
||||
|
||||
from octavia.common import constants
|
||||
from octavia.common import exceptions
|
||||
from octavia.db import models
|
||||
from octavia.openstack.common import uuidutils
|
||||
|
||||
|
||||
class BaseRepository(object):
|
||||
@ -26,36 +28,77 @@ class BaseRepository(object):
|
||||
model_class = None
|
||||
|
||||
def create(self, session, **model_kwargs):
|
||||
with session.begin():
|
||||
"""Base create method for a database entity.
|
||||
|
||||
:param session: A Sql Alchemy database session.
|
||||
:param model_kwargs: Attributes of the model to insert.
|
||||
:returns: octavia.common.data_model
|
||||
"""
|
||||
with session.begin(subtransactions=True):
|
||||
model = self.model_class(**model_kwargs)
|
||||
session.add(model)
|
||||
return model.to_data_model()
|
||||
|
||||
def delete(self, session, **filters):
|
||||
"""Deletes an entity from the database.
|
||||
|
||||
:param session: A Sql Alchemy database session.
|
||||
:param filters: Filters to decide which entity should be deleted.
|
||||
:returns: None
|
||||
"""
|
||||
model = session.query(self.model_class).filter_by(**filters).first()
|
||||
with session.begin():
|
||||
with session.begin(subtransactions=True):
|
||||
session.delete(model)
|
||||
session.flush()
|
||||
|
||||
def delete_batch(self, session, ids=None):
|
||||
"""Batch deletes by entity ids."""
|
||||
ids = ids or []
|
||||
[self.delete(session, id) for id in ids]
|
||||
|
||||
def update(self, session, id, **model_kwargs):
|
||||
with session.begin():
|
||||
"""Updates an entity in the database.
|
||||
|
||||
:param session: A Sql Alchemy database session.
|
||||
:param model_kwargs: Entity attributes that should be updates.
|
||||
:returns: octavia.common.data_model
|
||||
"""
|
||||
with session.begin(subtransactions=True):
|
||||
session.query(self.model_class).filter_by(
|
||||
id=id).update(model_kwargs)
|
||||
|
||||
def get(self, session, **filters):
|
||||
"""Retrieves an entity from the database.
|
||||
|
||||
:param session: A Sql Alchemy database session.
|
||||
:param filters: Filters to decide which entity should be retrieved.
|
||||
:returns: octavia.common.data_model
|
||||
"""
|
||||
model = session.query(self.model_class).filter_by(**filters).first()
|
||||
if not model:
|
||||
raise exceptions.NotFound(resource=self.model_class.__name__)
|
||||
return
|
||||
return model.to_data_model()
|
||||
|
||||
def get_all(self, session, **filters):
|
||||
"""Retrieves a list of entities from the database.
|
||||
|
||||
:param session: A Sql Alchemy database session.
|
||||
:param filters: Filters to decide which entities should be retrieved.
|
||||
:returns: [octavia.common.data_model]
|
||||
"""
|
||||
model_list = session.query(self.model_class).filter_by(**filters).all()
|
||||
data_model_list = [model.to_data_model() for model in model_list]
|
||||
return data_model_list
|
||||
|
||||
def exists(self, session, id):
|
||||
"""Determines whether an entity exists in the database by its id.
|
||||
|
||||
:param session: A Sql Alchemy database session.
|
||||
:param id: id of entity to check for existance.
|
||||
:returns: octavia.common.data_model
|
||||
"""
|
||||
return bool(session.query(self.model_class).filter_by(id=id).first())
|
||||
|
||||
|
||||
class Repositories(object):
|
||||
|
||||
@ -71,18 +114,128 @@ class Repositories(object):
|
||||
self.amphora = AmphoraRepository()
|
||||
self.sni = SNIRepository()
|
||||
|
||||
def create_load_balancer_and_vip(self, session, load_balancer_dict,
|
||||
vip_dict):
|
||||
"""Inserts load balancer and vip entities into the database.
|
||||
|
||||
Inserts load balancer and vip entities into the database in one
|
||||
transaction and returns the data model of the load balancer.
|
||||
|
||||
:param session: A Sql Alchemy database session.
|
||||
:param load_balancer_dict: Dictionary representation of a load balancer
|
||||
:param vip_dict: Dictionary representation of a vip
|
||||
:returns: octava.common.data_models.LoadBalancer
|
||||
"""
|
||||
with session.begin():
|
||||
load_balancer_dict['id'] = uuidutils.generate_uuid()
|
||||
lb = models.LoadBalancer(**load_balancer_dict)
|
||||
session.add(lb)
|
||||
vip_dict['load_balancer_id'] = load_balancer_dict['id']
|
||||
vip = models.Vip(**vip_dict)
|
||||
session.add(vip)
|
||||
return self.load_balancer.get(session, id=lb.id)
|
||||
|
||||
def create_pool_on_listener(self, session, listener_id,
|
||||
pool_dict, sp_dict=None):
|
||||
"""Inserts a pool and session persistence entity into the database.
|
||||
|
||||
:param session: A Sql Alchemy database session.
|
||||
:param listener_id: id of the listener the pool will be referenced by
|
||||
:param pool_dict: Dictionary representation of a pool
|
||||
:param sp_dict: Dictionary representation of a session persistence
|
||||
:returns: octavia.common.data_models.Pool
|
||||
"""
|
||||
with session.begin(subtransactions=True):
|
||||
pool_dict['id'] = uuidutils.generate_uuid()
|
||||
db_pool = self.pool.create(session, **pool_dict)
|
||||
if sp_dict:
|
||||
sp_dict['pool_id'] = pool_dict['id']
|
||||
self.session_persistence.create(session, **sp_dict)
|
||||
self.listener.update(session, listener_id,
|
||||
default_pool_id=pool_dict['id'])
|
||||
return self.pool.get(session, id=db_pool.id)
|
||||
|
||||
def update_pool_on_listener(self, session, pool_id, pool_dict, sp_dict):
|
||||
"""Updates a pool and session persistence entity in the database.
|
||||
|
||||
:param session: A Sql Alchemy database session.
|
||||
:param pool_dict: Dictionary representation of a pool
|
||||
:param sp_dict: Dictionary representation of a session persistence
|
||||
:returns: octavia.common.data_models.Pool
|
||||
"""
|
||||
with session.begin(subtransactions=True):
|
||||
self.pool.update(session, pool_id, **pool_dict)
|
||||
if sp_dict:
|
||||
if self.session_persistence.exists(session, pool_id):
|
||||
self.session_persistence.update(session, pool_id,
|
||||
**sp_dict)
|
||||
else:
|
||||
sp_dict['pool_id'] = pool_id
|
||||
self.session_persistence.create(session, **sp_dict)
|
||||
db_pool = self.pool.get(session, id=pool_id)
|
||||
if db_pool.session_persistence is not None and not sp_dict:
|
||||
self.session_persistence.delete(session, pool_id=pool_id)
|
||||
db_pool = self.pool.get(session, id=pool_id)
|
||||
return db_pool
|
||||
|
||||
def test_and_set_lb_and_listener_prov_status(self, session, lb_id,
|
||||
listener_id, lb_prov_status,
|
||||
listener_prov_status):
|
||||
"""Tests and sets a load balancer and listener provisioning status.
|
||||
|
||||
Puts a lock on the load balancer table to check the status of a
|
||||
load balancer. If the status is ACTIVE then the status of the load
|
||||
balancer and listener is updated and the method returns True. If the
|
||||
status is not ACTIVE, then nothing is done and False is returned.
|
||||
|
||||
:param session: A Sql Alchemy database session.
|
||||
:param lb_id: id of Load Balancer
|
||||
:param listener_id: id of a Listener
|
||||
:param lb_prov_status: Status to set Load Balancer and Listener if
|
||||
check passes.
|
||||
:returns: bool
|
||||
"""
|
||||
success = self.load_balancer.test_and_set_provisioning_status(
|
||||
session, lb_id, lb_prov_status)
|
||||
self.listener.update(session, listener_id,
|
||||
provisioning_status=listener_prov_status)
|
||||
return success
|
||||
|
||||
|
||||
class LoadBalancerRepository(BaseRepository):
|
||||
|
||||
model_class = models.LoadBalancer
|
||||
|
||||
def test_and_set_provisioning_status(self, session, id, status):
|
||||
"""Tests and sets a load balancer and provisioning status.
|
||||
|
||||
Puts a lock on the load balancer table to check the status of a
|
||||
load balancer. If the status is ACTIVE then the status of the load
|
||||
balancer is updated and the method returns True. If the
|
||||
status is not ACTIVE, then nothing is done and False is returned.
|
||||
|
||||
:param session: A Sql Alchemy database session.
|
||||
:param id: id of Load Balancer
|
||||
:param status: Status to set Load Balancer if check passes.
|
||||
:returns: bool
|
||||
"""
|
||||
with session.begin(subtransactions=True):
|
||||
lb = session.query(self.model_class).with_for_update().filter_by(
|
||||
id=id).one()
|
||||
if lb.provisioning_status not in constants.MUTABLE_STATUSES:
|
||||
return False
|
||||
lb.provisioning_status = status
|
||||
session.add(lb)
|
||||
return True
|
||||
|
||||
|
||||
class VipRepository(BaseRepository):
|
||||
|
||||
model_class = models.Vip
|
||||
|
||||
def update(self, session, load_balancer_id, **model_kwargs):
|
||||
with session.begin():
|
||||
"""Updates a vip entity in the database by load_balancer_id."""
|
||||
with session.begin(subtransactions=True):
|
||||
session.query(self.model_class).filter_by(
|
||||
load_balancer_id=load_balancer_id).update(model_kwargs)
|
||||
|
||||
@ -92,7 +245,8 @@ class HealthMonitorRepository(BaseRepository):
|
||||
model_class = models.HealthMonitor
|
||||
|
||||
def update(self, session, pool_id, **model_kwargs):
|
||||
with session.begin():
|
||||
"""Updates a health monitor entity in the database by pool_id."""
|
||||
with session.begin(subtransactions=True):
|
||||
session.query(self.model_class).filter_by(
|
||||
pool_id=pool_id).update(model_kwargs)
|
||||
|
||||
@ -102,10 +256,16 @@ class SessionPersistenceRepository(BaseRepository):
|
||||
model_class = models.SessionPersistence
|
||||
|
||||
def update(self, session, pool_id, **model_kwargs):
|
||||
with session.begin():
|
||||
"""Updates a session persistence entity in the database by pool_id."""
|
||||
with session.begin(subtransactions=True):
|
||||
session.query(self.model_class).filter_by(
|
||||
pool_id=pool_id).update(model_kwargs)
|
||||
|
||||
def exists(self, session, pool_id):
|
||||
"""Checks if session persistence exists on a pool."""
|
||||
return bool(session.query(self.model_class).filter_by(
|
||||
pool_id=pool_id).first())
|
||||
|
||||
|
||||
class PoolRepository(BaseRepository):
|
||||
|
||||
@ -117,6 +277,7 @@ class MemberRepository(BaseRepository):
|
||||
model_class = models.Member
|
||||
|
||||
def delete_members(self, session, member_ids):
|
||||
"""Batch deletes members from a pool."""
|
||||
self.delete_batch(session, member_ids)
|
||||
|
||||
|
||||
@ -124,13 +285,19 @@ class ListenerRepository(BaseRepository):
|
||||
|
||||
model_class = models.Listener
|
||||
|
||||
def has_pool(self, session, id):
|
||||
"""Checks if a listener has a pool."""
|
||||
listener = self.get(session, id=id)
|
||||
return bool(listener.default_pool)
|
||||
|
||||
|
||||
class ListenerStatisticsRepository(BaseRepository):
|
||||
|
||||
model_class = models.ListenerStatistics
|
||||
|
||||
def update(self, session, listener_id, **model_kwargs):
|
||||
with session.begin():
|
||||
"""Updates a listener's statistics by a listener's id."""
|
||||
with session.begin(subtransactions=True):
|
||||
session.query(self.model_class).filter_by(
|
||||
listener_id=listener_id).update(model_kwargs)
|
||||
|
||||
@ -140,7 +307,8 @@ class AmphoraRepository(BaseRepository):
|
||||
model_class = models.Amphora
|
||||
|
||||
def associate(self, session, load_balancer_id, amphora_id):
|
||||
with session.begin():
|
||||
"""Associates an amphora with a load balancer."""
|
||||
with session.begin(subtransactions=True):
|
||||
load_balancer = session.query(models.LoadBalancer).filter_by(
|
||||
id=load_balancer_id).first()
|
||||
amphora = session.query(self.model_class).filter_by(
|
||||
@ -154,9 +322,10 @@ class SNIRepository(BaseRepository):
|
||||
|
||||
def update(self, session, listener_id=None, tls_container_id=None,
|
||||
**model_kwargs):
|
||||
"""Updates an SNI entity in the database."""
|
||||
if not listener_id and tls_container_id:
|
||||
raise exceptions.MissingArguments
|
||||
with session.begin():
|
||||
with session.begin(subtransactions=True):
|
||||
if listener_id:
|
||||
session.query(self.model_class).filter_by(
|
||||
listener_id=listener_id).update(model_kwargs)
|
||||
|
0
octavia/tests/functional/__init__.py
Normal file
0
octavia/tests/functional/__init__.py
Normal file
0
octavia/tests/functional/api/__init__.py
Normal file
0
octavia/tests/functional/api/__init__.py
Normal file
0
octavia/tests/functional/api/v1/__init__.py
Normal file
0
octavia/tests/functional/api/v1/__init__.py
Normal file
216
octavia/tests/functional/api/v1/base.py
Normal file
216
octavia/tests/functional/api/v1/base.py
Normal file
@ -0,0 +1,216 @@
|
||||
# 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.
|
||||
|
||||
import logging
|
||||
|
||||
import mock
|
||||
import pecan
|
||||
import pecan.testing
|
||||
|
||||
from octavia.api import config as pconfig
|
||||
from octavia.common import constants
|
||||
from octavia.db import api as db_api
|
||||
from octavia.db import repositories
|
||||
from octavia.tests.functional.db import base as base_db_test
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BaseAPITest(base_db_test.OctaviaDBTestBase):
|
||||
|
||||
BASE_PATH = '/v1'
|
||||
LBS_PATH = '/loadbalancers'
|
||||
LB_PATH = LBS_PATH + '/{lb_id}'
|
||||
LISTENERS_PATH = LB_PATH + '/listeners'
|
||||
LISTENER_PATH = LISTENERS_PATH + '/{listener_id}'
|
||||
POOLS_PATH = LISTENER_PATH + '/pools'
|
||||
POOL_PATH = POOLS_PATH + '/{pool_id}'
|
||||
MEMBERS_PATH = POOL_PATH + '/members'
|
||||
MEMBER_PATH = MEMBERS_PATH + '/{member_id}'
|
||||
HM_PATH = POOL_PATH + '/healthmonitor'
|
||||
|
||||
def setUp(self):
|
||||
super(BaseAPITest, self).setUp()
|
||||
self.lb_repo = repositories.LoadBalancerRepository()
|
||||
self.listener_repo = repositories.ListenerRepository()
|
||||
self.pool_repo = repositories.PoolRepository()
|
||||
self.member_repo = repositories.MemberRepository()
|
||||
patcher = mock.patch('octavia.api.v1.handlers.controller_simulator.'
|
||||
'handler.SimulatedControllerHandler')
|
||||
patcher.start()
|
||||
self.app = self._make_app()
|
||||
|
||||
def reset_pecan():
|
||||
patcher.stop()
|
||||
pecan.set_config({}, overwrite=True)
|
||||
|
||||
self.addCleanup(reset_pecan)
|
||||
|
||||
def _make_app(self):
|
||||
return pecan.testing.load_test_app({'app': pconfig.app,
|
||||
'wsme': pconfig.wsme})
|
||||
|
||||
def _get_full_path(self, path):
|
||||
return ''.join([self.BASE_PATH, path])
|
||||
|
||||
def delete(self, path, headers=None, status=202, expect_errors=False):
|
||||
headers = headers or {}
|
||||
full_path = self._get_full_path(path)
|
||||
response = self.app.delete(full_path,
|
||||
headers=headers,
|
||||
status=status,
|
||||
expect_errors=expect_errors)
|
||||
return response
|
||||
|
||||
def post(self, path, body, headers=None, status=202, expect_errors=False):
|
||||
headers = headers or {}
|
||||
full_path = self._get_full_path(path)
|
||||
response = self.app.post_json(full_path,
|
||||
params=body,
|
||||
headers=headers,
|
||||
status=status,
|
||||
expect_errors=expect_errors)
|
||||
return response
|
||||
|
||||
def put(self, path, body, headers=None, status=202, expect_errors=False):
|
||||
headers = headers or {}
|
||||
full_path = self._get_full_path(path)
|
||||
response = self.app.put_json(full_path,
|
||||
params=body,
|
||||
headers=headers,
|
||||
status=status,
|
||||
expect_errors=expect_errors)
|
||||
return response
|
||||
|
||||
def get(self, path, params=None, headers=None, status=200,
|
||||
expect_errors=False):
|
||||
full_path = self._get_full_path(path)
|
||||
response = self.app.get(full_path,
|
||||
params=params,
|
||||
headers=headers,
|
||||
status=status,
|
||||
expect_errors=expect_errors)
|
||||
return response
|
||||
|
||||
def create_load_balancer(self, vip, **optionals):
|
||||
req_dict = {'vip': vip}
|
||||
req_dict.update(optionals)
|
||||
response = self.post(self.LBS_PATH, req_dict)
|
||||
return response.json
|
||||
|
||||
def create_listener(self, lb_id, protocol, protocol_port, **optionals):
|
||||
req_dict = {'protocol': protocol, 'protocol_port': protocol_port}
|
||||
req_dict.update(optionals)
|
||||
path = self.LISTENERS_PATH.format(lb_id=lb_id)
|
||||
response = self.post(path, req_dict)
|
||||
return response.json
|
||||
|
||||
def create_pool(self, lb_id, listener_id, protocol, lb_algorithm,
|
||||
**optionals):
|
||||
req_dict = {'protocol': protocol, 'lb_algorithm': lb_algorithm}
|
||||
req_dict.update(optionals)
|
||||
path = self.POOLS_PATH.format(lb_id=lb_id, listener_id=listener_id)
|
||||
response = self.post(path, req_dict)
|
||||
return response.json
|
||||
|
||||
def create_member(self, lb_id, listener_id, pool_id, ip_address,
|
||||
protocol_port, **optionals):
|
||||
req_dict = {'ip_address': ip_address, 'protocol_port': protocol_port}
|
||||
req_dict.update(optionals)
|
||||
path = self.MEMBERS_PATH.format(lb_id=lb_id, listener_id=listener_id,
|
||||
pool_id=pool_id)
|
||||
response = self.post(path, req_dict)
|
||||
return response.json
|
||||
|
||||
def create_health_monitor(self, lb_id, listener_id, pool_id, type,
|
||||
delay, timeout, fall_threshold, rise_threshold,
|
||||
**optionals):
|
||||
req_dict = {'type': type,
|
||||
'delay': delay,
|
||||
'timeout': timeout,
|
||||
'fall_threshold': fall_threshold,
|
||||
'rise_threshold': rise_threshold}
|
||||
req_dict.update(optionals)
|
||||
path = self.HM_PATH.format(lb_id=lb_id, listener_id=listener_id,
|
||||
pool_id=pool_id)
|
||||
response = self.post(path, req_dict)
|
||||
return response.json
|
||||
|
||||
def _set_lb_and_children_statuses(self, lb_id, prov_status, op_status):
|
||||
self.lb_repo.update(db_api.get_session(), lb_id,
|
||||
provisioning_status=prov_status,
|
||||
operating_status=op_status)
|
||||
lb_listeners = self.listener_repo.get_all(db_api.get_session(),
|
||||
load_balancer_id=lb_id)
|
||||
for listener in lb_listeners:
|
||||
if listener.default_pool_id:
|
||||
self.pool_repo.update(db_api.get_session(),
|
||||
listener.default_pool_id,
|
||||
operating_status=op_status)
|
||||
for member in listener.default_pool.members:
|
||||
self.member_repo.update(db_api.get_session(), member.id,
|
||||
operating_status=op_status)
|
||||
self.listener_repo.update(db_api.get_session(), listener.id,
|
||||
provisioning_status=prov_status,
|
||||
operating_status=op_status)
|
||||
|
||||
def set_lb_status(self, lb_id, status=constants.ACTIVE):
|
||||
if status == constants.DELETED:
|
||||
op_status = constants.OFFLINE
|
||||
elif status == constants.ACTIVE:
|
||||
op_status = constants.ONLINE
|
||||
else:
|
||||
db_lb = self.lb_repo.get(db_api.get_session(), id=lb_id)
|
||||
op_status = db_lb.operating_status
|
||||
self._set_lb_and_children_statuses(lb_id, status, op_status)
|
||||
return self.get(self.LB_PATH.format(lb_id=lb_id)).json
|
||||
|
||||
def assert_final_lb_statuses(self, lb_id, delete=False):
|
||||
expected_prov_status = constants.ACTIVE
|
||||
expected_op_status = constants.ONLINE
|
||||
if delete:
|
||||
expected_prov_status = constants.DELETED
|
||||
expected_op_status = constants.OFFLINE
|
||||
self.set_lb_status(lb_id, status=expected_prov_status)
|
||||
self.assert_correct_lb_status(lb_id, expected_prov_status,
|
||||
expected_op_status)
|
||||
|
||||
def assert_final_listener_statuses(self, lb_id, listener_id, delete=False):
|
||||
expected_prov_status = constants.ACTIVE
|
||||
expected_op_status = constants.ONLINE
|
||||
if delete:
|
||||
expected_prov_status = constants.DELETED
|
||||
expected_op_status = constants.OFFLINE
|
||||
self.set_lb_status(lb_id, status=expected_prov_status)
|
||||
self.assert_correct_listener_status(lb_id, listener_id,
|
||||
expected_prov_status,
|
||||
expected_op_status)
|
||||
|
||||
def assert_correct_lb_status(self, lb_id, provisioning_status,
|
||||
operating_status):
|
||||
api_lb = self.get(self.LB_PATH.format(lb_id=lb_id)).json
|
||||
self.assertEqual(provisioning_status,
|
||||
api_lb.get('provisioning_status'))
|
||||
self.assertEqual(operating_status,
|
||||
api_lb.get('operating_status'))
|
||||
|
||||
def assert_correct_listener_status(self, lb_id, listener_id,
|
||||
provisioning_status, operating_status):
|
||||
api_listener = self.get(self.LISTENER_PATH.format(
|
||||
lb_id=lb_id, listener_id=listener_id)).json
|
||||
self.assertEqual(provisioning_status,
|
||||
api_listener.get('provisioning_status'))
|
||||
self.assertEqual(operating_status,
|
||||
api_listener.get('operating_status'))
|
197
octavia/tests/functional/api/v1/test_health_monitor.py
Normal file
197
octavia/tests/functional/api/v1/test_health_monitor.py
Normal file
@ -0,0 +1,197 @@
|
||||
# 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 octavia.common import constants
|
||||
from octavia.tests.functional.api.v1 import base
|
||||
|
||||
|
||||
class TestHealthMonitor(base.BaseAPITest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestHealthMonitor, self).setUp()
|
||||
self.lb = self.create_load_balancer({})
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.listener = self.create_listener(self.lb.get('id'),
|
||||
constants.PROTOCOL_HTTP, 80)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.pool = self.create_pool(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.hm_path = self.HM_PATH.format(lb_id=self.lb.get('id'),
|
||||
listener_id=self.listener.get('id'),
|
||||
pool_id=self.pool.get('id'))
|
||||
|
||||
def test_get(self):
|
||||
api_hm = self.create_health_monitor(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
self.pool.get('id'),
|
||||
constants.HEALTH_MONITOR_HTTP,
|
||||
1, 1, 1, 1)
|
||||
self.set_lb_status(lb_id=self.lb.get('id'))
|
||||
response = self.get(self.hm_path)
|
||||
response_body = response.json
|
||||
self.assertEqual(api_hm, response_body)
|
||||
|
||||
def test_bad_get(self):
|
||||
self.get(self.hm_path, status=404)
|
||||
|
||||
def test_create(self):
|
||||
api_hm = self.create_health_monitor(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
self.pool.get('id'),
|
||||
constants.HEALTH_MONITOR_HTTP,
|
||||
1, 1, 1, 1)
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.assert_correct_listener_status(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.assertEqual(constants.HEALTH_MONITOR_HTTP, api_hm.get('type'))
|
||||
self.assertEqual(1, api_hm.get('delay'))
|
||||
self.assertEqual(1, api_hm.get('timeout'))
|
||||
self.assertEqual(1, api_hm.get('fall_threshold'))
|
||||
self.assertEqual(1, api_hm.get('rise_threshold'))
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.ACTIVE,
|
||||
constants.ONLINE)
|
||||
self.assert_correct_listener_status(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.ACTIVE, constants.ONLINE)
|
||||
|
||||
def test_bad_create(self):
|
||||
hm_json = {'name': 'test1'}
|
||||
self.post(self.hm_path, hm_json, status=400)
|
||||
|
||||
def test_duplicate_create(self):
|
||||
api_hm = self.create_health_monitor(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
self.pool.get('id'),
|
||||
constants.HEALTH_MONITOR_HTTP,
|
||||
1, 1, 1, 1)
|
||||
self.set_lb_status(lb_id=self.lb.get('id'))
|
||||
self.post(self.hm_path, api_hm, status=409)
|
||||
|
||||
def test_update(self):
|
||||
self.create_health_monitor(self.lb.get('id'), self.listener.get('id'),
|
||||
self.pool.get('id'),
|
||||
constants.HEALTH_MONITOR_HTTP,
|
||||
1, 1, 1, 1)
|
||||
self.set_lb_status(lb_id=self.lb.get('id'))
|
||||
new_hm = {'type': constants.HEALTH_MONITOR_HTTPS}
|
||||
self.put(self.hm_path, new_hm)
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.assert_correct_listener_status(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.ACTIVE,
|
||||
constants.ONLINE)
|
||||
self.assert_correct_listener_status(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.ACTIVE, constants.ONLINE)
|
||||
|
||||
def test_bad_update(self):
|
||||
self.create_health_monitor(self.lb.get('id'), self.listener.get('id'),
|
||||
self.pool.get('id'),
|
||||
constants.HEALTH_MONITOR_HTTP,
|
||||
1, 1, 1, 1)
|
||||
new_hm = {'type': 'bad_type', 'delay': 2}
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.put(self.hm_path, new_hm, status=400)
|
||||
|
||||
def test_delete(self):
|
||||
api_hm = self.create_health_monitor(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
self.pool['id'],
|
||||
constants.HEALTH_MONITOR_HTTP,
|
||||
1, 1, 1, 1)
|
||||
self.set_lb_status(lb_id=self.lb.get('id'))
|
||||
response = self.get(self.hm_path)
|
||||
self.assertEqual(api_hm, response.json)
|
||||
self.delete(self.hm_path)
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.assert_correct_listener_status(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.ACTIVE,
|
||||
constants.ONLINE)
|
||||
self.assert_correct_listener_status(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.ACTIVE, constants.ONLINE)
|
||||
|
||||
def test_bad_delete(self):
|
||||
self.delete(self.hm_path, status=404)
|
||||
|
||||
def test_create_when_lb_pending_update(self):
|
||||
self.put(self.LB_PATH.format(lb_id=self.lb.get('id')),
|
||||
body={'name': 'test_name_change'})
|
||||
self.post(self.hm_path, body={'type': constants.HEALTH_MONITOR_HTTP,
|
||||
'delay': 1, 'timeout': 1,
|
||||
'fall_threshold': 1,
|
||||
'rise_threshold': 1}, status=409)
|
||||
|
||||
def test_update_when_lb_pending_update(self):
|
||||
self.create_health_monitor(self.lb.get('id'), self.listener.get('id'),
|
||||
self.pool.get('id'),
|
||||
constants.HEALTH_MONITOR_HTTP, 1, 1, 1, 1)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.put(self.LB_PATH.format(lb_id=self.lb.get('id')),
|
||||
body={'name': 'test_name_change'})
|
||||
self.put(self.hm_path, body={'rise_threshold': 2}, status=409)
|
||||
|
||||
def test_delete_when_lb_pending_update(self):
|
||||
self.create_health_monitor(self.lb.get('id'), self.listener.get('id'),
|
||||
self.pool.get('id'),
|
||||
constants.HEALTH_MONITOR_HTTP, 1, 1, 1, 1)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.put(self.LB_PATH.format(lb_id=self.lb.get('id')),
|
||||
body={'name': 'test_name_change'})
|
||||
self.delete(self.hm_path, status=409)
|
||||
|
||||
def test_create_when_lb_pending_delete(self):
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb.get('id')))
|
||||
self.post(self.hm_path, body={'type': constants.HEALTH_MONITOR_HTTP,
|
||||
'delay': 1, 'timeout': 1,
|
||||
'fall_threshold': 1,
|
||||
'rise_threshold': 1}, status=409)
|
||||
|
||||
def test_update_when_lb_pending_delete(self):
|
||||
self.create_health_monitor(self.lb.get('id'), self.listener.get('id'),
|
||||
self.pool.get('id'),
|
||||
constants.HEALTH_MONITOR_HTTP, 1, 1, 1, 1)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb.get('id')))
|
||||
self.put(self.hm_path, body={'rise_threshold': 2}, status=409)
|
||||
|
||||
def test_delete_when_lb_pending_delete(self):
|
||||
self.create_health_monitor(self.lb.get('id'), self.listener.get('id'),
|
||||
self.pool.get('id'),
|
||||
constants.HEALTH_MONITOR_HTTP, 1, 1, 1, 1)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb.get('id')))
|
||||
self.delete(self.hm_path, status=409)
|
302
octavia/tests/functional/api/v1/test_listener.py
Normal file
302
octavia/tests/functional/api/v1/test_listener.py
Normal file
@ -0,0 +1,302 @@
|
||||
# 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 octavia.common import constants
|
||||
from octavia.openstack.common import uuidutils
|
||||
from octavia.tests.functional.api.v1 import base
|
||||
|
||||
|
||||
class TestListener(base.BaseAPITest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestListener, self).setUp()
|
||||
self.lb = self.create_load_balancer({})
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.listeners_path = self.LISTENERS_PATH.format(
|
||||
lb_id=self.lb.get('id'))
|
||||
|
||||
def test_get_all(self):
|
||||
listener1 = self.create_listener(self.lb.get('id'),
|
||||
constants.PROTOCOL_HTTP, 80)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
listener2 = self.create_listener(self.lb.get('id'),
|
||||
constants.PROTOCOL_HTTP, 81)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
listener3 = self.create_listener(self.lb.get('id'),
|
||||
constants.PROTOCOL_HTTP, 82)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
response = self.get(self.listeners_path)
|
||||
api_listeners = response.json
|
||||
self.assertEqual(3, len(api_listeners))
|
||||
listener1['provisioning_status'] = constants.ACTIVE
|
||||
listener1['operating_status'] = constants.ONLINE
|
||||
listener2['provisioning_status'] = constants.ACTIVE
|
||||
listener2['operating_status'] = constants.ONLINE
|
||||
listener3['provisioning_status'] = constants.ACTIVE
|
||||
listener3['operating_status'] = constants.ONLINE
|
||||
self.assertIn(listener1, api_listeners)
|
||||
self.assertIn(listener2, api_listeners)
|
||||
self.assertIn(listener3, api_listeners)
|
||||
|
||||
def test_get_all_bad_lb_id(self):
|
||||
path = self.LISTENERS_PATH.format(lb_id='SEAN-CONNERY')
|
||||
self.get(path, status=404)
|
||||
|
||||
def test_get(self):
|
||||
listener = self.create_listener(self.lb.get('id'),
|
||||
constants.PROTOCOL_HTTP, 80)
|
||||
listener_path = self.LISTENER_PATH.format(
|
||||
lb_id=self.lb.get('id'), listener_id=listener.get('id'))
|
||||
response = self.get(listener_path)
|
||||
api_lb = response.json
|
||||
expected = {'name': None, 'description': None, 'enabled': True,
|
||||
'operating_status': constants.OFFLINE,
|
||||
'provisioning_status': constants.PENDING_CREATE,
|
||||
'connection_limit': None}
|
||||
listener.update(expected)
|
||||
self.assertEqual(listener, api_lb)
|
||||
|
||||
def test_get_bad_listener_id(self):
|
||||
listener_path = self.LISTENER_PATH.format(lb_id=self.lb.get('id'),
|
||||
listener_id='SEAN-CONNERY')
|
||||
self.get(listener_path, status=404)
|
||||
|
||||
def test_create(self):
|
||||
lb_listener = {'name': 'listener1', 'description': 'desc1',
|
||||
'enabled': False, 'protocol': constants.PROTOCOL_HTTP,
|
||||
'protocol_port': 80, 'connection_limit': 10,
|
||||
'tls_certificate_id': uuidutils.generate_uuid()}
|
||||
response = self.post(self.listeners_path, lb_listener)
|
||||
listener_api = response.json
|
||||
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')))
|
||||
lb_listener['id'] = listener_api.get('id')
|
||||
self.assertEqual(lb_listener, listener_api)
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.assert_final_lb_statuses(self.lb.get('id'))
|
||||
self.assert_final_listener_statuses(self.lb.get('id'),
|
||||
listener_api.get('id'))
|
||||
|
||||
def test_create_defaults(self):
|
||||
defaults = {'name': None, 'description': None, 'enabled': True,
|
||||
'connection_limit': None, 'tls_certificate_id': None}
|
||||
lb_listener = {'protocol': constants.PROTOCOL_HTTP,
|
||||
'protocol_port': 80}
|
||||
response = self.post(self.listeners_path, lb_listener)
|
||||
listener_api = response.json
|
||||
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.assertEqual(lb_listener, listener_api)
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.assert_final_lb_statuses(self.lb.get('id'))
|
||||
self.assert_final_listener_statuses(self.lb.get('id'),
|
||||
listener_api.get('id'))
|
||||
|
||||
def test_update(self):
|
||||
tls_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,
|
||||
tls_certificate_id=tls_uuid)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
new_listener = {'name': 'listener2', 'enabled': True}
|
||||
listener_path = self.LISTENER_PATH.format(
|
||||
lb_id=self.lb.get('id'), listener_id=listener.get('id'))
|
||||
response = self.put(listener_path, new_listener)
|
||||
api_listener = response.json
|
||||
update_expect = {'name': 'listener2', 'enabled': True,
|
||||
'provisioning_status': constants.PENDING_UPDATE,
|
||||
'operating_status': constants.ONLINE}
|
||||
listener.update(update_expect)
|
||||
self.assertEqual(listener, api_listener)
|
||||
response = self.get(listener_path)
|
||||
api_listener = response.json
|
||||
self.assertEqual(listener, api_listener)
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.assert_final_lb_statuses(self.lb.get('id'))
|
||||
self.assert_final_listener_statuses(self.lb.get('id'),
|
||||
api_listener.get('id'))
|
||||
|
||||
def test_update_bad_listener_id(self):
|
||||
listener_path = self.LISTENER_PATH.format(lb_id=self.lb.get('id'),
|
||||
listener_id='SEAN-CONNERY')
|
||||
self.put(listener_path, body={}, status=404)
|
||||
|
||||
def test_create_listeners_same_port(self):
|
||||
listener1 = self.create_listener(self.lb.get('id'),
|
||||
constants.PROTOCOL_TCP, 80)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
listener2_post = {'protocol': listener1.get('protocol'),
|
||||
'protocol_port': listener1.get('protocol_port')}
|
||||
self.post(self.listeners_path, listener2_post, status=409)
|
||||
|
||||
def test_update_listeners_same_port(self):
|
||||
listener1 = self.create_listener(self.lb.get('id'),
|
||||
constants.PROTOCOL_TCP, 80)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
listener2 = self.create_listener(self.lb.get('id'),
|
||||
constants.PROTOCOL_TCP, 81)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
listener2_put = {'protocol': listener1.get('protocol'),
|
||||
'protocol_port': listener1.get('protocol_port')}
|
||||
listener2_path = self.LISTENER_PATH.format(
|
||||
lb_id=self.lb.get('id'), listener_id=listener2.get('id'))
|
||||
self.put(listener2_path, listener2_put, status=409)
|
||||
|
||||
def test_delete(self):
|
||||
listener = self.create_listener(self.lb.get('id'),
|
||||
constants.PROTOCOL_HTTP, 80)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
listener_path = self.LISTENER_PATH.format(
|
||||
lb_id=self.lb.get('id'), listener_id=listener.get('id'))
|
||||
self.delete(listener_path)
|
||||
response = self.get(listener_path)
|
||||
api_listener = response.json
|
||||
expected = {'name': None, 'description': None, 'enabled': True,
|
||||
'operating_status': constants.ONLINE,
|
||||
'provisioning_status': constants.PENDING_DELETE,
|
||||
'connection_limit': None}
|
||||
listener.update(expected)
|
||||
self.assertEqual(listener, api_listener)
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.assert_final_lb_statuses(self.lb.get('id'))
|
||||
self.assert_final_listener_statuses(self.lb.get('id'),
|
||||
api_listener.get('id'),
|
||||
delete=True)
|
||||
|
||||
def test_delete_bad_listener_id(self):
|
||||
listener_path = self.LISTENER_PATH.format(lb_id=self.lb.get('id'),
|
||||
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(self.lb.get('id'),
|
||||
constants.PROTOCOL_TCP, 80)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
new_listener = {'protocol': 'SEAN_CONNERY',
|
||||
'protocol_port': 80}
|
||||
listener_path = self.LISTENER_PATH.format(
|
||||
lb_id=self.lb.get('id'), listener_id=listener.get('id'))
|
||||
self.put(listener_path, new_listener, status=400)
|
||||
|
||||
def test_update_pending_create(self):
|
||||
lb = self.create_load_balancer({}, name='lb1', description='desc1',
|
||||
enabled=False)
|
||||
lb_listener = {'name': 'listener1', 'description': 'desc1',
|
||||
'enabled': False, 'protocol': constants.PROTOCOL_HTTP,
|
||||
'protocol_port': 80, 'connection_limit': 10}
|
||||
self.post(self.LISTENERS_PATH.format(lb_id=lb.get('id')),
|
||||
lb_listener, status=409)
|
||||
|
||||
def test_delete_pending_update(self):
|
||||
lb = self.create_load_balancer({}, name='lb1', description='desc1',
|
||||
enabled=False)
|
||||
self.set_lb_status(lb.get('id'))
|
||||
lb_listener = {'name': 'listener1', 'description': 'desc1',
|
||||
'enabled': False, 'protocol': constants.PROTOCOL_HTTP,
|
||||
'protocol_port': 80, 'connection_limit': 10}
|
||||
api_listener = self.post(
|
||||
self.LISTENERS_PATH.format(lb_id=lb.get('id')), lb_listener).json
|
||||
self.delete(self.LISTENER_PATH.format(
|
||||
lb_id=lb.get('id'), listener_id=api_listener.get('id')),
|
||||
status=409)
|
||||
|
||||
def test_update_pending_update(self):
|
||||
lb = self.create_load_balancer({}, name='lb1', description='desc1',
|
||||
enabled=False)
|
||||
self.set_lb_status(lb.get('id'))
|
||||
lb_listener = {'name': 'listener1', 'description': 'desc1',
|
||||
'enabled': False, 'protocol': constants.PROTOCOL_HTTP,
|
||||
'protocol_port': 80, 'connection_limit': 10}
|
||||
api_listener = self.post(
|
||||
self.LISTENERS_PATH.format(lb_id=lb.get('id')), lb_listener).json
|
||||
self.set_lb_status(lb.get('id'))
|
||||
self.put(self.LB_PATH.format(lb_id=lb.get('id')), {'name': 'hi'})
|
||||
self.put(self.LISTENER_PATH.format(
|
||||
lb_id=lb.get('id'), listener_id=api_listener.get('id')),
|
||||
{}, status=409)
|
||||
|
||||
def test_update_pending_delete(self):
|
||||
lb = self.create_load_balancer({}, name='lb1', description='desc1',
|
||||
enabled=False)
|
||||
self.set_lb_status(lb.get('id'))
|
||||
lb_listener = {'name': 'listener1', 'description': 'desc1',
|
||||
'enabled': False, 'protocol': constants.PROTOCOL_HTTP,
|
||||
'protocol_port': 80, 'connection_limit': 10}
|
||||
api_listener = self.post(
|
||||
self.LISTENERS_PATH.format(lb_id=lb.get('id')), lb_listener).json
|
||||
self.set_lb_status(lb.get('id'))
|
||||
self.delete(self.LB_PATH.format(lb_id=lb.get('id')))
|
||||
self.put(self.LISTENER_PATH.format(
|
||||
lb_id=lb.get('id'), listener_id=api_listener.get('id')),
|
||||
{}, status=409)
|
||||
|
||||
def test_delete_pending_delete(self):
|
||||
lb = self.create_load_balancer({}, name='lb1', description='desc1',
|
||||
enabled=False)
|
||||
self.set_lb_status(lb.get('id'))
|
||||
lb_listener = {'name': 'listener1', 'description': 'desc1',
|
||||
'enabled': False, 'protocol': constants.PROTOCOL_HTTP,
|
||||
'protocol_port': 80, 'connection_limit': 10}
|
||||
api_listener = self.post(
|
||||
self.LISTENERS_PATH.format(lb_id=lb.get('id')), lb_listener).json
|
||||
self.set_lb_status(lb.get('id'))
|
||||
self.delete(self.LB_PATH.format(lb_id=lb.get('id')))
|
||||
self.delete(self.LISTENER_PATH.format(
|
||||
lb_id=lb.get('id'), listener_id=api_listener.get('id')),
|
||||
status=409)
|
||||
|
||||
def test_create_with_tls_termination_data(self):
|
||||
tls = {'certificate': 'blah', 'intermediate_certificate': 'blah',
|
||||
'private_key': 'blah', 'passphrase': 'blah'}
|
||||
listener = self.create_listener(self.lb.get('id'),
|
||||
constants.PROTOCOL_HTTP, 80,
|
||||
tls_termination=tls)
|
||||
self.assertIsNone(listener.get('tls_termination'))
|
||||
get_listener = self.get(self.LISTENER_PATH.format(
|
||||
lb_id=self.lb.get('id'), listener_id=listener.get('id'))).json
|
||||
self.assertIsNone(get_listener.get('tls_termination'))
|
||||
|
||||
def test_update_with_tls_termination_data(self):
|
||||
tls = {'certificate': 'blah', 'intermediate_certificate': 'blah',
|
||||
'private_key': 'blah', 'passphrase': 'blah'}
|
||||
listener = self.create_listener(self.lb.get('id'),
|
||||
constants.PROTOCOL_HTTP, 80)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
listener_path = self.LISTENER_PATH.format(
|
||||
lb_id=self.lb.get('id'), listener_id=listener.get('id'))
|
||||
listener = self.put(listener_path, {'tls_termination': tls}).json
|
||||
self.assertIsNone(listener.get('tls_termination'))
|
||||
get_listener = self.get(listener_path).json
|
||||
self.assertIsNone(get_listener.get('tls_termination'))
|
208
octavia/tests/functional/api/v1/test_load_balancer.py
Normal file
208
octavia/tests/functional/api/v1/test_load_balancer.py
Normal file
@ -0,0 +1,208 @@
|
||||
# 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 octavia.common import constants
|
||||
from octavia.openstack.common import uuidutils
|
||||
from octavia.tests.functional.api.v1 import base
|
||||
|
||||
|
||||
class TestLoadBalancer(base.BaseAPITest):
|
||||
|
||||
def test_empty_list(self):
|
||||
response = self.get(self.LBS_PATH)
|
||||
api_list = response.json
|
||||
self.assertEqual([], api_list)
|
||||
|
||||
def test_create(self):
|
||||
lb_json = {'name': 'test1', 'vip': {}}
|
||||
response = self.post(self.LBS_PATH, lb_json)
|
||||
api_lb = response.json
|
||||
self.assertTrue(uuidutils.is_uuid_like(api_lb.get('id')))
|
||||
self.assertEqual(lb_json.get('name'), api_lb.get('name'))
|
||||
self.assertEqual(constants.PENDING_CREATE,
|
||||
api_lb.get('provisioning_status'))
|
||||
self.assertEqual(constants.OFFLINE,
|
||||
api_lb.get('operating_status'))
|
||||
self.assertTrue(api_lb.get('enabled'))
|
||||
self.assert_final_lb_statuses(api_lb.get('id'))
|
||||
|
||||
def test_create_without_vip(self):
|
||||
lb_json = {'name': 'test1'}
|
||||
self.post(self.LB_PATH, lb_json, status=400)
|
||||
|
||||
def test_get_all(self):
|
||||
lb1 = self.create_load_balancer({}, name='lb1')
|
||||
lb2 = self.create_load_balancer({}, name='lb2')
|
||||
lb3 = self.create_load_balancer({}, name='lb3')
|
||||
response = self.get(self.LBS_PATH)
|
||||
lbs = response.json
|
||||
lb_id_names = [(lb.get('id'), lb.get('name')) for lb in lbs]
|
||||
self.assertEqual(3, len(lbs))
|
||||
self.assertIn((lb1.get('id'), lb1.get('name')), lb_id_names)
|
||||
self.assertIn((lb2.get('id'), lb2.get('name')), lb_id_names)
|
||||
self.assertIn((lb3.get('id'), lb3.get('name')), lb_id_names)
|
||||
|
||||
def test_get(self):
|
||||
vip = {'ip_address': '10.0.0.1',
|
||||
'floating_ip_id': uuidutils.generate_uuid(),
|
||||
'net_port_id': uuidutils.generate_uuid(),
|
||||
'subnet_id': uuidutils.generate_uuid(),
|
||||
'floating_ip_network_id': uuidutils.generate_uuid()}
|
||||
lb = self.create_load_balancer(vip, name='lb1',
|
||||
description='test1_desc',
|
||||
enabled=False)
|
||||
response = self.get(self.LB_PATH.format(lb_id=lb.get('id')))
|
||||
self.assertEqual('lb1', response.json.get('name'))
|
||||
self.assertEqual('test1_desc', response.json.get('description'))
|
||||
self.assertFalse(response.json.get('enabled'))
|
||||
self.assertEqual(vip, response.json.get('vip'))
|
||||
|
||||
def test_get_bad_lb_id(self):
|
||||
path = self.LB_PATH.format(lb_id='SEAN-CONNERY')
|
||||
self.get(path, status=404)
|
||||
|
||||
def test_create_with_vip(self):
|
||||
vip = {'ip_address': '10.0.0.1',
|
||||
'floating_ip_id': uuidutils.generate_uuid(),
|
||||
'net_port_id': uuidutils.generate_uuid(),
|
||||
'subnet_id': uuidutils.generate_uuid(),
|
||||
'floating_ip_network_id': uuidutils.generate_uuid()}
|
||||
lb_json = {'name': 'test1', 'description': 'test1_desc',
|
||||
'vip': vip, 'enabled': False}
|
||||
response = self.post(self.LBS_PATH, lb_json)
|
||||
api_lb = response.json
|
||||
self.assertTrue(uuidutils.is_uuid_like(api_lb.get('id')))
|
||||
self.assertEqual(lb_json.get('name'), api_lb.get('name'))
|
||||
self.assertEqual(lb_json.get('description'), api_lb.get('description'))
|
||||
self.assertEqual(constants.PENDING_CREATE,
|
||||
api_lb['provisioning_status'])
|
||||
self.assertEqual(constants.OFFLINE,
|
||||
api_lb['operating_status'])
|
||||
self.assertEqual(vip, api_lb.get('vip'))
|
||||
self.assertEqual(lb_json.get('enabled'), api_lb.get('enabled'))
|
||||
self.assert_final_lb_statuses(api_lb.get('id'))
|
||||
|
||||
def test_create_with_long_name(self):
|
||||
lb_json = {'name': 'n' * 256, 'vip': {}}
|
||||
self.post(self.LBS_PATH, lb_json, status=400)
|
||||
|
||||
def test_create_with_long_description(self):
|
||||
lb_json = {'description': 'n' * 256, 'vip': {}}
|
||||
self.post(self.LBS_PATH, lb_json, status=400)
|
||||
|
||||
def test_create_with_nonuuid_vip_attributes(self):
|
||||
lb_json = {'vip': {'floating_ip_id': 'HI'}}
|
||||
self.post(self.LBS_PATH, lb_json, status=400)
|
||||
|
||||
def test_update(self):
|
||||
lb = self.create_load_balancer({}, name='lb1', description='desc1',
|
||||
enabled=False)
|
||||
lb_json = {'name': 'lb2'}
|
||||
lb = self.set_lb_status(lb.get('id'))
|
||||
response = self.put(self.LB_PATH.format(lb_id=lb.get('id')), lb_json)
|
||||
api_lb = response.json
|
||||
r_vip = api_lb.get('vip')
|
||||
self.assertIsNone(r_vip.get('floating_ip_id'))
|
||||
self.assertEqual('lb2', api_lb.get('name'))
|
||||
self.assertEqual('desc1', api_lb.get('description'))
|
||||
self.assertFalse(api_lb.get('enabled'))
|
||||
self.assertEqual(constants.PENDING_UPDATE,
|
||||
api_lb.get('provisioning_status'))
|
||||
self.assertEqual(lb.get('operational_status'),
|
||||
api_lb.get('operational_status'))
|
||||
self.assert_final_lb_statuses(api_lb.get('id'))
|
||||
|
||||
def test_update_with_vip(self):
|
||||
lb = self.create_load_balancer({}, name='lb1', description='desc1',
|
||||
enabled=False)
|
||||
lb_json = {'vip': {'floating_ip_id': '1234'}}
|
||||
lb = self.set_lb_status(lb.get('id'))
|
||||
response = self.put(self.LB_PATH.format(lb_id=lb.get('id')), lb_json)
|
||||
api_lb = response.json
|
||||
r_vip = api_lb.get('vip')
|
||||
self.assertIsNone(r_vip.get('floating_ip_id'))
|
||||
self.assertEqual('lb1', api_lb.get('name'))
|
||||
self.assertEqual('desc1', api_lb.get('description'))
|
||||
self.assertFalse(api_lb.get('enabled'))
|
||||
self.assertEqual(constants.PENDING_UPDATE,
|
||||
api_lb.get('provisioning_status'))
|
||||
self.assertEqual(lb.get('operational_status'),
|
||||
api_lb.get('operational_status'))
|
||||
self.assert_final_lb_statuses(api_lb.get('id'))
|
||||
|
||||
def test_update_bad_lb_id(self):
|
||||
path = self.LB_PATH.format(lb_id='SEAN-CONNERY')
|
||||
self.put(path, body={}, status=404)
|
||||
|
||||
def test_update_pending_create(self):
|
||||
lb = self.create_load_balancer({}, name='lb1', description='desc1',
|
||||
enabled=False)
|
||||
lb_json = {'vip': {'floating_ip_id': '1234'}}
|
||||
self.put(self.LB_PATH.format(lb_id=lb.get('id')), lb_json, status=409)
|
||||
|
||||
def test_delete_pending_create(self):
|
||||
lb = self.create_load_balancer({}, name='lb1', description='desc1',
|
||||
enabled=False)
|
||||
self.delete(self.LB_PATH.format(lb_id=lb.get('id')), status=409)
|
||||
|
||||
def test_update_pending_update(self):
|
||||
lb = self.create_load_balancer({}, name='lb1', description='desc1',
|
||||
enabled=False)
|
||||
lb_json = {'vip': {'floating_ip_id': '1234'}}
|
||||
lb = self.set_lb_status(lb.get('id'))
|
||||
self.put(self.LB_PATH.format(lb_id=lb.get('id')), lb_json)
|
||||
self.put(self.LB_PATH.format(lb_id=lb.get('id')), lb_json, status=409)
|
||||
|
||||
def test_delete_pending_update(self):
|
||||
lb = self.create_load_balancer({}, name='lb1', description='desc1',
|
||||
enabled=False)
|
||||
lb_json = {'vip': {'floating_ip_id': '1234'}}
|
||||
lb = self.set_lb_status(lb.get('id'))
|
||||
self.put(self.LB_PATH.format(lb_id=lb.get('id')), lb_json)
|
||||
self.delete(self.LB_PATH.format(lb_id=lb.get('id')), status=409)
|
||||
|
||||
def test_update_pending_delete(self):
|
||||
lb = self.create_load_balancer({}, name='lb1', description='desc1',
|
||||
enabled=False)
|
||||
lb = self.set_lb_status(lb.get('id'))
|
||||
self.delete(self.LB_PATH.format(lb_id=lb.get('id')))
|
||||
lb_json = {'vip': {'floating_ip_id': '1234'}}
|
||||
self.put(self.LB_PATH.format(lb_id=lb.get('id')), lb_json, status=409)
|
||||
|
||||
def test_delete_pending_delete(self):
|
||||
lb = self.create_load_balancer({}, name='lb1', description='desc1',
|
||||
enabled=False)
|
||||
lb = self.set_lb_status(lb.get('id'))
|
||||
self.delete(self.LB_PATH.format(lb_id=lb.get('id')))
|
||||
self.delete(self.LB_PATH.format(lb_id=lb.get('id')), status=409)
|
||||
|
||||
def test_delete(self):
|
||||
lb = self.create_load_balancer({}, name='lb1', description='desc1',
|
||||
enabled=False)
|
||||
lb = self.set_lb_status(lb.get('id'))
|
||||
self.delete(self.LB_PATH.format(lb_id=lb.get('id')))
|
||||
response = self.get(self.LB_PATH.format(lb_id=lb.get('id')))
|
||||
api_lb = response.json
|
||||
self.assertEqual('lb1', api_lb.get('name'))
|
||||
self.assertEqual('desc1', api_lb.get('description'))
|
||||
self.assertFalse(api_lb.get('enabled'))
|
||||
self.assertEqual(constants.PENDING_DELETE,
|
||||
api_lb.get('provisioning_status'))
|
||||
self.assertEqual(lb.get('operational_status'),
|
||||
api_lb.get('operational_status'))
|
||||
self.assert_final_lb_statuses(api_lb.get('id'), delete=True)
|
||||
|
||||
def test_delete_bad_lb_id(self):
|
||||
path = self.LB_PATH.format(lb_id='bad_uuid')
|
||||
self.delete(path, status=404)
|
250
octavia/tests/functional/api/v1/test_member.py
Normal file
250
octavia/tests/functional/api/v1/test_member.py
Normal file
@ -0,0 +1,250 @@
|
||||
# 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 octavia.common import constants
|
||||
from octavia.openstack.common import uuidutils
|
||||
from octavia.tests.functional.api.v1 import base
|
||||
|
||||
|
||||
class TestMember(base.BaseAPITest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestMember, self).setUp()
|
||||
self.lb = self.create_load_balancer({})
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.listener = self.create_listener(self.lb.get('id'),
|
||||
constants.PROTOCOL_HTTP, 80)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.pool = self.create_pool(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.members_path = self.MEMBERS_PATH.format(
|
||||
lb_id=self.lb.get('id'), listener_id=self.listener.get('id'),
|
||||
pool_id=self.pool.get('id'))
|
||||
self.member_path = self.members_path + '/{member_id}'
|
||||
|
||||
def test_get(self):
|
||||
api_member = self.create_member(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
self.pool.get('id'),
|
||||
'10.0.0.1', 80)
|
||||
response = self.get(self.member_path.format(
|
||||
member_id=api_member.get('id')))
|
||||
response_body = response.json
|
||||
self.assertEqual(api_member, response_body)
|
||||
|
||||
def test_bad_get(self):
|
||||
self.get(self.member_path.format(member_id=uuidutils.generate_uuid()),
|
||||
status=404)
|
||||
|
||||
def test_get_all(self):
|
||||
api_m_1 = self.create_member(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
self.pool.get('id'),
|
||||
'10.0.0.1', 80)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
api_m_2 = self.create_member(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
self.pool.get('id'),
|
||||
'10.0.0.2', 80)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
# Original objects didn't have the updated operating status that exists
|
||||
# in the DB.
|
||||
api_m_1['operating_status'] = constants.ONLINE
|
||||
api_m_2['operating_status'] = constants.ONLINE
|
||||
response = self.get(self.members_path)
|
||||
response_body = response.json
|
||||
self.assertIsInstance(response_body, list)
|
||||
self.assertEqual(2, len(response_body))
|
||||
self.assertIn(api_m_1, response_body)
|
||||
self.assertIn(api_m_2, response_body)
|
||||
|
||||
def test_empty_get_all(self):
|
||||
response = self.get(self.members_path)
|
||||
response_body = response.json
|
||||
self.assertIsInstance(response_body, list)
|
||||
self.assertEqual(0, len(response_body))
|
||||
|
||||
def test_create(self):
|
||||
api_member = self.create_member(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
self.pool.get('id'),
|
||||
'10.0.0.1', 80)
|
||||
self.assertEqual('10.0.0.1', api_member.get('ip_address'))
|
||||
self.assertEqual(80, api_member.get('protocol_port'))
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.assert_correct_listener_status(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.ACTIVE,
|
||||
constants.ONLINE)
|
||||
self.assert_correct_listener_status(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.ACTIVE, constants.ONLINE)
|
||||
|
||||
def test_bad_create(self):
|
||||
api_member = {'name': 'test1'}
|
||||
self.post(self.members_path, api_member, status=400)
|
||||
|
||||
def test_duplicate_create(self):
|
||||
member = {'ip_address': '10.0.0.1', 'protocol_port': 80}
|
||||
self.post(self.members_path, member, status=202)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.post(self.members_path, member, status=409)
|
||||
|
||||
def test_update(self):
|
||||
api_member = self.create_member(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
self.pool.get('id'),
|
||||
'10.0.0.1', 80)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
new_member = {'protocol_port': 88}
|
||||
self.put(self.member_path.format(member_id=api_member.get('id')),
|
||||
new_member, status=202)
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.assert_correct_listener_status(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
response = self.get(self.member_path.format(
|
||||
member_id=api_member.get('id')))
|
||||
response_body = response.json
|
||||
self.assertEqual(88, response_body.get('protocol_port'))
|
||||
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.ACTIVE,
|
||||
constants.ONLINE)
|
||||
self.assert_correct_listener_status(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.ACTIVE, constants.ONLINE)
|
||||
|
||||
def test_bad_update(self):
|
||||
api_member = self.create_member(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
self.pool.get('id'),
|
||||
'10.0.0.1', 80)
|
||||
new_member = {'protocol_port': 'ten'}
|
||||
self.put(self.member_path.format(member_id=api_member.get('id')),
|
||||
new_member, expect_errors=True)
|
||||
|
||||
def test_duplicate_update(self):
|
||||
member = {'ip_address': '10.0.0.1', 'protocol_port': 80}
|
||||
self.post(self.members_path, member)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
member['protocol_port'] = 81
|
||||
response = self.post(self.members_path, member)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
member2 = response.json
|
||||
member['protocol_port'] = 80
|
||||
self.put(self.member_path.format(member_id=member2.get('id')),
|
||||
member, status=409)
|
||||
|
||||
def test_delete(self):
|
||||
api_member = self.create_member(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
self.pool.get('id'),
|
||||
'10.0.0.1', 80)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
response = self.get(self.member_path.format(
|
||||
member_id=api_member.get('id')))
|
||||
api_member['operating_status'] = constants.ONLINE
|
||||
self.assertEqual(api_member, response.json)
|
||||
self.delete(self.member_path.format(member_id=api_member.get('id')))
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.assert_correct_listener_status(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.ACTIVE,
|
||||
constants.ONLINE)
|
||||
self.assert_correct_listener_status(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.ACTIVE, constants.ONLINE)
|
||||
|
||||
def test_bad_delete(self):
|
||||
self.delete(self.member_path.format(
|
||||
member_id=uuidutils.generate_uuid()), status=404)
|
||||
|
||||
def test_create_when_lb_pending_update(self):
|
||||
self.create_member(self.lb.get('id'), self.listener.get('id'),
|
||||
self.pool.get('id'), ip_address="10.0.0.2",
|
||||
protocol_port=80)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.put(self.LB_PATH.format(lb_id=self.lb.get('id')),
|
||||
body={'name': 'test_name_change'})
|
||||
self.post(self.members_path,
|
||||
body={'ip_address': '10.0.0.1', 'protocol_port': 80},
|
||||
status=409)
|
||||
|
||||
def test_update_when_lb_pending_update(self):
|
||||
member = self.create_member(self.lb.get('id'), self.listener.get('id'),
|
||||
self.pool.get('id'), ip_address="10.0.0.1",
|
||||
protocol_port=80)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.put(self.LB_PATH.format(lb_id=self.lb.get('id')),
|
||||
body={'name': 'test_name_change'})
|
||||
self.put(self.member_path.format(member_id=member.get('id')),
|
||||
body={'protocol_port': 88}, status=409)
|
||||
|
||||
def test_delete_when_lb_pending_update(self):
|
||||
member = self.create_member(self.lb.get('id'), self.listener.get('id'),
|
||||
self.pool.get('id'), ip_address="10.0.0.1",
|
||||
protocol_port=80)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.put(self.LB_PATH.format(lb_id=self.lb.get('id')),
|
||||
body={'name': 'test_name_change'})
|
||||
self.delete(self.member_path.format(member_id=member.get('id')),
|
||||
status=409)
|
||||
|
||||
def test_create_when_lb_pending_delete(self):
|
||||
self.create_member(self.lb.get('id'), self.listener.get('id'),
|
||||
self.pool.get('id'), ip_address="10.0.0.1",
|
||||
protocol_port=80)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb.get('id')))
|
||||
self.post(self.members_path,
|
||||
body={'ip_address': '10.0.0.2', 'protocol_port': 88},
|
||||
status=409)
|
||||
|
||||
def test_update_when_lb_pending_delete(self):
|
||||
member = self.create_member(self.lb.get('id'), self.listener.get('id'),
|
||||
self.pool.get('id'), ip_address="10.0.0.1",
|
||||
protocol_port=80)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb.get('id')))
|
||||
self.put(self.member_path.format(member_id=member.get('id')),
|
||||
body={'protocol_port': 88}, status=409)
|
||||
|
||||
def test_delete_when_lb_pending_delete(self):
|
||||
member = self.create_member(self.lb.get('id'), self.listener.get('id'),
|
||||
self.pool.get('id'), ip_address="10.0.0.1",
|
||||
protocol_port=80)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb.get('id')))
|
||||
self.delete(self.member_path.format(member_id=member.get('id')),
|
||||
status=409)
|
390
octavia/tests/functional/api/v1/test_pool.py
Normal file
390
octavia/tests/functional/api/v1/test_pool.py
Normal file
@ -0,0 +1,390 @@
|
||||
# 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 octavia.common import constants
|
||||
from octavia.openstack.common import uuidutils
|
||||
from octavia.tests.functional.api.v1 import base
|
||||
|
||||
|
||||
class TestPool(base.BaseAPITest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestPool, self).setUp()
|
||||
self.lb = self.create_load_balancer({})
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.listener = self.create_listener(self.lb.get('id'),
|
||||
constants.PROTOCOL_HTTP, 80)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.pools_path = self.POOLS_PATH.format(
|
||||
lb_id=self.lb.get('id'), listener_id=self.listener.get('id'))
|
||||
self.pool_path = self.pools_path + '/{pool_id}'
|
||||
|
||||
def test_get(self):
|
||||
api_pool = self.create_pool(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN)
|
||||
self.set_lb_status(lb_id=self.lb.get('id'))
|
||||
api_pool['operating_status'] = constants.ONLINE
|
||||
response = self.get(self.pool_path.format(pool_id=api_pool.get('id')))
|
||||
response_body = response.json
|
||||
self.assertEqual(api_pool, response_body)
|
||||
|
||||
def test_bad_get(self):
|
||||
self.get(self.pool_path.format(pool_id=uuidutils.generate_uuid()),
|
||||
status=404)
|
||||
|
||||
def test_get_all(self):
|
||||
api_pool = self.create_pool(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN)
|
||||
self.set_lb_status(lb_id=self.lb.get('id'))
|
||||
response = self.get(self.pools_path)
|
||||
response_body = response.json
|
||||
self.assertIsInstance(response_body, list)
|
||||
self.assertEqual(1, len(response_body))
|
||||
self.assertEqual(api_pool.get('id'), response_body[0].get('id'))
|
||||
|
||||
def test_empty_get_all(self):
|
||||
response = self.get(self.pools_path)
|
||||
response_body = response.json
|
||||
self.assertIsInstance(response_body, list)
|
||||
self.assertEqual(0, len(response_body))
|
||||
|
||||
def test_create(self):
|
||||
api_pool = self.create_pool(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN)
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.assert_correct_listener_status(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.assertEqual(constants.PROTOCOL_HTTP, api_pool.get('protocol'))
|
||||
self.assertEqual(constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
api_pool.get('lb_algorithm'))
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.ACTIVE,
|
||||
constants.ONLINE)
|
||||
self.assert_correct_listener_status(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.ACTIVE, constants.ONLINE)
|
||||
|
||||
def test_bad_create(self):
|
||||
api_pool = {'name': 'test1'}
|
||||
self.post(self.pools_path, api_pool, status=400)
|
||||
|
||||
def test_duplicate_create(self):
|
||||
pool = {'protocol': constants.PROTOCOL_HTTP,
|
||||
'lb_algorithm': constants.LB_ALGORITHM_ROUND_ROBIN}
|
||||
self.post(self.pools_path, pool)
|
||||
self.post(self.pools_path, pool, status=409)
|
||||
|
||||
def test_create_bad_protocol(self):
|
||||
pool = {'protocol': 'STUPID_PROTOCOL',
|
||||
'lb_algorithm': constants.LB_ALGORITHM_ROUND_ROBIN}
|
||||
self.post(self.pools_path, pool, status=400)
|
||||
|
||||
def test_update(self):
|
||||
api_pool = self.create_pool(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN)
|
||||
self.set_lb_status(lb_id=self.lb.get('id'))
|
||||
new_pool = {'name': 'new_name'}
|
||||
self.put(self.pool_path.format(pool_id=api_pool.get('id')),
|
||||
new_pool, status=202)
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.assert_correct_listener_status(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
response = self.get(self.pool_path.format(pool_id=api_pool.get('id')))
|
||||
response_body = response.json
|
||||
self.assertEqual('new_name', response_body.get('name'))
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.ACTIVE,
|
||||
constants.ONLINE)
|
||||
self.assert_correct_listener_status(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.ACTIVE, constants.ONLINE)
|
||||
|
||||
def test_bad_update(self):
|
||||
api_pool = self.create_pool(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN)
|
||||
new_pool = {'enabled': 'one'}
|
||||
self.put(self.pool_path.format(pool_id=api_pool.get('id')),
|
||||
new_pool, status=400)
|
||||
|
||||
def test_delete(self):
|
||||
api_pool = self.create_pool(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN)
|
||||
self.set_lb_status(lb_id=self.lb.get('id'))
|
||||
api_pool['operating_status'] = constants.ONLINE
|
||||
response = self.get(self.pool_path.format(
|
||||
pool_id=api_pool.get('id')))
|
||||
self.assertEqual(api_pool, response.json)
|
||||
self.delete(self.pool_path.format(pool_id=api_pool.get('id')))
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.assert_correct_listener_status(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.ACTIVE,
|
||||
constants.ONLINE)
|
||||
self.assert_correct_listener_status(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.ACTIVE, constants.ONLINE)
|
||||
|
||||
def test_bad_delete(self):
|
||||
self.delete(self.pool_path.format(
|
||||
pool_id=uuidutils.generate_uuid()), status=404)
|
||||
|
||||
def test_create_with_session_persistence(self):
|
||||
sp = {"type": constants.SESSION_PERSISTENCE_HTTP_COOKIE,
|
||||
"cookie_name": "test_cookie_name"}
|
||||
api_pool = self.create_pool(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
session_persistence=sp)
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.assert_correct_listener_status(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
response = self.get(self.pool_path.format(
|
||||
pool_id=api_pool.get('id')))
|
||||
response_body = response.json
|
||||
sess_p = response_body.get('session_persistence')
|
||||
self.assertIsNotNone(sess_p)
|
||||
self.assertEqual(constants.SESSION_PERSISTENCE_HTTP_COOKIE,
|
||||
sess_p.get('type'))
|
||||
self.assertEqual('test_cookie_name', sess_p.get('cookie_name'))
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.ACTIVE,
|
||||
constants.ONLINE)
|
||||
self.assert_correct_listener_status(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.ACTIVE, constants.ONLINE)
|
||||
|
||||
def test_create_with_bad_session_persistence(self):
|
||||
sp = {"type": "persistence_type",
|
||||
"cookie_name": "test_cookie_name"}
|
||||
pool = {'protocol': constants.PROTOCOL_HTTP,
|
||||
'lb_algorithm': constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
'session_persistence': sp}
|
||||
self.post(self.pools_path, pool, status=400)
|
||||
|
||||
def test_add_session_persistence(self):
|
||||
sp = {"type": constants.SESSION_PERSISTENCE_HTTP_COOKIE,
|
||||
"cookie_name": "test_cookie_name"}
|
||||
api_pool = self.create_pool(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN)
|
||||
self.set_lb_status(lb_id=self.lb.get('id'))
|
||||
response = self.put(self.pool_path.format(pool_id=api_pool.get('id')),
|
||||
body={'session_persistence': sp})
|
||||
api_pool = response.json
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.assert_correct_listener_status(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.assertEqual(sp, api_pool.get('session_persistence'))
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.ACTIVE,
|
||||
constants.ONLINE)
|
||||
self.assert_correct_listener_status(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.ACTIVE, constants.ONLINE)
|
||||
|
||||
def test_update_session_persistence(self):
|
||||
sp = {"type": constants.SESSION_PERSISTENCE_HTTP_COOKIE,
|
||||
"cookie_name": "test_cookie_name"}
|
||||
api_pool = self.create_pool(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
session_persistence=sp)
|
||||
self.set_lb_status(lb_id=self.lb.get('id'))
|
||||
response = self.get(self.pool_path.format(
|
||||
pool_id=api_pool.get('id')))
|
||||
response_body = response.json
|
||||
sess_p = response_body.get('session_persistence')
|
||||
sess_p['cookie_name'] = 'new_test_cookie_name'
|
||||
api_pool = self.put(self.pool_path.format(pool_id=api_pool.get('id')),
|
||||
body={'session_persistence': sess_p}).json
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.assert_correct_listener_status(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.assertEqual(sess_p, api_pool.get('session_persistence'))
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.ACTIVE,
|
||||
constants.ONLINE)
|
||||
self.assert_correct_listener_status(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.ACTIVE, constants.ONLINE)
|
||||
|
||||
def test_update_bad_session_persistence(self):
|
||||
sp = {"type": constants.SESSION_PERSISTENCE_HTTP_COOKIE,
|
||||
"cookie_name": "test_cookie_name"}
|
||||
api_pool = self.create_pool(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
session_persistence=sp)
|
||||
self.set_lb_status(lb_id=self.lb.get('id'))
|
||||
response = self.get(self.pool_path.format(
|
||||
pool_id=api_pool.get('id')))
|
||||
response_body = response.json
|
||||
sess_p = response_body.get('session_persistence')
|
||||
sess_p['type'] = 'persistence_type'
|
||||
self.put(self.pool_path.format(pool_id=api_pool.get('id')),
|
||||
body={'session_persistence': sess_p}, status=400)
|
||||
|
||||
def test_delete_with_session_persistence(self):
|
||||
sp = {"type": constants.SESSION_PERSISTENCE_HTTP_COOKIE,
|
||||
"cookie_name": "test_cookie_name"}
|
||||
api_pool = self.create_pool(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
session_persistence=sp)
|
||||
self.set_lb_status(lb_id=self.lb.get('id'))
|
||||
self.delete(self.pool_path.format(pool_id=api_pool.get('id')),
|
||||
status=202)
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.assert_correct_listener_status(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.ACTIVE,
|
||||
constants.ONLINE)
|
||||
self.assert_correct_listener_status(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.ACTIVE, constants.ONLINE)
|
||||
|
||||
def test_delete_session_persistence(self):
|
||||
sp = {"type": constants.SESSION_PERSISTENCE_HTTP_COOKIE,
|
||||
"cookie_name": "test_cookie_name"}
|
||||
api_pool = self.create_pool(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
session_persistence=sp)
|
||||
self.set_lb_status(lb_id=self.lb.get('id'))
|
||||
sp = {'session_persistence': None}
|
||||
api_pool = self.put(self.pool_path.format(pool_id=api_pool.get('id')),
|
||||
body=sp, status=202).json
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.assert_correct_listener_status(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.PENDING_UPDATE,
|
||||
constants.ONLINE)
|
||||
self.assertEqual(None, api_pool.get('session_persistence'))
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.assert_correct_lb_status(self.lb.get('id'),
|
||||
constants.ACTIVE,
|
||||
constants.ONLINE)
|
||||
self.assert_correct_listener_status(self.lb.get('id'),
|
||||
self.listener.get('id'),
|
||||
constants.ACTIVE, constants.ONLINE)
|
||||
|
||||
def test_create_when_lb_pending_update(self):
|
||||
self.put(self.LB_PATH.format(lb_id=self.lb.get('id')),
|
||||
body={'name': 'test_name_change'})
|
||||
self.post(self.pools_path,
|
||||
body={'protocol': constants.PROTOCOL_HTTP,
|
||||
'lb_algorithm': constants.LB_ALGORITHM_ROUND_ROBIN},
|
||||
status=409)
|
||||
|
||||
def test_update_when_lb_pending_update(self):
|
||||
pool = self.create_pool(self.lb.get('id'), self.listener.get('id'),
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.put(self.LB_PATH.format(lb_id=self.lb.get('id')),
|
||||
body={'name': 'test_name_change'})
|
||||
self.put(self.pool_path.format(pool_id=pool.get('id')),
|
||||
body={'protocol': constants.PROTOCOL_HTTPS},
|
||||
status=409)
|
||||
|
||||
def test_delete_when_lb_pending_update(self):
|
||||
pool = self.create_pool(self.lb.get('id'), self.listener.get('id'),
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.put(self.LB_PATH.format(lb_id=self.lb.get('id')),
|
||||
body={'name': 'test_name_change'})
|
||||
self.delete(self.pool_path.format(pool_id=pool.get('id')), status=409)
|
||||
|
||||
def test_create_when_lb_pending_delete(self):
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb.get('id')))
|
||||
self.post(self.pools_path,
|
||||
body={'protocol': constants.PROTOCOL_HTTP,
|
||||
'lb_algorithm': constants.LB_ALGORITHM_ROUND_ROBIN},
|
||||
status=409)
|
||||
|
||||
def test_update_when_lb_pending_delete(self):
|
||||
pool = self.create_pool(self.lb.get('id'), self.listener.get('id'),
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb.get('id')))
|
||||
self.put(self.pool_path.format(pool_id=pool.get('id')),
|
||||
body={'protocol': constants.PROTOCOL_HTTPS},
|
||||
status=409)
|
||||
|
||||
def test_delete_when_lb_pending_delete(self):
|
||||
pool = self.create_pool(self.lb.get('id'), self.listener.get('id'),
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN)
|
||||
self.set_lb_status(self.lb.get('id'))
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb.get('id')))
|
||||
self.delete(self.pool_path.format(pool_id=pool.get('id')), status=409)
|
0
octavia/tests/functional/db/__init__.py
Normal file
0
octavia/tests/functional/db/__init__.py
Normal file
@ -12,9 +12,12 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo.config import cfg
|
||||
from oslo.db import options as db_options
|
||||
from oslo.db.sqlalchemy import test_base
|
||||
|
||||
from octavia.common import constants
|
||||
from octavia.db import api as db_api
|
||||
from octavia.db import base_models
|
||||
from octavia.db import models
|
||||
|
||||
@ -23,24 +26,27 @@ class OctaviaDBTestBase(test_base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(OctaviaDBTestBase, self).setUp()
|
||||
# NOTE(blogan): doing this for now because using the engine and
|
||||
# session set up in the fixture for test_base.DbTestCase does not work
|
||||
# with the API functional tests. Need to investigate more if this
|
||||
# becomes a problem
|
||||
cfg.CONF.register_opts(db_options.database_opts, 'database')
|
||||
cfg.CONF.set_override('connection', 'sqlite://', group='database')
|
||||
# needed for closure
|
||||
engine = self.engine
|
||||
base_models.BASE.metadata.create_all(bind=engine)
|
||||
self._seed_lookup_tables()
|
||||
engine = db_api.get_engine()
|
||||
session = db_api.get_session()
|
||||
base_models.BASE.metadata.create_all(engine)
|
||||
self._seed_lookup_tables(session)
|
||||
|
||||
def unregister_models():
|
||||
def clear_tables():
|
||||
"""Unregister all data models."""
|
||||
base_models.BASE.metadata.drop_all(bind=engine)
|
||||
base_models.BASE.metadata.drop_all(engine)
|
||||
|
||||
self.addCleanup(unregister_models)
|
||||
self.addCleanup(clear_tables)
|
||||
|
||||
self.session = self._get_session()
|
||||
self.session = session
|
||||
|
||||
def _get_session(self):
|
||||
return self.sessionmaker(bind=self.engine, expire_on_commit=True)
|
||||
|
||||
def _seed_lookup_tables(self):
|
||||
session = self._get_session()
|
||||
def _seed_lookup_tables(self, session):
|
||||
self._seed_lookup_table(
|
||||
session, constants.SUPPORTED_PROVISIONING_STATUSES,
|
||||
models.ProvisioningStatus)
|
@ -15,13 +15,14 @@
|
||||
from octavia.common import constants
|
||||
from octavia.common import data_models
|
||||
from octavia.db import models
|
||||
from octavia.tests.unit.db import base
|
||||
from octavia.openstack.common import uuidutils
|
||||
from octavia.tests.functional.db import base
|
||||
|
||||
|
||||
class ModelTestMixin(object):
|
||||
|
||||
FAKE_UUID_1 = '0123456789012345678901234567890123456'
|
||||
FAKE_UUID_2 = '1234567890123456789012345678901234567'
|
||||
FAKE_UUID_1 = uuidutils.generate_uuid()
|
||||
FAKE_UUID_2 = uuidutils.generate_uuid()
|
||||
|
||||
def _insert(self, session, model_cls, model_kwargs):
|
||||
with session.begin():
|
||||
@ -69,9 +70,7 @@ class ModelTestMixin(object):
|
||||
return self._insert(session, models.SessionPersistence, kwargs)
|
||||
|
||||
def create_health_monitor(self, session, pool_id, **overrides):
|
||||
kwargs = {'tenant_id': self.FAKE_UUID_1,
|
||||
'id': self.FAKE_UUID_1,
|
||||
'pool_id': pool_id,
|
||||
kwargs = {'pool_id': pool_id,
|
||||
'type': constants.HEALTH_MONITOR_HTTP,
|
||||
'delay': 1,
|
||||
'timeout': 1,
|
||||
@ -115,7 +114,7 @@ class ModelTestMixin(object):
|
||||
def create_amphora(self, session, **overrides):
|
||||
kwargs = {'id': self.FAKE_UUID_1,
|
||||
'host_id': self.FAKE_UUID_1,
|
||||
'status': constants.ONLINE}
|
||||
'status': constants.ACTIVE}
|
||||
kwargs.update(overrides)
|
||||
return self._insert(session, models.Amphora, kwargs)
|
||||
|
||||
@ -361,26 +360,27 @@ class HealthMonitorModelTest(base.OctaviaDBTestBase, ModelTestMixin):
|
||||
|
||||
def test_update(self):
|
||||
health_monitor = self.create_health_monitor(self.session, self.pool.id)
|
||||
health_monitor_id = health_monitor.id
|
||||
health_monitor.name = 'test1'
|
||||
new_health_monitor = self.session.query(
|
||||
models.HealthMonitor).filter_by(id=health_monitor_id).first()
|
||||
models.HealthMonitor).filter_by(
|
||||
pool_id=health_monitor.pool_id).first()
|
||||
self.assertEqual('test1', new_health_monitor.name)
|
||||
|
||||
def test_delete(self):
|
||||
health_monitor = self.create_health_monitor(self.session, self.pool.id)
|
||||
health_monitor_id = health_monitor.id
|
||||
with self.session.begin():
|
||||
self.session.delete(health_monitor)
|
||||
self.session.flush()
|
||||
new_health_monitor = self.session.query(
|
||||
models.HealthMonitor).filter_by(id=health_monitor_id).first()
|
||||
models.HealthMonitor).filter_by(
|
||||
pool_id=health_monitor.pool_id).first()
|
||||
self.assertIsNone(new_health_monitor)
|
||||
|
||||
def test_pool_relationship(self):
|
||||
health_monitor = self.create_health_monitor(self.session, self.pool.id)
|
||||
new_health_monitor = self.session.query(
|
||||
models.HealthMonitor).filter_by(id=health_monitor.id).first()
|
||||
models.HealthMonitor).filter_by(
|
||||
pool_id=health_monitor.pool_id).first()
|
||||
self.assertIsNotNone(new_health_monitor.pool)
|
||||
self.assertTrue(isinstance(new_health_monitor.pool, models.Pool))
|
||||
|
||||
@ -478,10 +478,10 @@ class SNIModelTest(base.OctaviaDBTestBase, ModelTestMixin):
|
||||
|
||||
def test_update(self):
|
||||
sni = self.create_sni(self.session, listener_id=self.listener.id)
|
||||
sni.listener_id = self.FAKE_UUID_2
|
||||
sni.tls_container_id = self.FAKE_UUID_2
|
||||
new_sni = self.session.query(
|
||||
models.SNI).filter_by(listener_id=self.FAKE_UUID_2).first()
|
||||
self.assertEqual(self.FAKE_UUID_2, new_sni.listener_id)
|
||||
models.SNI).filter_by(listener_id=self.FAKE_UUID_1).first()
|
||||
self.assertEqual(self.FAKE_UUID_2, new_sni.tls_container_id)
|
||||
|
||||
def test_delete(self):
|
||||
sni = self.create_sni(self.session, listener_id=self.listener.id)
|
||||
@ -592,7 +592,7 @@ class DataModelConversionTest(base.OctaviaDBTestBase, ModelTestMixin):
|
||||
|
||||
def test_health_monitor_tree(self):
|
||||
hm_db = self.session.query(models.HealthMonitor).filter_by(
|
||||
id=self.hm.id).first()
|
||||
pool_id=self.hm.pool_id).first()
|
||||
self.check_health_monitor(hm_db.to_data_model())
|
||||
|
||||
def test_member_tree(self):
|
||||
@ -735,8 +735,6 @@ class DataModelConversionTest(base.OctaviaDBTestBase, ModelTestMixin):
|
||||
self.assertEqual(constants.SESSION_PERSISTENCE_HTTP_COOKIE, sp.type)
|
||||
|
||||
def check_health_monitor_data_model(self, hm):
|
||||
self.assertEqual(self.FAKE_UUID_1, hm.tenant_id)
|
||||
self.assertEqual(self.FAKE_UUID_1, hm.id)
|
||||
self.assertEqual(constants.HEALTH_MONITOR_HTTP, hm.type)
|
||||
self.assertEqual(1, hm.delay)
|
||||
self.assertEqual(1, hm.timeout)
|
@ -14,10 +14,9 @@
|
||||
|
||||
from octavia.common import constants
|
||||
from octavia.common import data_models as models
|
||||
from octavia.common import exceptions
|
||||
from octavia.db import repositories as repo
|
||||
from octavia.openstack.common import uuidutils
|
||||
from octavia.tests.unit.db import base
|
||||
from octavia.tests.functional.db import base
|
||||
|
||||
|
||||
class BaseRepositoryTest(base.OctaviaDBTestBase):
|
||||
@ -54,6 +53,205 @@ class BaseRepositoryTest(base.OctaviaDBTestBase):
|
||||
self.assertIsInstance(member_list, list)
|
||||
|
||||
|
||||
class AllRepositoriesTest(base.OctaviaDBTestBase):
|
||||
|
||||
def setUp(self):
|
||||
super(AllRepositoriesTest, self).setUp()
|
||||
self.repos = repo.Repositories()
|
||||
self.listener = self.repos.listener.create(
|
||||
self.session, protocol=constants.PROTOCOL_HTTP, protocol_port=80,
|
||||
enabled=True, provisioning_status=constants.ACTIVE,
|
||||
operating_status=constants.ONLINE)
|
||||
|
||||
def test_all_repos_has_correct_repos(self):
|
||||
repo_attr_names = ('load_balancer', 'vip', 'health_monitor',
|
||||
'session_persistence', 'pool', 'member', 'listener',
|
||||
'listener_stats', 'amphora', 'sni')
|
||||
for repo_attr in repo_attr_names:
|
||||
single_repo = getattr(self.repos, repo_attr, None)
|
||||
message = ("Class Repositories should have %s instance"
|
||||
" variable.") % repo_attr
|
||||
self.assertIsNotNone(single_repo, message=message)
|
||||
message = (("instance variable, %(repo_name)s, of class "
|
||||
"Repositories should be an instance of %(base)s") %
|
||||
{'repo_name': repo_attr,
|
||||
'base': repo.BaseRepository.__name__})
|
||||
self.assertIsInstance(single_repo, repo.BaseRepository,
|
||||
msg=message)
|
||||
|
||||
for attr in vars(self.repos):
|
||||
if attr.startswith('_') or attr in repo_attr_names:
|
||||
continue
|
||||
possible_repo = getattr(self.repos, attr, None)
|
||||
message = ('Class Repositories is not expected to have %s instance'
|
||||
' variable as a repository.' % attr)
|
||||
self.assertFalse(isinstance(possible_repo, repo.BaseRepository),
|
||||
msg=message)
|
||||
|
||||
def test_create_load_balancer_and_vip(self):
|
||||
lb = {'name': 'test1', 'description': 'desc1', 'enabled': True,
|
||||
'provisioning_status': constants.PENDING_UPDATE,
|
||||
'operating_status': constants.OFFLINE}
|
||||
vip = {'floating_ip_id': uuidutils.generate_uuid(),
|
||||
'floating_ip_network_id': uuidutils.generate_uuid(),
|
||||
'ip_address': '10.0.0.1',
|
||||
'net_port_id': uuidutils.generate_uuid(),
|
||||
'subnet_id': uuidutils.generate_uuid()}
|
||||
lb_dm = self.repos.create_load_balancer_and_vip(self.session, lb, vip)
|
||||
lb_dm_dict = lb_dm.to_dict()
|
||||
del lb_dm_dict['vip']
|
||||
del lb_dm_dict['listeners']
|
||||
del lb_dm_dict['amphorae']
|
||||
del lb_dm_dict['tenant_id']
|
||||
self.assertEqual(lb, lb_dm_dict)
|
||||
vip_dm_dict = lb_dm.vip.to_dict()
|
||||
vip_dm_dict['load_balancer_id'] = lb_dm.id
|
||||
del vip_dm_dict['load_balancer']
|
||||
self.assertEqual(vip, vip_dm_dict)
|
||||
|
||||
def test_create_pool_on_listener_without_sp(self):
|
||||
pool = {'protocol': constants.PROTOCOL_HTTP, 'name': 'pool1',
|
||||
'description': 'desc1',
|
||||
'lb_algorithm': constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
'enabled': True, 'operating_status': constants.ONLINE}
|
||||
pool_dm = self.repos.create_pool_on_listener(self.session,
|
||||
self.listener.id,
|
||||
pool)
|
||||
pool_dm_dict = pool_dm.to_dict()
|
||||
del pool_dm_dict['members']
|
||||
del pool_dm_dict['health_monitor']
|
||||
del pool_dm_dict['session_persistence']
|
||||
del pool_dm_dict['listener']
|
||||
del pool_dm_dict['tenant_id']
|
||||
self.assertEqual(pool, pool_dm_dict)
|
||||
new_listener = self.repos.listener.get(self.session,
|
||||
id=self.listener.id)
|
||||
self.assertEqual(pool_dm.id, new_listener.default_pool_id)
|
||||
|
||||
def test_create_pool_on_listener_with_sp(self):
|
||||
pool = {'protocol': constants.PROTOCOL_HTTP, 'name': 'pool1',
|
||||
'description': 'desc1',
|
||||
'lb_algorithm': constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
'enabled': True, 'operating_status': constants.ONLINE}
|
||||
sp = {'type': constants.SESSION_PERSISTENCE_HTTP_COOKIE,
|
||||
'cookie_name': 'cookie_monster'}
|
||||
pool_dm = self.repos.create_pool_on_listener(self.session,
|
||||
self.listener.id,
|
||||
pool, sp_dict=sp)
|
||||
pool_dm_dict = pool_dm.to_dict()
|
||||
del pool_dm_dict['members']
|
||||
del pool_dm_dict['health_monitor']
|
||||
del pool_dm_dict['session_persistence']
|
||||
del pool_dm_dict['listener']
|
||||
del pool_dm_dict['tenant_id']
|
||||
self.assertEqual(pool, pool_dm_dict)
|
||||
sp_dm_dict = pool_dm.session_persistence.to_dict()
|
||||
del sp_dm_dict['pool']
|
||||
sp['pool_id'] = pool_dm.id
|
||||
self.assertEqual(sp, sp_dm_dict)
|
||||
new_listener = self.repos.listener.get(self.session,
|
||||
id=self.listener.id)
|
||||
self.assertEqual(pool_dm.id, new_listener.default_pool_id)
|
||||
new_sp = self.repos.session_persistence.get(self.session,
|
||||
pool_id=pool_dm.id)
|
||||
self.assertIsNotNone(new_sp)
|
||||
|
||||
def test_update_pool_on_listener_without_sp(self):
|
||||
pool = {'protocol': constants.PROTOCOL_HTTP, 'name': 'pool1',
|
||||
'description': 'desc1',
|
||||
'lb_algorithm': constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
'enabled': True, 'operating_status': constants.ONLINE}
|
||||
pool_dm = self.repos.create_pool_on_listener(self.session,
|
||||
self.listener.id, pool)
|
||||
update_pool = {'protocol': constants.PROTOCOL_TCP, 'name': 'up_pool'}
|
||||
new_pool_dm = self.repos.update_pool_on_listener(
|
||||
self.session, pool_dm.id, update_pool, None)
|
||||
pool_dm_dict = new_pool_dm.to_dict()
|
||||
del pool_dm_dict['members']
|
||||
del pool_dm_dict['health_monitor']
|
||||
del pool_dm_dict['session_persistence']
|
||||
del pool_dm_dict['listener']
|
||||
del pool_dm_dict['tenant_id']
|
||||
pool.update(update_pool)
|
||||
self.assertEqual(pool, pool_dm_dict)
|
||||
self.assertIsNone(new_pool_dm.session_persistence)
|
||||
|
||||
def test_update_pool_on_listener_with_existing_sp(self):
|
||||
pool = {'protocol': constants.PROTOCOL_HTTP, 'name': 'pool1',
|
||||
'description': 'desc1',
|
||||
'lb_algorithm': constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
'enabled': True, 'operating_status': constants.ONLINE}
|
||||
sp = {'type': constants.SESSION_PERSISTENCE_HTTP_COOKIE,
|
||||
'cookie_name': 'cookie_monster'}
|
||||
pool_dm = self.repos.create_pool_on_listener(self.session,
|
||||
self.listener.id,
|
||||
pool, sp_dict=sp)
|
||||
update_pool = {'protocol': constants.PROTOCOL_TCP, 'name': 'up_pool'}
|
||||
update_sp = {'type': constants.SESSION_PERSISTENCE_SOURCE_IP}
|
||||
new_pool_dm = self.repos.update_pool_on_listener(
|
||||
self.session, pool_dm.id, update_pool, update_sp)
|
||||
pool_dm_dict = new_pool_dm.to_dict()
|
||||
del pool_dm_dict['members']
|
||||
del pool_dm_dict['health_monitor']
|
||||
del pool_dm_dict['session_persistence']
|
||||
del pool_dm_dict['listener']
|
||||
del pool_dm_dict['tenant_id']
|
||||
pool.update(update_pool)
|
||||
self.assertEqual(pool, pool_dm_dict)
|
||||
sp_dm_dict = new_pool_dm.session_persistence.to_dict()
|
||||
del sp_dm_dict['pool']
|
||||
sp['pool_id'] = pool_dm.id
|
||||
sp.update(update_sp)
|
||||
self.assertEqual(sp, sp_dm_dict)
|
||||
|
||||
def test_update_pool_on_listener_with_nonexisting_sp(self):
|
||||
pool = {'protocol': constants.PROTOCOL_HTTP, 'name': 'pool1',
|
||||
'description': 'desc1',
|
||||
'lb_algorithm': constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
'enabled': True, 'operating_status': constants.ONLINE}
|
||||
pool_dm = self.repos.create_pool_on_listener(self.session,
|
||||
self.listener.id,
|
||||
pool)
|
||||
update_pool = {'protocol': constants.PROTOCOL_TCP, 'name': 'up_pool'}
|
||||
update_sp = {'type': constants.SESSION_PERSISTENCE_HTTP_COOKIE,
|
||||
'cookie_name': 'monster_cookie'}
|
||||
new_pool_dm = self.repos.update_pool_on_listener(
|
||||
self.session, pool_dm.id, update_pool, update_sp)
|
||||
sp_dm_dict = new_pool_dm.session_persistence.to_dict()
|
||||
del sp_dm_dict['pool']
|
||||
update_sp['pool_id'] = pool_dm.id
|
||||
update_sp.update(update_sp)
|
||||
self.assertEqual(update_sp, sp_dm_dict)
|
||||
|
||||
def test_update_pool_on_listener_with_nonexisting_sp_delete_sp(self):
|
||||
pool = {'protocol': constants.PROTOCOL_HTTP, 'name': 'pool1',
|
||||
'description': 'desc1',
|
||||
'lb_algorithm': constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
'enabled': True, 'operating_status': constants.ONLINE}
|
||||
pool_dm = self.repos.create_pool_on_listener(self.session,
|
||||
self.listener.id,
|
||||
pool)
|
||||
update_pool = {'protocol': constants.PROTOCOL_TCP, 'name': 'up_pool'}
|
||||
new_pool_dm = self.repos.update_pool_on_listener(
|
||||
self.session, pool_dm.id, update_pool, None)
|
||||
self.assertIsNone(new_pool_dm.session_persistence)
|
||||
|
||||
def test_update_pool_on_listener_with_existing_sp_delete_sp(self):
|
||||
pool = {'protocol': constants.PROTOCOL_HTTP, 'name': 'pool1',
|
||||
'description': 'desc1',
|
||||
'lb_algorithm': constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
'enabled': True, 'operating_status': constants.ONLINE}
|
||||
sp = {'type': constants.SESSION_PERSISTENCE_HTTP_COOKIE,
|
||||
'cookie_name': 'cookie_monster'}
|
||||
pool_dm = self.repos.create_pool_on_listener(self.session,
|
||||
self.listener.id,
|
||||
pool, sp_dict=sp)
|
||||
update_pool = {'protocol': constants.PROTOCOL_TCP, 'name': 'up_pool'}
|
||||
new_pool_dm = self.repos.update_pool_on_listener(
|
||||
self.session, pool_dm.id, update_pool, None)
|
||||
self.assertIsNone(new_pool_dm.session_persistence)
|
||||
|
||||
|
||||
class PoolRepositoryTest(BaseRepositoryTest):
|
||||
|
||||
def create_pool(self, pool_id, tenant_id):
|
||||
@ -106,8 +304,7 @@ class PoolRepositoryTest(BaseRepositoryTest):
|
||||
pool = self.create_pool(pool_id=self.FAKE_UUID_1,
|
||||
tenant_id=self.FAKE_UUID_2)
|
||||
self.pool_repo.delete(self.session, id=pool.id)
|
||||
self.assertRaises(exceptions.NotFound, self.pool_repo.get,
|
||||
self.session, id=pool.id)
|
||||
self.assertIsNone(self.pool_repo.get(self.session, id=pool.id))
|
||||
|
||||
def test_delete_with_member(self):
|
||||
pool = self.create_pool(pool_id=self.FAKE_UUID_1,
|
||||
@ -122,16 +319,13 @@ class PoolRepositoryTest(BaseRepositoryTest):
|
||||
self.assertEqual(1, len(new_pool.members))
|
||||
self.assertEqual(member, new_pool.members[0])
|
||||
self.pool_repo.delete(self.session, id=pool.id)
|
||||
self.assertRaises(exceptions.NotFound, self.pool_repo.get,
|
||||
self.session, id=pool.id)
|
||||
self.assertRaises(exceptions.NotFound, self.member_repo.get,
|
||||
self.session, id=member.id)
|
||||
self.assertIsNone(self.pool_repo.get(self.session, id=pool.id))
|
||||
self.assertIsNone(self.member_repo.get(self.session, id=member.id))
|
||||
|
||||
def test_delete_with_health_monitor(self):
|
||||
pool = self.create_pool(pool_id=self.FAKE_UUID_1,
|
||||
tenant_id=self.FAKE_UUID_2)
|
||||
hm = self.hm_repo.create(self.session, id=self.FAKE_UUID_3,
|
||||
tenant_id=self.FAKE_UUID_2, pool_id=pool.id,
|
||||
hm = self.hm_repo.create(self.session, pool_id=pool.id,
|
||||
type=constants.HEALTH_MONITOR_HTTP,
|
||||
delay=1, timeout=1, fall_threshold=1,
|
||||
rise_threshold=1, enabled=True)
|
||||
@ -139,10 +333,8 @@ class PoolRepositoryTest(BaseRepositoryTest):
|
||||
self.assertEqual(pool, new_pool)
|
||||
self.assertEqual(hm, new_pool.health_monitor)
|
||||
self.pool_repo.delete(self.session, id=pool.id)
|
||||
self.assertRaises(exceptions.NotFound, self.pool_repo.get,
|
||||
self.session, id=pool.id)
|
||||
self.assertRaises(exceptions.NotFound, self.hm_repo.get, self.session,
|
||||
pool_id=hm.pool_id)
|
||||
self.assertIsNone(self.pool_repo.get(self.session, id=pool.id))
|
||||
self.assertIsNone(self.hm_repo.get(self.session, pool_id=hm.pool_id))
|
||||
|
||||
def test_delete_with_session_persistence(self):
|
||||
pool = self.create_pool(pool_id=self.FAKE_UUID_1,
|
||||
@ -155,16 +347,13 @@ class PoolRepositoryTest(BaseRepositoryTest):
|
||||
self.assertEqual(pool, new_pool)
|
||||
self.assertEqual(sp, new_pool.session_persistence)
|
||||
self.pool_repo.delete(self.session, id=new_pool.id)
|
||||
self.assertRaises(exceptions.NotFound, self.pool_repo.get,
|
||||
self.session, id=pool.id)
|
||||
self.assertRaises(exceptions.NotFound, self.sp_repo.get, self.session,
|
||||
pool_id=sp.pool_id)
|
||||
self.assertIsNone(self.pool_repo.get(self.session, id=pool.id))
|
||||
self.assertIsNone(self.sp_repo.get(self.session, pool_id=sp.pool_id))
|
||||
|
||||
def test_delete_with_all_children(self):
|
||||
pool = self.create_pool(pool_id=self.FAKE_UUID_1,
|
||||
tenant_id=self.FAKE_UUID_2)
|
||||
hm = self.hm_repo.create(self.session, id=self.FAKE_UUID_1,
|
||||
tenant_id=self.FAKE_UUID_2, pool_id=pool.id,
|
||||
hm = self.hm_repo.create(self.session, pool_id=pool.id,
|
||||
type=constants.HEALTH_MONITOR_HTTP,
|
||||
delay=1, timeout=1, fall_threshold=1,
|
||||
rise_threshold=1, enabled=True)
|
||||
@ -187,14 +376,10 @@ class PoolRepositoryTest(BaseRepositoryTest):
|
||||
self.assertEqual(hm, new_pool.health_monitor)
|
||||
self.assertEqual(sp, new_pool.session_persistence)
|
||||
self.pool_repo.delete(self.session, id=pool.id)
|
||||
self.assertRaises(exceptions.NotFound, self.pool_repo.get,
|
||||
self.session, id=pool.id)
|
||||
self.assertRaises(exceptions.NotFound, self.member_repo.get,
|
||||
self.session, id=member.id)
|
||||
self.assertRaises(exceptions.NotFound, self.hm_repo.get, self.session,
|
||||
pool_id=hm.pool_id)
|
||||
self.assertRaises(exceptions.NotFound, self.sp_repo.get, self.session,
|
||||
pool_id=sp.pool_id)
|
||||
self.assertIsNone(self.pool_repo.get(self.session, id=pool.id))
|
||||
self.assertIsNone(self.member_repo.get(self.session, id=member.id))
|
||||
self.assertIsNone(self.hm_repo.get(self.session, pool_id=hm.pool_id))
|
||||
self.assertIsNone(self.sp_repo.get(self.session, pool_id=sp.pool_id))
|
||||
|
||||
|
||||
class MemberRepositoryTest(BaseRepositoryTest):
|
||||
@ -261,8 +446,7 @@ class MemberRepositoryTest(BaseRepositoryTest):
|
||||
member = self.create_member(self.FAKE_UUID_1, self.FAKE_UUID_2,
|
||||
self.pool.id, "10.0.0.1")
|
||||
self.member_repo.delete(self.session, id=member.id)
|
||||
self.assertRaises(exceptions.NotFound, self.member_repo.get,
|
||||
self.session, id=member.id)
|
||||
self.assertIsNone(self.member_repo.get(self.session, id=member.id))
|
||||
new_pool = self.pool_repo.get(self.session, id=self.pool.id)
|
||||
self.assertIsNotNone(new_pool)
|
||||
self.assertEqual(0, len(new_pool.members))
|
||||
@ -311,8 +495,8 @@ class SessionPersistenceRepositoryTest(BaseRepositoryTest):
|
||||
def test_delete(self):
|
||||
sp = self.create_session_persistence(self.pool.id)
|
||||
self.sp_repo.delete(self.session, pool_id=sp.pool_id)
|
||||
self.assertRaises(exceptions.NotFound, self.member_repo.get,
|
||||
self.session, pool_id=sp.pool_id)
|
||||
self.assertIsNone(self.member_repo.get(self.session,
|
||||
pool_id=sp.pool_id))
|
||||
new_pool = self.pool_repo.get(self.session, id=self.pool.id)
|
||||
self.assertIsNotNone(new_pool)
|
||||
self.assertIsNone(new_pool.session_persistence)
|
||||
@ -380,8 +564,7 @@ class ListenerRepositoryTest(BaseRepositoryTest):
|
||||
def test_delete(self):
|
||||
listener = self.create_listener(self.FAKE_UUID_1, 80)
|
||||
self.listener_repo.delete(self.session, id=listener.id)
|
||||
self.assertRaises(exceptions.NotFound, self.listener_repo.get,
|
||||
self.session, id=listener.id)
|
||||
self.assertIsNone(self.listener_repo.get(self.session, id=listener.id))
|
||||
|
||||
def test_delete_with_sni(self):
|
||||
listener = self.create_listener(self.FAKE_UUID_1, 80)
|
||||
@ -391,10 +574,9 @@ class ListenerRepositoryTest(BaseRepositoryTest):
|
||||
self.assertIsNotNone(new_listener)
|
||||
self.assertEqual(sni, new_listener.sni_containers[0])
|
||||
self.listener_repo.delete(self.session, id=new_listener.id)
|
||||
self.assertRaises(exceptions.NotFound, self.listener_repo.get,
|
||||
self.session, id=listener.id)
|
||||
self.assertRaises(exceptions.NotFound, self.sni_repo.get, self.session,
|
||||
listener_id=listener.id)
|
||||
self.assertIsNone(self.listener_repo.get(self.session, id=listener.id))
|
||||
self.assertIsNone(self.sni_repo.get(self.session,
|
||||
listener_id=listener.id))
|
||||
|
||||
def test_delete_with_stats(self):
|
||||
listener = self.create_listener(self.FAKE_UUID_1, 80)
|
||||
@ -405,10 +587,9 @@ class ListenerRepositoryTest(BaseRepositoryTest):
|
||||
self.assertIsNotNone(new_listener)
|
||||
self.assertEqual(stats, new_listener.stats)
|
||||
self.listener_repo.delete(self.session, id=listener.id)
|
||||
self.assertRaises(exceptions.NotFound, self.listener_repo.get,
|
||||
self.session, id=listener.id)
|
||||
self.assertRaises(exceptions.NotFound, self.listener_stats_repo.get,
|
||||
self.session, listener_id=listener.id)
|
||||
self.assertIsNone(self.listener_repo.get(self.session, id=listener.id))
|
||||
self.assertIsNone(self.listener_stats_repo.get(
|
||||
self.session, listener_id=listener.id))
|
||||
|
||||
def test_delete_with_pool(self):
|
||||
pool = self.pool_repo.create(
|
||||
@ -423,10 +604,8 @@ class ListenerRepositoryTest(BaseRepositoryTest):
|
||||
self.assertIsNotNone(new_listener)
|
||||
self.assertEqual(pool, new_listener.default_pool)
|
||||
self.listener_repo.delete(self.session, id=new_listener.id)
|
||||
self.assertRaises(exceptions.NotFound, self.listener_repo.get,
|
||||
self.session, id=listener.id)
|
||||
self.assertRaises(exceptions.NotFound, self.pool_repo.get,
|
||||
self.session, id=pool.id)
|
||||
self.assertIsNone(self.listener_repo.get(self.session, id=listener.id))
|
||||
self.assertIsNone(self.pool_repo.get(self.session, id=pool.id))
|
||||
|
||||
def test_delete_with_all_children(self):
|
||||
pool = self.pool_repo.create(
|
||||
@ -448,14 +627,12 @@ class ListenerRepositoryTest(BaseRepositoryTest):
|
||||
self.assertEqual(sni, new_listener.sni_containers[0])
|
||||
self.assertEqual(stats, new_listener.stats)
|
||||
self.listener_repo.delete(self.session, id=listener.id)
|
||||
self.assertRaises(exceptions.NotFound, self.listener_repo.get,
|
||||
self.session, id=listener.id)
|
||||
self.assertRaises(exceptions.NotFound, self.sni_repo.get, self.session,
|
||||
listener_id=listener.id)
|
||||
self.assertRaises(exceptions.NotFound, self.listener_stats_repo.get,
|
||||
self.session, listener_id=sni.listener_id)
|
||||
self.assertRaises(exceptions.NotFound, self.pool_repo.get,
|
||||
self.session, id=pool.id)
|
||||
self.assertIsNone(self.listener_repo.get(self.session, id=listener.id))
|
||||
self.assertIsNone(self.sni_repo.get(self.session,
|
||||
listener_id=listener.id))
|
||||
self.assertIsNone(self.listener_stats_repo.get(
|
||||
self.session, listener_id=sni.listener_id))
|
||||
self.assertIsNone(self.pool_repo.get(self.session, id=pool.id))
|
||||
|
||||
|
||||
class ListenerStatisticsRepositoryTest(BaseRepositoryTest):
|
||||
@ -506,8 +683,8 @@ class ListenerStatisticsRepositoryTest(BaseRepositoryTest):
|
||||
stats = self.create_listener_stats(self.listener.id)
|
||||
self.listener_stats_repo.delete(self.session,
|
||||
listener_id=stats.listener_id)
|
||||
self.assertRaises(exceptions.NotFound, self.listener_stats_repo.get,
|
||||
self.session, listener_id=stats.listener_id)
|
||||
self.assertIsNone(self.listener_stats_repo.get(
|
||||
self.session, listener_id=stats.listener_id))
|
||||
new_listener = self.listener_repo.get(self.session,
|
||||
id=self.listener.id)
|
||||
self.assertIsNotNone(new_listener)
|
||||
@ -527,8 +704,7 @@ class HealthMonitorRepositoryTest(BaseRepositoryTest):
|
||||
|
||||
def create_health_monitor(self, pool_id):
|
||||
health_monitor = self.hm_repo.create(
|
||||
self.session, id=self.FAKE_UUID_1, tenant_id=self.FAKE_UUID_2,
|
||||
type=constants.HEALTH_MONITOR_HTTP, pool_id=pool_id,
|
||||
self.session, type=constants.HEALTH_MONITOR_HTTP, pool_id=pool_id,
|
||||
delay=1, timeout=1, fall_threshold=1, rise_threshold=1,
|
||||
http_method="POST", url_path="http://localhost:80/index.php",
|
||||
expected_codes="200", enabled=True)
|
||||
@ -564,8 +740,7 @@ class HealthMonitorRepositoryTest(BaseRepositoryTest):
|
||||
def test_delete(self):
|
||||
hm = self.create_health_monitor(self.pool.id)
|
||||
self.hm_repo.delete(self.session, pool_id=hm.pool_id)
|
||||
self.assertRaises(exceptions.NotFound, self.hm_repo.get, self.session,
|
||||
pool_id=hm.pool_id)
|
||||
self.assertIsNone(self.hm_repo.get(self.session, pool_id=hm.pool_id))
|
||||
new_pool = self.pool_repo.get(self.session, id=self.pool.id)
|
||||
self.assertIsNotNone(new_pool)
|
||||
self.assertIsNone(new_pool.health_monitor)
|
||||
@ -617,8 +792,7 @@ class LoadBalancerRepositoryTest(BaseRepositoryTest):
|
||||
def test_delete(self):
|
||||
lb = self.create_loadbalancer(self.FAKE_UUID_1)
|
||||
self.lb_repo.delete(self.session, id=lb.id)
|
||||
self.assertRaises(exceptions.NotFound, self.lb_repo.get, self.session,
|
||||
id=lb.id)
|
||||
self.assertIsNone(self.lb_repo.get(self.session, id=lb.id))
|
||||
|
||||
def test_delete_with_amphora(self):
|
||||
lb = self.create_loadbalancer(self.FAKE_UUID_1)
|
||||
@ -631,8 +805,7 @@ class LoadBalancerRepositoryTest(BaseRepositoryTest):
|
||||
self.assertEqual(1, len(new_lb.amphorae))
|
||||
self.assertEqual(amphora, new_lb.amphorae[0])
|
||||
self.lb_repo.delete(self.session, id=new_lb.id)
|
||||
self.assertRaises(exceptions.NotFound, self.lb_repo.get, self.session,
|
||||
id=lb.id)
|
||||
self.assertIsNone(self.lb_repo.get(self.session, id=lb.id))
|
||||
new_amphora = self.amphora_repo.get(self.session, id=amphora.id)
|
||||
self.assertIsNotNone(new_amphora)
|
||||
self.assertIsNone(new_amphora.load_balancer_id)
|
||||
@ -653,8 +826,7 @@ class LoadBalancerRepositoryTest(BaseRepositoryTest):
|
||||
self.assertIn(amphora_1, new_lb.amphorae)
|
||||
self.assertIn(amphora_2, new_lb.amphorae)
|
||||
self.lb_repo.delete(self.session, id=new_lb.id)
|
||||
self.assertRaises(exceptions.NotFound, self.lb_repo.get, self.session,
|
||||
id=lb.id)
|
||||
self.assertIsNone(self.lb_repo.get(self.session, id=lb.id))
|
||||
new_amphora_1 = self.amphora_repo.get(self.session, id=amphora_1.id)
|
||||
new_amphora_2 = self.amphora_repo.get(self.session, id=amphora_2.id)
|
||||
self.assertIsNotNone(new_amphora_1)
|
||||
@ -671,10 +843,9 @@ class LoadBalancerRepositoryTest(BaseRepositoryTest):
|
||||
self.assertIsNotNone(new_lb.vip)
|
||||
self.assertEqual(vip, new_lb.vip)
|
||||
self.lb_repo.delete(self.session, id=new_lb.id)
|
||||
self.assertRaises(exceptions.NotFound, self.lb_repo.get, self.session,
|
||||
id=lb.id)
|
||||
self.assertRaises(exceptions.NotFound, self.vip_repo.get, self.session,
|
||||
load_balancer_id=lb.id)
|
||||
self.assertIsNone(self.lb_repo.get(self.session, id=lb.id))
|
||||
self.assertIsNone(self.vip_repo.get(self.session,
|
||||
load_balancer_id=lb.id))
|
||||
|
||||
def test_delete_with_listener(self):
|
||||
lb = self.create_loadbalancer(self.FAKE_UUID_1)
|
||||
@ -690,10 +861,8 @@ class LoadBalancerRepositoryTest(BaseRepositoryTest):
|
||||
self.assertEqual(1, len(new_lb.listeners))
|
||||
self.assertEqual(listener, new_lb.listeners[0])
|
||||
self.lb_repo.delete(self.session, id=new_lb.id)
|
||||
self.assertRaises(exceptions.NotFound, self.lb_repo.get, self.session,
|
||||
id=lb.id)
|
||||
self.assertRaises(exceptions.NotFound, self.listener_repo.get,
|
||||
self.session, id=listener.id)
|
||||
self.assertIsNone(self.lb_repo.get(self.session, id=lb.id))
|
||||
self.assertIsNone(self.listener_repo.get(self.session, id=listener.id))
|
||||
|
||||
def test_delete_with_many_listeners(self):
|
||||
lb = self.create_loadbalancer(self.FAKE_UUID_1)
|
||||
@ -717,12 +886,11 @@ class LoadBalancerRepositoryTest(BaseRepositoryTest):
|
||||
self.assertIn(listener_1, new_lb.listeners)
|
||||
self.assertIn(listener_2, new_lb.listeners)
|
||||
self.lb_repo.delete(self.session, id=new_lb.id)
|
||||
self.assertRaises(exceptions.NotFound, self.lb_repo.get, self.session,
|
||||
id=lb.id)
|
||||
self.assertRaises(exceptions.NotFound, self.listener_repo.get,
|
||||
self.session, id=listener_1.id)
|
||||
self.assertRaises(exceptions.NotFound, self.listener_repo.get,
|
||||
self.session, id=listener_2.id)
|
||||
self.assertIsNone(self.lb_repo.get(self.session, id=lb.id))
|
||||
self.assertIsNone(self.listener_repo.get(self.session,
|
||||
id=listener_1.id))
|
||||
self.assertIsNone(self.listener_repo.get(self.session,
|
||||
id=listener_2.id))
|
||||
|
||||
def test_delete_with_all_children(self):
|
||||
lb = self.create_loadbalancer(self.FAKE_UUID_1)
|
||||
@ -748,15 +916,35 @@ class LoadBalancerRepositoryTest(BaseRepositoryTest):
|
||||
self.assertEqual(amphora, new_lb.amphorae[0])
|
||||
self.assertEqual(listener, new_lb.listeners[0])
|
||||
self.lb_repo.delete(self.session, id=new_lb.id)
|
||||
self.assertRaises(exceptions.NotFound, self.lb_repo.get, self.session,
|
||||
id=lb.id)
|
||||
self.assertIsNone(self.lb_repo.get(self.session, id=lb.id))
|
||||
new_amphora = self.amphora_repo.get(self.session, id=amphora.id)
|
||||
self.assertIsNotNone(new_amphora)
|
||||
self.assertIsNone(new_amphora.load_balancer_id)
|
||||
self.assertRaises(exceptions.NotFound, self.vip_repo.get, self.session,
|
||||
load_balancer_id=lb.id)
|
||||
self.assertRaises(exceptions.NotFound, self.listener_repo.get,
|
||||
self.session, id=listener.id)
|
||||
self.assertIsNone(self.vip_repo.get(self.session,
|
||||
load_balancer_id=lb.id))
|
||||
self.assertIsNone(self.listener_repo.get(self.session, id=listener.id))
|
||||
|
||||
def test_test_and_set_provisioning_status_immutable(self):
|
||||
lb_id = uuidutils.generate_uuid()
|
||||
self.lb_repo.create(self.session, id=lb_id,
|
||||
provisioning_status=constants.PENDING_CREATE,
|
||||
operating_status=constants.OFFLINE,
|
||||
enabled=True)
|
||||
self.assertFalse(self.lb_repo.test_and_set_provisioning_status(
|
||||
self.session, lb_id, constants.PENDING_UPDATE))
|
||||
lb = self.lb_repo.get(self.session, id=lb_id)
|
||||
self.assertEqual(constants.PENDING_CREATE, lb.provisioning_status)
|
||||
|
||||
def test_test_and_set_provisioning_status_mutable(self):
|
||||
lb_id = uuidutils.generate_uuid()
|
||||
self.lb_repo.create(self.session, id=lb_id,
|
||||
provisioning_status=constants.ACTIVE,
|
||||
operating_status=constants.OFFLINE,
|
||||
enabled=True)
|
||||
self.lb_repo.test_and_set_provisioning_status(
|
||||
self.session, lb_id, constants.PENDING_UPDATE)
|
||||
lb = self.lb_repo.get(self.session, id=lb_id)
|
||||
self.assertEqual(constants.PENDING_UPDATE, lb.provisioning_status)
|
||||
|
||||
|
||||
class VipRepositoryTest(BaseRepositoryTest):
|
||||
@ -799,8 +987,8 @@ class VipRepositoryTest(BaseRepositoryTest):
|
||||
vip = self.create_vip(self.lb.id)
|
||||
self.vip_repo.delete(self.session,
|
||||
load_balancer_id=vip.load_balancer_id)
|
||||
self.assertRaises(exceptions.NotFound, self.vip_repo.get, self.session,
|
||||
load_balancer_id=vip.load_balancer_id)
|
||||
self.assertIsNone(self.vip_repo.get(
|
||||
self.session, load_balancer_id=vip.load_balancer_id))
|
||||
new_lb = self.lb_repo.get(self.session, id=self.lb.id)
|
||||
self.assertIsNotNone(new_lb)
|
||||
self.assertIsNone(new_lb.vip)
|
||||
@ -848,8 +1036,8 @@ class SNIRepositoryTest(BaseRepositoryTest):
|
||||
def test_delete(self):
|
||||
sni = self.create_sni(self.listener.id)
|
||||
self.sni_repo.delete(self.session, listener_id=sni.listener_id)
|
||||
self.assertRaises(exceptions.NotFound, self.sni_repo.get, self.session,
|
||||
listener_id=sni.listener_id)
|
||||
self.assertIsNone(self.sni_repo.get(self.session,
|
||||
listener_id=sni.listener_id))
|
||||
new_listener = self.listener_repo.get(self.session,
|
||||
id=self.listener.id)
|
||||
self.assertIsNotNone(new_listener)
|
||||
@ -895,8 +1083,7 @@ class AmphoraRepositoryTest(BaseRepositoryTest):
|
||||
def test_delete(self):
|
||||
amphora = self.create_amphora(self.FAKE_UUID_1)
|
||||
self.amphora_repo.delete(self.session, id=amphora.id)
|
||||
self.assertRaises(exceptions.NotFound, self.amphora_repo.get,
|
||||
self.session, id=amphora.id)
|
||||
self.assertIsNone(self.amphora_repo.get(self.session, id=amphora.id))
|
||||
|
||||
def test_associate_amphora_load_balancer(self):
|
||||
amphora = self.create_amphora(self.FAKE_UUID_1)
|
||||
@ -910,7 +1097,6 @@ class AmphoraRepositoryTest(BaseRepositoryTest):
|
||||
amphora = self.create_amphora(self.FAKE_UUID_1)
|
||||
self.amphora_repo.associate(self.session, self.lb.id, amphora.id)
|
||||
self.amphora_repo.delete(self.session, id=amphora.id)
|
||||
self.assertRaises(exceptions.NotFound, self.amphora_repo.get,
|
||||
self.session, id=amphora.id)
|
||||
self.assertIsNone(self.amphora_repo.get(self.session, id=amphora.id))
|
||||
new_lb = self.lb_repo.get(self.session, id=self.lb.id)
|
||||
self.assertEqual(0, len(new_lb.amphorae))
|
0
octavia/tests/unit/api/__init__.py
Normal file
0
octavia/tests/unit/api/__init__.py
Normal file
0
octavia/tests/unit/api/v1/__init__.py
Normal file
0
octavia/tests/unit/api/v1/__init__.py
Normal file
0
octavia/tests/unit/api/v1/types/__init__.py
Normal file
0
octavia/tests/unit/api/v1/types/__init__.py
Normal file
225
octavia/tests/unit/api/v1/types/base.py
Normal file
225
octavia/tests/unit/api/v1/types/base.py
Normal file
@ -0,0 +1,225 @@
|
||||
# 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 exc
|
||||
from wsme.rest import json as wsme_json
|
||||
|
||||
from octavia.api.v1.types import base as base_type
|
||||
from octavia.common import constants
|
||||
from octavia.openstack.common import uuidutils
|
||||
from octavia.tests.unit import base
|
||||
|
||||
|
||||
def build_body(mandatory_fields, extra_attributes):
|
||||
body = {}
|
||||
for key in mandatory_fields:
|
||||
body[key] = mandatory_fields[key]
|
||||
for key in extra_attributes:
|
||||
body[key] = extra_attributes[key]
|
||||
return body
|
||||
|
||||
|
||||
class BaseTypesTest(base.TestCase):
|
||||
_type = base_type.BaseType
|
||||
_mandatory_fields = {}
|
||||
|
||||
|
||||
class BaseTestUuid(base.TestCase):
|
||||
|
||||
def assert_uuid_attr(self, attr):
|
||||
kwargs = {attr: uuidutils.generate_uuid()}
|
||||
self._type(**kwargs)
|
||||
|
||||
def assert_uuid_attr_fail_with_integer(self, attr):
|
||||
kwargs = {attr: 1}
|
||||
self.assertRaises(exc.InvalidInput, self._type, **kwargs)
|
||||
|
||||
def assert_uuid_attr_fail_with_short_str(self, attr):
|
||||
kwargs = {attr: '12345'}
|
||||
self.assertRaises(exc.InvalidInput, self._type, **kwargs)
|
||||
|
||||
def assert_uuid_attr_fail_with_shorter_than_uuid(self, attr):
|
||||
kwargs = {attr: uuidutils.generate_uuid()[1:]}
|
||||
self.assertRaises(exc.InvalidInput, self._type, **kwargs)
|
||||
|
||||
def assert_uuid_attr_fail_with_longer_than_uuid(self, attr):
|
||||
kwargs = {attr: uuidutils.generate_uuid() + "0"}
|
||||
self.assertRaises(exc.InvalidInput, self._type, **kwargs)
|
||||
|
||||
|
||||
class BaseTestString(base.TestCase):
|
||||
|
||||
def _default_min_max_lengths(self, min_length=None, max_length=None):
|
||||
if max_length is None:
|
||||
if min_length is None:
|
||||
max_length = 255
|
||||
min_length = 2
|
||||
else:
|
||||
max_length = min_length + 1
|
||||
else:
|
||||
if min_length is None:
|
||||
min_length = max_length - 1
|
||||
return min_length, max_length
|
||||
|
||||
def assert_string_attr(self, attr, min_length=None, max_length=None):
|
||||
min_length, max_length = self._default_min_max_lengths(min_length,
|
||||
max_length)
|
||||
string_val = 'a' * (max_length - 1)
|
||||
kwargs = {attr: string_val}
|
||||
self._type(**kwargs)
|
||||
|
||||
def assert_string_attr_min_length(self, attr, min_length):
|
||||
min_length, max_length = self._default_min_max_lengths(min_length)
|
||||
string_val = 'a' * (min_length - 1)
|
||||
kwargs = {attr: string_val}
|
||||
# No point in testing if min_length is <= 0
|
||||
if min_length > 0:
|
||||
self.assertRaises(exc.InvalidInput, self._type, **kwargs)
|
||||
|
||||
def assert_string_attr_max_length(self, attr, max_length=None):
|
||||
min_length, max_length = self._default_min_max_lengths(max_length)
|
||||
string_val = 'a' * (max_length + 1)
|
||||
kwargs = {attr: string_val}
|
||||
self.assertRaises(exc.InvalidInput, self._type, **kwargs)
|
||||
|
||||
|
||||
class BaseTestBool(base.TestCase):
|
||||
|
||||
def assert_bool_attr(self, attr):
|
||||
kwargs = {attr: True}
|
||||
self.assertIsNotNone(self._type(**kwargs))
|
||||
kwargs = {attr: False}
|
||||
self.assertIsNotNone(self._type(**kwargs))
|
||||
|
||||
def assert_bool_attr_non_bool(self, attr):
|
||||
kwargs = {attr: 'test'}
|
||||
self.assertRaises(exc.InvalidInput, self._type, **kwargs)
|
||||
|
||||
|
||||
class TestIdMixin(BaseTestUuid):
|
||||
id_attr = 'id'
|
||||
|
||||
def test_id(self):
|
||||
self.assert_uuid_attr(self.id_attr)
|
||||
self.assert_uuid_attr_fail_with_integer(self.id_attr)
|
||||
self.assert_uuid_attr_fail_with_short_str(self.id_attr)
|
||||
self.assert_uuid_attr_fail_with_shorter_than_uuid(self.id_attr)
|
||||
self.assert_uuid_attr_fail_with_longer_than_uuid(self.id_attr)
|
||||
|
||||
def test_id_readonly(self):
|
||||
body = build_body(self._mandatory_fields,
|
||||
{self.id_attr: uuidutils.generate_uuid()})
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson,
|
||||
self._type, body)
|
||||
|
||||
|
||||
class TestTenantIdMixin(BaseTestUuid):
|
||||
tenant_id_attr = 'tenant_id'
|
||||
|
||||
def test_tenant_id(self):
|
||||
self.assert_uuid_attr(self.tenant_id_attr)
|
||||
self.assert_uuid_attr_fail_with_integer(self.tenant_id_attr)
|
||||
self.assert_uuid_attr_fail_with_short_str(self.tenant_id_attr)
|
||||
self.assert_uuid_attr_fail_with_shorter_than_uuid(self.tenant_id_attr)
|
||||
self.assert_uuid_attr_fail_with_longer_than_uuid(self.tenant_id_attr)
|
||||
|
||||
def test_tenant_id_readonly(self):
|
||||
body = build_body(self._mandatory_fields,
|
||||
{self.tenant_id_attr: uuidutils.generate_uuid()})
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson,
|
||||
self._type, body)
|
||||
|
||||
|
||||
class TestNameMixin(BaseTestString):
|
||||
name_attr = 'name'
|
||||
|
||||
def test_name(self):
|
||||
self.assert_string_attr(self.name_attr, min_length=0, max_length=255)
|
||||
self.assert_string_attr_min_length(self.name_attr, 0)
|
||||
self.assert_string_attr_max_length(self.name_attr, 255)
|
||||
|
||||
def test_editable_name(self):
|
||||
name = "Name"
|
||||
body = build_body(self._mandatory_fields, {self.name_attr: name})
|
||||
type_instance = wsme_json.fromjson(self._type, body)
|
||||
self.assertEqual(name, type_instance.name)
|
||||
|
||||
|
||||
class TestDescriptionMixin(BaseTestString):
|
||||
description_attr = 'description'
|
||||
|
||||
def test_description(self):
|
||||
self.assert_string_attr(self.description_attr, min_length=0,
|
||||
max_length=255)
|
||||
self.assert_string_attr_min_length(self.description_attr, 0)
|
||||
self.assert_string_attr_max_length(self.description_attr, 255)
|
||||
|
||||
def test_editable_description(self):
|
||||
description = "Description"
|
||||
body = build_body(self._mandatory_fields,
|
||||
{self.description_attr: description})
|
||||
type_instance = wsme_json.fromjson(self._type, body)
|
||||
self.assertEqual(description, type_instance.description)
|
||||
|
||||
|
||||
class TestEnabledMixin(BaseTestBool):
|
||||
enabled_attr = 'enabled'
|
||||
|
||||
def test_enabled(self):
|
||||
self.assert_bool_attr(self.enabled_attr)
|
||||
self.assert_bool_attr_non_bool(self.enabled_attr)
|
||||
|
||||
def test_default_enabled_true(self):
|
||||
body = build_body(self._mandatory_fields, {})
|
||||
type_instance = wsme_json.fromjson(self._type, body)
|
||||
self.assertTrue(type_instance.enabled)
|
||||
|
||||
def test_editable_enabled(self):
|
||||
body = build_body(self._mandatory_fields, {"enabled": False})
|
||||
type_instance = wsme_json.fromjson(self._type, body)
|
||||
self.assertFalse(type_instance.enabled)
|
||||
|
||||
|
||||
class TestProvisioningStatusMixin(BaseTestString):
|
||||
provisioning_attr = 'provisioning_status'
|
||||
|
||||
def test_provisioning_status(self):
|
||||
self.assert_string_attr(self.provisioning_attr, min_length=0,
|
||||
max_length=16)
|
||||
self.assert_string_attr_min_length(self.provisioning_attr, 0)
|
||||
self.assert_string_attr_max_length(self.provisioning_attr, 16)
|
||||
|
||||
def test_provisioning_status_readonly(self):
|
||||
status = constants.ACTIVE
|
||||
body = build_body(self._mandatory_fields,
|
||||
{self.provisioning_attr: status})
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson,
|
||||
self._type, body)
|
||||
|
||||
|
||||
class TestOperatingStatusMixin(BaseTestString):
|
||||
operating_attr = 'operating_status'
|
||||
|
||||
def test_operating_status(self):
|
||||
self.assert_string_attr(self.operating_attr, min_length=0,
|
||||
max_length=16)
|
||||
self.assert_string_attr_min_length(self.operating_attr, 0)
|
||||
self.assert_string_attr_max_length(self.operating_attr, 16)
|
||||
|
||||
def test_operating_status_readonly(self):
|
||||
status = constants.ONLINE
|
||||
body = build_body(self._mandatory_fields,
|
||||
{self.operating_attr: status})
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson,
|
||||
self._type, body)
|
120
octavia/tests/unit/api/v1/types/test_health_monitors.py
Normal file
120
octavia/tests/unit/api/v1/types/test_health_monitors.py
Normal file
@ -0,0 +1,120 @@
|
||||
# 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 exc
|
||||
from wsme.rest import json as wsme_json
|
||||
from wsme import types as wsme_types
|
||||
|
||||
from octavia.api.v1.types import health_monitor as hm_type
|
||||
from octavia.common import constants
|
||||
from octavia.tests.unit.api.v1.types import base
|
||||
|
||||
|
||||
class TestHealthMonitor(object):
|
||||
|
||||
_type = None
|
||||
|
||||
def test_invalid_type(self):
|
||||
body = {"type": 10, "delay": 1, "timeout": 1, "fall_threshold": 1}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_delay(self):
|
||||
body = {"type": constants.HEALTH_MONITOR_HTTP, "delay": "one",
|
||||
"timeout": 1, "fall_threshold": 1}
|
||||
self.assertRaises(ValueError, wsme_json.fromjson, self._type, body)
|
||||
|
||||
def test_invalid_timeout(self):
|
||||
body = {"type": constants.HEALTH_MONITOR_HTTP, "delay": 1,
|
||||
"timeout": "one", "fall_threshold": 1}
|
||||
self.assertRaises(ValueError, wsme_json.fromjson, self._type, body)
|
||||
|
||||
def test_invalid_fall_threshold(self):
|
||||
body = {"type": constants.HEALTH_MONITOR_HTTP, "delay": 1,
|
||||
"timeout": 1, "fall_threshold": "one"}
|
||||
self.assertRaises(ValueError, wsme_json.fromjson, self._type, body)
|
||||
|
||||
def test_invalid_rise_threshold(self):
|
||||
body = {"type": constants.HEALTH_MONITOR_HTTP, "delay": 1,
|
||||
"timeout": 1, "fall_threshold": 1, "rise_threshold": "one"}
|
||||
self.assertRaises(ValueError, wsme_json.fromjson, self._type, body)
|
||||
|
||||
def test_invalid_http_method(self):
|
||||
body = {"type": constants.HEALTH_MONITOR_HTTP, "delay": 1,
|
||||
"timeout": 1, "fall_threshold": 1, "http_method": 1}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_url_path(self):
|
||||
body = {"type": constants.HEALTH_MONITOR_HTTP, "delay": 1,
|
||||
"timeout": 1, "fall_threshold": 1, "url_path": 1}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_expected_codes(self):
|
||||
body = {"type": constants.HEALTH_MONITOR_HTTP, "delay": 1,
|
||||
"timeout": 1, "fall_threshold": 1, "expected_codes": 1}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
|
||||
class TestHealthMonitorPOST(base.BaseTypesTest, TestHealthMonitor):
|
||||
|
||||
_type = hm_type.HealthMonitorPOST
|
||||
|
||||
def test_health_monitor(self):
|
||||
body = {"type": constants.HEALTH_MONITOR_HTTP, "delay": 1,
|
||||
"timeout": 1, "fall_threshold": 1, "rise_threshold": 1}
|
||||
hm = wsme_json.fromjson(self._type, body)
|
||||
self.assertTrue(hm.enabled)
|
||||
|
||||
def test_type_mandatory(self):
|
||||
body = {"delay": 80, "timeout": 1, "fall_threshold": 1,
|
||||
"rise_threshold": 1}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_delay_mandatory(self):
|
||||
body = {"type": constants.HEALTH_MONITOR_HTTP, "timeout": 1,
|
||||
"fall_threshold": 1, "rise_threshold": 1}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_timeout_mandatory(self):
|
||||
body = {"type": constants.HEALTH_MONITOR_HTTP, "delay": 1,
|
||||
"fall_threshold": 1, "rise_threshold": 1}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_fall_threshold_mandatory(self):
|
||||
body = {"type": constants.HEALTH_MONITOR_HTTP, "delay": 1,
|
||||
"timeout": 1, "rise_threshold": 1}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_rise_threshold_mandatory(self):
|
||||
body = {"type": constants.HEALTH_MONITOR_HTTP, "delay": 1,
|
||||
"timeout": 1, "fall_threshold": 1}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
|
||||
class TestHealthMonitorPUT(base.BaseTypesTest, TestHealthMonitor):
|
||||
|
||||
_type = hm_type.HealthMonitorPUT
|
||||
|
||||
def test_health_monitor(self):
|
||||
body = {"protocol": constants.PROTOCOL_HTTPS}
|
||||
hm = wsme_json.fromjson(self._type, body)
|
||||
self.assertEqual(wsme_types.Unset, hm.enabled)
|
90
octavia/tests/unit/api/v1/types/test_listeners.py
Normal file
90
octavia/tests/unit/api/v1/types/test_listeners.py
Normal file
@ -0,0 +1,90 @@
|
||||
# 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 exc
|
||||
from wsme.rest import json as wsme_json
|
||||
from wsme import types as wsme_types
|
||||
|
||||
from octavia.api.v1.types import listener as lis_type
|
||||
from octavia.common import constants
|
||||
from octavia.tests.unit.api.v1.types import base
|
||||
|
||||
|
||||
class TestListener(object):
|
||||
|
||||
_type = None
|
||||
|
||||
def test_invalid_name(self):
|
||||
body = {"protocol": constants.PROTOCOL_HTTP, "protocol_port": 80,
|
||||
"name": 0}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_description(self):
|
||||
body = {"protocol": constants.PROTOCOL_HTTP, "protocol_port": 80,
|
||||
"description": 0}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_enabled(self):
|
||||
body = {"protocol": constants.PROTOCOL_HTTP, "protocol_port": 80,
|
||||
"enabled": "true"}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_protocol(self):
|
||||
body = {"protocol": 10, "protocol_port": 80}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_protocol_port(self):
|
||||
body = {"protocol": constants.PROTOCOL_HTTP, "protocol_port": "test"}
|
||||
self.assertRaises(ValueError, wsme_json.fromjson, self._type, body)
|
||||
|
||||
def test_invalid_connection_limit(self):
|
||||
body = {"protocol": constants.PROTOCOL_HTTP, "protocol_port": 80,
|
||||
"connection_limit": "test"}
|
||||
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}
|
||||
listener = wsme_json.fromjson(self._type, body)
|
||||
self.assertTrue(listener.enabled)
|
||||
|
||||
def test_protocol_mandatory(self):
|
||||
body = {"protocol_port": 80}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_protocol_port_mandatory(self):
|
||||
body = {"protocol": constants.PROTOCOL_HTTP}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
|
||||
class TestListenerPUT(base.BaseTypesTest, TestListener):
|
||||
|
||||
_type = lis_type.ListenerPUT
|
||||
|
||||
def test_listener(self):
|
||||
body = {"name": "test", "description": "test", "connection_limit": 10,
|
||||
"protocol": constants.PROTOCOL_HTTP, "protocol_port": 80}
|
||||
listener = wsme_json.fromjson(self._type, body)
|
||||
self.assertEqual(wsme_types.Unset, listener.enabled)
|
115
octavia/tests/unit/api/v1/types/test_load_balancers.py
Normal file
115
octavia/tests/unit/api/v1/types/test_load_balancers.py
Normal file
@ -0,0 +1,115 @@
|
||||
# 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 exc
|
||||
from wsme.rest import json as wsme_json
|
||||
from wsme import types as wsme_types
|
||||
|
||||
from octavia.api.v1.types import load_balancer as lb_type
|
||||
from octavia.openstack.common import uuidutils
|
||||
from octavia.tests.unit.api.v1.types import base
|
||||
|
||||
|
||||
class TestLoadBalancer(object):
|
||||
|
||||
_type = None
|
||||
|
||||
def test_load_balancer(self):
|
||||
body = {"name": "test_name", "description": "test_description",
|
||||
"vip": {}}
|
||||
lb = wsme_json.fromjson(self._type, body)
|
||||
self.assertTrue(lb.enabled)
|
||||
|
||||
def test_invalid_name(self):
|
||||
body = {"name": 0}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_name_length(self):
|
||||
body = {"name": "x" * 256}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_description(self):
|
||||
body = {"description": 0}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_description_length(self):
|
||||
body = {"name": "x" * 256}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_enabled(self):
|
||||
body = {"enabled": "true"}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
|
||||
class TestLoadBalancerPOST(base.BaseTypesTest, TestLoadBalancer):
|
||||
|
||||
_type = lb_type.LoadBalancerPOST
|
||||
|
||||
def test_vip_mandatory(self):
|
||||
body = {"name": "test"}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
|
||||
class TestLoadBalancerPUT(base.BaseTypesTest, TestLoadBalancer):
|
||||
|
||||
_type = lb_type.LoadBalancerPUT
|
||||
|
||||
def test_load_balancer(self):
|
||||
body = {"name": "test_name", "description": "test_description"}
|
||||
lb = wsme_json.fromjson(self._type, body)
|
||||
self.assertEqual(wsme_types.Unset, lb.enabled)
|
||||
|
||||
|
||||
class TestVip(base.BaseTypesTest):
|
||||
|
||||
_type = lb_type.VIP
|
||||
|
||||
def test_vip(self):
|
||||
body = {"vip": {"ip_address": "10.0.0.1",
|
||||
"net_port_id": uuidutils.generate_uuid(),
|
||||
"subnet_id": uuidutils.generate_uuid(),
|
||||
"floating_ip_id": uuidutils.generate_uuid(),
|
||||
"floating_ip_network_id": uuidutils.generate_uuid()}}
|
||||
wsme_json.fromjson(self._type, body)
|
||||
|
||||
def test_invalid_ip_address(self):
|
||||
body = {"ip_address": uuidutils.generate_uuid()}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_net_port_id(self):
|
||||
body = {"net_port_id": "invalid_uuid"}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_subnet_id(self):
|
||||
body = {"subnet_id": "invalid_uuid"}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_floating_ip_id(self):
|
||||
body = {"floating_ip_id": "invalid_uuid"}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_floating_network_ip_id(self):
|
||||
body = {"floating_ip_network_id": "invalid_uuid"}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
87
octavia/tests/unit/api/v1/types/test_members.py
Normal file
87
octavia/tests/unit/api/v1/types/test_members.py
Normal file
@ -0,0 +1,87 @@
|
||||
# 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 exc
|
||||
from wsme.rest import json as wsme_json
|
||||
from wsme import types as wsme_types
|
||||
|
||||
from octavia.api.v1.types import member as member_type
|
||||
from octavia.tests.unit.api.v1.types import base
|
||||
|
||||
|
||||
class TestMemberPOST(base.BaseTypesTest):
|
||||
|
||||
_type = member_type.MemberPOST
|
||||
|
||||
def test_member(self):
|
||||
body = {"ip_address": "10.0.0.1", "protocol_port": 80}
|
||||
member = wsme_json.fromjson(self._type, body)
|
||||
self.assertTrue(member.enabled)
|
||||
self.assertEqual(1, member.weight)
|
||||
self.assertEqual(wsme_types.Unset, member.subnet_id)
|
||||
|
||||
def test_address_mandatory(self):
|
||||
body = {"name": "test"}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_protocol_mandatory(self):
|
||||
body = {"name": "test"}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_address(self):
|
||||
body = {"ip_address": "test", "protocol_port": 443}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_subnet_id(self):
|
||||
body = {"ip_address": "10.0.0.1", "protocol_port": 443,
|
||||
"subnet_id": "invalid_uuid"}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_enabled(self):
|
||||
body = {"ip_address": "10.0.0.1", "protocol_port": 443,
|
||||
"enabled": "true"}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_protocol_port(self):
|
||||
body = {"ip_address": "10.0.0.1", "protocol_port": "test"}
|
||||
self.assertRaises(ValueError, wsme_json.fromjson, self._type, body)
|
||||
|
||||
def test_invalid_weight(self):
|
||||
body = {"ip_address": "10.0.0.1", "protocol_port": 443,
|
||||
"weight": "test"}
|
||||
self.assertRaises(ValueError, wsme_json.fromjson, self._type, body)
|
||||
|
||||
|
||||
class TestMemberPUT(base.BaseTypesTest):
|
||||
|
||||
_type = member_type.MemberPUT
|
||||
|
||||
def test_member(self):
|
||||
body = {"protocol_port": 80}
|
||||
member = wsme_json.fromjson(self._type, body)
|
||||
self.assertEqual(wsme_types.Unset, member.weight)
|
||||
self.assertEqual(wsme_types.Unset, member.enabled)
|
||||
|
||||
def test_invalid_protocol_port(self):
|
||||
body = {"protocol_port": "test"}
|
||||
self.assertRaises(ValueError, wsme_json.fromjson, self._type, body)
|
||||
|
||||
def test_invalid_weight(self):
|
||||
body = {"weight": "test"}
|
||||
self.assertRaises(ValueError, wsme_json.fromjson, self._type, body)
|
126
octavia/tests/unit/api/v1/types/test_pools.py
Normal file
126
octavia/tests/unit/api/v1/types/test_pools.py
Normal file
@ -0,0 +1,126 @@
|
||||
# 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 exc
|
||||
from wsme.rest import json as wsme_json
|
||||
from wsme import types as wsme_types
|
||||
|
||||
from octavia.api.v1.types import pool as pool_type
|
||||
from octavia.common import constants
|
||||
from octavia.tests.unit.api.v1.types import base
|
||||
|
||||
|
||||
class TestSessionPersistence(object):
|
||||
|
||||
_type = None
|
||||
|
||||
def test_session_persistence(self):
|
||||
body = {"type": constants.SESSION_PERSISTENCE_HTTP_COOKIE}
|
||||
sp = wsme_json.fromjson(self._type, body)
|
||||
self.assertIsNotNone(sp.type)
|
||||
|
||||
def test_invalid_type(self):
|
||||
body = {"type": 10}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_cookie_name(self):
|
||||
body = {"type": constants.SESSION_PERSISTENCE_HTTP_COOKIE,
|
||||
"cookie_name": 10}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
|
||||
class TestPoolPOST(base.BaseTypesTest):
|
||||
|
||||
_type = pool_type.PoolPOST
|
||||
|
||||
def test_pool(self):
|
||||
body = {"protocol": constants.PROTOCOL_HTTP,
|
||||
"lb_algorithm": constants.LB_ALGORITHM_ROUND_ROBIN}
|
||||
pool = wsme_json.fromjson(self._type, body)
|
||||
self.assertTrue(pool.enabled)
|
||||
|
||||
def test_protocol_mandatory(self):
|
||||
body = {"lb_algorithm": constants.LB_ALGORITHM_ROUND_ROBIN}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_lb_algorithm_mandatory(self):
|
||||
body = {"protocol": constants.PROTOCOL_HTTP}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_name(self):
|
||||
body = {"name": 10, "protocol": constants.PROTOCOL_HTTP,
|
||||
"lb_algorithm": constants.LB_ALGORITHM_ROUND_ROBIN}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_description(self):
|
||||
body = {"description": 10, "protocol": constants.PROTOCOL_HTTP,
|
||||
"lb_algorithm": constants.LB_ALGORITHM_ROUND_ROBIN}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_protocol(self):
|
||||
body = {"protocol": 10,
|
||||
"lb_algorithm": constants.LB_ALGORITHM_ROUND_ROBIN}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_lb_algorithm(self):
|
||||
body = {"protocol": constants.PROTOCOL_HTTP, "lb_algorithm": 10}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
|
||||
class TestPoolPUT(base.BaseTypesTest):
|
||||
|
||||
_type = pool_type.PoolPUT
|
||||
|
||||
def test_pool(self):
|
||||
body = {"name": "test_name"}
|
||||
pool = wsme_json.fromjson(self._type, body)
|
||||
self.assertEqual(wsme_types.Unset, pool.enabled)
|
||||
|
||||
def test_invalid_name(self):
|
||||
body = {"name": 10}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_description(self):
|
||||
body = {"description": 10}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_lb_algorithm(self):
|
||||
body = {"lb_algorithm": 10}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
|
||||
class TestSessionPersistencePOST(base.BaseTypesTest, TestSessionPersistence):
|
||||
|
||||
_type = pool_type.SessionPersistencePOST
|
||||
|
||||
def test_type_mandatory(self):
|
||||
body = {"cookie_name": "test_name"}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
|
||||
class TestSessionPersistencePUT(base.BaseTypesTest, TestSessionPersistence):
|
||||
|
||||
_type = pool_type.SessionPersistencePUT
|
@ -21,8 +21,6 @@ class TestExceptions(base.TestCase):
|
||||
|
||||
def test_exception(self):
|
||||
try:
|
||||
raise exc.NotFound()
|
||||
raise exc.NotFound(resource="test", id="test")
|
||||
except exc.NotFound:
|
||||
pass
|
||||
else:
|
||||
raise Exception()
|
||||
|
@ -4,6 +4,7 @@ blockdiag
|
||||
docutils==0.11
|
||||
nwdiag
|
||||
oslosphinx
|
||||
pecan>=0.7.0
|
||||
pbr>=0.6,<1.0
|
||||
seqdiag
|
||||
sphinx
|
||||
@ -35,5 +36,5 @@ python-barbicanclient>=3.0
|
||||
python-keystoneclient>=0.11.1
|
||||
python-novaclient>=2.17.0
|
||||
posix_ipc
|
||||
|
||||
pyOpenSSL>=0.14
|
||||
wsme==0.6.1
|
||||
|
Loading…
Reference in New Issue
Block a user