]>
Commit | Line | Data |
---|---|---|
11fdf7f2 | 1 | # -*- coding: utf-8 -*- |
f67539c2 | 2 | # pylint: disable=too-many-lines |
11fdf7f2 TL |
3 | from __future__ import absolute_import |
4 | ||
5 | import unittest | |
f91f0fd5 TL |
6 | from unittest.mock import MagicMock, Mock, patch |
7 | from urllib.parse import urlencode | |
11fdf7f2 | 8 | |
f91f0fd5 TL |
9 | from ceph.deployment.service_spec import NFSServiceSpec |
10 | from orchestrator import DaemonDescription, ServiceDescription | |
11fdf7f2 | 11 | |
11fdf7f2 | 12 | from .. import mgr |
f91f0fd5 | 13 | from ..controllers.nfsganesha import NFSGaneshaUi |
11fdf7f2 | 14 | from ..services import ganesha |
f91f0fd5 TL |
15 | from ..services.ganesha import ClusterType, Export, GaneshaConf, GaneshaConfParser, NFSException |
16 | from ..settings import Settings | |
17 | from . import ControllerTestCase # pylint: disable=no-name-in-module | |
18 | from . import KVStoreMockMixin # pylint: disable=no-name-in-module | |
11fdf7f2 TL |
19 | |
20 | ||
21 | class GaneshaConfTest(unittest.TestCase, KVStoreMockMixin): | |
f67539c2 TL |
22 | daemon_raw_config = """ |
23 | NFS_CORE_PARAM { | |
24 | Enable_NLM = false; | |
25 | Enable_RQUOTA = false; | |
26 | Protocols = 4; | |
27 | NFS_Port = 14000; | |
28 | } | |
29 | ||
30 | MDCACHE { | |
31 | Dir_Chunk = 0; | |
32 | } | |
33 | ||
34 | NFSv4 { | |
35 | RecoveryBackend = rados_cluster; | |
36 | Minor_Versions = 1, 2; | |
37 | } | |
38 | ||
39 | RADOS_KV { | |
40 | pool = nfs-ganesha; | |
41 | namespace = vstart; | |
42 | UserId = vstart; | |
43 | nodeid = a; | |
44 | } | |
45 | ||
46 | RADOS_URLS { | |
47 | Userid = vstart; | |
48 | watch_url = 'rados://nfs-ganesha/vstart/conf-nfs.vstart'; | |
49 | } | |
50 | ||
51 | %url rados://nfs-ganesha/vstart/conf-nfs.vstart | |
52 | """ | |
53 | ||
11fdf7f2 TL |
54 | export_1 = """ |
55 | EXPORT { | |
56 | Export_ID=1; | |
57 | Protocols = 4; | |
58 | Path = /; | |
59 | Pseudo = /cephfs_a/; | |
60 | Access_Type = RW; | |
61 | Protocols = 4; | |
62 | Attr_Expiration_Time = 0; | |
63 | # Delegations = R; | |
64 | # Squash = root; | |
65 | ||
66 | FSAL { | |
67 | Name = CEPH; | |
68 | Filesystem = "a"; | |
69 | User_Id = "ganesha"; | |
70 | # Secret_Access_Key = "YOUR SECRET KEY HERE"; | |
71 | } | |
72 | ||
73 | CLIENT | |
74 | { | |
75 | Clients = 192.168.0.10, 192.168.1.0/8; | |
76 | Squash = None; | |
77 | } | |
78 | ||
79 | CLIENT | |
80 | { | |
81 | Clients = 192.168.0.0/16; | |
82 | Squash = All; | |
83 | Access_Type = RO; | |
84 | } | |
85 | } | |
86 | """ | |
87 | ||
88 | export_2 = """ | |
89 | EXPORT | |
90 | { | |
91 | Export_ID=2; | |
92 | ||
93 | Path = "/"; | |
94 | ||
95 | Pseudo = "/rgw"; | |
96 | ||
97 | Access_Type = RW; | |
98 | ||
99 | squash = AllAnonymous; | |
100 | ||
101 | Protocols = 4, 3; | |
102 | ||
103 | Transports = TCP, UDP; | |
104 | ||
105 | FSAL { | |
106 | Name = RGW; | |
107 | User_Id = "testuser"; | |
108 | Access_Key_Id ="access_key"; | |
109 | Secret_Access_Key = "secret_key"; | |
110 | } | |
111 | } | |
112 | """ | |
113 | ||
114 | conf_nodea = ''' | |
f91f0fd5 | 115 | %url "rados://ganesha/ns/export-2" |
11fdf7f2 TL |
116 | |
117 | %url "rados://ganesha/ns/export-1"''' | |
118 | ||
119 | conf_nodeb = '%url "rados://ganesha/ns/export-1"' | |
120 | ||
f91f0fd5 TL |
121 | conf_nfs_foo = ''' |
122 | %url "rados://ganesha2/ns2/export-1" | |
123 | ||
124 | %url "rados://ganesha2/ns2/export-2"''' | |
125 | ||
11fdf7f2 TL |
126 | class RObject(object): |
127 | def __init__(self, key, raw): | |
128 | self.key = key | |
129 | self.raw = raw | |
130 | ||
131 | def read(self, _): | |
132 | return self.raw.encode('utf-8') | |
133 | ||
134 | def stat(self): | |
135 | return len(self.raw), None | |
136 | ||
137 | def _ioctx_write_full_mock(self, key, content): | |
f91f0fd5 TL |
138 | if key not in self.temp_store[self.temp_store_namespace]: |
139 | self.temp_store[self.temp_store_namespace][key] = \ | |
140 | GaneshaConfTest.RObject(key, content.decode('utf-8')) | |
11fdf7f2 | 141 | else: |
f91f0fd5 | 142 | self.temp_store[self.temp_store_namespace][key].raw = content.decode('utf-8') |
11fdf7f2 TL |
143 | |
144 | def _ioctx_remove_mock(self, key): | |
f91f0fd5 | 145 | del self.temp_store[self.temp_store_namespace][key] |
11fdf7f2 TL |
146 | |
147 | def _ioctx_list_objects_mock(self): | |
f91f0fd5 TL |
148 | return [obj for _, obj in self.temp_store[self.temp_store_namespace].items()] |
149 | ||
150 | def _ioctl_stat_mock(self, key): | |
151 | return self.temp_store[self.temp_store_namespace][key].stat() | |
152 | ||
153 | def _ioctl_read_mock(self, key, size): | |
154 | return self.temp_store[self.temp_store_namespace][key].read(size) | |
155 | ||
156 | def _ioctx_set_namespace_mock(self, namespace): | |
157 | self.temp_store_namespace = namespace | |
11fdf7f2 | 158 | |
f67539c2 TL |
159 | @staticmethod |
160 | def _set_user_defined_clusters_location(clusters_pool_namespace='ganesha/ns'): | |
161 | Settings.GANESHA_CLUSTERS_RADOS_POOL_NAMESPACE = clusters_pool_namespace | |
162 | ||
11fdf7f2 TL |
163 | def setUp(self): |
164 | self.mock_kv_store() | |
165 | ||
f91f0fd5 | 166 | self.clusters = { |
f91f0fd5 TL |
167 | 'foo': { |
168 | 'pool': 'ganesha2', | |
169 | 'namespace': 'ns2', | |
170 | 'type': ClusterType.ORCHESTRATOR, | |
171 | 'daemon_conf': 'conf-nfs.foo', | |
172 | 'daemons': ['foo.host_a', 'foo.host_b'], | |
173 | 'exports': { | |
174 | 1: ['foo.host_a', 'foo.host_b'], | |
175 | 2: ['foo.host_a', 'foo.host_b'], | |
176 | 3: ['foo.host_a', 'foo.host_b'] # for new-added export | |
177 | } | |
178 | } | |
11fdf7f2 TL |
179 | } |
180 | ||
f67539c2 TL |
181 | # Unset user-defined location. |
182 | self._set_user_defined_clusters_location('') | |
f91f0fd5 TL |
183 | |
184 | self.temp_store_namespace = None | |
185 | self._reset_temp_store() | |
186 | ||
11fdf7f2 | 187 | self.io_mock = MagicMock() |
f91f0fd5 TL |
188 | self.io_mock.set_namespace.side_effect = self._ioctx_set_namespace_mock |
189 | self.io_mock.read = self._ioctl_read_mock | |
190 | self.io_mock.stat = self._ioctl_stat_mock | |
11fdf7f2 TL |
191 | self.io_mock.list_objects.side_effect = self._ioctx_list_objects_mock |
192 | self.io_mock.write_full.side_effect = self._ioctx_write_full_mock | |
193 | self.io_mock.remove_object.side_effect = self._ioctx_remove_mock | |
194 | ||
195 | ioctx_mock = MagicMock() | |
196 | ioctx_mock.__enter__ = Mock(return_value=(self.io_mock)) | |
197 | ioctx_mock.__exit__ = Mock(return_value=None) | |
198 | ||
199 | mgr.rados = MagicMock() | |
200 | mgr.rados.open_ioctx.return_value = ioctx_mock | |
81eedcae | 201 | |
f91f0fd5 | 202 | self._mock_orchestrator(True) |
11fdf7f2 TL |
203 | |
204 | ganesha.CephX = MagicMock() | |
205 | ganesha.CephX.list_clients.return_value = ['ganesha'] | |
206 | ganesha.CephX.get_client_key.return_value = 'ganesha' | |
207 | ||
208 | ganesha.CephFS = MagicMock() | |
209 | ||
f91f0fd5 TL |
210 | def _reset_temp_store(self): |
211 | self.temp_store_namespace = None | |
212 | self.temp_store = { | |
213 | 'ns': { | |
214 | 'export-1': GaneshaConfTest.RObject("export-1", self.export_1), | |
215 | 'export-2': GaneshaConfTest.RObject("export-2", self.export_2), | |
216 | 'conf-nodea': GaneshaConfTest.RObject("conf-nodea", self.conf_nodea), | |
217 | 'conf-nodeb': GaneshaConfTest.RObject("conf-nodeb", self.conf_nodeb), | |
218 | }, | |
219 | 'ns2': { | |
220 | 'export-1': GaneshaConfTest.RObject("export-1", self.export_1), | |
221 | 'export-2': GaneshaConfTest.RObject("export-2", self.export_2), | |
222 | 'conf-nfs.foo': GaneshaConfTest.RObject("conf-nfs.foo", self.conf_nfs_foo) | |
223 | } | |
224 | } | |
225 | ||
226 | def _mock_orchestrator(self, enable): | |
227 | # mock nfs services | |
228 | cluster_info = self.clusters['foo'] | |
229 | orch_nfs_services = [ | |
230 | ServiceDescription(spec=NFSServiceSpec(service_id='foo', | |
231 | pool=cluster_info['pool'], | |
232 | namespace=cluster_info['namespace'])) | |
233 | ] if enable else [] | |
234 | # pylint: disable=protected-access | |
235 | ganesha.Ganesha._get_orch_nfs_services = Mock(return_value=orch_nfs_services) | |
236 | ||
237 | # mock nfs daemons | |
238 | def _get_nfs_instances(service_name=None): | |
239 | if not enable: | |
240 | return [] | |
241 | instances = { | |
242 | 'nfs.foo': [ | |
243 | DaemonDescription(daemon_id='foo.host_a', status=1), | |
244 | DaemonDescription(daemon_id='foo.host_b', status=1) | |
245 | ], | |
246 | 'nfs.bar': [ | |
247 | DaemonDescription(daemon_id='bar.host_c', status=1) | |
248 | ] | |
249 | } | |
250 | if service_name is not None: | |
251 | return instances[service_name] | |
252 | result = [] | |
253 | for _, daemons in instances.items(): | |
254 | result.extend(daemons) | |
255 | return result | |
256 | ganesha.GaneshaConfOrchestrator._get_orch_nfs_instances = Mock( | |
257 | side_effect=_get_nfs_instances) | |
258 | ||
f67539c2 TL |
259 | def test_parse_daemon_raw_config(self): |
260 | expected_daemon_config = [ | |
261 | { | |
262 | "block_name": "NFS_CORE_PARAM", | |
263 | "enable_nlm": False, | |
264 | "enable_rquota": False, | |
265 | "protocols": 4, | |
266 | "nfs_port": 14000 | |
267 | }, | |
268 | { | |
269 | "block_name": "MDCACHE", | |
270 | "dir_chunk": 0 | |
271 | }, | |
272 | { | |
273 | "block_name": "NFSV4", | |
274 | "recoverybackend": "rados_cluster", | |
275 | "minor_versions": [1, 2] | |
276 | }, | |
277 | { | |
278 | "block_name": "RADOS_KV", | |
279 | "pool": "nfs-ganesha", | |
280 | "namespace": "vstart", | |
281 | "userid": "vstart", | |
282 | "nodeid": "a" | |
283 | }, | |
284 | { | |
285 | "block_name": "RADOS_URLS", | |
286 | "userid": "vstart", | |
287 | "watch_url": "'rados://nfs-ganesha/vstart/conf-nfs.vstart'" | |
288 | }, | |
289 | { | |
290 | "block_name": "%url", | |
291 | "value": "rados://nfs-ganesha/vstart/conf-nfs.vstart" | |
292 | } | |
293 | ] | |
294 | daemon_config = GaneshaConfParser(self.daemon_raw_config).parse() | |
295 | self.assertEqual(daemon_config, expected_daemon_config) | |
296 | ||
11fdf7f2 TL |
297 | def test_export_parser_1(self): |
298 | blocks = GaneshaConfParser(self.export_1).parse() | |
299 | self.assertIsInstance(blocks, list) | |
300 | self.assertEqual(len(blocks), 1) | |
301 | export = Export.from_export_block(blocks[0], '_default_', | |
302 | GaneshaConf.ganesha_defaults({})) | |
303 | ||
304 | self.assertEqual(export.export_id, 1) | |
305 | self.assertEqual(export.path, "/") | |
306 | self.assertEqual(export.pseudo, "/cephfs_a") | |
307 | self.assertIsNone(export.tag) | |
308 | self.assertEqual(export.access_type, "RW") | |
309 | self.assertEqual(export.squash, "root_squash") | |
310 | self.assertEqual(export.protocols, {4}) | |
311 | self.assertEqual(export.transports, {"TCP", "UDP"}) | |
312 | self.assertEqual(export.fsal.name, "CEPH") | |
313 | self.assertEqual(export.fsal.user_id, "ganesha") | |
314 | self.assertEqual(export.fsal.fs_name, "a") | |
315 | self.assertEqual(export.fsal.sec_label_xattr, None) | |
316 | self.assertEqual(len(export.clients), 2) | |
317 | self.assertEqual(export.clients[0].addresses, | |
318 | ["192.168.0.10", "192.168.1.0/8"]) | |
319 | self.assertEqual(export.clients[0].squash, "no_root_squash") | |
320 | self.assertIsNone(export.clients[0].access_type) | |
321 | self.assertEqual(export.clients[1].addresses, ["192.168.0.0/16"]) | |
322 | self.assertEqual(export.clients[1].squash, "all_squash") | |
323 | self.assertEqual(export.clients[1].access_type, "RO") | |
324 | self.assertEqual(export.cluster_id, '_default_') | |
325 | self.assertEqual(export.attr_expiration_time, 0) | |
326 | self.assertEqual(export.security_label, False) | |
327 | ||
328 | def test_export_parser_2(self): | |
329 | blocks = GaneshaConfParser(self.export_2).parse() | |
330 | self.assertIsInstance(blocks, list) | |
331 | self.assertEqual(len(blocks), 1) | |
332 | export = Export.from_export_block(blocks[0], '_default_', | |
333 | GaneshaConf.ganesha_defaults({})) | |
334 | ||
335 | self.assertEqual(export.export_id, 2) | |
336 | self.assertEqual(export.path, "/") | |
337 | self.assertEqual(export.pseudo, "/rgw") | |
338 | self.assertIsNone(export.tag) | |
339 | self.assertEqual(export.access_type, "RW") | |
340 | self.assertEqual(export.squash, "all_squash") | |
341 | self.assertEqual(export.protocols, {4, 3}) | |
342 | self.assertEqual(export.transports, {"TCP", "UDP"}) | |
343 | self.assertEqual(export.fsal.name, "RGW") | |
344 | self.assertEqual(export.fsal.rgw_user_id, "testuser") | |
345 | self.assertEqual(export.fsal.access_key, "access_key") | |
346 | self.assertEqual(export.fsal.secret_key, "secret_key") | |
347 | self.assertEqual(len(export.clients), 0) | |
348 | self.assertEqual(export.cluster_id, '_default_') | |
349 | ||
350 | def test_daemon_conf_parser_a(self): | |
351 | blocks = GaneshaConfParser(self.conf_nodea).parse() | |
352 | self.assertIsInstance(blocks, list) | |
353 | self.assertEqual(len(blocks), 2) | |
354 | self.assertEqual(blocks[0]['block_name'], "%url") | |
355 | self.assertEqual(blocks[0]['value'], "rados://ganesha/ns/export-2") | |
356 | self.assertEqual(blocks[1]['block_name'], "%url") | |
357 | self.assertEqual(blocks[1]['value'], "rados://ganesha/ns/export-1") | |
358 | ||
359 | def test_daemon_conf_parser_b(self): | |
360 | blocks = GaneshaConfParser(self.conf_nodeb).parse() | |
361 | self.assertIsInstance(blocks, list) | |
362 | self.assertEqual(len(blocks), 1) | |
363 | self.assertEqual(blocks[0]['block_name'], "%url") | |
364 | self.assertEqual(blocks[0]['value'], "rados://ganesha/ns/export-1") | |
365 | ||
366 | def test_ganesha_conf(self): | |
f91f0fd5 TL |
367 | for cluster_id, info in self.clusters.items(): |
368 | self._do_test_ganesha_conf(cluster_id, info['exports']) | |
369 | self._reset_temp_store() | |
370 | ||
371 | def _do_test_ganesha_conf(self, cluster, expected_exports): | |
372 | ganesha_conf = GaneshaConf.instance(cluster) | |
11fdf7f2 TL |
373 | exports = ganesha_conf.exports |
374 | ||
375 | self.assertEqual(len(exports.items()), 2) | |
376 | self.assertIn(1, exports) | |
377 | self.assertIn(2, exports) | |
378 | ||
379 | # export_id = 1 asserts | |
380 | export = exports[1] | |
381 | self.assertEqual(export.export_id, 1) | |
382 | self.assertEqual(export.path, "/") | |
383 | self.assertEqual(export.pseudo, "/cephfs_a") | |
384 | self.assertIsNone(export.tag) | |
385 | self.assertEqual(export.access_type, "RW") | |
386 | self.assertEqual(export.squash, "root_squash") | |
387 | self.assertEqual(export.protocols, {4}) | |
388 | self.assertEqual(export.transports, {"TCP", "UDP"}) | |
389 | self.assertEqual(export.fsal.name, "CEPH") | |
390 | self.assertEqual(export.fsal.user_id, "ganesha") | |
391 | self.assertEqual(export.fsal.fs_name, "a") | |
392 | self.assertEqual(export.fsal.sec_label_xattr, None) | |
393 | self.assertEqual(len(export.clients), 2) | |
394 | self.assertEqual(export.clients[0].addresses, | |
395 | ["192.168.0.10", "192.168.1.0/8"]) | |
396 | self.assertEqual(export.clients[0].squash, "no_root_squash") | |
397 | self.assertIsNone(export.clients[0].access_type) | |
398 | self.assertEqual(export.clients[1].addresses, ["192.168.0.0/16"]) | |
399 | self.assertEqual(export.clients[1].squash, "all_squash") | |
400 | self.assertEqual(export.clients[1].access_type, "RO") | |
401 | self.assertEqual(export.attr_expiration_time, 0) | |
402 | self.assertEqual(export.security_label, False) | |
f91f0fd5 | 403 | self.assertSetEqual(export.daemons, set(expected_exports[1])) |
11fdf7f2 TL |
404 | |
405 | # export_id = 2 asserts | |
406 | export = exports[2] | |
407 | self.assertEqual(export.export_id, 2) | |
408 | self.assertEqual(export.path, "/") | |
409 | self.assertEqual(export.pseudo, "/rgw") | |
410 | self.assertIsNone(export.tag) | |
411 | self.assertEqual(export.access_type, "RW") | |
412 | self.assertEqual(export.squash, "all_squash") | |
413 | self.assertEqual(export.protocols, {4, 3}) | |
414 | self.assertEqual(export.transports, {"TCP", "UDP"}) | |
415 | self.assertEqual(export.fsal.name, "RGW") | |
416 | self.assertEqual(export.fsal.rgw_user_id, "testuser") | |
417 | self.assertEqual(export.fsal.access_key, "access_key") | |
418 | self.assertEqual(export.fsal.secret_key, "secret_key") | |
419 | self.assertEqual(len(export.clients), 0) | |
f91f0fd5 | 420 | self.assertSetEqual(export.daemons, set(expected_exports[2])) |
11fdf7f2 TL |
421 | |
422 | def test_config_dict(self): | |
f91f0fd5 TL |
423 | for cluster_id, info in self.clusters.items(): |
424 | self._do_test_config_dict(cluster_id, info['exports']) | |
425 | self._reset_temp_store() | |
426 | ||
427 | def _do_test_config_dict(self, cluster, expected_exports): | |
428 | conf = GaneshaConf.instance(cluster) | |
11fdf7f2 TL |
429 | export = conf.exports[1] |
430 | ex_dict = export.to_dict() | |
431 | self.assertDictEqual(ex_dict, { | |
f91f0fd5 | 432 | 'daemons': expected_exports[1], |
11fdf7f2 TL |
433 | 'export_id': 1, |
434 | 'path': '/', | |
435 | 'pseudo': '/cephfs_a', | |
f91f0fd5 | 436 | 'cluster_id': cluster, |
11fdf7f2 TL |
437 | 'tag': None, |
438 | 'access_type': 'RW', | |
439 | 'squash': 'root_squash', | |
440 | 'security_label': False, | |
441 | 'protocols': [4], | |
442 | 'transports': ['TCP', 'UDP'], | |
443 | 'clients': [{ | |
444 | 'addresses': ["192.168.0.10", "192.168.1.0/8"], | |
445 | 'access_type': None, | |
446 | 'squash': 'no_root_squash' | |
447 | }, { | |
448 | 'addresses': ["192.168.0.0/16"], | |
449 | 'access_type': 'RO', | |
450 | 'squash': 'all_squash' | |
451 | }], | |
452 | 'fsal': { | |
453 | 'name': 'CEPH', | |
454 | 'user_id': 'ganesha', | |
455 | 'fs_name': 'a', | |
456 | 'sec_label_xattr': None | |
457 | } | |
458 | }) | |
459 | ||
460 | export = conf.exports[2] | |
461 | ex_dict = export.to_dict() | |
462 | self.assertDictEqual(ex_dict, { | |
f91f0fd5 | 463 | 'daemons': expected_exports[2], |
11fdf7f2 TL |
464 | 'export_id': 2, |
465 | 'path': '/', | |
466 | 'pseudo': '/rgw', | |
f91f0fd5 | 467 | 'cluster_id': cluster, |
11fdf7f2 TL |
468 | 'tag': None, |
469 | 'access_type': 'RW', | |
470 | 'squash': 'all_squash', | |
471 | 'security_label': False, | |
472 | 'protocols': [3, 4], | |
473 | 'transports': ['TCP', 'UDP'], | |
474 | 'clients': [], | |
475 | 'fsal': { | |
476 | 'name': 'RGW', | |
477 | 'rgw_user_id': 'testuser' | |
478 | } | |
479 | }) | |
480 | ||
481 | def test_config_from_dict(self): | |
f91f0fd5 TL |
482 | for cluster_id, info in self.clusters.items(): |
483 | self._do_test_config_from_dict(cluster_id, info['exports']) | |
484 | self._reset_temp_store() | |
485 | ||
486 | def _do_test_config_from_dict(self, cluster_id, expected_exports): | |
11fdf7f2 | 487 | export = Export.from_dict(1, { |
f91f0fd5 | 488 | 'daemons': expected_exports[1], |
11fdf7f2 TL |
489 | 'export_id': 1, |
490 | 'path': '/', | |
f91f0fd5 | 491 | 'cluster_id': cluster_id, |
11fdf7f2 TL |
492 | 'pseudo': '/cephfs_a', |
493 | 'tag': None, | |
494 | 'access_type': 'RW', | |
495 | 'squash': 'root_squash', | |
496 | 'security_label': True, | |
497 | 'protocols': [4], | |
498 | 'transports': ['TCP', 'UDP'], | |
499 | 'clients': [{ | |
500 | 'addresses': ["192.168.0.10", "192.168.1.0/8"], | |
501 | 'access_type': None, | |
502 | 'squash': 'no_root_squash' | |
503 | }, { | |
504 | 'addresses': ["192.168.0.0/16"], | |
505 | 'access_type': 'RO', | |
506 | 'squash': 'all_squash' | |
507 | }], | |
508 | 'fsal': { | |
509 | 'name': 'CEPH', | |
510 | 'user_id': 'ganesha', | |
511 | 'fs_name': 'a', | |
512 | 'sec_label_xattr': 'security.selinux' | |
513 | } | |
514 | }) | |
515 | ||
516 | self.assertEqual(export.export_id, 1) | |
517 | self.assertEqual(export.path, "/") | |
518 | self.assertEqual(export.pseudo, "/cephfs_a") | |
519 | self.assertIsNone(export.tag) | |
520 | self.assertEqual(export.access_type, "RW") | |
521 | self.assertEqual(export.squash, "root_squash") | |
522 | self.assertEqual(export.protocols, {4}) | |
523 | self.assertEqual(export.transports, {"TCP", "UDP"}) | |
524 | self.assertEqual(export.fsal.name, "CEPH") | |
525 | self.assertEqual(export.fsal.user_id, "ganesha") | |
526 | self.assertEqual(export.fsal.fs_name, "a") | |
527 | self.assertEqual(export.fsal.sec_label_xattr, 'security.selinux') | |
528 | self.assertEqual(len(export.clients), 2) | |
529 | self.assertEqual(export.clients[0].addresses, | |
530 | ["192.168.0.10", "192.168.1.0/8"]) | |
531 | self.assertEqual(export.clients[0].squash, "no_root_squash") | |
532 | self.assertIsNone(export.clients[0].access_type) | |
533 | self.assertEqual(export.clients[1].addresses, ["192.168.0.0/16"]) | |
534 | self.assertEqual(export.clients[1].squash, "all_squash") | |
535 | self.assertEqual(export.clients[1].access_type, "RO") | |
f91f0fd5 TL |
536 | self.assertEqual(export.daemons, set(expected_exports[1])) |
537 | self.assertEqual(export.cluster_id, cluster_id) | |
11fdf7f2 TL |
538 | self.assertEqual(export.attr_expiration_time, 0) |
539 | self.assertEqual(export.security_label, True) | |
540 | ||
541 | export = Export.from_dict(2, { | |
f91f0fd5 | 542 | 'daemons': expected_exports[2], |
11fdf7f2 TL |
543 | 'export_id': 2, |
544 | 'path': '/', | |
545 | 'pseudo': '/rgw', | |
f91f0fd5 | 546 | 'cluster_id': cluster_id, |
11fdf7f2 TL |
547 | 'tag': None, |
548 | 'access_type': 'RW', | |
549 | 'squash': 'all_squash', | |
550 | 'security_label': False, | |
551 | 'protocols': [4, 3], | |
552 | 'transports': ['TCP', 'UDP'], | |
553 | 'clients': [], | |
554 | 'fsal': { | |
555 | 'name': 'RGW', | |
556 | 'rgw_user_id': 'testuser' | |
557 | } | |
558 | }) | |
559 | ||
560 | self.assertEqual(export.export_id, 2) | |
561 | self.assertEqual(export.path, "/") | |
562 | self.assertEqual(export.pseudo, "/rgw") | |
563 | self.assertIsNone(export.tag) | |
564 | self.assertEqual(export.access_type, "RW") | |
565 | self.assertEqual(export.squash, "all_squash") | |
566 | self.assertEqual(export.protocols, {4, 3}) | |
567 | self.assertEqual(export.transports, {"TCP", "UDP"}) | |
568 | self.assertEqual(export.fsal.name, "RGW") | |
569 | self.assertEqual(export.fsal.rgw_user_id, "testuser") | |
570 | self.assertIsNone(export.fsal.access_key) | |
571 | self.assertIsNone(export.fsal.secret_key) | |
572 | self.assertEqual(len(export.clients), 0) | |
f91f0fd5 TL |
573 | self.assertEqual(export.daemons, set(expected_exports[2])) |
574 | self.assertEqual(export.cluster_id, cluster_id) | |
11fdf7f2 TL |
575 | |
576 | def test_gen_raw_config(self): | |
f91f0fd5 TL |
577 | for cluster_id, info in self.clusters.items(): |
578 | self._do_test_gen_raw_config(cluster_id, info['exports']) | |
579 | self._reset_temp_store() | |
580 | ||
581 | def _do_test_gen_raw_config(self, cluster_id, expected_exports): | |
582 | conf = GaneshaConf.instance(cluster_id) | |
11fdf7f2 TL |
583 | # pylint: disable=W0212 |
584 | export = conf.exports[1] | |
585 | del conf.exports[1] | |
586 | conf._save_export(export) | |
f91f0fd5 | 587 | conf = GaneshaConf.instance(cluster_id) |
11fdf7f2 TL |
588 | exports = conf.exports |
589 | self.assertEqual(len(exports.items()), 2) | |
590 | self.assertIn(1, exports) | |
591 | self.assertIn(2, exports) | |
592 | ||
593 | # export_id = 1 asserts | |
594 | export = exports[1] | |
595 | self.assertEqual(export.export_id, 1) | |
596 | self.assertEqual(export.path, "/") | |
597 | self.assertEqual(export.pseudo, "/cephfs_a") | |
598 | self.assertIsNone(export.tag) | |
599 | self.assertEqual(export.access_type, "RW") | |
600 | self.assertEqual(export.squash, "root_squash") | |
601 | self.assertEqual(export.protocols, {4}) | |
602 | self.assertEqual(export.transports, {"TCP", "UDP"}) | |
603 | self.assertEqual(export.fsal.name, "CEPH") | |
604 | self.assertEqual(export.fsal.user_id, "ganesha") | |
605 | self.assertEqual(export.fsal.fs_name, "a") | |
606 | self.assertEqual(export.fsal.sec_label_xattr, None) | |
607 | self.assertEqual(len(export.clients), 2) | |
608 | self.assertEqual(export.clients[0].addresses, | |
609 | ["192.168.0.10", "192.168.1.0/8"]) | |
610 | self.assertEqual(export.clients[0].squash, "no_root_squash") | |
611 | self.assertIsNone(export.clients[0].access_type) | |
612 | self.assertEqual(export.clients[1].addresses, ["192.168.0.0/16"]) | |
613 | self.assertEqual(export.clients[1].squash, "all_squash") | |
614 | self.assertEqual(export.clients[1].access_type, "RO") | |
f91f0fd5 TL |
615 | self.assertEqual(export.daemons, set(expected_exports[1])) |
616 | self.assertEqual(export.cluster_id, cluster_id) | |
11fdf7f2 TL |
617 | self.assertEqual(export.attr_expiration_time, 0) |
618 | self.assertEqual(export.security_label, False) | |
619 | ||
620 | # export_id = 2 asserts | |
621 | export = exports[2] | |
622 | self.assertEqual(export.export_id, 2) | |
623 | self.assertEqual(export.path, "/") | |
624 | self.assertEqual(export.pseudo, "/rgw") | |
625 | self.assertIsNone(export.tag) | |
626 | self.assertEqual(export.access_type, "RW") | |
627 | self.assertEqual(export.squash, "all_squash") | |
628 | self.assertEqual(export.protocols, {4, 3}) | |
629 | self.assertEqual(export.transports, {"TCP", "UDP"}) | |
630 | self.assertEqual(export.fsal.name, "RGW") | |
631 | self.assertEqual(export.fsal.rgw_user_id, "testuser") | |
632 | self.assertEqual(export.fsal.access_key, "access_key") | |
633 | self.assertEqual(export.fsal.secret_key, "secret_key") | |
634 | self.assertEqual(len(export.clients), 0) | |
f91f0fd5 TL |
635 | self.assertEqual(export.daemons, set(expected_exports[2])) |
636 | self.assertEqual(export.cluster_id, cluster_id) | |
11fdf7f2 TL |
637 | |
638 | def test_update_export(self): | |
f91f0fd5 TL |
639 | for cluster_id, info in self.clusters.items(): |
640 | self._do_test_update_export(cluster_id, info['exports']) | |
641 | self._reset_temp_store() | |
642 | ||
643 | def _do_test_update_export(self, cluster_id, expected_exports): | |
11fdf7f2 TL |
644 | ganesha.RgwClient = MagicMock() |
645 | admin_inst_mock = MagicMock() | |
646 | admin_inst_mock.get_user_keys.return_value = { | |
647 | 'access_key': 'access_key', | |
648 | 'secret_key': 'secret_key' | |
649 | } | |
650 | ganesha.RgwClient.admin_instance.return_value = admin_inst_mock | |
651 | ||
f91f0fd5 | 652 | conf = GaneshaConf.instance(cluster_id) |
11fdf7f2 TL |
653 | conf.update_export({ |
654 | 'export_id': 2, | |
f91f0fd5 | 655 | 'daemons': expected_exports[2], |
11fdf7f2 TL |
656 | 'path': 'bucket', |
657 | 'pseudo': '/rgw/bucket', | |
f91f0fd5 | 658 | 'cluster_id': cluster_id, |
11fdf7f2 TL |
659 | 'tag': 'bucket_tag', |
660 | 'access_type': 'RW', | |
661 | 'squash': 'all_squash', | |
662 | 'security_label': False, | |
663 | 'protocols': [4, 3], | |
664 | 'transports': ['TCP', 'UDP'], | |
665 | 'clients': [{ | |
666 | 'addresses': ["192.168.0.0/16"], | |
667 | 'access_type': None, | |
668 | 'squash': None | |
669 | }], | |
670 | 'fsal': { | |
671 | 'name': 'RGW', | |
672 | 'rgw_user_id': 'testuser' | |
673 | } | |
674 | }) | |
675 | ||
f91f0fd5 | 676 | conf = GaneshaConf.instance(cluster_id) |
11fdf7f2 TL |
677 | export = conf.get_export(2) |
678 | self.assertEqual(export.export_id, 2) | |
679 | self.assertEqual(export.path, "bucket") | |
680 | self.assertEqual(export.pseudo, "/rgw/bucket") | |
681 | self.assertEqual(export.tag, "bucket_tag") | |
682 | self.assertEqual(export.access_type, "RW") | |
683 | self.assertEqual(export.squash, "all_squash") | |
684 | self.assertEqual(export.protocols, {4, 3}) | |
685 | self.assertEqual(export.transports, {"TCP", "UDP"}) | |
686 | self.assertEqual(export.fsal.name, "RGW") | |
687 | self.assertEqual(export.fsal.rgw_user_id, "testuser") | |
688 | self.assertEqual(export.fsal.access_key, "access_key") | |
689 | self.assertEqual(export.fsal.secret_key, "secret_key") | |
690 | self.assertEqual(len(export.clients), 1) | |
691 | self.assertEqual(export.clients[0].addresses, ["192.168.0.0/16"]) | |
692 | self.assertIsNone(export.clients[0].squash) | |
693 | self.assertIsNone(export.clients[0].access_type) | |
f91f0fd5 TL |
694 | self.assertEqual(export.daemons, set(expected_exports[2])) |
695 | self.assertEqual(export.cluster_id, cluster_id) | |
11fdf7f2 TL |
696 | |
697 | def test_remove_export(self): | |
f91f0fd5 TL |
698 | for cluster_id, info in self.clusters.items(): |
699 | self._do_test_remove_export(cluster_id, info['exports']) | |
700 | self._reset_temp_store() | |
701 | ||
702 | def _do_test_remove_export(self, cluster_id, expected_exports): | |
703 | conf = GaneshaConf.instance(cluster_id) | |
11fdf7f2 TL |
704 | conf.remove_export(1) |
705 | exports = conf.list_exports() | |
706 | self.assertEqual(len(exports), 1) | |
707 | self.assertEqual(2, exports[0].export_id) | |
708 | export = conf.get_export(2) | |
709 | self.assertEqual(export.export_id, 2) | |
710 | self.assertEqual(export.path, "/") | |
711 | self.assertEqual(export.pseudo, "/rgw") | |
712 | self.assertIsNone(export.tag) | |
713 | self.assertEqual(export.access_type, "RW") | |
714 | self.assertEqual(export.squash, "all_squash") | |
715 | self.assertEqual(export.protocols, {4, 3}) | |
716 | self.assertEqual(export.transports, {"TCP", "UDP"}) | |
717 | self.assertEqual(export.fsal.name, "RGW") | |
718 | self.assertEqual(export.fsal.rgw_user_id, "testuser") | |
719 | self.assertEqual(export.fsal.access_key, "access_key") | |
720 | self.assertEqual(export.fsal.secret_key, "secret_key") | |
721 | self.assertEqual(len(export.clients), 0) | |
f91f0fd5 TL |
722 | self.assertEqual(export.daemons, set(expected_exports[2])) |
723 | self.assertEqual(export.cluster_id, cluster_id) | |
11fdf7f2 TL |
724 | |
725 | def test_create_export_rgw(self): | |
f91f0fd5 TL |
726 | for cluster_id, info in self.clusters.items(): |
727 | self._do_test_create_export_rgw(cluster_id, info['exports']) | |
728 | self._reset_temp_store() | |
729 | ||
730 | def _do_test_create_export_rgw(self, cluster_id, expected_exports): | |
11fdf7f2 TL |
731 | ganesha.RgwClient = MagicMock() |
732 | admin_inst_mock = MagicMock() | |
733 | admin_inst_mock.get_user_keys.return_value = { | |
734 | 'access_key': 'access_key2', | |
735 | 'secret_key': 'secret_key2' | |
736 | } | |
737 | ganesha.RgwClient.admin_instance.return_value = admin_inst_mock | |
738 | ||
f91f0fd5 | 739 | conf = GaneshaConf.instance(cluster_id) |
11fdf7f2 | 740 | ex_id = conf.create_export({ |
f91f0fd5 | 741 | 'daemons': expected_exports[3], |
11fdf7f2 TL |
742 | 'path': 'bucket', |
743 | 'pseudo': '/rgw/bucket', | |
744 | 'tag': 'bucket_tag', | |
f91f0fd5 | 745 | 'cluster_id': cluster_id, |
11fdf7f2 TL |
746 | 'access_type': 'RW', |
747 | 'squash': 'all_squash', | |
748 | 'security_label': False, | |
749 | 'protocols': [4, 3], | |
750 | 'transports': ['TCP', 'UDP'], | |
751 | 'clients': [{ | |
752 | 'addresses': ["192.168.0.0/16"], | |
753 | 'access_type': None, | |
754 | 'squash': None | |
755 | }], | |
756 | 'fsal': { | |
757 | 'name': 'RGW', | |
758 | 'rgw_user_id': 'testuser' | |
759 | } | |
760 | }) | |
761 | ||
f91f0fd5 | 762 | conf = GaneshaConf.instance(cluster_id) |
11fdf7f2 TL |
763 | exports = conf.list_exports() |
764 | self.assertEqual(len(exports), 3) | |
765 | export = conf.get_export(ex_id) | |
766 | self.assertEqual(export.export_id, ex_id) | |
767 | self.assertEqual(export.path, "bucket") | |
768 | self.assertEqual(export.pseudo, "/rgw/bucket") | |
769 | self.assertEqual(export.tag, "bucket_tag") | |
770 | self.assertEqual(export.access_type, "RW") | |
771 | self.assertEqual(export.squash, "all_squash") | |
772 | self.assertEqual(export.protocols, {4, 3}) | |
773 | self.assertEqual(export.transports, {"TCP", "UDP"}) | |
774 | self.assertEqual(export.fsal.name, "RGW") | |
775 | self.assertEqual(export.fsal.rgw_user_id, "testuser") | |
776 | self.assertEqual(export.fsal.access_key, "access_key2") | |
777 | self.assertEqual(export.fsal.secret_key, "secret_key2") | |
778 | self.assertEqual(len(export.clients), 1) | |
779 | self.assertEqual(export.clients[0].addresses, ["192.168.0.0/16"]) | |
780 | self.assertIsNone(export.clients[0].squash) | |
781 | self.assertIsNone(export.clients[0].access_type) | |
f91f0fd5 TL |
782 | self.assertEqual(export.daemons, set(expected_exports[3])) |
783 | self.assertEqual(export.cluster_id, cluster_id) | |
11fdf7f2 TL |
784 | |
785 | def test_create_export_cephfs(self): | |
f91f0fd5 TL |
786 | for cluster_id, info in self.clusters.items(): |
787 | self._do_test_create_export_cephfs(cluster_id, info['exports']) | |
788 | self._reset_temp_store() | |
789 | ||
790 | def _do_test_create_export_cephfs(self, cluster_id, expected_exports): | |
11fdf7f2 TL |
791 | ganesha.CephX = MagicMock() |
792 | ganesha.CephX.list_clients.return_value = ["fs"] | |
793 | ganesha.CephX.get_client_key.return_value = "fs_key" | |
794 | ||
795 | ganesha.CephFS = MagicMock() | |
796 | ganesha.CephFS.dir_exists.return_value = True | |
797 | ||
f91f0fd5 | 798 | conf = GaneshaConf.instance(cluster_id) |
11fdf7f2 | 799 | ex_id = conf.create_export({ |
f91f0fd5 | 800 | 'daemons': expected_exports[3], |
11fdf7f2 TL |
801 | 'path': '/', |
802 | 'pseudo': '/cephfs2', | |
f91f0fd5 | 803 | 'cluster_id': cluster_id, |
11fdf7f2 TL |
804 | 'tag': None, |
805 | 'access_type': 'RW', | |
806 | 'squash': 'all_squash', | |
807 | 'security_label': True, | |
808 | 'protocols': [4], | |
809 | 'transports': ['TCP'], | |
810 | 'clients': [], | |
811 | 'fsal': { | |
812 | 'name': 'CEPH', | |
813 | 'user_id': 'fs', | |
814 | 'fs_name': None, | |
815 | 'sec_label_xattr': 'security.selinux' | |
816 | } | |
817 | }) | |
818 | ||
f91f0fd5 | 819 | conf = GaneshaConf.instance(cluster_id) |
11fdf7f2 TL |
820 | exports = conf.list_exports() |
821 | self.assertEqual(len(exports), 3) | |
822 | export = conf.get_export(ex_id) | |
823 | self.assertEqual(export.export_id, ex_id) | |
824 | self.assertEqual(export.path, "/") | |
825 | self.assertEqual(export.pseudo, "/cephfs2") | |
826 | self.assertIsNone(export.tag) | |
827 | self.assertEqual(export.access_type, "RW") | |
828 | self.assertEqual(export.squash, "all_squash") | |
829 | self.assertEqual(export.protocols, {4}) | |
830 | self.assertEqual(export.transports, {"TCP"}) | |
831 | self.assertEqual(export.fsal.name, "CEPH") | |
832 | self.assertEqual(export.fsal.user_id, "fs") | |
833 | self.assertEqual(export.fsal.cephx_key, "fs_key") | |
834 | self.assertEqual(export.fsal.sec_label_xattr, "security.selinux") | |
835 | self.assertIsNone(export.fsal.fs_name) | |
836 | self.assertEqual(len(export.clients), 0) | |
f91f0fd5 TL |
837 | self.assertEqual(export.daemons, set(expected_exports[3])) |
838 | self.assertEqual(export.cluster_id, cluster_id) | |
11fdf7f2 TL |
839 | self.assertEqual(export.attr_expiration_time, 0) |
840 | self.assertEqual(export.security_label, True) | |
f91f0fd5 TL |
841 | |
842 | def test_reload_daemons(self): | |
843 | # Fail to import call in Python 3.8, see https://bugs.python.org/issue35753 | |
844 | mock_call = unittest.mock.call | |
845 | ||
846 | # Orchestrator cluster: reload all daemon config objects. | |
847 | conf = GaneshaConf.instance('foo') | |
848 | calls = [mock_call(conf) for conf in conf.list_daemon_confs()] | |
849 | for daemons in [[], ['a', 'b']]: | |
850 | conf.reload_daemons(daemons) | |
851 | self.io_mock.notify.assert_has_calls(calls) | |
852 | self.io_mock.reset_mock() | |
853 | ||
854 | # User-defined cluster: reload daemons in the parameter | |
f67539c2 | 855 | self._set_user_defined_clusters_location() |
f91f0fd5 TL |
856 | conf = GaneshaConf.instance('_default_') |
857 | calls = [mock_call('conf-{}'.format(daemon)) for daemon in ['nodea', 'nodeb']] | |
858 | conf.reload_daemons(['nodea', 'nodeb']) | |
859 | self.io_mock.notify.assert_has_calls(calls) | |
860 | ||
861 | def test_list_daemons(self): | |
862 | for cluster_id, info in self.clusters.items(): | |
863 | instance = GaneshaConf.instance(cluster_id) | |
864 | daemons = instance.list_daemons() | |
865 | for daemon in daemons: | |
866 | self.assertEqual(daemon['cluster_id'], cluster_id) | |
867 | self.assertEqual(daemon['cluster_type'], info['type']) | |
868 | self.assertIn('daemon_id', daemon) | |
869 | self.assertIn('status', daemon) | |
870 | self.assertIn('status_desc', daemon) | |
871 | self.assertEqual([daemon['daemon_id'] for daemon in daemons], info['daemons']) | |
872 | ||
873 | def test_validate_orchestrator(self): | |
874 | cluster_id = 'foo' | |
875 | cluster_info = self.clusters[cluster_id] | |
876 | instance = GaneshaConf.instance(cluster_id) | |
877 | export = MagicMock() | |
878 | ||
879 | # export can be linked to none or all daemons | |
880 | export_daemons = [[], cluster_info['daemons']] | |
881 | for daemons in export_daemons: | |
882 | export.daemons = daemons | |
883 | instance.validate(export) | |
884 | ||
885 | # raise if linking to partial or non-exist daemons | |
886 | export_daemons = [cluster_info['daemons'][:1], 'xxx'] | |
887 | for daemons in export_daemons: | |
888 | with self.assertRaises(NFSException): | |
889 | export.daemons = daemons | |
890 | instance.validate(export) | |
891 | ||
892 | def test_validate_user(self): | |
f67539c2 | 893 | self._set_user_defined_clusters_location() |
f91f0fd5 | 894 | cluster_id = '_default_' |
f91f0fd5 TL |
895 | instance = GaneshaConf.instance(cluster_id) |
896 | export = MagicMock() | |
897 | ||
898 | # export can be linked to none, partial, or all daemons | |
f67539c2 TL |
899 | fake_daemons = ['nodea', 'nodeb'] |
900 | export_daemons = [[], fake_daemons[:1], fake_daemons] | |
f91f0fd5 TL |
901 | for daemons in export_daemons: |
902 | export.daemons = daemons | |
903 | instance.validate(export) | |
904 | ||
905 | # raise if linking to non-exist daemons | |
906 | export_daemons = ['xxx'] | |
907 | for daemons in export_daemons: | |
908 | with self.assertRaises(NFSException): | |
909 | export.daemons = daemons | |
910 | instance.validate(export) | |
911 | ||
912 | def _verify_locations(self, locations, cluster_ids): | |
913 | for cluster_id in cluster_ids: | |
914 | self.assertIn(cluster_id, locations) | |
915 | cluster = locations.pop(cluster_id) | |
f67539c2 | 916 | self.assertDictEqual(cluster, {key: cluster[key] for key in [ |
f91f0fd5 TL |
917 | 'pool', 'namespace', 'type', 'daemon_conf']}) |
918 | self.assertDictEqual(locations, {}) | |
919 | ||
920 | def test_get_cluster_locations(self): | |
921 | # pylint: disable=protected-access | |
f91f0fd5 | 922 | |
f67539c2 | 923 | # There is only a Orchestrator cluster. |
f91f0fd5 | 924 | self._mock_orchestrator(True) |
f91f0fd5 TL |
925 | locations = ganesha.Ganesha._get_clusters_locations() |
926 | self._verify_locations(locations, ['foo']) | |
927 | ||
928 | # No cluster. | |
929 | self._mock_orchestrator(False) | |
930 | with self.assertRaises(NFSException): | |
931 | ganesha.Ganesha._get_clusters_locations() | |
932 | ||
f67539c2 TL |
933 | # There is only a user-defined cluster. |
934 | self._set_user_defined_clusters_location() | |
935 | self._mock_orchestrator(False) | |
936 | locations = ganesha.Ganesha._get_clusters_locations() | |
937 | self._verify_locations(locations, ['_default_']) | |
938 | ||
939 | # There are both Orchestrator cluster and user-defined cluster. | |
940 | self._set_user_defined_clusters_location() | |
941 | self._mock_orchestrator(True) | |
942 | locations = ganesha.Ganesha._get_clusters_locations() | |
943 | self._verify_locations(locations, ['foo', '_default_']) | |
944 | ||
f91f0fd5 TL |
945 | def test_get_cluster_locations_conflict(self): |
946 | # pylint: disable=protected-access | |
f67539c2 TL |
947 | |
948 | # Pool/namespace collision. | |
949 | self._set_user_defined_clusters_location('ganesha2/ns2') | |
950 | with self.assertRaises(NFSException) as ctx: | |
951 | ganesha.Ganesha._get_clusters_locations() | |
952 | self.assertIn('already in use', str(ctx.exception)) | |
953 | ||
954 | # Cluster name collision with orch. cluster. | |
955 | self._set_user_defined_clusters_location('foo:ganesha/ns') | |
956 | with self.assertRaises(NFSException) as ctx: | |
957 | ganesha.Ganesha._get_clusters_locations() | |
958 | self.assertIn('Detected a conflicting NFS-Ganesha cluster', str(ctx.exception)) | |
959 | ||
960 | # Cluster name collision with user-defined cluster. | |
961 | self._set_user_defined_clusters_location('cluster1:ganesha/ns,cluster1:fake-pool/fake-ns') | |
962 | with self.assertRaises(NFSException) as ctx: | |
f91f0fd5 | 963 | ganesha.Ganesha._get_clusters_locations() |
f67539c2 | 964 | self.assertIn('Duplicate Ganesha cluster definition', str(ctx.exception)) |
f91f0fd5 TL |
965 | |
966 | ||
967 | class NFSGaneshaUiControllerTest(ControllerTestCase): | |
968 | @classmethod | |
969 | def setup_server(cls): | |
970 | # pylint: disable=protected-access | |
971 | NFSGaneshaUi._cp_config['tools.authenticate.on'] = False | |
972 | cls.setup_controllers([NFSGaneshaUi]) | |
973 | ||
974 | @classmethod | |
975 | def _create_ls_dir_url(cls, fs_name, query_params): | |
976 | api_url = '/ui-api/nfs-ganesha/lsdir/{}'.format(fs_name) | |
977 | if query_params is not None: | |
978 | return '{}?{}'.format(api_url, urlencode(query_params)) | |
979 | return api_url | |
980 | ||
981 | @patch('dashboard.controllers.nfsganesha.CephFS') | |
982 | def test_lsdir(self, cephfs_class): | |
983 | cephfs_class.return_value.ls_dir.return_value = [ | |
984 | {'path': '/foo'}, | |
985 | {'path': '/foo/bar'} | |
986 | ] | |
987 | mocked_ls_dir = cephfs_class.return_value.ls_dir | |
988 | ||
989 | reqs = [ | |
990 | { | |
991 | 'params': None, | |
992 | 'cephfs_ls_dir_args': ['/', 1], | |
993 | 'path0': '/', | |
994 | 'status': 200 | |
995 | }, | |
996 | { | |
997 | 'params': {'root_dir': '/', 'depth': '1'}, | |
998 | 'cephfs_ls_dir_args': ['/', 1], | |
999 | 'path0': '/', | |
1000 | 'status': 200 | |
1001 | }, | |
1002 | { | |
1003 | 'params': {'root_dir': '', 'depth': '1'}, | |
1004 | 'cephfs_ls_dir_args': ['/', 1], | |
1005 | 'path0': '/', | |
1006 | 'status': 200 | |
1007 | }, | |
1008 | { | |
1009 | 'params': {'root_dir': '/foo', 'depth': '3'}, | |
1010 | 'cephfs_ls_dir_args': ['/foo', 3], | |
1011 | 'path0': '/foo', | |
1012 | 'status': 200 | |
1013 | }, | |
1014 | { | |
1015 | 'params': {'root_dir': 'foo', 'depth': '6'}, | |
1016 | 'cephfs_ls_dir_args': ['/foo', 5], | |
1017 | 'path0': '/foo', | |
1018 | 'status': 200 | |
1019 | }, | |
1020 | { | |
1021 | 'params': {'root_dir': '/', 'depth': '-1'}, | |
1022 | 'status': 400 | |
1023 | }, | |
1024 | { | |
1025 | 'params': {'root_dir': '/', 'depth': 'abc'}, | |
1026 | 'status': 400 | |
1027 | } | |
1028 | ] | |
1029 | ||
1030 | for req in reqs: | |
1031 | self._get(self._create_ls_dir_url('a', req['params'])) | |
1032 | self.assertStatus(req['status']) | |
1033 | ||
1034 | # Returned paths should contain root_dir as first element | |
1035 | if req['status'] == 200: | |
1036 | paths = self.json_body()['paths'] | |
1037 | self.assertEqual(paths[0], req['path0']) | |
1038 | cephfs_class.assert_called_once_with('a') | |
1039 | ||
1040 | # Check the arguments passed to `CephFS.ls_dir`. | |
1041 | if req.get('cephfs_ls_dir_args'): | |
1042 | mocked_ls_dir.assert_called_once_with(*req['cephfs_ls_dir_args']) | |
1043 | else: | |
1044 | mocked_ls_dir.assert_not_called() | |
1045 | mocked_ls_dir.reset_mock() | |
1046 | cephfs_class.reset_mock() | |
1047 | ||
1048 | @patch('dashboard.controllers.nfsganesha.cephfs') | |
1049 | @patch('dashboard.controllers.nfsganesha.CephFS') | |
1050 | def test_lsdir_non_existed_dir(self, cephfs_class, cephfs): | |
1051 | cephfs.ObjectNotFound = Exception | |
1052 | cephfs.PermissionError = Exception | |
1053 | cephfs_class.return_value.ls_dir.side_effect = cephfs.ObjectNotFound() | |
1054 | self._get(self._create_ls_dir_url('a', {'root_dir': '/foo', 'depth': '3'})) | |
1055 | cephfs_class.assert_called_once_with('a') | |
1056 | cephfs_class.return_value.ls_dir.assert_called_once_with('/foo', 3) | |
1057 | self.assertStatus(200) | |
1058 | self.assertJsonBody({'paths': []}) |