blob: 9a7744b48974477e61918d2120429150f9f0850e [file] [log] [blame]
Oleh Hryhorov7594fa82019-02-26 13:45:12 +00001import functools
Vasyl Saienko4eda4f22018-04-26 19:30:39 +03002import logging
Oleh Hryhorov7594fa82019-02-26 13:45:12 +00003import time
Oleksiy Petrenko77a7e9c2019-02-06 13:03:53 +02004
5try:
6 import os_client_config
7except ImportError:
8 os_client_config = None
9from salt import exceptions
Vasyl Saienko4eda4f22018-04-26 19:30:39 +030010
Oleh Hryhorov7594fa82019-02-26 13:45:12 +000011from keystoneclient import exceptions as ks_exceptions
12
Vasyl Saienko4eda4f22018-04-26 19:30:39 +030013log = logging.getLogger(__name__)
14
15
16class KeystoneException(Exception):
17
18 _msg = "Keystone module exception occured."
19
20 def __init__(self, message=None, **kwargs):
21 super(KeystoneException, self).__init__(message or self._msg)
22
23
24class NoKeystoneEndpoint(KeystoneException):
25 _msg = "Keystone endpoint not found in keystone catalog."
26
27
28class NoAuthPluginConfigured(KeystoneException):
29 _msg = ("You are using keystoneauth auth plugin that does not support "
30 "fetching endpoint list from token (noauth or admin_token).")
31
32
33class NoCredentials(KeystoneException):
34 _msg = "Please provide cloud name present in clouds.yaml."
35
36
37class ResourceNotFound(KeystoneException):
38 _msg = "Uniq resource: {resource} with name: {name} not found."
39
40 def __init__(self, resource, name, **kwargs):
41 super(KeystoneException, self).__init__(
42 self._msg.format(resource=resource, name=name))
43
44
45class MultipleResourcesFound(KeystoneException):
46 _msg = "Multiple resource: {resource} with name: {name} found."
47
48 def __init__(self, resource, name, **kwargs):
49 super(KeystoneException, self).__init__(
50 self._msg.format(resource=resource, name=name))
51
52
53def _get_raw_client(cloud_name):
Oleksiy Petrenko77a7e9c2019-02-06 13:03:53 +020054 if not os_client_config:
55 raise exceptions.SaltInvocationError(
56 "Cannot load os-client-config. Please check your environment "
57 "configuration.")
Vasyl Saienko4eda4f22018-04-26 19:30:39 +030058 service_type = 'identity'
59 config = os_client_config.OpenStackConfig()
60 cloud = config.get_one_cloud(cloud_name)
61 adapter = cloud.get_session_client(service_type)
62 adapter.version = '3'
63 try:
64 access_info = adapter.session.auth.get_access(adapter.session)
65 endpoints = access_info.service_catalog.get_endpoints()
66 except (AttributeError, ValueError):
67 e = NoAuthPluginConfigured()
68 log.exception('%s' % e)
69 raise e
70 if service_type not in endpoints:
71 if not service_type:
72 e = NoKeystoneEndpoint()
73 log.error('%s' % e)
74 raise e
75 return adapter
76
77
78def send(method, microversion_header=None):
79 def wrap(func):
Oleh Hryhorov7594fa82019-02-26 13:45:12 +000080 @functools.wraps(func)
Vasyl Saienko4eda4f22018-04-26 19:30:39 +030081 def wrapped_f(*args, **kwargs):
82 headers = kwargs.pop('headers', {})
83 if kwargs.get('microversion'):
84 headers.setdefault(microversion_header,
85 kwargs.get('microversion'))
86 cloud_name = kwargs.pop('cloud_name')
Oleh Hryhorov7594fa82019-02-26 13:45:12 +000087 connect_retries = 30
88 connect_retry_delay = 1
Vasyl Saienko4eda4f22018-04-26 19:30:39 +030089 if not cloud_name:
90 e = NoCredentials()
91 log.error('%s' % e)
92 raise e
Oleh Hryhorov7594fa82019-02-26 13:45:12 +000093 adapter = None
94 for i in range(connect_retries):
95 try:
96 adapter = _get_raw_client(cloud_name)
97 except (ks_exceptions.DiscoveryFailure) as e:
98 msg = ("Got exception when determining a suitable "
99 "URL for Keystone plugin. Sleeping for %ss. Attepmpts "
100 "%s of %s")
101 log.error(msg % (connect_retry_delay, i, connect_retries))
102 time.sleep(connect_retry_delay)
103 continue
104 break
105 if not adapter:
106 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 +0300107 # Remove salt internal kwargs
108 kwarg_keys = list(kwargs.keys())
109 for k in kwarg_keys:
110 if k.startswith('__'):
111 kwargs.pop(k)
Oleh Hryhorov7594fa82019-02-26 13:45:12 +0000112 url, json = func(*args, **kwargs)
113 response = None
114 for i in range(connect_retries):
115 try:
116 response = getattr(adapter, method)(
117 url, connect_retries=connect_retries,
118 json=json)
119 except Exception as e:
120 if not hasattr(e, 'http_status') or (e.http_status >= 500
121 or e.http_status == 0):
122 msg = ("Got retriable exception when contacting "
123 "Keystone API. Sleeping for %ss. Attepmpts "
124 "%s of %s")
125 log.error(msg % (connect_retry_delay, i, connect_retries))
126 time.sleep(connect_retry_delay)
127 continue
128 break
129 if not response or not response.content:
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300130 return {}
131 try:
132 resp = response.json()
Oleh Hryhorov7594fa82019-02-26 13:45:12 +0000133 except ValueError:
Vasyl Saienko4eda4f22018-04-26 19:30:39 +0300134 resp = response.content
135 return resp
136 return wrapped_f
137 return wrap