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