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