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 @@
</div>
</div>
<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>
</fieldset>
</form>
......@@ -67,69 +67,64 @@
<script id="auth" type="text/html">
<article>
<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>
</script>
<script id="instance" type="text/html">
{{! Service page template }}
<article>
<form class="form-horizontal">
<fieldset>
<legend>
{{ title }}
<div class="pull-right">
<div class="btn">Bang</div>
<div class="btn">Destroy</div>
<div class="btn">Certificates</div>
</div>
</legend>
<div class="control-group">
<label class="control-label">Reference</label>
<div class="controls">
<span class="uneditable-input">{{ title }}</span>
</div>
</div>
<div class="control-group">
<label class="control-label">Software release</label>
<div class="controls">
<span class="uneditable-input">{{ software_release }}</span>
</div>
</div>
<div class="control-group">
<label class="control-label">Software type</label>
<div class="controls">
<span class="uneditable-input">{{ software_type }}</span>
</div>
</div>
<div class="control-group">
<label class="control-label">Status</label>
<div class="controls">
<div class="btn-group">
<button class="btn" id="stopInstance">Stop</button>
<button class="btn btn-success disabled" id="startInstance">Started</button>
</div>
</div>
</div>
</fieldset>
</form>
<form class="form-horizontal" id="instance-form">
<fieldset>
<legend>
{{ title }}
<div class="pull-right">
<div class="btn">Bang</div>
<div class="btn">Destroy</div>
<div class="btn">Certificates</div>
</div>
</legend>
<div class="control-group">
<label class="control-label">Reference</label>
<div class="controls">
<input name="title" type="text" value="{{ title }}" readOnly></input>
</div>
</div>
<div class="control-group">
<label class="control-label">Software release</label>
<div class="controls">
<input type="text" name="software_release" value="{{ software_release }}" readOnly></input>
</div>
</div>
<div class="control-group">
<label class="control-label">Software type</label>
<div class="controls">
<input type="text" name="software_type" value="{{ software_type }}"></input>
</div>
</div>
<div class="control-group">
<label class="control-label">Status</label>
<div class="controls" id="instanceStatus">
{{{ status }}}
</div>
</div>
</fieldset>
<table class="table">
<caption>Connection parameters</caption>
{{# connection}}
<tr><td>key</td><td>{{ key }}</td></tr>
<tr><td>{{.}}</td><td>{{ key }}</td></tr>
{{/ connection}}
</table>
<form>
<fieldset>
<legend>
<fieldset>
<legend>
Parameter XML
<div class="pull-right">
<button type="submit" class="btn btn-primary">Save changes</button>
<button class="btn">Cancel</button>
</div>
</legend>
</legend>
<textarea name="xml" style="width: 98%; height: 110px;">
<?xml version="1.0" encoding="utf-8"?>
<instance>
......@@ -137,10 +132,31 @@
<parameter id="nbd_port">1024</parameter>
</instance>
</textarea>
</fieldset>
</fieldset>
</form>
</article>
</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">
<form class="form-inline">
......@@ -157,7 +173,7 @@
<div class="alert alert-{{ state }}">
<a class="close" data-dismiss="alert" href="#">×</a>
<h4 class="alert-heading">{{ state }} - {{ date }}</h4>
{{ message }}
{{ message }}
</div>
</script>
</head>
......@@ -222,6 +238,6 @@
<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/urlHandler.js"></script>
<script type="text/javascript" src="static/js/form.js"></script>
<script type="text/javascript" src="static/js/core.js"></script>
</body>
</html>
......@@ -28,7 +28,7 @@ var comp1 = {computer_id: "COMP-1",
var inst0 =
{instance_id: "INST-0",
status: "start",
status: "start_requested",
software_release: "http://example.com/example.cfg",
software_type: "type_provided_by_the_software",
slave: "False",
......@@ -49,7 +49,7 @@ var inst0 =
var inst1 =
{instance_id: "INST-1",
status: "start",
status: "stop_requested",
software_release: "http://example.com/example.cfg",
software_type: "type_provided_by_the_software",
slave: "False",
......@@ -71,11 +71,11 @@ var inst1 =
var fakeserver = sinon.fakeServer.create();
// Get instance
fakeserver.respondWith("GET", "/instance/200",[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/stop",[200, {"Content-Type":"application/json; charset=utf-8"}, JSON.stringify(inst0)]);
fakeserver.respondWith("GET", "/instance/start",[200, {"Content-Type":"application/json; charset=utf-8"}, JSON.stringify(inst1)]);
// Get instance FAIL
fakeserver.respondWith("GET", "/instance/408",[408, {"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/start",[200, {"Content-Type":"application/json; charset=utf-8"}, "NOT FOUND"]);
fakeserver.respondWith("GET", "/instance/stop",[200, {"Content-Type":"application/json; charset=utf-8"}, "NEED AUTH"]);
var tmp = $.ajax;
$.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 @@
*/
// Hash parser utility
/**
* @param {String} hashTag hashTag.
* @return {String} a clean hashtag.
*/
$.parseHash = function(hashTag) {
var tokenized = $.extractAuth(hashTag);
if (tokenized) {
$.publish('auth', tokenized);
location.hash = hashTag.split("&")[0]
return location.hash
}
return hashTag
var tokenized = $.extractAuth(hashTag);
if (tokenized) {
$.publish('auth', tokenized);
location.hash = hashTag.split('&')[0];
return location.hash;
}
return hashTag;
};
$.extractAuth = function (hashTag) {
var del = hashTag.indexOf('&');
if (del != -1) {
var splitted = hashTag.substring(del+1).split('&');
var result = {};
for (p in splitted) {
var s = splitted[p].split('=');
result[s[0]] = s[1];
}
return result;
}
return false;
var del = hashTag.indexOf('&');
if (del != -1) {
var splitted = hashTag.substring(del + 1).split('&');
var result = {};
for (p in splitted) {
var s = splitted[p].split('=');
result[s[0]] = s[1];
}
return result;
}
return false;
};
$.genHash = function(url) {
return '/' + url.join('/');
return '/' + url.join('/');
};
/* Pub / Sub Pattern
WARNING
What's happening when we destroy a DOM object subscribed ?
WARNING
What's happening when we destroy a DOM object subscribed ?
*/
var o = $({});
$.subscribe = function() {
o.on.apply(o, arguments);
o.on.apply(o, arguments);
};
$.unsubscribe = function() {
o.off.apply(o, arguments);
o.off.apply(o, arguments);
};
$.publish = function() {
o.trigger.apply(o, arguments);
o.trigger.apply(o, arguments);
};
// Event Handlers
$.hashHandler = function(){ $.publish("urlChange", $.parseHash(window.location.hash.substr(1))); };
$.redirectHandler = function(event, url){ window.location.hash = $.genHash(url); };
$.hashHandler = function(){ $.publish('urlChange', $.parseHash(window.location.hash.substr(1))); };
$.redirectHandler = function(e, url){ window.location.hash = $.genHash([url]); };
// redirections manager
$.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