diff options
Diffstat (limited to 'usecaseui-portal/src/app/views/onboard-vnf-vm')
5 files changed, 724 insertions, 0 deletions
diff --git a/usecaseui-portal/src/app/views/onboard-vnf-vm/onboard-vnf-vm.component.css b/usecaseui-portal/src/app/views/onboard-vnf-vm/onboard-vnf-vm.component.css new file mode 100644 index 00000000..4e80750c --- /dev/null +++ b/usecaseui-portal/src/app/views/onboard-vnf-vm/onboard-vnf-vm.component.css @@ -0,0 +1,67 @@ +/* + Copyright (C) 2019 CMCC, Inc. and others. All rights reserved. + + 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. +*/ +.title { + font: 700 18px/18px "思源黑体"; + color: #4c5e70; + margin-bottom: 18px; +} +hr { + border: none; + height: 2px; + background-color: #dce1e7; + margin-bottom: 20px; +} +.list { + background-color: #fff; + border-radius: 5px; + padding: 10px; +} +.list nz-table tbody td span.onboarding { + font-size: 12px; + color: #147dc2; +} +.list nz-table tbody td span.onboarded { + font-size: 14px; + color: #147dc2; +} +.list nz-table tbody td span.updating { + font-size: 12px; + color: blue; +} +.list nz-table tbody td span.deleting { + font-size: 12px; + color: red; +} +.list nz-table tbody td span.invalid { + font-size: 14px; + color: purple; +} +.list nz-table tbody td i.anticon { + cursor: pointer; + font-size: 18px; + padding: 2px; +} +.list nz-table tbody td i.anticon:hover { + color: #147dc2; +} +.list nz-table tbody td .cannotclick { + pointer-events: none; + color: #aaa; + opacity: 0.6; +} +.list nz-table tbody td .fileIcon{ + display: none; +}
\ No newline at end of file diff --git a/usecaseui-portal/src/app/views/onboard-vnf-vm/onboard-vnf-vm.component.html b/usecaseui-portal/src/app/views/onboard-vnf-vm/onboard-vnf-vm.component.html new file mode 100644 index 00000000..c48d68f9 --- /dev/null +++ b/usecaseui-portal/src/app/views/onboard-vnf-vm/onboard-vnf-vm.component.html @@ -0,0 +1,123 @@ +<!-- + Copyright (C) 2019 CMCC, Inc. and others. All rights reserved. + + 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. +--> + +<!-- tab --> +<nz-tabset [nzTabPosition]="'top'" [nzType]="'card'"> + <nz-tab *ngFor="let tab of tabs" [nzTitle]="tab" (nzClick)="handleTabChange(tab)"></nz-tab> +</nz-tabset> +<div class="list"> + <!-- upload --> + <div class="listUploadContainer"> + <div class="listupload"> + <nz-upload nzType="drag" [(nzFileList)]="fileList" [nzBeforeUpload]="beforeUpload"> + <p class="ant-upload-drag-icon"> + <i nz-icon type="inbox" class="anticon anticon-inbox"></i> + </p> + <p class="ant-upload-text"> {{"i18nTextDefine_Click_CSAR_File" | translate}} </p> + <p class="ant-upload-hint"></p> + </nz-upload> + <button nz-button [nzLoading]="uploading" (click)="onClick()" [disabled]="fileList.length == 0" class="upload"> + {{ uploading ? 'Uploading' : 'Start Upload' }} + </button> + </div> + <div class="listlin"></div> + <div class="listfile"> + <div class="listFileTitle"> {{"i18nTextDefine_Uploaded_files" | translate}} </div> + <div class="nouploadfile" [style.display]="display">{{"i18nTextDefine_Nofileuploading" | translate}}</div> + <div class="listfilebgc" *ngIf="file"> + <i class="anticon anticon-link icon"></i> + <div class="color" [ngClass]="{'progress':file.status}">{{file.name}}</div> + <div class="color" *ngIf="file.status"> + <nz-progress [nzPercent]="file.progress" [nzShowInfo]="false"></nz-progress> + </div> + <div class="color" *ngIf="!file.status"> + <span *ngIf="file.success === 0">{{"i18nTextDefine_File_upload_completed" | translate}}</span> + <span *ngIf="file.success === 1">{{"i18nTextDefine_File_upload_failed" | translate}}</span> + </div> + <div *ngIf="!file.status"> + <i class="anticon anticon-check-circle success" *ngIf="file.success === 0"></i> + <i class="anticon anticon-exclamation-circle fail" *ngIf="file.success === 1"></i> + </div> + </div> + </div> + </div> + <!-- table --> + <nz-spin [nzSpinning]="isSpinning" class="listContainer"> + <div class="mask" *ngIf="isSpinning"></div> + <nz-table #nzTable [nzData]="currentTab === 'NS'? nsTableData: (currentTab === 'VNF'? vnfTableData : pnfTableData)" nzShowSizeChanger [nzFrontPagination]="true" + [nzShowQuickJumper]="true" [nzPageSizeOptions]="[5,10,15,20]" [(nzPageSize)]="pageSize" + [(nzPageIndex)]='pageIndex' nzSize="middle"> + <thead> + <tr class="theadColor"> + <th nzWidth="15%"> {{"i18nTextDefine_NO" | translate}} </th> + <th nzWidth="15%"> {{"i18nTextDefine_Name" | translate}} </th> + <th nzWidth="15%"> {{"i18nTextDefine_Version" | translate}} </th> + <th nzWidth="15%"> {{"i18nTextDefine_OnboardingState" | translate}} </th> + <th nzWidth="15%" *ngIf="currentTab !== 'PNF'"> {{"i18nTextDefine_OperationalState" | translate}} </th> + <th nzWidth="10%"> {{"i18nTextDefine_UsageState" | translate}} </th> + <th nzWidth="15%"> {{"i18nTextDefine_Operationbutton" | translate}} </th> + </tr> + </thead> + <tbody *ngIf="currentTab === 'NS'"> + <tr *ngFor="let item of nzTable.data;let i = index;"> + <td>{{i+1}}</td> + <td>{{item.nsdName || item.name }}</td> + <td>{{item.nsdVersion || item.version}}</td> + <td>{{item.nsdOnboardingState ? item.nsdOnboardingState : status}}</td> + <td>{{item.nsdOperationalState}}</td> + <td>{{item.nsdUsageState}}</td> + <td> + <i [ngClass]="{'cannotclick': isUpdate}" + class="anticon anticon-upload upicon" #upload_icon (click)="updataService(item.uuid)" + *ngIf="item.uuid"></i> + <i class="anticon anticon-delete" (click)="showDeleteConfirm(item.id)" + *ngIf="item.id"></i> + </td> + </tr> + </tbody> + <tbody *ngIf="currentTab === 'VNF'"> + <tr *ngFor="let item of nzTable.data;let i = index;"> + <td>{{i+1}}</td> + <td>{{item.vnfProductName || item.name }}</td> + <td>{{item.vnfdVersion || item.version}}</td> + <td>{{item.onboardingState}}</td> + <td>{{item.operationalState}}</td> + <td>{{item.usageState}}</td> + <td> + <i [ngClass]="{'cannotclick': isUpdate}" + class="anticon anticon-upload upicon" #upload_icon (click)="updataService(item.uuid)" + *ngIf="item.uuid"></i> + <i class="anticon anticon-delete" (click)="showDeleteConfirm(item.id)" + *ngIf="item.id"></i> + </td> + </tr> + </tbody> + <tbody *ngIf="currentTab === 'PNF'" > + <tr *ngFor="let item of nzTable.data; let i = index;"> + <td>{{i+1}}</td> + <td>{{item.pnfdName}}</td> + <td>{{item.pnfdVersion}}</td> + <td>{{item.pnfdOnboardingState}}</td> + <td>{{item.pnfdUsageState}}</td> + <td> + <i class="anticon anticon-delete" (click)="showDeleteConfirm(item.id)"></i> + </td> + </tr> + </tbody> + </nz-table> + </nz-spin> +</div> +<app-notification #notification [isServicesList]="false"></app-notification>
\ No newline at end of file diff --git a/usecaseui-portal/src/app/views/onboard-vnf-vm/onboard-vnf-vm.component.less b/usecaseui-portal/src/app/views/onboard-vnf-vm/onboard-vnf-vm.component.less new file mode 100644 index 00000000..f877e1ca --- /dev/null +++ b/usecaseui-portal/src/app/views/onboard-vnf-vm/onboard-vnf-vm.component.less @@ -0,0 +1,138 @@ +/* + Copyright (C) 2019 CMCC, Inc. and others. All rights reserved. + + 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. +*/ +th{ + background-color: rgba(241,243,244,.9) !important; +} +.list { + padding: 20px; + .listUploadContainer{ + display: flex; + align-items: flex-start; + width: 100%; + height: 30%; + margin-bottom: 1%; + .listupload { + position: relative; + width: 22%; + margin-left: 13%; + .upload{ + position: absolute; + left: 50%; + bottom: -48px; + transform: translateX(-50%); + color: #FFFFFF; + font-size: 13px; + background-color: #3E9BFF; + font-family: ArialMT; + } + } + .listlin { + width: 1%; + margin-left: 10%; + height: 177px; + margin-bottom: 30px; + border-right: 2px solid #EEEEEE; + } + .listfile { + width: 43%; + height: 100%; + margin-left: 10%; + .listFileTitle{ + height: 15%; + padding-bottom: 15px; + color: rgba(66,84,143,1); + font-family: ArialMT; + } + .nouploadfile{ + height: 80%; + width: 100%; + text-align: center; + font-size: 22px; + margin: 5% 0 10px; + } + .listfilebgc { + display: flex; + justify-content: space-around; + align-items: center; + width:100%; + height: 80%; + padding: 5px; + background-color: #fff; + border-radius: 2px; + color: #42548F; + .icon{ + width: 15px; + } + :nth-child(3){ + width: 50%; + } + :nth-child(4){ + width: 5%; + height:100%; + .success{ + color:#7BC7F3!important; + } + .fail{ + color:#fb5c5c!important; + } + } + .color { + color:rgba(66,84,143,1); + span{ + color:rgba(66,84,143,0.7); + } + .progress{ + color:rgba(66,84,143,0.7); + } + } + } + + } + + } + .listContainer{ + height: 69%; + .mask { + top: 0; + left: 0; + position: fixed; + width: 100%; + height: 100%; + opacity: 0.1; + background: black; + z-index: 1049; + } + } + nz-table { + tbody { + td { + i.anticon { + cursor: pointer; + font-size: 18px; + padding: 2px; + &:hover{ + color: #147dc2; + } + } + .cannotclick { + pointer-events: none; + color: #aaa; + opacity: 0.6; + } + } + } + } +} diff --git a/usecaseui-portal/src/app/views/onboard-vnf-vm/onboard-vnf-vm.component.spec.ts b/usecaseui-portal/src/app/views/onboard-vnf-vm/onboard-vnf-vm.component.spec.ts new file mode 100644 index 00000000..51980b8a --- /dev/null +++ b/usecaseui-portal/src/app/views/onboard-vnf-vm/onboard-vnf-vm.component.spec.ts @@ -0,0 +1,36 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { TranslateModule } from '@ngx-translate/core'; +import { HttpClientModule } from '@angular/common/http'; +import { NzMessageService, NzModalService } from 'ng-zorro-antd'; + +import { OnboardVnfVmComponent } from './onboard-vnf-vm.component'; +import { onboardService } from '../../core/services/onboard.service'; + +describe('OnboardVnfVmComponent', () => { + let component: OnboardVnfVmComponent; + let fixture: ComponentFixture<OnboardVnfVmComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + schemas: [ + CUSTOM_ELEMENTS_SCHEMA, + NO_ERRORS_SCHEMA + ], + imports: [TranslateModule, HttpClientModule], + declarations: [OnboardVnfVmComponent], + providers: [onboardService, NzMessageService, NzModalService] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(OnboardVnfVmComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/usecaseui-portal/src/app/views/onboard-vnf-vm/onboard-vnf-vm.component.ts b/usecaseui-portal/src/app/views/onboard-vnf-vm/onboard-vnf-vm.component.ts new file mode 100644 index 00000000..682f48cf --- /dev/null +++ b/usecaseui-portal/src/app/views/onboard-vnf-vm/onboard-vnf-vm.component.ts @@ -0,0 +1,360 @@ +/* + Copyright (C) 2019 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { HttpClient, HttpRequest, HttpResponse } from '@angular/common/http'; +import { Component, OnInit, HostBinding, TemplateRef, ViewChild } from '@angular/core'; +import { onboardService } from '../../core/services/onboard.service'; +import { slideToRight } from '../../shared/utils/animates'; +import { NzMessageService, UploadFile, NzModalRef, NzModalService } from 'ng-zorro-antd'; +import { filter } from 'rxjs/operators'; + +@Component({ + selector: 'app-onboard-vnf-vm', + templateUrl: './onboard-vnf-vm.component.html', + styleUrls: ['./onboard-vnf-vm.component.less'], + animations: [slideToRight] +}) +export class OnboardVnfVmComponent implements OnInit { + @HostBinding('@routerAnimate') routerAnimateState; + @ViewChild('notification') notification: any; + + // upload + tabs: string[] = ['NS', 'VNF', 'PNF']; + currentTab: string = 'NS' + fileList: UploadFile[] = []; + uploading: boolean = false; + infoId: string; + display: string = 'block'; + + // table + isSpinning: boolean = false; + nsTableData: any[]; + vnfTableData: any[]; + pnfTableData: any[]; + status: string = "Onboard Available"; + pageIndex: number = 1; + pageSize: number = 10; + + // update or delete + isUpdate: boolean = false; + jobId: string; + + //url + url = { + ns: '/api/nsd/v1/ns_descriptors/*_*/nsd_content', + vnf: '/api/vnfpkgm/v1/vnf_packages/*_*/package_content', + pnf: '/api/nsd/v1/pnf_descriptors/*_*/pnfd_content' + }; + + file: { + name: string, + uid: string, + progress: number, + status: boolean, + success: number + }; + + constructor( + private myhttp: onboardService, + private http: HttpClient, + private msg: NzMessageService, + private modalService: NzModalService + ) { } + + //default Call ns data by default + ngOnInit() { + this.getTableData(); + } + + // Handling tab switching request data + handleTabChange(tab: string): void { + this.currentTab = tab; + this.fileList = []; + this.display = 'block'; + delete this.file; + switch (tab) { + case 'NS': + this.getTableData(); + break + case 'VNF': + this.getTableVnfData() + break + case 'PNF': + this.getTablePnfData() + break + } + } + + //before put create--Drag and drop files to the page before uploading + requestBody = { + "userDefinedData": { + "additionalProp1": "", + "additionalProp2": "", + "additionalProp3": "" + } + } + + beforeUpload = (file: UploadFile): boolean => { + this.fileList.splice(0, 1, file); + let API: string; + if (this.currentTab === 'NS') { + API = 'createNetworkServiceData'; + } else if (this.currentTab === 'VNF') { + API = 'createVnfData'; + } else { + API = 'createPnfData'; + } + this.myhttp.getCreatensData(API, this.requestBody)//on-line + .subscribe((data) => { + this.infoId = data["id"]; + }, (err) => { + console.log(err); + }) + return false; + } + + // Drag and drop and click the upload button + onClick(): void { + this.display = 'none'; + let tab = this.currentTab === 'NS' ? 'ns' : (this.currentTab === 'VNF' ? 'vnf' : 'pnf') + this.handleUpload(this.url[tab].replace("*_*", this.infoId)); + } + + handleUpload(url: string): void { + const formData = new FormData(); + // tslint:disable-next-line:no-any + this.fileList.forEach((file: any) => { + formData.set('file', file); + }); + this.uploading = true; + this.file = { + name: this.fileList[0].name, + uid: this.fileList[0].uid, + progress: 0, + status: true, + success: 0 + }; + let requery = (file) => { + file.progress += 3; + setTimeout(() => { + if (file.progress < 100) { + requery(file) + } + }, 100) + }; + requery(this.file); + const req = new HttpRequest('PUT', url, formData, { + reportProgress: true, + withCredentials: true + }); + //Upload pre-empty array + this.fileList = []; + this.http.request(req) + .pipe(filter(e => e instanceof HttpResponse)) + .subscribe( + (event: {}) => { + this.file.progress = 100; + this.file.status = false; + this.uploading = false; + this.msg.success('upload successfully.'); + this.currentTab === 'NS' ? this.getTableData() : (this.currentTab === 'VNF' ? this.getTableVnfData() : this.getTablePnfData()); + }, + err => { + this.file.progress = 100; + this.file.status = false; + this.file.success = 1; + this.uploading = false; + this.msg.error('upload failed.'); + } + ); + } + + // Get the NS list + getTableData(): void { + this.isSpinning = true; + //ns vfc lists + this.myhttp.getOnboardTableData() + .subscribe((data) => { + this.nsTableData = data; + //ns sdc list + this.myhttp.getSDC_NSTableData() + .subscribe((data) => { + this.isSpinning = false; //loading hide + let nsData = data; + // this.NSTableData.map((nsvfc) => { nsvfc.sameid = nsData.find((nssdc) => { return nsvfc.id == nssdc.uuid }) && nsvfc.id; return nsvfc; }); + let sameData = nsData.filter((nssdc) => { return !this.nsTableData.find((nsvfc) => { return nsvfc.id == nssdc.uuid }) }); + this.nsTableData = this.nsTableData.concat(sameData); + }, (err) => { + console.error(err); + this.isSpinning = false; + }) + }, (err) => { + console.error(err); + this.isSpinning = false; + }) + } + + // Get the vnf list + getTableVnfData(): void { + this.isSpinning = true; + //vnf vfc lists + this.myhttp.getOnboardTableVnfData() + .subscribe((data) => { + this.vnfTableData = data; + //vnf sdc lists + this.myhttp.getSDC_VNFTableData() + .subscribe((data) => { + this.isSpinning = false; //loading hide + let vnfData = data; + // this.VNFTableData.map((vnfvfc) => { vnfvfc.sameid = this.vnfData.find((nssdc) => { return vnfvfc.id == nssdc.uuid }) && vnfvfc.id; return vnfvfc; }); + let sameData = vnfData.filter((vnfsdc) => { return !this.vnfTableData.find((vnfvfc) => { return vnfvfc.id == vnfsdc.uuid }) }); + this.vnfTableData = this.vnfTableData.concat(sameData); + }, (err) => { + console.error(err); + this.isSpinning = false; + }) + }, (err) => { + console.error(err); + this.isSpinning = false; + }) + } + + // Get pnf list + getTablePnfData() { + this.isSpinning = true; + this.myhttp.getOnboardTablePnfData() + .subscribe((data) => { + this.pnfTableData = data; + this.isSpinning = false; //loading hide + }, (err) => { + console.error(err); + this.isSpinning = false; + }) + } + + // confirm + showConfirm(requestBody: object, id: string): void { + let API = this.currentTab === 'NS' ? 'getNsonboard' : 'getVnfonboard'; + this.modalService.confirm({ + nzTitle: '<p>Are you sure you want to do this?</p>', + nzOnOk: () => { + this.myhttp[API](requestBody) + .subscribe((data) => { + if (data.status == "success") { + if (this.currentTab === 'NS') { + this.isUpdate = false; + this.notification.notificationSuccess(this.currentTab, "OnboardingState", id); + this.getTableData(); + } else { + this.jobId = data.jobId; + this.queryProgress(this.jobId, id); + this.getTableVnfData(); + } + } else { + this.isUpdate = false; + this.notification.notificationFailed(this.currentTab, "OnboardingState", id); + return false + } + }, (err) => { + console.log(err); + }) + } + }) + } + + + // ns onboard Upload button + updataService(id: string) { + this.isUpdate = true; + let requestBody = { "csarId": id }; + this.showConfirm(requestBody, id) + } + + //Progress Progress inquiry + queryProgress(jobId: string, id: string): any { + let mypromise = new Promise((res) => { + this.myhttp.getProgress(jobId, 0) + .subscribe((data) => { + if (data.responseDescriptor == null || data.responseDescriptor == "null" || data.responseDescriptor.progress == undefined || data.responseDescriptor.progress == null) { + this.isUpdate = true; + setTimeout(() => { + this.queryProgress(this.jobId, id); + }, 10000) + return false + } + if (data.responseDescriptor.progress > 100) { + this.isUpdate = false; + this.notification.notificationFailed(this.currentTab, 'OnboardingState', id); + } else if (data.responseDescriptor.progress < 100) { + this.isUpdate = true; + setTimeout(() => { + this.queryProgress(this.jobId, id); + }, 5000) + } else { + res(data); + this.isUpdate = false; + this.notification.notificationSuccess(this.currentTab, 'OnboardingState', id); + } + return false + }) + }) + return mypromise; + } + + /* delete button */ + showDeleteConfirm(pkgid: string): void { + this.modalService.confirm({ + nzTitle: 'Do you Want to delete these items?', + nzContent: 'Do you Want to delete these items?', + nzOkText: 'Yes', + nzCancelText: 'No', + nzOnOk: () => new Promise((resolve) => { + this.deleteService(pkgid, resolve); + }).catch(() => console.log('Oops errors!')) + }); + } + + //delete nsItem + deleteService(pkgid, resolve) { + let API: string; + if (this.currentTab === 'NS') { + API = 'deleteNsIdData'; + } else if (this.currentTab === 'VNF') { + API = 'deleteVnfIdData'; + } else { + API = 'deletePnfIdData'; + } + this.myhttp[API](pkgid) + .subscribe((data) => { + this.notification.notificationSuccess(this.currentTab, 'OnboardingState', pkgid); + resolve() + //refresh list after successful deletion + switch (this.currentTab) { + case 'NS': + this.getTableData(); + break + case 'VNF': + this.getTableVnfData(); + break + case 'PNF': + this.getTablePnfData(); + break + } + }, (err) => { + console.log(err); + this.notification.notificationFailed(this.currentTab, 'OnboardingState', pkgid); + }) + } +} |