summaryrefslogtreecommitdiffstats
path: root/glusternagios
diff options
context:
space:
mode:
authorShubhendu Tripathi <shtripat@redhat.com>2014-04-01 15:07:44 +0530
committerBala.FA <barumuga@redhat.com>2014-04-28 16:20:46 +0530
commit2873ff21e4f99b35ab88595a96c0ee45c83d26c3 (patch)
tree70c350ef307c63072a604b3874ea70e3a5366123 /glusternagios
parentea28af4f36370506a76a5ae4fbd56990dd49c71a (diff)
gluster-nagios-common: Added gluster cli module
Introduced gluster cli module to add all the gluster related get methods Change-Id: I440ae89ac3f93f961024a6e78870154f57b7dfbd Signed-off-by: Shubhendu Tripathi <shtripat@redhat.com> Reviewed-on: https://code.engineering.redhat.com/gerrit/22253 Reviewed-by: Darshan Narayana Murthy <dnarayan@redhat.com> Reviewed-by: Timothy Asir Jeyasingh <tjeyasin@redhat.com> Reviewed-by: Balamurugan Arumugam <barumuga@redhat.com> Reviewed-by: Sahina Bose <sabose@redhat.com> Tested-by: Sahina Bose <sabose@redhat.com>
Diffstat (limited to 'glusternagios')
-rw-r--r--glusternagios/Makefile.am2
-rwxr-xr-xglusternagios/glustercli.py469
-rw-r--r--glusternagios/hostname.py41
3 files changed, 512 insertions, 0 deletions
diff --git a/glusternagios/Makefile.am b/glusternagios/Makefile.am
index 55f8642..7f46e08 100644
--- a/glusternagios/Makefile.am
+++ b/glusternagios/Makefile.am
@@ -1,4 +1,6 @@
dist_glusternagioscommonpylib_PYTHON = \
__init__.py \
+ glustercli.py \
+ hostname.py \
utils.py \
$(NULL)
diff --git a/glusternagios/glustercli.py b/glusternagios/glustercli.py
new file mode 100755
index 0000000..0a126e7
--- /dev/null
+++ b/glusternagios/glustercli.py
@@ -0,0 +1,469 @@
+# Copyright 2014 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Refer to the README and COPYING files for full details of the license
+#
+
+import xml.etree.cElementTree as etree
+import ethtool
+
+import utils
+from utils import CommandPath
+from hostname import getHostNameFqdn, HostNameException
+
+glusterCmdPath = CommandPath("gluster",
+ "/usr/sbin/gluster")
+
+
+# Class for exception definition
+class GlusterCmdFailedException(Exception):
+ message = "command execution failed"
+
+ def __init__(self, rc=0, out=(), err=()):
+ self.rc = rc
+ self.out = out
+ self.err = err
+
+ def __str__(self):
+ o = '\n'.join(self.out)
+ e = '\n'.join(self.err)
+ if o and e:
+ m = o + '\n' + e
+ else:
+ m = o or e
+
+ s = self.message
+ if m:
+ s += '\nerror: ' + m
+ if self.rc:
+ s += '\nreturn code: %s' % self.rc
+ return s
+
+
+if hasattr(etree, 'ParseError'):
+ _etreeExceptions = (etree.ParseError, AttributeError, ValueError)
+else:
+ _etreeExceptions = (SyntaxError, AttributeError, ValueError)
+
+
+def _getGlusterVolCmd():
+ return [glusterCmdPath.cmd, "--mode=script", "volume"]
+
+
+def _getGlusterPeerCmd():
+ return [glusterCmdPath.cmd, "--mode=script", "peer"]
+
+
+def _getGlusterSystemCmd():
+ return [glusterCmdPath.cmd, "system::"]
+
+
+class HostStatus:
+ CONNECTED = 'CONNECTED'
+ DISCONNECTED = 'DISCONNECTED'
+ UNKNOWN = 'UNKNOWN'
+
+
+class VolumeStatus:
+ ONLINE = 'ONLINE'
+ OFFLINE = 'OFFLINE'
+
+
+class TransportType:
+ TCP = 'TCP'
+ RDMA = 'RDMA'
+
+
+class TaskType:
+ REBALANCE = 'REBALANCE'
+ REPLACE_BRICK = 'REPLACE_BRICK'
+ REMOVE_BRICK = 'REMOVE_BRICK'
+
+
+def _getaddr(dev):
+ dev_info_list = ethtool.get_interfaces_info(dev.encode('utf8'))
+ addr = dev_info_list[0].ipv4_address
+ if addr is None:
+ addr = ''
+ return addr
+
+
+def _getIpAddresses():
+ devinfo = {}
+ for dev in ethtool.get_active_devices():
+ try:
+ devinfo[dev] = ethtool.get_ipaddr(dev)
+ except IOError, e:
+ print e
+
+ return devinfo
+
+
+def _getGlusterHostName():
+ try:
+ return getHostNameFqdn()
+ except HostNameException:
+ return ''
+
+
+def _getLocalIpAddress():
+ for ip in _getIpAddresses():
+ if not ip.startswith('127.'):
+ return ip
+ return ''
+
+
+def _execGluster(cmd):
+ return utils.execCmd(cmd)
+
+
+def _execGlusterXml(cmd):
+ cmd.append('--xml')
+ rc, out, err = utils.execCmd(cmd)
+ if rc != 0:
+ raise GlusterCmdFailedException(rc, out, err)
+ try:
+ tree = etree.fromstring('\n'.join(out))
+ rv = int(tree.find('opRet').text)
+ msg = tree.find('opErrstr').text
+ errNo = int(tree.find('opErrno').text)
+ except _etreeExceptions:
+ raise GlusterCmdFailedException(err=out)
+ if rv == 0:
+ return tree
+ else:
+ if errNo != 0:
+ rv = errNo
+ raise GlusterCmdFailedException(rc=rv, err=[msg])
+
+
+def hostUUIDGet():
+ command = _getGlusterSystemCmd() + ["uuid", "get"]
+ rc, out, err = _execGluster(command)
+ if rc == 0:
+ for line in out:
+ if line.startswith('UUID: '):
+ return line[6:]
+
+ raise GlusterCmdFailedException()
+
+
+def _parseVolumeStatus(tree):
+ status = {'name': tree.find('volStatus/volumes/volume/volName').text,
+ 'bricks': [],
+ 'nfs': [],
+ 'shd': []}
+ hostname = _getLocalIpAddress() or _getGlusterHostName()
+ for el in tree.findall('volStatus/volumes/volume/node'):
+ value = {}
+
+ for ch in el.getchildren():
+ value[ch.tag] = ch.text or ''
+
+ if value['path'] == 'localhost':
+ value['path'] = hostname
+
+ if value['status'] == '1':
+ value['status'] = 'ONLINE'
+ else:
+ value['status'] = 'OFFLINE'
+
+ if value['hostname'] == 'NFS Server':
+ status['nfs'].append({'hostname': value['path'],
+ 'port': value['port'],
+ 'status': value['status'],
+ 'pid': value['pid']})
+ elif value['hostname'] == 'Self-heal Daemon':
+ status['shd'].append({'hostname': value['path'],
+ 'status': value['status'],
+ 'pid': value['pid']})
+ else:
+ status['bricks'].append({'brick': '%s:%s' % (value['hostname'],
+ value['path']),
+ 'port': value['port'],
+ 'status': value['status'],
+ 'pid': value['pid']})
+ return status
+
+
+def _parseVolumeStatusDetail(tree):
+ status = {'name': tree.find('volStatus/volumes/volume/volName').text,
+ 'bricks': []}
+ for el in tree.findall('volStatus/volumes/volume/node'):
+ value = {}
+
+ for ch in el.getchildren():
+ value[ch.tag] = ch.text or ''
+
+ sizeTotal = int(value['sizeTotal'])
+ value['sizeTotal'] = sizeTotal / (1024.0 * 1024.0)
+ sizeFree = int(value['sizeFree'])
+ value['sizeFree'] = sizeFree / (1024.0 * 1024.0)
+ status['bricks'].append({'brick': '%s:%s' % (value['hostname'],
+ value['path']),
+ 'sizeTotal': '%.3f' % (value['sizeTotal'],),
+ 'sizeFree': '%.3f' % (value['sizeFree'],),
+ 'device': value['device'],
+ 'blockSize': value['blockSize'],
+ 'mntOptions': value['mntOptions'],
+ 'fsName': value['fsName']})
+ return status
+
+
+def _parseVolumeStatusClients(tree):
+ status = {'name': tree.find('volStatus/volumes/volume/volName').text,
+ 'bricks': []}
+ for el in tree.findall('volStatus/volumes/volume/node'):
+ hostname = el.find('hostname').text
+ path = el.find('path').text
+
+ clientsStatus = []
+ for c in el.findall('clientsStatus/client'):
+ clientValue = {}
+ for ch in c.getchildren():
+ clientValue[ch.tag] = ch.text or ''
+ clientsStatus.append({'hostname': clientValue['hostname'],
+ 'bytesRead': clientValue['bytesRead'],
+ 'bytesWrite': clientValue['bytesWrite']})
+
+ status['bricks'].append({'brick': '%s:%s' % (hostname, path),
+ 'clientsStatus': clientsStatus})
+ return status
+
+
+def _parseVolumeStatusMem(tree):
+ status = {'name': tree.find('volStatus/volumes/volume/volName').text,
+ 'bricks': []}
+ for el in tree.findall('volStatus/volumes/volume/node'):
+ brick = {'brick': '%s:%s' % (el.find('hostname').text,
+ el.find('path').text),
+ 'mallinfo': {},
+ 'mempool': []}
+
+ for ch in el.find('memStatus/mallinfo').getchildren():
+ brick['mallinfo'][ch.tag] = ch.text or ''
+
+ for c in el.findall('memStatus/mempool/pool'):
+ mempool = {}
+ for ch in c.getchildren():
+ mempool[ch.tag] = ch.text or ''
+ brick['mempool'].append(mempool)
+
+ status['bricks'].append(brick)
+ return status
+
+
+def volumeStatus(volumeName, brick=None, option=None):
+ """
+ Get volume status
+
+ Arguments:
+ * VolumeName
+ * brick
+ * option = 'detail' or 'clients' or 'mem' or None
+ Returns:
+ When option=None,
+ {'name': NAME,
+ 'bricks': [{'brick': BRICK,
+ 'port': PORT,
+ 'status': STATUS,
+ 'pid': PID}, ...],
+ 'nfs': [{'hostname': HOST,
+ 'port': PORT,
+ 'status': STATUS,
+ 'pid': PID}, ...],
+ 'shd: [{'hostname': HOST,
+ 'status': STATUS,
+ 'pid': PID}, ...]}
+
+ When option='detail',
+ {'name': NAME,
+ 'bricks': [{'brick': BRICK,
+ 'sizeTotal': SIZE,
+ 'sizeFree': FREESIZE,
+ 'device': DEVICE,
+ 'blockSize': BLOCKSIZE,
+ 'mntOptions': MOUNTOPTIONS,
+ 'fsName': FSTYPE}, ...]}
+
+ When option='clients':
+ {'name': NAME,
+ 'bricks': [{'brick': BRICK,
+ 'clientsStatus': [{'hostname': HOST,
+ 'bytesRead': BYTESREAD,
+ 'bytesWrite': BYTESWRITE}, ...]},
+ ...]}
+
+ When option='mem':
+ {'name': NAME,
+ 'bricks': [{'brick': BRICK,
+ 'mallinfo': {'arena': int,
+ 'fordblks': int,
+ 'fsmblks': int,
+ 'hblkhd': int,
+ 'hblks': int,
+ 'keepcost': int,
+ 'ordblks': int,
+ 'smblks': int,
+ 'uordblks': int,
+ 'usmblks': int},
+ 'mempool': [{'allocCount': int,
+ 'coldCount': int,
+ 'hotCount': int,
+ 'maxAlloc': int,
+ 'maxStdAlloc': int,
+ 'name': NAME,
+ 'padddedSizeOf': int,
+ 'poolMisses': int},...]}, ...]}
+ """
+ command = _getGlusterVolCmd() + ["status", volumeName]
+ if brick:
+ command.append(brick)
+ if option:
+ command.append(option)
+ try:
+ xmltree = _execGlusterXml(command)
+ except GlusterCmdFailedException as e:
+ raise GlusterCmdFailedException(rc=e.rc, err=e.err)
+ try:
+ if option == 'detail':
+ return _parseVolumeStatusDetail(xmltree)
+ elif option == 'clients':
+ return _parseVolumeStatusClients(xmltree)
+ elif option == 'mem':
+ return _parseVolumeStatusMem(xmltree)
+ else:
+ return _parseVolumeStatus(xmltree)
+ except _etreeExceptions:
+ raise GlusterCmdFailedException(err=[etree.tostring(xmltree)])
+
+
+def _parseVolumeInfo(tree):
+ """
+ {VOLUMENAME: {'brickCount': BRICKCOUNT,
+ 'bricks': [BRICK1, BRICK2, ...],
+ 'options': {OPTION: VALUE, ...},
+ 'transportType': [TCP,RDMA, ...],
+ 'uuid': UUID,
+ 'volumeName': NAME,
+ 'volumeStatus': STATUS,
+ 'volumeType': TYPE}, ...}
+ """
+ volumes = {}
+ for el in tree.findall('volInfo/volumes/volume'):
+ value = {}
+ value['volumeName'] = el.find('name').text
+ value['uuid'] = el.find('id').text
+ value['volumeType'] = el.find('typeStr').text.upper().replace('-', '_')
+ status = el.find('statusStr').text.upper()
+ if status == 'STARTED':
+ value["volumeStatus"] = VolumeStatus.ONLINE
+ else:
+ value["volumeStatus"] = VolumeStatus.OFFLINE
+ value['brickCount'] = el.find('brickCount').text
+ value['distCount'] = el.find('distCount').text
+ value['stripeCount'] = el.find('stripeCount').text
+ value['replicaCount'] = el.find('replicaCount').text
+ transportType = el.find('transport').text
+ if transportType == '0':
+ value['transportType'] = [TransportType.TCP]
+ elif transportType == '1':
+ value['transportType'] = [TransportType.RDMA]
+ else:
+ value['transportType'] = [TransportType.TCP, TransportType.RDMA]
+ value['bricks'] = []
+ value['options'] = {}
+ value['bricksInfo'] = []
+ for b in el.findall('bricks/brick'):
+ value['bricks'].append(b.text)
+ for o in el.findall('options/option'):
+ value['options'][o.find('name').text] = o.find('value').text
+ for d in el.findall('bricks/brick'):
+ brickDetail = {}
+ #this try block is to maintain backward compatibility
+ #it returns an empty list when gluster doesnot return uuid
+ try:
+ brickDetail['name'] = d.find('name').text
+ #brickDetail['hostUuid'] = d.find('hostUuid').text
+ value['bricksInfo'].append(brickDetail)
+ except AttributeError:
+ break
+ volumes[value['volumeName']] = value
+ return volumes
+
+
+def volumeInfo(volumeName=None, remoteServer=None):
+ """
+ Returns:
+ {VOLUMENAME: {'brickCount': BRICKCOUNT,
+ 'bricks': [BRICK1, BRICK2, ...],
+ 'options': {OPTION: VALUE, ...},
+ 'transportType': [TCP,RDMA, ...],
+ 'uuid': UUID,
+ 'volumeName': NAME,
+ 'volumeStatus': STATUS,
+ 'volumeType': TYPE}, ...}
+ """
+ command = _getGlusterVolCmd() + ["info"]
+ if remoteServer:
+ command += ['--remote-host=%s' % remoteServer]
+ if volumeName:
+ command.append(volumeName)
+ try:
+ xmltree = _execGlusterXml(command)
+ except GlusterCmdFailedException as e:
+ raise GlusterCmdFailedException(rc=e.rc, err=e.err)
+ try:
+ return _parseVolumeInfo(xmltree)
+ except _etreeExceptions:
+ raise GlusterCmdFailedException(err=[etree.tostring(xmltree)])
+
+
+def _parsePeerStatus(tree, gHostName, gUuid, gStatus):
+ hostList = [{'hostname': gHostName,
+ 'uuid': gUuid,
+ 'status': gStatus}]
+
+ for el in tree.findall('peerStatus/peer'):
+ if el.find('state').text != '3':
+ status = HostStatus.UNKNOWN
+ elif el.find('connected').text == '1':
+ status = HostStatus.CONNECTED
+ else:
+ status = HostStatus.DISCONNECTED
+ hostList.append({'hostname': el.find('hostname').text,
+ 'uuid': el.find('uuid').text,
+ 'status': status})
+
+ return hostList
+
+
+def peerStatus():
+ """
+ Returns:
+ [{'hostname': HOSTNAME, 'uuid': UUID, 'status': STATE}, ...]
+ """
+ command = _getGlusterPeerCmd() + ["status"]
+ try:
+ xmltree = _execGlusterXml(command)
+ except GlusterCmdFailedException as e:
+ raise GlusterCmdFailedException(rc=e.rc, err=e.err)
+ try:
+ return _parsePeerStatus(xmltree,
+ _getLocalIpAddress() or _getGlusterHostName(),
+ hostUUIDGet(), HostStatus.CONNECTED)
+ except _etreeExceptions:
+ raise GlusterCmdFailedException(err=[etree.tostring(xmltree)])
diff --git a/glusternagios/hostname.py b/glusternagios/hostname.py
new file mode 100644
index 0000000..6277569
--- /dev/null
+++ b/glusternagios/hostname.py
@@ -0,0 +1,41 @@
+# Copyright 2014 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Refer to the README and COPYING files for full details of the license
+#
+
+import utils
+
+_hostNameCommandPath = utils.CommandPath("hostname",
+ "/bin/hostname",
+ )
+
+
+class HostNameException(Exception):
+ def __init__(self, rc):
+ self.rc = rc
+ self.message = 'hostname execution failed with error code %s' % self.rc
+
+ def __str__(self):
+ return self.message
+
+
+def getHostNameFqdn():
+ rc, out, err = utils.execCmd([_hostNameCommandPath.cmd, '--fqdn'])
+ if rc:
+ raise HostNameException(rc)
+ else:
+ return out[0]