300 lines
12 KiB
Python
300 lines
12 KiB
Python
# 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 rest
|
|
|
|
from oslo_log import log as logging
|
|
|
|
from tricircle.common import constants
|
|
import tricircle.common.context as t_context
|
|
import tricircle.common.exceptions as t_exceptions
|
|
from tricircle.common.i18n import _
|
|
from tricircle.common import policy
|
|
from tricircle.common import utils
|
|
|
|
from tricircle.db import api as db_api
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
SUPPORTED_FILTERS = ['id', 'top_id', 'bottom_id', 'pod_id', 'project_id',
|
|
'resource_type', 'created_at', 'updated_at']
|
|
|
|
|
|
class RoutingController(rest.RestController):
|
|
|
|
def __init__(self):
|
|
pass
|
|
|
|
@expose(generic=True, template='json')
|
|
def post(self, **kw):
|
|
context = t_context.extract_context_from_environ()
|
|
|
|
if not policy.enforce(context, policy.ADMIN_API_ROUTINGS_CREATE):
|
|
return utils.format_api_error(
|
|
403, _("Unauthorized to create resource routing"))
|
|
|
|
if 'routing' not in kw:
|
|
return utils.format_api_error(
|
|
400, _("Request body not found"))
|
|
|
|
routing = kw['routing']
|
|
|
|
for field in ('top_id', 'bottom_id', 'pod_id',
|
|
'project_id', 'resource_type'):
|
|
value = routing.get(field)
|
|
if value is None or len(value.strip()) == 0:
|
|
return utils.format_api_error(
|
|
400, _("Field %(field)s can not be empty") % {
|
|
'field': field})
|
|
|
|
# the resource type should be properly provisioned.
|
|
resource_type = routing.get('resource_type').strip()
|
|
if not constants.is_valid_resource_type(resource_type):
|
|
return utils.format_api_error(
|
|
400, _('There is no such resource type'))
|
|
|
|
try:
|
|
top_id = routing.get('top_id').strip()
|
|
bottom_id = routing.get('bottom_id').strip()
|
|
pod_id = routing.get('pod_id').strip()
|
|
project_id = routing.get('project_id').strip()
|
|
|
|
routing = db_api.create_resource_mapping(context, top_id,
|
|
bottom_id, pod_id,
|
|
project_id,
|
|
resource_type)
|
|
if not routing:
|
|
return utils.format_api_error(
|
|
409, _('Resource routing already exists'))
|
|
except Exception as e:
|
|
LOG.exception('Failed to create resource routing: '
|
|
'%(exception)s ', {'exception': e})
|
|
return utils.format_api_error(
|
|
500, _('Failed to create resource routing'))
|
|
|
|
return {'routing': routing}
|
|
|
|
def _get_filters(self, params):
|
|
"""Return a dictionary of query param filters from the request.
|
|
|
|
:param params: the URI params coming from the wsgi layer
|
|
:return: (flag, filters), flag indicates whether the filters are valid,
|
|
and the filters denote a list of key-value pairs.
|
|
"""
|
|
filters = {}
|
|
unsupported_filters = {}
|
|
for filter_name in params:
|
|
if filter_name in SUPPORTED_FILTERS:
|
|
# map filter name
|
|
filters[filter_name] = params.get(filter_name)
|
|
else:
|
|
unsupported_filters[filter_name] = params.get(filter_name)
|
|
|
|
if unsupported_filters:
|
|
return False, unsupported_filters
|
|
return True, filters
|
|
|
|
@expose(generic=True, template='json')
|
|
def get_all(self, **kwargs):
|
|
context = t_context.extract_context_from_environ()
|
|
|
|
if not policy.enforce(context, policy.ADMIN_API_ROUTINGS_LIST):
|
|
return utils.format_api_error(
|
|
403, _('Unauthorized to show all resource routings'))
|
|
|
|
# default value -1 means no pagination, then maximum pagination
|
|
# limit from configuration will be used.
|
|
_limit = kwargs.pop('limit', -1)
|
|
|
|
try:
|
|
limit = int(_limit)
|
|
limit = utils.get_pagination_limit(limit)
|
|
except ValueError as e:
|
|
LOG.exception('Failed to convert pagination limit to an integer: '
|
|
'%(exception)s ', {'exception': e})
|
|
msg = (_("Limit should be an integer or a valid literal "
|
|
"for int() rather than '%s'") % _limit)
|
|
return utils.format_api_error(400, msg)
|
|
|
|
marker = kwargs.pop('marker', None)
|
|
if marker is not None:
|
|
try:
|
|
marker = int(marker)
|
|
try:
|
|
# we throw an exception if a marker with
|
|
# invalid ID is specified.
|
|
db_api.get_resource_routing(context, marker)
|
|
except t_exceptions.ResourceNotFound:
|
|
return utils.format_api_error(
|
|
400, _('Marker %s is an invalid ID') % marker)
|
|
except ValueError as e:
|
|
LOG.exception('Failed to convert page marker to an integer: '
|
|
'%(exception)s ', {'exception': e})
|
|
msg = (_("Marker should be an integer or a valid literal "
|
|
"for int() rather than '%s'") % marker)
|
|
return utils.format_api_error(400, msg)
|
|
|
|
is_valid_filter, filters = self._get_filters(kwargs)
|
|
|
|
if not is_valid_filter:
|
|
msg = (_('Unsupported filter type: %(filters)s') % {
|
|
'filters': ', '.join(
|
|
[filter_name for filter_name in filters])
|
|
})
|
|
return utils.format_api_error(400, msg)
|
|
|
|
if 'id' in filters:
|
|
try:
|
|
# resource routing id is an integer.
|
|
filters['id'] = int(filters['id'])
|
|
except ValueError as e:
|
|
LOG.exception('Failed to convert routing id to an integer:'
|
|
' %(exception)s ', {'exception': e})
|
|
msg = (_("Id should be an integer or a valid literal "
|
|
"for int() rather than '%s'") % filters['id'])
|
|
return utils.format_api_error(400, msg)
|
|
|
|
# project ID from client should be equal to the one from
|
|
# context, since only the project ID in which the user
|
|
# is authorized will be used as the filter.
|
|
filters['project_id'] = context.project_id
|
|
expand_filters = [{'key': filter_name, 'comparator': 'eq',
|
|
'value': filters[filter_name]}
|
|
for filter_name in filters]
|
|
try:
|
|
routings = db_api.list_resource_routings(context, expand_filters,
|
|
limit, marker,
|
|
sorts=[('id', 'desc')])
|
|
links = []
|
|
if len(routings) >= limit:
|
|
marker = routings[-1]['id']
|
|
# if we reach the first element, then no elements in next page,
|
|
# so link to next page won't be provided.
|
|
if marker != 1:
|
|
base = constants.ROUTING_PATH
|
|
link = "%s?limit=%s&marker=%s" % (base, limit, marker)
|
|
|
|
links.append({"rel": "next",
|
|
"href": link})
|
|
|
|
result = {}
|
|
result["routings"] = routings
|
|
if links:
|
|
result["routings_links"] = links
|
|
return result
|
|
except Exception as e:
|
|
LOG.exception('Failed to show all resource routings: '
|
|
'%(exception)s ', {'exception': e})
|
|
return utils.format_api_error(
|
|
500, _('Failed to show all resource routings'))
|
|
|
|
@expose(generic=True, template='json')
|
|
def get_one(self, _id):
|
|
context = t_context.extract_context_from_environ()
|
|
|
|
if not policy.enforce(context, policy.ADMIN_API_ROUTINGS_SHOW):
|
|
return utils.format_api_error(
|
|
403, _('Unauthorized to show the resource routing'))
|
|
|
|
try:
|
|
return {'routing': db_api.get_resource_routing(context, _id)}
|
|
except t_exceptions.ResourceNotFound:
|
|
return utils.format_api_error(
|
|
404, _('Resource routing not found'))
|
|
|
|
@expose(generic=True, template='json')
|
|
def delete(self, _id):
|
|
context = t_context.extract_context_from_environ()
|
|
|
|
if not policy.enforce(context, policy.ADMIN_API_ROUTINGS_DELETE):
|
|
return utils.format_api_error(
|
|
403, _('Unauthorized to delete the resource routing'))
|
|
|
|
try:
|
|
db_api.get_resource_routing(context, _id)
|
|
except t_exceptions.ResourceNotFound:
|
|
return utils.format_api_error(404,
|
|
_('Resource routing not found'))
|
|
try:
|
|
db_api.delete_resource_routing(context, _id)
|
|
pecan.response.status = 200
|
|
return pecan.response
|
|
except Exception as e:
|
|
LOG.exception('Failed to delete the resource routing: '
|
|
'%(exception)s ', {'exception': e})
|
|
return utils.format_api_error(
|
|
500, _('Failed to delete the resource routing'))
|
|
|
|
@expose(generic=True, template='json')
|
|
def put(self, _id, **kw):
|
|
context = t_context.extract_context_from_environ()
|
|
|
|
if not policy.enforce(context, policy.ADMIN_API_ROUTINGS_PUT):
|
|
return utils.format_api_error(
|
|
403, _('Unauthorized to update resource routing'))
|
|
|
|
try:
|
|
db_api.get_resource_routing(context, _id)
|
|
except t_exceptions.ResourceNotFound:
|
|
return utils.format_api_error(404,
|
|
_('Resource routing not found'))
|
|
|
|
if 'routing' not in kw:
|
|
return utils.format_api_error(
|
|
400, _('Request body not found'))
|
|
|
|
update_dict = kw['routing']
|
|
|
|
# values to be updated should not be empty
|
|
for field in update_dict:
|
|
value = update_dict.get(field)
|
|
if value is None or len(value.strip()) == 0:
|
|
return utils.format_api_error(
|
|
400, _("Field %(field)s can not be empty") % {
|
|
'field': field})
|
|
|
|
# the resource type should be properly provisioned.
|
|
if 'resource_type' in update_dict:
|
|
if not constants.is_valid_resource_type(
|
|
update_dict['resource_type']):
|
|
return utils.format_api_error(
|
|
400, _('There is no such resource type'))
|
|
|
|
# the pod with new pod_id should exist in pod table
|
|
if 'pod_id' in update_dict:
|
|
new_pod_id = update_dict.get('pod_id')
|
|
try:
|
|
# find the pod through the pod_id and verify whether it exists
|
|
db_api.get_pod(context, new_pod_id)
|
|
except t_exceptions.ResourceNotFound:
|
|
return utils.format_api_error(
|
|
400, _("The pod %(new_pod_id)s doesn't"
|
|
" exist") % {'new_pod_id': new_pod_id})
|
|
except Exception as e:
|
|
LOG.exception('Failed to update resource routing: '
|
|
'%(exception)s ', {'exception': e})
|
|
return utils.format_api_error(
|
|
500, _('Failed to update resource routing'))
|
|
|
|
try:
|
|
routing_updated = db_api.update_resource_routing(
|
|
context, _id, update_dict)
|
|
return {'routing': routing_updated}
|
|
except Exception as e:
|
|
LOG.exception('Failed to update resource routing: '
|
|
'%(exception)s ', {'exception': e})
|
|
return utils.format_api_error(
|
|
500, _('Failed to update resource routing'))
|