Add cascade delete for APIv2
Change-Id: I96ee5963137cc3b396de538db429bab2963562a6 Closes-Bug: #1663701
This commit is contained in:
parent
9bfa58af9f
commit
b80cc9c923
|
@ -1,6 +1,12 @@
|
|||
###############################################################################
|
||||
# Path fields
|
||||
###############################################################################
|
||||
cascade-delete:
|
||||
description: |
|
||||
If ``true`` will delete all child objects of the load balancer.
|
||||
in: path
|
||||
required: false
|
||||
type: boolean
|
||||
path-listener-id:
|
||||
description: |
|
||||
The ID of the listener to query.
|
||||
|
|
|
@ -203,12 +203,12 @@ Creating a Fully Populated Load Balancer
|
|||
----------------------------------------
|
||||
|
||||
You can configure all documented features of the load balancer at
|
||||
creation time by specifying the additional elements or attributes in the
|
||||
request.
|
||||
creation time by specifying the additional elements or attributes
|
||||
in the request.
|
||||
|
||||
Note: all pools must have names, and must only be fully defined once. To
|
||||
reference a pool from multiple objects, supply the pool name only for all
|
||||
subsequent references..
|
||||
subsequent references.
|
||||
|
||||
Request Example
|
||||
---------------
|
||||
|
@ -378,6 +378,9 @@ Remove a load balancer
|
|||
|
||||
Removes a load balancer and its associated configuration from the project.
|
||||
|
||||
The optional parameter ``cascade`` when defined as ``true`` will delete all
|
||||
child objects of the load balancer.
|
||||
|
||||
The API immediately purges any and all configuration data, depending on the
|
||||
configuration settings. You cannot recover it.
|
||||
|
||||
|
@ -399,6 +402,7 @@ Request
|
|||
|
||||
.. rest_parameters:: ../parameters.yaml
|
||||
|
||||
- cascade: cascade-delete
|
||||
- loadbalancer_id: path-loadbalancer-id
|
||||
|
||||
Curl Example
|
||||
|
|
|
@ -17,6 +17,7 @@ from oslo_config import cfg
|
|||
from oslo_db import exception as odb_exceptions
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import excutils
|
||||
from oslo_utils import strutils
|
||||
import pecan
|
||||
from wsme import types as wtypes
|
||||
from wsmeext import pecan as wsme_pecan
|
||||
|
@ -323,12 +324,18 @@ class LoadBalancersController(base.BaseController):
|
|||
result = self._convert_db_to_type(db_lb, lb_types.LoadBalancerResponse)
|
||||
return lb_types.LoadBalancerRootResponse(loadbalancer=result)
|
||||
|
||||
def _delete(self, id, cascade=False):
|
||||
@wsme_pecan.wsexpose(None, wtypes.text, wtypes.text, status_code=204)
|
||||
def delete(self, id, cascade=False):
|
||||
"""Deletes a load balancer."""
|
||||
context = pecan.request.context.get('octavia_context')
|
||||
cascade = strutils.bool_from_string(cascade)
|
||||
db_lb = self._get_db_lb(context.session, id)
|
||||
self._test_lb_status(context.session, id,
|
||||
lb_status=constants.PENDING_DELETE)
|
||||
if (db_lb.listeners or db_lb.pools) and not cascade:
|
||||
msg = _("Cannot delete Load Balancer %s - it has children") % id
|
||||
LOG.warning(msg)
|
||||
raise exceptions.ValidationException(detail=msg)
|
||||
|
||||
try:
|
||||
LOG.info("Sending deleted Load Balancer %s to the handler",
|
||||
|
@ -341,8 +348,3 @@ class LoadBalancersController(base.BaseController):
|
|||
provisioning_status=constants.ERROR)
|
||||
result = self._convert_db_to_type(db_lb, lb_types.LoadBalancerResponse)
|
||||
return lb_types.LoadBalancersRootResponse(loadbalancer=result)
|
||||
|
||||
@wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
|
||||
def delete(self, id):
|
||||
"""Deletes a load balancer."""
|
||||
return self._delete(id)
|
||||
|
|
|
@ -81,13 +81,16 @@ class PoolFlows(object):
|
|||
# health monitor should cascade
|
||||
# members should cascade
|
||||
delete_pool_flow.add(database_tasks.MarkPoolPendingDeleteInDB(
|
||||
name='mark_pool_pending_delete_in_db_' + name,
|
||||
requires=constants.POOL,
|
||||
rebind={constants.POOL: name}))
|
||||
delete_pool_flow.add(database_tasks.CountPoolChildrenForQuota(
|
||||
name='count_pool_children_for_quota_' + name,
|
||||
requires=constants.POOL,
|
||||
provides=constants.POOL_CHILD_COUNT,
|
||||
rebind={constants.POOL: name}))
|
||||
delete_pool_flow.add(model_tasks.DeleteModelObject(
|
||||
name='delete_model_object_' + name,
|
||||
rebind={constants.OBJECT: name}))
|
||||
delete_pool_flow.add(database_tasks.DeletePoolInDB(
|
||||
name='delete_pool_in_db_' + name,
|
||||
|
|
|
@ -110,9 +110,17 @@ class BaseAPITest(base_db_test.OctaviaDBTestBase):
|
|||
def _build_body(self, json):
|
||||
return {self.root_tag: json}
|
||||
|
||||
def delete(self, path, headers=None, status=204, expect_errors=False):
|
||||
def delete(self, path, headers=None, params=None, status=204,
|
||||
expect_errors=False):
|
||||
headers = headers or {}
|
||||
params = params or {}
|
||||
full_path = self._get_full_path(path)
|
||||
param_string = ""
|
||||
for k, v in params.items():
|
||||
param_string += "{key}={value}&".format(key=k, value=v)
|
||||
if param_string:
|
||||
full_path = "{path}?{params}".format(
|
||||
path=full_path, params=param_string.rstrip("&"))
|
||||
response = self.app.delete(full_path,
|
||||
headers=headers,
|
||||
status=status,
|
||||
|
|
|
@ -555,7 +555,8 @@ class TestHealthMonitor(base.BaseAPITest):
|
|||
status=409)
|
||||
|
||||
def test_create_when_lb_pending_delete(self):
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id))
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id),
|
||||
params={'cascade': "true"})
|
||||
self.create_health_monitor(
|
||||
self.pool_id,
|
||||
constants.HEALTH_MONITOR_HTTP, 1, 1, 1, 1, status=409)
|
||||
|
@ -565,7 +566,8 @@ class TestHealthMonitor(base.BaseAPITest):
|
|||
self.pool_id, constants.HEALTH_MONITOR_HTTP,
|
||||
1, 1, 1, 1).get(self.root_tag)
|
||||
self.set_lb_status(self.lb_id)
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id))
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id),
|
||||
params={'cascade': "true"})
|
||||
new_hm = {'max_retries': 2}
|
||||
self.put(self.HM_PATH.format(healthmonitor_id=api_hm.get('id')),
|
||||
body=self._build_body(new_hm), status=409)
|
||||
|
@ -575,6 +577,7 @@ class TestHealthMonitor(base.BaseAPITest):
|
|||
self.pool_id, constants.HEALTH_MONITOR_HTTP,
|
||||
1, 1, 1, 1).get(self.root_tag)
|
||||
self.set_lb_status(self.lb_id)
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id))
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id),
|
||||
params={'cascade': "true"})
|
||||
self.delete(self.HM_PATH.format(healthmonitor_id=api_hm.get('id')),
|
||||
status=409)
|
||||
|
|
|
@ -579,7 +579,8 @@ class TestL7Policy(base.BaseAPITest):
|
|||
status=409)
|
||||
|
||||
def test_create_when_lb_pending_delete(self):
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id))
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id),
|
||||
params={'cascade': "true"})
|
||||
new_l7policy = {
|
||||
'listener_id': self.listener_id,
|
||||
'action': constants.L7POLICY_ACTION_REDIRECT_TO_URL,
|
||||
|
@ -592,7 +593,8 @@ class TestL7Policy(base.BaseAPITest):
|
|||
constants.L7POLICY_ACTION_REJECT,
|
||||
).get(self.root_tag)
|
||||
self.set_lb_status(self.lb_id)
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id))
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id),
|
||||
params={'cascade': "true"})
|
||||
new_l7policy = {
|
||||
'action': constants.L7POLICY_ACTION_REDIRECT_TO_URL,
|
||||
'redirect_url': 'http://www.example.com'}
|
||||
|
@ -605,7 +607,8 @@ class TestL7Policy(base.BaseAPITest):
|
|||
constants.L7POLICY_ACTION_REJECT,
|
||||
).get(self.root_tag)
|
||||
self.set_lb_status(self.lb_id)
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id))
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id),
|
||||
params={'cascade': "true"})
|
||||
self.delete(self.L7POLICY_PATH.format(
|
||||
l7policy_id=l7policy.get('id')),
|
||||
status=409)
|
||||
|
|
|
@ -496,7 +496,8 @@ class TestL7Rule(base.BaseAPITest):
|
|||
constants.L7RULE_COMPARE_TYPE_STARTS_WITH,
|
||||
'/api').get(self.root_tag)
|
||||
self.set_lb_status(self.lb_id)
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id))
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id),
|
||||
params={'cascade': "true"})
|
||||
new_l7rule = {'type': constants.L7RULE_TYPE_HEADER,
|
||||
'compare_type':
|
||||
constants.L7RULE_COMPARE_TYPE_STARTS_WITH,
|
||||
|
@ -511,7 +512,8 @@ class TestL7Rule(base.BaseAPITest):
|
|||
constants.L7RULE_COMPARE_TYPE_STARTS_WITH,
|
||||
'/api').get(self.root_tag)
|
||||
self.set_lb_status(self.lb_id)
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id))
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id),
|
||||
params={'cascade': "true"})
|
||||
new_l7rule = {'type': constants.L7RULE_TYPE_COOKIE,
|
||||
'compare_type':
|
||||
constants.L7RULE_COMPARE_TYPE_ENDS_WITH,
|
||||
|
@ -526,6 +528,7 @@ class TestL7Rule(base.BaseAPITest):
|
|||
constants.L7RULE_COMPARE_TYPE_STARTS_WITH,
|
||||
'/api').get(self.root_tag)
|
||||
self.set_lb_status(self.lb_id)
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id))
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id),
|
||||
params={'cascade': "true"})
|
||||
self.delete(self.l7rule_path.format(l7rule_id=l7rule.get('id')),
|
||||
status=409)
|
||||
|
|
|
@ -586,17 +586,19 @@ class TestListener(base.BaseAPITest):
|
|||
lb = self.create_load_balancer(uuidutils.generate_uuid(),
|
||||
name='lb1', description='desc1',
|
||||
admin_state_up=False)
|
||||
self.set_lb_status(lb['loadbalancer']['id'])
|
||||
lb_id = lb['loadbalancer'].get('id')
|
||||
self.set_lb_status(lb_id)
|
||||
lb_listener = {'name': 'listener1', 'description': 'desc1',
|
||||
'admin_state_up': False,
|
||||
'protocol': constants.PROTOCOL_HTTP,
|
||||
'protocol_port': 80, 'connection_limit': 10,
|
||||
'loadbalancer_id': lb['loadbalancer']['id']}
|
||||
'loadbalancer_id': lb_id}
|
||||
body = self._build_body(lb_listener)
|
||||
api_listener = self.post(
|
||||
self.LISTENERS_PATH, body).json['listener']
|
||||
self.set_lb_status(lb['loadbalancer']['id'])
|
||||
self.delete(self.LB_PATH.format(lb_id=lb['loadbalancer']['id']))
|
||||
self.LISTENERS_PATH, body).json.get(self.root_tag)
|
||||
self.set_lb_status(lb_id)
|
||||
self.delete(self.LB_PATH.format(lb_id=lb_id),
|
||||
params={'cascade': "true"})
|
||||
lb_listener_put = {'name': 'listener1_updated'}
|
||||
body = self._build_body(lb_listener_put)
|
||||
listener_path = self.LISTENER_PATH.format(
|
||||
|
@ -607,17 +609,19 @@ class TestListener(base.BaseAPITest):
|
|||
lb = self.create_load_balancer(uuidutils.generate_uuid(),
|
||||
name='lb1', description='desc1',
|
||||
admin_state_up=False)
|
||||
self.set_lb_status(lb['loadbalancer']['id'])
|
||||
lb_id = lb['loadbalancer'].get('id')
|
||||
self.set_lb_status(lb_id)
|
||||
lb_listener = {'name': 'listener1', 'description': 'desc1',
|
||||
'admin_state_up': False,
|
||||
'protocol': constants.PROTOCOL_HTTP,
|
||||
'protocol_port': 80, 'connection_limit': 10,
|
||||
'loadbalancer_id': lb['loadbalancer']['id']}
|
||||
'loadbalancer_id': lb_id}
|
||||
body = self._build_body(lb_listener)
|
||||
api_listener = self.post(
|
||||
self.LISTENERS_PATH, body).json['listener']
|
||||
self.set_lb_status(lb['loadbalancer']['id'])
|
||||
self.delete(self.LB_PATH.format(lb_id=lb['loadbalancer']['id']))
|
||||
self.LISTENERS_PATH, body).json.get(self.root_tag)
|
||||
self.set_lb_status(lb_id)
|
||||
self.delete(self.LB_PATH.format(lb_id=lb_id),
|
||||
params={'cascade': "true"})
|
||||
listener_path = self.LISTENER_PATH.format(
|
||||
listener_id=api_listener['id'])
|
||||
self.delete(listener_path, status=409)
|
||||
|
|
|
@ -580,6 +580,54 @@ class TestLoadBalancer(base.BaseAPITest):
|
|||
api_lb.get('operational_status'))
|
||||
self.assert_final_lb_statuses(api_lb.get('id'), delete=True)
|
||||
|
||||
def test_delete_fails_with_pool(self):
|
||||
project_id = uuidutils.generate_uuid()
|
||||
lb = self.create_load_balancer(uuidutils.generate_uuid(),
|
||||
name='lb1',
|
||||
project_id=project_id,
|
||||
description='desc1').get(self.root_tag)
|
||||
lb_id = lb.get('id')
|
||||
self.set_lb_status(lb_id)
|
||||
self.create_pool(
|
||||
lb_id,
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN)
|
||||
self.set_lb_status(lb_id)
|
||||
self.delete(self.LB_PATH.format(lb_id=lb_id), status=400)
|
||||
|
||||
def test_delete_fails_with_listener(self):
|
||||
project_id = uuidutils.generate_uuid()
|
||||
lb = self.create_load_balancer(uuidutils.generate_uuid(),
|
||||
name='lb1',
|
||||
project_id=project_id,
|
||||
description='desc1').get(self.root_tag)
|
||||
lb_id = lb.get('id')
|
||||
self.set_lb_status(lb_id)
|
||||
self.create_listener(constants.PROTOCOL_HTTP, 80, lb_id)
|
||||
self.set_lb_status(lb_id)
|
||||
self.delete(self.LB_PATH.format(lb_id=lb_id), status=400)
|
||||
|
||||
def test_cascade_delete(self):
|
||||
project_id = uuidutils.generate_uuid()
|
||||
lb = self.create_load_balancer(uuidutils.generate_uuid(),
|
||||
name='lb1',
|
||||
project_id=project_id,
|
||||
description='desc1').get(self.root_tag)
|
||||
lb_id = lb.get('id')
|
||||
self.set_lb_status(lb_id)
|
||||
listener = self.create_listener(
|
||||
constants.PROTOCOL_HTTP, 80, lb_id).get('listener')
|
||||
listener_id = listener.get('id')
|
||||
self.set_lb_status(lb_id)
|
||||
self.create_pool(
|
||||
lb_id,
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
listener_id=listener_id)
|
||||
self.set_lb_status(lb_id)
|
||||
self.delete(self.LB_PATH.format(lb_id=lb_id),
|
||||
params={'cascade': "true"})
|
||||
|
||||
def test_delete_bad_lb_id(self):
|
||||
path = self.LB_PATH.format(lb_id='bad_uuid')
|
||||
self.delete(path, status=404)
|
||||
|
|
|
@ -464,7 +464,8 @@ class TestMember(base.BaseAPITest):
|
|||
self.create_member(self.pool_id, address="10.0.0.1",
|
||||
protocol_port=80)
|
||||
self.set_lb_status(self.lb_id)
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id))
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id),
|
||||
params={'cascade': "true"})
|
||||
member = {'address': '10.0.0.2', 'protocol_port': 88,
|
||||
'project_id': self.project_id}
|
||||
self.post(self.members_path, body=self._build_body(member),
|
||||
|
@ -475,7 +476,8 @@ class TestMember(base.BaseAPITest):
|
|||
self.pool_id, address="10.0.0.1", protocol_port=80,
|
||||
name="member1").get(self.root_tag)
|
||||
self.set_lb_status(self.lb_id)
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id))
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id),
|
||||
params={'cascade': "true"})
|
||||
self.put(self.member_path.format(member_id=member.get('id')),
|
||||
body=self._build_body({'name': "member2"}), status=409)
|
||||
|
||||
|
@ -484,6 +486,7 @@ class TestMember(base.BaseAPITest):
|
|||
self.pool_id, address="10.0.0.1",
|
||||
protocol_port=80).get(self.root_tag)
|
||||
self.set_lb_status(self.lb_id)
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id))
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id),
|
||||
params={'cascade': "true"})
|
||||
self.delete(self.member_path.format(
|
||||
member_id=member.get('id')), status=409)
|
||||
|
|
|
@ -798,7 +798,8 @@ class TestPool(base.BaseAPITest):
|
|||
status=409)
|
||||
|
||||
def test_create_when_lb_pending_delete(self):
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id))
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id),
|
||||
params={'cascade': "true"})
|
||||
new_pool = {
|
||||
'loadbalancer_id': self.lb_id,
|
||||
'listener_id': self.listener_id,
|
||||
|
@ -814,7 +815,8 @@ class TestPool(base.BaseAPITest):
|
|||
constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
listener_id=self.listener_id).get(self.root_tag)
|
||||
self.set_lb_status(self.lb_id)
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id))
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id),
|
||||
params={'cascade': "true"})
|
||||
new_pool = {'admin_state_up': False}
|
||||
self.put(self.POOL_PATH.format(pool_id=api_pool.get('id')),
|
||||
self._build_body(new_pool), status=409)
|
||||
|
@ -826,6 +828,7 @@ class TestPool(base.BaseAPITest):
|
|||
constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
listener_id=self.listener_id).get(self.root_tag)
|
||||
self.set_lb_status(self.lb_id)
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id))
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id),
|
||||
params={'cascade': "true"})
|
||||
self.delete(self.POOL_PATH.format(pool_id=api_pool.get('id')),
|
||||
status=409)
|
||||
|
|
Loading…
Reference in New Issue