From 92379ec7de56dd396dec958868a3a5fb2bb03a41 Mon Sep 17 00:00:00 2001 From: "SANDHYA.JS" Date: Fri, 13 Jun 2025 17:29:35 +0530 Subject: [PATCH] Feature: 11055 Support of several node groups in clusters created by OSM - Added control plane support in managed post - When aws vim account is selected paylaod will get differed - Added details page in clusters to view node and ksu for specified cluster - Fixed Bug 2402 - Unable to create Ns Config template from Ui bug by chnaging api Change-Id: I4eb327fd86b0c4a706b05a8ed10524e4d2c5bc95 Signed-off-by: SANDHYA.JS --- .eslintrc.json | 2 +- angular.json | 3 +- src/app/k8s/K8sModule.ts | 14 +- .../k8s/k8s-action/K8sActionComponent.html | 72 ++-- src/app/k8s/k8s-action/K8sActionComponent.ts | 62 ++- .../K8sAddClusterComponent.html | 89 +++-- .../K8sAddClusterComponent.scss | 114 ++++-- .../k8s-add-cluster/K8sAddClusterComponent.ts | 170 ++++++-- src/app/k8s/k8s-info/K8sInfoComponent.html | 248 ++++++++++++ src/app/k8s/k8s-info/K8sInfoComponent.scss | 314 +++++++++++++++ src/app/k8s/k8s-info/K8sInfoComponent.ts | 373 ++++++++++++++++++ .../k8s/k8s-ksu/ksu-add/KSUAddComponent.html | 6 +- .../k8s/k8s-ksu/ksu-add/KSUAddComponent.ts | 67 ++-- .../k8s/k8s-ksu/ksu-details/KSUComponent.html | 9 +- .../k8s/k8s-ksu/ksu-details/KSUComponent.ts | 34 +- .../K8sAppProfileComponent.html | 7 +- .../K8sAppProfileComponent.ts | 34 +- .../K8sInfraConfigProfileComponent.html | 7 +- .../K8sInfraConfigProfileComponent.ts | 34 +- .../K8sInfraControllerProfileComponent.html | 7 +- .../K8sInfraControllerProfileComponent.ts | 34 +- .../K8sResourceProfileComponent.html | 7 +- .../K8sResourceProfileComponent.ts | 34 +- .../k8s/k8scluster/K8sClusterComponent.html | 12 +- src/app/k8s/k8scluster/K8sClusterComponent.ts | 32 +- src/app/k8s/node-add/NodeAddComponent.html | 126 ++++++ src/app/k8s/node-add/NodeAddComponent.scss | 17 + src/app/k8s/node-add/NodeAddComponent.ts | 359 +++++++++++++++++ .../oka-packages/OKAPackageComponent.html | 7 +- .../oka-packages/OKAPackageComponent.ts | 34 +- .../compose-packages/ComposePackages.html | 14 +- .../compose-packages/ComposePackages.ts | 27 +- src/app/utilities/delete/DeleteComponent.html | 9 +- src/app/utilities/delete/DeleteComponent.ts | 38 +- .../OkaPackagesActionComponent.html | 6 +- .../utilities/show-info/ShowInfoComponent.ts | 26 +- src/assets/i18n/de.json | 24 +- src/assets/i18n/en.json | 24 +- src/assets/i18n/es.json | 24 +- src/assets/i18n/pt.json | 24 +- src/environments/environment.prod.ts | 1 + src/environments/environment.ts | 1 + src/models/CommonModel.ts | 19 +- src/models/K8sModel.ts | 62 ++- src/models/MenuModel.ts | 20 +- src/models/VNFDModel.ts | 1 + src/services/SharedService.ts | 33 +- tsconfig.json | 2 + 48 files changed, 2267 insertions(+), 416 deletions(-) create mode 100644 src/app/k8s/k8s-info/K8sInfoComponent.html create mode 100644 src/app/k8s/k8s-info/K8sInfoComponent.scss create mode 100644 src/app/k8s/k8s-info/K8sInfoComponent.ts create mode 100644 src/app/k8s/node-add/NodeAddComponent.html create mode 100644 src/app/k8s/node-add/NodeAddComponent.scss create mode 100644 src/app/k8s/node-add/NodeAddComponent.ts diff --git a/.eslintrc.json b/.eslintrc.json index 1e17cd7..ae848d9 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -238,7 +238,7 @@ "ignore": [ -1, 0, - 1 + 1,2,3 ], "ignoreArrayIndexes": true, "ignoreDefaultValues": true diff --git a/angular.json b/angular.json index 066e675..e9d40e9 100644 --- a/angular.json +++ b/angular.json @@ -46,7 +46,8 @@ "node_modules/@fortawesome/fontawesome-free/css/all.min.css" ], "scripts": [ - "src/assets/js/tar.js" + "src/assets/js/tar.js", + "node_modules/bootstrap/dist/js/bootstrap.bundle.min.js" ], "stylePreprocessorOptions": { "includePaths": [ diff --git a/src/app/k8s/K8sModule.ts b/src/app/k8s/K8sModule.ts index 4b9ff15..913353c 100644 --- a/src/app/k8s/K8sModule.ts +++ b/src/app/k8s/K8sModule.ts @@ -35,6 +35,7 @@ import { K8sAppProfileComponent } from 'K8sAppProfileComponent'; import { K8sAttachProfileComponent } from 'K8sAttachProfileComponent'; import { K8sClusterComponent } from 'K8sClusterComponent'; import { K8sComponent } from 'K8sComponent'; +import { K8sInfoComponent } from 'K8sInfoComponent'; import { K8sInfraConfigAddComponent } from 'K8sInfraConfigAddComponent'; import { K8sInfraConfigProfileComponent } from 'K8sInfraConfigProfileComponent'; import { K8sInfraControllerProfileComponent } from 'K8sInfraControllerProfileComponent'; @@ -44,6 +45,7 @@ import { KSUAddComponent } from 'KSUAddComponent'; import { KSUComponent } from 'KSUComponent'; import { LoaderModule } from 'LoaderModule'; import { Ng2SmartTableModule } from 'ng2-smart-table'; +import { NodeAddComponent } from 'NodeAddComponent'; import { PagePerRowModule } from 'PagePerRowModule'; import { PageReloadModule } from 'PageReloadModule'; @@ -66,6 +68,14 @@ const routes: Routes = [ }, component: K8sClusterComponent }, + { + path: 'details/:id', + data: { + breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' }, + projectInfo, { title: 'PAGE.K8S.MENUK8SCLUSTER', url: '/k8s/cluster' }, { title: '{id}', url: null }] + }, + component: K8sInfoComponent + }, { path: 'infra-config-profile', data: { @@ -149,7 +159,9 @@ const routes: Routes = [ K8sAppProfileComponent, K8sResourceProfileComponent, KSUComponent, - KSUAddComponent + KSUAddComponent, + K8sInfoComponent, + NodeAddComponent, ], providers: [DataService], schemas: [CUSTOM_ELEMENTS_SCHEMA] diff --git a/src/app/k8s/k8s-action/K8sActionComponent.html b/src/app/k8s/k8s-action/K8sActionComponent.html index f545606..57f4480 100644 --- a/src/app/k8s/k8s-action/K8sActionComponent.html +++ b/src/app/k8s/k8s-action/K8sActionComponent.html @@ -16,38 +16,44 @@ limitations under the License. Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) -->
- - - - - -
-
-
-
-
+
+ + +
\ No newline at end of file diff --git a/src/app/k8s/k8s-action/K8sActionComponent.ts b/src/app/k8s/k8s-action/K8sActionComponent.ts index 919dda1..0728381 100644 --- a/src/app/k8s/k8s-action/K8sActionComponent.ts +++ b/src/app/k8s/k8s-action/K8sActionComponent.ts @@ -20,6 +20,7 @@ */ import { HttpHeaders } from '@angular/common/http'; import { ChangeDetectorRef, Component, Injector } from '@angular/core'; +import { Router } from '@angular/router'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { TranslateService } from '@ngx-translate/core'; import { NotifierService } from 'angular-notifier'; @@ -31,6 +32,7 @@ import { K8sAttachProfileComponent } from 'K8sAttachProfileComponent'; import { K8sInfraConfigAddComponent } from 'K8sInfraConfigAddComponent'; import { INFRACONFIGPAYLOAD, K8SCLUSTERDATADISPLAY, K8SPayload, K8SREPODATADISPLAY } from 'K8sModel'; import { KSUAddComponent } from 'KSUAddComponent'; +import { NodeAddComponent } from 'NodeAddComponent'; import { RestService } from 'RestService'; import { isNullOrUndefined, SharedService } from 'SharedService'; import { ShowInfoComponent } from 'ShowInfoComponent'; @@ -72,6 +74,9 @@ export class K8sActionComponent { /** Check cluster or not @public */ public isCluster = false; + /** Check node group or not @public */ + public isNode = false; + /** Check the loading results for loader status @public */ public isLoadingDownloadResult: boolean = false; @@ -82,7 +87,10 @@ export class K8sActionComponent { private sharedService: SharedService; /** Contains instance ID @private */ - private instanceID: string; + private k8sID: string; + + /** Contains instance name @private */ + private k8sName: string; /** Utilizes rest service for any CRUD operations @private */ private restService: RestService; @@ -100,6 +108,9 @@ export class K8sActionComponent { /** Controls the header form @private */ private headers: HttpHeaders; + /** Holds teh instance of AuthService class of type AuthService @private */ + private router: Router; + constructor(injector: Injector) { this.injector = injector; this.modalService = this.injector.get(NgbModal); @@ -108,6 +119,7 @@ export class K8sActionComponent { this.translateService = this.injector.get(TranslateService); this.restService = this.injector.get(RestService); this.cd = this.injector.get(ChangeDetectorRef); + this.router = this.injector.get(Router); } /** @@ -118,7 +130,8 @@ export class K8sActionComponent { Accept: 'application/zip, application/json', 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0' }); - this.instanceID = this.value.identifier; + this.k8sID = this.value.identifier; + this.k8sName = this.value.name; this.getK8sType = this.value.pageType; this.state = this.value.state; if ((this.value.createdbyosm) === 'true') { @@ -137,6 +150,12 @@ export class K8sActionComponent { } else { this.isProfile = false; } + + if (this.value.pageType === 'node') { + this.isNode = true; + } else { + this.isNode = false; + } } /** Delete User Account @public */ @@ -155,6 +174,13 @@ export class K8sActionComponent { }); } + /** Details page @public */ + public nodeK8s(): void { + this.router.navigate(['/k8s/details/' + this.k8sID]).catch((): void => { + // Catch Navigation Error + }); + } + /** Shows information using modalservice @public */ public infoK8s(pageType: string): void { let pageName: string = ''; @@ -162,18 +188,26 @@ export class K8sActionComponent { if (pageType === 'repo') { pageName = 'k8s-repo'; title = 'PAGE.K8S.K8SREPODETAILS'; + } else if (pageType === 'node') { + pageName = 'k8s-node'; + title = 'Node Details'; + } else if (pageType === 'k8-ksu') { + pageName = 'k8s-ksu'; + title = 'KSU Details'; } else { pageName = 'k8s-cluster'; title = 'PAGE.K8S.K8SCLUSTERDETAILS'; } // eslint-disable-next-line security/detect-non-literal-fs-filename this.modalService.open(ShowInfoComponent, { backdrop: 'static' }).componentInstance.params = { - id: this.instanceID, + id: this.k8sID, page: pageName, titleName: title, createdbyosm: this.value.createdbyosm, bootstrap: this.value.bootstrap, - key: this.value.key + key: this.value.key, + cluster_mode: this.value.cluster_mode, + cluster_id: this.value.cluster_id }; } /** Edit profile @public */ @@ -208,6 +242,22 @@ export class K8sActionComponent { }); } + /** Handle Scaling @public */ + public scaling(editType: string): void { + // eslint-disable-next-line security/detect-non-literal-fs-filename + const modalRef: NgbModalRef = this.modalService.open(NodeAddComponent, { backdrop: 'static' }); + modalRef.componentInstance.profileID = this.value.identifier; + modalRef.componentInstance.clusterId = this.value.cluster_id; + modalRef.componentInstance.profileType = editType; + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + } + }).catch((): void => { + // Catch Navigation Error + }); + } + /** Edit profile under cluster @public */ public editClusterProfile(editType: string): void { @@ -261,10 +311,10 @@ export class K8sActionComponent { headers: this.headers, responseType: 'blob' }; - this.restService.getResource(environment.K8SCREATECLUSTER_URL + '/' + this.instanceID + '/get_creds') + this.restService.getResource(environment.K8SCREATECLUSTER_URL + '/' + this.k8sID + '/get_creds') .subscribe((res: { op_id: string }) => { if (!isNullOrUndefined(res.op_id)) { - this.restService.getResource(environment.K8SCREATECLUSTER_URL + '/' + this.instanceID + '/get_creds_file' + '/' + res.op_id, httpOptions) + this.restService.getResource(environment.K8SCREATECLUSTER_URL + '/' + this.k8sID + '/get_creds_file' + '/' + res.op_id, httpOptions) .subscribe((response: Blob) => { this.isLoadingDownloadResult = true; if (!isNullOrUndefined(response)) { diff --git a/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.html b/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.html index e9ac743..73f1038 100644 --- a/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.html +++ b/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.html @@ -15,7 +15,7 @@ limitations under the License. Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) --> -
+ - -
+
-
+
-
- +
+
- +
-
- +
+
- +
@@ -169,9 +208,7 @@ Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.i - +
\ No newline at end of file diff --git a/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.scss b/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.scss index 8885be8..1fd08a8 100644 --- a/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.scss +++ b/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.scss @@ -18,42 +18,90 @@ @import "../../../assets/scss/variable.scss"; @import "../../../assets/scss/mixins/mixin"; .quotacheck { - box-shadow: none; - border-radius: 2px; - border-color: $gray-600; + box-shadow: none; + border-radius: 2px; + border-color: $gray-600; } .form-check { - padding-left: 30px; + padding-left: 30px; } .quotaCheck-label { - position: absolute; - left: 26px; + position: absolute; + left: 26px; } .tooltip-icon { - position: relative; - margin-left: 5px; - cursor: pointer; - } - - .tooltip-icon .tooltip-text { - visibility: hidden; - width: 200px; - background-color: #fff; - color: #555; - text-align: center; - padding: 5px 10px; - border-radius: 4px; - @include border(all, 1, solid, $gray-300); - z-index: 1; - bottom: 125%; - left: 50%; - transform: translateX(-50%); - opacity: 0; - transition: opacity 0.3s; - } - - .tooltip-icon:hover .tooltip-text { - visibility: visible; - opacity: 1; - } - \ No newline at end of file + position: relative; + margin-left: 5px; + cursor: pointer; +} +.tooltip-icon .tooltip-text { + visibility: hidden; + width: 200px; + background-color: #fff; + color: #555; + text-align: center; + padding: 5px 10px; + border-radius: 4px; + @include border(all, 1, solid, $gray-300); + z-index: 1; + bottom: 125%; + left: 50%; + transform: translateX(-50%); + opacity: 0; + transition: opacity 0.3s; +} +.tooltip-icon:hover .tooltip-text { + visibility: visible; + opacity: 1; +} +.chip-input-container { + display: flex; + align-items: center; + flex-wrap: wrap; + border: 1px solid #e9ecef; + padding: 5px; + border-radius: 5px; + min-height: 38px; + width: 100%; + position: relative; + box-sizing: border-box; +} +.chip-input-container:focus-within { + border: 1px solid #afb5c1; + background-color: #f2f4f5; +} +.chips-wrapper { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 5px; + width: 100%; +} +.chip { + background: #007bff; + color: white; + padding: 5px 10px; + border-radius: 15px; + font-size: 14px; + display: flex; + align-items: center; + height: 28px; + white-space: nowrap; +} +.chip button { + background: none; + border: none; + color: white; + margin-left: 5px; + cursor: pointer; + font-size: 14px; +} +.chip-input { + border: none; + outline: none; + flex: 1; + min-width: 100px; + height: 28px; + padding: 4px; + background: transparent; +} \ No newline at end of file diff --git a/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.ts b/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.ts index 34ceeab..69fe54f 100644 --- a/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.ts +++ b/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.ts @@ -20,7 +20,7 @@ */ import { HttpHeaders } from '@angular/common/http'; import { Component, ElementRef, Injector, Input, OnInit, ViewChild } from '@angular/core'; -import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { AbstractControl, FormArray, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { TranslateService } from '@ngx-translate/core'; import { NotifierService } from 'angular-notifier'; @@ -51,6 +51,9 @@ export class K8sAddClusterComponent implements OnInit { /** Contains all vim account collections */ public vimAccountSelect: VimAccountDetails; + /** Contains selected vim */ + public vimType: string; + /** Input contains Modal dialog component Instance @public */ @Input() public profileType: string; @@ -163,7 +166,7 @@ export class K8sAddClusterComponent implements OnInit { public k8sclusterFormAction(): void { this.k8sclusterForm = this.formBuilder.group({ name: ['', [Validators.required]], - k8s_version: ['', [Validators.required]], + k8s_version: [''], vim_account: [null, [Validators.required]], description: [''], nets: ['', [Validators.required]], @@ -175,6 +178,9 @@ export class K8sAddClusterComponent implements OnInit { node_size: ['', [Validators.required]], bootstrap: [true], k8sVersion: ['', [Validators.required]], + iam_role: [''], + private_subnet: this.formBuilder.array([]), + public_subnet: this.formBuilder.array([]), update: [''] }); } @@ -182,6 +188,65 @@ export class K8sAddClusterComponent implements OnInit { /** convenience getter for easy access to form fields */ get f(): FormGroup['controls'] { return this.k8sclusterForm.controls; } + /** convenience getter for easy access to form array privatesubnet */ + get chipsprivateArray(): FormArray { + return this.k8sclusterForm.get('private_subnet') as FormArray; + } + + /** To add privatesubnet chips */ + public addprivateChips() { + const input = document.getElementById('private_subnet') as HTMLInputElement; + const value = input.value.trim(); + + if (value) { + this.chipsprivateArray.push(new FormControl(value)); + input.value = ''; + } + } + /** convenience getter for easy access to form array publicsubnet */ + get chipspublicArray(): FormArray { + return this.k8sclusterForm.get('public_subnet') as FormArray; + } + + /** To add publicsubnet chips */ + public addpublicChips() { + const input = document.getElementById('public_subnet') as HTMLInputElement; + const value = input.value.trim(); + + if (value) { + this.chipspublicArray.push(new FormControl(value)); + input.value = ''; + } + } + + /** To remove privatesubnet chips */ + public removeprivateChip(index: number) { + this.chipsprivateArray.removeAt(index); + } + + /** To remove privatesubnet last chip */ + public removeprivateLastChip(event: KeyboardEvent) { + const input = event.target as HTMLInputElement; + if (event.key === 'Backspace' && input.value === '' && this.chipsprivateArray.length > 0) { + this.chipsprivateArray.removeAt(this.chipsprivateArray.length - 1); // Remove last chip if input is empty + event.preventDefault(); + } + } + /** To remove publicsubnet chips */ + public removepublicChip(index: number) { + this.chipspublicArray.removeAt(index); + } + + /** To remove publicsubnet last chip */ + public removepublicLastChip(event: KeyboardEvent) { + const input = event.target as HTMLInputElement; + if (event.key === 'Backspace' && input.value === '' && this.chipspublicArray.length > 0) { + this.chipspublicArray.removeAt(this.chipspublicArray.length - 1); // Remove last chip if input is empty + event.preventDefault(); + } + } + + /** Call the vimAccount details in the selection options @public */ public getDetailsvimAccount(): void { this.isLoadingResults = true; @@ -219,6 +284,7 @@ export class K8sAddClusterComponent implements OnInit { /** Contain selected vimAccount details @public */ public getDetailsvim(event: VimAccountDetails): void { this.vimAccountId = event._id; + this.vimType = event.vim_type; } /** On modal submit k8sAddClusterSubmit will called @public */ @@ -229,6 +295,23 @@ export class K8sAddClusterComponent implements OnInit { this.getFormControl('deployment_methods').disable(); this.getFormControl('k8sVersion').disable(); this.getFormControl('update').disable(); + this.clusterUrl = environment.K8SCREATECLUSTER_URL; + if (this.vimType === 'aws') { + this.getFormControl('resource_group').disable(); + this.getFormControl('k8sVersion').disable(); + this.getFormControl('node_size').disable(); + this.getFormControl('node_count').disable(); + this.getFormControl('nets').disable(); + this.getFormControl('credentials').disable(); + this.getFormControl('update').disable(); + this.getFormControl('deployment_methods').disable(); + } else if (this.vimType !== 'aws') { + this.getFormControl('nets').disable(); + this.getFormControl('credentials').disable(); + this.getFormControl('deployment_methods').disable(); + this.getFormControl('k8sVersion').disable(); + this.getFormControl('update').disable(); + } this.manageCluster(); } else if (this.profileType === 'Register' && this.isChecked === true) { this.clusterUrl = environment.K8SCREATECLUSTER_URL + '/register'; @@ -240,6 +323,9 @@ export class K8sAddClusterComponent implements OnInit { this.getFormControl('nets').disable(); this.getFormControl('deployment_methods').disable(); this.getFormControl('k8sVersion').disable(); + this.getFormControl('iam_role').disable(); + this.getFormControl('private_subnet').disable(); + this.getFormControl('public_subnet').disable(); this.getFormControl('update').disable(); this.registerCluster(); } if (this.isChecked === false && this.profileType === 'Register') { @@ -250,6 +336,9 @@ export class K8sAddClusterComponent implements OnInit { this.getFormControl('node_count').disable(); this.getFormControl('node_size').disable(); this.getFormControl('k8sVersion').disable(); + this.getFormControl('iam_role').disable(); + this.getFormControl('private_subnet').disable(); + this.getFormControl('public_subnet').disable(); this.getFormControl('update').disable(); this.oldregisterCluster(); } else if (this.profileType === 'upgrade') { @@ -265,7 +354,9 @@ export class K8sAddClusterComponent implements OnInit { this.getFormControl('bootstrap').disable(); this.getFormControl('node_count').disable(); this.getFormControl('node_size').disable(); - this.getFormControl('k8s_version').disable(); + this.getFormControl('iam_role').disable(); + this.getFormControl('private_subnet').disable(); + this.getFormControl('public_subnet').disable(); this.getFormControl('update').disable(); this.updateCluster(); } else if (this.profileType === 'update') { @@ -281,8 +372,10 @@ export class K8sAddClusterComponent implements OnInit { this.getFormControl('node_size').disable(); this.getFormControl('k8s_version').disable(); this.getFormControl('k8sVersion').disable(); + this.getFormControl('iam_role').disable(); + this.getFormControl('private_subnet').disable(); + this.getFormControl('public_subnet').disable(); this.getFormControl('name').disable(); - this.editCluster(); } } @@ -375,7 +468,6 @@ export class K8sAddClusterComponent implements OnInit { if (this.k8sclusterForm.value.description === '') { delete this.k8sclusterForm.value.description; } - const apiURLHeader: APIURLHEADER = { url: this.clusterUrl, httpOptions: { headers: this.headers } @@ -393,7 +485,7 @@ export class K8sAddClusterComponent implements OnInit { }); } - /** Manage cluster @public */ + /** controlplane cluster flow @public */ public manageCluster(): void { this.submitted = true; this.sharedService.cleanForm(this.k8sclusterForm); @@ -404,36 +496,51 @@ export class K8sAddClusterComponent implements OnInit { message: 'Done' }; const apiURLHeader: APIURLHEADER = { - url: environment.K8SCREATECLUSTER_URL, + url: this.clusterUrl, httpOptions: { headers: this.headers } }; this.isLoadingResults = true; - const payload: K8SPayload = { - name: this.k8sclusterForm.value.name, - vim_account: this.k8sclusterForm.value.vim_account, - location: this.k8sclusterForm.value.location, - region_name: this.k8sclusterForm.value.region_name, - resource_group: this.k8sclusterForm.value.resource_group, - k8s_version: this.k8sclusterForm.value.k8s_version, - node_size: this.k8sclusterForm.value.node_size, - node_count: Number(this.k8sclusterForm.value.node_count), - description: this.k8sclusterForm.value.description, - bootstrap: Boolean(this.k8sclusterForm.value.bootstrap) - }; - if (this.k8sclusterForm.value.region_name === '') { - delete payload.region_name; - } else if (this.k8sclusterForm.value.resource_group === '') { - delete payload.resource_group; - } else if (this.k8sclusterForm.value.description === '') { - delete payload.description; + const formData = this.k8sclusterForm.value; + if (this.k8sclusterForm.value.private_subnet.length <= 0) { + delete this.k8sclusterForm.value.private_subnet; } - if (this.k8sclusterForm.value.region_name === '' && this.k8sclusterForm.value.resource_group === '' && this.k8sclusterForm.value.description === '') { - delete payload.region_name; - delete payload.resource_group; - delete payload.description; + if (this.k8sclusterForm.value.public_subnet.length <= 0) { + delete this.k8sclusterForm.value.public_subnet; } - this.restService.postResource(apiURLHeader, payload).subscribe((result: {}) => { + if (this.vimType !== 'aws') { + this.payload = { + name: this.k8sclusterForm.value.name, + vim_account: this.k8sclusterForm.value.vim_account, + location: this.k8sclusterForm.value.location, + region_name: this.k8sclusterForm.value.region_name, + resource_group: this.k8sclusterForm.value.resource_group, + k8s_version: this.k8sclusterForm.value.k8s_version, + node_size: this.k8sclusterForm.value.node_size, + node_count: Number(this.k8sclusterForm.value.node_count), + description: this.k8sclusterForm.value.description, + bootstrap: Boolean(this.k8sclusterForm.value.bootstrap) + }; + if (this.k8sclusterForm.value.region_name === '') { + delete this.payload.region_name; + } + if (this.k8sclusterForm.value.resource_group === '') { + delete this.payload.resource_group; + } + if (this.k8sclusterForm.value.description === '') { + delete this.payload.description; + } + } else { + this.payload = Object.keys(formData).reduce((acc, key) => { + // eslint-disable-next-line security/detect-object-injection + if (formData[key] !== null && formData[key] !== undefined && formData[key] !== '') { + // eslint-disable-next-line security/detect-object-injection + acc[key] = formData[key]; + } + return acc; + }, {}); + } + this.restService.postResource(apiURLHeader, this.payload).subscribe((result: {}) => { this.activeModal.close(modalData); this.isLoadingResults = false; this.notifierService.notify('success', this.k8sclusterForm.value.name + @@ -475,8 +582,7 @@ export class K8sAddClusterComponent implements OnInit { }); } - - /** Update cluster @public */ + /** Edit cluster @public */ public editCluster(): void { this.submitted = true; this.sharedService.cleanForm(this.k8sclusterForm); diff --git a/src/app/k8s/k8s-info/K8sInfoComponent.html b/src/app/k8s/k8s-info/K8sInfoComponent.html new file mode 100644 index 0000000..4bf3783 --- /dev/null +++ b/src/app/k8s/k8s-info/K8sInfoComponent.html @@ -0,0 +1,248 @@ + +
+
+
{{clusterName}}
+
+
+
+ + +
+
+
+ + + +
+
+ + + +
+
+ {{ item.name }} + + + + + +
+
+

+ {{ 'PAGE.K8S.DESCRIPTION' | translate }}: + + + {{ item.description }} + + {{ + popoverText }} + +

+
+

+ {{ 'PAGE.K8S.PACKAGENAME' | translate }}: + {{ item?.package_name[i] }} +

+

+ {{ 'PAGE.K8S.PACKAGEPATH' | translate }}: + {{ item?.package_path[i] }} +

+

+ {{'MODIFIED' | translate }}: + {{ item.modified }} +

+
+ +
+
+
+
+ +
+
+ {{ item.name }} + + + + + +
+
+

+ {{ 'PAGE.K8S.DESCRIPTION' | translate}}: + + + {{ item.description }} + + {{ + popoverText }} + +

+
+

+ {{ 'PAGE.K8S.PACKAGENAME' | translate }}: + {{ item?.package_name[0] }} +

+

+ {{ 'PAGE.K8S.PACKAGEPATH' | translate }}: + {{ item?.package_path[0] }} +

+

+ {{ 'MODIFIED' | translate }}: + {{ item.modified }} +

+
+ +
+
+
+
+ +
+ {{ 'NODATA' | translate }} +
+
+
+
+
+
+ + + +
+
+
+
+ {{ item.name }} + + {{ countData?.k8s_version }} + + + + + + +
+
+

+ {{ 'PAGE.K8S.DESCRIPTION' | translate }}: + + + {{ item.description }} + + {{ popoverText + }} + +

+
+

{{ 'PAGE.K8S.NODESIZE' | translate }}: + {{ + item.nodeSize }} +

+

{{ 'PAGE.K8S.NODECOUNT' | translate }}: + {{ item.nodeCount }} +

+

{{ 'MODIFIED' | translate }}: {{ item.modified }}

+
+ +
+
+
+ +
+ {{ 'NODATA' | translate }} +
+
+
+
+ \ No newline at end of file diff --git a/src/app/k8s/k8s-info/K8sInfoComponent.scss b/src/app/k8s/k8s-info/K8sInfoComponent.scss new file mode 100644 index 0000000..d3b10b6 --- /dev/null +++ b/src/app/k8s/k8s-info/K8sInfoComponent.scss @@ -0,0 +1,314 @@ +/* + Copyright 2020 TATA ELXSI + + 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. + + Author: SANDHYA JS (sandhya.j@tataelxsi.co.in) +*/ +@import "../../../assets/scss/variable.scss"; +@import "../../../assets/scss/mixins/mixin.scss"; + +.text-dark { + color: $gray-800; +} + +.font-weight-bold { + font-weight: 700; +} + +button.bg-light { + background-color: $gray-200; + &:hover, + &:focus { + background-color: $paleblue; + } +} + +.btn-group { + display: flex; + &.list.action { + justify-content: center; + align-items: flex-start; + flex-wrap: wrap; + .btn { + border-top: 2px solid $primary; + transition: + transform 0.3s ease-in-out, + background-color 0.3s, + color 0.3s; + &.active { + background-color: $theme-bg-color; + font-weight: bold; + margin: 5px 0 0; + transform: scale(1); + .circle { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border-radius: 50%; + background-color: $primary; + color: white; + position: absolute; + top: 0; + transform: translateY(-50%); + } + b { + font-size: 1.2em; + } + p { + font-size: 1em; + } + } + &:not(.active) { + transform: scale(0.97); + opacity: 0.8; + .circle { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border-radius: 50%; + background-color: $gray-200; + color: black; + position: absolute; + top: 0; + transform: translateY(-50%); + border: 1px solid $primary; + } + } + } + .collapse { + transition: + background-color 0.3s, + color 0.3s; + } + } +} +.collapse { + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-start; + padding: 5px; + background-color: $theme-bg-color; +} + +.btn p { + margin: 0; + padding-top: 20px; +} + +.label-value-pair, +.label-value-pair-description { + display: flex; + justify-content: space-between; + margin-bottom: 10px; + @include padding-value(3, 10, 0, 10); + align-items: center; + gap: 8px; + position: relative; +} + +.label, +.label1 { + font-weight: bold; + color: #343a40; + white-space: nowrap; +} + +.value, +.value1 { + display: -webkit-box; + -webkit-line-clamp: 2; + line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; + cursor: pointer; + max-height: 3em; + line-height: 1.5em; + position: relative; + margin-left: 5px; + color: #343a40; + flex: 1; + min-width: 0; + overflow: visible; +} + +.custom-card { + @include wh-value(null, 290px); + @include roundedCorners(5); + position: relative; + @include border(all, 1, solid, rgba(238, 238, 238, 0.75)); + @include box-shadow(0px, 1px, 15px, 0px, rgba(69, 90, 100, 0.1)); + background-color: $white; +} + +.d-flex { + display: flex; + flex-wrap: nowrap; + align-items: center; + gap: 2px; + width: 100%; +} + +.search-container { + position: relative; + width: 100%; + max-width: 300px; + height: 30px; + display: flex; +} + +.search-input { + width: 100%; + padding-right: 40px; + box-sizing: border-box; + border: 1px solid $primary; +} + +.search-icon { + position: absolute; + right: 10px; + top: 50%; + transform: translateY(-50%); + color: $primary; +} + +.sort-dropdown { + display: flex; + align-items: center; + position: relative; + flex-shrink: 0; + min-width: 150px; + max-width: 200px; + margin-left: 2px; +} + +#sort-icon { + margin-right: 10px; + cursor: pointer; +} + +#sort-options { + padding: 2px; + cursor: pointer; +} + +.custom-card-header { + background-color: $primary; + @include box-shadow(0px, 1px, 15px, 0px, rgba(69, 90, 100, 0.1)); + @include roundedCorners(5); + color: white; +} + +.version { + background-color: $secondary; + width: 40px; +} + +.short-description { + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; + cursor: pointer; + max-height: 3em; + line-height: 1.5em; + white-space: normal; + line-clamp: 2; +} + +/* Tooltip Styling */ +.popover-description { + position: absolute; + background-color: $primary; + color: white; + padding: 8px; + border-radius: 5px; + font-size: 12px; + max-width: 300px; + white-space: normal; + display: none; + z-index: 1000; +} + +.value1:hover .popover-description { + display: block; +} + +.card-header, +.card-footer { + padding: 12px; + font-weight: bold; +} + +.card-body { + padding: 0; + margin: 0; + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; +} + +.no-data { + display: flex; + justify-content: center; + align-items: center; + padding: 20px 0; + text-align: center; + height: 260px; +} + +::ng-deep .carousel-indicators { + display: none; +} + +/* Hide default Bootstrap background images for arrows */ +::ng-deep .carousel-control-prev-icon, +::ng-deep .carousel-control-next-icon { + background-image: none; + display: none; +} + +/* Add Font Awesome icons for Previous & Next Arrows */ +::ng-deep .carousel-control-prev::before { + content: "\f104"; + font-family: "Font Awesome 5 Free"; + font-weight: 900; + width: 20px; + height: 20px; + position: relative; + color: white; + right: 22px; + bottom: 35px; + background-color: $primary; + border-radius: 50%; +} + +::ng-deep .carousel-control-next::before { + content: "\f105"; + font-family: "Font Awesome 5 Free"; + font-weight: 900; + position: relative; + left: 22px; + bottom: 35px; + width: 20px; + height: 20px; + color: white; + background-color: $primary; + border-radius: 50%; +} diff --git a/src/app/k8s/k8s-info/K8sInfoComponent.ts b/src/app/k8s/k8s-info/K8sInfoComponent.ts new file mode 100644 index 0000000..7a9d577 --- /dev/null +++ b/src/app/k8s/k8s-info/K8sInfoComponent.ts @@ -0,0 +1,373 @@ +/* + Copyright 2020 TATA ELXSI + + 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. + + Author: SANDHYA JS (sandhya.j@tataelxsi.co.in) +*/ +/** + * @file Info K8s Page + */ +import { Component, HostListener, Injector, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { DeleteComponent } from 'DeleteComponent'; +import { environment } from 'environment'; +import { K8SCLUSTERDATA, K8SCLUSTERDATADISPLAY, K8SNODEDATA } from 'K8sModel'; +import { KSUAddComponent } from 'KSUAddComponent'; +import { NodeAddComponent } from 'NodeAddComponent'; +import { RestService } from 'RestService'; +import { isNullOrUndefined, SharedService } from 'SharedService'; +import { ShowInfoComponent } from 'ShowInfoComponent'; + +/** + * Creating component + * @Component K8sInfoComponent.html as template url + */ +@Component({ + selector: 'app-info-k8s', + templateUrl: './K8sInfoComponent.html', + styleUrls: ['./K8sInfoComponent.scss'] +}) +/** Exporting a class @exports K8sInfoComponent */ +export class K8sInfoComponent implements OnInit { + /** To inject services @public */ + public injector: Injector; + + /** Contains active button @public */ + public activeButton = 1; + + /** condition to show tooltip @public */ + public showTooltip = false; + + /** Showing more details of collapase */ + public isCollapsed1: boolean = true; + + /** Showing more details of collapase */ + public isCollapsed2: boolean = false; + + /** Check the Projects loading results @public */ + public isLoadingResults: boolean = false; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** variables contains paramsID @public */ + public paramsID: string; + + /** Contains cluster name @public */ + public clusterName: string; + + /** Contains cluster data with count @public */ + public countData: K8SCLUSTERDATA = {}; + + /** Contains node count @public */ + public node_count: number; + + /** Contains ksu count @public */ + public ksu_count: number; + + /** Contains node details @public */ + public nodeDetail: {}[] = []; + + /** Contains ksu details @public */ + public ksuDetail: {}[] = []; + + /** Condition for popover visibility @public */ + public isPopoverVisible = false; + + /** Contains popover text to show @public */ + public popoverText = ''; + + /** Instance of the rest service @private */ + private restService: RestService; + + /** Holds teh instance of AuthService class of type AuthService @private */ + private activatedRoute: ActivatedRoute; + + /** Utilizes modal service for any modal operations @private */ + private modalService: NgbModal; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.activatedRoute = this.injector.get(ActivatedRoute); + this.modalService = this.injector.get(NgbModal); + this.sharedService = this.injector.get(SharedService); + } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + this.paramsID = this.activatedRoute.snapshot.paramMap.get('id'); + this.generateData(); + } + + /** To view Details @public */ + public onView(identifier: string, type: number): void { + let pageName: string = ''; + let title: string = ''; + if (type === 1) { + pageName = 'k8s-node'; + title = 'Node Details'; + } else { + pageName = 'k8s-ksu'; + title = 'KSU Details'; + } + // eslint-disable-next-line security/detect-non-literal-fs-filename + this.modalService.open(ShowInfoComponent, { backdrop: 'static' }).componentInstance.params = { + id: identifier, + page: pageName, + titleName: title, + cluster_id: this.paramsID + }; + } + + /** Edit Node @public */ + public onEdit(type: number): void { + // eslint-disable-next-line security/detect-non-literal-fs-filename + const modalRef: NgbModalRef = this.modalService.open(NodeAddComponent, { backdrop: 'static' }); + if (type === 1) { + modalRef.componentInstance.profileType = 'card-node'; + } + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + } + }).catch((): void => { + // Catch Navigation Error + }); + } + + /** Delete Operation @public */ + public onDelete(id_value: string, name_value: string, type: number): void { + let pageName: string = ''; + let title: string = ''; + if (type === 1) { + pageName = 'card-node'; + title = 'Node Details'; + } else { + pageName = 'card-ksu'; + title = 'KSU Details'; + } + // eslint-disable-next-line security/detect-non-literal-fs-filename + const modalRef: NgbModalRef = this.modalService.open(DeleteComponent, { backdrop: 'static' }); + modalRef.componentInstance.params = { + id: id_value, + page: pageName, + titleName: 'Node Details', + cluster_id: this.paramsID, + name: name_value + }; + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + this.nodeData(); + } + }).catch((): void => { + // Catch Navigation Error + }); + } + + /** popover functionality to show full description @public */ + public showPopover(event: MouseEvent, description: string) { + this.popoverText = description; + this.isPopoverVisible = true; + const popover = (event.target as HTMLElement).nextElementSibling as HTMLElement; + if (popover) { + // eslint-disable-next-line @typescript-eslint/no-magic-numbers + popover.style.top = `${event.clientY + 10}px`; + popover.style.left = `${event.clientX}px`; + } + } + + /** To hide popover @public */ + public hidePopover() { + this.isPopoverVisible = false; + } + + /** Add Node/KSU @public */ + public addK8sCluster(type?: string): void { + if (type === 'card_node') { + // eslint-disable-next-line security/detect-non-literal-fs-filename + const modalRef: NgbModalRef = this.modalService.open(NodeAddComponent, { backdrop: 'static' }); + modalRef.componentInstance.profileType = type; + modalRef.componentInstance.clusterId = this.paramsID; + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + this.nodeData(); + } + }).catch((): void => { + // Catch Navigation Error + }); + } else if (type === 'card_ksu') { + // eslint-disable-next-line security/detect-non-literal-fs-filename + const modalRef: NgbModalRef = this.modalService.open(KSUAddComponent, { backdrop: 'static' }); + modalRef.componentInstance.profileType = 'card-add'; + modalRef.componentInstance.profileID = this.paramsID; + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + this.ksuData(); + } + }).catch((): void => { + // Catch Navigation Error + }); + } + } + + /** Close tooltip when clicking outside @public */ + @HostListener('document:click', ['$event']) + closeTooltip(event: Event) { + if (!(event.target as HTMLElement).classList.contains('short-description')) { + this.showTooltip = false; + } + } + /** Generate nodeData object from loop and return for the datasource @public */ + public generateNodeData(nodedata: K8SCLUSTERDATA): K8SCLUSTERDATADISPLAY { + return { + name: nodedata.name, + state: !isNullOrUndefined(nodedata.resourceState) ? nodedata.resourceState : 'N/A', + identifier: nodedata._id, + version: nodedata.k8s_version, + description: !isNullOrUndefined(nodedata.description) ? nodedata.description : '-', + nodeCount: nodedata.node_count, + nodeSize: nodedata.node_size, + created: !isNullOrUndefined(nodedata?._admin?.created) ? this.sharedService.convertEpochTime(Number(nodedata._admin.created)) : '-', + modified: !isNullOrUndefined(nodedata?._admin?.modified) ? this.sharedService.convertEpochTime(Number(nodedata._admin.modified)) : '-' + }; + } + /** Generate ksuData object from loop and return for the datasource @public */ + public generateKsuData(ksudata: K8SCLUSTERDATA): K8SCLUSTERDATADISPLAY { + return { + name: ksudata.name, + state: !isNullOrUndefined(ksudata.resourceState) ? ksudata.resourceState : 'N/A', + identifier: ksudata._id, + version: ksudata.k8s_version, + description: !isNullOrUndefined(ksudata.description) ? ksudata.description : '-', + package_name: ksudata.package_name, + package_path: ksudata.package_path, + created: !isNullOrUndefined(ksudata?._admin?.created) ? this.sharedService.convertEpochTime(Number(ksudata._admin.created)) : '-', + modified: !isNullOrUndefined(ksudata?._admin?.modified) ? this.sharedService.convertEpochTime(Number(ksudata._admin.modified)) : '-' + }; + } + + /** Generate k8s Data function @public */ + public generateData(): void { + this.isLoadingResults = true; + this.restService.getResource(environment.K8SCREATECLUSTER_URL + '/' + this.paramsID).subscribe((k8sClusterDatas: K8SCLUSTERDATA) => { + if (!isNullOrUndefined(k8sClusterDatas)) { + this.node_count = k8sClusterDatas.node_count; + this.ksu_count = k8sClusterDatas.ksu_count; + this.countData = k8sClusterDatas; + this.clusterName = k8sClusterDatas.name; + this.nodeData(); + } + this.isLoadingResults = false; + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLoadingResults = false; + }); + } + + /** Generate node Data function @public */ + public nodeData(): void { + this.isLoadingResults = true; + this.restService.getResource(environment.K8SCREATECLUSTER_URL + '/' + this.paramsID + '/nodegroup').subscribe((k8sClusterDatas: K8SCLUSTERDATA) => { + if (!isNullOrUndefined(k8sClusterDatas)) { + this.nodeDetail = []; + this.node_count = k8sClusterDatas.count; + k8sClusterDatas.data?.forEach((clusterData: K8SNODEDATA) => { + const k8sClusterDataObj: K8SCLUSTERDATADISPLAY = this.generateNodeData(clusterData); + this.nodeDetail.push(k8sClusterDataObj); + }); + } + this.isLoadingResults = false; + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLoadingResults = false; + }); + } + + /** Generate ksu Data function @public */ + public ksuData(): void { + this.isLoadingResults = true; + this.restService.getResource(environment.K8SCREATECLUSTER_URL + '/' + this.paramsID + '/ksus').subscribe((k8sClusterDatas: K8SCLUSTERDATA) => { + if (!isNullOrUndefined(k8sClusterDatas)) { + this.ksuDetail = []; + this.ksu_count = k8sClusterDatas.count; + k8sClusterDatas.data?.forEach((clusterData: K8SNODEDATA) => { + const k8sClusterDataObj: K8SCLUSTERDATADISPLAY = this.generateKsuData(clusterData); + this.ksuDetail.push(k8sClusterDataObj); + }); + } + this.isLoadingResults = false; + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLoadingResults = false; + }); + } + + /** To capture active button @public */ + public setActiveButton(buttonNumber: number) { + this.activeButton = buttonNumber; + if (buttonNumber === 1) { + this.nodeData(); + this.isCollapsed1 = true; + this.isCollapsed2 = false; + } else if (buttonNumber === 2) { + this.ksuData(); + this.isCollapsed2 = true; + this.isCollapsed1 = false; + } + } + + /** Scaling @public */ + public scaling(id: string, type?: string): void { + // eslint-disable-next-line security/detect-non-literal-fs-filename + const modalRef: NgbModalRef = this.modalService.open(NodeAddComponent, { backdrop: 'static' }); + modalRef.componentInstance.clusterId = this.paramsID; + modalRef.componentInstance.profileID = id; + modalRef.componentInstance.profileType = type; + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + this.nodeData(); + } + }).catch((): void => { + // Catch Navigation Error + }); + } + + /** Edit KSU @public */ + public editKsu(id?: string): void { + // eslint-disable-next-line security/detect-non-literal-fs-filename + const modalRef: NgbModalRef = this.modalService.open(KSUAddComponent, { backdrop: 'static' }); + modalRef.componentInstance.profileID = id; + modalRef.componentInstance.profileType = 'edit'; + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + this.ksuData(); + } + }).catch((): void => { + // Catch Navigation Error + }); + } +} diff --git a/src/app/k8s/k8s-ksu/ksu-add/KSUAddComponent.html b/src/app/k8s/k8s-ksu/ksu-add/KSUAddComponent.html index e75cc7e..40154b1 100644 --- a/src/app/k8s/k8s-ksu/ksu-add/KSUAddComponent.html +++ b/src/app/k8s/k8s-ksu/ksu-add/KSUAddComponent.html @@ -17,7 +17,7 @@ Author: SANDHYA JS (sandhya.j@tataelxsi.co.in) -->
@@ -117,7 +117,7 @@ Author: SANDHYA JS (sandhya.j@tataelxsi.co.in)
-
diff --git a/src/app/k8s/k8scluster/K8sClusterComponent.ts b/src/app/k8s/k8scluster/K8sClusterComponent.ts index c79493e..c50831c 100644 --- a/src/app/k8s/k8scluster/K8sClusterComponent.ts +++ b/src/app/k8s/k8scluster/K8sClusterComponent.ts @@ -76,7 +76,7 @@ export class K8sClusterComponent implements OnInit, OnDestroy { /** operational State in deletion data @public */ public operationalStateThirdStep: string = CONFIGCONSTANT.k8OperationalStateThirdStep; - /** operational State failed deletion data @public */ + /** operational State failed creation data @public */ public operationalStateFourthStep: string = CONFIGCONSTANT.k8OperationalStateFourthStep; /** operational State failed creation data @public */ @@ -165,7 +165,7 @@ export class K8sClusterComponent implements OnInit, OnDestroy { } }, state: { - title: this.translateService.instant('GITSTATE'), width: '15%', type: 'html', + title: this.translateService.instant('STATE'), width: '15%', type: 'html', filter: { type: 'list', config: { @@ -176,7 +176,7 @@ export class K8sClusterComponent implements OnInit, OnDestroy { { value: this.operationalStateThirdStep, title: this.operationalStateThirdStep }, { value: this.operationalStateFourthStep, title: this.operationalStateFourthStep }, { value: this.operationalStateFifthStep, title: this.operationalStateFifthStep }, - { value: this.operationalStateSixthStep, title: this.operationalStateFifthStep } + { value: this.operationalStateSixthStep, title: this.operationalStateSixthStep } ] } }, @@ -187,30 +187,27 @@ export class K8sClusterComponent implements OnInit, OnDestroy { `; } else if (row.state === this.operationalStateSecondStep) { return ` - - `; - } else if (row.state === this.operationalStateThirdStep) { - return ` - + `; } else if (row.state === this.operationalStateFourthStep) { return ` - + `; } else if (row.state === this.operationalStateFifthStep) { return ` - + `; } else if (row.state === this.operationalStateSixthStep) { return ` - + `; } else { - return `${row.state}`; + return ` + + `; } } }, - created: { title: this.translateService.instant('CREATED'), width: '15%' }, Actions: { name: 'Action', width: '5%', filter: false, sort: false, title: this.translateService.instant('ACTIONS'), type: 'custom', valuePrepareFunction: (cell: K8SCLUSTERDATADISPLAY, row: K8SCLUSTERDATADISPLAY): K8SCLUSTERDATADISPLAY => row, @@ -248,8 +245,10 @@ export class K8sClusterComponent implements OnInit, OnDestroy { if (type === 'Register') { modalRef.componentInstance.profileType = 'Register'; - } else { + } else if (type === 'Manage') { modalRef.componentInstance.profileType = 'Manage'; + } else { + modalRef.componentInstance.profileType = 'Control'; } modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { if (result) { @@ -271,9 +270,8 @@ export class K8sClusterComponent implements OnInit, OnDestroy { public generateK8sclusterData(k8sClusterdata: K8SCLUSTERDATA): K8SCLUSTERDATADISPLAY { return { name: k8sClusterdata.name, - state: !isNullOrUndefined(k8sClusterdata.state) ? k8sClusterdata.state : 'N/A', + state: !isNullOrUndefined(k8sClusterdata.resourceState) ? k8sClusterdata.resourceState : 'N/A', identifier: k8sClusterdata._id, - operationalState: !isNullOrUndefined(k8sClusterdata._admin.operationalState) ? k8sClusterdata._admin.operationalState : 'N/A', version: k8sClusterdata.k8s_version, created: this.sharedService.convertEpochTime(Number(k8sClusterdata._admin.created)), modified: this.sharedService.convertEpochTime(Number(k8sClusterdata._admin.modified)), @@ -281,7 +279,7 @@ export class K8sClusterComponent implements OnInit, OnDestroy { createdbyosm: !isNullOrUndefined(k8sClusterdata.created) ? (k8sClusterdata.created) : 'false', bootstrap: !isNullOrUndefined(k8sClusterdata.bootstrap) ? (k8sClusterdata.bootstrap) : false, key: (k8sClusterdata.key === 'registered') ? true : false, - clusterMode: (k8sClusterdata.created === 'true') ? 'MANAGED' : 'REGISTERED' + clusterMode: (k8sClusterdata.created === 'true') ? 'managed' : 'registered' }; } diff --git a/src/app/k8s/node-add/NodeAddComponent.html b/src/app/k8s/node-add/NodeAddComponent.html new file mode 100644 index 0000000..5c44524 --- /dev/null +++ b/src/app/k8s/node-add/NodeAddComponent.html @@ -0,0 +1,126 @@ + + + + + + + \ No newline at end of file diff --git a/src/app/k8s/node-add/NodeAddComponent.scss b/src/app/k8s/node-add/NodeAddComponent.scss new file mode 100644 index 0000000..c55461a --- /dev/null +++ b/src/app/k8s/node-add/NodeAddComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + 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. + + Author: SANDHYA JS (sandhya.j@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/k8s/node-add/NodeAddComponent.ts b/src/app/k8s/node-add/NodeAddComponent.ts new file mode 100644 index 0000000..604a869 --- /dev/null +++ b/src/app/k8s/node-add/NodeAddComponent.ts @@ -0,0 +1,359 @@ +/* + Copyright 2020 TATA ELXSI + + 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. + + Author: SANDHYA JS (sandhya.j@tataelxsi.co.in) +*/ +/** + * @file K8sAddClusterComponent.ts. + */ +import { HttpHeaders } from '@angular/common/http'; +import { Component, Injector, Input, OnInit } from '@angular/core'; +import { AbstractControl, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { NotifierService } from 'angular-notifier'; +import { APIURLHEADER, ERRORDATA, MODALCLOSERESPONSEDATA, TYPESECTION } from 'CommonModel'; +import { environment } from 'environment'; +import { K8SCLUSTERDATA, K8SPayload } from 'K8sModel'; +import { RestService } from 'RestService'; +import { isNullOrUndefined, SharedService } from 'SharedService'; +/** + * Creating Component + * @Component takes NodeAddComponent.html as template url + */ +@Component({ + selector: 'app-node-add', + templateUrl: './NodeAddComponent.html', + styleUrls: ['./NodeAddComponent.scss'] +}) +/** Exporting a class @exports NodeAddComponent */ +export class NodeAddComponent implements OnInit { + /** To inject services @public */ + public injector: Injector; + + /** FormGroup instance added to the form @ html @public */ + public nodeForm: FormGroup; + + /** Contains all vim account collections */ + public clusterItems: K8SCLUSTERDATA; + + /** Input contains Modal dialog component Instance @public */ + @Input() public profileType: string; + + /** Input contains Modal dialog component Instance @public */ + @Input() public profileID: string; + + /** Input contains Modal dialog component Instance @public */ + @Input() public clusterId: string; + + /** Instance for active modal service @public */ + public activeModal: NgbActiveModal; + + /** Variable set for twoway bindng @public */ + public vimAccountId: string; + + /** contains url @public */ + public clusterUrl: string; + + /** contains privatesubnet items @public */ + privateItems: [] = []; + + /** contains publicsubnet items @public */ + publicItems: [] = []; + + /** contains privatesubnet items from api @public */ + privatesubnetItems: {}; + + /** contains publicsubnet itams from api @public */ + publicsubnetItems: {}; + + /** contains cluster ID @public */ + public cluster_name: string; + + /** contains scaling action paylaod@public */ + public scalingPayload = {}; + + /** Form submission Add */ + public submitted: boolean = false; + + /** contains payload */ + public payload: K8SPayload; + + /** Check the loading results @public */ + public isLoadingResults: boolean = false; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** FormBuilder instance added to the formBuilder @private */ + private formBuilder: FormBuilder; + + /** Utilizes rest service for any CRUD operations @private */ + private restService: RestService; + + /** Notifier service to popup notification @private */ + private notifierService: NotifierService; + + /** Contains tranlsate instance @private */ + private translateService: TranslateService; + + /** Controls the header form @private */ + private headers: HttpHeaders; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.activeModal = this.injector.get(NgbActiveModal); + this.formBuilder = this.injector.get(FormBuilder); + this.notifierService = this.injector.get(NotifierService); + this.translateService = this.injector.get(TranslateService); + this.sharedService = this.injector.get(SharedService); + } + + public ngOnInit(): void { + /** On Initializing call the methods */ + this.nodeFormAction(); + this.getDetailsvimAccount(); + this.headers = new HttpHeaders({ + Accept: 'application/json', + 'Content-Type': 'application/json', + 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0' + }); + if (this.profileType === 'card_node') { + this.cluster_name = this.clusterId; + this.clusterChange(); + } else if (this.profileType === 'k8s-scale' || this.profileType === 'edit-node') { + this.cluster_name = this.clusterId; + this.getDetails(); + } + else { + this.cluster_name = null; + } + } + + /** On modal initializing forms @public */ + public nodeFormAction(): void { + this.nodeForm = this.formBuilder.group({ + name: ['', [Validators.required]], + description: [''], + node_count: ['', [Validators.required]], + node_size: ['', [Validators.required]], + nodeCount: [''], + role: [''], + public_subnet: [[]], + private_subnet: [[]], + cluster_id: ['', [Validators.required]], + edit_name: [''] + }); + } + + /** convenience getter for easy access to form fields */ + get f(): FormGroup['controls'] { return this.nodeForm.controls; } + + + /** Call the vimAccount details in the selection options @public */ + public getDetailsvimAccount(): void { + this.isLoadingResults = true; + this.restService.getResource(environment.K8SCREATECLUSTER_URL).subscribe((vimAccounts: K8SCLUSTERDATA) => { + this.clusterItems = vimAccounts; + this.isLoadingResults = false; + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLoadingResults = false; + }); + } + + /** To patch value for edit @public */ + public getDetails(): void { + this.isLoadingResults = true; + this.restService.getResource(environment.K8SCREATECLUSTER_URL + '/' + this.cluster_name + '/nodegroup' + '/' + this.profileID).subscribe((vimAccounts: K8SCLUSTERDATA) => { + if (this.profileType === 'edit-node') { + this.nodeForm.patchValue({ edit_name: vimAccounts.name, description: vimAccounts.description }); + } else if (this.profileType === 'k8s-scale') { + this.nodeForm.patchValue({ nodeCount: vimAccounts.node_count }); + } + this.isLoadingResults = false; + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLoadingResults = false; + }); + } + + /** To get privatesubnet and publicsubnet details @public */ + public clusterChange(): void { + this.isLoadingResults = true; + this.restService.getResource(environment.K8SCREATECLUSTER_URL + '/' + this.cluster_name).subscribe((vimAccounts: K8SCLUSTERDATA) => { + if (!isNullOrUndefined(vimAccounts)) { + this.privatesubnetItems = vimAccounts.private_subnet; + this.publicsubnetItems = vimAccounts.public_subnet; + } + this.isLoadingResults = false; + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLoadingResults = false; + }); + } + + /** On modal submit nodeSubmit will called @public */ + public nodeSubmit(): void { + if (this.profileType === 'card_node') { + this.getFormControl('nodeCount').disable(); + this.getFormControl('cluster_id').disable(); + this.getFormControl('edit_name').disable(); + this.clusterUrl = environment.K8SCREATECLUSTER_URL + '/' + this.cluster_name + '/nodegroup'; + this.createNode(); + } else if (this.profileType === 'k8s-scale') { + this.getFormControl('cluster_id').disable(); + this.getFormControl('name').disable(); + this.getFormControl('description').disable(); + this.getFormControl('private_subnet').disable(); + this.getFormControl('public_subnet').disable(); + this.getFormControl('role').disable(); + this.getFormControl('node_count').disable(); + this.getFormControl('node_size').disable(); + this.getFormControl('edit_name').disable(); + this.clusterUrl = environment.K8SCREATECLUSTER_URL + '/' + this.cluster_name + '/nodegroup' + '/' + this.profileID + '/scale'; + this.scaling(); + } else if (this.profileType === 'edit-node') { + this.getFormControl('cluster_id').disable(); + this.getFormControl('nodeCount').disable(); + this.getFormControl('private_subnet').disable(); + this.getFormControl('public_subnet').disable(); + this.getFormControl('role').disable(); + this.getFormControl('node_count').disable(); + this.getFormControl('node_size').disable(); + this.getFormControl('name').disable(); + this.clusterUrl = environment.K8SCREATECLUSTER_URL + '/' + this.cluster_name + '/nodegroup' + '/' + this.profileID; + this.update(); + } + } + + /** Create Node @public */ + public createNode(): void { + this.submitted = true; + this.sharedService.cleanForm(this.nodeForm); + if (this.nodeForm.invalid) { + return; + } + const modalData: MODALCLOSERESPONSEDATA = { + message: 'Done' + }; + const apiURLHeader: APIURLHEADER = { + url: this.clusterUrl, + httpOptions: { headers: this.headers } + }; + this.isLoadingResults = true; + const formData = this.nodeForm.value; + const payload = Object.keys(formData).reduce((acc, key) => { + const value = formData[key]; + if (key === 'node_count' && value !== null && value !== undefined && value !== '') { + acc[key] = Number(value); + } else if (value !== null && value !== undefined && value !== '' && value.length !== 0) { + acc[key] = value; + } + return acc; + }, {}); + this.restService.postResource(apiURLHeader, payload).subscribe((result: {}) => { + this.activeModal.close(modalData); + this.isLoadingResults = false; + this.notifierService.notify('success', this.nodeForm.value.name + + this.translateService.instant('PAGE.K8S.NODECREATEDSUCCESSFULLY')); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'post'); + this.isLoadingResults = false; + }); + } + /** Node Scaling @public */ + public scaling(): void { + this.submitted = true; + this.sharedService.cleanForm(this.nodeForm); + if (this.nodeForm.invalid) { + return; + } + const modalData: MODALCLOSERESPONSEDATA = { + message: 'Done' + }; + const apiURLHeader: APIURLHEADER = { + url: this.clusterUrl, + httpOptions: { headers: this.headers } + }; + this.isLoadingResults = true; + if (this.profileType === 'k8s-scale') { + this.scalingPayload = { + node_count: Number(this.nodeForm.value.nodeCount) + }; + } + this.restService.postResource(apiURLHeader, this.scalingPayload).subscribe((result: {}) => { + this.activeModal.close(modalData); + this.isLoadingResults = false; + this.notifierService.notify('success', + this.translateService.instant('PAGE.K8S.NODEUPDATEDSUCCESSFULLY')); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'post'); + this.isLoadingResults = false; + }); + } + /** Node update @public */ + public update(): void { + this.submitted = true; + this.sharedService.cleanForm(this.nodeForm); + if (this.nodeForm.invalid) { + return; + } + const modalData: MODALCLOSERESPONSEDATA = { + message: 'Done' + }; + const apiURLHeader: APIURLHEADER = { + url: this.clusterUrl, + httpOptions: { headers: this.headers } + }; + this.isLoadingResults = true; + if (this.profileType === 'edit-node') { + if (this.nodeForm.value.description === '') { + this.scalingPayload = { + name: this.nodeForm.value.edit_name + }; + delete this.nodeForm.value.description; + } else if (this.nodeForm.value.name === '') { + this.scalingPayload = { + description: this.nodeForm.value.description + }; + delete this.nodeForm.value.name; + } else { + this.scalingPayload = { + name: this.nodeForm.value.edit_name, + description: this.nodeForm.value.description + }; + } + } + this.restService.patchResource(apiURLHeader, this.scalingPayload).subscribe((result: {}) => { + this.activeModal.close(modalData); + this.isLoadingResults = false; + this.notifierService.notify('success', + this.translateService.instant('PAGE.K8S.NODEUPDATEDSUCCESSFULLY')); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'post'); + this.isLoadingResults = false; + }); + } + /** Used to get the AbstractControl of controlName passed @private */ + private getFormControl(controlName: string): AbstractControl { + // eslint-disable-next-line security/detect-object-injection + return this.nodeForm.controls[controlName]; + } +} diff --git a/src/app/packages/oka-packages/OKAPackageComponent.html b/src/app/packages/oka-packages/OKAPackageComponent.html index 8858311..b2661a2 100644 --- a/src/app/packages/oka-packages/OKAPackageComponent.html +++ b/src/app/packages/oka-packages/OKAPackageComponent.html @@ -28,10 +28,9 @@ Author: SANDHYA JS (sandhya.j@tataelxsi.co.in)
diff --git a/src/app/packages/oka-packages/OKAPackageComponent.ts b/src/app/packages/oka-packages/OKAPackageComponent.ts index 702d8e6..bf6e122 100644 --- a/src/app/packages/oka-packages/OKAPackageComponent.ts +++ b/src/app/packages/oka-packages/OKAPackageComponent.ts @@ -70,9 +70,6 @@ export class OKAPackageComponent implements OnInit { /** operational State failed deletion data @public */ public operationalStateFourthStep: string = CONFIGCONSTANT.k8OperationalStateFourthStep; - /** operational State failed creation data @public */ - public operationalStateFifthStep: string = CONFIGCONSTANT.k8OperationalStateFifthStep; - /** Check the loading results @public */ public isLoadingResults: boolean = true; @@ -125,7 +122,7 @@ export class OKAPackageComponent implements OnInit { name: { title: this.translateService.instant('NAME'), width: '15%', sortDirection: 'asc' }, identifier: { title: this.translateService.instant('IDENTIFIER'), width: '20%' }, state: { - title: this.translateService.instant('GITSTATE'), width: '15%', type: 'html', + title: this.translateService.instant('STATE'), width: '15%', type: 'html', filter: { type: 'list', config: { @@ -134,34 +131,27 @@ export class OKAPackageComponent implements OnInit { { value: this.operationalStateFirstStep, title: this.operationalStateFirstStep }, { value: this.operationalStateSecondStep, title: this.operationalStateSecondStep }, { value: this.operationalStateThirdStep, title: this.operationalStateThirdStep }, - { value: this.operationalStateThirdStep, title: this.operationalStateFourthStep }, - { value: this.operationalStateThirdStep, title: this.operationalStateFifthStep } + { value: this.operationalStateThirdStep, title: this.operationalStateFourthStep } ] } }, valuePrepareFunction: (cell: VNFD, row: VNFD): string => { if (row.state === this.operationalStateFirstStep) { return ` - - `; + + `; } else if (row.state === this.operationalStateSecondStep) { return ` - - `; - } else if (row.state === this.operationalStateThirdStep) { - return ` - - `; + + `; } else if (row.state === this.operationalStateFourthStep) { return ` - - `; - } else if (row.state === this.operationalStateFifthStep) { - return ` - - `; + + `; } else { - return `${row.state}`; + return ` + + `; } } }, @@ -218,7 +208,7 @@ export class OKAPackageComponent implements OnInit { onboardingState: okadpackagedata._admin.onboardingState, usageState: okadpackagedata._admin.usageState, created: this.sharedService.convertEpochTime(Number(okadpackagedata._admin.created)), - state: okadpackagedata.state + state: okadpackagedata.resourceState }; } /** Handle compose new oka package method @public */ diff --git a/src/app/utilities/compose-packages/ComposePackages.html b/src/app/utilities/compose-packages/ComposePackages.html index 007b0ab..be8081e 100644 --- a/src/app/utilities/compose-packages/ComposePackages.html +++ b/src/app/utilities/compose-packages/ComposePackages.html @@ -36,9 +36,12 @@ Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.i - - - + + +
{{'CONFIG' | translate}}*
+ formControlName="config" id="config" [ngClass]="{ 'is-invalid': submitted && f.config.errors }">
{{'FILEUPLOADLABEL' | translate}}
- +
-