Merge "Improve OpenStack clients API"

This commit is contained in:
Jenkins
2015-12-01 16:32:04 +00:00
committed by Gerrit Code Review
9 changed files with 266 additions and 86 deletions

View File

@@ -16,34 +16,32 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
from urlparse import urlparse
from ceilometerclient import client
from ceilometerclient.exc import HTTPUnauthorized
from watcher.common import keystone
class Client(object):
# todo olso conf: this must be sync with ceilometer
CEILOMETER_API_VERSION = '2'
class CeilometerClient(object):
def __init__(self, api_version='2'):
self._cmclient = None
self._api_version = api_version
def __init__(self):
ksclient = keystone.Client()
self.creds = ksclient.get_credentials()
self.creds['os_auth_token'] = ksclient.get_token()
self.creds['token'] = ksclient.get_token()
self.creds['ceilometer_url'] = "http://" + urlparse(
ksclient.get_endpoint(
service_type='metering',
endpoint_type='publicURL')).netloc
self.connect()
def connect(self):
@property
def cmclient(self):
"""Initialization of Ceilometer client."""
self.cmclient = client.get_client(self.CEILOMETER_API_VERSION,
**self.creds)
if not self._cmclient:
ksclient = keystone.KeystoneClient()
creds = ksclient.get_credentials()
endpoint = ksclient.get_endpoint(
service_type='metering',
endpoint_type='publicURL')
self._cmclient = client.get_client(self._api_version,
ceilometer_url=endpoint,
**creds)
return self._cmclient
def build_query(user_id=None, tenant_id=None, resource_id=None,
def build_query(self, user_id=None, tenant_id=None, resource_id=None,
user_ids=None, tenant_ids=None, resource_ids=None):
"""Returns query built from given parameters.
This query can be then used for querying resources, meters and
@@ -78,37 +76,32 @@ class Client(object):
return query
def query_sample(self, meter_name, query, limit=1):
def query_retry(self, f, *args, **kargs):
try:
samples = self.ceilometerclient().samples.list(
meter_name=meter_name,
limit=limit,
q=query)
return f(*args, **kargs)
except HTTPUnauthorized:
self.connect()
samples = self.ceilometerclient().samples.list(
meter_name=meter_name,
limit=limit,
q=query)
self.reset_client()
return f(*args, **kargs)
except Exception:
raise
return samples
def get_endpoint(self, service_type, endpoint_type=None):
ksclient = keystone.Client()
endpoint = ksclient.get_endpoint(service_type=service_type,
endpoint_type=endpoint_type)
return endpoint
def query_sample(self, meter_name, query, limit=1):
return self.query_retry(f=self.cmclient.samples.list,
meter_name=meter_name,
limit=limit,
q=query)
def statistic_list(self, meter_name, query=None, period=None):
"""List of statistics."""
statistics = self.ceilometerclient().statistics.list(
meter_name=meter_name, q=query, period=period)
statistics = self.cmclient.statistics.list(
meter_name=meter_name,
q=query,
period=period)
return statistics
def meter_list(self, query=None):
"""List the user's meters."""
meters = self.ceilometerclient().meters.list(query)
meters = self.query_retry(f=self.cmclient.meters.list, query=query)
return meters
def statistic_aggregation(self,
@@ -129,25 +122,14 @@ class Client(object):
"""Representing a statistic aggregate by operators"""
query = self.build_query(resource_id=resource_id)
try:
statistic = self.cmclient.statistics.list(
meter_name=meter_name,
q=query,
period=period,
aggregates=[
{'func': aggregate}],
groupby=['resource_id'])
except HTTPUnauthorized:
self.connect()
statistic = self.cmclient.statistics.list(
meter_name=meter_name,
q=query,
period=period,
aggregates=[
{'func': aggregate}],
groupby=['resource_id'])
except Exception:
raise
statistic = self.query_retry(f=self.cmclient.statistics.list,
meter_name=meter_name,
q=query,
period=period,
aggregates=[
{'func': aggregate}],
groupby=['resource_id'])
item_value = None
if statistic:
item_value = statistic[-1]._info.get('aggregate').get('avg')
@@ -171,3 +153,6 @@ class Client(object):
return samples[-1]._info['counter_volume']
else:
return False
def reset_client(self):
self._cmclient = None

View File

@@ -282,6 +282,10 @@ class NoDataFound(BaseException):
return self._desc
class KeystoneFailure(WatcherException):
message = _("'Keystone API endpoint is missing''")
class ClusterEmpty(WatcherException):
message = _("The list of hypervisor(s) in the cluster is empty.'")

View File

@@ -16,14 +16,19 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
import datetime
from keystoneclient.auth.identity import v3
from keystoneclient import session
import keystoneclient.v3.client as ksclient
from oslo_config import cfg
from oslo_log import log
from urlparse import urljoin
from urlparse import urlparse
from keystoneclient.auth.identity import generic
from keystoneclient import session as keystone_session
from watcher.common.exception import KeystoneFailure
LOG = log.getLogger(__name__)
CONF = cfg.CONF
@@ -35,30 +40,66 @@ CONF.import_opt('admin_password', 'keystonemiddleware.auth_token',
group='keystone_authtoken')
CONF.import_opt('auth_uri', 'keystonemiddleware.auth_token',
group='keystone_authtoken')
CONF.import_opt('auth_version', 'keystonemiddleware.auth_token',
group='keystone_authtoken')
CONF.import_opt('insecure', 'keystonemiddleware.auth_token',
group='keystone_authtoken')
class Client(object):
class KeystoneClient(object):
def __init__(self):
ks_args = self.get_credentials()
self.ks_client = ksclient.Client(**ks_args)
self._ks_client = None
self._session = None
self._auth = None
self._token = None
def get_endpoint(self, **kwargs):
kc = self._get_ksclient()
if not kc.has_service_catalog():
raise KeystoneFailure('No Keystone service catalog '
'loaded')
attr = None
filter_value = None
if kwargs.get('region_name'):
attr = 'region'
filter_value = kwargs.get('region_name')
return self.ks_client.service_catalog.url_for(
return self._get_ksclient().service_catalog.url_for(
service_type=kwargs.get('service_type') or 'metering',
attr=attr,
filter_value=filter_value,
endpoint_type=kwargs.get('endpoint_type') or 'publicURL')
def get_token(self):
return self.ks_client.auth_token
def _is_apiv3(self, auth_url, auth_version):
return auth_version == 'v3.0' or '/v3' in urlparse(auth_url).path
@staticmethod
def get_credentials():
def get_keystone_url(self, auth_url, auth_version):
"""Gives an http/https url to contact keystone.
"""
api_v3 = self._is_apiv3(auth_url, auth_version)
api_version = 'v3' if api_v3 else 'v2.0'
# NOTE(lucasagomes): Get rid of the trailing '/' otherwise urljoin()
# fails to override the version in the URL
return urljoin(auth_url.rstrip('/'), api_version)
def _get_ksclient(self):
"""Get an endpoint and auth token from Keystone.
"""
ks_args = self.get_credentials()
auth_version = CONF.keystone_authtoken.auth_version
auth_url = CONF.keystone_authtoken.auth_uri
api_version = self._is_apiv3(auth_url, auth_version)
if api_version:
from keystoneclient.v3 import client
else:
from keystoneclient.v2_0 import client
# generic
# ksclient = client.Client(version=api_version,
# session=session,**ks_args)
return client.Client(**ks_args)
def get_credentials(self):
creds = \
{'auth_url': CONF.keystone_authtoken.auth_uri,
'username': CONF.keystone_authtoken.admin_user,
@@ -71,10 +112,6 @@ class Client(object):
def get_session(self):
creds = self.get_credentials()
auth = v3.Password(**creds)
return session.Session(auth=auth)
def is_token_expired(self, token):
expires = datetime.datetime.strptime(token['expires'],
'%Y-%m-%dT%H:%M:%SZ')
return datetime.datetime.now() >= expires
self._auth = generic.Password(**creds)
session = keystone_session.Session(auth=self._auth)
return session