Merge "Implement volume metadata basic operation"
This commit is contained in:
commit
122d2b1866
@ -18,8 +18,10 @@ import pecan
|
||||
import oslo_log.log as logging
|
||||
|
||||
from tricircle.cinder_apigw.controllers import volume
|
||||
from tricircle.cinder_apigw.controllers import volume_metadata
|
||||
from tricircle.cinder_apigw.controllers import volume_type
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -69,6 +71,10 @@ class V2Controller(object):
|
||||
'types': volume_type.VolumeTypeController
|
||||
}
|
||||
|
||||
self.volumes_sub_controller = {
|
||||
'metadata': volume_metadata.VolumeMetaDataController,
|
||||
}
|
||||
|
||||
@pecan.expose()
|
||||
def _lookup(self, tenant_id, *remainder):
|
||||
if not remainder:
|
||||
@ -78,6 +84,14 @@ class V2Controller(object):
|
||||
if resource not in self.resource_controller:
|
||||
pecan.abort(404)
|
||||
return
|
||||
if resource == 'volumes' and len(remainder) >= 3:
|
||||
volume_id = remainder[1]
|
||||
sub_resource = remainder[2]
|
||||
if sub_resource not in self.volumes_sub_controller:
|
||||
pecan.abort(404)
|
||||
return
|
||||
return self.volumes_sub_controller[sub_resource](
|
||||
tenant_id, volume_id), remainder[3:]
|
||||
return self.resource_controller[resource](tenant_id), remainder[1:]
|
||||
|
||||
@pecan.expose(generic=True, template='json')
|
||||
|
@ -85,15 +85,15 @@ class VolumeController(rest.RestController):
|
||||
return utils.format_cinder_error(
|
||||
500, _('Bottom Pod endpoint incorrect'))
|
||||
|
||||
b_headers = self._convert_header(t_release,
|
||||
b_release,
|
||||
request.headers)
|
||||
b_headers = hclient.convert_header(t_release,
|
||||
b_release,
|
||||
request.headers)
|
||||
|
||||
t_vol = kw['volume']
|
||||
|
||||
# add or remove key-value in the request for diff. version
|
||||
b_vol_req = self._convert_object(t_release, b_release, t_vol,
|
||||
res_type=cons.RT_VOLUME)
|
||||
b_vol_req = hclient.convert_object(t_release, b_release, t_vol,
|
||||
res_type=cons.RT_VOLUME)
|
||||
|
||||
# convert az to the configured one
|
||||
# remove the AZ parameter to bottom request for default one
|
||||
@ -142,9 +142,9 @@ class VolumeController(rest.RestController):
|
||||
return utils.format_cinder_error(
|
||||
500, _('Failed to create volume resource routing'))
|
||||
|
||||
ret_vol = self._convert_object(b_release, t_release,
|
||||
b_vol_ret,
|
||||
res_type=cons.RT_VOLUME)
|
||||
ret_vol = hclient.convert_object(b_release, t_release,
|
||||
b_vol_ret,
|
||||
res_type=cons.RT_VOLUME)
|
||||
|
||||
ret_vol['availability_zone'] = pod['az_name']
|
||||
|
||||
@ -163,11 +163,12 @@ class VolumeController(rest.RestController):
|
||||
t_release = cons.R_MITAKA
|
||||
b_release = cons.R_MITAKA
|
||||
|
||||
b_headers = self._convert_header(t_release,
|
||||
b_release,
|
||||
request.headers)
|
||||
b_headers = hclient.convert_header(t_release,
|
||||
b_release,
|
||||
request.headers)
|
||||
|
||||
s_ctx = self._get_res_routing_ref(context, _id, request.url)
|
||||
s_ctx = hclient.get_res_routing_ref(context, _id, request.url,
|
||||
cons.ST_CINDER)
|
||||
if not s_ctx:
|
||||
return utils.format_cinder_error(
|
||||
404, _('Volume %s could not be found.') % _id)
|
||||
@ -188,11 +189,11 @@ class VolumeController(rest.RestController):
|
||||
if b_status == 200:
|
||||
if b_ret_body.get('volume') is not None:
|
||||
b_vol_ret = b_ret_body['volume']
|
||||
ret_vol = self._convert_object(b_release, t_release,
|
||||
b_vol_ret,
|
||||
res_type=cons.RT_VOLUME)
|
||||
ret_vol = hclient.convert_object(b_release, t_release,
|
||||
b_vol_ret,
|
||||
res_type=cons.RT_VOLUME)
|
||||
|
||||
pod = self._get_pod_by_top_id(context, _id)
|
||||
pod = utils.get_pod_by_top_id(context, _id)
|
||||
if pod:
|
||||
ret_vol['availability_zone'] = pod['az_name']
|
||||
|
||||
@ -288,7 +289,8 @@ class VolumeController(rest.RestController):
|
||||
t_release = cons.R_MITAKA
|
||||
b_release = cons.R_MITAKA
|
||||
|
||||
s_ctx = self._get_res_routing_ref(context, _id, request.url)
|
||||
s_ctx = hclient.get_res_routing_ref(context, _id, request.url,
|
||||
cons.ST_CINDER)
|
||||
if not s_ctx:
|
||||
return utils.format_cinder_error(
|
||||
404, _('Volume %s could not be found.') % _id)
|
||||
@ -297,15 +299,15 @@ class VolumeController(rest.RestController):
|
||||
return utils.format_cinder_error(
|
||||
404, _('Bottom Pod endpoint incorrect'))
|
||||
|
||||
b_headers = self._convert_header(t_release,
|
||||
b_release,
|
||||
request.headers)
|
||||
b_headers = hclient.convert_header(t_release,
|
||||
b_release,
|
||||
request.headers)
|
||||
|
||||
t_vol = kw['volume']
|
||||
|
||||
# add or remove key-value in the request for diff. version
|
||||
b_vol_req = self._convert_object(t_release, b_release, t_vol,
|
||||
res_type=cons.RT_VOLUME)
|
||||
b_vol_req = hclient.convert_object(t_release, b_release, t_vol,
|
||||
res_type=cons.RT_VOLUME)
|
||||
|
||||
b_body = jsonutils.dumps({'volume': b_vol_req})
|
||||
|
||||
@ -321,11 +323,11 @@ class VolumeController(rest.RestController):
|
||||
if b_status == 200:
|
||||
if b_ret_body.get('volume') is not None:
|
||||
b_vol_ret = b_ret_body['volume']
|
||||
ret_vol = self._convert_object(b_release, t_release,
|
||||
b_vol_ret,
|
||||
res_type=cons.RT_VOLUME)
|
||||
ret_vol = hclient.convert_object(b_release, t_release,
|
||||
b_vol_ret,
|
||||
res_type=cons.RT_VOLUME)
|
||||
|
||||
pod = self._get_pod_by_top_id(context, _id)
|
||||
pod = utils.get_pod_by_top_id(context, _id)
|
||||
if pod:
|
||||
ret_vol['availability_zone'] = pod['az_name']
|
||||
|
||||
@ -351,7 +353,8 @@ class VolumeController(rest.RestController):
|
||||
t_release = cons.R_MITAKA
|
||||
b_release = cons.R_MITAKA
|
||||
|
||||
s_ctx = self._get_res_routing_ref(context, _id, request.url)
|
||||
s_ctx = hclient.get_res_routing_ref(context, _id, request.url,
|
||||
cons.ST_CINDER)
|
||||
if not s_ctx:
|
||||
return utils.format_cinder_error(
|
||||
404, _('Volume %s could not be found.') % _id)
|
||||
@ -360,9 +363,9 @@ class VolumeController(rest.RestController):
|
||||
return utils.format_cinder_error(
|
||||
404, _('Bottom Pod endpoint incorrect'))
|
||||
|
||||
b_headers = self._convert_header(t_release,
|
||||
b_release,
|
||||
request.headers)
|
||||
b_headers = hclient.convert_header(t_release,
|
||||
b_release,
|
||||
request.headers)
|
||||
|
||||
resp = hclient.forward_req(context, 'DELETE',
|
||||
b_headers,
|
||||
@ -373,49 +376,5 @@ class VolumeController(rest.RestController):
|
||||
|
||||
# don't remove the resource routing for delete is async. operation
|
||||
# remove the routing when query is executed but not find
|
||||
|
||||
# No content in the resp actually
|
||||
return response
|
||||
|
||||
# move to common function if other modules need
|
||||
def _get_res_routing_ref(self, context, _id, t_url):
|
||||
|
||||
pod = self._get_pod_by_top_id(context, _id)
|
||||
|
||||
if not pod:
|
||||
return None
|
||||
|
||||
pod_name = pod['pod_name']
|
||||
|
||||
s_ctx = hclient.get_pod_service_ctx(
|
||||
context,
|
||||
t_url,
|
||||
pod_name,
|
||||
s_type=cons.ST_CINDER)
|
||||
|
||||
if s_ctx['b_url'] == '':
|
||||
LOG.error(_LE("bottom pod endpoint incorrect %s") %
|
||||
pod_name)
|
||||
|
||||
return s_ctx
|
||||
|
||||
# move to common function if other modules need
|
||||
def _get_pod_by_top_id(self, context, _id):
|
||||
|
||||
mappings = db_api.get_bottom_mappings_by_top_id(
|
||||
context, _id,
|
||||
cons.RT_VOLUME)
|
||||
|
||||
if not mappings or len(mappings) != 1:
|
||||
return None
|
||||
|
||||
return mappings[0][0]
|
||||
|
||||
def _convert_header(self, from_release, to_release, header):
|
||||
|
||||
return header
|
||||
|
||||
def _convert_object(self, from_release, to_release, res_object,
|
||||
res_type=cons.RT_VOLUME):
|
||||
|
||||
return res_object
|
||||
|
287
tricircle/cinder_apigw/controllers/volume_metadata.py
Normal file
287
tricircle/cinder_apigw/controllers/volume_metadata.py
Normal file
@ -0,0 +1,287 @@
|
||||
# Copyright 2016 OpenStack Foundation.
|
||||
# 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.
|
||||
|
||||
|
||||
from pecan import expose
|
||||
from pecan import request
|
||||
from pecan import response
|
||||
from pecan import rest
|
||||
|
||||
from oslo_log import log as logging
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
from tricircle.common import constants as cons
|
||||
import tricircle.common.context as t_context
|
||||
from tricircle.common import httpclient as hclient
|
||||
from tricircle.common.i18n import _
|
||||
from tricircle.common.i18n import _LE
|
||||
from tricircle.common import utils
|
||||
import tricircle.db.api as db_api
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class VolumeMetaDataController(rest.RestController):
|
||||
|
||||
def __init__(self, tenant_id, volume_id):
|
||||
self.tenant_id = tenant_id
|
||||
self.volume_id = volume_id
|
||||
|
||||
@expose(generic=True, template='json')
|
||||
def post(self, **kw):
|
||||
"""Create volume metadata associated with a volume.
|
||||
|
||||
:param kw: dictionary of values to be created
|
||||
:returns: created volume metadata
|
||||
"""
|
||||
context = t_context.extract_context_from_environ()
|
||||
|
||||
if 'metadata' not in kw:
|
||||
return utils.format_cinder_error(
|
||||
400, _("Missing required element 'metadata' in "
|
||||
"request body."))
|
||||
|
||||
try:
|
||||
pod = utils.get_pod_by_top_id(context, self.volume_id)
|
||||
if pod is None:
|
||||
return utils.format_cinder_error(
|
||||
404, _('Volume %(volume_id)s could not be found.') % {
|
||||
'volume_id': self.volume_id
|
||||
})
|
||||
|
||||
t_pod = db_api.get_top_pod(context)
|
||||
if not t_pod:
|
||||
LOG.error(_LE("Top Pod not configured"))
|
||||
return utils.format_cinder_error(
|
||||
500, _('Top Pod not configured'))
|
||||
except Exception as e:
|
||||
LOG.exception(_LE('Fail to create metadata for a volume:'
|
||||
'%(volume_id)s'
|
||||
'%(exception)s'),
|
||||
{'volume_id': self.volume_id,
|
||||
'exception': e})
|
||||
return utils.format_cinder_error(500, _('Fail to create metadata'))
|
||||
|
||||
t_release = cons.R_MITAKA
|
||||
b_release = cons.R_MITAKA
|
||||
|
||||
s_ctx = hclient.get_pod_service_ctx(
|
||||
context,
|
||||
request.url,
|
||||
pod['pod_name'],
|
||||
s_type=cons.ST_CINDER)
|
||||
|
||||
if s_ctx['b_url'] == '':
|
||||
LOG.error(_LE("Bottom pod endpoint incorrect %s") %
|
||||
pod['pod_name'])
|
||||
return utils.format_cinder_error(
|
||||
500, _('Bottom pod endpoint incorrect'))
|
||||
|
||||
b_headers = hclient.convert_header(t_release, b_release,
|
||||
request.headers)
|
||||
|
||||
t_metadata = kw['metadata']
|
||||
|
||||
# add or remove key-value in the request for diff. version
|
||||
b_vol_req = hclient.convert_object(t_release, b_release, t_metadata,
|
||||
res_type=cons.RT_VOl_METADATA)
|
||||
|
||||
b_body = jsonutils.dumps({'metadata': b_vol_req})
|
||||
|
||||
resp = hclient.forward_req(
|
||||
context,
|
||||
'POST',
|
||||
b_headers,
|
||||
s_ctx['b_url'],
|
||||
b_body)
|
||||
b_status = resp.status_code
|
||||
b_body_ret = jsonutils.loads(resp.content)
|
||||
|
||||
# convert response from the bottom pod
|
||||
# for different version.
|
||||
response.status = b_status
|
||||
if b_status == 200:
|
||||
if b_body_ret.get('metadata') is not None:
|
||||
b_metadata_ret = b_body_ret['metadata']
|
||||
|
||||
vol_ret = hclient.convert_object(b_release, t_release,
|
||||
b_metadata_ret,
|
||||
res_type=cons.
|
||||
RT_VOl_METADATA)
|
||||
|
||||
return {'metadata': vol_ret}
|
||||
|
||||
return b_body_ret
|
||||
|
||||
@expose(generic=True, template='json')
|
||||
def get_one(self):
|
||||
"""Get all metadata associated with a volume."""
|
||||
context = t_context.extract_context_from_environ()
|
||||
|
||||
t_release = cons.R_MITAKA
|
||||
b_release = cons.R_MITAKA
|
||||
|
||||
b_headers = hclient.convert_header(t_release,
|
||||
b_release,
|
||||
request.headers)
|
||||
|
||||
try:
|
||||
s_ctx = hclient.get_res_routing_ref(context, self.volume_id,
|
||||
request.url, cons.ST_CINDER)
|
||||
if not s_ctx:
|
||||
return utils.format_cinder_error(
|
||||
500, _('Fail to find resource'))
|
||||
except Exception as e:
|
||||
LOG.exception(_LE('Fail to get metadata for a volume:'
|
||||
'%(volume_id)s'
|
||||
'%(exception)s'),
|
||||
{'volume_id': self.volume_id,
|
||||
'exception': e})
|
||||
return utils.format_cinder_error(500, _('Fail to get metadata'))
|
||||
|
||||
if s_ctx['b_url'] == '':
|
||||
return utils.format_cinder_error(
|
||||
500, _('Bottom pod endpoint incorrect'))
|
||||
|
||||
resp = hclient.forward_req(context, 'GET',
|
||||
b_headers,
|
||||
s_ctx['b_url'],
|
||||
request.body)
|
||||
|
||||
b_body_ret = jsonutils.loads(resp.content)
|
||||
|
||||
b_status = resp.status_code
|
||||
response.status = b_status
|
||||
if b_status == 200:
|
||||
if b_body_ret.get('metadata') is not None:
|
||||
b_metadata_ret = b_body_ret['metadata']
|
||||
vol_ret = hclient.convert_object(b_release, t_release,
|
||||
b_metadata_ret,
|
||||
res_type=cons.
|
||||
RT_VOl_METADATA)
|
||||
return {'metadata': vol_ret}
|
||||
|
||||
return b_body_ret
|
||||
|
||||
@expose(generic=True, template='json')
|
||||
def put(self, **kw):
|
||||
"""Update volume metadata.
|
||||
|
||||
:param kw: dictionary of values to be updated
|
||||
:returns: updated volume type
|
||||
"""
|
||||
context = t_context.extract_context_from_environ()
|
||||
|
||||
if 'metadata' not in kw:
|
||||
return utils.format_cinder_error(
|
||||
400, _("Missing required element 'metadata' in "
|
||||
"request body."))
|
||||
|
||||
t_release = cons.R_MITAKA
|
||||
b_release = cons.R_MITAKA
|
||||
|
||||
try:
|
||||
s_ctx = hclient.get_res_routing_ref(context, self.volume_id,
|
||||
request.url, cons.ST_CINDER)
|
||||
if not s_ctx:
|
||||
return utils.format_cinder_error(
|
||||
404, _('Resource not found'))
|
||||
except Exception as e:
|
||||
LOG.exception(_LE('Fail to update metadata for a volume: '
|
||||
'%(volume_id)s'
|
||||
'%(exception)s'),
|
||||
{'volume_id': self.volume_id,
|
||||
'exception': e})
|
||||
return utils.format_cinder_error(
|
||||
500, _('Fail to update metadata'))
|
||||
|
||||
if s_ctx['b_url'] == '':
|
||||
return utils.format_cinder_error(
|
||||
500, _('Bottom pod endpoint incorrect'))
|
||||
|
||||
b_headers = hclient.convert_header(t_release,
|
||||
b_release,
|
||||
request.headers)
|
||||
|
||||
t_metadata = kw['metadata']
|
||||
|
||||
# add or remove key/value in the request for diff. version
|
||||
b_vol_req = hclient.convert_object(t_release, b_release, t_metadata,
|
||||
res_type=cons.RT_VOl_METADATA)
|
||||
|
||||
b_body = jsonutils.dumps({'metadata': b_vol_req})
|
||||
|
||||
resp = hclient.forward_req(context, 'PUT',
|
||||
b_headers,
|
||||
s_ctx['b_url'],
|
||||
b_body)
|
||||
|
||||
b_status = resp.status_code
|
||||
b_body_ret = jsonutils.loads(resp.content)
|
||||
response.status = b_status
|
||||
|
||||
if b_status == 200:
|
||||
if b_body_ret.get('metadata') is not None:
|
||||
b_metadata_ret = b_body_ret['metadata']
|
||||
vol_ret = hclient.convert_object(b_release, t_release,
|
||||
b_metadata_ret,
|
||||
res_type=cons.
|
||||
RT_VOl_METADATA)
|
||||
return {'metadata': vol_ret}
|
||||
|
||||
return b_body_ret
|
||||
|
||||
@expose(generic=True, template='json')
|
||||
def delete(self, key):
|
||||
"""Delete the given metadata item from a volume."""
|
||||
context = t_context.extract_context_from_environ()
|
||||
|
||||
t_release = cons.R_MITAKA
|
||||
b_release = cons.R_MITAKA
|
||||
|
||||
try:
|
||||
s_ctx = hclient.get_res_routing_ref(context, self.volume_id,
|
||||
request.url, cons.ST_CINDER)
|
||||
if not s_ctx:
|
||||
return utils.format_cinder_error(
|
||||
404, _('Fail to find resource'))
|
||||
except Exception as e:
|
||||
LOG.exception(_LE('Fail to delete metadata from a volume: '
|
||||
'%(volume_id)s'
|
||||
'%(exception)s'),
|
||||
{'volume_id': self.volume_id,
|
||||
'exception': e})
|
||||
return utils.format_cinder_error(
|
||||
500, _('Fail to delete metadata'))
|
||||
|
||||
if s_ctx['b_url'] == '':
|
||||
return utils.format_cinder_error(
|
||||
500, _('Bottom pod endpoint incorrect'))
|
||||
|
||||
b_headers = hclient.convert_header(t_release,
|
||||
b_release,
|
||||
request.headers)
|
||||
|
||||
resp = hclient.forward_req(context, 'DELETE',
|
||||
b_headers,
|
||||
s_ctx['b_url'],
|
||||
request.body)
|
||||
|
||||
response.status = resp.status_code
|
||||
|
||||
# don't remove the resource routing for delete is async. operation
|
||||
# remove the routing when query is executed but not found
|
||||
# No content in the resp actually
|
||||
return response
|
@ -26,6 +26,7 @@ ST_GLANCE = 'glance'
|
||||
# resource_type
|
||||
RT_SERVER = 'server'
|
||||
RT_VOLUME = 'volume'
|
||||
RT_VOl_METADATA = 'volume_metadata'
|
||||
RT_BACKUP = 'backup'
|
||||
RT_SNAPSHOT = 'snapshot'
|
||||
RT_NETWORK = 'network'
|
||||
|
@ -19,11 +19,18 @@ import urlparse
|
||||
from requests import Request
|
||||
from requests import Session
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from tricircle.common import client
|
||||
from tricircle.common import constants as cons
|
||||
from tricircle.common.i18n import _LE
|
||||
from tricircle.common import utils
|
||||
from tricircle.db import api as db_api
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# the url could be endpoint registered in the keystone
|
||||
# or url sent to tricircle service, which is stored in
|
||||
# pecan.request.url
|
||||
@ -147,3 +154,37 @@ def forward_req(context, action, b_headers, b_url, b_body):
|
||||
timeout=60)
|
||||
|
||||
return resp
|
||||
|
||||
|
||||
def get_res_routing_ref(context, _id, t_url, s_type):
|
||||
"""Get the service context according to resource routing.
|
||||
|
||||
:param _id: the top id of resource
|
||||
:param t_url: request url
|
||||
:param s_type: service type
|
||||
:returns: service context
|
||||
"""
|
||||
pod = utils.get_pod_by_top_id(context, _id)
|
||||
|
||||
if not pod:
|
||||
return None
|
||||
|
||||
pod_name = pod['pod_name']
|
||||
|
||||
s_ctx = get_pod_service_ctx(context, t_url, pod_name,
|
||||
s_type=s_type)
|
||||
|
||||
if s_ctx['b_url'] == '':
|
||||
LOG.error(_LE("bottom pod endpoint incorrect %s") %
|
||||
pod_name)
|
||||
|
||||
return s_ctx
|
||||
|
||||
|
||||
def convert_header(from_release, to_release, header):
|
||||
return header
|
||||
|
||||
|
||||
def convert_object(from_release, to_release, res_object,
|
||||
res_type=cons.RT_VOLUME):
|
||||
return res_object
|
||||
|
@ -17,8 +17,14 @@ import six
|
||||
|
||||
import pecan
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from tricircle.common import constants as cons
|
||||
import tricircle.common.exceptions as t_exceptions
|
||||
from tricircle.common.i18n import _
|
||||
import tricircle.db.api as db_api
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_import_path(cls):
|
||||
@ -143,3 +149,19 @@ def format_nova_error(code, message, error_type=None):
|
||||
|
||||
def format_cinder_error(code, message, error_type=None):
|
||||
return format_error(code, message, error_type)
|
||||
|
||||
|
||||
def get_pod_by_top_id(context, _id):
|
||||
"""Get pod resource from pod table .
|
||||
|
||||
:param _id: the top id of resource
|
||||
:returns: pod resource
|
||||
"""
|
||||
mappings = db_api.get_bottom_mappings_by_top_id(
|
||||
context, _id,
|
||||
cons.RT_VOLUME)
|
||||
|
||||
if not mappings or len(mappings) != 1:
|
||||
return None
|
||||
|
||||
return mappings[0][0]
|
||||
|
Loading…
x
Reference in New Issue
Block a user