![zhiyuan_cai](/assets/img/avatar_default.png)
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
139 lines
5.9 KiB
Python
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']
|