]>
git.proxmox.com Git - ceph.git/blob - ceph/qa/tasks/barbican.py
46b036ef4ced680dc80421721ec1ad45fb0183fa
2 Deploy and configure Barbican for Teuthology
8 from six
.moves
import http_client
9 from six
.moves
.urllib
.parse
import urlparse
12 from teuthology
import misc
as teuthology
13 from teuthology
import contextutil
14 from teuthology
.orchestra
import run
15 from teuthology
.exceptions
import ConfigError
17 log
= logging
.getLogger(__name__
)
20 @contextlib.contextmanager
21 def download(ctx
, config
):
23 Download the Barbican from github.
24 Remove downloaded file upon exit.
26 The context passed in should be identical to the context
27 passed in to the main task.
29 assert isinstance(config
, dict)
30 log
.info('Downloading barbican...')
31 testdir
= teuthology
.get_testdir(ctx
)
32 for (client
, cconf
) in config
.items():
33 branch
= cconf
.get('force-branch', 'master')
34 log
.info("Using branch '%s' for barbican", branch
)
36 sha1
= cconf
.get('sha1')
37 log
.info('sha1=%s', sha1
)
39 ctx
.cluster
.only(client
).run(
44 ctx
.cluster
.only(client
).run(
48 'https://github.com/openstack/barbican.git',
49 '{tdir}/barbican'.format(tdir
=testdir
),
53 ctx
.cluster
.only(client
).run(
55 'cd', '{tdir}/barbican'.format(tdir
=testdir
),
57 'git', 'reset', '--hard', sha1
,
63 log
.info('Removing barbican...')
64 testdir
= teuthology
.get_testdir(ctx
)
66 ctx
.cluster
.only(client
).run(
70 '{tdir}/barbican'.format(tdir
=testdir
),
74 def get_barbican_dir(ctx
):
75 return '{tdir}/barbican'.format(tdir
=teuthology
.get_testdir(ctx
))
77 def run_in_barbican_dir(ctx
, client
, args
):
78 ctx
.cluster
.only(client
).run(
79 args
=['cd', get_barbican_dir(ctx
), run
.Raw('&&'), ] + args
,
82 def run_in_barbican_venv(ctx
, client
, args
):
83 run_in_barbican_dir(ctx
, client
,
85 '.barbicanenv/bin/activate',
89 @contextlib.contextmanager
90 def setup_venv(ctx
, config
):
92 Setup the virtualenv for Barbican using pip.
94 assert isinstance(config
, dict)
95 log
.info('Setting up virtualenv for barbican...')
96 for (client
, _
) in config
.items():
97 run_in_barbican_dir(ctx
, client
, ['virtualenv', '.barbicanenv'])
98 run_in_barbican_venv(ctx
, client
, ['pip', 'install', 'pytz', '-e', get_barbican_dir(ctx
)])
101 def assign_ports(ctx
, config
, initial_port
):
103 Assign port numbers starting from @initial_port
107 for remote
, roles_for_host
in ctx
.cluster
.remotes
.items():
108 for role
in roles_for_host
:
110 role_endpoints
[role
] = (remote
.name
.split('@')[1], port
)
113 return role_endpoints
115 def set_authtoken_params(ctx
, cclient
, cconfig
):
116 section_config_list
= cconfig
['keystone_authtoken'].items()
117 for config
in section_config_list
:
119 run_in_barbican_dir(ctx
, cclient
,
121 '/[[]filter:authtoken]/{p;s##'+'{} = {}'.format(name
, val
)+'#;}',
122 'etc/barbican/barbican-api-paste.ini'])
124 keystone_role
= cconfig
.get('use-keystone-role', None)
125 public_host
, public_port
= ctx
.keystone
.public_endpoints
[keystone_role
]
126 url
= 'http://{host}:{port}/v3'.format(host
=public_host
,
128 run_in_barbican_dir(ctx
, cclient
,
130 '/[[]filter:authtoken]/{p;s##'+'auth_uri = {}'.format(url
)+'#;}',
131 'etc/barbican/barbican-api-paste.ini'])
132 admin_host
, admin_port
= ctx
.keystone
.admin_endpoints
[keystone_role
]
133 admin_url
= 'http://{host}:{port}/v3'.format(host
=admin_host
,
135 run_in_barbican_dir(ctx
, cclient
,
137 '/[[]filter:authtoken]/{p;s##'+'auth_url = {}'.format(admin_url
)+'#;}',
138 'etc/barbican/barbican-api-paste.ini'])
140 def fix_barbican_api_paste(ctx
, cclient
):
141 run_in_barbican_dir(ctx
, cclient
,
143 '/\\[pipeline:barbican_api]/ {p;n; /^pipeline =/ '+
144 '{ s/.*/pipeline = unauthenticated-context apiapp/;p;d } } ; p',
145 './etc/barbican/barbican-api-paste.ini'])
147 def fix_barbican_api(ctx
, cclient
):
148 run_in_barbican_dir(ctx
, cclient
,
150 '/prop_dir =/ s#etc/barbican#{}/etc/barbican#'.format(get_barbican_dir(ctx
)),
153 def copy_policy_json(ctx
, cclient
):
154 run_in_barbican_dir(ctx
, cclient
,
156 get_barbican_dir(ctx
)+'/etc/barbican/policy.json',
157 get_barbican_dir(ctx
)])
159 def create_barbican_conf(ctx
, cclient
):
160 barbican_host
, barbican_port
= ctx
.barbican
.endpoints
[cclient
]
161 barbican_url
= 'http://{host}:{port}'.format(host
=barbican_host
,
163 log
.info("barbican url=%s", barbican_url
)
165 run_in_barbican_dir(ctx
, cclient
,
167 'echo -n -e "[DEFAULT]\nhost_href=' + barbican_url
+ '\n" ' + \
170 @contextlib.contextmanager
171 def configure_barbican(ctx
, config
):
173 Configure barbican paste-api and barbican-api.
175 assert isinstance(config
, dict)
176 (cclient
, cconfig
) = next(iter(config
.items()))
178 keystone_role
= cconfig
.get('use-keystone-role', None)
179 if keystone_role
is None:
180 raise ConfigError('use-keystone-role not defined in barbican task')
182 set_authtoken_params(ctx
, cclient
, cconfig
)
183 fix_barbican_api(ctx
, cclient
)
184 fix_barbican_api_paste(ctx
, cclient
)
185 copy_policy_json(ctx
, cclient
)
186 create_barbican_conf(ctx
, cclient
)
192 @contextlib.contextmanager
193 def run_barbican(ctx
, config
):
194 assert isinstance(config
, dict)
195 log
.info('Running barbican...')
197 for (client
, _
) in config
.items():
198 (remote
,) = ctx
.cluster
.only(client
).remotes
.keys()
199 cluster_name
, _
, client_id
= teuthology
.split_role(client
)
201 # start the public endpoint
202 client_public_with_id
= 'barbican.public' + '.' + client_id
204 run_cmd
= ['cd', get_barbican_dir(ctx
), run
.Raw('&&'),
205 '.', '.barbicanenv/bin/activate', run
.Raw('&&'),
206 'HOME={}'.format(get_barbican_dir(ctx
)), run
.Raw('&&'),
208 run
.Raw('& { read; kill %1; }')]
209 #run.Raw('1>/dev/null')
211 run_cmd
= 'cd ' + get_barbican_dir(ctx
) + ' && ' + \
212 '. .barbicanenv/bin/activate && ' + \
213 'HOME={}'.format(get_barbican_dir(ctx
)) + ' && ' + \
214 'exec bin/barbican-api & { read; kill %1; }'
216 ctx
.daemons
.add_daemon(
217 remote
, 'barbican', client_public_with_id
,
218 cluster
=cluster_name
,
219 args
=['bash', '-c', run_cmd
],
220 logger
=log
.getChild(client
),
222 cwd
=get_barbican_dir(ctx
),
227 # sleep driven synchronization
228 run_in_barbican_venv(ctx
, client
, ['sleep', '15'])
232 log
.info('Stopping Barbican instance')
233 ctx
.daemons
.get_daemon('barbican', client_public_with_id
,
237 @contextlib.contextmanager
238 def create_secrets(ctx
, config
):
240 Create a main and an alternate s3 user.
242 assert isinstance(config
, dict)
243 (cclient
, cconfig
) = next(iter(config
.items()))
245 rgw_user
= cconfig
['rgw_user']
247 keystone_role
= cconfig
.get('use-keystone-role', None)
248 keystone_host
, keystone_port
= ctx
.keystone
.public_endpoints
[keystone_role
]
249 barbican_host
, barbican_port
= ctx
.barbican
.endpoints
[cclient
]
250 barbican_url
= 'http://{host}:{port}'.format(host
=barbican_host
,
252 log
.info("barbican_url=%s", barbican_url
)
253 #fetching user_id of user that gets secrets for radosgw
254 token_req
= http_client
.HTTPConnection(keystone_host
, keystone_port
, timeout
=30)
258 headers
={'Content-Type':'application/json'},
262 "methods": ["password"],
265 "domain": {"id": "default"},
266 "name": rgw_user
["username"],
267 "password": rgw_user
["password"]
273 "domain": {"id": "default"},
274 "name": rgw_user
["tenantName"]
279 rgw_access_user_resp
= token_req
.getresponse()
280 if not (rgw_access_user_resp
.status
>= 200 and
281 rgw_access_user_resp
.status
< 300):
282 raise Exception("Cannot authenticate user "+rgw_user
["username"]+" for secret creation")
283 # baru_resp = json.loads(baru_req.data)
284 rgw_access_user_data
= json
.loads(six
.ensure_str(rgw_access_user_resp
.read()))
285 rgw_user_id
= rgw_access_user_data
['token']['user']['id']
286 if 'secrets' in cconfig
:
287 for secret
in cconfig
['secrets']:
288 if 'name' not in secret
:
289 raise ConfigError('barbican.secrets must have "name" field')
290 if 'base64' not in secret
:
291 raise ConfigError('barbican.secrets must have "base64" field')
292 if 'tenantName' not in secret
:
293 raise ConfigError('barbican.secrets must have "tenantName" field')
294 if 'username' not in secret
:
295 raise ConfigError('barbican.secrets must have "username" field')
296 if 'password' not in secret
:
297 raise ConfigError('barbican.secrets must have "password" field')
299 token_req
= http_client
.HTTPConnection(keystone_host
, keystone_port
, timeout
=30)
303 headers
={'Content-Type':'application/json'},
307 "methods": ["password"],
310 "domain": {"id": "default"},
311 "name": secret
["username"],
312 "password": secret
["password"]
318 "domain": {"id": "default"},
319 "name": secret
["tenantName"]
324 token_resp
= token_req
.getresponse()
325 if not (token_resp
.status
>= 200 and
326 token_resp
.status
< 300):
327 raise Exception("Cannot authenticate user "+secret
["username"]+" for secret creation")
329 token_id
= token_resp
.getheader('x-subject-token')
331 key1_json
= json
.dumps(
333 "name": secret
['name'],
334 "expiration": "2020-12-31T19:14:44.180394",
338 "payload": secret
['base64'],
339 "payload_content_type": "application/octet-stream",
340 "payload_content_encoding": "base64"
343 sec_req
= http_client
.HTTPConnection(barbican_host
, barbican_port
, timeout
=30)
348 headers
={'Content-Type': 'application/json',
350 'X-Auth-Token': token_id
},
354 log
.info("catched exception!")
355 run_in_barbican_venv(ctx
, cclient
, ['sleep', '900'])
357 barbican_sec_resp
= sec_req
.getresponse()
358 if not (barbican_sec_resp
.status
>= 200 and
359 barbican_sec_resp
.status
< 300):
360 raise Exception("Cannot create secret")
361 barbican_data
= json
.loads(six
.ensure_str(barbican_sec_resp
.read()))
362 if 'secret_ref' not in barbican_data
:
363 raise ValueError("Malformed secret creation response")
364 secret_ref
= barbican_data
["secret_ref"]
365 log
.info("secret_ref=%s", secret_ref
)
366 secret_url_parsed
= urlparse(secret_ref
)
367 acl_json
= json
.dumps(
370 "users": [rgw_user_id
],
371 "project-access": True
374 acl_req
= http_client
.HTTPConnection(secret_url_parsed
.netloc
, timeout
=30)
377 secret_url_parsed
.path
+'/acl',
378 headers
={'Content-Type': 'application/json',
380 'X-Auth-Token': token_id
},
383 barbican_acl_resp
= acl_req
.getresponse()
384 if not (barbican_acl_resp
.status
>= 200 and
385 barbican_acl_resp
.status
< 300):
386 raise Exception("Cannot set ACL for secret")
388 key
= {'id': secret_ref
.split('secrets/')[1], 'payload': secret
['base64']}
389 ctx
.barbican
.keys
[secret
['name']] = key
391 run_in_barbican_venv(ctx
, cclient
, ['sleep', '3'])
398 @contextlib.contextmanager
399 def task(ctx
, config
):
401 Deploy and configure Keystone
403 Example of configuration:
407 cluster_path: /home/adam/ceph-1/build
416 description: Encryption Tenant
418 description: Barbican
420 description: S3 project
422 - name: rgwcrypt-user
423 password: rgwcrypt-pass
425 - name: barbican-user
426 password: barbican-pass
431 roles: [ name: Member, name: creator ]
445 description: Keystone Identity Service
449 use-keystone-role: client.0
451 auth_plugin: password
452 username: barbican-user
453 password: barbican-pass
454 user_domain_name: Default
457 username: rgwcrypt-user
458 password: rgwcrypt-pass
461 base64: a2V5MS5GcWVxKzhzTGNLaGtzQkg5NGVpb1FKcFpGb2c=
466 base64: a2V5Mi5yNUNNMGFzMVdIUVZxcCt5NGVmVGlQQ1k4YWg=
476 use-keystone-role: client.0
477 use-barbican-role: client.0
479 assert config
is None or isinstance(config
, list) \
480 or isinstance(config
, dict), \
481 "task keystone only supports a list or dictionary for configuration"
482 all_clients
= ['client.{id}'.format(id=id_
)
483 for id_
in teuthology
.all_roles_of_type(ctx
.cluster
, 'client')]
486 if isinstance(config
, list):
487 config
= dict.fromkeys(config
)
489 overrides
= ctx
.config
.get('overrides', {})
490 # merge each client section, not the top level.
491 for client
in config
.keys():
492 if not config
[client
]:
494 teuthology
.deep_merge(config
[client
], overrides
.get('barbican', {}))
496 log
.debug('Barbican config is %s', config
)
498 if not hasattr(ctx
, 'keystone'):
499 raise ConfigError('barbican must run after the keystone task')
502 ctx
.barbican
= argparse
.Namespace()
503 ctx
.barbican
.endpoints
= assign_ports(ctx
, config
, 9311)
504 ctx
.barbican
.keys
= {}
506 with contextutil
.nested(
507 lambda: download(ctx
=ctx
, config
=config
),
508 lambda: setup_venv(ctx
=ctx
, config
=config
),
509 lambda: configure_barbican(ctx
=ctx
, config
=config
),
510 lambda: run_barbican(ctx
=ctx
, config
=config
),
511 lambda: create_secrets(ctx
=ctx
, config
=config
),