blob: abea33d85b800440870a05d2dd5163645ac4d8b5 [file] [log] [blame]
Vasyl Saienko4eda4f22018-04-26 19:30:39 +03001import logging
2import os_client_config
Oleh Hryhorov80691a32019-02-26 13:45:12 +00003import functools
4import time
5
6from keystoneclient import exceptions as ks_exceptions
Vasyl Saienko4eda4f22018-04-26 19:30:39 +03007
8log = logging.getLogger(__name__)
9
10
11class KeystoneException(Exception):
12
13 _msg = "Keystone module exception occured."
14
15 def __init__(self, message=None, **kwargs):
16 super(KeystoneException, self).__init__(message or self._msg)
17
18
19class NoKeystoneEndpoint(KeystoneException):
20 _msg = "Keystone endpoint not found in keystone catalog."
21
22
23class NoAuthPluginConfigured(KeystoneException):
24 _msg = ("You are using keystoneauth auth plugin that does not support "
25 "fetching endpoint list from token (noauth or admin_token).")
26
27
28class NoCredentials(KeystoneException):
29 _msg = "Please provide cloud name present in clouds.yaml."
30
31
32class ResourceNotFound(KeystoneException):
33 _msg = "Uniq resource: {resource} with name: {name} not found."
34
35 def __init__(self, resource, name, **kwargs):
36 super(KeystoneException, self).__init__(
37 self._msg.format(resource=resource, name=name))
38
39
40class MultipleResourcesFound(KeystoneException):
41 _msg = "Multiple resource: {resource} with name: {name} found."
42
43 def __init__(self, resource, name, **kwargs):
44 super(KeystoneException, self).__init__(
45 self._msg.format(resource=resource, name=name))
46
47
48def _get_raw_client(cloud_name):
49 service_type = 'identity'
50 config = os_client_config.OpenStackConfig()
51 cloud = config.get_one_cloud(cloud_name)
52 adapter = cloud.get_session_client(service_type)
53 adapter.version = '3'
54 try:
55 access_info = adapter.session.auth.get_access(adapter.session)
56 endpoints = access_info.service_catalog.get_endpoints()
57 except (AttributeError, ValueError):
58 e = NoAuthPluginConfigured()
59 log.exception('%s' % e)
60 raise e
61 if service_type not in endpoints:
62 if not service_type:
63 e = NoKeystoneEndpoint()
64 log.error('%s' % e)
65 raise e
66 return adapter
67
68
69def send(method, microversion_header=None):
70 def wrap(func):
Oleh Hryhorov80691a32019-02-26 13:45:12 +000071 @functools.wraps(func)
Vasyl Saienko4eda4f22018-04-26 19:30:39 +030072 def wrapped_f(*args, **kwargs):
73 headers = kwargs.pop('headers', {})
74 if kwargs.get('microversion'):
75 headers.setdefault(microversion_header,
76 kwargs.get('microversion'))
77 cloud_name = kwargs.pop('cloud_name')
Oleh Hryhorov80691a32019-02-26 13:45:12 +000078 connect_retries = 30
79 connect_retry_delay = 1
Vasyl Saienko4eda4f22018-04-26 19:30:39 +030080 if not cloud_name:
81 e = NoCredentials()
82 log.error('%s' % e)
83 raise e
Oleh Hryhorov80691a32019-02-26 13:45:12 +000084 adapter = None
85 for i in range(connect_retries):
86 try:
87 adapter = _get_raw_client(cloud_name)
88 except (ks_exceptions.DiscoveryFailure) as e:
89 msg = ("Got exception when determining a suitable "
90 "URL for Keystone plugin. Sleeping for %ss. Attepmpts "
91 "%s of %s")
92 log.error(msg % (connect_retry_delay, i, connect_retries))
93 time.sleep(connect_retry_delay)
94 continue
95 break
96 if not adapter:
97 raise ks_exceptions.DiscoveryFailure("Could not connect to Keystone API to determine a suitable URL for the plugin")
Vasyl Saienko4eda4f22018-04-26 19:30:39 +030098 # Remove salt internal kwargs
99 kwarg_keys = list(kwargs.keys())
100 for k in kwarg_keys:
101 if k.startswith('__'):
102 kwargs.pop(k)
Oleh Hryhorov80691a32019-02-26 13:45:12 +0000103 url, json = func(*args, **kwargs)
104 response = None
Oleksandr Shyshko322b0d02019-04-18 13:16:31 +0000105 last_exception = None
Oleh Hryhorov80691a32019-02-26 13:45:12 +0000106 for i in range(connect_retries):
107 try:
108 response = getattr(adapter, method)(
109 url, connect_retries=connect_retries,
110 json=json)
111 except Exception as e:
Oleksandr Shyshko322b0d02019-04-18 13:16:31 +0000112 last_exception = e
Oleh Hryhorov80691a32019-02-26 13:45:12 +0000113 if not hasattr(e, 'http_status') or (e.http_status >= 500
114 or e.http_status == 0):
115 msg = ("Got retriable exception when contacting "
116 "Keystone API. Sleeping for %ss. Attepmpts "
117 "%s of %s")
118 log.error(msg % (connect_retry_delay, i, connect_retries))
119 time.sleep(connect_retry_delay)
120 continue
Oleksandr Shyshko322b0d02019-04-18 13:16:31 +0000121 else:
122 break
123 else:
124 last_exception = None
125 break
126 if last_exception:
127 raise KeystoneException(last_exception.message)
Oleh Hryhorov80691a32019-02-26 13:45:12 +0000128 if not response or not response.content:
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300129 return {}
130 try:
131 resp = response.json()
Oleh Hryhorov80691a32019-02-26 13:45:12 +0000132 except ValueError:
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300133 resp = response.content
134 return resp
135 return wrapped_f
136 return wrap