]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | """ |
2 | Admin Socket task -- used in rados, powercycle, and smoke testing | |
3 | """ | |
7c673cae FG |
4 | |
5 | import json | |
6 | import logging | |
7 | import os | |
8 | import time | |
9 | ||
9f95a23c | 10 | from teuthology.exceptions import CommandFailedError |
7c673cae FG |
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: | |
9f95a23c | 71 | for client, tests in config.items(): |
7c673cae FG |
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 | """ | |
7c673cae FG |
87 | testdir = teuthology.get_testdir(ctx) |
88 | max_tries = 120 | |
20effc67 TL |
89 | sub_commands = [c.strip() for c in command.split('||')] |
90 | ex = None | |
91 | for _ in range(max_tries): | |
92 | for sub_command in sub_commands: | |
93 | try: | |
94 | out = remote.sh([ | |
95 | 'sudo', | |
96 | 'adjust-ulimits', | |
97 | 'ceph-coverage', | |
98 | '{tdir}/archive/coverage'.format(tdir=testdir), | |
99 | 'ceph', | |
100 | '--admin-daemon', socket_path, | |
101 | ] + sub_command.split(' ') + args) | |
102 | except CommandFailedError as e: | |
103 | ex = e | |
104 | log.info('ceph cli "%s" returned an error %s, ' | |
105 | 'command not registered yet?', sub_command, e) | |
106 | else: | |
107 | log.debug('admin socket command %s returned %s', | |
108 | sub_command, out) | |
109 | return json.loads(out) | |
110 | else: | |
111 | # exhausted all commands | |
9f95a23c TL |
112 | log.info('sleeping and retrying ...') |
113 | time.sleep(1) | |
20effc67 TL |
114 | else: |
115 | # i tried max_tries times.. | |
116 | assert ex is not None | |
117 | raise ex | |
118 | ||
7c673cae FG |
119 | |
120 | def _run_tests(ctx, client, tests): | |
121 | """ | |
122 | Create a temp directory and wait for a client socket to be created. | |
123 | For each test, copy the executable locally and run the test. | |
124 | Remove temp directory when finished. | |
125 | ||
126 | :param ctx: Context | |
127 | :param client: client machine to run the test | |
128 | :param tests: list of tests to run | |
129 | """ | |
130 | testdir = teuthology.get_testdir(ctx) | |
131 | log.debug('Running admin socket tests on %s', client) | |
9f95a23c | 132 | (remote,) = ctx.cluster.only(client).remotes.keys() |
7c673cae FG |
133 | socket_path = '/var/run/ceph/ceph-{name}.asok'.format(name=client) |
134 | overrides = ctx.config.get('overrides', {}).get('admin_socket', {}) | |
135 | ||
136 | try: | |
137 | tmp_dir = os.path.join( | |
138 | testdir, | |
139 | 'admin_socket_{client}'.format(client=client), | |
140 | ) | |
141 | remote.run( | |
142 | args=[ | |
143 | 'mkdir', | |
144 | '--', | |
145 | tmp_dir, | |
146 | run.Raw('&&'), | |
147 | # wait for client process to create the socket | |
148 | 'while', 'test', '!', '-e', socket_path, run.Raw(';'), | |
149 | 'do', 'sleep', '1', run.Raw(';'), 'done', | |
150 | ], | |
151 | ) | |
152 | ||
9f95a23c | 153 | for command, config in tests.items(): |
7c673cae FG |
154 | if config is None: |
155 | config = {} | |
156 | teuthology.deep_merge(config, overrides) | |
157 | log.debug('Testing %s with config %s', command, str(config)) | |
158 | ||
159 | test_path = None | |
160 | if 'test' in config: | |
161 | # hack: the git_url is always ceph-ci or ceph | |
162 | git_url = teuth_config.get_ceph_git_url() | |
163 | repo_name = 'ceph.git' | |
164 | if git_url.count('ceph-ci'): | |
165 | repo_name = 'ceph-ci.git' | |
166 | url = config['test'].format( | |
167 | branch=config.get('branch', 'master'), | |
168 | repo=repo_name, | |
169 | ) | |
170 | test_path = os.path.join(tmp_dir, command) | |
171 | remote.run( | |
172 | args=[ | |
173 | 'wget', | |
174 | '-q', | |
175 | '-O', | |
176 | test_path, | |
177 | '--', | |
178 | url, | |
179 | run.Raw('&&'), | |
180 | 'chmod', | |
181 | 'u=rx', | |
182 | '--', | |
183 | test_path, | |
184 | ], | |
185 | ) | |
186 | ||
187 | args = config.get('args', []) | |
188 | assert isinstance(args, list), \ | |
189 | 'admin socket command args must be a list' | |
190 | sock_out = _socket_command(ctx, remote, socket_path, command, args) | |
191 | if test_path is not None: | |
192 | remote.run( | |
193 | args=[ | |
194 | test_path, | |
195 | ], | |
196 | stdin=json.dumps(sock_out), | |
197 | ) | |
198 | ||
199 | finally: | |
200 | remote.run( | |
201 | args=[ | |
202 | 'rm', '-rf', '--', tmp_dir, | |
203 | ], | |
204 | ) |