summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xglusternagios/glustercli.py56
-rw-r--r--tests/test_glustercli.py112
2 files changed, 142 insertions, 26 deletions
diff --git a/glusternagios/glustercli.py b/glusternagios/glustercli.py
index 19469c1..af20676 100755
--- a/glusternagios/glustercli.py
+++ b/glusternagios/glustercli.py
@@ -94,10 +94,10 @@ class VolumeQuotaStatus:
HARD_LIMIT_EXCEEDED = 'HARD_LIMIT_EXCEEDED'
-class VolumeSplitBrainStatus:
+class VolumeHealInfoStatus:
NOTAPPLICABLE = 'NA'
OK = 'OK'
- SPLITBRAIN = 'SPLITBRAIN'
+ ENTRIESFOUND = 'ENTRIESFOUND'
class GeoRepStatus:
@@ -501,17 +501,24 @@ def _parseVolumeQuotaStatus(out, isDisabled=False):
def _parseVolumeSelfHealInfo(out):
value = {}
- splitbrainentries = 0
+ unsyncedentries = 0
+ undergoingheal = 0
for line in out:
if line.startswith('Number of entries'):
- entries = int(line.split(':')[1])
+ try:
+ entries = int(line.split(':')[1])
+ except:
+ entries = 0
if entries > 0:
- splitbrainentries += entries
- if splitbrainentries > 0:
- value['status'] = VolumeSplitBrainStatus.SPLITBRAIN
+ unsyncedentries += entries
+ if line.find('undergoing heal') > -1:
+ undergoingheal += 1
+ if unsyncedentries > 0:
+ value['status'] = VolumeHealInfoStatus.ENTRIESFOUND
else:
- value['status'] = VolumeSplitBrainStatus.OK
- value['unsyncedentries'] = splitbrainentries
+ value['status'] = VolumeHealInfoStatus.OK
+ value['unsyncedentries'] = unsyncedentries
+ value['undergoingheal'] = undergoingheal
return value
@@ -622,7 +629,8 @@ def volumeHealStatus(volumeName, remoteServer=None):
* VolumeName
Returns:
{VOLUMENAME: {'status': SELFHEALSTATUS,
- 'unsyncedentries': ENTRYCOUNT}}
+ 'unsyncedentries': ENTRYCOUNT
+ 'undergoingheal': COUNT}}
"""
command = _getGlusterVolCmd() + ["heal", volumeName, "info"]
return _volumeHealCommandOutput(volumeName, command, remoteServer)
@@ -634,7 +642,8 @@ def volumeHealSplitBrainStatus(volumeName, remoteServer=None):
* VolumeName
Returns:
{VOLUMENAME: {'status': SELFHEALSTATUS,
- 'unsyncedentries': ENTRYCOUNT}}
+ 'unsyncedentries': ENTRYCOUNT
+ 'undergoingheal': COUNT}}
"""
command = _getGlusterVolCmd() + ["heal", volumeName, "info", "split-brain"]
return _volumeHealCommandOutput(volumeName, command, remoteServer)
@@ -646,25 +655,28 @@ def _volumeHealCommandOutput(volumeName, command, remoteServer=None):
* VolumeName
Returns:
{VOLUMENAME: {'status': SELFHEALSTATUS,
- 'unsyncedentries': ENTRYCOUNT}}
+ 'unsyncedentries': ENTRYCOUNT
+ 'undergoingheal': COUNT}}
"""
if remoteServer:
command += ['--remote-host=%s' % remoteServer]
- rc, out, err = _execGluster(command)
+ volumes = volumeInfo(volumeName, remoteServer)
volume = {}
value = {}
- if rc == 0:
- value = _parseVolumeSelfHealInfo(out)
- volume[volumeName] = value
- return volume
- else:
- if len(err) > 0 and err[0].find("is not of type replicate") > -1:
- value['status'] = VolumeSplitBrainStatus.NOTAPPLICABLE
- value['unsyncedentries'] = 0
+ if "REPLICATE" in volumes[volumeName]["volumeType"]:
+ rc, out, err = _execGluster(command)
+ if rc == 0:
+ value = _parseVolumeSelfHealInfo(out)
volume[volumeName] = value
return volume
- raise GlusterCmdFailedException(rc=rc, out=out, err=err)
+ raise GlusterCmdFailedException(rc=rc, out=out, err=err)
+ else:
+ value['status'] = VolumeHealInfoStatus.NOTAPPLICABLE
+ value['undergoingheal'] = 0
+ value['unsyncedentries'] = 0
+ volume[volumeName] = value
+ return volume
def volumeQuotaStatus(volumeName, remoteServer=None):
diff --git a/tests/test_glustercli.py b/tests/test_glustercli.py
index d9ef1ab..d770b92 100644
--- a/tests/test_glustercli.py
+++ b/tests/test_glustercli.py
@@ -29,6 +29,8 @@ class GlusterCliTests(TestCaseBase):
def _parseVolumeInfo_empty_test(self):
out = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+
+
<cliOutput>
<opRet>0</opRet>
<opErrno>0</opErrno>
@@ -1114,13 +1116,23 @@ class GlusterCliTests(TestCaseBase):
@mock.patch('glusternagios.utils.execCmd')
@mock.patch('glusternagios.glustercli._getGlusterVolCmd')
- def test_getVolumeHealSplitBrainStatusNonRepl(self, mock_glusterVolCmd,
+ @mock.patch('glusternagios.glustercli.volumeInfo')
+ def test_getVolumeHealSplitBrainStatusNonRepl(self,
+ mock_volumeInfo,
+ mock_glusterVolCmd,
mock_execCmd,):
mock_glusterVolCmd.return_value = ["gluster", "volume"]
+ mock_volumeInfo.return_value = {'test-vol':
+ {'volumeType': 'DISTRIBUTE',
+ 'replicaCount': 1,
+ 'brickCount': 2
+ }
+ }
mock_execCmd.return_value = 2, None, ["Volume test-vol is not "
"of type replicate"]
expectedOut = {'test-vol':
- {'status': gcli.VolumeSplitBrainStatus.NOTAPPLICABLE,
+ {'status': gcli.VolumeHealInfoStatus.NOTAPPLICABLE,
+ 'undergoingheal': 0,
'unsyncedentries': 0}}
status = gcli.volumeHealSplitBrainStatus("test-vol")
print(status)
@@ -1128,19 +1140,79 @@ class GlusterCliTests(TestCaseBase):
@mock.patch('glusternagios.utils.execCmd')
@mock.patch('glusternagios.glustercli._getGlusterVolCmd')
- def test_getVolumeHealSplitBrainStatus(self, mock_glusterVolCmd,
+ @mock.patch('glusternagios.glustercli.volumeInfo')
+ def test_getVolumeHealSplitBrainStatus(self,
+ mock_volumeInfo,
+ mock_glusterVolCmd,
mock_execCmd,):
mock_glusterVolCmd.return_value = ["gluster", "volume"]
+ mock_volumeInfo.return_value = {'test-vol':
+ {'volumeType': 'REPLICATE',
+ 'replicaCount': 2,
+ 'brickCount': 2
+ }
+ }
mock_execCmd.return_value = (0,
self.__getGlusterSelfHealInfoResult(),
None)
expectedOut = {'test-vol':
- {'status': gcli.VolumeSplitBrainStatus.SPLITBRAIN,
+ {'status': gcli.VolumeHealInfoStatus.ENTRIESFOUND,
+ 'undergoingheal': 0,
'unsyncedentries': 10}}
status = gcli.volumeHealSplitBrainStatus("test-vol")
print(status)
self.assertEquals(status, expectedOut)
+ @mock.patch('glusternagios.utils.execCmd')
+ @mock.patch('glusternagios.glustercli._getGlusterVolCmd')
+ @mock.patch('glusternagios.glustercli.volumeInfo')
+ def test_getVolumeHealInfoUndergoingStatus(self,
+ mock_volumeInfo,
+ mock_glusterVolCmd,
+ mock_execCmd,):
+ mock_glusterVolCmd.return_value = ["gluster", "volume"]
+ mock_volumeInfo.return_value = {'test-vol':
+ {'volumeType': 'REPLICATE',
+ 'replicaCount': 2,
+ 'brickCount': 2
+ }
+ }
+ mock_execCmd.return_value = (0,
+ self.__getGlusterSelfHealInfoUndergoingResult(),
+ None)
+ expectedOut = {'test-vol':
+ {'status': gcli.VolumeHealInfoStatus.ENTRIESFOUND,
+ 'undergoingheal': 1,
+ 'unsyncedentries': 10}}
+ status = gcli.volumeHealSplitBrainStatus("test-vol")
+ print(status)
+ self.assertEquals(status, expectedOut)
+
+ @mock.patch('glusternagios.utils.execCmd')
+ @mock.patch('glusternagios.glustercli._getGlusterVolCmd')
+ @mock.patch('glusternagios.glustercli.volumeInfo')
+ def test_getVolumeHealSplitBrainStatusNoBrick(self,
+ mock_volumeInfo,
+ mock_glusterVolCmd,
+ mock_execCmd,):
+ mock_glusterVolCmd.return_value = ["gluster", "volume"]
+ mock_volumeInfo.return_value = {'test-vol':
+ {'volumeType': 'REPLICATE',
+ 'replicaCount': 2,
+ 'brickCount': 2
+ }
+ }
+ mock_execCmd.return_value = (0,
+ self.__getGlusterSelfHealInfoResultBrickDown(),
+ None)
+ expectedOut = {'test-vol':
+ {'status': gcli.VolumeHealInfoStatus.OK,
+ 'undergoingheal': 0,
+ 'unsyncedentries': 0}}
+ status = gcli.volumeHealSplitBrainStatus("test-vol")
+ print(status)
+ self.assertEquals(status, expectedOut)
+
@mock.patch('glusternagios.glustercli._getGlusterVolCmd')
@mock.patch('glusternagios.glustercli.volumeInfo')
def test_getVolumeGeoRepStatus(self,
@@ -1272,6 +1344,38 @@ class GlusterCliTests(TestCaseBase):
"/dir.10/file.2",
"/dir.7/file.4"]
+ def __getGlusterSelfHealInfoUndergoingResult(self):
+ return ["Gathering list of entries to be healed "
+ "on volume rep has been successful",
+ "",
+ "Brick node2:/bricks/b3",
+ "Status: Brick is Not connected",
+ "Number of entries in split-brain: NA"
+ "",
+ "Brick node1:/bricks/b3",
+ "Number of entries in split-brain: 10",
+ "/dir.7/file.5 - Possibly undergoing heal",
+ "/dir.8/file.3",
+ "/dir.9/file.5",
+ "/dir.2/file.4",
+ "/dir.9/file.4",
+ "/dir.4/file.1",
+ "/file.4",
+ "/dir.7/file.2",
+ "/dir.10/file.2",
+ "/dir.7/file.4"]
+
+ def __getGlusterSelfHealInfoResultBrickDown(self):
+ return ["Gathering list of entries to be healed "
+ "on volume rep has been successful",
+ "",
+ "Brick node2:/bricks/b3",
+ "Status: Brick is Not connected",
+ "Number of entries in split-brain: 0"
+ "",
+ "Brick node1:/bricks/b3",
+ "Number of entries in split-brain: -"]
+
def __getGlusterGeoRepStatusResult(self):
return """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<cliOutput>