]>
git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/nfs/tests/test_nfs.py
1fca67249e0d459710f521de349bd04db2be06bd
4 from typing
import Optional
, Tuple
, Iterator
, List
, Any
6 from contextlib
import contextmanager
7 from unittest
import mock
8 from unittest
.mock
import MagicMock
9 from mgr_module
import MgrModule
, NFS_POOL_NAME
11 from rados
import ObjectNotFound
13 from ceph
.deployment
.service_spec
import NFSServiceSpec
14 from nfs
import Module
15 from nfs
.export
import ExportMgr
, normalize_path
16 from nfs
.ganesha_conf
import GaneshaConfParser
, Export
, RawBlock
17 from nfs
.cluster
import NFSCluster
18 from orchestrator
import ServiceDescription
, DaemonDescription
, OrchResult
31 Attr_Expiration_Time = 0;
38 # Secret_Access_Key = "YOUR SECRET KEY HERE";
43 Clients = 192.168.0.10, 192.168.1.0/8;
49 Clients = 192.168.0.0/16;
63 squash = AllAnonymous;
65 Transports = TCP, UDP;
69 User_Id = "nfs.foo.bucket";
70 Access_Key_Id ="the_access_key";
71 Secret_Access_Key = "the_secret_key";
79 user_id = "nfs.foo.1";
81 secret_access_key = "AQCjU+hgjyReLBAAddJa0Dza/ZHqjX5+JiePMA==";
88 attr_expiration_time = 0;
89 security_label = true;
96 %url "rados://{NFS_POOL_NAME}/{cluster_id}/export-1"
98 %url "rados://{NFS_POOL_NAME}/{cluster_id}/export-2"'''
100 class RObject(object):
101 def __init__(self
, key
: str, raw
: str) -> None:
105 def read(self
, _
: Optional
[int]) -> bytes
:
106 return self
.raw
.encode('utf-8')
108 def stat(self
) -> Tuple
[int, None]:
109 return len(self
.raw
), None
111 def _ioctx_write_full_mock(self
, key
: str, content
: bytes
) -> None:
112 if key
not in self
.temp_store
[self
.temp_store_namespace
]:
113 self
.temp_store
[self
.temp_store_namespace
][key
] = \
114 TestNFS
.RObject(key
, content
.decode('utf-8'))
116 self
.temp_store
[self
.temp_store_namespace
][key
].raw
= content
.decode('utf-8')
118 def _ioctx_remove_mock(self
, key
: str) -> None:
119 del self
.temp_store
[self
.temp_store_namespace
][key
]
121 def _ioctx_list_objects_mock(self
) -> List
['TestNFS.RObject']:
122 r
= [obj
for _
, obj
in self
.temp_store
[self
.temp_store_namespace
].items()]
125 def _ioctl_stat_mock(self
, key
):
126 return self
.temp_store
[self
.temp_store_namespace
][key
].stat()
128 def _ioctl_read_mock(self
, key
: str, size
: Optional
[Any
] = None) -> bytes
:
129 if key
not in self
.temp_store
[self
.temp_store_namespace
]:
131 return self
.temp_store
[self
.temp_store_namespace
][key
].read(size
)
133 def _ioctx_set_namespace_mock(self
, namespace
: str) -> None:
134 self
.temp_store_namespace
= namespace
136 def _reset_temp_store(self
) -> None:
137 self
.temp_store_namespace
= None
140 'export-1': TestNFS
.RObject("export-1", self
.export_1
),
141 'export-2': TestNFS
.RObject("export-2", self
.export_2
),
142 'conf-nfs.foo': TestNFS
.RObject("conf-nfs.foo", self
.conf_nfs_foo
)
147 def _mock_orchestrator(self
, enable
: bool) -> Iterator
:
148 self
.io_mock
= MagicMock()
149 self
.io_mock
.set_namespace
.side_effect
= self
._ioctx
_set
_namespace
_mock
150 self
.io_mock
.read
= self
._ioctl
_read
_mock
151 self
.io_mock
.stat
= self
._ioctl
_stat
_mock
152 self
.io_mock
.list_objects
.side_effect
= self
._ioctx
_list
_objects
_mock
153 self
.io_mock
.write_full
.side_effect
= self
._ioctx
_write
_full
_mock
154 self
.io_mock
.remove_object
.side_effect
= self
._ioctx
_remove
_mock
157 orch_nfs_services
= [
158 ServiceDescription(spec
=NFSServiceSpec(service_id
=self
.cluster_id
))
162 DaemonDescription('nfs', 'foo.mydaemon', 'myhostname')
165 def mock_exec(cls
, args
):
166 if args
[1:3] == ['bucket', 'stats']:
168 "owner": "bucket_owner_user",
170 return 0, json
.dumps(bucket_info
), ''
173 "display_name": "foo",
181 "access_key": "the_access_key",
182 "secret_key": "the_secret_key"
187 "op_mask": "read, write, delete",
188 "default_placement": "",
189 "default_storage_class": "",
190 "placement_tags": [],
193 "check_on_raw": False,
200 "check_on_raw": False,
209 if args
[2] == 'list':
210 return 0, json
.dumps([u
]), ''
211 return 0, json
.dumps(u
), ''
213 def mock_describe_service(cls
, *args
, **kwargs
):
214 if kwargs
['service_type'] == 'nfs':
215 return OrchResult(orch_nfs_services
)
216 return OrchResult([])
218 def mock_list_daemons(cls
, *args
, **kwargs
):
219 if kwargs
['daemon_type'] == 'nfs':
220 return OrchResult(orch_nfs_daemons
)
221 return OrchResult([])
223 with mock
.patch('nfs.module.Module.describe_service', mock_describe_service
) as describe_service
, \
224 mock
.patch('nfs.module.Module.list_daemons', mock_list_daemons
) as list_daemons
, \
225 mock
.patch('nfs.module.Module.rados') as rados
, \
226 mock
.patch('nfs.export.available_clusters',
227 return_value
=[self
.cluster_id
]), \
228 mock
.patch('nfs.export.restart_nfs_service'), \
229 mock
.patch('nfs.cluster.restart_nfs_service'), \
230 mock
.patch
.object(MgrModule
, 'tool_exec', mock_exec
), \
231 mock
.patch('nfs.export.check_fs', return_value
=True), \
232 mock
.patch('nfs.ganesha_conf.check_fs', return_value
=True), \
233 mock
.patch('nfs.export.ExportMgr._create_user_key',
234 return_value
='thekeyforclientabc'):
236 rados
.open_ioctx
.return_value
.__enter
__.return_value
= self
.io_mock
237 rados
.open_ioctx
.return_value
.__exit
__ = mock
.Mock(return_value
=None)
239 self
._reset
_temp
_store
()
243 def test_parse_daemon_raw_config(self
) -> None:
244 expected_daemon_config
= [
245 RawBlock('NFS_CORE_PARAM', values
={
247 "enable_rquota": False,
251 RawBlock('MDCACHE', values
={
254 RawBlock('NFSV4', values
={
255 "recoverybackend": "rados_cluster",
256 "minor_versions": [1, 2]
258 RawBlock('RADOS_KV', values
={
259 "pool": NFS_POOL_NAME
,
260 "namespace": "vstart",
264 RawBlock('RADOS_URLS', values
={
266 "watch_url": f
"'rados://{NFS_POOL_NAME}/vstart/conf-nfs.vstart'"
268 RawBlock('%url', values
={
269 "value": f
"rados://{NFS_POOL_NAME}/vstart/conf-nfs.vstart"
272 daemon_raw_config
= """
275 Enable_RQUOTA = false;
285 RecoveryBackend = rados_cluster;
286 Minor_Versions = 1, 2;
298 watch_url = 'rados://{}/vstart/conf-nfs.vstart';
301 %url rados://{}/vstart/conf-nfs.vstart
302 """.replace('{}', NFS_POOL_NAME
)
303 daemon_config
= GaneshaConfParser(daemon_raw_config
).parse()
304 assert daemon_config
== expected_daemon_config
306 def _validate_export_1(self
, export
: Export
):
307 assert export
.export_id
== 1
308 assert export
.path
== "/"
309 assert export
.pseudo
== "/cephfs_a/"
310 assert export
.access_type
== "RW"
311 # assert export.squash == "root_squash" # probably correct value
312 assert export
.squash
== "no_root_squash"
313 assert export
.protocols
== [4]
314 # assert export.transports == {"TCP", "UDP"}
315 assert export
.fsal
.name
== "CEPH"
316 assert export
.fsal
.user_id
== "ganesha"
317 assert export
.fsal
.fs_name
== "a"
318 assert export
.fsal
.sec_label_xattr
== None
319 assert len(export
.clients
) == 2
320 assert export
.clients
[0].addresses
== \
321 ["192.168.0.10", "192.168.1.0/8"]
322 # assert export.clients[0].squash == "no_root_squash" # probably correct value
323 assert export
.clients
[0].squash
== "None"
324 assert export
.clients
[0].access_type
is None
325 assert export
.clients
[1].addresses
== ["192.168.0.0/16"]
326 # assert export.clients[1].squash == "all_squash" # probably correct value
327 assert export
.clients
[1].squash
== "All"
328 assert export
.clients
[1].access_type
== "RO"
329 assert export
.cluster_id
== 'foo'
330 assert export
.attr_expiration_time
== 0
331 # assert export.security_label == False # probably correct value
332 assert export
.security_label
== True
334 def test_export_parser_1(self
) -> None:
335 blocks
= GaneshaConfParser(self
.export_1
).parse()
336 assert isinstance(blocks
, list)
337 assert len(blocks
) == 1
338 export
= Export
.from_export_block(blocks
[0], self
.cluster_id
)
339 self
._validate
_export
_1(export
)
341 def _validate_export_2(self
, export
: Export
):
342 assert export
.export_id
== 2
343 assert export
.path
== "/"
344 assert export
.pseudo
== "/rgw"
345 assert export
.access_type
== "RW"
346 # assert export.squash == "all_squash" # probably correct value
347 assert export
.squash
== "AllAnonymous"
348 assert export
.protocols
== [4, 3]
349 assert set(export
.transports
) == {"TCP", "UDP"}
350 assert export
.fsal
.name
== "RGW"
351 assert export
.fsal
.user_id
== "nfs.foo.bucket"
352 assert export
.fsal
.access_key_id
== "the_access_key"
353 assert export
.fsal
.secret_access_key
== "the_secret_key"
354 assert len(export
.clients
) == 0
355 assert export
.cluster_id
== 'foo'
357 def test_export_parser_2(self
) -> None:
358 blocks
= GaneshaConfParser(self
.export_2
).parse()
359 assert isinstance(blocks
, list)
360 assert len(blocks
) == 1
361 export
= Export
.from_export_block(blocks
[0], self
.cluster_id
)
362 self
._validate
_export
_2(export
)
364 def test_daemon_conf_parser(self
) -> None:
365 blocks
= GaneshaConfParser(self
.conf_nfs_foo
).parse()
366 assert isinstance(blocks
, list)
367 assert len(blocks
) == 2
368 assert blocks
[0].block_name
== "%url"
369 assert blocks
[0].values
['value'] == f
"rados://{NFS_POOL_NAME}/{self.cluster_id}/export-1"
370 assert blocks
[1].block_name
== "%url"
371 assert blocks
[1].values
['value'] == f
"rados://{NFS_POOL_NAME}/{self.cluster_id}/export-2"
373 def _do_mock_test(self
, func
) -> None:
374 with self
._mock
_orchestrator
(True):
376 self
._reset
_temp
_store
()
378 def test_ganesha_conf(self
) -> None:
379 self
._do
_mock
_test
(self
._do
_test
_ganesha
_conf
)
381 def _do_test_ganesha_conf(self
) -> None:
382 nfs_mod
= Module('nfs', '', '')
383 ganesha_conf
= ExportMgr(nfs_mod
)
384 exports
= ganesha_conf
.exports
[self
.cluster_id
]
386 assert len(exports
) == 2
388 self
._validate
_export
_1([e
for e
in exports
if e
.export_id
== 1][0])
389 self
._validate
_export
_2([e
for e
in exports
if e
.export_id
== 2][0])
391 def test_config_dict(self
) -> None:
392 self
._do
_mock
_test
(self
._do
_test
_config
_dict
)
394 def _do_test_config_dict(self
) -> None:
395 nfs_mod
= Module('nfs', '', '')
396 conf
= ExportMgr(nfs_mod
)
397 export
= [e
for e
in conf
.exports
['foo'] if e
.export_id
== 1][0]
398 ex_dict
= export
.to_dict()
400 assert ex_dict
== {'access_type': 'RW',
401 'clients': [{'access_type': None,
402 'addresses': ['192.168.0.10', '192.168.1.0/8'],
404 {'access_type': 'RO',
405 'addresses': ['192.168.0.0/16'],
407 'cluster_id': self
.cluster_id
,
409 'fsal': {'fs_name': 'a', 'name': 'CEPH', 'user_id': 'ganesha'},
412 'pseudo': '/cephfs_a/',
413 'security_label': True,
414 'squash': 'no_root_squash',
417 export
= [e
for e
in conf
.exports
['foo'] if e
.export_id
== 2][0]
418 ex_dict
= export
.to_dict()
419 assert ex_dict
== {'access_type': 'RW',
421 'cluster_id': self
.cluster_id
,
423 'fsal': {'name': 'RGW',
424 'access_key_id': 'the_access_key',
425 'secret_access_key': 'the_secret_key',
426 'user_id': 'nfs.foo.bucket'},
430 'security_label': True,
431 'squash': 'AllAnonymous',
432 'transports': ['TCP', 'UDP']}
434 def test_config_from_dict(self
) -> None:
435 self
._do
_mock
_test
(self
._do
_test
_config
_from
_dict
)
437 def _do_test_config_from_dict(self
) -> None:
438 export
= Export
.from_dict(1, {
441 'cluster_id': self
.cluster_id
,
442 'pseudo': '/cephfs_a',
444 'squash': 'root_squash',
445 'security_label': True,
447 'transports': ['TCP', 'UDP'],
449 'addresses': ["192.168.0.10", "192.168.1.0/8"],
451 'squash': 'no_root_squash'
453 'addresses': ["192.168.0.0/16"],
455 'squash': 'all_squash'
459 'user_id': 'ganesha',
461 'sec_label_xattr': 'security.selinux'
465 assert export
.export_id
== 1
466 assert export
.path
== "/"
467 assert export
.pseudo
== "/cephfs_a"
468 assert export
.access_type
== "RW"
469 assert export
.squash
== "root_squash"
470 assert set(export
.protocols
) == {4}
471 assert set(export
.transports
) == {"TCP", "UDP"}
472 assert export
.fsal
.name
== "CEPH"
473 assert export
.fsal
.user_id
== "ganesha"
474 assert export
.fsal
.fs_name
== "a"
475 assert export
.fsal
.sec_label_xattr
== 'security.selinux'
476 assert len(export
.clients
) == 2
477 assert export
.clients
[0].addresses
== \
478 ["192.168.0.10", "192.168.1.0/8"]
479 assert export
.clients
[0].squash
== "no_root_squash"
480 assert export
.clients
[0].access_type
is None
481 assert export
.clients
[1].addresses
== ["192.168.0.0/16"]
482 assert export
.clients
[1].squash
== "all_squash"
483 assert export
.clients
[1].access_type
== "RO"
484 assert export
.cluster_id
== self
.cluster_id
485 assert export
.attr_expiration_time
== 0
486 assert export
.security_label
488 export
= Export
.from_dict(2, {
492 'cluster_id': self
.cluster_id
,
494 'squash': 'all_squash',
495 'security_label': False,
497 'transports': ['TCP', 'UDP'],
501 'user_id': 'rgw.foo.bucket',
502 'access_key_id': 'the_access_key',
503 'secret_access_key': 'the_secret_key'
507 assert export
.export_id
== 2
508 assert export
.path
== "bucket"
509 assert export
.pseudo
== "/rgw"
510 assert export
.access_type
== "RW"
511 assert export
.squash
== "all_squash"
512 assert set(export
.protocols
) == {4, 3}
513 assert set(export
.transports
) == {"TCP", "UDP"}
514 assert export
.fsal
.name
== "RGW"
515 assert export
.fsal
.user_id
== "rgw.foo.bucket"
516 assert export
.fsal
.access_key_id
== "the_access_key"
517 assert export
.fsal
.secret_access_key
== "the_secret_key"
518 assert len(export
.clients
) == 0
519 assert export
.cluster_id
== self
.cluster_id
521 @pytest.mark
.parametrize(
528 def test_export_from_to_export_block(self
, block
):
529 blocks
= GaneshaConfParser(block
).parse()
530 export
= Export
.from_export_block(blocks
[0], self
.cluster_id
)
531 newblock
= export
.to_export_block()
532 export2
= Export
.from_export_block(newblock
, self
.cluster_id
)
533 newblock2
= export2
.to_export_block()
534 assert newblock
== newblock2
536 @pytest.mark
.parametrize(
543 def test_export_from_to_dict(self
, block
):
544 blocks
= GaneshaConfParser(block
).parse()
545 export
= Export
.from_export_block(blocks
[0], self
.cluster_id
)
547 export2
= Export
.from_dict(j
['export_id'], j
)
548 j2
= export2
.to_dict()
551 @pytest.mark
.parametrize(
558 def test_export_validate(self
, block
):
559 blocks
= GaneshaConfParser(block
).parse()
560 export
= Export
.from_export_block(blocks
[0], self
.cluster_id
)
561 nfs_mod
= Module('nfs', '', '')
562 with mock
.patch('nfs.ganesha_conf.check_fs', return_value
=True):
563 export
.validate(nfs_mod
)
565 def test_update_export(self
):
566 self
._do
_mock
_test
(self
._do
_test
_update
_export
)
568 def _do_test_update_export(self
):
569 nfs_mod
= Module('nfs', '', '')
570 conf
= ExportMgr(nfs_mod
)
571 r
= conf
.apply_export(self
.cluster_id
, json
.dumps({
574 'pseudo': '/rgw/bucket',
575 'cluster_id': self
.cluster_id
,
577 'squash': 'all_squash',
578 'security_label': False,
580 'transports': ['TCP', 'UDP'],
582 'addresses': ["192.168.0.0/16"],
588 'user_id': 'nfs.foo.bucket',
589 'access_key_id': 'the_access_key',
590 'secret_access_key': 'the_secret_key',
595 export
= conf
._fetch
_export
('foo', '/rgw/bucket')
596 assert export
.export_id
== 2
597 assert export
.path
== "bucket"
598 assert export
.pseudo
== "/rgw/bucket"
599 assert export
.access_type
== "RW"
600 assert export
.squash
== "all_squash"
601 assert export
.protocols
== [4, 3]
602 assert export
.transports
== ["TCP", "UDP"]
603 assert export
.fsal
.name
== "RGW"
604 assert export
.fsal
.access_key_id
== "the_access_key"
605 assert export
.fsal
.secret_access_key
== "the_secret_key"
606 assert len(export
.clients
) == 1
607 assert export
.clients
[0].squash
is None
608 assert export
.clients
[0].access_type
is None
609 assert export
.cluster_id
== self
.cluster_id
611 # do it again, with changes
612 r
= conf
.apply_export(self
.cluster_id
, json
.dumps({
615 'pseudo': '/rgw/bucket',
616 'cluster_id': self
.cluster_id
,
619 'security_label': False,
621 'transports': ['TCP'],
623 'addresses': ["192.168.10.0/16"],
629 'user_id': 'nfs.foo.newbucket',
630 'access_key_id': 'the_access_key',
631 'secret_access_key': 'the_secret_key',
636 export
= conf
._fetch
_export
('foo', '/rgw/bucket')
637 assert export
.export_id
== 2
638 assert export
.path
== "newbucket"
639 assert export
.pseudo
== "/rgw/bucket"
640 assert export
.access_type
== "RO"
641 assert export
.squash
== "root"
642 assert export
.protocols
== [4]
643 assert export
.transports
== ["TCP"]
644 assert export
.fsal
.name
== "RGW"
645 assert export
.fsal
.access_key_id
== "the_access_key"
646 assert export
.fsal
.secret_access_key
== "the_secret_key"
647 assert len(export
.clients
) == 1
648 assert export
.clients
[0].squash
is None
649 assert export
.clients
[0].access_type
is None
650 assert export
.cluster_id
== self
.cluster_id
652 # again, but without export_id
653 r
= conf
.apply_export(self
.cluster_id
, json
.dumps({
654 'path': 'newestbucket',
655 'pseudo': '/rgw/bucket',
656 'cluster_id': self
.cluster_id
,
659 'security_label': False,
661 'transports': ['TCP'],
663 'addresses': ["192.168.10.0/16"],
669 'user_id': 'nfs.foo.newestbucket',
670 'access_key_id': 'the_access_key',
671 'secret_access_key': 'the_secret_key',
676 export
= conf
._fetch
_export
(self
.cluster_id
, '/rgw/bucket')
677 assert export
.export_id
== 2
678 assert export
.path
== "newestbucket"
679 assert export
.pseudo
== "/rgw/bucket"
680 assert export
.access_type
== "RW"
681 assert export
.squash
== "root"
682 assert export
.protocols
== [4]
683 assert export
.transports
== ["TCP"]
684 assert export
.fsal
.name
== "RGW"
685 assert export
.fsal
.access_key_id
== "the_access_key"
686 assert export
.fsal
.secret_access_key
== "the_secret_key"
687 assert len(export
.clients
) == 1
688 assert export
.clients
[0].squash
is None
689 assert export
.clients
[0].access_type
is None
690 assert export
.cluster_id
== self
.cluster_id
692 def test_update_export_with_ganesha_conf(self
):
693 self
._do
_mock
_test
(self
._do
_test
_update
_export
_with
_ganesha
_conf
)
695 def _do_test_update_export_with_ganesha_conf(self
):
696 nfs_mod
= Module('nfs', '', '')
697 conf
= ExportMgr(nfs_mod
)
698 r
= conf
.apply_export(self
.cluster_id
, self
.export_3
)
701 def test_update_export_with_list(self
):
702 self
._do
_mock
_test
(self
._do
_test
_update
_export
_with
_list
)
704 def _do_test_update_export_with_list(self
):
705 nfs_mod
= Module('nfs', '', '')
706 conf
= ExportMgr(nfs_mod
)
707 r
= conf
.apply_export(self
.cluster_id
, json
.dumps([
710 'pseudo': '/rgw/bucket',
711 'cluster_id': self
.cluster_id
,
714 'security_label': False,
716 'transports': ['TCP'],
718 'addresses': ["192.168.0.0/16"],
724 'user_id': 'nfs.foo.bucket',
725 'access_key_id': 'the_access_key',
726 'secret_access_key': 'the_secret_key',
731 'pseudo': '/rgw/bucket2',
732 'cluster_id': self
.cluster_id
,
735 'security_label': False,
737 'transports': ['TCP'],
739 'addresses': ["192.168.0.0/16"],
745 'user_id': 'nfs.foo.bucket2',
746 'access_key_id': 'the_access_key',
747 'secret_access_key': 'the_secret_key',
753 export
= conf
._fetch
_export
('foo', '/rgw/bucket')
754 assert export
.export_id
== 3
755 assert export
.path
== "bucket"
756 assert export
.pseudo
== "/rgw/bucket"
757 assert export
.access_type
== "RW"
758 assert export
.squash
== "root"
759 assert export
.protocols
== [4]
760 assert export
.transports
== ["TCP"]
761 assert export
.fsal
.name
== "RGW"
762 assert export
.fsal
.access_key_id
== "the_access_key"
763 assert export
.fsal
.secret_access_key
== "the_secret_key"
764 assert len(export
.clients
) == 1
765 assert export
.clients
[0].squash
is None
766 assert export
.clients
[0].access_type
is None
767 assert export
.cluster_id
== self
.cluster_id
769 export
= conf
._fetch
_export
('foo', '/rgw/bucket2')
770 assert export
.export_id
== 4
771 assert export
.path
== "bucket2"
772 assert export
.pseudo
== "/rgw/bucket2"
773 assert export
.access_type
== "RO"
774 assert export
.squash
== "root"
775 assert export
.protocols
== [4]
776 assert export
.transports
== ["TCP"]
777 assert export
.fsal
.name
== "RGW"
778 assert export
.fsal
.access_key_id
== "the_access_key"
779 assert export
.fsal
.secret_access_key
== "the_secret_key"
780 assert len(export
.clients
) == 1
781 assert export
.clients
[0].squash
is None
782 assert export
.clients
[0].access_type
is None
783 assert export
.cluster_id
== self
.cluster_id
785 def test_remove_export(self
) -> None:
786 self
._do
_mock
_test
(self
._do
_test
_remove
_export
)
788 def _do_test_remove_export(self
) -> None:
789 nfs_mod
= Module('nfs', '', '')
790 conf
= ExportMgr(nfs_mod
)
791 assert len(conf
.exports
[self
.cluster_id
]) == 2
792 assert conf
.delete_export(cluster_id
=self
.cluster_id
,
793 pseudo_path
="/rgw") == (0, "Successfully deleted export", "")
794 exports
= conf
.exports
[self
.cluster_id
]
795 assert len(exports
) == 1
796 assert exports
[0].export_id
== 1
798 def test_create_export_rgw_bucket(self
):
799 self
._do
_mock
_test
(self
._do
_test
_create
_export
_rgw
_bucket
)
801 def _do_test_create_export_rgw_bucket(self
):
802 nfs_mod
= Module('nfs', '', '')
803 conf
= ExportMgr(nfs_mod
)
805 exports
= conf
.list_exports(cluster_id
=self
.cluster_id
)
806 ls
= json
.loads(exports
[1])
809 r
= conf
.create_export(
811 cluster_id
=self
.cluster_id
,
813 pseudo_path
='/mybucket',
816 addr
=["192.168.0.0/16"]
820 exports
= conf
.list_exports(cluster_id
=self
.cluster_id
)
821 ls
= json
.loads(exports
[1])
824 export
= conf
._fetch
_export
('foo', '/mybucket')
825 assert export
.export_id
826 assert export
.path
== "bucket"
827 assert export
.pseudo
== "/mybucket"
828 assert export
.access_type
== "none"
829 assert export
.squash
== "none"
830 assert export
.protocols
== [4]
831 assert export
.transports
== ["TCP"]
832 assert export
.fsal
.name
== "RGW"
833 assert export
.fsal
.user_id
== "bucket_owner_user"
834 assert export
.fsal
.access_key_id
== "the_access_key"
835 assert export
.fsal
.secret_access_key
== "the_secret_key"
836 assert len(export
.clients
) == 1
837 assert export
.clients
[0].squash
== 'root'
838 assert export
.clients
[0].access_type
== 'rw'
839 assert export
.clients
[0].addresses
== ["192.168.0.0/16"]
840 assert export
.cluster_id
== self
.cluster_id
842 def test_create_export_rgw_bucket_user(self
):
843 self
._do
_mock
_test
(self
._do
_test
_create
_export
_rgw
_bucket
_user
)
845 def _do_test_create_export_rgw_bucket_user(self
):
846 nfs_mod
= Module('nfs', '', '')
847 conf
= ExportMgr(nfs_mod
)
849 exports
= conf
.list_exports(cluster_id
=self
.cluster_id
)
850 ls
= json
.loads(exports
[1])
853 r
= conf
.create_export(
855 cluster_id
=self
.cluster_id
,
857 user_id
='other_user',
858 pseudo_path
='/mybucket',
861 addr
=["192.168.0.0/16"]
865 exports
= conf
.list_exports(cluster_id
=self
.cluster_id
)
866 ls
= json
.loads(exports
[1])
869 export
= conf
._fetch
_export
('foo', '/mybucket')
870 assert export
.export_id
871 assert export
.path
== "bucket"
872 assert export
.pseudo
== "/mybucket"
873 assert export
.access_type
== "none"
874 assert export
.squash
== "none"
875 assert export
.protocols
== [4]
876 assert export
.transports
== ["TCP"]
877 assert export
.fsal
.name
== "RGW"
878 assert export
.fsal
.access_key_id
== "the_access_key"
879 assert export
.fsal
.secret_access_key
== "the_secret_key"
880 assert len(export
.clients
) == 1
881 assert export
.clients
[0].squash
== 'root'
882 assert export
.fsal
.user_id
== "other_user"
883 assert export
.clients
[0].access_type
== 'rw'
884 assert export
.clients
[0].addresses
== ["192.168.0.0/16"]
885 assert export
.cluster_id
== self
.cluster_id
887 def test_create_export_rgw_user(self
):
888 self
._do
_mock
_test
(self
._do
_test
_create
_export
_rgw
_user
)
890 def _do_test_create_export_rgw_user(self
):
891 nfs_mod
= Module('nfs', '', '')
892 conf
= ExportMgr(nfs_mod
)
894 exports
= conf
.list_exports(cluster_id
=self
.cluster_id
)
895 ls
= json
.loads(exports
[1])
898 r
= conf
.create_export(
900 cluster_id
=self
.cluster_id
,
902 pseudo_path
='/mybucket',
905 addr
=["192.168.0.0/16"]
909 exports
= conf
.list_exports(cluster_id
=self
.cluster_id
)
910 ls
= json
.loads(exports
[1])
913 export
= conf
._fetch
_export
('foo', '/mybucket')
914 assert export
.export_id
915 assert export
.path
== "/"
916 assert export
.pseudo
== "/mybucket"
917 assert export
.access_type
== "none"
918 assert export
.squash
== "none"
919 assert export
.protocols
== [4]
920 assert export
.transports
== ["TCP"]
921 assert export
.fsal
.name
== "RGW"
922 assert export
.fsal
.access_key_id
== "the_access_key"
923 assert export
.fsal
.secret_access_key
== "the_secret_key"
924 assert len(export
.clients
) == 1
925 assert export
.clients
[0].squash
== 'root'
926 assert export
.fsal
.user_id
== "some_user"
927 assert export
.clients
[0].access_type
== 'rw'
928 assert export
.clients
[0].addresses
== ["192.168.0.0/16"]
929 assert export
.cluster_id
== self
.cluster_id
931 def test_create_export_cephfs(self
):
932 self
._do
_mock
_test
(self
._do
_test
_create
_export
_cephfs
)
934 def _do_test_create_export_cephfs(self
):
935 nfs_mod
= Module('nfs', '', '')
936 conf
= ExportMgr(nfs_mod
)
938 exports
= conf
.list_exports(cluster_id
=self
.cluster_id
)
939 ls
= json
.loads(exports
[1])
942 r
= conf
.create_export(
944 cluster_id
=self
.cluster_id
,
947 pseudo_path
='/cephfs2',
950 addr
=["192.168.1.0/8"],
954 exports
= conf
.list_exports(cluster_id
=self
.cluster_id
)
955 ls
= json
.loads(exports
[1])
958 export
= conf
._fetch
_export
('foo', '/cephfs2')
959 assert export
.export_id
960 assert export
.path
== "/"
961 assert export
.pseudo
== "/cephfs2"
962 assert export
.access_type
== "none"
963 assert export
.squash
== "none"
964 assert export
.protocols
== [4]
965 assert export
.transports
== ["TCP"]
966 assert export
.fsal
.name
== "CEPH"
967 assert export
.fsal
.user_id
== "nfs.foo.3"
968 assert export
.fsal
.cephx_key
== "thekeyforclientabc"
969 assert len(export
.clients
) == 1
970 assert export
.clients
[0].squash
== 'root'
971 assert export
.clients
[0].access_type
== 'rw'
972 assert export
.clients
[0].addresses
== ["192.168.1.0/8"]
973 assert export
.cluster_id
== self
.cluster_id
975 def _do_test_cluster_ls(self
):
976 nfs_mod
= Module('nfs', '', '')
977 cluster
= NFSCluster(nfs_mod
)
979 rc
, out
, err
= cluster
.list_nfs_cluster()
981 assert out
== self
.cluster_id
983 def test_cluster_ls(self
):
984 self
._do
_mock
_test
(self
._do
_test
_cluster
_ls
)
986 def _do_test_cluster_info(self
):
987 nfs_mod
= Module('nfs', '', '')
988 cluster
= NFSCluster(nfs_mod
)
990 rc
, out
, err
= cluster
.show_nfs_cluster_info(self
.cluster_id
)
992 assert json
.loads(out
) == {"foo": {"virtual_ip": None, "backend": []}}
994 def test_cluster_info(self
):
995 self
._do
_mock
_test
(self
._do
_test
_cluster
_info
)
997 def _do_test_cluster_config(self
):
998 nfs_mod
= Module('nfs', '', '')
999 cluster
= NFSCluster(nfs_mod
)
1001 rc
, out
, err
= cluster
.get_nfs_cluster_config(self
.cluster_id
)
1005 rc
, out
, err
= cluster
.set_nfs_cluster_config(self
.cluster_id
, '# foo\n')
1008 rc
, out
, err
= cluster
.get_nfs_cluster_config(self
.cluster_id
)
1010 assert out
== "# foo\n"
1012 rc
, out
, err
= cluster
.reset_nfs_cluster_config(self
.cluster_id
)
1015 rc
, out
, err
= cluster
.get_nfs_cluster_config(self
.cluster_id
)
1019 def test_cluster_config(self
):
1020 self
._do
_mock
_test
(self
._do
_test
_cluster
_config
)
1023 @pytest.mark
.parametrize(
1026 ("/foo/bar/baz", "/foo/bar/baz"),
1027 ("/foo/bar/baz/", "/foo/bar/baz"),
1028 ("/foo/bar/baz ", "/foo/bar/baz"),
1029 ("/foo/./bar/baz", "/foo/bar/baz"),
1030 ("/foo/bar/baz/..", "/foo/bar"),
1031 ("//foo/bar/baz", "/foo/bar/baz"),
1035 def test_normalize_path(path
, expected
):
1036 assert normalize_path(path
) == expected
1039 def test_ganesha_validate_squash():
1040 """Check error handling of internal validation function for squash value."""
1041 from nfs
.ganesha_conf
import _validate_squash
1042 from nfs
.exception
import NFSInvalidOperation
1044 _validate_squash("root")
1045 with pytest
.raises(NFSInvalidOperation
):
1046 _validate_squash("toot")
1049 def test_ganesha_validate_access_type():
1050 """Check error handling of internal validation function for access type value."""
1051 from nfs
.ganesha_conf
import _validate_access_type
1052 from nfs
.exception
import NFSInvalidOperation
1054 for ok
in ("rw", "ro", "none"):
1055 _validate_access_type(ok
)
1056 with pytest
.raises(NFSInvalidOperation
):
1057 _validate_access_type("any")