aboutsummaryrefslogtreecommitdiffstats
path: root/webapp/src/app/@core-xds/services/target.service.ts
diff options
context:
space:
mode:
Diffstat (limited to 'webapp/src/app/@core-xds/services/target.service.ts')
-rw-r--r--webapp/src/app/@core-xds/services/target.service.ts285
1 files changed, 285 insertions, 0 deletions
diff --git a/webapp/src/app/@core-xds/services/target.service.ts b/webapp/src/app/@core-xds/services/target.service.ts
new file mode 100644
index 0000000..9c995ea
--- /dev/null
+++ b/webapp/src/app/@core-xds/services/target.service.ts
@@ -0,0 +1,285 @@
+/**
+* @license
+* Copyright (C) 2018 "IoT.bzh"
+* Author Sebastien Douheret <sebastien@iot.bzh>
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import { Injectable, SecurityContext, isDevMode } from '@angular/core';
+import { Observable } from 'rxjs/Observable';
+import { Subject } from 'rxjs/Subject';
+import { BehaviorSubject } from 'rxjs/BehaviorSubject';
+
+import { XDSAgentService, IXDSTargetConfig, IXDSTargetTerminal } from '../services/xdsagent.service';
+
+/* FIXME: syntax only compatible with TS>2.4.0
+export enum TargetTypeEnum {
+ UNSET = '',
+ STANDARD: 'standard',
+}
+*/
+export type TargetTypeEnum = '' | 'standard';
+export const TargetType = {
+ UNSET: <TargetTypeEnum>'',
+ STANDARD: <TargetTypeEnum>'standard',
+};
+
+export const TargetTypes = [
+ { value: TargetType.STANDARD, display: 'Standard' },
+];
+
+export const TargetStatus = {
+ ErrorConfig: 'ErrorConfig',
+ Disable: 'Disable',
+ Enable: 'Enable',
+};
+
+export type TerminalTypeEnum = '' | 'ssh';
+export const TerminalType = {
+ UNSET: <TerminalTypeEnum>'',
+ SSH: <TerminalTypeEnum>'ssh',
+};
+
+export interface ITarget extends IXDSTargetConfig {
+ isUsable?: boolean;
+}
+
+export interface ITerminal extends IXDSTargetTerminal {
+ targetID?: string;
+}
+
+export interface ITerminalOutput {
+ termID: string;
+ timestamp: string;
+ stdout: string;
+ stderr: string;
+}
+
+export interface ITerminalExit {
+ termID: string;
+ timestamp: string;
+ code: number;
+ error: string;
+}
+
+@Injectable()
+export class TargetService {
+ public targets$: Observable<ITarget[]>;
+ public curTarget$: Observable<ITarget>;
+ public terminalOutput$ = <Subject<ITerminalOutput>>new Subject();
+ public terminalExit$ = <Subject<ITerminalExit>>new Subject();
+
+ private _tgtsList: ITarget[] = [];
+ private tgtsSubject = <BehaviorSubject<ITarget[]>>new BehaviorSubject(this._tgtsList);
+ private _current: ITarget;
+ private curTgtSubject = <BehaviorSubject<ITarget>>new BehaviorSubject(this._current);
+ private curServerID;
+ private termSocket: SocketIOClient.Socket;
+
+ constructor(private xdsSvr: XDSAgentService) {
+ this._current = null;
+ this.targets$ = this.tgtsSubject.asObservable();
+ this.curTarget$ = this.curTgtSubject.asObservable();
+
+ this.xdsSvr.XdsConfig$.subscribe(cfg => {
+ if (!cfg || cfg.servers.length < 1) {
+ return;
+ }
+
+ // FIXME support multiple server
+ this.curServerID = cfg.servers[0].id;
+
+ // Load initial targets list
+ this.xdsSvr.getTargets(this.curServerID).subscribe((targets) => {
+ this._tgtsList = [];
+ targets.forEach(p => {
+ this._addTarget(p, true);
+ });
+
+ // TODO: get previous val from xds-config service / cookie
+ if (this._tgtsList.length > 0) {
+ this._current = this._tgtsList[0];
+ this.curTgtSubject.next(this._current);
+ }
+
+ this.tgtsSubject.next(this._tgtsList);
+ });
+ });
+
+ // Add listener on targets creation, deletion and change events
+ this.xdsSvr.onTargetAdd().subscribe(tgt => this._addTarget(tgt));
+ this.xdsSvr.onTargetDelete().subscribe(tgt => this._delTarget(tgt));
+ this.xdsSvr.onTargetChange().subscribe(tgt => this._updateTarget(tgt));
+
+ // Register events to forward terminal Output and Exit
+ this.xdsSvr.onSocketConnect().subscribe(socket => {
+ this.termSocket = socket;
+
+ // Handle terminal output
+ socket.on('term:output', data => {
+ const termOut = <ITerminalOutput>{
+ termID: data.termID,
+ timestamp: data.timestamp,
+ stdout: atob(data.stdout),
+ stderr: atob(data.stderr),
+ };
+ this.terminalOutput$.next(termOut);
+ });
+
+ // Handle terminal exit event
+ socket.on('term:exit', data => {
+ this.terminalExit$.next(Object.assign({}, <ITerminalExit>data));
+ });
+
+ });
+ }
+
+ setCurrent(p: ITarget): ITarget | undefined {
+ if (!p) {
+ this._current = null;
+ return undefined;
+ }
+ return this.setCurrentById(p.id);
+ }
+
+ setCurrentById(id: string): ITarget | undefined {
+ const p = this._tgtsList.find(item => item.id === id);
+ if (p) {
+ this._current = p;
+ this.curTgtSubject.next(this._current);
+ }
+ return this._current;
+ }
+
+ getCurrent(): ITarget {
+ return this._current;
+ }
+
+ getTargetById(id: string): ITarget | undefined {
+ const t = this._tgtsList.find(item => item.id === id);
+ return t;
+ }
+
+ add(tgt: ITarget): Observable<ITarget> {
+ return this.xdsSvr.addTarget(this.curServerID, tgt);
+ }
+
+ delete(tgt: ITarget): Observable<ITarget> {
+ const idx = this._getTargetIdx(tgt.id);
+ const delTgt = tgt;
+ if (idx === -1) {
+ throw new Error('Invalid target id (id=' + tgt.id + ')');
+ }
+ return this.xdsSvr.deleteTarget(this.curServerID, tgt.id)
+ .map(res => delTgt);
+ }
+
+ setSettings(tgt: ITarget): Observable<ITarget> {
+ return this.xdsSvr.updateTarget(this.curServerID, tgt);
+ }
+
+ terminalOpen(tgtID: string, termID: string, cfg?: IXDSTargetTerminal): Observable<IXDSTargetTerminal> {
+ if (termID === '' || termID === undefined) {
+ // create a new terminal when no termID is set
+ if (cfg === undefined) {
+ cfg = <IXDSTargetTerminal>{
+ name: 'ssh to ' + this.getTargetById(tgtID).name,
+ type: TerminalType.SSH,
+ };
+ }
+ return this.xdsSvr.createTerminalTarget(this.curServerID, tgtID, cfg)
+ .flatMap(res => {
+ return this.xdsSvr.openTerminalTarget(this.curServerID, tgtID, res.id);
+ });
+ } else {
+ return this.xdsSvr.openTerminalTarget(this.curServerID, tgtID, termID);
+ }
+ }
+
+ terminalClose(tgtID, termID: string): Observable<IXDSTargetTerminal> {
+ return this.xdsSvr.closeTerminalTarget(this.curServerID, tgtID, termID);
+ }
+
+ terminalWrite(data: string) {
+ if (this.termSocket) {
+ this.termSocket.emit('term:input', btoa(data));
+ }
+ }
+
+ terminalResize(tgtID, termID: string, cols, rows: number): Observable<IXDSTargetTerminal> {
+ return this.xdsSvr.resizeTerminalTarget(this.curServerID, tgtID, termID, cols, rows);
+ }
+
+ /*** Private functions ***/
+
+ private _isUsableTarget(p) {
+ return p && (p.status === TargetStatus.Enable);
+ }
+
+ private _getTargetIdx(id: string): number {
+ return this._tgtsList.findIndex((item) => item.id === id);
+ }
+
+ private _addTarget(tgt: ITarget, noNext?: boolean): ITarget {
+
+ tgt.isUsable = this._isUsableTarget(tgt);
+
+ // add new target
+ this._tgtsList.push(tgt);
+
+ // sort target array
+ this._tgtsList.sort((a, b) => {
+ if (a.name < b.name) {
+ return -1;
+ }
+ if (a.name > b.name) {
+ return 1;
+ }
+ return 0;
+ });
+
+ if (!noNext) {
+ this.tgtsSubject.next(this._tgtsList);
+ }
+
+ return tgt;
+ }
+
+ private _delTarget(tgt: ITarget) {
+ const idx = this._tgtsList.findIndex(item => item.id === tgt.id);
+ if (idx === -1) {
+ if (isDevMode) {
+ /* tslint:disable:no-console */
+ console.log('Warning: Try to delete target unknown id: tgt=', tgt);
+ }
+ return;
+ }
+ const delId = this._tgtsList[idx].id;
+ this._tgtsList.splice(idx, 1);
+ if (delId === this._current.id) {
+ this.setCurrent(this._tgtsList[0]);
+ }
+ this.tgtsSubject.next(this._tgtsList);
+ }
+
+ private _updateTarget(tgt: ITarget) {
+ const i = this._getTargetIdx(tgt.id);
+ if (i >= 0) {
+ this._tgtsList[i].status = tgt.status;
+ this._tgtsList[i].isUsable = this._isUsableTarget(tgt);
+ this.tgtsSubject.next(this._tgtsList);
+ }
+ }
+
+}