adopt ec2 api to work with keystone v3 api
Change-Id: Idbafeff0aff9a32852ef0dbeaae5b341a7c06b61
This commit is contained in:
parent
e630f2159f
commit
0167580e6d
|
@ -213,7 +213,6 @@ Legacy OpenStack release notice
|
|||
|
||||
EC2 API supports Havana, Icehouse, Juno with additional limitations:
|
||||
|
||||
|
||||
Instance related:
|
||||
- rootDeviceName Instance property
|
||||
- kernelId Instance property
|
||||
|
@ -237,7 +236,8 @@ EC2 API supports Nova client (>=2.16.0) with no microversion support.
|
|||
Additional limitations are the same, except network interfaces'
|
||||
deleteOnTermination.
|
||||
|
||||
Preferred way to run EC2 API in these releases is to run it in virtual environment:
|
||||
|
||||
Preferred way to run EC2 API in older releases is to run it in virtual environment:
|
||||
- create virtual environment by running command 'python tools/install_venv.py'
|
||||
- run install inside venv 'tools/with_venv.sh ./install.sh'
|
||||
- and then you need to run EC2 API services: 'ec2-api', 'ec2-api-metadata'
|
||||
|
|
|
@ -228,11 +228,21 @@ class EC2KeystoneAuth(wsgi.Middleware):
|
|||
result = response.json()
|
||||
|
||||
try:
|
||||
token_id = result['access']['token']['id']
|
||||
user_id = result['access']['user']['id']
|
||||
project_id = result['access']['token']['tenant']['id']
|
||||
user_name = result['access']['user'].get('name')
|
||||
project_name = result['access']['token']['tenant'].get('name')
|
||||
if 'token' in result:
|
||||
# NOTE(andrey-mp): response from keystone v3
|
||||
token_id = response.headers['x-subject-token']
|
||||
user_id = result['token']['user']['id']
|
||||
project_id = result['token']['project']['id']
|
||||
user_name = result['token']['user'].get('name')
|
||||
project_name = result['token']['project'].get('name')
|
||||
catalog = result['token']['catalog']
|
||||
else:
|
||||
token_id = result['access']['token']['id']
|
||||
user_id = result['access']['user']['id']
|
||||
project_id = result['access']['token']['tenant']['id']
|
||||
user_name = result['access']['user'].get('name')
|
||||
project_name = result['access']['token']['tenant'].get('name')
|
||||
catalog = result['access']['serviceCatalog']
|
||||
except (AttributeError, KeyError):
|
||||
LOG.exception(_("Keystone failure"))
|
||||
msg = _("Failure communicating with keystone")
|
||||
|
@ -244,7 +254,6 @@ class EC2KeystoneAuth(wsgi.Middleware):
|
|||
remote_address = req.headers.get('X-Forwarded-For',
|
||||
remote_address)
|
||||
|
||||
catalog = result['access']['serviceCatalog']
|
||||
ctxt = context.RequestContext(user_id, project_id,
|
||||
request_id=request_id,
|
||||
user_name=user_name,
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
# limitations under the License.
|
||||
|
||||
|
||||
from keystoneclient.v2_0 import client as kc
|
||||
from novaclient import client as novaclient
|
||||
from novaclient import exceptions as nova_exception
|
||||
from oslo_config import cfg
|
||||
|
@ -135,13 +134,13 @@ def cinder(context):
|
|||
|
||||
|
||||
def keystone(context):
|
||||
_keystone = kc.Client(
|
||||
keystone_client_class = ec2_context.get_keystone_client_class()
|
||||
return keystone_client_class(
|
||||
token=context.auth_token,
|
||||
project_id=context.project_id,
|
||||
tenant_id=context.project_id,
|
||||
auth_url=CONF.keystone_url)
|
||||
|
||||
return _keystone
|
||||
|
||||
|
||||
def nova_cert(context):
|
||||
_cert_api = _rpcapi_CertAPI(context)
|
||||
|
@ -162,6 +161,9 @@ def _url_for(context, **kwargs):
|
|||
for endpoint in service['endpoints']:
|
||||
if 'publicURL' in endpoint:
|
||||
return endpoint['publicURL']
|
||||
elif endpoint.get('interface') == 'public':
|
||||
# NOTE(andrey-mp): keystone v3
|
||||
return endpoint['url']
|
||||
else:
|
||||
return None
|
||||
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
|
||||
import uuid
|
||||
|
||||
from keystoneclient.v2_0 import client as keystone_client
|
||||
from keystoneclient import client as keystone_client
|
||||
from keystoneclient.v2_0 import client as keystone_client_v2
|
||||
from keystoneclient.v3 import client as keystone_client_v3
|
||||
from oslo_config import cfg
|
||||
from oslo_context import context
|
||||
from oslo_log import log as logging
|
||||
|
@ -35,6 +37,8 @@ ec2_opts = [
|
|||
secret=True),
|
||||
cfg.StrOpt('admin_tenant_name',
|
||||
help=_("Admin tenant name")),
|
||||
# TODO(andrey-mp): keystone v3 allows to pass domain_name
|
||||
# or domain_id to auth. This code should support this feature.
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
@ -143,15 +147,33 @@ def is_user_context(context):
|
|||
return True
|
||||
|
||||
|
||||
_keystone_client_class = None
|
||||
|
||||
|
||||
def get_keystone_client_class():
|
||||
global _keystone_client_class
|
||||
if _keystone_client_class is None:
|
||||
keystone = keystone_client.Client(auth_url=CONF.keystone_url)
|
||||
if isinstance(keystone, keystone_client_v2.Client):
|
||||
_keystone_client_class = keystone_client_v2.Client
|
||||
elif isinstance(keystone, keystone_client_v3.Client):
|
||||
_keystone_client_class = keystone_client_v3.Client
|
||||
else:
|
||||
raise exception.EC2KeystoneDiscoverFailure()
|
||||
return _keystone_client_class
|
||||
|
||||
|
||||
def get_os_admin_context():
|
||||
"""Create a context to interact with OpenStack as an administrator."""
|
||||
current_context = context.get_current()
|
||||
if (current_context and current_context.is_os_admin):
|
||||
return current_context
|
||||
# TODO(ft): make an authentification token reusable
|
||||
keystone = keystone_client.Client(
|
||||
keystone_client_class = get_keystone_client_class()
|
||||
keystone = keystone_client_class(
|
||||
username=CONF.admin_user,
|
||||
password=CONF.admin_password,
|
||||
project_name=CONF.admin_tenant_name,
|
||||
tenant_name=CONF.admin_tenant_name,
|
||||
auth_url=CONF.keystone_url,
|
||||
)
|
||||
|
|
|
@ -96,6 +96,10 @@ class EC2APIPasteAppNotFound(EC2APIException):
|
|||
msg_fmt = _("Could not load paste app '%(name)s' from %(path)s")
|
||||
|
||||
|
||||
class EC2KeystoneDiscoverFailure(EC2APIException):
|
||||
msg_fmt = _("Could not discover keystone versions.")
|
||||
|
||||
|
||||
# Internal ec2api metadata exceptions
|
||||
|
||||
class EC2MetadataException(EC2APIException):
|
||||
|
|
|
@ -123,13 +123,14 @@ class ClientsTestCase(test_base.BaseTestCase):
|
|||
self.assertEqual('fake_token', res.client.auth_token)
|
||||
self.assertEqual('cinder_url', res.client.management_url)
|
||||
|
||||
@mock.patch('keystoneclient.v2_0.client.Client')
|
||||
def test_keystone(self, keystone):
|
||||
@mock.patch('ec2api.context.get_keystone_client_class',
|
||||
return_value=mock.Mock(return_value=mock.Mock()))
|
||||
def test_keystone(self, keystone_client_class):
|
||||
context = mock.NonCallableMock(
|
||||
auth_token='fake_token',
|
||||
project_id='fake_project')
|
||||
res = clients.keystone(context)
|
||||
self.assertEqual(keystone.return_value, res)
|
||||
keystone.assert_called_with(
|
||||
self.assertEqual(keystone_client_class.return_value.return_value, res)
|
||||
keystone_client_class.return_value.assert_called_with(
|
||||
auth_url='keystone_url', token='fake_token',
|
||||
tenant_id='fake_project')
|
||||
tenant_id='fake_project', project_id='fake_project')
|
||||
|
|
|
@ -12,12 +12,15 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from keystoneclient.v2_0 import client as keystone_client_v2
|
||||
from keystoneclient.v3 import client as keystone_client_v3
|
||||
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 ec2_context
|
||||
from ec2api import exception
|
||||
|
||||
cfg.CONF.import_opt('keystone_url', 'ec2api.api')
|
||||
|
||||
|
@ -35,10 +38,12 @@ class ContextTestCase(test_base.BaseTestCase):
|
|||
def test_get_os_admin_context(self, keystone):
|
||||
service_catalog = mock.Mock()
|
||||
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)
|
||||
ec2_context._keystone_client_class = mock.Mock(
|
||||
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 = ec2_context.get_os_admin_context()
|
||||
self.assertEqual('fake_user_id', context.user_id)
|
||||
self.assertEqual('fake_project_id', context.project_id)
|
||||
|
@ -46,13 +51,35 @@ class ContextTestCase(test_base.BaseTestCase):
|
|||
self.assertEqual('fake_service_catalog', context.service_catalog)
|
||||
self.assertTrue(context.is_os_admin)
|
||||
conf = cfg.CONF
|
||||
keystone.assert_called_once_with(
|
||||
username=conf.admin_user,
|
||||
password=conf.admin_password,
|
||||
tenant_name=conf.admin_tenant_name,
|
||||
auth_url=conf.keystone_url)
|
||||
ec2_context._keystone_client_class.assert_called_once_with(
|
||||
username=conf.admin_user,
|
||||
password=conf.admin_password,
|
||||
tenant_name=conf.admin_tenant_name,
|
||||
project_name=conf.admin_tenant_name,
|
||||
auth_url=conf.keystone_url)
|
||||
service_catalog.get_data.assert_called_once_with()
|
||||
|
||||
keystone.reset_mock()
|
||||
self.assertEqual(context, ec2_context.get_os_admin_context())
|
||||
self.assertFalse(keystone.called)
|
||||
|
||||
@mock.patch('keystoneclient.client.Client')
|
||||
def test_get_keystone_client_class(self, client):
|
||||
client.return_value = mock.MagicMock(spec=keystone_client_v2.Client)
|
||||
ec2_context._keystone_client_class = None
|
||||
client_class = ec2_context.get_keystone_client_class()
|
||||
client.assert_called_once_with(auth_url='http://localhost:5000/v2.0')
|
||||
self.assertEqual(keystone_client_v2.Client, client_class)
|
||||
client.reset_mock()
|
||||
|
||||
client.return_value = mock.MagicMock(spec=keystone_client_v3.Client)
|
||||
ec2_context._keystone_client_class = None
|
||||
client_class = ec2_context.get_keystone_client_class()
|
||||
client.assert_called_once_with(auth_url='http://localhost:5000/v2.0')
|
||||
self.assertEqual(keystone_client_v3.Client, client_class)
|
||||
client.reset_mock()
|
||||
|
||||
client.return_value = mock.MagicMock()
|
||||
ec2_context._keystone_client_class = None
|
||||
self.assertRaises(exception.EC2KeystoneDiscoverFailure,
|
||||
ec2_context.get_keystone_client_class)
|
||||
|
|
|
@ -333,17 +333,19 @@ class ProxyTestCase(test_base.BaseTestCase):
|
|||
self.handler._unpack_request_attributes(req)
|
||||
self.assertEqual(1, constant_time_compare.call_count)
|
||||
|
||||
@mock.patch('keystoneclient.v2_0.client.Client')
|
||||
@mock.patch('ec2api.context.get_keystone_client_class')
|
||||
@mock.patch('novaclient.client.Client')
|
||||
@mock.patch('ec2api.db.api.IMPL')
|
||||
@mock.patch('ec2api.metadata.api.instance_api')
|
||||
def test_get_metadata(self, instance_api, db_api, nova, keystone):
|
||||
def test_get_metadata(self, instance_api, db_api, nova,
|
||||
keystone_client_class):
|
||||
service_catalog = mock.MagicMock()
|
||||
service_catalog.get_data.return_value = []
|
||||
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)
|
||||
keystone_client_class.return_value.return_value = mock.Mock(
|
||||
auth_user_id='fake_user_id',
|
||||
auth_tenant_id='fake_project_id',
|
||||
auth_token='fake_token',
|
||||
service_catalog=service_catalog)
|
||||
nova.return_value.fixed_ips.get.return_value = (
|
||||
mock.Mock(hostname='fake_name'))
|
||||
nova.return_value.servers.list.return_value = [
|
||||
|
|
Loading…
Reference in New Issue