]>
git.proxmox.com Git - ceph.git/blob - ceph/qa/tasks/barbican.py
2 Deploy and configure Barbican for Teuthology
12 from urllib
.parse
import urlparse
14 from teuthology
import misc
as teuthology
15 from teuthology
import contextutil
16 from teuthology
.orchestra
import run
17 from teuthology
.exceptions
import ConfigError
19 log
= logging
.getLogger(__name__
)
22 @contextlib.contextmanager
23 def download(ctx
, config
):
25 Download the Barbican from github.
26 Remove downloaded file upon exit.
28 The context passed in should be identical to the context
29 passed in to the main task.
31 assert isinstance(config
, dict)
32 log
.info('Downloading barbican...')
33 testdir
= teuthology
.get_testdir(ctx
)
34 for (client
, cconf
) in config
.items():
35 branch
= cconf
.get('force-branch', 'master')
36 log
.info("Using branch '%s' for barbican", branch
)
38 sha1
= cconf
.get('sha1')
39 log
.info('sha1=%s', sha1
)
41 ctx
.cluster
.only(client
).run(
46 ctx
.cluster
.only(client
).run(
50 'https://github.com/openstack/barbican.git',
51 '{tdir}/barbican'.format(tdir
=testdir
),
55 ctx
.cluster
.only(client
).run(
57 'cd', '{tdir}/barbican'.format(tdir
=testdir
),
59 'git', 'reset', '--hard', sha1
,
65 log
.info('Removing barbican...')
66 testdir
= teuthology
.get_testdir(ctx
)
68 ctx
.cluster
.only(client
).run(
72 '{tdir}/barbican'.format(tdir
=testdir
),
76 def get_barbican_dir(ctx
):
77 return '{tdir}/barbican'.format(tdir
=teuthology
.get_testdir(ctx
))
79 def run_in_barbican_dir(ctx
, client
, args
):
80 ctx
.cluster
.only(client
).run(
81 args
=['cd', get_barbican_dir(ctx
), run
.Raw('&&'), ] + args
,
84 def run_in_barbican_venv(ctx
, client
, args
):
85 run_in_barbican_dir(ctx
, client
,
87 '.barbicanenv/bin/activate',
91 @contextlib.contextmanager
92 def setup_venv(ctx
, config
):
94 Setup the virtualenv for Barbican using pip.
96 assert isinstance(config
, dict)
97 log
.info('Setting up virtualenv for barbican...')
98 for (client
, _
) in config
.items():
99 run_in_barbican_dir(ctx
, client
,
100 ['python3', '-m', 'venv', '.barbicanenv'])
101 run_in_barbican_venv(ctx
, client
,
102 ['pip', 'install', '--upgrade', 'pip'])
103 run_in_barbican_venv(ctx
, client
,
104 ['pip', 'install', 'pytz',
105 '-e', get_barbican_dir(ctx
)])
108 def assign_ports(ctx
, config
, initial_port
):
110 Assign port numbers starting from @initial_port
114 for remote
, roles_for_host
in ctx
.cluster
.remotes
.items():
115 for role
in roles_for_host
:
117 role_endpoints
[role
] = (remote
.name
.split('@')[1], port
)
120 return role_endpoints
122 def set_authtoken_params(ctx
, cclient
, cconfig
):
123 section_config_list
= cconfig
['keystone_authtoken'].items()
124 for config
in section_config_list
:
126 run_in_barbican_dir(ctx
, cclient
,
128 '/[[]filter:authtoken]/{p;s##'+'{} = {}'.format(name
, val
)+'#;}',
129 'etc/barbican/barbican-api-paste.ini'])
131 keystone_role
= cconfig
.get('use-keystone-role', None)
132 public_host
, public_port
= ctx
.keystone
.public_endpoints
[keystone_role
]
133 url
= 'http://{host}:{port}/v3'.format(host
=public_host
,
135 run_in_barbican_dir(ctx
, cclient
,
137 '/[[]filter:authtoken]/{p;s##'+'auth_uri = {}'.format(url
)+'#;}',
138 'etc/barbican/barbican-api-paste.ini'])
139 admin_host
, admin_port
= ctx
.keystone
.admin_endpoints
[keystone_role
]
140 admin_url
= 'http://{host}:{port}/v3'.format(host
=admin_host
,
142 run_in_barbican_dir(ctx
, cclient
,
144 '/[[]filter:authtoken]/{p;s##'+'auth_url = {}'.format(admin_url
)+'#;}',
145 'etc/barbican/barbican-api-paste.ini'])
147 def fix_barbican_api_paste(ctx
, cclient
):
148 run_in_barbican_dir(ctx
, cclient
,
150 '/\\[pipeline:barbican_api]/ {p;n; /^pipeline =/ '+
151 '{ s/.*/pipeline = unauthenticated-context apiapp/;p;d } } ; p',
152 './etc/barbican/barbican-api-paste.ini'])
154 def fix_barbican_api(ctx
, cclient
):
155 run_in_barbican_dir(ctx
, cclient
,
157 '/prop_dir =/ s#etc/barbican#{}/etc/barbican#'.format(get_barbican_dir(ctx
)),
160 def create_barbican_conf(ctx
, cclient
):
161 barbican_host
, barbican_port
= ctx
.barbican
.endpoints
[cclient
]
162 barbican_url
= 'http://{host}:{port}'.format(host
=barbican_host
,
164 log
.info("barbican url=%s", barbican_url
)
166 run_in_barbican_dir(ctx
, cclient
,
168 'echo -n -e "[DEFAULT]\nhost_href=' + barbican_url
+ '\n" ' + \
171 log
.info("run barbican db upgrade")
172 config_path
= get_barbican_dir(ctx
) + '/barbican.conf'
173 run_in_barbican_venv(ctx
, cclient
, ['barbican-manage', '--config-file', config_path
,
175 log
.info("run barbican db sync_secret_stores")
176 run_in_barbican_venv(ctx
, cclient
, ['barbican-manage', '--config-file', config_path
,
177 'db', 'sync_secret_stores'])
179 @contextlib.contextmanager
180 def configure_barbican(ctx
, config
):
182 Configure barbican paste-api and barbican-api.
184 assert isinstance(config
, dict)
185 (cclient
, cconfig
) = next(iter(config
.items()))
187 keystone_role
= cconfig
.get('use-keystone-role', None)
188 if keystone_role
is None:
189 raise ConfigError('use-keystone-role not defined in barbican task')
191 set_authtoken_params(ctx
, cclient
, cconfig
)
192 fix_barbican_api(ctx
, cclient
)
193 fix_barbican_api_paste(ctx
, cclient
)
194 create_barbican_conf(ctx
, cclient
)
200 @contextlib.contextmanager
201 def run_barbican(ctx
, config
):
202 assert isinstance(config
, dict)
203 log
.info('Running barbican...')
205 for (client
, _
) in config
.items():
206 (remote
,) = ctx
.cluster
.only(client
).remotes
.keys()
207 cluster_name
, _
, client_id
= teuthology
.split_role(client
)
209 # start the public endpoint
210 client_public_with_id
= 'barbican.public' + '.' + client_id
212 run_cmd
= ['cd', get_barbican_dir(ctx
), run
.Raw('&&'),
213 '.', '.barbicanenv/bin/activate', run
.Raw('&&'),
214 'HOME={}'.format(get_barbican_dir(ctx
)), run
.Raw('&&'),
216 run
.Raw('& { read; kill %1; }')]
217 #run.Raw('1>/dev/null')
219 run_cmd
= 'cd ' + get_barbican_dir(ctx
) + ' && ' + \
220 '. .barbicanenv/bin/activate && ' + \
221 'HOME={}'.format(get_barbican_dir(ctx
)) + ' && ' + \
222 'exec bin/barbican-api & { read; kill %1; }'
224 ctx
.daemons
.add_daemon(
225 remote
, 'barbican', client_public_with_id
,
226 cluster
=cluster_name
,
227 args
=['bash', '-c', run_cmd
],
228 logger
=log
.getChild(client
),
230 cwd
=get_barbican_dir(ctx
),
235 # sleep driven synchronization
236 run_in_barbican_venv(ctx
, client
, ['sleep', '15'])
240 log
.info('Stopping Barbican instance')
241 ctx
.daemons
.get_daemon('barbican', client_public_with_id
,
245 @contextlib.contextmanager
246 def create_secrets(ctx
, config
):
248 Create a main and an alternate s3 user.
250 assert isinstance(config
, dict)
251 (cclient
, cconfig
) = next(iter(config
.items()))
253 rgw_user
= cconfig
['rgw_user']
255 keystone_role
= cconfig
.get('use-keystone-role', None)
256 keystone_host
, keystone_port
= ctx
.keystone
.public_endpoints
[keystone_role
]
257 barbican_host
, barbican_port
= ctx
.barbican
.endpoints
[cclient
]
258 barbican_url
= 'http://{host}:{port}'.format(host
=barbican_host
,
260 log
.info("barbican_url=%s", barbican_url
)
261 #fetching user_id of user that gets secrets for radosgw
262 token_req
= http
.client
.HTTPConnection(keystone_host
, keystone_port
, timeout
=30)
266 headers
={'Content-Type':'application/json'},
270 "methods": ["password"],
273 "domain": {"id": "default"},
274 "name": rgw_user
["username"],
275 "password": rgw_user
["password"]
281 "domain": {"id": "default"},
282 "name": rgw_user
["tenantName"]
287 rgw_access_user_resp
= token_req
.getresponse()
288 if not (rgw_access_user_resp
.status
>= 200 and
289 rgw_access_user_resp
.status
< 300):
290 raise Exception("Cannot authenticate user "+rgw_user
["username"]+" for secret creation")
291 # baru_resp = json.loads(baru_req.data)
292 rgw_access_user_data
= json
.loads(rgw_access_user_resp
.read().decode())
293 rgw_user_id
= rgw_access_user_data
['token']['user']['id']
294 if 'secrets' in cconfig
:
295 for secret
in cconfig
['secrets']:
296 if 'name' not in secret
:
297 raise ConfigError('barbican.secrets must have "name" field')
298 if 'base64' not in secret
:
299 raise ConfigError('barbican.secrets must have "base64" field')
300 if 'tenantName' not in secret
:
301 raise ConfigError('barbican.secrets must have "tenantName" field')
302 if 'username' not in secret
:
303 raise ConfigError('barbican.secrets must have "username" field')
304 if 'password' not in secret
:
305 raise ConfigError('barbican.secrets must have "password" field')
307 token_req
= http
.client
.HTTPConnection(keystone_host
, keystone_port
, timeout
=30)
311 headers
={'Content-Type':'application/json'},
315 "methods": ["password"],
318 "domain": {"id": "default"},
319 "name": secret
["username"],
320 "password": secret
["password"]
326 "domain": {"id": "default"},
327 "name": secret
["tenantName"]
332 token_resp
= token_req
.getresponse()
333 if not (token_resp
.status
>= 200 and
334 token_resp
.status
< 300):
335 raise Exception("Cannot authenticate user "+secret
["username"]+" for secret creation")
337 expire
= time
.time() + 5400 # now + 90m
338 (expire_fract
,dummy
) = math
.modf(expire
)
339 expire_format
= "%%FT%%T.%06d" % (round(expire_fract
*1000000))
340 expiration
= time
.strftime(expire_format
, time
.gmtime(expire
))
341 token_id
= token_resp
.getheader('x-subject-token')
343 key1_json
= json
.dumps(
345 "name": secret
['name'],
346 "expiration": expiration
,
350 "payload": secret
['base64'],
351 "payload_content_type": "application/octet-stream",
352 "payload_content_encoding": "base64"
355 sec_req
= http
.client
.HTTPConnection(barbican_host
, barbican_port
, timeout
=30)
360 headers
={'Content-Type': 'application/json',
362 'X-Auth-Token': token_id
},
366 log
.info("catched exception!")
367 run_in_barbican_venv(ctx
, cclient
, ['sleep', '900'])
369 barbican_sec_resp
= sec_req
.getresponse()
370 if not (barbican_sec_resp
.status
>= 200 and
371 barbican_sec_resp
.status
< 300):
372 raise Exception("Cannot create secret")
373 barbican_data
= json
.loads(barbican_sec_resp
.read().decode())
374 if 'secret_ref' not in barbican_data
:
375 raise ValueError("Malformed secret creation response")
376 secret_ref
= barbican_data
["secret_ref"]
377 log
.info("secret_ref=%s", secret_ref
)
378 secret_url_parsed
= urlparse(secret_ref
)
379 acl_json
= json
.dumps(
382 "users": [rgw_user_id
],
383 "project-access": True
386 acl_req
= http
.client
.HTTPConnection(secret_url_parsed
.netloc
, timeout
=30)
389 secret_url_parsed
.path
+'/acl',
390 headers
={'Content-Type': 'application/json',
392 'X-Auth-Token': token_id
},
395 barbican_acl_resp
= acl_req
.getresponse()
396 if not (barbican_acl_resp
.status
>= 200 and
397 barbican_acl_resp
.status
< 300):
398 raise Exception("Cannot set ACL for secret")
400 key
= {'id': secret_ref
.split('secrets/')[1], 'payload': secret
['base64']}
401 ctx
.barbican
.keys
[secret
['name']] = key
403 run_in_barbican_venv(ctx
, cclient
, ['sleep', '3'])
410 @contextlib.contextmanager
411 def task(ctx
, config
):
413 Deploy and configure Keystone
415 Example of configuration:
419 cluster_path: /home/adam/ceph-1/build
428 description: Encryption Tenant
430 description: Barbican
432 description: S3 project
434 - name: rgwcrypt-user
435 password: rgwcrypt-pass
437 - name: barbican-user
438 password: barbican-pass
443 roles: [ name: Member, name: creator ]
457 description: Keystone Identity Service
461 use-keystone-role: client.0
463 auth_plugin: password
464 username: barbican-user
465 password: barbican-pass
466 user_domain_name: Default
469 username: rgwcrypt-user
470 password: rgwcrypt-pass
473 base64: a2V5MS5GcWVxKzhzTGNLaGtzQkg5NGVpb1FKcFpGb2c=
478 base64: a2V5Mi5yNUNNMGFzMVdIUVZxcCt5NGVmVGlQQ1k4YWg=
488 use-keystone-role: client.0
489 use-barbican-role: client.0
491 assert config
is None or isinstance(config
, list) \
492 or isinstance(config
, dict), \
493 "task keystone only supports a list or dictionary for configuration"
494 all_clients
= ['client.{id}'.format(id=id_
)
495 for id_
in teuthology
.all_roles_of_type(ctx
.cluster
, 'client')]
498 if isinstance(config
, list):
499 config
= dict.fromkeys(config
)
501 overrides
= ctx
.config
.get('overrides', {})
502 # merge each client section, not the top level.
503 for client
in config
.keys():
504 if not config
[client
]:
506 teuthology
.deep_merge(config
[client
], overrides
.get('barbican', {}))
508 log
.debug('Barbican config is %s', config
)
510 if not hasattr(ctx
, 'keystone'):
511 raise ConfigError('barbican must run after the keystone task')
514 ctx
.barbican
= argparse
.Namespace()
515 ctx
.barbican
.endpoints
= assign_ports(ctx
, config
, 9311)
516 ctx
.barbican
.keys
= {}
518 with contextutil
.nested(
519 lambda: download(ctx
=ctx
, config
=config
),
520 lambda: setup_venv(ctx
=ctx
, config
=config
),
521 lambda: configure_barbican(ctx
=ctx
, config
=config
),
522 lambda: run_barbican(ctx
=ctx
, config
=config
),
523 lambda: create_secrets(ctx
=ctx
, config
=config
),