views.py 31.9 KB
Newer Older
1
# -*- coding: utf-8 -*-
2
# vim: set et sts=2:
Marco Mariani's avatar
Marco Mariani committed
3 4
# pylint: disable-msg=W0311,C0301,C0103,C0111

5
import codecs
6
import json
Łukasz Nowak's avatar
Łukasz Nowak committed
7 8
import os
import shutil
9
import subprocess
10
import sup_process
11
import thread
12
import urllib
Łukasz Nowak's avatar
Łukasz Nowak committed
13

Marco Mariani's avatar
Marco Mariani committed
14 15 16
from flask import (Flask, request, redirect, url_for, render_template,
                   g, flash, jsonify, session, abort, send_file)

17
import slapos
18
from slapos.runner.utils import (checkSoftwareFolder, configNewSR, checkUserCredential,
19 20
                                 createNewUser, getBuildAndRunParams,
                                 getProfilePath, getSlapgridResult,
21
                                 html_escape,
22
                                 listFolder, getBuildAndRunParams,
23
                                 getProjectTitle, getRcode, updateUserCredential,
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
24
                                 getSlapStatus, getSvcStatus,
25
                                 getSvcTailProcess, getUsernameList, isInstanceRunning,
26
                                 isSoftwareRunning, isSoftwareReleaseReady, isText,
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
27 28
                                 loadSoftwareRList, md5sum, newSoftware,
                                 readFileFrom, readParameters, realpath,
29
                                 removeCurrentInstance,
30
                                 removeSoftwareByName, runSlapgridUntilSuccess,
31
                                 saveBuildAndRunParams,
32
                                 setMiniShellHistory,
33
                                 stopProxy,
34
                                 svcStartStopProcess, svcStartAll, svcStopAll, tail,
35
                                 updateInstanceParameter)
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
36

37
from git import GitCommandError
Marco Mariani's avatar
Marco Mariani committed
38
from slapos.runner.fileBrowser import FileBrowser
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
39
from slapos.runner.gittools import (cloneRepo, gitStatus, switchBranch,
40 41
                                    addBranch, getDiff, gitCommit, gitPush,
                                    gitPull, updateGitConfig, safeResult)
Marco Mariani's avatar
Marco Mariani committed
42 43


Łukasz Nowak's avatar
Łukasz Nowak committed
44
app = Flask(__name__)
45
app.config['MAX_CONTENT_LENGTH'] = 20 * 1024 * 1024
Marco Mariani's avatar
Marco Mariani committed
46
file_request = FileBrowser(app.config)
47

48
import logging
49
logger = logging.getLogger('slaprunner')
Marco Mariani's avatar
Marco Mariani committed
50

51 52
def login_redirect(*args, **kwargs):
  return redirect(url_for('login'))
Łukasz Nowak's avatar
Łukasz Nowak committed
53

54

55 56
@app.before_request
def before_request():
57 58
  if request.path.startswith('/static') \
    or request.path == '/isSRReady':
59
    return
60
  """
61 62 63 64 65 66 67
  account = getSession(app.config)
  if account:
    session['title'] = getProjectTitle(app.config)
  else:
    session['title'] = "No account is defined"
    if request.path != "/setAccount" and request.path != "/configAccount":
      return redirect(url_for('setAccount'))
68 69
  """
  session['title'] = getProjectTitle(app.config)
70
  g.instance_monitoring_url =  app.config['instance_monitoring_url']
71

Łukasz Nowak's avatar
Łukasz Nowak committed
72 73 74 75
# general views
def home():
  return render_template('index.html')

Marco Mariani's avatar
Marco Mariani committed
76

77 78 79 80
# general views
def browseWorkspace():
  return render_template('workspace.html')

Marco Mariani's avatar
Marco Mariani committed
81

82 83 84 85
@app.route("/login")
def login():
  return render_template('login.html')

Marco Mariani's avatar
Marco Mariani committed
86

87 88
@app.route("/setAccount")
def setAccount():
89
  """account = getSession(app.config)
90
  if not account:
91
    return render_template('account.html')
92
  return redirect(url_for('login'))"""
93

Marco Mariani's avatar
Marco Mariani committed
94

95
def myAccount():
96 97 98 99 100 101 102 103 104 105 106 107
  # account = getSession(app.config)
  git_user_path = os.path.join(app.config['etc_dir'], '.git_user')
  name = email = ""
  if os.path.exists(git_user_path):
    with open(git_user_path, 'r') as gfile:
      name, email = gfile.read().split(';')
  return render_template(
    'account.html',
    username_list=getUsernameList(app.config),
    email=email,
    name=name.decode('utf-8'),
    params=getBuildAndRunParams(app.config))
108

Marco Mariani's avatar
Marco Mariani committed
109

110 111 112 113
def getSlapgridParameters():
  return jsonify(getBuildAndRunParams(app.config))


114
def manageRepository():
115
  public_key = open(app.config['public_key']).read()
116 117 118 119
  account = [] #getSession(app.config)
  git_user_path = os.path.join(app.config['etc_dir'], '.git_user')
  name = email = ""
  if os.path.exists(git_user_path):
120
    with codecs.open(git_user_path, 'r', encoding='utf-8') as gfile:
121
      name, email = gfile.read().split(';')
122 123
  return render_template('manageRepository.html', workDir='workspace',
            project=listFolder(app.config, 'workspace'),
124 125
            public_key=public_key, name=name.decode('utf-8'),
            email=email)
126

Marco Mariani's avatar
Marco Mariani committed
127

128 129
@app.route("/doLogin", methods=['POST'])
def doLogin():
130 131 132
  #XXX Now has to check the .htpasswd if we want to warn
  #the user that he misspelled his name/password
  return jsonify(code=1, result="")
133 134


Łukasz Nowak's avatar
Łukasz Nowak committed
135 136
# software views
def editSoftwareProfile():
137
  profile = getProfilePath(app.config['etc_dir'], app.config['software_profile'])
138 139
  if profile == "":
    flash('Error: can not open profile, please select your project first')
140
  return render_template('updateSoftwareProfile.html', workDir='workspace',
141
      profile=profile, projectList=listFolder(app.config, 'workspace'))
Łukasz Nowak's avatar
Łukasz Nowak committed
142

Marco Mariani's avatar
Marco Mariani committed
143

Łukasz Nowak's avatar
Łukasz Nowak committed
144
def inspectSoftware():
145 146
  return render_template('runResult.html', softwareRoot='software_link/',
                         softwares=loadSoftwareRList(app.config))
Łukasz Nowak's avatar
Łukasz Nowak committed
147

Marco Mariani's avatar
Marco Mariani committed
148

Alain Takoudjou's avatar
Alain Takoudjou committed
149
#remove content of compiled software release
Łukasz Nowak's avatar
Łukasz Nowak committed
150 151 152
def removeSoftware():
  if isSoftwareRunning(app.config) or isInstanceRunning(app.config):
    flash('Software installation or instantiation in progress, cannot remove')
153
  elif os.path.exists(app.config['software_root']):
Łukasz Nowak's avatar
Łukasz Nowak committed
154 155
    svcStopAll(app.config)
    shutil.rmtree(app.config['software_root'])
156 157
    for link in os.listdir(app.config['software_link']):
      os.remove(os.path.join(app.config['software_link'], link))
Łukasz Nowak's avatar
Łukasz Nowak committed
158 159 160
    flash('Software removed')
  return redirect(url_for('inspectSoftware'))

Marco Mariani's avatar
Marco Mariani committed
161

Łukasz Nowak's avatar
Łukasz Nowak committed
162
def runSoftwareProfile():
163 164
  thread.start_new_thread(runSlapgridUntilSuccess, (app.config, "software"))
  return jsonify(result=True)
Marco Mariani's avatar
Marco Mariani committed
165

Łukasz Nowak's avatar
Łukasz Nowak committed
166 167 168

# instance views
def editInstanceProfile():
169
  profile = getProfilePath(app.config['etc_dir'], app.config['instance_profile'])
170
  if profile == "":
171 172
    flash('Error: can not open instance profile for this Software Release')
  return render_template('updateInstanceProfile.html', workDir='workspace',
173
      profile=profile, projectList=listFolder(app.config, 'workspace'))
Łukasz Nowak's avatar
Łukasz Nowak committed
174

Marco Mariani's avatar
Marco Mariani committed
175

Alain Takoudjou's avatar
Alain Takoudjou committed
176
# get status of all computer partitions and process state
Łukasz Nowak's avatar
Łukasz Nowak committed
177 178
def inspectInstance():
  if os.path.exists(app.config['instance_root']):
179 180 181 182 183
    file_path = 'instance_root'
    supervisor = getSvcStatus(app.config)
  else:
    file_path = ''
    supervisor = []
184 185 186 187 188 189 190 191
  if "application/json" in request.accept_mimetypes.best:
    result_list = []
    for service in supervisor:
      result_list.append({
        'service_name': service[0],
        'status': service[1],
      })
    return jsonify(result_list)
Łukasz Nowak's avatar
Łukasz Nowak committed
192
  return render_template('instanceInspect.html',
193 194 195 196
                         file_path=file_path,
                         supervisor=supervisor,
                         slap_status=getSlapStatus(app.config),
                         partition_amount=app.config['partition_amount'])
Łukasz Nowak's avatar
Łukasz Nowak committed
197

198 199 200 201 202 203 204 205 206
def getConnectionParameter(partition_reference):
  """
  Return connection parameters of a partition.
  """
  slap = slapos.slap.slap()
  slap.initializeConnection(app.config['master_url'])
  partition = slap.registerComputerPartition(app.config['computer_id'], partition_reference)
  return jsonify(partition.getConnectionParameterDict())
app.add_url_rule("/getConnectionParameter/<partition_reference>", 'getConnectionParameter', getConnectionParameter, methods=['GET'])
Łukasz Nowak's avatar
Łukasz Nowak committed
207

Alain Takoudjou's avatar
Alain Takoudjou committed
208
#Reload instance process ans returns new value to ajax
209 210
def supervisordStatus():
  result = getSvcStatus(app.config)
211
  if not result:
212
    return jsonify(code=0, result="")
213
  # XXX-Marco -> template
214 215 216
  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>"
217
    html += "<td  class='first'><b><a href='" + url_for('tailProcess', process=item[0]) + "'>" + item[0] + "</a></b></td>"
218
    html += "<td align='center'><a href='" + url_for('startStopProccess', process=item[0], action=item[1]) + "'>" + item[1] + "</a></td>"
Marco Mariani's avatar
Marco Mariani committed
219
    html += "<td align='center'>" + item[3] + "</td><td>" + item[5] + "</td>"
220
    html += "<td align='center'><a href='" + url_for('startStopProccess', process=item[0], action='RESTART') + "'>Restart</a></td>"
Marco Mariani's avatar
Marco Mariani committed
221
    html += "</tr>"
222 223
  return jsonify(code=1, result=html)

Marco Mariani's avatar
Marco Mariani committed
224

Łukasz Nowak's avatar
Łukasz Nowak committed
225
def removeInstance():
226
  logger.warning("User clicked on 'Destroy All Services'. Removing all instances...")
227 228 229
  result = removeCurrentInstance(app.config)
  if isinstance(result, str):
    flash(result)
Łukasz Nowak's avatar
Łukasz Nowak committed
230 231
  return redirect(url_for('inspectInstance'))

Marco Mariani's avatar
Marco Mariani committed
232

Łukasz Nowak's avatar
Łukasz Nowak committed
233 234 235
def runInstanceProfile():
  if not os.path.exists(app.config['instance_root']):
    os.mkdir(app.config['instance_root'])
236 237
  thread.start_new_thread(runSlapgridUntilSuccess, (app.config, "instance"))
  return jsonify(result=True)
Marco Mariani's avatar
Marco Mariani committed
238

Łukasz Nowak's avatar
Łukasz Nowak committed
239

240 241 242 243 244 245 246 247 248
def viewLog():
  return render_template('viewLog.html')

def getFileLog():
  logfile = request.form.get('filename', '').encode('utf-8')
  if logfile == "instance.log":
    file_path = app.config['instance_log']
  elif logfile == "software.log":
    file_path = app.config['software_log']
Łukasz Nowak's avatar
Łukasz Nowak committed
249
  else:
250 251
    file_path = realpath(app.config, logfile)
  try:
252 253
    if not os.path.exists(file_path):
      raise IOError
254
    if not isText(file_path):
255
      content = "Can not open binary file, please select a text file!"
256 257 258 259
    if 'truncate' in request.form:
      content = tail(open(file_path), int(request.form['truncate']))
    else:
      with open(file_path) as f:
260 261
        content = f.read()
    return jsonify(code=1, result=html_escape(content))
262 263
  except:
    return jsonify(code=0, result="Warning: Log file doesn't exist yet or empty log!!")
Łukasz Nowak's avatar
Łukasz Nowak committed
264

Marco Mariani's avatar
Marco Mariani committed
265

Łukasz Nowak's avatar
Łukasz Nowak committed
266 267 268
def stopAllPartition():
  svcStopAll(app.config)
  return redirect(url_for('inspectInstance'))
269

270 271 272
def startAllPartition():
  svcStartAll(app.config)
  return redirect(url_for('inspectInstance'))
Marco Mariani's avatar
Marco Mariani committed
273

274 275 276 277
def tailProcess(process):
  return render_template('processTail.html',
      process_log=getSvcTailProcess(app.config, process), process=process)

Marco Mariani's avatar
Marco Mariani committed
278

279 280 281 282
def startStopProccess(process, action):
  svcStartStopProcess(app.config, process, action)
  return redirect(url_for('inspectInstance'))

Marco Mariani's avatar
Marco Mariani committed
283

284
def openProject(method):
285 286
  return render_template('projectFolder.html', method=method,
                         workDir='workspace')
287

Marco Mariani's avatar
Marco Mariani committed
288

289
def cloneRepository():
290
  path = realpath(app.config, request.form['name'], False)
291 292 293 294 295 296 297
  if not path:
    return jsonify(code=0, result="Can not access folder: Permission Denied")
  try:
    cloneRepo(request.form['repo'], path, request.form['user'], request.form['email'])
    return jsonify(code=1, result="")
  except GitCommandError, e:
    return jsonify(code=0, result=safeResult(str(e)))
298

Marco Mariani's avatar
Marco Mariani committed
299

300 301 302
def listDirectory():
  folderList = listFolder(app.config, request.form['name'])
  return jsonify(result=folderList)
303

Marco Mariani's avatar
Marco Mariani committed
304

305
def createSoftware():
306
  return newSoftware(request.form['folder'], app.config, session)
307

Marco Mariani's avatar
Marco Mariani committed
308

309 310 311
def checkFolder():
  return checkSoftwareFolder(request.form['path'], app.config)

Marco Mariani's avatar
Marco Mariani committed
312

313 314
def setCurrentProject():
  if configNewSR(app.config, request.form['path']):
315 316 317
    session['title'] = getProjectTitle(app.config)
    return jsonify(code=1, result="")
  else:
318
    return jsonify(code=0, result=("Can not setup this Software Release"))
319

Marco Mariani's avatar
Marco Mariani committed
320

321
def getProjectStatus():
322 323
  path = realpath(app.config, request.form['project'])
  if path:
324 325 326 327 328
    try:
      result, branch, isdirty = gitStatus(path)
      return jsonify(code=1, result=result, branch=branch, dirty=isdirty)
    except GitCommandError, e:
      return jsonify(code=0, result=safeResult(str(e)))
329 330
  else:
    return jsonify(code=0, result="Can not read folder: Permission Denied")
331

Marco Mariani's avatar
Marco Mariani committed
332

Alain Takoudjou's avatar
Alain Takoudjou committed
333
#view for current software release files
334
def editCurrentProject():
335 336 337 338 339
  project_file = os.path.join(app.config['etc_dir'], ".project")
  # XXX hardcoded default project
  project = "workspace/slapos"
  if os.path.exists(project_file):
    project = open(project_file).read()
340
  projectList = listFolder(app.config, 'workspace')
341
  if projectList:
342
    return render_template('softwareFolder.html', workDir='runner_workdir',
343
                           project=project,
344
                           projectList=projectList)
345
  else:
346 347
    flash('Please clone slapos repository, or your own repository')
    return redirect(url_for('manageRepository'))
Marco Mariani's avatar
Marco Mariani committed
348

Alain Takoudjou's avatar
Alain Takoudjou committed
349
#create file or directory
350
def createFile():
351 352
  path = realpath(app.config, request.form['file'], False)
  if not path:
353
    return jsonify(code=0, result="Error when creating your %s: Permission Denied" % request.form['type'])
354 355
  try:
    if request.form['type'] == "file":
Marco Mariani's avatar
Marco Mariani committed
356
      open(path, 'w')
357
    else:
358
      os.mkdir(path)
359
    return jsonify(code=1, result="")
360
  except Exception as e:
361 362
    return jsonify(code=0, result=str(e))

Marco Mariani's avatar
Marco Mariani committed
363

Alain Takoudjou's avatar
Alain Takoudjou committed
364
#remove file or directory
365 366 367 368 369 370 371
def removeFile():
  try:
    if request.form['type'] == "folder":
      shutil.rmtree(request.form['path'])
    else:
      os.remove(request.form['path'])
    return jsonify(code=1, result="")
372
  except Exception as e:
373
    return jsonify(code=0, result=str(e))
374

Marco Mariani's avatar
Marco Mariani committed
375

376
def removeSoftwareDir():
377 378 379
    status, message = removeSoftwareByName(app.config, request.form['md5'],
      request.form['title'])
    return jsonify(code=status, result=message)
380

Marco Mariani's avatar
Marco Mariani committed
381

Alain Takoudjou's avatar
Alain Takoudjou committed
382
#read file and return content to ajax
383
def getFileContent():
384 385
  file_path = realpath(app.config, request.form['file'])
  if file_path:
Alain Takoudjou's avatar
Alain Takoudjou committed
386 387 388
    if not isText(file_path):
      return jsonify(code=0,
            result="Can not open a binary file, please select a text file!")
Marco Mariani's avatar
Marco Mariani committed
389
    if 'truncate' in request.form:
390
      content = tail(codecs.open(file_path, "r", "utf_8"), int(request.form['truncate']))
391
      return jsonify(code=1, result=content)
Marco Mariani's avatar
Marco Mariani committed
392
    else:
393
      return jsonify(code=1, result=codecs.open(file_path, "r", "utf_8").read())
394 395
  else:
    return jsonify(code=0, result="Error: No such file!")
396

Marco Mariani's avatar
Marco Mariani committed
397

398
def saveFileContent():
399
  file_path = realpath(app.config, request.form['file'], False)
400
  if file_path:
401 402
    with open(file_path, 'w') as f:
      f.write(request.form['content'].encode("utf-8"))
403 404
    return jsonify(code=1, result="")
  else:
405
    return jsonify(code=0, result="Rejected!! Cannot access to this location.")
406

Marco Mariani's avatar
Marco Mariani committed
407

408
def changeBranch():
409 410
  path = realpath(app.config, request.form['project'])
  if path:
411 412 413 414 415 416 417 418
    try:
      if switchBranch(path, request.form['name']):
        return jsonify(code=1, result="")
      else:
        json = "This is already your active branch for this project"
        return jsonify(code=1, result=json)
    except GitCommandError, e:
      return jsonify(code=0, result=safeResult(str(e)))
419 420
  else:
    return jsonify(code=0, result="Can not read folder: Permission Denied")
421

Marco Mariani's avatar
Marco Mariani committed
422

423
def newBranch():
424 425
  path = realpath(app.config, request.form['project'])
  if path:
426 427 428 429 430 431 432 433 434 435 436
    try:
      if request.form['create'] == '1':
        result = addBranch(path, request.form['name'])
      else:
        result =  addBranch(path, request.form['name'], True)
      if result:
        return jsonify(code=1, result="")
      else:
        return jsonify(code=0, result="Failed to checkout to branch %s.")
    except GitCommandError, e:
      return jsonify(code=0, result=safeResult(str(e)))
437 438
  else:
    return jsonify(code=0, result="Can not read folder: Permission Denied")
439

Marco Mariani's avatar
Marco Mariani committed
440

441
def getProjectDiff():
442 443
  path = realpath(app.config, request.form['project'])
  if path:
444
    return jsonify(code=1, result=getDiff(path))
445
  else:
446 447
    return jsonify(code=0,
                  result="Error: No such file or directory. PERMISSION DENIED!")
448

Marco Mariani's avatar
Marco Mariani committed
449

450 451 452 453 454 455 456 457
def commitProjectFiles():
  path = realpath(app.config, request.form['project'])
  if path:
    return gitCommit(path, request.form['msg'])
  else:
    return jsonify(code=0, result="Can not read folder: Permission Denied")


458
def pushProjectFiles():
459 460
  path = realpath(app.config, request.form['project'])
  if path:
461
    return gitPush(path)
462 463
  else:
    return jsonify(code=0, result="Can not read folder: Permission Denied")
464

Marco Mariani's avatar
Marco Mariani committed
465

466
def pullProjectFiles():
467 468 469 470 471
  path = realpath(app.config, request.form['project'])
  if path:
    return gitPull(path)
  else:
    return jsonify(code=0, result="Can not read folder: Permission Denied")
472

Marco Mariani's avatar
Marco Mariani committed
473

474
def checkFileType():
475 476 477
  path = realpath(app.config, request.form['path'])
  if not path:
    return jsonify(code=0, result="Can not open file: Permission Denied!")
478 479 480
  if isText(path):
    return jsonify(code=1, result="text")
  else:
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
481 482
    return jsonify(code=0,
                   result="Can not open a binary file, please select a text file!")
483

Marco Mariani's avatar
Marco Mariani committed
484

485
def getmd5sum():
486 487 488 489
  realfile = realpath(app.config, request.form['file'])
  if not realfile:
    return jsonify(code=0, result="Can not open file: Permission Denied!")
  md5 = md5sum(realfile)
490 491 492
  if md5:
    return jsonify(code=1, result=md5)
  else:
493 494
    return jsonify(code=0, result="Can not get md5sum for this file!")

Marco Mariani's avatar
Marco Mariani committed
495

Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
496
#return information about state of slapgrid process
497 498 499
def slapgridResult():
  software_state = isSoftwareRunning(app.config)
  instance_state = isInstanceRunning(app.config)
Marco Mariani's avatar
Marco Mariani committed
500 501 502 503 504
  log_result = {
    'content': '',
    'position': 0,
    'truncated': False
  }
Marco Mariani's avatar
Marco Mariani committed
505
  if request.form['log'] in ['software', 'instance']:
506 507
    log_file = request.form['log'] + "_log"
    if os.path.exists(app.config[log_file]):
508 509
      log_result = readFileFrom(open(app.config[log_file]),
                                int(request.form['position']))
510 511 512 513 514 515 516 517 518
  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,
Marco Mariani's avatar
Marco Mariani committed
519 520
                 result=(instance_state or software_state), content=log_result)

521 522

def stopSlapgrid():
523 524
  counter_file = os.path.join(app.config['runner_workdir'], '.turn-left')
  open(counter_file, 'w+').write(str(0))
525
  result = sup_process.killRunningProcess(app.config, request.form['type'])
526 527
  return jsonify(result=result)

Marco Mariani's avatar
Marco Mariani committed
528

529 530 531 532 533
def getPath():
  files = request.form['file'].split('#')
  list = []
  for p in files:
    path = realpath(app.config, p)
534
    if not path:
535 536 537 538
      list = []
      break
    else:
      list.append(path)
Marco Mariani's avatar
Marco Mariani committed
539
  realfile = '#'.join(list)
540
  if not realfile:
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
541 542
    return jsonify(code=0,
                   result="Can not access to this file: Permission Denied!")
543
  else:
544 545
    return jsonify(code=1, result=realfile)

Marco Mariani's avatar
Marco Mariani committed
546

547
def saveParameterXml():
548 549 550
  """
  Update instance parameter into a local xml file.
  """
551
  project = os.path.join(app.config['etc_dir'], ".project")
552 553
  if not os.path.exists(project):
    return jsonify(code=0, result="Please first open a Software Release")
554
  content = request.form['parameter'].encode("utf-8")
555
  param_path = os.path.join(app.config['etc_dir'], ".parameter.xml")
556
  try:
557 558
    with open(param_path, 'w') as f:
      f.write(content)
559
    result = readParameters(param_path)
560
  except Exception as e:
561
    result = str(e)
562
  software_type = None
563
  if request.form['software_type']:
564
    software_type = request.form['software_type']
565
  if type(result) == type(''):
566
    return jsonify(code=0, result=result)
567
  else:
568
    try:
569
      updateInstanceParameter(app.config, software_type)
570
    except Exception as e:
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
571 572
      return jsonify(
        code=0,
573
        result="An error occurred while applying your settings!<br/>%s" % e)
574 575
    return jsonify(code=1, result="")

Marco Mariani's avatar
Marco Mariani committed
576

577 578 579
def getSoftwareType():
  software_type_path = os.path.join(app.config['etc_dir'], ".software_type.xml")
  if os.path.exists(software_type_path):
580
    return jsonify(code=1, result=open(software_type_path).read())
581 582
  return jsonify(code=1, result="default")

Marco Mariani's avatar
Marco Mariani committed
583

Alain Takoudjou's avatar
Alain Takoudjou committed
584
#read instance parameters into the local xml file and return a dict
585
def getParameterXml(request):
586
  param_path = os.path.join(app.config['etc_dir'], ".parameter.xml")
587
  if not os.path.exists(param_path):
588
    default = '<?xml version="1.0" encoding="utf-8"?>\n<instance>\n</instance>'
589 590
    return jsonify(code=1, result=default)
  if request == "xml":
591
    parameters = open(param_path).read()
592 593 594 595
  else:
    parameters = readParameters(param_path)
  if type(parameters) == type('') and request != "xml":
    return jsonify(code=0, result=parameters)
596
  else:
597 598
    return jsonify(code=1, result=parameters)

599

600
#update user-defined slapgrid parameters
601
def updateBuildAndRunConfig():
602 603 604 605 606 607 608 609 610 611 612
  code = 1
  try:
    max_run_instance = int(request.form['max_run_instance'].strip())
    max_run_software = int(request.form['max_run_software'].strip())
  except ValueError:
    code = 0
    result = "Error! You should have provided an integer"
  run_instance = (True if request.form['run_instance']=="true" else False)
  run_software = (True if request.form['run_software']=="true" else False)
  if code:
    params =  {}
613 614 615 616 617
    params['run_instance'] = run_instance
    params['run_software'] = run_software
    params['max_run_instance'] = max_run_instance
    params['max_run_software'] = max_run_software
    saveBuildAndRunParams(app.config, params)
618 619 620 621
    result = "Your parameters have correctly been updated"
  return jsonify(code=code, result=result)


622 623
#update user account data
def updateAccount():
624

625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
  username = request.form['username'].strip()
  password = request.form['password'].strip()
  new_password = request.form['new_password'].strip()
  email = request.form['email'].strip()
  name = request.form['name'].strip()

  if username and password and new_password:
    if not checkUserCredential(app.config, username, password):
      return jsonify(code=0, result="Username and password doesn't match for '%s'" % username)

    result = updateUserCredential(app.config, username, new_password)
    if not result:
      return jsonify(code=0, result="Failed to update Credentials!")

  try:
    updateGitConfig(app.config['default_repository_path'], name, email)
  except GitCommandError, e:
    return jsonify(code=0, result=str(e))
643 644 645
  git_user_file = os.path.join(app.config['etc_dir'], '.git_user')
  with codecs.open(git_user_file, 'w', encoding='utf-8') as gfile:
    gfile.write(u'{};{}'.format(name, email))
646
  return jsonify(code=1, result="")
647

648

649 650 651
#update user account data
@app.route("/configAccount", methods=['POST'])
def configAccount():
652
  """last_account = getSession(app.config)
653 654 655 656 657 658 659 660 661 662
  if last_account:
    return jsonify(code=0,
                   result="Unable to respond to your request, permission denied.")

  account = []
  account.append(request.form['username'].strip())
  account.append(request.form['password'].strip())
  account.append(request.form['email'].strip())
  account.append(request.form['name'].strip())
  code = request.form['rcode'].strip()
663
  recovery_code = getRcode(app.config)
664 665 666 667 668 669
  if code != recovery_code:
    return jsonify(code=0, result="Your password recovery code is not valid!")
  result = saveSession(app.config, account)
  if type(result) == type(""):
    return jsonify(code=0, result=result)
  else:
670
    return jsonify(code=1, result="")"""
671

672 673 674 675 676 677 678
def addUser():
  if createNewUser(app.config, request.form['username'],
                   request.form['password']):
    return jsonify(code=1, result="New user succesfully saved")
  else:
    return jsonify(code=0, result="Problem while creating new user")

679

680 681 682 683 684 685 686 687 688 689 690 691 692
#Global File Manager
def fileBrowser():
  if request.method == 'POST':
    filename = request.form.get('filename', '').encode('utf-8')
    dir = request.form['dir'].encode('utf-8')
    newfilename = request.form.get('newfilename', '').encode('utf-8')
    files = request.form.get('files', '').encode('utf-8')
    if not request.form.has_key('opt') or not request.form['opt']:
      opt = 1
    else:
      opt = int(request.form['opt'])
  else:
    opt = int(request.args.get('opt'))
693

694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748
  try:
    if opt == 1:
      #list files and directories
      result = file_request.listDirs(dir)
    elif opt == 2:
      #Create file
      result = file_request.makeFile(dir, filename)
    elif opt == 3:
      #Create directory
      result = file_request.makeDirectory(dir, filename)
    elif opt == 4:
      #Delete a list of files or/and directories
      result = file_request.deleteItem(dir, files)
    elif opt == 5:
      #copy a lis of files or/and directories
      result = file_request.copyItem(dir, files)
    elif opt == 6:
      #rename file or directory
      result = file_request.rename(dir, filename, newfilename)
    elif opt == 7:
      result = file_request.copyItem(dir, files, del_source=True)
    elif opt == 8:
      #donwload file
      filename = request.args.get('filename').encode('utf-8')
      result = file_request.downloadFile(request.args.get('dir').encode('utf-8'),
                filename)
      try:
        return send_file(result, attachment_filename=filename, as_attachment=True)
      except:
        abort(404)
    elif opt == 9:
      result = file_request.readFile(dir, filename, False)
    elif opt == 11:
      #Upload file
      result = file_request.uploadFile(dir, request.files)
    elif opt == 14:
      #Copy file or directory as ...
      result = file_request.copyAsFile(dir, filename, newfilename)
    elif opt == 16:
      #zip file
      result = file_request.zipFile(dir, filename, newfilename)
    elif opt == 17:
      #zip file
      result = file_request.unzipFile(dir, filename, newfilename)
    elif opt == 20:
      #Fancy Load folder
      key = request.args.get('key')
      dir = request.args.get('dir').encode('utf-8')
      listfiles = request.args.get('listfiles', '')
      data = file_request.fancylistDirs(dir, key, listfiles)
      result = json.dumps(data)
    else:
      result = "ARGS PARSE ERROR: Bad option..."
  except Exception as e:
    return str(e)
749 750
  return result

Marco Mariani's avatar
Marco Mariani committed
751

752 753 754
def editFile():
  return render_template('editFile.html', workDir='workspace',
    profile=urllib.unquote(request.args.get('profile', '')),
755
    projectList=listFolder(app.config, 'workspace'),
756 757
    filename=urllib.unquote(request.args.get('filename', '')))

758
def shell():
759
  return render_template('shell.html')
Marco Mariani's avatar
Marco Mariani committed
760

761

762 763 764
def isSRReady():
  return isSoftwareReleaseReady(app.config)

765

766 767 768 769 770 771 772 773
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(' ');
774
      real_cmd = cmd[:]
775 776 777 778
      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]))
779
      if os.path.exists(cd_dir) and os.path.isdir(cd_dir):
780 781 782 783
        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
784
        command = command.replace(' '.join(real_cmd), '').strip(';')
785
        if not command:
786
          return jsonify(path=cwd, data="Changed directory, now in : "+cwd)
787
  try:
788
    setMiniShellHistory(app.config, command)
789
    command = "timeout 600 " + command
790
    return jsonify(path=cwd, data=subprocess.check_output(command, stderr=subprocess.STDOUT, shell=True, cwd=cwd, env={'PATH': app.config['path']}))
791
  except subprocess.CalledProcessError as e:
792
    return jsonify(path=cwd, data=e.output, returncode=e.returncode, error=True)
793 794


795 796 797 798 799 800 801 802 803 804
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)


805 806
#Setup List of URLs
app.add_url_rule('/', 'home', home)
807 808 809
app.add_url_rule('/browseWorkspace', 'browseWorkspace', browseWorkspace)
app.add_url_rule('/editSoftwareProfile', 'editSoftwareProfile',
                editSoftwareProfile)
810 811
app.add_url_rule('/inspectSoftware', 'inspectSoftware', inspectSoftware)
app.add_url_rule('/removeSoftware', 'removeSoftware', removeSoftware)
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
812
app.add_url_rule('/runSoftwareProfile', 'runSoftwareProfile',
813
                 runSoftwareProfile, methods=['GET', 'POST'])
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
814 815 816 817 818 819 820
app.add_url_rule('/editInstanceProfile', 'editInstanceProfile',
                 editInstanceProfile)
app.add_url_rule('/inspectInstance', 'inspectInstance',
                 inspectInstance, methods=['GET'])
app.add_url_rule('/supervisordStatus', 'supervisordStatus',
                 supervisordStatus, methods=['GET'])
app.add_url_rule('/runInstanceProfile', 'runInstanceProfile',
821
                 runInstanceProfile, methods=['GET', 'POST'])
822
app.add_url_rule('/removeInstance', 'removeInstance', removeInstance)
823 824 825 826
app.add_url_rule('/viewLog', 'viewLog',
                 viewLog, methods=['GET'])
app.add_url_rule("/getFileLog", 'getFileLog', getFileLog,
                 methods=['POST'])
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
827 828
app.add_url_rule('/stopAllPartition', 'stopAllPartition',
                 stopAllPartition, methods=['GET'])
829 830
app.add_url_rule('/startAllPartition', 'startAllPartition',
                 startAllPartition, methods=['GET'])
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
831 832 833 834 835 836
app.add_url_rule('/tailProcess/name/<process>', 'tailProcess',
                 tailProcess, methods=['GET'])
app.add_url_rule('/startStopProccess/name/<process>/cmd/<action>',
                 'startStopProccess', startStopProccess, methods=['GET'])
app.add_url_rule("/getParameterXml/<request>", 'getParameterXml',
                 getParameterXml, methods=['GET'])
837 838
app.add_url_rule('/getSoftwareType', 'getSoftwareType',
                 getSoftwareType, methods=['GET'])
839
app.add_url_rule("/stopSlapgrid", 'stopSlapgrid', stopSlapgrid, methods=['POST'])
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
840 841
app.add_url_rule("/slapgridResult", 'slapgridResult',
                 slapgridResult, methods=['POST'])
842
app.add_url_rule("/getmd5sum", 'getmd5sum', getmd5sum, methods=['POST'])
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
843 844
app.add_url_rule("/checkFileType", 'checkFileType', checkFileType,
                 methods=['POST'])
845 846
app.add_url_rule("/commitProjectFiles", 'commitProjectFiles', commitProjectFiles,
                 methods=['POST'])
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
847 848 849 850
app.add_url_rule("/pullProjectFiles", 'pullProjectFiles', pullProjectFiles,
                 methods=['POST'])
app.add_url_rule("/pushProjectFiles", 'pushProjectFiles', pushProjectFiles,
                 methods=['POST'])
851 852
app.add_url_rule("/getProjectDiff", 'getProjectDiff', getProjectDiff,
                 methods=['POST'])
853 854
app.add_url_rule("/newBranch", 'newBranch', newBranch, methods=['POST'])
app.add_url_rule("/changeBranch", 'changeBranch', changeBranch, methods=['POST'])
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
855 856 857 858 859 860
app.add_url_rule("/saveFileContent", 'saveFileContent', saveFileContent,
                 methods=['POST'])
app.add_url_rule("/removeSoftwareDir", 'removeSoftwareDir', removeSoftwareDir,
                 methods=['POST'])
app.add_url_rule("/getFileContent", 'getFileContent', getFileContent,
                 methods=['POST'])
861 862 863
app.add_url_rule("/removeFile", 'removeFile', removeFile, methods=['POST'])
app.add_url_rule("/createFile", 'createFile', createFile, methods=['POST'])
app.add_url_rule("/editCurrentProject", 'editCurrentProject', editCurrentProject)
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
864 865 866 867 868 869
app.add_url_rule("/getProjectStatus", 'getProjectStatus', getProjectStatus,
                 methods=['POST'])
app.add_url_rule('/openProject/<method>', 'openProject', openProject,
                 methods=['GET'])
app.add_url_rule("/setCurrentProject", 'setCurrentProject', setCurrentProject,
                 methods=['POST'])
870
app.add_url_rule("/checkFolder", 'checkFolder', checkFolder, methods=['POST'])
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
871 872 873 874
app.add_url_rule('/createSoftware', 'createSoftware', createSoftware,
                 methods=['POST'])
app.add_url_rule('/cloneRepository', 'cloneRepository', cloneRepository,
                 methods=['POST'])
875 876
app.add_url_rule('/listDirectory', 'listDirectory', listDirectory, methods=['POST'])
app.add_url_rule('/manageRepository', 'manageRepository', manageRepository)
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
877 878
app.add_url_rule("/saveParameterXml", 'saveParameterXml', saveParameterXml,
                 methods=['POST'])
879 880
app.add_url_rule("/getPath", 'getPath', getPath, methods=['POST'])
app.add_url_rule("/myAccount", 'myAccount', myAccount)
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
881 882
app.add_url_rule("/updateAccount", 'updateAccount', updateAccount,
                 methods=['POST'])
883
app.add_url_rule("/updateBuildAndRunConfig", 'updateBuildAndRunConfig', updateBuildAndRunConfig,
884
                 methods=['POST'])
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
885 886
app.add_url_rule("/fileBrowser", 'fileBrowser', fileBrowser,
                 methods=['GET', 'POST'])
887
app.add_url_rule("/editFile", 'editFile', editFile, methods=['GET'])
888
app.add_url_rule('/shell', 'shell', shell)
889
app.add_url_rule('/isSRReady', 'isSRReady', isSRReady)
890
app.add_url_rule('/addUser', 'addUser', addUser, methods=['POST'])
891
app.add_url_rule('/getSlapgridParameters', 'getSlapgridParameters', getSlapgridParameters, methods=['GET'])
892
app.add_url_rule('/runCommand', 'runCommand', runCommand, methods=['POST'])
893
app.add_url_rule("/getMiniShellHistory", 'getMiniShellHistory', getMiniShellHistory, methods=['GET'])