From fb5fe068660319d079da6c44aab7d7e639c8a015 Mon Sep 17 00:00:00 2001 From: Steve Baker Date: Thu, 8 Nov 2012 15:49:19 +1300 Subject: [PATCH] Move client connection out of resources.py. Client connection references are currently cached at the resource instance level, which doesn't seem very useful. This change moves the client connection and caching to clients.py, then puts a Clients instance in a Stack. This means that (for example) all requests to nova in one stack will come from the same client instance. Change-Id: I22519f8ae4278ad128d3785d090294285f3a1b89 --- heat/engine/clients.py | 191 ++++++++++++++++++++++++++++++ heat/engine/parser.py | 2 + heat/engine/resources/resource.py | 155 +----------------------- 3 files changed, 197 insertions(+), 151 deletions(-) create mode 100644 heat/engine/clients.py diff --git a/heat/engine/clients.py b/heat/engine/clients.py new file mode 100644 index 0000000000..2ebf833589 --- /dev/null +++ b/heat/engine/clients.py @@ -0,0 +1,191 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# +# 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 novaclient.v1_1 import client as nc +from keystoneclient.v2_0 import client as kc + +# swiftclient not available in all distributions - make s3 an optional +# feature +try: + from swiftclient import client as swiftclient + swiftclient_present = True +except ImportError: + swiftclient_present = False +# quantumclient not available in all distributions - make quantum an optional +# feature +try: + from quantumclient.v2_0 import client as quantumclient + quantumclient_present = True +except ImportError: + quantumclient_present = False + +from heat.openstack.common import log as logging + +logger = logging.getLogger('heat.engine.clients') + + +class Clients(object): + ''' + Convenience class to create and cache client instances. + ''' + + def __init__(self, context): + self.context = context + self._nova = {} + self._keystone = None + self._swift = None + self._quantum = None + + def keystone(self): + if self._keystone: + return self._keystone + + con = self.context + args = { + 'auth_url': con.auth_url, + } + + if con.password is not None: + args['username'] = con.username + args['password'] = con.password + args['tenant_name'] = con.tenant + args['tenant_id'] = con.tenant_id + elif con.auth_token is not None: + args['username'] = con.service_user + args['password'] = con.service_password + args['tenant_name'] = con.service_tenant + args['token'] = con.auth_token + else: + logger.error("Keystone connection failed, no password or " + + "auth_token!") + return None + + client = kc.Client(**args) + client.authenticate() + self._keystone = client + return self._keystone + + def nova(self, service_type='compute'): + if service_type in self._nova: + return self._nova[service_type] + + con = self.context + args = { + 'project_id': con.tenant, + 'auth_url': con.auth_url, + 'service_type': service_type, + } + + if con.password is not None: + args['username'] = con.username + args['api_key'] = con.password + elif con.auth_token is not None: + args['username'] = con.service_user + args['api_key'] = con.service_password + args['project_id'] = con.service_tenant + args['proxy_token'] = con.auth_token + args['proxy_tenant_id'] = con.tenant_id + else: + logger.error("Nova connection failed, no password or auth_token!") + return None + + client = None + try: + # Workaround for issues with python-keyring, need no_cache=True + # ref https://bugs.launchpad.net/python-novaclient/+bug/1020238 + # TODO(shardy): May be able to remove when the bug above is fixed + client = nc.Client(no_cache=True, **args) + client.authenticate() + self._nova[service_type] = client + except TypeError: + # for compatibility with essex, which doesn't have no_cache=True + # TODO(shardy): remove when we no longer support essex + client = nc.Client(**args) + client.authenticate() + self._nova[service_type] = client + + return client + + def swift(self): + if swiftclient_present == False: + return None + if self._swift: + return self._swift + + con = self.context + args = { + 'auth_version': '2' + } + + if con.password is not None: + args['user'] = con.username + args['key'] = con.password + args['authurl'] = con.auth_url + args['tenant_name'] = con.tenant + elif con.auth_token is not None: + args['user'] = None + args['key'] = None + args['authurl'] = None + args['preauthtoken'] = con.auth_token + # Lookup endpoint for object-store service type + service_type = 'object-store' + endpoints = self.keystone().service_catalog.get_endpoints( + service_type=service_type) + if len(endpoints[service_type]) == 1: + args['preauthurl'] = endpoints[service_type][0]['publicURL'] + else: + logger.error("No endpoint found for %s service type" % + service_type) + return None + else: + logger.error("Swift connection failed, no password or " + + "auth_token!") + return None + + self._swift = swiftclient.Connection(**args) + return self._swift + + def quantum(self): + if quantumclient_present == False: + return None + if self._quantum: + logger.debug('using existing _quantum') + return self._quantum + + con = self.context + args = { + 'auth_url': con.auth_url, + '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['username'] = con.service_user + args['password'] = con.service_password + args['tenant_name'] = con.service_tenant + args['token'] = con.auth_token + 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 diff --git a/heat/engine/parser.py b/heat/engine/parser.py index 2fc045d744..fc40a032a4 100644 --- a/heat/engine/parser.py +++ b/heat/engine/parser.py @@ -26,6 +26,7 @@ from heat.engine import template from heat.engine import timestamp from heat.engine.parameters import Parameters from heat.engine.template import Template +from heat.engine.clients import Clients from heat.db import api as db_api from heat.openstack.common import log as logging @@ -61,6 +62,7 @@ class Stack(object): ''' self.id = stack_id self.context = context + self.clients = Clients(context) self.t = tmpl self.name = stack_name self.state = state diff --git a/heat/engine/resources/resource.py b/heat/engine/resources/resource.py index 186f588012..a605a13376 100644 --- a/heat/engine/resources/resource.py +++ b/heat/engine/resources/resource.py @@ -16,24 +16,6 @@ import base64 from datetime import datetime -from novaclient.v1_1 import client as nc -from keystoneclient.v2_0 import client as kc - -# swiftclient not available in all distributions - make s3 an optional -# feature -try: - from swiftclient import client as swiftclient - swiftclient_present = True -except ImportError: - swiftclient_present = False -# quantumclient not available in all distributions - make quantum an optional -# feature -try: - from quantumclient.v2_0 import client as quantumclient - quantumclient_present = True -except ImportError: - quantumclient_present = False - from heat.common import exception from heat.common import config from heat.db import api as db_api @@ -184,145 +166,16 @@ class Resource(object): deps += (self, None) def keystone(self): - if self._keystone: - return self._keystone - - con = self.context - args = { - 'auth_url': con.auth_url, - } - - if con.password is not None: - args['username'] = con.username - args['password'] = con.password - args['tenant_name'] = con.tenant - args['tenant_id'] = con.tenant_id - elif con.auth_token is not None: - args['username'] = con.service_user - args['password'] = con.service_password - args['tenant_name'] = con.service_tenant - args['token'] = con.auth_token - else: - logger.error("Keystone connection failed, no password or " + - "auth_token!") - return None - - client = kc.Client(**args) - client.authenticate() - self._keystone = client - return self._keystone + return self.stack.clients.keystone() def nova(self, service_type='compute'): - if service_type in self._nova: - return self._nova[service_type] - - con = self.context - args = { - 'project_id': con.tenant, - 'auth_url': con.auth_url, - 'service_type': service_type, - } - - if con.password is not None: - args['username'] = con.username - args['api_key'] = con.password - elif con.auth_token is not None: - args['username'] = con.service_user - args['api_key'] = con.service_password - args['project_id'] = con.service_tenant - args['proxy_token'] = con.auth_token - args['proxy_tenant_id'] = con.tenant_id - else: - logger.error("Nova connection failed, no password or auth_token!") - return None - - client = None - try: - # Workaround for issues with python-keyring, need no_cache=True - # ref https://bugs.launchpad.net/python-novaclient/+bug/1020238 - # TODO(shardy): May be able to remove when the bug above is fixed - client = nc.Client(no_cache=True, **args) - client.authenticate() - self._nova[service_type] = client - except TypeError: - # for compatibility with essex, which doesn't have no_cache=True - # TODO(shardy): remove when we no longer support essex - client = nc.Client(**args) - client.authenticate() - self._nova[service_type] = client - - return client + return self.stack.clients.nova(service_type) def swift(self): - if swiftclient_present == False: - return None - if self._swift: - return self._swift - - con = self.context - args = { - 'auth_version': '2' - } - - if con.password is not None: - args['user'] = con.username - args['key'] = con.password - args['authurl'] = con.auth_url - args['tenant_name'] = con.tenant - elif con.auth_token is not None: - args['user'] = None - args['key'] = None - args['authurl'] = None - args['preauthtoken'] = con.auth_token - # Lookup endpoint for object-store service type - service_type = 'object-store' - endpoints = self.keystone().service_catalog.get_endpoints( - service_type=service_type) - if len(endpoints[service_type]) == 1: - args['preauthurl'] = endpoints[service_type][0]['publicURL'] - else: - logger.error("No endpoint found for %s service type" % - service_type) - return None - else: - logger.error("Swift connection failed, no password or " + - "auth_token!") - return None - - self._swift = swiftclient.Connection(**args) - return self._swift + return self.stack.clients.swift() def quantum(self): - if quantumclient_present == False: - return None - if self._quantum: - logger.debug('using existing _quantum') - return self._quantum - - con = self.context - args = { - 'auth_url': con.auth_url, - '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['username'] = con.service_user - args['password'] = con.service_password - args['tenant_name'] = con.service_tenant - args['token'] = con.auth_token - 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 + return self.stack.clients.quantum() def create(self): '''