]> git.proxmox.com Git - ceph.git/blame - ceph/qa/tasks/rgw.py
import quincy beta 17.1.0
[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
TL
140
141 rgw_cmd.extend([
142 '--rgw_crypt_vault_addr', "{}:{}".format(*ctx.vault.endpoints[vault_role]),
143 '--rgw_crypt_vault_token_file', token_path
144 ])
f67539c2
TL
145 elif pykmip_role is not None:
146 if not hasattr(ctx, 'pykmip'):
147 raise ConfigError('rgw must run after the pykmip task')
148 ctx.rgw.pykmip_role = pykmip_role
149 rgw_cmd.extend([
150 '--rgw_crypt_kmip_addr', "{}:{}".format(*ctx.pykmip.endpoints[pykmip_role]),
151 ])
9f95a23c 152
20effc67
TL
153 clientcert = ctx.ssl_certificates.get('kmip-client')
154 servercert = ctx.ssl_certificates.get('kmip-server')
155 clientca = ctx.ssl_certificates.get('kmiproot')
156
157 clientkey = clientcert.key
158 clientcert = clientcert.certificate
159 serverkey = servercert.key
160 servercert = servercert.certificate
161 rootkey = clientca.key
162 rootcert = clientca.certificate
163
164 cert_path = '/etc/ceph/'
165 ctx.cluster.only(client).run(args=['sudo', 'cp', clientcert, cert_path])
166 ctx.cluster.only(client).run(args=['sudo', 'cp', clientkey, cert_path])
167 ctx.cluster.only(client).run(args=['sudo', 'cp', servercert, cert_path])
168 ctx.cluster.only(client).run(args=['sudo', 'cp', serverkey, cert_path])
169 ctx.cluster.only(client).run(args=['sudo', 'cp', rootkey, cert_path])
170 ctx.cluster.only(client).run(args=['sudo', 'cp', rootcert, cert_path])
171
172 clientcert = cert_path + 'kmip-client.crt'
173 clientkey = cert_path + 'kmip-client.key'
174 servercert = cert_path + 'kmip-server.crt'
175 serverkey = cert_path + 'kmip-server.key'
176 rootkey = cert_path + 'kmiproot.key'
177 rootcert = cert_path + 'kmiproot.crt'
178
179 ctx.cluster.only(client).run(args=['sudo', 'chmod', '600', clientcert, clientkey, servercert, serverkey, rootkey, rootcert])
180 ctx.cluster.only(client).run(args=['sudo', 'chown', 'ceph', clientcert, clientkey, servercert, serverkey, rootkey, rootcert])
181
11fdf7f2 182 rgw_cmd.extend([
7c673cae
FG
183 '--foreground',
184 run.Raw('|'),
185 'sudo',
186 'tee',
9f95a23c 187 '/var/log/ceph/rgw.{client_with_cluster}.stdout'.format(client_with_cluster=client_with_cluster),
7c673cae
FG
188 run.Raw('2>&1'),
189 ])
190
191 if client_config.get('valgrind'):
f67539c2 192 cmd_prefix = get_valgrind_args(
7c673cae 193 testdir,
31f18b77 194 client_with_cluster,
7c673cae 195 cmd_prefix,
20effc67
TL
196 client_config.get('valgrind'),
197 # see https://github.com/ceph/teuthology/pull/1600
198 exit_on_first_error=False
7c673cae
FG
199 )
200
201 run_cmd = list(cmd_prefix)
202 run_cmd.extend(rgw_cmd)
203
204 ctx.daemons.add_daemon(
31f18b77 205 remote, 'rgw', client_with_id,
7c673cae 206 cluster=cluster_name,
f67539c2 207 fsid=ctx.ceph[cluster_name].fsid,
7c673cae
FG
208 args=run_cmd,
209 logger=log.getChild(client),
210 stdin=run.PIPE,
211 wait=False,
212 )
213
214 # XXX: add_daemon() doesn't let us wait until radosgw finishes startup
11fdf7f2
TL
215 for client in clients:
216 endpoint = ctx.rgw.role_endpoints[client]
217 url = endpoint.url()
218 log.info('Polling {client} until it starts accepting connections on {url}'.format(client=client, url=url))
9f95a23c 219 (remote,) = ctx.cluster.only(client).remotes.keys()
81eedcae 220 wait_for_radosgw(url, remote)
7c673cae
FG
221
222 try:
223 yield
224 finally:
11fdf7f2 225 for client in clients:
31f18b77
FG
226 cluster_name, daemon_type, client_id = teuthology.split_role(client)
227 client_with_id = daemon_type + '.' + client_id
228 client_with_cluster = cluster_name + '.' + client_with_id
229 ctx.daemons.get_daemon('rgw', client_with_id, cluster_name).stop()
7c673cae
FG
230 ctx.cluster.only(client).run(
231 args=[
232 'rm',
233 '-f',
31f18b77
FG
234 '{tdir}/rgw.opslog.{client}.sock'.format(tdir=testdir,
235 client=client_with_cluster),
7c673cae
FG
236 ],
237 )
20effc67 238 ctx.cluster.only(client).run(args=['sudo', 'rm', '-f', token_path])
7c673cae 239
11fdf7f2 240def assign_endpoints(ctx, config, default_cert):
7c673cae 241 role_endpoints = {}
9f95a23c 242 for role, client_config in config.items():
11fdf7f2
TL
243 client_config = client_config or {}
244 remote = get_remote_for_role(ctx, role)
245
246 cert = client_config.get('ssl certificate', default_cert)
247 if cert:
248 # find the certificate created by the ssl task
249 if not hasattr(ctx, 'ssl_certificates'):
250 raise ConfigError('rgw: no ssl task found for option "ssl certificate"')
251 ssl_certificate = ctx.ssl_certificates.get(cert, None)
252 if not ssl_certificate:
253 raise ConfigError('rgw: missing ssl certificate "{}"'.format(cert))
254 else:
255 ssl_certificate = None
256
494da23a
TL
257 port = client_config.get('port', 443 if ssl_certificate else 80)
258
259 # if dns-name is given, use it as the hostname (or as a prefix)
260 dns_name = client_config.get('dns-name', '')
261 if len(dns_name) == 0 or dns_name.endswith('.'):
262 dns_name += remote.hostname
263
264 website_dns_name = client_config.get('dns-s3website-name')
e306af50
TL
265 if website_dns_name is not None and (len(website_dns_name) == 0 or website_dns_name.endswith('.')):
266 website_dns_name += remote.hostname
494da23a
TL
267
268 role_endpoints[role] = RGWEndpoint(remote.hostname, port, ssl_certificate, dns_name, website_dns_name)
7c673cae
FG
269
270 return role_endpoints
271
7c673cae 272@contextlib.contextmanager
224ce89b 273def create_pools(ctx, clients):
7c673cae 274 """Create replicated or erasure coded data pools for rgw."""
7c673cae 275
31f18b77 276 log.info('Creating data pools')
224ce89b
WB
277 for client in clients:
278 log.debug("Obtaining remote for client {}".format(client))
9f95a23c 279 (remote,) = ctx.cluster.only(client).remotes.keys()
11fdf7f2 280 data_pool = 'default.rgw.buckets.data'
7c673cae
FG
281 cluster_name, daemon_type, client_id = teuthology.split_role(client)
282
283 if ctx.rgw.ec_data_pool:
11fdf7f2 284 create_ec_pool(remote, data_pool, client, ctx.rgw.data_pool_pg_size,
b5b8bbf5 285 ctx.rgw.erasure_code_profile, cluster_name, 'rgw')
7c673cae 286 else:
11fdf7f2
TL
287 create_replicated_pool(remote, data_pool, ctx.rgw.data_pool_pg_size, cluster_name, 'rgw')
288
289 index_pool = 'default.rgw.buckets.index'
290 create_replicated_pool(remote, index_pool, ctx.rgw.index_pool_pg_size, cluster_name, 'rgw')
291
7c673cae
FG
292 if ctx.rgw.cache_pools:
293 create_cache_pool(remote, data_pool, data_pool + '.cache', 64,
181888fb 294 64*1024*1024, cluster_name)
31f18b77 295 log.debug('Pools created')
7c673cae
FG
296 yield
297
298@contextlib.contextmanager
224ce89b 299def configure_compression(ctx, clients, compression):
31f18b77
FG
300 """ set a compression type in the default zone placement """
301 log.info('Configuring compression type = %s', compression)
224ce89b 302 for client in clients:
7c673cae
FG
303 # XXX: the 'default' zone and zonegroup aren't created until we run RGWRados::init_complete().
304 # issue a 'radosgw-admin user list' command to trigger this
305 rgwadmin(ctx, client, cmd=['user', 'list'], check_status=True)
306
307 rgwadmin(ctx, client,
308 cmd=['zone', 'placement', 'modify', '--rgw-zone', 'default',
31f18b77
FG
309 '--placement-id', 'default-placement',
310 '--compression', compression],
7c673cae 311 check_status=True)
7c673cae
FG
312 yield
313
20effc67
TL
314@contextlib.contextmanager
315def configure_datacache(ctx, clients, datacache_path):
316 """ create directory for rgw datacache """
317 log.info('Preparing directory for rgw datacache at %s', datacache_path)
318 for client in clients:
319 if(datacache_path != None):
320 ctx.cluster.only(client).run(args=['mkdir', '-p', datacache_path])
321 ctx.cluster.only(client).run(args=['sudo', 'chmod', 'a+rwx', datacache_path])
322 else:
323 log.info('path for datacache was not provided')
324 yield
325
11fdf7f2
TL
326@contextlib.contextmanager
327def configure_storage_classes(ctx, clients, storage_classes):
328 """ set a compression type in the default zone placement """
329
330 sc = [s.strip() for s in storage_classes.split(',')]
331
332 for client in clients:
333 # XXX: the 'default' zone and zonegroup aren't created until we run RGWRados::init_complete().
334 # issue a 'radosgw-admin user list' command to trigger this
335 rgwadmin(ctx, client, cmd=['user', 'list'], check_status=True)
336
337 for storage_class in sc:
338 log.info('Configuring storage class type = %s', storage_class)
339 rgwadmin(ctx, client,
340 cmd=['zonegroup', 'placement', 'add',
341 '--rgw-zone', 'default',
342 '--placement-id', 'default-placement',
343 '--storage-class', storage_class],
344 check_status=True)
345 rgwadmin(ctx, client,
346 cmd=['zone', 'placement', 'add',
347 '--rgw-zone', 'default',
348 '--placement-id', 'default-placement',
349 '--storage-class', storage_class,
350 '--data-pool', 'default.rgw.buckets.data.' + storage_class.lower()],
351 check_status=True)
352 yield
353
7c673cae
FG
354@contextlib.contextmanager
355def task(ctx, config):
356 """
7c673cae
FG
357 For example, to run rgw on all clients::
358
359 tasks:
360 - ceph:
361 - rgw:
362
363 To only run on certain clients::
364
365 tasks:
366 - ceph:
367 - rgw: [client.0, client.3]
368
369 or
370
371 tasks:
372 - ceph:
373 - rgw:
374 client.0:
375 client.3:
376
7c673cae
FG
377 To run radosgw through valgrind:
378
379 tasks:
380 - ceph:
381 - rgw:
382 client.0:
383 valgrind: [--tool=memcheck]
384 client.3:
385 valgrind: [--tool=memcheck]
11fdf7f2
TL
386
387 To configure data or index pool pg_size:
388
389 overrides:
390 rgw:
391 data_pool_pg_size: 256
392 index_pool_pg_size: 128
7c673cae
FG
393 """
394 if config is None:
395 config = dict(('client.{id}'.format(id=id_), None)
396 for id_ in teuthology.all_roles_of_type(
397 ctx.cluster, 'client'))
398 elif isinstance(config, list):
399 config = dict((name, None) for name in config)
400
224ce89b
WB
401 clients = config.keys() # http://tracker.ceph.com/issues/20417
402
7c673cae
FG
403 overrides = ctx.config.get('overrides', {})
404 teuthology.deep_merge(config, overrides.get('rgw', {}))
405
7c673cae 406 ctx.rgw = argparse.Namespace()
7c673cae 407
31f18b77
FG
408 ctx.rgw.ec_data_pool = bool(config.pop('ec-data-pool', False))
409 ctx.rgw.erasure_code_profile = config.pop('erasure_code_profile', {})
410 ctx.rgw.cache_pools = bool(config.pop('cache-pools', False))
20effc67 411 ctx.rgw.frontend = config.pop('frontend', 'beast')
31f18b77 412 ctx.rgw.compression_type = config.pop('compression type', None)
11fdf7f2
TL
413 ctx.rgw.storage_classes = config.pop('storage classes', None)
414 default_cert = config.pop('ssl certificate', None)
415 ctx.rgw.data_pool_pg_size = config.pop('data_pool_pg_size', 64)
416 ctx.rgw.index_pool_pg_size = config.pop('index_pool_pg_size', 64)
20effc67
TL
417 ctx.rgw.datacache = bool(config.pop('datacache', False))
418 ctx.rgw.datacache_path = config.pop('datacache_path', None)
7c673cae 419 ctx.rgw.config = config
7c673cae 420
224ce89b
WB
421 log.debug("config is {}".format(config))
422 log.debug("client list is {}".format(clients))
11fdf7f2
TL
423
424 ctx.rgw.role_endpoints = assign_endpoints(ctx, config, default_cert)
425
31f18b77 426 subtasks = [
224ce89b 427 lambda: create_pools(ctx=ctx, clients=clients),
31f18b77
FG
428 ]
429 if ctx.rgw.compression_type:
7c673cae 430 subtasks.extend([
224ce89b 431 lambda: configure_compression(ctx=ctx, clients=clients,
31f18b77 432 compression=ctx.rgw.compression_type),
7c673cae 433 ])
20effc67
TL
434 if ctx.rgw.datacache:
435 subtasks.extend([
436 lambda: configure_datacache(ctx=ctx, clients=clients,
437 datacache_path=ctx.rgw.datacache_path),
438 ])
11fdf7f2
TL
439 if ctx.rgw.storage_classes:
440 subtasks.extend([
441 lambda: configure_storage_classes(ctx=ctx, clients=clients,
442 storage_classes=ctx.rgw.storage_classes),
443 ])
31f18b77 444 subtasks.extend([
224ce89b 445 lambda: start_rgw(ctx=ctx, config=config, clients=clients),
31f18b77 446 ])
7c673cae 447
7c673cae
FG
448 with contextutil.nested(*subtasks):
449 yield