Add get_os_admin_context function in context module
A preparation step to get rid of Nova DB usage. Move a function of OS admin context initialization to context module to use it in all of API. We're going to use this function to initialize Nova client to get instance object with an admin account (which is the only option to get neccessary extended instance attributes). Change-Id: I195d899ed245ea3489c7e97b56ee6aaff9a08be2
This commit is contained in:
parent
0275db9401
commit
5480e9b850
|
@ -176,9 +176,9 @@ function configure_ec2api {
|
|||
iniset $EC2API_CONF_FILE keystone_authtoken admin_password $SERVICE_PASSWORD
|
||||
iniset $EC2API_CONF_FILE keystone_authtoken signing_dir $EC2API_KEYSTONE_SIGNING_DIR
|
||||
|
||||
iniset $EC2API_CONF_FILE metadata admin_tenant_name $SERVICE_TENANT_NAME
|
||||
iniset $EC2API_CONF_FILE metadata admin_user $EC2API_ADMIN_USER
|
||||
iniset $EC2API_CONF_FILE metadata admin_password $SERVICE_PASSWORD
|
||||
iniset $EC2API_CONF_FILE DEFAULT admin_tenant_name $SERVICE_TENANT_NAME
|
||||
iniset $EC2API_CONF_FILE DEFAULT admin_user $EC2API_ADMIN_USER
|
||||
iniset $EC2API_CONF_FILE DEFAULT admin_password $SERVICE_PASSWORD
|
||||
|
||||
iniset $EC2API_CONF_FILE DEFAULT keystone_url "http://${KEYSTONE_AUTH_HOST}:35357/v2.0"
|
||||
iniset $EC2API_CONF_FILE DEFAULT region_list "$REGION_NAME"
|
||||
|
|
|
@ -212,7 +212,7 @@ class InstanceDescriber(common.TaggableItemsDescriber):
|
|||
# NOTE(ft): these filters are needed for metadata server
|
||||
# which calls describe_instances with an admin account
|
||||
# (but project_id is substituted to an instance's one).
|
||||
search_opts={'all_tenants': self.context.cross_tenants,
|
||||
search_opts={'all_tenants': self.context.is_os_admin,
|
||||
'project_id': self.context.project_id})
|
||||
|
||||
def auto_update_db(self, instance, os_instance):
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
import uuid
|
||||
|
||||
from keystoneclient.v2_0 import client as keystone_client
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import timeutils
|
||||
import six
|
||||
|
@ -25,6 +27,19 @@ from ec2api.i18n import _
|
|||
from ec2api.openstack.common import local
|
||||
|
||||
|
||||
ec2_opts = [
|
||||
cfg.StrOpt('admin_user',
|
||||
help=_("Admin user")),
|
||||
cfg.StrOpt('admin_password',
|
||||
help=_("Admin password"),
|
||||
secret=True),
|
||||
cfg.StrOpt('admin_tenant_name',
|
||||
help=_("Admin tenant name")),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(ec2_opts)
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
@ -43,7 +58,7 @@ class RequestContext(object):
|
|||
is_admin=None, roles=None, remote_address=None,
|
||||
auth_token=None, user_name=None, project_name=None,
|
||||
overwrite=True, service_catalog=None, api_version=None,
|
||||
cross_tenants=None, **kwargs):
|
||||
is_os_admin=None, **kwargs):
|
||||
"""Parameters
|
||||
|
||||
:param overwrite: Set to False to ensure that the greenthread local
|
||||
|
@ -55,7 +70,7 @@ class RequestContext(object):
|
|||
"""
|
||||
if kwargs:
|
||||
LOG.warn(_('Arguments dropped when creating context: %s') %
|
||||
str(kwargs))
|
||||
str(kwargs))
|
||||
|
||||
self.user_id = user_id
|
||||
self.project_id = project_id
|
||||
|
@ -78,7 +93,7 @@ class RequestContext(object):
|
|||
self.project_name = project_name
|
||||
self.is_admin = is_admin
|
||||
# TODO(ft): call policy.check_is_admin if is_admin is None
|
||||
self.cross_tenants = cross_tenants
|
||||
self.is_os_admin = is_os_admin
|
||||
self.api_version = api_version
|
||||
if overwrite or not hasattr(local.store, 'context'):
|
||||
self.update_store()
|
||||
|
@ -141,6 +156,24 @@ def is_user_context(context):
|
|||
return True
|
||||
|
||||
|
||||
def get_os_admin_context():
|
||||
"""Create a context to interact with OpenStack as an administrator."""
|
||||
# TODO(ft): make an authentification token reusable
|
||||
keystone = keystone_client.Client(
|
||||
username=CONF.admin_user,
|
||||
password=CONF.admin_password,
|
||||
tenant_name=CONF.admin_tenant_name,
|
||||
auth_url=CONF.keystone_url,
|
||||
)
|
||||
service_catalog = keystone.service_catalog.get_data()
|
||||
return RequestContext(
|
||||
keystone.auth_user_id,
|
||||
keystone.auth_tenant_id,
|
||||
auth_token=keystone.auth_token,
|
||||
service_catalog=service_catalog,
|
||||
is_os_admin=True)
|
||||
|
||||
|
||||
def require_context(ctxt):
|
||||
"""Raise exception.Forbidden()
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@ import posixpath
|
|||
import urlparse
|
||||
|
||||
import httplib2
|
||||
from keystoneclient.v2_0 import client as keystone_client
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import six
|
||||
|
@ -59,13 +58,6 @@ metadata_opts = [
|
|||
cfg.StrOpt('nova_client_priv_key',
|
||||
default='',
|
||||
help=_("Private key of client certificate.")),
|
||||
cfg.StrOpt('admin_user',
|
||||
help=_("Admin user")),
|
||||
cfg.StrOpt('admin_password',
|
||||
help=_("Admin password"),
|
||||
secret=True),
|
||||
cfg.StrOpt('admin_tenant_name',
|
||||
help=_("Admin tenant name")),
|
||||
cfg.StrOpt('metadata_proxy_shared_secret',
|
||||
default='',
|
||||
help=_('Shared secret to sign instance-id request'),
|
||||
|
@ -162,7 +154,7 @@ class MetadataRequestHandler(wsgi.Application):
|
|||
return req.headers
|
||||
|
||||
remote_ip = self._get_remote_ip(req)
|
||||
context = self._get_context()
|
||||
context = ec2context.get_os_admin_context()
|
||||
instance_id, project_id = (
|
||||
api.get_os_instance_and_project_id(context, remote_ip))
|
||||
return {
|
||||
|
@ -180,30 +172,13 @@ class MetadataRequestHandler(wsgi.Application):
|
|||
raise exception.EC2MetadataInvalidAddress()
|
||||
return remote_ip
|
||||
|
||||
def _get_context(self):
|
||||
# TODO(ft): make authentification token reusable
|
||||
keystone = keystone_client.Client(
|
||||
username=CONF.metadata.admin_user,
|
||||
password=CONF.metadata.admin_password,
|
||||
tenant_name=CONF.metadata.admin_tenant_name,
|
||||
auth_url=CONF.keystone_url,
|
||||
)
|
||||
service_catalog = keystone.service_catalog.get_data()
|
||||
return ec2context.RequestContext(
|
||||
keystone.auth_user_id,
|
||||
keystone.auth_tenant_id,
|
||||
auth_token=keystone.auth_token,
|
||||
service_catalog=service_catalog,
|
||||
is_admin=True,
|
||||
cross_tenants=True)
|
||||
|
||||
def _sign_instance_id(self, instance_id):
|
||||
return hmac.new(CONF.metadata.metadata_proxy_shared_secret,
|
||||
instance_id,
|
||||
hashlib.sha256).hexdigest()
|
||||
|
||||
def _get_metadata(self, req, path_tokens):
|
||||
context = self._get_context()
|
||||
context = ec2context.get_os_admin_context()
|
||||
if req.headers.get('X-Instance-ID'):
|
||||
os_instance_id, project_id, remote_ip = (
|
||||
self._unpack_request_attributes(req))
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
# Copyright 2014
|
||||
# The Cloudscaling Group, Inc.
|
||||
#
|
||||
# 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 mock
|
||||
from oslo_config import cfg
|
||||
from oslo_config import fixture as config_fixture
|
||||
from oslotest import base as test_base
|
||||
|
||||
from ec2api import context as ec2context
|
||||
|
||||
cfg.CONF.import_opt('keystone_url', 'ec2api.api')
|
||||
|
||||
|
||||
class ContextTestCase(test_base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(ContextTestCase, self).setUp()
|
||||
conf = config_fixture.Config()
|
||||
conf.config(admin_user='admin',
|
||||
admin_password='password',
|
||||
admin_tenant_name='service')
|
||||
|
||||
@mock.patch('keystoneclient.v2_0.client.Client')
|
||||
def test_get_os_admin_context(self, keystone):
|
||||
service_catalog = mock.MagicMock()
|
||||
service_catalog.get_data.return_value = 'fake_service_catalog'
|
||||
keystone.return_value = mock.Mock(auth_user_id='fake_user_id',
|
||||
auth_tenant_id='fake_project_id',
|
||||
auth_token='fake_token',
|
||||
service_catalog=service_catalog)
|
||||
context = ec2context.get_os_admin_context()
|
||||
self.assertEqual('fake_user_id', context.user_id)
|
||||
self.assertEqual('fake_project_id', context.project_id)
|
||||
self.assertEqual('fake_token', context.auth_token)
|
||||
self.assertEqual('fake_service_catalog', context.service_catalog)
|
||||
self.assertTrue(context.is_os_admin)
|
||||
conf = cfg.CONF
|
||||
keystone.assert_called_with(
|
||||
username=conf.admin_user,
|
||||
password=conf.admin_password,
|
||||
tenant_name=conf.admin_tenant_name,
|
||||
auth_url=conf.keystone_url)
|
|
@ -39,9 +39,6 @@ class ProxyTestCase(test_base.BaseTestCase):
|
|||
auth_ca_cert=None,
|
||||
nova_client_cert='nova_cert',
|
||||
nova_client_priv_key='nova_priv_key',
|
||||
admin_user='admin',
|
||||
admin_password='password',
|
||||
admin_tenant_name='service',
|
||||
metadata_proxy_shared_secret='secret')
|
||||
|
||||
@mock.patch('ec2api.metadata.api.get_version_list')
|
||||
|
@ -91,7 +88,7 @@ class ProxyTestCase(test_base.BaseTestCase):
|
|||
@mock.patch('ec2api.metadata.api.get_metadata_item')
|
||||
@mock.patch('ec2api.metadata.api.get_os_instance_and_project_id')
|
||||
@mock.patch.object(metadata.MetadataRequestHandler, '_get_remote_ip')
|
||||
@mock.patch.object(metadata.MetadataRequestHandler, '_get_context')
|
||||
@mock.patch('ec2api.context.get_os_admin_context')
|
||||
def test_get_metadata_by_ip(self, get_context, get_remote_ip, get_ids,
|
||||
get_metadata_item):
|
||||
get_context.return_value = mock.Mock(project_id='fake_admin_project')
|
||||
|
@ -116,7 +113,7 @@ class ProxyTestCase(test_base.BaseTestCase):
|
|||
@mock.patch('ec2api.metadata.api.get_metadata_item')
|
||||
@mock.patch.object(metadata.MetadataRequestHandler,
|
||||
'_unpack_request_attributes')
|
||||
@mock.patch.object(metadata.MetadataRequestHandler, '_get_context')
|
||||
@mock.patch('ec2api.context.get_os_admin_context')
|
||||
def test_get_metadata_by_instance_id(self, get_context, unpack_request,
|
||||
get_metadata_item):
|
||||
get_context.return_value = mock.Mock(project_id='fake_admin_project')
|
||||
|
@ -243,7 +240,7 @@ class ProxyTestCase(test_base.BaseTestCase):
|
|||
self._proxy_request_test_helper(response_code=302)
|
||||
|
||||
@mock.patch.object(metadata.MetadataRequestHandler, '_sign_instance_id')
|
||||
@mock.patch.object(metadata.MetadataRequestHandler, '_get_context')
|
||||
@mock.patch('ec2api.context.get_os_admin_context')
|
||||
@mock.patch.object(metadata.MetadataRequestHandler, '_get_remote_ip')
|
||||
def test_build_proxy_request_headers(self, get_remote_ip, get_context,
|
||||
sign_instance_id):
|
||||
|
@ -300,28 +297,6 @@ class ProxyTestCase(test_base.BaseTestCase):
|
|||
cfg.CONF.set_override('use_forwarded_for', False)
|
||||
self.assertEqual('fake_addr', self.handler._get_remote_ip(req))
|
||||
|
||||
@mock.patch('keystoneclient.v2_0.client.Client')
|
||||
def test_get_context(self, keystone):
|
||||
service_catalog = mock.MagicMock()
|
||||
service_catalog.get_data.return_value = 'fake_service_catalog'
|
||||
keystone.return_value = mock.Mock(auth_user_id='fake_user_id',
|
||||
auth_tenant_id='fake_project_id',
|
||||
auth_token='fake_token',
|
||||
service_catalog=service_catalog)
|
||||
context = self.handler._get_context()
|
||||
self.assertEqual('fake_user_id', context.user_id)
|
||||
self.assertEqual('fake_project_id', context.project_id)
|
||||
self.assertEqual('fake_token', context.auth_token)
|
||||
self.assertEqual('fake_service_catalog', context.service_catalog)
|
||||
self.assertTrue(context.is_admin)
|
||||
self.assertTrue(context.cross_tenants)
|
||||
conf = cfg.CONF
|
||||
keystone.assert_called_with(
|
||||
username=conf.metadata.admin_user,
|
||||
password=conf.metadata.admin_password,
|
||||
tenant_name=conf.metadata.admin_tenant_name,
|
||||
auth_url=conf.keystone_url)
|
||||
|
||||
def test_unpack_request_attributes(self):
|
||||
sign = (
|
||||
'97e7709481495f1a3a589e5ee03f8b5d51a3e0196768e300c441b58fe0382f4d')
|
||||
|
|
|
@ -296,9 +296,9 @@ iniset $CONF_FILE keystone_authtoken admin_tenant_name $SERVICE_TENANT
|
|||
iniset $CONF_FILE keystone_authtoken auth_protocol $AUTH_PROTO
|
||||
iniset $CONF_FILE keystone_authtoken auth_port $AUTH_PORT
|
||||
|
||||
iniset $CONF_FILE metadata admin_user $SERVICE_USERNAME
|
||||
iniset $CONF_FILE metadata admin_password $SERVICE_PASSWORD
|
||||
iniset $CONF_FILE metadata admin_tenant_name $SERVICE_TENANT
|
||||
iniset $CONF_FILE DEFAULT admin_user $SERVICE_USERNAME
|
||||
iniset $CONF_FILE DEFAULT admin_password $SERVICE_PASSWORD
|
||||
iniset $CONF_FILE DEFAULT admin_tenant_name $SERVICE_TENANT
|
||||
|
||||
if [[ -f "$NOVA_CONF" ]]; then
|
||||
copynovaopt s3_host
|
||||
|
|
Loading…
Reference in New Issue