trio2o/tricircle/common/lock_handle.py
zhiyuan_cai 8c0e8276c3 Implement security group - Nova API gateway part
Nova API gateway needs to finish two tasks for security group
functionality. One is to create security group in bottom pod if
it does not exist. Only when the user boot a server can we know
which pod to create the security group. The other one is to
handle default security group. We decide to replace the default
"remote group" rules with several "remote ip prefix" rules.
Each rule points to a CIDR of one of the project's subnets.
Since the collection of subnets may change, we need to update
bottom security group rules.

Currently these two tasks both run in synchronous way. For
better response time the second task can be implemented in
asynchronous way later.

Neutron plugin part for security group functionality will be
covered in next patch.

Change-Id: I1822ff16e3dfc2a89c34f7833adcc1ad1d952462
2016-03-14 09:47:50 +08:00

139 lines
5.9 KiB
Python

# Copyright 2015 Huawei Technologies Co., Ltd.
# All Rights Reserved
#
# 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 datetime
import eventlet
import oslo_db.exception as db_exc
from tricircle.db import core
from tricircle.db import models
ALL_DONE = 0 # both route and bottom resource exist
RES_DONE = 1 # only bottom resource exists
NONE_DONE = 2 # neither router nor bottom resources exists
# The case only router exists is not considered, there may be some manual
# operations on bottom pod which results to this problem.
def get_or_create_route(t_ctx, q_ctx,
project_id, pod, _id, _type, list_ele_method):
# use configuration option later
route_expire_threshold = 30
with t_ctx.session.begin():
routes = core.query_resource(
t_ctx, models.ResourceRouting,
[{'key': 'top_id', 'comparator': 'eq', 'value': _id},
{'key': 'pod_id', 'comparator': 'eq',
'value': pod['pod_id']}], [])
if routes:
route = routes[0]
if route['bottom_id']:
return route, ALL_DONE
else:
route_time = route['updated_at'] or route['created_at']
current_time = datetime.datetime.utcnow()
delta = current_time - route_time
if delta.seconds > route_expire_threshold:
# NOTE(zhiyuan) cannot directly remove the route, we have
# a race here that other worker is updating this route, we
# need to check if the corresponding element has been
# created by other worker
eles = list_ele_method(t_ctx, q_ctx, pod, _id, _type)
if eles:
route['bottom_id'] = eles[0]['id']
core.update_resource(t_ctx,
models.ResourceRouting,
route['id'], route)
return route, RES_DONE
try:
core.delete_resource(t_ctx,
models.ResourceRouting,
route['id'])
except db_exc.ResourceNotFound:
pass
try:
# NOTE(zhiyuan) try/except block inside a with block will cause
# problem, so move them out of the block and manually handle the
# session context
t_ctx.session.begin()
route = core.create_resource(t_ctx, models.ResourceRouting,
{'top_id': _id,
'pod_id': pod['pod_id'],
'project_id': project_id,
'resource_type': _type})
t_ctx.session.commit()
return route, NONE_DONE
except db_exc.DBDuplicateEntry:
t_ctx.session.rollback()
return None, NONE_DONE
finally:
t_ctx.session.close()
def get_or_create_element(t_ctx, q_ctx,
project_id, pod, ele, _type, body,
list_ele_method, create_ele_method):
# use configuration option later
max_tries = 5
for _ in xrange(max_tries):
route, status = get_or_create_route(
t_ctx, q_ctx, project_id, pod, ele['id'], _type, list_ele_method)
if not route:
eventlet.sleep(0)
continue
if status == RES_DONE or status == ALL_DONE:
# in these cases, bottom_id must exist
break
if status == NONE_DONE:
try:
ele = create_ele_method(t_ctx, q_ctx, pod, body, _type)
except Exception:
with t_ctx.session.begin():
try:
core.delete_resource(t_ctx,
models.ResourceRouting,
route['id'])
except db_exc.ResourceNotFound:
# NOTE(zhiyuan) this is a rare case that other worker
# considers the route expires and delete it though it
# was just created, maybe caused by out-of-sync time
pass
raise
with t_ctx.session.begin():
# NOTE(zhiyuan) it's safe to update route, the bottom network
# has been successfully created, so other worker will not
# delete this route
route['bottom_id'] = ele['id']
core.update_resource(t_ctx, models.ResourceRouting,
route['id'], route)
break
if not route:
raise Exception('Fail to create %s routing entry' % _type)
if not route['bottom_id']:
raise Exception('Fail to bind top and bottom %s' % _type)
# NOTE(zhiyuan) Status being ALL_DONE means that the routing entry is
# complete when we retrieve the resource, so we return False to indicate
# that we can directly use this resource safely. Status being RES_DONE and
# NONE_DONE means that the routing entry is not complete when we retrieve
# the resource but we manage to fill the entry finally, so we return True
# to indicate that we may leave some work to do.
if status == ALL_DONE:
return False, route['bottom_id']
else:
return True, route['bottom_id']