Allow Cinder to call Nova client

This code allows Cinder to call Nova client functions. This will be used
for online migration and guest-assisted snapshots, which are both in
progress.

Change-Id: I9c94917bddcf250b880ca730df463a8402fe6b1d
This commit is contained in:
Avishay Traeger 2013-08-11 19:40:10 +03:00
parent 08b6480ca8
commit dee13e6a36
5 changed files with 192 additions and 2 deletions

View File

@ -28,6 +28,7 @@ import webob.exc
from cinder.api.openstack import wsgi
from cinder import context
from cinder.openstack.common import jsonutils
from cinder.openstack.common import log as logging
from cinder import wsgi as base_wsgi
@ -98,6 +99,16 @@ class CinderKeystoneContext(base_wsgi.Middleware):
# Build a context, including the auth_token...
remote_address = req.remote_addr
service_catalog = None
if req.headers.get('X_SERVICE_CATALOG') is not None:
try:
catalog_header = req.headers.get('X_SERVICE_CATALOG')
service_catalog = jsonutils.loads(catalog_header)
except ValueError:
raise webob.exc.HTTPInternalServerError(
_('Invalid service catalog json.'))
if CONF.use_forwarded_for:
remote_address = req.headers.get('X-Forwarded-For', remote_address)
ctx = context.RequestContext(user_id,
@ -105,7 +116,8 @@ class CinderKeystoneContext(base_wsgi.Middleware):
project_name=project_name,
roles=roles,
auth_token=auth_token,
remote_address=remote_address)
remote_address=remote_address,
service_catalog=service_catalog)
req.environ['cinder.context'] = ctx
return self.application

View File

@ -0,0 +1,34 @@
# Copyright 2013 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.
import oslo.config.cfg
import cinder.openstack.common.importutils
_compute_opts = [
oslo.config.cfg.StrOpt('compute_api_class',
default='cinder.compute.nova.API',
help='The full class name of the '
'compute API class to use'),
]
oslo.config.cfg.CONF.register_opts(_compute_opts)
def API():
importutils = cinder.openstack.common.importutils
compute_api_class = oslo.config.cfg.CONF.compute_api_class
cls = importutils.import_class(compute_api_class)
return cls()

99
cinder/compute/nova.py Normal file
View File

@ -0,0 +1,99 @@
# Copyright 2013 IBM Corp.
#
# 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.
"""
Handles all requests to Nova.
"""
from novaclient import service_catalog
from novaclient.v1_1 import client as nova_client
from oslo.config import cfg
from cinder.db import base
from cinder.openstack.common import log as logging
nova_opts = [
cfg.StrOpt('nova_catalog_info',
default='compute:nova:publicURL',
help='Info to match when looking for nova in the service '
'catalog. Format is : separated values of the form: '
'<service_type>:<service_name>:<endpoint_type>'),
cfg.StrOpt('nova_endpoint_template',
default=None,
help='Override service catalog lookup with template for nova '
'endpoint e.g. http://localhost:8774/v2/%(tenant_id)s'),
cfg.StrOpt('os_region_name',
default=None,
help='region name of this node'),
cfg.StrOpt('nova_ca_certificates_file',
default=None,
help='Location of ca certicates file to use for nova client '
'requests.'),
cfg.BoolOpt('nova_api_insecure',
default=False,
help='Allow to perform insecure SSL requests to nova'),
]
CONF = cfg.CONF
CONF.register_opts(nova_opts)
LOG = logging.getLogger(__name__)
def novaclient(context):
# FIXME: the novaclient ServiceCatalog object is mis-named.
# It actually contains the entire access blob.
# Only needed parts of the service catalog are passed in, see
# nova/context.py.
compat_catalog = {
'access': {'serviceCatalog': context.service_catalog or []}
}
sc = service_catalog.ServiceCatalog(compat_catalog)
if CONF.nova_endpoint_template:
url = CONF.nova_endpoint_template % context.to_dict()
else:
info = CONF.nova_catalog_info
service_type, service_name, endpoint_type = info.split(':')
# extract the region if set in configuration
if CONF.os_region_name:
attr = 'region'
filter_value = CONF.os_region_name
else:
attr = None
filter_value = None
url = sc.url_for(attr=attr,
filter_value=filter_value,
service_type=service_type,
service_name=service_name,
endpoint_type=endpoint_type)
LOG.debug(_('Novaclient connection created using URL: %s') % url)
c = nova_client.Client(context.user_id,
context.auth_token,
context.project_id,
auth_url=url,
insecure=CONF.nova_api_insecure,
cacert=CONF.nova_ca_certificates_file)
# noauth extracts user_id:project_id from auth_token
c.client.auth_token = context.auth_token or '%s:%s' % (context.user_id,
context.project_id)
c.client.management_url = url
return c
class API(base.Base):
"""API for interacting with novaclient."""

View File

@ -45,7 +45,8 @@ class RequestContext(object):
def __init__(self, user_id, project_id, is_admin=None, read_deleted="no",
roles=None, project_name=None, remote_address=None,
timestamp=None, request_id=None, auth_token=None,
overwrite=True, quota_class=None, **kwargs):
overwrite=True, quota_class=None, service_catalog=None,
**kwargs):
"""
:param read_deleted: 'no' indicates deleted records are hidden, 'yes'
indicates deleted records are visible, 'only' indicates that
@ -85,6 +86,14 @@ class RequestContext(object):
if overwrite or not hasattr(local.store, 'context'):
self.update_store()
if service_catalog:
# Only include required parts of service_catalog
self.service_catalog = [s for s in service_catalog
if s.get('type') in ('compute')]
else:
# if list is empty or none
self.service_catalog = []
def _get_read_deleted(self):
return self._read_deleted
@ -115,6 +124,7 @@ class RequestContext(object):
'request_id': self.request_id,
'auth_token': self.auth_token,
'quota_class': self.quota_class,
'service_catalog': self.service_catalog,
'tenant': self.tenant,
'user': self.user}

View File

@ -0,0 +1,35 @@
# Copyright 2013 IBM Corp.
#
# 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 exceptions as cinder_exception
from cinder.compute import nova
from cinder import context
from cinder import exception
from cinder import test
class FakeNovaClient(object):
def __init__(self):
pass
class NovaApiTestCase(test.TestCase):
def setUp(self):
super(NovaApiTestCase, self).setUp()
self.api = nova.API()
self.novaclient = FakeNovaClient()
self.ctx = context.get_admin_context()
self.mox.StubOutWithMock(nova, 'novaclient')