Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / qa / tasks / admin_socket.py
1 """
2 Admin Socket task -- used in rados, powercycle, and smoke testing
3 """
4 from cStringIO import StringIO
5
6 import json
7 import logging
8 import os
9 import time
10
11 from teuthology.orchestra import run
12 from teuthology import misc as teuthology
13 from teuthology.parallel import parallel
14 from teuthology.config import config as teuth_config
15
16 log = logging.getLogger(__name__)
17
18
19 def task(ctx, config):
20     """
21     Run an admin socket command, make sure the output is json, and run
22     a test program on it. The test program should read json from
23     stdin. This task succeeds if the test program exits with status 0.
24
25     To run the same test on all clients::
26
27         tasks:
28         - ceph:
29         - rados:
30         - admin_socket:
31             all:
32               dump_requests:
33                 test: http://example.com/script
34
35     To restrict it to certain clients::
36
37         tasks:
38         - ceph:
39         - rados: [client.1]
40         - admin_socket:
41             client.1:
42               dump_requests:
43                 test: http://example.com/script
44
45     If an admin socket command has arguments, they can be specified as
46     a list::
47
48         tasks:
49         - ceph:
50         - rados: [client.0]
51         - admin_socket:
52             client.0:
53               dump_requests:
54                 test: http://example.com/script
55               help:
56                 test: http://example.com/test_help_version
57                 args: [version]
58
59     Note that there must be a ceph client with an admin socket running
60     before this task is run. The tests are parallelized at the client
61     level. Tests for a single client are run serially.
62
63     :param ctx: Context
64     :param config: Configuration
65     """
66     assert isinstance(config, dict), \
67         'admin_socket task requires a dict for configuration'
68     teuthology.replace_all_with_clients(ctx.cluster, config)
69
70     with parallel() as ptask:
71         for client, tests in config.iteritems():
72             ptask.spawn(_run_tests, ctx, client, tests)
73
74
75 def _socket_command(ctx, remote, socket_path, command, args):
76     """
77     Run an admin socket command and return the result as a string.
78
79     :param ctx: Context
80     :param remote: Remote site
81     :param socket_path: path to socket
82     :param command: command to be run remotely
83     :param args: command arguments
84
85     :returns: output of command in json format
86     """
87     json_fp = StringIO()
88     testdir = teuthology.get_testdir(ctx)
89     max_tries = 120
90     while True:
91         proc = remote.run(
92             args=[
93                 'sudo',
94                 'adjust-ulimits',
95                 'ceph-coverage',
96                 '{tdir}/archive/coverage'.format(tdir=testdir),
97                 'ceph',
98                 '--admin-daemon', socket_path,
99                 ] + command.split(' ') + args,
100             stdout=json_fp,
101             check_status=False,
102             )
103         if proc.exitstatus == 0:
104             break
105         assert max_tries > 0
106         max_tries -= 1
107         log.info('ceph cli returned an error, command not registered yet?')
108         log.info('sleeping and retrying ...')
109         time.sleep(1)
110     out = json_fp.getvalue()
111     json_fp.close()
112     log.debug('admin socket command %s returned %s', command, out)
113     return json.loads(out)
114
115 def _run_tests(ctx, client, tests):
116     """
117     Create a temp directory and wait for a client socket to be created.
118     For each test, copy the executable locally and run the test.
119     Remove temp directory when finished.
120
121     :param ctx: Context
122     :param client: client machine to run the test
123     :param tests: list of tests to run
124     """
125     testdir = teuthology.get_testdir(ctx)
126     log.debug('Running admin socket tests on %s', client)
127     (remote,) = ctx.cluster.only(client).remotes.iterkeys()
128     socket_path = '/var/run/ceph/ceph-{name}.asok'.format(name=client)
129     overrides = ctx.config.get('overrides', {}).get('admin_socket', {})
130
131     try:
132         tmp_dir = os.path.join(
133             testdir,
134             'admin_socket_{client}'.format(client=client),
135             )
136         remote.run(
137             args=[
138                 'mkdir',
139                 '--',
140                 tmp_dir,
141                 run.Raw('&&'),
142                 # wait for client process to create the socket
143                 'while', 'test', '!', '-e', socket_path, run.Raw(';'),
144                 'do', 'sleep', '1', run.Raw(';'), 'done',
145                 ],
146             )
147
148         for command, config in tests.iteritems():
149             if config is None:
150                 config = {}
151             teuthology.deep_merge(config, overrides)
152             log.debug('Testing %s with config %s', command, str(config))
153
154             test_path = None
155             if 'test' in config:
156                 # hack: the git_url is always ceph-ci or ceph
157                 git_url = teuth_config.get_ceph_git_url()
158                 repo_name = 'ceph.git'
159                 if git_url.count('ceph-ci'):
160                     repo_name = 'ceph-ci.git'
161                 url = config['test'].format(
162                     branch=config.get('branch', 'master'),
163                     repo=repo_name,
164                     )
165                 test_path = os.path.join(tmp_dir, command)
166                 remote.run(
167                     args=[
168                         'wget',
169                         '-q',
170                         '-O',
171                         test_path,
172                         '--',
173                         url,
174                         run.Raw('&&'),
175                         'chmod',
176                         'u=rx',
177                         '--',
178                         test_path,
179                         ],
180                     )
181
182             args = config.get('args', [])
183             assert isinstance(args, list), \
184                 'admin socket command args must be a list'
185             sock_out = _socket_command(ctx, remote, socket_path, command, args)
186             if test_path is not None:
187                 remote.run(
188                     args=[
189                         test_path,
190                         ],
191                     stdin=json.dumps(sock_out),
192                     )
193
194     finally:
195         remote.run(
196             args=[
197                 'rm', '-rf', '--', tmp_dir,
198                 ],
199             )