This is an automated email from the ASF dual-hosted git repository. rohit pushed a commit to branch 4.11 in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/4.11 by this push: new 13779dd kvm: add support for custom KVM hook scripts (#2819) 13779dd is described below commit 13779ddd2ee3d816271de681426243c005c1d8be Author: ernjvr <ern...@gmail.com> AuthorDate: Mon Aug 27 10:16:52 2018 +0200 kvm: add support for custom KVM hook scripts (#2819) KVM hook script include - logic to execute custom scripts & logging requirements KVM hook script include - add logic to create custom directory if not exists & extra logging --- agent/bindir/libvirtqemuhook.in | 84 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 6 deletions(-) diff --git a/agent/bindir/libvirtqemuhook.in b/agent/bindir/libvirtqemuhook.in index 55ab3e6..598968b 100755 --- a/agent/bindir/libvirtqemuhook.in +++ b/agent/bindir/libvirtqemuhook.in @@ -6,9 +6,7 @@ # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at -# # http://www.apache.org/licenses/LICENSE-2.0 -# # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -19,6 +17,9 @@ import logging import re import sys +import os +import subprocess +from threading import Timer from xml.dom.minidom import parse from cloudutils.configFileOps import configFileOps from cloudutils.networkConfig import networkConfig @@ -30,19 +31,24 @@ logging.basicConfig(filename='/var/log/libvirt/qemu-hook.log', level=logging.INFO) logger = logging.getLogger('qemu-hook') +customDir = "/etc/libvirt/hooks/custom" +customDirPermissions = 0744 +timeoutSeconds = 10 * 60 +validQemuActions = ['prepare', 'start', 'started', 'stopped', 'release', 'migrate', 'restore', 'reconnect', 'attach'] + def isOldStyleBridge(brName): if brName.find("cloudVirBr") == 0: - return True + return True else: - return False + return False def isNewStyleBridge(brName): if brName.startswith('brvx-'): return False if re.match(r"br(\w+)-(\d+)", brName) == None: - return False + return False else: - return True + return True def getGuestNetworkDevice(): netlib = networkConfig() @@ -71,6 +77,66 @@ def handleMigrateBegin(): pass +def executeCustomScripts(sysArgs): + createDirectoryIfNotExists(customDir, customDirPermissions) + scripts = getCustomScriptsFromDirectory() + + for scriptName in scripts: + executeScript(scriptName, sysArgs) + + +def executeScript(scriptName, sysArgs): + logger.info('Executing custom script: %s, parameters: %s' % (scriptName, ' '.join(map(str, sysArgs)))) + path = customDir + os.path.sep + scriptName + + if not os.access(path, os.X_OK): + logger.warning('Custom script: %s is not executable; skipping execution.' % scriptName) + return + + try: + process = subprocess.Popen([path] + sysArgs, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, shell=False) + try: + timer = Timer(timeoutSeconds, terminateProcess, [process, scriptName]) + timer.start() + output, error = process.communicate() + + if process.returncode == -15: + logger.error('Custom script: %s terminated after timeout of %s second[s].' + % (scriptName, timeoutSeconds)) + return + if process.returncode != 0: + logger.info('return code: %s' % str(process.returncode)) + raise Exception(error) + logger.info('Custom script: %s finished successfully; output: \n%s' % + (scriptName, str(output))) + finally: + timer.cancel() + except (OSError, Exception) as e: + logger.exception("Custom script: %s finished with error: \n%s" % (scriptName, e)) + + +def terminateProcess(process, scriptName): + logger.warning('Custom script: %s taking longer than %s second[s]; terminating..' % (scriptName, str(timeoutSeconds))) + process.terminate() + + +def getCustomScriptsFromDirectory(): + return sorted(filter(lambda fileName: (fileName is not None) & (fileName != "") & ('_' in fileName) & + (fileName.startswith((action + '_')) | fileName.startswith(('all' + '_'))), + os.listdir(customDir)), key=lambda fileName: substringAfter(fileName, '_')) + + +def createDirectoryIfNotExists(dir, permissions): + if not os.path.exists(dir): + logger.info('Directory %s does not exist; creating it.' % dir) + os.makedirs(dir, permissions) + + +def substringAfter(s, delimiter): + return s.partition(delimiter)[2] + + if __name__ == '__main__': if len(sys.argv) != 5: sys.exit(0) @@ -79,5 +145,11 @@ if __name__ == '__main__': logger.debug("Executing qemu hook with args: %s" % sys.argv) action, status = sys.argv[2:4] + if action not in validQemuActions: + logger.error('The given action: %s, is not a valid libvirt qemu operation.' % action) + sys.exit(0) + if action == "migrate" and status == "begin": handleMigrateBegin() + + executeCustomScripts(sys.argv[1:])