
Enable users to attach a volume to a server via Nova API gateway. Change-Id: Ia348a9adaad144bdd31c5d42553011af755b566f
348 lines
14 KiB
Python
348 lines
14 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.
|
|
|
|
|
|
from cinderclient import client as c_client
|
|
from cinderclient import exceptions as c_exceptions
|
|
import glanceclient as g_client
|
|
import glanceclient.exc as g_exceptions
|
|
from neutronclient.common import exceptions as q_exceptions
|
|
from neutronclient.neutron import client as q_client
|
|
from novaclient import client as n_client
|
|
from novaclient import exceptions as n_exceptions
|
|
from oslo_config import cfg
|
|
from oslo_log import log as logging
|
|
from requests import exceptions as r_exceptions
|
|
|
|
from tricircle.common import constants as cons
|
|
from tricircle.common import exceptions
|
|
|
|
|
|
client_opts = [
|
|
cfg.IntOpt('cinder_timeout',
|
|
default=60,
|
|
help='timeout for cinder client in seconds'),
|
|
cfg.IntOpt('glance_timeout',
|
|
default=60,
|
|
help='timeout for glance client in seconds'),
|
|
cfg.IntOpt('neutron_timeout',
|
|
default=60,
|
|
help='timeout for neutron client in seconds'),
|
|
cfg.IntOpt('nova_timeout',
|
|
default=60,
|
|
help='timeout for nova client in seconds'),
|
|
]
|
|
cfg.CONF.register_opts(client_opts, group='client')
|
|
|
|
|
|
LIST, CREATE, DELETE, GET, ACTION, UPDATE = 1, 2, 4, 8, 16, 32
|
|
operation_index_map = {'list': LIST, 'create': CREATE, 'delete': DELETE,
|
|
'get': GET, 'action': ACTION, 'update': UPDATE}
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
def _transform_filters(filters):
|
|
filter_dict = {}
|
|
for query_filter in filters:
|
|
# only eq filter supported at first
|
|
if query_filter['comparator'] != 'eq':
|
|
continue
|
|
key = query_filter['key']
|
|
value = query_filter['value']
|
|
filter_dict[key] = value
|
|
return filter_dict
|
|
|
|
|
|
class ResourceHandle(object):
|
|
def __init__(self, auth_url):
|
|
self.auth_url = auth_url
|
|
self.endpoint_url = None
|
|
|
|
def is_endpoint_url_set(self):
|
|
return self.endpoint_url is not None
|
|
|
|
def update_endpoint_url(self, url):
|
|
self.endpoint_url = url
|
|
|
|
|
|
class GlanceResourceHandle(ResourceHandle):
|
|
service_type = cons.ST_GLANCE
|
|
support_resource = {'image': LIST | GET}
|
|
|
|
def _get_client(self, cxt):
|
|
return g_client.Client('1',
|
|
token=cxt.auth_token,
|
|
auth_url=self.auth_url,
|
|
endpoint=self.endpoint_url,
|
|
timeout=cfg.CONF.client.glance_timeout)
|
|
|
|
def handle_list(self, cxt, resource, filters):
|
|
try:
|
|
client = self._get_client(cxt)
|
|
collection = '%ss' % resource
|
|
return [res.to_dict() for res in getattr(
|
|
client, collection).list(filters=_transform_filters(filters))]
|
|
except g_exceptions.InvalidEndpoint:
|
|
self.endpoint_url = None
|
|
raise exceptions.EndpointNotAvailable('glance',
|
|
client.http_client.endpoint)
|
|
|
|
def handle_get(self, cxt, resource, resource_id):
|
|
try:
|
|
client = self._get_client(cxt)
|
|
collection = '%ss' % resource
|
|
return getattr(client, collection).get(resource_id).to_dict()
|
|
except g_exceptions.InvalidEndpoint:
|
|
self.endpoint_url = None
|
|
raise exceptions.EndpointNotAvailable('glance',
|
|
client.http_client.endpoint)
|
|
except g_exceptions.HTTPNotFound:
|
|
LOG.debug("%(resource)s %(resource_id)s not found",
|
|
{'resource': resource, 'resource_id': resource_id})
|
|
|
|
|
|
class NeutronResourceHandle(ResourceHandle):
|
|
service_type = cons.ST_NEUTRON
|
|
support_resource = {'network': LIST | CREATE | DELETE | GET,
|
|
'subnet': LIST | CREATE | DELETE | GET,
|
|
'port': LIST | CREATE | DELETE | GET,
|
|
'router': LIST | CREATE | ACTION | UPDATE,
|
|
'security_group': LIST | CREATE | GET,
|
|
'security_group_rule': LIST | CREATE | DELETE,
|
|
'floatingip': LIST | CREATE}
|
|
|
|
def _get_client(self, cxt):
|
|
return q_client.Client('2.0',
|
|
token=cxt.auth_token,
|
|
auth_url=self.auth_url,
|
|
endpoint_url=self.endpoint_url,
|
|
timeout=cfg.CONF.client.neutron_timeout)
|
|
|
|
def handle_list(self, cxt, resource, filters):
|
|
try:
|
|
client = self._get_client(cxt)
|
|
collection = '%ss' % resource
|
|
search_opts = _transform_filters(filters)
|
|
return [res for res in getattr(
|
|
client, 'list_%s' % collection)(**search_opts)[collection]]
|
|
except q_exceptions.ConnectionFailed:
|
|
self.endpoint_url = None
|
|
raise exceptions.EndpointNotAvailable(
|
|
'neutron', client.httpclient.endpoint_url)
|
|
|
|
def handle_create(self, cxt, resource, *args, **kwargs):
|
|
try:
|
|
client = self._get_client(cxt)
|
|
ret = getattr(client, 'create_%s' % resource)(
|
|
*args, **kwargs)
|
|
if resource in ret:
|
|
return ret[resource]
|
|
else:
|
|
return ret['%ss' % resource]
|
|
except q_exceptions.ConnectionFailed:
|
|
self.endpoint_url = None
|
|
raise exceptions.EndpointNotAvailable(
|
|
'neutron', client.httpclient.endpoint_url)
|
|
|
|
def handle_update(self, cxt, resource, *args, **kwargs):
|
|
try:
|
|
client = self._get_client(cxt)
|
|
return getattr(client, 'update_%s' % resource)(
|
|
*args, **kwargs)[resource]
|
|
except q_exceptions.ConnectionFailed:
|
|
self.endpoint_url = None
|
|
raise exceptions.EndpointNotAvailable(
|
|
'neutron', client.httpclient.endpoint_url)
|
|
|
|
def handle_get(self, cxt, resource, resource_id):
|
|
try:
|
|
client = self._get_client(cxt)
|
|
return getattr(client, 'show_%s' % resource)(resource_id)[resource]
|
|
except q_exceptions.ConnectionFailed:
|
|
self.endpoint_url = None
|
|
raise exceptions.EndpointNotAvailable(
|
|
'neutron', client.httpclient.endpoint_url)
|
|
except q_exceptions.NotFound:
|
|
LOG.debug("%(resource)s %(resource_id)s not found",
|
|
{'resource': resource, 'resource_id': resource_id})
|
|
|
|
def handle_delete(self, cxt, resource, resource_id):
|
|
try:
|
|
client = self._get_client(cxt)
|
|
return getattr(client, 'delete_%s' % resource)(resource_id)
|
|
except q_exceptions.ConnectionFailed:
|
|
self.endpoint_url = None
|
|
raise exceptions.EndpointNotAvailable(
|
|
'neutron', client.httpclient.endpoint_url)
|
|
except q_exceptions.NotFound:
|
|
LOG.debug("Delete %(resource)s %(resource_id)s which not found",
|
|
{'resource': resource, 'resource_id': resource_id})
|
|
|
|
def handle_action(self, cxt, resource, action, *args, **kwargs):
|
|
try:
|
|
client = self._get_client(cxt)
|
|
return getattr(client, '%s_%s' % (action, resource))(*args,
|
|
**kwargs)
|
|
except q_exceptions.ConnectionFailed:
|
|
self.endpoint_url = None
|
|
raise exceptions.EndpointNotAvailable(
|
|
'neutron', client.httpclient.endpoint_url)
|
|
|
|
|
|
class NovaResourceHandle(ResourceHandle):
|
|
service_type = cons.ST_NOVA
|
|
support_resource = {'flavor': LIST,
|
|
'server': LIST | CREATE | GET,
|
|
'aggregate': LIST | CREATE | DELETE | ACTION,
|
|
'server_volume': ACTION}
|
|
|
|
def _get_client(self, cxt):
|
|
cli = n_client.Client('2',
|
|
auth_token=cxt.auth_token,
|
|
auth_url=self.auth_url,
|
|
timeout=cfg.CONF.client.nova_timeout)
|
|
cli.set_management_url(
|
|
self.endpoint_url.replace('$(tenant_id)s', cxt.tenant))
|
|
return cli
|
|
|
|
def _adapt_resource(self, resource):
|
|
if resource == 'server_volume':
|
|
return 'volume'
|
|
else:
|
|
return resource
|
|
|
|
def handle_list(self, cxt, resource, filters):
|
|
try:
|
|
resource = self._adapt_resource(resource)
|
|
client = self._get_client(cxt)
|
|
collection = '%ss' % resource
|
|
# only server list supports filter
|
|
if resource == 'server':
|
|
search_opts = _transform_filters(filters)
|
|
return [res.to_dict() for res in getattr(
|
|
client, collection).list(search_opts=search_opts)]
|
|
else:
|
|
return [res.to_dict() for res in getattr(client,
|
|
collection).list()]
|
|
except r_exceptions.ConnectTimeout:
|
|
self.endpoint_url = None
|
|
raise exceptions.EndpointNotAvailable('nova',
|
|
client.client.management_url)
|
|
|
|
def handle_create(self, cxt, resource, *args, **kwargs):
|
|
try:
|
|
resource = self._adapt_resource(resource)
|
|
client = self._get_client(cxt)
|
|
collection = '%ss' % resource
|
|
return getattr(client, collection).create(
|
|
*args, **kwargs).to_dict()
|
|
except r_exceptions.ConnectTimeout:
|
|
self.endpoint_url = None
|
|
raise exceptions.EndpointNotAvailable('nova',
|
|
client.client.management_url)
|
|
|
|
def handle_get(self, cxt, resource, resource_id):
|
|
try:
|
|
resource = self._adapt_resource(resource)
|
|
client = self._get_client(cxt)
|
|
collection = '%ss' % resource
|
|
return getattr(client, collection).get(resource_id).to_dict()
|
|
except r_exceptions.ConnectTimeout:
|
|
self.endpoint_url = None
|
|
raise exceptions.EndpointNotAvailable('nova',
|
|
client.client.management_url)
|
|
except n_exceptions.NotFound:
|
|
LOG.debug("%(resource)s %(resource_id)s not found",
|
|
{'resource': resource, 'resource_id': resource_id})
|
|
|
|
def handle_delete(self, cxt, resource, resource_id):
|
|
try:
|
|
resource = self._adapt_resource(resource)
|
|
client = self._get_client(cxt)
|
|
collection = '%ss' % resource
|
|
return getattr(client, collection).delete(resource_id)
|
|
except r_exceptions.ConnectTimeout:
|
|
self.endpoint_url = None
|
|
raise exceptions.EndpointNotAvailable('nova',
|
|
client.client.management_url)
|
|
except n_exceptions.NotFound:
|
|
LOG.debug("Delete %(resource)s %(resource_id)s which not found",
|
|
{'resource': resource, 'resource_id': resource_id})
|
|
|
|
def handle_action(self, cxt, resource, action, *args, **kwargs):
|
|
try:
|
|
resource = self._adapt_resource(resource)
|
|
client = self._get_client(cxt)
|
|
collection = '%ss' % resource
|
|
resource_manager = getattr(client, collection)
|
|
return getattr(resource_manager, action)(*args, **kwargs)
|
|
except r_exceptions.ConnectTimeout:
|
|
self.endpoint_url = None
|
|
raise exceptions.EndpointNotAvailable('nova',
|
|
client.client.management_url)
|
|
|
|
|
|
class CinderResourceHandle(ResourceHandle):
|
|
service_type = cons.ST_CINDER
|
|
support_resource = {'volume': GET | ACTION,
|
|
'transfer': CREATE | ACTION}
|
|
|
|
def _get_client(self, cxt):
|
|
cli = c_client.Client('2',
|
|
auth_token=cxt.auth_token,
|
|
auth_url=self.auth_url,
|
|
timeout=cfg.CONF.client.cinder_timeout)
|
|
cli.set_management_url(
|
|
self.endpoint_url.replace('$(tenant_id)s', cxt.tenant))
|
|
return cli
|
|
|
|
def handle_get(self, cxt, resource, resource_id):
|
|
try:
|
|
client = self._get_client(cxt)
|
|
collection = '%ss' % resource
|
|
res = getattr(client, collection).get(resource_id)
|
|
info = {}
|
|
info.update(res._info)
|
|
return info
|
|
except r_exceptions.ConnectTimeout:
|
|
self.endpoint_url = None
|
|
raise exceptions.EndpointNotAvailable('cinder',
|
|
client.client.management_url)
|
|
|
|
def handle_delete(self, cxt, resource, resource_id):
|
|
try:
|
|
client = self._get_client(cxt)
|
|
collection = '%ss' % resource
|
|
return getattr(client, collection).delete(resource_id)
|
|
except r_exceptions.ConnectTimeout:
|
|
self.endpoint_url = None
|
|
raise exceptions.EndpointNotAvailable('cinder',
|
|
client.client.management_url)
|
|
except c_exceptions.NotFound:
|
|
LOG.debug("Delete %(resource)s %(resource_id)s which not found",
|
|
{'resource': resource, 'resource_id': resource_id})
|
|
|
|
def handle_action(self, cxt, resource, action, *args, **kwargs):
|
|
try:
|
|
client = self._get_client(cxt)
|
|
collection = '%ss' % resource
|
|
resource_manager = getattr(client, collection)
|
|
getattr(resource_manager, action)(*args, **kwargs)
|
|
except r_exceptions.ConnectTimeout:
|
|
self.endpoint_url = None
|
|
raise exceptions.EndpointNotAvailable('cinder',
|
|
client.client.management_url)
|