Commit 8cebacc6 authored by Nicolas Wavrant's avatar Nicolas Wavrant

Merge branch 'slaprunner-paas'

parents c4716eaa c9a34429
...@@ -200,9 +200,10 @@ class SlaprunnerTestSuite(ResiliencyTestSuite): ...@@ -200,9 +200,10 @@ class SlaprunnerTestSuite(ResiliencyTestSuite):
def _getRcode(self): def _getRcode(self):
#XXX-Nicolas: hardcoded url. Best way right now to automate the tests... #XXX-Nicolas: hardcoded url. Best way right now to automate the tests...
monitoring_password = "passwordtochange"
monitor_url = self.monitor_url + "?script=zero-knowledge%2Fsettings.cgi" monitor_url = self.monitor_url + "?script=zero-knowledge%2Fsettings.cgi"
result = self._opener_director.open(monitor_url, result = self._opener_director.open(monitor_url,
"password=passwordtochange") "password=" + monitoring_password + ";password_2=" + monitoring_password)
if result.getcode() is not 200: if result.getcode() is not 200:
raise NotHttpOkException(result.getcode()) raise NotHttpOkException(result.getcode())
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import ConfigParser import ConfigParser
import datetime import datetime
import flask
import logging import logging
import logging.handlers import logging.handlers
import os import os
...@@ -133,4 +134,23 @@ def serve(config): ...@@ -133,4 +134,23 @@ def serve(config):
config.logger.info('Done.') config.logger.info('Done.')
app.wsgi_app = ProxyFix(app.wsgi_app) app.wsgi_app = ProxyFix(app.wsgi_app)
def getUpdatedParameter(self, var):
configuration_parser = ConfigParser.SafeConfigParser()
configuration_file_path = os.path.abspath(os.getenv('RUNNER_CONFIG'))
configuration_parser.read(configuration_file_path)
for section in configuration_parser.sections():
if configuration_parser.has_option(section, var):
return configuration_parser.get(section, var)
# if the requested var is dependant of flask
if var in self.keys():
temp_dict = dict()
temp_dict.update(self)
return temp_dict[var]
else:
raise KeyError
flask.config.Config.__getitem__ = getUpdatedParameter
run() run()
...@@ -96,8 +96,6 @@ class SlaprunnerTestCase(unittest.TestCase): ...@@ -96,8 +96,6 @@ class SlaprunnerTestCase(unittest.TestCase):
software_profile='software.cfg', software_profile='software.cfg',
SECRET_KEY="123456", SECRET_KEY="123456",
PERMANENT_SESSION_LIFETIME=datetime.timedelta(days=31), PERMANENT_SESSION_LIFETIME=datetime.timedelta(days=31),
auto_deploy = True,
autorun = False,
instance_monitoring_url = 'https://[' + config.ipv6_address + ']:9684', instance_monitoring_url = 'https://[' + config.ipv6_address + ']:9684',
) )
self.app = views.app.test_client() self.app = views.app.test_client()
...@@ -122,6 +120,10 @@ class SlaprunnerTestCase(unittest.TestCase): ...@@ -122,6 +120,10 @@ class SlaprunnerTestCase(unittest.TestCase):
project = os.path.join(self.app.config['etc_dir'], '.project') project = os.path.join(self.app.config['etc_dir'], '.project')
users = os.path.join(self.app.config['etc_dir'], '.users') users = os.path.join(self.app.config['etc_dir'], '.users')
#reset tested parameters
self.updateConfigParameter('autorun', False)
self.updateConfigParameter('auto_deploy', True)
if os.path.exists(users): if os.path.exists(users):
os.unlink(users) os.unlink(users)
if os.path.exists(project): if os.path.exists(project):
...@@ -139,6 +141,15 @@ class SlaprunnerTestCase(unittest.TestCase): ...@@ -139,6 +141,15 @@ class SlaprunnerTestCase(unittest.TestCase):
killRunningProcess('slapgrid-cp', recursive=True) killRunningProcess('slapgrid-cp', recursive=True)
killRunningProcess('slapgrid-sr', recursive=True) killRunningProcess('slapgrid-sr', recursive=True)
def updateConfigParameter(self, parameter, value):
config_parser = ConfigParser.SafeConfigParser()
config_parser.read(os.getenv('RUNNER_CONFIG'))
for section in config_parser.sections():
if config_parser.has_option(section, parameter):
config_parser.set(section, parameter, str(value))
with open(os.getenv('RUNNER_CONFIG'), 'wb') as configfile:
config_parser.write(configfile)
def configAccount(self, username, password, email, name, rcode): def configAccount(self, username, password, email, name, rcode):
"""Helper for configAccount""" """Helper for configAccount"""
return self.app.post('/configAccount', return self.app.post('/configAccount',
...@@ -433,8 +444,8 @@ class SlaprunnerTestCase(unittest.TestCase): ...@@ -433,8 +444,8 @@ class SlaprunnerTestCase(unittest.TestCase):
"""Scenario 7: isSRReady won't overwrite the existing """Scenario 7: isSRReady won't overwrite the existing
Sofware Instance if it has been deployed yet""" Sofware Instance if it has been deployed yet"""
# Test that SR won't be deployed with auto_deploy=False # Test that SR won't be deployed with auto_deploy=False
self.app.config['auto_deploy'] = False self.updateConfigParameter('auto_deploy', False)
self.app.config['autorun'] = False self.updateConfigParameter('autorun', False)
project = open(os.path.join(self.app.config['etc_dir'], project = open(os.path.join(self.app.config['etc_dir'],
'.project'), "w") '.project'), "w")
project.write(self.software + 'slaprunner-test') project.write(self.software + 'slaprunner-test')
...@@ -442,7 +453,7 @@ class SlaprunnerTestCase(unittest.TestCase): ...@@ -442,7 +453,7 @@ class SlaprunnerTestCase(unittest.TestCase):
response = isSoftwareReleaseReady(self.app.config) response = isSoftwareReleaseReady(self.app.config)
self.assertEqual(response, "0") self.assertEqual(response, "0")
# Test if auto_deploy parameter starts the deployment of SR # Test if auto_deploy parameter starts the deployment of SR
self.app.config['auto_deploy'] = True self.updateConfigParameter('auto_deploy', True)
self.setupSoftwareFolder() self.setupSoftwareFolder()
response = isSoftwareReleaseReady(self.app.config) response = isSoftwareReleaseReady(self.app.config)
self.assertEqual(response, "2") self.assertEqual(response, "2")
...@@ -522,6 +533,23 @@ class SlaprunnerTestCase(unittest.TestCase): ...@@ -522,6 +533,23 @@ class SlaprunnerTestCase(unittest.TestCase):
self.assertEqual(response, (1, MAX_RUN_INSTANCE)) self.assertEqual(response, (1, MAX_RUN_INSTANCE))
def test_dynamicParametersReading(self):
"""Test if the value of a parameter can change in the flask application
only by changing the value of slapos.cfg config file. This can happen when
slapgrid processes the webrunner's partition.
"""
config_file = os.path.join(self.app.config['etc_dir'], 'slapos.cfg')
runner_config_old = os.environ['RUNNER_CONFIG']
os.environ['RUNNER_CONFIG'] = config_file
open(config_file, 'w').write("[section]\nvar=value")
config = self.app.config
self.assertEqual(config['var'], "value")
open(config_file, 'w').write("[section]\nvar=value_changed")
self.assertEqual(config['var'], "value_changed")
# cleanup
os.environ['RUNNER_CONFIG'] = runner_config_old
def main(): def main():
# Empty parser for now - so that erp5testnode is happy when doing --help # Empty parser for now - so that erp5testnode is happy when doing --help
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
.swith_btn{background: url(../images/gnome-session-switch.png) center right no-repeat;width: 105px;} .swith_btn{background: url(../images/gnome-session-switch.png) center right no-repeat;width: 105px;}
.flist_btn{background: url(../images/list2_down.png) center right no-repeat;width: 26px;} .flist_btn{background: url(../images/list2_down.png) center right no-repeat;width: 26px;}
.fmenu_btn{background: url(../images/ui_menu_blue.png) center right no-repeat;width: 58px;} .fmenu_btn{background: url(../images/ui_menu_blue.png) center right no-repeat;width: 58px;}
.shell_btn{background: url(../images/terminal.png) center right no-repeat;width: 62px;}
#tabControl{overflow: hidden;} #tabControl{overflow: hidden;}
#tabControl .item{float:left; min-width: 60px; background: #D5D5D5; height: 22px; padding-top: 8px; font-size:1em; #tabControl .item{float:left; min-width: 60px; background: #D5D5D5; height: 22px; padding-top: 8px; font-size:1em;
...@@ -43,4 +44,42 @@ border-left:1px #E4E4E4 solid; cursor:pointer; color: #5C7077; text-shadow: 0px ...@@ -43,4 +44,42 @@ border-left:1px #E4E4E4 solid; cursor:pointer; color: #5C7077; text-shadow: 0px
#tabContent pre.active {display: block;} #tabContent pre.active {display: block;}
.item-hide{display:none;} .item-hide{display:none;}
#shell-window {
width: 974px;
height: 350px;
z-index: 10;
border: 2px solid #6A93A0;
border-top: none;
background: #9bbed6;
position: absolute;
display: none;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
}
#shell-result {
width: 98%;
height: 300px;
margin-left: 1%;
margin-right: 1%;
margin-top: 5px;
padding: 3px;
background: #E4E4E4;
color: #074A86;
box-sizing: border-box;
border: 2px inset #6A93A0;
overflow: auto;
}
.runned-command
{
font-weight: bold;
}
#shell-input {
width: 96%;
margin-left: 1%;
margin-right: 1%;
position: absolute;
bottom: 10px;
background: #E4E4E4;
color: #074A86;
border: inset 1px #678dad;
}
...@@ -106,7 +106,7 @@ body { ...@@ -106,7 +106,7 @@ body {
padding-top: 3px; padding-top: 3px;
margin-left:20px; margin-left:20px;
float: left; float: left;
width: 700px; width: 695px;
height: 22px; height: 22px;
text-align: center; text-align: center;
color: #4c6172; color: #4c6172;
...@@ -434,6 +434,10 @@ padding: 10px;height: 80px;padding-bottom:15px;} ...@@ -434,6 +434,10 @@ padding: 10px;height: 80px;padding-bottom:15px;}
.gradient{background-color:#f4f4f4;background:-moz-linear-gradient(#f4f4f4,#ececec);background:-webkit-linear-gradient(#f4f4f4,#ececec);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr='#f4f4f4',endColorstr='#ececec')";background:linear-gradient(#f4f4f4,#ececec);} .gradient{background-color:#f4f4f4;background:-moz-linear-gradient(#f4f4f4,#ececec);background:-webkit-linear-gradient(#f4f4f4,#ececec);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr='#f4f4f4',endColorstr='#ececec')";background:linear-gradient(#f4f4f4,#ececec);}
#slapstate {
display: none;
}
.log_content{ .log_content{
border: solid 1px #519ADA; border: solid 1px #519ADA;
padding: 2px; padding: 2px;
...@@ -506,6 +510,9 @@ padding: 10px;height: 80px;padding-bottom:15px;} ...@@ -506,6 +510,9 @@ padding: 10px;height: 80px;padding-bottom:15px;}
font-size: 16px; font-size: 16px;
} }
.last_build{
font-size: 0.9em;
}
.log_content_box{ .log_content_box{
padding: 10px 5px; padding: 10px 5px;
font-size: 14px; font-size: 14px;
...@@ -528,13 +535,13 @@ padding: 10px;height: 80px;padding-bottom:15px;} ...@@ -528,13 +535,13 @@ padding: 10px;height: 80px;padding-bottom:15px;}
display: block; display: block;
float: left; float: left;
} }
.log_info_box div.state_running span{ .log_info_box div.state_running span, .log_info_box div.state_notupdated span{
background: #FFC800; background: #FFC800;
} }
.log_info_box div.state_terminated span{ .log_info_box div.state_terminated span{
background: #00C800; background: #00C800;
} }
.log_info_box div.state_waiting span, .log_info_box div.state_stopped span{ .log_info_box div.state_waiting span, .log_info_box div.state_stopped span, .log_info_box div.state_failed span{
background: #FF3131; background: #FF3131;
} }
......
...@@ -59,7 +59,7 @@ $(document).ready(function () { ...@@ -59,7 +59,7 @@ $(document).ready(function () {
}, },
success: function (data) { success: function (data) {
if (data.code === 1) { if (data.code === 1) {
url = 'https://' + $("input#username").val() + ':' + $("input#password").val() + '@' + location.host + $SCRIPT_ROOT + '/'; var url = 'https://' + $("input#username").val() + ':' + $("input#password").val() + '@' + location.host + $SCRIPT_ROOT + '/';
window.location.href = url; window.location.href = url;
} else { } else {
$("#error").Popup(data.result, {type: 'error', duration: 5000}); $("#error").Popup(data.result, {type: 'error', duration: 5000});
......
...@@ -89,9 +89,12 @@ $(document).ready(function () { ...@@ -89,9 +89,12 @@ $(document).ready(function () {
} }
xml += '<instance>\n'; xml += '<instance>\n';
if (size > 1) { if (size > 1) {
for (i = 2; i <= size; i += 1) { // we have to remove the 1st line, which just diplay column names
if ($('input#txt_' + i).val() !== '') { for (i = 0; i < size - 1; i += 1) {
xml += '<parameter id="' + $('input#txt_' + i).val() + '">' + $('textarea#value_' + i).val() + '</parameter>\n'; var parameter_name = $("#partitionParameter tr").find("input")[i].value;
var parameter_value = $("#partitionParameter tr").find("textarea")[i].value;
if (parameter_name !== '') {
xml += '<parameter id="' + parameter_name + '">' + parameter_value + '</parameter>\n';
} }
} }
} }
......
/*jslint undef: true */ /*jslint undef: true */
/*global $, window, $SCRIPT_ROOT, setRunningState, setCookie, getCookie, deleteCookie */ /*global $, window, $SCRIPT_ROOT, setRunningState, setCookie, getCookie, deleteCookie */
/*global currentState: true, running: true, $current: true, processType: true, currentProcess: true */ /*global currentState: true, running: false, $current: true, currentProcess: true, processTypes: true */
/*global sendStop: true, processState: true, openedlogpage: true, logReadingPosition: true, speed: true */ /*global sendStop: true, openedlogpage: true, logReadingPosition: true, speed: true */
/*global isRunning: true */ /*global isRunning: true */
/* vim: set et sts=4: */ /* vim: set et sts=4: */
//Global Traitment!!! //Global Traitment!!!
var url = $SCRIPT_ROOT + "/slapgridResult";
var currentState = false; var currentState = false;
var running = true; var running = false;
var processType = ""; var currentProcess = "";
var currentProcess; var processTypes = {instance:"instance", software:"software"};
var sendStop = false; var sendStop = false;
var forcedStop = false; var forcedStop = false;
var processState = "Checking"; //define slapgrid running state
var openedlogpage = ""; //content software or instance if the current page is software or instance log, otherwise nothing var openedlogpage = ""; //content software or instance if the current page is software or instance log, otherwise nothing
var logReadingPosition = 0; var logReadingPosition = 0;
var speed = 5000; var speed = 5000;
var maxLogSize = 100000; //Define the max limit of log to display ~ 2500 lines var maxLogSize = 100000; //Define the max limit of log to display ~ 2500 lines
var currentLogSize = 0; //Define the size of log actually displayed var currentLogSize = 0; //Define the size of log actually displayed
var last_run = "";
var runInstance = false;
$(document).ready(function () {
$.get("/getSlapgridParameters",null,function(data) {
runInstance = data.run_instance;
});
});
var isRunning = function () { var isRunning = function () {
"use strict"; "use strict";
if (running) { if (running) {
...@@ -50,35 +57,30 @@ function removeFirstLog() { ...@@ -50,35 +57,30 @@ function removeFirstLog() {
$("#salpgridLog p:first-child").remove(); $("#salpgridLog p:first-child").remove();
} }
function getRunningState() { function writeLogs(data) {
"use strict"; "use strict";
var size = 0, var log_info = "",
log_info = "",
param = {
position: logReadingPosition,
log: (processState !== "Checking" && openedlogpage !== "") ? processType.toLowerCase() : ""
},
jqxhr = $.post(url, param, function (data) {
setRunningState(data);
size = data.content.position - logReadingPosition; size = data.content.position - logReadingPosition;
if (size < 0) {
clearAll(false);
}
if (logReadingPosition !== 0 && data.content.truncated) { if (logReadingPosition !== 0 && data.content.truncated) {
log_info = "<p class='info' rel='0'>SLAPRUNNER INFO: SLAPGRID-LOG HAS BEEN TRUNCATED HERE. To see full log reload your log page</p>"; log_info = "<p class='info' rel='0'>SLAPRUNNER INFO: SLAPGRID-LOG HAS BEEN TRUNCATED HERE. To see full log reload your log page</p>";
} }
logReadingPosition = data.content.position; logReadingPosition = data.content.position;
if (data.content.content !== "") {
if (data.content.content !== "") { if (data.content.content !== "") {
$("#salpgridLog").append(log_info + "<p rel='" + size + "'>" + data.content.content.toHtmlChar() + "</p>"); $("#salpgridLog").append(log_info + "<p rel='" + size + "'>" + data.content.content.toHtmlChar() + "</p>");
$("#salpgridLog") $("#salpgridLog")
.scrollTop($("#salpgridLog")[0].scrollHeight - $("#salpgridLog").height()); .scrollTop($("#salpgridLog")[0].scrollHeight - $("#salpgridLog").height());
} }
} if (running && openedlogpage !== "") {
if (running && processState === "Checking" && openedlogpage !== "") {
$("#salpgridLog").show(); $("#salpgridLog").show();
$("#manualLog").hide(); $("#manualLog").hide();
$("#slapstate").show(); $("#slapstate").show();
$("#openloglist").hide(); $("#openloglist").hide();
} }
processState = running ? "Running" : "Stopped";
currentLogSize += parseInt(size, 10); currentLogSize += parseInt(size, 10);
if (currentLogSize > maxLogSize) { if (currentLogSize > maxLogSize) {
//Remove the first element into log div //Remove the first element into log div
...@@ -87,6 +89,55 @@ function getRunningState() { ...@@ -87,6 +89,55 @@ function getRunningState() {
removeFirstLog(); //in cas of previous <p/> size is 0 removeFirstLog(); //in cas of previous <p/> size is 0
} }
} }
}
function getRunningState() {
"use strict";
var url = $SCRIPT_ROOT + "/slapgridResult",
build_success = 0,
run_success = 0,
param = {
position: logReadingPosition,
log: (openedlogpage !== "") ? currentProcess : ""
},
jqxhr = $.post(url, param, function (data) {
running = data.result;
if (data.instance.state) {
currentProcess = processTypes.instance;
} else if (data.software.state) {
currentProcess = processTypes.software;
}
if (last_run === "") {
last_run = data.instance.last_build;
}
$("#last_build_software").text("last build: " + data.software.last_build);
if (data.instance.last_build !== last_run) {
currentProcess = processTypes.instance;
last_run = data.instance.last_build;
}
$("#last_build_instance").text("last run: " + data.instance.last_build);
writeLogs(data);
setRunningState(data);
//show accurate right panel
if (running) {
$("#slapstate").show();
$("#openloglist").hide();
}
if(data.result) {
if (currentProcess === processTypes.software && runInstance) {
updateStatus("instance", "waiting");
}
updateStatus(currentProcess, "running");
} else {
build_success = (data.software.success === 0)? "terminated":"failed";
if ( last_run < data.software.last_build && data.software.success === 1 ) {
run_success = "notupdated";
} else {
run_success = (data.instance.success === 0)? "terminated":"failed";
}
updateStatus("software", build_success);
updateStatus("instance", run_success);
}
}).error(function () { }).error(function () {
clearAll(false); clearAll(false);
}); });
...@@ -103,20 +154,16 @@ function stopProcess() { ...@@ -103,20 +154,16 @@ function stopProcess() {
var urlfor = $SCRIPT_ROOT + "stopSlapgrid", var urlfor = $SCRIPT_ROOT + "stopSlapgrid",
type = "slapgrid-sr"; type = "slapgrid-sr";
if (processType === "Instance") { if (currentProcess === processTypes.instance) {
type = "slapgrid-cp"; type = "slapgrid-cp";
} }
$.post(urlfor, {type: type}, function (data) { $.post(urlfor, {type: type}, function (data) {
//if (data.result) {
//$("#error").Popup("Failled to run Slapgrid", {type:'error', duration:3000}); });
//}
}) })
.error(function () { .error(function () {
$("#error").Popup("Failed to stop Slapgrid process", {type: 'error', duration: 3000}); $("#error").Popup("Failed to stop Slapgrid process", {type: 'error', duration: 3000});
}) })
.complete(function () { .complete(function () {
sendStop = false; sendStop = false;
processState = "Stopped";
forcedStop = true; forcedStop = true;
}); });
} }
...@@ -129,8 +176,9 @@ function bindRun() { ...@@ -129,8 +176,9 @@ function bindRun() {
stopProcess(); stopProcess();
} else { } else {
if (!isRunning()) { if (!isRunning()) {
setCookie("slapgridCMD", "Software"); runProcess($SCRIPT_ROOT + "/runSoftwareProfile").then(function() {
window.location.href = $SCRIPT_ROOT + "/viewLog?logfile=software.log"; window.location.href = $SCRIPT_ROOT + "/viewLog?logfile=software.log";
});
} }
} }
return false; return false;
...@@ -140,9 +188,10 @@ function bindRun() { ...@@ -140,9 +188,10 @@ function bindRun() {
stopProcess(); stopProcess();
} else { } else {
if (!isRunning()) { if (!isRunning()) {
setCookie("slapgridCMD", "Instance"); runProcess($SCRIPT_ROOT + "/runInstanceProfile").then(function() {
if (window.location.pathname === "/viewLog") if (window.location.pathname === "/viewLog")
window.location.href = $SCRIPT_ROOT + "/viewLog?logfile=instance.log"; window.location.href = $SCRIPT_ROOT + "/viewLog?logfile=instance.log";
});
} }
} }
return false; return false;
...@@ -166,16 +215,13 @@ function updateStatus(elt, val) { ...@@ -166,16 +215,13 @@ function updateStatus(elt, val) {
break; break;
case "running": case "running":
$(src).children('p').text("Processing"); $(src).children('p').text("Processing");
processType = elt;
getRunningState()
break; break;
} case "notupdated":
// in case of failure $(src).children('p').text("Not updated");
if ($("#salpgridLog").text().indexOf("Failed to run buildout profile") !== -1) { break;
var src = '#' + elt + '_run_state', value = 'state_' + "stopped"; case "failed":
$(src).removeClass(); $(src).children('p').text("Failed");
$(src).addClass(value); break;
$(src).children('p').text("Buildout Failed");
} }
} }
...@@ -186,40 +232,33 @@ function setRunningState(data) { ...@@ -186,40 +232,33 @@ function setRunningState(data) {
$("#running").show(); $("#running").show();
running = true; running = true;
//change run menu title and style //change run menu title and style
if (data.software) { if (data.software.state) {
if ( $("#running").children('span').length === 0 ) { if ( $("#running").children('span').length === 0 ) {
$("#softrun").removeClass('slapos_run'); $("#softrun").removeClass('slapos_run');
$("#softrun").addClass('slapos_stop'); $("#softrun").addClass('slapos_stop');
if($("[class=software][id=running_info]").length === 0)
$("#running img").before('<p id="running_info" class="software">Building software...</p>'); $("#running img").before('<p id="running_info" class="software">Building software...</p>');
} }
processType = "Software";
} }
if (data.instance) { if (data.instance.state) {
///Draft!! ///Draft!!
if ( $("#running").children('span').length === 0 ) { if ( $("#running").children('span').length === 0 ) {
$("#softrun").removeClass('slapos_run'); $("#softrun").removeClass('slapos_run');
$("#softrun").addClass('slapos_stop'); $("#softrun").addClass('slapos_stop');
if($("[class=instance][id=running_info]").length === 0)
$("#running img").before('<p id="running_info" class="instance">Running instance...</p>'); $("#running img").before('<p id="running_info" class="instance">Running instance...</p>');
} }
if (processType === "Software") {
running = false;
$("#running_info").remove();
$("#softrun").addClass('slapos_run');
$("#softrun").removeClass('slapos_stop');
$("#instrun").click();
}
processType = "Instance";
} }
} }
} else { } else {
if ( $("#running").is(":visible") ) { if ( $("#running").is(":visible") ) {
$("#error").Popup("Slapgrid finished running your " + processType + " Profile", {type: 'info', duration: 3000}); $("#error").Popup("Slapgrid finished running your " + currentProcess + " profile", {type: 'info', duration: 3000});
if ( forcedStop ) { if ( forcedStop ) {
updateStatus('instance', 'stopped'); updateStatus('instance', 'stopped');
updateStatus('software', 'stopped'); updateStatus('software', 'stopped');
} }
else { else {
updateStatus(processType.toLowerCase(), 'terminated'); updateStatus(currentProcess, 'terminated');
} }
//Update window!!! //Update window!!!
$("#slapswitch").attr('rel', 'opend'); $("#slapswitch").attr('rel', 'opend');
...@@ -227,7 +266,6 @@ function setRunningState(data) { ...@@ -227,7 +266,6 @@ function setRunningState(data) {
} }
$("#running").hide(); $("#running").hide();
$("#running_info").remove(); $("#running_info").remove();
running = false; //nothing is currently running
$("#softrun").removeClass('slapos_stop'); $("#softrun").removeClass('slapos_stop');
$("#softrun").addClass('slapos_run'); $("#softrun").addClass('slapos_run');
if ( $("#running").children('span').length > 0 ) { if ( $("#running").children('span').length > 0 ) {
...@@ -238,46 +276,16 @@ function setRunningState(data) { ...@@ -238,46 +276,16 @@ function setRunningState(data) {
currentState = data.result; currentState = data.result;
} }
function runProcess(urlfor, data) { function runProcess(urlfor) {
"use strict"; "use strict";
if (!isRunning()) { if (!isRunning()) {
running = true; return $.post(urlfor).then(function() {
processState = "Running";
currentProcess = $.post(urlfor)
.error(function () {
$("#error").Popup("Failled to run Slapgrid", {type: 'error', duration: 3000});
});
if ( $("#running_info").children('span').length > 0 ) { if ( $("#running_info").children('span').length > 0 ) {
$("#running_info").children('p').remove(); $("#running_info").children('p').remove();
} }
});
} }
} }
setInterval('GetStateRegularly()', 5000); getRunningState();
function GetStateRegularly() { setInterval('getRunningState()', 3000);
getRunningState();
}
function checkSavedCmd() {
"use strict";
var result = getCookie("slapgridCMD");
if (!result) {
return false;
}
forcedStop = false;
if (result === "Software") {
running = false;
runProcess(($SCRIPT_ROOT + "/runSoftwareProfile"),
{result: true, instance: false, software: true});
updateStatus('software', 'running');
updateStatus('instance', 'waiting');
} else if (result === "Instance") {
running = false;
runProcess(($SCRIPT_ROOT + "/runInstanceProfile"),
{result: true, instance: true, software: false});
updateStatus('software', 'terminated');
updateStatus('instance', 'running');
}
deleteCookie("slapgridCMD");
return (result !== null);
}
/*jslint undef: true */
/*global $, document, $SCRIPT_ROOT, window */
/*global path: true */
/* vim: set et sts=4: */
var shellHistory = "";
var currentCommand = 0;
$(document).ready(function () {
"use strict";
var updateHistory = function () {
$.getJSON("/getMiniShellHistory", function (data) {
shellHistory = data;
currentCommand = shellHistory.length;
});
};
updateHistory();
$("#shell").click (function() {
// We have to do that because once slide effect is activated, div is considered as visible
$("#shell").css("background-color", "#E4E4E4");
if ( ! $("#shell-window").is(':visible') ) {
$("#shell").css("background-color", "#C7C7C7");
}
$("#shell-window").slideToggle("fast");
if ( $("#shell-window").is(':visible') ) {
$("#shell-input").focus();
}
});
$("#shell-input").keypress(function (event) {
//if Enter is pressed
if(event.which === 13) {
event.preventDefault();
var command = $("#shell-input").val();
var data = { command: command };
$("#shell-result").append("<p id=\"waiting_for_command\"><img src=\"/static/css/images/loading-min.gif\" /></p>")
$("#shell-result").scrollTop($("#shell-result")[0].scrollHeight);
$.ajax({
type: "POST",
url: $SCRIPT_ROOT + "/runCommand",
data: data,
timeout: 600000
})
.done( function (data) {
var data = "<p><span class=\"runned-command\">" + data.path + " >>> " + command + "</span></p><br/><pre>" + data.data + "</pre><br/>";
$("#shell-result").append(data);
$("#shell-input").val("");
$("#shell-result").scrollTop($("#shell-result")[0].scrollHeight);
updateHistory();
})
.fail( function(xhr, status, error) {
$("#error").Popup("Error while sending command. Server answered with :\n" + xhr.statusCode().status + " : " + error, {type: 'error', duration: 3000})
})
.always( function() {
$("#waiting_for_command").remove();
});
}
});
$("#shell-input").keydown(function (event) {
//if Key Up is pressed
if(event.which == 38) {
event.preventDefault();
currentCommand--;
if (currentCommand <= 0)
currentCommand = 0;
$("#shell-input").val(shellHistory[currentCommand]);
}
//if Key Down is pressed
if(event.which === 40) {
event.preventDefault();
currentCommand++;
if (currentCommand > shellHistory.length) {
currentCommand = shellHistory.length;
$("#shell-input").val("");
} else {
$("#shell-input").val(shellHistory[currentCommand]);
}
}
//if Tab is pressed
if(event.which === 9) {
event.preventDefault();
$("#error").Popup("Sorry, Tab completion is not handled for the moment in MiniShell", {type: 'info', duration: 1000})
}
});
});
/*jslint undef: true */ /*jslint undef: true */
/*global $, document, window, processState, getCookie, setCookie, setSpeed, $SCRIPT_ROOT */ /*global $, document, window, getCookie, setCookie, setSpeed, $SCRIPT_ROOT */
/*global openedlogpage: true */ /*global openedlogpage: true, running: false */
/* vim: set et sts=4: */ /* vim: set et sts=4: */
$(document).ready(function () { $(document).ready(function () {
...@@ -53,7 +53,7 @@ $(document).ready(function () { ...@@ -53,7 +53,7 @@ $(document).ready(function () {
} }
function updatelogBox() { function updatelogBox() {
if (processState === "Stopped" || processState === "Checking" || $("#manual").is(":checked")) { if (! running || $("#manual").is(":checked")) {
$("#salpgridLog").hide(); $("#salpgridLog").hide();
$("#manualLog").show(); $("#manualLog").show();
$("#slapstate").hide(); $("#slapstate").hide();
...@@ -113,7 +113,7 @@ $(document).ready(function () { ...@@ -113,7 +113,7 @@ $(document).ready(function () {
}) })
.always(function() { .always(function() {
sending = false; sending = false;
if (processState === "Stopped" || processState === "Checking" || $("#manual").is(":checked")) { if (! running || $("#manual").is(":checked")) {
$("#logheader").html(info); $("#logheader").html(info);
} else { } else {
$("#logheader").html("Inspecting slapgrid log - Click for more options"); $("#logheader").html("Inspecting slapgrid log - Click for more options");
......
...@@ -44,9 +44,6 @@ ...@@ -44,9 +44,6 @@
$("#error").Popup($("input#fmsg").val(), {type:'info', duration:5000, load:true}); $("#error").Popup($("input#fmsg").val(), {type:'info', duration:5000, load:true});
} }
bindRun(); bindRun();
if(!checkSavedCmd()){
getRunningState();
}
$('ul.sf-menu').superfish({ $('ul.sf-menu').superfish({
delay: 600, delay: 600,
speed: 'fast', speed: 'fast',
...@@ -67,6 +64,10 @@ ...@@ -67,6 +64,10 @@
<div class="block_header"> <div class="block_header">
<a href="{{ url_for('home') }}" style="float:left;" id="home" title="Home"><img alt="" src="{{ url_for('static', filename='images/home.png') }}" /></a> <a href="{{ url_for('home') }}" style="float:left;" id="home" title="Home"><img alt="" src="{{ url_for('static', filename='images/home.png') }}" /></a>
<div class="line"></div> <div class="line"></div>
<a href="http://community.slapos.org/wiki/osoe-Lecture.SlapOS.Extended" style="float:left;position:relative;top:1px;" target="_blank"><img src="{{ url_for('static', filename="images/doc.png")}}" alt="documentation" title="Documentation" /></a>
<div class="line"></div>
<a href="http://community.slapos.org/forum" style="float:left;position:relative;top:1px;" target="_blank"><img src="{{ url_for('static', filename="images/forum.png")}}" alt="forum" title="Forum" /></a>
<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"> <div class="run">
<div id="running" style="display:none"> <div id="running" style="display:none">
...@@ -121,7 +122,7 @@ ...@@ -121,7 +122,7 @@
</div> </div>
{% if request.path != '/login' %} {% if request.path != '/login' %}
<div id="footer"> <div id="footer">
SlapOS web runner &copy; Vifib SARL 2011, 2012, 2013 - All right reserved - Creative Commons Shared Alike SlapOS web runner &copy; Vifib SARL 2011-2014 - All right reserved - Creative Commons Shared Alike
</div> </div>
{%endif%} {%endif%}
</div> </div>
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
<link href="{{ url_for('static', filename='css/colorbox.css', _external=False) }}" rel="stylesheet" type="text/css" media="screen" /> <link href="{{ url_for('static', filename='css/colorbox.css', _external=False) }}" rel="stylesheet" type="text/css" media="screen" />
<script src="{{ url_for('static', filename='js/jquery/jquery.colorbox-min.js') }}" type="application/javascript" charset="utf-8"></script> <script src="{{ url_for('static', filename='js/jquery/jquery.colorbox-min.js') }}" type="application/javascript" charset="utf-8"></script>
<script src="{{ url_for('static', filename='js/scripts/softwareFolder.js') }}" type="application/javascript" charset="utf-8"></script> <script src="{{ url_for('static', filename='js/scripts/softwareFolder.js') }}" type="application/javascript" charset="utf-8"></script>
<script src="{{ url_for('static', filename='js/scripts/shell.js') }}" type="application/javascript" charset="utf-8"></script>
{% endblock %} {% endblock %}
{% block body %} {% block body %}
<style> <style>
...@@ -50,11 +51,18 @@ ...@@ -50,11 +51,18 @@
<li id="fullscreen"><span class="expand_editor" title="Show Editor in Full window. Hint: Use Ctrl+E">&nbsp;</span></li> <li id="fullscreen"><span class="expand_editor" title="Show Editor in Full window. Hint: Use Ctrl+E">&nbsp;</span></li>
<li id="save"><span class="save_btn" title="Save current file. Hint: Use Ctrl+S">&nbsp;</span></li> <li id="save"><span class="save_btn" title="Save current file. Hint: Use Ctrl+S">&nbsp;</span></li>
<li id="option"><span class="fmenu_btn" title='Show more options' rel='tooltip'>Menu</span></li> <li id="option"><span class="fmenu_btn" title='Show more options' rel='tooltip'>Menu</span></li>
<li id="shell"><span class="shell_btn" title="Run a command in a shell">Shell</span></li>
</ul> </ul>
<div id="tabControl"></div> <div id="tabControl"></div>
<div class="clear"></div> <div class="clear"></div>
</div> </div>
<div class="clear"></div> <div class="clear"></div>
<div id="shell-window">
<div id="shell-result">
</div>
<input type="text" name="command" id="shell-input" autocomplete="off" placeholder="Type command ..." />
</div>
<div class="clear"></div>
<div class="software_details"> <div class="software_details">
<div id="details_box"> <div id="details_box">
<div id="fileTree" class="file_tree_short"></div> <div id="fileTree" class="file_tree_short"></div>
......
...@@ -37,6 +37,8 @@ ...@@ -37,6 +37,8 @@
<p>Processing</p> <p>Processing</p>
<div class="clear"></div> <div class="clear"></div>
</div> </div>
<p id="last_build_software" class="last_build"></p><br/>
<div class="clear"></div>
<p>SlapOS rebuild your software from source, allowing you to easily patch or add any free software. <a href="{{ url_for('viewLog', logfile='software.log') }}">Learn how!</a></p> <p>SlapOS rebuild your software from source, allowing you to easily patch or add any free software. <a href="{{ url_for('viewLog', logfile='software.log') }}">Learn how!</a></p>
</div> </div>
...@@ -49,6 +51,8 @@ ...@@ -49,6 +51,8 @@
<p>Waiting for starting</p> <p>Waiting for starting</p>
<div class="clear"></div> <div class="clear"></div>
</div> </div>
<p id="last_build_instance" class="last_build"></p><br/>
<div class="clear"></div>
<p>SlapOS configure your running environment to match your needs. <a href="{{ url_for('viewLog', logfile='instance.log') }}">Learn how!</a></p> <p>SlapOS configure your running environment to match your needs. <a href="{{ url_for('viewLog', logfile='instance.log') }}">Learn how!</a></p>
</div> </div>
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
# pylint: disable-msg=W0311,C0301,C0103,C0111,W0141,W0142 # pylint: disable-msg=W0311,C0301,C0103,C0111,W0141,W0142
import ConfigParser import ConfigParser
import datetime
import json import json
import logging import logging
import md5 import md5
...@@ -275,6 +276,29 @@ def isSoftwareRunning(config=None): ...@@ -275,6 +276,29 @@ def isSoftwareRunning(config=None):
return isRunning('slapgrid-sr') return isRunning('slapgrid-sr')
def slapgridResultToFile(config, step, returncode, datetime):
filename = step + "_info.json"
file = os.path.join(config['runner_workdir'], filename)
result = {'last_build':datetime, 'success':returncode}
open(file, "w").write(json.dumps(result))
def getSlapgridResult(config, step):
filename = step + "_info.json"
file = os.path.join(config['runner_workdir'], filename)
if os.path.exists(file):
result = json.loads(open(file, "r").read())
else:
result = {'last_build': 0, 'success':-1}
return result
def waitProcess(config, process, step):
process.wait()
date = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
slapgridResultToFile(config, step, process.returncode, date)
def runSoftwareWithLock(config, lock=True): def runSoftwareWithLock(config, lock=True):
""" """
Use Slapgrid to compile current Software Release and wait until Use Slapgrid to compile current Software Release and wait until
...@@ -301,10 +325,13 @@ def runSoftwareWithLock(config, lock=True): ...@@ -301,10 +325,13 @@ def runSoftwareWithLock(config, lock=True):
name='slapgrid-sr', stdout=None) name='slapgrid-sr', stdout=None)
if lock: if lock:
slapgrid.wait() slapgrid.wait()
date = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
slapgridResultToFile(config, "software", slapgrid.returncode, date)
#Saves the current compile software for re-use #Saves the current compile software for re-use
config_SR_folder(config) config_SR_folder(config)
return ( True if slapgrid.returncode == 0 else False ) return ( True if slapgrid.returncode == 0 else False )
else: else:
thread.start_new_thread(waitProcess, (config, slapgrid, "software"))
return False return False
...@@ -401,8 +428,11 @@ def runInstanceWithLock(config, lock=True): ...@@ -401,8 +428,11 @@ def runInstanceWithLock(config, lock=True):
name='slapgrid-cp', stdout=None) name='slapgrid-cp', stdout=None)
if lock: if lock:
slapgrid.wait() slapgrid.wait()
date = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
slapgridResultToFile(config, "instance", slapgrid.returncode, date)
return ( True if slapgrid.returncode == 0 else False ) return ( True if slapgrid.returncode == 0 else False )
else: else:
thread.start_new_thread(waitProcess, (config, slapgrid, "instance"))
return False return False
...@@ -896,3 +926,17 @@ def setupDefaultSR(config): ...@@ -896,3 +926,17 @@ def setupDefaultSR(config):
configNewSR(config, config['default_sr']) configNewSR(config, config['default_sr'])
if config['auto_deploy']: if config['auto_deploy']:
thread.start_new_thread(buildAndRun, (config,)) thread.start_new_thread(buildAndRun, (config,))
def setMiniShellHistory(config, command):
history_max_size = 10
command = command + "\n"
history_file = config['minishell_history_file']
if os.path.exists(history_file):
history = open(history_file, 'r').readlines()
if len(history) >= history_max_size:
del history[0]
else:
history = []
history.append(command)
open(history_file, 'w+').write(''.join(history))
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
import json import json
import os import os
import shutil import shutil
import subprocess
import thread import thread
import urllib import urllib
...@@ -14,7 +15,8 @@ from flask import (Flask, request, redirect, url_for, render_template, ...@@ -14,7 +15,8 @@ from flask import (Flask, request, redirect, url_for, render_template,
from slapos.runner.process import killRunningProcess from slapos.runner.process import killRunningProcess
from slapos.runner.utils import (checkSoftwareFolder, configNewSR, from slapos.runner.utils import (checkSoftwareFolder, configNewSR,
createNewUser, getProfilePath, createNewUser, getBuildAndRunParams,
getProfilePath, getSlapgridResult,
listFolder, getBuildAndRunParams, listFolder, getBuildAndRunParams,
getProjectTitle, getRcode, getSession, getProjectTitle, getRcode, getSession,
getSlapStatus, getSvcStatus, getSlapStatus, getSvcStatus,
...@@ -26,6 +28,7 @@ from slapos.runner.utils import (checkSoftwareFolder, configNewSR, ...@@ -26,6 +28,7 @@ from slapos.runner.utils import (checkSoftwareFolder, configNewSR,
removeSoftwareByName, runInstanceWithLock, removeSoftwareByName, runInstanceWithLock,
runSoftwareWithLock, runSlapgridUntilSuccess, runSoftwareWithLock, runSlapgridUntilSuccess,
saveSession, saveBuildAndRunParams, saveSession, saveBuildAndRunParams,
setMiniShellHistory,
svcStartStopProcess, svcStopAll, tail, svcStartStopProcess, svcStopAll, tail,
updateInstanceParameter) updateInstanceParameter)
...@@ -93,6 +96,10 @@ def myAccount(): ...@@ -93,6 +96,10 @@ def myAccount():
params=getBuildAndRunParams(app.config)) params=getBuildAndRunParams(app.config))
def getSlapgridParameters():
return jsonify(getBuildAndRunParams(app.config))
def manageRepository(): def manageRepository():
public_key = open(app.config['public_key']).read() public_key = open(app.config['public_key']).read()
account = getSession(app.config) account = getSession(app.config)
...@@ -215,6 +222,8 @@ def getFileLog(): ...@@ -215,6 +222,8 @@ def getFileLog():
else: else:
file_path = realpath(app.config, logfile) file_path = realpath(app.config, logfile)
try: try:
if not os.path.exists(file_path):
raise IOError
if not isText(file_path): if not isText(file_path):
return jsonify(code=0, return jsonify(code=0,
result="Can not open binary file, please select a text file!") result="Can not open binary file, please select a text file!")
...@@ -446,7 +455,15 @@ def slapgridResult(): ...@@ -446,7 +455,15 @@ def slapgridResult():
if os.path.exists(app.config[log_file]): if os.path.exists(app.config[log_file]):
log_result = readFileFrom(open(app.config[log_file]), log_result = readFileFrom(open(app.config[log_file]),
int(request.form['position'])) int(request.form['position']))
return jsonify(software=software_state, instance=instance_state, build_result = getSlapgridResult(app.config, 'software')
run_result = getSlapgridResult(app.config, 'instance')
software_info = {'state':software_state,
'last_build':build_result['last_build'],
'success':build_result['success']}
instance_info = {'state':instance_state,
'last_build':run_result['last_build'],
'success':run_result['success']}
return jsonify(software=software_info, instance=instance_info,
result=(instance_state or software_state), content=log_result) result=(instance_state or software_state), content=log_result)
...@@ -691,6 +708,47 @@ def isSRReady(): ...@@ -691,6 +708,47 @@ def isSRReady():
return isSoftwareReleaseReady(app.config) return isSoftwareReleaseReady(app.config)
def runCommand():
cwd = open(app.config['minishell_cwd_file'], 'r').read().strip()
command = request.form.get("command", '').strip()
parsed_commands = command.split(';');
# does the user want to change current directory ?
for cmd in parsed_commands:
if 'cd' == cmd[:2]:
cmd = cmd.split(' ');
real_cmd = cmd[:]
if len(cmd) == 1:
cmd.append(os.environ.get('HOME'))
# shorten directory's name, to avoid things like : /a/../b
cd_dir = os.path.realpath(os.path.join(cwd, cmd[1]))
if os.path.exists(cd_dir) and os.path.isdir(cd_dir):
cwd = cd_dir
# save new cwd in the config file
open(app.config['minishell_cwd_file'], 'w').write(cwd)
# if the command was just cd, execute it. Otherwise, execute the rest
command = command.replace(' '.join(real_cmd), '').strip(';')
if not command:
return jsonify(path=cwd, data="Changed directory, now in : "+cwd)
try:
setMiniShellHistory(app.config, command)
command = "timeout 600 " + command
return jsonify(path=cwd, data=subprocess.check_output(command, stderr=subprocess.STDOUT, shell=True, cwd=cwd))
except subprocess.CalledProcessError as e:
error = "Error : process exited with exit code " + str(e.returncode) + \
"\nProcess says :\n" + e.output
return error
def getMiniShellHistory():
history_file = app.config['minishell_history_file']
if not os.path.exists(history_file):
return ""
history = open(history_file, 'r').readlines()
for line, text in enumerate(history):
history[line] = text.strip()
return json.dumps(history)
#Setup List of URLs #Setup List of URLs
app.add_url_rule('/', 'home', home) app.add_url_rule('/', 'home', home)
app.add_url_rule('/browseWorkspace', 'browseWorkspace', browseWorkspace) app.add_url_rule('/browseWorkspace', 'browseWorkspace', browseWorkspace)
...@@ -773,3 +831,6 @@ app.add_url_rule("/editFile", 'editFile', editFile, methods=['GET']) ...@@ -773,3 +831,6 @@ app.add_url_rule("/editFile", 'editFile', editFile, methods=['GET'])
app.add_url_rule('/shell', 'shell', shell) app.add_url_rule('/shell', 'shell', shell)
app.add_url_rule('/isSRReady', 'isSRReady', isSRReady) app.add_url_rule('/isSRReady', 'isSRReady', isSRReady)
app.add_url_rule('/addUser', 'addUser', addUser, methods=['POST']) app.add_url_rule('/addUser', 'addUser', addUser, methods=['POST'])
app.add_url_rule('/getSlapgridParameters', 'getSlapgridParameters', getSlapgridParameters, methods=['GET'])
app.add_url_rule('/runCommand', 'runCommand', runCommand, methods=['POST'])
app.add_url_rule("/getMiniShellHistory", 'getMiniShellHistory', getMiniShellHistory, methods=['GET'])
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