Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6283dddb22 | ||
|
|
c3b8bd119f | ||
|
|
2e31fe279c | ||
|
|
899fd2fda7 | ||
|
|
dc116c9f7f | ||
|
|
455cc3e75d | ||
|
|
16f574f744 | ||
|
|
1486e2fd21 | ||
|
|
c0349f440e | ||
|
|
1a99589c8b | ||
|
|
b54a590f51 | ||
|
|
eafd78f91c | ||
|
|
243c65c64b | ||
|
|
672ba4194a | ||
|
|
9f539bd96a | ||
|
|
55b9f0c032 | ||
|
|
00dcb33872 | ||
|
|
429838cfc5 | ||
|
|
3a2d6eb0b6 | ||
|
|
4290406c19 | ||
|
|
9057f58342 | ||
|
|
6d03a4f6ed | ||
|
|
5d914c890f | ||
|
|
b1286a1b2b | ||
|
|
a5b5f2a8ca | ||
|
|
8bb7856102 | ||
|
|
8280b32872 | ||
|
|
39ab3fe6dd | ||
|
|
fbe30b740c | ||
|
|
ab5ab80765 | ||
|
|
180a20c8d6 | ||
|
|
91d7017b03 | ||
|
|
5b6cb73b09 | ||
|
|
2003a039f2 | ||
|
|
96b1302520 | ||
|
|
891acb714e | ||
|
|
7cf4e7b5e7 | ||
|
|
0c78992958 | ||
|
|
ed44eb7fef | ||
|
|
8ed6612686 | ||
|
|
793b8f8739 | ||
|
|
a9b3b57414 |
4
dist/qmi-cloud/index.html
vendored
4
dist/qmi-cloud/index.html
vendored
@@ -6,8 +6,8 @@
|
|||||||
<base href="/">
|
<base href="/">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="icon" href="assets/favicon.ico">
|
<link rel="icon" href="assets/favicon.ico">
|
||||||
<link rel="stylesheet" href="styles.d596c43fc1f81eecfae1.css"></head>
|
<link rel="stylesheet" href="styles.921aafa95031aeb74181.css"></head>
|
||||||
<body>
|
<body>
|
||||||
<app-root></app-root>
|
<app-root></app-root>
|
||||||
<script src="runtime.689ba4fd6cadb82c1ac2.js" defer></script><script src="polyfills-es5.2f1b30b563fe6f309b2d.js" nomodule defer></script><script src="polyfills.60117177d3b4f4827ace.js" defer></script><script src="scripts.73c34722d75b092f2620.js" defer></script><script src="main.ece894ac50d4edc475b5.js" defer></script></body>
|
<script src="runtime.689ba4fd6cadb82c1ac2.js" defer></script><script src="polyfills-es5.feb8e3dfdd8e1cace860.js" nomodule defer></script><script src="polyfills.60117177d3b4f4827ace.js" defer></script><script src="scripts.73c34722d75b092f2620.js" defer></script><script src="main.eca58c33a1ad840ee769.js" defer></script></body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "qmi-cloud-app",
|
"name": "qmi-cloud-app",
|
||||||
"version": "1.3.0",
|
"version": "1.3.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node -r esm server/server.js",
|
"start": "node -r esm server/server.js",
|
||||||
"start:dev": "nodemon -r esm server/server.js",
|
"start:dev": "nodemon -r esm server/server.js",
|
||||||
|
|||||||
@@ -62,7 +62,8 @@ async function init(type) {
|
|||||||
"isDestroyed":false,
|
"isDestroyed":false,
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"statusVms": "Stopped",
|
"statusVms": "Stopped",
|
||||||
"vmImage": {"$exists": true}
|
"vmImage": {"$exists": true},
|
||||||
|
"vmImage.vm1": { "$exists": true }
|
||||||
};
|
};
|
||||||
if ( type === "warning" ) {
|
if ( type === "warning" ) {
|
||||||
filter.pendingNextAction = {$ne: "destroy"};
|
filter.pendingNextAction = {$ne: "destroy"};
|
||||||
|
|||||||
@@ -62,7 +62,8 @@ async function init(type) {
|
|||||||
"isDestroyed": false,
|
"isDestroyed": false,
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"statusVms": "Running",
|
"statusVms": "Running",
|
||||||
"vmImage": { "$exists": true }
|
"vmImage": { "$exists": true },
|
||||||
|
"vmImage.vm1": { "$exists": true }
|
||||||
};
|
};
|
||||||
if ( type === "warning" ) {
|
if ( type === "warning" ) {
|
||||||
filter.pendingNextAction = { $ne: "stopVms" };
|
filter.pendingNextAction = { $ne: "stopVms" };
|
||||||
|
|||||||
@@ -20,14 +20,17 @@ function _getRegion(provision) {
|
|||||||
return region;
|
return region;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deallocate(provision, isSendEmailAfter) {
|
function deallocate(provision, isSendEmailAfter) {
|
||||||
|
|
||||||
let rgName = _getRgName(provision);
|
let rgName = _getRgName(provision);
|
||||||
let region = _getRegion(provision);
|
let region = _getRegion(provision);
|
||||||
|
|
||||||
console.log("AWSCLI# Stopping EC2s for resource group: "+rgName);
|
console.log("AWSCLI# Stopping EC2s for resource group: "+rgName);
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
var ec2 = new AWS.EC2({apiVersion: '2016-11-15', region: region});
|
var ec2 = new AWS.EC2({apiVersion: '2016-11-15', region: region});
|
||||||
|
|
||||||
var params = {
|
var params = {
|
||||||
DryRun: false,
|
DryRun: false,
|
||||||
Filters: [
|
Filters: [
|
||||||
@@ -41,34 +44,45 @@ async function deallocate(provision, isSendEmailAfter) {
|
|||||||
ec2.describeInstances(params, async function (err, data) {
|
ec2.describeInstances(params, async function (err, data) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log("AWSCLI# ERROR describing EC2s: "+rgName, err.stack);
|
console.log("AWSCLI# ERROR describing EC2s: "+rgName, err.stack);
|
||||||
} else {
|
await db.provision.update(provision._id.toString(), {"statusVms": "Running" });
|
||||||
|
reject(err);
|
||||||
|
} else if (data && data.Reservations && data.Reservations.length) {
|
||||||
|
|
||||||
var ec2Ids = data.Reservations[0].Instances.map(ec2=> ec2.InstanceId);
|
var ec2Ids = data.Reservations[0].Instances.map(ec2=> ec2.InstanceId);
|
||||||
console.log("AWSCLI# Stopping EC2s ids", ec2Ids);
|
console.log("AWSCLI# Stopping Ec2s ids...", ec2Ids);
|
||||||
params = {
|
params = {
|
||||||
DryRun: false,
|
DryRun: false,
|
||||||
InstanceIds: ec2Ids
|
InstanceIds: ec2Ids
|
||||||
};
|
};
|
||||||
|
|
||||||
ec2.stopInstances(params, async function(err, data) {
|
ec2.stopInstances(params, async function(err1, data) {
|
||||||
if (err) {
|
if (err1) {
|
||||||
console.log("AWSCLI# ERROR stopping EC2s: "+rgName, err.stack);
|
console.log("AWSCLI# ERROR stopping EC2s: "+rgName, err1.stack);
|
||||||
|
await db.provision.update(provision._id.toString(), {"statusVms": "Running" });
|
||||||
|
reject(err1);
|
||||||
} else {
|
} else {
|
||||||
console.log("AWSCLI# EC2s stopped!");
|
console.log("AWSCLI# Ec2s stopped!");
|
||||||
utils.afterStopVms( provision, isSendEmailAfter );
|
await utils.afterStopVms( provision, isSendEmailAfter );
|
||||||
|
resolve(data);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
console.log("AWSCLI# No Ec2 Instances found: "+rgName);
|
||||||
|
resolve(data);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function start(provision) {
|
function start(provision) {
|
||||||
|
|
||||||
let rgName = _getRgName(provision);
|
let rgName = _getRgName(provision);
|
||||||
let region = _getRegion(provision);
|
let region = _getRegion(provision);
|
||||||
|
|
||||||
console.log("AWSCLI# Stopping EC2s for resource group: "+rgName);
|
console.log("AWSCLI# Stopping EC2s for resource group: "+rgName);
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
var ec2 = new AWS.EC2({apiVersion: '2016-11-15', region: region});
|
var ec2 = new AWS.EC2({apiVersion: '2016-11-15', region: region});
|
||||||
var params = {
|
var params = {
|
||||||
DryRun: false,
|
DryRun: false,
|
||||||
@@ -83,25 +97,33 @@ async function start(provision) {
|
|||||||
ec2.describeInstances(params, async function (err, data) {
|
ec2.describeInstances(params, async function (err, data) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log("AWSCLI# ERROR describing EC2s: "+rgName, err.stack);
|
console.log("AWSCLI# ERROR describing EC2s: "+rgName, err.stack);
|
||||||
} else {
|
await db.provision.update(provision._id.toString(), {"statusVms": "Stopped" });
|
||||||
|
reject(err);
|
||||||
|
} else if (data && data.Reservations && data.Reservations.length) {
|
||||||
var ec2Ids = data.Reservations[0].Instances.map(ec2=> ec2.InstanceId);
|
var ec2Ids = data.Reservations[0].Instances.map(ec2=> ec2.InstanceId);
|
||||||
console.log("AWSCLI# Starting EC2s ids", ec2Ids);
|
console.log("AWSCLI# Starting Ec2s ids...", ec2Ids);
|
||||||
params = {
|
params = {
|
||||||
DryRun: false,
|
DryRun: false,
|
||||||
InstanceIds: ec2Ids
|
InstanceIds: ec2Ids
|
||||||
};
|
};
|
||||||
|
|
||||||
ec2.startInstances(params, async function(err, data) {
|
ec2.startInstances(params, async function(err1, data) {
|
||||||
if (err) {
|
if (err1) {
|
||||||
console.log("AWSCLI# ERROR starting EC2s: "+rgName, err.stack);
|
console.log("AWSCLI# ERROR starting EC2s: "+rgName, err1.stack);
|
||||||
} else if (data) {
|
await db.provision.update(provision._id.toString(), {"statusVms": "Stopped" });
|
||||||
console.log("AWSCLI# EC2s started!");
|
reject(err1);
|
||||||
utils.afterStartVms( provision );
|
} else {
|
||||||
|
console.log("AWSCLI# Ec2s started!");
|
||||||
|
await utils.afterStartVms( provision );
|
||||||
|
resolve(data);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
console.log("AWSCLI# No Ec2 Instances found: "+rgName);
|
||||||
|
resolve(data);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.deallocate = deallocate;
|
module.exports.deallocate = deallocate;
|
||||||
|
|||||||
@@ -47,13 +47,13 @@ async function deallocate(provision, isSendEmailAfter ) {
|
|||||||
let rgName = _getRgName(provision);
|
let rgName = _getRgName(provision);
|
||||||
console.log("AzureCLI# Deallocating VMs for resource group: "+rgName);
|
console.log("AzureCLI# Deallocating VMs for resource group: "+rgName);
|
||||||
|
|
||||||
|
try {
|
||||||
var computeClient = await _getClient(provision.scenario);
|
var computeClient = await _getClient(provision.scenario);
|
||||||
let finalResult = await computeClient.virtualMachines.list(rgName);
|
let finalResult = await computeClient.virtualMachines.list(rgName);
|
||||||
if ( finalResult && finalResult.length > 0 ) {
|
if ( finalResult && finalResult.length > 0 ) {
|
||||||
db.provision.update(provision._id, {"statusVms": "Stopping"});
|
db.provision.update(provision._id, {"statusVms": "Stopping"});
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
await asyncForEach(finalResult, async function(vm) {
|
await asyncForEach(finalResult, async function(vm) {
|
||||||
await computeClient.virtualMachines.deallocate(rgName, vm.name);
|
await computeClient.virtualMachines.deallocate(rgName, vm.name);
|
||||||
});
|
});
|
||||||
@@ -72,6 +72,8 @@ async function start(provision){
|
|||||||
let rgName = _getRgName(provision);
|
let rgName = _getRgName(provision);
|
||||||
console.log("AzureCLI# Starting VMs for resource group: "+rgName);
|
console.log("AzureCLI# Starting VMs for resource group: "+rgName);
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
var computeClient = await _getClient(provision.scenario);
|
var computeClient = await _getClient(provision.scenario);
|
||||||
let finalResult = await computeClient.virtualMachines.list(rgName);
|
let finalResult = await computeClient.virtualMachines.list(rgName);
|
||||||
|
|
||||||
@@ -79,12 +81,12 @@ async function start(provision){
|
|||||||
db.provision.update(provision._id, {"statusVms": "Starting"});
|
db.provision.update(provision._id, {"statusVms": "Starting"});
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
await asyncForEach(finalResult, async function(vm) {
|
await asyncForEach(finalResult, async function(vm) {
|
||||||
await computeClient.virtualMachines.start(rgName, vm.name);
|
await computeClient.virtualMachines.start(rgName, vm.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
await utils.afterStartVms( provision );
|
await utils.afterStartVms( provision );
|
||||||
|
console.log("AzureCLI# All VMs RUNNING for resource group: "+rgName);
|
||||||
|
|
||||||
} catch ( error ) {
|
} catch ( error ) {
|
||||||
console.log("AzureCLI# ERROR starting VMs: "+rgName, error);
|
console.log("AzureCLI# ERROR starting VMs: "+rgName, error);
|
||||||
|
|||||||
@@ -4,33 +4,42 @@ const db = require("./mongo");
|
|||||||
|
|
||||||
|
|
||||||
async function deallocate(provId, isSendEmailAfter ) {
|
async function deallocate(provId, isSendEmailAfter ) {
|
||||||
|
try {
|
||||||
let provision = await db.provision.getById(provId);
|
let provision = await db.provision.getById(provId);
|
||||||
if ( !provision ) return;
|
if ( !provision ) return;
|
||||||
|
|
||||||
azurecli.deallocate(provision, isSendEmailAfter);
|
|
||||||
|
|
||||||
if (provision.scenario === 'azqmi-fort'){
|
if (provision.scenario === 'azqmi-fort'){
|
||||||
awscli.stop(provision, isSendEmailAfter);
|
return awscli.deallocate(provision, isSendEmailAfter);
|
||||||
} else {
|
} else {
|
||||||
azurecli.deallocate(provision, isSendEmailAfter);
|
return azurecli.deallocate(provision, isSendEmailAfter);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("CLI# ERROR stopping VMs", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function start(provId){
|
async function start(provId){
|
||||||
|
|
||||||
|
try {
|
||||||
let provision = await db.provision.getById(provId);
|
let provision = await db.provision.getById(provId);
|
||||||
if ( !provision ) return;
|
if ( !provision ) return;
|
||||||
|
|
||||||
if (provision.scenario === 'azqmi-fort'){
|
if (provision.scenario === 'azqmi-fort'){
|
||||||
awscli.start(provision);
|
return awscli.start(provision);
|
||||||
} else {
|
} else {
|
||||||
azurecli.start(provision);
|
return azurecli.start(provision);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("CLI# ERROR starting VMs", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateVmsTags(provId, tagsEdit) {
|
async function updateVmsTags(provId, tagsEdit) {
|
||||||
azurecli.updateVmsTags(provId, tagsEdit);
|
try {
|
||||||
|
return azurecli.updateVmsTags(provId, tagsEdit);
|
||||||
|
} catch (err) {
|
||||||
|
console.log("CLI# ERROR updateVmsTags", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.deallocate = deallocate;
|
module.exports.deallocate = deallocate;
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ const schema = new mongoose.Schema({
|
|||||||
user: {
|
user: {
|
||||||
type: mongoose.Types.ObjectId, ref: 'User'
|
type: mongoose.Types.ObjectId, ref: 'User'
|
||||||
},
|
},
|
||||||
|
description: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
created: {
|
created: {
|
||||||
type: Date,
|
type: Date,
|
||||||
default: Date.now,
|
default: Date.now,
|
||||||
|
|||||||
@@ -102,6 +102,10 @@ const provisionSchema = new mongoose.Schema({
|
|||||||
},
|
},
|
||||||
version: {
|
version: {
|
||||||
type: Number
|
type: Number
|
||||||
|
},
|
||||||
|
parent: {
|
||||||
|
type: mongoose.Types.ObjectId,
|
||||||
|
ref: 'Provision'
|
||||||
}
|
}
|
||||||
},{
|
},{
|
||||||
toObject: {virtuals:true},
|
toObject: {virtuals:true},
|
||||||
|
|||||||
36
qmi-cloud-common/models/SharedProvision.js
Normal file
36
qmi-cloud-common/models/SharedProvision.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
const mongoose = require('mongoose')
|
||||||
|
mongoose.set('useFindAndModify', false);
|
||||||
|
//mongoose.set('debug', true)
|
||||||
|
const schema = new mongoose.Schema({
|
||||||
|
created: {
|
||||||
|
type: Date,
|
||||||
|
default: Date.now,
|
||||||
|
index : true
|
||||||
|
},
|
||||||
|
updated: {
|
||||||
|
type: Date,
|
||||||
|
default: Date.now
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
type: mongoose.Types.ObjectId,
|
||||||
|
ref: 'User',
|
||||||
|
index: true
|
||||||
|
},
|
||||||
|
provision: {
|
||||||
|
type: mongoose.Types.ObjectId,
|
||||||
|
ref: 'Provision',
|
||||||
|
index: true
|
||||||
|
},
|
||||||
|
sharedWithUser: {
|
||||||
|
type: mongoose.Types.ObjectId,
|
||||||
|
ref: 'User',
|
||||||
|
index: true
|
||||||
|
},
|
||||||
|
canManage: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = mongoose.model('sharedProvision', schema);
|
||||||
@@ -43,6 +43,7 @@ const ApiKey = require('./models/ApiKey');
|
|||||||
const Notification = require('./models/Notification');
|
const Notification = require('./models/Notification');
|
||||||
const Subscription = require('./models/Subscription');
|
const Subscription = require('./models/Subscription');
|
||||||
const Event = require('./models/Event');
|
const Event = require('./models/Event');
|
||||||
|
const SharedProvision = require('./models/SharedProvision');
|
||||||
|
|
||||||
|
|
||||||
const getNewCountExtend = function(provision) {
|
const getNewCountExtend = function(provision) {
|
||||||
@@ -94,6 +95,9 @@ const getPage = async ( model, filter, page, populates, select ) => {
|
|||||||
if ( model === Provision ) {
|
if ( model === Provision ) {
|
||||||
exec = exec.populate({ path: 'user', select: 'displayName upn'}).populate({path:'destroy', select: "-user -jobId"}).populate({path:'_scenarioDoc', select: "-availableProductVersions -updated -created"}).populate({path: "schedule"}).populate('deployOpts');
|
exec = exec.populate({ path: 'user', select: 'displayName upn'}).populate({path:'destroy', select: "-user -jobId"}).populate({path:'_scenarioDoc', select: "-availableProductVersions -updated -created"}).populate({path: "schedule"}).populate('deployOpts');
|
||||||
}
|
}
|
||||||
|
if ( model === SharedProvision ) {
|
||||||
|
exec = exec.populate('provision').populate({path: 'sharedWithUser', select: 'displayName upn'}).populate({path: 'user', select: 'displayName upn'});
|
||||||
|
}
|
||||||
|
|
||||||
if ( model === ApiKey ) {
|
if ( model === ApiKey ) {
|
||||||
exec = exec.populate('user');
|
exec = exec.populate('user');
|
||||||
@@ -161,6 +165,18 @@ const get = async (model, filter, select, skip, limit, populates, reply) => {
|
|||||||
exec = exec.populate({ path: 'user', select: 'displayName upn'}).populate({path:'destroy', select: "-user -jobId"}).populate({path:'_scenarioDoc', select: "-availableProductVersions -updated -created"}).populate({path: "schedule"}).populate('deployOpts');
|
exec = exec.populate({ path: 'user', select: 'displayName upn'}).populate({path:'destroy', select: "-user -jobId"}).populate({path:'_scenarioDoc', select: "-availableProductVersions -updated -created"}).populate({path: "schedule"}).populate('deployOpts');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( model === SharedProvision ) {
|
||||||
|
exec = exec.populate({path:'provision', populate: [{
|
||||||
|
path: 'schedule',
|
||||||
|
},{
|
||||||
|
path: '_scenarioDoc',
|
||||||
|
select: "-availableProductVersions -updated -created"
|
||||||
|
},{
|
||||||
|
path: 'destroy',
|
||||||
|
select: "-user -jobId"
|
||||||
|
}]}).populate({path: 'sharedWithUser', select: 'displayName upn'}).populate({path: 'user', select: 'displayName upn'});
|
||||||
|
}
|
||||||
|
|
||||||
if ( model === ApiKey ) {
|
if ( model === ApiKey ) {
|
||||||
exec = exec.populate('user');
|
exec = exec.populate('user');
|
||||||
}
|
}
|
||||||
@@ -193,6 +209,9 @@ const getById = async (model, id, reply) => {
|
|||||||
if ( model === Provision ) {
|
if ( model === Provision ) {
|
||||||
exec = exec.populate('user').populate('destroy').populate('_scenarioDoc').populate("schedule").populate('deployOpts');
|
exec = exec.populate('user').populate('destroy').populate('_scenarioDoc').populate("schedule").populate('deployOpts');
|
||||||
}
|
}
|
||||||
|
if ( model === SharedProvision ) {
|
||||||
|
exec = exec.populate('provision').populate({path: 'sharedWithUser', select: 'displayName upn'}).populate({path: 'user', select: 'displayName upn'});
|
||||||
|
}
|
||||||
if ( model === ApiKey ) {
|
if ( model === ApiKey ) {
|
||||||
exec = exec.populate('user');
|
exec = exec.populate('user');
|
||||||
}
|
}
|
||||||
@@ -212,6 +231,9 @@ const getOne = async (model, filter, reply) => {
|
|||||||
if ( model === Provision ) {
|
if ( model === Provision ) {
|
||||||
exec = exec.populate('user').populate('destroy').populate('_scenarioDoc').populate("schedule").populate('deployOpts');
|
exec = exec.populate('user').populate('destroy').populate('_scenarioDoc').populate("schedule").populate('deployOpts');
|
||||||
}
|
}
|
||||||
|
if ( model === SharedProvision ) {
|
||||||
|
exec = exec.populate('provision').populate({path: 'sharedWithUser', select: 'displayName upn'}).populate({path: 'user', select: 'displayName upn'});
|
||||||
|
}
|
||||||
if ( model === ApiKey ) {
|
if ( model === ApiKey ) {
|
||||||
exec = exec.populate('user');
|
exec = exec.populate('user');
|
||||||
}
|
}
|
||||||
@@ -243,6 +265,9 @@ const update = async (model, id, body, reply) => {
|
|||||||
if ( model === Provision ) {
|
if ( model === Provision ) {
|
||||||
exec = exec.populate('user').populate('destroy').populate('_scenarioDoc').populate("schedule").populate('deployOpts');
|
exec = exec.populate('user').populate('destroy').populate('_scenarioDoc').populate("schedule").populate('deployOpts');
|
||||||
}
|
}
|
||||||
|
if ( model === SharedProvision ) {
|
||||||
|
exec = exec.populate('provision').populate({path: 'sharedWithUser', select: 'displayName upn'}).populate({path: 'user', select: 'displayName upn'});
|
||||||
|
}
|
||||||
if ( model === ApiKey ) {
|
if ( model === ApiKey ) {
|
||||||
exec = exec.populate('user');
|
exec = exec.populate('user');
|
||||||
}
|
}
|
||||||
@@ -267,6 +292,9 @@ const updateMany = async (model, filter, body, reply) => {
|
|||||||
if ( model === Provision ) {
|
if ( model === Provision ) {
|
||||||
exec = exec.populate('user').populate('destroy').populate('_scenarioDoc').populate("schedule").populate('deployOpts');
|
exec = exec.populate('user').populate('destroy').populate('_scenarioDoc').populate("schedule").populate('deployOpts');
|
||||||
}
|
}
|
||||||
|
if ( model === SharedProvision ) {
|
||||||
|
exec = exec.populate('provision').populate({path: 'sharedWithUser', select: 'displayName upn'}).populate({path: 'user', select: 'displayName upn'});
|
||||||
|
}
|
||||||
if ( model === ApiKey ) {
|
if ( model === ApiKey ) {
|
||||||
exec = exec.populate('user');
|
exec = exec.populate('user');
|
||||||
}
|
}
|
||||||
@@ -283,8 +311,15 @@ const updateMany = async (model, filter, body, reply) => {
|
|||||||
|
|
||||||
const del = async (model, id, reply) => {
|
const del = async (model, id, reply) => {
|
||||||
try {
|
try {
|
||||||
const entity = await model.findByIdAndRemove(id)
|
return await model.findByIdAndRemove(id);
|
||||||
return entity;
|
} catch (err) {
|
||||||
|
throw boom.boomify(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const delMany = async(model, filter, reply) => {
|
||||||
|
try {
|
||||||
|
return await model.deleteMany(filter);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw boom.boomify(err)
|
throw boom.boomify(err)
|
||||||
}
|
}
|
||||||
@@ -327,6 +362,9 @@ function _m(model) {
|
|||||||
},
|
},
|
||||||
count: async (filter, reply) => {
|
count: async (filter, reply) => {
|
||||||
return count(model, filter, reply);
|
return count(model, filter, reply);
|
||||||
|
},
|
||||||
|
delMany: async(filter, reply) => {
|
||||||
|
return delMany(model, filter, reply);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -343,6 +381,7 @@ module.exports = {
|
|||||||
subscription: _m(Subscription),
|
subscription: _m(Subscription),
|
||||||
event: _m(Event),
|
event: _m(Event),
|
||||||
user: _m(User),
|
user: _m(User),
|
||||||
|
sharedProvision: _m(SharedProvision),
|
||||||
utils: {
|
utils: {
|
||||||
getNewTimeRunning: getNewTimeRunning,
|
getNewTimeRunning: getNewTimeRunning,
|
||||||
getNewCountExtend: getNewCountExtend
|
getNewCountExtend: getNewCountExtend
|
||||||
@@ -358,7 +397,8 @@ module.exports = {
|
|||||||
Notification: Notification,
|
Notification: Notification,
|
||||||
ApiKey: ApiKey,
|
ApiKey: ApiKey,
|
||||||
Subscription: Subscription,
|
Subscription: Subscription,
|
||||||
Event: Event
|
Event: Event,
|
||||||
|
SharedProvision: SharedProvision
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,6 @@ async function afterStartVms( provision ) {
|
|||||||
msg += `TotalTimeRunning so far: ${provision.timeRunning} mins`;
|
msg += `TotalTimeRunning so far: ${provision.timeRunning} mins`;
|
||||||
|
|
||||||
await db.provision.update(provision._id.toString(), patch);
|
await db.provision.update(provision._id.toString(), patch);
|
||||||
console.log("AzureCLI# All VMs RUNNING for resource group: "+rgName);
|
|
||||||
|
|
||||||
db.event.add({ user: provision.user._id, provision: provision._id, type: 'vms.start-ondemand', message: msg });
|
db.event.add({ user: provision.user._id, provision: provision._id, type: 'vms.start-ondemand', message: msg });
|
||||||
|
|
||||||
|
|||||||
@@ -15,4 +15,5 @@ FROM node:15.12.0-alpine AS production
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=sources /app ./
|
COPY --from=sources /app ./
|
||||||
|
|
||||||
|
|
||||||
CMD ["node", "-r", "esm", "index.js"]
|
CMD ["node", "-r", "esm", "index.js"]
|
||||||
@@ -43,7 +43,7 @@ module.exports = async function(job) {
|
|||||||
const dateNow = new Date();
|
const dateNow = new Date();
|
||||||
let patch = {
|
let patch = {
|
||||||
"status": "provisioning",
|
"status": "provisioning",
|
||||||
"statusVms": prov.vmImage? "Running" : "N/A",
|
"statusVms": (prov.vmImage && prov.vmImage.vm1)? "Running" : "N/A",
|
||||||
"runningFrom": dateNow,
|
"runningFrom": dateNow,
|
||||||
"runningTime": 0,
|
"runningTime": 0,
|
||||||
"countExtend": 0
|
"countExtend": 0
|
||||||
|
|||||||
@@ -81,6 +81,11 @@ function _buildVarsExec( exec, provision, scenario ) {
|
|||||||
exec.push(`user_email=${provision.user.upn}`);
|
exec.push(`user_email=${provision.user.upn}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( provision.scenario.indexOf('azqmi-synapse') !== -1 || provision.scenario.indexOf('azqmi-qdi') !== -1 ) {
|
||||||
|
exec.push('-var');
|
||||||
|
exec.push(`user_oid=${provision.user.oid}`);
|
||||||
|
}
|
||||||
|
|
||||||
if (!provision.vmImage) {
|
if (!provision.vmImage) {
|
||||||
//Old way
|
//Old way
|
||||||
if ( provision.vmType ) {
|
if ( provision.vmType ) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "qmi-cloud-worker",
|
"name": "qmi-cloud-worker",
|
||||||
"version": "1.3.0",
|
"version": "1.3.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node -r esm index.js",
|
"start": "node -r esm index.js",
|
||||||
"start:dev": "nodemon -r esm index.js",
|
"start:dev": "nodemon -r esm index.js",
|
||||||
|
|||||||
@@ -44,6 +44,12 @@ module.exports = async function(job){
|
|||||||
return { destroy: update, provision: update2 };
|
return { destroy: update, provision: update2 };
|
||||||
}).then(async function(res) {
|
}).then(async function(res) {
|
||||||
console.log(`ProcessorDestroy# Provision destroyed!` );
|
console.log(`ProcessorDestroy# Provision destroyed!` );
|
||||||
|
if ( res.provision.scenario === "azqmi-qdi" ) {
|
||||||
|
let tempApiKey = await db.apiKey.getOne({"description": res.provision._id});
|
||||||
|
if (tempApiKey && tempApiKey._id){
|
||||||
|
db.apiKey.del(tempApiKey._id);
|
||||||
|
}
|
||||||
|
}
|
||||||
db.event.add({ user: provMongo.user._id, provision: provMongo._id, type: 'provision.destroy-finished' });
|
db.event.add({ user: provMongo.user._id, provision: provMongo._id, type: 'provision.destroy-finished' });
|
||||||
return Promise.resolve({"success": true, job: res});
|
return Promise.resolve({"success": true, job: res});
|
||||||
}).catch(function(err) {
|
}).catch(function(err) {
|
||||||
|
|||||||
@@ -12,6 +12,15 @@ const passport = require('../passport');
|
|||||||
* summary: Get all API keys
|
* summary: Get all API keys
|
||||||
* tags:
|
* tags:
|
||||||
* - admin
|
* - admin
|
||||||
|
* parameters:
|
||||||
|
* - name: filter
|
||||||
|
* in: query
|
||||||
|
* required: false
|
||||||
|
* type: object
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: object
|
||||||
* produces:
|
* produces:
|
||||||
* - application/json
|
* - application/json
|
||||||
* responses:
|
* responses:
|
||||||
@@ -20,7 +29,8 @@ const passport = require('../passport');
|
|||||||
*/
|
*/
|
||||||
router.get('/', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
|
router.get('/', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const result = await db.apiKey.get();
|
const filter = req.query.filter? JSON.parse(req.query.filter) : {};
|
||||||
|
const result = await db.apiKey.get(filter);
|
||||||
return res.json(result);
|
return res.json(result);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
@@ -42,6 +52,15 @@ router.get('/', passport.ensureAuthenticatedAndAdmin, async (req, res, next) =>
|
|||||||
* in: path
|
* in: path
|
||||||
* type: string
|
* type: string
|
||||||
* required: true
|
* required: true
|
||||||
|
* requestBody:
|
||||||
|
* required: true
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
* description:
|
||||||
|
* type: string
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: API KEY
|
* description: API KEY
|
||||||
@@ -52,9 +71,8 @@ router.post('/:userId', passport.ensureAuthenticatedAndAdmin, async (req, res, n
|
|||||||
if ( !user ) {
|
if ( !user ) {
|
||||||
res.status(404).json({"err": "user not found"});
|
res.status(404).json({"err": "user not found"});
|
||||||
}
|
}
|
||||||
var body = {
|
var body = req.body;
|
||||||
user: req.params.userId
|
body.user = req.params.userId;
|
||||||
}
|
|
||||||
const result = await db.apiKey.add(body);
|
const result = await db.apiKey.add(body);
|
||||||
return res.json(result);
|
return res.json(result);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -64,10 +82,10 @@ router.post('/:userId', passport.ensureAuthenticatedAndAdmin, async (req, res, n
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /apikeys/{id}:
|
* /apikeys/{id}/revoke:
|
||||||
* put:
|
* put:
|
||||||
* description: Deactivate API Key
|
* description: Revoke API Key
|
||||||
* summary: Deactivate API Key
|
* summary: Revoke API Key
|
||||||
* tags:
|
* tags:
|
||||||
* - admin
|
* - admin
|
||||||
* produces:
|
* produces:
|
||||||
@@ -81,7 +99,7 @@ router.post('/:userId', passport.ensureAuthenticatedAndAdmin, async (req, res, n
|
|||||||
* 200:
|
* 200:
|
||||||
* description: API KEY
|
* description: API KEY
|
||||||
*/
|
*/
|
||||||
router.put('/:id', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
|
router.put('/:id/revoke', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const result = await db.apiKey.update(req.params.id, {"isActive": false});
|
const result = await db.apiKey.update(req.params.id, {"isActive": false});
|
||||||
return res.json(result);
|
return res.json(result);
|
||||||
@@ -90,4 +108,32 @@ router.put('/:id', passport.ensureAuthenticatedAndAdmin, async (req, res, next)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /apikeys/{id}:
|
||||||
|
* delete:
|
||||||
|
* description: Delete API Key
|
||||||
|
* summary: Delete API Key
|
||||||
|
* tags:
|
||||||
|
* - admin
|
||||||
|
* produces:
|
||||||
|
* - application/json
|
||||||
|
* parameters:
|
||||||
|
* - name: id
|
||||||
|
* in: path
|
||||||
|
* type: string
|
||||||
|
* required: true
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: API KEY
|
||||||
|
*/
|
||||||
|
router.delete('/:id', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const result = await db.apiKey.del(req.params.id);
|
||||||
|
return res.json(result);
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
@@ -7,6 +7,11 @@ const fs = require('fs-extra');
|
|||||||
const cli = require('qmi-cloud-common/cli');
|
const cli = require('qmi-cloud-common/cli');
|
||||||
const barracuda = require('qmi-cloud-common/barracuda');
|
const barracuda = require('qmi-cloud-common/barracuda');
|
||||||
|
|
||||||
|
async function asyncForEach(array, callback) {
|
||||||
|
for (let index = 0; index < array.length; index++) {
|
||||||
|
await callback(array[index], index, array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
import { queues, TF_APPLY_QUEUE, TF_APPLY_QSEOK_QUEUE, TF_DESTROY_QUEUE, STOP_CONTAINER_QUEUE } from 'qmi-cloud-common/queues';
|
import { queues, TF_APPLY_QUEUE, TF_APPLY_QSEOK_QUEUE, TF_DESTROY_QUEUE, STOP_CONTAINER_QUEUE } from 'qmi-cloud-common/queues';
|
||||||
|
|
||||||
@@ -18,6 +23,15 @@ import { queues, TF_APPLY_QUEUE, TF_APPLY_QSEOK_QUEUE, TF_DESTROY_QUEUE, STOP_CO
|
|||||||
* summary: Get all users
|
* summary: Get all users
|
||||||
* tags:
|
* tags:
|
||||||
* - admin
|
* - admin
|
||||||
|
* parameters:
|
||||||
|
* - name: filter
|
||||||
|
* in: query
|
||||||
|
* required: false
|
||||||
|
* type: object
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: object
|
||||||
* produces:
|
* produces:
|
||||||
* - application/json
|
* - application/json
|
||||||
* responses:
|
* responses:
|
||||||
@@ -26,7 +40,8 @@ import { queues, TF_APPLY_QUEUE, TF_APPLY_QSEOK_QUEUE, TF_DESTROY_QUEUE, STOP_CO
|
|||||||
*/
|
*/
|
||||||
router.get('/', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
|
router.get('/', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const result = await db.user.get();
|
const filter = req.query.filter? JSON.parse(req.query.filter) : {};
|
||||||
|
const result = await db.user.get(filter);
|
||||||
return res.json(result);
|
return res.json(result);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
@@ -768,6 +783,14 @@ router.post('/:userId/provisions/:id/destroy', passport.ensureAuthenticatedAndIs
|
|||||||
return res.status(404).json({"msg": "Not found privision with id "+req.params.id});
|
return res.status(404).json({"msg": "Not found privision with id "+req.params.id});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ( provision.destroy && provision.destroy.status !== 'error' ) {
|
||||||
|
console.log(`APIUser# This provision is already destroyed or being destroyed right now: ${provision._id}`);
|
||||||
|
return res.status(200).json(provision);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
console.log(`APIUser# Queueing destroy provision: ${provision._id}`);
|
||||||
const destroyJob = await db.destroy.add({ "user": userId });
|
const destroyJob = await db.destroy.add({ "user": userId });
|
||||||
provision = await db.provision.update(req.params.id, {"destroy": destroyJob._id});
|
provision = await db.provision.update(req.params.id, {"destroy": destroyJob._id});
|
||||||
const scenarioSource = await db.scenario.getOne({name: provision.scenario});
|
const scenarioSource = await db.scenario.getOne({name: provision.scenario});
|
||||||
@@ -780,6 +803,30 @@ router.post('/:userId/provisions/:id/destroy', passport.ensureAuthenticatedAndIs
|
|||||||
_scenario: scenarioSource
|
_scenario: scenarioSource
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//Check children provisions
|
||||||
|
let children = await db.provision.get({ "parent": provision._id, "isDestroyed": false, "isDeleted": false });
|
||||||
|
|
||||||
|
if (children.results.length > 0 ) {
|
||||||
|
await asyncForEach(children.results, async function(child) {
|
||||||
|
|
||||||
|
if ( !child.destroy || child.destroy.status === 'error' ) {
|
||||||
|
console.log(`APIUser# Queueing destroy children provision: ${child._id}`);
|
||||||
|
let destroyJobChild = await db.destroy.add({ "user": userId });
|
||||||
|
await db.provision.update(child._id, {"destroy": destroyJobChild._id});
|
||||||
|
let scenarioSourceChild = await db.scenario.getOne({name: child.scenario});
|
||||||
|
|
||||||
|
queues[TF_DESTROY_QUEUE].add("tf_destroy_job", {
|
||||||
|
scenario: child.scenario,
|
||||||
|
provId: child._id,
|
||||||
|
user: req.user,
|
||||||
|
id: destroyJobChild._id,
|
||||||
|
_scenario: scenarioSourceChild
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log(`APIUser# This child provision is already destroyed or being destroyed right now: ${child._id}`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
return res.status(200).json(provision);
|
return res.status(200).json(provision);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -787,6 +834,218 @@ router.post('/:userId/provisions/:id/destroy', passport.ensureAuthenticatedAndIs
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /users/{userId}/provisions/{id}/share/{withUserId}:
|
||||||
|
* put:
|
||||||
|
* description: Share provision with another user
|
||||||
|
* summary: Share provision with another user
|
||||||
|
* produces:
|
||||||
|
* - application/json
|
||||||
|
* parameters:
|
||||||
|
* - name: userId
|
||||||
|
* in: path
|
||||||
|
* type: string
|
||||||
|
* required: true
|
||||||
|
* - name: id
|
||||||
|
* in: path
|
||||||
|
* type: string
|
||||||
|
* required: true
|
||||||
|
* - name: withUserId
|
||||||
|
* in: path
|
||||||
|
* type: string
|
||||||
|
* required: true
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Provision
|
||||||
|
* 404:
|
||||||
|
* description: Not found
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
router.put('/:userId/provisions/:id/share/:withUserId', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||||
|
|
||||||
|
try {
|
||||||
|
const userId = req.params.userId === 'me'? req.user._id : req.params.userId;
|
||||||
|
|
||||||
|
if ( req.params.withUserId === userId ) {
|
||||||
|
return res.status(400).json({"msg": "Can't share with the same user"});
|
||||||
|
}
|
||||||
|
|
||||||
|
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});
|
||||||
|
}
|
||||||
|
|
||||||
|
let found = await db.sharedProvision.getOne({
|
||||||
|
"user": userId,
|
||||||
|
"provision": provision._id,
|
||||||
|
"sharedWithUser": req.params.withUserId
|
||||||
|
});
|
||||||
|
if ( !found ) {
|
||||||
|
found = await db.sharedProvision.add({"user": userId, "provision": provision._id, "sharedWithUser": req.params.withUserId})
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.json(found);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
return res.status(error.output.statusCode).json({"err":error});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /users/{userId}/provisions/{id}/share/{withUserId}:
|
||||||
|
* delete:
|
||||||
|
* description: Stop sharing this provision with another user
|
||||||
|
* summary: Stop sharing this provision with another user
|
||||||
|
* produces:
|
||||||
|
* - application/json
|
||||||
|
* parameters:
|
||||||
|
* - name: userId
|
||||||
|
* in: path
|
||||||
|
* type: string
|
||||||
|
* required: true
|
||||||
|
* - name: id
|
||||||
|
* in: path
|
||||||
|
* type: string
|
||||||
|
* required: true
|
||||||
|
* - name: withUserId
|
||||||
|
* in: path
|
||||||
|
* type: string
|
||||||
|
* required: true
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Provision
|
||||||
|
* 404:
|
||||||
|
* description: Not found
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
router.delete('/:userId/provisions/:id/share/:withUserId', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||||
|
|
||||||
|
try {
|
||||||
|
const userId = req.params.userId === 'me'? req.user._id : req.params.userId;
|
||||||
|
|
||||||
|
if ( req.params.withUserId === userId ) {
|
||||||
|
return res.status(400).json({"msg": "Can't share with the same user"});
|
||||||
|
}
|
||||||
|
const result = await db.sharedProvision.delMany({"user": userId, "provision": req.params.id, "sharedWithUser": req.params.withUserId});
|
||||||
|
return res.json(result);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
return res.status(error.output.statusCode).json({"err":error});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /users/{userId}/provisions/{id}/share:
|
||||||
|
* delete:
|
||||||
|
* description: Stop sharing this provision with everybody
|
||||||
|
* summary: Stop sharing this provision with everybody
|
||||||
|
* produces:
|
||||||
|
* - application/json
|
||||||
|
* parameters:
|
||||||
|
* - name: userId
|
||||||
|
* in: path
|
||||||
|
* type: string
|
||||||
|
* required: true
|
||||||
|
* - name: id
|
||||||
|
* in: path
|
||||||
|
* type: string
|
||||||
|
* required: true
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Provision
|
||||||
|
* 404:
|
||||||
|
* description: Not found
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
router.delete('/:userId/provisions/:id/share', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||||
|
|
||||||
|
try {
|
||||||
|
const userId = req.params.userId === 'me'? req.user._id : req.params.userId;
|
||||||
|
|
||||||
|
if ( req.params.withUserId === userId ) {
|
||||||
|
return res.status(400).json({"msg": "Can't share with the same user"});
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await db.sharedProvision.delMany({"user": userId, "provision": req.params.id});
|
||||||
|
return res.json(result);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
return res.status(error.output.statusCode).json({"err":error});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /users/{userId}/provisions/{id}/share:
|
||||||
|
* get:
|
||||||
|
* description: Get shares of this provision
|
||||||
|
* summary: Get shares of this provision
|
||||||
|
* produces:
|
||||||
|
* - application/json
|
||||||
|
* parameters:
|
||||||
|
* - name: userId
|
||||||
|
* in: path
|
||||||
|
* type: string
|
||||||
|
* required: true
|
||||||
|
* - name: id
|
||||||
|
* in: path
|
||||||
|
* type: string
|
||||||
|
* required: true
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Provision
|
||||||
|
* 404:
|
||||||
|
* description: Not found
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
router.get('/:userId/provisions/:id/share', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||||
|
|
||||||
|
try {
|
||||||
|
const userId = req.params.userId === 'me'? req.user._id : req.params.userId;
|
||||||
|
|
||||||
|
const result = await db.sharedProvision.get({"user": userId, "provision": req.params.id});
|
||||||
|
return res.json(result);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
return res.status(error.output.statusCode).json({"err":error});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /users/{userId}/sharedprovisions:
|
||||||
|
* get:
|
||||||
|
* description: Get provision shared with me
|
||||||
|
* summary: Get provision shared with me
|
||||||
|
* produces:
|
||||||
|
* - application/json
|
||||||
|
* parameters:
|
||||||
|
* - name: userId
|
||||||
|
* in: path
|
||||||
|
* type: string
|
||||||
|
* required: true
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Provision
|
||||||
|
* 404:
|
||||||
|
* description: Not found
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
router.get('/:userId/sharedprovisions', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||||
|
|
||||||
|
try {
|
||||||
|
const userId = req.params.userId === 'me'? req.user._id : req.params.userId;
|
||||||
|
let result = await db.sharedProvision.get({"sharedWithUser": userId});
|
||||||
|
return res.json(result);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
return res.status(error.output.statusCode).json({"err":error});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /users/{userId}/provisions:
|
* /users/{userId}/provisions:
|
||||||
|
|||||||
@@ -39,5 +39,3 @@
|
|||||||
<!--<h1>Scenarios</h1>-->
|
<!--<h1>Scenarios</h1>-->
|
||||||
<table-vmtypes></table-vmtypes>
|
<table-vmtypes></table-vmtypes>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<qmi-alert></qmi-alert>
|
|
||||||
|
|||||||
@@ -7,11 +7,15 @@ import { HomeComponent } from './home/home.component';
|
|||||||
import { AuthGuard } from './services/auth.guard';
|
import { AuthGuard } from './services/auth.guard';
|
||||||
import { FaqComponent } from './faq/faq.component';
|
import { FaqComponent } from './faq/faq.component';
|
||||||
import { UserDashboardComponent } from './user/user-dashboard.component';
|
import { UserDashboardComponent } from './user/user-dashboard.component';
|
||||||
|
import { ScenariosSectionComponent } from './scenarios/scenarios-section.component';
|
||||||
|
import { ProvisionsSharedComponent } from './provisions/provisions-shared.component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{ path: 'home', component: HomeComponent},
|
{ path: 'home', component: HomeComponent},
|
||||||
{ path: 'faq', component: FaqComponent},
|
{ path: 'faq', component: FaqComponent},
|
||||||
|
{ path: 'scenarios', component: ScenariosSectionComponent, canActivate: [AuthGuard]},
|
||||||
{ path: 'provisions', component: ProvisionsComponent, canActivate: [AuthGuard]},
|
{ path: 'provisions', component: ProvisionsComponent, canActivate: [AuthGuard]},
|
||||||
|
{ path: 'sharedprovision', component: ProvisionsSharedComponent, canActivate: [AuthGuard]},
|
||||||
{ path: 'admin', component: AdminComponent, canActivate: [AuthGuard]},
|
{ path: 'admin', component: AdminComponent, canActivate: [AuthGuard]},
|
||||||
{ path: 'admin/:tab', component: AdminComponent, canActivate: [AuthGuard]},
|
{ path: 'admin/:tab', component: AdminComponent, canActivate: [AuthGuard]},
|
||||||
{ path: 'stats', component: StatsComponent, canActivate: [AuthGuard]},
|
{ path: 'stats', component: StatsComponent, canActivate: [AuthGuard]},
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { AppComponent } from './app.component';
|
|||||||
import { UiModule } from './ui/ui.module';
|
import { UiModule } from './ui/ui.module';
|
||||||
import { HomeComponent } from './home/home.component';
|
import { HomeComponent } from './home/home.component';
|
||||||
import { ProvisionsComponent } from './provisions/provisions.component';
|
import { ProvisionsComponent } from './provisions/provisions.component';
|
||||||
|
import { ProvisionsSharedComponent } from './provisions/provisions-shared.component';
|
||||||
import { AuthGuard } from './services/auth.guard';
|
import { AuthGuard } from './services/auth.guard';
|
||||||
import { ProvisionsService } from './services/provisions.service';
|
import { ProvisionsService } from './services/provisions.service';
|
||||||
import { ScenariosService } from './services/scenarios.service';
|
import { ScenariosService } from './services/scenarios.service';
|
||||||
@@ -16,6 +17,7 @@ import { MarkdownModule, MarkedOptions, MarkedRenderer } from 'ngx-markdown';
|
|||||||
import { HttpClientModule, HttpClient } from '@angular/common/http';
|
import { HttpClientModule, HttpClient } from '@angular/common/http';
|
||||||
import { LogsComponent } from './logs/logs.component';
|
import { LogsComponent } from './logs/logs.component';
|
||||||
import { ScenariosComponent } from './scenarios/scenarios.component';
|
import { ScenariosComponent } from './scenarios/scenarios.component';
|
||||||
|
import { ScenariosSectionComponent } from './scenarios/scenarios-section.component';
|
||||||
import { AdminComponent } from './admin/admin.component';
|
import { AdminComponent } from './admin/admin.component';
|
||||||
import { PopoverconfirmComponent } from './popoverconfirm/popoverconfirm.component';
|
import { PopoverconfirmComponent } from './popoverconfirm/popoverconfirm.component';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
@@ -33,6 +35,7 @@ import { FilterPipe } from './filter.pipe';
|
|||||||
import { FaqComponent } from './faq/faq.component';
|
import { FaqComponent } from './faq/faq.component';
|
||||||
import { NewProvisionConfirmComponent } from './modals/new-provision.component';
|
import { NewProvisionConfirmComponent } from './modals/new-provision.component';
|
||||||
import { ScenarioModalComponent } from './modals/edit-scenario.component';
|
import { ScenarioModalComponent } from './modals/edit-scenario.component';
|
||||||
|
import { ShareModalComponent } from './modals/share.component';
|
||||||
import { SubscriptionModalComponent } from './modals/edit-subscription.component';
|
import { SubscriptionModalComponent } from './modals/edit-subscription.component';
|
||||||
import { TableSubsComponent } from './tables/table-subscriptions.component';
|
import { TableSubsComponent } from './tables/table-subscriptions.component';
|
||||||
import { TableVmTypesComponent } from './tables/table-vmtypes.component';
|
import { TableVmTypesComponent } from './tables/table-vmtypes.component';
|
||||||
@@ -66,8 +69,10 @@ export function markedOptions(): MarkedOptions {
|
|||||||
AppComponent,
|
AppComponent,
|
||||||
HomeComponent,
|
HomeComponent,
|
||||||
ProvisionsComponent,
|
ProvisionsComponent,
|
||||||
|
ProvisionsSharedComponent,
|
||||||
LogsComponent,
|
LogsComponent,
|
||||||
ScenariosComponent,
|
ScenariosComponent,
|
||||||
|
ScenariosSectionComponent,
|
||||||
AdminComponent,
|
AdminComponent,
|
||||||
PopoverconfirmComponent,
|
PopoverconfirmComponent,
|
||||||
TableProvisionsAdminComponent,
|
TableProvisionsAdminComponent,
|
||||||
@@ -82,6 +87,7 @@ export function markedOptions(): MarkedOptions {
|
|||||||
TableNotificationsComponent,
|
TableNotificationsComponent,
|
||||||
ScenarioModalComponent,
|
ScenarioModalComponent,
|
||||||
SubscriptionModalComponent,
|
SubscriptionModalComponent,
|
||||||
|
ShareModalComponent,
|
||||||
TableSubsComponent,
|
TableSubsComponent,
|
||||||
TableApiKeysComponent,
|
TableApiKeysComponent,
|
||||||
ApikeyModalComponent,
|
ApikeyModalComponent,
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
<div class="qmialert" *ngIf="alert">
|
<div class="qmialert" *ngIf="alert">
|
||||||
<div #qmialert class="alert alert-dismissible fade show" [ngClass]="alert.type" role="alert">
|
<div #qmialert class="alert alert-dismissible fade show" [ngClass]="alert.type" role="alert">
|
||||||
<h5 class="alert-heading"><span>{{alert.type === 'alert-primary'? 'Done!' : 'Oops'}}</span></h5>
|
<h4 class="alert-heading">
|
||||||
|
<span *ngIf="alert.type === 'alert-dark'" class="fa fa-info-circle"></span>
|
||||||
|
<span *ngIf="alert.type === 'alert-danger'" class="fa fa-warning"></span>
|
||||||
|
<span> {{alert.type === 'alert-dark'? 'Done!' : 'Oops!'}}</span>
|
||||||
|
</h4>
|
||||||
<button type="button" class="close" aria-label="Close" (click)="closeAlert()">
|
<button type="button" class="close" aria-label="Close" (click)="closeAlert()">
|
||||||
<span aria-hidden="true">×</span>
|
<span aria-hidden="true">×</span>
|
||||||
</button>
|
</button>
|
||||||
<hr>
|
<hr>
|
||||||
<h6 class="alert-heading">
|
<h6 class="alert-heading">
|
||||||
<span *ngIf="alert.type === 'alert-warning'" class="fa fa-warning"></span>
|
<span [innerHTML]="alert.text"></span>
|
||||||
<span *ngIf="alert.type === 'alert-primary'" class="fa fa-info-circle"></span>
|
|
||||||
<span> {{alert.text}}</span>
|
|
||||||
</h6>
|
</h6>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
.qmialert {
|
.qmialert {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0px;
|
bottom: 10px;
|
||||||
left: 0px;
|
left: 50%;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
width: 100%;
|
width: 90%;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
}
|
}
|
||||||
@@ -13,6 +13,7 @@ export class AlertComponent implements OnInit, OnDestroy {
|
|||||||
subscription: Subscription;
|
subscription: Subscription;
|
||||||
alert : any = null;
|
alert : any = null;
|
||||||
|
|
||||||
|
|
||||||
constructor(private _alertService: AlertService) {}
|
constructor(private _alertService: AlertService) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
|||||||
@@ -14,11 +14,14 @@
|
|||||||
<option *ngFor="let item of users" [value]="item._id">{{item.displayName}}</option>
|
<option *ngFor="let item of users" [value]="item._id">{{item.displayName}}</option>
|
||||||
</select>
|
</select>
|
||||||
</section>
|
</section>
|
||||||
|
<div class="md-form">
|
||||||
|
<input mdbInput type="text" name="text" [(ngModel)]="sendData.description" id="description" class="form-control">
|
||||||
|
<label for="description" class="">Description *:</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal-footer d-flex justify-content-center">
|
<div class="modal-footer d-flex justify-content-center">
|
||||||
<button style="margin-right: 100px;" *ngIf="sendData._id" mdbBtn color="danger" outline="true" class="waves-light" size="sm" mdbWavesEffect (click)="delete();">Delete</button>
|
|
||||||
<button mdbBtn color="dark-green" size="sm" outline="true" class="waves-effect" mdbWavesEffect (click)="modalRef.hide()">Cancel</button>
|
<button mdbBtn color="dark-green" size="sm" outline="true" class="waves-effect" mdbWavesEffect (click)="modalRef.hide()">Cancel</button>
|
||||||
<button mdbBtn color="dark-green" class="waves-light" size="sm" mdbWavesEffect (click)="confirm();">Save</button>
|
<button mdbBtn color="dark-green" class="waves-light" size="sm" mdbWavesEffect (click)="confirm();">Save</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export class ApikeyModalComponent implements OnInit, OnDestroy {
|
|||||||
this.sendData.user = this.selectedUser;
|
this.sendData.user = this.selectedUser;
|
||||||
console.log("sendData", this.sendData);
|
console.log("sendData", this.sendData);
|
||||||
|
|
||||||
this._usersService.addApikey(this.sendData.user).subscribe( res=> {
|
this._usersService.addApikey(this.selectedUser, this.sendData).subscribe( res=> {
|
||||||
console.log("done", res);
|
console.log("done", res);
|
||||||
this.action.next("DONE!!!");
|
this.action.next("DONE!!!");
|
||||||
this.modalRef.hide();
|
this.modalRef.hide();
|
||||||
@@ -51,12 +51,4 @@ export class ApikeyModalComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delete() : void {
|
|
||||||
this._usersService.delApikey(this.sendData._id).subscribe( res=> {
|
|
||||||
console.log("done", res);
|
|
||||||
this.action.next("DONE!!!");
|
|
||||||
this.modalRef.hide();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -100,7 +100,8 @@ export class ProvisionModalComponent implements OnInit, OnDestroy {
|
|||||||
console.log("SendData", this.sendData);
|
console.log("SendData", this.sendData);
|
||||||
|
|
||||||
this._provisionsService.updateProvisionUser(this.provision._id, this.provision.user._id, this.sendData ).subscribe( res=>{
|
this._provisionsService.updateProvisionUser(this.provision._id, this.provision.user._id, this.sendData ).subscribe( res=>{
|
||||||
this.action.next(res);
|
let user = this.users.filter( u => u._id === this.selectedUser);
|
||||||
|
this.action.next({"user": user[0]});
|
||||||
this.modalRef.hide();
|
this.modalRef.hide();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
<div class="mydata">{{item.value}}</div>
|
<div class="mydata">{{item.value}}</div>
|
||||||
</div>
|
</div>
|
||||||
<h5 *ngIf="events" class="info-subtitle">Events</h5>
|
<h5 *ngIf="events" class="info-subtitle">Events</h5>
|
||||||
<table *ngIf="events" mdbTable stickyHeader="true" hover="true" class="table table-sm">
|
<table *ngIf="events" class="table table-sm">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngFor="let item of events; let i = index">
|
<tr *ngFor="let item of events; let i = index">
|
||||||
<td style="min-width:120px;">{{item.created | date: 'MMM dd, yyyy - H:mm'}}</td>
|
<td style="min-width:120px;">{{item.created | date: 'MMM dd, yyyy - H:mm'}}</td>
|
||||||
|
|||||||
43
src/app/modals/share.component.html
Normal file
43
src/app/modals/share.component.html
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<!--Content-->
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header text-center">
|
||||||
|
<button type="button" class="close pull-right" aria-label="Close" (click)="modalRef.hide()">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
<h4 class="modal-title w-100 font-weight-bold">Share provision with others</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body" style="max-height: 650px; overflow: auto;">
|
||||||
|
<div *ngIf="sharedUsers && sharedUsers.length > 0">
|
||||||
|
<label>Sharing this provision with: </label>
|
||||||
|
<table>
|
||||||
|
<tr *ngFor="let withUser of sharedUsers; let i = index">
|
||||||
|
<td>{{withUser.displayName}}</td>
|
||||||
|
<td>
|
||||||
|
<button style="margin-left: 10px;" class="lui-button lui-text-danger" (click)="stopShare(withUser._id);">
|
||||||
|
<span class="lui-icon lui-icon--bin" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<section style="padding: 20px 0px;">
|
||||||
|
<label><mdb-icon fas icon="user"></mdb-icon> Share with:</label>
|
||||||
|
<select style="width: 250px;" class="browser-default custom-select custom-select-sm" [(ngModel)]="selectedUser">
|
||||||
|
<option *ngFor="let item of users" [value]="item._id">{{item.displayName}}</option>
|
||||||
|
</select>
|
||||||
|
<button class="lui-button text-success" (click)="share();">
|
||||||
|
Share
|
||||||
|
</button>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer d-flex justify-content-center">
|
||||||
|
|
||||||
|
<button mdbBtn color="dark-green" size="sm" outline="true" class="waves-effect" mdbWavesEffect (click)="modalRef.hide()">Close</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--/.Content-->
|
||||||
19
src/app/modals/share.component.scss
Normal file
19
src/app/modals/share.component.scss
Normal 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;
|
||||||
|
}
|
||||||
64
src/app/modals/share.component.ts
Normal file
64
src/app/modals/share.component.ts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||||
|
import { MDBModalRef } from 'angular-bootstrap-md';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { UsersService } from '../services/users.service';
|
||||||
|
import { ProvisionsService } from '../services/provisions.service';
|
||||||
|
import { AuthGuard } from '../services/auth.guard';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'qmi-share-provision',
|
||||||
|
templateUrl: './share.component.html',
|
||||||
|
styleUrls: ['./share.component.scss']
|
||||||
|
})
|
||||||
|
export class ShareModalComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
|
provision;
|
||||||
|
action: Subject<any> = new Subject();
|
||||||
|
users;
|
||||||
|
sharedUsers;
|
||||||
|
selectedUser;
|
||||||
|
|
||||||
|
constructor( private _auth: AuthGuard, public modalRef: MDBModalRef, private _usersService: UsersService, private _provisionsService: ProvisionsService ) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh() : void {
|
||||||
|
this._provisionsService.getSharesForProvision(this.provision.user._id, this.provision._id).subscribe(res=> {
|
||||||
|
this.sharedUsers = res;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this._usersService.getUsers(true).subscribe(res=> {
|
||||||
|
this.users = res.results;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
share() : void {
|
||||||
|
|
||||||
|
//this.modalRef.hide();
|
||||||
|
|
||||||
|
this._provisionsService.shareProvision(this.provision.user._id, this.provision._id, this.selectedUser ).subscribe( res=>{
|
||||||
|
this.refresh();
|
||||||
|
//this.modalRef.hide();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
stopShare(withUserId) : void {
|
||||||
|
|
||||||
|
|
||||||
|
//this.modalRef.hide();
|
||||||
|
|
||||||
|
this._provisionsService.stopShareProvision(this.provision.user._id, this.provision._id, withUserId ).subscribe( res=>{
|
||||||
|
this.refresh();
|
||||||
|
//this.modalRef.hide();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
108
src/app/provisions/provisions-shared.component.html
Normal file
108
src/app/provisions/provisions-shared.component.html
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
<app-logs [show]="logShow" (onClose)="onLogsClose()" [type]="logstype" [selectedprov]="selectedprov"></app-logs>
|
||||||
|
<div style="margin-top: 80px; min-height: 650px;">
|
||||||
|
<h1>Shared Provisions</h1>
|
||||||
|
<div *ngIf="provisions && provisions.length" class="flexcontainer">
|
||||||
|
<div *ngFor="let provision of provisions; let i = index" class="box">
|
||||||
|
<div class="title desc">
|
||||||
|
<div>Shared by <b>{{provision.owner.displayName}}</b></div>
|
||||||
|
</div>
|
||||||
|
<div class="title desc {{provision.status}} {{provision.statusVms}} {{provision.destroy? provision.destroy.status : ''}}">
|
||||||
|
<div mdbTooltip="{{provision.description}}" class="subtitle">{{provision.description}}</div>
|
||||||
|
</div>
|
||||||
|
<div style="border-top: 1px dashed #fff;" class="title {{provision.status}} {{provision.statusVms}} {{provision.destroy? provision.destroy.status : ''}}">
|
||||||
|
<div class="maintitle"><span *ngIf="provision._scenarioDoc">{{provision._scenarioDoc.title}}</span><span *ngIf="!provision._scenarioDoc">Old scenario (please destroy)</span></div>
|
||||||
|
<div style="font-size: 14px;">{{provision.scenario}} (v{{provision.scenarioVersion}}) <span *ngIf="provision.scenarioVersion !== provision._scenarioDoc.version" class="text-danger newversion">New version available!</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="contentbox">
|
||||||
|
<div *ngIf="provision.vmImage && provision.vmImage.vm1">
|
||||||
|
<div *ngIf="provision.statusVms">
|
||||||
|
Current VMs status:
|
||||||
|
<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>
|
||||||
|
<span *ngIf="provision.statusVms === 'Stopped'"> (for {{provision.inactiveAsDays}})</span>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="provision.statusVms">
|
||||||
|
<mdb-icon fas icon="play-circle"></mdb-icon> Accumulated Running Time: <span style="font-weight: bold;">{{provision.runningAsDays}}</span>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="provision.schedule && !provision.schedule.is24x7">
|
||||||
|
<span mdbTooltip="{{!provision.schedule.isStartupTimeEnable? 'Scheduled startup deactivated. Re-enable it in the schedule panel.' : ''}}" [ngClass]="{'linethrough': !provision.schedule.isStartupTimeEnable}" *ngIf="provision.statusVms === 'Stopped'">
|
||||||
|
<mdb-icon far icon="clock"></mdb-icon> VMs startup scheduled at <b>{{provision.schedule.localeStartupTime}}h</b> ({{provision.schedule.localTimezone}})
|
||||||
|
</span>
|
||||||
|
<span *ngIf="provision.statusVms === 'Running'"><mdb-icon far icon="clock"></mdb-icon> VMs shutdown scheduled at <b>{{provision.schedule.localeShutdownTime}}h</b> ({{provision.schedule.localTimezone}})</span>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="provision.statusVms === 'Running' || provision.statusVms === 'Stopping'">
|
||||||
|
<div *ngIf="!provision.schedule || provision.schedule.is24x7">
|
||||||
|
VMs will run <b>nonstop</b> for <b>{{provision.autoshutdownAsDays}}</b><span *ngIf="provision.status === 'provisioned'"> - <a mdbTooltip="Renew 24x7 period" href (click)="openConfirmExtendModal($event, provision)"><mdb-icon fas icon="redo-alt"></mdb-icon></a></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--<div *ngIf="provision.statusVms === 'Running' || provision.statusVms === 'Stopping'">
|
||||||
|
<mdb-icon *ngIf="provision.schedule && !provision.schedule.is24x7" fas icon="stopwatch"></mdb-icon>
|
||||||
|
<span *ngIf="provision.schedule && !provision.schedule.is24x7"> Scheduled shutdown in ~ <span style="font-weight: bold;">{{provision.autoshutdownAsHours}}</span></span>
|
||||||
|
|
||||||
|
</div>-->
|
||||||
|
<div *ngIf="provision.statusVms === 'Stopped' || provision.statusVms === 'Starting'">
|
||||||
|
<span *ngIf="!provision.schedule || provision.schedule.is24x7 || (provision.schedule && !provision.schedule.is24x7 && !provision.schedule.isStartupTimeEnable)">
|
||||||
|
<mdb-icon fas icon="times-circle"></mdb-icon> Scheduled auto-destroy in ~ <b>{{provision.autoDestroyAsDays}}</b>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Provision ({{provision.created | date: 'MMM dd, yyyy'}})
|
||||||
|
-
|
||||||
|
<span>
|
||||||
|
<span style="text-transform: capitalize;" class="badge badge-secondary" [ngClass]="{'badge-warning': provision.status === 'provisioning' || provision.status === 'initializing', 'badge-success': provision.status === 'provisioned', 'badge-danger': provision.status === 'error' || provision.status === 'error_init' || provision.status === 'error_plan' }">{{provision.status}}</span>
|
||||||
|
<span *ngIf="provision.status === 'provisioning' || provision.status === 'initializing'" class="spinner-border spinner-border-sm text-warning" role="status">
|
||||||
|
<span class="sr-only"></span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
-
|
||||||
|
<a href (click)="showLogs($event, provision, 'provision')" class="lui-text-info">Logs</a>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="provision.destroy">
|
||||||
|
Destroy ({{provision.destroy.created | date: 'MMM dd'}})
|
||||||
|
-
|
||||||
|
<span>
|
||||||
|
<span style="text-transform: capitalize;" class="badge badge-secondary" [ngClass]="{'badge-warning': provision.destroy.status === 'destroying', 'badge-success': provision.destroy.status === 'destroyed', 'badge-danger': provision.destroy.status === 'error'}">{{provision.destroy.status}}</span>
|
||||||
|
<span *ngIf="provision.destroy.status === 'destroying'" class="spinner-border spinner-border-sm text-warning" role="status">
|
||||||
|
<span class="sr-only"></span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
-
|
||||||
|
<a *ngIf="provision.destroy._id" href (click)="showLogs($event, provision, 'destroy')" class="lui-text-info">Logs</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<button mdbTooltip="Provision information" style="margin-right: 5px;" class="lui-button" (click)="openModal(provision)">
|
||||||
|
<span class="lui-icon lui-icon--info" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
<button style="margin-right: 5px;" mdbTooltip="Edit running schedule" *ngIf="provision.canManage && provision.vmImage && provision.vmImage.vm1 && provision._scenarioDoc.isDivvyEnabled && provision.status === 'provisioned'" (click)="openScheduleModal(provision)" class="lui-button">
|
||||||
|
<mdb-icon far icon="clock"></mdb-icon>
|
||||||
|
</button>
|
||||||
|
<button style="margin-right: 5px;" mdbTooltip="Stop VMs" *ngIf="provision.vmImage && provision.vmImage.vm1 && provision.status === 'provisioned' && provision.statusVms === 'Running'" (click)="openConfirmStopModal(provision)" class="lui-button">
|
||||||
|
<span class="lui-icon lui-icon--stop" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
<button style="margin-right: 5px;" mdbTooltip="Start VMs" *ngIf="provision.vmImage && provision.vmImage.vm1 && 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="Renew 24x7 period" *ngIf="(!provision.schedule || provision.schedule.is24x7) && provision.status === 'provisioned' && provision.statusVms === 'Running'" (click)="openConfirmExtendModal(provision)" class="lui-button">
|
||||||
|
<mdb-icon fas icon="redo-alt"></mdb-icon> 24x7
|
||||||
|
</button>-->
|
||||||
|
<button style="float: right;" mdbTooltip="Destroy provision" *ngIf="provision.canManage && !provision.isDestroyed && (provision.status === 'provisioned' || provision.status === 'error' || provision.status === 'aborted') && (!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>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="!provisions || provisions.length === 0" style="text-align: center;">
|
||||||
|
<p>No one has shared any provisions with you yet.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<qmi-alert></qmi-alert>
|
||||||
250
src/app/provisions/provisions-shared.component.ts
Normal file
250
src/app/provisions/provisions-shared.component.ts
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { ProvisionsService } from '../services/provisions.service';
|
||||||
|
import { Subscription, timer} from 'rxjs';
|
||||||
|
import { switchMap } from 'rxjs/operators';
|
||||||
|
import { AuthGuard } from '../services/auth.guard';
|
||||||
|
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-shared',
|
||||||
|
templateUrl: './provisions-shared.component.html',
|
||||||
|
styleUrls: ['./provisions.component.scss'],
|
||||||
|
providers: [ProvisionsService]
|
||||||
|
})
|
||||||
|
export class ProvisionsSharedComponent implements OnInit {
|
||||||
|
|
||||||
|
private _userId;
|
||||||
|
provisions;
|
||||||
|
destroys;
|
||||||
|
subscription: Subscription;
|
||||||
|
instantSubs: Subscription;
|
||||||
|
logShow: boolean = false;
|
||||||
|
logstype: String = 'provision';
|
||||||
|
public selectedprov: Object = null;
|
||||||
|
history: Boolean = false;
|
||||||
|
|
||||||
|
constructor(private modalService: MDBModalService, private _alertService: AlertService, private _provisionsService: ProvisionsService, private _auth: AuthGuard) {
|
||||||
|
this._auth.getUserInfo().subscribe( value => {
|
||||||
|
this._userId = value? value._id : null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _refresh(): void {
|
||||||
|
this.instantSubs = this._provisionsService.getProvisionsSharedWithMe(this._userId).subscribe( provisions=>{
|
||||||
|
provisions = provisions.results.map(p=>{
|
||||||
|
p.provision.owner = p.user;
|
||||||
|
p.provision.canManage = p.canManage;
|
||||||
|
return p.provision;
|
||||||
|
});
|
||||||
|
this.provisions = provisions.filter(p => !p.destroy || !p.destroy.status || p.destroy.status !== 'destroyed');
|
||||||
|
this.instantSubs.unsubscribe();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.subscription = timer(0, 8000).pipe( switchMap(() => this._provisionsService.getProvisionsSharedWithMe(this._userId) ) ).subscribe(provisions => {
|
||||||
|
provisions = provisions.results.map(p=>{
|
||||||
|
p.provision.owner = p.user;
|
||||||
|
p.provision.canManage = p.canManage;
|
||||||
|
return p.provision;
|
||||||
|
});
|
||||||
|
this.provisions = provisions.filter(p => !p.destroy || !p.destroy.status || p.destroy.status !== 'destroyed');
|
||||||
|
|
||||||
|
if ( this.provisions && this.provisions.length === 0 ) {
|
||||||
|
this.subscription.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.subscription.unsubscribe();
|
||||||
|
if ( this.instantSubs ) {
|
||||||
|
this.instantSubs.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setModal(provision, frame) : void {
|
||||||
|
frame.show();
|
||||||
|
this._provisionsService.setSelectedProv(provision);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
del(provision): void {
|
||||||
|
this._provisionsService.delProvision(provision._id.toString(), this._userId).subscribe( res => {
|
||||||
|
this._refresh();
|
||||||
|
this._alertService.showAlert({
|
||||||
|
type: 'alert-dark',
|
||||||
|
text: `Provision entry <b>${provision.scenario}</b> was deleted from your history`
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleHistory(): void {
|
||||||
|
this.history = !this.history;
|
||||||
|
}
|
||||||
|
|
||||||
|
openConfirmDestroyModal(provision) {
|
||||||
|
var modalRef = this.modalService.show(ModalConfirmComponent, {
|
||||||
|
class: 'modal-sm modal-notify modal-danger',
|
||||||
|
containerClass: '',
|
||||||
|
data: {
|
||||||
|
info: {
|
||||||
|
title: 'Confirm destroy this provision?',
|
||||||
|
icon: 'times-circle'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
var sub = modalRef.content.action.subscribe( (result: any) => {
|
||||||
|
sub.unsubscribe();
|
||||||
|
this._provisionsService.newDestroy(provision._id.toString(), this._userId).subscribe( res => {
|
||||||
|
this._refresh();
|
||||||
|
this._alertService.showAlert({
|
||||||
|
type: 'alert-dark',
|
||||||
|
text: `Provision of scenario <b>${provision.scenario}</b> is going to be destroyed.`
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
openConfirmStopModal(provision) {
|
||||||
|
var modalRef = this.modalService.show(ModalConfirmComponent, {
|
||||||
|
class: 'modal-sm modal-notify modal-info',
|
||||||
|
containerClass: '',
|
||||||
|
data: {
|
||||||
|
info: {
|
||||||
|
title: 'Confirm Stop VMs?',
|
||||||
|
icon: 'stop',
|
||||||
|
buttonColor: 'grey'
|
||||||
|
},
|
||||||
|
provision: provision
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
var sub = modalRef.content.action.subscribe( (result: any) => {
|
||||||
|
console.log("Confirm result", result);
|
||||||
|
sub.unsubscribe();
|
||||||
|
this._provisionsService.stopVms(provision._id.toString(), this._userId, result).subscribe( res => {
|
||||||
|
provision.statusVms = res.statusVms;
|
||||||
|
provision.timeRunning = res.timeRunning;
|
||||||
|
provision.runningFrom = res.runningFrom;
|
||||||
|
this._alertService.showAlert({
|
||||||
|
type: 'alert-dark',
|
||||||
|
text: `Stopping all VMs for scenario <b>${provision.scenario}</b>...`
|
||||||
|
});
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
openConfirmStartModal(provision) {
|
||||||
|
var modalRef = this.modalService.show(ModalConfirmComponent, {
|
||||||
|
class: 'modal-sm modal-notify modal-info',
|
||||||
|
containerClass: '',
|
||||||
|
data: {
|
||||||
|
info: {
|
||||||
|
title: 'Confirm Start VMs?',
|
||||||
|
icon: 'play',
|
||||||
|
buttonColor: 'grey'
|
||||||
|
},
|
||||||
|
provision: provision
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
var sub = modalRef.content.action.subscribe( (result: any) => {
|
||||||
|
sub.unsubscribe();
|
||||||
|
this._provisionsService.startVms(provision._id.toString(), this._userId).subscribe( res => {
|
||||||
|
provision.statusVms = res.statusVms;
|
||||||
|
provision.timeRunning = res.timeRunning;
|
||||||
|
provision.runningFrom = res.runningFrom;
|
||||||
|
this._alertService.showAlert({
|
||||||
|
type: 'alert-dark',
|
||||||
|
text: `Starting all VMs for scenario <b>${provision.scenario}</b>...`
|
||||||
|
});
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
openConfirmExtendModal($event, provision) {
|
||||||
|
$event.preventDefault();
|
||||||
|
var modalRef = this.modalService.show(ModalConfirmComponent, {
|
||||||
|
class: 'modal-sm modal-notify modal-info',
|
||||||
|
containerClass: '',
|
||||||
|
data: {
|
||||||
|
info: {
|
||||||
|
title: `Renew 24x7 period?`,
|
||||||
|
icon: 'redo-alt',
|
||||||
|
buttonColor: 'grey'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
var sub = modalRef.content.action.subscribe( (result: any) => {
|
||||||
|
sub.unsubscribe();
|
||||||
|
this._provisionsService.extend(provision._id.toString(), this._userId).subscribe( res => {
|
||||||
|
provision.countExtend = res.countExtend;
|
||||||
|
provision.timeRunning = res.timeRunning;
|
||||||
|
provision.runningFrom = res.runningFrom;
|
||||||
|
this._alertService.showAlert({
|
||||||
|
type: 'alert-dark',
|
||||||
|
text: `Running period extended another ${provision._scenarioDoc.allowed24x7RunningDays} days (from now) for provision <b>${provision.scenario}</b>`
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
this.logstype = type;
|
||||||
|
this.logShow = false;
|
||||||
|
this.selectedprov = provision;
|
||||||
|
this.logShow = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
openModal(provision) {
|
||||||
|
this.modalService.show(ModalInfoComponent, {
|
||||||
|
class: 'modal-lg',
|
||||||
|
containerClass: '',
|
||||||
|
data: {
|
||||||
|
info:provision
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
onLogsClose(): void {
|
||||||
|
this.selectedprov = null;
|
||||||
|
this.logShow = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onStartProvision(scenario): void {
|
||||||
|
this._alertService.showAlert({
|
||||||
|
type: 'alert-dark',
|
||||||
|
text: `Scenario <b>${scenario.name}</b> is going to be provisioned. Scroll up to your Provisions to watch out progress.`
|
||||||
|
});
|
||||||
|
this._refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
<app-logs [show]="logShow" (onClose)="onLogsClose()" [type]="logstype" [selectedprov]="selectedprov"></app-logs>
|
<app-logs [show]="logShow" (onClose)="onLogsClose()" [type]="logstype" [selectedprov]="selectedprov"></app-logs>
|
||||||
<div style="margin-top: 80px;">
|
<div style="margin-top: 80px; min-height: 650px;">
|
||||||
<h1>Provisions</h1>
|
<h1>Provisions <span *ngIf="provisions && provisions.length">({{provisions.length}})</span></h1>
|
||||||
<h4 style="border-bottom: 1px solid #ccc;">Current</h4>
|
|
||||||
<div *ngIf="provisions && provisions.length" class="flexcontainer">
|
<div *ngIf="provisions && provisions.length" class="flexcontainer">
|
||||||
<div *ngFor="let provision of provisions; let i = index" class="box">
|
<div *ngFor="let provision of provisions; let i = index" class="box">
|
||||||
<div class="title desc {{provision.status}} {{provision.statusVms}} {{provision.destroy? provision.destroy.status : ''}}">
|
<div class="title desc {{provision.status}} {{provision.statusVms}} {{provision.destroy? provision.destroy.status : ''}}">
|
||||||
@@ -9,10 +8,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div style="border-top: 1px dashed #fff;" class="title {{provision.status}} {{provision.statusVms}} {{provision.destroy? provision.destroy.status : ''}}">
|
<div style="border-top: 1px dashed #fff;" class="title {{provision.status}} {{provision.statusVms}} {{provision.destroy? provision.destroy.status : ''}}">
|
||||||
<div class="maintitle"><span *ngIf="provision._scenarioDoc">{{provision._scenarioDoc.title}}</span><span *ngIf="!provision._scenarioDoc">Old scenario (please destroy)</span></div>
|
<div class="maintitle"><span *ngIf="provision._scenarioDoc">{{provision._scenarioDoc.title}}</span><span *ngIf="!provision._scenarioDoc">Old scenario (please destroy)</span></div>
|
||||||
<div style="font-size: 14px;">{{provision.scenario}} (v{{provision.scenarioVersion}})</div>
|
<div style="font-size: 14px;">{{provision.scenario}} (v{{provision.scenarioVersion}}) <span *ngIf="provision.scenarioVersion !== provision._scenarioDoc.version" class="text-danger newversion">New version available!</span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="contentbox">
|
<div class="contentbox">
|
||||||
<div *ngIf="provision.vmImage">
|
<div *ngIf="provision.vmImage && provision.vmImage.vm1">
|
||||||
<div *ngIf="provision.statusVms">
|
<div *ngIf="provision.statusVms">
|
||||||
Current VMs status:
|
Current VMs status:
|
||||||
<span>
|
<span>
|
||||||
@@ -80,13 +79,13 @@
|
|||||||
<button mdbTooltip="Provision information" style="margin-right: 5px;" class="lui-button" (click)="openModal(provision)">
|
<button mdbTooltip="Provision information" style="margin-right: 5px;" class="lui-button" (click)="openModal(provision)">
|
||||||
<span class="lui-icon lui-icon--info" aria-hidden="true"></span>
|
<span class="lui-icon lui-icon--info" aria-hidden="true"></span>
|
||||||
</button>
|
</button>
|
||||||
<button style="margin-right: 5px;" mdbTooltip="Edit running schedule" *ngIf="provision.vmImage && provision._scenarioDoc.isDivvyEnabled && provision.status === 'provisioned'" (click)="openScheduleModal(provision)" class="lui-button">
|
<button style="margin-right: 5px;" mdbTooltip="Edit running schedule" *ngIf="provision.vmImage && provision.vmImage.vm1 && provision._scenarioDoc.isDivvyEnabled && provision.status === 'provisioned'" (click)="openScheduleModal(provision)" class="lui-button">
|
||||||
<mdb-icon far icon="clock"></mdb-icon>
|
<mdb-icon far icon="clock"></mdb-icon>
|
||||||
</button>
|
</button>
|
||||||
<button style="margin-right: 5px;" mdbTooltip="Stop VMs" *ngIf="provision.vmImage && provision.status === 'provisioned' && provision.statusVms === 'Running'" (click)="openConfirmStopModal(provision)" class="lui-button">
|
<button style="margin-right: 5px;" mdbTooltip="Stop VMs" *ngIf="provision.vmImage && provision.vmImage.vm1 && provision.status === 'provisioned' && provision.statusVms === 'Running'" (click)="openConfirmStopModal(provision)" class="lui-button">
|
||||||
<span class="lui-icon lui-icon--stop" aria-hidden="true"></span>
|
<span class="lui-icon lui-icon--stop" aria-hidden="true"></span>
|
||||||
</button>
|
</button>
|
||||||
<button style="margin-right: 5px;" mdbTooltip="Start VMs" *ngIf="provision.vmImage && provision.status === 'provisioned' && provision.statusVms === 'Stopped'" (click)="openConfirmStartModal(provision)" class="lui-button">
|
<button style="margin-right: 5px;" mdbTooltip="Start VMs" *ngIf="provision.vmImage && provision.vmImage.vm1 && provision.status === 'provisioned' && provision.statusVms === 'Stopped'" (click)="openConfirmStartModal(provision)" class="lui-button">
|
||||||
<span class="lui-icon lui-icon--run" aria-hidden="true"></span>
|
<span class="lui-icon lui-icon--run" aria-hidden="true"></span>
|
||||||
</button>
|
</button>
|
||||||
<!--<button style="margin-right: 5px;" mdbTooltip="Renew 24x7 period" *ngIf="(!provision.schedule || provision.schedule.is24x7) && provision.status === 'provisioned' && provision.statusVms === 'Running'" (click)="openConfirmExtendModal(provision)" class="lui-button">
|
<!--<button style="margin-right: 5px;" mdbTooltip="Renew 24x7 period" *ngIf="(!provision.schedule || provision.schedule.is24x7) && provision.status === 'provisioned' && provision.statusVms === 'Running'" (click)="openConfirmExtendModal(provision)" class="lui-button">
|
||||||
@@ -98,15 +97,19 @@
|
|||||||
<button style="float: right;" mdbTooltip="Remove entry" *ngIf="(provision.isDestroyed && provision.destroy.status === 'destroyed') || provision.status === 'error_init' || provision.status === 'error_plan'" (click)="del(provision)" class="lui-button">
|
<button style="float: right;" mdbTooltip="Remove entry" *ngIf="(provision.isDestroyed && provision.destroy.status === 'destroyed') || provision.status === 'error_init' || provision.status === 'error_plan'" (click)="del(provision)" class="lui-button">
|
||||||
<span class="lui-icon lui-icon--bin lui-text-danger" aria-hidden="true"></span>
|
<span class="lui-icon lui-icon--bin lui-text-danger" aria-hidden="true"></span>
|
||||||
</button>
|
</button>
|
||||||
|
<button style="float: right;" mdbTooltip="Share provision" class="lui-button" (click)="share(provision)">
|
||||||
|
<span class="lui-icon lui-icon--share" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="!provisions || provisions.length === 0">
|
<div *ngIf="!provisions || provisions.length === 0" style="text-align: center;">
|
||||||
<p>There are no current Running provisions</p>
|
<p>You don't have any active provisions.</p>
|
||||||
|
<p>Search available <a routerLink="/scenarios" routerLinkActive="active">Scenarios</a> and start provisioning now.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h4 style="padding-top: 40px; border-bottom: 1px solid #ccc;" *ngIf="destroys && destroys.length">History</h4>
|
<h4 style="padding-top: 40px;" *ngIf="destroys && destroys.length">History <button mdbTooltip="Show/Hide History" (click)="toggleHistory()" class="lui-button"><span class="lui-icon lui-icon--triangle-{{history? 'top' : 'bottom'}}" aria-hidden="true"></span></button></h4>
|
||||||
<div *ngIf="destroys && destroys.length" class="flexcontainer">
|
<div *ngIf="destroys && destroys.length && history" class="flexcontainer">
|
||||||
<div *ngFor="let provision of destroys; let i = index" class="box">
|
<div *ngFor="let provision of destroys; let i = index" class="box">
|
||||||
<div class="title desc {{provision.status}} {{provision.destroy.status}}">
|
<div class="title desc {{provision.status}} {{provision.destroy.status}}">
|
||||||
<div mdbTooltip="{{provision.description}}" class="subtitle">{{provision.description}}</div>
|
<div mdbTooltip="{{provision.description}}" class="subtitle">{{provision.description}}</div>
|
||||||
@@ -115,7 +118,7 @@
|
|||||||
<div class="maintitle"><span *ngIf="provision._scenarioDoc">{{provision._scenarioDoc.title}}</span><span *ngIf="!provision._scenarioDoc">Old scenario (please delete)</span></div>
|
<div class="maintitle"><span *ngIf="provision._scenarioDoc">{{provision._scenarioDoc.title}}</span><span *ngIf="!provision._scenarioDoc">Old scenario (please delete)</span></div>
|
||||||
<div style="font-size: 14px;">{{provision.scenario}} (v{{provision.scenarioVersion}})</div>
|
<div style="font-size: 14px;">{{provision.scenario}} (v{{provision.scenarioVersion}})</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="contentbox">
|
<div class="contentbox destroyed">
|
||||||
<div>
|
<div>
|
||||||
Provision ({{provision.created | date: 'MMM dd, yyyy - H:mm'}}h)
|
Provision ({{provision.created | date: 'MMM dd, yyyy - H:mm'}}h)
|
||||||
-
|
-
|
||||||
@@ -151,9 +154,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!--
|
||||||
<div style="margin-top: 40px; margin-bottom: 100px;">
|
<div style="margin-top: 40px; margin-bottom: 100px;">
|
||||||
<h1>Scenarios</h1>
|
<h1>Scenarios</h1>
|
||||||
<app-scenarios (onStartProvision)="onStartProvision($event)"></app-scenarios>
|
<app-scenarios (onStartProvision)="onStartProvision($event)"></app-scenarios>
|
||||||
</div>
|
</div>
|
||||||
|
-->
|
||||||
<qmi-alert></qmi-alert>
|
<qmi-alert></qmi-alert>
|
||||||
@@ -13,6 +13,8 @@ h1 {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
border-top: 1px solid #ccc;
|
||||||
|
padding-top: 10px;
|
||||||
|
|
||||||
.box {
|
.box {
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
@@ -81,6 +83,9 @@ h1 {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
|
&.destroyed {
|
||||||
|
min-height: 100px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.buttons {
|
.buttons {
|
||||||
@@ -130,3 +135,11 @@ h1 {
|
|||||||
.linethrough {
|
.linethrough {
|
||||||
text-decoration: line-through;
|
text-decoration: line-through;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.newversion {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 10px;
|
||||||
|
margin-left: 8px;
|
||||||
|
padding: 2px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import { MDBModalService } from 'angular-bootstrap-md';
|
|||||||
import { ModalInfoComponent } from '../modals/modalinfo.component';
|
import { ModalInfoComponent } from '../modals/modalinfo.component';
|
||||||
import { ModalConfirmComponent } from '../modals/confirm.component';
|
import { ModalConfirmComponent } from '../modals/confirm.component';
|
||||||
import { ProvisionModalComponent } from '../modals/edit-provision.component';
|
import { ProvisionModalComponent } from '../modals/edit-provision.component';
|
||||||
|
import { ShareModalComponent } from '../modals/share.component';
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -26,6 +27,7 @@ export class ProvisionsComponent implements OnInit {
|
|||||||
logShow: boolean = false;
|
logShow: boolean = false;
|
||||||
logstype: String = 'provision';
|
logstype: String = 'provision';
|
||||||
public selectedprov: Object = null;
|
public selectedprov: Object = null;
|
||||||
|
history: Boolean = false;
|
||||||
|
|
||||||
constructor(private modalService: MDBModalService, private _alertService: AlertService, private _provisionsService: ProvisionsService, private _auth: AuthGuard) {
|
constructor(private modalService: MDBModalService, private _alertService: AlertService, private _provisionsService: ProvisionsService, private _auth: AuthGuard) {
|
||||||
this._auth.getUserInfo().subscribe( value => {
|
this._auth.getUserInfo().subscribe( value => {
|
||||||
@@ -47,6 +49,11 @@ export class ProvisionsComponent implements OnInit {
|
|||||||
provisions = provisions.results;
|
provisions = provisions.results;
|
||||||
this.provisions = provisions.filter(p => !p.destroy || !p.destroy.status || p.destroy.status !== 'destroyed');
|
this.provisions = provisions.filter(p => !p.destroy || !p.destroy.status || p.destroy.status !== 'destroyed');
|
||||||
this.destroys = provisions.filter(p => p.destroy && p.destroy.status === 'destroyed');
|
this.destroys = provisions.filter(p => p.destroy && p.destroy.status === 'destroyed');
|
||||||
|
|
||||||
|
if ( this.provisions && this.provisions.length === 0 ) {
|
||||||
|
this.subscription.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,12 +74,16 @@ export class ProvisionsComponent implements OnInit {
|
|||||||
this._provisionsService.delProvision(provision._id.toString(), this._userId).subscribe( res => {
|
this._provisionsService.delProvision(provision._id.toString(), this._userId).subscribe( res => {
|
||||||
this._refresh();
|
this._refresh();
|
||||||
this._alertService.showAlert({
|
this._alertService.showAlert({
|
||||||
type: 'alert-primary',
|
type: 'alert-dark',
|
||||||
text: `Provision entry '${provision.scenario}' was deleted from your history`
|
text: `Provision entry <b>${provision.scenario}</b> was deleted from your history`
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleHistory(): void {
|
||||||
|
this.history = !this.history;
|
||||||
|
}
|
||||||
|
|
||||||
openConfirmDestroyModal(provision) {
|
openConfirmDestroyModal(provision) {
|
||||||
var modalRef = this.modalService.show(ModalConfirmComponent, {
|
var modalRef = this.modalService.show(ModalConfirmComponent, {
|
||||||
class: 'modal-sm modal-notify modal-danger',
|
class: 'modal-sm modal-notify modal-danger',
|
||||||
@@ -90,8 +101,8 @@ export class ProvisionsComponent implements OnInit {
|
|||||||
this._provisionsService.newDestroy(provision._id.toString(), this._userId).subscribe( res => {
|
this._provisionsService.newDestroy(provision._id.toString(), this._userId).subscribe( res => {
|
||||||
this._refresh();
|
this._refresh();
|
||||||
this._alertService.showAlert({
|
this._alertService.showAlert({
|
||||||
type: 'alert-primary',
|
type: 'alert-dark',
|
||||||
text: `Provision of scenario '${provision.scenario}' is going to be destroyed`
|
text: `Provision of scenario <b>${provision.scenario}</b> is going to be destroyed.`
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -119,13 +130,28 @@ export class ProvisionsComponent implements OnInit {
|
|||||||
provision.timeRunning = res.timeRunning;
|
provision.timeRunning = res.timeRunning;
|
||||||
provision.runningFrom = res.runningFrom;
|
provision.runningFrom = res.runningFrom;
|
||||||
this._alertService.showAlert({
|
this._alertService.showAlert({
|
||||||
type: 'alert-primary',
|
type: 'alert-dark',
|
||||||
text: `Stopping all VMs for scenario '${provision.scenario}'...`
|
text: `Stopping all VMs for scenario <b>${provision.scenario}</b>...`
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
share(provision) {
|
||||||
|
var modalRef = this.modalService.show(ShareModalComponent, {
|
||||||
|
class: 'modal-md modal-notify',
|
||||||
|
containerClass: '',
|
||||||
|
data: {
|
||||||
|
provision: provision
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
var sub = modalRef.content.action.subscribe( (result: any) => {
|
||||||
|
sub.unsubscribe();
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
openConfirmStartModal(provision) {
|
openConfirmStartModal(provision) {
|
||||||
var modalRef = this.modalService.show(ModalConfirmComponent, {
|
var modalRef = this.modalService.show(ModalConfirmComponent, {
|
||||||
class: 'modal-sm modal-notify modal-info',
|
class: 'modal-sm modal-notify modal-info',
|
||||||
@@ -147,8 +173,8 @@ export class ProvisionsComponent implements OnInit {
|
|||||||
provision.timeRunning = res.timeRunning;
|
provision.timeRunning = res.timeRunning;
|
||||||
provision.runningFrom = res.runningFrom;
|
provision.runningFrom = res.runningFrom;
|
||||||
this._alertService.showAlert({
|
this._alertService.showAlert({
|
||||||
type: 'alert-primary',
|
type: 'alert-dark',
|
||||||
text: `Starting all VMs for scenario '${provision.scenario}'...`
|
text: `Starting all VMs for scenario <b>${provision.scenario}</b>...`
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@@ -176,8 +202,8 @@ export class ProvisionsComponent implements OnInit {
|
|||||||
provision.timeRunning = res.timeRunning;
|
provision.timeRunning = res.timeRunning;
|
||||||
provision.runningFrom = res.runningFrom;
|
provision.runningFrom = res.runningFrom;
|
||||||
this._alertService.showAlert({
|
this._alertService.showAlert({
|
||||||
type: 'alert-primary',
|
type: 'alert-dark',
|
||||||
text: `Running period extended another ${provision._scenarioDoc.allowed24x7RunningDays} days (from now) for provision '${provision.scenario}'`
|
text: `Running period extended another ${provision._scenarioDoc.allowed24x7RunningDays} days (from now) for provision <b>${provision.scenario}</b>`
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -225,8 +251,8 @@ export class ProvisionsComponent implements OnInit {
|
|||||||
|
|
||||||
onStartProvision(scenario): void {
|
onStartProvision(scenario): void {
|
||||||
this._alertService.showAlert({
|
this._alertService.showAlert({
|
||||||
type: 'alert-primary',
|
type: 'alert-dark',
|
||||||
text: `Scenario '${scenario.name}' is going to be provisioned. Scroll up to your Provisions to watch out progress.`
|
text: `Scenario <b>${scenario.name}</b> is going to be provisioned. Scroll up to your Provisions to watch out progress.`
|
||||||
});
|
});
|
||||||
this._refresh();
|
this._refresh();
|
||||||
}
|
}
|
||||||
|
|||||||
5
src/app/scenarios/scenarios-section.component.html
Normal file
5
src/app/scenarios/scenarios-section.component.html
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<div style="margin-top: 80px;">
|
||||||
|
<h1>Scenarios</h1>
|
||||||
|
<app-scenarios (onStartProvision)="onStartProvision($event)"></app-scenarios>
|
||||||
|
</div>
|
||||||
|
<qmi-alert></qmi-alert>
|
||||||
0
src/app/scenarios/scenarios-section.component.scss
Normal file
0
src/app/scenarios/scenarios-section.component.scss
Normal file
35
src/app/scenarios/scenarios-section.component.ts
Normal file
35
src/app/scenarios/scenarios-section.component.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { AlertService } from '../services/alert.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-scenarios-section',
|
||||||
|
templateUrl: './scenarios-section.component.html',
|
||||||
|
styleUrls: ['./scenarios-section.component.scss']
|
||||||
|
})
|
||||||
|
export class ScenariosSectionComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
|
|
||||||
|
constructor(private router: Router, private _alertService: AlertService) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {}
|
||||||
|
|
||||||
|
onStartProvision(scenario): void {
|
||||||
|
|
||||||
|
//this.router.navigate(['provisions']);
|
||||||
|
|
||||||
|
this._alertService.showAlert({
|
||||||
|
type: 'alert-dark',
|
||||||
|
text: `Scenario <b>${scenario.name}</b> is going to be provisioned. Go to <a class='alert-link' href='/provisions'><b>My Provisions</b></a> to watch out the progress.`
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
<mdb-card-body style="position: relative;">
|
<mdb-card-body style="position: relative;">
|
||||||
|
|
||||||
<!--Text-->
|
<!--Text-->
|
||||||
<mdb-card-text>
|
<mdb-card-text class="qmicardtext">
|
||||||
<div [innerHTML]="s.description"></div>
|
<div [innerHTML]="s.description"></div>
|
||||||
</mdb-card-text>
|
</mdb-card-text>
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
|
||||||
|
|
||||||
.flexcontainer{
|
.flexcontainer{
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@@ -7,11 +9,13 @@
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.qmicard {
|
.qmicard {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
position: relative;
|
position: relative;
|
||||||
max-width: 500px;
|
max-width: 525px;
|
||||||
|
|
||||||
.card-badge {
|
.card-badge {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -22,10 +26,7 @@
|
|||||||
.card-body {
|
.card-body {
|
||||||
padding:10px 15px;
|
padding:10px 15px;
|
||||||
}
|
}
|
||||||
.card-text {
|
|
||||||
font-size: .8rem;
|
|
||||||
font-weight: 300;
|
|
||||||
}
|
|
||||||
.card-icon {
|
.card-icon {
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export class ScenariosComponent implements OnInit, OnDestroy {
|
|||||||
this.onStartProvision.emit(scenario);
|
this.onStartProvision.emit(scenario);
|
||||||
}, data => {
|
}, data => {
|
||||||
this._alertService.showAlert({
|
this._alertService.showAlert({
|
||||||
type: 'alert-warning',
|
type: 'alert-danger',
|
||||||
text: `${data.error.msg}`
|
text: `${data.error.msg}`
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export class AlertService {
|
|||||||
}
|
}
|
||||||
this.to = setTimeout (function(){
|
this.to = setTimeout (function(){
|
||||||
this.alertEmitter.emit(null);
|
this.alertEmitter.emit(null);
|
||||||
}.bind(this), 5000);
|
}.bind(this), 7000);
|
||||||
}
|
}
|
||||||
|
|
||||||
getAlertEmitter(): EventEmitter<any> {
|
getAlertEmitter(): EventEmitter<any> {
|
||||||
|
|||||||
@@ -25,10 +25,14 @@ export class ProvisionsService {
|
|||||||
if ( filter ){
|
if ( filter ){
|
||||||
params = params.append("filter", JSON.stringify(filter));
|
params = params.append("filter", JSON.stringify(filter));
|
||||||
}
|
}
|
||||||
/*params = params.append("populates", JSON.stringify([
|
params = params.append("select", "-path -terraformImage -updated -jobId -logFile -pendingNextAction -outputs -vmImage");
|
||||||
{ path: 'user', select: 'displayName upn' },
|
params = params.append("populates", JSON.stringify([
|
||||||
{ path:'destroy', select: "-user -jobId" }
|
{ path: 'user', select: 'displayName' },
|
||||||
]));*/
|
{ path: 'destroy', select: "-user -jobId" },
|
||||||
|
{ path: 'schedule' },
|
||||||
|
{ path: '_scenarioDoc', select: "allowed24x7RunningDays allowedInnactiveDays isDivvyEnabled"},
|
||||||
|
{ path: 'deployOpts', select: "location subsId"}
|
||||||
|
]));
|
||||||
return this.httpClient.get(`${environment.apiVersionPath}/provisions`, { params: params }).pipe(map((provisions:any)=>{
|
return this.httpClient.get(`${environment.apiVersionPath}/provisions`, { params: params }).pipe(map((provisions:any)=>{
|
||||||
provisions.results.forEach(p => {
|
provisions.results.forEach(p => {
|
||||||
if (!p.deployOpts){
|
if (!p.deployOpts){
|
||||||
@@ -49,6 +53,32 @@ export class ProvisionsService {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getProvisionsSharedWithMe(userId): Observable<any> {
|
||||||
|
return this.httpClient.get(`${environment.apiVersionPath}/users/${userId}/sharedprovisions`).pipe(map((res:any)=>{
|
||||||
|
res.results.forEach(item => {
|
||||||
|
this.timeRunning(item.provision);
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
getSharesForProvision(userId, provisionId): Observable<any> {
|
||||||
|
return this.httpClient.get(`${environment.apiVersionPath}/users/${userId}/provisions/${provisionId}/share`).pipe(map((res:any)=>{
|
||||||
|
let users = res.results.map(item => {
|
||||||
|
return item.sharedWithUser;
|
||||||
|
})
|
||||||
|
return users;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
shareProvision(userId, provisionId, withUserId): Observable<any> {
|
||||||
|
return this.httpClient.put(`${environment.apiVersionPath}/users/${userId}/provisions/${provisionId}/share/${withUserId}`, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
stopShareProvision(userId, provisionId, withUserId): Observable<any> {
|
||||||
|
return this.httpClient.delete(`${environment.apiVersionPath}/users/${userId}/provisions/${provisionId}/share/${withUserId}`);
|
||||||
|
}
|
||||||
|
|
||||||
getProvisionById(id) : Observable<any> {
|
getProvisionById(id) : Observable<any> {
|
||||||
return this.httpClient.get(`${environment.apiVersionPath}/provisions/${id}`).pipe(map((p:any)=>{
|
return this.httpClient.get(`${environment.apiVersionPath}/provisions/${id}`).pipe(map((p:any)=>{
|
||||||
this.timeRunning(p);
|
this.timeRunning(p);
|
||||||
|
|||||||
@@ -44,12 +44,16 @@ export class UsersService {
|
|||||||
return this.httpClient.get(`${environment.apiVersionPath}/apikeys`);
|
return this.httpClient.get(`${environment.apiVersionPath}/apikeys`);
|
||||||
}
|
}
|
||||||
|
|
||||||
addApikey(userId): Observable<any> {
|
addApikey(userId, body): Observable<any> {
|
||||||
return this.httpClient.post(`${environment.apiVersionPath}/apikeys/${userId}`, null);
|
return this.httpClient.post(`${environment.apiVersionPath}/apikeys/${userId}`, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
revokeApikey(id): Observable<any> {
|
||||||
|
return this.httpClient.put(`${environment.apiVersionPath}/apikeys/${id}/revoke`, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
delApikey(id): Observable<any> {
|
delApikey(id): Observable<any> {
|
||||||
return this.httpClient.put(`${environment.apiVersionPath}/apikeys/${id}`, null);
|
return this.httpClient.delete(`${environment.apiVersionPath}/apikeys/${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
getScenarios(userId) : Observable<any> {
|
getScenarios(userId) : Observable<any> {
|
||||||
|
|||||||
@@ -11,8 +11,9 @@
|
|||||||
<thead class="sticky-top">
|
<thead class="sticky-top">
|
||||||
<tr>
|
<tr>
|
||||||
<th style="width: 155px;">Created</th>
|
<th style="width: 155px;">Created</th>
|
||||||
<th>Key</th>
|
|
||||||
<th>User (role)</th>
|
<th>User (role)</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>Key</th>
|
||||||
<th>Is Active?</th>
|
<th>Is Active?</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -21,17 +22,21 @@
|
|||||||
<tr *ngFor="let item of elements; let i = index">
|
<tr *ngFor="let item of elements; let i = index">
|
||||||
<td *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex"
|
<td *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex"
|
||||||
scope="row">{{item.created | date: 'MMM dd, yyyy - H:mm'}}</td>
|
scope="row">{{item.created | date: 'MMM dd, yyyy - H:mm'}}</td>
|
||||||
<td *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex">{{item.apiKey}}</td>
|
|
||||||
<td *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex">{{item.user.displayName}} ({{item.user.role}})</td>
|
<td *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex">{{item.user.displayName}} ({{item.user.role}})</td>
|
||||||
|
<td *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex">{{item.description}}</td>
|
||||||
|
<td *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex">{{item.apiKey}}</td>
|
||||||
<td style="text-align: center;" *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex">
|
<td style="text-align: center;" *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex">
|
||||||
<mdb-icon *ngIf="item.isActive" fas icon="check"></mdb-icon>
|
<mdb-icon *ngIf="item.isActive" fas icon="check"></mdb-icon>
|
||||||
<mdb-icon *ngIf="!item.isActive" class="red-text" fas icon="times"></mdb-icon>
|
<mdb-icon *ngIf="!item.isActive" class="red-text" fas icon="times"></mdb-icon>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex">
|
<td *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex">
|
||||||
<button *ngIf="item.isActive" title="Revoke" (click)="openConfirmDeleteModal(item)" class="lui-button">
|
<button style="margin-right: 3px;" *ngIf="item.isActive" title="Revoke" (click)="openConfirmRevokeModal(item)" class="lui-button">
|
||||||
Revoke
|
Revoke
|
||||||
</button>
|
</button>
|
||||||
|
<button style="margin-right: 3px;" *ngIf="!item.isActive" title="Delete" (click)="openConfirmDeleteModal(item)" class="lui-button">
|
||||||
|
<span class="lui-icon lui-icon--bin" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ export class TableApiKeysComponent implements OnInit, AfterViewInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
openConfirmDeleteModal(apiKey) {
|
openConfirmRevokeModal(apiKey) {
|
||||||
var modalRef = this.modalService.show(ModalConfirmComponent, {
|
var modalRef = this.modalService.show(ModalConfirmComponent, {
|
||||||
class: 'modal-sm modal-notify modal-danger',
|
class: 'modal-sm modal-notify modal-danger',
|
||||||
containerClass: '',
|
containerClass: '',
|
||||||
@@ -110,6 +110,27 @@ export class TableApiKeysComponent implements OnInit, AfterViewInit {
|
|||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
var sub = modalRef.content.action.subscribe( (result: any) => {
|
||||||
|
sub.unsubscribe();
|
||||||
|
this._usersService.revokeApikey(apiKey._id).subscribe( res=> {
|
||||||
|
console.log("done", res);
|
||||||
|
this.refreshData();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
openConfirmDeleteModal(apiKey) {
|
||||||
|
var modalRef = this.modalService.show(ModalConfirmComponent, {
|
||||||
|
class: 'modal-sm modal-notify modal-danger',
|
||||||
|
containerClass: '',
|
||||||
|
data: {
|
||||||
|
info: {
|
||||||
|
title: 'Confirm delete?',
|
||||||
|
icon: 'trash-alt'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
var sub = modalRef.content.action.subscribe( (result: any) => {
|
var sub = modalRef.content.action.subscribe( (result: any) => {
|
||||||
sub.unsubscribe();
|
sub.unsubscribe();
|
||||||
this._usersService.delApikey(apiKey._id).subscribe( res=> {
|
this._usersService.delApikey(apiKey._id).subscribe( res=> {
|
||||||
@@ -117,6 +138,7 @@ export class TableApiKeysComponent implements OnInit, AfterViewInit {
|
|||||||
this.refreshData();
|
this.refreshData();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -70,8 +70,9 @@
|
|||||||
<i style="display: block;" *ngIf="provision.schedule && !provision.schedule.is24x7 && !provision.schedule.isStartupTimeEnable"><mdb-icon fas icon="times-circle"></mdb-icon> Destroy in: {{provision.autoDestroyAsDays}}</i>
|
<i style="display: block;" *ngIf="provision.schedule && !provision.schedule.is24x7 && !provision.schedule.isStartupTimeEnable"><mdb-icon fas icon="times-circle"></mdb-icon> Destroy in: {{provision.autoDestroyAsDays}}</i>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td *ngIf="pagingIsDisabled || (i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex)" class="ell" title="{{provision.path}}" >
|
<td *ngIf="pagingIsDisabled || (i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex)" class="ell" >
|
||||||
<a href (click)="openOwnerChange($event, provision)" class="lui-text-info">{{provision.user.displayName}}</a>
|
<a href="/user/{{provision.user._id}}" target="_blank" class="lui-text-info" title="Go to User">{{provision.user.displayName}}</a>
|
||||||
|
<a style="padding-left: 8px;" href (click)="openOwnerChange($event, provision)" class="fa fa-pencil" title="Change owner and more"></a>
|
||||||
</td>
|
</td>
|
||||||
<td (click)="openInfoModal(provision._id)" *ngIf="pagingIsDisabled || (i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex)">
|
<td (click)="openInfoModal(provision._id)" *ngIf="pagingIsDisabled || (i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex)">
|
||||||
<span style="text-transform: capitalize;" class="badge badge-secondary" [ngClass]="{'badge-warning': provision.status === 'provisioning' || provision.status === 'initializing', 'badge-success': provision.status === 'provisioned', 'badge-danger': (provision.status === 'error' || provision.status === 'error_init' || provision.status === 'error_plan') }">{{provision.status}}</span>
|
<span style="text-transform: capitalize;" class="badge badge-secondary" [ngClass]="{'badge-warning': provision.status === 'provisioning' || provision.status === 'initializing', 'badge-success': provision.status === 'provisioned', 'badge-danger': (provision.status === 'error' || provision.status === 'error_init' || provision.status === 'error_plan') }">{{provision.status}}</span>
|
||||||
@@ -101,10 +102,10 @@
|
|||||||
<button style="margin-right: 3px;" title="Abort provision" *ngIf="!provision.isDestroyed && provision.status === 'provisioning'" (click)="openConfirmAbortModal(provision)" class="lui-button lui-text-danger">
|
<button style="margin-right: 3px;" title="Abort provision" *ngIf="!provision.isDestroyed && provision.status === 'provisioning'" (click)="openConfirmAbortModal(provision)" class="lui-button lui-text-danger">
|
||||||
Abort
|
Abort
|
||||||
</button>
|
</button>
|
||||||
<button style="margin-right: 3px;" title="Stop Vms" *ngIf="provision.vmImage && !provision.isDestroyed && provision.status === 'provisioned' && provision.statusVms === 'Running'" (click)="openConfirmStopModal(provision)" class="lui-button">
|
<button style="margin-right: 3px;" title="Stop Vms" *ngIf="provision.vmImage && provision.vmImage.vm1 && !provision.isDestroyed && provision.status === 'provisioned' && provision.statusVms === 'Running'" (click)="openConfirmStopModal(provision)" class="lui-button">
|
||||||
<span class="lui-icon lui-icon--stop" aria-hidden="true"></span>
|
<span class="lui-icon lui-icon--stop" aria-hidden="true"></span>
|
||||||
</button>
|
</button>
|
||||||
<button style="margin-right: 3px;" title="Start Vms" *ngIf="provision.vmImage && !provision.isDestroyed && provision.status === 'provisioned' && provision.statusVms === 'Stopped'" (click)="openConfirmStartModal(provision)" class="lui-button">
|
<button style="margin-right: 3px;" title="Start Vms" *ngIf="provision.vmImage && provision.vmImage.vm1 && !provision.isDestroyed && provision.status === 'provisioned' && provision.statusVms === 'Stopped'" (click)="openConfirmStartModal(provision)" class="lui-button">
|
||||||
<span class="lui-icon lui-icon--run" aria-hidden="true"></span>
|
<span class="lui-icon lui-icon--run" aria-hidden="true"></span>
|
||||||
</button>
|
</button>
|
||||||
<button style="margin-right: 3px;" title="Destroy provision" *ngIf="!provision.isDestroyed && (!provision.destroy || provision.destroy.status !== 'destroying') && (provision.status === 'provisioned' || provision.status === 'error' || provision.status === 'aborted' )" (click)="openConfirmDestroyModal(provision)" class="lui-button lui-text-danger">
|
<button style="margin-right: 3px;" title="Destroy provision" *ngIf="!provision.isDestroyed && (!provision.destroy || provision.destroy.status !== 'destroying') && (provision.status === 'provisioned' || provision.status === 'error' || provision.status === 'aborted' )" (click)="openConfirmDestroyModal(provision)" class="lui-button lui-text-danger">
|
||||||
|
|||||||
@@ -146,8 +146,8 @@ export class TableProvisionsAdminComponent implements OnInit, OnDestroy, AfterVi
|
|||||||
sub.unsubscribe();
|
sub.unsubscribe();
|
||||||
provision.user = result.user;
|
provision.user = result.user;
|
||||||
this._alertService.showAlert({
|
this._alertService.showAlert({
|
||||||
type: 'alert-primary',
|
type: 'alert-dark',
|
||||||
text: `New owner ${provision.user.displayName} assigned to this provision`
|
text: `New owner <b>${provision.user.displayName}</b> assigned to this provision`
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -215,8 +215,8 @@ export class TableProvisionsAdminComponent implements OnInit, OnDestroy, AfterVi
|
|||||||
provision.startVms = res.startVms;
|
provision.startVms = res.startVms;
|
||||||
sub.unsubscribe();
|
sub.unsubscribe();
|
||||||
this._alertService.showAlert({
|
this._alertService.showAlert({
|
||||||
type: 'alert-primary',
|
type: 'alert-dark',
|
||||||
text: `Starting all VMs for scenario '${provision.scenario}'...`
|
text: `Starting all VMs for scenario <b>${provision.scenario}</b>...`
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -266,8 +266,8 @@ export class TableProvisionsAdminComponent implements OnInit, OnDestroy, AfterVi
|
|||||||
provision.startVms = res.startVms;
|
provision.startVms = res.startVms;
|
||||||
sub.unsubscribe();
|
sub.unsubscribe();
|
||||||
this._alertService.showAlert({
|
this._alertService.showAlert({
|
||||||
type: 'alert-primary',
|
type: 'alert-dark',
|
||||||
text: `Stopping all VMs for scenario '${provision.scenario}'...`
|
text: `Stopping all VMs for scenario <b>${provision.scenario}</b>...`
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -277,8 +277,8 @@ export class TableProvisionsAdminComponent implements OnInit, OnDestroy, AfterVi
|
|||||||
provision.status = res.status;
|
provision.status = res.status;
|
||||||
sub.unsubscribe();
|
sub.unsubscribe();
|
||||||
this._alertService.showAlert({
|
this._alertService.showAlert({
|
||||||
type: 'alert-primary',
|
type: 'alert-dark',
|
||||||
text: `Aborting provision for scenario '${provision.scenario}'...`
|
text: `Aborting provision for scenario <b>${provision.scenario}</b>...`
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -319,8 +319,8 @@ export class TableProvisionsAdminComponent implements OnInit, OnDestroy, AfterVi
|
|||||||
sub.unsubscribe();
|
sub.unsubscribe();
|
||||||
this._provisionsService.newDestroy(provision._id.toString(), provision.user._id).subscribe( provUpdated => {
|
this._provisionsService.newDestroy(provision._id.toString(), provision.user._id).subscribe( provUpdated => {
|
||||||
this._alertService.showAlert({
|
this._alertService.showAlert({
|
||||||
type: 'alert-primary',
|
type: 'alert-dark',
|
||||||
text: `Provision of scenario '${provision.scenario}' is going to be destroyed`
|
text: `Provision of scenario <b>${provision.scenario}</b> is going to be destroyed`
|
||||||
});
|
});
|
||||||
provision.destroy = provUpdated.destroy;
|
provision.destroy = provUpdated.destroy;
|
||||||
})
|
})
|
||||||
@@ -347,8 +347,8 @@ export class TableProvisionsAdminComponent implements OnInit, OnDestroy, AfterVi
|
|||||||
});
|
});
|
||||||
this._initElements();
|
this._initElements();
|
||||||
this._alertService.showAlert({
|
this._alertService.showAlert({
|
||||||
type: 'alert-primary',
|
type: 'alert-dark',
|
||||||
text: `Provision entry '${provision.scenario}' was deleted`
|
text: `Provision entry <b>${provision.scenario}</b> was deleted from History.`
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -395,8 +395,8 @@ export class TableProvisionsAdminComponent implements OnInit, OnDestroy, AfterVi
|
|||||||
provision.timeRunning = res.timeRunning;
|
provision.timeRunning = res.timeRunning;
|
||||||
provision.runningFrom = res.runningFrom;
|
provision.runningFrom = res.runningFrom;
|
||||||
this._alertService.showAlert({
|
this._alertService.showAlert({
|
||||||
type: 'alert-primary',
|
type: 'alert-dark',
|
||||||
text: `Running period extended another ${provision._scenarioDoc.allowed24x7RunningDays} days (from now) for provision '${provision.scenario}'`
|
text: `Running period extended another ${provision._scenarioDoc.allowed24x7RunningDays} days (from now) for provision <b>${provision.scenario}</b>`
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,14 +7,17 @@
|
|||||||
</a>
|
</a>
|
||||||
<div class="navbar-expand mr-auto">
|
<div class="navbar-expand mr-auto">
|
||||||
<div class="navbar-nav">
|
<div class="navbar-nav">
|
||||||
<a class="nav-item nav-link" routerLink="/home" routerLinkActive="active">Home</a>
|
<!--<a class="nav-item nav-link" routerLink="/home" routerLinkActive="active">Home</a>-->
|
||||||
<a *ngIf="user" class="nav-item nav-link" routerLink="/provisions" routerLinkActive="active">Provisions</a>
|
<a *ngIf="user" class="nav-item nav-link" routerLink="/scenarios" routerLinkActive="active">Scenarios</a>
|
||||||
<a *ngIf="user && (user.role === 'admin' || user.role === 'superadmin')" class="nav-item nav-link" routerLink="/admin" routerLinkActive="active">Admin</a>
|
<a *ngIf="user" class="nav-item nav-link" routerLink="/provisions" routerLinkActive="active">My Provisions</a>
|
||||||
<!--<a *ngIf="user" class="nav-item nav-link" routerLink="/stats" routerLinkActive="active">Stats</a>-->
|
<a *ngIf="user" class="nav-item nav-link" routerLink="/sharedprovision" routerLinkActive="active">Shared Provisions</a>
|
||||||
|
<!--<a *ngIf="user && (user.role === 'admin' || user.role === 'superadmin')" class="nav-item nav-link" routerLink="/admin" routerLinkActive="active">ADMIN</a>
|
||||||
|
<a *ngIf="user" class="nav-item nav-link" routerLink="/stats" routerLinkActive="active">Stats</a>-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="navbar-expand ml-auto navbar-nav">
|
<div class="navbar-expand ml-auto navbar-nav">
|
||||||
<div class="navbar-nav">
|
<div class="navbar-nav">
|
||||||
|
<a *ngIf="user && (user.role === 'admin' || user.role === 'superadmin')" class="nav-item nav-link" routerLink="/admin" routerLinkActive="active">Admin</a>
|
||||||
<a class="nav-item nav-link" routerLink="/faq" routerLinkActive="active">FAQ</a>
|
<a class="nav-item nav-link" routerLink="/faq" routerLinkActive="active">FAQ</a>
|
||||||
<a *ngIf="user && (user.role === 'admin' || user.role === 'superadmin')" class="nav-item nav-link" href="/api-docs" target="_blank">API Docs</a>
|
<a *ngIf="user && (user.role === 'admin' || user.role === 'superadmin')" class="nav-item nav-link" href="/api-docs" target="_blank">API Docs</a>
|
||||||
<a *ngIf="user" href (click)="logout($event)" class="nav-item nav-link" style="padding-left: 40px; color: #009845 !important;">Logout <i class="fas fa-sign-out-alt"></i></a>
|
<a *ngIf="user" href (click)="logout($event)" class="nav-item nav-link" style="padding-left: 40px; color: #009845 !important;">Logout <i class="fas fa-sign-out-alt"></i></a>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1>Provisions <span *ngIf="provisions && provisions.length">({{provisions.length}})</span></h1>
|
<h1>Provisions <span *ngIf="provisions && provisions.length">({{provisions.length}})</span></h1>
|
||||||
<h4 style="border-bottom: 1px solid #ccc;">Current</h4>
|
<h4>Current</h4>
|
||||||
<div *ngIf="provisions && provisions.length" class="flexcontainer">
|
<div *ngIf="provisions && provisions.length" class="flexcontainer">
|
||||||
<div *ngFor="let provision of provisions; let i = index" class="box">
|
<div *ngFor="let provision of provisions; let i = index" class="box">
|
||||||
<div class="title desc {{provision.status}} {{provision.statusVms}} {{provision.destroy? provision.destroy.status : ''}}">
|
<div class="title desc {{provision.status}} {{provision.statusVms}} {{provision.destroy? provision.destroy.status : ''}}">
|
||||||
@@ -19,10 +19,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div style="border-top: 1px dashed #fff;" class="title {{provision.status}} {{provision.statusVms}} {{provision.destroy? provision.destroy.status : ''}}">
|
<div style="border-top: 1px dashed #fff;" class="title {{provision.status}} {{provision.statusVms}} {{provision.destroy? provision.destroy.status : ''}}">
|
||||||
<div class="maintitle"><span *ngIf="provision._scenarioDoc">{{provision._scenarioDoc.title}}</span><span *ngIf="!provision._scenarioDoc">Old scenario (please destroy)</span></div>
|
<div class="maintitle"><span *ngIf="provision._scenarioDoc">{{provision._scenarioDoc.title}}</span><span *ngIf="!provision._scenarioDoc">Old scenario (please destroy)</span></div>
|
||||||
<div style="font-size: 14px;">{{provision.scenario}} (v{{provision.scenarioVersion}})</div>
|
<div style="font-size: 14px;">{{provision.scenario}} (v{{provision.scenarioVersion}}) <span *ngIf="provision.scenarioVersion !== provision._scenarioDoc.version" class="text-danger newversion">New version available!</span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="contentbox">
|
<div class="contentbox">
|
||||||
<div *ngIf="provision.vmImage">
|
<div *ngIf="provision.vmImage && provision.vmImage.vm1">
|
||||||
<div *ngIf="provision.statusVms">
|
<div *ngIf="provision.statusVms">
|
||||||
Current VMs status:
|
Current VMs status:
|
||||||
<span>
|
<span>
|
||||||
@@ -116,8 +116,8 @@
|
|||||||
<p>There are no current Running provisions</p>
|
<p>There are no current Running provisions</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h4 style="padding-top: 40px; border-bottom: 1px solid #ccc;" *ngIf="destroys && destroys.length">History</h4>
|
<h4 style="padding-top: 40px;" *ngIf="destroys && destroys.length">History <button mdbTooltip="Show/Hide History" (click)="toggleHistory()" class="lui-button"><span class="lui-icon lui-icon--triangle-{{history? 'top' : 'bottom'}}" aria-hidden="true"></span></button></h4>
|
||||||
<div *ngIf="destroys && destroys.length" class="flexcontainer">
|
<div *ngIf="destroys && destroys.length && history" class="flexcontainer">
|
||||||
<div *ngFor="let provision of destroys; let i = index" class="box">
|
<div *ngFor="let provision of destroys; let i = index" class="box">
|
||||||
<div class="title desc {{provision.status}} {{provision.destroy.status}}">
|
<div class="title desc {{provision.status}} {{provision.destroy.status}}">
|
||||||
<div mdbTooltip="{{provision.description}}" class="subtitle">{{provision.description}}</div>
|
<div mdbTooltip="{{provision.description}}" class="subtitle">{{provision.description}}</div>
|
||||||
@@ -126,7 +126,7 @@
|
|||||||
<div class="maintitle"><span *ngIf="provision._scenarioDoc">{{provision._scenarioDoc.title}}</span><span *ngIf="!provision._scenarioDoc">Old scenario (please delete)</span></div>
|
<div class="maintitle"><span *ngIf="provision._scenarioDoc">{{provision._scenarioDoc.title}}</span><span *ngIf="!provision._scenarioDoc">Old scenario (please delete)</span></div>
|
||||||
<div style="font-size: 14px;">{{provision.scenario}} (v{{provision.scenarioVersion}})</div>
|
<div style="font-size: 14px;">{{provision.scenario}} (v{{provision.scenarioVersion}})</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="contentbox">
|
<div class="contentbox destroyed">
|
||||||
<div>
|
<div>
|
||||||
Provision ({{provision.created | date: 'MMM dd, yyyy - H:mm'}}h)
|
Provision ({{provision.created | date: 'MMM dd, yyyy - H:mm'}}h)
|
||||||
-
|
-
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ h1 {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
border-top: 1px solid #ccc;
|
||||||
|
padding-top: 10px;
|
||||||
|
|
||||||
.box {
|
.box {
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
@@ -81,6 +83,9 @@ h1 {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
|
&.destroyed {
|
||||||
|
min-height: 100px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.buttons {
|
.buttons {
|
||||||
@@ -130,3 +135,11 @@ h1 {
|
|||||||
.linethrough {
|
.linethrough {
|
||||||
text-decoration: line-through;
|
text-decoration: line-through;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.newversion {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 10px;
|
||||||
|
margin-left: 8px;
|
||||||
|
padding: 2px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@ export class UserDashboardComponent implements OnInit, OnDestroy{
|
|||||||
private sub: any;
|
private sub: any;
|
||||||
private instantSubs: any;
|
private instantSubs: any;
|
||||||
public selectedprov: Object = null;
|
public selectedprov: Object = null;
|
||||||
|
history: Boolean = false;
|
||||||
|
|
||||||
|
|
||||||
logShow: boolean = false;
|
logShow: boolean = false;
|
||||||
@@ -61,6 +62,10 @@ export class UserDashboardComponent implements OnInit, OnDestroy{
|
|||||||
this.logShow = true;
|
this.logShow = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleHistory(): void {
|
||||||
|
this.history = !this.history;
|
||||||
|
}
|
||||||
|
|
||||||
onLogsClose(): void {
|
onLogsClose(): void {
|
||||||
this.selectedprov = null;
|
this.selectedprov = null;
|
||||||
this.logShow = false;
|
this.logShow = false;
|
||||||
|
|||||||
@@ -10,6 +10,28 @@
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
width: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: rgba(0,0,0,.5);
|
||||||
|
-webkit-box-shadow: 0 0 1px rgba(255,255,255,.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
p.card-text.qmicardtext {
|
||||||
|
max-height: 350px;
|
||||||
|
overflow:scroll;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
.alert-dark .alert-link {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: "Source Sans Pro",sans-serif !important;
|
font-family: "Source Sans Pro",sans-serif !important;
|
||||||
color: #162a4b !important;
|
color: #162a4b !important;
|
||||||
|
|||||||
Reference in New Issue
Block a user