Oved Ourfali has uploaded a new change for review.

Change subject: docker: new docker plugin
......................................................................

docker: new docker plugin

This patch adds a docker UI plugin, which allows you to create a VM that
runs a docker image.
See README file for more details.

Change-Id: I6fbeecd38207815399f1e4afc86dd57465a010a9
Signed-off-by: Oved Ourfali <oourf...@redhat.com>
---
A docker-plugin/README
A docker-plugin/docker-resources/icon_help.png
A docker-plugin/docker-resources/launch-docker-dialog.html
A docker-plugin/docker-resources/plugin.html
A docker-plugin/docker-resources/progress.gif
A docker-plugin/docker.json
6 files changed, 468 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/samples-uiplugins 
refs/changes/14/25814/1

diff --git a/docker-plugin/README b/docker-plugin/README
new file mode 100644
index 0000000..e6dfcd7
--- /dev/null
+++ b/docker-plugin/README
@@ -0,0 +1,28 @@
+Docker plugin
+-------------------
+
+This plugin allows to create an oVirt VM that runs a docker image inside it.
+Requirements:
+1. The template that has a docker, cloud-init, and oVirt-guest agent installed
+2. The docker service should be configured with the "-r" option, to make sure 
the docker container is restarted after rebooting the VM
+
+INSTALLING
+
+1. Configure oVirt Engine HTTP(S) origin(s), and the available docker images, 
either via editing the existing plugin config file ($PLUGIN_HOME/docker.json), 
or by creating a new user plugin config file in 
$ENGINE_ETC/ui-plugins/docker-config.json
+- The current config file has some docker images configured, as well as the 
"localhost" origin
+
+Expose plugin metadata to oVirt Engine
+  $ ln -s $PLUGIN_HOME/docker.json $ENGINE_USR/ui-plugins/docker.json
+
+Expose plugin static files to oVirt Engine
+  $ ln -s $PLUGIN_HOME/docker-resources $ENGINE_USR/ui-plugins/docker-resources
+
+USAGE:
+1. Fill in the DC, Cluster, Template (template that has the requirements 
above), VM properties, Docker details (image, port mapping and command if 
needed), and cloud-init data.
+2. Run the created VM
+
+Notes:
+1. If the docker image already exists in the template then the docker 
container will probably be started quickly.
+However, if it isn't then it will be downloaded from the docker public 
registry, and that may take a while, depnding on the size of the image.
+2. The list of containers is currently hard-coded in the plugin configuration 
file. The code to get the list from the external public docker image repository 
is available, but it requires JSONP support to prevent cross-origin issues.
+
diff --git a/docker-plugin/docker-resources/icon_help.png 
b/docker-plugin/docker-resources/icon_help.png
new file mode 100644
index 0000000..fd0f452
--- /dev/null
+++ b/docker-plugin/docker-resources/icon_help.png
Binary files differ
diff --git a/docker-plugin/docker-resources/launch-docker-dialog.html 
b/docker-plugin/docker-resources/launch-docker-dialog.html
new file mode 100644
index 0000000..0b6ba06
--- /dev/null
+++ b/docker-plugin/docker-resources/launch-docker-dialog.html
@@ -0,0 +1,236 @@
+<!DOCTYPE html>
+<html>
+<head></head>
+<style>
+
+.select {
+  border: 1px solid gray;
+  background-color: white;
+  width: 265px;
+}
+
+.body {
+  margin: 0 !important;
+  color: #333;
+  font-family: Arial Unicode MS, Arial, sans-serif;
+  font-size: small;
+}
+
+.form {
+  top: 0;
+  position: absolute;
+}
+
+</style>
+<body class="body">
+<script src="http://code.jquery.com/jquery-latest.js";></script>
+<p>
+
+<form name="launchform" class="form">
+<table style="width:100%; background-color: #e5e5e5; padding: 5px; margin: 
0px;">
+<col width="250px">
+<tr>
+<td>Data-Center</td><td><select name="dcs" size="1" id="dcs" class="target 
select">
+</select></td>
+</tr>
+<tr>
+<td>Cluster</td><td><select name="clusters" size="1" class="select">
+</select></td>
+</tr>
+<tr>
+<td>Template</td><td><select name="templates" size="1" class="select">
+</select></td>
+</tr>
+</table>
+<br/>
+<table>
+<col width="250px">
+<tr><td>Name</td><td><input type="text" name="name" class="select"></td></tr>
+<tr><td>Number of Sockets</td><td><input type="text" name="sockets" 
class="select"></td></tr>
+<tr><td>Number of Cores</td><td><input type="text" name="cores" 
class="select"></td></tr>
+<tr><td>Memory Size (in GB)</td><td><input type="text" name="memory" 
class="select"></td></tr>
+</table>
+<br/>
+<br/>
+Docker Details:<br/>
+<table style="width:100%">
+<col width="250px">
+<tr>
+<td>Image</td><td><select name="images" size="1" class="select">
+</select></td>
+</tr>
+<tr><td>Port Mapping (e.g. 80:80)<img src="icon_help.png" title="Map a network 
port to the container"</td><td><input type="text" name="port" class="select" 
title="Map a network port to the container"></td></tr>
+<tr><td>Command<img src="icon_help.png" title="Needed in images without an 
entrypoint"></td><td><input type="text" name="command" class="select" 
title="Needed in images without an entrypoint"></td></tr>
+</table>
+
+<br/>
+Cloud-Init Data:<br/>
+<table style="width:100%">
+<col width="250px">
+<tr><td>Host name</td><td><input type="text" name="hostname" 
class="select"></td></tr>
+<tr><td>SSH key</td><td><input type="text" name="sshkey" 
class="select"></td></tr>
+</table>
+</form>
+<div style="top:0; line-height: 430px; width:100%; height:100%; 
background-color: white; text-align: center; vertical-align: middle; 
display:none; position: absolute; z-index:9999;" id="progress">
+<img src="progress.gif">
+</div>
+
+
+</p>
+
+
+<script type='text/javascript'>
+
+function template(templateName) {
+    this.name = templateName;
+    this.toJsonString = function () { return JSON.stringify(this); };
+};
+
+function cluster(clusterName) {
+    this.name = clusterName;
+    this.toJsonString = function () { return JSON.stringify(this); };
+};
+
+function vm(vmName, vmTemplateName, vmClusterName, vmMemory, 
vmCpuTopologySockets, vmCpuTopologyCores, initialization) {
+    this.name = vmName;
+    this.template = new template(vmTemplateName);
+    this.cluster = new cluster(vmClusterName);
+    this.memory = vmMemory;
+    this.cpu = new cpu(new topology(vmCpuTopologySockets, vmCpuTopologyCores));
+    this.initialization = initialization;
+    this.toJsonString = function () { return JSON.stringify(this); };
+};
+
+function topology(sockets, cores) {
+    this.sockets = sockets;
+    this.cores = cores;
+    this.toJsonString = function () { return JSON.stringify(this); };
+}
+
+function cpu(topology) {
+    this.topology = topology;
+    this.toJsonString = function () { return JSON.stringify(this); };
+}
+
+function createDockerCustomScript(image, command, port) {
+    // We call docker service restart and 
+    var prefix = 'runcmd:\n- [ service, docker, restart ]\n- [ sleep, 10 ]\n- 
[ docker, run, -d';
+    var suffix = ' ]';
+    var result = prefix;
+
+    // Setting port mapping if it is set
+    if (port) {
+        result += ', -p, "' + port + '"';
+    }
+
+    // Setting the image
+    result += ', "' + image + '"';
+
+    // Setting a command if it is set
+    if (command.trim()) {
+        result += ', "' + command.trim().replace(" ", " , ") + '"';
+    }
+
+    result += suffix;
+
+    return result;
+}
+
+function initialization(hostName, sshKey, regenerateSshKeys, customScript) {
+    this.host_name = hostName;
+    this.authorized_ssh_keys = sshKey;
+    this.regenerate_ssh_keys = regenerateSshKeys;
+    this.custom_script = customScript;
+    this.toJsonString = function () { return JSON.stringify(this); };
+}
+
+function createNewVm(sessionId, apiEntryPoint, vm) {
+    var dialog = window;
+    var vmsUrl = apiEntryPoint + "/vms";
+    jQuery.ajax({
+        type: "POST",
+        url: vmsUrl,
+        headers: { 'engineSessionId' : sessionId, 'Prefer' : 'persistent-auth' 
},
+        data: vm.toJsonString(),
+        contentType: "application/json; charset=utf-8",
+        dataType: "json",
+
+        success: function (data, status, jqXHR) {
+             alert('VM created successfully');
+
+             // Closing the dialog
+                parent.postMessage('CloseDialog', '*');
+        },
+    
+        error: function (jqXHR, status) { 
+             // Showing the error
+             alert(JSON.stringify(jqXHR));
+
+             // Hiding the progress widget
+                $( "#progress" ).hide();
+        }
+     });
+
+     // Showing the progress widget
+     $( "#progress" ).show();
+}
+
+function dcSelectionChangedEvent() {
+    var selectedIndex = document.launchform.dcs.options.selectedIndex;
+    parent.postMessage('GetClustersAndTemplates-' + 
document.launchform.dcs.options[selectedIndex].value, '*');
+}
+
+// The plugin should call this function in response to GetDataCenterScore 
message
+function updateDataCenters(dcs) {
+    for (var index in dcs) {
+        document.launchform.dcs.options[index] = new Option(dcs[index], 
dcs[index], false, false);
+    }
+    dcSelectionChangedEvent();
+};
+
+function updateClusters(clusters) {
+    for (var index in clusters) {
+        document.launchform.clusters.options[index] = new 
Option(clusters[index], clusters[index], false, false);
+    }
+};
+
+function updateTemplates(templates) {
+    for (var index in templates) {
+        document.launchform.templates.options[index] = new 
Option(templates[index], templates[index], false, false);
+    }
+};
+
+function updateImages(images) {
+    for (var index in images) {
+        document.launchform.images.options[index] = new Option(images[index], 
images[index], false, false);
+    }
+};
+
+function addVm(sessionId, apiEntryPoint) {
+    var name = document.launchform.name.value;
+    var template = 
document.launchform.templates.options[document.launchform.templates.options.selectedIndex].value;
+    var cluster = 
document.launchform.clusters.options[document.launchform.clusters.options.selectedIndex].value;
+    var memory = document.launchform.memory.value*1024*1024*1024;
+    var sockets = document.launchform.sockets.value;
+    var cores = document.launchform.cores.value;
+    var hostName = document.launchform.hostname.value;
+    var sshKey = document.launchform.sshkey.value;
+    var command = document.launchform.command.value;
+    var image = 
document.launchform.images.options[document.launchform.images.options.selectedIndex].value;
+    var port = document.launchform.port.value;
+    var customScript = createDockerCustomScript(image, command, port);
+
+    createNewVm(sessionId, apiEntryPoint, new vm(name, template, cluster, 
memory, sockets, cores, new initialization(hostName, sshKey, false, 
customScript)));
+}
+
+       parent.postMessage('GetDataCenters', '*');
+       parent.postMessage('GetImages', '*');
+
+    $( "#dcs" ).change(function() {
+        dcSelectionChangedEvent();
+    });
+
+</script>
+
+</body>
+</html>
diff --git a/docker-plugin/docker-resources/plugin.html 
b/docker-plugin/docker-resources/plugin.html
new file mode 100644
index 0000000..916efca
--- /dev/null
+++ b/docker-plugin/docker-resources/plugin.html
@@ -0,0 +1,183 @@
+<!doctype html>
+<!--
+       oVirt Docker Plugin
+-->
+<html>
+<head>
+    <meta charset="utf-8">
+</head>
+<body>
+
+<script src="http://code.jquery.com/jquery-latest.js";></script>
+<script type='text/javascript'>
+
+var formWindow = null;
+var restSessionId = '';
+var api = parent.pluginApi('docker');
+
+// Get runtime plugin configuration, i.e. custom configuration (if any)
+// merged on top of default configuration (if any)
+var config = api.configObject();
+
+
+function getDCList(sessionId, apiEntryPoint) {
+    var dcs = new Array();
+    var dcsUrl = apiEntryPoint + "/datacenters";
+    jQuery.ajax({ 
+        type: "GET",
+        dataType: "json",
+        url: dcsUrl,
+        headers: { 'engineSessionId' : sessionId, 'Prefer' : 'persistent-auth' 
},
+        success: function(data) {
+            for (var index in data.data_center) {
+                dcs[index] = data.data_center[index].name;
+            }
+            formWindow.updateDataCenters(dcs);
+        }
+    });
+}
+
+function setImageList(images) {
+    formWindow.updateImages(images);
+}
+
+// Currently not in use.
+// Useful for getting the images from the public repo, once it supports JSONP
+function getImageList(query) {
+    var images = new Array();
+    jQuery.ajax({
+        type: "GET",
+        dataType: "json",
+        url: "https://index.docker.io/v1/search?q="; + 
encodeURIComponent(query),
+        success: function(data) {
+            for (var index in data.results) {
+                images[index] = data.results[index].name + " (" + 
data.results[index].description + ")";
+            }
+            formWindow.updateImages(images);
+        },
+        error: function (jqXHR, status) {
+             alert(JSON.stringify(jqXHR));
+        }
+    });
+}
+
+function getClusterList(sessionId, apiEntryPoint, dcName) {
+    var clusters = new Array();
+    var clustersUrl = apiEntryPoint + "/clusters?search=" + 
encodeURIComponent('datacenter=' + dcName);
+    jQuery.ajax({ 
+        type: "GET",
+        dataType: "json",
+        url: clustersUrl,
+        headers: { 'engineSessionId' : sessionId, 'Prefer' : 'persistent-auth' 
},
+        success: function(data) {
+            for (var index in data.cluster) {
+                clusters[index] = data.cluster[index].name;
+            }
+            formWindow.updateClusters(clusters);
+        }
+    });
+}
+
+function getTemplateList(sessionId, apiEntryPoint, dcName) {
+    var templates = new Array();
+    var templatesUrl = apiEntryPoint + "/templates?search=" + 
encodeURIComponent('datacenter=' + dcName);
+    jQuery.ajax({ 
+        type: "GET",
+        dataType: "json",
+        url: templatesUrl,
+        headers: { 'engineSessionId' : sessionId, 'Prefer' : 'persistent-auth' 
},
+        success: function(data) {
+            for (var index in data.template) {
+                templates[index] = data.template[index].name;
+            }
+            formWindow.updateTemplates(templates);
+        }
+    });
+}
+
+function addVm() {
+    formWindow.addVm(restSessionId, config.apiEntryPoint);
+}
+
+    // Customize API options that affect specific features of plugin API
+       api.options({
+               // Configure source origin(s), i.e. protocol://domain:port
+               // from which HTML5 message events will be accepted
+               allowedMessageOrigins: config.allowedOrigins
+       });
+
+       var init = function() {
+               api.addMainTabActionButton('VirtualMachine', 'Create Docker VM',
+            {
+                isEnabled: function() {
+                    return arguments.length == 0;
+                           },
+                           onClick: function() {
+                    api.showDialog('Create Docker VM', 'launch-docker',
+                                   
'/ovirt-engine/webadmin/plugin/docker/launch-docker-dialog.html',
+                                   '540px', '500px',
+                                   {
+                                       buttons: [
+                                                    {
+                                                        label: 'Cancel',
+                                                        onClick: function() {
+                                                            
api.closeDialog('launch-docker');
+                                                        }
+                                                    },
+                                                    {
+                                                        label: 'OK',
+                                                        onClick: addVm
+                                                    }
+                                                ],
+                                                resizeEnabled: true,
+                                                closeIconVisible: true,
+                                                closeOnEscKey: true
+                                   });
+                           }
+                   }
+        );
+       };
+
+       // Register event handler functions for later invocation by UI plugin 
infrastructure
+       api.register({
+
+        // Called by the infrastructure as part of plugin initialization,
+        // will be called just once during the lifetime of a plugin
+               UiInit: function() {
+                       init();
+               },
+        RestApiSessionAcquired: function(sessionId) {
+            restSessionId = sessionId;
+            alert(restSessionId);
+        },
+        MessageReceived: function(data, sourceWindow) {
+            // If we get here, we already passed allowed source origin check
+            var eventDataPair = data.split('-');
+            switch (eventDataPair[0]) {
+                case 'GetDataCenters':
+                    formWindow = sourceWindow;
+                    getDCList(restSessionId, config.apiEntryPoint);
+                    break;
+                case 'GetClustersAndTemplates':
+                    formWindow = sourceWindow;
+                    getClusterList(restSessionId, config.apiEntryPoint, 
eventDataPair[1]);
+                    getTemplateList(restSessionId, config.apiEntryPoint, 
eventDataPair[1]);
+                    break;
+                case 'GetImages':
+                    formWindow = sourceWindow;
+                    setImageList(config.dockerImages);
+                    break;
+                case 'CloseDialog':
+                    api.closeDialog('launch-docker');
+                    break;
+            }
+        },
+       });
+
+       // Tell plugin infrastructure that we are good to go, expect UiInit 
callback
+       api.ready();
+
+</script>
+
+</body>
+</html>
diff --git a/docker-plugin/docker-resources/progress.gif 
b/docker-plugin/docker-resources/progress.gif
new file mode 100644
index 0000000..d5fc984
--- /dev/null
+++ b/docker-plugin/docker-resources/progress.gif
Binary files differ
diff --git a/docker-plugin/docker.json b/docker-plugin/docker.json
new file mode 100644
index 0000000..f97d663
--- /dev/null
+++ b/docker-plugin/docker.json
@@ -0,0 +1,21 @@
+{
+
+    // Unique name of the plugin
+    "name": "docker",
+
+    // URL of plugin host page that bootstraps plugin code
+    // This URL maps to $ENGINE_USR/ui-plugins/docker-resources/plugin.html
+    "url": "plugin/docker/plugin.html",
+
+    // Path to plugin resource files relative to descriptor
+    // This path maps to $ENGINE_USR/ui-plugins/space-shooter-resources
+    "resourcePath": "docker-resources",
+
+    // Default configuration associated with the plugin
+    "config": {
+        "allowedOrigins": ["http://localhost:8700";],
+        "apiEntryPoint": "http://localhost:8700/ovirt-engine/api";,
+        "dockerImages": ["fedora", "ubuntu", "cirros", 
"goldmann/wildfly-cluster:front-end"]
+    }
+
+}


-- 
To view, visit http://gerrit.ovirt.org/25814
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I6fbeecd38207815399f1e4afc86dd57465a010a9
Gerrit-PatchSet: 1
Gerrit-Project: samples-uiplugins
Gerrit-Branch: master
Gerrit-Owner: Oved Ourfali <oourf...@redhat.com>
_______________________________________________
Engine-patches mailing list
Engine-patches@ovirt.org
http://lists.ovirt.org/mailman/listinfo/engine-patches

Reply via email to