blob: f6a46ff57e23f5a8a33a70547068d9d2df8b1750 [file] [log] [blame]
Vasyl Saienko057b0ca2019-04-27 09:20:20 +03001import functools
Oleksiy Petrenko95664c02018-04-19 17:05:16 +03002import logging
3import os_client_config
Vasyl Saienko057b0ca2019-04-27 09:20:20 +03004import time
Oleksiy Petrenko95664c02018-04-19 17:05:16 +03005from uuid import UUID
Vasyl Saienko057b0ca2019-04-27 09:20:20 +03006
Oleksiy Petrenko95664c02018-04-19 17:05:16 +03007try:
8 from urllib.parse import urlsplit
9except ImportError:
10 from urlparse import urlsplit
11
12log = logging.getLogger(__name__)
13
14
15class BarbicanException(Exception):
16
17 _msg = "Barbican module exception occured."
18
19 def __init__(self, message=None, **kwargs):
20 super(BarbicanException, self).__init__(message or self._msg)
21
22
23class NoBarbicanEndpoint(BarbicanException):
24 _msg = "Barbican endpoint not found in keystone catalog."
25
26
27class NoAuthPluginConfigured(BarbicanException):
28 _msg = ("You are using keystoneauth auth plugin that does not support "
29 "fetching endpoint list from token (noauth or admin_token).")
30
31
32class NoCredentials(BarbicanException):
33 _msg = "Please provide cloud name present in clouds.yaml."
34
35
36class ResourceNotFound(BarbicanException):
37 _msg = "Uniq resource: {resource} with name: {name} not found."
38
39 def __init__(self, resource, name, **kwargs):
40 super(BarbicanException, self).__init__(
41 self._msg.format(resource=resource, name=name))
42
43
44class MultipleResourcesFound(BarbicanException):
45 _msg = "Multiple resource: {resource} with name: {name} found."
46
47 def __init__(self, resource, name, **kwargs):
48 super(BarbicanException, self).__init__(
49 self._msg.format(resource=resource, name=name))
50
51
52def _get_raw_client(cloud_name):
53 service_type = 'key-manager'
Vasyl Saienko1e36a462018-06-01 12:46:18 +030054 config = os_client_config.OpenStackConfig()
55 cloud = config.get_one_cloud(cloud_name)
56 adapter = cloud.get_session_client(service_type)
57 adapter.version = '1'
Oleksiy Petrenko95664c02018-04-19 17:05:16 +030058 try:
59 access_info = adapter.session.auth.get_access(adapter.session)
60 endpoints = access_info.service_catalog.get_endpoints()
Vasyl Saienko1e36a462018-06-01 12:46:18 +030061 except (AttributeError, ValueError) as exc:
62 log.exception('%s' % exc)
Oleksiy Petrenko95664c02018-04-19 17:05:16 +030063 e = NoAuthPluginConfigured()
64 log.exception('%s' % e)
65 raise e
66 if service_type not in endpoints:
67 if not service_type:
68 e = NoBarbicanEndpoint()
69 log.error('%s' % e)
70 raise e
71 return adapter
72
73
74def send(method):
75 def wrap(func):
Vasyl Saienko057b0ca2019-04-27 09:20:20 +030076 @functools.wraps(func)
Oleksiy Petrenko95664c02018-04-19 17:05:16 +030077 def wrapped_f(*args, **kwargs):
78 cloud_name = kwargs.pop('cloud_name')
Vasyl Saienko057b0ca2019-04-27 09:20:20 +030079 connect_retries = 30
80 connect_retry_delay = 1
Oleksiy Petrenko95664c02018-04-19 17:05:16 +030081 if not cloud_name:
82 e = NoCredentials()
83 log.error('%s' % e)
84 raise e
85 adapter = _get_raw_client(cloud_name)
86 # Remove salt internal kwargs
87 kwarg_keys = list(kwargs.keys())
88 for k in kwarg_keys:
89 if k.startswith('__'):
90 kwargs.pop(k)
91 url, request_kwargs = func(*args, **kwargs)
Vasyl Saienko057b0ca2019-04-27 09:20:20 +030092 response = None
93 for i in range(connect_retries):
94 try:
95 response = getattr(adapter, method)(
96 url, connect_retries=connect_retries,
97 **request_kwargs)
98 except Exception as e:
99 if not hasattr(e, 'http_status') or (e.http_status >= 500
100 or e.http_status == 0):
101 msg = ("Got retriable exception when contacting "
102 "Barbican API. Sleeping for %ss. Attepmpts "
103 "%s of %s")
104 log.error(msg % (connect_retry_delay, i, connect_retries))
105 time.sleep(connect_retry_delay)
106 continue
107 break
108 if not response or not response.content:
Oleksiy Petrenko95664c02018-04-19 17:05:16 +0300109 return {}
110 try:
111 resp = response.json()
Vasyl Saienko057b0ca2019-04-27 09:20:20 +0300112 except ValueError:
Oleksiy Petrenko95664c02018-04-19 17:05:16 +0300113 resp = response.content
114 return resp
115 return wrapped_f
116 return wrap
117
118
119def _check_uuid(val):
120 try:
121 return str(UUID(val)).replace('-', '') == val
122 except (TypeError, ValueError, AttributeError):
123 return False
124
125
126def _parse_secret_href(href):
127 return urlsplit(href).path.split('/')[-1]
128
129
130def get_by_name_or_uuid(resource_list, resp_key):
131 def wrap(func):
132 def wrapped_f(*args, **kwargs):
133 if 'name' in kwargs:
134 ref = kwargs.pop('name', None)
135 start_arg = 0
136 else:
137 start_arg = 1
138 ref = args[0]
139 cloud_name = kwargs['cloud_name']
140 if _check_uuid(ref):
141 uuid = ref
142 else:
Vasyl Saienko057b0ca2019-04-27 09:20:20 +0300143 retries = 30
144 while retries:
Oleksiy Petrenko95664c02018-04-19 17:05:16 +0300145 # Then we have name not uuid
Vasyl Saienko057b0ca2019-04-27 09:20:20 +0300146 resp = resource_list(
147 name=ref, cloud_name=cloud_name).get(resp_key)
148 if resp is not None:
149 break
150 retries -= 1
151 time.sleep(1)
Oleksiy Petrenko95664c02018-04-19 17:05:16 +0300152 if len(resp) == 0:
153 raise ResourceNotFound(resp_key, ref)
154 elif len(resp) > 1:
155 raise MultipleResourcesFound(resp_key, ref)
156 href = resp[0]['secret_ref']
157 uuid = _parse_secret_href(href)
158 return func(uuid, *args[start_arg:], **kwargs)
159 return wrapped_f
160 return wrap