Merge "Improve OpenStack clients API"
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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.'")
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user