Change in vdsm[master]: gluster: Added verbs for read, update, add and remove hooks
tjeyasin at redhat.com
tjeyasin at redhat.com
Mon Apr 22 16:12:59 UTC 2013
Hello Ayal Baron, Bala.FA, Saggi Mizrahi, Dan Kenigsberg,
I'd like you to do a code review. Please visit
http://gerrit.ovirt.org/14145
to review the following change.
Change subject: gluster: Added verbs for read, update, add and remove hooks
......................................................................
gluster: Added verbs for read, update, add and remove hooks
Below new verbs are added to perform read, update, add and remove hooks
* hookRead
Output Structure:
{'content': HOOK_CONTENT,
'type': MIME_TYPE,
'md5sum': MD5SUM}
* hookUpdate
* hookAdd
* hookRemove
Change-Id: Ie123356209aa0f71176bd06b9c53e0e96211e716
Signed-off-by: Timothy Asir <tjeyasin at redhat.com>
---
M client/vdsClientGluster.py
M vdsm/gluster/__init__.py
M vdsm/gluster/api.py
M vdsm/gluster/exception.py
M vdsm/gluster/hooks.py
5 files changed, 266 insertions(+), 3 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/45/14145/1
diff --git a/client/vdsClientGluster.py b/client/vdsClientGluster.py
index 3eafe1c..e31961b 100644
--- a/client/vdsClientGluster.py
+++ b/client/vdsClientGluster.py
@@ -17,6 +17,8 @@
# Refer to the README and COPYING files for full details of the license
#
+import hashlib
+import base64
import pprint as pp
from vdsClient import service
@@ -333,6 +335,60 @@
status = self.s.glusterHookDisable(glusterCmd, level, hookName)
return status['status']['code'], status['status']['message']
+ def do_glusterHookRead(self, args):
+ params = self._eqSplit(args)
+ glusterCmd = params.get('command', '')
+ level = params.get('level', '')
+ hookName = params.get('hookName', '')
+
+ status = self.s.glusterHookRead(glusterCmd, level, hookName)
+ pp.pprint(status)
+ return status['status']['code'], status['status']['message']
+
+ def do_glusterHookUpdate(self, args):
+ params = self._eqSplit(args)
+ glusterCmd = params.get('command', '')
+ level = params.get('level', '')
+ hookName = params.get('hookName', '')
+ hookFile = params.get('hookFile', '')
+ with open(hookFile, 'r') as f:
+ hookData = f.read()
+ content = base64.b64encode(hookData)
+ md5sum = hashlib.md5(hookData).hexdigest()
+
+ status = self.s.glusterHookUpdate(glusterCmd, level, hookName,
+ content, md5sum)
+ pp.pprint(status)
+ return status['status']['code'], status['status']['message']
+
+ def do_glusterHookAdd(self, args):
+ params = self._eqSplit(args)
+ glusterCmd = params.get('command', '')
+ level = params.get('level', '')
+ hookName = params.get('hookName', '')
+ hookFile = params.get('hookFile', '')
+ hookEnable = False
+ if params.get('enable', '').upper() == 'TRUE':
+ hookEnable = True
+ with open(hookFile, 'r') as f:
+ hookData = f.read()
+ md5sum = hashlib.md5(hookData).hexdigest()
+ content = base64.b64encode(hookData)
+
+ status = self.s.glusterHookAdd(glusterCmd, level, hookName,
+ content, md5sum, hookEnable)
+ pp.pprint(status)
+ return status['status']['code'], status['status']['message']
+
+ def do_glusterHookRemove(self, args):
+ params = self._eqSplit(args)
+ glusterCmd = params.get('command', '')
+ level = params.get('level', '')
+ hookName = params.get('hookName', '')
+
+ status = self.s.glusterHookRemove(glusterCmd, level, hookName)
+ return status['status']['code'], status['status']['message']
+
def getGlusterCmdDict(serv):
return \
@@ -562,4 +618,35 @@
'hookName=<hook_name>\n\t'
'<hook_name> is an existing hook name',
'Disable hook script'
+ )),
+ 'glusterHookRead': (
+ serv.do_glusterHookRead,
+ ('command=<gluster_command> level={pre|post} '
+ 'hookName=<hook_name>\n\t'
+ '<hook_name> is an existing hook name',
+ 'Read hook script'
+ )),
+ 'glusterHookUpdate': (
+ serv.do_glusterHookUpdate,
+ ('command=<gluster_command> level={pre|post} '
+ 'hookName=<hook_name> hookFile=<hook_file>\n\t'
+ '<hook_name> is an existing hook name',
+ '<hook_file> is the input hook file name contains hook data',
+ 'Update hook script'
+ )),
+ 'glusterHookAdd': (
+ serv.do_glusterHookAdd,
+ ('command=<gluster_command> level={pre|post} '
+ 'hookName=<hook_name> hookFile=<hook_file> '
+ ' enable={true|false}\n\t'
+ '<hook_name> is a new hook name',
+ '<hook_file> is the input hook file name contains hook data',
+ 'Add hook script'
+ )),
+ 'glusterHookRemove': (
+ serv.do_glusterHookRemove,
+ ('command=<gluster_command> level={pre|post} '
+ 'hookName=<hook_name>\n\t'
+ '<hook_name> is an existing hook name',
+ 'Remove hook script'
)), }
diff --git a/vdsm/gluster/__init__.py b/vdsm/gluster/__init__.py
index 4c21788..04f9a9a 100644
--- a/vdsm/gluster/__init__.py
+++ b/vdsm/gluster/__init__.py
@@ -18,6 +18,8 @@
# Refer to the README and COPYING files for full details of the license
#
+import os
+import tempfile
from functools import wraps
MODULE_LIST = ('cli', 'hooks')
@@ -45,3 +47,11 @@
except ImportError:
pass
return methods
+
+
+def safeWrite(fileName, content):
+ with tempfile.NamedTemporaryFile(dir=os.path.dirname(fileName),
+ delete=False) as tmp:
+ tmp.write(content)
+ tmpFileName = tmp.name
+ os.rename(tmpFileName, fileName)
diff --git a/vdsm/gluster/api.py b/vdsm/gluster/api.py
index fbb49b7..12423a3 100644
--- a/vdsm/gluster/api.py
+++ b/vdsm/gluster/api.py
@@ -251,6 +251,27 @@
def hookDisable(self, glusterCmd, hookLevel, hookName, options=None):
self.svdsmProxy.glusterHookDisable(glusterCmd, hookLevel, hookName)
+ @exportAsVerb
+ def hookRead(self, glusterCmd, hookLevel, hookName, options=None):
+ return self.svdsmProxy.glusterHookRead(glusterCmd, hookLevel,
+ hookName)
+
+ @exportAsVerb
+ def hookUpdate(self, glusterCmd, hookLevel, hookName, hookData,
+ hookMd5Sum, options=None):
+ self.svdsmProxy.glusterHookUpdate(glusterCmd, hookLevel, hookName,
+ hookData, hookMd5Sum)
+
+ @exportAsVerb
+ def hookAdd(self, glusterCmd, hookLevel, hookName, hookData, hookMd5Sum,
+ enable=False, options=None):
+ self.svdsmProxy.glusterHookAdd(glusterCmd, hookLevel, hookName,
+ hookData, hookMd5Sum, enable)
+
+ @exportAsVerb
+ def hookRemove(self, glusterCmd, hookLevel, hookName, options=None):
+ self.svdsmProxy.glusterHookRemove(glusterCmd, hookLevel, hookName)
+
def getGlusterMethods(gluster):
l = []
diff --git a/vdsm/gluster/exception.py b/vdsm/gluster/exception.py
index a130c50..ec35868 100644
--- a/vdsm/gluster/exception.py
+++ b/vdsm/gluster/exception.py
@@ -435,3 +435,45 @@
self.message = \
'Hook %s of command %s, level %s conflict found' % \
(hookName, glusterCmd, level)
+
+
+class GlusterHookReadFailedException(GlusterHookException):
+ code = 4506
+ message = "Hook read failed"
+
+
+class GlusterHookUpdateFailedException(GlusterHookException):
+ code = 4507
+ message = "Hook update failed"
+
+
+class GlusterHookAlreadyExistException(GlusterHookException):
+ code = 4508
+
+ def __init__(self, glusterCmd=None, level=None, hookName=None):
+ self.glusterCmd = glusterCmd
+ self.level = level
+ self.hookName = hookName
+ self.message = \
+ 'Hook %s of command %s, level %s already exist' % \
+ (hookName, glusterCmd, level)
+
+
+class GlusterHookCheckSumMismatchException(GlusterException):
+ code = 4509
+
+ def __init__(self, computedMd5Sum, expectedMd5Sum):
+ self.computedMd5Sum = computedMd5Sum
+ self.expectedMd5Sum = expectedMd5Sum
+ self.message = 'Hook file check sum:%s mismatch %s' % (computedMd5Sum,
+ expectedMd5Sum)
+
+
+class GlusterHookAddFailedException(GlusterHookException):
+ code = 4510
+ message = "Hook add failed"
+
+
+class GlusterHookRemoveFailedException(GlusterHookException):
+ code = 4511
+ message = "Hook remove failed"
diff --git a/vdsm/gluster/hooks.py b/vdsm/gluster/hooks.py
index 78cb69d..13192b4 100644
--- a/vdsm/gluster/hooks.py
+++ b/vdsm/gluster/hooks.py
@@ -20,11 +20,14 @@
import os
import stat
+import base64
import hashlib
+import errno
import magic
import exception as ge
from functools import wraps
from . import exportToSuperVdsm
+from . import safeWrite
_glusterHooksPath = '/var/lib/glusterd/hooks/1'
_mimeType = None
@@ -119,13 +122,15 @@
raise ge.GlusterHookListException(err=[errMsg])
-def _getHookFileNames(glusterCmd, hookLevel, hookName):
+def _getHookFileNames(glusterCmd, hookLevel, hookName, raiseConflict=True):
enabledFile = os.path.join(_glusterHooksPath, glusterCmd, hookLevel,
HookStatus.ENABLE + hookName)
disabledFile = os.path.join(_glusterHooksPath, glusterCmd, hookLevel,
HookStatus.DISABLE + hookName)
- if os.path.exists(enabledFile) and os.path.exists(disabledFile):
- raise ge.GlusterHookConflictException(glusterCmd, hookLevel, hookName)
+ if raiseConflict:
+ if os.path.exists(enabledFile) and os.path.exists(disabledFile):
+ raise ge.GlusterHookConflictException(glusterCmd, hookLevel,
+ hookName)
return enabledFile, disabledFile
@@ -159,3 +164,101 @@
raise ge.GlusterHookDisableFailedException(err=[errMsg])
elif not os.path.exists(disabledFile):
raise ge.GlusterHookNotFoundException(glusterCmd, hookLevel, hookName)
+
+
+ at checkArgs
+ at exportToSuperVdsm
+def hookRead(glusterCmd, hookLevel, hookName):
+ """
+ Returns:
+ {'content': HOOK_CONTENT,
+ 'type': MIME_TYPE,
+ 'md5sum': MD5SUM}
+ """
+ enabledFile, disabledFile = _getHookFileNames(glusterCmd,
+ hookLevel.lower(), hookName)
+ if os.path.exists(enabledFile):
+ hookFile = enabledFile
+ elif os.path.exists(disabledFile):
+ hookFile = disabledFile
+ else:
+ raise ge.GlusterHookNotFoundException(glusterCmd, hookLevel, hookName)
+ try:
+ with open(hookFile, 'r') as f:
+ encodedString = base64.b64encode(f.read())
+ return {'content': encodedString,
+ 'type': _getMimeType(hookFile),
+ 'md5sum': _computeMd5Sum(hookFile)}
+ except IOError, e:
+ errMsg = "[Errno %s] %s: '%s'" % (e.errno, e.strerror, e.filename)
+ raise ge.GlusterHookReadException(err=[errMsg])
+
+
+def _hookUpdateOrAdd(glusterCmd, hookLevel, hookName, hookData, hookMd5Sum,
+ update=True, enable=False):
+ enabledFile, disabledFile = _getHookFileNames(glusterCmd,
+ hookLevel.lower(), hookName)
+ hookFound = os.path.exists(enabledFile) or os.path.exists(disabledFile)
+ if update:
+ if not hookFound:
+ raise ge.GlusterHookNotFoundException(glusterCmd, hookLevel,
+ hookName)
+ else:
+ if hookFound:
+ raise ge.GlusterHookAlreadyExistException(glusterCmd, hookLevel,
+ hookName)
+
+ content = base64.b64decode(hookData)
+ md5Sum = hashlib.md5(content).hexdigest()
+ if hookMd5Sum != md5Sum:
+ raise ge.GlusterHookCheckSumMismatchException(md5Sum, hookMd5Sum)
+
+ if enable or os.path.exists(enabledFile):
+ safeWrite(enabledFile, content)
+ os.chmod(enabledFile, stat.S_IXUSR)
+ else:
+ safeWrite(disabledFile, content)
+
+
+ at checkArgs
+ at exportToSuperVdsm
+def hookUpdate(glusterCmd, hookLevel, hookName, hookData, hookMd5Sum):
+ try:
+ return _hookUpdateOrAdd(glusterCmd, hookLevel, hookName, hookData,
+ hookMd5Sum)
+ except IOError, e:
+ errMsg = "[Errno %s] %s: '%s'" % (e.errno, e.strerror, e.filename)
+ raise ge.GlusterHookUpdateFailedException(err=[errMsg])
+
+
+ at checkArgs
+ at exportToSuperVdsm
+def hookAdd(glusterCmd, hookLevel, hookName, hookData, hookMd5Sum,
+ enable=False):
+ try:
+ return _hookUpdateOrAdd(glusterCmd, hookLevel, hookName, hookData,
+ hookMd5Sum, update=False, enable=enable)
+ except IOError, e:
+ errMsg = "[Errno %s] %s: '%s'" % (e.errno, e.strerror, e.filename)
+ raise ge.GlusterHookAddFailedException(err=[errMsg])
+
+
+ at checkArgs
+ at exportToSuperVdsm
+def hookRemove(glusterCmd, hookLevel, hookName):
+ enabledFile, disabledFile = _getHookFileNames(glusterCmd,
+ hookLevel.lower(),
+ hookName,
+ raiseConflict=False)
+ try:
+ os.remove(enabledFile)
+ except OSError, e:
+ if errno.ENOENT != e.errno:
+ errMsg = "[Errno %s] %s: '%s'" % (e.errno, e.strerror, e.filename)
+ raise ge.GlusterHookRemoveFailedException(err=[errMsg])
+ os.remove(disabledFile)
+ except OSError:
+ # Gluster does not care about disabledHook during pre/post
+ # command execution. So, It doesn't matter, if we unable to remove
+ # disabledHook file
+ pass
--
To view, visit http://gerrit.ovirt.org/14145
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ie123356209aa0f71176bd06b9c53e0e96211e716
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Timothy Asir <tjeyasin at redhat.com>
Gerrit-Reviewer: Ayal Baron <abaron at redhat.com>
Gerrit-Reviewer: Bala.FA <barumuga at redhat.com>
Gerrit-Reviewer: Dan Kenigsberg <danken at redhat.com>
Gerrit-Reviewer: Saggi Mizrahi <smizrahi at redhat.com>
More information about the vdsm-patches
mailing list