Daniel Erez has uploaded a new change for review. Change subject: Introducing ui-vdsm-hooks plugin ......................................................................
Introducing ui-vdsm-hooks plugin Using CGI scripts located on the host's web-server, VDSM commands (vdsClient) can be invoked directly from the UI. Added the following to Host context menu: * GetAllTasks * StopAllTasks * ClearAllTasks * GetStorageDomainsList * GetConnectedStoragePool * DisconnectStoragePool * StopSPM * VdsClient API - UI for all VdsClient API commands. Blog post: http://derezvir.blogspot.co.il/2013/06/ovirt-ui-vdsm-hooks-plugin.html Change-Id: I1531e41eb87f33a7961a9d9ceb5f8a03c12a8cba Signed-off-by: Daniel Erez <[email protected]> --- A ui-vdsm-hooks-plugin/README A ui-vdsm-hooks-plugin/cgi-bin/ui-vdsm-hooks/scripts/vdsClientApi.py A ui-vdsm-hooks-plugin/cgi-bin/ui-vdsm-hooks/ui-vdsm-hooks.cgi A ui-vdsm-hooks-plugin/ui-vdsm-hooks-files/start.html A ui-vdsm-hooks-plugin/ui-vdsm-hooks.json 5 files changed, 278 insertions(+), 0 deletions(-) git pull ssh://gerrit.ovirt.org:29418/samples-uiplugins refs/changes/77/15477/1 diff --git a/ui-vdsm-hooks-plugin/README b/ui-vdsm-hooks-plugin/README new file mode 100644 index 0000000..4926705 --- /dev/null +++ b/ui-vdsm-hooks-plugin/README @@ -0,0 +1,13 @@ +********************** +* Installation Steps * +********************** + +< Engine > +* Copy the following to '/usr/share/ovirt-engine/ui-plugins': +- ui-vdsm-hooks.json +- ui-vdsm-hooks-files/start.html +* Login to the WebAdmin: +- Hosts main-tab -> select a Host -> new context menu options + +< Host > +* Copy cgi-bin folder to your favourite web server. diff --git a/ui-vdsm-hooks-plugin/cgi-bin/ui-vdsm-hooks/scripts/vdsClientApi.py b/ui-vdsm-hooks-plugin/cgi-bin/ui-vdsm-hooks/scripts/vdsClientApi.py new file mode 100644 index 0000000..d035190 --- /dev/null +++ b/ui-vdsm-hooks-plugin/cgi-bin/ui-vdsm-hooks/scripts/vdsClientApi.py @@ -0,0 +1,115 @@ +#!/usr/bin/python + +import cgi +import json +import os +import subprocess as sub + +def executeVdsCommand(command='', args=[]): + cmd = ['vdsClient', '0', command] + args + return sub.Popen(cmd, stdout=sub.PIPE).stdout.read().rstrip('\n') + +def getVdsClientAPI(): + output = executeVdsCommand() + return output[output.find('Commands')+9:] + +def getApiCmds(api): + lines = api.split('\n') + cmds = [] + cmd = '' + + for line in lines: + if not line.startswith('\t'): + cmds.append(cmd) + cmd = '' + cmd += line + '\n' + + cmds[0] = 'Select a command' + + return cmds + +def createForm(apiCmds, selectedIndex=0): + scriptFile = os.path.dirname(__file__).replace('/var/www', '') + '/vdsClientApi.py' + print '<form action="' + scriptFile + '">' + createSelectBox(apiCmds, selectedIndex) + createCmdDescription(apiCmds, selectedIndex) + createTextArea(selectedIndex) + print '</form>' + +def createSelectBox(apiCmds, selectedIndex): + print '<select name="cmdDropDown" onchange="this.form.submit();">' + for i in range(0, len(apiCmds)): + label = apiCmds[i].split('\n')[0] + selected = 'selected' if selectedIndex == i else '' + print '<option value="' + str(i) + '" ' + selected + '>' + label + '</option>' + print '</select>' + print '<br/><br/>' + +def createCmdDescription(apiCmds, selectedIndex): + if selectedIndex == 0: + return + + printFormattedHTML(apiCmds[selectedIndex]) + print '<br/>' + +def createTextArea(selectedIndex): + if selectedIndex == 0: + return + + cmdArgs = getFormValue('cmdArgs') + cmdExecuteBtn = getFormValue('cmdExecuteBtn') + text = cmdArgs if (cmdArgs and cmdExecuteBtn) else '' + + print '<textarea name="cmdArgs" cols="60" rows="2" placeholder="Type arguments here">' + print text + '</textarea><br/><br/>' + print '<input type="submit" name="cmdExecuteBtn" value="Execute"' + print '<br/>' + +def handleSelection(apiCmds, selectedIndex): + print 'Content-Type: text/html\n\n' + print '<br/>' + createForm(apiCmds, selectedIndex) + +def handleExecute(apiCmds, selectedIndex, cmdArgs): + cmd = apiCmds[selectedIndex].split('\n')[0] + args = cmdArgs.split(' ') if cmdArgs else [] + output = executeVdsCommand(cmd, args) + + print '<b>Output:</b><br/>' + + try: + parsedJson = json.loads(output.replace("'", '"')) + printFormattedHTML(json.dumps(parsedJson, indent=4)) + except: + printFormattedHTML(output) + +def printFormattedHTML(str): + escaped = cgi.escape(str) + print escaped.replace('\n', '<br/>').replace(' ', ' ').replace('\t', ' ') + +def getFormValue(key): + form = cgi.FieldStorage() + return form.getvalue(key) + +def main(): + api = getVdsClientAPI() + apiCmds = getApiCmds(api) + + form = cgi.FieldStorage() + selectedIndex = getFormValue('cmdDropDown') + cmdArgs = getFormValue('cmdArgs') + cmdExecuteBtn = getFormValue('cmdExecuteBtn') + + if selectedIndex: + handleSelection(apiCmds, int(selectedIndex)) + else: + createForm(apiCmds) + + if cmdExecuteBtn: + handleExecute(apiCmds, int(selectedIndex), cmdArgs) + +try: + main() +except: + cgi.print_exception() + diff --git a/ui-vdsm-hooks-plugin/cgi-bin/ui-vdsm-hooks/ui-vdsm-hooks.cgi b/ui-vdsm-hooks-plugin/cgi-bin/ui-vdsm-hooks/ui-vdsm-hooks.cgi new file mode 100644 index 0000000..6983b16 --- /dev/null +++ b/ui-vdsm-hooks-plugin/cgi-bin/ui-vdsm-hooks/ui-vdsm-hooks.cgi @@ -0,0 +1,93 @@ +#!/usr/bin/python + +import cgi +import json +import re +import subprocess as sub + +def executeCommand(command): + if command in commands: + return commands[command]() + return "Error: command is not supported" + +def executeVdsCommand(command='', args={}): + cmd = ['vdsClient', '0', command] + args + output = sub.Popen(cmd, stdout=sub.PIPE).stdout.read().rstrip('\n') + return cgi.escape(output) + +def getConnectedStoragePoolsList(): + return executeVdsCommand("getConnectedStoragePoolsList", []) + +def disconnectStoragePool(): + spID = executeCommand("getConnectedStoragePoolsList") + return executeVdsCommand("disconnectStoragePool", [ spID, '0', '0' ]) + +def getStorageDomainsList(): + spID = executeCommand("getConnectedStoragePoolsList") + return executeVdsCommand("getStorageDomainsList", [ spID ]) + +def spmStop(): + spID = executeCommand("getConnectedStoragePoolsList") + return executeVdsCommand("spmStop", [ spID ]) + +def getAllTasks(): + return executeVdsCommand("getAllTasks", []) + +def stopAllTasks(): + return forEachTaskExecute("stopTask") + +def clearAllTasks(): + return forEachTaskExecute("clearTask") + +def forEachTaskExecute(vdsCmd): + taskIDs = re.findall("id = [\w,-]*", getAllTasks()) + for taskID in taskIDs: + uuid = taskID.replace("id = ", "") + executeVdsCommand(vdsCmd, [ uuid ]) + return "" + +def runScript(): + form = cgi.FieldStorage() + scriptFile = form.getfirst("args", "") + return sub.Popen("scripts/" + scriptFile, stdout=sub.PIPE).stdout.read() + +commands = { + 'runScript': runScript, + 'getAllTasks': getAllTasks, + 'stopAllTasks': stopAllTasks, + 'clearAllTasks': clearAllTasks, + 'getStorageDomainsList': getStorageDomainsList, + 'getConnectedStoragePoolsList': getConnectedStoragePoolsList, + 'disconnectStoragePool': disconnectStoragePool, + 'spmStop': spmStop, +} + +def printOutput(output): + try: + parsedJson = json.loads(output.replace("'", '"')) + printFormattedHTML(json.dumps(parsedJson, indent=4)) + except: + printFormattedHTML(output) + +def printFormattedHTML(str): + print str.replace('\n', '<br/>').replace(' ', ' ').replace('\t', ' ') + +def main(): + # Get URL parameters + form = cgi.FieldStorage() + command = form.getfirst("command", "") + + # Print HTML header + print "Content-Type: text/html\n\n" + if not command == 'runScript': + print "<b>Output:</b><br/><br/>" + + # Execute command + output = executeCommand(command) + printOutput(output) + +try: + main() +except: + cgi.print_exception() + diff --git a/ui-vdsm-hooks-plugin/ui-vdsm-hooks-files/start.html b/ui-vdsm-hooks-plugin/ui-vdsm-hooks-files/start.html new file mode 100644 index 0000000..292a2b0 --- /dev/null +++ b/ui-vdsm-hooks-plugin/ui-vdsm-hooks-files/start.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<html> +<head> +<script type='text/javascript'> + var api = parent.pluginApi('UI-VDSM-Hooks-Plugin'); + var config = api.configObject(); + + api.register({ + UiInit : function() { + addHostMainTabActionButton('VdsClient API', 'runScript', 'vdsClientApi.py', "800px", "600px"); + addHostMainTabActionButton('GetAllTasks', 'getAllTasks'); + addHostMainTabActionButton('StopAllTasks', 'stopAllTasks'); + addHostMainTabActionButton('ClearAllTasks', 'clearAllTasks'); + addHostMainTabActionButton('GetStorageDomainsList', 'getStorageDomainsList'); + addHostMainTabActionButton('GetConnectedStoragePool', 'getConnectedStoragePoolsList'); + addHostMainTabActionButton('DisconnectStoragePool', 'disconnectStoragePool'); + addHostMainTabActionButton('StopSPM', 'spmStop'); + } + }); + api.ready(); + + var addHostMainTabActionButton = function(title, command, args, width, height) { + api.addMainTabActionButton('Host', title, { + onClick : function() { + var url = getCgiUrl(arguments[0], command, args); + var dialogToken = "hook-dialog"; + api.showDialog(title, dialogToken, url, width || "500px", height || "400px", + { + resizeEnabled: true, + } + ); + }, + isEnabled : function() { + return arguments.length == 1; + }, + location: 'OnlyFromContext' + }); + } + + // Get 'CGI' scripts URL using specified host address + var getCgiUrl = function(selectedHost, command, args) { + var hostAddress = selectedHost.hostname; + var cgiUrl = 'http://' + hostAddress + config.cgiUrl + "?command=" + command + "&args=" + args; + + return cgiUrl; + } +</script> +</head> +<body> +</body> +</html> \ No newline at end of file diff --git a/ui-vdsm-hooks-plugin/ui-vdsm-hooks.json b/ui-vdsm-hooks-plugin/ui-vdsm-hooks.json new file mode 100644 index 0000000..f11dfad --- /dev/null +++ b/ui-vdsm-hooks-plugin/ui-vdsm-hooks.json @@ -0,0 +1,6 @@ +{ + "name": "UI-VDSM-Hooks-Plugin", + "url": "/webadmin/webadmin/plugin/UI-VDSM-Hooks-Plugin/start.html", + "resourcePath": "ui-vdsm-hooks-files", + "config": { "cgiUrl": "/cgi-bin/ui-vdsm-hooks/ui-vdsm-hooks.cgi" } +} \ No newline at end of file -- To view, visit http://gerrit.ovirt.org/15477 To unsubscribe, visit http://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I1531e41eb87f33a7961a9d9ceb5f8a03c12a8cba Gerrit-PatchSet: 1 Gerrit-Project: samples-uiplugins Gerrit-Branch: master Gerrit-Owner: Daniel Erez <[email protected]> _______________________________________________ Engine-patches mailing list [email protected] http://lists.ovirt.org/mailman/listinfo/engine-patches
