Chaoyi Huang 81b45f2c1d Move statless design from experiment to master branch
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>
2016-01-14 12:56:57 +08:00

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