Only use a token for openstack client operations.

This uses the same techniques as horizon for getting client
instances with an existing token.

This change also fetches a new auth_token if the context has none,
as will happen when the context is created from saved user_creds
rather than a real request.

While it appears that some code paths will result in an extra call to the
keystone client, the actual calls to keystone will be less. This is
because each client was making its own calls to keystone when
authenticating with a username and password.

Implements blueprint auth-token-only

Change-Id: If6a63a5079464758f42d5d5e83dfffb196f4a7f6
This commit is contained in:
Steve Baker 2013-07-05 16:08:13 +12:00
parent 44a101737f
commit f77195cb68
10 changed files with 136 additions and 60 deletions

View File

@ -154,3 +154,7 @@ class KeystoneClient(object):
def url_for(self, **kwargs):
return self.client.service_catalog.url_for(**kwargs)
@property
def auth_token(self):
return self.client.auth_token

View File

@ -61,6 +61,12 @@ class OpenStackClients(object):
self._quantum = None
self._cinder = None
@property
def auth_token(self):
# if there is no auth token in the context
# attempt to get one using the context username and password
return self.context.auth_token or self.keystone().auth_token
def keystone(self):
if self._keystone:
return self._keystone
@ -76,28 +82,23 @@ class OpenStackClients(object):
return self._nova[service_type]
con = self.context
if self.auth_token is None:
logger.error("Nova connection failed, no auth_token!")
return None
args = {
'project_id': con.tenant,
'auth_url': con.auth_url,
'service_type': service_type,
'username': None,
'api_key': None
}
if con.password is not None:
args['username'] = con.username
args['api_key'] = con.password
elif con.auth_token is not None:
args['username'] = None
args['api_key'] = None
else:
logger.error("Nova connection failed, no password or auth_token!")
return None
client = novaclient.Client(1.1, **args)
if con.password is None and con.auth_token is not None:
management_url = self.url_for(service_type=service_type)
client.client.auth_token = con.auth_token
client.client.management_url = management_url
management_url = self.url_for(service_type=service_type)
client.client.auth_token = self.auth_token
client.client.management_url = management_url
self._nova[service_type] = client
return client
@ -109,24 +110,19 @@ class OpenStackClients(object):
return self._swift
con = self.context
if self.auth_token is None:
logger.error("Swift connection failed, no auth_token!")
return None
args = {
'auth_version': '2.0',
'tenant_name': con.tenant,
'user': con.username
'user': con.username,
'key': None,
'authurl': None,
'preauthtoken': self.auth_token,
'preauthurl': self.url_for(service_type='object-store')
}
if con.password is not None:
args['key'] = con.password
args['authurl'] = con.auth_url
elif con.auth_token is not None:
args['key'] = None
args['authurl'] = None
args['preauthtoken'] = con.auth_token
args['preauthurl'] = self.url_for(service_type='object-store')
else:
logger.error("Swift connection failed, no password or " +
"auth_token!")
return None
self._swift = swiftclient.Connection(**args)
return self._swift
@ -134,28 +130,20 @@ class OpenStackClients(object):
if quantumclient is None:
return None
if self._quantum:
logger.debug('using existing _quantum')
return self._quantum
con = self.context
if self.auth_token is None:
logger.error("Quantum connection failed, no auth_token!")
return None
args = {
'auth_url': con.auth_url,
'service_type': 'network',
'token': self.auth_token,
'endpoint_url': self.url_for(service_type='network')
}
if con.password is not None:
args['username'] = con.username
args['password'] = con.password
args['tenant_name'] = con.tenant
elif con.auth_token is not None:
args['token'] = con.auth_token
args['endpoint_url'] = self.url_for(service_type='network')
else:
logger.error("Quantum connection failed, "
"no password or auth_token!")
return None
logger.debug('quantum args %s', args)
self._quantum = quantumclient.Client(**args)
return self._quantum
@ -167,29 +155,22 @@ class OpenStackClients(object):
return self._cinder
con = self.context
if self.auth_token is None:
logger.error("Cinder connection failed, no auth_token!")
return None
args = {
'service_type': 'volume',
'auth_url': con.auth_url,
'project_id': con.tenant
'project_id': con.tenant,
'username': None,
'api_key': None
}
if con.password is not None:
args['username'] = con.username
args['api_key'] = con.password
elif con.auth_token is not None:
args['username'] = None
args['api_key'] = None
else:
logger.error("Cinder connection failed, "
"no password or auth_token!")
return None
logger.debug('cinder args %s', args)
self._cinder = cinderclient.Client('1', **args)
if con.password is None and con.auth_token is not None:
management_url = self.url_for(service_type='volume')
self._cinder.client.auth_token = con.auth_token
self._cinder.client.management_url = management_url
management_url = self.url_for(service_type='volume')
self._cinder.client.auth_token = self.auth_token
self._cinder.client.management_url = management_url
return self._cinder

View File

@ -105,6 +105,7 @@ class FakeKeystoneClient():
self.access = access
self.secret = secret
self.creds = None
self.auth_token = 'abcd1234'
def create_stack_user(self, username, password=''):
self.username = username

View File

@ -26,6 +26,7 @@ from heat.engine import parameters
from heat.engine import scheduler
from heat.engine import template
from heat.tests.fakes import FakeKeystoneClient
from heat.tests.common import HeatTestCase
from heat.tests.utils import dummy_context
from heat.tests.utils import setup_dummy_db
@ -532,6 +533,18 @@ class StackTest(HeatTestCase):
self.assertEqual(stack.state, (None, None))
self.assertEqual(stack.status_reason, '')
def test_no_auth_token(self):
ctx = dummy_context()
ctx.auth_token = None
self.m.StubOutWithMock(clients.OpenStackClients, 'keystone')
clients.OpenStackClients.keystone().AndReturn(FakeKeystoneClient())
self.m.ReplayAll()
stack = parser.Stack(ctx, 'test_stack', parser.Template({}))
self.assertEqual('abcd1234', stack.clients.auth_token)
self.m.VerifyAll()
def test_state(self):
stack = parser.Stack(self.ctx, 'test_stack', parser.Template({}),
action=parser.Stack.CREATE,

View File

@ -15,6 +15,7 @@
from testtools import skipIf
from heat.engine import clients
from heat.common import exception
from heat.common import template_format
from heat.engine import properties
@ -26,6 +27,7 @@ from heat.engine.resources.quantum import router
from heat.engine.resources.quantum.quantum import QuantumResource as qr
from heat.openstack.common.importutils import try_import
from heat.tests.common import HeatTestCase
from heat.tests import fakes
from heat.tests import utils
from heat.tests.utils import setup_dummy_db
from heat.tests.utils import parse_stack
@ -211,6 +213,7 @@ class QuantumNetTest(HeatTestCase):
self.m.StubOutWithMock(quantumclient.Client, 'create_network')
self.m.StubOutWithMock(quantumclient.Client, 'delete_network')
self.m.StubOutWithMock(quantumclient.Client, 'show_network')
self.m.StubOutWithMock(clients.OpenStackClients, 'keystone')
setup_dummy_db()
def create_net(self, t, stack, resource_name):
@ -220,6 +223,8 @@ class QuantumNetTest(HeatTestCase):
return rsrc
def test_net(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
quantumclient.Client.create_network({
'network': {'name': u'the_network', 'admin_state_up': True}
}).AndReturn({"network": {
@ -342,6 +347,7 @@ class QuantumSubnetTest(HeatTestCase):
self.m.StubOutWithMock(quantumclient.Client, 'create_subnet')
self.m.StubOutWithMock(quantumclient.Client, 'delete_subnet')
self.m.StubOutWithMock(quantumclient.Client, 'show_subnet')
self.m.StubOutWithMock(clients.OpenStackClients, 'keystone')
setup_dummy_db()
def create_subnet(self, t, stack, resource_name):
@ -353,6 +359,8 @@ class QuantumSubnetTest(HeatTestCase):
def test_subnet(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
quantumclient.Client.create_subnet({
'subnet': {
'name': utils.PhysName('test_stack', 'test_subnet'),
@ -447,6 +455,8 @@ class QuantumSubnetTest(HeatTestCase):
def test_subnet_disable_dhcp(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
quantumclient.Client.create_subnet({
'subnet': {
'name': utils.PhysName('test_stack', 'test_subnet'),
@ -525,6 +535,7 @@ class QuantumRouterTest(HeatTestCase):
self.m.StubOutWithMock(quantumclient.Client, 'remove_interface_router')
self.m.StubOutWithMock(quantumclient.Client, 'add_gateway_router')
self.m.StubOutWithMock(quantumclient.Client, 'remove_gateway_router')
self.m.StubOutWithMock(clients.OpenStackClients, 'keystone')
setup_dummy_db()
def create_router(self, t, stack, resource_name):
@ -554,6 +565,8 @@ class QuantumRouterTest(HeatTestCase):
return rsrc
def test_router(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
quantumclient.Client.create_router({
'router': {
'name': utils.PhysName('test_stack', 'router'),
@ -659,6 +672,8 @@ class QuantumRouterTest(HeatTestCase):
self.m.VerifyAll()
def test_router_interface(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
quantumclient.Client.add_interface_router(
'3e46229d-8fce-4733-819a-b5fe630550f8',
{'subnet_id': '91e47a57-7508-46fe-afc9-fc454e8580e1'}
@ -687,6 +702,8 @@ class QuantumRouterTest(HeatTestCase):
self.m.VerifyAll()
def test_gateway_router(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
quantumclient.Client.add_gateway_router(
'3e46229d-8fce-4733-819a-b5fe630550f8',
{'network_id': 'fc68ea2c-b60b-4b4f-bd82-94ec81110766'}
@ -725,10 +742,13 @@ class QuantumFloatingIPTest(HeatTestCase):
self.m.StubOutWithMock(quantumclient.Client, 'create_port')
self.m.StubOutWithMock(quantumclient.Client, 'delete_port')
self.m.StubOutWithMock(quantumclient.Client, 'show_port')
self.m.StubOutWithMock(clients.OpenStackClients, 'keystone')
setup_dummy_db()
def test_floating_ip(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
quantumclient.Client.create_floatingip({
'floatingip': {'floating_network_id': u'abcd1234'}
}).AndReturn({'floatingip': {
@ -789,6 +809,8 @@ class QuantumFloatingIPTest(HeatTestCase):
def test_port(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
quantumclient.Client.create_port({'port': {
'network_id': u'xyz1234',
'fixed_ips': [
@ -853,6 +875,8 @@ class QuantumFloatingIPTest(HeatTestCase):
def test_floatip_port(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
quantumclient.Client.create_floatingip({
'floatingip': {'floating_network_id': u'abcd1234'}
}).AndReturn({'floatingip': {

View File

@ -20,9 +20,11 @@ from heat.common import template_format
from heat.openstack.common.importutils import try_import
from heat.engine.resources import s3
from heat.engine import resource
from heat.engine import clients
from heat.engine import scheduler
from heat.tests.common import HeatTestCase
from heat.tests import utils
from heat.tests import fakes
from heat.tests.utils import setup_dummy_db
from heat.tests.utils import parse_stack
@ -63,6 +65,7 @@ class s3Test(HeatTestCase):
self.m.StubOutWithMock(swiftclient.Connection, 'put_container')
self.m.StubOutWithMock(swiftclient.Connection, 'delete_container')
self.m.StubOutWithMock(swiftclient.Connection, 'get_auth')
self.m.StubOutWithMock(clients.OpenStackClients, 'keystone')
setup_dummy_db()
@ -75,6 +78,8 @@ class s3Test(HeatTestCase):
return rsrc
def test_attributes(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
container_name = utils.PhysName('test_stack', 'test_resource')
swiftclient.Connection.put_container(
container_name,
@ -111,6 +116,8 @@ class s3Test(HeatTestCase):
self.m.VerifyAll()
def test_public_read(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
container_name = utils.PhysName('test_stack', 'test_resource')
swiftclient.Connection.put_container(
utils.PhysName('test_stack', 'test_resource'),
@ -129,6 +136,8 @@ class s3Test(HeatTestCase):
self.m.VerifyAll()
def test_public_read_write(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
container_name = utils.PhysName('test_stack', 'test_resource')
swiftclient.Connection.put_container(
container_name,
@ -147,6 +156,8 @@ class s3Test(HeatTestCase):
self.m.VerifyAll()
def test_authenticated_read(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
container_name = utils.PhysName('test_stack', 'test_resource')
swiftclient.Connection.put_container(
container_name,
@ -164,6 +175,8 @@ class s3Test(HeatTestCase):
self.m.VerifyAll()
def test_website(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
container_name = utils.PhysName('test_stack', 'test_resource')
swiftclient.Connection.put_container(
container_name,
@ -181,6 +194,8 @@ class s3Test(HeatTestCase):
self.m.VerifyAll()
def test_delete_exception(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
container_name = utils.PhysName('test_stack', 'test_resource')
swiftclient.Connection.put_container(
container_name,
@ -199,6 +214,8 @@ class s3Test(HeatTestCase):
def test_delete_retain(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
# first run, with retain policy
swiftclient.Connection.put_container(
utils.PhysName('test_stack', 'test_resource'),

View File

@ -19,6 +19,7 @@ from heat.common import template_format
from heat.engine import parser
from heat.engine import resource
from heat.tests.common import HeatTestCase
from heat.tests.fakes import FakeKeystoneClient
from heat.tests.utils import dummy_context
from heat.tests.utils import setup_dummy_db
from heat.tests.v1_1 import fakes
@ -87,6 +88,7 @@ Resources:
super(SecurityGroupTest, self).setUp()
self.fc = fakes.FakeClient()
self.m.StubOutWithMock(clients.OpenStackClients, 'nova')
self.m.StubOutWithMock(clients.OpenStackClients, 'keystone')
self.m.StubOutWithMock(nova_sgr.SecurityGroupRuleManager, 'create')
self.m.StubOutWithMock(nova_sgr.SecurityGroupRuleManager, 'delete')
self.m.StubOutWithMock(nova_sg.SecurityGroupManager, 'create')
@ -275,6 +277,8 @@ Resources:
@stack_delete_after
def test_security_group_quantum(self):
#create script
clients.OpenStackClients.keystone().AndReturn(
FakeKeystoneClient())
sg_name = utils.PhysName('test_stack', 'the_sg')
quantumclient.Client.create_security_group({
'security_group': {
@ -415,6 +419,8 @@ Resources:
@stack_delete_after
def test_security_group_quantum_exception(self):
#create script
clients.OpenStackClients.keystone().AndReturn(
FakeKeystoneClient())
sg_name = utils.PhysName('test_stack', 'the_sg')
quantumclient.Client.create_security_group({
'security_group': {

View File

@ -20,9 +20,11 @@ from testtools import skipIf
from heat.common import template_format
from heat.openstack.common.importutils import try_import
from heat.engine.resources import swift
from heat.engine import clients
from heat.engine import resource
from heat.engine import scheduler
from heat.tests.common import HeatTestCase
from heat.tests import fakes
from heat.tests import utils
from heat.tests.utils import setup_dummy_db
from heat.tests.utils import parse_stack
@ -64,6 +66,7 @@ class swiftTest(HeatTestCase):
self.m.StubOutWithMock(swiftclient.Connection, 'delete_container')
self.m.StubOutWithMock(swiftclient.Connection, 'head_container')
self.m.StubOutWithMock(swiftclient.Connection, 'get_auth')
self.m.StubOutWithMock(clients.OpenStackClients, 'keystone')
setup_dummy_db()
@ -114,6 +117,8 @@ class swiftTest(HeatTestCase):
"x-container-bytes-used": "17680980",
"content-type": "text/plain; charset=utf-8"}
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
container_name = utils.PhysName('test_stack', 'test_resource')
swiftclient.Connection.put_container(
container_name,
@ -155,6 +160,8 @@ class swiftTest(HeatTestCase):
self.m.VerifyAll()
def test_public_read(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
container_name = utils.PhysName('test_stack', 'test_resource')
swiftclient.Connection.put_container(
container_name,
@ -172,6 +179,8 @@ class swiftTest(HeatTestCase):
self.m.VerifyAll()
def test_public_read_write(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
container_name = utils.PhysName('test_stack', 'test_resource')
swiftclient.Connection.put_container(
container_name,
@ -190,6 +199,8 @@ class swiftTest(HeatTestCase):
self.m.VerifyAll()
def test_website(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
container_name = utils.PhysName('test_stack', 'test_resource')
swiftclient.Connection.put_container(
container_name,
@ -207,6 +218,8 @@ class swiftTest(HeatTestCase):
self.m.VerifyAll()
def test_delete_exception(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
container_name = utils.PhysName('test_stack', 'test_resource')
swiftclient.Connection.put_container(
container_name,
@ -225,6 +238,8 @@ class swiftTest(HeatTestCase):
def test_delete_retain(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
# first run, with retain policy
swiftclient.Connection.put_container(
utils.PhysName('test_stack', 'test_resource'),

View File

@ -17,8 +17,10 @@ from testtools import skipIf
from heat.common import exception
from heat.common import template_format
from heat.engine import parser
from heat.engine import clients
from heat.engine import resource
from heat.tests.common import HeatTestCase
from heat.tests import fakes
from heat.tests import utils
from heat.tests.utils import dummy_context
from heat.tests.utils import setup_dummy_db
@ -60,6 +62,7 @@ class VPCTestBase(HeatTestCase):
quantumclient.Client, 'create_security_group_rule')
self.m.StubOutWithMock(
quantumclient.Client, 'delete_security_group_rule')
self.m.StubOutWithMock(clients.OpenStackClients, 'keystone')
def create_stack(self, template):
t = template_format.parse(template)
@ -74,6 +77,10 @@ class VPCTestBase(HeatTestCase):
stack.store()
return stack
def mock_keystone(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
def mock_create_network(self):
self.vpc_name = utils.PhysName('test_stack', 'the_vpc')
quantumclient.Client.create_network(
@ -331,6 +338,7 @@ Resources:
'''
def test_vpc(self):
self.mock_keystone()
self.mock_create_network()
self.mock_delete_network()
self.m.ReplayAll()
@ -362,6 +370,7 @@ Resources:
'''
def test_subnet(self):
self.mock_keystone()
self.mock_create_network()
self.mock_create_subnet()
self.mock_delete_subnet()
@ -538,6 +547,7 @@ Resources:
quantumclient.Client.delete_port('dddd').AndReturn(None)
def test_network_interface(self):
self.mock_keystone()
self.mock_create_security_group()
self.mock_create_network()
self.mock_create_subnet()
@ -565,6 +575,7 @@ Resources:
self.m.VerifyAll()
def test_network_interface_no_groupset(self):
self.mock_keystone()
self.mock_create_network()
self.mock_create_subnet()
self.mock_show_subnet()
@ -592,6 +603,7 @@ Resources:
self.assertEquals(str(expected_exception), str(real_exception))
def test_network_interface_error_no_ref(self):
self.mock_keystone()
self.mock_create_network()
self.mock_create_subnet()
self.mock_show_subnet()
@ -667,6 +679,7 @@ Resources:
quantumclient.Client.remove_gateway_router('ffff').AndReturn(None)
def test_internet_gateway(self):
self.mock_keystone()
self.mock_create_internet_gateway()
self.mock_create_network()
self.mock_create_subnet()
@ -727,6 +740,7 @@ Resources:
'''
def test_route_table(self):
self.mock_keystone()
self.mock_create_network()
self.mock_create_subnet()
self.mock_create_route_table()

View File

@ -102,7 +102,8 @@ def dummy_context(user='test_username', tenant_id='test_tenant_id',
'username': user,
'password': password,
'roles': roles,
'auth_url': 'http://localhost:5000/v2.0'
'auth_url': 'http://localhost:5000/v2.0',
'auth_token': 'abcd1234'
})