diff --git a/src/app/job/job.component.html b/src/app/job/job.component.html index b708c3c8cac304add0491076dde2287f5ece35cc..e11bec3f7edb4dec5ed7af8e93b2bbaa1a7d4589 100644 --- a/src/app/job/job.component.html +++ b/src/app/job/job.component.html @@ -1,6 +1,6 @@ <mat-card> <mat-card-title>{{ jobdata.name }}</mat-card-title> - <div *ngIf="(jobdata.connectionState === undefined || jobdata.connectionState == 0)"> + <div *ngIf="jobdata.connectionState == 0"> <div gdAreas="name name name name| status resources space buttons" gdColumns="20% 60% auto 20%"> diff --git a/src/app/job/job.component.ts b/src/app/job/job.component.ts index c3b76f49d53fe3b0cb0823002aa3451170f8829a..55a6767db3700c2c33d1c1936efb02a7b1de1312 100644 --- a/src/app/job/job.component.ts +++ b/src/app/job/job.component.ts @@ -19,6 +19,7 @@ export class JobComponent implements OnInit { constructor(private tesService: TesService, private strudelAppsService: StrudelappsService) { + } ngOnInit() { diff --git a/src/app/launcher/launcher.component.html b/src/app/launcher/launcher.component.html index 1e6dfdcbb1f1e4b3d77dac395e846bf7c42156e4..768bba7e6f8e208fa6bd5f232d39b56e3aa19f5a 100644 --- a/src/app/launcher/launcher.component.html +++ b/src/app/launcher/launcher.component.html @@ -41,11 +41,17 @@ Advanced Options </mat-expansion-panel-header> Select an API server - <mat-select (selectionChange)="tesService.apiserver.next($event.value)" [value]="tesService.apiserver|async"> + <!--(selectionChange)="tesService.setApiServer($event.value)" [compareWith]="tesService.compareServers"--> + <mat-select [ngModel]="selectedApiServer" (selectionChange)="tesService.setApiServer($event.value)"> <mat-option *ngFor="let apis of tesService.apiservers|async" [value]="apis"> {{ apis.name }} </mat-option> </mat-select> + <!--<mat-select [ngModel]="tesService.apiserver | async" (ngModelChange)="tesService.setApiServer($event)"> + <mat-option *ngFor="let apis of tesService.apiservers|async" [value]="apis"> + {{ apis.name }} + </mat-option> + </mat-select>--> </mat-expansion-panel> </mat-accordion> </mat-sidenav> diff --git a/src/app/launcher/launcher.component.ts b/src/app/launcher/launcher.component.ts index 89ab94026a4b690df50940531dbb4b31f20efa18..608faa87feaf9ccf282957277c5cebfc438a40c0 100644 --- a/src/app/launcher/launcher.component.ts +++ b/src/app/launcher/launcher.component.ts @@ -41,6 +41,7 @@ export class LauncherComponent implements OnInit { public sshauthzservers: SshAuthzServer[]; private launchwindow: any; private launchwindowWatcher: any; + public selectedApiServer: any; constructor( public dialog: MatDialog, public tesService: TesService, @@ -49,6 +50,7 @@ export class LauncherComponent implements OnInit { ) { this.authService.sshAuthzServers.subscribe(o => {this.sshauthzservers = o}); this.identitySubject = new BehaviorSubject(undefined); + this.tesService.apiserver.subscribe((s) => this.selectedApiServer = s); } ngOnInit() { @@ -56,6 +58,7 @@ export class LauncherComponent implements OnInit { setTimeout( () => this.authService.updateAgentContents() ) } + //updateIdentities(identities) { // this.identities = identities; // console.log(this.identities); diff --git a/src/app/tes.service.ts b/src/app/tes.service.ts index 3466aefc240919b392d9fd4d321e2c8344aa2769..662bf7ad58b34b884492e20f351b6cc093a9fdc5 100644 --- a/src/app/tes.service.ts +++ b/src/app/tes.service.ts @@ -32,8 +32,10 @@ Its also responsible for querying a compute site for running jobs */ providedIn: 'root', }) export class TesService { -public Base=environment.tesurl; -private twsproxy = environment.twsurl; +public Base: string; +//public Base=environment.tesurl; +//private twsproxy = environment.twsurl; +private twsproxy: string; // public Base='http://localhost:5000'; public statusMsg: BehaviorSubject<any>; public jobs: any[]; @@ -58,9 +60,8 @@ public apiservers: BehaviorSubject<APIServer[]>; this.busy = new BehaviorSubject<boolean>(false); // this.joblist = new BehaviorSubject<{[id: string]: Job[]}>({}); this.joblist = new BehaviorSubject<Job[]>([]); - this.apiserver = new BehaviorSubject<APIServer>(undefined); + this.apiserver = new BehaviorSubject<APIServer>(null); this.apiservers = new BehaviorSubject<APIServer[]>([]); - this.apiserver.subscribe((r)=>this.onApiServerChange(r)); this.timerSubscription = null; this.appwindowWatcher = null; @@ -73,14 +74,56 @@ public setStatusMsg(statusMsg: BehaviorSubject<any>) { this.statusMsg = statusMsg; } -private onApiServerChange(r) { +public compareServer(a: APIServer, b: APIServer) { + if (a.name == b.name) { + return true; + } + return false; +} + +private loadLastApiServer() { + var lastserver: APIServer; + try { + console.log('loading api server from local storage'); + lastserver = JSON.parse(localStorage.getItem('apiserver')); + } catch { + lastserver = undefined; + } + // If we got a value for the last server used, we need to search the list of available servers for a match + if (lastserver != undefined) { + for (let s of this.apiservers.value) { + if (lastserver.name == s.name) { + this.apiserver.next(s); + } + } + } + // If we didn't get a match, we'll just pick the first available server + if (this.apiserver.value == null) { + if (this.apiservers.value.length > 0) { + this.apiserver.next(this.apiservers.value[0]); + } else { + // If there are NO available backed servers ... well we shouldn't get here. + // In the future if we start detecting backends as down for maintainnce it might happen + this.statusMsg.next("There was an error selecting a backend API server. Please try reloading"); + return; + } + } this.Base = this.apiserver.value.tes; this.twsproxy = this.apiserver.value.tws; + console.log('load succeeded'); +} +private saveLastApiServer(s: APIServer) { + console.log('saving api server value to local storage') + localStorage.setItem('apiserver', JSON.stringify(s)); } setApiServer(server: APIServer) { console.log('calling setAPIServer'); this.apiserver.next(server); + console.log(this.apiserver); + this.Base = this.apiserver.value.tes; + this.twsproxy = this.apiserver.value.tws; + this.saveLastApiServer(this.apiserver.value) } getAPIServers() { @@ -92,7 +135,7 @@ private onApiServerChange(r) { private updateAPIServers(resp) { this.apiservers.next(<APIServer[]>resp); - this.apiserver.next(this.apiservers.value[0]); + this.loadLastApiServer(); } @@ -110,17 +153,52 @@ private onApiServerChange(r) { return params.toString(); } + updateJoblist(resp, identity: Identity) { - let joblist = <Job[]>resp; - let alljobs = this.joblist.value; - let i = 0; - for (let j of joblist) { - j.app = this.strudelappsService.getApp(j.name,identity.site.appCatalog.value); - j.identity = identity; + // resp contains a javascript represnetiation of a list of jobs + // We want to update the joblist BUT we don't want to create new Job objects + // instead we want to reuse existing job objects removing any which are no longer valid + // and adding any new ones. We also want the list sorted from largest jobid to smallest (oldest job) + // The sort is lexographic since sometimes jobids are a string rather than a number + + var joblist: Job[] = [] + var jobquery: Job[] = <Job[]>resp; + var lastjoblist: Job[] = this.joblist.value; + var qjobids: any[] = []; + var jobids: any[] = []; + var j: Job + + for (j of jobquery) { + qjobids.push(j.jobid); + } + for (j of lastjoblist) { + if (qjobids.indexOf(j.jobid) != -1) { + if (jobids.indexOf(j.jobid) == -1) { + joblist.push(j); + jobids.push(j.jobid); + } + } + } + for (j of jobquery) { + if (jobids.indexOf(j.jobid) == -1) { + joblist.push(j); + jobids.push(j.jobid); + } + } + + for (j of joblist) { + if (j.app === undefined) { + j.app = this.strudelappsService.getApp(j.name,identity.site.appCatalog.value); + } + if (j.identity == undefined) { + j.identity = identity; + } + if (j.connectionState == undefined) { + j.connectionState = 0; + } } + joblist = joblist.sort((a,b) => (a.jobid < b.jobid)? 1:-1); this.joblist.next(joblist) - // alljobs[identity.repr()] = joblist; - // this.joblist.next(alljobs); } private getBatchInterfaceError(error: any) { @@ -258,12 +336,17 @@ private onApiServerChange(r) { let batchhost = job.batch_host; let params = new URLSearchParams; let headers = new HttpHeaders(); + console.log('setting Connectionstate 2'); job.connectionState = 2; + + // let sleepDuration = 20; + //var now = new Date().getTime(); + //while(new Date().getTime() < now + sleepDuration){ /* do nothing */ } + let options = { headers: headers, withCredentials: true}; params.set('cmd',JSON.stringify(job.app.paramscmd)); let paramstr = params.toString(); this.http.post<string>(this.Base+'/createtunnel/'+username+'/'+loginhost+'/'+batchhost+'?'+paramstr, job.appinst, options) - // .pipe(catchError(this.handleError)) .subscribe(() => { this.getAppUrl(job) } ) } @@ -291,6 +374,14 @@ private onApiServerChange(r) { console.log('window loc is ',windowloc); // let windowloc = this.router.config this.appwindow = window.open(windowloc); + console.log('check if the appwindow is valid',this.appwindow); + if (this.appwindow == null) { + this.statusMsg.next('It looks like a window failed to open. Please check your popup blocker settings (Strudel 2 needs to be able to open a window to your application'); + return; + } + if (this.appwindow.closed) { + console.log('appwindow failed to open'); + } if (!(this.appwindowWatcher === null)) { this.appwindowWatcher.unsubscribe() }