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 ...@@ -5,6 +5,7 @@ import logging.handlers
import os import os
import sys import sys
import subprocess import subprocess
import hashlib
class Parser(OptionParser): class Parser(OptionParser):
""" """
...@@ -121,5 +122,11 @@ def serve(config): ...@@ -121,5 +122,11 @@ def serve(config):
) )
if not os.path.exists(workdir): if not os.path.exists(workdir):
os.mkdir(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), app.run(host=config.runner_host, port=int(config.runner_port),
debug=config.debug, threaded=True) debug=config.debug, threaded=True)
# -*- coding: utf-8 -*-
import slapos.slap import slapos.slap
import time import time
import subprocess import subprocess
...@@ -21,6 +23,15 @@ class Popen(subprocess.Popen): ...@@ -21,6 +23,15 @@ class Popen(subprocess.Popen):
self.stdin = None self.stdin = None
def cloneRepo(data): 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'] workDir = data['path']
if not workDir: if not workDir:
return jsonify(code=0, return jsonify(code=0,
...@@ -34,7 +45,7 @@ def cloneRepo(data): ...@@ -34,7 +45,7 @@ def cloneRepo(data):
config_writer = repo.config_writer() config_writer = repo.config_writer()
config_writer.add_section("user") config_writer.add_section("user")
if data["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"] != "": if data["email"] != "":
config_writer.set_value("user", "email", data["email"]) config_writer.set_value("user", "email", data["email"])
code = 1 code = 1
...@@ -45,6 +56,11 @@ def cloneRepo(data): ...@@ -45,6 +56,11 @@ def cloneRepo(data):
return jsonify(code=code, result=json) return jsonify(code=code, result=json)
def gitStatus(project): 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 code = 0
json = "" json = ""
try: try:
...@@ -59,6 +75,12 @@ def gitStatus(project): ...@@ -59,6 +75,12 @@ def gitStatus(project):
return jsonify(code=code, result=json, branch=branch, dirty=isdirty) return jsonify(code=code, result=json, branch=branch, dirty=isdirty)
def switchBranch(project, name): 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 code = 0
json = "" json = ""
try: try:
...@@ -76,6 +98,13 @@ def switchBranch(project, name): ...@@ -76,6 +98,13 @@ def switchBranch(project, name):
return jsonify(code=code, result=json) return jsonify(code=code, result=json)
def addBranch(project, name, onlyCheckout=False): 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 code = 0
json = "" json = ""
try: try:
...@@ -91,6 +120,7 @@ def addBranch(project, name, onlyCheckout=False): ...@@ -91,6 +120,7 @@ def addBranch(project, name, onlyCheckout=False):
return jsonify(code=code, result=json) return jsonify(code=code, result=json)
def getDiff(project): def getDiff(project):
"""Get git diff for the specified project directory"""
result = "" result = ""
try: try:
repo = Repo(project) repo = Repo(project)
...@@ -102,6 +132,10 @@ def getDiff(project): ...@@ -102,6 +132,10 @@ def getDiff(project):
return result return result
def gitPush(project, msg): def gitPush(project, msg):
"""Commit and Push changes for the specified repository
Args:
project: directory of the local repository
msg: commit message"""
code = 0 code = 0
json = "" json = ""
undo_commit = False undo_commit = False
...@@ -143,5 +177,6 @@ def gitPull(project): ...@@ -143,5 +177,6 @@ def gitPull(project):
return jsonify(code=code, result=result) return jsonify(code=code, result=result)
def safeResult(result): def safeResult(result):
"""Parse string and remove credential of the user"""
regex=re.compile("(https:\/\/)([\w\d\._-]+:[\w\d\._-]+)\@([\S]+\s)", re.VERBOSE) regex=re.compile("(https:\/\/)([\w\d\._-]+:[\w\d\._-]+)\@([\S]+\s)", re.VERBOSE)
return regex.sub(r'\1\3', result) return regex.sub(r'\1\3', result)
\ No newline at end of file
...@@ -10,6 +10,9 @@ ...@@ -10,6 +10,9 @@
#tabContaier textarea { #tabContaier textarea {
width:702px; 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{ #tabContaier > ul{
overflow:hidden; overflow:hidden;
height:34px; height:34px;
...@@ -39,9 +42,9 @@ ...@@ -39,9 +42,9 @@
} }
#tabContaier > ul > li a.active{ #tabContaier > ul > li a.active{
background:#fbfbfb; background:#fbfbfb;
border:1px solid #fff; border:1px solid #fff;
border-top:0; border-top:0;
border-right:0; border-right:0;
color:#333; color:#333;
} }
#tabContaier > ul > li:first-child a{border-left:0} #tabContaier > ul > li:first-child a{border-left:0}
...@@ -55,4 +58,4 @@ ...@@ -55,4 +58,4 @@
} }
.tabContents p{ .tabContents p{
padding:0 0 10px; padding:0 0 10px;
} }
...@@ -4,7 +4,6 @@ blockquote, q {quotes: none;} ...@@ -4,7 +4,6 @@ blockquote, q {quotes: none;}
blockquote:before, blockquote:after, q:before, q:after {content: none;} blockquote:before, blockquote:after, q:before, q:after {content: none;}
:focus {outline: 0 none;} :focus {outline: 0 none;}
img{border:0} img{border:0}
a{ a{
text-decoration: none; text-decoration: none;
color: #19485C; color: #19485C;
...@@ -15,11 +14,12 @@ a:hover { ...@@ -15,11 +14,12 @@ a:hover {
} }
table { table {
margin: 0; margin: 0;padding:0;
border-right: none; border-right: none;
border-bottom: none; border-bottom: none;
font-size: 14px; font-size: 14px;
background: #fff; background: #fff;
border-spacing:0;
} }
td{ td{
padding: 4px; padding: 4px;
...@@ -36,16 +36,19 @@ th{ ...@@ -36,16 +36,19 @@ th{
font-weight: normal; font-weight: normal;
font-size: 18px; font-size: 18px;
} }
table.small th{padding: 4px;font-size: 16px;}
textarea { textarea {
width:762px; width:762px;
font-family: 'Helvetica Neue',Tahoma,Helvetica,Arial,sans-serif;
} }
body { body {
background: url("../images/1307251316-background-stripes.gif") repeat #9C9C9C; background: #2281C1;/*url("../images/1307251316-background-stripes.gif") repeat #9C9C9C;*/
font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; font-family: 'Helvetica Neue',Tahoma,Helvetica,Arial,sans-serif;
color: #000000; color: #000000;
font-size: 13px; font-size: 13px;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
} }
#page #page
...@@ -67,7 +70,7 @@ body { ...@@ -67,7 +70,7 @@ body {
.block_header{ .block_header{
text-align: left; text-align: left;
padding-left: 25px; padding-left: 20px;
height: 30px; height: 30px;
} }
...@@ -81,7 +84,7 @@ body { ...@@ -81,7 +84,7 @@ body {
font-weight: normal; font-weight: normal;
padding-top: 3px; padding-top: 3px;
float: left; float: left;
width: 690px; width: 656px;
height: 22px; height: 22px;
text-align: center; text-align: center;
color: #4c6172; color: #4c6172;
...@@ -257,11 +260,8 @@ body { ...@@ -257,11 +260,8 @@ body {
box-shadow: 1px 1px 1px #888888; box-shadow: 1px 1px 1px #888888;
} }
.button:active { .button:active {
background-position: 0 top; background: #eee;
position: relative; color: #000;
top: 1px;
padding: 6px 10px 4px;
box-shadow: 1px 1px 1px #888888;
} }
.focusField{ .focusField{
...@@ -314,7 +314,7 @@ input[type="radio"], input[type="checkbox"]{ ...@@ -314,7 +314,7 @@ input[type="radio"], input[type="checkbox"]{
#home_box{ #home_box{
background: none; background: none;
border: 1px solid #678dad; border: 2px solid #87B0D4;
padding: 0; padding: 0;
color: #4c6172; color: #4c6172;
margin: 15px 59px 15px 59px; margin: 15px 59px 15px 59px;
...@@ -333,7 +333,7 @@ input[type="radio"], input[type="checkbox"]{ ...@@ -333,7 +333,7 @@ input[type="radio"], input[type="checkbox"]{
#home_box h2{ #home_box h2{
font-weight: normal; font-weight: normal;
font-size: 23px; font-size: 23px;
color: #4c6172; color: #475F73;
} }
#home_box p{ #home_box p{
...@@ -351,7 +351,6 @@ input[type="radio"], input[type="checkbox"]{ ...@@ -351,7 +351,6 @@ input[type="radio"], input[type="checkbox"]{
.inner_box{ .inner_box{
background: none; background: none;
margin: 0; margin: 0;
border: 1px solid #CAD4DC;
display: block; display: block;
padding: 30px 0 30px 0; padding: 30px 0 30px 0;
} }
...@@ -371,23 +370,24 @@ input[type="radio"], input[type="checkbox"]{ ...@@ -371,23 +370,24 @@ input[type="radio"], input[type="checkbox"]{
margin: 10px 45px 10px 45px; margin: 10px 45px 10px 45px;
padding: 10px; padding: 10px;
padding-bottom:15px; padding-bottom:15px;
border: #678dad;
width: 530px; width: 530px;
border: 1px solid #678dad; 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; background: #96b9d7;
} }
.smenu{ .smenu{
display: block; display: block;
height: 80px; height: 80px;
margin: 0 10px 0 45px; margin: 0 10px 0 45px;
padding: 10px; padding: 10px;
padding-bottom:15px; padding-bottom:15px;
border: #678dad;
width: 250px; width: 250px;
border: 1px solid #678dad; border: 1px solid #678dad;
float: left; float: left;
...@@ -541,7 +541,6 @@ h2.hight:hover{ ...@@ -541,7 +541,6 @@ h2.hight:hover{
overflow: auto; overflow: auto;
height: 95px; height: 95px;
padding: 5px; padding: 5px;
width: 604px;
background:#fff; background:#fff;
margin-bottom: 10px; margin-bottom: 10px;
color: #3A494F; color: #3A494F;
...@@ -580,9 +579,10 @@ a.lshare{ ...@@ -580,9 +579,10 @@ a.lshare{
height: 18px; height: 18px;
font-size: 15px; font-size: 15px;
border: solid 1px #678dad; border: solid 1px #678dad;
color: #4DA0C6; color: #4DA0C6;
text-align:center; text-align:center;
font-weight:bold; font-weight:bold;
cursor:pointer;
} }
a.lshare:hover{ a.lshare:hover{
background:#D9D9D9; background:#D9D9D9;
...@@ -593,7 +593,9 @@ a.lshare:hover{ ...@@ -593,7 +593,9 @@ a.lshare:hover{
a.lshare:focus{ a.lshare:focus{
border:solid 1px #73A6FF; 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{ a.lshare img{
margin: 5px; margin: 5px;
} }
...@@ -635,19 +637,28 @@ a.lshare img{ ...@@ -635,19 +637,28 @@ a.lshare img{
width: 36px; width: 36px;
height: 26px; height: 26px;
} }
#error td.close{ #error td.b_close{
width: 30px; width: 30px;
height:22px; 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;} .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;} .alert_message{ background: url(../images/alert.png) center no-repeat; height: 26px;}
.error_message{ background: url(../images/exit.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;} .confirm_message{ background: url(../images/confirm.png) center no-repeat; height: 26px;}
.info_message{ background: url(../images/info.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;} #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, .close{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:hover, .close:hover{background:url(../images/close_hover.png) no-repeat 0px 0px;}
.md5sum {margin:10px; font-size:15px;} .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;} .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; .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{ ...@@ -656,6 +667,7 @@ a.lshare img{
margin-top:10px; box-shadow: 1px 1px 1px #888888;} margin-top:10px; box-shadow: 1px 1px 1px #888888;}
.menu-box-left ul{margin:0px; list-style:none;} .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{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:hover{background:#F0F2F2;}
.menu-box-left li.checked{background:#fff;} .menu-box-left li.checked{background:#fff;}
.menu-box-right h2{text-align:center} .menu-box-right h2{text-align:center}
...@@ -679,7 +691,8 @@ a.lshare img{ ...@@ -679,7 +691,8 @@ a.lshare img{
.popup li{border-bottom: 1px dashed #666666; padding:5px; padding-top:5px;} .popup li{border-bottom: 1px dashed #666666; padding:5px; padding-top:5px;}
.popup-value{display:none;} .popup-value{display:none;}
textarea.parameter {border: solid 1px #678dad; color: #666666; height:110px;} 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] { input[type=radio] {
} }
...@@ -689,3 +702,12 @@ input[type=radio]:checked { ...@@ -689,3 +702,12 @@ input[type=radio]:checked {
input[type=radio]:hover { input[type=radio]:hover {
box-shadow: 0px 1px 3px #F0F1F2; 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(){ $(document).ready(function(){
$(".tabContents").hide(); // Hide all tab content divs by default $(".tabContents").hide(); // Hide all tab content divs by default
var hashes = window.location.href.split('#'); var hashes = window.location.href.split('#');
if (hashes.length == 2){ var fromheight = 0;
var previoustab = null;
if (hashes.length == 2 && hashes[1] != ""){
$("#tabContaier>ul li").each(function() { $("#tabContaier>ul li").each(function() {
var $tab = $(this).find("a"); 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]){ if ($tab.attr("href") == "#"+hashes[1]){
$tab.addClass("active"); $tab.addClass("active");
$("#"+hashes[1]).show(); $("#"+hashes[1]).show();
previoustab = "#"+hashes[1];
} }
//alert($(this).attr("href")); //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 $("#tabContaier ul li a").click(function(){ //Fire the click event
if($(this).hasClass('active')){ if($(this).hasClass('active')){
return; return;
} }
fromheight = $(previoustab).height();
var activeTab = $(this).attr("href"); // Catch the click link 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 $("#tabContaier ul li a").removeClass("active"); // Remove pre-highlighted link
$(this).addClass("active"); // set clicked link to highlight state $(this).addClass("active"); // set clicked link to highlight state
$(".tabContents").hide(); // hide currently visible tab content div $(".tabContents").hide(); // hide currently visible tab content div
$(activeTab).fadeIn(); // show the target tab content div by matching clicked link. $(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 @@ ...@@ -3,47 +3,67 @@
// //
(function ($, document, window) { (function ($, document, window) {
var isShow = null;
var showDelayTimer = null;
$.extend($.fn, { $.extend($.fn, {
Popup: function(msg, option) { Popup: function(msg, option) {
var h;
if (option.type == undefined) option.type = "info"; if (option.type == undefined) option.type = "info";
if (option.closebtn == undefined) option.closebtn = false; if (option.closebtn == undefined) option.closebtn = false;
if (option.duration == undefined) option.duration = 0; if (option.duration == undefined) option.duration = 0;
if (option.load == undefined) option.load = false; if (option.load == undefined) option.load = false;
$box = $(this); $box = $(this);
$box.empty(); if(showDelayTimer){clearTimeout(showDelayTimer);}
$box.css('top','-1000px'); if(isShow){
$box.show(); $box.fadeOut('normal', function() {
$box.append('<div><table id="bcontent"><tr>' + setupBox();
'<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");
});
} }
else{ else{setupBox();}
$box.css('top', + ($(window).scrollTop() - h) +'px'); function setupBox(){
$box.animate({ top:"+=" + h + "px" }, "slow"); $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){ function showBox(){
setTimeout(function(){ if(option.load){
close(); $(window).load(function(){
}, option.duration); $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(){ function close(){
$box.animate({ top:"-=" + h + "px" }, "slow", function(){ $box.animate({ top:"-=" + h + "px" }, "slow", function(){
$box.fadeOut("normal"); $box.fadeOut("normal", function() {
}); isShow = false;
});
});
} }
} }
}); });
}(jQuery, document, this)); }(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*/ /*Common javascript function*/
String.prototype.toHtmlChar = function(){ String.prototype.toHtmlChar = function(){
c = {'<':'&lt;', '>':'&gt;', '&':'&amp;', '"':'&quot;', "'":'&#039;', var c = {'<':'&lt;', '>':'&gt;', '&':'&amp;', '"':'&quot;', "'":'&#039;',
'#':'&#035;' }; '#':'&#035;' };
return this.replace( /[<&>'"#]/g, function(s) { return c[s]; } ); return this.replace( /[<&>'"#]/g, function(s) { return c[s]; } );
} }
...@@ -8,23 +8,50 @@ String.prototype.trim = function () { ...@@ -8,23 +8,50 @@ String.prototype.trim = function () {
return this.replace(/^\s*/, "").replace(/\s*$/, ""); return this.replace(/^\s*/, "").replace(/\s*$/, "");
} }
/**************************/
$(document).ready(function() { /****************************************/
$('input[type="text"]').addClass("idleField"); function setInput($elt) {
$('input[type="text"]').focus(function() { if(!$elt){var $elt = $('input[type="text"], input[type="password"]');}
$(this).removeClass("idleField").addClass("focusField"); $elt.addClass("idleField");
if (this.value == this.defaultValue){ $elt.focus(function() {
this.value = ''; $(this).removeClass("idleField").addClass("focusField");
} if (this.value == this.defaultValue){
if(this.value != this.defaultValue){ this.value = '';
this.select(); }
} if(this.value != this.defaultValue){
}); this.select();
$('input[type="text"]').blur(function() { }
$(this).removeClass("focusField").addClass("idleField"); });
if ($.trim(this.value) == ''){ $elt.blur(function() {
this.value = (this.defaultValue ? this.defaultValue : ''); $(this).removeClass("focusField").addClass("idleField");
} if ($.trim(this.value) == ''){
}); this.value = (this.defaultValue ? this.defaultValue : '');
}); }
\ No newline at end of file });
}
/**************************/
(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() { $(document).ready( function() {
var send = false; var send = false;
var cloneRequest; 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); selectFile(file);
}); });
configRadio(); configRadio();
...@@ -22,12 +22,12 @@ $(document).ready( function() { ...@@ -22,12 +22,12 @@ $(document).ready( function() {
$("#clone").append("Clone"); $("#clone").append("Clone");
send = false; send = false;
return; return;
} }
var repo_url = $("input#repo").val(); var repo_url = $("input#repo").val();
var email = ""; var email = "";
var name = "" var name = ""
/* /^(ht|f)tps?:\/\/[a-z0-9-\.]+\.[a-z]{2,4}\/?([^\s<>\#%"\,\{\}\\|\\\^\[\]`]+)?$/ */ /* /^(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}); $("#error").Popup("Invalid url for the repository", {type:'alert', duration:3000});
return false; return false;
} }
...@@ -35,7 +35,7 @@ $(document).ready( function() { ...@@ -35,7 +35,7 @@ $(document).ready( function() {
$("#error").Popup("Invalid project name", {type:'alert', duration:3000}); $("#error").Popup("Invalid project name", {type:'alert', duration:3000});
return false; return false;
} }
if($("input#user").val() != "" && $("input#user").val() != "Enter your name..."){ if($("input#user").val() !== ""){
name = $("input#user").val(); name = $("input#user").val();
} }
if($("input#email").val() != "" && $("input#email").val() != "Enter your email adress..."){ if($("input#email").val() != "" && $("input#email").val() != "Enter your email adress..."){
...@@ -86,7 +86,7 @@ $(document).ready( function() { ...@@ -86,7 +86,7 @@ $(document).ready( function() {
$("#error").Popup("Your repository is cloned!", {type:'confirm', duration:3000}); $("#error").Popup("Your repository is cloned!", {type:'confirm', duration:3000});
$("input#repo").val("Enter the url of your repository..."); $("input#repo").val("Enter the url of your repository...");
$("input#name").val("Enter the project name..."); $("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); selectFile(file);
}); });
} }
...@@ -97,15 +97,21 @@ $(document).ready( function() { ...@@ -97,15 +97,21 @@ $(document).ready( function() {
$("#clone").empty(); $("#clone").empty();
$("#clone").append("Clone"); $("#clone").append("Clone");
send = false; 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; return false;
}); });
function configRadio(){ function configRadio(){
$("#modelist li").each(function(index) { $("#modelist li").each(function(index) {
var boxselector = "#box" + index; var boxselector = "#box" + index;
if($(this).hasClass('checked')){ if($(this).hasClass('checked')){
$(this).removeClass('checked'); $(this).removeClass('checked');
$(boxselector).slideUp("normal"); $(boxselector).slideUp("normal");
} }
if($(this).find("input:radio").is(':checked')){ if($(this).find("input:radio").is(':checked')){
...@@ -119,7 +125,7 @@ $(document).ready( function() { ...@@ -119,7 +125,7 @@ $(document).ready( function() {
} }
}); });
} }
function selectFile(file){ function selectFile(file){
//nothing //nothing
return; return;
......
...@@ -4,28 +4,130 @@ $(document).ready( function() { ...@@ -4,28 +4,130 @@ $(document).ready( function() {
$($("#slappart li")[0]).find("input:radio").attr('checked', true); $($("#slappart li")[0]).find("input:radio").attr('checked', true);
$(".menu-box-right>div").css('min-height', $("#slappart li").length*26 + 20 + "px"); $(".menu-box-right>div").css('min-height', $("#slappart li").length*26 + 20 + "px");
configRadio(); configRadio();
var send = false;
var lastli = null; var lastli = null;
var partitionAmount = $("imput#partitionAmount").val(); var partitionAmount = $("input#partitionAmount").val();
$("#slappart li").each(function(){ $("#slappart li").each(function(){
lastli = $(this); lastli = $(this);
$(this).find("input:radio").change(function(){ $(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(); var root = $("input#root").val();
if (root == "") return; 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, $('#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){ }, function(file){
//User have double click on file in to the fileTree //User have double click on file in to the fileTree
viewFile(file); viewFile(file);
}); });
} }
function viewFile(file){ function viewFile(file){
//User have double click on file in to the fileTree //User have double click on file in to the fileTree
loadFileContent(file); loadFileContent(file);
...@@ -49,7 +151,7 @@ $(document).ready( function() { ...@@ -49,7 +151,7 @@ $(document).ready( function() {
} }
}); });
}); });
function loadFileContent(file){ function loadFileContent(file){
$.ajax({ $.ajax({
type: "POST", type: "POST",
...@@ -62,14 +164,14 @@ $(document).ready( function() { ...@@ -62,14 +164,14 @@ $(document).ready( function() {
type: "POST", type: "POST",
url: $SCRIPT_ROOT + '/getFileContent', url: $SCRIPT_ROOT + '/getFileContent',
data: {file:file, truncate:1500}, data: {file:file, truncate:1500},
success: function(data){ success: function(data){
if(data.code == 1){ if(data.code == 1){
$("#inline_content").empty(); $("#inline_content").empty();
$("#inline_content").append('<h2 style="color: #4c6172; font: 18px \'Helvetica Neue\', Helvetica, Arial, sans-serif;">Inspect Instance Content: ' + $("#inline_content").append('<h2 style="color: #4c6172; font: 18px \'Helvetica Neue\', Helvetica, Arial, sans-serif;">Inspect Instance Content: ' +
file +'</h2>'); file +'</h2>');
$("#inline_content").append('<br/><div class="main_content"><pre id="editor"></pre></div>'); $("#inline_content").append('<br/><div class="main_content"><pre id="editor"></pre></div>');
setupEditor(); setupEditor();
$(".inline").colorbox({inline:true, width: "847px", onComplete:function(){ $(".inline").colorbox({inline:true, width: "847px", onComplete:function(){
editor.getSession().setValue(data.result); editor.getSession().setValue(data.result);
}}); }});
$(".inline").click(); $(".inline").click();
...@@ -91,10 +193,74 @@ $(document).ready( function() { ...@@ -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(){ function configRadio(){
$("#slappart li").each(function() { $("#slappart li").each(function() {
var $radio = $(this).find("input:radio"); var $radio = $(this).find("input:radio");
var boxselector = "#box" + $radio.attr('id'); var boxselector = "#box" + $radio.attr('id');
if($(this).hasClass('checked')){ if($(this).hasClass('checked')){
$(this).removeClass('checked'); $(this).removeClass('checked');
$(boxselector).slideUp("normal"); $(boxselector).slideUp("normal");
...@@ -103,11 +269,24 @@ $(document).ready( function() { ...@@ -103,11 +269,24 @@ $(document).ready( function() {
$(this).addClass('checked'); $(this).addClass('checked');
//change content here //change content here
$(boxselector).slideDown("normal"); $(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 = ace.edit("editor");
editor.setTheme("ace/theme/crimson_editor"); editor.setTheme("ace/theme/crimson_editor");
...@@ -116,14 +295,19 @@ $(document).ready( function() { ...@@ -116,14 +295,19 @@ $(document).ready( function() {
editor.getSession().setTabSize(2); editor.getSession().setTabSize(2);
editor.getSession().setUseSoftTabs(true); editor.getSession().setUseSoftTabs(true);
editor.renderer.setHScrollBarAlwaysVisible(false); editor.renderer.setHScrollBarAlwaysVisible(false);
editor.setReadOnly(true); if(!editable){editor.setReadOnly(true);}
} }
function setupSlappart(){ function setupSlappart(){
for(var i=0; i<partitionAmount; i++){ for(var i=0; i<partitionAmount; i++){
var elt = $("#slappart"+i+"Parameter"); var elt = $("#slappart"+i+"Parameter");
if(elt != undefined) elt.click(function(){ var fileId = $("#slappart"+i+"Files");
alert(elt.attr('id')); 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() { ...@@ -9,27 +9,33 @@ $(document).ready( function() {
$("#info").append("Please select your file or folder into the box..."); $("#info").append("Please select your file or folder into the box...");
fillContent(); fillContent();
}); });
function selectFile(file){ function selectFile(file){
var relativeFile = file.replace(runnerDir + "/" + $("#softwarelist").val(), ""); var relativeFile = file.replace(runnerDir + "/" + $("#softwarelist").val(), "");
$("#info").empty(); $("#info").empty();
$("#info").append("Selection: " + relativeFile); $("#info").append("Selection: " + relativeFile);
return; return;
} }
function fillContent(){ function fillContent(){
var folder = $("#softwarelist").val(); var folder = $("#softwarelist").val();
var elt = $("option:selected", $("#softwarelist")); var elt = $("option:selected", $("#softwarelist"));
$('#fileTree').fileTree({ root: runnerDir + "/" + folder, script: $SCRIPT_ROOT + '/readFolder', if(elt.val() !== "No Software Release found"){
folderEvent: 'click', expandSpeed: 750, collapseSpeed: 750, multiFolder: false, selectFolder: true }, function(file) { $('#fileTree').fileTree({ root: runnerDir + "/" + folder, script: $SCRIPT_ROOT + '/readFolder',
selectFile(file); folderEvent: 'click', expandSpeed: 750, collapseSpeed: 750, multiFolder: false, selectFolder: true }, function(file) {
}, function(file){ viewFile(file)}); selectFile(file);
$("#softcontent").empty(); }, function(file){ viewFile(file)});
$("#softcontent").append("File content: " + elt.attr('title')); $("#softcontent").empty();
$("#softcontent").append("File content: " + elt.attr('title'));
}
} }
$("#open").click(function(){ $("#open").click(function(){
var elt = $("option:selected", $("#softwarelist")); 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({ $.ajax({
type: "POST", type: "POST",
url: $SCRIPT_ROOT + '/setCurrentProject', url: $SCRIPT_ROOT + '/setCurrentProject',
...@@ -39,14 +45,18 @@ $(document).ready( function() { ...@@ -39,14 +45,18 @@ $(document).ready( function() {
location.href = $SCRIPT_ROOT + '/editSoftwareProfile' location.href = $SCRIPT_ROOT + '/editSoftwareProfile'
} }
else{ else{
$("#error").Popup(data.result, {type:'error'}); $("#error").Popup(data.result, {type:'error', duration:5000});
} }
} }
}); });
return false; return false;
}); });
$("#delete").click(function(){ $("#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; if(send) return;
send = false; send = false;
$.ajax({ $.ajax({
...@@ -55,21 +65,27 @@ $(document).ready( function() { ...@@ -55,21 +65,27 @@ $(document).ready( function() {
data: "name=" + $("#softwarelist").val(), data: "name=" + $("#softwarelist").val(),
success: function(data){ success: function(data){
if(data.code == 1){ if(data.code == 1){
var folder = $("#softwarelist").val(); 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)});
$("input#file").val(""); $("input#file").val("");
$("#info").empty(); $("#info").empty();
$("#info").append("Please select your file or folder into the box..."); $("#info").append("Please select your file or folder into the box...");
$("#softwarelist").empty(); $("#softwarelist").empty();
for(i=0; i<data.result.length; i++){ for(i=0; i<data.result.length; i++){
$("#softwarelist").append('<option value="' + data.result[i]["md5"] + $("#softwarelist").append('<option value="' + data.result[i]["md5"] +
'" title="' + data.result[i]["title"] +'" rel="' + '" title="' + data.result[i]["title"] +'" rel="' +
data.result[i]["path"] +'">' + data.result[i]["title"] + '</option>'); 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}); $("#error").Popup("Operation complete, Selected Software Release has been delete!", {type:'confirm', duration:5000});
} }
else{ else{
...@@ -78,15 +94,15 @@ $(document).ready( function() { ...@@ -78,15 +94,15 @@ $(document).ready( function() {
send = false; send = false;
} }
}); });
return false; return false;
}); });
function viewFile(file){ function viewFile(file){
//User have double click on file in to the fileTree //User have double click on file in to the fileTree
var name = file.replace(runnerDir + "/" + $("#softwarelist").val(), "/software"); var name = file.replace(runnerDir + "/" + $("#softwarelist").val(), "/software");
loadFileContent(file, name); loadFileContent(file, name);
} }
function loadFileContent(file, filename){ function loadFileContent(file, filename){
$.ajax({ $.ajax({
type: "POST", type: "POST",
...@@ -99,14 +115,14 @@ $(document).ready( function() { ...@@ -99,14 +115,14 @@ $(document).ready( function() {
type: "POST", type: "POST",
url: $SCRIPT_ROOT + '/getFileContent', url: $SCRIPT_ROOT + '/getFileContent',
data: {file:file, truncate:1500}, data: {file:file, truncate:1500},
success: function(data){ success: function(data){
if(data.code == 1){ if(data.code == 1){
$("#inline_content").empty(); $("#inline_content").empty();
$("#inline_content").append('<h2 style="color: #4c6172; font: 18px \'Helvetica Neue\', Helvetica, Arial, sans-serif;">Inspect Software Content: ' + $("#inline_content").append('<h2 style="color: #4c6172; font: 18px \'Helvetica Neue\', Helvetica, Arial, sans-serif;">Inspect Software Content: ' +
filename +'</h2>'); filename +'</h2>');
$("#inline_content").append('<br/><div class="main_content"><pre id="editor"></pre></div>'); $("#inline_content").append('<br/><div class="main_content"><pre id="editor"></pre></div>');
setupEditor(); setupEditor();
$(".inline").colorbox({inline:true, width: "847px", onComplete:function(){ $(".inline").colorbox({inline:true, width: "847px", onComplete:function(){
editor.getSession().setValue(data.result); editor.getSession().setValue(data.result);
}}); }});
$(".inline").click(); $(".inline").click();
...@@ -128,11 +144,11 @@ $(document).ready( function() { ...@@ -128,11 +144,11 @@ $(document).ready( function() {
} }
}); });
} }
function setupEditor(){ function setupEditor(){
editor = ace.edit("editor"); editor = ace.edit("editor");
editor.setTheme("ace/theme/crimson_editor"); editor.setTheme("ace/theme/crimson_editor");
var CurentMode = require("ace/mode/text").Mode; var CurentMode = require("ace/mode/text").Mode;
editor.getSession().setMode(new CurentMode()); editor.getSession().setMode(new CurentMode());
editor.getSession().setTabSize(2); 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){ ...@@ -106,7 +106,7 @@ function setRunningState(data){
$current.css("color", "#000"); $current.css("color", "#000");
$current = undefined; $current = undefined;
currentState = false; 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; 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 @@ ...@@ -3,64 +3,77 @@
{% block head %} {% block head %}
{{ super() }} {{ 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/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/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/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 %} {% endblock %}
{% block body %} {% block body %}
<h2>Clone a repository into your workspace</h2><br/>
<h2 class='title'>Clone your repository into the workspace</h2><br/> <div id="tabContaier">
<div id="repository" style="margin-left:40px;"> <ul>
<label for='name'>Project name*: </label> <li><a href="#tab1" class="active">Clone your repository</a></li>
<input type="text" name="name" id="name" size='20' value="Enter the project name..." /> <li><a href="#tab2">Manage your project folder</a></li>
<label for='repo'>&nbsp;url*: &nbsp;&nbsp;</label> </ul><!-- //Tab buttons -->
<input type="text" name="repo" id="repo" size='25' value="Enter the url of your repository..." /><br/> <div class="tabDetails">
<label for='user'>Your name: &nbsp;&nbsp;&nbsp;&nbsp;</label> <div id="tab1" class="tabContents">
<input type="text" name="user" id="user" size='20' value="Enter your name..." /> <div id="repository" style="margin-left:40px;">
<label for='email'>Email: </label> <label for='name'>Project name*: </label>
<input type="text" name="email" id="email" size='25' value="Enter your email adress..." /> <input type="text" name="name" id="name" size='20' value="Enter the project name..." />
<input type="hidden" name="workdir" id="workdir" value="{{workDir}}" /> <label for='repo'>&nbsp;url*: &nbsp;&nbsp;</label>
<button class="button" id="clone">clone</button> <input type="text" name="repo" id="repo" size='25' value="Enter the url of your repository..." /><br/>
<img class="waitting" id="imgwaitting" src="{{ url_for('static', filename='images/waiting.gif') }}" alt="" /> <label for='user'>Your name: &nbsp;&nbsp;&nbsp;&nbsp;</label>
<br/><br/> <input type="text" name="user" id="user" size='20' value="{{name}}" />
</div> <label for='email'>Email: </label>
<h2>Set your Security Mode</h2> <input type="text" name="email" id="email" size='25' value="{% if not email %}Enter your email adress...{% else %}{{email}}{%endif%}" />
<div class="menu-box-right"> <input type="hidden" name="workdir" id="workdir" value="{{workDir}}" />
<div style="background:#fff; padding:10px; min-height:100px; font-size:14px;"> <button class="button" id="clone">clone</button>
<div id="box0"> <img class="waitting" id="imgwaitting" src="{{ url_for('static', filename='images/waiting.gif') }}" alt="" />
<h2>Clone Repository without using HTTPS and SSH</h2><br/> <br/><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 </div>
use SSH authentication. Otherwise use your public key or your login and password to clone your project by choosing https or ssh mode. Note <br/>
that, with readonly mode you can not be able to push your changes.</p> <h2>Set your Security Mode</h2>
<br/> <div class="menu-box-right" style="width: 592px;">
</div> <div style="background:#fff; padding:10px; min-height:100px; font-size:14px;">
<div id="box1" style="display:none"> <div id="box0">
<h2>You can use this public key to setup your repository</h2><br/> <h2>Clone Repository without using HTTPS and SSH</h2><br/>
<textarea class="public_key" readonly> <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
{{public_key}} use SSH authentication. Otherwise use your public key or your login and password to clone your project by choosing https or ssh mode. Note
</textarea> that, with readonly mode you can not be able to push your changes.</p>
</div> <br/>
<div id="box2" style="display:none;"> </div>
<h2>Enter your username and password for https authentication access</h2><br/> <div id="box1" style="display:none">
<div style="margin-left:80px; margin-bottom:20px;"> <h2>You can use this public key to setup your repository</h2><br/>
<label for='username'>Your username:&nbsp;&nbsp;</label> <textarea class="mb_style public_key" readonly>
<input type="text" name="username" id="username" size='20' value="Enter your username..." /><br/><br/> {{public_key}}
<label for='password'>Your password: &nbsp;&nbsp;</label> </textarea>
<input type="password" name="password" id="password" size='20' value="" class="idleField" /> </div>
</div> <div id="box2" style="display:none;">
<p></p> <h2>Enter your username and password for https authentication access</h2><br/>
</div> <div style="margin-left:80px; margin-bottom:20px;">
</div> <label for='username'>Your username:&nbsp;&nbsp;</label>
</div> <input type="text" name="username" id="username" size='20' value="Enter your username..." /><br/><br/>
<div class="menu-box-left"> <label for='password'>Your password: &nbsp;&nbsp;</label>
<ul id="modelist"> <input type="password" name="password" id="password" size='20' value="" class="idleField" />
<li class="checked"><input type="radio" name="security" id="nothing" value="nothing" /><label for="nothing">ReadOnly</label></li> </div>
<li><input type="radio" name="security" id="ssh" value="SSH" checked /><label for="ssh">SSH Mode</label></li> <p></p>
<li style="border-bottom:none"><input type="radio" name="security" id="https" value="HTTPS" /><label for="https">Https Mode</label></li> </div>
</ul> </div>
</div> </div>
<div class="clear"></div><br/> <div class="menu-box-left" style="width: 115px;">
<div id="file_navigation"> <ul id="modelist">
<h2 class='title'>Your project folder</h2><br/> <li class="checked"><input type="radio" name="security" id="nothing" value="nothing" /><label for="nothing">ReadOnly</label></li>
<div id="fileTree" class="file_tree"></div> <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> </div>
{% endblock %} {% endblock %}
{% extends "layout.html" %} {% extends "layout.html" %}
{% block title %}SlapOs buildout web based runner {% endblock %} {% block title %}SlapOS buildout web based runner {% endblock %}
{% block body %} {% block body %}
<div id="home_box"> <div id="home_box">
<div class="inner_box"> <div class="inner_box">
...@@ -23,14 +23,20 @@ ...@@ -23,14 +23,20 @@
<img src="{{ url_for('static', filename='images/manage_repo.png') }}" /> <img src="{{ url_for('static', filename='images/manage_repo.png') }}" />
</div> </div>
<div class="clear"></div> <div class="clear"></div>
<div class="lmenu"> <div class="lmenu smaller">
<h2><a href="{{ url_for('openProject', method='new')}}">Create your new Software Release</a></h2> <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 <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. run the new software release.-->
</p> </p>
<img src="{{ url_for('static', filename='images/folder_blue.png') }}" /> <img src="{{ url_for('static', filename='images/folder_blue.png') }}" />
<div class="clear"></div> <div class="clear"></div>
</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>
</div> </div>
{% endblock %} {% endblock %}
...@@ -19,14 +19,15 @@ ...@@ -19,14 +19,15 @@
<div id="tabContaier"> <div id="tabContaier">
<ul> <ul>
<li><a href="#tab1" class="active">Slapgrid Supervisor</a></li> <li><a href="#tab1" class="active">Slapgrid Supervisor</a></li>
<li><a href="#tab2">SLAP Response & Parameters</a></li> <li><a href="#tab2">SLAP Response</a></li>
<li><a href="#tab3">Partitions Content</a></li> <li><a href="#tab3" id="parameterTab">Parameters</a></li>
<li><a href="#tab4" id="instancetabfiles">Partitions Content</a></li>
</ul><!-- //Tab buttons --> </ul><!-- //Tab buttons -->
<div class="tabDetails"> <div class="tabDetails">
<div id="tab1" class="tabContents"> <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> <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 %} {% if supervisor %}
<table cellpadding="0" cellspacing="0" width="100%"> <table cellpadding="0" cellspacing="0" width="100%" id="supervisordcontent">
<tr> <tr>
<th>Partition and Process name</th><th>Status</th><th>Process PID </th><th> UpTime</th><th></th> <th>Partition and Process name</th><th>Status</th><th>Process PID </th><th> UpTime</th><th></th>
</tr> </tr>
...@@ -39,7 +40,10 @@ ...@@ -39,7 +40,10 @@
</tr> </tr>
{% endfor %} {% endfor %}
</table><br/> </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 %} {% else %}
<h2>No process to display, please run your instance</h2> <h2>No process to display, please run your instance</h2>
{%endif%} {%endif%}
...@@ -52,13 +56,13 @@ ...@@ -52,13 +56,13 @@
{% for item in slap_status %} {% for item in slap_status %}
<div id="box{{item[0]}}" style="display:none;"> <div id="box{{item[0]}}" style="display:none;">
{% if item[1] %} {% if item[1] %}
<!--<h2><span style="float:left; margin-left:10px;" id="{{item[0]}}title">Slap Response for {{item[0]}}</span> <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> <a href="#" id="{{item[0]}}Parameter" rel="{{item[0]}}" class="lshare simple" style="float:right" title='Restart all partition process'>Restart</a>
</h2>--> <a href="#" id="{{item[0]}}Files" rel="{{item[0]}}" class="lshare simple no-right-border" style="float:right">Files</a>
<h2>Slap Response for {{item[0]}}</h2> </h2>
<div class="clear"></div><br/> <div class="clear"></div><br/>
<div id="bcontent{{item[0]}}"> <div id="bcontent{{item[0]}}">
<table cellpadding="0" cellspacing="0" width="100%"> <table cellpadding="0" cellspacing="0" width="100%">
<tr> <tr>
<th>Parameter Name</th><th>Parameter Value</th> <th>Parameter Name</th><th>Parameter Value</th>
</tr> </tr>
...@@ -67,9 +71,9 @@ ...@@ -67,9 +71,9 @@
<td class="propertie first">{{k}}</td><td align='left'>{{item[1][k]}}</td> <td class="propertie first">{{k}}</td><td align='left'>{{item[1][k]}}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
</div> </div>
{% else %} {% else %}
<h2>Empty Partition</h2></br> <h2>Empty Partition</h2></br>
<center><img alt="" src="{{ url_for('static', filename='images/empty.png') }}" /></center> <center><img alt="" src="{{ url_for('static', filename='images/empty.png') }}" /></center>
<br/><h2>Partition {{item[0]}} is still empty</h2> <br/><h2>Partition {{item[0]}} is still empty</h2>
...@@ -82,8 +86,8 @@ ...@@ -82,8 +86,8 @@
<ul id="slappart"> <ul id="slappart">
{% for item in slap_status %} {% for item in slap_status %}
<li><input type="radio" name="slapresponse" id="{{item[0]}}" value="{{item[0]}}" /> <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> <label for="{{item[0]}}" {% if item[1] %}style="font-weight:bold"{%endif%}>{{item[0]}}</label></li>
{% endfor %} {% endfor %}
</ul> </ul>
</div> </div>
<div class="clear"></div><br/> <div class="clear"></div><br/>
...@@ -92,8 +96,39 @@ ...@@ -92,8 +96,39 @@
{%endif%} {%endif%}
</div><!-- end tab2 --> </div><!-- end tab2 -->
<div id="tab3" class="tabContents"> <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><!-- 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>
</div> </div>
<!-- This contains the hidden content for inline calls --> <!-- This contains the hidden content for inline calls -->
...@@ -103,4 +138,5 @@ ...@@ -103,4 +138,5 @@
</div> </div>
</div> </div>
{{instance}}
{% endblock %} {% endblock %}
...@@ -19,10 +19,16 @@ ...@@ -19,10 +19,16 @@
<script type=text/javascript> <script type=text/javascript>
$SCRIPT_ROOT = {{ request.script_root|tojson|safe }}; $SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
</script> </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/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 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() { $(document).ready(function() {
if($("input#fmsg").val() != ""){ if($("input#fmsg").val() != ""){
$("#error").Popup($("input#fmsg").val(), {type:'info', duration:5000, load:true}); $("#error").Popup($("input#fmsg").val(), {type:'info', duration:5000, load:true});
...@@ -33,20 +39,22 @@ ...@@ -33,20 +39,22 @@
} }
}); });
</script> </script>
{%endif%}
{% endblock %} {% endblock %}
</head> </head>
<body> <body>
<div id="error"></div> <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" /> <input type="hidden" name="fmsg" value="{{ get_flashed_messages()[0] }}" id="fmsg" />
<!--<div id="logo">--> {% if request.path != '/login' %}
<!--<a href="{{ url_for('home') }}"><img src="{{ url_for('static', filename='images/logo.png') }}" alt="" /></a>-->
<!--</div>-->
<div id="header"> <div id="header">
<div class="block_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> <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> <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> <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> <div class="line"></div>
<h2 class="info">{% block title %}{% endblock %} - {{session.title}}</h2> <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') }}" <div class="run"><span id="running" style="display:none"><img alt="" src="{{ url_for('static', filename='images/ajax_roller.gif') }}"
...@@ -71,24 +79,27 @@ ...@@ -71,24 +79,27 @@
</div> </div>
<div class="clear"></div> <div class="clear"></div>
</div> </div>
<div id="main"> {% endif %}
<div {% if request.path == '/' %} class="home_content" {%else%} id="content" {% endif %}> <div {% if request.path != '/login' %}id="main"{% endif %}>
{% if request.path != '/' %} <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 class="main_head">
</div> </div>
<div class="content"> <div class="content">
{% endif %} {% endif %}
{% block body %}{% endblock %} {% block body %}{% endblock %}
{% if request.path != '/' %} {% if request.path != '/' and request.path != '/login'%}
</div> </div>
<div class="main_foot"> <div class="main_foot">
</div> </div>
{% endif %} {% endif %}
</div> </div>
</div> </div>
{% if request.path != '/login' %}
<div id="footer"> <div id="footer">
SlapOs web runner &copy; Vifib SARL 2011 - All right reserved - Creative Commons Shared Alike Non Commercial SlapOs web runner &copy; Vifib SARL 2011 - All right reserved - Creative Commons Shared Alike Non Commercial
</div> </div>
{%endif%}
</div> </div>
<div class="popup"> <div class="popup">
<table id="dpop" cellpadding="0" border="0"> <table id="dpop" cellpadding="0" border="0">
...@@ -110,11 +121,11 @@ ...@@ -110,11 +121,11 @@
<div id="tooltip-home" style="display:none"> <div id="tooltip-home" style="display:none">
<span style="font-weight:bold">QUICK ACCESS TO MENU</span><br/><br/> <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> <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('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='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> </ul>
</div> </div>
</body> </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 @@ ...@@ -14,7 +14,7 @@
<input type="hidden" name="method" id="method" value="{{method}}" /> <input type="hidden" name="method" id="method" value="{{method}}" />
{% if method == "new" %} {% if method == "new" %}
<div id="addsoftware"> <div id="addsoftware">
<h2>Create your software release</h2> <h2 class="title">Create your software release</h2><br/>
<label for='software'>Name: </label> <label for='software'>Name: </label>
<input type="text" name="software" id="software" size='30' value="Enter software name..." /> <input type="text" name="software" id="software" size='30' value="Enter software name..." />
<br/><br/> <br/><br/>
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
</div> </div>
{% elif method == "open" %} {% elif method == "open" %}
<div id="openSoftware"> <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="fileTree" class="file_tree"></div>
<div id="file_info" class="file_info"> <div id="file_info" class="file_info">
<img src="{{ url_for('static', filename='images/check.png') }}" class="check" id="check" alt=""/> <img src="{{ url_for('static', filename='images/check.png') }}" class="check" id="check" alt=""/>
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
<input type="hidden" name="runnerdir" id="runnerdir" value="{{softwareRoot}}" /> <input type="hidden" name="runnerdir" id="runnerdir" value="{{softwareRoot}}" />
<label for='softwarelist'>Select software: </label> <label for='softwarelist'>Select software: </label>
<select name="softwarelist" id="softwarelist"> <select name="softwarelist" id="softwarelist">
{%if not softwares %}<option >No Software Release found</option>{% endif %}
{%for soft in softwares %} {%for soft in softwares %}
<option value="{{soft['md5']}}" title="{{soft['title']}}" rel="{{soft['path']}}">{{soft['title']}}</option> <option value="{{soft['md5']}}" title="{{soft['title']}}" rel="{{soft['path']}}">{{soft['title']}}</option>
{%endfor%} {%endfor%}
...@@ -23,8 +24,8 @@ ...@@ -23,8 +24,8 @@
&nbsp;&nbsp;<button id ="delete" class="button" title="Remove this software">Remove</button> &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> &nbsp;&nbsp;<button id ="open" class="button" title="Set this software as current software release">Open</button>
<br/><br/> <br/><br/>
<h2 id="softcontent">No content to displays</h2> <h2 id="softcontent">No file or folder to display</h2>
<div id="fileTree" class="file_tree" style='height:200px;'></div> <div id="fileTree" class="file_tree" style='height:200px;' title="Double click to open file"></div>
<div id="file_info" class="file_info"> <div id="file_info" class="file_info">
<span id="info">Please select your file or folder into the box...</span></div> <span id="info">Please select your file or folder into the box...</span></div>
<!-- This contains the hidden content for inline calls --> <!-- This contains the hidden content for inline calls -->
......
# -*- coding: utf-8 -*-
import slapos.slap import slapos.slap
import time import time
import subprocess import subprocess
...@@ -11,6 +13,7 @@ import shutil ...@@ -11,6 +13,7 @@ import shutil
import string import string
import hashlib import hashlib
import signal import signal
import multiprocessing
...@@ -32,13 +35,91 @@ html_escape_table = { ...@@ -32,13 +35,91 @@ html_escape_table = {
">": "&gt;", ">": "&gt;",
"<": "&lt;", "<": "&lt;",
} }
def html_escape(text): def html_escape(text):
"""Produce entities within text.""" """Produce entities within text."""
return "".join(html_escape_table.get(c,c) for c in 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): 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']): if not os.path.exists(config['instance_root']):
os.mkdir(config['instance_root']) os.mkdir(config['instance_root'])
slap = slapos.slap.slap() slap = slapos.slap.slap()
...@@ -59,8 +140,7 @@ def updateProxy(config): ...@@ -59,8 +140,7 @@ def updateProxy(config):
'address': config['ipv4_address'], 'address': config['ipv4_address'],
'instance_root': config['instance_root'], 'instance_root': config['instance_root'],
'netmask': '255.255.255.255', 'netmask': '255.255.255.255',
'partition_list': [ 'partition_list': [],
],
'reference': config['computer_id'], 'reference': config['computer_id'],
'software_root': config['software_root']} 'software_root': config['software_root']}
for i in xrange(0, int(config['partition_amount'])): for i in xrange(0, int(config['partition_amount'])):
...@@ -81,19 +161,17 @@ def updateProxy(config): ...@@ -81,19 +161,17 @@ def updateProxy(config):
#get instance parameter #get instance parameter
param_path = os.path.join(config['runner_workdir'], ".parameter.xml") param_path = os.path.join(config['runner_workdir'], ".parameter.xml")
xml_result = readParameters(param_path) xml_result = readParameters(param_path)
partition_parameter_kw = None
if type(xml_result) != type('') and xml_result.has_key('instance'): if type(xml_result) != type('') and xml_result.has_key('instance'):
partition_parameter_kw = xml_result['instance'] partition_parameter_kw = xml_result['instance']
else:
partition_parameter_kw = None
computer.updateConfiguration(xml_marshaller.dumps(slap_config)) 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, partition_parameter_kw=partition_parameter_kw, software_type=None,
filter_kw=None, state=None, shared=False) filter_kw=None, state=None, shared=False)
#open(param_path, 'w').write(xml_marshaller.dumps(sr_request.
# getInstanceParameterDict()))
return True return True
def readPid(file): def readPid(file):
"""Read process pid from file `file`"""
if os.path.exists(file): if os.path.exists(file):
data = open(file).read().strip() data = open(file).read().strip()
try: try:
...@@ -104,10 +182,39 @@ def readPid(file): ...@@ -104,10 +182,39 @@ def readPid(file):
def writePid(file, pid): def writePid(file, pid):
"""Save process pid into a file `file`"""
open(file, 'w').write(str(pid)) 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): def startProxy(config):
"""Start Slapproxy server"""
proxy_pid = os.path.join(config['runner_workdir'], 'proxy.pid') proxy_pid = os.path.join(config['runner_workdir'], 'proxy.pid')
pid = readPid(proxy_pid) pid = readPid(proxy_pid)
running = False running = False
...@@ -126,6 +233,7 @@ def startProxy(config): ...@@ -126,6 +233,7 @@ def startProxy(config):
def stopProxy(config): def stopProxy(config):
"""Stop Slapproxy server"""
pid = readPid(os.path.join(config['runner_workdir'], 'proxy.pid')) pid = readPid(os.path.join(config['runner_workdir'], 'proxy.pid'))
if pid: if pid:
try: try:
...@@ -135,10 +243,15 @@ def stopProxy(config): ...@@ -135,10 +243,15 @@ def stopProxy(config):
def removeProxyDb(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']): if os.path.exists(config['database_uri']):
os.unlink(config['database_uri']) os.unlink(config['database_uri'])
def isSoftwareRunning(config): 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') slapgrid_pid = os.path.join(config['runner_workdir'], 'slapgrid-sr.pid')
pid = readPid(slapgrid_pid) pid = readPid(slapgrid_pid)
if pid: if pid:
...@@ -154,6 +267,10 @@ def isSoftwareRunning(config): ...@@ -154,6 +267,10 @@ def isSoftwareRunning(config):
def runSoftwareWithLock(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') slapgrid_pid = os.path.join(config['runner_workdir'], 'slapgrid-sr.pid')
if not isSoftwareRunning(config): if not isSoftwareRunning(config):
if not os.path.exists(config['software_root']): if not os.path.exists(config['software_root']):
...@@ -164,7 +281,12 @@ def runSoftwareWithLock(config): ...@@ -164,7 +281,12 @@ def runSoftwareWithLock(config):
logfile = open(config['software_log'], 'w') logfile = open(config['software_log'], 'w')
if not updateProxy(config): if not updateProxy(config):
return False 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) writePid(slapgrid_pid, slapgrid.pid)
slapgrid.wait() slapgrid.wait()
#Saves the current compile software for re-use #Saves the current compile software for re-use
...@@ -191,6 +313,9 @@ def runSoftwareWithLock(config): ...@@ -191,6 +313,9 @@ def runSoftwareWithLock(config):
def isInstanceRunning(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') slapgrid_pid = os.path.join(config['runner_workdir'], 'slapgrid-cp.pid')
pid = readPid(slapgrid_pid) pid = readPid(slapgrid_pid)
if pid: if pid:
...@@ -205,6 +330,7 @@ def isInstanceRunning(config): ...@@ -205,6 +330,7 @@ def isInstanceRunning(config):
return running return running
def killRunningSlapgrid(config, ptype): def killRunningSlapgrid(config, ptype):
"""Kill slapgrid process and all running children process"""
slapgrid_pid = os.path.join(config['runner_workdir'], ptype) slapgrid_pid = os.path.join(config['runner_workdir'], ptype)
pid = readPid(slapgrid_pid) pid = readPid(slapgrid_pid)
if pid: if pid:
...@@ -233,25 +359,43 @@ def pidppid(pid): ...@@ -233,25 +359,43 @@ def pidppid(pid):
return list(int(p) for p, pp in ppid if int(pp) == pid) return list(int(p) for p, pp in ppid if int(pp) == pid)
def runInstanceWithLock(config): 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') slapgrid_pid = os.path.join(config['runner_workdir'], 'slapgrid-cp.pid')
if not isInstanceRunning(config): if not isInstanceRunning(config):
startProxy(config) startProxy(config)
logfile = open(config['instance_log'], 'w') logfile = open(config['instance_log'], 'w')
if not updateProxy(config): if not updateProxy(config):
return False 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) writePid(slapgrid_pid, slapgrid.pid)
slapgrid.wait() slapgrid.wait()
return True return True
return False return False
def getProfilePath(peojectDir, profile): def getProfilePath(projectDir, profile):
if not os.path.exists(os.path.join(peojectDir, ".project")): """
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 return False
projectFolder = open(os.path.join(peojectDir, ".project")).read() projectFolder = open(os.path.join(projectDir, ".project")).read()
return os.path.join(projectFolder, profile) return os.path.join(projectFolder, profile)
def getSlapStatus(config): def getSlapStatus(config):
"""Return all Slapos Partitions with associate informations"""
slap = slapos.slap.slap() slap = slapos.slap.slap()
slap.initializeConnection(config['master_url']) slap.initializeConnection(config['master_url'])
partition_list = [] partition_list = []
...@@ -259,8 +403,7 @@ def getSlapStatus(config): ...@@ -259,8 +403,7 @@ def getSlapStatus(config):
try: try:
for partition in computer.getComputerPartitionList(): for partition in computer.getComputerPartitionList():
# Note: Internal use of API, as there is no reflexion interface in SLAP # Note: Internal use of API, as there is no reflexion interface in SLAP
partition_list.append((partition.getId(), partition._connection_dict.copy(), partition_list.append((partition.getId(), partition._connection_dict.copy()))
partition._parameter_dict.copy()))
except Exception: except Exception:
pass pass
if partition_list: if partition_list:
...@@ -283,10 +426,12 @@ def runBuildoutAnnotate(config): ...@@ -283,10 +426,12 @@ def runBuildoutAnnotate(config):
return False return False
def svcStopAll(config): def svcStopAll(config):
"""Stop all Instance process on this computer"""
return Popen([config['supervisor'], config['configuration_file_path'], return Popen([config['supervisor'], config['configuration_file_path'],
'shutdown']).communicate()[0] 'shutdown']).communicate()[0]
def removeInstanceRoot(config): def removeInstanceRoot(config):
"""Clean instance directory and stop all its running process"""
if os.path.exists(config['instance_root']): if os.path.exists(config['instance_root']):
svcStopAll(config) svcStopAll(config)
for root, dirs, files in os.walk(config['instance_root']): for root, dirs, files in os.walk(config['instance_root']):
...@@ -298,6 +443,7 @@ def removeInstanceRoot(config): ...@@ -298,6 +443,7 @@ def removeInstanceRoot(config):
shutil.rmtree(config['instance_root']) shutil.rmtree(config['instance_root'])
def getSvcStatus(config): def getSvcStatus(config):
"""Return all Softwares Instances process Informations"""
result = Popen([config['supervisor'], config['configuration_file_path'], result = Popen([config['supervisor'], config['configuration_file_path'],
'status']).communicate()[0] 'status']).communicate()[0]
regex = "(^unix:.+\.socket)|(^error:).*$" regex = "(^unix:.+\.socket)|(^error:).*$"
...@@ -311,15 +457,40 @@ def getSvcStatus(config): ...@@ -311,15 +457,40 @@ def getSvcStatus(config):
return supervisord return supervisord
def getSvcTailProcess(config, process): 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'], return Popen([config['supervisor'], config['configuration_file_path'],
"tail", process]).communicate()[0] "tail", process]).communicate()[0]
def svcStartStopProcess(config, process, action): 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"} cmd = {"RESTART":"restart", "STOPPED":"start", "RUNNING":"stop", "EXITED":"start", "STOP":"stop"}
return Popen([config['supervisor'], config['configuration_file_path'], return Popen([config['supervisor'], config['configuration_file_path'],
cmd[action], process]).communicate()[0] cmd[action], process]).communicate()[0]
def getFolderContent(config, folder): 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;">'] r=['<ul class="jqueryFileTree" style="display: none;">']
try: try:
folder = str(folder) folder = str(folder)
...@@ -347,6 +518,16 @@ def getFolderContent(config, folder): ...@@ -347,6 +518,16 @@ def getFolderContent(config, folder):
return jsonify(result=''.join(r)) return jsonify(result=''.join(r))
def getFolder(config, folder): 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;">'] r=['<ul class="jqueryFileTree" style="display: none;">']
try: try:
folder = str(folder) folder = str(folder)
...@@ -371,6 +552,13 @@ def getFolder(config, folder): ...@@ -371,6 +552,13 @@ def getFolder(config, folder):
return jsonify(result=''.join(r)) return jsonify(result=''.join(r))
def getProjectList(folder): 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 = []
project_list = sorted(os.listdir(folder), key=str.lower) project_list = sorted(os.listdir(folder), key=str.lower)
for elt in project_list: for elt in project_list:
...@@ -378,6 +566,14 @@ def getProjectList(folder): ...@@ -378,6 +566,14 @@ def getProjectList(folder):
return project return project
def configNewSR(config, projectpath): 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) folder = realpath(config, projectpath)
if folder: if folder:
if isInstanceRunning(config): if isInstanceRunning(config):
...@@ -388,12 +584,22 @@ def configNewSR(config, projectpath): ...@@ -388,12 +584,22 @@ def configNewSR(config, projectpath):
removeProxyDb(config) removeProxyDb(config)
startProxy(config) startProxy(config)
removeInstanceRoot(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) open(os.path.join(config['runner_workdir'], ".project"), 'w').write(projectpath)
return True return True
else: else:
return False return False
def newSoftware(folder, config, session): 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 = "" json = ""
code = 0 code = 0
runner_dir = config['runner_workdir'] runner_dir = config['runner_workdir']
...@@ -427,12 +633,14 @@ def newSoftware(folder, config, session): ...@@ -427,12 +633,14 @@ def newSoftware(folder, config, session):
return jsonify(code=code, result=json) return jsonify(code=code, result=json)
def checkSoftwareFolder(path, config): def checkSoftwareFolder(path, config):
"""Check id `path` is a valid Software Release folder"""
realdir = realpath(config, path) realdir = realpath(config, path)
if realdir and os.path.exists(os.path.join(realdir, config['software_profile'])): if realdir and os.path.exists(os.path.join(realdir, config['software_profile'])):
return jsonify(result=path) return jsonify(result=path)
return jsonify(result="") return jsonify(result="")
def getProjectTitle(config): def getProjectTitle(config):
"""Generate the name of the current software Release (for slaprunner UI)"""
conf = os.path.join(config['runner_workdir'], ".project") conf = os.path.join(config['runner_workdir'], ".project")
if os.path.exists(conf): if os.path.exists(conf):
project = open(conf, "r").read().split("/") project = open(conf, "r").read().split("/")
...@@ -440,7 +648,22 @@ def getProjectTitle(config): ...@@ -440,7 +648,22 @@ def getProjectTitle(config):
return software + " (" + string.join(project[:(len(project) - 2)], '/') + ")" return software + " (" + string.join(project[:(len(project) - 2)], '/') + ")"
return "No Profile" 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): 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 import pickle
file_path = os.path.join(runner_dir, '.softdata') file_path = os.path.join(runner_dir, '.softdata')
if not os.path.exists(file_path): if not os.path.exists(file_path):
...@@ -451,6 +674,12 @@ def loadSoftwareData(runner_dir): ...@@ -451,6 +674,12 @@ def loadSoftwareData(runner_dir):
return data return data
def writeSoftwareData(runner_dir, 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 import pickle
file_path = os.path.join(runner_dir, '.softdata') file_path = os.path.join(runner_dir, '.softdata')
pkl_file = open(file_path, 'wb') pkl_file = open(file_path, 'wb')
...@@ -459,11 +688,16 @@ def writeSoftwareData(runner_dir, data): ...@@ -459,11 +688,16 @@ def writeSoftwareData(runner_dir, data):
pkl_file.close() pkl_file.close()
def removeSoftwareByName(config, folderName): 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): 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) path = os.path.join(config['software_root'], folderName)
if not os.path.exists(path): 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) svcStopAll(config)
shutil.rmtree(path) shutil.rmtree(path)
#update compiled software list #update compiled software list
...@@ -475,7 +709,7 @@ def removeSoftwareByName(config, folderName): ...@@ -475,7 +709,7 @@ def removeSoftwareByName(config, folderName):
writeSoftwareData(config['runner_workdir'], data) writeSoftwareData(config['runner_workdir'], data)
break break
i = i+1 i = i+1
return jsonify(code=1, result=data) return data
def tail(f, lines=20): def tail(f, lines=20):
""" """
...@@ -583,6 +817,13 @@ def realpath(config, path, check_exist=True): ...@@ -583,6 +817,13 @@ def realpath(config, path, check_exist=True):
return False return False
def readParameters(path): 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): if os.path.exists(path):
try: try:
xmldoc = minidom.parse(path) xmldoc = minidom.parse(path)
...@@ -591,8 +832,7 @@ def readParameters(path): ...@@ -591,8 +832,7 @@ def readParameters(path):
sub_object = {} sub_object = {}
for subnode in elt.childNodes: for subnode in elt.childNodes:
if subnode.nodeType != subnode.TEXT_NODE: if subnode.nodeType != subnode.TEXT_NODE:
sub_object[str(subnode.getAttribute('id'))] = str(subnode. sub_object[str(subnode.getAttribute('id'))] = subnode.childNodes[0].data #.decode('utf-8').decode('utf-8')
childNodes[0].data)
object[str(elt.tagName)] = sub_object object[str(elt.tagName)] = sub_object
return object return object
except Exception, e: except Exception, e:
......
# -*- coding: utf-8 -*-
from flask import Flask, request, redirect, url_for, \ from flask import Flask, request, redirect, url_for, \
render_template, flash, jsonify, session render_template, flash, jsonify, session
from utils import * from utils import *
...@@ -9,21 +11,51 @@ from gittools import cloneRepo, gitStatus, switchBranch, addBranch, getDiff, \ ...@@ -9,21 +11,51 @@ from gittools import cloneRepo, gitStatus, switchBranch, addBranch, getDiff, \
app = Flask(__name__) app = Flask(__name__)
#Access Control: Only static files and login pages are allowed to guest
@app.before_request @app.before_request
def 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 # general views
@app.route('/') @app.route('/')
def home(): 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') 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') @app.route('/configRepo')
def configRepo(): def configRepo():
public_key = open(app.config['public_key'], 'r').read() 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 # software views
@app.route('/editSoftwareProfile') @app.route('/editSoftwareProfile')
...@@ -43,6 +75,7 @@ def inspectSoftware(): ...@@ -43,6 +75,7 @@ def inspectSoftware():
return render_template('runResult.html', softwareRoot='software_root', return render_template('runResult.html', softwareRoot='software_root',
softwares=loadSoftwareData(app.config['runner_workdir'])) softwares=loadSoftwareData(app.config['runner_workdir']))
#remove content of compiled software release
@app.route('/removeSoftware') @app.route('/removeSoftware')
def removeSoftware(): def removeSoftware():
file_config = os.path.join(app.config['runner_workdir'], ".softdata") file_config = os.path.join(app.config['runner_workdir'], ".softdata")
...@@ -80,6 +113,7 @@ def editInstanceProfile(): ...@@ -80,6 +113,7 @@ def editInstanceProfile():
return render_template('updateInstanceProfile.html', workDir='workspace', return render_template('updateInstanceProfile.html', workDir='workspace',
profile=profile, projectList=getProjectList(app.config['workspace'])) profile=profile, projectList=getProjectList(app.config['workspace']))
# get status of all computer partitions and process state
@app.route('/inspectInstance', methods=['GET']) @app.route('/inspectInstance', methods=['GET'])
def inspectInstance(): def inspectInstance():
file_content = '' file_content = ''
...@@ -88,17 +122,39 @@ def inspectInstance(): ...@@ -88,17 +122,39 @@ def inspectInstance():
file_content = 'instance_root' file_content = 'instance_root'
result = getSvcStatus(app.config) result = getSvcStatus(app.config)
if len(result) == 0: if len(result) == 0:
result = [] result = []
return render_template('instanceInspect.html', return render_template('instanceInspect.html',
file_path=file_content, supervisor=result, slap_status=getSlapStatus(app.config), file_path=file_content, supervisor=result, slap_status=getSlapStatus(app.config),
supervisore=result, partition_amount=app.config['partition_amount']) 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') @app.route('/removeInstance')
def removeInstance(): def removeInstance():
if isInstanceRunning(app.config): if isInstanceRunning(app.config):
flash('Instantiation in progress, cannot remove') flash('Instantiation in progress, cannot remove')
else: else:
stopProxy(app.config)
removeProxyDb(app.config)
startProxy(app.config)
removeInstanceRoot(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') flash('Instance removed')
return redirect(url_for('inspectInstance')) return redirect(url_for('inspectInstance'))
...@@ -201,6 +257,7 @@ def getProjectStatus(): ...@@ -201,6 +257,7 @@ def getProjectStatus():
else: else:
return jsonify(code=0, result="Can not read folder: Permission Denied") return jsonify(code=0, result="Can not read folder: Permission Denied")
#view for current software release files
@app.route("/editCurrentProject") @app.route("/editCurrentProject")
def editCurrentProject(): def editCurrentProject():
project = os.path.join(app.config['runner_workdir'], ".project") project = os.path.join(app.config['runner_workdir'], ".project")
...@@ -210,6 +267,7 @@ def editCurrentProject(): ...@@ -210,6 +267,7 @@ def editCurrentProject():
projectList=getProjectList(app.config['workspace'])) projectList=getProjectList(app.config['workspace']))
return redirect(url_for('configRepo')) return redirect(url_for('configRepo'))
#create file or directory
@app.route("/createFile", methods=['POST']) @app.route("/createFile", methods=['POST'])
def createFile(): def createFile():
path = realpath(app.config, request.form['file'], False) path = realpath(app.config, request.form['file'], False)
...@@ -225,6 +283,7 @@ def createFile(): ...@@ -225,6 +283,7 @@ def createFile():
except Exception, e: except Exception, e:
return jsonify(code=0, result=str(e)) return jsonify(code=0, result=str(e))
#remove file or directory
@app.route("/removeFile", methods=['POST']) @app.route("/removeFile", methods=['POST'])
def removeFile(): def removeFile():
try: try:
...@@ -238,8 +297,13 @@ def removeFile(): ...@@ -238,8 +297,13 @@ def removeFile():
@app.route("/removeSoftwareDir", methods=['POST']) @app.route("/removeSoftwareDir", methods=['POST'])
def removeSoftwareDir(): 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']) @app.route("/getFileContent", methods=['POST'])
def getFileContent(): def getFileContent():
file_path = realpath(app.config, request.form['file']) file_path = realpath(app.config, request.form['file'])
...@@ -256,7 +320,7 @@ def getFileContent(): ...@@ -256,7 +320,7 @@ def getFileContent():
def saveFileContent(): def saveFileContent():
file_path = realpath(app.config, request.form['file']) file_path = realpath(app.config, request.form['file'])
if file_path: 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="") return jsonify(code=1, result="")
else: else:
return jsonify(code=0, result="Error: No such file!") return jsonify(code=0, result="Error: No such file!")
...@@ -323,6 +387,7 @@ def getmd5sum(): ...@@ -323,6 +387,7 @@ def getmd5sum():
else: else:
return jsonify(code=0, result="Can not get md5sum for this file!") return jsonify(code=0, result="Can not get md5sum for this file!")
#return informations about state of slapgrid process
@app.route("/slapgridResult", methods=['POST']) @app.route("/slapgridResult", methods=['POST'])
def slapgridResult(): def slapgridResult():
software_state = isSoftwareRunning(app.config) software_state = isSoftwareRunning(app.config)
...@@ -359,31 +424,61 @@ def getPath(): ...@@ -359,31 +424,61 @@ def getPath():
else: else:
return jsonify(code=1, result=realfile) return jsonify(code=1, result=realfile)
#update instance parameter into a local xml file
@app.route("/saveParameterXml", methods=['POST']) @app.route("/saveParameterXml", methods=['POST'])
def redParameterXml(): def saveParameterXml():
project = os.path.join(app.config['runner_workdir'], ".project") project = os.path.join(app.config['runner_workdir'], ".project")
if not os.path.exists(project): if not os.path.exists(project):
return jsonify(code=0, result="Please first open a Software Release") 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") param_path = os.path.join(app.config['runner_workdir'], ".parameter.xml")
f = open(param_path, 'w') try:
f.write(content) f = open(param_path, 'w')
f.close() f.write(content)
result = readParameters(param_path) 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(''): if type(result) == type(''):
return jsonify(code=0, result="XML Error: " + result) return jsonify(code=0, result=result)
else: else:
try: try:
updateProxy(app.config) updateInstanceParameter(app.config, software_type)
except Exeption: except Exception, e:
return jsonify(code=0, result="An error occurred while applying your settings!") return jsonify(code=0, result="An error occurred while applying your settings!<br/>" + str(e))
return jsonify(code=1, result="") return jsonify(code=1, result="")
@app.route("/getParameterXml", methods=['GET']) #read instance parameters into the local xml file and return a dict
def getParameterXml(): @app.route("/getParameterXml/<request>", methods=['GET'])
def getParameterXml(request):
param_path = os.path.join(app.config['runner_workdir'], ".parameter.xml") param_path = os.path.join(app.config['runner_workdir'], ".parameter.xml")
if os.path.exists(param_path): if not os.path.exists(param_path):
content = open(param_path, 'r').read() default = '<?xml version="1.0" encoding="utf-8"?>\n'
return html_escape(content) 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: else:
return "&lt;?xml version='1.0' encoding='utf-8'?&gt;" return jsonify(code=1, result="")
\ No newline at end of file \ 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