Commit 548e90a6 authored by Thomas Lechauve's avatar Thomas Lechauve

New feature: Can start or stop an instance

You can now change the state of an instance with buttons.
There is also an autorefresh every 30 seconds to update all parameters
in the instance's form.
parent 7f1b6510
This diff is collapsed.
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,7 @@
</div> </div>
</div> </div>
<div class="form-actions"> <div class="form-actions">
<button type="submit" class="btn btn-primary" name="submit">Request</button> <button type="submit" class="btn btn-primary" name="submit">Request</button>
</div> </div>
</fieldset> </fieldset>
</form> </form>
...@@ -67,69 +67,64 @@ ...@@ -67,69 +67,64 @@
<script id="auth" type="text/html"> <script id="auth" type="text/html">
<article> <article>
<p>Authentification needed. Are you agree to be redirect to login ?</p> <p>Authentification needed. Are you agree to be redirect to login ?</p>
<a href="{{ host }}?response_type=token&client_id={{ client_id }}&redirect_uri={{ redirect }}">Redirect</a> <a href="{{ host }}?response_type=token&client_id={{ client_id }}&redirect_uri={{ redirect }}">Redirect</a>
</article> </article>
</script> </script>
<script id="instance" type="text/html"> <script id="instance" type="text/html">
{{! Service page template }} {{! Service page template }}
<article> <article>
<form class="form-horizontal"> <form class="form-horizontal" id="instance-form">
<fieldset> <fieldset>
<legend> <legend>
{{ title }} {{ title }}
<div class="pull-right"> <div class="pull-right">
<div class="btn">Bang</div> <div class="btn">Bang</div>
<div class="btn">Destroy</div> <div class="btn">Destroy</div>
<div class="btn">Certificates</div> <div class="btn">Certificates</div>
</div> </div>
</legend> </legend>
<div class="control-group"> <div class="control-group">
<label class="control-label">Reference</label> <label class="control-label">Reference</label>
<div class="controls"> <div class="controls">
<span class="uneditable-input">{{ title }}</span> <input name="title" type="text" value="{{ title }}" readOnly></input>
</div> </div>
</div> </div>
<div class="control-group"> <div class="control-group">
<label class="control-label">Software release</label> <label class="control-label">Software release</label>
<div class="controls"> <div class="controls">
<span class="uneditable-input">{{ software_release }}</span> <input type="text" name="software_release" value="{{ software_release }}" readOnly></input>
</div> </div>
</div> </div>
<div class="control-group"> <div class="control-group">
<label class="control-label">Software type</label> <label class="control-label">Software type</label>
<div class="controls"> <div class="controls">
<span class="uneditable-input">{{ software_type }}</span> <input type="text" name="software_type" value="{{ software_type }}"></input>
</div> </div>
</div> </div>
<div class="control-group"> <div class="control-group">
<label class="control-label">Status</label> <label class="control-label">Status</label>
<div class="controls"> <div class="controls" id="instanceStatus">
<div class="btn-group"> {{{ status }}}
<button class="btn" id="stopInstance">Stop</button> </div>
<button class="btn btn-success disabled" id="startInstance">Started</button> </div>
</div> </fieldset>
</div>
</div>
</fieldset>
</form>
<table class="table"> <table class="table">
<caption>Connection parameters</caption> <caption>Connection parameters</caption>
{{# connection}} {{# connection}}
<tr><td>key</td><td>{{ key }}</td></tr> <tr><td>{{.}}</td><td>{{ key }}</td></tr>
{{/ connection}} {{/ connection}}
</table> </table>
<form> <fieldset>
<fieldset> <legend>
<legend>
Parameter XML Parameter XML
<div class="pull-right"> <div class="pull-right">
<button type="submit" class="btn btn-primary">Save changes</button> <button type="submit" class="btn btn-primary">Save changes</button>
<button class="btn">Cancel</button> <button class="btn">Cancel</button>
</div> </div>
</legend> </legend>
<textarea name="xml" style="width: 98%; height: 110px;"> <textarea name="xml" style="width: 98%; height: 110px;">
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<instance> <instance>
...@@ -137,10 +132,31 @@ ...@@ -137,10 +132,31 @@
<parameter id="nbd_port">1024</parameter> <parameter id="nbd_port">1024</parameter>
</instance> </instance>
</textarea> </textarea>
</fieldset> </fieldset>
</form> </form>
</article> </article>
</script> </script>
<script id="instance.stop_requested" type="text/html">
<div class="btn-group">
<button class="btn btn-danger disabled" disabled="disabled" id="stopInstance">Stopped</button>
<button class="btn" id="startInstance">Start</button>
</div>
</script>
<script id="instance.draft" type="text/html">
<div class="btn-group">
<button class="btn disabled" disabled="disabled">Stop</button>
<button class="btn disabled" disabled="disabled">Start</button>
</div>
</script>
<script id="instance.start_requested" type="text/html">
<div class="btn-group">
<button class="btn" id="stopInstance">Stop</button>
<button class="btn btn-success disabled" disabled="disabled" id="startInstance">Started</button>
</div>
</script>
<script id="simple-form" type="text/html"> <script id="simple-form" type="text/html">
<form class="form-inline"> <form class="form-inline">
...@@ -157,7 +173,7 @@ ...@@ -157,7 +173,7 @@
<div class="alert alert-{{ state }}"> <div class="alert alert-{{ state }}">
<a class="close" data-dismiss="alert" href="#">×</a> <a class="close" data-dismiss="alert" href="#">×</a>
<h4 class="alert-heading">{{ state }} - {{ date }}</h4> <h4 class="alert-heading">{{ state }} - {{ date }}</h4>
{{ message }} {{ message }}
</div> </div>
</script> </script>
</head> </head>
...@@ -222,6 +238,6 @@ ...@@ -222,6 +238,6 @@
<script type="text/javascript" src="static/js/jquery.slapos.js"></script> <script type="text/javascript" src="static/js/jquery.slapos.js"></script>
<!--<script type="text/javascript" src="static/js/fake.js"></script>--> <!--<script type="text/javascript" src="static/js/fake.js"></script>-->
<script type="text/javascript" src="static/js/urlHandler.js"></script> <script type="text/javascript" src="static/js/urlHandler.js"></script>
<script type="text/javascript" src="static/js/form.js"></script> <script type="text/javascript" src="static/js/core.js"></script>
</body> </body>
</html> </html>
...@@ -28,7 +28,7 @@ var comp1 = {computer_id: "COMP-1", ...@@ -28,7 +28,7 @@ var comp1 = {computer_id: "COMP-1",
var inst0 = var inst0 =
{instance_id: "INST-0", {instance_id: "INST-0",
status: "start", status: "start_requested",
software_release: "http://example.com/example.cfg", software_release: "http://example.com/example.cfg",
software_type: "type_provided_by_the_software", software_type: "type_provided_by_the_software",
slave: "False", slave: "False",
...@@ -49,7 +49,7 @@ var inst0 = ...@@ -49,7 +49,7 @@ var inst0 =
var inst1 = var inst1 =
{instance_id: "INST-1", {instance_id: "INST-1",
status: "start", status: "stop_requested",
software_release: "http://example.com/example.cfg", software_release: "http://example.com/example.cfg",
software_type: "type_provided_by_the_software", software_type: "type_provided_by_the_software",
slave: "False", slave: "False",
...@@ -71,11 +71,11 @@ var inst1 = ...@@ -71,11 +71,11 @@ var inst1 =
var fakeserver = sinon.fakeServer.create(); var fakeserver = sinon.fakeServer.create();
// Get instance // Get instance
fakeserver.respondWith("GET", "/instance/200",[200, {"Content-Type":"application/json; charset=utf-8"}, JSON.stringify(inst0)]); fakeserver.respondWith("GET", "/instance/stop",[200, {"Content-Type":"application/json; charset=utf-8"}, JSON.stringify(inst0)]);
fakeserver.respondWith("GET", "/instance/201",[200, {"Content-Type":"application/json; charset=utf-8"}, JSON.stringify(inst1)]); fakeserver.respondWith("GET", "/instance/start",[200, {"Content-Type":"application/json; charset=utf-8"}, JSON.stringify(inst1)]);
// Get instance FAIL // Get instance FAIL
fakeserver.respondWith("GET", "/instance/408",[408, {"Content-Type":"application/json; charset=utf-8"}, "NOT FOUND"]); fakeserver.respondWith("GET", "/instance/start",[200, {"Content-Type":"application/json; charset=utf-8"}, "NOT FOUND"]);
fakeserver.respondWith("POST", "/instance",[401, {"Content-Type":"application/json; charset=utf-8"}, "NEED AUTH"]); fakeserver.respondWith("GET", "/instance/stop",[200, {"Content-Type":"application/json; charset=utf-8"}, "NEED AUTH"]);
var tmp = $.ajax; var tmp = $.ajax;
$.ajax = function(url, options){ $.ajax = function(url, options){
......
/**
* NEXEDI
* Author: Thomas Lechauve
* Date: 4/17/12
*/
(function($) {
var routes = {
"/instance" : "requestInstance",
"/instance/:url" : "showInstance",
"/computers" : "listComputers",
"/instances" : "listInstances",
"/invoices" : "listInvoices"
}
var router = function(e, d){
var $this = $(this);
$.each(routes, function(pattern, callback){
pattern = pattern.replace(/:\w+/g, '([^\/]+)');
var regex = new RegExp('^' + pattern + '$');
var result = regex.exec(d);
if (result) {
result.shift();
methods[callback].apply($this, result);
}
});
}
var getDate = function () {
var today = new Date();
return [today.getFullYear(), today.getMonth(), today.getDay()].join('/')
+ ' ' + [today.getHours(), today.getMinutes(), today.getSeconds()].join(':');
};
var substractLists = function(l1, l2){
var newList = [];
$.each(l2, function(){
if ($.inArray(""+this, l1) == -1) {
newList.push(""+this);
}
});
return newList;
};
var redirect = function(){
$(this).vifib('render', 'auth', {
'host':'http://10.8.2.34:12002/erp5/web_site_module/hosting/request-access-token',
'client_id': 'client',
'redirect':escape(window.location.href)
})
};
var payment = function(jqxhr){
var message = $.parseJSON(jqXHR.responseText).error
$(this).vifib('popup', message, "information");
};
var notFound = function(jqxhr){
var message = $.parseJSON(jqXHR.responseText).error
$(this).vifib('popup', message);
};
var serverError = function(jqxhr){
var message = $.parseJSON(jqxhr.responseText).error
$(this).vifib('popup', message);
};
var spinOptions = {color: "#FFF", lines:9, length:3, width:2, radius:6, rotate:0, trail:36, speed:1.0};
var methods = {
init: function() {
// Initialize slapos in this context
$(this).slapos();
var $this = $(this);
// Bind Loading content
$("#loading").ajaxStart(function(){
$(this).spin(spinOptions);
}).ajaxStop(function(){
$(this).spin(false);
});
// Bind to urlChange event
return this.each(function(){
$.subscribe("urlChange", function(e, d){
router.call($this, e, d);
});
$.subscribe("auth", function(e, d){
$(this).vifib("authenticate", d);
});
});
},
genInstanceUrl: function(uri){
return location.href.split('#')[0] + "#/instance/" + encodeURIComponent(uri)
},
extractInstanceURIFromUrl: function () {
return decodeURIComponent($(this).attr('href').split('/').pop())
},
authenticate: function(data) {
for (var d in data) {
if (data.hasOwnProperty(d)) {
$(this).slapos('store', d, data[d]);
}
}
},
refresh: function(method, interval, eventName){
eventName = eventName || 'ajaxStop';
var $this = $(this);
$(this).one(eventName, function(){
var id = setInterval(function(){
method.call($this);
}, interval * 1000);
$.subscribe('urlChange', function(e, d){
clearInterval(id);
})
});
},
showInstance: function (uri) {
var statusCode = {
401: redirect,
402: payment,
404: notFound,
500: serverError
};
$(this).slapos('instanceInfo', uri, {
success: function(infos){
$(this).vifib('render', 'instance', infos);
},
statusCode: statusCode
});
},
getCurrentList: function () {
var list = [];
$.each($(this).find('a'), function () {
list.push($(this).vifib('extractInstanceURIFromUrl'));
});
return list;
},
listComputers: function(){
$(this).vifib('render', 'server.list');
},
refreshRowInstance: function(){
return this.each(function(){
var url = $(this).find('a').vifib('extractInstanceURIFromUrl');
$(this).vifib('fillRowInstance', url);
});
},
fillRowInstance: function(url){
return this.each(function(){
$(this).slapos('instanceInfo', url, {
success: function(instance){
$.extend(instance, {'url': methods.genInstanceUrl(url)});
$(this).vifib('render', 'instance.list.elem', instance);
}
});
});
},
refreshListInstance: function () {
var currentList = $(this).vifib('getCurrentList');
$(this).slapos('instanceList', {
success: function (data) {
var $this = $(this);
var newList = substractLists(currentList, data['list']);
var oldList = substractLists(data['list'], currentList);
$.each(newList, function(){
var url = this+"";
var row = $("<tr></tr>").vifib('fillRowInstance', url);
$this.prepend(row);
});
console.log("newList")
console.log(newList)
console.log("oldList")
console.log(oldList)
},
});
},
listInstances: function(){
var $this = $(this);
var statusCode = {
401: redirect,
402: payment,
404: notFound,
500: serverError,
503: serverError
};
var table = $(this).vifib('render', 'instance.list').find("#instance-table");
table.vifib('refresh', methods.refreshListInstance, 30);
$(this).slapos('instanceList', {
success: function(data){
$.each(data['list'], function(){
var url = this+"";
var row = $("<tr></tr>").vifib('fillRowInstance', url);
row.vifib('refresh', methods.refreshRowInstance, 30);
table.append(row);
});
}, statusCode: statusCode});
},
listInvoices: function(){
$(this).vifib('render', 'invoice.list');
},
instanceInfo: function (url, callback) {
$(this).slapos('instanceInfo', {
success: callback, url: url
});
},
requestInstance: function() {
$(this).vifib('render', 'form.new.instance');
var data = {};
$(this).find("form").submit(function(){
$(this).find('input').serializeArray().map(function(elem){
data[elem["name"]] = elem["value"];
});
$(this).vifib('requestAsking', data);
return false;
});
},
requestAsking: function(data){
var statusCode = {
401: redirect,
402: payment,
404: notFound,
500: serverError
};
var instance = {
software_type: "type_provided_by_the_software",
slave: false,
status: "started",
parameter: {
Custom1: "one string",
Custom2: "one float",
Custom3: ["abc", "def"],
},
sla: {
computer_id: "COMP-0",
}
};
$.extend(instance, data);
var args = {
statusCode: statusCode,
data: instance,
success: function (response) {
console.log(response);
}
};
return this.each(function(){
$(this).slapos('instanceRequest', args);
});
},
popup: function(message, state) {
state = state || 'error';
return this.each(function(){
$(this).prepend(ich['error'](
{'message': message, 'state': state, 'date': getDate()}
, true))
})
},
render: function(template, data){
return this.each(function(){
$(this).html(ich[template](data, true));
});
},
renderAppend: function(template, data){
$(this).append(ich[template](data, true));
},
renderPrepend: function(template, data){
$(this).prepend(ich[template](data, true));
}
};
$.fn.vifib = function(method){
if ( methods[method] ) {
return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || ! method ) {
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + method + ' does not exist on jQuery.vifib' );
}
};
})(jQuery);
$("#main").vifib();
This diff is collapsed.
...@@ -5,53 +5,57 @@ ...@@ -5,53 +5,57 @@
*/ */
// Hash parser utility /**
* @param {String} hashTag hashTag.
* @return {String} a clean hashtag.
*/
$.parseHash = function(hashTag) { $.parseHash = function(hashTag) {
var tokenized = $.extractAuth(hashTag); var tokenized = $.extractAuth(hashTag);
if (tokenized) { if (tokenized) {
$.publish('auth', tokenized); $.publish('auth', tokenized);
location.hash = hashTag.split("&")[0] location.hash = hashTag.split('&')[0];
return location.hash return location.hash;
} }
return hashTag return hashTag;
}; };
$.extractAuth = function (hashTag) { $.extractAuth = function (hashTag) {
var del = hashTag.indexOf('&'); var del = hashTag.indexOf('&');
if (del != -1) { if (del != -1) {
var splitted = hashTag.substring(del+1).split('&'); var splitted = hashTag.substring(del + 1).split('&');
var result = {}; var result = {};
for (p in splitted) { for (p in splitted) {
var s = splitted[p].split('='); var s = splitted[p].split('=');
result[s[0]] = s[1]; result[s[0]] = s[1];
} }
return result; return result;
} }
return false; return false;
}; };
$.genHash = function(url) { $.genHash = function(url) {
return '/' + url.join('/'); return '/' + url.join('/');
}; };
/* Pub / Sub Pattern /* Pub / Sub Pattern
WARNING WARNING
What's happening when we destroy a DOM object subscribed ? What's happening when we destroy a DOM object subscribed ?
*/ */
var o = $({}); var o = $({});
$.subscribe = function() { $.subscribe = function() {
o.on.apply(o, arguments); o.on.apply(o, arguments);
}; };
$.unsubscribe = function() { $.unsubscribe = function() {
o.off.apply(o, arguments); o.off.apply(o, arguments);
}; };
$.publish = function() { $.publish = function() {
o.trigger.apply(o, arguments); o.trigger.apply(o, arguments);
}; };
// Event Handlers // Event Handlers
$.hashHandler = function(){ $.publish("urlChange", $.parseHash(window.location.hash.substr(1))); }; $.hashHandler = function(){ $.publish('urlChange', $.parseHash(window.location.hash.substr(1))); };
$.redirectHandler = function(event, url){ window.location.hash = $.genHash(url); }; $.redirectHandler = function(e, url){ window.location.hash = $.genHash([url]); };
// redirections manager // redirections manager
$.redirect = function(url){ $.publish('redirect', url); }; $.redirect = function(url){ $.publish('redirect', url); };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment