Commit 9b29e26f authored by Chris Hines's avatar Chris Hines
Browse files

add in user info reporting

parent d3cdf312
Pipeline #7503 passed with stages
in 3 minutes and 3 seconds
......@@ -19,7 +19,7 @@ export class SshauthzServer {}
})
export class AuthorisationService {
// public token: BehaviorSubject<AuthToken>;
public token: AuthToken
public token: BehaviorSubject<AuthToken>;
public sshAuthzServers: BehaviorSubject<SshAuthzServer[]>;
public loggedInAuthZ: BehaviorSubject<SshAuthzServer[]>;
public loggedOutAuthZ: BehaviorSubject<SshAuthzServer[]>;
......@@ -38,7 +38,7 @@ export class AuthorisationService {
private router: Router,
private backendSelectionService: BackendSelectionService,
private location: Location) {
// this.token = new BehaviorSubject<AuthToken>(new AuthToken('',''));
this.token = new BehaviorSubject<AuthToken>(null);
this.readyToNavigate = new Subject<[Boolean,string]>();
this.readyToNavigate.next([false,'']);
this.progress = new Subject<string>();
......@@ -156,23 +156,23 @@ export class AuthorisationService {
return
}
// this.token.next(new AuthToken(tokenmatch[1],tuple[0]));
this.token = new AuthToken(tokenmatch[1],tuple[0]);
this.token.next(new AuthToken(tokenmatch[1],tuple[0]));
//this.token = new AuthToken(tokenmatch[1],tuple[0]);
}
public getCert() {
var token: AuthToken;
token = this.token.value
if (this.backendSelectionService.apiserver.value === null) {
this.backendSelectionService.apiserver.pipe(take(1)).subscribe(() => this.getCert())
}
let token = this.token
if (token.token === undefined || token.token === '' || token.token == null) {
return
this.token.pipe(take(1)).subscribe(() => this.getCert())
}
let starttime = new Date();
let newkeypair = keypair();
let publicKey = forge.pki.publicKeyFromPem(newkeypair.public);
......
......@@ -37,4 +37,6 @@ export class Strudelapp {
export class Health {
stat: string;
msg: string;
type: string;
data: any;
}
......@@ -11,6 +11,7 @@ export class Identity {
systemalerts: BehaviorSubject<Health[]>;
accountalerts: BehaviorSubject<Health[]>;
joblist: BehaviorSubject<Job[]>;
quotas: any[];
constructor( username: string, site: Computesite) {
this.username = username;
this.site = site;
......@@ -18,6 +19,7 @@ export class Identity {
this.systemalerts = new BehaviorSubject<Health[]>([]);
this.accountalerts = new BehaviorSubject<Health[]>([]);
this.joblist = new BehaviorSubject<Job[]>([]);
this.quotas = [];
}
copy_skip_catalog(): Identity {
......@@ -34,6 +36,7 @@ export class Identity {
id.site.dtn = this.site.dtn;
id.site.lscmd = this.site.lscmd;
id.site.dtnport = this.site.dtnport;
id.quotas = [];
return id;
}
displayName(): string {
......
import { Component, OnInit } from '@angular/core';
import { AuthorisationService } from '../authorisation.service';
import {TesService} from '../tes.service';
import { Router, NavigationStart } from '@angular/router';
import { timer } from 'rxjs/observable/timer';
import { AuthorisationService } from '../authorisation.service';
......@@ -14,29 +12,14 @@ import { timer } from 'rxjs/observable/timer';
})
export class KeygenComponent implements OnInit {
constructor(private router: Router, private tesService: TesService, private authService: AuthorisationService) { }
//constructor(private router: Router, private authService: AuthorisationService) { }
constructor(private router: Router, private authService: AuthorisationService) { }
ngOnInit() {
// The sequence is tricky here:
// 1) the auth service loads, examines the fragment and stores the OAuth2 Token required to generate certificates
// 2) The KeyGen component becomes visible providing a Spinner
// 3) The timer(0) executes once the component is visiable causing the authService to move onto the next phase
// 4) The authservice generates ssh keys in javascript (slow) and gets them signed (also slow)
// 5) The authservice indicates its done by setting readtToNavigate => true
// 6) the router navigates to the main page (removing the spinner etc)
// ready to navigate will be set once the keys are generated
// This component is the callback form OAuth flows.
// We hold here while the authService extract the token from the URI and generates keys
// Once the keys are generated we can navigate away from this callback component
this.authService.readyToNavigate.subscribe(readyToNavigate => this.navigate(readyToNavigate));
// use of a timer(0) means this will be executed AFTER the component is visiable (hopefully)
timer(0).subscribe( () => this.genKeys() );
}
genKeys() {
this.authService.getCert(this.authService.token)
}
activateToken(token) {
console.log('activate token called');
this.authService.getCert();
}
navigate(readyToNavigate: [Boolean,string]) {
......
......@@ -14,9 +14,9 @@
<mat-sidenav #idSideNav mode="side" opened>
<div fxLayout="column" style="height: 100%">
<div>
<mat-accordion style="width: 100%">
<mat-accordion style="width: 100%" [displayMode]="flat">
<div *ngFor="let id of (computeSitesService.appidentities | async)">
<mat-expansion-panel (opened)="selectId(id)" style="width: 100%">
<mat-expansion-panel (afterExpand)="selectId(id)" (closed)="selectId(id)" style="width: 100%">
<mat-expansion-panel-header>
<mat-panel-title>
<span fxFlex matBadge="{{ countErrors((id.systemalerts | async), (id.accountalerts | async)) }}"
......@@ -50,8 +50,15 @@
<mat-sidenav-content>
<div fxLayout="column" style="height: 100%">
<!-- the list of warning either on the computer system or the users account -->
<div *ngIf="(identitySubject | async) !== null" >
<div *ngIf="(identitySubject | async) !== null && (appSubject | async) === null" >
<div *ngIf="countErrors(((identitySubject | async).systemalerts | async),((identitySubject | async).accountalerts | async)) == 0; then noAlerts else displayAlerts"></div>
<ng-template #noAlerts><h2> No Alerts for {{ (identitySubject | async).displayName() }}</h2></ng-template>
<ng-template #displayAlerts>
<h2>Alerts for {{ (identitySubject | async).displayName() }}</h2>
<!-- This is all the system level alerts-->
<mat-list>
<div *ngFor="let h of ((identitySubject | async).systemalerts | async)">
<mat-list-item>
......@@ -63,32 +70,50 @@
</mat-list-item>
</div>
</mat-list>
</div>
<div *ngIf="(identitySubject | async) !== null" >
<!-- this is all the user level alerts that are not quota -->
<mat-list>
<div *ngFor="let h of ((identitySubject | async).accountalerts | async)">
<div *ngIf="h.type === undefined || h.type != 'quota'">
<mat-list-item>
<div *ngIf="h.stat == 'error'">
<div class='health-warn'>
<div [ngClass]="h.stat == 'error' || h.stat == 'warn' ? 'health-warn': 'health-ok'">
{{ h.msg }}
</div>
</div>
</mat-list-item>
</div>
</mat-list>
</div>
</mat-list>
<!-- The dialog asking how many resources to use to run a job -->
<div *ngIf="app !== null && identity !== null" >
<app-launch-dialog [identity]="identitySubject | async" [appSubject]="appSubject"></app-launch-dialog>
</div>
<!-- this table contains all the user level alerts that are quotas -->
<table mat-table [dataSource]="quotas" style="width: 100%">
<ng-container matColumnDef="resource">
<th mat-header-cell *matHeaderCellDef> Resource </th>
<td mat-cell *matCellDef="let quota"> {{quota.data.resource}} </td>
</ng-container>
<ng-container matColumnDef="usage">
<th mat-header-cell *matHeaderCellDef> Usage </th>
<td mat-cell *matCellDef="let quota"> {{humanKBytes(quota.data.usage)}} </td>
</ng-container>
<ng-container matColumnDef="quota">
<th mat-header-cell *matHeaderCellDef> Quota </th>
<td mat-cell *matCellDef="let quota"> {{humanKBytes(quota.data.quota)}} </td>
</ng-container>
<!-- the list of running jobs -->
<app-joblist [identitySubject]="identitySubject" [appSubject]="appSubject"></app-joblist>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [ngClass]="quotaClass(row)"></tr>
</table>
</ng-template>
</div>
<div *ngIf="(appSubject | async) !== null && (identitySubject | async) !== null" >
<app-launch-dialog [identity]="identitySubject | async" [appSubject]="appSubject"></app-launch-dialog>
<app-joblist [identitySubject]="identitySubject" [appSubject]="appSubject"></app-joblist>
</div>
</div>
</mat-sidenav-content>
</mat-sidenav-container>
......
import { Component, OnInit, NgModule, Inject, Input } from '@angular/core';
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA, MatDialogModule, MatDialogConfig } from '@angular/material';
import { MatTableDataSource, MatDialog, MatDialogRef, MAT_DIALOG_DATA, MatDialogModule, MatDialogConfig } from '@angular/material';
import { Location } from '@angular/common';
// import { HttpClientModule, HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { Observable, Subject } from 'rxjs';
import { Subscription } from 'rxjs';
import { fromEvent } from 'rxjs';
import { timer } from 'rxjs/observable/timer';
import { repeat } from 'rxjs/operators';
import { repeat, takeUntil, skip } from 'rxjs/operators';
import { Router, NavigationStart } from '@angular/router';
import {Strudelapp} from '../strudelapp';
......@@ -25,6 +26,12 @@ import { BehaviorSubject } from 'rxjs/BehaviorSubject';
export interface Quota {
resource: string;
usage: string;
quota: string;
}
......@@ -47,6 +54,9 @@ export class LauncherComponent implements OnInit {
private launchwindow: any;
private launchwindowWatcher: any;
private subscriptions: Subscription[];
public quotas: BehaviorSubject<any[]>;
displayedColumns: string[] = ['resource', 'usage', 'quota'];
constructor( public dialog: MatDialog,
public tesService: TesService,
......@@ -61,24 +71,48 @@ export class LauncherComponent implements OnInit {
this.subscriptions.push(this.computeSitesService.appidentities.subscribe(o => this.getHealth(o)));
this.appSubject = new BehaviorSubject<Strudelapp>(null);
this.identitySubject = new BehaviorSubject<Identity>(null);
this.quotas = new BehaviorSubject<any[]>([]);
}
countErrors(a: Health[], b: Health[]) {
var count: number = 0
var h: Health;
for (h of a) {
if (h.stat == 'error') {
if (h.stat == 'error' || h.stat == 'warn') {
count++;
}
}
for (h of b) {
if (h.stat == 'error') {
if (h.stat == 'error' || h.stat == 'warn') {
count++;
}
}
return count;
}
humanKBytes(n: number) {
if (n > 1024*1024*1024) {
let v = n/1024/1024/1024;
return v.toFixed(0)+'TB';
}
if (n > 1024*1024) {
let v = n/1024/1024;
return v.toFixed(0)+'GB';
}
if (n > 1024) {
let v = n/1024;
return v.toFixed(0)+'MB';
}
return '0 MB';
}
quotaClass(quota) {
if (quota.stat == 'error' || quota.stat == 'warn') {
return 'health-warn';
} else {
return 'health-ok';
}
}
navLogin(o) {
if (o.length == 0) {
......@@ -110,6 +144,13 @@ export class LauncherComponent implements OnInit {
selectId(id: Identity) {
this.identitySubject.next(id);
this.appSubject.next(null);
if (id != null) {
// We will subscribe to account alerts until the id changes (in which case we will subscribe to account alerts
// on the next id
// BUT
// since identitySubject is a behaviorsubject and already has a value, we must skip(1)
id.accountalerts.pipe(takeUntil(this.identitySubject.pipe(skip(1)))).subscribe((msgs) => { this.quotas.next(msgs.filter(q => q.type =='quota')) });
}
}
selectApp(app: Strudelapp) {
......
......@@ -26,11 +26,9 @@
</mat-select>
</mat-form-field>
</div>
<div style="width: 100%">
<button fxFlex mat-button (click)="login()">Login</button>
<div fxFlex *ngIf="(authService.loggedInAuthZ | async).length > 0">
<button fxFlex mat-button routerLink="/launch">Cancel</button>
</div>
<div fxLayout="row" fxLayoutAlign="space-around center" style="width: 100%">
<button mat-flat-button (click)="login()" color=primary style="width: 25%; text-align: center">Login</button>
<button *ngIf="(authService.loggedInAuthZ | async).length > 0" mat-flat-button routerLink="/launch" color=warn style="width: 25%; text-align: center">Cancel</button>
</div>
<div *ngIf="selected !== undefined">
{{ selected.desc }}
......
......@@ -8,12 +8,15 @@
</mat-toolbar-row>
</mat-toolbar>
<div style="height: 10%"></div>
<div fxLayout=row>
<div fxLayout=row style="width: 100vw">
<div fxFlex></div>
<div fxFlex gdAreas="header header | logout cancel " gdGap="10%" style="text-align: center">
<div gdArea="header" >Really Logout?</div>
<div gdArea="logout"> <button mat-flat-button (click)="logout()" color=warn >Logout</button></div>
<div gdArea="cancel"> <button mat-flat-button routerLink="/launch" color=primary >Cancel</button></div>
<div fxLayout="column" style="width: 25%">
<div style="text-align: center">Really Logout?</div>
<div fxLayout="row" fxLayoutAlign="space-around center" style="width: 100%">
<button mat-flat-button (click)="logout()" color=warn style="width: 25%;">Logout</button>
<button mat-flat-button routerLink="/launch" color=primary style="width: 25%;">Cancel</button>
</div>
</div>
<div fxFlex></div>
</div>
......
......@@ -280,12 +280,14 @@ addUserHealth(identity,resp) {
let ci = identity.accountalerts.value;
console.log('in add User HEalth');
for (let i of resp) {
if (i.stat != 'ok') {
let h = new Health();
h.stat = 'error';
h.stat = i.stat;
h.msg = i.message;
ci.push(h);
if (i.type != undefined) {
h.type = i.type;
h.data = i.data;
}
ci.push(h);
}
identity.accountalerts.next(ci);
console.log('id health is',identity.accountalerts.value);
......
......@@ -9,7 +9,7 @@
"appCatalogUri": "./assets/config/m3apps.dev.json",
"cancelcmd": "/usr/local/sv2/dev/sv2scancel.sh {jobid}",
"statcmd": "/usr/local/sv2/dev/sv2stat.py",
"userhealth": "/usr/local/sv2/dev/userhealth/bin/jsonuserhealth",
"userhealth": "/home/chines/userhealth/uijson.py",
"cacheturis": ["https://cachet-dev.erc.monash.edu.au/api/v1/incidents"]
},
{
......
......@@ -6,6 +6,13 @@
.health-warn {
color: mat-color($warn);
text-color: mat-color($warn);
}
.health-warn .mat-cell {
color: mat-color($warn);
}
.health-ok .mat-cell {
color: mat-color($foreground);
}
.mat-menu-panel {
.mat-menu-item {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment