text: import os
  import threading
  import yaml
  import json
  import hashlib
  import datetime
  import shutil
  import argparse
  import socket
  import BaseHTTPServer
  import SimpleHTTPServer
  import httplib
  import logging, logging.handlers
  
  import socket
  from threading import Thread, Event
  from time import sleep
  
  from flask import (Flask, request, redirect, url_for, jsonify,
                       render_template, send_from_directory)
  from werkzeug import (secure_filename, SharedDataMiddleware)
  from flask.ext.socketio import SocketIO, emit
  
  from common import *
  from core import *
  from dispatcher import *
  from compileLauncher import *
  from mesosutils import *
  import db
  
  
  webapp = Flask(__name__, static_url_path='')
  socketio = SocketIO(webapp)
  logger = logging.getLogger("")
  
  dispatcher = None
  driverDispatch = None
  
  dispatcherTerminate = Event()
  webserverTerminate  = Event()
  haltFlag = Event()
  
  # driver = None
  # driver_t = None
  # driverInitilize = Event()
  
  compileService = None
  driverCompiler = None
  
  lastCompile = None
  compile_tasks = {}
  
  index_message = 'Welcome to K3'
  
  
  class SocketIOHandler(logging.Handler):
      def emit(self, record):
          socketio.emit('my response', record.getMessage(), namespace='/compile')
  
  #===============================================================================
  #   General Web Service Functions
  #===============================================================================
  def initWeb(port, **kwargs):
    """
      Peforms web service initialization
    """
    # Configure logging
    # logging.Formatter(fmt='[%(asctime)s %(levelname)-5s %(name)s] %(message)s',datefmt='%H:%M:%S')
    log_fmt = ServiceFormatter('[%(asctime)s %(levelname)6s] %(message)s')
    log_console = logging.StreamHandler()
    log_console.setFormatter(log_fmt)
    logger.setLevel(logging.DEBUG)
    logger.addHandler(log_console)
  
  
    logger.debug("Setting up directory structure")
  
    # LOCAL DIR : Local path for storing all K3 Applications, job files, executor, output, etc.
    LOCAL_DIR  = kwargs.get('local', '/k3/web')
  
    # SERVER URL : URL for serving static content. Defaults to using Flask as static handler via /fs/ endpoint
    SERVER_URL = kwargs.get('server', '/fs/')
  
    host   = kwargs.get('host', socket.gethostname())
    master = kwargs.get('master', None)
  
    JOBS_TARGET    = 'jobs'
    APPS_TARGET    = 'apps'
    ARCHIVE_TARGET = 'archive'
    BUILD_TARGET   = 'build'
    LOG_TARGET     = 'log'
  
    #  TODO: Either do away with this Flask_request  (python vers limitation)
    #       or simplify this
    APPS_DEST = os.path.join(LOCAL_DIR, APPS_TARGET)
    APPS_URL = os.path.join(SERVER_URL, APPS_TARGET)
    JOBS_DEST = os.path.join(LOCAL_DIR, JOBS_TARGET)
    JOBS_URL = os.path.join(SERVER_URL, JOBS_TARGET)
    ARCHIVE_DEST = os.path.join(LOCAL_DIR, ARCHIVE_TARGET)
    ARCHIVE_URL = os.path.join(SERVER_URL, ARCHIVE_TARGET)
    BUILD_DEST = os.path.join(LOCAL_DIR, BUILD_TARGET)
    BUILD_URL = os.path.join(SERVER_URL, BUILD_TARGET)
    LOG_DEST  = os.path.join(LOCAL_DIR, LOG_TARGET)
  
    #  Store dir structures in web context
    webapp.config['DIR']      = LOCAL_DIR
    webapp.config['PORT']     = port
    webapp.config['HOST']     = host
    webapp.config['ADDR']     = 'http://%s:%d' % (host, port)
    webapp.config['MESOS']    = master
    webapp.config['UPLOADED_APPS_DEST']     = APPS_DEST
    webapp.config['UPLOADED_APPS_URL']      = APPS_URL
    webapp.config['UPLOADED_JOBS_DEST']     = JOBS_DEST
    webapp.config['UPLOADED_JOBS_URL']      = JOBS_URL
    webapp.config['UPLOADED_ARCHIVE_DEST']  = ARCHIVE_DEST
    webapp.config['UPLOADED_ARCHIVE_URL']   = ARCHIVE_URL
    webapp.config['UPLOADED_BUILD_DEST']  = BUILD_DEST
    webapp.config['UPLOADED_BUILD_URL']   = BUILD_URL
    webapp.config['LOG_DEST']             = LOG_DEST
    webapp.config['COMPILE_OFF']  = not(kwargs.get('compile', False))
  
    # Create dirs, if necessary
    for p in [LOCAL_DIR, JOBS_TARGET, APPS_TARGET, ARCHIVE_TARGET, BUILD_TARGET, LOG_TARGET]:
      path = os.path.join(LOCAL_DIR, p)
      if not os.path.exists(path):
        os.mkdir(path)
  
    #  Configure rotating log file
    logfile = os.path.join(LOG_DEST, 'web.log')
    webapp.config['LOGFILE'] = logfile
    log_file = logging.handlers.RotatingFileHandler(logfile, maxBytes=2*1024*1024, backupCount=5, mode='w')
    log_file.setFormatter(log_fmt)
    logger.addHandler(log_file)
  
    logger.info("\n\n\n\n\n====================  <<<<< K3 >>>>> ===================================")
    logger.info("FLASK WEB Initializing:\n" +
      "    Host  : %s\n" % host +
      "    Port  : %d\n" % port +
      "    Master: %s\n" % master +
      "    Local : %s\n" % LOCAL_DIR +
      "    Server: %s\n" % SERVER_URL +
      "    Port  : %d\n" % port)
  
  
    # Configure Compilation Service
    if not webapp.config['COMPILE_OFF']:
  
      compileLogger = logging.getLogger("compiler")
  
      sio_fmt = ServiceFormatter('\n' + u'[%(asctime)s] %(message)s\n'.encode("utf8", errors='ignore'))
      sio = SocketIOHandler()
      sio.setFormatter(sio_fmt)
      compileLogger.addHandler(sio)
  
      compileFile = os.path.join(LOG_DEST, 'compiler.log')
      webapp.config['COMPILELOG'] = compileFile
  
      compile_fmt = ServiceFormatter(u'[%(asctime)s] %(message)s'.encode("utf8", errors='ignore'))
      compile_file = logging.handlers.RotatingFileHandler(compileFile, maxBytes=5*1024*1024, backupCount=20, mode='w')
      compile_file.setFormatter(compile_fmt)
      compileLogger.addHandler(compile_file)
  
      compileLogger.info("\n\n\n\n\n====================  <<<<< Compiler Service Initiated >>>>> ===================================")
  
      # Check for executor(s), copy if necessary
      compiler_nm = 'CompileExecutor.py'
      compiler_exec = os.path.join(LOCAL_DIR, compiler_nm)
      if not os.path.exists(compiler_exec):
        logger.info("Compiler executor not found. Copying to web root dir.")
        shutil.copyfile(compiler_nm, compiler_exec)
  
      launcher_nm = 'k3executor.py'
      launcher_exec = os.path.join(LOCAL_DIR, launcher_nm)
      if not os.path.exists(launcher_exec):
        logger.info("K3 Executor not found. Copying to web root dir.")
        shutil.copyfile(launcher_nm, launcher_exec)
  
  def returnError(msg, errcode):
    """
      returnError -- Helper function to format & return error messages & codes
    """
    logger.warning("[FLASKWEB] Returning error code %d, `%s`" % (errcode, msg))
    if request.headers['Accept'] == 'application/json':
      return msg, errcode
    else:
      return render_template("error.html", message=msg, code=errcode)
  
  
  def shutdown_server():
      global dispatcher
      logging.warning ("[FLASKWEB] Attempting to kill the server")
      func = request.environ.get('werkzeug.server.shutdown')
      if func is None:
          raise RuntimeError("Not running the server")
      func()
      webserverTerminate.set()
      dispatcher.terminate = True
  
  def shutdown_dispatcher():
      # driver.stop()
      # driver_t.join()
      for k, v in compile_tasks.items():
        v.kill()
      logging.info ("[FLASKWEB] Detached from Mesos")
      dispatcherTerminate.set()
  
  
  
  #===============================================================================
  #   General Web Service End Points
  #===============================================================================
  @webapp.route('/')
  def root():
    """
    #------------------------------------------------------------------------------
    #  / - Home (welcome msg)
    #------------------------------------------------------------------------------
    """
    if request.headers['Accept'] == 'application/json':
      return "Welcome\n\n", 200
    else:
      return redirect(url_for('index'))
  
  
  @webapp.route('/index')
  def index():
    if request.headers['Accept'] == 'application/json':
      return "Welcome\n\n", 200
    else:
      return render_template('index.html')
  
  
  @webapp.route('/about')
  def about():
    """
    #------------------------------------------------------------------------------
    #  /about - Display about page
    #------------------------------------------------------------------------------
    """
    logger.debug('[FLASKWEB  /about]')
    if request.headers['Accept'] == 'application/json':
      return redirect(url_for('staticFile', filename="rest.txt"))
    else:
      return render_template('about.html')
  
  
  @webapp.route('/restapi')
  def restapi():
    """
    #------------------------------------------------------------------------------
    #  /restapi - Display complete list of API EndPoints
    #------------------------------------------------------------------------------
    """
    logger.debug('[FLASKWEB  /restapi] API Reference request')
    if request.headers['Accept'] == 'application/json':
      return redirect(url_for('staticFile', filename="rest.txt"))
    else:
      with open('static/rest.txt') as restapi:
        rest = restapi.read()
        return render_template('rest.html', api=rest)
  
  
  @webapp.route('/log')
  def getLog():
    """
    #------------------------------------------------------------------------------
    #  /log - Displays current log file
    #------------------------------------------------------------------------------
    """
    with open(webapp.config['LOGFILE'], 'r') as logfile:
      output = logfile.read()
    if request.headers['Accept'] == 'application/json':
      return output, 200
    else:
      return render_template("output.html", output=output)
  
  
  @webapp.route('/yamlinstructions')
  def getYamlInstructions():
    """
    #------------------------------------------------------------------------------
    #  /yamlinstructions - Displays YAML Instructions
    #------------------------------------------------------------------------------
    """
    with open('role_file_template.yaml', 'r') as yamlfile:
      output = yamlfile.read()
    if request.headers['Accept'] == 'application/json':
      return output, 200
    else:
      return render_template("output.html", output=output)
  
  
  @webapp.route('/trace')
  def trace():
    """
    #------------------------------------------------------------------------------
    #  /trace - Debugging response. Returns the client's HTTP request data in json
    #------------------------------------------------------------------------------
    """
    logger.debug('[FLASKWEB  /trace] Trace debug request')
    output = {}
    output['args'] = request.args
    output['form'] = request.form
    output['method'] = request.method
    output['url'] = request.url
    output['client_ip']  = request.remote_addr
    output['headers'] = {k: str(v) for k,v in request.headers.items()}
    return jsonify(output), 200
  
  
  @webapp.route('/restart')
  def restart():
      """
      #------------------------------------------------------------------------------
      #  /restart - Restart the K3 Dispatch Service (kills/cleans all running tasks)
      #------------------------------------------------------------------------------
      """
      logging.warning ("[FLASKWEB] Shutting down K3 Dispatcher....")
      shutdown_dispatcher()
      return 'Dispatcher is restarting.....Give me a millisec'
  
  
  
  @webapp.route('/kill')
  def shutdown():
      """
      #------------------------------------------------------------------------------
      #  /kill - Kill the server  (TODO: Clean this up)
      #------------------------------------------------------------------------------
      """
      logging.warning ("[FLASKWEB] Shutting down Flask Web Server....")
      shutdown_server()
      logging.warning ("[FLASKWEB] Shutting down K3 Dispatcher....")
      shutdown_dispatcher()
  
      haltFlag.set()
      return 'Server is going down...'
  
  
  
  #===============================================================================
  #   Static Content Service
  #===============================================================================
  @webapp.route('/fs/<path:path>/')
  def staticFile(path):
    """
    #------------------------------------------------------------------------------
    #  /fs - File System Exposure for the local webroot folder
    #        Note: Direct file access via curl should include a trailing slash (/)
    #           Otherwise, you will get a 302 redirect to the actual file
    #------------------------------------------------------------------------------
    """
    logger.info('[FLASKWEB  /fs] Static File Request for `%s`' % path)
    local = os.path.join(webapp.config['DIR'], path)
    if not os.path.exists(local):
      return returnError("File not found: %s" % path, 404)
    if os.path.isdir(local):
      contents = sorted(os.listdir(local))
      for i, f in enumerate(contents):
        if os.path.isdir(f):
          contents[i] += '/'
  
      if request.headers['Accept'] == 'application/json':
        return jsonify(dict(cwd=local, contents=contents)), 200
      else:
        return render_template('listing.html', cwd=path, listing=contents), 200
  
    else:
      if 'stdout' in local or 'output' in local or local.split('.')[-1] in ['txt', 'yaml', 'yml', 'json', 'log']:
        with open(local, 'r') as file:
          # output = unicode(file.read(), 'utf-8')
          output = file.read()
  
          if request.headers['Accept'] == 'application/json':
            return output, 200
          else:
            return render_template("output.html", output=output)
  
      return send_from_directory(webapp.config['DIR'], path)
  
  
  #===============================================================================
  #   Application End Points
  #===============================================================================
  @webapp.route('/app', methods=['GET', 'POST'])
  def uploadAppRedir():
    """
      Redirect to uploadApp (/apps)
    """
    logger.debug('[FLASKWEB  /app] Redirect to /apps')
    return uploadApp()
  
  @webapp.route('/apps', methods=['GET', 'POST'])
  def uploadApp():
    """
    #------------------------------------------------------------------------------
    #  /apps, /app - Application Level interface
    #     POST   Upload new application
    #       curl -i -X POST -H "Accept: application/json"
    #           -F file=@<filename> http://<host>:<port>/apps
    #
    #     GET    Display both list of loaded apps and form to upload new ones
    #
    #     /app will redirect to /apps
    #------------------------------------------------------------------------------
    """
    if request.method == 'POST':
        logger.debug("[FLASKWEB  /apps] POST request to upload new application")
        file = request.files['file']
        if file:
            name = secure_filename(file.filename)
            path = os.path.join(webapp.config['UPLOADED_APPS_DEST'], name)
            if not os.path.exists(path):
              os.mkdir(path)
            uid = getUID() if 'uid' not in request.form or not request.form['uid'] else request.form['uid']
            path =  os.path.join(path, uid)
            if not os.path.exists(path):
              os.mkdir(path)
            fullpath = os.path.join(path, name)
            file.save(fullpath)
  
            jobdir = os.path.join(webapp.config['UPLOADED_JOBS_DEST'], name)
            if not os.path.exists(jobdir):
              os.mkdir(jobdir)
            # hash = hashlib.md5(open(fullpath).read()).hexdigest()
  
            hash = request.form['tag'] if 'tag' in request.form else ''
  
            # if (db.checkHash(hash)):
            db.insertApp(dict(uid=uid, name=name, hash=hash))
            logger.info("[FLASKWEB] Added new application: `%s`, uid=`%s`", name, uid)
            #TODO:  Complete with paths for archive & app
  
            if request.headers['Accept'] == 'application/json':
                output = dict(name=name, uid=uid, status='SUCCESS', greeting='Thank You!')
                return jsonify(output), 200
            else:
                return redirect(url_for('uploadApp'))
  
    logger.debug('[FLASKWEB  /apps] GET request for list of apps')
    applist = db.getAllApps()
    versions = {a['name']: db.getVersions(a['name'], limit=5) for a in applist}
  
    # TODO: Add 2nd link on AppList: 1 to launch latest, 1 to show all versions
    if request.headers['Accept'] == 'application/json':
        return jsonify(dict(apps=applist)), 200
    else:
        return render_template('apps.html', applist=applist, versions=versions)
  
  
  @webapp.route('/app/<appName>')
  def getAppRedir(appName):
    """
      Redirect to uploadApp (/apps/<appName>)
    """
    logger.debug('[FLASKWEB  /app/<appName>] Redirect to /apps/%s' % appName)
    return getApp(appName)
  
  @webapp.route('/apps/<appName>')
  def getApp(appName):
    """
    #------------------------------------------------------------------------------
    #  /apps/<appName> - Specific Application Level interface
    #         GET    Display all versions for given application
    #------------------------------------------------------------------------------
    """
    logger.debug('[FLASKWEB  /apps/<appName>] GET request for app, `%s`' % appName)
    applist = [a['name'] for a in db.getAllApps()]
    if appName in applist:
      versionList = db.getVersions(appName)
      if request.headers['Accept'] == 'application/json':
        return jsonify(dict(name=appName, versions=versionList)), 200
      else:
        return render_template("apps.html", name=appName, versionList=versionList)
    else:
      return returnError("Application %s does not exist" % appName, 404)
  
  
  @webapp.route('/app/<appName>/<appUID>', methods=['GET', 'POST'])
  def archiveAppRedir(appName, appUID):
    """
      Redirect to archiveApp
    """
    logger.debug('[FLASKWEB  /app/<appName>/<appUID>] Redirec to /apps/%s/%s'
        % (appName, appUID))
    return archiveApp(appName, appUID)
  
  
  @webapp.route('/apps/<appName>/<appUID>', methods=['GET', 'POST'])
  def archiveApp(appName, appUID):
    """
    #------------------------------------------------------------------------------
    #  /apps/<appName>/<appUID> - Specific Application Level interface
    #         POST   (Upload archive data (C++, Source, etc....)
    #            curl -i -X POST -H "Accept: application/json"
    #                    -F "file=<filename>" http://qp1:5000/apps/<addName>/<addUID>
    #
    #         GET    (TODO) Display archived files...  NotImplemented
    #------------------------------------------------------------------------------
    """
    logger.debug('[FLASKWEB  /app/<appName>/<appUID>] %s Request for App Archive `%s`, UID=`%s`' % (request.method, appName, appUID))
    applist = [a['name'] for a in db.getAllApps()]
    uname = AppID.getAppId(appName, appUID)
  
    # if appName not in applist:
    #   logger.warning("Archive request for app that does not exist: %s", appName)
    #   return returnError("Application %s does not exist" % appName, 404)
  
    if request.method == 'POST':
        file = request.files['file']
        if file:
            filename = secure_filename(file.filename)
            path = os.path.join(webapp.config['UPLOADED_BUILD_DEST'], uname).encode(encoding='utf8', errors='ignore')
            logger.debug("Archiving file, %s, to %s" % (filename, path))
            if not os.path.exists(path):
              os.mkdir(path)
            file.save(os.path.join(path, filename))
            return "File Uploaded & archived\n", 202
        else:
            logger.warning("Archive request, but no file provided.")
            return "No file received\n", 400
  
    elif request.method == 'GET':
      path = os.path.join(webapp.config['UPLOADED_BUILD_URL'], uname)
      return redirect(path, 302)
  
  
  @webapp.route('/delete/app/<appName>', methods=['POST'])
  def deleteApp(appName):
    """
    #------------------------------------------------------------------------------
    #  /delete/app/<appName>
    #     POST     Deletes an app from the web server
    #         NOTE: Data files will remain in webroot on the server, but
    #           the app will be inaccessible through the interface
    #           (metadata is removed from the internal db)
    #------------------------------------------------------------------------------
    """
    logger.debug('[FLASKWEB  /delete/app/<appName>] Request to delete App `%s`', appName)
    applist = [a['name'] for a in db.getAllApps()]
    if appName not in applist:
      return returnError("Application %s does not exist" % appName, 404)
  
    logger.info("[FLASKWEB]  DELETING all versions of app, `%s`")
    db.deleteAllApps(appName)
  
    if request.headers['Accept'] == 'application/json':
      return jsonify(dict(app=appName, status='DELETED, files remain on server')), 200
    else:
      applist = db.getAllApps()
      versions = {a['name']: db.getVersions(a['name'], limit=5) for a in applist}
      return render_template('apps.html', applist=applist, versions=versions)
  
  
  #===============================================================================
  #   Job End Points
  #===============================================================================
  @webapp.route('/job')
  def listJobsRedir():
    """
      Redirect to listJobs
    """
    logger.debug('[FLASKWEB  /job] Redirecting to /jobs')
    return listJobs()
  
  @webapp.route('/jobs')
  def listJobs():
    """
    #------------------------------------------------------------------------------
    #  /jobs - Current runtime & completed job Interface
    #         GET    Display currently executing & recently completed jobs
    #------------------------------------------------------------------------------
    """
    logger.debug('[FLASKWEB  /jobs] Request for job listing')
    jobs = db.getJobs(numdays=2)
    for job in jobs:
      job['time'] = datetime.datetime.strptime(job['time'], db.TS_FMT).replace(tzinfo=db.timezone('UTC')).isoformat()
      if job['complete']:
        job['complete'] = datetime.datetime.strptime(job['complete'], db.TS_FMT).replace(tzinfo=db.timezone('UTC')).isoformat()
  
    #  Garbage Collect Orpahened jobs
    compiles = db.getCompiles()
    for compile in compiles:
      if compile['submit']:
        compile['submit'] = datetime.datetime.strptime(compile['submit'], db.TS_FMT).replace(tzinfo=db.timezone('UTC')).isoformat()
      if compile['complete']:
        compile['complete'] = datetime.datetime.strptime(compile['complete'], db.TS_FMT).replace(tzinfo=db.timezone('UTC')).isoformat()
    # for c in compiles:
    #   if c['uid'] not in compile_tasks.keys():
    #     db.updateCompile(c['uid'], status='KILLED', done=True)
    # compiles = db.getCompiles()
  
    if request.headers['Accept'] == 'application/json':
      return jsonify(dict(LaunchJobs=jobs, CompilingJobs=compiles)), 200
    else:
      return render_template("jobs.html", joblist=jobs, compilelist=compiles)
  
  
  @webapp.route('/jobs/<appName>', methods=['GET', 'POST'])
  def createJobLatest(appName):
    """
      Redirect createJob using the latest uploaded application version
    """
    logger.debug('[FLASKWEB  /jobs/<appName>] Redirect to current version of /jobs/%s' % appName)
    app = db.getApp(appName)
    if app:
      return createJob(appName, app['uid'])
    else:
      return returnError("Application %s does not exist" % appName, 404)
  
  @webapp.route('/jobs/<appName>/<appUID>', methods=['GET', 'POST'])
  def createJob(appName, appUID):
    """
    #------------------------------------------------------------------------------
    #  /jobs/<appName>
    #  /jobs/<appName>/<appUID  - Launch a new K3 Job
    #         POST    Create new K3 Job
    #          curl -i -X POST -H "Accept: application/json"
    #             -F "file=@<rolefile>"
    #             -F logging=[True | False]
    #             -F jsonlog=[True | False]
    #             -F jsonfinal=[True | False]
    #             -F perf_profile=[True | False]
    #             -F perf_frequency=[n]
    #             -F cmd_prefix='str'
    #             -F cmd_infix='str'
    #             -F cmd_suffix='str'
    #             -F http://<host>:<port>/jobs/<appName>/<appUID>
    #             NOTE: if appUID is omitted, job will be submitted to latest version of this app
    #
    #         GET    Display job list for this application
    #------------------------------------------------------------------------------
    """
    logger.debug('[FLASKWEB  /jobs/<appName>/<appUID>] Job Request for %s' % appName)
    global dispatcher
    applist = [a['name'] for a in db.getAllApps()]
    if appName in applist:
      if request.method == 'POST':
          logger.debug("POST Request for a new job")
          # TODO: Get user
          file = request.files['file']
          text = request.form['text'] if 'text' in request.form else None
          k3logging      = True if 'logging' in request.form else False
          jsonlog        = True if 'jsonlog' in request.form else False
          jsonfinal      = True if 'jsonfinal' in request.form else False
          perf_profile   = True if 'perf_profile' in request.form else False
          perf_frequency = request.form['perf_frequency'] if 'perf_frequency' in request.form else ''
          cmd_prefix     = request.form['cmd_prefix'] if 'cmd_prefix' in request.form else None
          cmd_infix      = request.form['cmd_infix'] if 'cmd_infix' in request.form else None
          cmd_suffix     = request.form['cmd_suffix'] if 'cmd_suffix' in request.form else None
          core_dump      = True if 'core_dump' in request.form else False
          stdout = request.form['stdout'] if 'stdout' in request.form else False
          user = request.form['user'] if 'user' in request.form else 'anonymous'
          tag = request.form['tag'] if 'tag' in request.form else ''
  
          # User handling: jsonfinal is a qualifier flag for the json logging flag
          if jsonfinal and not jsonlog:
            jsonlog = True
  
          logger.debug("K3 LOGGING is :  %s"         %  ("ON" if k3logging else "OFF"))
          logger.debug("JSON LOGGING is :  %s"       %  ("ON" if jsonlog else "OFF"))
          logger.debug("JSON FINAL LOGGING is :  %s" %  ("ON" if jsonfinal else "OFF"))
          logger.debug("PERF PROFILING is :  %s"     %  ("ON" if perf_profile else "OFF"))
          logger.debug("CORE DUMPS are :  %s"        %  ("ON" if core_dump else "OFF"))
          # trials = int(request.form['trials']) if 'trials' in request.form else 1
  
          # Check for valid submission
          if not file and not text:
            logger.error('Error. Cannot create job: No input file and no YAML for enviornment configuration provided. ')
            return render_template("error.html", code=404, message="Invalid job request")
  
          # Post new job request, get job ID & submit time
          thisjob = dict(appName=appName, appUID=appUID, user=user, tag=tag)
          jobId, time = db.insertJob(thisjob)
          thisjob = dict(jobId=jobId, time=time)
  
          # Save yaml to file (either from file or text input)
          path = os.path.join(webapp.config['UPLOADED_JOBS_DEST'], appName)
          if not os.path.exists(path):
            os.mkdir(path)
  
          path = os.path.join(path, str(jobId))
          filename = 'role.yaml'
          if not os.path.exists(path):
            os.mkdir(path)
  
          if file:
              file.save(os.path.join(path, filename))
          else:
              with open(os.path.join(path, filename), 'w') as file:
                file.write(text)
  
          # Create new Mesos K3 Job
          apploc = webapp.config['ADDR']+os.path.join(webapp.config['UPLOADED_APPS_URL'], appName, appUID, appName)
  
          newJob = Job(binary=apploc, appName=appName, jobId=jobId,
                       rolefile=os.path.join(path, filename), logging=k3logging,
                       jsonlog=jsonlog, jsonfinal=jsonfinal,
                       perf_profile=perf_profile, perf_frequency=perf_frequency, core_dump=core_dump,
                       cmd_prefix=cmd_prefix, cmd_infix=cmd_infix, cmd_suffix=cmd_suffix)
  
          # Submit to Mesos
          dispatcher.submit(newJob)
  
          thisjob = dict(thisjob, url=dispatcher.getSandboxURL(jobId), status='SUBMITTED')
  
          if 'application/json' in request.headers['Accept']:
            return jsonify(thisjob), 202
          else:
            return render_template('last.html', appName=appName, lastjob=thisjob)
  
      elif request.method == 'GET':
        jobs = db.getJobs(appName=appName)
        if 'application/json' in request.headers['Accept']:
          return jsonify(dict(jobs=jobs))
        else:
          preload = request.args['preload'] if 'preload' in request.args else 'Sample'
          logger.debug("YAML file preload = %s" % preload)
          if preload == 'Instructions':
              yamlFile = 'role_file_template.yaml'
          elif preload == 'Last':
              lastJobId = max([ d['jobId'] for d in jobs])
              path = os.path.join(webapp.config['UPLOADED_JOBS_DEST'], appName, "%d" % lastJobId, 'role.yaml')
              logger.debug("   YAML PATH= %s" % path)
              if os.path.exists(path):
                yamlFile = path
              else:
                yamlFile = None
          else:
            yamlFile = 'sample.yaml'
          if yamlFile:
            with open(yamlFile, 'r') as f:
              sample = f.read()
          else:
            sample = "(No YAML file to display)"
          return render_template("newjob.html", name=appName, uid=appUID, sample=sample)
  
    else:
      return returnError("There is no application, %s" % appName, 404)
  
  
  @webapp.route('/job/<jobId>')
  def getJobRedir(jobId):
      """
        Redirect to getJob
      """
      jobs = db.getJobs(jobId=jobId)
      job = None if len(jobs) == 0 else jobs[0]
      if job == None:
        return returnError("Job ID, %s, does not exist" % jobId, 404)
      appName = job['appName']
      return getJob(appName, jobId)
  
  @webapp.route('/jobs/<appName>/<jobId>/status')
  def getJob(appName, jobId):
      """
      #------------------------------------------------------------------------------
      #  /jobs/<appName>/<jobId>/status - Detailed Job info
      #         GET     Display detailed job info  (default for all methods)
      #------------------------------------------------------------------------------
      """
      jobs = db.getJobs(jobId=jobId)
      job = None if len(jobs) == 0 else jobs[0]
      k3job = dispatcher.getJob(int(jobId))
  
      if job == None:
        return returnError("Job ID, %s, does not exist" % jobId, 404)
  
      thisjob = dict(job, url=dispatcher.getSandboxURL(jobId))
      if k3job != None:
        thisjob['master'] = k3job.master
      local = os.path.join(webapp.config['UPLOADED_JOBS_DEST'], appName, str(jobId)).encode(encoding='utf8', errors='ignore')
      path = os.path.join(webapp.config['UPLOADED_JOBS_DEST'], appName, str(jobId),'role.yaml').encode(encoding='utf8', errors='ignore')
      if os.path.exists(local) and os.path.exists(path):
        with open(path, 'r') as role:
          thisjob['roles'] = role.read()
      else:
        return returnError("Job Data no longer exists", 400)
  
      thisjob['sandbox'] = sorted (os.listdir(local))
  
      if 'application/json' in request.headers['Accept']:
        return jsonify(thisjob)
      else:
        return render_template("last.html", appName=appName, lastjob=thisjob)
  
  
  @webapp.route('/job/<jobId>/replay')
  def replayJobRedir(jobId):
      """
        Redirect to replayJob
      """
      jobs = db.getJobs(jobId=jobId)
      job = None if len(jobs) == 0 else jobs[0]
      if job == None:
        return returnError("Job ID, %s, does not exist" % jobId, 404)
      appName = job['appName']
      logging.info ("[FLASKWEB] REPLAYING JOB # %s" % jobId)
      return replayJob(appName, jobId)
  
  @webapp.route('/jobs/<appName>/<jobId>/replay', methods=['GET', 'POST'])
  def replayJob(appName, jobId):
    """
    #------------------------------------------------------------------------------
    #  /jobs/<appName>/<appUID/replay  - Replay a previous K3 Job
    #         POST    Create new K3 Job
    #          curl -i -X POST -H "Accept: application/json" http://<host>:<port>/jobs/<appName>/<appUID>/replay
    #------------------------------------------------------------------------------
    """
    global dispatcher
    joblist = db.getJobs(jobId=jobId)
    oldjob = None if len(joblist) == 0 else joblist[0]
    if oldjob:
        logger.info("[FLASKWEB] REPLAYING %s" % jobId),
        # Post new job request, get job ID & submit time
        thisjob = dict(appName=oldjob['appName'],
                       appUID=oldjob['hash'],
                       user=oldjob['user'],
                       tag='REPLAY: %s' % oldjob['tag'])
  
        new_jobId, time = db.insertJob(thisjob)
        thisjob = dict(jobId=new_jobId, time=time)
        logging.info ("[FLASKWEB] new Replay JOBID: %s" % new_jobId),
  
  
        # Save yaml to file (either from file or text input)
        role_src = os.path.join(webapp.config['UPLOADED_JOBS_DEST'], appName, str(jobId), 'role.yaml').encode('utf8', 'ignore')
        path = os.path.join(webapp.config['UPLOADED_JOBS_DEST'], appName, str(new_jobId)).encode('utf8', 'ignore')
        os.mkdir(path)
        role_copy = os.path.join(path, 'role.yaml')
        shutil.copyfile(role_src, os.path.join(path, role_copy))
  
        # Create new Mesos K3 Job
        try:
          newJob = Job(binary=webapp.config['ADDR']+os.path.join(webapp.config['UPLOADED_APPS_URL'], appName, oldjob['hash'], appName),
                       appName=appName, jobId=new_jobId, rolefile=role_copy)
        except K3JobError as err:
          db.deleteJob(jobId)
          logger.error("JOB ERROR: %s" % err)
          return returnError(err.value, 400)
  
        logging.info ("[FLASKWEB] NEW JOB ID: %s" % newJob.jobId)
  
        # Submit to Mesos
        dispatcher.submit(newJob)
        thisjob = dict(thisjob, url=dispatcher.getSandboxURL(new_jobId), status='SUBMITTED')
  
        if 'application/json' in request.headers['Accept']:
            return jsonify(thisjob), 202
        else:
            return render_template('last.html', appName=appName, lastjob=thisjob)
  
    else:
      return returnError("There is no Job, %s\n" % jobId, 404)
  
  
  @webapp.route('/job/<jobId>/archive', methods=['GET', 'POST'])
  def archiveJobRedir(jobId):
      """
        Redirect to archiveJob
      """
      jobs = db.getJobs(jobId=jobId)
      job = None if len(jobs) == 0 else jobs[0]
      appName = job['appName']
      if job == None:
        return returnError("Job ID, %s, does not exist" % jobId, 404)
      return archiveJob(appName, jobId)
  
  
  @webapp.route('/jobs/<appName>/<jobId>/archive', methods=['GET', 'POST'])
  def archiveJob(appName, jobId):
      """"
      #------------------------------------------------------------------------------
      #  /jobs/<appName>/<jobId>/archive - Endpoint to receive & archive files
      #         GET     returns curl command
      #         POST    Accept files for archiving here
      #           curl -i -X POST -H "Accept: application/json"
      #              -F file=@<filename> http://<host>:<post>/<appName>/<jobId>/archive
      #------------------------------------------------------------------------------
      """
      job_id = str(jobId).encode('utf8', 'ignore')
      if job_id.find('.') > 0:
        job_id = job_id.split('.')[0]
      jobs = db.getJobs(jobId=job_id)
      job = None if len(jobs) == 0 else jobs[0]
      if job == None:
        return returnError ("Job ID, %s, does not exist" % job_id, 404)
  
      if request.method == 'POST':
          file = request.files['file']
          if file:
              filename = secure_filename(file.filename)
              path = os.path.join(webapp.config['UPLOADED_JOBS_DEST'], appName, job_id, filename).encode(encoding='utf8', errors='ignore')
              file.save(path)
              return "File Uploaded & archived", 202
          else:
              return "No file received", 400
  
      elif request.method == 'GET':
          return '''
  Upload your file using the following CURL command:\n\n
     curl -i -X POST -H "Accept: application/json" -F file=@<filename> http://<server>:<port>/<appName>/<jobId>/archive
  ''', 200
  
  
  @webapp.route('/job/<jobId>/kill', methods=['GET'])
  def killRedir(jobId):
      """
        Redirect to killJob
      """
      jobs = db.getJobs(jobId=jobId)
      job = None if len(jobs) == 0 else jobs[0]
      appName = job['appName']
      if job == None:
        return returnError("Job ID, %s, does not exist" % jobId, 404)
      return killJob(appName, jobId)
  
  @webapp.route('/jobs/<appName>/<jobId>/kill', methods=['GET'])
  def killJob(appName, jobId):
      """
      #------------------------------------------------------------------------------
      #  /jobs/<appName>/<jobId>/kill - Job Interface to cancel a job
      #         GET     Kills a Job (if orphaned, updates status to killed)
      #           curl -i -H "Accept: application/json" http://qp1:5000/jobs/<appName>/<jobId>/kill
      #------------------------------------------------------------------------------
      """
      jobs = db.getJobs(jobId=jobId)
      job = None if len(jobs) == 0 else jobs[0]
  
      if job == None:
        return returnError ("Job ID, %s, does not exist" % jobId, 404)
  
      logging.info ("[FLASKWEB] Asked to KILL job #%s. Current Job status is %s" % (jobId, job['status']))
      # Separate check to kill orphaned jobs in Db
      # TODO: Merge Job with experiments to post updates to correct table
      if job['status'] == 'RUNNING' or job['status'] == 'SUBMITTED':
        db.updateJob(jobId, status='KILLED')
  
      if int(jobId) in dispatcher.getActiveJobs():
        status = 'KILLED'
        logging.debug('[FLASKWEB] Job %s is active. Signaling to kill in mesos.' % jobId)
        dispatcher.cancelJob(int(jobId), driverDispatch)
      else:
        status = 'ORPHANED and CLEANED'
        logging.debug('[FLASKWEB] Job # %s is ORPHANED and does not exist in current state. Cleaning up.' % jobId)
  
      ts = db.getTS_est()  #datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
      thisjob = dict(jobId=jobId, time=ts, url=dispatcher.getSandboxURL(jobId), status=status)
      if 'application/json' in request.headers['Accept']:
        return jsonify(thisjob)
      else:
        return render_template("last.html", appName=appName, lastjob=thisjob)
  
  @webapp.route('/delete/jobs', methods=['POST'])
  def deleteJobs():
    """
    #------------------------------------------------------------------------------
    #  /delete/jobs
    #         POST     Deletes list of K3 jobs
    #------------------------------------------------------------------------------
    """
    deleteList = request.form.getlist("delete_job")
    for jobId in deleteList:
      job = db.getJobs(jobId=jobId)[0]
      path = os.path.join(webapp.config['UPLOADED_JOBS_DEST'], job['appName'], jobId)
      shutil.rmtree(path, ignore_errors=True)
      db.deleteJob(jobId)
    return redirect(url_for('listJobs')), 302
  
  #------------------------------------------------------------------------------
  #  /jobs/<appName>/<jobId>/stdout - Job Interface for specific job
  #         GET     TODO: Consolidate STDOUT for current job (from all tasks)
  #         POST    TODO: Accept STDOUT & append here (if desired....)
  #------------------------------------------------------------------------------
  # @webapp.route('/jobs/<appName>/<jobId>/stdout', methods=['GET'])
  # def stdout(appName, jobId):
  #     jobs = db.getJobs(appName=appName)
  #     link = resolve(MASTER)
  #     print link
  #     sandbox = dispatcher.getSandboxURL(jobId)
  #     if sandbox:if
  #       print sandbox
  #       return '<a href="%s">%s</a>' % (sandbox, sandbox)
  #     else:
  #       return 'test'
  
  
  
  #===============================================================================
  #   Compile End Points
  #===============================================================================
  @webapp.route('/compileservice')
  def compService():
    """
    #------------------------------------------------------------------------------
    #  /compileservice
    #         GET       Return compile service status
    #------------------------------------------------------------------------------
    """
    return jsonify(compileService.getItems())
  
  
  @webapp.route('/compileservice/check')
  def compServiceCheck():
    """
    #------------------------------------------------------------------------------
    #  /compileservice/check
    #         GET       Quick check for status
    #------------------------------------------------------------------------------
    """
    # global compileService
    return compileService.state.name
  
  @webapp.route('/compileservice/up', methods=['GET', 'POST'])
  def compServiceUp():
    """
    #------------------------------------------------------------------------------
    #  /compileservice/up
    #        POST     Starts compile service
    #           curl -i -X POST -H "Accept: application/json"
    #                   -F numworkers=<numworkers>
    #                   -F gitpull=[True|False]
    #                   -F branch=<k3_branch>
    #                   -F cabalbuild=[True|False]
    #                   -F m_workerthread=<#master_service_threads>
    #                   -F w_workerthread=<#worker_service_threads>
    #                   -F heartbeat=<heartbeat_interval_in_secs>
    #                   http://<host>:<port>/compile
    #
    #    Default vals:  numworkers=(max workers), gitpull=True,
    #                   branch=development, cabalbuild=False,
    #                   m_workerthread=1, w_workerthread=1,
    #                   heartbeat=300, cppthread=12
  
    #
    #        GET     Redirect to Compile Form (html) or compile service setting (json)
    #------------------------------------------------------------------------------
    """
    global compileService
    if request.method == 'POST' and compileService.isDown():
      settings = dict(webaddr=webapp.config['ADDR'])
  
      settings['branch'] = request.form.get('branch', 'development')
      gitpull = request.form.get('gitpull', True)
      settings['gitpull'] = gitpull if isinstance(gitpull, bool) else (gitpull.upper() == 'TRUE')
      cabalbuild = request.form.get('cabalbuild', False)
      settings['cabalbuild'] = cabalbuild if isinstance(cabalbuild, bool) else (cabalbuild.upper() == 'TRUE')
  
      if 'm_workerthread' in request.form:
        settings['m_workerthread'] = request.form['m_workerthread']
  
      if 'w_workerthread' in request.form:
        settings['w_workerthread'] = request.form['w_workerthread']
  
      if 'heartbeat' in request.form:
        settings['heartbeat'] = request.form['heartbeat']
  
      # if 'cppthread' in request.form:
      #   settings['cppthread'] = request.form['cppthread']
  
  
      logger.debug ("[FLASKWEB] User GIT PULL Request    (?): %s" % str(settings['gitpull']))
      logger.debug ("[FLASKWEB] User CABAL BUILD Request (?): %s" % str(settings['cabalbuild']))
  
      # TODO: Set Worker Nodes here (dynamic allocation of workers)
  
  
  
      # if 'numworkers' not in request.form or request.form['numworkers'] == '':
      #   settings['numworkers'] = len(workerNodes)
      # else:
      #   settings['numworkers'] = int(request.form['numworkers'])
  
      nodelist = {n : [] for n in ['master', 'worker', 'client', 'none']}
      for host in compileService.getAllNodes()['all']:
      #   if host in request.form:
      #     logger.debug("    -->Recevied host val as: " + request.form[host])
      #     nodelist[request.form[host].lower()].append(host)
      #     logger.debug(' %s added as %s ' % (host, request.form[host].lower()))
      # compileService.setNodes(nodelist)
       
        test = request.form.getlist("role_" + host)
        logger.debug("Compiler Host Requested:  %s", str(test))
        for role in request.form.getlist("role_" + host):
          logger.debug(" Compiler: adding `%s` as `%s`", host, role)
          nodelist[role.lower()].append(host)
      compileService.setNodes(nodelist)
  
  
      compileService.update(settings)
      compileService.goUp()
  
  
  
    if request.headers['Accept'] == 'application/json':
      return jsonify(compileService.getItems()), 200
    else:
      return render_template("compile.html", status=compileService.state.name, hostlist=compileService.getAllNodes())
  
  
  
  @webapp.route('/compileservice/stop')
  def compServiceStop():
    """
    #------------------------------------------------------------------------------
    #  /compileservice/stop
    #         GET     Stop compile service Immediately
    #
    #       NOTE:  Be careful, it kills all jobs (There is no confirmation check)
    #------------------------------------------------------------------------------
    """
    global compileService
    compileService.goDown()
  
    if request.headers['Accept'] == 'application/json':
      return jsonify(compileService.getItems()), 200
    else:
      return render_template("compile.html", status=compileService.state.name, hostlist=compileService.getAllNodes())
  
  
  @webapp.route('/compileservice/down')
  def compServiceDown():
    """
    #------------------------------------------------------------------------------
    #  /compileservice/down
    #         GET     Shuts down compile service gracefully
    #
    #       NOTE:  Clears all pendings tasks
    #------------------------------------------------------------------------------
    """
    global compileService
    compileService.goDownGracefully()
  
    if request.headers['Accept'] == 'application/json':
      return jsonify(compileService.getItems()), 200
    else:
      return render_template("compile.html", status=compileService.state.name, hostlist=compileService.getAllNodes())
  
  
  
  #===============================================================================
  #   Compile End Points
  #===============================================================================
  @webapp.route('/compile', methods=['GET', 'POST'])
  def compile():
      global lastCompile
      """
      #------------------------------------------------------------------------------
      #  /compile
      #        POST    Submit new K3 Compile task
      #           curl -i -X POST -H "Accept: application/json"
      #                   -F name=<appName>
      #                   -F file=@<sourceFile>
      #                   -F blocksize=<blocksize>
      #                   -F compilestage=['both'|'cpp'|'bin']
      #                   -F compileargs=<compile_args>
      #                   -F mem=<mem_in_GB>
      #                   -F cpu=<#cores>
      #                   -F workload=['balanced'|'moderate'|'moderate2'|'extreme']
      #                   -F user=<userName> http://<host>:<port>/compile
      #
      #    NOTE:  -user & compileargs are optional.
      #           -If name is omitted, it is inferred from filename
      #    Default vals:  blocksize=8, compilestage='both', workload='balanced'
      #
      #         GET     Form for compiling new K3 Executable OR status of compiling tasks
      #------------------------------------------------------------------------------
      """
      if webapp.config['COMPILE_OFF']:
        logger.warning("Compilation requested, but denied (Feature not turned on)")
        return returnError("Compilation Features are not available", 400)
  
      logging.debug("[FLASKWEB /compile]   REQUEST ")
  
      if request.method == 'POST':
  
        if not compileService.isUp():
          logger.warning("Compilation requested, but denied (Compiler Service not ready)")
          return returnError("Compiler Service is not running. Ensure it is up before initiating a compilation task.", 400)
  
        if compileService.gracefulHalt:
          logger.warning("Compilation requested, but denied (Compiler Service is flagged to shut down, gracefully)")
          return returnError("Sorry. Your compilation request is denied because the Compiler Service is flagged to shut down, gracefully.", 400)
  
  
        file = request.files['file']
        text = request.form.get('text', None)
        name = request.form.get('name', None)
  
        # Create a unique ID
        uid = getUID()
  
        # Set default settings for a Compile Job
        # TODO: Update Settings
        settings = compileService.getItems()
  
        # update settings & error check where necessary
        settings['compileargs'] = request.form.get('compileargs', settings['compileargs'])
        settings['user'] = request.form.get('user', settings['user'])
        settings['tag'] = request.form.get('tag', settings['tag'])
  
        workload = request.form.get('workload', '').lower()
  
        if settings['compileargs'] == '':
          settings['compileargs'] = workloadOptions[workload]
  
        mem = int(request.form['mem']) if 'mem' in request.form else None
        cpu = int(request.form['cpu']) if 'cpu' in request.form else None
  
        stage = request.form.get('compilestage', settings['compilestage'])
        if stage not in ['both', 'cpp', 'bin']:
          return returnError("Invalid Input on key `compilestage`. Valid entries are ['both', 'cpp', 'bin']", 400)
        else:
          settings['compilestage'] = stage #getCompileStage(stage).value
  
        blocksize = request.form.get('blocksize', settings['blocksize'])
        if isinstance(blocksize, int):
          settings['blocksize'] = blocksize
        elif blocksize.isdigit():
          settings['blocksize'] = int(blocksize)
  
        # Determine application name (for pass-thru naming)
        if not name:
          if file:
            srcfile = secure_filename(file.filename)
            name = srcfile.split('.')[0]
          else:
            return returnError("No name provided for K3 program", 400)
  
        app = AppID(name, uid)
        uname = '%s-%s' % (name, uid)
        path = os.path.join(webapp.config['UPLOADED_BUILD_DEST'], uname).encode(encoding='utf8', errors='ignore')
  
        # Save K3 source to file (either from file or text input)
        settings['source'] = ('%s.k3' % name)
        settings['webaddr'] = webapp.config['ADDR']
        if not os.path.exists(path):
          os.mkdir(path)
  
        if file:
            file.save(os.path.join(path, settings['source']))
        else:
            file = open(os.path.join(path, settings['source']).encode(encoding='utf8', errors='ignore'), 'w')
            file.write(text)
            file.close()
  
        # Create Symlink for easy access to latest compiled task
        link = os.path.join(webapp.config['UPLOADED_BUILD_DEST'], name).encode(encoding='utf8', errors='ignore')
        if os.path.exists(link):
          os.remove(link)
  
        os.symlink(uname, link)
        url = os.path.join(webapp.config['UPLOADED_BUILD_URL'], uname).encode(encoding='utf8', errors='ignore')
  
        job = CompileJob(name, uid, path, settings, mem=mem, cpu=cpu)
        lastCompile = job.getItems()
  
        logger.info("[FLASKWEB] Submitting Compilation job for " + name)
        compileService.submit(job)
  
        # Prepare results to send back to the user
        outputurl = "/compile/%s" % uname
        cppsrc = os.path.join(webapp.config['UPLOADED_BUILD_URL'], uname, settings['source'])
  
        thiscompile = dict(lastCompile, url=dispatcher.getSandboxURL(uname),
                           status='SUBMITTED', outputurl=outputurl, cppsrc=cppsrc,uname=uname)
  
  
        # Return feedback to user
        if request.headers['Accept'] == 'application/json':
          return jsonify(thiscompile), 200
        else:
          return render_template("last.html", appName=name, lastcompile=thiscompile, status=compileService.state.name)
  
      else:
        # TODO: Return list of Active/Completed Compiling Tasks
        if request.headers['Accept'] == 'application/json':
          return ('TO SUBMIT A NEW COMPILATION:\n\n\tcurl -i -X POST -H "Accept: application/json" \
  -F name=<appName> -F file=@<sourceFile> -F blocksize=<blocksize> -F compilestage=<compilestage> \
  http://<host>:<port>/compile' if compileService.isUp()
            else 'START THE SERVICE: curl -i -H "Accept: application/json" http://<host>:<port>/compileservice/up')
  
        else:
          hostlist = dict(master=compileService.masterNodes, worker=compileService.workerNodes, client=compileService.clientNodes)
          return render_template("compile.html", status=compileService.state.name, hostlist=compileService.getAllNodes())
  
  
  def getCompilerOutput(uname):
      """
        Retrieves the compiler output from local file
      """
      fname = os.path.join(webapp.config['UPLOADED_BUILD_DEST'], uname, 'output').encode('utf8')
      if os.path.exists(fname):
        stdout_file = open(fname, 'r')
        output = unicode(stdout_file.read(), 'utf-8')
        stdout_file.close()
        return output
      else:
        return returnError("Output not available for " + uname, 404)
  
  
  @webapp.route('/compile/<uid>', methods=['GET'])
  def getCompile(uid):
    """
    #------------------------------------------------------------------------------
    #  /compile/<uid>
    #         GET     displays STDOUT & STDERR consolidated output for compile task
    #------------------------------------------------------------------------------
    """
    if webapp.config['COMPILE_OFF']:
      return returnError("Compilation Features are not available", 400)
  
    logger.debug("[FLASKWEB] Retrieving last compilation status")
  
    result = db.getCompiles(uid=uid)
    if len(result) == 0:
      result = db.getCompiles(uid=AppID.getUID(uid))
  
    if len(result) == 0:
      return returnError("No output found for compilation, %s\n\n" % uid, 400)
    else:
      output = result[0]
      output['uname'] = AppID.getAppId(output['name'], output['uid'])
      local = os.path.join(webapp.config['UPLOADED_BUILD_DEST'], output['uname'])
      output['sandbox'] = sorted (os.listdir(local))
  
      if request.headers['Accept'] == 'application/json':
        return jsonify(output), 200
      else:
        return render_template("last.html", lastcompile=output)
  
  
  
  @webapp.route('/compilestatus')
  def getCompileStatus():
    """
    #------------------------------------------------------------------------------
    #  /compilestatus - Short list of active jobs & current statuses
    #------------------------------------------------------------------------------
    """
    logger.debug("[FLASKWEB] Retrieving current active compilation status")
  
    jobs = compileService.getActiveState()
    title = "Active Compiling Tasks" if jobs else "NO Active Compiling Jobs"
  
    if request.headers['Accept'] == 'application/json':
      return jsonify(jobs), 200
    else:
      return render_template("keyvalue.html", title=title, store=jobs)
  
  
  @webapp.route('/compilelog')
  def getCompileLog():
    """
    #------------------------------------------------------------------------------
    #  /compilelog - Connects User to compile log websocket
    #------------------------------------------------------------------------------
    """
    if webapp.config['COMPILE_OFF']:
      return returnError("Compilation Features are not available", 400)
  
    logger.debug("[FLASKWEB] Connecting user to Compile Log WebSocket")
  
    with open(webapp.config['COMPILELOG'], 'r') as logfile:
      output = logfile.read().split('<<<<< Compiler Service Initiated >>>>>')[-1]
  
    if request.headers['Accept'] == 'application/json':
      return output, 200
    else:
      return render_template("socket.html", namespace='/compile', prefetch=output)
  
  
  
  @webapp.route('/compile/<uid>/kill', methods=['GET'])
  def killCompile(uid):
    """
    #------------------------------------------------------------------------------
    #  /compile/<uid>/kill
    #         GET     Kills an active compiling tasks (or removes an orphaned one from DB)
    #------------------------------------------------------------------------------
    """
    if webapp.config['COMPILE_OFF']:
      return returnError("Compilation Features are not available", 400)
  
  
    complist = db.getCompiles(uid=uid)
    if len(complist) == 0:
      complist = db.getCompiles(uid=AppID.getUID(uid))
  
    if len(complist) == 0:
      return returnError("Not currently tracking the compile task %s" % uid, 400)
  
    else:
      c = complist[0]
      logging.info ("[FLASKWEB] Asked to KILL Compile UID #%s. Current status is %s" % (c['uid'], c['status']))
  
      if c['status'] not in compileTerminatedStates:
        logging.info ("[FLASKWEB] KILLING Compile UID #%s. " % (c['uid']))
        c['status'] = CompileState.KILLED.name
        db.updateCompile(c['uid'], status=c['status'], done=True)
  
      svid = AppID.getAppId(c['name'], c['uid'])
      compileService.killJob(svid)
  
      if request.headers['Accept'] == 'application/json':
        return jsonify(c), 200
      else:
        return redirect(url_for('listJobs')), 302
  
  @webapp.route('/delete/compiles', methods=['POST'])
  def deleteCompiles():
    """
    #------------------------------------------------------------------------------
    #  /delete/compiles
    #         POST     Deletes list of inactive compile jobs
    #------------------------------------------------------------------------------
    """
    if webapp.config['COMPILE_OFF']:
      return returnError("Compilation Features are not available", 400)
  
  
    deleteList = request.form.getlist("delete_compile")
    for uid in deleteList:
      logger.info("[FLASKWEB /delete/compiles] DELETING compile job uid=" + uid)
      job = db.getCompiles(uid=uid)[0]
      db.deleteCompile(job['uid'])
    return redirect(url_for('listJobs')), 302
  
  @socketio.on('connect', namespace='/compile')
  def test_connect():
      logger.info('[FLASKWEB] Client is connected to /connect stream')
      emit('my response', 'Connected to Compile Log Stream')
  
  @socketio.on('message', namespace='/log')
  def test_message(message):
      emit('my response', "Hello User!")
  
  
  
  
  if __name__ == '__main__':
  
    #  Parse Args
    parser = argparse.ArgumentParser()
    parser.add_argument('-p', '--port', help='Flaskweb Server Port', default=5000, required=False)
    parser.add_argument('-m', '--master', help='URL for the Mesos Master (e.g. zk://localhost:2181/mesos', default='zk://localhost:2181/mesos', required=False)
    parser.add_argument('-d', '--dir', help='Local directory for hosting application and output files', default='/k3/web/', required=False)
    parser.add_argument('-c', '--compile', help='Enable Compilation Features (NOTE: will require a capable image)', action='store_true', required=False)
    parser.add_argument('--wipedb', help='Wipe the Database clean before running', action='store_true', required=False)
    parser.add_argument('--ip', help='Public accessible IP for connecting to flask', required=False)
    args = parser.parse_args()
  
    #  TODO:  Move to a Common module
    #
    # console.setFormatter(formatter)
    # log.addHandler(console)
  
    # logging.basicConfig(format='[%(asctime)s %(levelname)-5s %(name)s] %(message)s', level=logging.DEBUG, datefmt='%H:%M:%S')
    logger.info("K3 Flask Web Service is initiating.....")
  
  
    #  Program Initialization
    webapp.debug = True
  
    if args.wipedb:
      logger.info("Wiping database and exiting")
      db.dropTables()
      sys.exit(0)
  
    db.createTables()
    master = args.master
    port = int(args.port)
    host = socket.gethostname() if not args.ip else args.ip,
  
  
    initWeb(
      host=host,
      port=port,
      master=master,
      local=args.dir,
      compile=args.compile
    )
  
  
    #  Create long running framework, dispatcher, & driver
    frameworkDispatch = mesos_pb2.FrameworkInfo()
    frameworkDispatch.user = "" # Have Mesos fill in the current user.
    frameworkDispatch.name = "[DEV] Dispatcher"
  
    # Note: Each compile job runs as as a separate framework
    frameworkCompiler = mesos_pb2.FrameworkInfo()
    frameworkCompiler.user = ""
    frameworkCompiler.name = "[DEV] Compiler"
  
    # Start mesos schedulers & flask web service
    try:
  
  
      # Create Job Dispatcher
      logging.debug("[FLASKWEB] Dispatch Driver is initializing")
      dispatcher = Dispatcher(master, webapp.config['ADDR'], daemon=True)
      if dispatcher == None:
        logger.error("[FLASKWEB] Failed to create dispatcher. Aborting")
        sys.exit(1)
  
      driverDispatch = mesos.native.MesosSchedulerDriver(dispatcher, frameworkDispatch, master)
      threadDispatch = threading.Thread(target=driverDispatch.run)
      threadDispatch.start()
  
      # Create Compiler Service
      logging.debug("[FLASKWEB] Compiler Service is initializing")
      compileService = CompileServiceManager(webapp.config['LOG_DEST'], webapp.config['ADDR'])
      if compileService == None:
        logger.error("[FLASKWEB] Failed to create compiler service. Aborting")
        sys.exit(1)
      driverCompiler = mesos.native.MesosSchedulerDriver(compileService, frameworkCompiler, master)
      threadCompiler = threading.Thread(target=driverCompiler.run)
      threadCompiler.start()
  
      logger.info("[FLASKWEB] Starting FlaskWeb Server on IP %s", host[0])
      socketio.run(webapp, host=host[0], port=port, use_reloader=False)
      webserverTerminate.clear()
      initSocketIO = False
  
      # Block until flagged to halt
      haltFlag.wait()
  
      compileService.kill()
      logger.info("[FLASKWEB] Server is terminating")
      driverDispatch.stop()
      threadDispatch.join()
      logging.debug("[FLASKWEB] Driver thread complete")
      driverCompiler.stop()
      threadCompiler.join()
      logging.debug("[FLASKWEB] Compiler thread complete")
  
    except socket.error as e:
      logger.error("[FLASKWEB] Flask web cannot start: Port not available.")
      logger.error('ERROR:  %s', str(e))
      compileService.kill()
      driverDispatch.stop()
      threadDispatch.join()
      driverCompiler.stop()
      threadCompiler.join()
  
    except KeyboardInterrupt:
      logger.warning("[FLASKWEB] KEYBOARD INTERRUPT -- Shutting Down")
      compileService.kill()
      driverDispatch.stop()
      threadDispatch.join()
      driverCompiler.stop()
      threadCompiler.join()
  
  
  