[TIC-Web] The waiting queue added. for the image creation 82/125482/1
authorHeekyoung, Oh <heekyoung.oh@samsung.com>
Mon, 17 Apr 2017 12:21:44 +0000 (21:21 +0900)
committerHeekyoung, Oh <heekyoung.oh@samsung.com>
Mon, 17 Apr 2017 12:22:37 +0000 (21:22 +0900)
- The waiting queue added.
- The size of queue was 4.
- The image creation can runned one by one.

Change-Id: I5a10fceb0bec98852527f59af2cd53b97237a269
Signed-off-by: Heekyoung, Oh <heekyoung.oh@samsung.com>
config.json
controller/dbpool.js
controller/dbquery.js
controller/mic.js
controller/router.js
controller/session.js
controller/socketio.js
public/src/css/style.css
public/src/js/model/JobStatusModel.js
public/src/js/page/job.js
public/src/js/page/package.js

index 9e221cf..8485d2c 100644 (file)
         "HOST": "localhost",
         "TRACE": true,
         "DIALECT": "mariasql",
+        "PINGINACTIVE": 60000,
+        "PINGWAITRES": 60000,
         "POOL": {
             "MAX": 5,
             "MIN": 1,
             "IDLETIMEOUT": 500000,
-            "REAPINTERVAL": 500,
             "MAXWAITINGCLIENTS": 20
         }
     },
             "FS_IMAGE_ADD_FAIL": "ws/fs/image/add/fail",
             "FS_IMAGE_ADD_KILL": "ws/fs/image/add/kill",
             "MIC_AVAILABLE_FROM": "ws/mic/available/from",
-            "MIC_AVAILABLE_TO": "ws/mic/available/to"
+            "MIC_AVAILABLE_TO": "ws/mic/available/to",
+            "MIC_NEXT_JOB_FROM": "ws/mic/nextjob/from",
+            "MIC_NEXT_JOB_TO": "ws/mic/nextjob/to"
         },
         "JOB": {
-            "JOB_GET_ALL_COUNT": "/api/job/count",
+            "JOB_GET_ALL_COUNT": "/api/job/count/all",
+            "JOB_GET_QUEUE_COUNT": "/api/job/count/queue",
             "JOB_GET_ALL_LISTITEM": "/api/job/list",
             "JOB_GET_BYID": "/api/job/",
             "JOB_ADD_ONE": "/api/job/add/",
             "JOB_EDIT_ONE": "/api/job/edit/",
-            "JOB_READ_LOG": "/api/job/log/"
+            "JOB_READ_LOG": "/api/job/log/",
+            "JOB_GET_NEXTJOB": "/api/job/queue/next"
         },
         "PACKAGE": {
             "IMPORT": "/api/imports/",
index 27d897d..c0e0ce0 100644 (file)
@@ -23,69 +23,120 @@ var logger = JL('dbpool.js');
 
 var AppConfig = require('../config.json');
 
-function dbPool () {
-    var self = this;
+var createClient = function createClient (closeCallback) {
+    var configMariasql, mariasqlClient;
 
-    self.configMariasql = {
+    // config
+    configMariasql = {
         host: AppConfig.DATABASE.HOST,
         user: AppConfig.DATABASE.USERNAME,
         password: AppConfig.DATABASE.PASSWORD,
         db: AppConfig.DATABASE.DATABASE,
         trace: AppConfig.DATABASE.TRACE,
-        pingInactive: 60000,
-        pingWaitRes: 60000,
+        pingInactive: AppConfig.DATABASE.PINGINACTIVE,
+        pingWaitRes: AppConfig.DATABASE.PINGWAITRES,
         metadata: false
     };
 
-    self.pool = generic_pool.createPool({
-        name: AppConfig.DATABASE.DIALECT,
-
-        create: function (callback) {
-            return new Promise(function (resolve, reject) {
-                var mariasqlClient = new mariasql(self.configMariasql);
-                mariasqlClient.connect(function (err) {
-                    if (err) {
-                        logger.error('DB Connection Error !');
-                        logger.error(err);
-                        reject(err);
-                    }
-
-                    logger.info('DB Connection Success !');
-                    resolve(mariasqlClient);
-                });
-            });
-        },
-
-        destroy: function (client) {
-            return new Promise(function (resolve, reject) {
-                client.on('end', function () {
-                    resolve();
-                });
-                client.disconnect();
-                client.end();
-                client._handle = null;
-            });
-        },
+    mariasqlClient = new mariasql(configMariasql);
 
-        validate: function (client) {
-            return client.connected;
-        },
+    mariasqlClient.on('ready', function () {
+        logger.info('DB Connection Success !');
+    }).on('error', function (err) {
+        logger.error('DB Connection On Error ! (' + err.code + ') ' + err.message);
+    }).on('end', function () {
+        logger.info('DB Connection Ended !');
+    }).on('close', function () {
+        logger.info('DB Connection Closed !');
+    });
+
+    process.nextTick(function () {
+        if (typeof closeCallback === 'function') {
+            mariasqlClient.on('close', closeCallback);
+        }
+        mariasqlClient.on('close', function () {
+            mariasqlClient.end();
+        });
+    })
 
-        max: AppConfig.DATABASE.POOL.MAX,
+    return mariasqlClient;
+};
+
+function DBPool () {
+    // pool
+    this.pool;
+
+    this.init();
+};
 
-        min: AppConfig.DATABASE.POOL.MIN,
+DBPool.prototype.opts = {
+    name: AppConfig.DATABASE.DIALECT,
 
-        idleTimeoutMillis: AppConfig.DATABASE.POOL.IDLETIMEOUT,
+    max: AppConfig.DATABASE.POOL.MAX,
 
-        reapIntervalMillis: AppConfig.DATABASE.POOL.REAPINTERVAL,
+    min: AppConfig.DATABASE.POOL.MIN,
 
-        maxWaitingClients: AppConfig.DATABASE.POOL.MAXWAITINGCLIENTS,
+    idleTimeoutMillis: AppConfig.DATABASE.POOL.IDLETIMEOUT,
+
+    maxWaitingClients: AppConfig.DATABASE.POOL.MAXWAITINGCLIENTS,
+
+    autostart: true
+};
+
+DBPool.prototype.factory = {
+    create: function (callback) {
+        return new Promise(function (resolve, reject) {
+            var mariasqlClient;
+
+            function closeCallback () {
+                reject();
+            }
+
+            // client
+            mariasqlClient = createClient(closeCallback);
+            mariasqlClient.connect(function () {
+                resolve(mariasqlClient);
+            });
+        });
+    },
 
-        returnToHead: true
+    destroy: function (client) {
+        return new Promise(function (resolve, reject) {
+            client.on('end', function () {
+                resolve();
+            });
+            client.destroy();
+            client.end();
+            client._handle = null;
+        });
+    },
+
+    validate: function (client) {
+        return client.connected;
+    }
+};
+
+DBPool.prototype.init = function init () {
+    this.pool = generic_pool.createPool(this.factory, this.opts);
+
+    this.pool.on('factoryCreateError', function (err) {
+        if (err) {
+            logger.error(err.message);
+            throw err;
+        }
+        return;
+    });
+
+    this.pool.on('factoryDestroyError', function (err) {
+        if (err) {
+            logger.error(err.message);
+            throw err;
+        }
+        return;
     });
 };
 
-dbPool.prototype.query = function query (query, params) {
+DBPool.prototype.query = function query (query, params) {
     var mariaPool = this.pool;
     return new Promise (function (resolve, reject) {
         mariaPool.acquire().then(function (client) {
@@ -97,21 +148,35 @@ dbPool.prototype.query = function query (query, params) {
                 metadata: false
             }, function (err, rows) {
                 if (err) {
+                    logger.error('Error: ' + err.message);
                     mariaPool.destroy(client);
                     meta.result = err;
-                    reject(meta);
+                    return reject(meta);
                 }
 
                 meta.result = rows;
                 mariaPool.release(client);
                 logger.info(JSON.stringify(meta));
-                resolve(meta);
+                return resolve(meta);
             });
         }).catch(function (err) {
             logger.error(err.message);
-            reject(err);
+            return reject(err);
         });
     });
 };
 
-module.exports = dbPool;
+DBPool.prototype.clearAll = function clearAll () {
+    var mariaPool = this.pool;
+    mariaPool.drain(function () {
+        mariaPool.clear();
+    });
+};
+
+var dbPool = new DBPool();
+
+process.on('exit', function () {
+    dbPool.clearAll();
+});
+
+module.exports = dbPool;
\ No newline at end of file
index 0049356..4898d0d 100644 (file)
@@ -23,14 +23,6 @@ var _ = require('lodash');
 
 var dbpool = require('./dbpool');
 
-var pool = new dbpool({});
-
-process.on('exit', function () {
-    pool.drain(function () {
-        pool.clear();
-    });
-});
-
 /**
  * Client
  */
@@ -59,17 +51,41 @@ mariadb.queries = {
             'tic_image.image_name job_image_name, ',
             'tic_image.image_size job_image_size, ',
             'tic_job.job_hasksfile job_hasksfile, ',
+            'tic_job.job_ks job_ks, ',
+            'tic_job.job_arch job_arch, ',
             'tic_job.job_uptime job_uptime ',
         'from tic_job ',
         'left join tic_image on tic_job.job_image_id = tic_image.image_id ',
         'where tic_job.job_deleted = false ',
-        'and job_id = <%= strJobId %>;'
+        'and tic_job.job_id = <%= strJobId %>;'
     ],
     'getJobsTotalCount': [
         'select count(job_id) as total_count ',
         'from tic_job ',
         'where job_deleted = false;'
     ],
+    'getJobsQueueCount': [
+        'select count(job_id) as total_count ',
+        'from tic_job ',
+        'where job_deleted = false ',
+        'and job_status = "INPROGRESS" OR job_status = "READY";'
+    ],
+    'getJobsQueueNext': [
+        'select tic_job.job_id job_id, ',
+            'tic_job.job_status job_status, ',
+            'tic_job.job_image_id job_image_id, ',
+            'tic_image.image_name job_image_name, ',
+            'tic_image.image_size job_image_size, ',
+            'tic_job.job_hasksfile job_hasksfile, ',
+            'tic_job.job_ks job_ks, ',
+            'tic_job.job_arch job_arch, ',
+            'tic_job.job_uptime job_uptime ',
+        'from tic_job ',
+        'left join tic_image on tic_job.job_image_id = tic_image.image_id ',
+        'where tic_job.job_deleted = false ',
+        'and tic_job.job_status = "READY" ',
+        'order by tic_job.job_id asc limit 1;'
+    ],
     'getJobsAllList': [
         'select tic_job.job_id job_id, ',
             'tic_job.job_status job_status, ',
@@ -120,7 +136,7 @@ mariadb.queries = {
         ');'
     ],
     'getUser': [
-        'select user_email, user_group ',
+        'select user_id, user_email, user_group ',
         'from tic_user ',
         'where user_email = "<%= userEmail %>" and user_password = "<%= userPassword %>" ',
         'limit 1;'
@@ -133,7 +149,7 @@ mariadb.queries = {
 };
 
 mariadb.doQuery = function doQuery(queryString) {
-    return pool.query(queryString, []);
+    return dbpool.query(queryString, []);
 };
 
 /**
@@ -144,7 +160,7 @@ mariadb.doQuery = function doQuery(queryString) {
  */
 mariadb.editJob = function editJob(req, res) {
     var queryString, strJobId, reqParam,
-    job_status, job_deleted, job_hasksfile, job_image_id, job_ks, job_arch;
+    job_status, job_deleted, job_hasksfile, job_image_id, job_ks, job_arch, job_updater;
 
     function onSuccess(rows) {
         logger.info('editJob.success');
@@ -159,6 +175,7 @@ mariadb.editJob = function editJob(req, res) {
     job_image_id = reqParam.job_image_id;
     job_ks = reqParam.job_ks;
     job_arch = reqParam.job_arch;
+    job_updater = reqParam.job_updater;
 
     queryString = 'update tic_job set';
     if (job_image_id) {
@@ -179,7 +196,9 @@ mariadb.editJob = function editJob(req, res) {
     if (job_arch) {
         queryString += ' job_arch = "' + job_arch + '",';
     }
-    queryString += ' job_updater = "tic",';
+    if (job_updater) {
+        queryString += ' job_updater = "' + job_updater + '",';
+    }
     queryString += ' job_uptime = now()';
     queryString += ' where job_id = ' + strJobId + ';';
 
@@ -251,6 +270,18 @@ mariadb.getJobsTotalCount = function getJobsTotalCount(req, res) {
 };
 
 /**
+ * Get Counts of Job By job_status
+ */
+mariadb.getJobsQueueCount = function getJobsQueueCount(callback) {
+    var queryString = _.join(this.queries['getJobsQueueCount'], '');
+
+    logger.info('getJobsQueueCount: query = ' + queryString);
+
+    // call
+    this.doQuery(queryString).then(callback);
+};
+
+/**
  * Get All Jobs
  */
 mariadb.getJobsAllList = function getJobsAllList(req, res) {
@@ -275,6 +306,22 @@ mariadb.getJobsAllList = function getJobsAllList(req, res) {
     this.doQuery(queryString).then(onSuccess);
 };
 
+/**
+ * Get Next Job
+ */
+mariadb.getJobsQueueNext = function getJobsQueueNext(req, res) {
+    var queryString = _.join(this.queries['getJobsQueueNext'], '');
+
+    logger.info('getJobsQueueNext: query = ' + queryString);
+
+    function onSuccess (rows) {
+        res.json(rows.result);
+    }
+
+    // call
+    this.doQuery(queryString).then(onSuccess);
+};
+
 
 /**
  * IAMGES
@@ -476,42 +523,4 @@ mariadb.connectToClient = function connectToClient() {
     });
 };
 
-/**
- * Create the Client
- */
-mariadb.createClient = function createClient() {
-    return new Promise(function (resolve, reject) {
-        logger.info('create client');
-
-        var mariaSqlConfig;
-
-        // config
-        mariaSqlConfig = {
-            host: '127.0.0.1',
-            user: 'tic',
-            password: 'tic',
-            db: 'pdk',
-            timeout: 60000,
-            trace: true
-        }
-
-        // create
-        mariaSqlClient = new mariaSql(mariaSqlConfig);
-        resolve();
-    });
-};
-
-/**
- * Initialize
- */
-mariadb.init = function init() {
-    logger.info('init');
-
-    var self = this;
-    self.createClient()
-    .then(self.connectToClient);
-};
-
-// mariadb.init();
-
 module.exports = mariadb;
index fb15119..0b0a988 100644 (file)
@@ -22,11 +22,14 @@ var TreeKill = require('tree-kill');
 var util = require('util');
 var JL = require('jsnlog').JL;
 var _ = require('lodash');
+var client = require('./dbquery');
 var AppConfig = require('../config.json');
 
 var logger = JL('mic.js');
 
-var PROCESS_CNT_MAX = 1;
+var PROCESS_CNT_MAX = 4;
+var PROCESS_CNT_MIN = 0;
+var PROCESS_CNT_ONE = 1;
 
 var Mic = {};
 
@@ -40,11 +43,33 @@ var Mic = {};
  */
 var processMgr = [];
 
-Mic.isAvailable = function isAvailable() {
-    if (processMgr.length < PROCESS_CNT_MAX) {
-        return true;
+Mic.hasNextJob = function hasNextJob(callback) {
+    function onSuccess(rows) {
+        var queueCnt, hasNext;
+        queueCnt = Number(rows.result[0].total_count);
+        hasNext = false;
+        if (queueCnt >= PROCESS_CNT_ONE) {
+            hasNext = true;
+        }
+        callback(hasNext);
     }
-    return false;
+    client.getJobsQueueCount(onSuccess);
+};
+
+Mic.isAvailable = function isAvailable(callback) {
+    function onSuccess(rows) {
+        var queueCnt, isAvailable;
+
+        queueCnt = rows.result[0].total_count;
+        isAvailable = false;
+
+        if (queueCnt >= PROCESS_CNT_MIN && queueCnt < PROCESS_CNT_MAX) {
+            isAvailable = true;
+        }
+
+        callback(isAvailable);
+    }
+    client.getJobsQueueCount(onSuccess);
 }
 
 Mic.kill = function kill(paramObj) {
@@ -154,7 +179,7 @@ Mic.create = function create(paramObj, io) {
                     processMgr.pop(psIndex);
                 }
 
-                logger.error('Terminated (' + code + ')');
+                logger.info('Terminated (' + code + ')');
                 if (code === 0) {
                     sendMsg(AppConfig.EVENT.SOCKET.FS_IMAGE_ADD_FINISH, {
                         msg: 'Terminated (' + code + ')',
index 1c2f260..1826f76 100644 (file)
@@ -69,15 +69,27 @@ var init = function (serv) {
 
     /**
      * Get total Count of Jobs
-     * @URI /api/job/count
+     * @URI /api/count/all
      * @TYPE POST
      */
-    router.post('/job/count', function (req, res) {
-        logger.info('an api called that /api/job/count');
+    router.post('/job/count/all', function (req, res) {
+        logger.info('an api called that /api/count/all');
         client.getJobsTotalCount(req, res);
     });
 
     /**
+     * Get Status Count
+     * @URI /api/job/count/queue
+     * @TYPE POST
+     */
+    router.post('/job/count/queue', function (req, res) {
+        logger.info('an api called that /api/job/count/queue');
+        client.getJobsQueueCount(function (rows) {
+            res.json(rows.result);
+        });
+    })
+
+    /**
      * Add New Job
      * @URI /api/job/add
      * @TYPE POST
@@ -93,6 +105,9 @@ var init = function (serv) {
      * @TYPE POST
      */
     router.post('/job/:id', function (req, res) {
+        if (!Number(req.params.id)) {
+            return;
+        }
         logger.info('an api called that /api/job/' + req.params.id);
         client.getJobById(req, res);
     });
@@ -103,16 +118,32 @@ var init = function (serv) {
      * @TYPE POST
      */
     router.post('/job/edit/:id', function (req, res) {
+        if (!Number(req.params.id)) {
+            return;
+        }
         logger.info('an api called that /api/job/edit/' + req.params.id);
         client.editJob(req, res);
     });
 
     /**
+     * Get the job for status was "READY"
+     * @URI /api/job/next
+     * @TYPE POST
+     */
+    router.post('/job/queue/next', function (req, res) {
+        logger.info('an api called that /job/queue/next');
+        client.getJobsQueueNext(req, res);
+    });
+
+    /**
      * Read the log file
      * @URI /api/job/log/:id
      * @TYPE POST
      */
     router.post('/job/log/:id', function (req, res) {
+        if (!Number(req.params.id)) {
+            return;
+        }
         logger.info('an api called that /api/job/log/' + req.params.id);
         filesystem.readLogFile(req, res);
     });
@@ -157,6 +188,9 @@ var init = function (serv) {
      * @TYPE POST
      */
     router.post('/image/edit/:id', function (req, res) {
+        if (!Number(req.params.id)) {
+            return;
+        }
         logger.info('an api called that /api/image/edit/' + req.params.id);
         client.editImage(req, res);
     });
index e04f2e0..4257321 100644 (file)
@@ -64,6 +64,7 @@ Session.prototype.postSession = function(req, res) {
                     // save email in session
                     req.session.email = req.body.email;
                     req.session.group = user.data.user_group;
+                    req.session.userid = user.data.user_id;
                     logger.info('login success: ' + req.body.email + ' , ' + user.data.user_group);
                     res.json({
                         result: STATUS.SUCCESS,
@@ -88,7 +89,8 @@ Session.prototype.getSession = function(req, res) {
         res.json({
             status: STATUS.CONNECTED,
             email: req.session.email,
-            group: req.session.group
+            group: req.session.group,
+            id: req.session.userid
         });
     } else {
         res.json({
index a79df9d..909c066 100644 (file)
@@ -25,6 +25,13 @@ var AppConfig = require('../config.json');
 var listen = function listen (server) {
     var io = socketio.listen(server);
 
+    function fnEmitIsImageCreationAvailable () {
+        Mic.isAvailable(function (isAvailable) {
+            logger.info('mic available: ' + isAvailable);
+            io.sockets.emit(AppConfig.EVENT.SOCKET.MIC_AVAILABLE_TO, isAvailable);
+        });
+    }
+
     /**
      * Connection with MIC
      */
@@ -36,7 +43,7 @@ var listen = function listen (server) {
          */
         socket.on(AppConfig.EVENT.SOCKET.FS_IMAGE_ADD_FROM, function (data) {
             Mic.create(data, io);
-            io.sockets.emit(AppConfig.EVENT.SOCKET.MIC_AVAILABLE_TO, Mic.isAvailable());
+            fnEmitIsImageCreationAvailable();
         });
 
         /**
@@ -44,16 +51,26 @@ var listen = function listen (server) {
          */
         socket.on(AppConfig.EVENT.SOCKET.FS_IMAGE_ADD_KILL, function (data) {
             Mic.kill(data);
-
-            io.sockets.emit(AppConfig.EVENT.SOCKET.MIC_AVAILABLE_TO, Mic.isAvailable());
+            fnEmitIsImageCreationAvailable();
         });
 
         /**
          * AppConfig.EVENT.SOCKET.MIC_AVAILABLE_FROM = 'ws/mic/available/from'
          */
         socket.on(AppConfig.EVENT.SOCKET.MIC_AVAILABLE_FROM, function () {
-            logger.info('mic available: ' + Mic.isAvailable());
-            io.sockets.emit(AppConfig.EVENT.SOCKET.MIC_AVAILABLE_TO, Mic.isAvailable());
+            fnEmitIsImageCreationAvailable();
+        });
+
+        /**
+         * AppConfig.EVENT.SOCKET.MIC_AVAILABLE_FROM = 'ws/mic/nextjob/from'
+         */
+        socket.on(AppConfig.EVENT.SOCKET.MIC_NEXT_JOB_FROM, function () {
+            Mic.hasNextJob(function (hasNext) {
+                if (hasNext) {
+                    logger.info('mic has a next job : ' + hasNext);
+                    io.sockets.emit(AppConfig.EVENT.SOCKET.MIC_NEXT_JOB_TO, hasNext);
+                }
+            });
         });
 
         socket.on('disconnect', function () {
index a66e742..f3ebd73 100644 (file)
@@ -176,17 +176,17 @@ body {
 }
 .job-status-done {
     font-size: 2vh;
-    color: blue;
+    color: #337ab7;
     display: table-cell;
 }
 .job-status-cancel, .job-status-failed {
     font-size: 2vh;
-    color: red;
+    color: #e05151;
     display: table-cell;
 }
 .job-status-inprogress {
     font-size: 2vh;
-    color: green;
+    color: #2aa730;
     display: table-cell;
 }
 #tic-job-list {
@@ -218,7 +218,7 @@ body {
     background-color: slategray;
 }
 #tic-job-list .btndownload {
-    background-color: lightseagreen;
+    background-color: #337ab7;
 }
 #tic-job-list .btnnotactive {
     pointer-events: none;
@@ -288,6 +288,9 @@ td.extended_job_table_container > div {
     height: 100%;
     text-align: left;
     overflow: auto;
+    white-space: pre;
+    font-size: 1em;
+    text-overflow: ellipsis;
 }
 td.extended_job_table_container {
     height: 13vh;
@@ -408,6 +411,7 @@ tr.extended_job_table_row:hover td {
     float: left;
     margin-bottom: 8px;
     width: 75%;
+    font-size: 1.18em;
 }
 .image-list-name-btndownload {
     float: left;
@@ -420,7 +424,10 @@ tr.extended_job_table_row:hover td {
     text-overflow: ellipsis;
     white-space:nowrap;
     overflow: hidden;
-    display: table-row;
+}
+.image-list-detail > b {
+    display: inline-block;
+    width: 100px;
 }
 .image-list-btndownload {
     min-width: 10px;
index 17d4b3c..bfb7dae 100644 (file)
@@ -34,7 +34,7 @@ define([
             {
                 value: 'DONE', 
                 text: 'Done',
-                class: 'fa fa-circle job-status-done'
+                class: 'fa fa-check job-status-done'
             },
             {
                 value: 'CANCELED',
index d6beb42..7525d52 100644 (file)
@@ -20,8 +20,10 @@ define([
     'js/util',
     'js/logger',
     'js/page/image',
+    'js/page/package',
     'js/model/JobModel',
     'js/model/JobPagingModel',
+    'js/model/MicManager',
     'js/widget/JobPaging',
     'js/widget/JobTableItem',
     'js/widget/JobTableEmptyItem',
@@ -32,8 +34,10 @@ define([
     Util,
     Logger,
     Image,
+    Package,
     JobModel,
     JobPagingModel,
+    MicManager,
     JobPaging,
     JobTableItem,
     JobTableEmptyItem,
@@ -60,6 +64,7 @@ define([
 
     // const
     var JOB_STATUS_DONE = 'DONE';
+    var JOB_STATUS_INPROGRESS = 'INPROGRESS';
     var JOB_STATUS_FAILED = 'FAILED';
     var USER_DEFAULT = 'GUEST';
 
@@ -75,6 +80,7 @@ define([
             logger.info('_updateView');
             var targetTableBody = $('#tic-job-table > tbody');
             targetTableBody.empty();
+            var targetId;
 
             if (arrJobs.length <= 0) {
                 targetTableBody.append(new JobTableEmptyItem().getRow());
@@ -82,6 +88,14 @@ define([
                 // when not empty
                 _.forEach(arrJobs, function (value, index) {
                     targetTableBody.append(new JobTableItem(value, index).getRow());
+                    if (value.getJobStatus() === JOB_STATUS_INPROGRESS) {
+                        targetId = 'extended_job_table_row_' + value.getJobId();
+                        Util.POST(AppConfig.EVENT.JOB.JOB_READ_LOG + value.getJobId())
+                        .then(function (line) {
+                            targetId = '#' + targetId + ' > td > div';
+                            $(targetId).append(line);
+                        });
+                    }
                 });
 
                 // bind events
@@ -201,7 +215,16 @@ define([
     }
 
     function _initSocket() {
-        function _btnLogBlink (jobId, bBlink) {
+
+        function checksNextJob() {
+            return new Promise(function (resolve, reject) {
+                logger.info('checksNextJob');
+                client.emit(AppConfig.EVENT.SOCKET.MIC_NEXT_JOB_FROM);
+                resolve();
+            });
+        }
+
+        function _btnLogBlink(jobId, bBlink) {
             var btnLogSelector, btnLogElem;
             btnLogSelector = '#tic-job-list #job_table_row_' + jobId + ' .btnbiglog';
             btnLogElem = $(btnLogSelector);
@@ -215,6 +238,17 @@ define([
         }
 
         /**
+         * when has the next job
+         * AppConfig.EVENT.SOCKET.MIC_NEXT_JOB_TO = 'ws/mic/nextjob/to'
+         */
+        client.on(AppConfig.EVENT.SOCKET.MIC_NEXT_JOB_TO, function (hasNext) {
+            logger.info(AppConfig.EVENT.SOCKET.MIC_NEXT_JOB_TO);
+            if (hasNext) {
+                doCreateAnImage();
+            }
+        });
+
+        /**
          * when running on mic
          *
          * AppConfig.EVENT.SOCKET.FS_IMAGE_ADD_TO = 'ws/fs/image/add/to'
@@ -232,6 +266,8 @@ define([
         client.on(AppConfig.EVENT.SOCKET.FS_IMAGE_ADD_FINISH, function (dataObj) {
             var jobId, imageName;
 
+            logger.info('IMAGE_ADD_FINISH: ' + AppConfig.EVENT.SOCKET.FS_IMAGE_ADD_FINISH);
+
             // jobId
             jobId = dataObj.jobId;
 
@@ -248,7 +284,6 @@ define([
                 jobId: jobId
             });
 
-            logger.info(AppConfig.EVENT.SOCKET.FS_IMAGE_ADD_FINISH);
             _btnLogBlink(dataObj.jobId, false);
 
             function onError(err) {
@@ -261,8 +296,7 @@ define([
             function updateJobListView() {
                 logger.info('IMAGE_ADD_FINISH.updateJobListView');
 
-                // upate the list of jobs
-                updateList(ModelJobPaging.getCurrentPoint());
+                updateList();
 
                 Image.updateList();
 
@@ -271,10 +305,16 @@ define([
             }
 
             function updateJobInfo(data) {
+                /**
+                 * TODO
+                 * Change the value
+                 * from UserInfo.email to UserInfo.id
+                 */
                 // update the status and image_id
                 var msgObj = {
                     job_status: JOB_STATUS_DONE,
-                    job_image_id: data.image_id
+                    job_image_id: data.image_id,
+                    job_updater: UserInfo.email
                 };
 
                 logger.info('IMAGE_ADD_FINISH.updateJobInfo: ' + JSON.stringify(msgObj));
@@ -303,7 +343,6 @@ define([
                      */
                     imageInfo = _.find(arrFileInfo, {name: imageName});
                     logger.info('IMAGE_ADD_FINISH.addAnImage: ' + JSON.stringify(imageInfo));
-
                     if (imageInfo && imageInfo.type === 'file') {
                         msgObj = {
                             image_name: imageInfo.name,
@@ -328,6 +367,7 @@ define([
             return getAnImageInfo()
                 .then(addAnImage)
                 .then(updateJobInfo)
+                .then(checksNextJob)
                 .then(updateJobListView)
                 .catch(onError);
         });
@@ -358,9 +398,11 @@ define([
 
             // update the status
             msgObj = {
-                job_status: JOB_STATUS_FAILED
+                job_status: JOB_STATUS_FAILED,
+                job_updater: UserInfo.email
             };
             Util.POST(AppConfig.EVENT.JOB.JOB_EDIT_ONE + jobId, msgObj)
+            .then(checksNextJob)
             .then(function () {
                 // upate the list of jobs
                 updateList(ModelJobPaging.getCurrentPoint());
@@ -370,28 +412,77 @@ define([
 
     /**
      * @name doCreateAnImage
-     * @param paramObj {
-     *      jobId: '1',
-     *      pathKsFile: '/var/tmp/tic-web/1/default.ks',
-     *      pathOutput: '/var/tmp/tic-web/1/',
-     *      imageName: 'default',
-     *      imageArch: 'armv7l'
-     * }
      */
-    function doCreateAnImage(paramObj) {
-        var msgData;
-
+    function doCreateAnImage() {
         logger.info('doCreateAnImage');
 
-        msgData = {
-            jobId: paramObj.jobId,
-            pathKsFile: paramObj.pathKsFile,
-            pathOutput: paramObj.pathOutput,
-            imageName: paramObj.pathOutput + paramObj.imageName,
-            imageArch: paramObj.imageArch
-        };
-        client.emit(AppConfig.EVENT.SOCKET.FS_IMAGE_ADD_FROM, msgData);
-        updateList();
+        function onError(err) {
+            logger.error('doCreateAnImage.onError: ' + err.message);
+            throw err;
+        }
+
+        function doUpdateJobView (jobModel) {
+            logger.info('doCreateAnImage.doUpdateJobView');
+            Util.POST(AppConfig.EVENT.JOB.JOB_EDIT_ONE + jobModel.getJobId(), {
+                job_status: JOB_STATUS_INPROGRESS,
+                job_updater: UserInfo.email
+            }).then(function () {
+                // scroll
+                $('html, body').animate({
+                    scrollTop: $('#tic-job-section').offset().top
+                }, 500);
+                updateList();
+            });
+        }
+
+        function doCreate (jobModel) {
+            return new Promise(function (resolve, reject) {
+                var pathKsFile, imageName, msgData, msgObj;
+
+                logger.info('doCreateAnImage.doCreate');
+
+                pathKsFile = jobModel.getJobKsPath();
+                imageName = pathKsFile.substring(pathKsFile.lastIndexOf('/')+1, pathKsFile.lastIndexOf('.'));
+
+                // options for the creation
+                msgData = {
+                    jobId: jobModel.getJobId(),
+                    pathKsFile: pathKsFile,
+                    pathOutput: jobModel.getJobPath(),
+                    imageName: imageName + '.tar.gz',
+                    imageArch: jobModel.getJobArch()
+                };
+
+                // to create
+                client.emit(AppConfig.EVENT.SOCKET.FS_IMAGE_ADD_FROM, msgData);
+                resolve(jobModel);
+            });
+        }
+
+        function createNextJobModel (res) {
+            return new Promise(function (resolve, reject) {
+                var nextJobInfo, nextJobModel;
+
+                logger.info('doCreateAnImage.createNextJobModel');
+
+                nextJobInfo = res[0];
+                nextJobInfo['job_usergroup'] = _.isEmpty(UserInfo) ? USER_DEFAULT : UserInfo.group;
+
+                nextJobModel = new JobModel(nextJobInfo);
+                resolve(nextJobModel);
+            });
+        }
+
+        function getNextJob () {
+            logger.info('doCreateAnImage.getNextJob');
+            return Util.POST(AppConfig.EVENT.JOB.JOB_GET_NEXTJOB);
+        }
+
+        getNextJob()
+        .then(createNextJobModel)
+        .then(doCreate)
+        .then(doUpdateJobView)
+        .catch(onError);
     }
 
     /**
index ca9ace0..6a7990b 100644 (file)
@@ -49,6 +49,14 @@ define([
     var groups = null;
     var groupId = 0;
 
+    //To manage that the image creation
+    var micMgr = [];
+
+    // reason for reject (not on error)
+    var MIC_CNT_MAX = 4;
+    var MIC_CNT_ONE = 1;
+
+    // status
     var JOB_STATUS_INPROGRESS = 'INPROGRESS';
 
     // AppConfig
@@ -68,51 +76,41 @@ define([
             return;
         }
 
-        function doCreateAnImage() {
-            var pathKsFile, imageName;
-            logger.info('onClickHandlerForImgCreationBtn.doCreateAnImage');
-
-            // scroll
-            $('html, body').animate({
-                scrollTop: $('#tic-job-section').offset().top
-            }, 500);
+        function onError(err) {
+            logger.info('onClickHandlerForImgCreationBtn.onError');
+            if (err) {
+                logger.error(err.message);
+            }
+        }
 
-            // get the name of image
-            pathKsFile = newJobModel.getJobKsPath();
-            imageName = pathKsFile.substring(pathKsFile.lastIndexOf('/')+1, pathKsFile.lastIndexOf('.'));
+        function checksAvailable () {
+            return new Promise (function (resolve, reject) {
+                logger.info('onClickHandlerForImgCreationBtn.checksAvailable');
 
-            // options for the creation
-            var msgData = {
-                jobId: newJobModel.getJobId(),
-                pathKsFile: pathKsFile,
-                pathOutput: newJobModel.getJobPath(),
-                imageName: imageName + '.tar.gz',
-                imageArch: newJobModel.getJobArch()
-            };
+                function doChecking (res) {
+                    var isPossible, length;
+                    isPossible = false;
+                    length = Number(res[0].total_count);
+                    if (length === MIC_CNT_ONE) {
+                        isPossible = true;
+                    }
+                    resolve(isPossible);
+                }
 
-            // create
-            Job.doCreateAnImage(msgData);
+                return Util.POST(AppConfig.EVENT.JOB.JOB_GET_QUEUE_COUNT)
+                .then(doChecking);
+            });
         }
 
-        function getRecipeFile() {
-            var msgData;
-
-            logger.info('onClickHandlerForImgCreationBtn.getRecipeFile: job_path = ' + newJobModel.getJobPath());
-
-            /**
-             * FIXME
-             *
-             * filename will be removed.
-             * filenmae generated by ticcore
-             */
-            msgData = {
+        function getRecipeFile(newJobModel) {
+            var msgData = {
                 recipes: Settings.getRecipeStore(),
                 packages: _.map(checkedPackagesList, 'name'),
                 outdir: newJobModel.getJobPath()
             };
 
             function onErrorGetRecipeFile(err) {
-                logger.info('onClickHandlerForImgCreationBtn.onErrorGetRecipeFile');
+                logger.info('startExecutingJob.onErrorGetRecipeFile');
                 logger.error(err);
                 throw err;
             }
@@ -132,9 +130,8 @@ define([
                 newJobModel.setJobKs(strKsName);
                 newJobModel.setJobArch(strArch);
 
-                logger.info('onClickHandlerForImgCreationBtn.onSuccessGetRecipeFile: ' + responseObj.data.kspath);
+                logger.info('startExecutingJob.onSuccessGetRecipeFile: ' + responseObj.data.kspath);
                 msgObj = {
-                    job_status: JOB_STATUS_INPROGRESS,
                     job_hasksfile: true,
                     job_ks: strKsName,
                     job_arch: strArch
@@ -142,7 +139,11 @@ define([
                 return Util.POST(AppConfig.EVENT.JOB.JOB_EDIT_ONE + newJobModel.getJobId(), msgObj);
             }
 
-            return Util.POST(AppConfig.EVENT.PACKAGE.EXPORT, msgData)
+            function getKickStartFile () {
+                return Util.POST(AppConfig.EVENT.PACKAGE.EXPORT, msgData);
+            }
+
+            return getKickStartFile()
             .then(onSuccessGetRecipeFile)
             .catch(onErrorGetRecipeFile);
         }
@@ -155,13 +156,13 @@ define([
             });
         }
 
-        function getJobId(jobItem) {
-            logger.info('onClickHandlerForImgCreationBtn.getJobId');
+        function getJobInfo(jobItem) {
             var jobId = jobItem.job_id;
+            logger.info('onClickHandlerForImgCreationBtn.getJobInfo : ' + JSON.stringify(jobItem));
             return Util.POST(AppConfig.EVENT.JOB.JOB_GET_BYID + jobId);
         }
 
-        function doConfirmOk() {
+        function addJob(cnt) {
             logger.info('onClickHandlerForImgCreationBtn.addJob');
             return Util.POST(AppConfig.EVENT.JOB.JOB_ADD_ONE);
         }
@@ -173,11 +174,23 @@ define([
         }
 
         return showConfirmDialog()
-            .then(doConfirmOk)
-            .then(getJobId)
+            .then(addJob)
+            .then(getJobInfo)
             .then(setJobModel)
             .then(getRecipeFile)
-            .then(doCreateAnImage);
+            .then(checksAvailable)
+            .then(function (isPossible) {
+                if (isPossible) {
+                    Job.doCreateAnImage();
+                } else {
+                    // scroll
+                    $('html, body').animate({
+                        scrollTop: $('#tic-job-section').offset().top
+                    }, 500);
+                    Job.updateList();
+                }
+            })
+            .catch(onError);
     }
 
     function _getChecked() {