blob: 80d927f5238841fba61ee65ca6317b053e381872 [file] [log] [blame]
Vasyl Saienko2eb2a3b2019-04-27 09:20:20 +03001import functools
Oleksiy Petrenko95664c02018-04-19 17:05:16 +03002import logging
Vasyl Saienko2eb2a3b2019-04-27 09:20:20 +03003import time
Oleksiy Petrenko95664c02018-04-19 17:05:16 +03004from uuid import UUID
Oleksiy Petrenko7cd57572019-02-06 12:50:15 +02005
6
7try:
8 import os_client_config
9except ImportError:
10 os_client_config = None
11from salt import exceptions
12
13
Oleksiy Petrenko95664c02018-04-19 17:05:16 +030014try:
15 from urllib.parse import urlsplit
16except ImportError:
17 from urlparse import urlsplit
18
19log = logging.getLogger(__name__)
20
21
22class BarbicanException(Exception):
23
24 _msg = "Barbican module exception occured."
25
26 def __init__(self, message=None, **kwargs):
27 super(BarbicanException, self).__init__(message or self._msg)
28
29
30class NoBarbicanEndpoint(BarbicanException):
31 _msg = "Barbican endpoint not found in keystone catalog."
32
33
34class NoAuthPluginConfigured(BarbicanException):
35 _msg = ("You are using keystoneauth auth plugin that does not support "
36 "fetching endpoint list from token (noauth or admin_token).")
37
38
39class NoCredentials(BarbicanException):
40 _msg = "Please provide cloud name present in clouds.yaml."
41
42
43class ResourceNotFound(BarbicanException):
44 _msg = "Uniq resource: {resource} with name: {name} not found."
45
46 def __init__(self, resource, name, **kwargs):
47 super(BarbicanException, self).__init__(
48 self._msg.format(resource=resource, name=name))
49
50
51class MultipleResourcesFound(BarbicanException):
52 _msg = "Multiple resource: {resource} with name: {name} found."
53
54 def __init__(self, resource, name, **kwargs):
55 super(BarbicanException, self).__init__(
56 self._msg.format(resource=resource, name=name))
57
58
59def _get_raw_client(cloud_name):
Oleksiy Petrenko7cd57572019-02-06 12:50:15 +020060 if not os_client_config:
61 raise exceptions.SaltInvocationError(
62 "Cannot load os-client-config. Please check your environment "
63 "configuration.")
Oleksiy Petrenko95664c02018-04-19 17:05:16 +030064 service_type = 'key-manager'
Vasyl Saienko1e36a462018-06-01 12:46:18 +030065 config = os_client_config.OpenStackConfig()
66 cloud = config.get_one_cloud(cloud_name)
67 adapter = cloud.get_session_client(service_type)
68 adapter.version = '1'
Oleksiy Petrenko95664c02018-04-19 17:05:16 +030069 try:
70 access_info = adapter.session.auth.get_access(adapter.session)
71 endpoints = access_info.service_catalog.get_endpoints()
Vasyl Saienko1e36a462018-06-01 12:46:18 +030072 except (AttributeError, ValueError) as exc:
73 log.exception('%s' % exc)
Oleksiy Petrenko95664c02018-04-19 17:05:16 +030074 e = NoAuthPluginConfigured()
75 log.exception('%s' % e)
76 raise e
77 if service_type not in endpoints:
78 if not service_type:
79 e = NoBarbicanEndpoint()
80 log.error('%s' % e)
81 raise e
82 return adapter
83
84
85def send(method):
86 def wrap(func):
Vasyl Saienko2eb2a3b2019-04-27 09:20:20 +030087 @functools.wraps(func)
Oleksiy Petrenko95664c02018-04-19 17:05:16 +030088 def wrapped_f(*args, **kwargs):
89 cloud_name = kwargs.pop('cloud_name')
Vasyl Saienko2eb2a3b2019-04-27 09:20:20 +030090 connect_retries = 30
91 connect_retry_delay = 1
Oleksiy Petrenko95664c02018-04-19 17:05:16 +030092 if not cloud_name:
93 e = NoCredentials()
94 log.error('%s' % e)
95 raise e
96 adapter = _get_raw_client(cloud_name)
97 # Remove salt internal kwargs
98 kwarg_keys = list(kwargs.keys())
99 for k in kwarg_keys:
100 if k.startswith('__'):
101 kwargs.pop(k)
102 url, request_kwargs = func(*args, **kwargs)
Vasyl Saienko2eb2a3b2019-04-27 09:20:20 +0300103 response = None
104 for i in range(connect_retries):
105 try:
106 response = getattr(adapter, method)(
107 url, connect_retries=connect_retries,
108 **request_kwargs)
109 except Exception as e:
110 if not hasattr(e, 'http_status') or (e.http_status >= 500
111 or e.http_status == 0):
112 msg = ("Got retriable exception when contacting "
113 "Barbican API. Sleeping for %ss. Attepmpts "
114 "%s of %s")
115 log.error(msg % (connect_retry_delay, i, connect_retries))
116 time.sleep(connect_retry_delay)
117 continue
118 break
119 if not response or not response.content:
Oleksiy Petrenko95664c02018-04-19 17:05:16 +0300120 return {}
121 try:
122 resp = response.json()
Vasyl Saienko2eb2a3b2019-04-27 09:20:20 +0300123 except ValueError:
Oleksiy Petrenko95664c02018-04-19 17:05:16 +0300124 resp = response.content
125 return resp
126 return wrapped_f
127 return wrap
128
129
130def _check_uuid(val):
131 try:
132 return str(UUID(val)).replace('-', '') == val
133 except (TypeError, ValueError, AttributeError):
134 return False
135
136
137def _parse_secret_href(href):
138 return urlsplit(href).path.split('/')[-1]
139
140
141def get_by_name_or_uuid(resource_list, resp_key):
142 def wrap(func):
143 def wrapped_f(*args, **kwargs):
144 if 'name' in kwargs:
145 ref = kwargs.pop('name', None)
146 start_arg = 0
147 else:
148 start_arg = 1
149 ref = args[0]
150 cloud_name = kwargs['cloud_name']
151 if _check_uuid(ref):
152 uuid = ref
153 else:
Vasyl Saienko2eb2a3b2019-04-27 09:20:20 +0300154 retries = 30
155 while retries:
Oleksiy Petrenko95664c02018-04-19 17:05:16 +0300156 # Then we have name not uuid
Vasyl Saienko2eb2a3b2019-04-27 09:20:20 +0300157 resp = resource_list(
158 name=ref, cloud_name=cloud_name).get(resp_key)
159 if resp is not None:
160 break
161 retries -= 1
162 time.sleep(1)
Oleksiy Petrenko95664c02018-04-19 17:05:16 +0300163 if len(resp) == 0:
164 raise ResourceNotFound(resp_key, ref)
165 elif len(resp) > 1:
166 raise MultipleResourcesFound(resp_key, ref)
167 href = resp[0]['secret_ref']
168 uuid = _parse_secret_href(href)
169 return func(uuid, *args[start_arg:], **kwargs)
170 return wrapped_f
171 return wrap