]> git.proxmox.com Git - ceph.git/blame - ceph/qa/tasks/rgw.py
import ceph quincy 17.2.6
[ceph.git] / ceph / qa / tasks / rgw.py
CommitLineData
7c673cae
FG
1"""
2rgw routines
3"""
4import argparse
5import contextlib
7c673cae 6import logging
7c673cae 7
7c673cae
FG
8from teuthology.orchestra import run
9from teuthology import misc as teuthology
10from teuthology import contextutil
11fdf7f2 11from teuthology.exceptions import ConfigError
f67539c2 12from tasks.ceph_manager import get_valgrind_args
e306af50
TL
13from tasks.util import get_remote_for_role
14from tasks.util.rgw import rgwadmin, wait_for_radosgw
15from tasks.util.rados import (create_ec_pool,
16 create_replicated_pool,
17 create_cache_pool)
7c673cae
FG
18
19log = logging.getLogger(__name__)
20
11fdf7f2 21class RGWEndpoint:
494da23a 22 def __init__(self, hostname=None, port=None, cert=None, dns_name=None, website_dns_name=None):
11fdf7f2
TL
23 self.hostname = hostname
24 self.port = port
25 self.cert = cert
494da23a
TL
26 self.dns_name = dns_name
27 self.website_dns_name = website_dns_name
11fdf7f2
TL
28
29 def url(self):
30 proto = 'https' if self.cert else 'http'
31 return '{proto}://{hostname}:{port}/'.format(proto=proto, hostname=self.hostname, port=self.port)
32
7c673cae 33@contextlib.contextmanager
224ce89b 34def start_rgw(ctx, config, clients):
7c673cae
FG
35 """
36 Start rgw on remote sites.
37 """
38 log.info('Starting rgw...')
7c673cae 39 testdir = teuthology.get_testdir(ctx)
224ce89b 40 for client in clients:
9f95a23c 41 (remote,) = ctx.cluster.only(client).remotes.keys()
7c673cae
FG
42 cluster_name, daemon_type, client_id = teuthology.split_role(client)
43 client_with_id = daemon_type + '.' + client_id
44 client_with_cluster = cluster_name + '.' + client_with_id
7c673cae
FG
45
46 client_config = config.get(client)
47 if client_config is None:
48 client_config = {}
49 log.info("rgw %s config is %s", client, client_config)
7c673cae
FG
50 cmd_prefix = [
51 'sudo',
52 'adjust-ulimits',
53 'ceph-coverage',
54 '{tdir}/archive/coverage'.format(tdir=testdir),
55 'daemon-helper',
56 'term',
57 ]
58
59 rgw_cmd = ['radosgw']
60
31f18b77 61 log.info("Using %s as radosgw frontend", ctx.rgw.frontend)
7c673cae 62
11fdf7f2
TL
63 endpoint = ctx.rgw.role_endpoints[client]
64 frontends = ctx.rgw.frontend
65 frontend_prefix = client_config.get('frontend_prefix', None)
66 if frontend_prefix:
67 frontends += ' prefix={pfx}'.format(pfx=frontend_prefix)
68
69 if endpoint.cert:
70 # add the ssl certificate path
71 frontends += ' ssl_certificate={}'.format(endpoint.cert.certificate)
20effc67 72 frontends += ' ssl_port={}'.format(endpoint.port)
11fdf7f2
TL
73 else:
74 frontends += ' port={}'.format(endpoint.port)
75
7c673cae 76 rgw_cmd.extend([
11fdf7f2 77 '--rgw-frontends', frontends,
7c673cae
FG
78 '-n', client_with_id,
79 '--cluster', cluster_name,
80 '-k', '/etc/ceph/{client_with_cluster}.keyring'.format(client_with_cluster=client_with_cluster),
81 '--log-file',
82 '/var/log/ceph/rgw.{client_with_cluster}.log'.format(client_with_cluster=client_with_cluster),
83 '--rgw_ops_log_socket_path',
84 '{tdir}/rgw.opslog.{client_with_cluster}.sock'.format(tdir=testdir,
9f95a23c 85 client_with_cluster=client_with_cluster),
11fdf7f2
TL
86 ])
87
88 keystone_role = client_config.get('use-keystone-role', None)
89 if keystone_role is not None:
90 if not ctx.keystone:
91 raise ConfigError('rgw must run after the keystone task')
92 url = 'http://{host}:{port}/v1/KEY_$(tenant_id)s'.format(host=endpoint.hostname,
93 port=endpoint.port)
94 ctx.keystone.create_endpoint(ctx, keystone_role, 'swift', url)
95
96 keystone_host, keystone_port = \
97 ctx.keystone.public_endpoints[keystone_role]
98 rgw_cmd.extend([
99 '--rgw_keystone_url',
100 'http://{khost}:{kport}'.format(khost=keystone_host,
101 kport=keystone_port),
102 ])
103
9f95a23c 104
e306af50 105 if client_config.get('dns-name') is not None:
494da23a 106 rgw_cmd.extend(['--rgw-dns-name', endpoint.dns_name])
e306af50 107 if client_config.get('dns-s3website-name') is not None:
494da23a
TL
108 rgw_cmd.extend(['--rgw-dns-s3website-name', endpoint.website_dns_name])
109
9f95a23c
TL
110
111 vault_role = client_config.get('use-vault-role', None)
112 barbican_role = client_config.get('use-barbican-role', None)
f67539c2 113 pykmip_role = client_config.get('use-pykmip-role', None)
9f95a23c 114
20effc67 115 token_path = '/etc/ceph/vault-root-token'
9f95a23c
TL
116 if barbican_role is not None:
117 if not hasattr(ctx, 'barbican'):
118 raise ConfigError('rgw must run after the barbican task')
119
120 barbican_host, barbican_port = \
121 ctx.barbican.endpoints[barbican_role]
122 log.info("Use barbican url=%s:%s", barbican_host, barbican_port)
123
124 rgw_cmd.extend([
125 '--rgw_barbican_url',
126 'http://{bhost}:{bport}'.format(bhost=barbican_host,
127 bport=barbican_port),
128 ])
129 elif vault_role is not None:
130 if not ctx.vault.root_token:
131 raise ConfigError('vault: no "root_token" specified')
132 # create token on file
f67539c2 133 ctx.rgw.vault_role = vault_role
20effc67 134 ctx.cluster.only(client).run(args=['sudo', 'echo', '-n', ctx.vault.root_token, run.Raw('|'), 'sudo', 'tee', token_path])
9f95a23c
TL
135 log.info("Token file content")
136 ctx.cluster.only(client).run(args=['cat', token_path])
1911f103 137 log.info("Restrict access to token file")
20effc67 138 ctx.cluster.only(client).run(args=['sudo', 'chmod', '600', token_path])
1911f103 139 ctx.cluster.only(client).run(args=['sudo', 'chown', 'ceph', token_path])
9f95a23c 140
2a845540 141 vault_addr = "{}:{}".format(*ctx.vault.endpoints[vault_role])
9f95a23c 142 rgw_cmd.extend([
2a845540
TL
143 '--rgw_crypt_vault_addr', vault_addr,
144 '--rgw_crypt_vault_token_file', token_path,
145 '--rgw_crypt_sse_s3_vault_addr', vault_addr,
146 '--rgw_crypt_sse_s3_vault_token_file', token_path,
9f95a23c 147 ])
f67539c2
TL
148 elif pykmip_role is not None:
149 if not hasattr(ctx, 'pykmip'):
150 raise ConfigError('rgw must run after the pykmip task')
151 ctx.rgw.pykmip_role = pykmip_role
152 rgw_cmd.extend([
153 '--rgw_crypt_kmip_addr', "{}:{}".format(*ctx.pykmip.endpoints[pykmip_role]),
154 ])
9f95a23c 155
20effc67
TL
156 clientcert = ctx.ssl_certificates.get('kmip-client')
157 servercert = ctx.ssl_certificates.get('kmip-server')
158 clientca = ctx.ssl_certificates.get('kmiproot')
159
160 clientkey = clientcert.key
161 clientcert = clientcert.certificate
162 serverkey = servercert.key
163 servercert = servercert.certificate
164 rootkey = clientca.key
165 rootcert = clientca.certificate
166
167 cert_path = '/etc/ceph/'
168 ctx.cluster.only(client).run(args=['sudo', 'cp', clientcert, cert_path])
169 ctx.cluster.only(client).run(args=['sudo', 'cp', clientkey, cert_path])
170 ctx.cluster.only(client).run(args=['sudo', 'cp', servercert, cert_path])
171 ctx.cluster.only(client).run(args=['sudo', 'cp', serverkey, cert_path])
172 ctx.cluster.only(client).run(args=['sudo', 'cp', rootkey, cert_path])
173 ctx.cluster.only(client).run(args=['sudo', 'cp', rootcert, cert_path])
174
175 clientcert = cert_path + 'kmip-client.crt'
176 clientkey = cert_path + 'kmip-client.key'
177 servercert = cert_path + 'kmip-server.crt'
178 serverkey = cert_path + 'kmip-server.key'
179 rootkey = cert_path + 'kmiproot.key'
180 rootcert = cert_path + 'kmiproot.crt'
181
182 ctx.cluster.only(client).run(args=['sudo', 'chmod', '600', clientcert, clientkey, servercert, serverkey, rootkey, rootcert])
183 ctx.cluster.only(client).run(args=['sudo', 'chown', 'ceph', clientcert, clientkey, servercert, serverkey, rootkey, rootcert])
184
11fdf7f2 185 rgw_cmd.extend([
7c673cae
FG
186 '--foreground',
187 run.Raw('|'),
188 'sudo',
189 'tee',
9f95a23c 190 '/var/log/ceph/rgw.{client_with_cluster}.stdout'.format(client_with_cluster=client_with_cluster),
7c673cae
FG
191 run.Raw('2>&1'),
192 ])
193
194 if client_config.get('valgrind'):
f67539c2 195 cmd_prefix = get_valgrind_args(
7c673cae 196 testdir,
31f18b77 197 client_with_cluster,
7c673cae 198 cmd_prefix,
20effc67
TL
199 client_config.get('valgrind'),
200 # see https://github.com/ceph/teuthology/pull/1600
201 exit_on_first_error=False
7c673cae
FG
202 )
203
204 run_cmd = list(cmd_prefix)
205 run_cmd.extend(rgw_cmd)
206
207 ctx.daemons.add_daemon(
31f18b77 208 remote, 'rgw', client_with_id,
7c673cae 209 cluster=cluster_name,
f67539c2 210 fsid=ctx.ceph[cluster_name].fsid,
7c673cae
FG
211 args=run_cmd,
212 logger=log.getChild(client),
213 stdin=run.PIPE,
214 wait=False,
215 )
216
217 # XXX: add_daemon() doesn't let us wait until radosgw finishes startup
11fdf7f2
TL
218 for client in clients:
219 endpoint = ctx.rgw.role_endpoints[client]
220 url = endpoint.url()
221 log.info('Polling {client} until it starts accepting connections on {url}'.format(client=client, url=url))
9f95a23c 222 (remote,) = ctx.cluster.only(client).remotes.keys()
81eedcae 223 wait_for_radosgw(url, remote)
7c673cae
FG
224
225 try:
226 yield
227 finally:
11fdf7f2 228 for client in clients:
31f18b77
FG
229 cluster_name, daemon_type, client_id = teuthology.split_role(client)
230 client_with_id = daemon_type + '.' + client_id
231 client_with_cluster = cluster_name + '.' + client_with_id
232 ctx.daemons.get_daemon('rgw', client_with_id, cluster_name).stop()
7c673cae
FG
233 ctx.cluster.only(client).run(
234 args=[
235 'rm',
236 '-f',
31f18b77
FG
237 '{tdir}/rgw.opslog.{client}.sock'.format(tdir=testdir,
238 client=client_with_cluster),
7c673cae
FG
239 ],
240 )
20effc67 241 ctx.cluster.only(client).run(args=['sudo', 'rm', '-f', token_path])
7c673cae 242
11fdf7f2 243def assign_endpoints(ctx, config, default_cert):
7c673cae 244 role_endpoints = {}
9f95a23c 245 for role, client_config in config.items():
11fdf7f2
TL
246 client_config = client_config or {}
247 remote = get_remote_for_role(ctx, role)
248
249 cert = client_config.get('ssl certificate', default_cert)
250 if cert:
251 # find the certificate created by the ssl task
252 if not hasattr(ctx, 'ssl_certificates'):
253 raise ConfigError('rgw: no ssl task found for option "ssl certificate"')
254 ssl_certificate = ctx.ssl_certificates.get(cert, None)
255 if not ssl_certificate:
256 raise ConfigError('rgw: missing ssl certificate "{}"'.format(cert))
257 else:
258 ssl_certificate = None
259
494da23a
TL
260 port = client_config.get('port', 443 if ssl_certificate else 80)
261
262 # if dns-name is given, use it as the hostname (or as a prefix)
263 dns_name = client_config.get('dns-name', '')
264 if len(dns_name) == 0 or dns_name.endswith('.'):
265 dns_name += remote.hostname
266
267 website_dns_name = client_config.get('dns-s3website-name')
e306af50
TL
268 if website_dns_name is not None and (len(website_dns_name) == 0 or website_dns_name.endswith('.')):
269 website_dns_name += remote.hostname
494da23a
TL
270
271 role_endpoints[role] = RGWEndpoint(remote.hostname, port, ssl_certificate, dns_name, website_dns_name)
7c673cae
FG
272
273 return role_endpoints
274
7c673cae 275@contextlib.contextmanager
224ce89b 276def create_pools(ctx, clients):
7c673cae 277 """Create replicated or erasure coded data pools for rgw."""
7c673cae 278
31f18b77 279 log.info('Creating data pools')
224ce89b
WB
280 for client in clients:
281 log.debug("Obtaining remote for client {}".format(client))
9f95a23c 282 (remote,) = ctx.cluster.only(client).remotes.keys()
11fdf7f2 283 data_pool = 'default.rgw.buckets.data'
7c673cae
FG
284 cluster_name, daemon_type, client_id = teuthology.split_role(client)
285
286 if ctx.rgw.ec_data_pool:
11fdf7f2 287 create_ec_pool(remote, data_pool, client, ctx.rgw.data_pool_pg_size,
b5b8bbf5 288 ctx.rgw.erasure_code_profile, cluster_name, 'rgw')
7c673cae 289 else:
11fdf7f2
TL
290 create_replicated_pool(remote, data_pool, ctx.rgw.data_pool_pg_size, cluster_name, 'rgw')
291
292 index_pool = 'default.rgw.buckets.index'
293 create_replicated_pool(remote, index_pool, ctx.rgw.index_pool_pg_size, cluster_name, 'rgw')
294
7c673cae
FG
295 if ctx.rgw.cache_pools:
296 create_cache_pool(remote, data_pool, data_pool + '.cache', 64,
181888fb 297 64*1024*1024, cluster_name)
31f18b77 298 log.debug('Pools created')
7c673cae
FG
299 yield
300
301@contextlib.contextmanager
224ce89b 302def configure_compression(ctx, clients, compression):
31f18b77
FG
303 """ set a compression type in the default zone placement """
304 log.info('Configuring compression type = %s', compression)
224ce89b 305 for client in clients:
7c673cae
FG
306 # XXX: the 'default' zone and zonegroup aren't created until we run RGWRados::init_complete().
307 # issue a 'radosgw-admin user list' command to trigger this
308 rgwadmin(ctx, client, cmd=['user', 'list'], check_status=True)
309
310 rgwadmin(ctx, client,
311 cmd=['zone', 'placement', 'modify', '--rgw-zone', 'default',
31f18b77
FG
312 '--placement-id', 'default-placement',
313 '--compression', compression],
7c673cae 314 check_status=True)
7c673cae
FG
315 yield
316
20effc67
TL
317@contextlib.contextmanager
318def configure_datacache(ctx, clients, datacache_path):
319 """ create directory for rgw datacache """
320 log.info('Preparing directory for rgw datacache at %s', datacache_path)
321 for client in clients:
322 if(datacache_path != None):
323 ctx.cluster.only(client).run(args=['mkdir', '-p', datacache_path])
324 ctx.cluster.only(client).run(args=['sudo', 'chmod', 'a+rwx', datacache_path])
325 else:
326 log.info('path for datacache was not provided')
327 yield
328
11fdf7f2
TL
329@contextlib.contextmanager
330def configure_storage_classes(ctx, clients, storage_classes):
331 """ set a compression type in the default zone placement """
332
333 sc = [s.strip() for s in storage_classes.split(',')]
334
335 for client in clients:
336 # XXX: the 'default' zone and zonegroup aren't created until we run RGWRados::init_complete().
337 # issue a 'radosgw-admin user list' command to trigger this
338 rgwadmin(ctx, client, cmd=['user', 'list'], check_status=True)
339
340 for storage_class in sc:
341 log.info('Configuring storage class type = %s', storage_class)
342 rgwadmin(ctx, client,
343 cmd=['zonegroup', 'placement', 'add',
344 '--rgw-zone', 'default',
345 '--placement-id', 'default-placement',
346 '--storage-class', storage_class],
347 check_status=True)
348 rgwadmin(ctx, client,
349 cmd=['zone', 'placement', 'add',
350 '--rgw-zone', 'default',
351 '--placement-id', 'default-placement',
352 '--storage-class', storage_class,
353 '--data-pool', 'default.rgw.buckets.data.' + storage_class.lower()],
354 check_status=True)
355 yield
356
7c673cae
FG
357@contextlib.contextmanager
358def task(ctx, config):
359 """
7c673cae
FG
360 For example, to run rgw on all clients::
361
362 tasks:
363 - ceph:
364 - rgw:
365
366 To only run on certain clients::
367
368 tasks:
369 - ceph:
370 - rgw: [client.0, client.3]
371
372 or
373
374 tasks:
375 - ceph:
376 - rgw:
377 client.0:
378 client.3:
379
7c673cae
FG
380 To run radosgw through valgrind:
381
382 tasks:
383 - ceph:
384 - rgw:
385 client.0:
386 valgrind: [--tool=memcheck]
387 client.3:
388 valgrind: [--tool=memcheck]
11fdf7f2
TL
389
390 To configure data or index pool pg_size:
391
392 overrides:
393 rgw:
394 data_pool_pg_size: 256
395 index_pool_pg_size: 128
7c673cae
FG
396 """
397 if config is None:
398 config = dict(('client.{id}'.format(id=id_), None)
399 for id_ in teuthology.all_roles_of_type(
400 ctx.cluster, 'client'))
401 elif isinstance(config, list):
402 config = dict((name, None) for name in config)
403
224ce89b
WB
404 clients = config.keys() # http://tracker.ceph.com/issues/20417
405
7c673cae
FG
406 overrides = ctx.config.get('overrides', {})
407 teuthology.deep_merge(config, overrides.get('rgw', {}))
408
7c673cae 409 ctx.rgw = argparse.Namespace()
39ae355f 410 ctx.rgw_cloudtier = None
7c673cae 411
31f18b77
FG
412 ctx.rgw.ec_data_pool = bool(config.pop('ec-data-pool', False))
413 ctx.rgw.erasure_code_profile = config.pop('erasure_code_profile', {})
414 ctx.rgw.cache_pools = bool(config.pop('cache-pools', False))
20effc67 415 ctx.rgw.frontend = config.pop('frontend', 'beast')
31f18b77 416 ctx.rgw.compression_type = config.pop('compression type', None)
11fdf7f2
TL
417 ctx.rgw.storage_classes = config.pop('storage classes', None)
418 default_cert = config.pop('ssl certificate', None)
419 ctx.rgw.data_pool_pg_size = config.pop('data_pool_pg_size', 64)
420 ctx.rgw.index_pool_pg_size = config.pop('index_pool_pg_size', 64)
20effc67
TL
421 ctx.rgw.datacache = bool(config.pop('datacache', False))
422 ctx.rgw.datacache_path = config.pop('datacache_path', None)
7c673cae 423 ctx.rgw.config = config
7c673cae 424
224ce89b
WB
425 log.debug("config is {}".format(config))
426 log.debug("client list is {}".format(clients))
11fdf7f2
TL
427
428 ctx.rgw.role_endpoints = assign_endpoints(ctx, config, default_cert)
429
31f18b77 430 subtasks = [
224ce89b 431 lambda: create_pools(ctx=ctx, clients=clients),
31f18b77
FG
432 ]
433 if ctx.rgw.compression_type:
7c673cae 434 subtasks.extend([
224ce89b 435 lambda: configure_compression(ctx=ctx, clients=clients,
31f18b77 436 compression=ctx.rgw.compression_type),
7c673cae 437 ])
20effc67
TL
438 if ctx.rgw.datacache:
439 subtasks.extend([
440 lambda: configure_datacache(ctx=ctx, clients=clients,
441 datacache_path=ctx.rgw.datacache_path),
442 ])
11fdf7f2
TL
443 if ctx.rgw.storage_classes:
444 subtasks.extend([
445 lambda: configure_storage_classes(ctx=ctx, clients=clients,
446 storage_classes=ctx.rgw.storage_classes),
447 ])
31f18b77 448 subtasks.extend([
224ce89b 449 lambda: start_rgw(ctx=ctx, config=config, clients=clients),
31f18b77 450 ])
7c673cae 451
7c673cae
FG
452 with contextutil.nested(*subtasks):
453 yield