diff --git a/drivers/mpathcount.py b/drivers/mpathcount.py index 2bdac1bc7..6be7e3cf3 100755 --- a/drivers/mpathcount.py +++ b/drivers/mpathcount.py @@ -22,6 +22,7 @@ import xs_errors import mpath_cli import json +import subprocess supported = ['iscsi', 'lvmoiscsi', 'rawhba', 'lvmohba', 'ocfsohba', 'ocfsoiscsi', 'netapp', 'gfs2'] @@ -35,6 +36,7 @@ match_bySCSIid = False mpath_enabled = True SCSIid = 'NOTSUPPLIED' +XAPI_HEALTH_CHECK = '/opt/xensource/libexec/xapi-health-check' cached_DM_maj = None @@ -199,13 +201,31 @@ def check_devconfig(devconfig, sm_config, config, remove, add, mpath_status=None else: update_config(key, i, config[key], remove, add, mpath_status) - -def check_xapi_is_enabled(session, hostref): - host = session.xenapi.host.get_record(hostref) - if not host['enabled']: - util.SMlog("Xapi is not enabled, exiting") - mpc_exit(session, 0) - +def check_xapi_is_enabled(): + """Check XAPI health status""" + def _run_command(command, timeout): + try: + process = subprocess.Popen( + command, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True + ) + try: + stdout, stderr = process.communicate(timeout=timeout) + return process.returncode, stdout, stderr + except subprocess.TimeoutExpired: + process.kill() + util.SMlog(f"Command execution timeout after {timeout}s: {' '.join(command)}") + return -1, "", "Timeout" + except Exception as e: + util.SMlog(f"Error executing command: {e}") + return -1, "", str(e) + + returncode, _, stderr = _run_command([XAPI_HEALTH_CHECK], timeout=120) + if returncode != 0: + util.SMlog(f"XAPI health check failed: {stderr}") + return returncode == 0 if __name__ == '__main__': try: @@ -215,7 +235,7 @@ def check_xapi_is_enabled(session, hostref): sys.exit(-1) localhost = session.xenapi.host.get_by_uuid(get_localhost_uuid()) - check_xapi_is_enabled(session, localhost) + check_xapi_is_enabled() # Check whether multipathing is enabled (either for root dev or SRs) try: if get_root_dev_major() != get_dm_major(): diff --git a/tests/test_mpathcount.py b/tests/test_mpathcount.py index c02478057..922e1f204 100644 --- a/tests/test_mpathcount.py +++ b/tests/test_mpathcount.py @@ -211,27 +211,37 @@ def test_exit_log_out_error(self, mock_exit): session.xenapi.session.logout.assert_called_once() @mock.patch('mpathcount.sys.exit', autospec=True) - def test_check_xapi_enabled_yes(self, mock_exit): + @mock.patch('mpathcount.util.SMlog', autospec=True) + @mock.patch('mpathcount.subprocess.Popen', autospec=True) + def test_check_xapi_enabled_yes(self, mock_popen, mock_smlog, mock_exit): # Arrange - session = mock.MagicMock() - session.xenapi.host.get_record.return_value = {'enabled': True} - hostref = mock.MagicMock() + process_mock = mock.Mock() + attrs = {'communicate.return_value': ('output', ''), 'returncode': 0} + process_mock.configure_mock(**attrs) + mock_popen.return_value = process_mock # Act - mpathcount.check_xapi_is_enabled(session, hostref) + result = mpathcount.check_xapi_is_enabled() # Assert + self.assertTrue(result) mock_exit.assert_not_called() + mock_smlog.assert_not_called() @mock.patch('mpathcount.sys.exit', autospec=True) - def test_check_xapi_enabled_no(self, mock_exit): + @mock.patch('mpathcount.util.SMlog', autospec=True) + @mock.patch('mpathcount.subprocess.Popen', autospec=True) + def test_check_xapi_enabled_no(self, mock_popen, mock_smlog, mock_exit): # Arrange - session = mock.MagicMock() - session.xenapi.host.get_record.return_value = {'enabled': False} - hostref = mock.MagicMock() + process_mock = mock.Mock() + attrs = {'communicate.return_value': ('', 'error'), 'returncode': 1} + process_mock.configure_mock(**attrs) + mock_popen.return_value = process_mock # Act - mpathcount.check_xapi_is_enabled(session, hostref) + result = mpathcount.check_xapi_is_enabled() # Assert - mock_exit.assert_called_once_with(0) + self.assertFalse(result) + mock_exit.assert_not_called() + mock_smlog.assert_called_once_with('XAPI health check failed: error')