]>
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
, ['virtualenv', '.barbicanenv'])
100 run_in_barbican_venv(ctx
, client
, ['pip', 'install', 'pytz', '-e', get_barbican_dir(ctx
)])
103 def assign_ports(ctx
, config
, initial_port
):
105 Assign port numbers starting from @initial_port
109 for remote
, roles_for_host
in ctx
.cluster
.remotes
.items():
110 for role
in roles_for_host
:
112 role_endpoints
[role
] = (remote
.name
.split('@')[1], port
)
115 return role_endpoints
117 def set_authtoken_params(ctx
, cclient
, cconfig
):
118 section_config_list
= cconfig
['keystone_authtoken'].items()
119 for config
in section_config_list
:
121 run_in_barbican_dir(ctx
, cclient
,
123 '/[[]filter:authtoken]/{p;s##'+'{} = {}'.format(name
, val
)+'#;}',
124 'etc/barbican/barbican-api-paste.ini'])
126 keystone_role
= cconfig
.get('use-keystone-role', None)
127 public_host
, public_port
= ctx
.keystone
.public_endpoints
[keystone_role
]
128 url
= 'http://{host}:{port}/v3'.format(host
=public_host
,
130 run_in_barbican_dir(ctx
, cclient
,
132 '/[[]filter:authtoken]/{p;s##'+'auth_uri = {}'.format(url
)+'#;}',
133 'etc/barbican/barbican-api-paste.ini'])
134 admin_host
, admin_port
= ctx
.keystone
.admin_endpoints
[keystone_role
]
135 admin_url
= 'http://{host}:{port}/v3'.format(host
=admin_host
,
137 run_in_barbican_dir(ctx
, cclient
,
139 '/[[]filter:authtoken]/{p;s##'+'auth_url = {}'.format(admin_url
)+'#;}',
140 'etc/barbican/barbican-api-paste.ini'])
142 def fix_barbican_api_paste(ctx
, cclient
):
143 run_in_barbican_dir(ctx
, cclient
,
145 '/\\[pipeline:barbican_api]/ {p;n; /^pipeline =/ '+
146 '{ s/.*/pipeline = unauthenticated-context apiapp/;p;d } } ; p',
147 './etc/barbican/barbican-api-paste.ini'])
149 def fix_barbican_api(ctx
, cclient
):
150 run_in_barbican_dir(ctx
, cclient
,
152 '/prop_dir =/ s#etc/barbican#{}/etc/barbican#'.format(get_barbican_dir(ctx
)),
155 def copy_policy_json(ctx
, cclient
):
156 run_in_barbican_dir(ctx
, cclient
,
158 get_barbican_dir(ctx
)+'/etc/barbican/policy.json',
159 get_barbican_dir(ctx
)])
161 def create_barbican_conf(ctx
, cclient
):
162 barbican_host
, barbican_port
= ctx
.barbican
.endpoints
[cclient
]
163 barbican_url
= 'http://{host}:{port}'.format(host
=barbican_host
,
165 log
.info("barbican url=%s", barbican_url
)
167 run_in_barbican_dir(ctx
, cclient
,
169 'echo -n -e "[DEFAULT]\nhost_href=' + barbican_url
+ '\n" ' + \
172 @contextlib.contextmanager
173 def configure_barbican(ctx
, config
):
175 Configure barbican paste-api and barbican-api.
177 assert isinstance(config
, dict)
178 (cclient
, cconfig
) = next(iter(config
.items()))
180 keystone_role
= cconfig
.get('use-keystone-role', None)
181 if keystone_role
is None:
182 raise ConfigError('use-keystone-role not defined in barbican task')
184 set_authtoken_params(ctx
, cclient
, cconfig
)
185 fix_barbican_api(ctx
, cclient
)
186 fix_barbican_api_paste(ctx
, cclient
)
187 copy_policy_json(ctx
, cclient
)
188 create_barbican_conf(ctx
, cclient
)
194 @contextlib.contextmanager
195 def run_barbican(ctx
, config
):
196 assert isinstance(config
, dict)
197 log
.info('Running barbican...')
199 for (client
, _
) in config
.items():
200 (remote
,) = ctx
.cluster
.only(client
).remotes
.keys()
201 cluster_name
, _
, client_id
= teuthology
.split_role(client
)
203 # start the public endpoint
204 client_public_with_id
= 'barbican.public' + '.' + client_id
206 run_cmd
= ['cd', get_barbican_dir(ctx
), run
.Raw('&&'),
207 '.', '.barbicanenv/bin/activate', run
.Raw('&&'),
208 'HOME={}'.format(get_barbican_dir(ctx
)), run
.Raw('&&'),
210 run
.Raw('& { read; kill %1; }')]
211 #run.Raw('1>/dev/null')
213 run_cmd
= 'cd ' + get_barbican_dir(ctx
) + ' && ' + \
214 '. .barbicanenv/bin/activate && ' + \
215 'HOME={}'.format(get_barbican_dir(ctx
)) + ' && ' + \
216 'exec bin/barbican-api & { read; kill %1; }'
218 ctx
.daemons
.add_daemon(
219 remote
, 'barbican', client_public_with_id
,
220 cluster
=cluster_name
,
221 args
=['bash', '-c', run_cmd
],
222 logger
=log
.getChild(client
),
224 cwd
=get_barbican_dir(ctx
),
229 # sleep driven synchronization
230 run_in_barbican_venv(ctx
, client
, ['sleep', '15'])
234 log
.info('Stopping Barbican instance')
235 ctx
.daemons
.get_daemon('barbican', client_public_with_id
,
239 @contextlib.contextmanager
240 def create_secrets(ctx
, config
):
242 Create a main and an alternate s3 user.
244 assert isinstance(config
, dict)
245 (cclient
, cconfig
) = next(iter(config
.items()))
247 rgw_user
= cconfig
['rgw_user']
249 keystone_role
= cconfig
.get('use-keystone-role', None)
250 keystone_host
, keystone_port
= ctx
.keystone
.public_endpoints
[keystone_role
]
251 barbican_host
, barbican_port
= ctx
.barbican
.endpoints
[cclient
]
252 barbican_url
= 'http://{host}:{port}'.format(host
=barbican_host
,
254 log
.info("barbican_url=%s", barbican_url
)
255 #fetching user_id of user that gets secrets for radosgw
256 token_req
= http
.client
.HTTPConnection(keystone_host
, keystone_port
, timeout
=30)
260 headers
={'Content-Type':'application/json'},
264 "methods": ["password"],
267 "domain": {"id": "default"},
268 "name": rgw_user
["username"],
269 "password": rgw_user
["password"]
275 "domain": {"id": "default"},
276 "name": rgw_user
["tenantName"]
281 rgw_access_user_resp
= token_req
.getresponse()
282 if not (rgw_access_user_resp
.status
>= 200 and
283 rgw_access_user_resp
.status
< 300):
284 raise Exception("Cannot authenticate user "+rgw_user
["username"]+" for secret creation")
285 # baru_resp = json.loads(baru_req.data)
286 rgw_access_user_data
= json
.loads(rgw_access_user_resp
.read().decode())
287 rgw_user_id
= rgw_access_user_data
['token']['user']['id']
288 if 'secrets' in cconfig
:
289 for secret
in cconfig
['secrets']:
290 if 'name' not in secret
:
291 raise ConfigError('barbican.secrets must have "name" field')
292 if 'base64' not in secret
:
293 raise ConfigError('barbican.secrets must have "base64" field')
294 if 'tenantName' not in secret
:
295 raise ConfigError('barbican.secrets must have "tenantName" field')
296 if 'username' not in secret
:
297 raise ConfigError('barbican.secrets must have "username" field')
298 if 'password' not in secret
:
299 raise ConfigError('barbican.secrets must have "password" field')
301 token_req
= http
.client
.HTTPConnection(keystone_host
, keystone_port
, timeout
=30)
305 headers
={'Content-Type':'application/json'},
309 "methods": ["password"],
312 "domain": {"id": "default"},
313 "name": secret
["username"],
314 "password": secret
["password"]
320 "domain": {"id": "default"},
321 "name": secret
["tenantName"]
326 token_resp
= token_req
.getresponse()
327 if not (token_resp
.status
>= 200 and
328 token_resp
.status
< 300):
329 raise Exception("Cannot authenticate user "+secret
["username"]+" for secret creation")
331 expire
= time
.time() + 5400 # now + 90m
332 (expire_fract
,dummy
) = math
.modf(expire
)
333 expire_format
= "%%FT%%T.%06d" % (round(expire_fract
*1000000))
334 expiration
= time
.strftime(expire_format
, time
.gmtime(expire
))
335 token_id
= token_resp
.getheader('x-subject-token')
337 key1_json
= json
.dumps(
339 "name": secret
['name'],
340 "expiration": expiration
,
344 "payload": secret
['base64'],
345 "payload_content_type": "application/octet-stream",
346 "payload_content_encoding": "base64"
349 sec_req
= http
.client
.HTTPConnection(barbican_host
, barbican_port
, timeout
=30)
354 headers
={'Content-Type': 'application/json',
356 'X-Auth-Token': token_id
},
360 log
.info("catched exception!")
361 run_in_barbican_venv(ctx
, cclient
, ['sleep', '900'])
363 barbican_sec_resp
= sec_req
.getresponse()
364 if not (barbican_sec_resp
.status
>= 200 and
365 barbican_sec_resp
.status
< 300):
366 raise Exception("Cannot create secret")
367 barbican_data
= json
.loads(barbican_sec_resp
.read().decode())
368 if 'secret_ref' not in barbican_data
:
369 raise ValueError("Malformed secret creation response")
370 secret_ref
= barbican_data
["secret_ref"]
371 log
.info("secret_ref=%s", secret_ref
)
372 secret_url_parsed
= urlparse(secret_ref
)
373 acl_json
= json
.dumps(
376 "users": [rgw_user_id
],
377 "project-access": True
380 acl_req
= http
.client
.HTTPConnection(secret_url_parsed
.netloc
, timeout
=30)
383 secret_url_parsed
.path
+'/acl',
384 headers
={'Content-Type': 'application/json',
386 'X-Auth-Token': token_id
},
389 barbican_acl_resp
= acl_req
.getresponse()
390 if not (barbican_acl_resp
.status
>= 200 and
391 barbican_acl_resp
.status
< 300):
392 raise Exception("Cannot set ACL for secret")
394 key
= {'id': secret_ref
.split('secrets/')[1], 'payload': secret
['base64']}
395 ctx
.barbican
.keys
[secret
['name']] = key
397 run_in_barbican_venv(ctx
, cclient
, ['sleep', '3'])
404 @contextlib.contextmanager
405 def task(ctx
, config
):
407 Deploy and configure Keystone
409 Example of configuration:
413 cluster_path: /home/adam/ceph-1/build
422 description: Encryption Tenant
424 description: Barbican
426 description: S3 project
428 - name: rgwcrypt-user
429 password: rgwcrypt-pass
431 - name: barbican-user
432 password: barbican-pass
437 roles: [ name: Member, name: creator ]
451 description: Keystone Identity Service
455 use-keystone-role: client.0
457 auth_plugin: password
458 username: barbican-user
459 password: barbican-pass
460 user_domain_name: Default
463 username: rgwcrypt-user
464 password: rgwcrypt-pass
467 base64: a2V5MS5GcWVxKzhzTGNLaGtzQkg5NGVpb1FKcFpGb2c=
472 base64: a2V5Mi5yNUNNMGFzMVdIUVZxcCt5NGVmVGlQQ1k4YWg=
482 use-keystone-role: client.0
483 use-barbican-role: client.0
485 assert config
is None or isinstance(config
, list) \
486 or isinstance(config
, dict), \
487 "task keystone only supports a list or dictionary for configuration"
488 all_clients
= ['client.{id}'.format(id=id_
)
489 for id_
in teuthology
.all_roles_of_type(ctx
.cluster
, 'client')]
492 if isinstance(config
, list):
493 config
= dict.fromkeys(config
)
495 overrides
= ctx
.config
.get('overrides', {})
496 # merge each client section, not the top level.
497 for client
in config
.keys():
498 if not config
[client
]:
500 teuthology
.deep_merge(config
[client
], overrides
.get('barbican', {}))
502 log
.debug('Barbican config is %s', config
)
504 if not hasattr(ctx
, 'keystone'):
505 raise ConfigError('barbican must run after the keystone task')
508 ctx
.barbican
= argparse
.Namespace()
509 ctx
.barbican
.endpoints
= assign_ports(ctx
, config
, 9311)
510 ctx
.barbican
.keys
= {}
512 with contextutil
.nested(
513 lambda: download(ctx
=ctx
, config
=config
),
514 lambda: setup_venv(ctx
=ctx
, config
=config
),
515 lambda: configure_barbican(ctx
=ctx
, config
=config
),
516 lambda: run_barbican(ctx
=ctx
, config
=config
),
517 lambda: create_secrets(ctx
=ctx
, config
=config
),