This commit is contained in:
Manuel Romero
2020-07-27 13:41:50 +02:00
parent e45445f919
commit 885f2986ef
13 changed files with 201 additions and 60 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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

@@ -208,6 +208,71 @@ router.post('/:userId/provisions', passport.ensureAuthenticatedAndIsMe, async (r
}
});
/**
* @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);
}
});
/**
* @swagger
* /users/{userId}/provisions/{id}:

View File

@@ -7,14 +7,6 @@
<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>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 *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>
@@ -22,17 +14,17 @@
<div>
<b>Timezone:</b> {{getUTCTimes().timezone}} <i>(times relative to this timezone)</i>
</div>
<div class="md-form row" *ngIf="!autoShutdownData.is24x7">
<div class="md-form row" [ngClass]="{'disabled': autoShutdownData.is24x7}">
<div class="col-sm-6">
<label>Startup time:</label>
<ngb-timepicker style="font-size: 10px;" [(ngModel)]="autoShutdownData.startupTime" [meridian]="true" [minuteStep]="15"></ngb-timepicker>
<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"></ngb-timepicker>
<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>
@@ -42,6 +34,14 @@
<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

@@ -12,4 +12,8 @@ label {
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',
@@ -49,6 +50,7 @@ export class ProvisionModalComponent implements OnInit, OnDestroy {
"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;
}
@@ -59,8 +61,23 @@ 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();
});
@@ -71,7 +88,8 @@ export class ProvisionModalComponent implements OnInit, OnDestroy {
}
getUTCTimes() {
return this._provisionsService.getUTCTimes(this.autoShutdownData, this.provision.autoShutdown.localTimezone);
const zone = this.provision.autoShutdown? this.provision.autoShutdown.localTimezone : moment.tz.guess(true);
return this._provisionsService.getUTCTimes(this.autoShutdownData, zone);
}
}

View File

@@ -22,17 +22,17 @@
<div>
<b>Local timezone:</b> {{zone}} <i>(times relative to this timezone)</i>
</div>
<div class="md-form row" *ngIf="!autoShutdown.is24x7">
<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"></ngb-timepicker>
<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"></ngb-timepicker>
<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>

View File

@@ -16,4 +16,8 @@ label {
padding-left: 20px;
padding-top: 5px;
font-size: 12px
}
.disabled {
opacity: 0.2;
}

View File

@@ -110,7 +110,7 @@ export class NewProvisionConfirmComponent implements OnInit, OnDestroy {
}
if ( this.scenario.isDivvyEnabled ) {
if ( !this.autoShutdown.is24x7 ) {
let autoShutdownUTC = this._provisionsService.getUTCTimes(this.autoShutdown, this);
let autoShutdownUTC = this._provisionsService.getUTCTimes(this.autoShutdown, this.zone);
this.sendData.autoShutdownData = {
is24x7: false,
utcTagStartupTime: autoShutdownUTC.tagStartup,

View File

@@ -32,7 +32,9 @@
<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> VMs auto-shutdown in ~
<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'">
@@ -77,7 +79,7 @@
<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="Edit running schedule" *ngIf="provision.status === 'provisioned'" (click)="openScheduleModal(provision)" class="lui-button">
<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">

View File

@@ -12,7 +12,8 @@ import * as moment from 'moment-timezone';
export class ProvisionsService {
selectedProv;
RUNNING_PERIOD : number = 4; //days
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);
}
@@ -117,15 +122,16 @@ export class ProvisionsService {
totalRunningTime = totalRunningTime + Math.abs(now.getTime() - runningFromTime);
}
let runningFromDate = new Date(runningFromTime);
var runningPeriodMils = this.RUNNING_PERIOD*24*60*60*1000;
let durationRunningLeft;
if ( !p.isDestroyed && p.statusVms !== 'Stopped' && p.autoShutdown && !p.autoShutdown.is24x7 ) {
let split = p.autoShutdown.localeShutdownTime.split(":");
let runningToDate = new Date(runningFromTime);
let runningToDate = new Date();
runningToDate.setHours(split[0], split[1]);
durationRunningLeft = moment.duration(runningToDate.getTime()- now.getTime())
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());
}
@@ -140,7 +146,7 @@ export class ProvisionsService {
p.runningAsDays = (runningDays? (runningDays+"d "):"") + (runningHours? runningHours+"h ": "") + runningMinutes+"m";
p.runningAsHours = (runningAsHours? runningAsHours+"h ": "")+runningMinutes+"m";
let autoshutdownDays = Math.floor(durationRunningLeft.asDays());
let autoshutdownDays = Math.abs(Math.floor(durationRunningLeft.asDays()));
let autoshutdownHours = durationRunningLeft.hours();
let autoshutdownAsHours = Math.floor(durationRunningLeft.asHours())
let autoshutdownMinutes = durationRunningLeft.minutes();
@@ -150,7 +156,11 @@ export class ProvisionsService {
if ( (p.statusVms === 'Stopped' || p.statusVms === 'Starting') && !p.isDestroyed ) {
let autoDestroyDate = new Date(p.stoppedFrom);
autoDestroyDate.setTime(autoDestroyDate.getTime() + this.STOP_PERIOD*24*60*60*1000);
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);
let autoDestroyDays = Math.floor(durationStop.asDays());