You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datalab.apache.org by dg...@apache.org on 2020/12/03 13:57:29 UTC
[incubator-datalab] 02/05: Merge ODAHU UI to develop
This is an automated email from the ASF dual-hosted git repository.
dgnatyshyn pushed a commit to branch develop_odahu_UI
in repository https://gitbox.apache.org/repos/asf/incubator-datalab.git
commit e1c199268779dfd2af18b949dc5b98361f50dc7a
Author: Dmytro_Gnatyshyn <di...@ukr.net>
AuthorDate: Wed Dec 2 15:56:40 2020 +0200
Merge ODAHU UI to develop
---
.../create-legion-cluster.component.html | 93 +++++++++++++++++++
.../create-legion-cluster.component.scss | 7 ++
.../create-legion-cluster.component.ts | 103 +++++++++++++++++++++
.../create-legion-claster/index.ts | 45 +++++++++
.../app/administration/legion-deployment/index.ts | 50 ++++++++++
.../legion-deployment-data.service.ts | 27 ++++++
.../legion-deployment.component.html | 42 +++++++++
.../legion-deployment.component.scss} | 0
.../legion-deployment.component.ts | 59 ++++++++++++
.../legion-list/legion-list.component.html | 94 +++++++++++++++++++
.../legion-list/legion-list.component.scss | 77 +++++++++++++++
.../legion-list/legion-list.component.ts | 48 ++++++++++
.../management-grid/management-grid.component.html | 2 +-
.../app/core/services/legion-deployment.service.ts | 35 +++++++
.../resources-grid/resources-grid.component.html | 30 +++++-
.../resources-grid/resources-grid.component.scss | 13 +++
.../resources-grid/resources-grid.component.ts | 20 +++-
.../resources-grid/resources-grid.model.ts | 86 +++++++++++++++++
.../modal-dialog/odahu-action-dialog/index.ts | 15 +++
.../odahu-action-dialog.component.ts | 50 ++++++++++
20 files changed, 891 insertions(+), 5 deletions(-)
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/create-legion-claster/create-legion-cluster.component.html b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/create-legion-claster/create-legion-cluster.component.html
new file mode 100644
index 0000000..ff04e55
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/create-legion-claster/create-legion-cluster.component.html
@@ -0,0 +1,93 @@
+<div class="create-legion-cluster" id="dialog-box">
+ <header class="dialog-header">
+ <h4 class="modal-title">Create Odahu cluster</h4>
+ <button type="button" class="close" (click)="dialogRef.close()">×</button>
+ </header>
+ <div class="dialog-content selection">
+ <div id="scrolling" class="content-box mat-reset scrolling-content">
+ <form [formGroup]="createLegionClusterForm" *ngIf="createLegionClusterForm" novalidate>
+ <div class="control-group">
+ <label class="label">Select project</label>
+ <div class="control selector-wrapper">
+ <mat-form-field>
+ <mat-label>Select project</mat-label>
+ <mat-select formControlName="project" panelClass="create-resources-dialog">
+ <mat-option *ngFor="let project of projects" [value]="project.name" (click)="setEndpoints(project)">
+ {{ project.name }}</mat-option>
+ <mat-option *ngIf="!projects.length" class="multiple-select ml-10" disabled>
+ No projects for creating Odahu clusters
+ </mat-option>
+ </mat-select>
+ <button class="caret">
+ <i class="material-icons">keyboard_arrow_down</i>
+ </button>
+ </mat-form-field>
+ </div>
+ </div>
+
+ <div class="control-group">
+ <label class="label">Select endpoint</label>
+ <div class="control selector-wrapper" [ngClass]="{ 'not-active' : !endpoints.length }">
+ <mat-form-field>
+ <mat-label>Select endpoint</mat-label>
+ <mat-select formControlName="endpoint" disableOptionCentering [disabled]="!endpoints.length"
+ panelClass="create-resources-dialog">
+ <mat-option *ngFor="let endpoint of endpoints" [value]="endpoint">
+ {{ endpoint }}
+ </mat-option>
+ <mat-option *ngIf="!endpoints.length" class="multiple-select ml-10" disabled>Endpoints list is empty</mat-option>
+ </mat-select>
+ <button class="caret">
+ <i class="material-icons">keyboard_arrow_down</i>
+ </button>
+ </mat-form-field>
+ </div>
+ </div>
+
+ <div class="control-group name-control">
+ <label class="label">Name</label>
+ <div class="control">
+ <input type="text" class="form-control" placeholder="Enter Name" formControlName="name">
+ <span class="error" *ngIf="!createLegionClusterForm.controls.name.valid && createLegionClusterForm.controls.name.dirty && !createLegionClusterForm.controls.name.hasError('duplication')">
+ Odahu cluster name can only contain letters and numbers
+ </span>
+ <span class="error" *ngIf="createLegionClusterForm.controls.name.hasError('duplication')">This Odahu cluster name already exists.</span>
+ </div>
+ </div>
+
+ <div class="control-group name-control">
+ <label class="label">Custom tag</label>
+ <div class="control">
+ <input type="text" class="form-control" placeholder="Enter custom tag" formControlName="custom_tag">
+ <span class="error"
+ *ngIf="!createLegionClusterForm.controls.custom_tag.valid && createLegionClusterForm.controls.custom_tag.dirty">
+ Custom tag can only contain letters, numbers, hyphens and '_' but can not end with special characters</span>
+ </div>
+ </div>
+
+<!-- <div class="control-group">-->
+<!-- <label class="label" [ngStyle]="!createLegionClusterForm.controls.useExistingClusterUrl.value && {'width': '100%' }">-->
+<!-- <input type="checkbox" formControlName="useExistingClusterUrl"/> Use existing k8s cluster-->
+<!-- </label>-->
+<!-- <div class="control" *ngIf="createLegionClusterForm.controls.useExistingClusterUrl.value">-->
+<!-- <input type="text" class="form-control"-->
+<!-- formControlName="existingClusterUrl" placeholder="Enter ODAHU k8s cluster URL"/>-->
+<!-- <span class="error url-error">-->
+<!-- <span *ngIf="!createLegionClusterForm.controls.existingClusterUrl.valid">Please enter valid cluster URL</span>-->
+<!-- </span>-->
+<!-- </div>-->
+<!-- </div>-->
+
+ <div class="text-center m-top-30">
+ <button mat-raised-button type="button" class="butt action" (click)="dialogRef.close()">Cancel</button>
+ <button mat-raised-button type="button" class="butt butt-success action"
+ [disabled]="!createLegionClusterForm.valid" (click)="createOdahuCluster(createLegionClusterForm.value)">
+ Create
+ </button>
+ </div>
+
+ </form>
+ </div>
+ </div>
+</div>
+
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/create-legion-claster/create-legion-cluster.component.scss b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/create-legion-claster/create-legion-cluster.component.scss
new file mode 100644
index 0000000..324f0df
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/create-legion-claster/create-legion-cluster.component.scss
@@ -0,0 +1,7 @@
+.create-legion-cluster{
+ .error{
+ position: absolute;
+ left: 0;
+ top: 36px;
+ }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/create-legion-claster/create-legion-cluster.component.ts b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/create-legion-claster/create-legion-cluster.component.ts
new file mode 100644
index 0000000..7aa941d
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/create-legion-claster/create-legion-cluster.component.ts
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 { Component, OnInit, Inject } from '@angular/core';
+import { FormGroup, FormBuilder, Validators } from '@angular/forms';
+import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { ToastrService } from 'ngx-toastr';
+
+import { Project } from '../../project/project.component';
+import { ProjectService, LegionDeploymentService } from '../../../core/services';
+
+import { DICTIONARY } from '../../../../dictionary/global.dictionary';
+import {CheckUtils, PATTERNS} from '../../../core/util';
+
+
+@Component({
+ selector: 'create-legion-cluster',
+ templateUrl: 'create-legion-cluster.component.html',
+ styleUrls: ['./create-legion-cluster.component.scss']
+})
+
+export class CreateLegionClusterComponent implements OnInit {
+ readonly DICTIONARY = DICTIONARY;
+ public createLegionClusterForm: FormGroup;
+
+ projects: Project[] = [];
+ endpoints: Array<String> = [];
+
+ constructor(
+ @Inject(MAT_DIALOG_DATA) public data: any,
+ public toastr: ToastrService,
+ public dialogRef: MatDialogRef<CreateLegionClusterComponent>,
+ private _fb: FormBuilder,
+ private projectService: ProjectService,
+ private legionDeploymentService: LegionDeploymentService,
+ ) {
+ }
+
+ ngOnInit() {
+ this.getUserProjects();
+ this.initFormModel();
+ }
+
+ public getUserProjects(): void {
+ this.projectService.getUserProjectsList(true).subscribe((projects: any) => {
+ this.projects = projects.filter(project => {
+ return project.endpoints.length > project.odahu.filter(od => od.status !== 'FAILED' && od.status !== 'TERMINATED').length; }
+ );
+ });
+ }
+
+ public setEndpoints(project): void {
+ this.endpoints = project.endpoints
+ .filter(e => e.status === 'RUNNING' && !this.data.some(odahu => odahu.status !== 'FAILED'
+ && odahu.status !== 'TERMINATED'
+ && odahu.endpoint === e.name
+ && odahu.project === project.name)
+ )
+ .map(e => e.name);
+ }
+
+ private initFormModel(): void {
+ this.createLegionClusterForm = this._fb.group({
+ name: ['', [Validators.required, Validators.pattern(PATTERNS.namePattern), this.checkDuplication.bind(this)]],
+ project: ['', Validators.required],
+ endpoint: ['', [Validators.required]],
+ custom_tag: ['', [Validators.pattern(PATTERNS.namePattern)]]
+ });
+ }
+
+ private createOdahuCluster(value): void {
+ this.dialogRef.close();
+ this.legionDeploymentService.createOdahuNewCluster(value).subscribe(() => {
+ this.toastr.success('Odahu cluster creation is processing!', 'Success!');
+ }, error => this.toastr.error(error.message || 'Odahu cluster creation failed!', 'Oops!')
+ );
+ }
+
+ private checkDuplication(control) {
+ if (control && control.value) {
+ for (let index = 0; index < this.data.length; index++) {
+ if (CheckUtils.delimitersFiltering(control.value) === CheckUtils.delimitersFiltering(this.data[index].name))
+ return { duplication: true };
+ }
+ }
+ }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/create-legion-claster/index.ts b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/create-legion-claster/index.ts
new file mode 100644
index 0000000..a3adc9c
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/create-legion-claster/index.ts
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+
+import { MaterialModule } from '../../../shared/material.module';
+import { FormControlsModule } from '../../../shared/form-controls';
+import { KeysPipeModule, UnderscorelessPipeModule } from '../../../core/pipes';
+import {CreateLegionClusterComponent} from "./create-legion-cluster.component";
+
+export * from './create-legion-cluster.component';
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FormsModule,
+ ReactiveFormsModule,
+ FormControlsModule,
+ MaterialModule,
+ KeysPipeModule,
+ UnderscorelessPipeModule
+ ],
+ declarations: [CreateLegionClusterComponent],
+ entryComponents: [CreateLegionClusterComponent],
+ exports: [CreateLegionClusterComponent]
+})
+export class CreateLegionClusterModule { }
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/index.ts b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/index.ts
new file mode 100644
index 0000000..fa28e36
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/index.ts
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+
+import { MaterialModule } from '../../shared/material.module';
+import { FormControlsModule } from '../../shared/form-controls';
+import { UnderscorelessPipeModule } from '../../core/pipes/underscoreless-pipe';
+
+import {BubbleModule} from "../../shared/bubble";
+import {LegionDeploymentComponent} from "./legion-deployment.component";
+import {LegionDeploymentDataService} from "./legion-deployment-data.service";
+import {LegionListComponent} from "./legion-list/legion-list.component";
+import {CreateLegionClusterModule} from "./create-legion-claster";
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FormsModule,
+ ReactiveFormsModule,
+ MaterialModule,
+ FormControlsModule,
+ UnderscorelessPipeModule,
+ BubbleModule,
+ CreateLegionClusterModule
+ ],
+ declarations: [LegionDeploymentComponent, LegionListComponent],
+ entryComponents: [],
+ providers: [LegionDeploymentDataService],
+ exports: [LegionDeploymentComponent]
+})
+export class LegionDeploymentModule { }
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-deployment-data.service.ts b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-deployment-data.service.ts
new file mode 100644
index 0000000..83dfeaf
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-deployment-data.service.ts
@@ -0,0 +1,27 @@
+import { Injectable } from '@angular/core';
+import {BehaviorSubject, Observable} from 'rxjs';
+import {LegionDeploymentService} from '../../core/services';
+
+
+@Injectable({
+ providedIn: 'root'
+})
+
+export class LegionDeploymentDataService {
+ _legionClasters = new BehaviorSubject<any>(null);
+
+ constructor(private legionDeploymentService: LegionDeploymentService) {
+ this.getClastersList();
+ }
+
+ public updateClasters(): void {
+ this.getClastersList();
+ }
+
+ private getClastersList(): void {
+ this.legionDeploymentService.getOduhuClustersList().subscribe(
+ (response: any ) => {
+ return this._legionClasters.next(response);
+ });
+ }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-deployment.component.html b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-deployment.component.html
new file mode 100644
index 0000000..1368180
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-deployment.component.html
@@ -0,0 +1,42 @@
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you 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.
+ -->
+
+
+<div class="base-retreat">
+ <div class="sub-nav">
+ <div>
+ <button mat-raised-button class="butt butt-create" (click)="createLegionCluster()">
+ <i class="material-icons">add</i>Create new
+ </button>
+ </div>
+ <div>
+ <button mat-raised-button class="butt" (click)="refreshGrid()">
+ <i class="material-icons">autorenew</i>Refresh
+ </button>
+ </div>
+ </div>
+
+ <mat-divider></mat-divider>
+
+ <div>
+ <legion-list>
+ </legion-list>
+ </div>
+</div>
+
diff --git a/services/self-service/src/test/java/com/epam/datalab/backendapi/.gitkeep b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-deployment.component.scss
similarity index 100%
rename from services/self-service/src/test/java/com/epam/datalab/backendapi/.gitkeep
rename to services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-deployment.component.scss
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-deployment.component.ts b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-deployment.component.ts
new file mode 100644
index 0000000..263debf
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-deployment.component.ts
@@ -0,0 +1,59 @@
+import { Component, OnInit } from '@angular/core';
+import {LegionDeploymentDataService} from './legion-deployment-data.service';
+import {Subscription} from 'rxjs';
+import {MatDialog} from '@angular/material/dialog';
+import {ToastrService} from 'ngx-toastr';
+import {CreateLegionClusterComponent} from './create-legion-claster/create-legion-cluster.component';
+import {HealthStatusService, LegionDeploymentService} from '../../core/services';
+
+export interface OdahuCluster {
+ name: string;
+ project: string;
+ endpoint: string;
+}
+
+@Component({
+ selector: 'dlab-legion-deployment',
+ templateUrl: './legion-deployment.component.html',
+ styleUrls: ['./legion-deployment.component.scss']
+})
+export class LegionDeploymentComponent implements OnInit {
+
+ private legionClastersList: any[];
+ private subscriptions: Subscription = new Subscription();
+ private healthStatus;
+
+ constructor(
+ private legionDeploymentDataService: LegionDeploymentDataService,
+ private dialog: MatDialog,
+ public toastr: ToastrService,
+ public legionDeploymentService: LegionDeploymentService,
+ private healthStatusService: HealthStatusService,
+ ) { }
+
+ ngOnInit() {
+ this.getEnvironmentHealthStatus();
+ this.subscriptions.add(this.legionDeploymentDataService._legionClasters.subscribe(
+ (value) => {
+ if (value) this.legionClastersList = value;
+ }));
+ this.refreshGrid();
+ }
+
+ public createLegionCluster(): void {
+ this.dialog.open(CreateLegionClusterComponent, { data: this.legionClastersList, panelClass: 'modal-lg' })
+ .afterClosed().subscribe((result) => {
+ result && this.getEnvironmentHealthStatus();
+ this.refreshGrid();
+ });
+ }
+
+ private getEnvironmentHealthStatus(): void {
+ this.healthStatusService.getEnvironmentHealthStatus()
+ .subscribe((result: any) => this.healthStatus = result);
+ }
+
+ public refreshGrid(): void {
+ this.legionDeploymentDataService.updateClasters();
+ }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-list/legion-list.component.html b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-list/legion-list.component.html
new file mode 100644
index 0000000..a670b2f
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-list/legion-list.component.html
@@ -0,0 +1,94 @@
+
+<table mat-table [dataSource]="dataSource" class="legion-clasters-table mat-elevation-z6 selection">
+ <ng-container matColumnDef="project">
+ <th mat-header-cell *matHeaderCellDef class="project"> Project name </th>
+ <td mat-cell *matCellDef="let element" class="project">
+ <span *ngIf="element && element.project">{{element.project}}</span>
+ </td>
+ <td mat-footer-cell *matFooterCellDef></td>
+ </ng-container>
+
+ <ng-container matColumnDef="endpoint-url">
+ <th mat-header-cell *matHeaderCellDef class="endpoint"> Endpoint </th>
+ <td mat-cell *matCellDef="let element" class="endpoint">
+ <span *ngIf="element && element.endpoint" matTooltip="{{element.endpoint}}" [matTooltipPosition]="'above'">
+ {{element.endpoint}}
+ </span>
+ </td>
+ <td mat-footer-cell *matFooterCellDef></td>
+ </ng-container>
+
+ <ng-container matColumnDef="legion-name">
+ <th mat-header-cell *matHeaderCellDef class="legion-name"> Odahu cluster name </th>
+ <td mat-cell *matCellDef="let element" class="legion-name">
+ <span *ngIf="element && element.name">{{element.name}}</span>
+ </td>
+ <td mat-footer-cell *matFooterCellDef></td>
+ </ng-container>
+
+ <ng-container matColumnDef="legion-status">
+ <th mat-header-cell *matHeaderCellDef class="legion-status"> Odahu cluster status </th>
+ <td mat-cell *matCellDef="let element" class="legion-status">
+ <span *ngIf="element && element.name" [ngClass]="element.status.toLowerCase()">{{ element.status | titlecase}}</span>
+ </td>
+ <td mat-footer-cell *matFooterCellDef></td>
+ </ng-container>
+
+ <ng-container matColumnDef="actions">
+ <th mat-header-cell *matHeaderCellDef class="legion-actions"></th>
+ <td mat-cell *matCellDef="let element" class="settings">
+ <span *ngIf="element && element.name" #settings (click)="actions.toggle($event, settings)" class="actions" [ngClass]="{'disabled': element.status !== 'RUNNING' && element.status !== 'STOPPED'}"></span>
+ <bubble-up #actions class="list-menu" position="bottom-left" alternative="top-left">
+ <ul class="list-unstyled">
+ <div class="active-items"></div>
+ <li class="project-seting-item" *ngIf="element.status === 'STOPPED'" (click)="odahuAction(element, 'start')">
+ <i class="material-icons">play_circle_outline</i>
+ <a class="action">
+ Start
+ </a>
+ </li>
+ <li class="project-seting-item" *ngIf="element.status === 'RUNNING'" (click)="odahuAction(element, 'stop')">
+ <i class="material-icons">pause_circle_outline</i>
+ <a class="action" >
+ Stop
+ </a>
+ </li>
+ <li class="project-seting-item" [ngClass]="{'disabled' : element.status === 'STOPPED'}" *ngIf="element.status !== 'TERMINATED' && element.status !== 'TERMINATING'" (click)="odahuAction(element, 'terminate')">
+ <i class="material-icons">phonelink_off</i>
+ <a class="action">
+ Terminate
+ </a>
+ </li>
+ <!--<li class="project-seting-item">-->
+ <!--<i class="material-icons">arrow_downward</i>-->
+ <!--<a>-->
+ <!--Scale-down-->
+ <!--</a>-->
+ <!--</li>-->
+ <!--<li class="project-seting-item">-->
+ <!--<i class="material-icons">arrow_upward</i>-->
+ <!--<a class="action">-->
+ <!--Scale-up-->
+ <!--</a>-->
+ <!--</li>-->
+ </ul>
+ </bubble-up>
+ </td>
+ <td mat-footer-cell *matFooterCellDef></td>
+ </ng-container>
+
+ <ng-container matColumnDef="placeholder">
+ <td mat-footer-cell *matFooterCellDef
+ [colSpan]="displayedColumns.length"
+ class="info">
+ <span>No Odahu clusters</span>
+ </td>
+ <td mat-footer-cell *matFooterCellDef></td>
+ </ng-container>
+
+ <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
+ <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
+ <tr [hidden]="legionClustersList && legionClustersList.length" mat-footer-row *matFooterRowDef="['placeholder']"></tr>
+</table>
+
+
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-list/legion-list.component.scss b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-list/legion-list.component.scss
new file mode 100644
index 0000000..21fc43e
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-list/legion-list.component.scss
@@ -0,0 +1,77 @@
+.legion-clasters-table {
+ width: 100%;
+
+ .project, .endpoint, .legion-name, .legion-status{
+ width: 23%;
+ }
+
+ td.settings {
+ position: relative;
+ vertical-align: middle !important;
+ text-align: right;
+ }
+
+ .list-menu {
+ min-width: 190px;
+ }
+
+ .material-icons {
+ font-size: 18px;
+ padding-top: 1px;
+ }
+
+ td.settings .actions {
+ background-image: url(../../../../assets/svg/settings_icon.svg);
+ width: 16px;
+ height: 16px;
+ display: inline-block;
+ text-align: center;
+ cursor: pointer;
+ &.disabled {
+ opacity: 0.4;
+ cursor: not-allowed;
+ pointer-events: none;
+ }
+ }
+
+ td {
+ &.info.mat-footer-cell{
+ text-align: center;
+ padding: 40px;
+ }
+ .settings {
+ position: relative;
+ vertical-align: middle !important;
+ text-align: right;
+
+ .actions {
+ background-image: url(../../../../assets/svg/settings_icon.svg);
+ width: 16px;
+ height: 16px;
+ display: inline-block;
+ text-align: center;
+ cursor: pointer;
+ }
+ }
+
+ .project-seting-item {
+ display: flex;
+ padding: 10px;
+ align-items: center;
+ border-bottom: 1px solid #edf1f5;
+ cursor: pointer;
+ color: #577289;
+
+ &:hover {
+ color: #36afd5;
+ transition: all .45s ease-in-out;
+ }
+
+ a {
+ padding-left: 5px;
+ }
+ }
+ }
+}
+
+
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-list/legion-list.component.ts b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-list/legion-list.component.ts
new file mode 100644
index 0000000..7e7913e
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-list/legion-list.component.ts
@@ -0,0 +1,48 @@
+import { Component, OnInit } from '@angular/core';
+import {Subscription} from 'rxjs';
+import {LegionDeploymentDataService} from '../legion-deployment-data.service';
+import { MatTableDataSource } from '@angular/material/table';
+import {LegionDeploymentService} from '../../../core/services';
+import {ToastrService} from 'ngx-toastr';
+import {MatDialog} from '@angular/material/dialog';
+import {OdahuActionDialogComponent} from '../../../shared/modal-dialog/odahu-action-dialog';
+
+@Component({
+ selector: 'legion-list',
+ templateUrl: './legion-list.component.html',
+ styleUrls: ['./legion-list.component.scss']
+})
+export class LegionListComponent implements OnInit {
+ private legionClustersList: any[];
+ private subscriptions: Subscription = new Subscription();
+ public dataSource: MatTableDataSource<any>;
+ displayedColumns: string[] = [ 'legion-name', 'project', 'endpoint-url', 'legion-status', 'actions'];
+
+ constructor(
+ private legionDeploymentDataService: LegionDeploymentDataService,
+ private legionDeploymentService: LegionDeploymentService,
+ public toastr: ToastrService,
+ public dialog: MatDialog
+ ) { }
+
+ ngOnInit() {
+ this.subscriptions.add(this.legionDeploymentDataService._legionClasters.subscribe(
+ (value) => {
+ if (value) {
+ this.legionClustersList = value;
+ this.dataSource = new MatTableDataSource(value);
+ }
+ }));
+ }
+
+ private odahuAction(element: any, action: string) {
+ this.dialog.open(OdahuActionDialogComponent, {data: {type: action, item: element}, panelClass: 'modal-sm'})
+ .afterClosed().subscribe(result => {
+ result && this.legionDeploymentService.odahuAction(element, action).subscribe(v =>
+ this.legionDeploymentDataService.updateClasters(),
+ error => this.toastr.error(`Odahu cluster ${action} failed!`, 'Oops!')
+ ) ;
+ }, error => this.toastr.error(error.message || `Odahu cluster ${action} failed!`, 'Oops!')
+ );
+ }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html
index 523cd59..4be7e7d 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html
@@ -188,7 +188,7 @@
</th>
<td mat-cell *matCellDef="let element" class="settings actions-col">
<span [ngClass]="{'not-allow' : selected?.length}">
- <span #settings class="actions" (click)="actions.toggle($event, settings)" *ngIf="element.type !== 'edge node'"
+ <span #settings class="actions" (click)="actions.toggle($event, settings)" *ngIf="element.type !== 'edge node' && element.type !== 'odahu'"
[ngClass]="{
'disabled' : (element.status !== 'running' && element.status !== 'stopped' && element.status !== 'stopping' && element.status !== 'failed')
|| selected?.length || inProgress(element.resources)}">
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/services/legion-deployment.service.ts b/services/self-service/src/main/resources/webapp/src/app/core/services/legion-deployment.service.ts
new file mode 100644
index 0000000..1b0872e
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/core/services/legion-deployment.service.ts
@@ -0,0 +1,35 @@
+import { Injectable } from '@angular/core';
+import {Observable} from 'rxjs';
+import { map, catchError } from 'rxjs/operators';
+import { ApplicationServiceFacade } from './applicationServiceFacade.service';
+import { ErrorUtils } from '../util';
+
+@Injectable()
+
+export class LegionDeploymentService {
+ constructor(private applicationServiceFacade: ApplicationServiceFacade) { }
+
+ public createOdahuNewCluster(data): Observable<{}> {
+ return this.applicationServiceFacade
+ .createOdahuCluster(data)
+ .pipe(
+ map(response => response),
+ catchError(ErrorUtils.handleServiceError));
+ }
+
+ public getOduhuClustersList(): Observable<{}> {
+ return this.applicationServiceFacade
+ .getOdahuList()
+ .pipe(
+ map(response => response),
+ catchError(ErrorUtils.handleServiceError));
+ }
+
+ public odahuAction(data, action) {
+ return this.applicationServiceFacade
+ .odahuStartStop(data, action)
+ .pipe(
+ map(response => response),
+ catchError(ErrorUtils.handleServiceError));
+ }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.html
index 981d56e..6af8f68 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.html
@@ -115,8 +115,9 @@
<!-- [@detailExpand]="element == expandedElement ? 'expanded' : 'collapsed'" -->
<tr *ngFor="let element of element.exploratory; let i = index" class="element-row mat-row">
- <td class="name-col highlight" (click)="printDetailEnvironmentModal(element)">
- <span matTooltip="{{ element.name }}" matTooltipPosition="above">{{ element.name }}</span>
+ <td class="name-col">
+ <span *ngIf="element.shape !== 'odahu cluster'" matTooltip="{{ element.name }}" matTooltipPosition="above" (click)="printDetailEnvironmentModal(element)">{{ element.name }}</span>
+ <span *ngIf="element.shape === 'odahu cluster'" matTooltip="{{ element.name }}" matTooltipPosition="above" (click)="printDetailLegionModal(element)">{{ element.name }}</span>
</td>
<td class="status-col status" ngClass="{{ element.status.toLowerCase() || ''}}">
{{element.status | underscoreless }}
@@ -162,7 +163,7 @@
</span>
<bubble-up #actions class="list-menu" position="bottom-left" alternative="top-left">
- <ul class="list-unstyled">
+ <ul class="list-unstyled" *ngIf="element.shape !== 'odahu cluster'">
<div class="active-items" *ngIf="element.status.toLowerCase() !== 'failed'
&& element.status !== 'terminating'
&& element.status !== 'terminated'
@@ -248,6 +249,29 @@
</div>
</li>
</ul>
+ <ul class="list-unstyled" *ngIf="element.shape === 'odahu cluster'">
+ <li class="project-seting-item" *ngIf="element.status === 'stopped'" (click)="odahuAction(element, 'start')">
+ <i class="material-icons">play_circle_outline</i>
+ <a class="action">
+ Start
+ </a>
+ </li>
+ <li class="project-seting-item" *ngIf="element.status === 'running'" (click)="odahuAction(element, 'stop')">
+ <i class="material-icons">pause_circle_outline</i>
+ <a class="action" >
+ Stop
+ </a>
+ </li>
+ <li class="project-seting-item"
+ [ngClass]="{'disabled': element.status === 'stopped' || element.status.toLowerCase() === 'stopping' || element.status.toLowerCase() === 'starting'}"
+ *ngIf="element.status === element.status !== 'terminated' && element.status !== 'terminating'" (click)="odahuAction(element, 'terminate')"
+ >
+ <i class="material-icons">phonelink_off</i>
+ <a class="action">
+ Terminate
+ </a>
+ </li>
+ </ul>
</bubble-up>
</td>
</tr>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.scss
index 03df6ff..3e1bb78 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.scss
@@ -441,3 +441,16 @@ table.resources {
+.info {
+ padding: 40px;
+ text-align: center;
+}
+
+.legion-icon{
+ vertical-align: middle;
+ margin-left: 10px;
+}
+
+.content-row{
+ background-clip: padding-box;
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.ts
index 7bae81b..dcfa052 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.ts
@@ -22,7 +22,7 @@ import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import { animate, state, style, transition, trigger } from '@angular/animations';
import { ToastrService } from 'ngx-toastr';
import { MatDialog } from '@angular/material/dialog';
-import {ProjectService, UserResourceService} from '../../core/services';
+import {ProjectService, UserResourceService, LegionDeploymentService} from '../../core/services';
import { ExploratoryModel, Exploratory } from './resources-grid.model';
import { FilterConfigurationModel } from './filter-configuration.model';
import { GeneralEnvironmentStatus } from '../../administration/management/management.model';
@@ -37,6 +37,7 @@ import { ConfirmationDialogComponent } from '../../shared/modal-dialog/confirmat
import { SchedulerComponent } from '../scheduler';
import { DICTIONARY } from '../../../dictionary/global.dictionary';
import {ProgressBarService} from '../../core/services/progress-bar.service';
+import {OdahuActionDialogComponent} from '../../shared/modal-dialog/odahu-action-dialog';
import {ComputationModel} from '../computational/computational-resource.model';
import {NotebookModel} from '../exploratory/notebook.model';
import {AuditService} from '../../core/services/audit.service';
@@ -132,6 +133,7 @@ export class ResourcesGridComponent implements OnInit {
private progressBarService: ProgressBarService,
private projectService: ProjectService,
private auditService: AuditService,
+ private legionDeploymentService: LegionDeploymentService,
) { }
ngOnInit(): void {
@@ -227,6 +229,11 @@ export class ResourcesGridComponent implements OnInit {
.afterClosed().subscribe(() => this.buildGrid());
}
+ public printDetailLegionModal(data): void {
+ this.dialog.open(DetailDialogComponent, { data: {legion: data}, panelClass: 'modal-lg' })
+ .afterClosed().subscribe(() => this.buildGrid());
+ }
+
public printCostDetails(data): void {
this.dialog.open(CostDetailsDialogComponent, { data: data, panelClass: 'modal-xl' })
.afterClosed().subscribe(() => this.buildGrid());
@@ -445,6 +452,17 @@ export class ResourcesGridComponent implements OnInit {
(error) => console.log('UPDATE USER PREFERENCES ERROR ', error));
}
+ private odahuAction(element: any, action: string) {
+ this.dialog.open(OdahuActionDialogComponent, {data: {type: action, item: element}, panelClass: 'modal-sm'})
+ .afterClosed().subscribe(result => {
+ result && this.legionDeploymentService.odahuAction(element, action).subscribe(v =>
+ this.buildGrid(),
+ error => this.toastr.error(`Odahu cluster ${action} failed!`, 'Oops!')
+ ) ;
+ }, error => this.toastr.error(error.message || `Odahu cluster ${action} failed!`, 'Oops!')
+ );
+ }
+
public logAction(name) {
this.auditService.sendDataToAudit({
resource_name: name, info: `Open terminal, requested for notebook`, type: 'WEB_TERMINAL'
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.model.ts b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.model.ts
index e1b1360..1465199 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.model.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.model.ts
@@ -56,6 +56,92 @@ export class ExploratoryModel {
public static loadEnvironments(data: Array<any>) {
if (data) {
+
+
+ // return data.map((value) => {
+ // const exploratory = value.exploratory.map(el => {
+ // let provider;
+ // if (el.cloud_provider) {
+ // provider = el.cloud_provider.toLowerCase();
+ // }
+ // return new ExploratoryModel(
+ // provider,
+ // el.exploratory_name,
+ // el.template_name,
+ // el.image,
+ // el.status,
+ // el.shape,
+ // el.computational_resources,
+ // el.up_time,
+ // el.exploratory_url,
+ // value.shared[el.endpoint].edge_node_ip,
+ // el.private_ip,
+ // el.exploratory_user,
+ // el.exploratory_pass,
+ // value.shared[el.endpoint][DICTIONARY[provider].bucket_name],
+ // value.shared[el.endpoint][DICTIONARY[provider].shared_bucket_name],
+ // el.error_message,
+ // el[DICTIONARY[provider].billing.cost],
+ // el[DICTIONARY[provider].billing.currencyCode],
+ // el.billing,
+ // el.libs,
+ // value.shared[el.endpoint][DICTIONARY[provider].user_storage_account_name],
+ // value.shared[el.endpoint][DICTIONARY[provider].shared_storage_account_name],
+ // value.shared[el.endpoint][DICTIONARY[provider].datalake_name],
+ // value.shared[el.endpoint][DICTIONARY[provider].datalake_user_directory_name],
+ // value.shared[el.endpoint][DICTIONARY[provider].datalake_shared_directory_name],
+ // el.project,
+ // el.endpoint,
+ // el.tags
+ // );
+ // });
+ //
+ // const odahu = value.odahu.map(el => {
+ // let provider;
+ // if (el.cloud_provider) {
+ // provider = el.cloud_provider.toLowerCase();
+ // } else {
+ // provider = 'azure';
+ // }
+ // return new ExploratoryModel(
+ // provider,
+ // el.name,
+ // el.template_name,
+ // el.image,
+ // el.status.toLowerCase(),
+ // 'odahu cluster',
+ // [],
+ // el.up_time,
+ // el.urls,
+ // value.shared[el.endpoint].edge_node_ip,
+ // el.private_ip,
+ // el.exploratory_user,
+ // el.exploratory_pass,
+ // el.grafana_admin,
+ // el.grafana_pass,
+ // el.error_message,
+ // el[DICTIONARY[provider].billing.cost],
+ // el[DICTIONARY[provider].billing.currencyCode],
+ // [],
+ // [],
+ // '',
+ // '',
+ // '',
+ // '',
+ // '',
+ // el.project,
+ // el.endpoint,
+ // el.tags
+ // )});
+ // return {
+ // project: value.project,
+ // exploratory: [...exploratory, ...odahu]
+ // };
+ // });
+ // }
+ // }
+
+
return data.map((value) => {
return {
project: value.project,
diff --git a/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/odahu-action-dialog/index.ts b/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/odahu-action-dialog/index.ts
new file mode 100644
index 0000000..bfc5420
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/odahu-action-dialog/index.ts
@@ -0,0 +1,15 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { OdahuActionDialogComponent } from './odahu-action-dialog.component';
+import { MaterialModule } from '../../material.module';
+import {FormsModule} from '@angular/forms';
+
+export * from './odahu-action-dialog.component';
+
+@NgModule({
+ imports: [CommonModule, MaterialModule, FormsModule],
+ declarations: [OdahuActionDialogComponent],
+ entryComponents: [OdahuActionDialogComponent],
+ exports: [OdahuActionDialogComponent]
+})
+export class OdahuActionDialogModule {}
diff --git a/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/odahu-action-dialog/odahu-action-dialog.component.ts b/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/odahu-action-dialog/odahu-action-dialog.component.ts
new file mode 100644
index 0000000..921ff99
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/odahu-action-dialog/odahu-action-dialog.component.ts
@@ -0,0 +1,50 @@
+import { Component, Inject } from '@angular/core';
+import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
+
+
+@Component({
+ selector: 'edge-action-dialog',
+ template: `
+ <div id="dialog-box">
+ <header class="dialog-header">
+ <h4 class="modal-title"><span class="action">{{data.type | titlecase}}</span> Odahu cluster</h4>
+ <button type="button" class="close" (click)="dialogRef.close()">×</button>
+ </header>
+ <div mat-dialog-content class="content message mat-dialog-content">
+ <h3>Odahu cluster <span class="strong">{{data.item.name}} </span>will be {{label[data.type]}}</h3>
+ <p class="m-top-20 action-text"><span class="strong">Do you want to proceed?</span></p>
+
+ <div class="text-center m-top-30 m-bott-30">
+ <button type="button" class="butt" mat-raised-button (click)="dialogRef.close()">No</button>
+ <button type="button" class="butt butt-success" mat-raised-button (click)="dialogRef.close(true)">Yes</button>
+ </div>
+ </div>
+ </div>
+ `,
+ styles: [`
+ .content { color: #718ba6; padding: 20px 50px; font-size: 14px; font-weight: 400; margin: 0; }
+ .info .confirm-dialog { color: #607D8B; }
+ header { display: flex; justify-content: space-between; color: #607D8B; }
+ h3 { font-weight: 300; }
+ header h4 i { vertical-align: bottom; }
+ header a i { font-size: 20px; }
+ header a:hover i { color: #35afd5; cursor: pointer; }
+ .action{text-transform: capitalize}
+ .action-text { text-align: center; }
+ label { font-size: 15px; font-weight: 500; font-family: "Open Sans",sans-serif; cursor: pointer; display: flex; align-items: center;}
+ label input {margin-top: 2px; margin-right: 5px;}
+ `]
+})
+
+export class OdahuActionDialogComponent {
+ public label = {
+ stop: 'stopped',
+ start: 'started',
+ terminate: 'terminated',
+ };
+
+ constructor(
+ public dialogRef: MatDialogRef<OdahuActionDialogComponent>,
+ @Inject(MAT_DIALOG_DATA) public data: any) {
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datalab.apache.org
For additional commands, e-mail: commits-help@datalab.apache.org