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('    ', 
'&emsp;').replace('\t', '&emsp;')
+
+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('    ', '&emsp;').replace('\t', 
'&emsp;')
+
+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

Reply via email to