Commit daa69dc0 authored by Cédric de Saint Martin's avatar Cédric de Saint Martin

Merge branch 'slaprunner'

Conflicts:
	CHANGES.txt
parents f209a4f8 a17c7c92
......@@ -5,6 +5,7 @@ import logging.handlers
import os
import sys
import subprocess
import hashlib
class Parser(OptionParser):
"""
......@@ -121,5 +122,11 @@ def serve(config):
)
if not os.path.exists(workdir):
os.mkdir(workdir)
if not os.path.exists(os.path.join(config.runner_workdir, '.users')):
#set default user and password
salt = "runner81" #to be changed
pwd = hashlib.md5( salt + "insecure" ).hexdigest()
user = "root;"+pwd+";;Slaprunner Administrator"
open(os.path.join(config.runner_workdir, '.users'), 'w').write(user)
app.run(host=config.runner_host, port=int(config.runner_port),
debug=config.debug, threaded=True)
# -*- coding: utf-8 -*-
import slapos.slap
import time
import subprocess
......@@ -21,6 +23,15 @@ class Popen(subprocess.Popen):
self.stdin = None
def cloneRepo(data):
"""Clonne a repository
Args:
data: a dictionnary of parameters to use:
data['path'] is the path of the new project
data['repo'] is the url of the repository to be cloned
data['email'] is the user email
data['user'] is the name of the user
Returns:
a jsonify data"""
workDir = data['path']
if not workDir:
return jsonify(code=0,
......@@ -34,7 +45,7 @@ def cloneRepo(data):
config_writer = repo.config_writer()
config_writer.add_section("user")
if data["user"] != "":
config_writer.set_value("user", "name", data["user"])
config_writer.set_value("user", "name", data["user"].encode("utf-8"))
if data["email"] != "":
config_writer.set_value("user", "email", data["email"])
code = 1
......@@ -45,6 +56,11 @@ def cloneRepo(data):
return jsonify(code=code, result=json)
def gitStatus(project):
"""Run git status and return status of specified project folder
Args:
project: path of the projet ti get status
Returns:
a parsed string that contains the result of git status"""
code = 0
json = ""
try:
......@@ -59,6 +75,12 @@ def gitStatus(project):
return jsonify(code=code, result=json, branch=branch, dirty=isdirty)
def switchBranch(project, name):
"""Switch a git branch
Args:
project: directory of the local git repository
name: switch from current branch to `name` branch
Returns:
a jsonify data"""
code = 0
json = ""
try:
......@@ -76,6 +98,13 @@ def switchBranch(project, name):
return jsonify(code=code, result=json)
def addBranch(project, name, onlyCheckout=False):
"""Add new git branch to the repository
Args:
project: directory of the local git repository
name: name of the new branch
onlyCheckout: if True then the branch `name` is created before checkout
Returns:
a jsonify data"""
code = 0
json = ""
try:
......@@ -91,6 +120,7 @@ def addBranch(project, name, onlyCheckout=False):
return jsonify(code=code, result=json)
def getDiff(project):
"""Get git diff for the specified project directory"""
result = ""
try:
repo = Repo(project)
......@@ -102,6 +132,10 @@ def getDiff(project):
return result
def gitPush(project, msg):
"""Commit and Push changes for the specified repository
Args:
project: directory of the local repository
msg: commit message"""
code = 0
json = ""
undo_commit = False
......@@ -143,5 +177,6 @@ def gitPull(project):
return jsonify(code=code, result=result)
def safeResult(result):
"""Parse string and remove credential of the user"""
regex=re.compile("(https:\/\/)([\w\d\._-]+:[\w\d\._-]+)\@([\S]+\s)", re.VERBOSE)
return regex.sub(r'\1\3', result)
\ No newline at end of file
......@@ -10,6 +10,9 @@
#tabContaier textarea {
width:702px;
}
#tabContaier textarea.slap{white-space: pre-wrap;word-wrap: break-word;overflow: hidden;color: #6F6F6F;width:430px; max-height:120px;
resize: none; height:18px;padding:3px;min-height:18px;font-size: 13px;}
#tabContaier textarea.mb_style{width:560px;}
#tabContaier > ul{
overflow:hidden;
height:34px;
......@@ -39,9 +42,9 @@
}
#tabContaier > ul > li a.active{
background:#fbfbfb;
border:1px solid #fff;
border:1px solid #fff;
border-top:0;
border-right:0;
border-right:0;
color:#333;
}
#tabContaier > ul > li:first-child a{border-left:0}
......@@ -55,4 +58,4 @@
}
.tabContents p{
padding:0 0 10px;
}
}
......@@ -4,7 +4,6 @@ blockquote, q {quotes: none;}
blockquote:before, blockquote:after, q:before, q:after {content: none;}
:focus {outline: 0 none;}
img{border:0}
a{
text-decoration: none;
color: #19485C;
......@@ -15,11 +14,12 @@ a:hover {
}
table {
margin: 0;
margin: 0;padding:0;
border-right: none;
border-bottom: none;
font-size: 14px;
background: #fff;
border-spacing:0;
}
td{
padding: 4px;
......@@ -36,16 +36,19 @@ th{
font-weight: normal;
font-size: 18px;
}
table.small th{padding: 4px;font-size: 16px;}
textarea {
width:762px;
font-family: 'Helvetica Neue',Tahoma,Helvetica,Arial,sans-serif;
}
body {
background: url("../images/1307251316-background-stripes.gif") repeat #9C9C9C;
font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif;
background: #2281C1;/*url("../images/1307251316-background-stripes.gif") repeat #9C9C9C;*/
font-family: 'Helvetica Neue',Tahoma,Helvetica,Arial,sans-serif;
color: #000000;
font-size: 13px;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}
#page
......@@ -67,7 +70,7 @@ body {
.block_header{
text-align: left;
padding-left: 25px;
padding-left: 20px;
height: 30px;
}
......@@ -81,7 +84,7 @@ body {
font-weight: normal;
padding-top: 3px;
float: left;
width: 690px;
width: 656px;
height: 22px;
text-align: center;
color: #4c6172;
......@@ -257,11 +260,8 @@ body {
box-shadow: 1px 1px 1px #888888;
}
.button:active {
background-position: 0 top;
position: relative;
top: 1px;
padding: 6px 10px 4px;
box-shadow: 1px 1px 1px #888888;
background: #eee;
color: #000;
}
.focusField{
......@@ -314,7 +314,7 @@ input[type="radio"], input[type="checkbox"]{
#home_box{
background: none;
border: 1px solid #678dad;
border: 2px solid #87B0D4;
padding: 0;
color: #4c6172;
margin: 15px 59px 15px 59px;
......@@ -333,7 +333,7 @@ input[type="radio"], input[type="checkbox"]{
#home_box h2{
font-weight: normal;
font-size: 23px;
color: #4c6172;
color: #475F73;
}
#home_box p{
......@@ -351,7 +351,6 @@ input[type="radio"], input[type="checkbox"]{
.inner_box{
background: none;
margin: 0;
border: 1px solid #CAD4DC;
display: block;
padding: 30px 0 30px 0;
}
......@@ -371,23 +370,24 @@ input[type="radio"], input[type="checkbox"]{
margin: 10px 45px 10px 45px;
padding: 10px;
padding-bottom:15px;
border: #678dad;
width: 530px;
border: 1px solid #678dad;
}
.lmenu:hover, .smenu:hover, .sright_menu:hover{
.smaller{margin-left:10px; width: 350px; float:right}
.smaller p{width: 280px;}
.umenu{display: block;margin: 10px 0 10px 45px; width: 147px; float:left;border: 1px solid #678dad;
padding: 10px;height: 80px;padding-bottom:15px;}
.umenu p{font-size: 13px;float: left;width: 77px;cursor:default;}
.lmenu:hover, .smenu:hover, .sright_menu:hover, .smaller:hover, .umenu:hover{
background: #96b9d7;
}
.smenu{
display: block;
height: 80px;
margin: 0 10px 0 45px;
padding: 10px;
padding-bottom:15px;
border: #678dad;
width: 250px;
border: 1px solid #678dad;
float: left;
......@@ -541,7 +541,6 @@ h2.hight:hover{
overflow: auto;
height: 95px;
padding: 5px;
width: 604px;
background:#fff;
margin-bottom: 10px;
color: #3A494F;
......@@ -580,9 +579,10 @@ a.lshare{
height: 18px;
font-size: 15px;
border: solid 1px #678dad;
color: #4DA0C6;
color: #4DA0C6;
text-align:center;
font-weight:bold;
cursor:pointer;
}
a.lshare:hover{
background:#D9D9D9;
......@@ -593,7 +593,9 @@ a.lshare:hover{
a.lshare:focus{
border:solid 1px #73A6FF;
}
a.no-right-border{border-right:none}
a.no-right-border:hover{border-right:none}
a.no-right-border:focus{border-right:none}
a.lshare img{
margin: 5px;
}
......@@ -635,19 +637,28 @@ a.lshare img{
width: 36px;
height: 26px;
}
#error td.close{
#error td.b_close{
width: 30px;
height:22px;
}
#error p{white-space: pre-wrap; word-wrap: break-word; width: 430px; padding-bottom: 5px;}
.noscroll {
overflow: hidden;
}
.form{padding:10px; padding-left:20px;}
.form label{display:block; float:left; width:150px; padding-top:10px;}
.form input[type=text] ,.form input[type=password] {float:left; width:190px;margin:5px;}
.hiddendiv {display: none;white-space: pre-wrap;min-height: 18px;font-size: 13px;
padding:3px;word-wrap: break-word;width:430px; max-height:120px;font-family: 'Helvetica Neue',Tahoma,Helvetica,Arial,sans-serif;}
.list{background: url(../images/menu_dropdown.png) left center no-repeat; padding-left:10px;}
.slidebox{padding:10px; }
.alert_message{ background: url(../images/alert.png) center no-repeat; height: 26px;}
.error_message{ background: url(../images/exit.png) center no-repeat; height: 26px;}
.confirm_message{ background: url(../images/confirm.png) center no-repeat; height: 26px;}
.info_message{ background: url(../images/info.png) center no-repeat; height: 26px;}
#error p{ font-size: 13px; color: #4A131F;}
#pClose{background:url(../images/close.png) no-repeat 0px 0px; display:block; width:22px; height:22px; cursor:pointer}
#pClose:hover{background:url(../images/close_hover.png) no-repeat 0px 0px;}
#pClose, .close{background:url(../images/close.png) no-repeat 0px 0px; display:block; width:22px; height:22px; cursor:pointer}
#pClose:hover, .close:hover{background:url(../images/close_hover.png) no-repeat 0px 0px;}
.md5sum {margin:10px; font-size:15px;}
.title{background: #e4e4e4; width: 100%; height: 25px; padding-top:2px; text-indent: 5px; color: #737373; text-shadow: 0px 1px #FFF;}
.menu-box-left{float:left; width: 120px; font-size:14px; background: #e4e4e4; padding:5px 0 5px 5px; margin-top:10px;
......@@ -656,6 +667,7 @@ a.lshare img{
margin-top:10px; box-shadow: 1px 1px 1px #888888;}
.menu-box-left ul{margin:0px; list-style:none;}
.menu-box-left li{padding: 2px; padding-left:10px; padding-right:10px; text-shadow: 0px 1px #fff;border-bottom:1px solid #fff;}
.menu-box-left li label, .menu-box-left li input[type=radio] { cursor:pointer}
.menu-box-left li:hover{background:#F0F2F2;}
.menu-box-left li.checked{background:#fff;}
.menu-box-right h2{text-align:center}
......@@ -679,7 +691,8 @@ a.lshare img{
.popup li{border-bottom: 1px dashed #666666; padding:5px; padding-top:5px;}
.popup-value{display:none;}
textarea.parameter {border: solid 1px #678dad; color: #666666; height:110px;}
.link{color:#fff; font-weight:bold; text-decoration:none}
.link:hover{color: #19485C;}
input[type=radio] {
}
......@@ -689,3 +702,12 @@ input[type=radio]:checked {
input[type=radio]:hover {
box-shadow: 0px 1px 3px #F0F1F2;
}
/* Login Css region *******/
#login-page{width:429px; height:236px; margin:130px auto 0px; background:url(../images/loginBox.png) no-repeat;
padding:10px; font-size:14px; color:#03406A}
#login-page h2{color:#fff; font-size:26px; font-weight:normal; text-indent:50px;}
.login-content{margin:10px; margin-top:40px; margin-bottom:0; height:90px;}
.login-button{width:140px; margin:0 auto;}
.login-element{float:left; min-width:120px;}
.login-label{padding:5px; font-size:16px;}
.login-input{width:220px;}
\ No newline at end of file
$(document).ready(function(){
$(".tabContents").hide(); // Hide all tab content divs by default
var hashes = window.location.href.split('#');
if (hashes.length == 2){
$(".tabContents").hide(); // Hide all tab content divs by default
var hashes = window.location.href.split('#');
var fromheight = 0;
var previoustab = null;
if (hashes.length == 2 && hashes[1] != ""){
$("#tabContaier>ul li").each(function() {
var $tab = $(this).find("a");
if($tab.hasClass("active")) $tab.removeClass("active");
if($tab.hasClass("active")){
$tab.removeClass("active");
}
if ($tab.attr("href") == "#"+hashes[1]){
$tab.addClass("active");
$("#"+hashes[1]).show();
previoustab = "#"+hashes[1];
}
//alert($(this).attr("href"));
});
}
else{$(".tabContents:first").show();} // Show the first div of tab content by default
else{$(".tabContents:first").show(); previoustab = ".tabContents:first";} // Show the first div of tab content by default
$("#tabContaier ul li a").click(function(){ //Fire the click event
if($(this).hasClass('active')){
return;
}
fromheight = $(previoustab).height();
var activeTab = $(this).attr("href"); // Catch the click link
$("#tabContaier .tabDetails").css("height", $("#tabContaier .tabDetails").height());
$("#tabContaier ul li a").removeClass("active"); // Remove pre-highlighted link
$(this).addClass("active"); // set clicked link to highlight state
$(".tabContents").hide(); // hide currently visible tab content div
$(activeTab).fadeIn(); // show the target tab content div by matching clicked link.
var diff = fromheight - $(activeTab).height();
if (diff > 0){$("#tabContaier .tabDetails").animate({height: '-=' + diff + 'px'}, 850, 'swing', function() {
$("#tabContaier .tabDetails").css("height", "");
});}
else{diff = -1*diff; $("#tabContaier .tabDetails").animate({height: '+=' + diff + 'px'}, 850, 'swing', function() {
$("#tabContaier .tabDetails").css("height", "");
});}
previoustab = activeTab;
$("#tabContaier .tabDetails").css("height", $("#tabContaier .tabDetails").height());
});
});
\ No newline at end of file
......@@ -3,47 +3,67 @@
//
(function ($, document, window) {
var isShow = null;
var showDelayTimer = null;
$.extend($.fn, {
Popup: function(msg, option) {
var h;
if (option.type == undefined) option.type = "info";
if (option.closebtn == undefined) option.closebtn = false;
if (option.duration == undefined) option.duration = 0;
if (option.load == undefined) option.load = false;
$box = $(this);
$box.empty();
$box.css('top','-1000px');
$box.show();
$box.append('<div><table id="bcontent"><tr>' +
'<td valign="middle" class="logo ' + option.type + '_message"></td>' +
'<td valign="middle"><p>' + msg + '</p></td>' +
'<td valign="middle" class="close"><span id="pClose"></span></td></tr></table></div>');
$(window).scroll(function(){
$box.animate({top:$(window).scrollTop()+"px" },{queue: false, duration: 350});
});
var h = $("#bcontent").height()+5;
$("#pClose").bind("click", function() {
close();
});
if(option.load){
$(window).load(function(){
$box.css('top', + ($(window).scrollTop() - h) +'px');
$box.animate({ top:"+=" + h + "px" }, "slow");
});
$box = $(this);
if(showDelayTimer){clearTimeout(showDelayTimer);}
if(isShow){
$box.fadeOut('normal', function() {
setupBox();
});
}
else{
$box.css('top', + ($(window).scrollTop() - h) +'px');
$box.animate({ top:"+=" + h + "px" }, "slow");
else{setupBox();}
function setupBox(){
$box.empty();
$box.css('top','-1000px');
$box.show();
$box.append('<div><table id="bcontent"><tr>' +
'<td valign="middle" class="logo ' + option.type + '_message"></td>' +
'<td valign="middle"><p>' + msg + '</p></td>' +
'<td valign="middle" class="b_close"><span id="pClose"></span></td></tr></table></div>');
$(window).scroll(function(){
$box.animate({top:$(window).scrollTop()+"px" },{queue: false, duration: 350});
});
h = $("#bcontent").height()+5;
$("#pClose").bind("click", function() {
close();
});
showBox();
if(option.duration != 0){
showDelayTimer = setTimeout(function(){
showDelayTimer = null;
close();
}, option.duration);
}
}
if(option.duration != 0){
setTimeout(function(){
close();
}, option.duration);
function showBox(){
if(option.load){
$(window).load(function(){
$box.css('top', + ($(window).scrollTop() - h) +'px');
$box.animate({ top:"+=" + h + "px" }, "slow");
isShow = true;
});
}
else{
$box.css('top', + ($(window).scrollTop() - h) +'px');
$box.animate({ top:"+=" + h + "px" }, "slow");
isShow = true;
}
}
function close(){
$box.animate({ top:"-=" + h + "px" }, "slow", function(){
$box.fadeOut("normal");
});
$box.animate({ top:"-=" + h + "px" }, "slow", function(){
$box.fadeOut("normal", function() {
isShow = false;
});
});
}
}
});
});
}(jQuery, document, this));
\ No newline at end of file
$(document).ready( function() {
var send = false;
$("#update").click(function(){
var haspwd = false;
if($("input#username").val() === "" || !$("input#username").val().match(/^[\w\d\._-]+$/)){
$("#error").Popup("Invalid user name. Please check it!", {type:'alert', duration:3000});
return false;
}
if($("input#name").val() === ""){
$("#error").Popup("Please enter your name and surname!", {type:'alert', duration:3000});
return false;
}
if(!$("input#email").val().match(/^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/)){
$("#error").Popup("Please enter a valid email adress!", {type:'alert', duration:3000});
return false;
}
if($("input#password").val() !== ""){
if($("input#password").val() === "" || !$("input#password").val().match(/^[\w\d\._-]+$/)){
$("#error").Popup("Please enter your new password!", {type:'alert', duration:3000});
return false;
}
if($("input#password").val() !== $("input#cpassword").val()){
$("#error").Popup("your password does not match!", {type:'alert', duration:3000});
return false;
}
haspwd = true;
}
if(send) return false;
send = true;
$.ajax({
type: "POST",
url: $SCRIPT_ROOT + '/updateAccount',
data: {name: $("input#name").val(), username:$("input#username").val(), email:$("input#email").val(),
password:((haspwd) ? $("input#password").val():"")},
success: function(data){
if(data.code ==1){
$("#error").Popup("Your account informations has been saved!", {type:'confirm', duration:3000});
}
else{
$("#error").Popup(data.result, {type:'error', duration:5000});
}
send = false;
},
error:function(){send = false;}
});
return false;
});
});
\ No newline at end of file
/*Common javascript function*/
String.prototype.toHtmlChar = function(){
c = {'<':'&lt;', '>':'&gt;', '&':'&amp;', '"':'&quot;', "'":'&#039;',
var c = {'<':'&lt;', '>':'&gt;', '&':'&amp;', '"':'&quot;', "'":'&#039;',
'#':'&#035;' };
return this.replace( /[<&>'"#]/g, function(s) { return c[s]; } );
}
......@@ -8,23 +8,50 @@ String.prototype.trim = function () {
return this.replace(/^\s*/, "").replace(/\s*$/, "");
}
/**************************/
$(document).ready(function() {
$('input[type="text"]').addClass("idleField");
$('input[type="text"]').focus(function() {
$(this).removeClass("idleField").addClass("focusField");
if (this.value == this.defaultValue){
this.value = '';
}
if(this.value != this.defaultValue){
this.select();
}
});
$('input[type="text"]').blur(function() {
$(this).removeClass("focusField").addClass("idleField");
if ($.trim(this.value) == ''){
this.value = (this.defaultValue ? this.defaultValue : '');
}
});
});
\ No newline at end of file
/****************************************/
function setInput($elt) {
if(!$elt){var $elt = $('input[type="text"], input[type="password"]');}
$elt.addClass("idleField");
$elt.focus(function() {
$(this).removeClass("idleField").addClass("focusField");
if (this.value == this.defaultValue){
this.value = '';
}
if(this.value != this.defaultValue){
this.select();
}
});
$elt.blur(function() {
$(this).removeClass("focusField").addClass("idleField");
if ($.trim(this.value) == ''){
this.value = (this.defaultValue ? this.defaultValue : '');
}
});
}
/**************************/
(function ($, document, window) {
$.extend($.fn, {
slideBox: function(state) {
if (!state) state = "hide";
var header = $("#"+$(this).attr('id')+">h2");
var box = $("#"+$(this).attr('id')+">div");
header.addClass(state);
if(state=="hide"){box.css('display', 'none');}
header.click(function(){
var state = box.css("display");
if (state == "none"){
box.slideDown("normal");
header.removeClass("hide");
header.addClass("show");
}
else{
box.slideUp("normal");
header.removeClass("show");
header.addClass("hide");
}
});
}
});
}(jQuery, document, this));
\ No newline at end of file
$(document).ready( function() {
var send = false;
var cloneRequest;
$('#fileTree').fileTree({ root: $("input#workdir").val(), script: $SCRIPT_ROOT + '/readFolder', folderEvent: 'click', expandSpeed: 750, collapseSpeed: 750, multiFolder: false }, function(file) {
$('#fileTree').fileTree({ root: $("input#workdir").val(), script: $SCRIPT_ROOT + '/readFolder', folderEvent: 'click', expandSpeed: 750, collapseSpeed: 750, multiFolder: false }, function(file) {
selectFile(file);
});
configRadio();
......@@ -22,12 +22,12 @@ $(document).ready( function() {
$("#clone").append("Clone");
send = false;
return;
}
}
var repo_url = $("input#repo").val();
var email = "";
var name = ""
/* /^(ht|f)tps?:\/\/[a-z0-9-\.]+\.[a-z]{2,4}\/?([^\s<>\#%"\,\{\}\\|\\\^\[\]`]+)?$/ */
if($("input#repo").val() == "" || !repo_url.match(/^[\w\d\.\/:~@_-]+$/)){
if($("input#repo").val() == "" || !repo_url.match(/^[\w\d\.\/:~@_-]+$/)){
$("#error").Popup("Invalid url for the repository", {type:'alert', duration:3000});
return false;
}
......@@ -35,7 +35,7 @@ $(document).ready( function() {
$("#error").Popup("Invalid project name", {type:'alert', duration:3000});
return false;
}
if($("input#user").val() != "" && $("input#user").val() != "Enter your name..."){
if($("input#user").val() !== ""){
name = $("input#user").val();
}
if($("input#email").val() != "" && $("input#email").val() != "Enter your email adress..."){
......@@ -86,7 +86,7 @@ $(document).ready( function() {
$("#error").Popup("Your repository is cloned!", {type:'confirm', duration:3000});
$("input#repo").val("Enter the url of your repository...");
$("input#name").val("Enter the project name...");
$('#fileTree').fileTree({ root: $("input#workdir").val(), script: $SCRIPT_ROOT + '/readFolder', folderEvent: 'click', expandSpeed: 750, collapseSpeed: 750, multiFolder: false }, function(file) {
$('#fileTree').fileTree({ root: $("input#workdir").val(), script: $SCRIPT_ROOT + '/readFolder', folderEvent: 'click', expandSpeed: 750, collapseSpeed: 750, multiFolder: false }, function(file) {
selectFile(file);
});
}
......@@ -97,15 +97,21 @@ $(document).ready( function() {
$("#clone").empty();
$("#clone").append("Clone");
send = false;
}
},
error: function(request,error) {
$("#error").Popup("unable to clone your project, please check your internet connection", {type:'error', duration:3000});
$("#imgwaitting").hide();
$("#clone").empty();
$("#clone").append("Clone");
}
});
return false;
});
function configRadio(){
$("#modelist li").each(function(index) {
$("#modelist li").each(function(index) {
var boxselector = "#box" + index;
if($(this).hasClass('checked')){
$(this).removeClass('checked');
$(this).removeClass('checked');
$(boxselector).slideUp("normal");
}
if($(this).find("input:radio").is(':checked')){
......@@ -119,7 +125,7 @@ $(document).ready( function() {
}
});
}
function selectFile(file){
//nothing
return;
......
......@@ -4,28 +4,130 @@ $(document).ready( function() {
$($("#slappart li")[0]).find("input:radio").attr('checked', true);
$(".menu-box-right>div").css('min-height', $("#slappart li").length*26 + 20 + "px");
configRadio();
var send = false;
var lastli = null;
var partitionAmount = $("imput#partitionAmount").val();
var partitionAmount = $("input#partitionAmount").val();
$("#slappart li").each(function(){
lastli = $(this);
$(this).find("input:radio").change(function(){
configRadio();
configRadio();
});
});
lastli.css("border-bottom", "none");
});
if(lastli){lastli.css("border-bottom", "none");}
$("#parameterkw").slideBox("show");
setupSlappart();
$("#softwareType").slideBox();
$("#reloadfiles").click(function(){
setupFileTree();
});
$("#refresh").click(function(){
if (send) return;
$("#imgwaitting").fadeIn();
$.ajax({
type: "GET",
url: $SCRIPT_ROOT + '/supervisordStatus',
data: "",
success: function(data){
if(data.code == 1){
$("#supervisordcontent").empty();
$("#supervisordcontent").append(data.result);
}
$("#imgwaitting").fadeOut();
}
});
return false;
});
$("#add_attribute").click(function(){
var size = Number($("#partitionParameter > tbody > tr").last().attr('id').split('_')[1]) + 1;
var row="<tr id='row_"+size+"'><td class='first'><input type='text' name='txt_"+size+"' id='txt_"+size+"'></td>"+
"<td style='padding:6px'><textarea class='slap' id='value_"+size+"'></textarea>"+
"</td><td valign='middle'><span style='margin-left: 10px;' id='btn_"+size+"' class='close'></span></td></tr>";
$("#partitionParameter").append(row);
setInput($("input#txt_"+size));
setupTextarea($("textarea#value_"+size));
$("#btn_"+size).click(function(){
var index = $(this).attr('id').split('_')[1];
$("tr#row_"+index).remove();
});
return false;
});
$("#updateParameters").click(function(){
updateParameter();
return false;
});
$("#xmlview").click(function(){
var content = '<h2 style="color: #4c6172; font: 18px \'Helvetica Neue\', Helvetica, Arial, sans-serif;">' +
'INSTANCE PARAMETERS: Load XML file</h2><p id="xmllog" class="message"><br/></p>';
content += '<div class="main_content" style="height:230px"><pre id="editor"></pre></div>'+
'<input type=submit value="Load" id="loadxml" class="button">';
$.ajax({
type: "GET",
url: $SCRIPT_ROOT + '/getParameterXml/xml',
success: function(data){
if(data.code == 1){
$("#inline_content").html(content);
setupEditor(true);
$(".inline").colorbox({inline:true, width: "600px", height: "410px", onComplete:function(){
editor.getSession().setValue(data.result);
}});
$(".inline").click();
$("#loadxml").click(function(){
//Parse XML file
try{
var xmlDoc = $.parseXML(editor.getSession().getValue()), $xml = $( xmlDoc );
if($xml.find("parsererror").length !== 0){$("p#xmllog").html("Error: Invalid XML document!<br/>");return false;}
} catch(err) {
$("p#xmllog").html("Error: Invalid XML document!<br/>");return false;
}
$.ajax({
type: "POST",
url: $SCRIPT_ROOT + '/saveParameterXml',
data: {software_type:"", parameter:editor.getSession().getValue()},
success: function(data){
if(data.code == 1){
location.href = $SCRIPT_ROOT + '/inspectInstance#tab3';
location.reload();
}
else{$("p#xmllog").html(data.result);}
}
});
return false;
});
}
else{
$("#error").Popup(data.result, {type:'error', duration:5000});
}
}
});
});
//Load previous instance parameters
loadParameter();
$("a#parameterTab").click(function(){
var size = $("#partitionParameter > tbody > tr").length;
for(var i=2; i<=size; i++){
$("textarea#value_"+i).keyup();
}
});
function setupFileTree(){
function setupFileTree(path){
var root = $("input#root").val();
if (root == "") return;
if (path){
root += "/" + path;
$("#tab4>h2").html("File content for <strong>" + path + "</strong>");
}
else{$("#tab4>h2").html("File content for all your partitions");}
$('#fileTree').empty();
$('#fileTree').fileTree({ root: root, script: $SCRIPT_ROOT + "/readFolder", folderEvent: 'click', expandSpeed: 750,
collapseSpeed: 750, multiFolder: false, selectFolder: false }, function(file) {
collapseSpeed: 750, multiFolder: false, selectFolder: false }, function(file) {
}, function(file){
//User have double click on file in to the fileTree
viewFile(file);
});
}
function viewFile(file){
//User have double click on file in to the fileTree
loadFileContent(file);
......@@ -49,7 +151,7 @@ $(document).ready( function() {
}
});
});
function loadFileContent(file){
$.ajax({
type: "POST",
......@@ -62,14 +164,14 @@ $(document).ready( function() {
type: "POST",
url: $SCRIPT_ROOT + '/getFileContent',
data: {file:file, truncate:1500},
success: function(data){
success: function(data){
if(data.code == 1){
$("#inline_content").empty();
$("#inline_content").append('<h2 style="color: #4c6172; font: 18px \'Helvetica Neue\', Helvetica, Arial, sans-serif;">Inspect Instance Content: ' +
file +'</h2>');
$("#inline_content").append('<br/><div class="main_content"><pre id="editor"></pre></div>');
setupEditor();
$(".inline").colorbox({inline:true, width: "847px", onComplete:function(){
$(".inline").colorbox({inline:true, width: "847px", onComplete:function(){
editor.getSession().setValue(data.result);
}});
$(".inline").click();
......@@ -91,10 +193,74 @@ $(document).ready( function() {
}
});
}
function updateParameter(){
var xml = '<?xml version="1.0" encoding="utf-8"?>\n', software_type="";
if($("input#software_type").val() != "" && $("input#software_type").val() != "Software Type here..."){
software_type = +$("input#software_type").val();
}
xml +='<instance>\n';
var size = $("#partitionParameter > tbody > tr").length;
if(size > 1){
for(var i=2; i<=size; i++){
if($("input#txt_"+i).val() != ""){
xml +='<parameter id="'+$("input#txt_"+i).val()+'">'+$("textarea#value_"+i).val()+'</parameter>\n';
}
}
}
xml +='</instance>\n';
$.ajax({
type: "POST",
url: $SCRIPT_ROOT + '/saveParameterXml',
data: {software_type:software_type, parameter:xml},
success: function(data){
if(data.code == 1){
$("#error").Popup("Instance parameters has been updated, please run your instance now!", {type:'confirm', duration:5000});
}
else{
$("#error").Popup(data.result, {type:'error', duration:5000});
}
}
});
}
function setupTextarea($txt){
var size = Number($txt.attr('id').split('_')[1]);
var hiddenDiv = $(document.createElement('div')),
content = null;
hiddenDiv.attr('id', 'div_'+size);
hiddenDiv.addClass('hiddendiv');
$('div#parameterkw').append(hiddenDiv);
$txt.keyup(function() {
content = $txt.val().replace(/\n/g, '<br>');
hiddenDiv.html(content);
if(hiddenDiv.height() > $txt.height() && hiddenDiv.height() > 120){return}
$txt.css('height', hiddenDiv.height()+"px");
});
}
function loadParameter(){
$.ajax({
type: "GET",
url: $SCRIPT_ROOT + '/getParameterXml/dict',
success: function(data){
if(data.code == 1){
var dict=data.result['instance'];
for (propertie in dict){
$("#add_attribute").click();
var size = Number($("#partitionParameter > tbody > tr").last().attr('id').split('_')[1]);
$("input#txt_"+size).val(propertie);
$("textarea#value_"+size).val(dict[propertie]);
$("textarea#value_"+size).keyup();
}
}
else{
$("#error").Popup(data.result, {type:'error', duration:5000});
}
}
});
}
function configRadio(){
$("#slappart li").each(function() {
$("#slappart li").each(function() {
var $radio = $(this).find("input:radio");
var boxselector = "#box" + $radio.attr('id');
var boxselector = "#box" + $radio.attr('id');
if($(this).hasClass('checked')){
$(this).removeClass('checked');
$(boxselector).slideUp("normal");
......@@ -103,11 +269,24 @@ $(document).ready( function() {
$(this).addClass('checked');
//change content here
$(boxselector).slideDown("normal");
}
});
}
function setupEditor(){
function setupBox(){
var state = $("#softwareType").css("display");
if (state == "none"){
$("#softwareType").slideDown("normal");
$("#softwareTypeHead").removeClass("hide");
$("#softwareTypeHead").addClass("show");
}
else{
$("#softwareType").slideUp("normal");
$("#softwareTypeHead").removeClass("show");
$("#softwareTypeHead").addClass("hide");
}
}
function setupEditor(editable){
editor = ace.edit("editor");
editor.setTheme("ace/theme/crimson_editor");
......@@ -116,14 +295,19 @@ $(document).ready( function() {
editor.getSession().setTabSize(2);
editor.getSession().setUseSoftTabs(true);
editor.renderer.setHScrollBarAlwaysVisible(false);
editor.setReadOnly(true);
if(!editable){editor.setReadOnly(true);}
}
function setupSlappart(){
for(var i=0; i<partitionAmount; i++){
var elt = $("#slappart"+i+"Parameter");
if(elt != undefined) elt.click(function(){
alert(elt.attr('id'));
});
var fileId = $("#slappart"+i+"Files");
if(elt && elt != undefined) elt.click(function(){
alert($(this).html());
});
if(fileId && fileId != undefined) fileId.click(function(){
$("#instancetabfiles").click();
setupFileTree($(this).attr("rel"));
});
}
}
});
\ No newline at end of file
......@@ -9,27 +9,33 @@ $(document).ready( function() {
$("#info").append("Please select your file or folder into the box...");
fillContent();
});
function selectFile(file){
var relativeFile = file.replace(runnerDir + "/" + $("#softwarelist").val(), "");
$("#info").empty();
$("#info").append("Selection: " + relativeFile);
return;
}
function fillContent(){
var folder = $("#softwarelist").val();
var elt = $("option:selected", $("#softwarelist"));
$('#fileTree').fileTree({ root: runnerDir + "/" + folder, script: $SCRIPT_ROOT + '/readFolder',
folderEvent: 'click', expandSpeed: 750, collapseSpeed: 750, multiFolder: false, selectFolder: true }, function(file) {
selectFile(file);
}, function(file){ viewFile(file)});
$("#softcontent").empty();
$("#softcontent").append("File content: " + elt.attr('title'));
if(elt.val() !== "No Software Release found"){
$('#fileTree').fileTree({ root: runnerDir + "/" + folder, script: $SCRIPT_ROOT + '/readFolder',
folderEvent: 'click', expandSpeed: 750, collapseSpeed: 750, multiFolder: false, selectFolder: true }, function(file) {
selectFile(file);
}, function(file){ viewFile(file)});
$("#softcontent").empty();
$("#softcontent").append("File content: " + elt.attr('title'));
}
}
$("#open").click(function(){
$("#open").click(function(){
var elt = $("option:selected", $("#softwarelist"));
if(elt.val() === "No Software Release found"){
$("#error").Popup("Please select your Software Release", {type:'alert', duration:5000});
return false;
}
$.ajax({
type: "POST",
url: $SCRIPT_ROOT + '/setCurrentProject',
......@@ -39,14 +45,18 @@ $(document).ready( function() {
location.href = $SCRIPT_ROOT + '/editSoftwareProfile'
}
else{
$("#error").Popup(data.result, {type:'error'});
$("#error").Popup(data.result, {type:'error', duration:5000});
}
}
});
return false;
});
$("#delete").click(function(){
if($("#softwarelist").val() === "No Software Release found"){
$("#error").Popup("Please select your Software Release", {type:'alert', duration:5000});
return false;
}
if(send) return;
send = false;
$.ajax({
......@@ -55,21 +65,27 @@ $(document).ready( function() {
data: "name=" + $("#softwarelist").val(),
success: function(data){
if(data.code == 1){
var folder = $("#softwarelist").val();
$('#fileTree').fileTree({ root: runnerDir + "/" + folder, script: $SCRIPT_ROOT + '/readFolder', folderEvent: 'click', expandSpeed: 750,
collapseSpeed: 750, multiFolder: false, selectFolder: true }, function(file) {
selectFile(file);
setupFileTree(runnerDir);
}, function(file){ viewFile(file)});
var folder = $("#softwarelist").val();
$("input#file").val("");
$("#info").empty();
$("#info").append("Please select your file or folder into the box...");
$("#softwarelist").empty();
for(i=0; i<data.result.length; i++){
$("#softwarelist").append('<option value="' + data.result[i]["md5"] +
'" title="' + data.result[i]["title"] +'" rel="' +
$("#softwarelist").append('<option value="' + data.result[i]["md5"] +
'" title="' + data.result[i]["title"] +'" rel="' +
data.result[i]["path"] +'">' + data.result[i]["title"] + '</option>');
}
if(data.result.length < 1){
$("#softwarelist").append('<option>No Software Release found</option>');
$('#fileTree').empty();
}
else{
folder = $("#softwarelist").val();
$('#fileTree').fileTree({ root: runnerDir + "/" + folder, script: $SCRIPT_ROOT + '/readFolder', folderEvent: 'click', expandSpeed: 750,
collapseSpeed: 750, multiFolder: false, selectFolder: true }, function(file) {
selectFile(file);
}, function(file){ viewFile(file)});
}
$("#error").Popup("Operation complete, Selected Software Release has been delete!", {type:'confirm', duration:5000});
}
else{
......@@ -78,15 +94,15 @@ $(document).ready( function() {
send = false;
}
});
return false;
return false;
});
function viewFile(file){
//User have double click on file in to the fileTree
var name = file.replace(runnerDir + "/" + $("#softwarelist").val(), "/software");
loadFileContent(file, name);
}
function loadFileContent(file, filename){
$.ajax({
type: "POST",
......@@ -99,14 +115,14 @@ $(document).ready( function() {
type: "POST",
url: $SCRIPT_ROOT + '/getFileContent',
data: {file:file, truncate:1500},
success: function(data){
success: function(data){
if(data.code == 1){
$("#inline_content").empty();
$("#inline_content").append('<h2 style="color: #4c6172; font: 18px \'Helvetica Neue\', Helvetica, Arial, sans-serif;">Inspect Software Content: ' +
filename +'</h2>');
$("#inline_content").append('<br/><div class="main_content"><pre id="editor"></pre></div>');
setupEditor();
$(".inline").colorbox({inline:true, width: "847px", onComplete:function(){
$(".inline").colorbox({inline:true, width: "847px", onComplete:function(){
editor.getSession().setValue(data.result);
}});
$(".inline").click();
......@@ -128,11 +144,11 @@ $(document).ready( function() {
}
});
}
function setupEditor(){
function setupEditor(){
editor = ace.edit("editor");
editor.setTheme("ace/theme/crimson_editor");
var CurentMode = require("ace/mode/text").Mode;
editor.getSession().setMode(new CurentMode());
editor.getSession().setTabSize(2);
......
$(document).ready( function() {
var send = false;
//change background
$("body").css("background", "#9C9C9C");
$("#login").click(function(){
if (send) return false;
if($("input#clogin").val() === "" || !$("input#clogin").val().match(/^[\w\d\.-]+$/)){
$("#error").Popup("Please enter a valid user name", {type:'alert', duration:3000});
return false;
}
if($("input#cpwd").val() === "" || $("input#cpwd").val() ==="******"){
$("#error").Popup("Please enter your password", {type:'alert', duration:3000});
return false;
}
send = true;
var param = {clogin:$("input#clogin").val(), cpwd:$("input#cpwd").val()};
var url = $SCRIPT_ROOT + "/doLogin";
$.post(url, param, function(data) {
if (data.code==1){
location.href = $SCRIPT_ROOT + '/';
}
else{
$("#error").Popup(data.result, {type:'alert', duration:3000});
}
})
.error(function() {$("#error").Popup("Cannot send your account identifier please try again!!",
{type:'alert', duration:3000});})
.complete(function() {
send = false;
});
return false;
});
});
\ No newline at end of file
......@@ -106,7 +106,7 @@ function setRunningState(data){
$current.css("color", "#000");
$current = undefined;
currentState = false;
$("#error").Popup("Successfully run " + processType + " Profile", {type:'info', duration:3000});
$("#error").Popup("Slapgrid completely finish running your " + processType + " Profile", {type:'info', duration:3000});
}
}
currentState = data.result;
......
{% extends "layout.html" %}
{% block title %}Update your account{% endblock %}
{% block head %}
{{ super() }}
<script src="{{ url_for('static', filename='js/scripts/account.js') }}" type="text/javascript" charset="utf-8"></script>
{% endblock %}
{% block body %}
<h2 class='title'>Your personal informations</h2><br/>
<form>
<div class='form'>
<label for="name">Your name: </label>
<input type='text' name='name' id='name' value='{{name}}'/>
<div class='clear'></div>
<label for="email">Your email adress: </label>
<input type='text' name='email' id='email' value='{{email}}'/>
<div class='clear'></div>
<label for="username">User name: </label>
<input type='text' name='username' id='username' value='{{username}}'/>
<div class='clear'></div>
<label for="password">Password: </label>
<input type='password' name='password' id='password' value=''/>
<div class='clear'></div>
<label for="cpassword">Confirm Password: </label>
<input type='password' name='cpassword' id='cpassword' value=''/>
<div class='clear'></div>
<br/>
<label></label>
<input type="submit" name="update" id ="update" value="Update" class="button"/>
<div class='clear'></div>
<br/><br/><br/>
</div>
<div id="file_info" class="file_info">leave passwords blank to preserve your current password...</div>
<br/>
</form>
{% endblock %}
......@@ -3,64 +3,77 @@
{% block head %}
{{ super() }}
<link href="{{ url_for('static', filename='css/jqueryFileTree.css', _external=False) }}" rel="stylesheet" type="text/css" media="screen" />
<link href="{{ url_for('static', filename='css/jqueryTabs.css', _external=False) }}" rel="stylesheet" type="text/css" media="screen" />
<script src="{{ url_for('static', filename='js/jquery/jqueryFileTree.js') }}" type="text/javascript" charset="utf-8"></script>
<script src="{{ url_for('static', filename='js/scripts/folder.js') }}" type="text/javascript" charset="utf-8"></script>
<script src="{{ url_for('static', filename='js/jquery/jqueryTabs.js') }}" type="text/javascript" charset="utf-8"></script>
{% endblock %}
{% block body %}
<h2 class='title'>Clone your repository into the workspace</h2><br/>
<div id="repository" style="margin-left:40px;">
<label for='name'>Project name*: </label>
<input type="text" name="name" id="name" size='20' value="Enter the project name..." />
<label for='repo'>&nbsp;url*: &nbsp;&nbsp;</label>
<input type="text" name="repo" id="repo" size='25' value="Enter the url of your repository..." /><br/>
<label for='user'>Your name: &nbsp;&nbsp;&nbsp;&nbsp;</label>
<input type="text" name="user" id="user" size='20' value="Enter your name..." />
<label for='email'>Email: </label>
<input type="text" name="email" id="email" size='25' value="Enter your email adress..." />
<input type="hidden" name="workdir" id="workdir" value="{{workDir}}" />
<button class="button" id="clone">clone</button>
<img class="waitting" id="imgwaitting" src="{{ url_for('static', filename='images/waiting.gif') }}" alt="" />
<br/><br/>
</div>
<h2>Set your Security Mode</h2>
<div class="menu-box-right">
<div style="background:#fff; padding:10px; min-height:100px; font-size:14px;">
<div id="box0">
<h2>Clone Repository without using HTTPS and SSH</h2><br/>
<p>Choose this mode if you don't have login and password for the repository and you if you don't have the possibility to
use SSH authentication. Otherwise use your public key or your login and password to clone your project by choosing https or ssh mode. Note
that, with readonly mode you can not be able to push your changes.</p>
<br/>
</div>
<div id="box1" style="display:none">
<h2>You can use this public key to setup your repository</h2><br/>
<textarea class="public_key" readonly>
{{public_key}}
</textarea>
</div>
<div id="box2" style="display:none;">
<h2>Enter your username and password for https authentication access</h2><br/>
<div style="margin-left:80px; margin-bottom:20px;">
<label for='username'>Your username:&nbsp;&nbsp;</label>
<input type="text" name="username" id="username" size='20' value="Enter your username..." /><br/><br/>
<label for='password'>Your password: &nbsp;&nbsp;</label>
<input type="password" name="password" id="password" size='20' value="" class="idleField" />
</div>
<p></p>
</div>
</div>
</div>
<div class="menu-box-left">
<ul id="modelist">
<li class="checked"><input type="radio" name="security" id="nothing" value="nothing" /><label for="nothing">ReadOnly</label></li>
<li><input type="radio" name="security" id="ssh" value="SSH" checked /><label for="ssh">SSH Mode</label></li>
<li style="border-bottom:none"><input type="radio" name="security" id="https" value="HTTPS" /><label for="https">Https Mode</label></li>
</ul>
</div>
<div class="clear"></div><br/>
<div id="file_navigation">
<h2 class='title'>Your project folder</h2><br/>
<div id="fileTree" class="file_tree"></div>
<h2>Clone a repository into your workspace</h2><br/>
<div id="tabContaier">
<ul>
<li><a href="#tab1" class="active">Clone your repository</a></li>
<li><a href="#tab2">Manage your project folder</a></li>
</ul><!-- //Tab buttons -->
<div class="tabDetails">
<div id="tab1" class="tabContents">
<div id="repository" style="margin-left:40px;">
<label for='name'>Project name*: </label>
<input type="text" name="name" id="name" size='20' value="Enter the project name..." />
<label for='repo'>&nbsp;url*: &nbsp;&nbsp;</label>
<input type="text" name="repo" id="repo" size='25' value="Enter the url of your repository..." /><br/>
<label for='user'>Your name: &nbsp;&nbsp;&nbsp;&nbsp;</label>
<input type="text" name="user" id="user" size='20' value="{{name}}" />
<label for='email'>Email: </label>
<input type="text" name="email" id="email" size='25' value="{% if not email %}Enter your email adress...{% else %}{{email}}{%endif%}" />
<input type="hidden" name="workdir" id="workdir" value="{{workDir}}" />
<button class="button" id="clone">clone</button>
<img class="waitting" id="imgwaitting" src="{{ url_for('static', filename='images/waiting.gif') }}" alt="" />
<br/><br/>
</div>
<br/>
<h2>Set your Security Mode</h2>
<div class="menu-box-right" style="width: 592px;">
<div style="background:#fff; padding:10px; min-height:100px; font-size:14px;">
<div id="box0">
<h2>Clone Repository without using HTTPS and SSH</h2><br/>
<p>Choose this mode if you don't have login and password for the repository and you if you don't have the possibility to
use SSH authentication. Otherwise use your public key or your login and password to clone your project by choosing https or ssh mode. Note
that, with readonly mode you can not be able to push your changes.</p>
<br/>
</div>
<div id="box1" style="display:none">
<h2>You can use this public key to setup your repository</h2><br/>
<textarea class="mb_style public_key" readonly>
{{public_key}}
</textarea>
</div>
<div id="box2" style="display:none;">
<h2>Enter your username and password for https authentication access</h2><br/>
<div style="margin-left:80px; margin-bottom:20px;">
<label for='username'>Your username:&nbsp;&nbsp;</label>
<input type="text" name="username" id="username" size='20' value="Enter your username..." /><br/><br/>
<label for='password'>Your password: &nbsp;&nbsp;</label>
<input type="password" name="password" id="password" size='20' value="" class="idleField" />
</div>
<p></p>
</div>
</div>
</div>
<div class="menu-box-left" style="width: 115px;">
<ul id="modelist">
<li class="checked"><input type="radio" name="security" id="nothing" value="nothing" /><label for="nothing">ReadOnly</label></li>
<li><input type="radio" name="security" id="ssh" value="SSH" checked /><label for="ssh">SSH Mode</label></li>
<li style="border-bottom:none"><input type="radio" name="security" id="https" value="HTTPS" /><label for="https">Https Mode</label></li>
</ul>
</div>
<div class="clear"></div><br/>
<!--Fin tab1-->
</div>
<div id="tab2" class="tabContents">
<h2>Content of your cloned project</h2><br/>
<div id="fileTree" class="file_tree_tabs"></div>
</div>
</div>
</div>
{% endblock %}
{% extends "layout.html" %}
{% block title %}SlapOs buildout web based runner {% endblock %}
{% block title %}SlapOS buildout web based runner {% endblock %}
{% block body %}
<div id="home_box">
<div class="inner_box">
......@@ -23,14 +23,20 @@
<img src="{{ url_for('static', filename='images/manage_repo.png') }}" />
</div>
<div class="clear"></div>
<div class="lmenu">
<h2><a href="{{ url_for('openProject', method='new')}}">Create your new Software Release</a></h2>
<p>To create a new Software Release, choose the project directory in which you want to create your software. You will then be able to edit and
run the new software release.
<div class="lmenu smaller">
<h2><a href="{{ url_for('openProject', method='new')}}">Create your Software Release</a></h2>
<p>To create a new Software Release, choose the project directory in which you want to create your software.<!-- You will then be able to edit and
run the new software release.-->
</p>
<img src="{{ url_for('static', filename='images/folder_blue.png') }}" />
<div class="clear"></div>
</div>
<div class="umenu">
<h2><a href="{{ url_for('myAccount')}}">Your Account</a></h2>
<p>Update your account informations</p>
<img src="{{ url_for('static', filename='images/user_card.png') }}" />
</div>
<div class="clear"></div>
</div>
</div>
{% endblock %}
......@@ -19,14 +19,15 @@
<div id="tabContaier">
<ul>
<li><a href="#tab1" class="active">Slapgrid Supervisor</a></li>
<li><a href="#tab2">SLAP Response & Parameters</a></li>
<li><a href="#tab3">Partitions Content</a></li>
<li><a href="#tab2">SLAP Response</a></li>
<li><a href="#tab3" id="parameterTab">Parameters</a></li>
<li><a href="#tab4" id="instancetabfiles">Partitions Content</a></li>
</ul><!-- //Tab buttons -->
<div class="tabDetails">
<div id="tab1" class="tabContents">
<p>This tab show all process generated by slapgrid for your application. You can click on the process name to display log.</p>
{% if supervisor %}
<table cellpadding="0" cellspacing="0" width="100%">
<table cellpadding="0" cellspacing="0" width="100%" id="supervisordcontent">
<tr>
<th>Partition and Process name</th><th>Status</th><th>Process PID </th><th> UpTime</th><th></th>
</tr>
......@@ -39,7 +40,10 @@
</tr>
{% endfor %}
</table><br/>
<a href="{{ url_for('stopAllPartition') }}" class="lshare simple">Stop all process</a>
<a href="#" id="refresh" class="lshare simple no-right-border" style="float:left">Refresh Status</a>
<a href="{{ url_for('stopAllPartition') }}" class="lshare simple" style="float:left">Stop all process</a>
<img class="waitting" id="imgwaitting" src="{{ url_for('static', filename='images/waiting.gif') }}" alt="" />
<div class="clear"></div><br/>
{% else %}
<h2>No process to display, please run your instance</h2>
{%endif%}
......@@ -52,13 +56,13 @@
{% for item in slap_status %}
<div id="box{{item[0]}}" style="display:none;">
{% if item[1] %}
<!--<h2><span style="float:left; margin-left:10px;" id="{{item[0]}}title">Slap Response for {{item[0]}}</span>
<a href="#" id="{{item[0]}}Parameter" rel="{{item[0]}}" class="lshare simple" style="float:right">SLAP Parameters</a>
</h2>-->
<h2>Slap Response for {{item[0]}}</h2>
<h2><span style="float:left; margin-left:10px;" id="{{item[0]}}title">Slap Response for {{item[0]}}</span>
<a href="#" id="{{item[0]}}Parameter" rel="{{item[0]}}" class="lshare simple" style="float:right" title='Restart all partition process'>Restart</a>
<a href="#" id="{{item[0]}}Files" rel="{{item[0]}}" class="lshare simple no-right-border" style="float:right">Files</a>
</h2>
<div class="clear"></div><br/>
<div id="bcontent{{item[0]}}">
<table cellpadding="0" cellspacing="0" width="100%">
<div id="bcontent{{item[0]}}">
<table cellpadding="0" cellspacing="0" width="100%">
<tr>
<th>Parameter Name</th><th>Parameter Value</th>
</tr>
......@@ -67,9 +71,9 @@
<td class="propertie first">{{k}}</td><td align='left'>{{item[1][k]}}</td>
</tr>
{% endfor %}
</table>
</table>
</div>
{% else %}
{% else %}
<h2>Empty Partition</h2></br>
<center><img alt="" src="{{ url_for('static', filename='images/empty.png') }}" /></center>
<br/><h2>Partition {{item[0]}} is still empty</h2>
......@@ -82,8 +86,8 @@
<ul id="slappart">
{% for item in slap_status %}
<li><input type="radio" name="slapresponse" id="{{item[0]}}" value="{{item[0]}}" />
<label for="{{item[0]}}" {% if item[1] %}style="font-weight:bold"{%endif%}>{{item[0]}}</label></li>
{% endfor %}
<label for="{{item[0]}}" {% if item[1] %}style="font-weight:bold"{%endif%}>{{item[0]}}</label></li>
{% endfor %}
</ul>
</div>
<div class="clear"></div><br/>
......@@ -92,8 +96,39 @@
{%endif%}
</div><!-- end tab2 -->
<div id="tab3" class="tabContents">
<div id="fileTree" class="file_tree_tabs"></div>
<div id="softwareType">
<h2 class='hight'>Software Type parameter</h2>
<div class="slidebox">
<label for="software_type">Software Type </label>
<input type="text" name="software_type" id="software_type" size="35" value="Software Type here..." />
</div>
</div>
<br/>
<div id="parameterkw">
<h2 class='hight'>Partitions Parameter</h2>
<div class="slidebox">
<table class="small" cellpadding="0" cellspacing="0" width="100%" id="partitionParameter">
<tr id="row_1">
<th width="150">Parameter Name</th><th>Parameter Value</th><th width="49">
<a href="#" class="link" id="add_attribute">[new]</a>
</th>
</tr>
</table>
</div>
</div>
<br/>
<div>
<a id="updateParameters" class="lshare simple no-right-border" style="float:left">Update Values</a>
<a href="#" id="xmlview" class="lshare simple" style="float:left">Load XML</a>
</div>
<div class="clear"></div>
</div><!-- end tab3 -->
<div id="tab4" class="tabContents">
<h2>File content for all your partitions</h2>
<div id="fileTree" class="file_tree_tabs" title="Double click to open file"></div>
<br/>
<a href="#" id="reloadfiles" class="lshare simple">Reload Files</a>
</div><!-- end tab4 -->
</div>
</div>
<!-- This contains the hidden content for inline calls -->
......@@ -103,4 +138,5 @@
</div>
</div>
{{instance}}
{% endblock %}
......@@ -19,10 +19,16 @@
<script type=text/javascript>
$SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
</script>
<script src="{{ url_for('static', filename='js/scripts/process.js') }}" type="text/javascript" charset="utf-8"></script>
<script src="{{ url_for('static', filename='js/scripts/cookies.js') }}" type="text/javascript" charset="utf-8"></script>
<script src="{{ url_for('static', filename='js/scripts/common.js') }}" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
<script type="text/javascript">
$(document).ready(function() {
setInput();
});
</script>
{% if request.path != '/login' %}
<script src="{{ url_for('static', filename='js/scripts/process.js') }}" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
$(document).ready(function() {
if($("input#fmsg").val() != ""){
$("#error").Popup($("input#fmsg").val(), {type:'info', duration:5000, load:true});
......@@ -33,20 +39,22 @@
}
});
</script>
{%endif%}
{% endblock %}
</head>
<body>
<div id="error"></div>
<div id="page">
<div {% if request.path != '/login' %}id="page"{%else%}id="login-page"{%endif%}>
<input type="hidden" name="fmsg" value="{{ get_flashed_messages()[0] }}" id="fmsg" />
<!--<div id="logo">-->
<!--<a href="{{ url_for('home') }}"><img src="{{ url_for('static', filename='images/logo.png') }}" alt="" /></a>-->
<!--</div>-->
{% if request.path != '/login' %}
<div id="header">
<div class="block_header">
<a href="{{ url_for('home') }}" style="float:left;" id="home" {% if request.path != '/' %}rel="tooltip"{% endif %} title="Home"><img alt="" src="{{ url_for('static', filename='images/home.png') }}" /></a>
<div class="line"></div>
<a href="{{ url_for('editCurrentProject') }}" style="float:left" title="Edit your current project"><img alt="" src="{{ url_for('static', filename='images/project.png') }}" /></a>
<div class="line"></div>
<a href="{{ url_for('logout') }}" style="float:left" title="Close your session"><img alt="" src="{{ url_for('static', filename='images/logout.png') }}" /></a>
<div class="line"></div>
<h2 class="info">{% block title %}{% endblock %} - {{session.title}}</h2>
<div class="run"><span id="running" style="display:none"><img alt="" src="{{ url_for('static', filename='images/ajax_roller.gif') }}"
......@@ -71,24 +79,27 @@
</div>
<div class="clear"></div>
</div>
<div id="main">
<div {% if request.path == '/' %} class="home_content" {%else%} id="content" {% endif %}>
{% if request.path != '/' %}
{% endif %}
<div {% if request.path != '/login' %}id="main"{% endif %}>
<div {% if request.path == '/' %} class="home_content" {%elif request.path == '/login'%} {%else%} id="content" {% endif %}>
{% if request.path != '/' and request.path != '/login'%}
<div class="main_head">
</div>
<div class="content">
{% endif %}
{% block body %}{% endblock %}
{% if request.path != '/' %}
{% if request.path != '/' and request.path != '/login'%}
</div>
<div class="main_foot">
</div>
{% endif %}
</div>
</div>
{% if request.path != '/login' %}
<div id="footer">
SlapOs web runner &copy; Vifib SARL 2011 - All right reserved - Creative Commons Shared Alike Non Commercial
</div>
{%endif%}
</div>
<div class="popup">
<table id="dpop" cellpadding="0" border="0">
......@@ -110,11 +121,11 @@
<div id="tooltip-home" style="display:none">
<span style="font-weight:bold">QUICK ACCESS TO MENU</span><br/><br/>
<div style="margin-top:3px;border-bottom: 1px dashed #666666; heigth:1px"></div>
<ul>
<ul>
<li><a href="{{ url_for('manageProject')}}">Manage Repositories</a></li>
<li><a href="{{ url_for('configRepo')}}" >Clone your Repository</a></li>
<li><a href="{{ url_for('configRepo')}}" >Clone your repository</a></li>
<li><a href="{{ url_for('openProject', method='open')}}">Open Software Release</a></li>
<li><a href="{{ url_for('openProject', method='new')}}">Create Software Release</a></li>
<li><a href="{{ url_for('openProject', method='new')}}">Create Software Release</a></li>
</ul>
</div>
</body>
......
{% extends "layout.html" %}
{% block head %}
{{ super() }}
<script src="{{ url_for('static', filename='js/scripts/login.js') }}" type="text/javascript" charset="utf-8"></script>
{% endblock %}
{% block body %}
<form method="POST" action="">
<h2>Login to Slapos Web Runner</h2>
<div class="login-content">
<div class="login-element login-label"><label for="clogin">Your login&nbsp; : </label></div>
<div class="login-element"><input type="text" class="login-input" name="clogin" id="clogin" value="Enter login..." /></div><br/><br/>
<div class="clear"></div>
<div class="login-element login-label"><label for="cpwd">Password : </label></div>
<div class="login-element"><input type="password" class="idleField login-input" name="cpwd" id="cpwd" value="******" /></div>
<div class="clear"></div>
</div>
<div style="text-align:center">
<input type="reset" class="button" value="reset" />
<input type="submit" class="button" id="login" value="login" />
</div>
</form>
{% endblock %}
\ No newline at end of file
......@@ -14,7 +14,7 @@
<input type="hidden" name="method" id="method" value="{{method}}" />
{% if method == "new" %}
<div id="addsoftware">
<h2>Create your software release</h2>
<h2 class="title">Create your software release</h2><br/>
<label for='software'>Name: </label>
<input type="text" name="software" id="software" size='30' value="Enter software name..." />
<br/><br/>
......@@ -28,7 +28,7 @@
</div>
{% elif method == "open" %}
<div id="openSoftware">
<h2>Select the folder of your software release into the box</h2>
<h2 class="title">Select the folder of your software release into the box</h2><br/>
<div id="fileTree" class="file_tree"></div>
<div id="file_info" class="file_info">
<img src="{{ url_for('static', filename='images/check.png') }}" class="check" id="check" alt=""/>
......
......@@ -16,6 +16,7 @@
<input type="hidden" name="runnerdir" id="runnerdir" value="{{softwareRoot}}" />
<label for='softwarelist'>Select software: </label>
<select name="softwarelist" id="softwarelist">
{%if not softwares %}<option >No Software Release found</option>{% endif %}
{%for soft in softwares %}
<option value="{{soft['md5']}}" title="{{soft['title']}}" rel="{{soft['path']}}">{{soft['title']}}</option>
{%endfor%}
......@@ -23,8 +24,8 @@
&nbsp;&nbsp;<button id ="delete" class="button" title="Remove this software">Remove</button>
&nbsp;&nbsp;<button id ="open" class="button" title="Set this software as current software release">Open</button>
<br/><br/>
<h2 id="softcontent">No content to displays</h2>
<div id="fileTree" class="file_tree" style='height:200px;'></div>
<h2 id="softcontent">No file or folder to display</h2>
<div id="fileTree" class="file_tree" style='height:200px;' title="Double click to open file"></div>
<div id="file_info" class="file_info">
<span id="info">Please select your file or folder into the box...</span></div>
<!-- This contains the hidden content for inline calls -->
......
# -*- coding: utf-8 -*-
import slapos.slap
import time
import subprocess
......@@ -11,6 +13,7 @@ import shutil
import string
import hashlib
import signal
import multiprocessing
......@@ -32,13 +35,91 @@ html_escape_table = {
">": "&gt;",
"<": "&lt;",
}
def html_escape(text):
"""Produce entities within text."""
return "".join(html_escape_table.get(c,c) for c in text)
def checkLogin(config, login, pwd):
"""
User authentication method
Args:
config: Slaprunner configuration.
login: username of the user.
pwd: password associate to username.
Returns:
a list of user informations or False if authentication fail.
list=[username, password, email, complete_name]
"""
user = getSession(config)
salt = "runner81" #to be changed
current_pwd = hashlib.md5( salt + pwd ).hexdigest()
if current_pwd == user[1]:
return user
return False
def getSession(config):
"""
Get the session data of current user.
Returns:
a list of user informations or False if fail to read data.
"""
user_path = os.path.join(config['runner_workdir'], '.users')
user = ""
if os.path.exists(user_path):
user = open(user_path, 'r').read().split(';')
if type(user) == type(""):
#Error: try to restore data from backup
if os.path.exists(user_path+'.back'):
os.rename(user_path+'.back', user_path)
user = open(user_path, 'r').read().split(';')
else:
return False
return user
def saveSession(config, session, account):
"""
Save account information for the current user
Args:
config: Slaprunner configuration
session: Flask session
account: New session data to be save
Returns:
True if all goes well or str (error message) if fail
"""
user = os.path.join(config['runner_workdir'], '.users')
backup = False
try:
if account[1]:
salt = "runner81" #to be changed
account[1] = hashlib.md5(salt + account[1]).hexdigest()
else:
account[1] = session['account'][1]
#backup previous data
open(user+'.back', 'w').write(';'.join(session['account']))
backup = True
#save new account data
open(user, 'w').write((';'.join(account)).encode("utf-8"))
session['account'] = account
return True
except Exception, e:
try:
if backup:
os.remove(user)
os.rename(user+'.back', user)
except:
pass
return str(e)
def updateProxy(config):
"""
Configure Slapos Node computer and partitions.
Send current Software Release to Slapproxy for compilation and deployment.
"""
if not os.path.exists(config['instance_root']):
os.mkdir(config['instance_root'])
slap = slapos.slap.slap()
......@@ -59,8 +140,7 @@ def updateProxy(config):
'address': config['ipv4_address'],
'instance_root': config['instance_root'],
'netmask': '255.255.255.255',
'partition_list': [
],
'partition_list': [],
'reference': config['computer_id'],
'software_root': config['software_root']}
for i in xrange(0, int(config['partition_amount'])):
......@@ -81,19 +161,17 @@ def updateProxy(config):
#get instance parameter
param_path = os.path.join(config['runner_workdir'], ".parameter.xml")
xml_result = readParameters(param_path)
partition_parameter_kw = None
if type(xml_result) != type('') and xml_result.has_key('instance'):
partition_parameter_kw = xml_result['instance']
else:
partition_parameter_kw = None
computer.updateConfiguration(xml_marshaller.dumps(slap_config))
sr_request = slap.registerOpenOrder().request(profile, partition_reference=partition_reference,
sr_request = slap.registerOpenOrder().request(profile, partition_reference=getSoftwareReleaseName(config),
partition_parameter_kw=partition_parameter_kw, software_type=None,
filter_kw=None, state=None, shared=False)
#open(param_path, 'w').write(xml_marshaller.dumps(sr_request.
# getInstanceParameterDict()))
return True
def readPid(file):
"""Read process pid from file `file`"""
if os.path.exists(file):
data = open(file).read().strip()
try:
......@@ -104,10 +182,39 @@ def readPid(file):
def writePid(file, pid):
"""Save process pid into a file `file`"""
open(file, 'w').write(str(pid))
def updateInstanceParameter(config, software_type=None):
"""
Reconfigure Slapproxy to re-deploy current Software Instance with parameters.
Args:
config: Slaprunner configuration.
software_type: reconfigure Software Instance with software type.
"""
slap = slapos.slap.slap()
slap.initializeConnection(config['master_url'])
#Get current software release profile
try:
software_folder = open(os.path.join(config['runner_workdir'],
".project")).read()
profile = realpath(config, os.path.join(software_folder,
config['software_profile']))
except:
raise Exception("Software Release profile not found")
#get instance parameter
param_path = os.path.join(config['runner_workdir'], ".parameter.xml")
xml_result = readParameters(param_path)
partition_parameter_kw = None
if type(xml_result) != type('') and xml_result.has_key('instance'):
partition_parameter_kw = xml_result['instance']
slap.registerOpenOrder().request(profile, partition_reference=getSoftwareReleaseName(config),
partition_parameter_kw=partition_parameter_kw, software_type=software_type,
filter_kw=None, state=None, shared=False)
def startProxy(config):
"""Start Slapproxy server"""
proxy_pid = os.path.join(config['runner_workdir'], 'proxy.pid')
pid = readPid(proxy_pid)
running = False
......@@ -126,6 +233,7 @@ def startProxy(config):
def stopProxy(config):
"""Stop Slapproxy server"""
pid = readPid(os.path.join(config['runner_workdir'], 'proxy.pid'))
if pid:
try:
......@@ -135,10 +243,15 @@ def stopProxy(config):
def removeProxyDb(config):
"""Remove Slapproxy database, this is use to initialize proxy for example when
configuring new Software Release"""
if os.path.exists(config['database_uri']):
os.unlink(config['database_uri'])
def isSoftwareRunning(config):
"""
Return True if slapgrid-sr is still running and false if slapgrid if not
"""
slapgrid_pid = os.path.join(config['runner_workdir'], 'slapgrid-sr.pid')
pid = readPid(slapgrid_pid)
if pid:
......@@ -154,6 +267,10 @@ def isSoftwareRunning(config):
def runSoftwareWithLock(config):
"""
Use Slapgrid to compile current Software Release and wait until
compilation is done
"""
slapgrid_pid = os.path.join(config['runner_workdir'], 'slapgrid-sr.pid')
if not isSoftwareRunning(config):
if not os.path.exists(config['software_root']):
......@@ -164,7 +281,12 @@ def runSoftwareWithLock(config):
logfile = open(config['software_log'], 'w')
if not updateProxy(config):
return False
slapgrid = Popen([config['slapgrid_sr'], '-vc', config['configuration_file_path']], stdout=logfile)
# Accelerate compilation by setting make -jX
environment = os.environ.copy()
environment['MAKEFLAGS'] = '-j%r' % multiprocessing.cpu_count()
slapgrid = Popen([config['slapgrid_sr'], '-vc',
config['configuration_file_path'], '--now'],
stdout=logfile, env=environment)
writePid(slapgrid_pid, slapgrid.pid)
slapgrid.wait()
#Saves the current compile software for re-use
......@@ -191,6 +313,9 @@ def runSoftwareWithLock(config):
def isInstanceRunning(config):
"""
Return True if slapgrid-cp is still running and false if slapgrid if not
"""
slapgrid_pid = os.path.join(config['runner_workdir'], 'slapgrid-cp.pid')
pid = readPid(slapgrid_pid)
if pid:
......@@ -205,6 +330,7 @@ def isInstanceRunning(config):
return running
def killRunningSlapgrid(config, ptype):
"""Kill slapgrid process and all running children process"""
slapgrid_pid = os.path.join(config['runner_workdir'], ptype)
pid = readPid(slapgrid_pid)
if pid:
......@@ -233,25 +359,43 @@ def pidppid(pid):
return list(int(p) for p, pp in ppid if int(pp) == pid)
def runInstanceWithLock(config):
"""
Use Slapgrid to deploy current Software Release and wait until
deployment is done.
"""
slapgrid_pid = os.path.join(config['runner_workdir'], 'slapgrid-cp.pid')
if not isInstanceRunning(config):
startProxy(config)
logfile = open(config['instance_log'], 'w')
if not updateProxy(config):
return False
slapgrid = Popen([config['slapgrid_cp'], '-vc', config['configuration_file_path']], stdout=logfile)
svcStopAll(config) #prevent lost control of process
slapgrid = Popen([config['slapgrid_cp'], '-vc',
config['configuration_file_path'], '--now'],
stdout=logfile)
writePid(slapgrid_pid, slapgrid.pid)
slapgrid.wait()
return True
return False
def getProfilePath(peojectDir, profile):
if not os.path.exists(os.path.join(peojectDir, ".project")):
def getProfilePath(projectDir, profile):
"""
Return the path of the current Software Release `profile`
Args:
projectDir: Slaprunner workspace location.
profile: file to search into the workspace.
Returns:
String, path of current Software Release profile
"""
if not os.path.exists(os.path.join(projectDir, ".project")):
return False
projectFolder = open(os.path.join(peojectDir, ".project")).read()
projectFolder = open(os.path.join(projectDir, ".project")).read()
return os.path.join(projectFolder, profile)
def getSlapStatus(config):
"""Return all Slapos Partitions with associate informations"""
slap = slapos.slap.slap()
slap.initializeConnection(config['master_url'])
partition_list = []
......@@ -259,8 +403,7 @@ def getSlapStatus(config):
try:
for partition in computer.getComputerPartitionList():
# Note: Internal use of API, as there is no reflexion interface in SLAP
partition_list.append((partition.getId(), partition._connection_dict.copy(),
partition._parameter_dict.copy()))
partition_list.append((partition.getId(), partition._connection_dict.copy()))
except Exception:
pass
if partition_list:
......@@ -283,10 +426,12 @@ def runBuildoutAnnotate(config):
return False
def svcStopAll(config):
"""Stop all Instance process on this computer"""
return Popen([config['supervisor'], config['configuration_file_path'],
'shutdown']).communicate()[0]
def removeInstanceRoot(config):
"""Clean instance directory and stop all its running process"""
if os.path.exists(config['instance_root']):
svcStopAll(config)
for root, dirs, files in os.walk(config['instance_root']):
......@@ -298,6 +443,7 @@ def removeInstanceRoot(config):
shutil.rmtree(config['instance_root'])
def getSvcStatus(config):
"""Return all Softwares Instances process Informations"""
result = Popen([config['supervisor'], config['configuration_file_path'],
'status']).communicate()[0]
regex = "(^unix:.+\.socket)|(^error:).*$"
......@@ -311,15 +457,40 @@ def getSvcStatus(config):
return supervisord
def getSvcTailProcess(config, process):
"""Get log for the specifie process
Args:
config: Slaprunner configuration
process: process name. this value is pass to supervisord.
Returns:
a string that contains the log of the process.
"""
return Popen([config['supervisor'], config['configuration_file_path'],
"tail", process]).communicate()[0]
def svcStartStopProcess(config, process, action):
"""Send start or stop process command to supervisord
Args:
config: Slaprunner configuration.
process: process to start or stop.
action: current state which is used to generate the new process state.
"""
cmd = {"RESTART":"restart", "STOPPED":"start", "RUNNING":"stop", "EXITED":"start", "STOP":"stop"}
return Popen([config['supervisor'], config['configuration_file_path'],
cmd[action], process]).communicate()[0]
def getFolderContent(config, folder):
"""
Read all file and folder into specified directory
Args:
config: Slaprunner configuration.
folder: the directory to read.
Returns:
Html formated string or error message when fail.
"""
r=['<ul class="jqueryFileTree" style="display: none;">']
try:
folder = str(folder)
......@@ -347,6 +518,16 @@ def getFolderContent(config, folder):
return jsonify(result=''.join(r))
def getFolder(config, folder):
"""
Read list of folder for the specified directory
Args:
config: Slaprunner configuration.
folder: the directory to read.
Returns:
Html formated string or error message when fail.
"""
r=['<ul class="jqueryFileTree" style="display: none;">']
try:
folder = str(folder)
......@@ -371,6 +552,13 @@ def getFolder(config, folder):
return jsonify(result=''.join(r))
def getProjectList(folder):
"""Return the list of projet (folder) into the workspace
Agrs:
folder: path of the workspace
Returns:
a list that contains each folder name.
"""
project = []
project_list = sorted(os.listdir(folder), key=str.lower)
for elt in project_list:
......@@ -378,6 +566,14 @@ def getProjectList(folder):
return project
def configNewSR(config, projectpath):
"""Configure a Software Release as current Software Release
Args:
config: slaprunner configuration
projectpath: path of the directory that contains the software realease to configure
Returns:
True if all is done well, otherwise return false.
"""
folder = realpath(config, projectpath)
if folder:
if isInstanceRunning(config):
......@@ -388,12 +584,22 @@ def configNewSR(config, projectpath):
removeProxyDb(config)
startProxy(config)
removeInstanceRoot(config)
param_path = os.path.join(config['runner_workdir'], ".parameter.xml")
if os.path.exists(param_path):
os.remove(param_path)
open(os.path.join(config['runner_workdir'], ".project"), 'w').write(projectpath)
return True
else:
return False
def newSoftware(folder, config, session):
"""
Create a new Software Release folder with default profiles
Args:
folder: directory of the new software release
config: slraprunner configuration
session: Flask session directory"""
json = ""
code = 0
runner_dir = config['runner_workdir']
......@@ -427,12 +633,14 @@ def newSoftware(folder, config, session):
return jsonify(code=code, result=json)
def checkSoftwareFolder(path, config):
"""Check id `path` is a valid Software Release folder"""
realdir = realpath(config, path)
if realdir and os.path.exists(os.path.join(realdir, config['software_profile'])):
return jsonify(result=path)
return jsonify(result="")
def getProjectTitle(config):
"""Generate the name of the current software Release (for slaprunner UI)"""
conf = os.path.join(config['runner_workdir'], ".project")
if os.path.exists(conf):
project = open(conf, "r").read().split("/")
......@@ -440,7 +648,22 @@ def getProjectTitle(config):
return software + " (" + string.join(project[:(len(project) - 2)], '/') + ")"
return "No Profile"
def getSoftwareReleaseName(config):
"""Get the name of the current Software Release"""
sr_profile = os.path.join(config['runner_workdir'], ".project")
if os.path.exists(sr_profile):
project = open(sr_profile, "r").read().split("/")
software = project[len(project) - 2]
return software.replace(' ', '_')
return "No_name"
def loadSoftwareData(runner_dir):
"""Get All Compiled Softwares Releases name and directory
Agrs:
runner_dir: base directory of slapos web runner.
Returns:
a dictionnary that contains all compiled Software Release with path"""
import pickle
file_path = os.path.join(runner_dir, '.softdata')
if not os.path.exists(file_path):
......@@ -451,6 +674,12 @@ def loadSoftwareData(runner_dir):
return data
def writeSoftwareData(runner_dir, data):
"""Save the list of compiled Software Release into a file
Args:
runner_dir: base directory of slapos web runner.
data: dictionnary data about real name and directory of each software release
"""
import pickle
file_path = os.path.join(runner_dir, '.softdata')
pkl_file = open(file_path, 'wb')
......@@ -459,11 +688,16 @@ def writeSoftwareData(runner_dir, data):
pkl_file.close()
def removeSoftwareByName(config, folderName):
"""Remove all content of the specified software release
Args:
config: slaprunner configuration
foldername: the name given to the software release"""
if isSoftwareRunning(config) or isInstanceRunning(config):
return jsonify(code=0, result="Software installation or instantiation in progress, cannot remove")
raise Exception("Software installation or instantiation in progress, cannot remove")
path = os.path.join(config['software_root'], folderName)
if not os.path.exists(path):
return jsonify(code=0, result="Can not remove software: No such file or directory")
raise Exception("Cannot remove software Release: No such file or directory")
svcStopAll(config)
shutil.rmtree(path)
#update compiled software list
......@@ -475,7 +709,7 @@ def removeSoftwareByName(config, folderName):
writeSoftwareData(config['runner_workdir'], data)
break
i = i+1
return jsonify(code=1, result=data)
return data
def tail(f, lines=20):
"""
......@@ -583,6 +817,13 @@ def realpath(config, path, check_exist=True):
return False
def readParameters(path):
"""Read Instance parameters stored into a local file.
Agrs:
path: path of the xml file that contains parameters
Return:
a dictionnary of instance parameters."""
if os.path.exists(path):
try:
xmldoc = minidom.parse(path)
......@@ -591,8 +832,7 @@ def readParameters(path):
sub_object = {}
for subnode in elt.childNodes:
if subnode.nodeType != subnode.TEXT_NODE:
sub_object[str(subnode.getAttribute('id'))] = str(subnode.
childNodes[0].data)
sub_object[str(subnode.getAttribute('id'))] = subnode.childNodes[0].data #.decode('utf-8').decode('utf-8')
object[str(elt.tagName)] = sub_object
return object
except Exception, e:
......
# -*- coding: utf-8 -*-
from flask import Flask, request, redirect, url_for, \
render_template, flash, jsonify, session
from utils import *
......@@ -9,21 +11,51 @@ from gittools import cloneRepo, gitStatus, switchBranch, addBranch, getDiff, \
app = Flask(__name__)
#Access Control: Only static files and login pages are allowed to guest
@app.before_request
def before_request():
session['title'] = getProjectTitle(app.config)
if (not session.has_key('account') or not session['account']) \
and request.path != '/login' \
and request.path != '/doLogin' and not request.path.startswith('/static'):
return redirect(url_for('login'))
if session.has_key('account') and session['account']:
session['title'] = getProjectTitle(app.config)
session['account'] = getSession(app.config)
# general views
@app.route('/')
def home():
if not os.path.exists(app.config['workspace']) or len(os.listdir(app.config['workspace'])) == 0:
return redirect(url_for('configRepo'))
return render_template('index.html')
@app.route("/login")
def login():
return render_template('login.html')
@app.route("/myAccount")
def myAccount():
return render_template('account.html', username=session['account'][0],
email=session['account'][2], name=session['account'][3].decode('utf-8'))
@app.route("/logout")
def logout():
session['account'] = None
return redirect(url_for('login'))
@app.route('/configRepo')
def configRepo():
public_key = open(app.config['public_key'], 'r').read()
return render_template('cloneRepository.html', workDir='workspace', public_key=public_key)
return render_template('cloneRepository.html', workDir='workspace',
public_key=public_key, name=session['account'][3].decode('utf-8'),
email=session['account'][2])
@app.route("/doLogin", methods=['POST'])
def doLogin():
check_user = checkLogin(app.config, request.form['clogin'], request.form['cpwd'])
if not check_user:
return jsonify(code=0, result="Login or password is incorrect, please check it!")
else:
session['account'] = check_user
return jsonify(code=1, result=check_user)
# software views
@app.route('/editSoftwareProfile')
......@@ -43,6 +75,7 @@ def inspectSoftware():
return render_template('runResult.html', softwareRoot='software_root',
softwares=loadSoftwareData(app.config['runner_workdir']))
#remove content of compiled software release
@app.route('/removeSoftware')
def removeSoftware():
file_config = os.path.join(app.config['runner_workdir'], ".softdata")
......@@ -80,6 +113,7 @@ def editInstanceProfile():
return render_template('updateInstanceProfile.html', workDir='workspace',
profile=profile, projectList=getProjectList(app.config['workspace']))
# get status of all computer partitions and process state
@app.route('/inspectInstance', methods=['GET'])
def inspectInstance():
file_content = ''
......@@ -88,17 +122,39 @@ def inspectInstance():
file_content = 'instance_root'
result = getSvcStatus(app.config)
if len(result) == 0:
result = []
result = []
return render_template('instanceInspect.html',
file_path=file_content, supervisor=result, slap_status=getSlapStatus(app.config),
supervisore=result, partition_amount=app.config['partition_amount'])
#Reload instance process ans returns new value to ajax
@app.route('/supervisordStatus', methods=['GET'])
def supervisordStatus():
result = getSvcStatus(app.config)
if not (result):
return jsonify(code=0, result="")
html = "<tr><th>Partition and Process name</th><th>Status</th><th>Process PID </th><th> UpTime</th><th></th></tr>"
for item in result:
html += "<tr>"
html +="<td class='first'><b><a href='" + url_for('tailProcess', process=item[0])+"'>"+item[0]+"</a></b></td>"
html +="<td align='center'><a href='"+url_for('startStopProccess', process=item[0], action=item[1])+"'>"+item[1]+"</a></td>"
html +="<td align='center'>"+item[3]+"</td><td>"+item[5]+"</td>"
html +="<td align='center'><a href='"+url_for('startStopProccess', process=item[0], action='RESTART')+"'>Restart</a></td>"
html +="</tr>"
return jsonify(code=1, result=html)
@app.route('/removeInstance')
def removeInstance():
if isInstanceRunning(app.config):
flash('Instantiation in progress, cannot remove')
else:
stopProxy(app.config)
removeProxyDb(app.config)
startProxy(app.config)
removeInstanceRoot(app.config)
param_path = os.path.join(app.config['runner_workdir'], ".parameter.xml")
if os.path.exists(param_path):
os.remove(param_path)
flash('Instance removed')
return redirect(url_for('inspectInstance'))
......@@ -201,6 +257,7 @@ def getProjectStatus():
else:
return jsonify(code=0, result="Can not read folder: Permission Denied")
#view for current software release files
@app.route("/editCurrentProject")
def editCurrentProject():
project = os.path.join(app.config['runner_workdir'], ".project")
......@@ -210,6 +267,7 @@ def editCurrentProject():
projectList=getProjectList(app.config['workspace']))
return redirect(url_for('configRepo'))
#create file or directory
@app.route("/createFile", methods=['POST'])
def createFile():
path = realpath(app.config, request.form['file'], False)
......@@ -225,6 +283,7 @@ def createFile():
except Exception, e:
return jsonify(code=0, result=str(e))
#remove file or directory
@app.route("/removeFile", methods=['POST'])
def removeFile():
try:
......@@ -238,8 +297,13 @@ def removeFile():
@app.route("/removeSoftwareDir", methods=['POST'])
def removeSoftwareDir():
return removeSoftwareByName(app.config, request.form['name'])
try:
data = removeSoftwareByName(app.config, request.form['name'])
return jsonify(code=1, result=data)
except Exception, e:
return jsonify(code=0, result=str(e))
#read file and return content to ajax
@app.route("/getFileContent", methods=['POST'])
def getFileContent():
file_path = realpath(app.config, request.form['file'])
......@@ -256,7 +320,7 @@ def getFileContent():
def saveFileContent():
file_path = realpath(app.config, request.form['file'])
if file_path:
open(file_path, 'w').write(request.form['content'])
open(file_path, 'w').write(request.form['content'].encode("utf-8"))
return jsonify(code=1, result="")
else:
return jsonify(code=0, result="Error: No such file!")
......@@ -323,6 +387,7 @@ def getmd5sum():
else:
return jsonify(code=0, result="Can not get md5sum for this file!")
#return informations about state of slapgrid process
@app.route("/slapgridResult", methods=['POST'])
def slapgridResult():
software_state = isSoftwareRunning(app.config)
......@@ -359,31 +424,61 @@ def getPath():
else:
return jsonify(code=1, result=realfile)
#update instance parameter into a local xml file
@app.route("/saveParameterXml", methods=['POST'])
def redParameterXml():
def saveParameterXml():
project = os.path.join(app.config['runner_workdir'], ".project")
if not os.path.exists(project):
return jsonify(code=0, result="Please first open a Software Release")
content = request.form['parameter']
content = request.form['parameter'].encode("utf-8")
param_path = os.path.join(app.config['runner_workdir'], ".parameter.xml")
f = open(param_path, 'w')
f.write(content)
f.close()
result = readParameters(param_path)
try:
f = open(param_path, 'w')
f.write(content)
f.close()
result = readParameters(param_path)
except Exception, e:
result = str(e)
software_type = None
if(request.form['software_type']):
software_type = request.form['software_type']
if type(result) == type(''):
return jsonify(code=0, result="XML Error: " + result)
return jsonify(code=0, result=result)
else:
try:
updateProxy(app.config)
except Exeption:
return jsonify(code=0, result="An error occurred while applying your settings!")
updateInstanceParameter(app.config, software_type)
except Exception, e:
return jsonify(code=0, result="An error occurred while applying your settings!<br/>" + str(e))
return jsonify(code=1, result="")
@app.route("/getParameterXml", methods=['GET'])
def getParameterXml():
#read instance parameters into the local xml file and return a dict
@app.route("/getParameterXml/<request>", methods=['GET'])
def getParameterXml(request):
param_path = os.path.join(app.config['runner_workdir'], ".parameter.xml")
if os.path.exists(param_path):
content = open(param_path, 'r').read()
return html_escape(content)
if not os.path.exists(param_path):
default = '<?xml version="1.0" encoding="utf-8"?>\n'
default += '<instance>\n</instance>'
return jsonify(code=1, result=default)
if request == "xml":
parameters = open(param_path, 'r').read()
else:
parameters = readParameters(param_path)
if type(parameters) == type('') and request != "xml":
return jsonify(code=0, result=parameters)
else:
return jsonify(code=1, result=parameters)
#update user account data
@app.route("/updateAccount", methods=['POST'])
def updateAccount():
account = []
user = os.path.join(app.config['runner_workdir'], '.users')
account.append(request.form['username'].strip())
account.append(request.form['password'].strip())
account.append(request.form['email'].strip())
account.append(request.form['name'].strip())
result = saveSession(app.config, session, account)
if type(result) == type(""):
return jsonify(code=0, result=result)
else:
return "&lt;?xml version='1.0' encoding='utf-8'?&gt;"
\ No newline at end of file
return jsonify(code=1, result="")
\ No newline at end of file
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