]>
Commit | Line | Data |
---|---|---|
f67539c2 | 1 | # NOTE: these tests are not yet compatible with vstart_runner.py. |
f6b5b4d7 TL |
2 | import errno |
3 | import json | |
4 | import time | |
5 | import logging | |
6 | from io import BytesIO | |
7 | ||
8 | from tasks.mgr.mgr_test_case import MgrTestCase | |
9 | from teuthology.exceptions import CommandFailedError | |
10 | ||
11 | log = logging.getLogger(__name__) | |
12 | ||
13 | ||
14 | # TODO Add test for cluster update when ganesha can be deployed on multiple ports. | |
15 | class TestNFS(MgrTestCase): | |
f91f0fd5 | 16 | def _cmd(self, *args): |
f6b5b4d7 TL |
17 | return self.mgr_cluster.mon_manager.raw_cluster_cmd(*args) |
18 | ||
19 | def _nfs_cmd(self, *args): | |
20 | return self._cmd("nfs", *args) | |
21 | ||
22 | def _orch_cmd(self, *args): | |
23 | return self._cmd("orch", *args) | |
24 | ||
25 | def _sys_cmd(self, cmd): | |
26 | cmd[0:0] = ['sudo'] | |
27 | ret = self.ctx.cluster.run(args=cmd, check_status=False, stdout=BytesIO(), stderr=BytesIO()) | |
28 | stdout = ret[0].stdout | |
29 | if stdout: | |
30 | return stdout.getvalue() | |
31 | ||
32 | def setUp(self): | |
33 | super(TestNFS, self).setUp() | |
b3b6e05e | 34 | self._load_module('nfs') |
f6b5b4d7 TL |
35 | self.cluster_id = "test" |
36 | self.export_type = "cephfs" | |
37 | self.pseudo_path = "/cephfs" | |
38 | self.path = "/" | |
39 | self.fs_name = "nfs-cephfs" | |
f67539c2 | 40 | self.expected_name = "nfs.test" |
f6b5b4d7 TL |
41 | self.sample_export = { |
42 | "export_id": 1, | |
43 | "path": self.path, | |
44 | "cluster_id": self.cluster_id, | |
45 | "pseudo": self.pseudo_path, | |
46 | "access_type": "RW", | |
47 | "squash": "no_root_squash", | |
48 | "security_label": True, | |
49 | "protocols": [ | |
50 | 4 | |
51 | ], | |
52 | "transports": [ | |
53 | "TCP" | |
54 | ], | |
55 | "fsal": { | |
56 | "name": "CEPH", | |
57 | "user_id": "test1", | |
58 | "fs_name": self.fs_name, | |
59 | "sec_label_xattr": '' | |
60 | }, | |
61 | "clients": [] | |
62 | } | |
63 | ||
f6b5b4d7 TL |
64 | def _check_nfs_server_status(self): |
65 | res = self._sys_cmd(['systemctl', 'status', 'nfs-server']) | |
66 | if isinstance(res, bytes) and b'Active: active' in res: | |
67 | self._disable_nfs() | |
68 | ||
69 | def _disable_nfs(self): | |
70 | log.info("Disabling NFS") | |
71 | self._sys_cmd(['systemctl', 'disable', 'nfs-server', '--now']) | |
72 | ||
f91f0fd5 TL |
73 | def _fetch_nfs_status(self): |
74 | return self._orch_cmd('ps', f'--service_name={self.expected_name}') | |
75 | ||
76 | def _check_nfs_cluster_status(self, expected_status, fail_msg): | |
77 | ''' | |
78 | Tests if nfs cluster created or deleted successfully | |
79 | :param expected_status: Status to be verified | |
80 | :param fail_msg: Message to be printed if test failed | |
81 | ''' | |
82 | # Wait for few seconds as ganesha daemon takes few seconds to be deleted/created | |
83 | wait_time = 10 | |
84 | while wait_time <= 60: | |
85 | time.sleep(wait_time) | |
86 | if expected_status in self._fetch_nfs_status(): | |
87 | return | |
88 | wait_time += 10 | |
89 | self.fail(fail_msg) | |
f6b5b4d7 TL |
90 | |
91 | def _check_auth_ls(self, export_id=1, check_in=False): | |
92 | ''' | |
93 | Tests export user id creation or deletion. | |
94 | :param export_id: Denotes export number | |
95 | :param check_in: Check specified export id | |
96 | ''' | |
97 | output = self._cmd('auth', 'ls') | |
98 | if check_in: | |
99 | self.assertIn(f'client.{self.cluster_id}{export_id}', output) | |
100 | else: | |
101 | self.assertNotIn(f'client-{self.cluster_id}', output) | |
102 | ||
103 | def _test_idempotency(self, cmd_func, cmd_args): | |
104 | ''' | |
105 | Test idempotency of commands. It first runs the TestNFS test method | |
106 | for a command and then checks the result of command run again. TestNFS | |
107 | test method has required checks to verify that command works. | |
108 | :param cmd_func: TestNFS method | |
109 | :param cmd_args: nfs command arguments to be run | |
110 | ''' | |
111 | cmd_func() | |
112 | ret = self.mgr_cluster.mon_manager.raw_cluster_cmd_result(*cmd_args) | |
113 | if ret != 0: | |
114 | self.fail("Idempotency test failed") | |
115 | ||
116 | def _test_create_cluster(self): | |
117 | ''' | |
118 | Test single nfs cluster deployment. | |
119 | ''' | |
120 | # Disable any running nfs ganesha daemon | |
121 | self._check_nfs_server_status() | |
b3b6e05e | 122 | self._nfs_cmd('cluster', 'create', self.cluster_id) |
f67539c2 | 123 | # Check for expected status and daemon name (nfs.<cluster_id>) |
f91f0fd5 | 124 | self._check_nfs_cluster_status('running', 'NFS Ganesha cluster deployment failed') |
f6b5b4d7 TL |
125 | |
126 | def _test_delete_cluster(self): | |
127 | ''' | |
128 | Test deletion of a single nfs cluster. | |
129 | ''' | |
b3b6e05e | 130 | self._nfs_cmd('cluster', 'rm', self.cluster_id) |
f91f0fd5 TL |
131 | self._check_nfs_cluster_status('No daemons reported', |
132 | 'NFS Ganesha cluster could not be deleted') | |
f6b5b4d7 TL |
133 | |
134 | def _test_list_cluster(self, empty=False): | |
135 | ''' | |
136 | Test listing of deployed nfs clusters. If nfs cluster is deployed then | |
137 | it checks for expected cluster id. Otherwise checks nothing is listed. | |
138 | :param empty: If true it denotes no cluster is deployed. | |
139 | ''' | |
140 | if empty: | |
141 | cluster_id = '' | |
142 | else: | |
143 | cluster_id = self.cluster_id | |
144 | nfs_output = self._nfs_cmd('cluster', 'ls') | |
145 | self.assertEqual(cluster_id, nfs_output.strip()) | |
146 | ||
147 | def _create_export(self, export_id, create_fs=False, extra_cmd=None): | |
148 | ''' | |
149 | Test creation of a single export. | |
150 | :param export_id: Denotes export number | |
151 | :param create_fs: If false filesytem exists. Otherwise create it. | |
152 | :param extra_cmd: List of extra arguments for creating export. | |
153 | ''' | |
154 | if create_fs: | |
155 | self._cmd('fs', 'volume', 'create', self.fs_name) | |
156 | export_cmd = ['nfs', 'export', 'create', 'cephfs', self.fs_name, self.cluster_id] | |
157 | if isinstance(extra_cmd, list): | |
158 | export_cmd.extend(extra_cmd) | |
159 | else: | |
160 | export_cmd.append(self.pseudo_path) | |
161 | # Runs the nfs export create command | |
162 | self._cmd(*export_cmd) | |
163 | # Check if user id for export is created | |
164 | self._check_auth_ls(export_id, check_in=True) | |
165 | res = self._sys_cmd(['rados', '-p', 'nfs-ganesha', '-N', self.cluster_id, 'get', | |
166 | f'export-{export_id}', '-']) | |
167 | # Check if export object is created | |
168 | if res == b'': | |
169 | self.fail("Export cannot be created") | |
170 | ||
171 | def _create_default_export(self): | |
172 | ''' | |
173 | Deploy a single nfs cluster and create export with default options. | |
174 | ''' | |
175 | self._test_create_cluster() | |
176 | self._create_export(export_id='1', create_fs=True) | |
177 | ||
178 | def _delete_export(self): | |
179 | ''' | |
180 | Delete an export. | |
181 | ''' | |
b3b6e05e | 182 | self._nfs_cmd('export', 'rm', self.cluster_id, self.pseudo_path) |
f6b5b4d7 TL |
183 | self._check_auth_ls() |
184 | ||
185 | def _test_list_export(self): | |
186 | ''' | |
187 | Test listing of created exports. | |
188 | ''' | |
189 | nfs_output = json.loads(self._nfs_cmd('export', 'ls', self.cluster_id)) | |
190 | self.assertIn(self.pseudo_path, nfs_output) | |
191 | ||
192 | def _test_list_detailed(self, sub_vol_path): | |
193 | ''' | |
194 | Test listing of created exports with detailed option. | |
195 | :param sub_vol_path: Denotes path of subvolume | |
196 | ''' | |
197 | nfs_output = json.loads(self._nfs_cmd('export', 'ls', self.cluster_id, '--detailed')) | |
198 | # Export-1 with default values (access type = rw and path = '\') | |
199 | self.assertDictEqual(self.sample_export, nfs_output[0]) | |
200 | # Export-2 with r only | |
201 | self.sample_export['export_id'] = 2 | |
202 | self.sample_export['pseudo'] = self.pseudo_path + '1' | |
203 | self.sample_export['access_type'] = 'RO' | |
204 | self.sample_export['fsal']['user_id'] = self.cluster_id + '2' | |
205 | self.assertDictEqual(self.sample_export, nfs_output[1]) | |
206 | # Export-3 for subvolume with r only | |
207 | self.sample_export['export_id'] = 3 | |
208 | self.sample_export['path'] = sub_vol_path | |
209 | self.sample_export['pseudo'] = self.pseudo_path + '2' | |
210 | self.sample_export['fsal']['user_id'] = self.cluster_id + '3' | |
211 | self.assertDictEqual(self.sample_export, nfs_output[2]) | |
212 | # Export-4 for subvolume | |
213 | self.sample_export['export_id'] = 4 | |
214 | self.sample_export['pseudo'] = self.pseudo_path + '3' | |
215 | self.sample_export['access_type'] = 'RW' | |
216 | self.sample_export['fsal']['user_id'] = self.cluster_id + '4' | |
217 | self.assertDictEqual(self.sample_export, nfs_output[3]) | |
218 | ||
f67539c2 TL |
219 | def _get_export(self): |
220 | ''' | |
221 | Returns export block in json format | |
222 | ''' | |
223 | return json.loads(self._nfs_cmd('export', 'get', self.cluster_id, self.pseudo_path)) | |
224 | ||
f6b5b4d7 TL |
225 | def _test_get_export(self): |
226 | ''' | |
227 | Test fetching of created export. | |
228 | ''' | |
f67539c2 | 229 | nfs_output = self._get_export() |
f6b5b4d7 TL |
230 | self.assertDictEqual(self.sample_export, nfs_output) |
231 | ||
232 | def _check_export_obj_deleted(self, conf_obj=False): | |
233 | ''' | |
234 | Test if export or config object are deleted successfully. | |
235 | :param conf_obj: It denotes config object needs to be checked | |
236 | ''' | |
237 | rados_obj_ls = self._sys_cmd(['rados', '-p', 'nfs-ganesha', '-N', self.cluster_id, 'ls']) | |
238 | ||
239 | if b'export-' in rados_obj_ls or (conf_obj and b'conf-nfs' in rados_obj_ls): | |
240 | self.fail("Delete export failed") | |
241 | ||
f91f0fd5 TL |
242 | def _get_port_ip_info(self): |
243 | ''' | |
244 | Return port and ip for a cluster | |
245 | ''' | |
b3b6e05e TL |
246 | #{'test': {'backend': [{'hostname': 'smithi068', 'ip': '172.21.15.68', 'port': 2049}]}} |
247 | info_output = json.loads(self._nfs_cmd('cluster', 'info', self.cluster_id))['test']['backend'][0] | |
248 | return info_output["port"], info_output["ip"] | |
f91f0fd5 TL |
249 | |
250 | def _test_mnt(self, pseudo_path, port, ip, check=True): | |
251 | ''' | |
252 | Test mounting of created exports | |
253 | :param pseudo_path: It is the pseudo root name | |
254 | :param port: Port of deployed nfs cluster | |
255 | :param ip: IP of deployed nfs cluster | |
256 | :param check: It denotes if i/o testing needs to be done | |
257 | ''' | |
258 | try: | |
259 | self.ctx.cluster.run(args=['sudo', 'mount', '-t', 'nfs', '-o', f'port={port}', | |
260 | f'{ip}:{pseudo_path}', '/mnt']) | |
261 | except CommandFailedError as e: | |
262 | # Check if mount failed only when non existing pseudo path is passed | |
263 | if not check and e.exitstatus == 32: | |
264 | return | |
265 | raise | |
266 | ||
522d829b TL |
267 | self.ctx.cluster.run(args=['sudo', 'chmod', '1777', '/mnt']) |
268 | ||
f67539c2 | 269 | try: |
522d829b TL |
270 | self.ctx.cluster.run(args=['touch', '/mnt/test']) |
271 | out_mnt = self._sys_cmd(['ls', '/mnt']) | |
f91f0fd5 | 272 | self.assertEqual(out_mnt, b'test\n') |
f67539c2 | 273 | finally: |
f91f0fd5 TL |
274 | self.ctx.cluster.run(args=['sudo', 'umount', '/mnt']) |
275 | ||
f67539c2 TL |
276 | def _write_to_read_only_export(self, pseudo_path, port, ip): |
277 | ''' | |
278 | Check if write to read only export fails | |
279 | ''' | |
280 | try: | |
281 | self._test_mnt(pseudo_path, port, ip) | |
282 | except CommandFailedError as e: | |
283 | # Write to cephfs export should fail for test to pass | |
284 | if e.exitstatus != errno.EPERM: | |
285 | raise | |
286 | ||
f6b5b4d7 TL |
287 | def test_create_and_delete_cluster(self): |
288 | ''' | |
289 | Test successful creation and deletion of the nfs cluster. | |
290 | ''' | |
291 | self._test_create_cluster() | |
292 | self._test_list_cluster() | |
293 | self._test_delete_cluster() | |
294 | # List clusters again to ensure no cluster is shown | |
295 | self._test_list_cluster(empty=True) | |
296 | ||
297 | def test_create_delete_cluster_idempotency(self): | |
298 | ''' | |
299 | Test idempotency of cluster create and delete commands. | |
300 | ''' | |
b3b6e05e TL |
301 | self._test_idempotency(self._test_create_cluster, ['nfs', 'cluster', 'create', self.cluster_id]) |
302 | self._test_idempotency(self._test_delete_cluster, ['nfs', 'cluster', 'rm', self.cluster_id]) | |
f6b5b4d7 TL |
303 | |
304 | def test_create_cluster_with_invalid_cluster_id(self): | |
305 | ''' | |
306 | Test nfs cluster deployment failure with invalid cluster id. | |
307 | ''' | |
308 | try: | |
309 | invalid_cluster_id = '/cluster_test' # Only [A-Za-z0-9-_.] chars are valid | |
b3b6e05e | 310 | self._nfs_cmd('cluster', 'create', invalid_cluster_id) |
f6b5b4d7 TL |
311 | self.fail(f"Cluster successfully created with invalid cluster id {invalid_cluster_id}") |
312 | except CommandFailedError as e: | |
313 | # Command should fail for test to pass | |
314 | if e.exitstatus != errno.EINVAL: | |
315 | raise | |
316 | ||
f6b5b4d7 TL |
317 | def test_create_and_delete_export(self): |
318 | ''' | |
319 | Test successful creation and deletion of the cephfs export. | |
320 | ''' | |
321 | self._create_default_export() | |
322 | self._test_get_export() | |
f91f0fd5 TL |
323 | port, ip = self._get_port_ip_info() |
324 | self._test_mnt(self.pseudo_path, port, ip) | |
f6b5b4d7 TL |
325 | self._delete_export() |
326 | # Check if rados export object is deleted | |
327 | self._check_export_obj_deleted() | |
f91f0fd5 | 328 | self._test_mnt(self.pseudo_path, port, ip, False) |
f6b5b4d7 TL |
329 | self._test_delete_cluster() |
330 | ||
331 | def test_create_delete_export_idempotency(self): | |
332 | ''' | |
333 | Test idempotency of export create and delete commands. | |
334 | ''' | |
335 | self._test_idempotency(self._create_default_export, ['nfs', 'export', 'create', 'cephfs', | |
336 | self.fs_name, self.cluster_id, | |
337 | self.pseudo_path]) | |
b3b6e05e | 338 | self._test_idempotency(self._delete_export, ['nfs', 'export', 'rm', self.cluster_id, |
f6b5b4d7 TL |
339 | self.pseudo_path]) |
340 | self._test_delete_cluster() | |
341 | ||
342 | def test_create_multiple_exports(self): | |
343 | ''' | |
344 | Test creating multiple exports with different access type and path. | |
345 | ''' | |
346 | # Export-1 with default values (access type = rw and path = '\') | |
347 | self._create_default_export() | |
348 | # Export-2 with r only | |
349 | self._create_export(export_id='2', extra_cmd=[self.pseudo_path+'1', '--readonly']) | |
350 | # Export-3 for subvolume with r only | |
351 | self._cmd('fs', 'subvolume', 'create', self.fs_name, 'sub_vol') | |
352 | fs_path = self._cmd('fs', 'subvolume', 'getpath', self.fs_name, 'sub_vol').strip() | |
353 | self._create_export(export_id='3', extra_cmd=[self.pseudo_path+'2', '--readonly', fs_path]) | |
354 | # Export-4 for subvolume | |
355 | self._create_export(export_id='4', extra_cmd=[self.pseudo_path+'3', fs_path]) | |
356 | # Check if exports gets listed | |
357 | self._test_list_detailed(fs_path) | |
358 | self._test_delete_cluster() | |
359 | # Check if rados ganesha conf object is deleted | |
360 | self._check_export_obj_deleted(conf_obj=True) | |
361 | self._check_auth_ls() | |
362 | ||
363 | def test_exports_on_mgr_restart(self): | |
364 | ''' | |
365 | Test export availability on restarting mgr. | |
366 | ''' | |
367 | self._create_default_export() | |
368 | # unload and load module will restart the mgr | |
369 | self._unload_module("cephadm") | |
370 | self._load_module("cephadm") | |
371 | self._orch_cmd("set", "backend", "cephadm") | |
f67539c2 TL |
372 | # Check if ganesha daemon is running |
373 | self._check_nfs_cluster_status('running', 'Failed to redeploy NFS Ganesha cluster') | |
f6b5b4d7 TL |
374 | # Checks if created export is listed |
375 | self._test_list_export() | |
f91f0fd5 TL |
376 | port, ip = self._get_port_ip_info() |
377 | self._test_mnt(self.pseudo_path, port, ip) | |
f6b5b4d7 TL |
378 | self._delete_export() |
379 | self._test_delete_cluster() | |
380 | ||
381 | def test_export_create_with_non_existing_fsname(self): | |
382 | ''' | |
383 | Test creating export with non-existing filesystem. | |
384 | ''' | |
385 | try: | |
386 | fs_name = 'nfs-test' | |
387 | self._test_create_cluster() | |
388 | self._nfs_cmd('export', 'create', 'cephfs', fs_name, self.cluster_id, self.pseudo_path) | |
389 | self.fail(f"Export created with non-existing filesystem {fs_name}") | |
390 | except CommandFailedError as e: | |
391 | # Command should fail for test to pass | |
392 | if e.exitstatus != errno.ENOENT: | |
393 | raise | |
394 | finally: | |
395 | self._test_delete_cluster() | |
396 | ||
397 | def test_export_create_with_non_existing_clusterid(self): | |
398 | ''' | |
399 | Test creating cephfs export with non-existing nfs cluster. | |
400 | ''' | |
401 | try: | |
402 | cluster_id = 'invalidtest' | |
403 | self._nfs_cmd('export', 'create', 'cephfs', self.fs_name, cluster_id, self.pseudo_path) | |
404 | self.fail(f"Export created with non-existing cluster id {cluster_id}") | |
405 | except CommandFailedError as e: | |
406 | # Command should fail for test to pass | |
407 | if e.exitstatus != errno.ENOENT: | |
408 | raise | |
409 | ||
410 | def test_export_create_with_relative_pseudo_path_and_root_directory(self): | |
411 | ''' | |
412 | Test creating cephfs export with relative or '/' pseudo path. | |
413 | ''' | |
414 | def check_pseudo_path(pseudo_path): | |
415 | try: | |
416 | self._nfs_cmd('export', 'create', 'cephfs', self.fs_name, self.cluster_id, | |
417 | pseudo_path) | |
418 | self.fail(f"Export created for {pseudo_path}") | |
419 | except CommandFailedError as e: | |
420 | # Command should fail for test to pass | |
421 | if e.exitstatus != errno.EINVAL: | |
422 | raise | |
423 | ||
424 | self._test_create_cluster() | |
425 | self._cmd('fs', 'volume', 'create', self.fs_name) | |
426 | check_pseudo_path('invalidpath') | |
427 | check_pseudo_path('/') | |
428 | check_pseudo_path('//') | |
f91f0fd5 | 429 | self._cmd('fs', 'volume', 'rm', self.fs_name, '--yes-i-really-mean-it') |
f6b5b4d7 TL |
430 | self._test_delete_cluster() |
431 | ||
f67539c2 TL |
432 | def test_write_to_read_only_export(self): |
433 | ''' | |
434 | Test write to readonly export. | |
435 | ''' | |
436 | self._test_create_cluster() | |
437 | self._create_export(export_id='1', create_fs=True, extra_cmd=[self.pseudo_path, '--readonly']) | |
438 | port, ip = self._get_port_ip_info() | |
439 | self._write_to_read_only_export(self.pseudo_path, port, ip) | |
440 | self._test_delete_cluster() | |
441 | ||
f6b5b4d7 TL |
442 | def test_cluster_info(self): |
443 | ''' | |
444 | Test cluster info outputs correct ip and hostname | |
445 | ''' | |
446 | self._test_create_cluster() | |
447 | info_output = json.loads(self._nfs_cmd('cluster', 'info', self.cluster_id)) | |
b3b6e05e TL |
448 | print(f'info {info_output}') |
449 | info_ip = info_output[self.cluster_id].get('backend', [])[0].pop("ip") | |
450 | host_details = { | |
451 | self.cluster_id: { | |
452 | 'backend': [ | |
453 | { | |
454 | "hostname": self._sys_cmd(['hostname']).decode("utf-8").strip(), | |
455 | "port": 2049 | |
456 | } | |
457 | ], | |
458 | "virtual_ip": None, | |
459 | } | |
460 | } | |
adb31ebb | 461 | host_ip = self._sys_cmd(['hostname', '-I']).decode("utf-8").split() |
b3b6e05e | 462 | print(f'host_ip is {host_ip}, info_ip is {info_ip}') |
f6b5b4d7 | 463 | self.assertDictEqual(info_output, host_details) |
b3b6e05e | 464 | self.assertTrue(info_ip in host_ip) |
f6b5b4d7 TL |
465 | self._test_delete_cluster() |
466 | ||
467 | def test_cluster_set_reset_user_config(self): | |
468 | ''' | |
469 | Test cluster is created using user config and reverts back to default | |
470 | config on reset. | |
471 | ''' | |
472 | self._test_create_cluster() | |
f6b5b4d7 TL |
473 | |
474 | pool = 'nfs-ganesha' | |
475 | user_id = 'test' | |
476 | fs_name = 'user_test_fs' | |
f91f0fd5 | 477 | pseudo_path = '/ceph' |
f6b5b4d7 TL |
478 | self._cmd('fs', 'volume', 'create', fs_name) |
479 | time.sleep(20) | |
480 | key = self._cmd('auth', 'get-or-create-key', f'client.{user_id}', 'mon', | |
481 | 'allow r', 'osd', | |
482 | f'allow rw pool={pool} namespace={self.cluster_id}, allow rw tag cephfs data={fs_name}', | |
483 | 'mds', f'allow rw path={self.path}').strip() | |
484 | config = f""" LOG {{ | |
485 | Default_log_level = FULL_DEBUG; | |
486 | }} | |
487 | ||
488 | EXPORT {{ | |
489 | Export_Id = 100; | |
490 | Transports = TCP; | |
491 | Path = /; | |
f91f0fd5 | 492 | Pseudo = {pseudo_path}; |
f6b5b4d7 TL |
493 | Protocols = 4; |
494 | Access_Type = RW; | |
495 | Attr_Expiration_Time = 0; | |
496 | Squash = None; | |
497 | FSAL {{ | |
498 | Name = CEPH; | |
499 | Filesystem = {fs_name}; | |
500 | User_Id = {user_id}; | |
501 | Secret_Access_Key = '{key}'; | |
502 | }} | |
503 | }}""" | |
f91f0fd5 | 504 | port, ip = self._get_port_ip_info() |
f6b5b4d7 TL |
505 | self.ctx.cluster.run(args=['sudo', 'ceph', 'nfs', 'cluster', 'config', |
506 | 'set', self.cluster_id, '-i', '-'], stdin=config) | |
507 | time.sleep(30) | |
f6b5b4d7 | 508 | res = self._sys_cmd(['rados', '-p', pool, '-N', self.cluster_id, 'get', |
f67539c2 | 509 | f'userconf-nfs.{user_id}', '-']) |
f6b5b4d7 | 510 | self.assertEqual(config, res.decode('utf-8')) |
f91f0fd5 | 511 | self._test_mnt(pseudo_path, port, ip) |
f6b5b4d7 TL |
512 | self._nfs_cmd('cluster', 'config', 'reset', self.cluster_id) |
513 | rados_obj_ls = self._sys_cmd(['rados', '-p', 'nfs-ganesha', '-N', self.cluster_id, 'ls']) | |
514 | if b'conf-nfs' not in rados_obj_ls and b'userconf-nfs' in rados_obj_ls: | |
515 | self.fail("User config not deleted") | |
516 | time.sleep(30) | |
f91f0fd5 | 517 | self._test_mnt(pseudo_path, port, ip, False) |
f6b5b4d7 TL |
518 | self._cmd('fs', 'volume', 'rm', fs_name, '--yes-i-really-mean-it') |
519 | self._test_delete_cluster() | |
f6b5b4d7 TL |
520 | |
521 | def test_cluster_set_user_config_with_non_existing_clusterid(self): | |
522 | ''' | |
523 | Test setting user config for non-existing nfs cluster. | |
524 | ''' | |
525 | try: | |
526 | cluster_id = 'invalidtest' | |
527 | self.ctx.cluster.run(args=['sudo', 'ceph', 'nfs', 'cluster', | |
528 | 'config', 'set', self.cluster_id, '-i', '-'], stdin='testing') | |
529 | self.fail(f"User config set for non-existing cluster {cluster_id}") | |
530 | except CommandFailedError as e: | |
531 | # Command should fail for test to pass | |
532 | if e.exitstatus != errno.ENOENT: | |
533 | raise | |
534 | ||
535 | def test_cluster_reset_user_config_with_non_existing_clusterid(self): | |
536 | ''' | |
537 | Test resetting user config for non-existing nfs cluster. | |
538 | ''' | |
539 | try: | |
540 | cluster_id = 'invalidtest' | |
541 | self._nfs_cmd('cluster', 'config', 'reset', cluster_id) | |
542 | self.fail(f"User config reset for non-existing cluster {cluster_id}") | |
543 | except CommandFailedError as e: | |
544 | # Command should fail for test to pass | |
545 | if e.exitstatus != errno.ENOENT: | |
546 | raise | |
f67539c2 TL |
547 | |
548 | def test_update_export(self): | |
549 | ''' | |
550 | Test update of exports | |
551 | ''' | |
552 | self._create_default_export() | |
553 | port, ip = self._get_port_ip_info() | |
554 | self._test_mnt(self.pseudo_path, port, ip) | |
555 | export_block = self._get_export() | |
556 | new_pseudo_path = '/testing' | |
557 | export_block['pseudo'] = new_pseudo_path | |
558 | export_block['access_type'] = 'RO' | |
559 | self.ctx.cluster.run(args=['sudo', 'ceph', 'nfs', 'export', 'update', '-i', '-'], | |
560 | stdin=json.dumps(export_block)) | |
561 | self._check_nfs_cluster_status('running', 'NFS Ganesha cluster restart failed') | |
562 | self._write_to_read_only_export(new_pseudo_path, port, ip) | |
563 | self._test_delete_cluster() | |
564 | ||
565 | def test_update_export_with_invalid_values(self): | |
566 | ''' | |
567 | Test update of export with invalid values | |
568 | ''' | |
569 | self._create_default_export() | |
570 | export_block = self._get_export() | |
571 | ||
572 | def update_with_invalid_values(key, value, fsal=False): | |
573 | export_block_new = dict(export_block) | |
574 | if fsal: | |
575 | export_block_new['fsal'] = dict(export_block['fsal']) | |
576 | export_block_new['fsal'][key] = value | |
577 | else: | |
578 | export_block_new[key] = value | |
579 | try: | |
580 | self.ctx.cluster.run(args=['sudo', 'ceph', 'nfs', 'export', 'update', '-i', '-'], | |
581 | stdin=json.dumps(export_block_new)) | |
582 | except CommandFailedError: | |
583 | pass | |
584 | ||
585 | update_with_invalid_values('export_id', 9) | |
586 | update_with_invalid_values('cluster_id', 'testing_new') | |
587 | update_with_invalid_values('pseudo', 'test_relpath') | |
588 | update_with_invalid_values('access_type', 'W') | |
589 | update_with_invalid_values('squash', 'no_squash') | |
590 | update_with_invalid_values('security_label', 'invalid') | |
591 | update_with_invalid_values('protocols', [2]) | |
592 | update_with_invalid_values('transports', ['UD']) | |
593 | update_with_invalid_values('name', 'RGW', True) | |
594 | update_with_invalid_values('user_id', 'testing_export', True) | |
595 | update_with_invalid_values('fs_name', 'b', True) | |
596 | self._test_delete_cluster() | |
b3b6e05e TL |
597 | |
598 | def test_cmds_without_reqd_args(self): | |
599 | ''' | |
600 | Test that cmd fails on not passing required arguments | |
601 | ''' | |
602 | def exec_cmd_invalid(*cmd): | |
603 | try: | |
604 | self._nfs_cmd(*cmd) | |
605 | self.fail(f"nfs {cmd} command executed successfully without required arguments") | |
606 | except CommandFailedError as e: | |
607 | # Command should fail for test to pass | |
608 | if e.exitstatus != errno.EINVAL: | |
609 | raise | |
610 | ||
611 | exec_cmd_invalid('cluster', 'create') | |
612 | exec_cmd_invalid('cluster', 'delete') | |
613 | exec_cmd_invalid('cluster', 'config', 'set') | |
614 | exec_cmd_invalid('cluster', 'config', 'reset') | |
615 | exec_cmd_invalid('export', 'create', 'cephfs') | |
616 | exec_cmd_invalid('export', 'create', 'cephfs', 'a_fs') | |
617 | exec_cmd_invalid('export', 'create', 'cephfs', 'a_fs', 'clusterid') | |
618 | exec_cmd_invalid('export', 'ls') | |
619 | exec_cmd_invalid('export', 'delete') | |
620 | exec_cmd_invalid('export', 'delete', 'clusterid') | |
621 | exec_cmd_invalid('export', 'get') | |
622 | exec_cmd_invalid('export', 'get', 'clusterid') |