Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
hpc-team
strudelv2_spa
Commits
9b29e26f
Commit
9b29e26f
authored
Jul 22, 2019
by
Chris Hines
Browse files
add in user info reporting
parent
d3cdf312
Pipeline
#7503
passed with stages
in 3 minutes and 3 seconds
Changes
11
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
src/app/authorisation.service.ts
View file @
9b29e26f
...
...
@@ -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>(n
ew AuthToken('','')
);
this
.
token
=
new
BehaviorSubject
<
AuthToken
>
(
n
ull
);
this
.
readyToNavigate
=
new
Subject
<
[
Boolean
,
string
]
>
();
this
.
readyToNavigate
.
next
([
false
,
''
]);
this
.
progress
=
new
Subject
<
string
>
();
...
...
@@ -156,38 +156,38 @@ 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
())
}
if
(
token
.
token
===
undefined
||
token
.
token
===
''
||
token
.
token
==
null
)
{
this
.
token
.
pipe
(
take
(
1
)).
subscribe
(()
=>
this
.
getCert
())
}
let
token
=
this
.
token
if
(
token
.
token
===
undefined
||
token
.
token
===
''
||
token
.
token
==
null
)
{
return
}
let
starttime
=
new
Date
();
let
newkeypair
=
keypair
();
let
publicKey
=
forge
.
pki
.
publicKeyFromPem
(
newkeypair
.
public
);
let
sshpub
=
forge
.
ssh
.
publicKeyToOpenSSH
(
publicKey
,
'
sv2@monash.edu
'
);
let
endtime
=
new
Date
();
let
starttime
=
new
Date
();
let
newkeypair
=
keypair
();
let
publicKey
=
forge
.
pki
.
publicKeyFromPem
(
newkeypair
.
public
);
let
sshpub
=
forge
.
ssh
.
publicKeyToOpenSSH
(
publicKey
,
'
sv2@monash.edu
'
);
let
endtime
=
new
Date
();
let
headers
=
new
HttpHeaders
();
let
options
=
{
headers
:
headers
,
withCredentials
:
true
};
let
headers
=
new
HttpHeaders
();
let
options
=
{
headers
:
headers
,
withCredentials
:
true
};
let
data
=
{
'
token
'
:
token
.
token
,
'
pubkey
'
:
sshpub
,
'
signing_url
'
:
token
.
sshauthzservice
.
sign
};
let
data
=
{
'
token
'
:
token
.
token
,
'
pubkey
'
:
sshpub
,
'
signing_url
'
:
token
.
sshauthzservice
.
sign
};
this
.
http
.
post
<
any
>
(
this
.
backendURI
+
'
/getcert
'
,
data
,
options
)
.
pipe
(
catchError
(
this
.
handleError
([])))
.
subscribe
(
resp
=>
this
.
makeKeyCert
(
newkeypair
.
private
,
resp
,
token
.
sshauthzservice
),
error
=>
this
.
signingError
(
error
,
token
.
sshauthzservice
));
this
.
http
.
post
<
any
>
(
this
.
backendURI
+
'
/getcert
'
,
data
,
options
)
.
pipe
(
catchError
(
this
.
handleError
([])))
.
subscribe
(
resp
=>
this
.
makeKeyCert
(
newkeypair
.
private
,
resp
,
token
.
sshauthzservice
),
error
=>
this
.
signingError
(
error
,
token
.
sshauthzservice
));
}
public
getKeys
(
id
?:
Identity
)
{
...
...
src/app/computesite.ts
View file @
9b29e26f
...
...
@@ -37,4 +37,6 @@ export class Strudelapp {
export
class
Health
{
stat
:
string
;
msg
:
string
;
type
:
string
;
data
:
any
;
}
src/app/identity.ts
View file @
9b29e26f
...
...
@@ -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
{
...
...
src/app/keygen/keygen.component.ts
View file @
9b29e26f
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
])
{
...
...
src/app/launcher/launcher.component.html
View file @
9b29e26f
...
...
@@ -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
(
open
ed)=
"selectId(id)"
style=
"width: 100%"
>
<mat-expansion-panel
(
afterExpand)=
"selectId(id)"
(clos
ed)=
"selectId(id)"
style=
"width: 100%"
>
<mat-expansion-panel-header>
<mat-panel-title>
<span
fxFlex
matBadge=
"{{ countErrors((id.systemalerts | async), (id.accountalerts | async)) }}"
...
...
@@ -49,46 +49,71 @@
</mat-sidenav>
<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"
>
<mat-list>
<div
*ngFor=
"let h of ((identitySubject | async).systemalerts | async)"
>
<mat-list-item>
<div
*ngIf=
"h.stat == 'error'"
>
<div
class=
'health-warn'
>
{{ h.msg }}
</div>
</div>
</mat-list-item>
</div>
</mat-list>
</div>
<div
*ngIf=
"(identitySubject | async) !== null"
>
<mat-list>
<div
*ngFor=
"let h of ((identitySubject | async).accountalerts | async)"
>
<mat-list-item>
<div
*ngIf=
"h.stat == 'error'"
>
<div
class=
'health-warn'
>
{{ h.msg }}
</div>
</div>
</mat-list-item>
</div>
</mat-list>
</div>
<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
>
<!-- 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>
<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>
<div
*ngIf=
"h.stat == 'error'"
>
<div
class=
'health-warn'
>
{{ h.msg }}
</div>
</div>
</mat-list-item>
</div>
</mat-list>
<!-- 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
[ngClass]=
"h.stat == 'error' || h.stat == 'warn' ? 'health-warn': 'health-ok'"
>
{{ h.msg }}
</div>
</mat-list-item>
</div>
</div>
</mat-list>
<!-- 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>
</div>
</mat-sidenav-content>
</mat-sidenav-container>
...
...
src/app/launcher/launcher.component.ts
View file @
9b29e26f
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
)
{
...
...
src/app/login/login.component.html
View file @
9b29e26f
...
...
@@ -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 }}
...
...
src/app/logout/logout.component.html
View file @
9b29e26f
...
...
@@ -8,13 +8,16 @@
</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>
<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>
</div>
...
...
src/app/tes.service.ts
View file @
9b29e26f
...
...
@@ -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
.
msg
=
i
.
message
;
ci
.
push
(
h
);
let
h
=
new
Health
();
h
.
stat
=
i
.
stat
;
h
.
msg
=
i
.
message
;
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
);
...
...
src/assets/config/computesites.json
View file @
9b29e26f
...
...
@@ -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"
]
},
{
...
...
src/health-warn.scss
View file @
9b29e26f
...
...
@@ -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
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment