@@ -2740,3 +2740,184 @@ def test_when_healthy_with_confirm(self):
27402740 '''
27412741 self .fs .set_max_mds (2 , confirm = True )
27422742 self .assertEqual (self .fs .get_var ('max_mds' ), 2 )
2743+
2744+
2745+ class TestToggleVolumes (CephFSTestCase ):
2746+ '''
2747+ Contains code for enabling/disabling mgr/volumes plugin.
2748+ '''
2749+
2750+ VOL_MOD_NAME = 'volumes'
2751+ CONFIRM = '--yes-i-really-mean-it'
2752+
2753+ def tearDown (self ):
2754+ '''
2755+ Ensure that the volumes plugin is enabled after the test has finished
2756+ running since not doing so might affect tearDown() of CephFSTestCase or
2757+ other superclasses.
2758+ '''
2759+ json_output = self .get_ceph_cmd_stdout ('mgr module ls --format json' )
2760+ json_output = json .loads (json_output )
2761+
2762+ if 'volumes' in json_output ['force_disabled_modules' ]:
2763+ self .run_ceph_cmd (f'mgr module enable { self .VOL_MOD_NAME } ' )
2764+
2765+ super (TestToggleVolumes , self ).tearDown ()
2766+
2767+ def test_force_disable_with_confirmation (self ):
2768+ '''
2769+ Test that running "ceph mgr module force disable volumes
2770+ --yes-i-really-mean-it" successfully disables volumes plugin.
2771+
2772+ Also test "ceph mgr module ls" output after this.
2773+ '''
2774+ self .run_ceph_cmd (f'mgr module force disable { self .VOL_MOD_NAME } '
2775+ f'{ self .CONFIRM } ' )
2776+
2777+ json_output = self .get_ceph_cmd_stdout ('mgr module ls --format json' )
2778+ json_output = json .loads (json_output )
2779+
2780+ self .assertIn (self .VOL_MOD_NAME , json_output ['always_on_modules' ])
2781+ self .assertIn (self .VOL_MOD_NAME , json_output ['force_disabled_modules' ])
2782+
2783+ self .assertNotIn (self .VOL_MOD_NAME , json_output ['enabled_modules' ])
2784+ self .assertNotIn (self .VOL_MOD_NAME , json_output ['disabled_modules' ])
2785+
2786+ def test_force_disable_fails_without_confirmation (self ):
2787+ '''
2788+ Test that running "ceph mgr module force disable volumes" fails with
2789+ EPERM when confirmation flag is not passed along.
2790+
2791+ Also test that output of this command suggests user to pass
2792+ --yes-i-really-mean-it.
2793+ '''
2794+ proc = self .run_ceph_cmd (
2795+ f'mgr module force disable { self .VOL_MOD_NAME } ' ,
2796+ stderr = StringIO (), check_status = False )
2797+
2798+ self .assertEqual (proc .returncode , errno .EPERM )
2799+
2800+ proc_stderr = proc .stderr .getvalue ()
2801+ self .assertIn ('EPERM' , proc_stderr )
2802+ # ensure that the confirmation flag was recommended
2803+ self .assertIn (self .CONFIRM , proc_stderr )
2804+
2805+ def test_force_disable_idempotency (self ):
2806+ '''
2807+ Test that running "ceph mgr module force disable volumes" passes when
2808+ volumes plugin was already force disabled.
2809+ '''
2810+ self .run_ceph_cmd (f'mgr module force disable { self .VOL_MOD_NAME } '
2811+ f'{ self .CONFIRM } ' )
2812+ sleep (5 )
2813+
2814+ json_output = self .get_ceph_cmd_stdout ('mgr module ls --format '
2815+ 'json-pretty' )
2816+ json_output = json .loads (json_output )
2817+
2818+ self .assertIn (self .VOL_MOD_NAME , json_output ['always_on_modules' ])
2819+ self .assertIn (self .VOL_MOD_NAME , json_output ['force_disabled_modules' ])
2820+
2821+ self .assertNotIn (self .VOL_MOD_NAME , json_output ['enabled_modules' ])
2822+ self .assertNotIn (self .VOL_MOD_NAME , json_output ['disabled_modules' ])
2823+
2824+ # XXX: this this test, running this command 2nd time should pass.
2825+ self .run_ceph_cmd (f'mgr module force disable { self .VOL_MOD_NAME } ' )
2826+
2827+ def test_force_disable_nonexistent_mod (self ):
2828+ '''
2829+ Test that passing non-existent name to "ceph mgr module force disable"
2830+ command leads to an error.
2831+ '''
2832+ proc = self .run_ceph_cmd (
2833+ f'mgr module force disable abcd { self .CONFIRM } ' ,
2834+ check_status = False , stderr = StringIO ())
2835+ self .assertEqual (proc .returncode , errno .EINVAL )
2836+ self .assertIn ('EINVAL' , proc .stderr .getvalue ())
2837+
2838+ def test_force_disable_non_alwayson_mod (self ):
2839+ '''
2840+ Test that passing non-existent name to "ceph mgr module force disable"
2841+ command leads to an error.
2842+ '''
2843+ json_output = self .get_ceph_cmd_stdout (
2844+ 'mgr module ls --format json-pretty' , check_status = False ,
2845+ stderr = StringIO ())
2846+ output_dict = json .loads (json_output )
2847+ some_non_alwayson_mod = output_dict ['enabled_modules' ][0 ]
2848+
2849+ proc = self .run_ceph_cmd (
2850+ f'mgr module force disable { some_non_alwayson_mod } { self .CONFIRM } ' ,
2851+ check_status = False , stderr = StringIO ())
2852+ self .assertEqual (proc .returncode , errno .EINVAL )
2853+ self .assertIn ('EINVAL' , proc .stderr .getvalue ())
2854+
2855+ def test_enabled_by_default (self ):
2856+ '''
2857+ Test that volumes plugin is enabled by default and is also reported as
2858+ "always on".
2859+ '''
2860+ json_output = self .get_ceph_cmd_stdout ('mgr module ls --format json' )
2861+ json_output = json .loads (json_output )
2862+
2863+ self .assertIn (self .VOL_MOD_NAME , json_output ['always_on_modules' ])
2864+
2865+ self .assertNotIn (self .VOL_MOD_NAME , json_output ['enabled_modules' ])
2866+ self .assertNotIn (self .VOL_MOD_NAME , json_output ['disabled_modules' ])
2867+ self .assertNotIn (self .VOL_MOD_NAME , json_output ['force_disabled_modules' ])
2868+
2869+ def test_disable_fails (self ):
2870+ '''
2871+ Test that running "ceph mgr module disable volumes" fails with EPERM.
2872+
2873+ This is expected since volumes is an always-on module and therefore
2874+ it can only be disabled using command "ceph mgr module force disable
2875+ volumes".
2876+ '''
2877+ proc = self .run_ceph_cmd (f'mgr module disable { self .VOL_MOD_NAME } ' ,
2878+ stderr = StringIO (), check_status = False )
2879+ self .assertEqual (proc .returncode , errno .EPERM )
2880+
2881+ proc_stderr = proc .stderr .getvalue ()
2882+ self .assertIn ('EPERM' , proc_stderr )
2883+
2884+ def test_enable_idempotency (self ):
2885+ '''
2886+ Test that enabling volumes plugin when it is already enabled doesn't
2887+ exit with non-zero return value.
2888+
2889+ Also test that it reports plugin as already enabled.
2890+ '''
2891+ proc = self .run_ceph_cmd (f'mgr module enable { self .VOL_MOD_NAME } ' ,
2892+ stderr = StringIO ())
2893+ self .assertEqual (proc .returncode , 0 )
2894+
2895+ proc_stderr = proc .stderr .getvalue ()
2896+ self .assertIn ('already enabled' , proc_stderr )
2897+ self .assertIn ('always-on' , proc_stderr )
2898+
2899+ def test_enable_post_disabling (self ):
2900+ '''
2901+ Test that enabling volumes plugin after (force-)disabling it works
2902+ successfully.
2903+
2904+ Alo test "ceph mgr module ls" output for volumes plugin afterwards.
2905+ '''
2906+ self .run_ceph_cmd (f'mgr module force disable { self .VOL_MOD_NAME } '
2907+ f'{ self .CONFIRM } ' )
2908+ # give bit of time for plugin to be disabled.
2909+ sleep (5 )
2910+
2911+ self .run_ceph_cmd (f'mgr module enable { self .VOL_MOD_NAME } ' )
2912+ # give bit of time for plugin to be functional again
2913+ sleep (5 )
2914+ json_output = self .get_ceph_cmd_stdout ('mgr module ls --format json' )
2915+ json_output = json .loads (json_output )
2916+ self .assertIn (self .VOL_MOD_NAME , json_output ['always_on_modules' ])
2917+ self .assertNotIn (self .VOL_MOD_NAME , json_output ['enabled_modules' ])
2918+ self .assertNotIn (self .VOL_MOD_NAME , json_output ['disabled_modules' ])
2919+ self .assertNotIn (self .VOL_MOD_NAME , json_output ['force_disabled_modules' ])
2920+
2921+ # plugin is reported properly by "ceph mgr module ls" command, check if
2922+ # it is also working fine.
2923+ self .run_ceph_cmd ('fs volume ls' )
0 commit comments