]>
git.proxmox.com Git - ceph.git/blob - ceph/qa/tasks/barbican.py
d43568c6103a0ff973a66421c3abcf3d4b95b79f
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 copy_policy_json(ctx
, cclient
):
161 run_in_barbican_dir(ctx
, cclient
,
163 get_barbican_dir(ctx
)+'/etc/barbican/policy.json',
164 get_barbican_dir(ctx
)])
166 def create_barbican_conf(ctx
, cclient
):
167 barbican_host
, barbican_port
= ctx
.barbican
.endpoints
[cclient
]
168 barbican_url
= 'http://{host}:{port}'.format(host
=barbican_host
,
170 log
.info("barbican url=%s", barbican_url
)
172 run_in_barbican_dir(ctx
, cclient
,
174 'echo -n -e "[DEFAULT]\nhost_href=' + barbican_url
+ '\n" ' + \
177 @contextlib.contextmanager
178 def configure_barbican(ctx
, config
):
180 Configure barbican paste-api and barbican-api.
182 assert isinstance(config
, dict)
183 (cclient
, cconfig
) = next(iter(config
.items()))
185 keystone_role
= cconfig
.get('use-keystone-role', None)
186 if keystone_role
is None:
187 raise ConfigError('use-keystone-role not defined in barbican task')
189 set_authtoken_params(ctx
, cclient
, cconfig
)
190 fix_barbican_api(ctx
, cclient
)
191 fix_barbican_api_paste(ctx
, cclient
)
192 copy_policy_json(ctx
, cclient
)
193 create_barbican_conf(ctx
, cclient
)
199 @contextlib.contextmanager
200 def run_barbican(ctx
, config
):
201 assert isinstance(config
, dict)
202 log
.info('Running barbican...')
204 for (client
, _
) in config
.items():
205 (remote
,) = ctx
.cluster
.only(client
).remotes
.keys()
206 cluster_name
, _
, client_id
= teuthology
.split_role(client
)
208 # start the public endpoint
209 client_public_with_id
= 'barbican.public' + '.' + client_id
211 run_cmd
= ['cd', get_barbican_dir(ctx
), run
.Raw('&&'),
212 '.', '.barbicanenv/bin/activate', run
.Raw('&&'),
213 'HOME={}'.format(get_barbican_dir(ctx
)), run
.Raw('&&'),
215 run
.Raw('& { read; kill %1; }')]
216 #run.Raw('1>/dev/null')
218 run_cmd
= 'cd ' + get_barbican_dir(ctx
) + ' && ' + \
219 '. .barbicanenv/bin/activate && ' + \
220 'HOME={}'.format(get_barbican_dir(ctx
)) + ' && ' + \
221 'exec bin/barbican-api & { read; kill %1; }'
223 ctx
.daemons
.add_daemon(
224 remote
, 'barbican', client_public_with_id
,
225 cluster
=cluster_name
,
226 args
=['bash', '-c', run_cmd
],
227 logger
=log
.getChild(client
),
229 cwd
=get_barbican_dir(ctx
),
234 # sleep driven synchronization
235 run_in_barbican_venv(ctx
, client
, ['sleep', '15'])
239 log
.info('Stopping Barbican instance')
240 ctx
.daemons
.get_daemon('barbican', client_public_with_id
,
244 @contextlib.contextmanager
245 def create_secrets(ctx
, config
):
247 Create a main and an alternate s3 user.
249 assert isinstance(config
, dict)
250 (cclient
, cconfig
) = next(iter(config
.items()))
252 rgw_user
= cconfig
['rgw_user']
254 keystone_role
= cconfig
.get('use-keystone-role', None)
255 keystone_host
, keystone_port
= ctx
.keystone
.public_endpoints
[keystone_role
]
256 barbican_host
, barbican_port
= ctx
.barbican
.endpoints
[cclient
]
257 barbican_url
= 'http://{host}:{port}'.format(host
=barbican_host
,
259 log
.info("barbican_url=%s", barbican_url
)
260 #fetching user_id of user that gets secrets for radosgw
261 token_req
= http
.client
.HTTPConnection(keystone_host
, keystone_port
, timeout
=30)
265 headers
={'Content-Type':'application/json'},
269 "methods": ["password"],
272 "domain": {"id": "default"},
273 "name": rgw_user
["username"],
274 "password": rgw_user
["password"]
280 "domain": {"id": "default"},
281 "name": rgw_user
["tenantName"]
286 rgw_access_user_resp
= token_req
.getresponse()
287 if not (rgw_access_user_resp
.status
>= 200 and
288 rgw_access_user_resp
.status
< 300):
289 raise Exception("Cannot authenticate user "+rgw_user
["username"]+" for secret creation")
290 # baru_resp = json.loads(baru_req.data)
291 rgw_access_user_data
= json
.loads(rgw_access_user_resp
.read().decode())
292 rgw_user_id
= rgw_access_user_data
['token']['user']['id']
293 if 'secrets' in cconfig
:
294 for secret
in cconfig
['secrets']:
295 if 'name' not in secret
:
296 raise ConfigError('barbican.secrets must have "name" field')
297 if 'base64' not in secret
:
298 raise ConfigError('barbican.secrets must have "base64" field')
299 if 'tenantName' not in secret
:
300 raise ConfigError('barbican.secrets must have "tenantName" field')
301 if 'username' not in secret
:
302 raise ConfigError('barbican.secrets must have "username" field')
303 if 'password' not in secret
:
304 raise ConfigError('barbican.secrets must have "password" field')
306 token_req
= http
.client
.HTTPConnection(keystone_host
, keystone_port
, timeout
=30)
310 headers
={'Content-Type':'application/json'},
314 "methods": ["password"],
317 "domain": {"id": "default"},
318 "name": secret
["username"],
319 "password": secret
["password"]
325 "domain": {"id": "default"},
326 "name": secret
["tenantName"]
331 token_resp
= token_req
.getresponse()
332 if not (token_resp
.status
>= 200 and
333 token_resp
.status
< 300):
334 raise Exception("Cannot authenticate user "+secret
["username"]+" for secret creation")
336 expire
= time
.time() + 5400 # now + 90m
337 (expire_fract
,dummy
) = math
.modf(expire
)
338 expire_format
= "%%FT%%T.%06d" % (round(expire_fract
*1000000))
339 expiration
= time
.strftime(expire_format
, time
.gmtime(expire
))
340 token_id
= token_resp
.getheader('x-subject-token')
342 key1_json
= json
.dumps(
344 "name": secret
['name'],
345 "expiration": expiration
,
349 "payload": secret
['base64'],
350 "payload_content_type": "application/octet-stream",
351 "payload_content_encoding": "base64"
354 sec_req
= http
.client
.HTTPConnection(barbican_host
, barbican_port
, timeout
=30)
359 headers
={'Content-Type': 'application/json',
361 'X-Auth-Token': token_id
},
365 log
.info("catched exception!")
366 run_in_barbican_venv(ctx
, cclient
, ['sleep', '900'])
368 barbican_sec_resp
= sec_req
.getresponse()
369 if not (barbican_sec_resp
.status
>= 200 and
370 barbican_sec_resp
.status
< 300):
371 raise Exception("Cannot create secret")
372 barbican_data
= json
.loads(barbican_sec_resp
.read().decode())
373 if 'secret_ref' not in barbican_data
:
374 raise ValueError("Malformed secret creation response")
375 secret_ref
= barbican_data
["secret_ref"]
376 log
.info("secret_ref=%s", secret_ref
)
377 secret_url_parsed
= urlparse(secret_ref
)
378 acl_json
= json
.dumps(
381 "users": [rgw_user_id
],
382 "project-access": True
385 acl_req
= http
.client
.HTTPConnection(secret_url_parsed
.netloc
, timeout
=30)
388 secret_url_parsed
.path
+'/acl',
389 headers
={'Content-Type': 'application/json',
391 'X-Auth-Token': token_id
},
394 barbican_acl_resp
= acl_req
.getresponse()
395 if not (barbican_acl_resp
.status
>= 200 and
396 barbican_acl_resp
.status
< 300):
397 raise Exception("Cannot set ACL for secret")
399 key
= {'id': secret_ref
.split('secrets/')[1], 'payload': secret
['base64']}
400 ctx
.barbican
.keys
[secret
['name']] = key
402 run_in_barbican_venv(ctx
, cclient
, ['sleep', '3'])
409 @contextlib.contextmanager
410 def task(ctx
, config
):
412 Deploy and configure Keystone
414 Example of configuration:
418 cluster_path: /home/adam/ceph-1/build
427 description: Encryption Tenant
429 description: Barbican
431 description: S3 project
433 - name: rgwcrypt-user
434 password: rgwcrypt-pass
436 - name: barbican-user
437 password: barbican-pass
442 roles: [ name: Member, name: creator ]
456 description: Keystone Identity Service
460 use-keystone-role: client.0
462 auth_plugin: password
463 username: barbican-user
464 password: barbican-pass
465 user_domain_name: Default
468 username: rgwcrypt-user
469 password: rgwcrypt-pass
472 base64: a2V5MS5GcWVxKzhzTGNLaGtzQkg5NGVpb1FKcFpGb2c=
477 base64: a2V5Mi5yNUNNMGFzMVdIUVZxcCt5NGVmVGlQQ1k4YWg=
487 use-keystone-role: client.0
488 use-barbican-role: client.0
490 assert config
is None or isinstance(config
, list) \
491 or isinstance(config
, dict), \
492 "task keystone only supports a list or dictionary for configuration"
493 all_clients
= ['client.{id}'.format(id=id_
)
494 for id_
in teuthology
.all_roles_of_type(ctx
.cluster
, 'client')]
497 if isinstance(config
, list):
498 config
= dict.fromkeys(config
)
500 overrides
= ctx
.config
.get('overrides', {})
501 # merge each client section, not the top level.
502 for client
in config
.keys():
503 if not config
[client
]:
505 teuthology
.deep_merge(config
[client
], overrides
.get('barbican', {}))
507 log
.debug('Barbican config is %s', config
)
509 if not hasattr(ctx
, 'keystone'):
510 raise ConfigError('barbican must run after the keystone task')
513 ctx
.barbican
= argparse
.Namespace()
514 ctx
.barbican
.endpoints
= assign_ports(ctx
, config
, 9311)
515 ctx
.barbican
.keys
= {}
517 with contextutil
.nested(
518 lambda: download(ctx
=ctx
, config
=config
),
519 lambda: setup_venv(ctx
=ctx
, config
=config
),
520 lambda: configure_barbican(ctx
=ctx
, config
=config
),
521 lambda: run_barbican(ctx
=ctx
, config
=config
),
522 lambda: create_secrets(ctx
=ctx
, config
=config
),