
The statless design was developed in the experiment branch, the experiment shows advantage in removing the status synchronization, uuid mapping compared to the stateful design, and also fully reduce the coupling with OpenStack services like Nova, Cinder. The overhead query latency for resources also acceptable. It's time to move the statless design to the master branch BP: https://blueprints.launchpad.net/tricircle/+spec/implement-stateless Change-Id: I51bbb60dc07da5b2e79f25e02209aa2eb72711ac Signed-off-by: Chaoyi Huang <joehuang@huawei.com>
302 lines
11 KiB
Python
302 lines
11 KiB
Python
# Copyright (c) 2015 Huawei Tech. 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 pecan
|
|
from pecan import expose
|
|
from pecan import Response
|
|
from pecan import rest
|
|
|
|
import oslo_db.exception as db_exc
|
|
from oslo_log import log as logging
|
|
from oslo_utils import uuidutils
|
|
|
|
from tricircle.common import az_ag
|
|
import tricircle.common.context as t_context
|
|
import tricircle.common.exceptions as t_exc
|
|
from tricircle.common.i18n import _
|
|
from tricircle.common.i18n import _LE
|
|
from tricircle.common import utils
|
|
|
|
from tricircle.db import api as db_api
|
|
from tricircle.db import core
|
|
from tricircle.db import models
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class PodsController(rest.RestController):
|
|
|
|
def __init__(self):
|
|
pass
|
|
|
|
@expose(generic=True, template='json')
|
|
def post(self, **kw):
|
|
context = t_context.extract_context_from_environ()
|
|
|
|
if not t_context.is_admin_context(context):
|
|
pecan.abort(400, _('Admin role required to create pods'))
|
|
return
|
|
|
|
if 'pod' not in kw:
|
|
pecan.abort(400, _('Request body pod not found'))
|
|
return
|
|
|
|
pod = kw['pod']
|
|
|
|
# if az_name is null, and there is already one in db
|
|
pod_name = pod.get('pod_name', '').strip()
|
|
pod_az_name = pod.get('pod_az_name', '').strip()
|
|
dc_name = pod.get('dc_name', '').strip()
|
|
az_name = pod.get('az_name', '').strip()
|
|
_uuid = uuidutils.generate_uuid()
|
|
|
|
if az_name == '' and pod_name == '':
|
|
return Response(_('Valid pod_name is required for top region'),
|
|
422)
|
|
|
|
if az_name != '' and pod_name == '':
|
|
return Response(_('Valid pod_name is required for pod'), 422)
|
|
|
|
if pod.get('az_name') is None:
|
|
if self._get_top_region(context) != '':
|
|
return Response(_('Top region already exists'), 409)
|
|
|
|
# if az_name is not null, then the pod region name should not
|
|
# be same as that the top region
|
|
if az_name != '':
|
|
if self._get_top_region(context) == pod_name and pod_name != '':
|
|
return Response(
|
|
_('Pod region name duplicated with the top region name'),
|
|
409)
|
|
|
|
# to create the top region, make the pod_az_name to null value
|
|
if az_name == '':
|
|
pod_az_name = ''
|
|
|
|
try:
|
|
with context.session.begin():
|
|
# if not top region,
|
|
# then add corresponding ag and az for the pod
|
|
if az_name != '':
|
|
ag_name = utils.get_ag_name(pod_name)
|
|
aggregate = az_ag.create_ag_az(context,
|
|
ag_name=ag_name,
|
|
az_name=az_name)
|
|
if aggregate is None:
|
|
return Response(_('Ag creation failure'), 400)
|
|
|
|
new_pod = core.create_resource(
|
|
context, models.Pod,
|
|
{'pod_id': _uuid,
|
|
'pod_name': pod_name,
|
|
'pod_az_name': pod_az_name,
|
|
'dc_name': dc_name,
|
|
'az_name': az_name})
|
|
except db_exc.DBDuplicateEntry as e1:
|
|
LOG.error(_LE('Record already exists: %(exception)s'),
|
|
{'exception': e1})
|
|
return Response(_('Record already exists'), 409)
|
|
except Exception as e2:
|
|
LOG.error(_LE('Fail to create pod: %(exception)s'),
|
|
{'exception': e2})
|
|
return Response(_('Fail to create pod'), 500)
|
|
|
|
return {'pod': new_pod}
|
|
|
|
@expose(generic=True, template='json')
|
|
def get_one(self, _id):
|
|
context = t_context.extract_context_from_environ()
|
|
|
|
if not t_context.is_admin_context(context):
|
|
pecan.abort(400, _('Admin role required to show pods'))
|
|
return
|
|
|
|
try:
|
|
return {'pod': db_api.get_pod(context, _id)}
|
|
except t_exc.ResourceNotFound:
|
|
pecan.abort(404, _('Pod not found'))
|
|
return
|
|
|
|
@expose(generic=True, template='json')
|
|
def get_all(self):
|
|
context = t_context.extract_context_from_environ()
|
|
|
|
if not t_context.is_admin_context(context):
|
|
pecan.abort(400, _('Admin role required to list pods'))
|
|
return
|
|
|
|
try:
|
|
return {'pods': db_api.list_pods(context)}
|
|
except Exception as e:
|
|
LOG.error(_LE('Fail to list pod: %(exception)s'),
|
|
{'exception': e})
|
|
pecan.abort(500, _('Fail to list pod'))
|
|
return
|
|
|
|
@expose(generic=True, template='json')
|
|
def delete(self, _id):
|
|
context = t_context.extract_context_from_environ()
|
|
|
|
if not t_context.is_admin_context(context):
|
|
pecan.abort(400, _('Admin role required to delete pods'))
|
|
return
|
|
|
|
try:
|
|
with context.session.begin():
|
|
pod = core.get_resource(context, models.Pod, _id)
|
|
if pod is not None:
|
|
ag_name = utils.get_ag_name(pod['pod_name'])
|
|
ag = az_ag.get_ag_by_name(context, ag_name)
|
|
if ag is not None:
|
|
az_ag.delete_ag(context, ag['id'])
|
|
core.delete_resource(context, models.Pod, _id)
|
|
pecan.response.status = 200
|
|
except t_exc.ResourceNotFound:
|
|
return Response(_('Pod not found'), 404)
|
|
except Exception as e:
|
|
LOG.error(_LE('Fail to delete pod: %(exception)s'),
|
|
{'exception': e})
|
|
return Response(_('Fail to delete pod'), 500)
|
|
|
|
def _get_top_region(self, ctx):
|
|
top_region_name = ''
|
|
try:
|
|
with ctx.session.begin():
|
|
pods = core.query_resource(ctx,
|
|
models.Pod, [], [])
|
|
for pod in pods:
|
|
if pod['az_name'] == '' and pod['pod_name'] != '':
|
|
return pod['pod_name']
|
|
except Exception:
|
|
return top_region_name
|
|
|
|
return top_region_name
|
|
|
|
|
|
class BindingsController(rest.RestController):
|
|
|
|
def __init__(self):
|
|
pass
|
|
|
|
@expose(generic=True, template='json')
|
|
def post(self, **kw):
|
|
context = t_context.extract_context_from_environ()
|
|
|
|
if not t_context.is_admin_context(context):
|
|
pecan.abort(400, _('Admin role required to create bindings'))
|
|
return
|
|
|
|
if 'pod_binding' not in kw:
|
|
pecan.abort(400, _('Request body not found'))
|
|
return
|
|
|
|
pod_b = kw['pod_binding']
|
|
tenant_id = pod_b.get('tenant_id', '').strip()
|
|
pod_id = pod_b.get('pod_id', '').strip()
|
|
_uuid = uuidutils.generate_uuid()
|
|
|
|
if tenant_id == '' or pod_id == '':
|
|
return Response(
|
|
_('Tenant_id and pod_id can not be empty'),
|
|
422)
|
|
|
|
# the az_pod_map_id should be exist for in the pod map table
|
|
try:
|
|
with context.session.begin():
|
|
pod = core.get_resource(context, models.Pod,
|
|
pod_id)
|
|
if pod.get('az_name') == '':
|
|
return Response(_('Top region can not be bound'), 422)
|
|
except t_exc.ResourceNotFound:
|
|
return Response(_('pod_id not found in pod'), 422)
|
|
except Exception as e:
|
|
LOG.error(_LE('Fail to create pod binding: %(exception)s'),
|
|
{'exception': e})
|
|
pecan.abort(500, _('Fail to create pod binding'))
|
|
return
|
|
|
|
try:
|
|
with context.session.begin():
|
|
pod_binding = core.create_resource(context, models.PodBinding,
|
|
{'id': _uuid,
|
|
'tenant_id': tenant_id,
|
|
'pod_id': pod_id})
|
|
except db_exc.DBDuplicateEntry:
|
|
return Response(_('Pod binding already exists'), 409)
|
|
except db_exc.DBConstraintError:
|
|
return Response(_('pod_id not exists in pod'), 422)
|
|
except db_exc.DBReferenceError:
|
|
return Response(_('DB reference not exists in pod'), 422)
|
|
except Exception as e:
|
|
LOG.error(_LE('Fail to create pod binding: %(exception)s'),
|
|
{'exception': e})
|
|
pecan.abort(500, _('Fail to create pod binding'))
|
|
return
|
|
|
|
return {'pod_binding': pod_binding}
|
|
|
|
@expose(generic=True, template='json')
|
|
def get_one(self, _id):
|
|
context = t_context.extract_context_from_environ()
|
|
|
|
if not t_context.is_admin_context(context):
|
|
pecan.abort(400, _('Admin role required to show bindings'))
|
|
return
|
|
|
|
try:
|
|
with context.session.begin():
|
|
pod_binding = core.get_resource(context,
|
|
models.PodBinding,
|
|
_id)
|
|
return {'pod_binding': pod_binding}
|
|
except t_exc.ResourceNotFound:
|
|
pecan.abort(404, _('Tenant pod binding not found'))
|
|
return
|
|
|
|
@expose(generic=True, template='json')
|
|
def get_all(self):
|
|
context = t_context.extract_context_from_environ()
|
|
|
|
if not t_context.is_admin_context(context):
|
|
pecan.abort(400, _('Admin role required to list bindings'))
|
|
return
|
|
|
|
try:
|
|
with context.session.begin():
|
|
pod_bindings = core.query_resource(context,
|
|
models.PodBinding,
|
|
[], [])
|
|
except Exception:
|
|
pecan.abort(500, _('Fail to list tenant pod bindings'))
|
|
return
|
|
|
|
return {'pod_bindings': pod_bindings}
|
|
|
|
@expose(generic=True, template='json')
|
|
def delete(self, _id):
|
|
context = t_context.extract_context_from_environ()
|
|
|
|
if not t_context.is_admin_context(context):
|
|
pecan.abort(400, _('Admin role required to delete bindings'))
|
|
return
|
|
|
|
try:
|
|
with context.session.begin():
|
|
core.delete_resource(context, models.PodBinding, _id)
|
|
pecan.response.status = 200
|
|
except t_exc.ResourceNotFound:
|
|
pecan.abort(404, _('Pod binding not found'))
|
|
return
|