6 Commits

Author SHA1 Message Date
Manuel Romero
885f2986ef cli 2020-07-27 13:41:50 +02:00
Manuel Romero
e45445f919 Using timezone for times 2020-07-24 15:54:53 +02:00
Manuel Romero
2040c9f914 fixes 2020-07-23 19:28:23 +02:00
Manuel Romero
93884ebeea polishing 2020-07-23 19:12:59 +02:00
Manuel Romero
dc9354a36a Fixes 2020-07-23 18:31:32 +02:00
Manuel Romero
5e1e193688 Scheduler 2020-07-23 18:13:37 +02:00
74 changed files with 249473 additions and 296 deletions

View File

Before

Width:  |  Height:  |  Size: 211 B

After

Width:  |  Height:  |  Size: 211 B

View File

Before

Width:  |  Height:  |  Size: 213 B

After

Width:  |  Height:  |  Size: 213 B

View File

Before

Width:  |  Height:  |  Size: 209 B

After

Width:  |  Height:  |  Size: 209 B

View File

Before

Width:  |  Height:  |  Size: 211 B

After

Width:  |  Height:  |  Size: 211 B

View File

Before

Width:  |  Height:  |  Size: 213 B

After

Width:  |  Height:  |  Size: 213 B

View File

Before

Width:  |  Height:  |  Size: 211 B

After

Width:  |  Height:  |  Size: 211 B

View File

Before

Width:  |  Height:  |  Size: 215 B

After

Width:  |  Height:  |  Size: 215 B

View File

Before

Width:  |  Height:  |  Size: 146 B

After

Width:  |  Height:  |  Size: 146 B

View File

Before

Width:  |  Height:  |  Size: 137 B

After

Width:  |  Height:  |  Size: 137 B

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 215 B

After

Width:  |  Height:  |  Size: 215 B

View File

Before

Width:  |  Height:  |  Size: 214 B

After

Width:  |  Height:  |  Size: 214 B

View File

Before

Width:  |  Height:  |  Size: 699 KiB

After

Width:  |  Height:  |  Size: 699 KiB

View File

Before

Width:  |  Height:  |  Size: 141 KiB

After

Width:  |  Height:  |  Size: 141 KiB

View File

Before

Width:  |  Height:  |  Size: 829 KiB

After

Width:  |  Height:  |  Size: 829 KiB

View File

Before

Width:  |  Height:  |  Size: 434 KiB

After

Width:  |  Height:  |  Size: 434 KiB

View File

@@ -6,8 +6,8 @@
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="assets/favicon.ico">
<link rel="stylesheet" href="styles.23a05ae088b0f881d0eb.css"></head>
</head>
<body>
<app-root></app-root>
<script src="runtime.e227d1a0e31cbccbf8ec.js" defer></script><script src="polyfills-es5.f1f388528ea207060cbf.js" nomodule defer></script><script src="polyfills.335424f161535f57733f.js" defer></script><script src="scripts.5520a99f673924c17e00.js" defer></script><script src="main.412f22a84714f529779a.js" defer></script></body>
<script src="runtime.js" defer></script><script src="polyfills-es5.js" nomodule defer></script><script src="polyfills.js" defer></script><script src="styles.js" defer></script><script src="scripts.js" defer></script><script src="vendor.js" defer></script><script src="main.js" defer></script></body>
</html>

File diff suppressed because one or more lines are too long

5459
dist/qmi-cloud/main.js vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/qmi-cloud/main.js.map vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

14575
dist/qmi-cloud/polyfills-es5.js vendored Normal file

File diff suppressed because it is too large Load Diff

1
dist/qmi-cloud/polyfills-es5.js.map vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

6848
dist/qmi-cloud/polyfills.js vendored Normal file

File diff suppressed because it is too large Load Diff

1
dist/qmi-cloud/polyfills.js.map vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
!function(e){function r(r){for(var n,l,f=r[0],i=r[1],p=r[2],c=0,s=[];c<f.length;c++)l=f[c],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in i)Object.prototype.hasOwnProperty.call(i,n)&&(e[n]=i[n]);for(a&&a(r);s.length;)s.shift()();return u.push.apply(u,p||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,f=1;f<t.length;f++)0!==o[t[f]]&&(n=!1);n&&(u.splice(r--,1),e=l(l.s=t[0]))}return e}var n={},o={0:0},u=[];function l(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,l),t.l=!0,t.exports}l.m=e,l.c=n,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,r){if(1&r&&(e=l(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)l.d(t,n,(function(r){return e[r]}).bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(r,"a",r),r},l.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},l.p="";var f=window.webpackJsonp=window.webpackJsonp||[],i=f.push.bind(f);f.push=r,f=f.slice();for(var p=0;p<f.length;p++)r(f[p]);var a=i;t()}([]);

155
dist/qmi-cloud/runtime.js vendored Normal file
View File

@@ -0,0 +1,155 @@
/******/ (function(modules) { // webpackBootstrap
/******/ // install a JSONP callback for chunk loading
/******/ function webpackJsonpCallback(data) {
/******/ var chunkIds = data[0];
/******/ var moreModules = data[1];
/******/ var executeModules = data[2];
/******/
/******/ // add "moreModules" to the modules object,
/******/ // then flag all "chunkIds" as loaded and fire callback
/******/ var moduleId, chunkId, i = 0, resolves = [];
/******/ for(;i < chunkIds.length; i++) {
/******/ chunkId = chunkIds[i];
/******/ if(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {
/******/ resolves.push(installedChunks[chunkId][0]);
/******/ }
/******/ installedChunks[chunkId] = 0;
/******/ }
/******/ for(moduleId in moreModules) {
/******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
/******/ modules[moduleId] = moreModules[moduleId];
/******/ }
/******/ }
/******/ if(parentJsonpFunction) parentJsonpFunction(data);
/******/
/******/ while(resolves.length) {
/******/ resolves.shift()();
/******/ }
/******/
/******/ // add entry modules from loaded chunk to deferred list
/******/ deferredModules.push.apply(deferredModules, executeModules || []);
/******/
/******/ // run deferred modules when all chunks ready
/******/ return checkDeferredModules();
/******/ };
/******/ function checkDeferredModules() {
/******/ var result;
/******/ for(var i = 0; i < deferredModules.length; i++) {
/******/ var deferredModule = deferredModules[i];
/******/ var fulfilled = true;
/******/ for(var j = 1; j < deferredModule.length; j++) {
/******/ var depId = deferredModule[j];
/******/ if(installedChunks[depId] !== 0) fulfilled = false;
/******/ }
/******/ if(fulfilled) {
/******/ deferredModules.splice(i--, 1);
/******/ result = __webpack_require__(__webpack_require__.s = deferredModule[0]);
/******/ }
/******/ }
/******/
/******/ return result;
/******/ }
/******/
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // object to store loaded and loading chunks
/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched
/******/ // Promise = chunk loading, 0 = chunk loaded
/******/ var installedChunks = {
/******/ "runtime": 0
/******/ };
/******/
/******/ var deferredModules = [];
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
/******/ var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
/******/ jsonpArray.push = webpackJsonpCallback;
/******/ jsonpArray = jsonpArray.slice();
/******/ for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
/******/ var parentJsonpFunction = oldJsonpFunction;
/******/
/******/
/******/ // run deferred modules from other chunks
/******/ checkDeferredModules();
/******/ })
/************************************************************************/
/******/ ([]);
//# sourceMappingURL=runtime.js.map

1
dist/qmi-cloud/runtime.js.map vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

20176
dist/qmi-cloud/scripts.js vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/qmi-cloud/scripts.js.map vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

659
dist/qmi-cloud/styles.js vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/qmi-cloud/styles.js.map vendored Normal file

File diff suppressed because one or more lines are too long

201117
dist/qmi-cloud/vendor.js vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/qmi-cloud/vendor.js.map vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -43,6 +43,7 @@
"jsonwebtoken": "^8.5.1",
"leonardo-ui": "^1.7.1",
"moment": "^2.24.0",
"moment-timezone": "^0.5.31",
"mongoose": "^5.7.4",
"ngx-markdown": "^9.0.0",
"nodemon": "^1.19.1",
@@ -75,4 +76,4 @@
"tslint": "~5.11.0",
"typescript": "~3.7.5"
}
}
}

View File

@@ -56,21 +56,17 @@ async function init(type) {
}
let provisions = await db.provision.get(filter);
let scenarios = await db.scenario.get();
await asyncForEach(provisions.results, async function(p) {
var _scenario = scenarios.results.filter(s=>{
return s.name === p.scenario
});
if ( _scenario.length ){
p._scenario = _scenario[0];
}
timeRunning(p);
if (!IS_REAL) {
console.log(`${p._id} - limit: ${limit} hs - actual duration: ${p.duration.hours} hs`);
}
if ( p.duration && p.duration.hours >= limit) {
await cb(p);
//Only if 24x7
if ( !p.autoShutdown || p.autoShutdown.is24x7 ) {
timeRunning(p);
if (!IS_REAL) {
console.log(`${p._id} - limit: ${limit} hs - actual duration: ${p.duration.hours} hs`);
}
if ( p.duration && p.duration.hours >= limit) {
await cb(p);
}
}
});
}
@@ -79,11 +75,11 @@ const doSendEmailWarning = async function(p) {
if ( p.pendingNextAction === 'stopVms') {
console.log(`Warning email Stop already sent. Wait for pending action to complete.`);
} else {
let msg = `Send warning STOP email - ${p.user.displayName} (${p.user.upn}) about provision '${p._scenario.title}' (${p._id}) being 'Running' more than ${RUNNING_LIMIT_HOURS_WARNING} hours (exactly ${p.duration.complete})`;
let msg = `Send warning STOP email - ${p.user.displayName} (${p.user.upn}) about provision '${p._scenarioDoc.title}' (${p._id}) being 'Running' more than ${RUNNING_LIMIT_HOURS_WARNING} hours (exactly ${p.duration.complete})`;
console.log(msg);
if ( IS_REAL ) {
await db.provision.update(p._id, {"pendingNextAction": "stopVms"});
await sendEmail.sendWillStopIn24(p, p._scenario);
await sendEmail.sendWillStopIn24(p, p._scenarioDoc);
await db.notification.add({ provision: p._id.toString(), type: 'warningStop', message: msg });
}
}
@@ -91,11 +87,17 @@ const doSendEmailWarning = async function(p) {
const doStop = async function(p) {
try {
let msg = `VMs stopped - ${p.user.displayName} (${p.user.upn}) about provision '${p._scenario.title}' (${p._id}) being 'Running' more than ${RUNNING_LIMIT_HOURS_STOP} hours (exactly ${p.duration.complete})`
let msg = `VMs stopped - ${p.user.displayName} (${p.user.upn}) about provision '${p._scenarioDoc.title}' (${p._id}) being 'Running' more than ${RUNNING_LIMIT_HOURS_STOP} hours (exactly ${p.duration.complete})`
console.log(msg);
if ( IS_REAL ) {
await azurecli.deallocate(p, true);
if ( IS_REAL ) {
//Disable Divvy
await azurecli.updateVmsTags(p, {
"24x7": false,
"StartupTime": false
});
//Stop VMs
await azurecli.deallocate(p, true);
await db.notification.add({ provision: p._id.toString(), type: 'stop', message: msg });
}

View File

@@ -56,6 +56,17 @@ async function start(provision){
let rgName = _getRgName(provision);
console.log("Starting VMs for resource group: "+rgName);
//Re-enable Divvy according to what's in database
if (provision.autoShutdown){
console.log("Re-enabling Divvy Tags according to database");
var tagsEdit = {
"24x7": provision.autoShutdown.is24x7? " " : false,
"StartupTime": provision.autoShutdown.utcTagStartupTime || false,
"ShutdownTime": provision.autoShutdown.utcTagShutdownTime || false
}
await updateVmsTags(provision, tagsEdit);
}
var computeClient = await _getClient(provision.scenario);
let finalResult = await computeClient.virtualMachines.list(rgName);

View File

@@ -15,6 +15,9 @@ const sc = new mongoose.Schema({
localeStartupTime: {
type: String
},
localTimezone: {
type: String
},
utcTagShutdownTime: {
type: String
},

View File

@@ -170,7 +170,7 @@ const update = async (model, id, body, reply) => {
try {
const { ...updateData } = body;
updateData.updated = new Date();
console.log("UPDATE", id, updateData);
//console.log("UPDATE", id, updateData);
var exec = model.findByIdAndUpdate(id, updateData, { new: true });
if ( model === Provision ) {
exec = exec.populate('user').populate('destroy').populate('_scenarioDoc').populate("autoShutdown");

View File

@@ -178,15 +178,15 @@ router.post('/:userId/provisions', passport.ensureAuthenticatedAndIsMe, async (r
req.body.autoShutdown = autoShutdown._id;
}
const mongoJob = await db.provision.add(req.body);
const provision = await db.provision.add(req.body);
if ( mongoJob.scenario === "azqmi-qseok" ){
if ( provision.scenario === "azqmi-qseok" ){
queues[TF_APPLY_QSEOK_QUEUE].add("tf_apply_qseok_job", {
scenario: req.body.scenario,
vmType: req.body.vmType,
nodeCount: req.body.nodeCount,
id: mongoJob._id,
id: provision._id,
user: req.user,
_scenario: scenarioSource
});
@@ -195,13 +195,78 @@ router.post('/:userId/provisions', passport.ensureAuthenticatedAndIsMe, async (r
scenario: req.body.scenario,
vmType: req.body.vmType,
nodeCount: req.body.nodeCount,
id: mongoJob._id,
id: provision._id,
user: req.user,
_scenario: scenarioSource
});
}
return res.status(200).json(mongoJob);
return res.status(200).json(provision);
} catch (error) {
next(error);
}
});
/**
* @swagger
* /users/{userId}/provisions/{id}:
* put:
* description: Update Provision by ID
* summary: Update Provision by ID
* parameters:
* - name: userId
* in: path
* type: string
* required: true
* - name: id
* in: path
* type: string
* required: true
* - in: body
* name: body
* description: Provision object
* required: true
* produces:
* - application/json
* responses:
* 200:
* description: Provision
*/
router.put('/:userId/provisions/:id', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
const provision = await db.provision.getById(req.params.id);
if (!provision){
return res.status(404).json({"msg": "Not found privision with id "+req.params.id});
}
try {
var resSchedule;
if ( req.body.autoShutdownData ) {
if ( req.body.autoShutdownData._id ) {
resSchedule = await db.provisionAutoShutdown.update(req.body.autoShutdownData._id, req.body.autoShutdownData);
} else {
resSchedule = await db.provisionAutoShutdown.add(req.body.autoShutdownData);
}
var tagsEdit = {
"24x7": resSchedule.is24x7? " " : false,
"StartupTime": resSchedule.utcTagStartupTime || false,
"ShutdownTime": resSchedule.utcTagShutdownTime || false
}
azurecli.updateVmsTags(provision, tagsEdit);
}
var patch = {};
if ( req.body.user ) {
patch.user = req.body.user;
}
if ( resSchedule ) {
patch.autoShutdown = resSchedule._id;
}
var result = {
provision: await db.provision.update(provision._id, patch)
}
return res.json(result);
} catch (error) {
next(error);
@@ -235,19 +300,19 @@ router.post('/:userId/provisions', passport.ensureAuthenticatedAndIsMe, async (r
router.delete('/:userId/provisions/:id', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
try {
const mongoJob = await db.provision.getById(req.params.id);
if (!mongoJob){
const provision = await db.provision.getById(req.params.id);
if (!provision){
return res.status(404).json({"msg": "Not found privision with id "+req.params.id});
}
//var delDest = mongoJob.destroy._id;
//if ( mongoJob.destroy ) {
// delDest = await db.destroy.del(mongoJob.destroy._id);
//var delDest = provision.destroy._id;
//if ( provision.destroy ) {
// delDest = await db.destroy.del(provision.destroy._id);
//}
const delProv = await db.provision.update(req.params.id, {"isDeleted": true});
//Move folder
if (fs.existsSync(`/provisions/${mongoJob.scenario}_${req.params.id}`)) {
fs.moveSync(`/provisions/${mongoJob.scenario}_${req.params.id}`, `/provisions/deleted/${mongoJob.scenario}_${req.params.id}`, { overwrite: true })
if (fs.existsSync(`/provisions/${provision.scenario}_${req.params.id}`)) {
fs.moveSync(`/provisions/${provision.scenario}_${req.params.id}`, `/provisions/deleted/${provision.scenario}_${req.params.id}`, { overwrite: true })
}
return res.json({"provision": delProv, "destroy": delProv.destroy});
} catch (error) {
@@ -282,11 +347,11 @@ router.delete('/:userId/provisions/:id', passport.ensureAuthenticatedAndIsMe, as
router.post('/:userId/provisions/:id/deallocatevms', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
try {
let mongoJob = await db.provision.getById(req.params.id);
if (!mongoJob){
let provision = await db.provision.getById(req.params.id);
if (!provision){
return res.status(404).json({"msg": "Not found provision with id "+req.params.id});
}
azurecli.deallocate(mongoJob);
azurecli.deallocate(provision);
return res.json({"statusVms": "Stopping"});
} catch (error) {
@@ -322,12 +387,12 @@ router.post('/:userId/provisions/:id/deallocatevms', passport.ensureAuthenticate
router.post('/:userId/provisions/:id/startvms', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
try {
let mongoJob = await db.provision.getById(req.params.id);
if (!mongoJob){
let provision = await db.provision.getById(req.params.id);
if (!provision){
return res.status(404).json({"msg": "Not found privision with id "+req.params.id});
}
azurecli.start(mongoJob);
azurecli.start(provision);
return res.json({"statusVms": "Starting"});
} catch (error) {
@@ -363,20 +428,22 @@ router.post('/:userId/provisions/:id/startvms', passport.ensureAuthenticatedAndI
router.post('/:userId/provisions/:id/extend', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
try {
let mongoJob = await db.provision.getById(req.params.id);
if (!mongoJob){
let provision = await db.provision.getById(req.params.id);
if (!provision){
return res.status(404).json({"msg": "Not found privision with id "+req.params.id});
}
/*if ( mongoJob.countExtend === 5 ) {
/*if ( provision.countExtend === 5 ) {
return res.status(200).json({"msg": "You have reached the limit for the number of times to extend the Running VMs period."});
}*/
let timeRunning = db.utils.getNewTimeRunning(mongoJob);
let countExtend = db.utils.getNewCountExtend(mongoJob);
mongoJob = await db.provision.update(req.params.id, {"runningFrom":new Date(), "timeRunning": timeRunning, "countExtend": countExtend, "pendingNextAction": undefined});
let timeRunning = db.utils.getNewTimeRunning(provision);
let countExtend = db.utils.getNewCountExtend(provision);
provision = await db.provision.update(req.params.id, {"runningFrom":new Date(), "timeRunning": timeRunning, "countExtend": countExtend, "pendingNextAction": undefined});
return res.json(mongoJob);
console.log(`Extending running period fo provision (${provision._id}), new total extends: ${countExtend}`);
return res.json(provision);
} catch (error) {
next(error);
@@ -411,24 +478,24 @@ router.post('/:userId/provisions/:id/destroy', passport.ensureAuthenticatedAndIs
try {
let mongoJob = await db.provision.getById(req.params.id);
if (!mongoJob){
let provision = await db.provision.getById(req.params.id);
if (!provision){
return res.status(404).json({"msg": "Not found privision with id "+req.params.id});
}
const destroyJob = await db.destroy.add({ "user": req.params.userId });
mongoJob = await db.provision.update(req.params.id, {"destroy": destroyJob._id});
const scenarioSource = await db.scenario.getOne({name: mongoJob.scenario});
provision = await db.provision.update(req.params.id, {"destroy": destroyJob._id});
const scenarioSource = await db.scenario.getOne({name: provision.scenario});
queues[TF_DESTROY_QUEUE].add("tf_destroy_job", {
scenario: mongoJob.scenario,
provId: mongoJob._id,
scenario: provision.scenario,
provId: provision._id,
user: req.user,
id: destroyJob._id,
_scenario: scenarioSource
});
return res.status(200).json(mongoJob);
return res.status(200).json(provision);
} catch (error) {
return res.status(error.output.statusCode).json({"err":error});

View File

@@ -7,14 +7,41 @@
<h4 class="modal-title w-100 font-weight-bold">Edit Provision</h4>
</div>
<div class="modal-body" style="max-height: 650px; overflow: auto;">
<div>
<section style="padding: 20px 0px;">
<label>Onwer (*):</label>
<select class="browser-default custom-select custom-select-sm" [(ngModel)]="selectedUser">
<option *ngFor="let item of users" [value]="item._id">{{item.displayName}}</option>
</select>
<div *ngIf="provision._scenarioDoc && provision._scenarioDoc.isDivvyEnabled" style="background: #f0f0f0; padding: 15px 15px; margin: 7px 0px;">
<div>
<h5><mdb-icon far icon="calendar-alt"></mdb-icon> Scenario schedule <small style="font-size: 50%;">(everyday this scenario will startup and shutdown as scheduled bellow:)</small></h5>
</div>
<div>
<b>Timezone:</b> {{getUTCTimes().timezone}} <i>(times relative to this timezone)</i>
</div>
<div class="md-form row" [ngClass]="{'disabled': autoShutdownData.is24x7}">
<div class="col-sm-6">
<label>Startup time:</label>
<ngb-timepicker readonly="true" style="font-size: 10px;" [(ngModel)]="autoShutdownData.startupTime" [meridian]="true" [minuteStep]="15" [disabled]="autoShutdownData.is24x7"></ngb-timepicker>
<div class="utc">
<i>UTC time: {{getUTCTimes().startupTime}}</i>
</div>
</div>
<div class="col-sm-6">
<label>Shutdown time:</label>
<ngb-timepicker style="font-size: 10px;" [(ngModel)]="autoShutdownData.shutdownTime" [meridian]="true" [minuteStep]="15" [disabled]="autoShutdownData.is24x7"></ngb-timepicker>
<div class="utc">
<i>UTC time: {{getUTCTimes().shutdownTime}}</i>
</div>
</div>
</div>
<section style="padding: 20px 0px; margin: 0px 20px">
<mdb-checkbox [default]="false" [checked]="autoShutdownData.is24x7" (change)="checkOnchangeAutoshutdown($event)">Run 24x7 <i>(<mdb-icon fas icon="exclamation-triangle"></mdb-icon> requires permission from regional manager)</i></mdb-checkbox>
</section>
</div>
<!--<div>
<section style="padding: 20px 0px;">
<label>Change Owner (*):</label>
<select class="browser-default custom-select custom-select-sm" [(ngModel)]="selectedUser">
<option *ngFor="let item of users" [value]="item._id">{{item.displayName}}</option>
</select>
</section>
</div>-->
</div>
<div class="modal-footer d-flex justify-content-center">

View File

@@ -0,0 +1,19 @@
label {
position: relative;
font-weight: bold;
}
.md-form {
margin-top: 0px;
margin-bottom: 0px;
}
.utc {
padding-left: 20px;
padding-top: 5px;
font-size: 12px
}
.disabled {
opacity: 0.2;
}

View File

@@ -3,6 +3,7 @@ import { MDBModalRef } from 'angular-bootstrap-md';
import { Subject } from 'rxjs';
import { UsersService } from '../services/users.service';
import { ProvisionsService } from '../services/provisions.service';
import * as moment from 'moment-timezone';
@Component({
selector: 'qmi-edit-provision',
@@ -15,6 +16,17 @@ export class ProvisionModalComponent implements OnInit, OnDestroy {
action: Subject<any> = new Subject();
users;
selectedUser;
autoShutdownData: any = {
is24x7: false,
startupTime: {
"hour": 7,
"minute": 0
},
shutdownTime: {
"hour": 20,
"minute": 0
}
};
sendData : any = {};
@@ -28,6 +40,20 @@ export class ProvisionModalComponent implements OnInit, OnDestroy {
if (this.provision.user ) {
this.selectedUser = this.provision.user._id;
}
if ( this.provision.autoShutdown ) {
console.log("Database autoshutdown", this.provision.autoShutdown);
this.autoShutdownData.shutdownTime = {
"hour": parseInt(this.provision.autoShutdown.localeShutdownTime.split(":")[0]),
"minute": parseInt(this.provision.autoShutdown.localeShutdownTime.split(":")[1])
}
this.autoShutdownData.startupTime = {
"hour": parseInt(this.provision.autoShutdown.localeStartupTime.split(":")[0]),
"minute": parseInt(this.provision.autoShutdown.localeStartupTime.split(":")[1])
}
this.autoShutdownData.is24x7 = this.provision.autoShutdown.is24x7;
} else {
this.autoShutdownData.is24x7 = true;
}
});
}
@@ -35,11 +61,35 @@ export class ProvisionModalComponent implements OnInit, OnDestroy {
}
confirm() : void {
this._provisionsService.updateProvisionAdmin(this.provision._id, { user: this.selectedUser } ).subscribe( res=>{
confirm() : void {
this.sendData.user = this.selectedUser;
let autoShutdownUTC = this.getUTCTimes();
this.sendData.autoShutdownData = this.provision.autoShutdown || {};
this.sendData.autoShutdownData.is24x7 = this.autoShutdownData.is24x7;
this.sendData.autoShutdownData.utcTagStartupTime = autoShutdownUTC.tagStartup;
this.sendData.autoShutdownData.utcTagShutdownTime = autoShutdownUTC.tagShutdown;
this.sendData.autoShutdownData.localeStartupTime = this.autoShutdownData.startupTime.hour+":"+this._provisionsService.addZero(this.autoShutdownData.startupTime.minute);
this.sendData.autoShutdownData.localeShutdownTime = this.autoShutdownData.shutdownTime.hour+":"+this._provisionsService.addZero(this.autoShutdownData.shutdownTime.minute);
this.sendData.autoShutdownData.localTimezone = autoShutdownUTC.timezone;
this.sendData.autoShutdownData.timezoneOffset = autoShutdownUTC.timezoneOffset;
console.log("SendData", this.sendData);
this._provisionsService.updateProvisionUser(this.provision._id, this.provision.user._id, this.sendData ).subscribe( res=>{
this.action.next(res);
this.modalRef.hide();
});
}
checkOnchangeAutoshutdown($event) {
this.autoShutdownData.is24x7 = $event.checked;
}
getUTCTimes() {
const zone = this.provision.autoShutdown? this.provision.autoShutdown.localTimezone : moment.tz.guess(true);
return this._provisionsService.getUTCTimes(this.autoShutdownData, zone);
}
}

View File

@@ -3,35 +3,41 @@
<button type="button" class="close pull-right" aria-label="Close" (click)="modalRef.hide()">
<span aria-hidden="true">×</span>
</button>
<h3 style="text-align: center;" class="modal-title w-100">Provision information</h3>
<h3 style="text-align: center;" class="modal-title w-100">{{info._scenarioDoc.title}}</h3>
</div>
<div class="modal-body">
<div>
<b>ProvisionID: </b> <span class="mydata">{{info._id}}</span>
<div style="text-align: right;">
<span class="mydata">{{info._id}}</span>
</div>
<div>
<b>Purpose: </b> {{info.description}}
</div>
<div>
<b>Scenario: </b> {{info._scenarioDoc.title}} <span *ngIf="info.isExternalAccess">( External Access is enabled )</span>
</div>
<div *ngIf="info.autoShutdown">
<mdb-icon far icon="calendar-alt"></mdb-icon> <span *ngIf="info.autoShutdown.is24x7"> 24x7</span><span *ngIf="!info.autoShutdown.is24x7"> Startup time: {{info.autoShutdown.localeStartupTime}}h, Shutdown time: {{info.autoShutdown.localeShutdownTime}}h</span>
</div>
<div *ngIf="!info.autoShutdown">
<mdb-icon far icon="calendar-alt"></mdb-icon> 24x7
</div>
<div *ngIf="info.vmType && !info.vmImage">
<b>Instance Type: </b> {{info.vmType}} <span *ngIf="info.nodeCount">( {{info.nodeCount}} nodes )</span>
<mdb-icon fas icon="server"></mdb-icon> {{info.vmType}} <span *ngIf="info.nodeCount">( {{info.nodeCount}} nodes )</span>
</div>
<div *ngFor="let item of info.vmImage | keyvalue">
<div *ngIf="item.value.version">
<b>Product version: </b> <span>{{item.value.version.name}}</span>
<div>
<mdb-icon fas icon="server"></mdb-icon> {{item.value.version.name}}
</div>
<div *ngIf="item.value.vmType" style="padding-left: 25px;">
<mdb-icon fas icon="long-arrow-alt-right"></mdb-icon> {{item.value.vmType}} <span *ngIf="item.value.diskSizeGb">( {{item.value.diskSizeGb}} GiB on disk ) </span> <span *ngIf="item.value.nodeCount">( {{item.value.nodeCount}} nodes )</span>
</div>
</div>
<div *ngIf="item.value.vmType" >
<b>Instance Type: </b> <span>{{item.value.vmType}} </span> <span *ngIf="item.value.diskSizeGb">( {{item.value.diskSizeGb}} GiB on disk ) </span> <span *ngIf="item.value.nodeCount">( {{item.value.nodeCount}} nodes )</span>
<div *ngIf="!item.value.version">
<div *ngIf="item.value.vmType">
<mdb-icon fas icon="server"></mdb-icon> {{item.value.vmType}} <span *ngIf="item.value.diskSizeGb">( {{item.value.diskSizeGb}} GiB on disk ) </span> <span *ngIf="item.value.nodeCount">( {{item.value.nodeCount}} nodes )</span>
</div>
</div>
</div>
<div *ngIf="info.autoShutdown">
<b>Running window: </b> <span *ngIf="info.autoShutdown.is24x7">24x7</span><span *ngIf="!info.autoShutdown.is24x7">Startup time: {{info.autoShutdown.localeStartupTime}}h, Shutdown time: {{info.autoShutdown.localeShutdownTime}}h</span>
</div>
<div *ngIf="!info.autoShutdown">
<b>Running window: </b> 24x7
<div *ngIf="info.isExternalAccess">
<mdb-icon fas icon="globe-americas"></mdb-icon> External access enabled
</div>
<h5 *ngIf="info.outputs" class="info-subtitle">Connection resources</h5>
<div *ngFor="let item of info.outputs | keyvalue">

View File

@@ -8,25 +8,44 @@
</div>
<div class="modal-body" style="max-height: 650px; overflow: auto;">
<!--<h5>{{scenario.title}}</h5>-->
<div class="md-form">
<div class="md-form" style="background: #f0f0f0; padding: 15px;">
<div>
<div>Purpose: (*)</div>
<h5>Purpose: (*)</h5>
<textarea [(ngModel)]="sendData.description" type="text" class="md-textarea form-control" rows="1" mdbInput placeholder="Short description or Salesforce opportunity"></textarea>
</div>
</div>
<section *ngIf="scenario.isExternal" style="padding: 20px 0px; margin: 0px 20px">
<mdb-checkbox [default]="false" (change)="checkOnchange($event)">Enable External Access <i>(<mdb-icon fas icon="exclamation-triangle"></mdb-icon> only if it's strictly necessary)</i></mdb-checkbox>
</section>
<section *ngIf="!scenario.isExternal" style="padding: 20px 0px; margin: 0px 20px">
<mdb-checkbox [default]="false" (change)="checkOnchange($event)" [disabled]="true"><span style="color:#ccc">Enable External Access <i>(only if it's strictly necessary)</i></span><br><i><mdb-icon fas icon="exclamation-triangle"></mdb-icon> This scenario only allows access from VPN</i></mdb-checkbox>
</section>
<div style="padding-top: 10px" class="md-form" >
<div *ngIf="scenario.isDivvyEnabled" style="background: #f0f0f0; padding: 15px 15px; margin: 7px 0px;">
<div>
<h5><mdb-icon far icon="calendar-alt"></mdb-icon> Scenario schedule <small style="font-size: 50%;">(everyday this scenario will startup and shutdown as scheduled bellow:)</small></h5>
</div>
<div>
<b>Local timezone:</b> {{zone}} <i>(times relative to this timezone)</i>
</div>
<div class="md-form row" [ngClass]="{'disabled': autoShutdown.is24x7}">
<div class="col-sm-6">
<label>Startup time:</label>
<ngb-timepicker style="font-size: 10px;" [(ngModel)]="autoShutdown.startupTime" [meridian]="true" [minuteStep]="15" [disabled]="autoShutdown.is24x7"></ngb-timepicker>
<div class="utc">
<i>UTC time: {{getUTCTimes().startupTime}}</i>
</div>
</div>
<div class="col-sm-6">
<label>Shutdown time:</label>
<ngb-timepicker style="font-size: 10px;" [(ngModel)]="autoShutdown.shutdownTime" [meridian]="true" [minuteStep]="15" [disabled]="autoShutdown.is24x7"></ngb-timepicker>
<div class="utc">
<i>UTC time: {{getUTCTimes().shutdownTime}}</i>
</div>
</div>
</div>
<section style="padding: 20px 0px; margin: 0px 20px">
<mdb-checkbox [default]="false" [checked]="autoShutdown.is24x7" (change)="checkOnchangeAutoshutdown($event)">Run 24x7 <i>(<mdb-icon fas icon="exclamation-triangle"></mdb-icon> requires permission from regional manager)</i></mdb-checkbox>
</section>
</div>
<div style="padding: 15px; margin: 7px 0px; background: #f0f0f0;" class="md-form" >
<div *ngFor="let server of scenario.availableProductVersions" style="margin-bottom: 15px;">
<h5 style="font-weight: 600; margin-bottom: 0px;">- {{server.product}}</h5>
<h5 style="font-weight: 600; margin-bottom: 0px;"><mdb-icon fas icon="server"></mdb-icon> {{server.product}}</h5>
<div *ngIf="server.versions && server.versions.length">
<div >
<label>Product version: (*)</label>
@@ -61,31 +80,17 @@
</select>
</div>
</div>
<div *ngIf="scenario.isDivvyEnabled" style="background: #f0f0f0; padding: 0px 15px;">
<hr>
<div style="background: #f0f0f0; padding: 15px 15px; margin: 7px 0px;">
<div>
<h5>Running window <small style="font-size: 50%;">(Everyday this scenario will startup and shutdown as scheduled bellow:)</small></h5>
<h5><mdb-icon fas icon="globe-americas"></mdb-icon> External access</h5>
</div>
<div class="md-form row" *ngIf="!autoShutdown.is24x7">
<div class="col-sm-6">
<label>Startup time:</label>
<ngb-timepicker style="font-size: 10px;" [(ngModel)]="autoShutdown.startupTime" [meridian]="true" [minuteStep]="15"></ngb-timepicker>
<div>
<i>UTC - {{getUTCTimes().startupTime}}</i>
</div>
</div>
<div class="col-sm-6">
<label>Shutdown time:</label>
<ngb-timepicker [(ngModel)]="autoShutdown.shutdownTime" [meridian]="true" [minuteStep]="15"></ngb-timepicker>
<div>
<i>UTC - {{getUTCTimes().shutdownTime}}</i>
</div>
</div>
</div>
<section style="padding: 20px 0px; margin: 0px 20px">
<mdb-checkbox [default]="false" [checked]="autoShutdown.is24x7" (change)="checkOnchangeAutoshutdown($event)">Enable 24x7 access <i>(Requires permission from regional manager)</i></mdb-checkbox>
<section *ngIf="scenario.isExternal" style="padding-left: 20px;">
<mdb-checkbox [default]="false" (change)="checkOnchange($event)">Enable (<mdb-icon fas icon="exclamation-triangle"></mdb-icon> only if it's strictly necessary)</mdb-checkbox>
</section>
<i *ngIf="!scenario.isExternal">This scenario only allows access from VPN</i>
</div>
</div>
<div class="modal-footer d-flex justify-content-center">

View File

@@ -10,8 +10,14 @@ label {
::placeholder {
color: #ccc;
}
.utc {
padding-left: 20px;
padding-top: 5px;
font-size: 12px
}
.ngb-tp-meridian .btn {
padding: 5px 10px;
}
.disabled {
opacity: 0.2;
}

View File

@@ -1,7 +1,10 @@
import { Component, Injectable, OnInit, OnDestroy } from '@angular/core';
import { Component, OnInit, OnDestroy } from '@angular/core';
import { MDBModalRef } from 'angular-bootstrap-md';
import { Subject, Subscription } from 'rxjs';
import { ScenariosService } from '../services/scenarios.service';
import { ProvisionsService } from '../services/provisions.service';
import * as moment from 'moment-timezone';
@Component({
@@ -38,9 +41,13 @@ export class NewProvisionConfirmComponent implements OnInit, OnDestroy {
}
};
constructor( public modalRef: MDBModalRef, private _scenariosService: ScenariosService ) {}
zone;
constructor( public modalRef: MDBModalRef, private _scenariosService: ScenariosService, private _provisionsService: ProvisionsService ) {}
ngOnInit() {
this.zone = moment.tz.guess(true);
this.vmTypesSub = this._scenariosService.getScenarioVmtypes().subscribe ( res => {
this.vmTypes = res.results.filter(v=>!v.disabled);
@@ -68,34 +75,6 @@ export class NewProvisionConfirmComponent implements OnInit, OnDestroy {
})
}
private _addZero(i) {
if (i < 10) {
i = "0" + i;
}
return i;
}
getUTCTimes() : any {
var dateStartup = new Date();
var dateShutdown = new Date();
dateStartup.setHours(this.autoShutdown.startupTime.hour);
dateStartup.setMinutes(this.autoShutdown.startupTime.minute);
dateStartup.setSeconds(0);
dateShutdown.setHours(this.autoShutdown.shutdownTime.hour);
dateShutdown.setMinutes(this.autoShutdown.shutdownTime.minute);
dateShutdown.setSeconds(0);
//var hourStart = this._addZero(dateStartup.getUTCHours());
//var hourShut = this._addZero(dateShutdown.getUTCHours());
var minShut = this._addZero(dateShutdown.getUTCMinutes());
var minStart = this._addZero(dateStartup.getUTCMinutes());
return {
startupTime: dateStartup.getUTCHours() +":"+minStart,
shutdownTime: dateShutdown.getUTCHours() +":"+minShut,
tagStartup: this._addZero(dateStartup.getUTCHours())+""+minStart,
tagShutdown: this._addZero(dateShutdown.getUTCHours())+""+minShut,
timezoneOffset: dateStartup.getTimezoneOffset()
}
}
ngOnDestroy() {
@@ -131,13 +110,15 @@ export class NewProvisionConfirmComponent implements OnInit, OnDestroy {
}
if ( this.scenario.isDivvyEnabled ) {
if ( !this.autoShutdown.is24x7 ) {
let autoShutdownUTC = this._provisionsService.getUTCTimes(this.autoShutdown, this.zone);
this.sendData.autoShutdownData = {
is24x7: false,
utcTagStartupTime: this.getUTCTimes().tagStartup,
utcTagShutdownTime: this.getUTCTimes().tagShutdown,
localeStartupTime: this.autoShutdown.startupTime.hour+":"+this._addZero(this.autoShutdown.startupTime.minute),
localeShutdownTime: this.autoShutdown.shutdownTime.hour+":"+this._addZero(this.autoShutdown.shutdownTime.minute),
timezoneOffset: this.getUTCTimes().timezoneOffset
utcTagStartupTime: autoShutdownUTC.tagStartup,
utcTagShutdownTime: autoShutdownUTC.tagShutdown,
localeStartupTime: this.autoShutdown.startupTime.hour+":"+this._provisionsService.addZero(this.autoShutdown.startupTime.minute),
localeShutdownTime: this.autoShutdown.shutdownTime.hour+":"+this._provisionsService.addZero(this.autoShutdown.shutdownTime.minute),
localTimezone: autoShutdownUTC.timezone,
timezoneOffset: this.zone
}
} else {
this.sendData.autoShutdownData = {
@@ -159,4 +140,8 @@ export class NewProvisionConfirmComponent implements OnInit, OnDestroy {
this.autoShutdown.is24x7 = $event.checked;
}
getUTCTimes() {
return this._provisionsService.getUTCTimes(this.autoShutdown, this.zone);
}
}

View File

@@ -13,40 +13,34 @@
</div>
<div class="contentbox">
<div *ngIf="provision.statusVms">
VMs status:
Current VMs status:&nbsp;
<span>
<span style="text-transform: capitalize;" class="badge badge-secondary" [ngClass]="{'badge-success': provision.statusVms === 'Running', 'badge-warning': provision.statusVms === 'Stopped' || provision.statusVms === 'Stopping' || provision.statusVms === 'Starting'}">{{provision.statusVms}}</span>
<span *ngIf="provision.statusVms === 'Starting' || provision.statusVms === 'Stopping'" class="spinner-border spinner-border-sm text-warning" role="status">
<span class="sr-only"></span>
</span>
</span>
( Running time:
<span style="font-weight: bold;" *ngIf="provision.runningDays">{{provision.runningDays}}d&nbsp;</span>
<span style="font-weight: bold;" *ngIf="provision.runningHours">{{provision.runningHours}}h&nbsp;</span>
<span style="font-weight: bold;">{{provision.runningMinutes}}m</span>&nbsp;)
</div>
<div *ngIf="provision.statusVms === 'Running' || provision.statusVms === 'Stopping'">
VMs will Auto-Stop in ~
<span style="font-weight: bold;">
<span *ngIf="provision.autoshutdownDays">{{provision.autoshutdownDays}}d&nbsp;</span>
<span *ngIf="provision.autoshutdownHours">{{provision.autoshutdownHours}}h&nbsp;</span>
<span>{{provision.autoshutdownMinutes}}m</span>
</span>
</div>
<div *ngIf="provision.autoShutdown">
<span *ngIf="!provision.autoShutdown.is24x7"><mdb-icon far icon="calendar-alt"></mdb-icon> From <b>{{provision.autoShutdown.localeStartupTime}}h</b> until <b>{{provision.autoShutdown.localeShutdownTime}}h</b></span>
<span *ngIf="provision.autoShutdown.is24x7"><mdb-icon far icon="calendar-alt"></mdb-icon> 24x7</span>
</div>
<div *ngIf="!provision.autoShutdown">
<mdb-icon far icon="calendar-alt"></mdb-icon> 24x7
</div>
<div *ngIf="provision.statusVms">
<mdb-icon far icon="clock"></mdb-icon> Accumulated running time: <span style="font-weight: bold;">{{provision.runningAsDays}}</span>
</div>
<div *ngIf="provision.statusVms === 'Running' || provision.statusVms === 'Stopping'">
<mdb-icon fas icon="stopwatch"></mdb-icon>
<span *ngIf="!provision.autoShutdown || provision.autoShutdown.is24x7"> 24x7 ends in ~</span>
<span *ngIf="provision.autoShutdown && !provision.autoShutdown.is24x7"> Scheduled shutdown in ~</span>
<span style="font-weight: bold;">{{provision.autoshutdownAsDays}}</span>
</div>
<div *ngIf="provision.statusVms === 'Stopped' || provision.statusVms === 'Starting'">
VMs will Auto-Destroy in ~
<span style="font-weight: bold;">
<span *ngIf="provision.autoDestroyDays">{{provision.autoDestroyDays}}d&nbsp;</span>
<span *ngIf="provision.autoDestroyHours">{{provision.autoDestroyHours}}h&nbsp;</span>
<span>{{provision.autoDestroyMinutes}}m</span>
</span>
(Inactive for
<span>
<span *ngIf="provision.inactiveDays">{{provision.inactiveDays}}d&nbsp;</span>
<span *ngIf="provision.inactiveHours">{{provision.inactiveHours}}h&nbsp;</span>
<span>{{provision.inactiveMinutes}}m</span>
</span>)
<mdb-icon fas icon="stopwatch"></mdb-icon> Scenario auto-destroy in ~
<span style="font-weight: bold;">{{provision.autoDestroyAsDays}}</span>
(Inactive for {{provision.inactiveAsDays}})
</div>
<hr>
<div>
@@ -85,8 +79,8 @@
<button style="margin-right: 5px;" mdbTooltip="Start VMs" *ngIf="provision.status === 'provisioned' && provision.statusVms === 'Stopped'" (click)="openConfirmStartModal(provision)" class="lui-button">
<span class="lui-icon lui-icon--run" aria-hidden="true"></span>
</button>
<button style="margin-right: 5px;" mdbTooltip="Extend Running period" *ngIf="provision.status === 'provisioned' && provision.statusVms === 'Running'" (click)="openConfirmExtendModal(provision)" class="lui-button">
<mdb-icon fas icon="plus-square"></mdb-icon>4d
<button style="margin-right: 5px;" mdbTooltip="Edit running schedule" *ngIf="provision._scenarioDoc.isDivvyEnabled && provision.status === 'provisioned'" (click)="openScheduleModal(provision)" class="lui-button">
<mdb-icon far icon="calendar-alt"></mdb-icon>
</button>
<button style="float: right;" mdbTooltip="Destroy provision" *ngIf="!provision.isDestroyed && (provision.status === 'provisioned' || provision.status === 'error') && (!provision.destroy || provision.destroy.status !== 'destroying')" (click)="openConfirmDestroyModal(provision)" class="lui-button">
<span class="lui-icon lui-icon--remove lui-text-danger" aria-hidden="true"></span>

View File

@@ -7,6 +7,8 @@ import { AlertService } from '../services/alert.service';
import { MDBModalService } from 'angular-bootstrap-md';
import { ModalInfoComponent } from '../modals/modalinfo.component';
import { ModalConfirmComponent } from '../modals/confirm.component';
import { ProvisionModalComponent } from '../modals/edit-provision.component';
@Component({
selector: 'app-provisions',
@@ -186,6 +188,22 @@ export class ProvisionsComponent implements OnInit {
});
}
openScheduleModal(provision) {
var modalRef = this.modalService.show(ProvisionModalComponent, {
class: 'modal-lg modal-notify',
containerClass: '',
data: {
provision: provision
}
} );
var sub = modalRef.content.action.subscribe( (data: any) => {
sub.unsubscribe();
console.log("openScheduleModal returns", data);
});
}
showLogs($event, provision, type): void {
$event.preventDefault();
$event.stopPropagation();

View File

@@ -3,7 +3,7 @@ import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { environment } from '../../environments/environment.prod';
import * as moment from 'moment';
import * as moment from 'moment-timezone';
@Injectable({
@@ -12,8 +12,9 @@ import * as moment from 'moment';
export class ProvisionsService {
selectedProv;
RUNNING_PERIOD : number = 4;
STOP_PERIOD : number = 20;
RUNNING_PERIOD : number = 7; //days
STOP_PERIOR_IS_EXTERNAL = 10; //days;
STOP_PERIOD : number = 20; //days
constructor( private httpClient: HttpClient ) { }
@@ -55,6 +56,10 @@ export class ProvisionsService {
return this.httpClient.put(`${environment.apiVersionPath}/provisions/${id}`, patch);
}
updateProvisionUser(id, userId, patch): Observable<any> {
return this.httpClient.put(`${environment.apiVersionPath}/users/${userId}/provisions/${id}`, patch);
}
newDestroy(id, userId) : Observable<any> {
return this.httpClient.post(`${environment.apiVersionPath}/users/${userId}/provisions/${id}/destroy`, null);
}
@@ -105,7 +110,7 @@ export class ProvisionsService {
return this.selectedProv;
}
timeRunning(p) : void{
timeRunning(p) : void {
if (!p.statusVms) {
return;
}
@@ -117,37 +122,97 @@ export class ProvisionsService {
totalRunningTime = totalRunningTime + Math.abs(now.getTime() - runningFromTime);
}
let authShutdownDate = new Date(runningFromTime);
authShutdownDate.setDate(authShutdownDate.getDate()+this.RUNNING_PERIOD);
let autoshutDown = authShutdownDate.getTime() - now.getTime();
let durationAutoShutdown = moment.duration(autoshutDown);
let durationRunningLeft;
if ( !p.isDestroyed && p.statusVms !== 'Stopped' && p.autoShutdown && !p.autoShutdown.is24x7 ) {
let split = p.autoShutdown.localeShutdownTime.split(":");
let runningToDate = new Date();
runningToDate.setHours(split[0], split[1]);
durationRunningLeft = moment.duration(runningToDate.getTime() - now.getTime())
} else {
let runningFromDate = new Date(runningFromTime);
var runningPeriodMils = this.RUNNING_PERIOD*24*60*60*1000; //7 days
runningFromDate.setTime(runningFromDate.getTime()+runningPeriodMils);
durationRunningLeft = moment.duration(runningFromDate.getTime() - now.getTime());
}
let duration = moment.duration(totalRunningTime);
p.runningDays = Math.floor(duration.asDays());
p.runningHours = duration.hours();
p.runningMinutes = duration.minutes();
p.autoshutdownDays = Math.floor(durationAutoShutdown.asDays());
p.autoshutdownHours = durationAutoShutdown.hours();
p.autoshutdownMinutes = durationAutoShutdown.minutes();
let runningDays = Math.floor(duration.asDays());
let runningHours = duration.hours();
let runningAsHours = Math.floor(duration.asHours());
let runningMinutes = duration.minutes();
p.runningAsDays = (runningDays? (runningDays+"d "):"") + (runningHours? runningHours+"h ": "") + runningMinutes+"m";
p.runningAsHours = (runningAsHours? runningAsHours+"h ": "")+runningMinutes+"m";
let autoshutdownDays = Math.abs(Math.floor(durationRunningLeft.asDays()));
let autoshutdownHours = durationRunningLeft.hours();
let autoshutdownAsHours = Math.floor(durationRunningLeft.asHours())
let autoshutdownMinutes = durationRunningLeft.minutes();
p.autoshutdownAsDays = (autoshutdownDays? (autoshutdownDays+"d "):"") + (autoshutdownHours? autoshutdownHours+"h ": "") + autoshutdownMinutes+"m";
p.autoshutdownAsHours = (autoshutdownAsHours? autoshutdownAsHours+"h ": "")+runningMinutes+"m";
if ( (p.statusVms === 'Stopped' || p.statusVms === 'Starting') && !p.isDestroyed ) {
let autoDestroyDate = new Date(p.stoppedFrom);
autoDestroyDate.setDate(autoDestroyDate.getDate() + this.STOP_PERIOD);
if (p.isExternalAccess){
autoDestroyDate.setTime(autoDestroyDate.getTime() + this.STOP_PERIOR_IS_EXTERNAL*24*60*60*1000);
} else {
autoDestroyDate.setTime(autoDestroyDate.getTime() + this.STOP_PERIOD*24*60*60*1000);
}
let autoDestroy = autoDestroyDate.getTime() - now.getTime();
let durationStop = moment.duration(autoDestroy);
p.autoDestroyDays = Math.floor(durationStop.asDays());
p.autoDestroyHours = durationStop.hours();
p.autoDestroyMinutes = durationStop.minutes();
let autoDestroyDays = Math.floor(durationStop.asDays());
let autoDestroyHours = durationStop.hours();
let autoDestroyAsHours = Math.floor(durationStop.asHours())
let autoDestroyMinutes = durationStop.minutes();
p.autoDestroyAsDays = (autoDestroyDays? (autoDestroyDays+"d "):"") + (autoDestroyHours? autoDestroyHours+"h ": "") + autoDestroyMinutes+"m";
p.autoshutdownAsHours = (autoDestroyAsHours? autoDestroyAsHours+"h ": "")+autoDestroyMinutes+"m";
let inactiveDate = new Date(p.stoppedFrom);
let inactive = Math.abs(inactiveDate.getTime() - now.getTime());
let durationInactive = moment.duration(inactive);
p.inactiveDays = Math.floor(durationInactive.asDays());
p.inactiveHours = durationInactive.hours();
p.inactiveMinutes = durationInactive.minutes();
let inactiveDays = Math.floor(durationInactive.asDays());
let inactiveHours = durationInactive.hours();
let inactiveAsHours = Math.floor(durationInactive.asHours());
let inactiveMinutes = durationInactive.minutes();
p.inactiveAsDays = (inactiveDays? (inactiveDays+"d "):"") + (inactiveHours? inactiveHours+"h ": "") + inactiveMinutes+"m";
p.inactiveAsHours = (inactiveAsHours? inactiveAsHours+"h ": "")+inactiveMinutes+"m";
}
}
addZero(i) : any {
if (i < 10) {
i = "0" + i;
}
return i;
}
getUTCTimes(autoShutdown, timezone) : any {
var dateStartup = moment.tz(timezone);
var dateShutdown = moment.tz(timezone);
dateStartup.set({
hour: autoShutdown.startupTime.hour,
minute: autoShutdown.startupTime.minute,
second: 0
});
dateShutdown.set({
hour: autoShutdown.shutdownTime.hour,
minute: autoShutdown.shutdownTime.minute,
second: 0
});
return {
startupTime: dateStartup.utc().format("H:mm"),
shutdownTime: dateShutdown.utc().format("H:mm"),
tagStartup: this.addZero(dateStartup.utc().format("H")) + dateStartup.utc().format("mm"),
tagShutdown: this.addZero(dateShutdown.utc().format("H")) + dateShutdown.utc().format("mm"),
timezoneOffset: moment.tz(timezone).utcOffset(),
timezone: timezone
}
}
}

View File

@@ -52,22 +52,13 @@
<span class="sr-only"></span>
</span>
</span>
<span *ngIf="provision.statusVms">(<span *ngIf="provision.runningDays">{{provision.runningDays}}d&nbsp;</span><span *ngIf="provision.runningHours">{{provision.runningHours}}h&nbsp;</span>{{provision.runningMinutes}}m)</span>
<span *ngIf="provision.statusVms" mdbTooltip="As hours: {{provision.runningAsHours}}">({{provision.runningAsDays}})</span>
<div style="font-size: 10px;" *ngIf="!provision.isDestroyed && provision.statusVms === 'Running'">
<i>VMs Auto-Stop in: </i>
<i>
<span *ngIf="provision.autoshutdownDays">{{provision.autoshutdownDays}}d&nbsp;</span>
<span *ngIf="provision.autoshutdownHours">{{provision.autoshutdownHours}}h&nbsp;</span>
<span>{{provision.autoshutdownMinutes}}m</span>
</i>
<i><mdb-icon fas icon="stopwatch"></mdb-icon> Auto-shutdown in: {{provision.autoshutdownAsDays}}</i>
</div>
<div style="font-size: 10px;" *ngIf="!provision.isDestroyed && provision.statusVms === 'Stopped'">
<i>VMs Auto-Destroy in: </i>
<i>
<span *ngIf="provision.autoDestroyDays">{{provision.autoDestroyDays}}d&nbsp;</span>
<span *ngIf="provision.autoDestroyHours">{{provision.autoDestroyHours}}h&nbsp;</span>
<span>{{provision.autoDestroyMinutes}}m</span>
</i>
<i><mdb-icon fas icon="stopwatch"></mdb-icon> Auto-destroy in: </i>
<i>{{provision.autoDestroyAsDays}}</i>
</div>
</td>
<td (click)="openInfoModal(provision._id)" *ngIf="pagingIsDisabled || (i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex)">

View File

@@ -42,3 +42,12 @@ body {
//@import "assets/album.css";
/* Importing Bootstrap SCSS file. */
@import '~bootstrap/scss/bootstrap';
.btn.btn-link {
padding: 0 !important;
margin: 0 !important;
}
.ngb-tp-meridian .btn {
padding: 5px 10px;
}

View File

@@ -7420,6 +7420,13 @@ moment-timezone@^0.5.25:
dependencies:
moment ">= 2.9.0"
moment-timezone@^0.5.31:
version "0.5.31"
resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.31.tgz#9c40d8c5026f0c7ab46eda3d63e49c155148de05"
integrity sha512-+GgHNg8xRhMXfEbv81iDtrVeTcWt0kWmTEY1XQK14dICTXnWJnT0dxdlPspwqF3keKMVPXwayEsk1DI0AA/jdA==
dependencies:
moment ">= 2.9.0"
"moment@>= 2.9.0", moment@^2.10.2, moment@^2.10.6, moment@^2.18.1, moment@^2.21.0, moment@^2.22.2:
version "2.24.0"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"