Initial Commit - NG UI
* Roboto and font-awesome fonts are added in package.json
* Replace Nginx alpine varient to stable
* Devops files are added
* Docker file aligned as per community reviews
* Enhancement - NS primitive, Azure inclusion and domain name
* RWD changes
Change-Id: If543efbf127964cbd8f4be4c5a67260c91407fd9
Signed-off-by: kumaran.m <kumaran.m@tataelxsi.co.in>
diff --git a/src/app/packages/PackagesComponent.html b/src/app/packages/PackagesComponent.html
new file mode 100644
index 0000000..06b8876
--- /dev/null
+++ b/src/app/packages/PackagesComponent.html
@@ -0,0 +1,18 @@
+<!--
+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: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+-->
+<router-outlet></router-outlet>
diff --git a/src/app/packages/PackagesComponent.scss b/src/app/packages/PackagesComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/packages/PackagesComponent.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: 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/packages/PackagesComponent.ts b/src/app/packages/PackagesComponent.ts
new file mode 100644
index 0000000..eb9991d
--- /dev/null
+++ b/src/app/packages/PackagesComponent.ts
@@ -0,0 +1,55 @@
+/*
+ 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: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+*/
+/**
+ * @file Packages-Component.ts.
+ */
+import { Component, Injector } from '@angular/core';
+import { Router, RouterEvent } from '@angular/router';
+/**
+ * Creating Component
+ * @Component takes PackagesComponent.html as template url
+ */
+@Component({
+ selector: 'app-packages',
+ templateUrl: './PackagesComponent.html',
+ styleUrls: ['./PackagesComponent.scss']
+})
+/** Exporting a class @exports PackagesComponent */
+export class PackagesComponent{
+ /** Invoke service injectors @public */
+ public injector: Injector;
+
+ /** Holds teh instance of AuthService class of type AuthService @private */
+ private router: Router;
+
+ // creates packages component
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.router = this.injector.get(Router);
+ this.router.events.subscribe((event: RouterEvent) => {
+ this.redirectToList(event.url);
+ });
+ }
+
+ /** Return to list NS Package List */
+ public redirectToList(getURL: string): void {
+ if (getURL === '/packages') {
+ this.router.navigate(['/packages/ns']).catch();
+ }
+ }
+}
diff --git a/src/app/packages/PackagesModule.ts b/src/app/packages/PackagesModule.ts
new file mode 100644
index 0000000..e6c6628
--- /dev/null
+++ b/src/app/packages/PackagesModule.ts
@@ -0,0 +1,127 @@
+/*
+ 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: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+*/
+/**
+ * @file Packages Module.
+ */
+import { CommonModule } from '@angular/common';
+import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
+import { ReactiveFormsModule } from '@angular/forms';
+import { FormsModule } from '@angular/forms';
+import { RouterModule, Routes } from '@angular/router';
+import { CodemirrorModule } from '@ctrl/ngx-codemirror';
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+import { NgSelectModule } from '@ng-select/ng-select';
+import { TranslateModule } from '@ngx-translate/core';
+import { ClonePackageComponent } from 'ClonePackage';
+import { DataService } from 'DataService';
+import { DragDirective } from 'DragDirective';
+import { EditPackagesComponent } from 'EditPackagesComponent';
+import { LoaderModule } from 'LoaderModule';
+import { NetsliceTemplateComponent } from 'NetsliceTemplate';
+import { SidebarModule } from 'ng-sidebar';
+import { Ng2SmartTableModule } from 'ng2-smart-table';
+import { NSComposerComponent } from 'NSComposer';
+import { NSPackagesComponent } from 'NSPackages';
+import { PackagesComponent } from 'Packages';
+import { PagePerRowModule } from 'PagePerRowModule';
+import { PageReloadModule } from 'PageReloadModule';
+import { ShowContentComponent } from 'ShowContent';
+import { VNFComposerComponent } from 'VNFComposer';
+import { VNFPackagesComponent } from 'VNFPackages';
+
+/** To halndle project information */
+const projectInfo: {} = { title: '{project}', url: '/' };
+
+/**
+ * configures routers
+ */
+const routes: Routes = [
+ {
+ path: '',
+ component: PackagesComponent,
+ children: [
+ {
+ path: 'ns',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' },
+ projectInfo, { title: 'NSPACKAGES', url: null }]
+ },
+ component: NSPackagesComponent
+ },
+ {
+ path: 'vnf',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' },
+ projectInfo, { title: 'VNFPACKAGES', url: null }]
+ },
+ component: VNFPackagesComponent
+ },
+ {
+ path: 'netslice',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' }
+ , projectInfo, { title: 'PAGE.DASHBOARD.NETSLICETEMPLATE', url: null }]
+ },
+ component: NetsliceTemplateComponent
+ },
+ {
+ path: ':type/edit/:id',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' }
+ , projectInfo, { title: '{type}', url: '/packages/{type}' }, { title: '{id}', url: null }]
+ },
+ component: EditPackagesComponent
+ },
+ {
+ path: 'ns/compose/:id',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' }
+ , projectInfo, { title: 'NSPACKAGES', url: '/packages/ns' }, { title: '{id}', url: null }]
+ },
+ component: NSComposerComponent
+ },
+ {
+ path: 'vnf/compose/:id',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' },
+ projectInfo, { title: 'VNFPACKAGES', url: '/packages/vnf' }, { title: '{id}', url: null }]
+ },
+ component: VNFComposerComponent
+ }
+ ]
+ }
+];
+
+/**
+ * Creating @NgModule component for Modules
+ */
+@NgModule({
+ imports: [ReactiveFormsModule.withConfig({ warnOnNgModelWithFormControl: 'never' }), FormsModule, CommonModule, Ng2SmartTableModule,
+ CodemirrorModule, TranslateModule, RouterModule.forChild(routes), NgbModule, NgSelectModule,
+ PagePerRowModule, SidebarModule.forRoot(), LoaderModule, PageReloadModule],
+ declarations: [PackagesComponent, NSPackagesComponent, VNFPackagesComponent, NetsliceTemplateComponent,
+ DragDirective, ShowContentComponent, NSComposerComponent, VNFComposerComponent, EditPackagesComponent, ClonePackageComponent],
+ providers: [DataService],
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
+ entryComponents: [ShowContentComponent, ClonePackageComponent]
+})
+/** Exporting a class @exports PackagesModule */
+export class PackagesModule {
+ /** Variables declared to avoid state-less class */
+ private packagesModule: string;
+}
diff --git a/src/app/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.html b/src/app/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.html
new file mode 100644
index 0000000..af52927
--- /dev/null
+++ b/src/app/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.html
@@ -0,0 +1,98 @@
+<!--
+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: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+-->
+<form [formGroup]="netSliceInstantiateForm" (ngSubmit)="instantiateNSTSubmit()">
+ <div class="modal-header">
+ <h4 class="modal-title" id="modal-basic-title">{{'NEW' | translate}} NSI</h4>
+ <button class="button-xs" type="button" class="close" aria-label="Close" (click)="activeModal.close()">
+ <i class="fas fa-times-circle text-danger"></i>
+ </button>
+ </div>
+ <div class="modal-body modal-body-custom-height netsliceinstantiate-ns">
+ <div class="form-group row">
+ <label class="col-sm-12 col-form-label mandatory-label"
+ [ngClass]="{'text-danger': netSliceInstantiateForm.invalid === true && submitted === true}">{{'MANDATORYCHECK' | translate}}</label>
+ <label class="col-sm-4 col-form-label" for="nsiName">{{'PAGE.NSTINSTANCEINSTANTIATE.NSNAME' | translate}}*</label>
+ <div class="col-sm-8">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.NSTINSTANCEINSTANTIATE.NSNAME' | translate}}" type="text" formControlName="nsiName"
+ id="nsiName" [ngClass]="{ 'is-invalid': submitted && f.nsiName.errors }" required>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label"
+ for="nsiDescription">{{'PAGE.NSTINSTANCEINSTANTIATE.DESCRIPTION' | translate}}*</label>
+ <div class="col-sm-8">
+ <textarea class="form-control" placeholder="{{'PAGE.NSTINSTANCEINSTANTIATE.DESCRIPTION' | translate}}"
+ type="text" formControlName="nsiDescription" id="nsiDescription"
+ [ngClass]="{ 'is-invalid': submitted && f.nsiDescription.errors }" required></textarea>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="nstId">{{'PAGE.NSTINSTANCEINSTANTIATE.NSTID' | translate}}*</label>
+ <div class="col-sm-8">
+ <ng-select [items]="netSliceSelect" bindLabel="name" bindValue="_id"
+ placeholder="{{'SELECT' | translate}} {{'PAGE.NSTINSTANCEINSTANTIATE.NSTID' | translate}}"
+ formControlName="nstId" [(ngModel)]="netsliceNstId" id="nstId"
+ [ngClass]="{ 'is-invalid': submitted && f.nstId.errors }" required>
+ </ng-select>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label"
+ for="vimAccountId">{{'PAGE.NSTINSTANCEINSTANTIATE.VIMACCOUNT' | translate}}*</label>
+ <div class="col-sm-8">
+ <ng-select [items]="vimDetailsSelect" bindLabel="name" bindValue="_id"
+ placeholder="{{'SELECT' | translate}} {{'PAGE.NSTINSTANCEINSTANTIATE.VIMACCOUNT' | translate}}"
+ formControlName="vimAccountId" [(ngModel)]="vimAccountId" id="vimAccountId"
+ [ngClass]="{ 'is-invalid': submitted && f.vimAccountId.errors }" required>
+ </ng-select>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="ssh_keys">{{'PAGE.NSTINSTANCEINSTANTIATE.SSHKEY' | translate}}</label>
+ <div class="col-sm-8">
+ <textarea class="form-control" placeholder="{{'PAGE.NSTINSTANCEINSTANTIATE.SSHKEYMSG' | translate}}"
+ formControlName="ssh_keys" id="ssh_keys"></textarea>
+ <div class="fileupload-text mt-1 mb-1">{{'FILEUPLOADLABEL' | translate}}</div>
+ <div class="custom-file">
+ <input type="file" #fileInputSSH class="custom-file-input" (change)="sshFile($event.target.files)"
+ id="customSSHFile">
+ <label class="custom-file-label" #fileInputSSHLabel for="customSSHFile">{{'CHOOSEFILE' | translate}}</label>
+ </div>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="config">{{'CONFIG' | translate}}</label>
+ <div class="col-sm-8">
+ <textarea class="form-control" placeholder="{{'YAMLCONFIG' | translate}}" formControlName="config"
+ id="config"></textarea>
+ <div class="fileupload-text mt-1 mb-1">{{'FILEUPLOADLABEL' | translate}}</div>
+ <div class="custom-file">
+ <input type="file" #fileInputConfig class="custom-file-input" (change)="configFile($event.target.files)"
+ id="customConfigFile">
+ <label class="custom-file-label" #fileInputConfigLabel for="customConfigFile">{{'CHOOSEFILE' | translate}}</label>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-danger" (click)="activeModal.close()">{{'CANCEL' | translate}}</button>
+ <button type="submit" class="btn btn-primary">{{'CREATE' | translate}}</button>
+ </div>
+</form>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.scss b/src/app/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.scss
new file mode 100644
index 0000000..0ecd95d
--- /dev/null
+++ b/src/app/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.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: 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/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.ts b/src/app/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.ts
new file mode 100644
index 0000000..ed5e414
--- /dev/null
+++ b/src/app/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.ts
@@ -0,0 +1,282 @@
+/*
+ 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: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+ */
+/**
+ * @file Instantiate NS Modal Component.
+ */
+import { HttpHeaders } from '@angular/common/http';
+import { Component, ElementRef, Injector, OnInit, ViewChild } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { Router } from '@angular/router';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { APIURLHEADER, ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import * as jsyaml from 'js-yaml';
+import { NetworkSliceData } from 'NetworkSliceModel';
+import { NSICREATEPARAMS } from 'NSDModel';
+import { RestService } from 'RestService';
+import { SharedService } from 'SharedService';
+import { isNullOrUndefined } from 'util';
+import { VimAccountDetails } from 'VimAccountModel';
+/**
+ * Creating component
+ * @Component takes InstantiateNetSliceTemplateComponent.html as template url
+ */
+@Component({
+ selector: 'app-instantiate-net-slice-template',
+ templateUrl: './InstantiateNetSliceTemplateComponent.html',
+ styleUrls: ['./InstantiateNetSliceTemplateComponent.scss']
+})
+/** Exporting a class @exports InstantiateNetSliceTemplateComponent */
+export class InstantiateNetSliceTemplateComponent implements OnInit {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** FormGroup instance added to the form @ html @public */
+ public netSliceInstantiateForm: FormGroup;
+
+ /** Instance for active modal service @public */
+ public activeModal: NgbActiveModal;
+
+ /** Variable set for twoway bindng @public */
+ public vimAccountId: string;
+
+ /** Contains all the net slice data collections */
+ public netSliceSelect: NetworkSliceData;
+
+ /** Contains all the VIM data collections */
+ public vimDetailsSelect: VimAccountDetails;
+
+ /** Variable set for twoway binding @public */
+ public netsliceNstId: string;
+
+ /** Form submission Add */
+ public submitted: boolean = false;
+
+ /** Check the loading results for loader status @public */
+ public isLoadingResults: boolean = false;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Element ref for fileInputConfig @public */
+ @ViewChild('fileInputConfig', { static: true }) public fileInputConfig: ElementRef;
+
+ /** Element ref for fileInputConfigLabel @public */
+ @ViewChild('fileInputConfigLabel', { static: true }) public fileInputConfigLabel: ElementRef;
+
+ /** Element ref for fileInputSSH @public */
+ @ViewChild('fileInputSSH', { static: true }) public fileInputSSH: ElementRef;
+
+ /** Element ref for fileInputSSHLabel @public */
+ @ViewChild('fileInputSSHLabel', { static: true }) public fileInputSSHLabel: ElementRef;
+
+ /** Holds teh instance of AuthService class of type AuthService @private */
+ private router: Router;
+
+ /** FormBuilder instance added to the formBuilder @private */
+ private formBuilder: FormBuilder;
+
+ /** Utilizes rest service for any CRUD operations @private */
+ private restService: RestService;
+
+ /** Utilizes data service for any communication @private */
+ private dataService: DataService;
+
+ /** Controls the header form @private */
+ private headers: HttpHeaders;
+
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** Contains the ssh key to be hosted in dom @private */
+ private copySSHKey: string;
+
+ 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.dataService = this.injector.get(DataService);
+ this.notifierService = this.injector.get(NotifierService);
+ this.router = this.injector.get(Router);
+ this.translateService = this.injector.get(TranslateService);
+ this.sharedService = this.injector.get(SharedService);
+ }
+
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ /** Setting up initial value for NSD */
+ this.netsliceNstId = '';
+ this.dataService.currentMessage.subscribe((event: NetworkSliceData) => {
+ if (event.identifier !== undefined || event.identifier !== '' || event.identifier !== null) {
+ this.netsliceNstId = event.identifier;
+ }
+ });
+ this.netSliceInstantiateFormAction();
+ this.headers = new HttpHeaders({
+ 'Content-Type': 'application/json',
+ Accept: 'application/json',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ /** On Initializing call the methods */
+ this.getNetSliceDetails();
+ this.getVIMDetails();
+ }
+
+ /** Call the netSlice details in the selection options @public */
+ public getNetSliceDetails(): void {
+ this.restService.getResource(environment.NETWORKSLICETEMPLATECONTENT_URL).subscribe((netSlicePackages: NetworkSliceData) => {
+ this.netSliceSelect = netSlicePackages;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ });
+ }
+
+ /** Call the VIM details in the selection options @public */
+ public getVIMDetails(): void {
+ this.restService.getResource(environment.VIMACCOUNTS_URL).subscribe((vimDetails: VimAccountDetails) => {
+ this.vimDetailsSelect = vimDetails;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ });
+ }
+
+ /** On modal initializing forms @public */
+ public netSliceInstantiateFormAction(): void {
+ this.netSliceInstantiateForm = this.formBuilder.group({
+ nsiName: ['', [Validators.required]],
+ nsiDescription: ['', [Validators.required]],
+ nstId: ['', [Validators.required]],
+ vimAccountId: ['', [Validators.required]],
+ ssh_keys: [null],
+ config: [null]
+ });
+ }
+ /** convenience getter for easy access to form fields */
+ get f(): FormGroup['controls'] { return this.netSliceInstantiateForm.controls; }
+
+ /** On modal submit instantiateNsSubmit will called @public */
+ public instantiateNSTSubmit(): void {
+ this.submitted = true;
+ this.sharedService.cleanForm(this.netSliceInstantiateForm);
+ if (this.netSliceInstantiateForm.invalid) {
+ return;
+ }
+ const modalData: MODALCLOSERESPONSEDATA = {
+ message: 'Done'
+ };
+ if (isNullOrUndefined(this.netSliceInstantiateForm.value.ssh_keys) || this.netSliceInstantiateForm.value.ssh_keys === '') {
+ delete this.netSliceInstantiateForm.value.ssh_keys;
+ } else {
+ this.copySSHKey = JSON.parse(JSON.stringify(this.netSliceInstantiateForm.value.ssh_keys));
+ // tslint:disable-next-line: no-backbone-get-set-outside-model
+ this.netSliceInstantiateForm.get('ssh_keys').setValue(this.copySSHKey);
+ }
+ if (isNullOrUndefined(this.netSliceInstantiateForm.value.config) || this.netSliceInstantiateForm.value.config === '') {
+ delete this.netSliceInstantiateForm.value.config;
+ } else {
+ const validJSON: boolean = this.sharedService.checkJson(this.netSliceInstantiateForm.value.config);
+ if (validJSON) {
+ this.netSliceInstantiateForm.value.config = JSON.parse(this.netSliceInstantiateForm.value.config);
+ Object.keys(this.netSliceInstantiateForm.value.config).forEach((item: string) => {
+ this.netSliceInstantiateForm.value[item] = this.netSliceInstantiateForm.value.config[item];
+ });
+ delete this.netSliceInstantiateForm.value.config;
+ } else {
+ this.notifierService.notify('error', this.translateService.instant('INVALIDCONFIG'));
+ return;
+ }
+ }
+ this.isLoadingResults = true;
+ const apiURLHeader: APIURLHEADER = {
+ url: environment.NETWORKSLICEINSTANCESCONTENT_URL,
+ httpOptions: { headers: this.headers }
+ };
+ this.restService.postResource(apiURLHeader, this.netSliceInstantiateForm.value)
+ .subscribe((result: {}) => {
+ this.activeModal.close(modalData);
+ this.isLoadingResults = false;
+ this.notifierService.notify('success', this.netSliceInstantiateForm.value.nsiName +
+ this.translateService.instant('PAGE.NETSLICE.CREATEDSUCCESSFULLY'));
+ this.router.navigate(['/instances/netslice']).catch();
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'post');
+ if (!isNullOrUndefined(this.copySSHKey)) {
+ // tslint:disable-next-line: no-backbone-get-set-outside-model
+ this.netSliceInstantiateForm.get('ssh_keys').setValue(this.copySSHKey);
+ }
+ this.isLoadingResults = false;
+ });
+ }
+
+ /** ssh file process @private */
+ public sshFile(files: FileList): void {
+ if (files && files.length === 1) {
+ this.sharedService.getFileString(files, 'pub').then((fileContent: string): void => {
+ const getSSHJson: string = jsyaml.load(fileContent, { json: true });
+ // tslint:disable-next-line: no-backbone-get-set-outside-model
+ this.netSliceInstantiateForm.get('ssh_keys').setValue(getSSHJson);
+ }).catch((err: string): void => {
+ if (err === 'typeError') {
+ this.notifierService.notify('error', this.translateService.instant('PUBFILETYPEERRROR'));
+ } else {
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ }
+ this.fileInputSSHLabel.nativeElement.innerText = this.translateService.instant('CHOOSEFILE');
+ this.fileInputSSH.nativeElement.value = null;
+ });
+ } else if (files && files.length > 1) {
+ this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION'));
+ }
+ this.fileInputSSHLabel.nativeElement.innerText = files[0].name;
+ this.fileInputSSH.nativeElement.value = null;
+ }
+
+ /** Config file process @private */
+ public configFile(files: FileList): void {
+ if (files && files.length === 1) {
+ this.sharedService.getFileString(files, 'yaml').then((fileContent: string): void => {
+ const getConfigJson: string = jsyaml.load(fileContent, { json: true });
+ // tslint:disable-next-line: no-backbone-get-set-outside-model
+ this.netSliceInstantiateForm.get('config').setValue(JSON.stringify(getConfigJson));
+ }).catch((err: string): void => {
+ if (err === 'typeError') {
+ this.notifierService.notify('error', this.translateService.instant('YAMLFILETYPEERRROR'));
+ } else {
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ }
+ this.fileInputConfigLabel.nativeElement.innerText = this.translateService.instant('CHOOSEFILE');
+ this.fileInputConfig.nativeElement.value = null;
+ });
+ } else if (files && files.length > 1) {
+ this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION'));
+ }
+ this.fileInputConfigLabel.nativeElement.innerText = files[0].name;
+ this.fileInputConfig.nativeElement.value = null;
+ }
+
+}
diff --git a/src/app/packages/instantiate-ns/InstantiateNsComponent.html b/src/app/packages/instantiate-ns/InstantiateNsComponent.html
new file mode 100644
index 0000000..dabd469
--- /dev/null
+++ b/src/app/packages/instantiate-ns/InstantiateNsComponent.html
@@ -0,0 +1,103 @@
+<!--
+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: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+-->
+<form [formGroup]="instantiateForm" (ngSubmit)="instantiateNsSubmit();">
+ <div class="modal-header">
+ <h4 class="modal-title" id="modal-basic-title">{{'PAGE.INSTANCEINSTANTIATE.NEWINSTANCE' | translate}}</h4>
+ <button class="button-xs" type="button" class="close" aria-label="Close" (click)="activeModal.close()">
+ <i class="fas fa-times-circle text-danger"></i>
+ </button>
+ </div>
+ <div class="modal-body modal-body-custom-height instantiate-ns">
+ <div class="form-group row">
+ <label class="col-sm-12 col-form-label mandatory-label"
+ [ngClass]="{'text-danger': instantiateForm.invalid === true && submitted === true}">{{'MANDATORYCHECK' | translate}}</label>
+ <label class="col-sm-4 col-form-label"
+ for="nsName">{{'PAGE.INSTANCEINSTANTIATE.NSNAME' | translate}}*</label>
+ <div class="col-sm-8">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.INSTANCEINSTANTIATE.NSNAME' | translate}}" type="text" formControlName="nsName"
+ id="nsName" (keydown.space)="$event.preventDefault();"
+ [ngClass]="{ 'is-invalid': submitted && f.nsName.errors }" required>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label"
+ for="nsDescription">{{'PAGE.INSTANCEINSTANTIATE.DESCRIPTION' | translate}}*</label>
+ <div class="col-sm-8">
+ <textarea class="form-control" placeholder="{{'PAGE.INSTANCEINSTANTIATE.DESCRIPTION' | translate}}"
+ type="text" formControlName="nsDescription" id="nsDescription"
+ [ngClass]="{ 'is-invalid': submitted && f.nsDescription.errors }" required></textarea>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="nsdId">{{'PAGE.INSTANCEINSTANTIATE.NSID' | translate}}*</label>
+ <div class="col-sm-8">
+ <ng-select [items]="nsdSelect" bindLabel="name" bindValue="_id"
+ placeholder="{{'SELECT' | translate}} {{'PAGE.INSTANCEINSTANTIATE.NSID' | translate}}"
+ formControlName="nsdId" [(ngModel)]="nsdId" id="nsdId"
+ [ngClass]="{ 'is-invalid': submitted && f.nsdId.errors }" required>
+ </ng-select>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label"
+ for="vimAccountId">{{'PAGE.INSTANCEINSTANTIATE.VIMACCOUNT' | translate}}*</label>
+ <div class="col-sm-8">
+ <ng-select [items]="vimAccountSelect" bindLabel="name" bindValue="_id"
+ placeholder="{{'SELECT' | translate}} {{'PAGE.INSTANCEINSTANTIATE.VIMACCOUNT' | translate}}"
+ formControlName="vimAccountId" [(ngModel)]="vimAccountId" id="vimAccountId"
+ [ngClass]="{ 'is-invalid': submitted && f.vimAccountId.errors }" required>
+ </ng-select>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label"
+ for="ssh_keys">{{'PAGE.INSTANCEINSTANTIATE.SSHKEY' | translate}}</label>
+ <div class="col-sm-8">
+ <textarea class="form-control" placeholder="{{'PAGE.INSTANCEINSTANTIATE.SSHKEYMSG' | translate}}"
+ formControlName="ssh_keys" id="ssh_keys"></textarea>
+ <div class="fileupload-text mt-1 mb-1">{{'FILEUPLOADLABEL' | translate}}</div>
+ <div class="custom-file">
+ <input type="file" #fileInputSSH class="custom-file-input" (change)="sshFile($event.target.files)"
+ id="customSSHFile">
+ <label class="custom-file-label" #fileInputSSHLabel
+ for="customSSHFile">{{'CHOOSEFILE' | translate}}</label>
+ </div>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="config">{{'CONFIG' | translate}}</label>
+ <div class="col-sm-8">
+ <textarea class="form-control" placeholder="{{'YAMLCONFIG' | translate}}" formControlName="config"
+ id="config"></textarea>
+ <div class="fileupload-text mt-1 mb-1">{{'FILEUPLOADLABEL' | translate}}</div>
+ <div class="custom-file">
+ <input type="file" #fileInputConfig class="custom-file-input"
+ (change)="configFile($event.target.files)" id="customConfigFile">
+ <label class="custom-file-label" #fileInputConfigLabel
+ for="customConfigFile">{{'CHOOSEFILE' | translate}}</label>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-danger" (click)="activeModal.close()">{{'CANCEL' | translate}}</button>
+ <button type="submit" class="btn btn-primary">{{'CREATE' | translate}}</button>
+ </div>
+</form>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/packages/instantiate-ns/InstantiateNsComponent.scss b/src/app/packages/instantiate-ns/InstantiateNsComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/packages/instantiate-ns/InstantiateNsComponent.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: 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/packages/instantiate-ns/InstantiateNsComponent.ts b/src/app/packages/instantiate-ns/InstantiateNsComponent.ts
new file mode 100644
index 0000000..515a24b
--- /dev/null
+++ b/src/app/packages/instantiate-ns/InstantiateNsComponent.ts
@@ -0,0 +1,268 @@
+/*
+ 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: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+*/
+/**
+ * @file Instantiate NS Modal Component.
+ */
+import { Component, ElementRef, Injector, OnInit, ViewChild } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { Router } from '@angular/router';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { APIURLHEADER, ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import * as jsyaml from 'js-yaml';
+import { NSCREATEPARAMS, NSData, NSDDetails } from 'NSDModel';
+import { RestService } from 'RestService';
+import { SharedService } from 'SharedService';
+import { isNullOrUndefined } from 'util';
+import { VimAccountDetails } from 'VimAccountModel';
+
+/**
+ * Creating component
+ * @Component takes InstantiateNsComponent.html as template url
+ */
+@Component({
+ selector: 'app-instantiate-ns',
+ templateUrl: './InstantiateNsComponent.html',
+ styleUrls: ['./InstantiateNsComponent.scss']
+})
+/** Exporting a class @exports InstantiateNsComponent */
+export class InstantiateNsComponent implements OnInit {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Contains all the nsd data collections */
+ public nsdSelect: NSDDetails;
+
+ /** FormGroup instance added to the form @ html @public */
+ public instantiateForm: FormGroup;
+
+ /** Contains all vim account collections */
+ public vimAccountSelect: VimAccountDetails;
+
+ /** Instance for active modal service @public */
+ public activeModal: NgbActiveModal;
+
+ /** Variable set for twoway binding @public */
+ public nsdId: string;
+
+ /** Variable set for twoway bindng @public */
+ public vimAccountId: string;
+
+ /** Form submission Add */
+ public submitted: boolean = false;
+
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = false;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Element ref for fileInputConfig @public */
+ @ViewChild('fileInputConfig', { static: true }) public fileInputConfig: ElementRef;
+
+ /** Element ref for fileInputConfigLabel @public */
+ @ViewChild('fileInputConfigLabel', { static: true }) public fileInputConfigLabel: ElementRef;
+
+ /** Element ref for fileInputSSH @public */
+ @ViewChild('fileInputSSH', { static: true }) public fileInputSSH: ElementRef;
+
+ /** Element ref for fileInputSSHLabel @public */
+ @ViewChild('fileInputSSHLabel', { static: true }) public fileInputSSHLabel: ElementRef;
+
+ /** Holds teh instance of AuthService class of type AuthService @private */
+ private router: Router;
+
+ /** FormBuilder instance added to the formBuilder @private */
+ private formBuilder: FormBuilder;
+
+ /** Utilizes rest service for any CRUD operations @private */
+ private restService: RestService;
+
+ /** Utilizes data service for any communication @private */
+ private dataService: DataService;
+
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** Contains the ssh key to be hosted in dom @private */
+ private copySSHKey: string;
+
+ 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.dataService = this.injector.get(DataService);
+ this.notifierService = this.injector.get(NotifierService);
+ this.router = this.injector.get(Router);
+ this.translateService = this.injector.get(TranslateService);
+ this.sharedService = this.injector.get(SharedService);
+ }
+
+ public ngOnInit(): void {
+ /** Setting up initial value for NSD */
+ this.dataService.currentMessage.subscribe((event: NSData) => {
+ if (event.identifier !== undefined || event.identifier !== '' || event.identifier !== null) {
+ this.nsdId = event.identifier;
+ }
+ });
+ /** On Initializing call the methods */
+ this.instantiateFormAction();
+ this.getDetailsnsd();
+ this.getDetailsvimAccount();
+ }
+
+ /** On modal initializing forms @public */
+ public instantiateFormAction(): void {
+ this.instantiateForm = this.formBuilder.group({
+ nsName: ['', [Validators.required]],
+ nsDescription: ['', [Validators.required]],
+ nsdId: ['', [Validators.required]],
+ vimAccountId: ['', [Validators.required]],
+ ssh_keys: [null],
+ config: [null]
+ });
+ }
+
+ /** Convenience getter for easy access to form fields */
+ get f(): FormGroup['controls'] { return this.instantiateForm.controls; }
+
+ /** Call the nsd details in the selection options @public */
+ public getDetailsnsd(): void {
+ this.restService.getResource(environment.NSDESCRIPTORSCONTENT_URL).subscribe((nsPackages: NSDDetails) => {
+ this.nsdSelect = nsPackages;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ });
+ }
+
+ /** Call the vimAccount details in the selection options @public */
+ public getDetailsvimAccount(): void {
+ this.restService.getResource(environment.VIMACCOUNTS_URL).subscribe((vimAccounts: VimAccountDetails) => {
+ this.vimAccountSelect = vimAccounts;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ });
+ }
+
+ /** On modal submit instantiateNsSubmit will called @public */
+ public instantiateNsSubmit(): void {
+ this.submitted = true;
+ this.sharedService.cleanForm(this.instantiateForm);
+ if (this.instantiateForm.invalid) {
+ return;
+ }
+ const modalData: MODALCLOSERESPONSEDATA = {
+ message: 'Done'
+ };
+ if (isNullOrUndefined(this.instantiateForm.value.ssh_keys) || this.instantiateForm.value.ssh_keys === '') {
+ delete this.instantiateForm.value.ssh_keys;
+ } else {
+ this.copySSHKey = JSON.parse(JSON.stringify(this.instantiateForm.value.ssh_keys));
+ // tslint:disable-next-line: no-backbone-get-set-outside-model
+ this.instantiateForm.get('ssh_keys').setValue([this.copySSHKey]);
+ }
+ if (isNullOrUndefined(this.instantiateForm.value.config) || this.instantiateForm.value.config === '') {
+ delete this.instantiateForm.value.config;
+ } else {
+ const validJSON: boolean = this.sharedService.checkJson(this.instantiateForm.value.config);
+ if (validJSON) {
+ this.instantiateForm.value.config = JSON.parse(this.instantiateForm.value.config);
+ Object.keys(this.instantiateForm.value.config).forEach((item: string) => {
+ this.instantiateForm.value[item] = this.instantiateForm.value.config[item];
+ });
+ delete this.instantiateForm.value.config;
+ } else {
+ this.notifierService.notify('error', this.translateService.instant('INVALIDCONFIG'));
+ return;
+ }
+ }
+ const apiURLHeader: APIURLHEADER = {
+ url: environment.NSINSTANCESCONTENT_URL
+ };
+ this.isLoadingResults = true;
+ this.restService.postResource(apiURLHeader, this.instantiateForm.value).subscribe((result: {}) => {
+ this.activeModal.close(modalData);
+ this.notifierService.notify('success', this.instantiateForm.value.nsName +
+ this.translateService.instant('PAGE.NSINSTANCE.CREATEDSUCCESSFULLY'));
+ this.router.navigate(['/instances/ns']).catch();
+ }, (error: ERRORDATA) => {
+ this.isLoadingResults = false;
+ this.restService.handleError(error, 'post');
+ if (!isNullOrUndefined(this.copySSHKey)) {
+ // tslint:disable-next-line: no-backbone-get-set-outside-model
+ this.instantiateForm.get('ssh_keys').setValue(this.copySSHKey);
+ }
+ });
+ }
+
+ /** ssh file process @private */
+ public sshFile(files: FileList): void {
+ if (files && files.length === 1) {
+ this.sharedService.getFileString(files, 'pub').then((fileContent: string): void => {
+ const getSSHJson: string = jsyaml.load(fileContent, { json: true });
+ // tslint:disable-next-line: no-backbone-get-set-outside-model
+ this.instantiateForm.get('ssh_keys').setValue(getSSHJson);
+ }).catch((err: string): void => {
+ if (err === 'typeError') {
+ this.notifierService.notify('error', this.translateService.instant('PUBFILETYPEERRROR'));
+ } else {
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ }
+ this.fileInputSSHLabel.nativeElement.innerText = this.translateService.instant('CHOOSEFILE');
+ this.fileInputSSH.nativeElement.value = null;
+ });
+ } else if (files && files.length > 1) {
+ this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION'));
+ }
+ this.fileInputSSHLabel.nativeElement.innerText = files[0].name;
+ this.fileInputSSH.nativeElement.value = null;
+ }
+
+ /** Config file process @private */
+ public configFile(files: FileList): void {
+ if (files && files.length === 1) {
+ this.sharedService.getFileString(files, 'yaml').then((fileContent: string): void => {
+ const getConfigJson: string = jsyaml.load(fileContent, { json: true });
+ // tslint:disable-next-line: no-backbone-get-set-outside-model
+ this.instantiateForm.get('config').setValue(JSON.stringify(getConfigJson));
+ }).catch((err: string): void => {
+ if (err === 'typeError') {
+ this.notifierService.notify('error', this.translateService.instant('YAMLFILETYPEERRROR'));
+ } else {
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ }
+ this.fileInputConfigLabel.nativeElement.innerText = this.translateService.instant('CHOOSEFILE');
+ this.fileInputConfig.nativeElement.value = null;
+ });
+ } else if (files && files.length > 1) {
+ this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION'));
+ }
+ this.fileInputConfigLabel.nativeElement.innerText = files[0].name;
+ this.fileInputConfig.nativeElement.value = null;
+ }
+}
diff --git a/src/app/packages/netslice-template/NetsliceTemplateComponent.html b/src/app/packages/netslice-template/NetsliceTemplateComponent.html
new file mode 100644
index 0000000..6d41e11
--- /dev/null
+++ b/src/app/packages/netslice-template/NetsliceTemplateComponent.html
@@ -0,0 +1,38 @@
+<!--
+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: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+-->
+<div class="row d-flex flex-row justify-content-between">
+ <div class="d-flex align-items-center header-style">{{'PAGE.DASHBOARD.NETSLICETEMPLATE' | translate}}</div>
+</div>
+<div class="row">
+ <div class="dropzone mt-2" appDrag (click)="fileInput.click()" (files)="filesDropped($event)">
+ <input hidden type="file" #fileInput (change)="filesDropped($event.target.files)">
+ <div class="text-wrapper">
+ <div class="text-center file-drop-title">
+ <i class="fas fa-upload"></i> {{'DROPFILES' | translate}}</div>
+ </div>
+ </div>
+</div>
+<div class="row mt-2 mb-0 form-group justify-content-end list-utilites-actions">
+ <page-per-row class="mr-2" (pagePerRow)="onChange($event)"></page-per-row>
+ <page-reload></page-reload>
+</div>
+<div class="smarttable-style bg-white mt-1">
+ <ng2-smart-table [ngClass]="checkDataClass" [settings]="settings" [source]="dataSource" (userRowSelect)="onUserRowSelect($event)">
+ </ng2-smart-table>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/packages/netslice-template/NetsliceTemplateComponent.scss b/src/app/packages/netslice-template/NetsliceTemplateComponent.scss
new file mode 100644
index 0000000..2c07d1b
--- /dev/null
+++ b/src/app/packages/netslice-template/NetsliceTemplateComponent.scss
@@ -0,0 +1,20 @@
+/*
+ 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: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+*/
+.ng2-smart-th.identifier{
+ width:40% !important;
+}
\ No newline at end of file
diff --git a/src/app/packages/netslice-template/NetsliceTemplateComponent.ts b/src/app/packages/netslice-template/NetsliceTemplateComponent.ts
new file mode 100644
index 0000000..5360518
--- /dev/null
+++ b/src/app/packages/netslice-template/NetsliceTemplateComponent.ts
@@ -0,0 +1,250 @@
+/*
+ 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: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+*/
+/**
+ * @file Netslice-Template.
+ */
+import { HttpHeaders } from '@angular/common/http';
+import { Component, ElementRef, Injector, ViewChild } from '@angular/core';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { APIURLHEADER, ERRORDATA } from 'CommonModel';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import { NetslicePackagesActionComponent } from 'NetslicePackagesAction';
+import { NetworkSliceData, NetworkSliceModel } from 'NetworkSliceModel';
+import { LocalDataSource } from 'ng2-smart-table';
+import { RestService } from 'RestService';
+import { Subscription } from 'rxjs';
+import { SharedService } from 'SharedService';
+
+/**
+ * Creating component
+ * @Component takes NetsliceTemplateComponent.html as template url
+ */
+@Component({
+ selector: 'app-netslice-template',
+ templateUrl: './NetsliceTemplateComponent.html',
+ styleUrls: ['./NetsliceTemplateComponent.scss']
+})
+/** Exporting a class @exports NetsliceTemplateComponent */
+export class NetsliceTemplateComponent {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Values for map config @public */
+ public selectedRows: object[] = [];
+
+ /** To consume REST API calls @public */
+ public dataSource: LocalDataSource = new LocalDataSource();
+
+ /** handle translate @public */
+ public translateService: TranslateService;
+
+ /** Columns list of the smart table @public */
+ public columnLists: object = {};
+
+ /** Settings for smarttable to populate the table with columns @public */
+ public settings: object = {};
+
+ /** Values for map config @public */
+ public selectList: object[] = [];
+
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = true;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Class for empty and present data @public */
+ public checkDataClass: string;
+
+ /** Element ref for fileInput @public */
+ @ViewChild('fileInput', { static: true }) public fileInput: ElementRef;
+
+ /** To consume REST API calls @private */
+ private dataService: DataService;
+
+ /** To consume REST API calls @private */
+ private restService: RestService;
+
+ /** Formation of appropriate Data for LocalDatasource @private */
+ private networkSliceData: NetworkSliceData[] = [];
+
+ /** variables holds file information @private */
+ private fileData: string | ArrayBuffer;
+
+ /** Controls the header form @private */
+ private headers: HttpHeaders;
+
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** Instance of subscriptions @private */
+ private generateDataSub: Subscription;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.dataService = this.injector.get(DataService);
+ this.translateService = this.injector.get(TranslateService);
+ this.notifierService = this.injector.get(NotifierService);
+ this.sharedService = this.injector.get(SharedService);
+ }
+
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ this.generateColumns();
+ this.generateSettings();
+ this.generateData();
+ this.headers = new HttpHeaders({
+ 'Content-Type': 'application/x-yaml',
+ Accept: 'application/json',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); });
+ }
+
+ /** smart table Header Colums @public */
+ public generateColumns(): void {
+ this.columnLists = {
+ name: { title: this.translateService.instant('NAME'), width: '20%', sortDirection: 'asc' },
+ identifier: { title: this.translateService.instant('IDENTIFIER'), width: '50%' },
+ usageState: { title: this.translateService.instant('USAGESTATE'), width: '20%' },
+ Actions: {
+ name: 'Actions', width: '10%', filter: false, sort: false, type: 'custom',
+ title: this.translateService.instant('ACTIONS'),
+ valuePrepareFunction: (cell: NetworkSliceData, row: NetworkSliceData): NetworkSliceData => row,
+ renderComponent: NetslicePackagesActionComponent
+ }
+ };
+ }
+
+ /** smart table Data Settings @public */
+ public generateSettings(): void {
+ this.settings = {
+ edit: {
+ editButtonContent: '<i class="fa fa-edit" title="Edit"></i>',
+ confirmSave: true
+ },
+ delete: {
+ deleteButtonContent: '<i class="far fa-trash-alt" title="delete"></i>',
+ confirmDelete: true
+ },
+ columns: this.columnLists,
+ actions: {
+ add: false,
+ edit: false,
+ delete: false,
+ position: 'right'
+ },
+ attr: this.sharedService.tableClassConfig(),
+ pager: this.sharedService.paginationPagerConfig(),
+ noDataMessage: this.translateService.instant('NODATAMSG')
+ };
+ }
+
+ /** smart table listing manipulation @private */
+ public onChange(perPageValue: number): void {
+ this.dataSource.setPaging(1, perPageValue, true);
+ }
+
+ /** smart table listing manipulation @private */
+ public onUserRowSelect(event: MessageEvent): void {
+ Object.assign(event.data, { page: 'network-slice' });
+ this.dataService.changeMessage(event.data);
+ }
+
+ /** Drag and drop feature and fetchind the details of files @private */
+ public filesDropped(files: FileList): void {
+ if (files && files.length === 1) {
+ this.isLoadingResults = true;
+ this.sharedService.getFileString(files, 'yaml').then((fileContent: String | ArrayBuffer): void => {
+ const apiURLHeader: APIURLHEADER = {
+ url: environment.NETWORKSLICETEMPLATECONTENT_URL,
+ httpOptions: { headers: this.headers }
+ };
+ this.saveFileData(apiURLHeader, fileContent);
+ }).catch((err: string): void => {
+ this.isLoadingResults = false;
+ if (err === 'typeError') {
+ this.notifierService.notify('error', this.translateService.instant('YAMLFILETYPEERRROR'));
+ } else {
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ }
+ });
+ } else if (files && files.length > 1) {
+ this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION'));
+ }
+ }
+
+ /** Post the droped files and reload the page @public */
+ public saveFileData(urlHeader: APIURLHEADER, fileData: {}): void {
+ this.fileInput.nativeElement.value = null;
+ this.restService.postResource(urlHeader, fileData).subscribe((result: {}) => {
+ this.notifierService.notify('success', this.translateService.instant('PAGE.NETSLICE.TEMPLATECREATEDSUCCESSFULLY'));
+ this.generateData();
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'post');
+ this.isLoadingResults = false;
+ });
+ }
+
+ /** Generate nsData object from loop and return for the datasource @public */
+ public generateNetworkSliceData(networkSlicePackageData: NetworkSliceModel): NetworkSliceData {
+ return {
+ name: networkSlicePackageData.name,
+ identifier: networkSlicePackageData._id,
+ usageState: networkSlicePackageData._admin.usageState
+ };
+ }
+
+ /**
+ * Lifecyle hook which get trigger on component destruction
+ */
+ public ngOnDestroy(): void {
+ this.generateDataSub.unsubscribe();
+ }
+
+ /** Fetching the data from server to Load in the smarttable @protected */
+ protected generateData(): void {
+ this.isLoadingResults = true;
+ this.restService.getResource(environment.NETWORKSLICETEMPLATECONTENT_URL).subscribe((networkSliceList: NetworkSliceModel[]) => {
+ this.networkSliceData = [];
+ networkSliceList.forEach((networkSlicePackageData: NetworkSliceModel) => {
+ const networkSliceDataObj: NetworkSliceData = this.generateNetworkSliceData(networkSlicePackageData);
+ this.networkSliceData.push(networkSliceDataObj);
+ });
+ if (this.networkSliceData.length > 0) {
+ this.checkDataClass = 'dataTables_present';
+ } else {
+ this.checkDataClass = 'dataTables_empty';
+ }
+ this.dataSource.load(this.networkSliceData).then((data: boolean) => {
+ this.isLoadingResults = false;
+ }).catch(() => {
+ this.isLoadingResults = false;
+ });
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ this.isLoadingResults = false;
+ });
+ }
+}
diff --git a/src/app/packages/ns-packages/NSPackagesComponent.html b/src/app/packages/ns-packages/NSPackagesComponent.html
new file mode 100644
index 0000000..5b67268
--- /dev/null
+++ b/src/app/packages/ns-packages/NSPackagesComponent.html
@@ -0,0 +1,44 @@
+<!--
+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: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+-->
+<div class="row d-flex flex-row justify-content-between">
+ <div class="d-flex align-items-center header-style">NS {{'PACKAGES' | translate}}</div>
+ <span class="button">
+ <button class="btn btn-primary" type="button" placement="top" container="body" ngbTooltip="{{'PAGE.NSPACKAGE.ADDNSPACKAGE' | translate}}"
+ (click)="composeNSPackage()">
+ <i class="fas fa-plus-circle" aria-hidden="true"></i> {{'PAGE.NSPACKAGE.ADDNSPACKAGE' | translate}}
+ </button>
+ </span>
+</div>
+<div class="row">
+ <div class="dropzone mt-2" appDrag (click)="fileInput.click()" (files)="filesDropped($event)">
+ <input hidden type="file" #fileInput (change)="filesDropped($event.target.files)">
+ <div class="text-wrapper">
+ <div class="text-center file-drop-title">
+ <i class="fas fa-upload"></i> {{'DROPFILES' | translate}}</div>
+ </div>
+ </div>
+</div>
+<div class="row mt-2 mb-0 form-group justify-content-end list-utilites-actions">
+ <page-per-row class="mr-2" (pagePerRow)="onChange($event)"></page-per-row>
+ <page-reload></page-reload>
+</div>
+<div class="smarttable-style bg-white mt-1">
+ <ng2-smart-table [ngClass]="checkDataClass" [settings]="settings" [source]="dataSource" (userRowSelect)="onUserRowSelect($event)">
+ </ng2-smart-table>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/packages/ns-packages/NSPackagesComponent.scss b/src/app/packages/ns-packages/NSPackagesComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/packages/ns-packages/NSPackagesComponent.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: 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/packages/ns-packages/NSPackagesComponent.ts b/src/app/packages/ns-packages/NSPackagesComponent.ts
new file mode 100644
index 0000000..46366e0
--- /dev/null
+++ b/src/app/packages/ns-packages/NSPackagesComponent.ts
@@ -0,0 +1,258 @@
+/*
+ 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: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+ */
+/**
+ * @file NS-Packages component.
+ */
+import { HttpHeaders } from '@angular/common/http';
+import { Component, ElementRef, Injector, OnInit, ViewChild } from '@angular/core';
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { APIURLHEADER, ERRORDATA } from 'CommonModel';
+import { ComposePackages } from 'ComposePackages';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import { LocalDataSource } from 'ng2-smart-table';
+import { NSData, NSDDetails } from 'NSDModel';
+import { NsPackagesActionComponent } from 'NsPackagesAction';
+import { RestService } from 'RestService';
+import { Subscription } from 'rxjs';
+import { SharedService } from 'SharedService';
+
+/**
+ * Creating component
+ * @Component takes NSPackagesComponent.html as template url
+ */
+@Component({
+ selector: 'app-ns-packages',
+ templateUrl: './NSPackagesComponent.html',
+ styleUrls: ['./NSPackagesComponent.scss']
+})
+
+/** Exporting a class @exports NSPackagesComponent */
+export class NSPackagesComponent implements OnInit {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Formation of appropriate Data for LocalDatasource @public */
+ public dataSource: LocalDataSource = new LocalDataSource();
+
+ /** handle translate @public */
+ public translateService: TranslateService;
+
+ /** Columns list of the smart table @public */
+ public columnLists: object = {};
+
+ /** Settings for smarttable to populate the table with columns @public */
+ public settings: object = {};
+
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = true;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Class for empty and present data @public */
+ public checkDataClass: string;
+
+ /** Element ref for fileInput @public */
+ @ViewChild('fileInput', { static: true }) public fileInput: ElementRef;
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ /** dataService to pass the data from one component to another @private */
+ private dataService: DataService;
+
+ /** Formation of appropriate Data for LocalDatasource @private */
+ private nsData: NSData[] = [];
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** variables holds file information @private */
+ private fileData: string | ArrayBuffer;
+
+ /** Controls the header form @private */
+ private headers: HttpHeaders;
+
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ /** Instance of subscriptions @private */
+ private generateDataSub: Subscription;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.dataService = this.injector.get(DataService);
+ this.sharedService = this.injector.get(SharedService);
+ this.translateService = this.injector.get(TranslateService);
+ this.notifierService = this.injector.get(NotifierService);
+ this.modalService = this.injector.get(NgbModal);
+ }
+
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ this.generateColumns();
+ this.generateSettings();
+ this.generateData();
+ this.headers = new HttpHeaders({
+ 'Content-Type': 'application/gzip',
+ Accept: 'application/json',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); });
+ }
+
+ /** smart table Header Colums @public */
+ public generateColumns(): void {
+ this.columnLists = {
+ shortName: { title: this.translateService.instant('SHORTNAME'), width: '15%', sortDirection: 'asc' },
+ identifier: { title: this.translateService.instant('IDENTIFIER'), width: '20%' },
+ description: { title: this.translateService.instant('DESCRIPTION'), width: '25%' },
+ vendor: { title: this.translateService.instant('VENDOR'), width: '15%' },
+ version: { title: this.translateService.instant('VERSION'), width: '10%' },
+ Actions: {
+ name: 'Actions', width: '15%', filter: false, sort: false, type: 'custom',
+ title: this.translateService.instant('ACTIONS'),
+ valuePrepareFunction: (cell: NSData, row: NSData): NSData => row, renderComponent: NsPackagesActionComponent
+ }
+ };
+ }
+
+ /** smart table Data Settings @public */
+ public generateSettings(): void {
+ this.settings = {
+ edit: {
+ editButtonContent: '<i class="fa fa-edit" title="Edit"></i>',
+ confirmSave: true
+ },
+ delete: {
+ deleteButtonContent: '<i class="far fa-trash-alt" title="delete"></i>',
+ confirmDelete: true
+ },
+ columns: this.columnLists,
+ actions: {
+ add: false,
+ edit: false,
+ delete: false,
+ position: 'right'
+ },
+ attr: this.sharedService.tableClassConfig(),
+ pager: this.sharedService.paginationPagerConfig(),
+ noDataMessage: this.translateService.instant('NODATAMSG')
+ };
+ }
+
+ /** smart table listing manipulation @public */
+ public onChange(perPageValue: number): void {
+ this.dataSource.setPaging(1, perPageValue, true);
+ }
+
+ /** smart table listing manipulation @public */
+ public onUserRowSelect(event: MessageEvent): void {
+ Object.assign(event.data, { page: 'ns-package' });
+ this.dataService.changeMessage(event.data);
+ }
+
+ /** Drag and drop feature and fetchind the details of files @public */
+ public filesDropped(files: FileList): void {
+ if (files && files.length === 1) {
+ this.isLoadingResults = true;
+ this.sharedService.getFileString(files, 'gz').then((fileContent: ArrayBuffer): void => {
+ const apiURLHeader: APIURLHEADER = {
+ url: environment.NSDESCRIPTORSCONTENT_URL,
+ httpOptions: { headers: this.headers }
+ };
+ this.saveFileData(apiURLHeader, fileContent);
+ }).catch((err: string): void => {
+ this.isLoadingResults = false;
+ if (err === 'typeError') {
+ this.notifierService.notify('error', this.translateService.instant('GZFILETYPEERRROR'));
+ } else {
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ }
+ });
+ } else if (files && files.length > 1) {
+ this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION'));
+ }
+ }
+
+ /** Post the droped files and reload the page @public */
+ public saveFileData(urlHeader: APIURLHEADER, fileData: {}): void {
+ this.fileInput.nativeElement.value = null;
+ this.restService.postResource(urlHeader, fileData).subscribe((result: {}) => {
+ this.notifierService.notify('success', this.translateService.instant('PAGE.NSPACKAGE.CREATEDSUCCESSFULLY'));
+ this.generateData();
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'post');
+ this.isLoadingResults = false;
+ });
+ }
+
+ /** Generate nsData object from loop and return for the datasource @public */
+ public generateNSData(nsdpackagedata: NSDDetails): NSData {
+ return {
+ shortName: nsdpackagedata['short-name'],
+ identifier: nsdpackagedata._id,
+ description: nsdpackagedata.description,
+ vendor: nsdpackagedata.vendor,
+ version: nsdpackagedata.version
+ };
+ }
+
+ /** Fetching the data from server to Load in the smarttable @public */
+ public generateData(): void {
+ this.isLoadingResults = true;
+ this.restService.getResource(environment.NSDESCRIPTORSCONTENT_URL).subscribe((nsdPackageData: NSDDetails[]) => {
+ this.nsData = [];
+ nsdPackageData.forEach((nsdpackagedata: NSDDetails) => {
+ const nsDataObj: NSData = this.generateNSData(nsdpackagedata);
+ this.nsData.push(nsDataObj);
+ });
+ if (this.nsData.length > 0) {
+ this.checkDataClass = 'dataTables_present';
+ } else {
+ this.checkDataClass = 'dataTables_empty';
+ }
+ this.dataSource.load(this.nsData).then((data: boolean) => {
+ this.isLoadingResults = false;
+ }).catch(() => {
+ this.isLoadingResults = false;
+ });
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ this.isLoadingResults = false;
+ });
+ }
+ /** Handle compose new ns package method @public */
+ public composeNSPackage(): void {
+ this.modalService.open(ComposePackages, { backdrop: 'static' }).componentInstance.params = { page: 'ns-package' };
+ }
+
+ /**
+ * Lifecyle hook which get trigger on component destruction
+ */
+ public ngOnDestroy(): void {
+ this.generateDataSub.unsubscribe();
+ }
+}
diff --git a/src/app/packages/ns-packages/ns-composer/NSComposerComponent.html b/src/app/packages/ns-packages/ns-composer/NSComposerComponent.html
new file mode 100644
index 0000000..4e78c16
--- /dev/null
+++ b/src/app/packages/ns-packages/ns-composer/NSComposerComponent.html
@@ -0,0 +1,264 @@
+<!--
+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: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+-->
+<ng-sidebar-container class="ns-topology-sidebar-container">
+ <!-- A sidebar -->
+ <ng-sidebar [(opened)]="sideBarOpened" position="left">
+ <div class="sidebar-header">
+ <span class="topology_title" *ngIf="isShowNSDDetails">{{'PAGE.TOPOLOGY.NSD' | translate}}</span>
+ <span class="topology_title" *ngIf="isShowVLDetails">{{'PAGE.TOPOLOGY.VIRTUALLINK' | translate}}</span>
+ <span class="topology_title" *ngIf="isShowVNFDetails">{{'PAGE.TOPOLOGY.VNF' | translate}}</span>
+ <span class="topology_title" *ngIf="isShowCPDetails">{{'PAGE.TOPOLOGY.CONNECTIONPOINT' | translate}}</span>
+ <button (click)="toggleSidebar()" class="close" type="button">
+ <i class="fas fa-times-circle text-danger" aria-hidden="true"></i>
+ </button>
+ </div>
+ <div class="sidebar-body">
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 mb-2" *ngIf="isShowNSDDetails">
+ <div class="row">
+ <div class="col-12 p-0">
+ <form autocomplete="off">
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'SHORTNAME' | translate }}</label>
+ <div class="col-sm-8 p-0">
+ <input type="text" class="form-control" placeholder="{{ 'SHORTNAME' | translate }}" name="shortName"
+ [(ngModel)]="vnfdPackageDetails.shortName">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'VENDOR' | translate }}</label>
+ <div class="col-sm-8 p-0">
+ <input type="text" class="form-control" placeholder="{{ 'VENDOR' | translate }}" name="vendor"
+ [(ngModel)]="vnfdPackageDetails.vendor">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'DESCRIPTION' | translate }}</label>
+ <div class="col-sm-8 p-0">
+ <textarea type="text" class="form-control" placeholder="{{ 'DESCRIPTION' | translate }}"
+ name="description" [(ngModel)]="vnfdPackageDetails.description"></textarea>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'VERSION' | translate }}</label>
+ <div class="col-sm-8 p-0">
+ <input type="text" class="form-control" placeholder="{{ 'VERSION' | translate }}" name="version"
+ [(ngModel)]="vnfdPackageDetails.version">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'ID' | translate }}</label>
+ <div class="col-sm-8 p-0">
+ <input type="text" class="form-control" placeholder="{{ 'ID' | translate }}" name="id"
+ [(ngModel)]="vnfdPackageDetails.id">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'NAME' | translate }}</label>
+ <div class="col-sm-8 p-0">
+ <input type="text" class="form-control" placeholder="{{ 'NAME' | translate }}" name="name"
+ [(ngModel)]="vnfdPackageDetails.name">
+ </div>
+ </div>
+ <button type="button" class="btn btn-primary" (click)="saveNSD()" placement="top"
+ ngbTooltip="Save">
+ <i class="fas fa-save"></i> {{'SAVE' | translate}}
+ </button>
+ </form>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 mb-2" *ngIf="isShowVLDetails">
+ <div class="row">
+ <div class="col-12 p-0">
+ <form autocomplete="off">
+ <div class="form-group row">
+ <label class="col-sm-4 p-0 col-form-label">{{ 'NAME' | translate }}</label>
+ <div class="col-sm-8 p-0">
+ <input type="text" class="form-control" placeholder="{{ 'NAME' | translate }}" name="name"
+ [(ngModel)]="vlDetails.name">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 p-0 col-form-label">{{'PAGE.NSPACKAGE.NSCOMPOSE.MGMTNETWORK' | translate}}</label>
+ <div class="col-sm-8 p-0">
+ <select class="form-control custom-select" name="mgmt-network" [(ngModel)]="vlDetails['mgmt-network']">
+ <option [value]="true">True</option>
+ <option [value]="false">False</option>
+ </select>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 p-0 col-form-label">{{'PAGE.NSPACKAGE.NSCOMPOSE.VIMNETWORKNAME' | translate}}</label>
+ <div class="col-sm-8 p-0">
+ <input type="text" class="form-control" placeholder="Vim network name" name="vim-network-name"
+ [(ngModel)]="vlDetails['vim-network-name']">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 p-0 col-form-label">{{'TYPE' | translate}}</label>
+ <div class="col-sm-8 p-0">
+ <input type="text" class="form-control" placeholder="Type" name="type" [(ngModel)]="vlDetails.type">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 p-0 col-form-label">{{ 'ID' | translate }}</label>
+ <div class="col-sm-8 p-0">
+ <input type="text" class="form-control" placeholder="{{ 'ID' | translate }}" name="id"
+ [(ngModel)]="vlDetails.id">
+ </div>
+ </div>
+ <button type="button" class="btn btn-primary" placement="top" ngbTooltip="Save"
+ (click)="saveVL(vlDetails.id)">
+ <i class="fas fa-save"></i> {{'SAVE' | translate}}
+ </button>
+ </form>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 mb-2" *ngIf="isShowVNFDetails">
+ <div class="row">
+ <div class="col-12 p-0">
+ <table class="table table-bordered text-dark custom-table">
+ <tbody>
+ <tr>
+ <td>{{'PAGE.NSPACKAGE.NSCOMPOSE.MEMBER-VNF-INDEX' | translate}}</td>
+ <td>{{ vnfData['member-vnf-index'] }}</td>
+ </tr>
+ <tr>
+ <td>{{'PAGE.NSPACKAGE.NSCOMPOSE.VNFD-ID-REF' | translate}}</td>
+ <td>{{ vnfData['vnfd-id-ref'] }}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 mb-2" *ngIf="isShowCPDetails">
+ <div class="row">
+ <div class="col-12 p-0">
+ <table class="table table-bordered text-dark custom-table">
+ <tbody>
+ <tr>
+ <td>{{'PAGE.NSPACKAGE.NSCOMPOSE.VLD-ID' | translate}}</td>
+ <td>{{ vlDetails['name'] }}</td>
+ </tr>
+ <tr>
+ <td>{{'PAGE.NSPACKAGE.NSCOMPOSE.VNFD-CP-REF' | translate}}</td>
+ <td>{{ cpData['vnfd-connection-point-ref'] }}</td>
+ </tr>
+ <tr>
+ <td>{{'MEMBERINDEX' | translate}}</td>
+ <td>{{ cpData['member-vnf-index-ref'] }}</td>
+ </tr>
+ <tr>
+ <td>{{'PAGE.NSPACKAGE.NSCOMPOSE.VNFD-ID-REF' | translate}}</td>
+ <td>{{ cpData['vnfd-id-ref'] }}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+ </ng-sidebar>
+ <!-- Page content -->
+ <div ng-sidebar-content>
+ <button (click)="toggleSidebar()" class="btn btn-default" placement="right" ngbTooltip="{{'OPEN' | translate }}">
+ <i class="fa fa-arrow-right detail-sidebar" aria-hidden="true"></i>
+ </button>
+ </div>
+</ng-sidebar-container>
+<div class="container-fluid text-dark">
+ <div class="row bg-white ns-composer-form">
+ <div class="col-xs-3 col-sm-3 col-md-3 col-lg-3 pl-0 px-0">
+ <div class="row">
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 mb-2">
+ <fieldset class="p-2">
+ <legend class="vl-legend">
+ {{'PAGE.TOPOLOGY.SELECTELEMENT' | translate}}
+ </legend>
+ <ul class="list-group list-group-flush dragable">
+ <li class="list-group-item" draggable="true" (dragstart)="drag($event)" id="vl">
+ <img src="assets/images/VL.svg" class="ns-svg" draggable="false"/>
+ <span class="span-overflow-text font-weight-bold">{{'PAGE.TOPOLOGY.VL' | translate}}</span>
+ <span class="drag-icon pull-right"><i class="fas fa-arrows-alt"></i></span>
+ </li>
+ </ul>
+ </fieldset>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
+ <fieldset class="p-2">
+ <legend class="vnfd-legend">
+ {{'PAGE.TOPOLOGY.VNFD' | translate}}
+ </legend>
+ <ul class="list-group list-group-flush dragable scroll-box">
+ <li id="list['id']" class="list-group-item" draggable="true" (dragstart)="drag($event)"
+ [attr.data-id]="list['id']" *ngFor="let list of vnfList" placement="top"
+ container="body" ngbTooltip="{{ list['short-name'] }}">
+ <img src="assets/images/VNFD.svg" class="ns-svg" draggable="false"/>
+ <span class="span-overflow-text font-weight-bold">{{ list['short-name'] }}</span>
+ <span class="drag-icon pull-right"><i class="fas fa-arrows-alt"></i></span>
+ </li>
+ </ul>
+ </fieldset>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-9 col-sm-9 col-md-9 col-lg-9">
+ <div class="row">
+ <div class="col-xs-6 col-sm-6 col-md-6 col-lg-6 pl-0">
+ <div class="btn-group list" role="group" aria-label="Basic example">
+ <button type="button" class="btn btn-primary topology-btn" (click)="onFreeze()"
+ [class.pinned]="classApplied" placement="top" container="body" ngbTooltip="{{(classApplied ? 'UNFREEZE' : 'FREEZE') | translate}}">
+ <i class="fas fa-thumbtack"></i>
+ </button>
+ <button type="button" class="btn btn-primary topology-btn" (click)="onEdit()" placement="top"
+ container="body" ngbTooltip="{{'EDIT' | translate}}">
+ <i class="fa fa-edit"></i>
+ </button>
+ <button type="button" class="btn btn-primary topology-btn" (click)="showInfo()" placement="top"
+ container="body" ngbTooltip="{{'PAGE.TOPOLOGY.HELP' | translate}}">
+ <i class="fas fa-info"></i>
+ </button>
+ </div>
+ </div>
+ <div class="col-xs-6 col-sm-6 col-md-6 col-lg-6 text-right pr-0 badgegroup">
+ <span class="badge badge-primary badge-pill bg-white text-body font-weight-bold">
+ <img src="assets/images/VNFD.svg" class="ns-svg" draggable="false"/>
+ <br>{{'PAGE.TOPOLOGY.VNF' | translate}}</span>
+ <span class="badge badge-primary badge-pill bg-white text-body font-weight-bold">
+ <img src="assets/images/VL.svg" class="ns-svg" draggable="false"/>
+ <br>{{'PAGE.TOPOLOGY.VL' | translate}}</span>
+ <span class="badge badge-primary badge-pill bg-white text-body font-weight-bold">
+ <img src="assets/images/CP.svg" class="ns-svg" draggable="false"/>
+ <br>{{'PAGE.TOPOLOGY.CP' | translate}}</span>
+ </div>
+ </div>
+ <div class="row border-all">
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 svg-container">
+ <svg preserveAspectRatio="xMidYMin slice" (drop)="drop($event)" (dragover)="allowDrop($event)"
+ id="graphContainer" #graphContainer>
+ </svg>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/packages/ns-packages/ns-composer/NSComposerComponent.scss b/src/app/packages/ns-packages/ns-composer/NSComposerComponent.scss
new file mode 100644
index 0000000..d750ccc
--- /dev/null
+++ b/src/app/packages/ns-packages/ns-composer/NSComposerComponent.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: 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/packages/ns-packages/ns-composer/NSComposerComponent.ts b/src/app/packages/ns-packages/ns-composer/NSComposerComponent.ts
new file mode 100644
index 0000000..082496e
--- /dev/null
+++ b/src/app/packages/ns-packages/ns-composer/NSComposerComponent.ts
@@ -0,0 +1,1089 @@
+/*
+ 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: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+*/
+/**
+ * @file NS Compose Component
+ */
+// tslint:disable: no-increment-decrement
+import { HttpHeaders } from '@angular/common/http';
+import { Component, ElementRef, Injector, ViewChild, ViewEncapsulation } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { APIURLHEADER, CONSTANTNUMBER, ERRORDATA, MODALCLOSERESPONSEDATA, MODALCLOSERESPONSEWITHCP } from 'CommonModel';
+import { ConfirmationTopologyComponent } from 'ConfirmationTopology';
+import * as d3 from 'd3';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import * as HttpStatus from 'http-status-codes';
+import * as jsyaml from 'js-yaml';
+import { COMPOSERNODES, CONSTITUENTVNFD, GRAPHDETAILS, NSDDetails, Tick, TickPath, VLD, VNFDCONNECTIONPOINTREF } from 'NSDModel';
+import { RestService } from 'RestService';
+import { SharedService } from 'SharedService';
+import { isNullOrUndefined } from 'util';
+import { VNFData, VNFDDetails } from 'VNFDModel';
+
+/**
+ * Creating component
+ * @Component takes NSComposerComponent.html as template url
+ */
+@Component({
+ selector: 'app-ns-composer',
+ templateUrl: './NSComposerComponent.html',
+ styleUrls: ['./NSComposerComponent.scss'],
+ encapsulation: ViewEncapsulation.None
+})
+/** Exporting a class @exports NSComposerComponent */
+export class NSComposerComponent {
+ /** To inject services @public */
+ public injector: Injector;
+ /** View child contains graphContainer ref @public */
+ @ViewChild('graphContainer', { static: true }) public graphContainer: ElementRef;
+ /** dataService to pass the data from one component to another @public */
+ public dataService: DataService;
+ /** Contains VNFD Informations @public */
+ public vnfdPackageDetails: VNFData = { identifier: '', shortName: '', vendor: '', description: '', version: '', id: '', name: '' };
+ /** Contains VL Details @public */
+ public vlDetails: VLD = {
+ name: '',
+ 'mgmt-network': true,
+ 'vim-network-name': '',
+ type: '',
+ id: ''
+ };
+ /** Contains the information of the type of modification @public */
+ public putType: string;
+ /** Conatins mousedown action @public */
+ public mousedownNode: COMPOSERNODES = null;
+ /** Conatins mouseup action @public */
+ public mouseupNode: COMPOSERNODES = null;
+ /** Conatins mousedownLink action @public */
+ public mousedownLink: COMPOSERNODES = null;
+ /** Conatins current Selection node action @public */
+ public currentSelectedNode: COMPOSERNODES = null;
+ /** Conatins current Selection node action @public */
+ public currentSelectedLink: COMPOSERNODES = null;
+ /** Need to show the NSD Details @public */
+ public isShowNSDDetails: boolean = true;
+ /** Contains the node information of VL @public */
+ public vlNodes: {}[] = [];
+ /** Need to show the VL Details @public */
+ public isShowVLDetails: boolean = false;
+ /** Contains the node information of VNF @public */
+ public vnfNodes: {}[] = [];
+ /** contains the VNF Details @public */
+ public vnfData: CONSTITUENTVNFD;
+ /** Need to show the VNF Details @public */
+ public isShowVNFDetails: boolean = false;
+ /** Contains the node information of CP @public */
+ public cpNodes: {}[] = [];
+ /** Need to show the CP Details */
+ public cpData: VNFDCONNECTIONPOINTREF;
+ /** Need to show the VNF Details @public */
+ public isShowCPDetails: boolean = false;
+ /** random number count @public */
+ public randomNumberLength: number;
+ /** Contains the vnfd information @public */
+ public vnfList: VNFDDetails[] = [];
+ /** Add the activeclass for the selected @public */
+ public activeClass: string = 'active';
+ /** Add the fixed class for the freeze @public */
+ public fixedClass: string = 'fixed';
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = true;
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+ /** Get VNF selected node @public */
+ public getVNFSelectedData: VNFDDetails[];
+ /** Assign the forcesimulation active @public */
+ public forceSimulationActive: boolean = false;
+ /** Assign pinned class for the button when freezed @public */
+ public classApplied: boolean = false;
+ /** Contains sidebar open status @public */
+ public sideBarOpened: boolean = false;
+ /** Contains SVG attributes @private */
+ // tslint:disable-next-line:no-any
+ private svg: any;
+ /** Contains the Drag line */
+ // tslint:disable-next-line: no-any
+ private dragLine: any;
+ /** Contains VL node @private */
+ // tslint:disable-next-line:no-any
+ private vlNode: any;
+ /** Contains VNFD node @private */
+ // tslint:disable-next-line:no-any
+ private vnfdnode: any;
+ /** Contains CP node @private */
+ // tslint:disable-next-line:no-any
+ private cpnode: any;
+ /** Rendered nodes represent VL @private */
+ // tslint:disable-next-line:no-any
+ private gvlNode: any;
+ /** Rendered nodes represent VL @private */
+ // tslint:disable-next-line:no-any
+ private gvnfdNode: any;
+ /** Rendered nodes represent VL @private */
+ // tslint:disable-next-line:no-any
+ private gcpNode: any;
+ /** Contains forced node animations @private */
+ // tslint:disable-next-line:no-any
+ private force: any;
+ /** Contains all the selected node @private */
+ private selectedNode: COMPOSERNODES[] = [];
+ /** variables used for CP @private */
+ private iConnectionPointRef: number = 0;
+ /** Contains the connected point @private */
+ private connectionPoint: string;
+ /** Contains all the NSD information @private */
+ private nsd: string;
+ /** Contains all the VNFD information @private */
+ private vnfd: string;
+ /** Contains id of the node @private */
+ private identifier: string;
+ /** Variables used for cp @private */
+ private jConnectionPointRef: number = 0;
+ /** Contains copy of NSD information @private */
+ private nsdCopy: string;
+ /** Contains the VNFD copy @private */
+ private vnfdCopy: string;
+ /** Contains name of the node @private */
+ private name: string;
+ /** Contains member vnf index value of the node @private */
+ private memberVnfIndexValue: number = 0;
+ /** Contains path information of the node */
+ // tslint:disable-next-line:no-any
+ private path: any;
+ /** Contains the node information @private */
+ private nodes: COMPOSERNODES[] = [];
+ /** Contains the link information of nodes @private */
+ private links: {}[] = [];
+ /** Contains the NS information @private */
+ private nsData: NSDDetails;
+ /** Instance of the rest service @private */
+ private restService: RestService;
+ /** Service holds the router information @private */
+ private router: Router;
+ /** Holds teh instance of AuthService class of type AuthService @private */
+ private activatedRoute: ActivatedRoute;
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+ /** Controls the header form @private */
+ private headers: HttpHeaders;
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+ /** Contains lastkeypressed instance @private */
+ private lastKeyDown: number = -1;
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+ /** Setting the Value of connection point refrence of the CP @private */
+ private setVnfdConnectionPointRef: string;
+ /** Setting the Value of VL name for confirmation @private */
+ private vlName: string;
+ /** Setting the Value of VNFD name for confirmation @private */
+ private setVnfdName: string;
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+ /** Contains selected node VLD objects @private */
+ private selectedVLDResult: VLD;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.dataService = this.injector.get(DataService);
+ this.router = this.injector.get(Router);
+ this.activatedRoute = this.injector.get(ActivatedRoute);
+ this.notifierService = this.injector.get(NotifierService);
+ this.translateService = this.injector.get(TranslateService);
+ this.modalService = this.injector.get(NgbModal);
+ this.sharedService = this.injector.get(SharedService);
+ }
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ // tslint:disable-next-line:no-backbone-get-set-outside-model
+ this.identifier = this.activatedRoute.snapshot.paramMap.get('id');
+ this.generateData();
+ this.headers = new HttpHeaders({
+ 'Content-Type': 'application/zip',
+ Accept: 'application/json',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ }
+ /** Events handles at drag on D3 region @public */
+ // tslint:disable-next-line:no-any
+ public drag(ev: any): void {
+ if (ev.target.id === 'vl') {
+ ev.dataTransfer.setData('text', ev.target.id);
+ } else {
+ ev.dataTransfer.setData('text', ev.target.attributes['data-id'].value);
+ }
+ }
+ /** On clicking redirect to NS edit page @public */
+ public onEdit(): void {
+ this.router.navigate(['/packages/ns/edit/', this.identifier]).catch(() => {
+ // Catch Navigation Error
+ });
+ }
+ /** Events handles drop at D3 region @public */
+ public drop(ev: DragEvent): void {
+ event.preventDefault();
+ this.name = ev.dataTransfer.getData('text');
+ if (this.name === 'vl') {
+ this.svg.selectAll('*').remove();
+ this.vldropComposer();
+ } else {
+ this.svg.selectAll('*').remove();
+ this.vnfd = ev.dataTransfer.getData('text');
+ this.vnfdropComposer();
+ }
+ }
+ /** Drop VL Composer Data @public */
+ public vldropComposer(): void {
+ this.randomNumberLength = CONSTANTNUMBER.randomNumber;
+ const generateId: string = 'ns_vl_' + this.randomString(
+ this.randomNumberLength,
+ '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ );
+ if (this.nsData.vld !== undefined) {
+ this.nsData.vld.push({
+ 'vim-network-name': 'PUBLIC',
+ name: generateId,
+ 'mgmt-network': true,
+ type: 'ELAN',
+ id: generateId
+ });
+ } else {
+ Object.assign(this.nsData, {
+ vld: [{
+ 'vim-network-name': 'PUBLIC',
+ name: generateId,
+ 'mgmt-network': true,
+ type: 'ELAN',
+ id: generateId
+ }]
+ });
+ }
+ this.putType = 'nsdadd';
+ this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
+ }
+ /** Drop VNFD Composer Data @public */
+ public vnfdropComposer(): void {
+ if (this.nsData['constituent-vnfd'] !== undefined) {
+ this.nsData['constituent-vnfd'].push({
+ 'vnfd-id-ref': this.vnfd,
+ 'member-vnf-index': ++this.memberVnfIndexValue
+ });
+ } else {
+ Object.assign(this.nsData, {
+ 'constituent-vnfd': [{
+ 'vnfd-id-ref': this.vnfd,
+ 'member-vnf-index': ++this.memberVnfIndexValue
+ }]
+ });
+ }
+ this.putType = 'vnfdadd';
+ this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
+ }
+ /** Events handles allow drop on D3 region @public */
+ public allowDrop(ev: DragEvent): void {
+ ev.preventDefault();
+ }
+ /** Save NSD Information @public */
+ public saveNSD(): void {
+ if (this.vnfdPackageDetails.shortName !== undefined) {
+ this.nsData['short-name'] = this.vnfdPackageDetails.shortName;
+ }
+ if (this.vnfdPackageDetails.vendor !== undefined) {
+ this.nsData.vendor = this.vnfdPackageDetails.vendor;
+ }
+ if (this.vnfdPackageDetails.description !== undefined) {
+ this.nsData.description = this.vnfdPackageDetails.description;
+ }
+ if (this.vnfdPackageDetails.version !== undefined) {
+ this.nsData.version = this.vnfdPackageDetails.version;
+ }
+ if (this.vnfdPackageDetails.id !== undefined) {
+ this.nsData.id = this.vnfdPackageDetails.id;
+ }
+ if (this.vnfdPackageDetails.name !== undefined) {
+ this.nsData.name = this.vnfdPackageDetails.name;
+ }
+ this.putType = 'nsdUpdate';
+ this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
+ }
+ /** Save Virtual Link @public */
+ public saveVL(vlid: string): void {
+ this.nsData.vld.forEach((result: VLD) => {
+ if (result.id === vlid) {
+ result.name = this.vlDetails.name;
+ result['mgmt-network'] = !isNullOrUndefined(this.vlDetails['mgmt-network']) ? this.vlDetails['mgmt-network'] : true;
+ result['vim-network-name'] = !isNullOrUndefined(this.vlDetails['vim-network-name']) ? this.vlDetails['vim-network-name'] : '';
+ result.type = this.vlDetails.type;
+ result.id = this.vlDetails.id;
+ }
+ });
+ this.putType = 'vlUpdate';
+ this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
+ }
+ /** Add the new Data @public */
+ public addData(apiURL: string, identifier: string, data: NSDDetails, putType: string): void {
+ this.isLoadingResults = true;
+ let successMessage: string = '';
+ if (putType === 'nsdadd') {
+ successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.ADDNSD';
+ } else if (putType === 'vnfdadd') {
+ successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.ADDVNFD';
+ } else if (putType === 'cpAdded') {
+ successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.ADDNS';
+ } else if (putType === 'nsdUpdate') {
+ successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.UPDATEDSUCCESSFULLY';
+ } else if (putType === 'vlUpdate') {
+ successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.UPDATEDSUCCESSFULLY';
+ } else if (putType === 'nsddelete') {
+ successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETENSD';
+ } else if (putType === 'vnfddelete') {
+ successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETEVNFD';
+ } else if (putType === 'nsdelete') {
+ successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETENS';
+ } else if (putType === 'linkdelete') {
+ successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETELINK';
+ }
+ /** Below hide for conflicts with light weight UI */
+ const apiURLHeader: APIURLHEADER = {
+ url: apiURL + '/' + identifier + '/nsd_content',
+ httpOptions: { headers: this.headers }
+ };
+ const nsData: {} = {};
+ nsData['nsd:nsd-catalog'] = {};
+ nsData['nsd:nsd-catalog'].nsd = [];
+ nsData['nsd:nsd-catalog'].nsd.push(data);
+ const descriptorInfo: string = jsyaml.dump(nsData, {sortKeys: true});
+ this.sharedService.targzFile({ packageType: 'nsd', id: this.identifier, descriptor: descriptorInfo })
+ .then((content: ArrayBuffer): void => {
+ this.restService.putResource(apiURLHeader, content).subscribe((res: {}) => {
+ this.generateData();
+ this.notifierService.notify('success', this.translateService.instant(successMessage));
+ this.isLoadingResults = false;
+ }, (error: ERRORDATA) => {
+ this.generateData();
+ this.restService.handleError(error, 'put');
+ this.isLoadingResults = false;
+ });
+ }).catch((): void => {
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ this.isLoadingResults = false;
+ });
+ }
+ /** Show Info @public */
+ public showInfo(): void {
+ const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
+ modalRef.componentInstance.topologyType = 'Info';
+ modalRef.componentInstance.topologytitle = this.translateService.instant('PAGE.TOPOLOGY.INFO');
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ // empty
+ }
+ }).catch();
+ }
+ /** Event to freeze the animation @public */
+ public onFreeze(): void {
+ this.classApplied = !this.classApplied;
+ const alreadyFixedIsActive: boolean = d3.select('svg#graphContainer').classed(this.fixedClass);
+ d3.select('svg#graphContainer').classed(this.fixedClass, !alreadyFixedIsActive);
+ if (alreadyFixedIsActive) {
+ this.force.stop();
+ }
+ this.forceSimulationActive = alreadyFixedIsActive;
+ this.nodes.forEach((d: COMPOSERNODES) => {
+ d.fx = (alreadyFixedIsActive) ? null : d.x;
+ d.fy = (alreadyFixedIsActive) ? null : d.y;
+ });
+ if (alreadyFixedIsActive) {
+ this.force.restart();
+ }
+ }
+ /** Events handles when dragended @public */
+ public toggleSidebar(): void {
+ this.sideBarOpened = !this.sideBarOpened;
+ this.deselectAllNodes();
+ this.showRightSideInfo(true, false, false, false);
+ }
+ /** Prepare information for node creation of VNFD @private */
+ private generateData(): void {
+ this.generateVNFData();
+ this.generateDataNSDTopology();
+ this.sideBarOpened = false;
+ }
+ /** Prepare the information of the VNFD @private */
+ private generateVNFData(): void {
+ this.restService.getResource(environment.VNFPACKAGESCONTENT_URL).subscribe((vnfdPackageData: VNFDDetails[]) => {
+ this.vnfList = vnfdPackageData;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ });
+ }
+ /** Prepare information for node creation of NSD Topology @private */
+ private generateDataNSDTopology(): void {
+ this.nodes = [];
+ this.links = [];
+ this.iConnectionPointRef = 0;
+ this.jConnectionPointRef = 0;
+ this.restService.getResource(environment.NSDESCRIPTORSCONTENT_URL + '/' + this.identifier).subscribe((nsData: NSDDetails) => {
+ delete nsData._admin;
+ delete nsData._id;
+ this.nsData = nsData;
+ this.vnfdPackageDetails.shortName = nsData['short-name'];
+ this.vnfdPackageDetails.vendor = nsData.vendor;
+ this.vnfdPackageDetails.description = nsData.description;
+ this.vnfdPackageDetails.version = nsData.version;
+ this.vnfdPackageDetails.id = nsData.id;
+ this.vnfdPackageDetails.name = nsData.name;
+ if (nsData.vld !== undefined) {
+ /** Details of the VL */
+ this.nsDataVLD(nsData);
+ }
+ if (nsData['constituent-vnfd'] !== undefined) {
+ /** Details of the VNFD */
+ this.nsDataConstituentVNFD(nsData);
+ }
+ if (nsData.vld !== undefined) {
+ this.nsDataVLDLinkCreation(nsData);
+ }
+ this.separateAndCreatenode();
+ }, (error: ERRORDATA) => {
+ if (error.error.status === HttpStatus.NOT_FOUND || error.error.status === HttpStatus.UNAUTHORIZED) {
+ this.router.navigateByUrl('404', { skipLocationChange: true }).catch();
+ } else {
+ this.restService.handleError(error, 'get');
+ }
+ this.isLoadingResults = false;
+ this.isShowNSDDetails = false;
+ });
+ }
+ /** nsData-vld undefined Call this function @private */
+ private nsDataVLD(nsData: NSDDetails): void {
+ nsData.vld.forEach((res: VLD) => {
+ this.nodes.push({ id: res.id, reflexive: false, type: 'vld', name: res.id, selectorId: res.id });
+ this.nsd = res.id;
+ if (res['vnfd-connection-point-ref'] !== undefined) {
+ res['vnfd-connection-point-ref'].forEach((result: VNFDCONNECTIONPOINTREF) => {
+ this.nodes.push(
+ {
+ id: this.nsd + ++this.iConnectionPointRef + ':' + result['vnfd-connection-point-ref'],
+ reflexive: false,
+ type: 'ns',
+ name: result['vnfd-connection-point-ref'],
+ nodeIndex: result['member-vnf-index-ref'],
+ selectorId: result['vnfd-connection-point-ref'] + '_' + result['member-vnf-index-ref'] + '-osm-' + this.nsd
+ });
+ });
+ }
+ });
+ }
+ /** nsData constituent-vnfd undefined Call this function @private */
+ private nsDataConstituentVNFD(nsData: NSDDetails): void {
+ nsData['constituent-vnfd'].forEach((res: CONSTITUENTVNFD) => {
+ this.nodes.push(
+ {
+ id: res['vnfd-id-ref'] + ':' + res['member-vnf-index'],
+ reflexive: false,
+ type: 'vnfd',
+ name: res['vnfd-id-ref'],
+ nodeIndex: res['member-vnf-index'],
+ selectorId: res['vnfd-id-ref'] + '_' + res['member-vnf-index']
+ });
+ this.vnfd = res['vnfd-id-ref'];
+ this.memberVnfIndexValue = res['member-vnf-index'];
+ });
+ }
+
+ /** nsData-vld undefined Call this function @private */
+ private nsDataVLDLinkCreation(nsData: NSDDetails): void {
+ nsData.vld.forEach((res: VLD) => {
+ this.nsdCopy = res.id;
+ if (res['vnfd-connection-point-ref'] !== undefined) {
+ this.nsDataVNFDConnectionPointRefrence(res);
+ }
+ });
+ }
+ /** nsData-vnfd-connection-point-ref undefined Call this function @private */
+ private nsDataVNFDConnectionPointRefrence(res: VLD): void {
+ res['vnfd-connection-point-ref'].forEach((result: VNFDCONNECTIONPOINTREF) => {
+ this.connectionPoint = this.nsdCopy + ++this.jConnectionPointRef + ':' + result['vnfd-connection-point-ref'];
+ this.vnfdCopy = result['vnfd-id-ref'] + ':' + result['member-vnf-index-ref'];
+ const connectionPointPos: number = this.nodes.map((e: COMPOSERNODES) => { return e.id; }).indexOf(this.connectionPoint);
+ const nsdPos: number = this.nodes.map((e: COMPOSERNODES) => { return e.id; }).indexOf(this.nsdCopy);
+ const vnfdPos: number = this.nodes.map((e: COMPOSERNODES) => { return e.id; }).indexOf(this.vnfdCopy);
+ this.links.push(
+ {
+ source: this.nodes[connectionPointPos],
+ target: this.nodes[nsdPos]
+ },
+ {
+ source: this.nodes[connectionPointPos],
+ target: this.nodes[vnfdPos]
+ });
+ });
+ }
+ /** Generate random string @private */
+ private randomString(length: number, chars: string): string {
+ let result: string = '';
+ for (let randomStringRef: number = length; randomStringRef > 0; --randomStringRef) {
+ result += chars[Math.floor(Math.random() * chars.length)];
+ }
+ return result;
+ }
+ /** Separate and create node @private */
+ private separateAndCreatenode(): void {
+ this.seprateNodes(this.nodes);
+ this.createnode(this.nodes);
+ this.isLoadingResults = false;
+ }
+ /** Get the default Configuration of containers @private */
+ private getGraphContainerAttr(): GRAPHDETAILS {
+ return {
+ width: 700,
+ height: 400,
+ nodeHeight: 50,
+ nodeWidth: 35,
+ textX: -35,
+ textY: 30,
+ radius: 5,
+ distance: 50,
+ strength: -500,
+ forcex: 2,
+ forcey: 2,
+ sourcePaddingYes: 17,
+ sourcePaddingNo: 12,
+ targetPaddingYes: 4,
+ targetPaddingNo: 3,
+ alphaTarget: 0.3,
+ imageX: -25,
+ imageY: -25,
+ shiftKeyCode: 17
+ };
+ }
+ /** Separate the nodes along with its tyep @private */
+ private seprateNodes(node: COMPOSERNODES[]): void {
+ this.vlNodes = []; this.vnfNodes = []; this.cpNodes = [];
+ node.forEach((nodeList: COMPOSERNODES) => {
+ if (nodeList.type === 'vld') {
+ this.vlNodes.push(nodeList);
+ } else if (nodeList.type === 'vnfd') {
+ this.vnfNodes.push(nodeList);
+ } else if (nodeList.type === 'ns') {
+ this.cpNodes.push(nodeList);
+ }
+ });
+ }
+ /** Node is created and render at D3 region @private */
+ private createnode(node: COMPOSERNODES[]): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ d3.selectAll('svg#graphContainer > *').remove();
+ d3.select(window).on('keydown', () => { this.keyDown(); });
+ d3.select(window).on('keyup', () => { this.keyUp(); });
+ this.svg = d3.select('#graphContainer')
+ .attr('oncontextmenu', 'return false;')
+ .attr('width', graphContainerAttr.width)
+ .attr('height', graphContainerAttr.height)
+ .on('mousemove', () => { this.mousemove(); });
+ this.force = d3.forceSimulation()
+ .force('charge', d3.forceManyBody().strength(graphContainerAttr.strength))
+ .force('link', d3.forceLink().id((d: TickPath) => d.id).distance(graphContainerAttr.distance))
+ .force('center', d3.forceCenter(graphContainerAttr.width / graphContainerAttr.forcex,
+ graphContainerAttr.height / graphContainerAttr.forcey))
+ .force('x', d3.forceX(graphContainerAttr.width / graphContainerAttr.forcex))
+ .force('y', d3.forceY(graphContainerAttr.height / graphContainerAttr.forcey))
+ .on('tick', () => { this.tick(); });
+ this.dragLine = this.svg.append('svg:path').attr('class', 'link dragline hidden').attr('d', 'M0,0L0,0');
+ this.path = this.svg.append('svg:g').selectAll('path');
+ this.vlNode = this.svg.append('svg:g').selectAll('vlnode');
+ this.vnfdnode = this.svg.append('svg:g').selectAll('vnfdnode');
+ this.cpnode = this.svg.append('svg:g').selectAll('cpnode');
+ // app starts here
+ this.restart(node);
+ }
+ /** update force layout (called automatically each iteration) @private */
+ private tick(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ // draw directed edges with proper padding from node centers
+ this.path.attr('class', 'link').attr('d', (d: Tick) => {
+ const deltaX: number = d.target.x - d.source.x;
+ const deltaY: number = d.target.y - d.source.y;
+ const dist: number = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
+ const normX: number = deltaX / dist;
+ const normY: number = deltaY / dist;
+ const sourcePadding: number = d.left ? graphContainerAttr.sourcePaddingYes : graphContainerAttr.sourcePaddingNo;
+ const targetPadding: number = d.right ? graphContainerAttr.targetPaddingYes : graphContainerAttr.targetPaddingNo;
+ const sourceX: number = d.source.x + (sourcePadding * normX);
+ const sourceY: number = d.source.y + (sourcePadding * normY);
+ const targetX: number = d.target.x - (targetPadding * normX);
+ const targetY: number = d.target.y - (targetPadding * normY);
+ return `M${sourceX},${sourceY}L${targetX},${targetY}`;
+ }).on('dblclick', (d: Tick) => { this.getDeleteLinkConfirmation(d); });
+ this.vlNode.attr('transform', (t: TickPath) => `translate(${t.x},${t.y})`);
+ this.vnfdnode.attr('transform', (t: TickPath) => `translate(${t.x},${t.y})`);
+ this.cpnode.attr('transform', (t: TickPath) => `translate(${t.x},${t.y})`);
+ }
+ /** Update graph (called when needed) at D3 region @private */
+ private restart(node: COMPOSERNODES[]): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.path = this.path.data(this.links);
+ this.vlNode = this.vlNode.data(this.vlNodes, (d: COMPOSERNODES) => d.id);
+ this.vnfdnode = this.vnfdnode.data(this.vnfNodes, (d: COMPOSERNODES) => d.id);
+ this.cpnode = this.cpnode.data(this.cpNodes, (d: COMPOSERNODES) => d.id);
+ this.resetAndCreateNodes();
+ this.force.nodes(node).force('link').links(this.links);
+ this.force.alphaTarget(graphContainerAttr.alphaTarget).restart();
+ }
+ /** Rest and create nodes @private */
+ private resetAndCreateNodes(): void {
+ this.path.exit().remove();
+ this.vlNode.exit().remove();
+ this.vnfdnode.exit().remove();
+ this.cpnode.exit().remove();
+ this.getPathNodes();
+ this.getVLNodes();
+ this.getVNFDNodes();
+ this.getCPNodes();
+ this.path.merge(this.path);
+ this.vlNode = this.gvlNode.merge(this.vlNode);
+ this.vnfdnode = this.gvnfdNode.merge(this.vnfdnode);
+ this.cpnode = this.gcpNode.merge(this.cpnode);
+ }
+ /** setting the Path @private */
+ private getPathNodes(): void {
+ this.path = this.path.enter().append('svg:path');
+ }
+ /** Setting all the VL nodes @private */
+ private getVLNodes(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.gvlNode = this.vlNode.enter().append('svg:g');
+ this.gvlNode.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
+ this.gvlNode.append('svg:image')
+ .style('opacity', 1)
+ .attr('x', graphContainerAttr.imageX)
+ .attr('y', graphContainerAttr.imageY)
+ .attr('id', (d: COMPOSERNODES) => { return d.selectorId; })
+ .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
+ .attr('xlink:href', 'assets/images/VL.svg')
+ .on('mousedown', (d: COMPOSERNODES) => { this.mouseDown(d); })
+ .on('mouseup', (d: COMPOSERNODES) => { this.mouseUp(d); })
+ .on('click', (d: COMPOSERNODES) => { this.singleClick(this.vlNode, d); this.onNodeClickToggleSidebar(); })
+ .on('dblclick', (d: COMPOSERNODES) => { this.getDeleteConfirmation(d); this.onNodedblClickToggleSidebar(); });
+ this.gvlNode.append('svg:text')
+ .attr('class', 'node_text')
+ .attr('y', graphContainerAttr.textY)
+ .text((d: COMPOSERNODES) => d.id);
+ }
+ /** Setting all the VNFD nodes @private */
+ private getVNFDNodes(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.gvnfdNode = this.vnfdnode.enter().append('svg:g');
+ this.gvnfdNode.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
+ this.gvnfdNode.append('svg:image')
+ .style('opacity', 1)
+ .attr('x', graphContainerAttr.imageX)
+ .attr('y', graphContainerAttr.imageY)
+ .attr('id', (d: COMPOSERNODES) => { return d.selectorId; })
+ .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
+ .attr('xlink:href', 'assets/images/VNFD.svg')
+ .on('mousedown', (d: COMPOSERNODES) => { this.mouseDown(d); })
+ .on('mouseup', (d: COMPOSERNODES) => { this.mouseUp(d); })
+ .on('click', (d: COMPOSERNODES) => { this.singleClick(this.vnfdnode, d); this.onNodeClickToggleSidebar(); })
+ .on('dblclick', (d: COMPOSERNODES) => { this.getDeleteConfirmation(d); this.onNodedblClickToggleSidebar(); });
+ this.gvnfdNode.append('svg:text')
+ .attr('class', 'node_text')
+ .attr('y', graphContainerAttr.textY)
+ .text((d: COMPOSERNODES) => d.id);
+ }
+ /** Setting all the CP nodes @private */
+ private getCPNodes(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.gcpNode = this.cpnode.enter().append('svg:g');
+ this.gcpNode.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
+ this.gcpNode.append('svg:image')
+ .style('opacity', 1)
+ .attr('x', graphContainerAttr.imageX)
+ .attr('y', graphContainerAttr.imageY)
+ .attr('id', (d: COMPOSERNODES) => { return d.selectorId; })
+ .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
+ .attr('xlink:href', 'assets/images/CP.svg')
+ .on('mousedown', (d: COMPOSERNODES) => { this.mouseDown(d); })
+ .on('mouseup', (d: COMPOSERNODES) => { this.mouseUp(d); })
+ .on('click', (d: COMPOSERNODES) => { this.singleClick(this.cpnode, d); this.onNodeClickToggleSidebar(); })
+ .on('dblclick', (d: COMPOSERNODES) => { this.getDeleteConfirmation(d); this.onNodedblClickToggleSidebar(); });
+ this.gcpNode.append('svg:text')
+ .attr('class', 'node_text')
+ .attr('y', graphContainerAttr.textY)
+ .text((d: COMPOSERNODES) => d.id);
+ }
+ /** Events handles when mousemove it will capture the selected node data @private */
+ private mousemove(): void {
+ if (!this.mousedownNode) { return; }
+ this.dragLine.attr('d',
+ `M${this.mousedownNode.x},${this.mousedownNode.y}L${d3.mouse(d3.event.currentTarget)[0]},${d3.mouse(d3.event.currentTarget)[1]}`);
+ }
+ /** Get confirmation Before Deleting the Link in Topology @private */
+ private getAddConfirmation(mouseData: COMPOSERNODES, getNsData: NSDDetails, addType: string, getVLDIndex: number): void {
+ let findVNFName: string = '';
+ let findVLDID: string = '';
+ if (mouseData.type === 'vld') {
+ findVNFName = this.mouseupNode.name;
+ findVLDID = this.mousedownNode.id;
+ } else {
+ findVNFName = this.mousedownNode.name;
+ findVLDID = this.mouseupNode.id;
+ }
+ getNsData.vld.forEach((result: VLD) => {
+ if (result.id === findVLDID) {
+ this.vlName = result.name;
+ this.getVNFSelectedData = this.vnfList.filter((vnfList: VNFDDetails) => vnfList.id === findVNFName);
+ this.setVnfdConnectionPointRef = this.getVNFSelectedData[0]['mgmt-interface'].cp;
+ this.setVnfdName = this.getVNFSelectedData[0].name;
+ this.selectedVLDResult = result;
+ }
+ });
+ if (this.vlName !== undefined && this.setVnfdName !== undefined && this.setVnfdConnectionPointRef !== undefined) {
+ const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
+ modalRef.componentInstance.topologyType = 'Add';
+ modalRef.componentInstance.cpDetails = this.getVNFSelectedData[0]['connection-point'];
+ this.translateService.get('PAGE.TOPOLOGY.ADDINGCP', {
+ vlname: '<b>' + this.vlName + '</b>',
+ vnfdname: '<b>' + this.setVnfdName + '</b>',
+ cpname: '<b>' + this.setVnfdConnectionPointRef + '</b>'
+ }).subscribe((res: string) => {
+ modalRef.componentInstance.topologyname = res;
+ });
+ modalRef.componentInstance.topologytitle = this.translateService.instant('PAGE.TOPOLOGY.CONNECTIONPOINT');
+ modalRef.result.then((result: MODALCLOSERESPONSEWITHCP) => {
+ if (result) {
+ this.nsData = getNsData;
+ this.generateCPForVNF(this.selectedVLDResult, result.connection_point, getVLDIndex);
+ this.addData(environment.NSDESCRIPTORS_URL, this.identifier, getNsData, addType);
+ } else {
+ this.deselectPath();
+ }
+ }).catch();
+ } else {
+ this.deselectPath();
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ }
+ }
+
+ /** Generate connection point for vnf using vld @private */
+ private generateCPForVNF(result: VLD, cp: string, getVLDIndex: number): void {
+ if (result['vnfd-connection-point-ref'] !== undefined) {
+ result['vnfd-connection-point-ref'].push({
+ 'member-vnf-index-ref': getVLDIndex,
+ 'vnfd-connection-point-ref': cp,
+ 'vnfd-id-ref': this.getVNFSelectedData[0].name
+ });
+ } else {
+ Object.assign(result, {
+ 'vnfd-connection-point-ref': [{
+ 'member-vnf-index-ref': getVLDIndex,
+ 'vnfd-connection-point-ref': cp,
+ 'vnfd-id-ref': this.getVNFSelectedData[0].name
+ }]
+ });
+ }
+ }
+
+ /** Events handles when mousedown click it will capture the selected node data @private */
+ private mouseDown(d: COMPOSERNODES): void {
+ event.preventDefault();
+ if (d3.event.ctrlKey) { return; }
+ if (d3.event.shiftKey) {
+ if (d.type === 'vnfd') {
+ this.selectedNode.push(d);
+ }
+ this.mousedownNode = d;
+ this.currentSelectedNode = (this.mousedownNode === this.currentSelectedNode) ? null : this.mousedownNode;
+ this.currentSelectedLink = null;
+ this.dragLine.style('marker-end', 'url(#end-arrow)').classed('hidden', false)
+ .attr('d', `M${this.mousedownNode.x},${this.mousedownNode.y}L${this.mousedownNode.x},${this.mousedownNode.y}`);
+ }
+ }
+ /** Event handles when mouseup event occures @private */
+ private mouseUp(d: COMPOSERNODES): void {
+ if (!this.mousedownNode) { return; }
+ this.dragLine.classed('hidden', true).style('marker-end', '');
+ this.mouseupNode = d;
+ if (this.mousedownNode.type === 'vld' && this.mouseupNode.type === 'vnfd') {
+ const getOldVLDIndex: string[] = this.mouseupNode.id.split(':');
+ const setOldVLDindex: number = +getOldVLDIndex[1];
+ this.putType = 'cpAdded';
+ this.getAddConfirmation(this.mousedownNode, this.nsData, this.putType, setOldVLDindex);
+ } else if (this.mousedownNode.type === 'vnfd' && this.mouseupNode.type === 'vld') {
+ const getOldVLDIndex: string[] = this.mousedownNode.id.split(':');
+ const setOldVLDindex: number = +getOldVLDIndex[1];
+ this.putType = 'cpAdded';
+ this.getAddConfirmation(this.mousedownNode, this.nsData, this.putType, setOldVLDindex);
+ } else if (this.mousedownNode.type === 'vnfd' && this.mouseupNode.type === 'ns') {
+ this.deselectPath();
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNFCP'));
+ } else if (this.mousedownNode.type === 'vld' && this.mouseupNode.type === 'ns') {
+ this.deselectPath();
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNFCP'));
+ } else if (this.mousedownNode.type === 'vld' && this.mouseupNode.type === 'vld') {
+ this.deselectPath();
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVL'));
+ } else if (this.mousedownNode.type === 'vnfd' && this.mouseupNode.type === 'vnfd') {
+ this.deselectPath();
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNF'));
+ } else if (this.mousedownNode.type === 'ns' && this.mouseupNode.type === 'ns') {
+ this.deselectPath();
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKCP'));
+ } else {
+ this.deselectPath();
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVLVNF'));
+ }
+ this.resetMouseVars();
+ // select new link
+ this.currentSelectedLink = d;
+ this.currentSelectedNode = null;
+ }
+ /** Mosue Drag Line false if it is not satisfied @private */
+ private deselectPath(): void {
+ this.dragLine.classed('hidden', true).style('marker-end', '').attr('d', 'M0,0L0,0');
+ }
+ /** reset Mouse varaibles @private */
+ private resetMouseVars(): void {
+ this.mousedownNode = null;
+ this.mouseupNode = null;
+ this.mousedownLink = null;
+ }
+ /** De-select all the selected nodes @private */
+ private deselectAllNodes(): void {
+ this.vlNode.select('image').classed(this.activeClass, false);
+ this.vnfdnode.select('image').classed(this.activeClass, false);
+ this.cpnode.select('image').classed(this.activeClass, false);
+ }
+ /** Show the right-side information @private */
+ private showRightSideInfo(nsdDetails: boolean, vldDetails: boolean, vnfDeails: boolean, cpDetails: boolean): void {
+ this.isShowNSDDetails = nsdDetails;
+ this.isShowVLDetails = vldDetails;
+ this.isShowVNFDetails = vnfDeails;
+ this.isShowCPDetails = cpDetails;
+ }
+ /** Events handles when Shift Click to perform create cp @private */
+ // tslint:disable-next-line: no-any
+ private singleClick(nodeSelected: any, d: COMPOSERNODES): void {
+ this.selectNodeExclusive(nodeSelected, d);
+ }
+ /** Selected nodes @private */
+ // tslint:disable-next-line: no-any
+ private selectNodeExclusive(nodeSeleced: any, d: COMPOSERNODES): void {
+ const alreadyIsActive: boolean = nodeSeleced.select('#' + d.selectorId).classed(this.activeClass);
+ this.deselectAllNodes();
+ nodeSeleced.select('#' + d.selectorId).classed(this.activeClass, !alreadyIsActive);
+ if (d.type === 'vld' && !alreadyIsActive) {
+ this.nsData.vld.forEach((result: VLD) => {
+ if (result.id === d.id) {
+ this.showRightSideInfo(false, true, false, false);
+ this.vlDetails = result;
+ }
+ });
+ } else if (d.type === 'vnfd' && !alreadyIsActive) {
+ this.nsData['constituent-vnfd'].forEach((result: CONSTITUENTVNFD) => {
+ if (result['member-vnf-index'] === d.nodeIndex && result['vnfd-id-ref'] === d.name) {
+ this.showRightSideInfo(false, false, true, false);
+ this.vnfData = result;
+ }
+ });
+ } else if (d.type === 'ns' && !alreadyIsActive) {
+ this.nsData.vld.forEach((result: VLD) => {
+ if (result['vnfd-connection-point-ref'] !== undefined) {
+ result['vnfd-connection-point-ref'].forEach((resultCP: VNFDCONNECTIONPOINTREF) => {
+ if (resultCP['member-vnf-index-ref'] === d.nodeIndex && resultCP['vnfd-connection-point-ref'] === d.name) {
+ this.cpData = resultCP;
+ this.vlDetails = result;
+ this.showRightSideInfo(false, false, false, true);
+ }
+ });
+ }
+ });
+ } else {
+ this.showRightSideInfo(true, false, false, false);
+ }
+ }
+ /** Get confirmation Before Deleting the Link in Topology @private */
+ private getDeleteLinkConfirmation(d: Tick): void {
+ const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
+ modalRef.componentInstance.topologyType = 'Delete';
+ modalRef.componentInstance.topologyname = this.translateService.instant('PAGE.TOPOLOGY.LINK');
+ modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.LINK';
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.doubleClickLink(d);
+ }
+ }).catch();
+ }
+ /** Events handles when Double Click to Delete the link @private */
+ private doubleClickLink(d: Tick): void {
+ let getID: string = '';
+ if (d.target.type === 'vld') {
+ getID = d.target.id;
+ } else if (d.source.type === 'vld') {
+ getID = d.source.id;
+ }
+ this.nodes.forEach((res: COMPOSERNODES) => {
+ if (res.id === getID) {
+ if (this.nsData.vld !== undefined) {
+ this.nsData.vld.forEach((vldresult: VLD) => {
+ if (vldresult.id === getID) {
+ delete vldresult['vnfd-connection-point-ref'];
+ }
+ });
+ }
+ }
+ });
+ this.putType = 'linkdelete';
+ this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
+ }
+ /** Get confirmation Before Deleting the Node in Topology @private */
+ private getDeleteConfirmation(d: COMPOSERNODES): void {
+ const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
+ modalRef.componentInstance.topologyType = 'Delete';
+ modalRef.componentInstance.topologyname = d.name;
+ if (d.type === 'vld') {
+ modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.VIRTUALLINK';
+ } else if (d.type === 'vnfd') {
+ modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.VNF';
+ } else if (d.type === 'ns') {
+ modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.CONNECTIONPOINT';
+ }
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.doubleClick(d);
+ }
+ }).catch();
+ }
+ /** Events handles when Double Click to Delete @private */
+ private doubleClick(d: COMPOSERNODES): void {
+ const deletedNode: COMPOSERNODES = d;
+ this.nodes.forEach((res: COMPOSERNODES) => {
+ if (res.id === d.id) {
+ if (deletedNode.type === 'vld') {
+ const pos: number = this.nsData.vld.map((e: VLD) => { return e.id; }).indexOf(d.id);
+ this.nsData.vld.splice(pos, 1);
+ this.putType = 'nsddelete';
+ } else if (deletedNode.type === 'vnfd') {
+ const constituentVNFD: string[] = [];
+ if (this.nsData['constituent-vnfd'] !== undefined) {
+ this.nsData['constituent-vnfd'].forEach((ref: CONSTITUENTVNFD) => {
+ constituentVNFD.push(ref['vnfd-id-ref'] + ':' + ref['member-vnf-index']);
+ });
+ }
+ const pos: number = constituentVNFD.map((e: string) => { return e; }).indexOf(d.id);
+ this.nsData['constituent-vnfd'].splice(pos, 1);
+ const getCP: string[] = d.id.split(':');
+ const memberVnfIndexRef: number = +getCP[1];
+ const vnfdIDRef: string = getCP[0];
+ if (this.nsData.vld !== undefined) {
+ this.nsData.vld.forEach((resf: VLD) => {
+ if (resf['vnfd-connection-point-ref'] !== undefined) {
+ resf['vnfd-connection-point-ref'].forEach((connectionPoint: VNFDCONNECTIONPOINTREF, index: number) => {
+ if (+connectionPoint['member-vnf-index-ref'] === memberVnfIndexRef && connectionPoint['vnfd-id-ref'] === vnfdIDRef) {
+ resf['vnfd-connection-point-ref'].splice(index, 1);
+ }
+ });
+ }
+ });
+ }
+ this.putType = 'vnfddelete';
+ } else if (deletedNode.type === 'ns') {
+ const getCP: string[] = d.selectorId.split('-osm-');
+ const memberVnfIndexRef: number = d.nodeIndex;
+ const vnfdIDRef: string = getCP[getCP.length - 1];
+ if (this.nsData.vld !== undefined) {
+ this.nsData.vld.forEach((resf: VLD) => {
+ if (resf['vnfd-connection-point-ref'] !== undefined && resf.id === vnfdIDRef) {
+ resf['vnfd-connection-point-ref'].forEach((connectionPoint: VNFDCONNECTIONPOINTREF, index: number) => {
+ if (connectionPoint['member-vnf-index-ref'] === memberVnfIndexRef) {
+ resf['vnfd-connection-point-ref'].splice(index, 1);
+ }
+ });
+ }
+ });
+ }
+ this.putType = 'nsdelete';
+ }
+ this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
+ }
+ });
+ }
+ /** Key press event @private */
+ private keyDown(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ if (this.lastKeyDown !== -1) { return; }
+ this.lastKeyDown = d3.event.keyCode;
+ if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) {
+ this.gvlNode.call(d3.drag()
+ .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)
+ );
+ this.gvnfdNode.call(d3.drag()
+ .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)
+ );
+ this.gcpNode.call(d3.drag()
+ .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)
+ );
+ this.svg.classed('ctrl', true);
+ }
+ }
+ /** Key realse event @private */
+ private keyUp(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.lastKeyDown = -1;
+ if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) {
+ this.gvlNode.on('.drag', null);
+ this.gvnfdNode.on('.drag', null);
+ this.gcpNode.on('.drag', null);
+ this.svg.classed('ctrl', false);
+ }
+ }
+ /** Events handles when dragstarted @private */
+ private dragstarted(d: COMPOSERNODES): void {
+ d.fx = d.x;
+ d.fy = d.y;
+ }
+ /** Events handles when dragged @private */
+ private dragged(d: COMPOSERNODES): void {
+ d.fx = d.x = d3.event.x;
+ d.fy = d.y = d3.event.y;
+ }
+ /** Events handles when dragended @private */
+ private dragended(d: COMPOSERNODES): void {
+ if (this.forceSimulationActive) {
+ d.fx = null;
+ d.fy = null;
+ } else {
+ d.fx = d.x;
+ d.fy = d.y;
+ this.forceSimulationActive = false;
+ }
+ }
+ /** Events handles when node double click @private */
+ private onNodedblClickToggleSidebar(): void {
+ this.sideBarOpened = false;
+ }
+ /** Events handles when node single click @private */
+ private onNodeClickToggleSidebar(): void {
+ this.sideBarOpened = true;
+ }
+}
diff --git a/src/app/packages/ns-packages/vnf-composer/VNFComposerComponent.html b/src/app/packages/ns-packages/vnf-composer/VNFComposerComponent.html
new file mode 100644
index 0000000..2aa8f12
--- /dev/null
+++ b/src/app/packages/ns-packages/vnf-composer/VNFComposerComponent.html
@@ -0,0 +1,312 @@
+<!--
+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: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+-->
+<ng-sidebar-container class="vnf-topology-sidebar-container">
+ <!-- A sidebar -->
+ <ng-sidebar [(opened)]="sideBarOpened" position="left">
+ <div class="sidebar-header">
+ <span class="topology_title" *ngIf="showRightSideInfo === 'vnfdInfo'">{{'PAGE.TOPOLOGY.VNFD' | translate}}</span>
+ <span class="topology_title" *ngIf="showRightSideInfo === 'vduInfo'">{{'PAGE.TOPOLOGY.VDU' | translate}}</span>
+ <span class="topology_title" *ngIf="showRightSideInfo === 'intvlInfo'">{{'PAGE.TOPOLOGY.VIRTUALLINK' | translate}}</span>
+ <span class="topology_title" *ngIf="showRightSideInfo === 'cpInfo'">{{'PAGE.TOPOLOGY.CONNECTIONPOINT' | translate}}</span>
+ <span class="topology_title" *ngIf="showRightSideInfo === 'intcpInfo'">{{'PAGE.TOPOLOGY.INTCONNECTIONPOINT' | translate}}</span>
+ <button (click)="toggleSidebar()" class="close" type="button">
+ <i class="fas fa-times-circle text-danger" aria-hidden="true"></i>
+ </button>
+ </div>
+ <div class="sidebar-body">
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 mb-2" *ngIf="showRightSideInfo === 'vnfdInfo'">
+ <div class="row">
+ <div class="col-12 p-0">
+ <form>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'SHORTNAME' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'SHORTNAME' | translate }}" name="shortName"
+ [(ngModel)]="vnfdInfo.shortName">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'DESCRIPTION' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'DESCRIPTION' | translate }}" name="description"
+ [(ngModel)]="vnfdInfo.description">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'VERSION' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'VERSION' | translate }}" name="version"
+ [(ngModel)]="vnfdInfo.version">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'ID' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'ID' | translate }}" name="id"
+ [(ngModel)]="vnfdInfo.id">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'NAME' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'NAME' | translate }}" name="name"
+ [(ngModel)]="vnfdInfo.name">
+ </div>
+ </div>
+ <button type="button" class="btn btn-primary btn-sm pull-right" (click)="saveVNFD()" placement="top" ngbTooltip="Save">
+ <i class="fas fa-save"></i> {{'SAVE' | translate}}
+ </button>
+ </form>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 mb-2" *ngIf="showRightSideInfo === 'vduInfo'">
+ <div class="row">
+ <div class="col-12 p-0">
+ <form>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'COUNT' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'COUNT' | translate }}" name="count"
+ [(ngModel)]="vduInfo.count">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'DESCRIPTION' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'DESCRIPTION' | translate }}" name="description"
+ [(ngModel)]="vduInfo.description">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'IMAGE' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'IMAGE' | translate }}" name="image"
+ [(ngModel)]="vduInfo.image">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'ID' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'ID' | translate }}" name="id"
+ [(ngModel)]="vduInfo.id">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'NAME' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'NAME' | translate }}" name="name"
+ [(ngModel)]="vduInfo.name">
+ </div>
+ </div>
+ <button type="button" class="btn btn-primary btn-sm pull-right" (click)="saveVDU(vduInfo.id)" placement="top"
+ ngbTooltip="Save">
+ <i class="fas fa-save"></i> {{'SAVE' | translate}}
+ </button>
+ </form>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 mb-2" *ngIf="showRightSideInfo === 'intvlInfo'">
+ <div class="row">
+ <div class="col-12 p-0">
+ <form>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'SHORTNAME' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'SHORTNAME' | translate }}" name="shortName"
+ [(ngModel)]="intvlInfo.shortName">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'NAME' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'NAME' | translate }}" name="name"
+ [(ngModel)]="intvlInfo.name">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'TYPE' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'TYPE' | translate }}" name="type"
+ [(ngModel)]="intvlInfo.type">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'IPPROFILEREF' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'IP Profile Ref' | translate }}"
+ name="ipProfileRef" [(ngModel)]="intvlInfo.ipProfileRef">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'ID' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'ID' | translate }}" name="id"
+ [(ngModel)]="intvlInfo.id">
+ </div>
+ </div>
+ <button type="button" class="btn btn-primary btn-sm pull-right" (click)="saveIntVL(intvlInfo.id)" placement="top"
+ ngbTooltip="Save">
+ <i class="fas fa-save"></i> {{'SAVE' | translate}}
+ </button>
+ </form>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 mb-2" *ngIf="showRightSideInfo === 'cpInfo'">
+ <div class="row">
+ <div class="col-12 p-0">
+ <form>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'TYPE' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'TYPE' | translate }}" name="type"
+ [(ngModel)]="cpInfo.type">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'NAME' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'NAME' | translate }}" name="name"
+ [(ngModel)]="cpInfo.name">
+ </div>
+ </div>
+ <button type="button" class="btn btn-primary btn-sm pull-right" (click)="saveCP(cpInfo.name)" placement="top"
+ ngbTooltip="Save">
+ <i class="fas fa-save"></i> {{'SAVE' | translate}}
+ </button>
+ </form>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 mb-2" *ngIf="showRightSideInfo === 'intcpInfo'">
+ <div class="row">
+ <div class="col-12 p-0">
+ <form>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'SHORTNAME' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" name="shortName" [(ngModel)]="intcpInfo.shortName" disabled>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'TYPE' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" name="type" [(ngModel)]="intcpInfo.type" disabled>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'ID' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" name="id" [(ngModel)]="intcpInfo.id" disabled>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'NAME' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" name="name" [(ngModel)]="intcpInfo.name" disabled>
+ </div>
+ </div>
+ </form>
+ </div>
+ </div>
+ </div>
+ </div>
+ </ng-sidebar>
+ <!-- Page content -->
+ <div ng-sidebar-content>
+ <button (click)="toggleSidebar()" class="btn btn-default" placement="right" ngbTooltip="{{'OPEN' | translate }}">
+ <i class="fa fa-arrow-right detail-sidebar" aria-hidden="true"></i>
+ </button>
+ </div>
+</ng-sidebar-container>
+<div class="container-fluid text-dark">
+ <div class="row bg-white vnf-composer-form">
+ <div class="col-xs-3 col-sm-3 col-md-3 col-lg-3 pl-0 px-0">
+ <div class="row">
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 mb-2">
+ <fieldset class="p-2">
+ <legend class="element-legend">
+ {{'PAGE.TOPOLOGY.SELECTELEMENT' | translate}}
+ </legend>
+ <ul class="list-group list-group-flush dragable">
+ <li class="list-group-item" draggable="true" (dragstart)="drag($event)" id="vdu">
+ <img src="assets/images/VDU.svg" class="vnf-svg" draggable="false"/>
+ <span class="span-overflow-text font-weight-bold">{{'PAGE.TOPOLOGY.VDU' | translate}}</span>
+ <span class="drag-icon pull-right"><i class="fas fa-arrows-alt"></i></span>
+ </li>
+ <li class="list-group-item" draggable="true" (dragstart)="drag($event)" id="cp">
+ <img src="assets/images/CP-VNF.svg" class="vnf-svg" draggable="false"/>
+ <span class="span-overflow-text font-weight-bold">{{'PAGE.TOPOLOGY.CP' | translate}}</span>
+ <span class="drag-icon pull-right"><i class="fas fa-arrows-alt"></i></span>
+ </li>
+ <li class="list-group-item" draggable="true" (dragstart)="drag($event)" id="intvl">
+ <img src="assets/images/INTVL.svg" class="vnf-svg" draggable="false"/>
+ <span class="span-overflow-text font-weight-bold">{{'PAGE.TOPOLOGY.INTVL' | translate}}</span>
+ <span class="drag-icon pull-right"><i class="fas fa-arrows-alt"></i></span>
+ </li>
+ </ul>
+ </fieldset>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-9 col-sm-9 col-md-9 col-lg-9">
+ <div class="row">
+ <div class="col-xs-6 col-sm-6 col-md-6 col-lg-6 pl-0">
+ <div class="btn-group list" role="group" aria-label="Basic example">
+ <button type="button" class="btn btn-primary topology-btn" (click)="onFreeze()"
+ [class.pinned]="classApplied" placement="top" container="body" ngbTooltip="{{(classApplied ? 'UNFREEZE' : 'FREEZE') | translate}}">
+ <i class="fas fa-thumbtack"></i>
+ </button>
+ <button type="button" class="btn btn-primary topology-btn" (click)="onEdit()" placement="top"
+ container="body" ngbTooltip="{{'EDIT' | translate}}">
+ <i class="fas fa-edit"></i>
+ </button>
+ <button type="button" class="btn btn-primary topology-btn" (click)="showInfo()" placement="top"
+ container="body" ngbTooltip="{{'PAGE.TOPOLOGY.HELP' | translate}}">
+ <i class="fas fa-info"></i>
+ </button>
+ </div>
+ </div>
+ <div class="col-xs-6 col-sm-6 col-md-6 col-lg-6 text-right pr-0 badgegroup">
+ <span class="badge badge-primary badge-pill bg-white text-body font-weight-bold">
+ <img src="assets/images/VDU.svg" class="vnf-svg" draggable="false"/>
+ <br>{{'PAGE.TOPOLOGY.VDU' | translate}}</span>
+ <span class="badge badge-primary badge-pill bg-white text-body font-weight-bold">
+ <img src="assets/images/CP-VNF.svg" class="vnf-svg" draggable="false"/>
+ <br>{{'PAGE.TOPOLOGY.CP' | translate}}</span>
+ <span class="badge badge-primary badge-pill bg-white text-body font-weight-bold">
+ <img src="assets/images/INTVL.svg" class="vnf-svg" draggable="false"/>
+ <br>{{'PAGE.TOPOLOGY.INTVL' | translate}}</span>
+ <span class="badge badge-primary badge-pill bg-white text-body font-weight-bold">
+ <img src="assets/images/INTCP.svg" class="vnf-svg" draggable="false"/>
+ <br>{{'PAGE.TOPOLOGY.INTCP' | translate}}</span>
+ </div>
+ </div>
+ <div class="row border-all">
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 svg-container">
+ <svg preserveAspectRatio="xMidYMin slice" (drop)="drop($event)" (dragover)="allowDrop($event)"
+ id="graphContainer" #graphContainer>
+ </svg>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/packages/ns-packages/vnf-composer/VNFComposerComponent.scss b/src/app/packages/ns-packages/vnf-composer/VNFComposerComponent.scss
new file mode 100644
index 0000000..4473c67
--- /dev/null
+++ b/src/app/packages/ns-packages/vnf-composer/VNFComposerComponent.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: 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/packages/ns-packages/vnf-composer/VNFComposerComponent.ts b/src/app/packages/ns-packages/vnf-composer/VNFComposerComponent.ts
new file mode 100644
index 0000000..21aad71
--- /dev/null
+++ b/src/app/packages/ns-packages/vnf-composer/VNFComposerComponent.ts
@@ -0,0 +1,1020 @@
+/*
+ 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: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+ */
+/**
+ * @file VNFComposerComponent
+ */
+import { HttpHeaders } from '@angular/common/http';
+import { Component, ElementRef, Injector, ViewChild, ViewEncapsulation } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { APIURLHEADER, ERRORDATA, GETAPIURLHEADER, MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { ConfirmationTopologyComponent } from 'ConfirmationTopology';
+import * as d3 from 'd3';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import * as HttpStatus from 'http-status-codes';
+import * as jsyaml from 'js-yaml';
+import { RestService } from 'RestService';
+import { SharedService } from 'SharedService';
+import { isNullOrUndefined } from 'util';
+import {
+ COMPOSERNODES, CONNECTIONPOINT, GRAPHDETAILS, InternalVLD, Tick, TickPath,
+ VDU, VDUInternalConnectionPoint, VLDInternalConnectionPoint, VNFDInterface, VNFDNODE
+} from 'VNFDModel';
+
+/**
+ * Creating component
+ * @Component takes VNFComposerComponent.html as template url
+ */
+@Component({
+ templateUrl: './VNFComposerComponent.html',
+ styleUrls: ['./VNFComposerComponent.scss'],
+ encapsulation: ViewEncapsulation.None
+})
+/** Exporting a class @exports VNFComposerComponent */
+export class VNFComposerComponent {
+ /** To inject services @public */
+ public injector: Injector;
+ /** View child contains graphContainer ref @public */
+ @ViewChild('graphContainer', { static: true }) public graphContainer: ElementRef;
+ /** dataService to pass the data from one component to another @public */
+ public dataService: DataService;
+ /** random number count @public */
+ public randomNumberLength: number;
+ /** Contains the vnfd information @public */
+ public vnfList: string[] = [];
+ /** Contains node type @public */
+ public nodeTypeRef: string;
+ /** Contains VNFD Information @public */
+ public vnfdInfo: VNFDNODE = { shortName: '', description: '', version: '', id: '', name: '' };
+ /** Contains right panel box information @public */
+ public showRightSideInfo: string = '';
+ /** Add the fixed class for the freeze @public */
+ public fixedClass: string = 'fixed';
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = true;
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+ /** Assign the forcesimulation active @public */
+ public forceSimulationActive: boolean = false;
+ /** Assign pinned class for the button when freezed @public */
+ public classApplied: boolean = false;
+ /** Contains sidebar open status @public */
+ public sideBarOpened: boolean = false;
+
+ /** Contains SVG attributes @private */
+ // tslint:disable-next-line:no-any
+ private svg: any;
+ /** Contains forced node animations @private */
+ // tslint:disable-next-line:no-any
+ private force: any;
+ /** Contains the Drag line */
+ // tslint:disable-next-line: no-any
+ private dragLine: any;
+ /** Contains id of the node @private */
+ private identifier: string;
+ /** Contains path information of the node */
+ // tslint:disable-next-line:no-any
+ private path: any;
+ /** Contains node network @private */
+ // tslint:disable-next-line:no-any
+ private network: any;
+ /** Contains node network @private */
+ // tslint:disable-next-line:no-any
+ private virutualDeploymentUnit: any;
+ /** Contains node connectionPoint @private */
+ // tslint:disable-next-line:no-any
+ private connectionPoint: any;
+ /** Contains node intConnectionPoint @private */
+ // tslint:disable-next-line:no-any
+ private intConnectionPoint: any;
+ /** Contains the node information @private */
+ private nodes: VNFDNODE[] = [];
+ /** Contains the link information of nodes @private */
+ private links: {}[] = [];
+ /** Instance of the rest service @private */
+ private restService: RestService;
+ /** Service holds the router information @private */
+ private router: Router;
+ /** Service contails all the shared service information @private */
+ private sharedService: SharedService;
+ /** Holds teh instance of AuthService class of type AuthService @private */
+ private activatedRoute: ActivatedRoute;
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+ /** Controls the header form @private */
+ private headers: HttpHeaders;
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+ /** Rendered nodes represent network @private */
+ // tslint:disable-next-line:no-any
+ private gNetwork: any;
+ /** Rendered nodes represent VDU @private */
+ // tslint:disable-next-line:no-any
+ private gVirutualDeploymentUnit: any;
+ /** Rendered nodes represent connection point @private */
+ // tslint:disable-next-line:no-any
+ private gConnectionPoint: any;
+ /** Rendered nodes represent internal connection point @private */
+ // tslint:disable-next-line:no-any
+ private gIntConnectionPoint: any;
+ /** Contains all the information about VNF Details @private */
+ private vnfdPackageDetails: VNFDNODE;
+ /** Conatins mousedown action @private */
+ private mousedownNode: COMPOSERNODES = null;
+ /** Conatins mouseup action @private */
+ private mouseupNode: COMPOSERNODES = null;
+ /** Conatins current Selection node action @private */
+ private currentSelectedNode: COMPOSERNODES = null;
+ /** Add the activeNode for the selected @private */
+ private activeNode: string = 'active';
+ /** Contains lastkeypressed instance @private */
+ private lastKeyDown: number = -1;
+ /** Contains VDU Information @private */
+ private vduInfo: VDU;
+ /** Contains Internal VL Information @private */
+ private intvlInfo: InternalVLD;
+ /** Contains Connection Point Information @private */
+ private cpInfo: CONNECTIONPOINT;
+ /** Contains Internal Connection Point Information @private */
+ private intcpInfo: VLDInternalConnectionPoint;
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.dataService = this.injector.get(DataService);
+ this.router = this.injector.get(Router);
+ this.activatedRoute = this.injector.get(ActivatedRoute);
+ this.notifierService = this.injector.get(NotifierService);
+ this.translateService = this.injector.get(TranslateService);
+ this.sharedService = this.injector.get(SharedService);
+ this.modalService = this.injector.get(NgbModal);
+ }
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ // tslint:disable-next-line:no-backbone-get-set-outside-model
+ this.identifier = this.activatedRoute.snapshot.paramMap.get('id');
+ this.generateData();
+ this.headers = new HttpHeaders({
+ 'Content-Type': 'application/zip',
+ Accept: 'application/json',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ }
+
+ /** Prepare information for node creation of VNFD @public */
+ public generateData(): void {
+ this.nodes = []; this.links = []; this.vnfdPackageDetails = null;
+ this.showRightSideInfo = 'vnfdInfo';
+ const httpOptions: GETAPIURLHEADER = {
+ headers: new HttpHeaders({
+ 'Content-Type': 'application/zip',
+ Accept: 'text/plain',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ }),
+ responseType: 'text'
+ };
+ this.restService.getResource(environment.VNFPACKAGES_URL + '/' + this.identifier + '/vnfd', httpOptions)
+ .subscribe((vnfdPackageDetails: VNFDNODE) => {
+ try {
+ const getJson: string = jsyaml.load(vnfdPackageDetails.toString(), { json: true });
+ if (getJson.hasOwnProperty('vnfd-catalog')) {
+ this.vnfdPackageDetails = getJson['vnfd-catalog'].vnfd[0];
+ } else if (getJson.hasOwnProperty('vnfd:vnfd-catalog')) {
+ this.vnfdPackageDetails = getJson['vnfd:vnfd-catalog'].vnfd[0];
+ } else if (getJson.hasOwnProperty('vnfd')) {
+ // tslint:disable-next-line: no-string-literal
+ this.vnfdPackageDetails = getJson['vnfd'][0];
+ }
+ this.generateCPPoint(this.vnfdPackageDetails);
+ this.generateVDU(this.vnfdPackageDetails);
+ this.generateInternalVLD(this.vnfdPackageDetails);
+ this.generateInternalCP(this.vnfdPackageDetails);
+ this.generateIntVLCPLinks(this.vnfdPackageDetails);
+ this.generateVDUCPLinks(this.vnfdPackageDetails);
+ this.createNode(this.nodes);
+ this.vnfdInfo.shortName = this.vnfdPackageDetails['short-name'];
+ this.vnfdInfo.description = this.vnfdPackageDetails.description;
+ this.vnfdInfo.version = this.vnfdPackageDetails.version;
+ this.vnfdInfo.id = this.vnfdPackageDetails.id;
+ this.vnfdInfo.name = this.vnfdPackageDetails.name;
+ } catch (e) {
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ }
+ this.isLoadingResults = false;
+ }, (error: ERRORDATA) => {
+ error.error = typeof error.error === 'string' ? jsyaml.load(error.error) : error.error;
+ if (error.error.status === HttpStatus.NOT_FOUND || error.error.status === HttpStatus.UNAUTHORIZED) {
+ this.router.navigateByUrl('404', { skipLocationChange: true }).catch();
+ } else {
+ this.restService.handleError(error, 'get');
+ }
+ this.isLoadingResults = false;
+ this.showRightSideInfo = '';
+ });
+ }
+ /** Events handles at drag on D3 region @public */
+ // tslint:disable-next-line:no-any
+ public drag(ev: any): void {
+ ev.dataTransfer.setData('text', ev.target.id);
+ }
+ /** Events handles drop at D3 region @public */
+ public drop(ev: DragEvent): void {
+ ev.preventDefault();
+ this.nodeTypeRef = ev.dataTransfer.getData('text');
+ if (this.nodeTypeRef === 'vdu') {
+ this.svg.selectAll('*').remove();
+ this.vduDropCompose();
+ } else if (this.nodeTypeRef === 'cp') {
+ this.svg.selectAll('*').remove();
+ this.cpDropCompose();
+ } else if (this.nodeTypeRef === 'intvl') {
+ this.svg.selectAll('*').remove();
+ this.intvlDropCompose();
+ }
+ }
+ /** Events handles allow drop on D3 region @public */
+ public allowDrop(ev: DragEvent): void {
+ ev.preventDefault();
+ }
+ /** Generate and list CP points @public */
+ public generateCPPoint(vnfdPackageDetails: VNFDNODE): void {
+ if (vnfdPackageDetails['connection-point'] !== undefined) {
+ vnfdPackageDetails['connection-point'].forEach((cp: CONNECTIONPOINT) => {
+ this.nodes.push({ id: cp.name, nodeTypeRef: 'cp', name: cp.name, type: cp.type });
+ });
+ }
+ }
+ /** Generate and list VDU @public */
+ public generateVDU(vnfdPackageDetails: VNFDNODE): void {
+ if (vnfdPackageDetails.vdu !== undefined) {
+ vnfdPackageDetails.vdu.forEach((vdu: VDU) => {
+ this.nodes.push({
+ id: vdu.name, nodeTypeRef: 'vdu', 'cloud-init-file': vdu['cloud-init-file'], count: vdu.count, description: vdu.description,
+ vduID: vdu.id, name: vdu.name, interface: vdu.interface, 'vm-flavor': vdu['vm-flavor']
+ });
+ });
+ }
+ }
+ /** Generate and list Internal VLD @public */
+ public generateInternalVLD(vnfdPackageDetails: VNFDNODE): void {
+ if (vnfdPackageDetails['internal-vld'] !== undefined) {
+ vnfdPackageDetails['internal-vld'].forEach((internalVLD: InternalVLD) => {
+ this.nodes.push({
+ id: internalVLD.name, nodeTypeRef: 'intvl', intVLID: internalVLD.id,
+ 'internal-connection-point': internalVLD['internal-connection-point'],
+ 'ip-profile-ref': internalVLD['ip-profile-ref'], name: internalVLD.name, 'short-name': internalVLD['short-name'],
+ type: internalVLD.type
+ });
+ });
+ }
+ }
+ /** Generate and list Internal CP @public */
+ public generateInternalCP(vnfdPackageDetails: VNFDNODE): void {
+ if (vnfdPackageDetails.vdu !== undefined) {
+ vnfdPackageDetails.vdu.forEach((intCP: VDUInternalConnectionPoint) => {
+ if (intCP['internal-connection-point'] !== undefined) {
+ intCP['internal-connection-point'].forEach((internalCP: VDUInternalConnectionPoint) => {
+ this.nodes.push({
+ id: internalCP.name, nodeTypeRef: 'intcp', name: internalCP.name,
+ 'short-name': internalCP['short-name'], type: internalCP.type
+ });
+ });
+ }
+ });
+ }
+ }
+ /** Generate VDU External and Internal CP Links @public */
+ public generateVDUCPLinks(vnfdPackageDetails: VNFDNODE): void {
+ if (vnfdPackageDetails.vdu !== undefined) {
+ vnfdPackageDetails.vdu.forEach((vdu: VDU) => {
+ const vduLink: string = vdu.name;
+ if (vdu.interface !== undefined) {
+ vdu.interface.forEach((interfaceDetails: VNFDInterface) => {
+ if (interfaceDetails['external-connection-point-ref'] !== undefined) {
+ this.links.push({ source: vduLink, target: interfaceDetails['external-connection-point-ref'] });
+ }
+ if (interfaceDetails['internal-connection-point-ref'] !== undefined) {
+ this.links.push({ source: vduLink, target: interfaceDetails['internal-connection-point-ref'] });
+ }
+ });
+ }
+ });
+ }
+ }
+ /** Generate Network/VLD/Internal VirtualLink, CP Links @public */
+ public generateIntVLCPLinks(vnfdPackageDetails: VNFDNODE): void {
+ if (vnfdPackageDetails['internal-vld'] !== undefined) {
+ vnfdPackageDetails['internal-vld'].forEach((internalVLD: InternalVLD) => {
+ const vldName: string = internalVLD.name;
+ if (internalVLD['internal-connection-point'] !== undefined) {
+ internalVLD['internal-connection-point'].forEach((intCP: VLDInternalConnectionPoint) => {
+ this.links.push({ source: vldName, target: intCP['id-ref'] });
+ });
+ }
+ });
+ }
+ }
+ /** VNFD details can be saved on users inputs @public */
+ public saveVNFD(): void {
+ this.vnfdPackageDetails['short-name'] = this.vnfdInfo.shortName;
+ this.vnfdPackageDetails.description = this.vnfdInfo.description;
+ this.vnfdPackageDetails.version = this.vnfdInfo.version;
+ this.vnfdPackageDetails.id = this.vnfdInfo.id;
+ this.vnfdPackageDetails.name = this.vnfdInfo.name;
+ this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
+ delete this.vnfdPackageDetails.shortName;
+ }
+ /** VDU details can be saved on users inputs @public */
+ public saveVDU(vduID: string): void {
+ this.vnfdPackageDetails.vdu.forEach((ref: VDU) => {
+ if (ref.id === vduID) {
+ ref.count = this.vduInfo.count;
+ ref.description = this.vduInfo.description;
+ ref.image = this.vduInfo.image;
+ ref.id = this.vduInfo.id;
+ ref.name = this.vduInfo.name;
+ }
+ });
+ this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
+ }
+ /** IntVL details can be saved on users inputs @public */
+ public saveIntVL(intVLID: string): void {
+ this.vnfdPackageDetails['internal-vld'].forEach((ref: InternalVLD) => {
+ if (ref.id === intVLID) {
+ ref['short-name'] = this.intvlInfo.shortName;
+ ref.name = this.intvlInfo.name;
+ ref.type = this.intvlInfo.type;
+ ref['ip-profile-ref'] = !isNullOrUndefined(this.intvlInfo.ipProfileRef) ? this.intvlInfo.ipProfileRef : '';
+ ref.id = this.intvlInfo.id;
+ delete ref.shortName;
+ delete ref.ipProfileRef;
+ }
+ });
+ this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
+ }
+ /** IntVL details can be saved on users inputs @public */
+ public saveCP(cpName: string): void {
+ this.vnfdPackageDetails['connection-point'].forEach((ref: CONNECTIONPOINT) => {
+ if (ref.name === cpName) {
+ if (!isNullOrUndefined(this.cpInfo.type)) {
+ ref.type = this.cpInfo.type;
+ }
+ ref.name = this.cpInfo.name;
+ }
+ });
+ this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
+ }
+ /** Edit topology @public */
+ public onEdit(): void {
+ this.router.navigate(['/packages/vnf/edit/', this.identifier]).catch();
+ }
+ /** Show Info @public */
+ public showInfo(): void {
+ const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
+ modalRef.componentInstance.topologyType = 'Info';
+ modalRef.componentInstance.topologytitle = this.translateService.instant('PAGE.TOPOLOGY.INFO');
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ // empty
+ }
+ }).catch();
+ }
+ /** Event to freeze the animation @public */
+ public onFreeze(): void {
+ this.classApplied = !this.classApplied;
+ const alreadyFixedIsActive: boolean = d3.select('svg#graphContainer').classed(this.fixedClass);
+ d3.select('svg#graphContainer').classed(this.fixedClass, !alreadyFixedIsActive);
+ if (alreadyFixedIsActive) {
+ this.force.stop();
+ }
+ this.forceSimulationActive = alreadyFixedIsActive;
+ this.nodes.forEach((d: COMPOSERNODES) => {
+ d.fx = (alreadyFixedIsActive) ? null : d.x;
+ d.fy = (alreadyFixedIsActive) ? null : d.y;
+ });
+ if (alreadyFixedIsActive) {
+ this.force.restart();
+ }
+ }
+ /** Events handles when dragended @public */
+ public toggleSidebar(): void {
+ this.sideBarOpened = !this.sideBarOpened;
+ this.deselectAllNodes();
+ this.showRightSideInfo = 'vnfdInfo';
+ }
+ /** Get the default Configuration of containers @private */
+ private getGraphContainerAttr(): GRAPHDETAILS {
+ return {
+ width: 700,
+ height: 400,
+ nodeHeight: 50,
+ nodeWidth: 35,
+ textX: -35,
+ textY: 30,
+ radius: 5,
+ distance: 50,
+ strength: -500,
+ forcex: 2,
+ forcey: 2,
+ sourcePaddingYes: 17,
+ sourcePaddingNo: 12,
+ targetPaddingYes: 4,
+ targetPaddingNo: 3,
+ alphaTarget: 0.3,
+ imageX: -25,
+ imageY: -25,
+ shiftKeyCode: 17
+ };
+ }
+ /** Node is created and render at D3 region @private */
+ private createNode(nodes: VNFDNODE[]): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ d3.selectAll('svg#graphContainer > *').remove();
+ d3.select(window).on('keydown', () => { this.keyDown(); });
+ d3.select(window).on('keyup', () => { this.keyUp(); });
+ this.svg = d3.select('#graphContainer').attr('oncontextmenu', 'return false;').attr('width', graphContainerAttr.width)
+ .attr('height', graphContainerAttr.height).on('mousemove', () => { this.mousemove(); });
+ this.force = d3.forceSimulation()
+ .force('link', d3.forceLink().id((d: TickPath) => d.id).distance(graphContainerAttr.distance))
+ .force('charge', d3.forceManyBody().strength(graphContainerAttr.strength))
+ .force('x', d3.forceX(graphContainerAttr.width / graphContainerAttr.forcex))
+ .force('y', d3.forceY(graphContainerAttr.height / graphContainerAttr.forcey))
+ .on('tick', () => { this.tick(); });
+ this.path = this.svg.append('svg:g').selectAll('path');
+ this.dragLine = this.svg.append('svg:path').attr('class', 'link dragline hidden').attr('d', 'M0,0L0,0');
+ this.network = this.svg.append('svg:g').selectAll('network');
+ this.virutualDeploymentUnit = this.svg.append('svg:g').selectAll('virutualDeploymentUnit');
+ this.connectionPoint = this.svg.append('svg:g').selectAll('connectionPoint');
+ this.intConnectionPoint = this.svg.append('svg:g').selectAll('intConnectionPoint');
+ this.restart(nodes);
+ }
+ /** Update force layout (called automatically each iteration) @private */
+ private tick(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.path.attr('d', (d: Tick) => {
+ const deltaX: number = d.target.x - d.source.x; const deltaY: number = d.target.y - d.source.y;
+ const dist: number = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
+ const normX: number = deltaX / dist; const normY: number = deltaY / dist;
+ const sourcePadding: number = d.left ? graphContainerAttr.sourcePaddingYes : graphContainerAttr.sourcePaddingNo;
+ const targetPadding: number = d.right ? graphContainerAttr.targetPaddingYes : graphContainerAttr.targetPaddingNo;
+ const sourceX: number = d.source.x + (sourcePadding * normX); const sourceY: number = d.source.y + (sourcePadding * normY);
+ const targetX: number = d.target.x - (targetPadding * normX); const targetY: number = d.target.y - (targetPadding * normY);
+ return `M${sourceX},${sourceY}L${targetX},${targetY}`;
+ }).on('dblclick', (d: Tick) => { this.getDeleteLinkConfirmation(d); });
+ this.network.attr('transform', (d: TickPath) => `translate(${d.x},${d.y})`);
+ this.virutualDeploymentUnit.attr('transform', (d: TickPath) => `translate(${d.x},${d.y})`);
+ this.connectionPoint.attr('transform', (d: TickPath) => `translate(${d.x},${d.y})`);
+ this.intConnectionPoint.attr('transform', (d: TickPath) => `translate(${d.x},${d.y})`);
+ }
+ /** Update graph (called when needed) @private */
+ private restart(nodes: VNFDNODE[]): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.path = this.path.data(this.links);
+ const cpNodes: {}[] = []; const vduNodes: {}[] = []; const vlNodes: {}[] = []; const intcpNodes: {}[] = []; //Nodes are known by id
+ nodes.forEach((nodeList: VNFDNODE) => {
+ if (nodeList.nodeTypeRef === 'cp') { cpNodes.push(nodeList); }
+ else if (nodeList.nodeTypeRef === 'vdu') { vduNodes.push(nodeList); }
+ else if (nodeList.nodeTypeRef === 'intvl') { vlNodes.push(nodeList); }
+ else if (nodeList.nodeTypeRef === 'intcp') { intcpNodes.push(nodeList); }
+ });
+ this.network = this.network.data(vlNodes, (d: { id: number }) => d.id);
+ this.virutualDeploymentUnit = this.virutualDeploymentUnit.data(vduNodes, (d: { id: number }) => d.id);
+ this.connectionPoint = this.connectionPoint.data(cpNodes, (d: { id: number }) => d.id);
+ this.intConnectionPoint = this.intConnectionPoint.data(intcpNodes, (d: { id: number }) => d.id);
+ this.resetAndCreateNodes();
+ this.force.nodes(nodes).force('link').links(this.links); //Set the graph in motion
+ this.force.alphaTarget(graphContainerAttr.alphaTarget).restart();
+ }
+ /** Rest and create nodes @private */
+ private resetAndCreateNodes(): void {
+ this.path.exit().remove();
+ this.network.exit().remove();
+ this.virutualDeploymentUnit.exit().remove();
+ this.connectionPoint.exit().remove();
+ this.intConnectionPoint.exit().remove();
+ this.getPathNodes();
+ this.getgNetwork();
+ this.getgVirutualDeploymentUnit();
+ this.getgConnectionPoint();
+ this.getgIntConnectionPoint();
+ this.network = this.gNetwork.merge(this.network);
+ this.virutualDeploymentUnit = this.gVirutualDeploymentUnit.merge(this.virutualDeploymentUnit);
+ this.connectionPoint = this.gConnectionPoint.merge(this.connectionPoint);
+ this.intConnectionPoint = this.gIntConnectionPoint.merge(this.intConnectionPoint);
+ this.path.merge(this.path);
+ }
+ /** Setting the Path @private */
+ private getPathNodes(): void {
+ this.path = this.path.enter().append('svg:path').attr('class', 'link');
+ }
+ /** Settings all the network attributes of nodes @private */
+ private getgNetwork(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.gNetwork = this.network.enter().append('svg:g');
+ this.gNetwork.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
+ this.gNetwork.append('svg:image')
+ .style('opacity', 1)
+ .attr('x', graphContainerAttr.imageX)
+ .attr('y', graphContainerAttr.imageY)
+ .attr('id', (d: VNFDNODE) => { return d.id; })
+ .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
+ .attr('xlink:href', 'assets/images/INTVL.svg')
+ .on('mousedown', (d: VNFDNODE) => { this.mouseDown(d); })
+ .on('mouseup', (d: VNFDNODE) => { this.mouseUp(d); })
+ .on('click', (d: VNFDNODE) => { this.singleClick(this.network, d); this.onNodeClickToggleSidebar(); })
+ .on('dblclick', (d: VNFDNODE) => { this.getDeleteNodeConfirmation(d); this.onNodedblClickToggleSidebar(); });
+ this.gNetwork.append('svg:text')
+ .attr('class', 'node_text')
+ .attr('y', graphContainerAttr.textY)
+ .text((d: { id: number }) => d.id);
+ }
+ /** Settings all the connection point attributes of nodes @private */
+ private getgVirutualDeploymentUnit(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.gVirutualDeploymentUnit = this.virutualDeploymentUnit.enter().append('svg:g');
+ this.gVirutualDeploymentUnit.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
+ this.gVirutualDeploymentUnit.append('svg:image')
+ .style('opacity', 1)
+ .attr('x', graphContainerAttr.imageX)
+ .attr('y', graphContainerAttr.imageY)
+ .attr('id', (d: VNFDNODE) => { return d.id; })
+ .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
+ .attr('xlink:href', 'assets/images/VDU.svg')
+ .on('mousedown', (d: VNFDNODE) => { this.mouseDown(d); })
+ .on('mouseup', (d: VNFDNODE) => { this.mouseUp(d); })
+ .on('click', (d: VNFDNODE) => { this.singleClick(this.virutualDeploymentUnit, d); this.onNodeClickToggleSidebar(); })
+ .on('dblclick', (d: VNFDNODE) => { this.getDeleteNodeConfirmation(d); this.onNodedblClickToggleSidebar(); });
+ this.gVirutualDeploymentUnit.append('svg:text')
+ .attr('class', 'node_text')
+ .attr('y', graphContainerAttr.textY)
+ .text((d: { id: string }) => d.id);
+ }
+ /** Settings all the connection point attributes of nodes @private */
+ private getgConnectionPoint(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.gConnectionPoint = this.connectionPoint.enter().append('svg:g');
+ this.gVirutualDeploymentUnit.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
+ this.gConnectionPoint.append('svg:image')
+ .style('opacity', 1)
+ .attr('x', graphContainerAttr.imageX)
+ .attr('y', graphContainerAttr.imageY)
+ .attr('id', (d: VNFDNODE) => { return d.id; })
+ .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
+ .attr('xlink:href', 'assets/images/CP-VNF.svg')
+ .on('mousedown', (d: VNFDNODE) => { this.mouseDown(d); })
+ .on('mouseup', (d: VNFDNODE) => { this.mouseUp(d); })
+ .on('click', (d: VNFDNODE) => { this.singleClick(this.connectionPoint, d); this.onNodeClickToggleSidebar(); })
+ .on('dblclick', (d: VNFDNODE) => { this.getDeleteNodeConfirmation(d); this.onNodedblClickToggleSidebar(); });
+ this.gConnectionPoint.append('svg:text')
+ .attr('class', 'node_text')
+ .attr('y', graphContainerAttr.textY)
+ .text((d: { id: string }) => d.id);
+ }
+ /** Settings all the internal connection point attributes of nodes @private */
+ private getgIntConnectionPoint(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.gIntConnectionPoint = this.intConnectionPoint.enter().append('svg:g');
+ this.gIntConnectionPoint.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
+ this.gIntConnectionPoint.append('svg:image')
+ .style('opacity', 1)
+ .attr('x', graphContainerAttr.imageX)
+ .attr('y', graphContainerAttr.imageY)
+ .attr('id', (d: VNFDNODE) => { return d.id; })
+ .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
+ .attr('xlink:href', 'assets/images/INTCP.svg')
+ .on('mousedown', (d: VNFDNODE) => { this.mouseDown(d); })
+ .on('mouseup', (d: VNFDNODE) => { this.mouseUp(d); })
+ .on('click', (d: VNFDNODE) => { this.singleClick(this.intConnectionPoint, d); this.onNodeClickToggleSidebar(); })
+ .on('dblclick', (d: VNFDNODE) => { this.getDeleteNodeConfirmation(d); this.onNodedblClickToggleSidebar(); });
+ this.gIntConnectionPoint.append('svg:text')
+ .attr('class', 'node_text')
+ .attr('y', graphContainerAttr.textY)
+ .text((d: { id: string }) => d.id);
+ }
+ /** Drop VDU Composer Data @private */
+ private vduDropCompose(): void {
+ const randomID: string = this.sharedService.randomString();
+ const vduNode: VNFDNODE[] = [{
+ nodeTypeRef: 'vdu', id: 'vdu_' + randomID, count: 1, description: '', name: 'vdu_' + randomID, image: 'ubuntu',
+ interface: [], 'internal-connection-point': [], 'monitoring-param': [], 'vm-flavor': {}
+ }];
+ const nodeCopy: VNFDNODE[] = this.nodes;
+ Array.prototype.push.apply(vduNode, nodeCopy);
+ this.nodes = vduNode;
+ if (this.vnfdPackageDetails.vdu === undefined) { this.vnfdPackageDetails.vdu = []; }
+ this.vnfdPackageDetails.vdu.push({
+ id: 'vdu_' + randomID, count: 1, description: '', name: 'vdu_' + randomID, image: 'ubuntu',
+ interface: [], 'internal-connection-point': [], 'monitoring-param': [], 'vm-flavor': {}
+ });
+ this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
+ }
+ /** Drop CP Composer Data @private */
+ private cpDropCompose(): void {
+ const randomID: string = this.sharedService.randomString();
+ const cpNode: VNFDNODE[] = [{ nodeTypeRef: 'cp', id: 'cp_' + randomID, name: 'cp_' + randomID }];
+ const nodeCopy: VNFDNODE[] = this.nodes;
+ Array.prototype.push.apply(cpNode, nodeCopy);
+ this.nodes = cpNode;
+ if (this.vnfdPackageDetails['connection-point'] === undefined) {
+ this.vnfdPackageDetails['connection-point'] = [];
+ }
+ this.vnfdPackageDetails['connection-point'].push({
+ id: 'cp_' + randomID, name: 'cp_' + randomID, type: 'VPORT'
+ });
+ this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
+ }
+ /** Drop IntVL Composer Data @private */
+ private intvlDropCompose(): void {
+ const randomID: string = this.sharedService.randomString();
+ const intvlNode: VNFDNODE[] = [{
+ nodeTypeRef: 'intvl', id: 'vnf_vl_' + randomID, name: 'vnf_vl_' + randomID, 'short-name': 'vnf_vl_' + randomID, 'ip-profile-ref': '',
+ type: 'ELAN'
+ }];
+ const nodeCopy: VNFDNODE[] = this.nodes;
+ Array.prototype.push.apply(intvlNode, nodeCopy);
+ this.nodes = intvlNode;
+ if (this.vnfdPackageDetails['internal-vld'] === undefined) { this.vnfdPackageDetails['internal-vld'] = []; }
+ this.vnfdPackageDetails['internal-vld'].push({
+ id: 'vnf_vl_' + randomID, name: 'vnf_vl_' + randomID, 'short-name': 'vnf_vl_' + randomID,
+ 'ip-profile-ref': '', type: 'ELAN', 'internal-connection-point': []
+ });
+ this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
+ }
+ /** Add the Add Nodes Data @private */
+ private addNodes(apiURL: string, identifier: string, data: VNFDNODE): void {
+ this.isLoadingResults = true;
+ const apiURLHeader: APIURLHEADER = {
+ url: apiURL + '/' + identifier + '/package_content',
+ httpOptions: { headers: this.headers }
+ };
+ const vnfData: {} = {};
+ vnfData['vnfd:vnfd-catalog'] = {};
+ vnfData['vnfd:vnfd-catalog'].vnfd = [];
+ vnfData['vnfd:vnfd-catalog'].vnfd.push(data);
+ const descriptorInfo: string = jsyaml.dump(vnfData, { sortKeys: true });
+ this.sharedService.targzFile({ packageType: 'vnfd', id: this.identifier, descriptor: descriptorInfo })
+ .then((content: ArrayBuffer): void => {
+ this.restService.putResource(apiURLHeader, content).subscribe((res: {}) => {
+ this.generateData();
+ this.notifierService.notify('success', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.UPDATEDSUCCESSFULLY'));
+ this.isLoadingResults = false;
+ }, (error: ERRORDATA) => {
+ this.generateData();
+ this.restService.handleError(error, 'put');
+ this.isLoadingResults = false;
+ });
+ }).catch((): void => {
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ this.isLoadingResults = false;
+ });
+ }
+ /** Events handles when mousedown click it will capture the selected node data @private */
+ private mouseDown(d: VNFDNODE): void {
+ event.preventDefault();
+ if (d3.event.ctrlKey) { return; }
+ if (d3.event.shiftKey) {
+ this.mousedownNode = d;
+ this.currentSelectedNode = (this.mousedownNode === this.currentSelectedNode) ? null : this.mousedownNode;
+ this.dragLine.classed('hidden', false)
+ .attr('d', `M${this.mousedownNode.x},${this.mousedownNode.y}L${this.mousedownNode.x},${this.mousedownNode.y}`);
+ }
+ }
+ /** Event handles when mouseup event occures @private */
+ private mouseUp(d: VNFDNODE): void {
+ if (!this.mousedownNode) { return; }
+ this.dragLine.classed('hidden', true);
+ this.mouseupNode = d;
+ if (this.mousedownNode.nodeTypeRef === 'vdu' && this.mouseupNode.nodeTypeRef === 'intcp') {
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.CANNOTLINKVDUANDINTCP'));
+ this.deselectPath();
+ }
+ else if (this.mousedownNode.nodeTypeRef === 'vdu' && this.mouseupNode.nodeTypeRef === 'vdu') {
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.CANNOTLINKVDUANDVDU'));
+ this.deselectPath();
+ }
+ else if (this.mousedownNode.nodeTypeRef === 'intcp' && this.mouseupNode.nodeTypeRef === 'vdu') {
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.CANNOTLINKINTCPANDVDU'));
+ this.deselectPath();
+ }
+ else if (this.mousedownNode.nodeTypeRef === 'cp' && this.mouseupNode.nodeTypeRef === 'intvl') {
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.CANNOTLINKCPANDVNFVL'));
+ this.deselectPath();
+ }
+ else if (this.mousedownNode.nodeTypeRef === 'intvl' && this.mouseupNode.nodeTypeRef === 'cp') {
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.CANNOTLINKVNFVLANDCP'));
+ this.deselectPath();
+ }
+ else if (this.mousedownNode.nodeTypeRef === 'intcp' && this.mouseupNode.nodeTypeRef === 'cp') {
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.CANNOTLINKINTCPANDCP'));
+ this.deselectPath();
+ }
+ else if (this.mousedownNode.nodeTypeRef === 'cp' && this.mouseupNode.nodeTypeRef === 'intcp') {
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.CANNOTLINKCPANDINTCP'));
+ this.deselectPath();
+ } else if (this.mousedownNode.nodeTypeRef === 'vdu' && this.mouseupNode.nodeTypeRef === 'cp') {
+ this.vnfdPackageDetails.vdu.forEach((vduDetails: VDU) => {
+ if (vduDetails.id === this.mousedownNode.id) {
+ if (vduDetails.interface === undefined) { vduDetails.interface = []; }
+ vduDetails.interface.push({
+ 'external-connection-point-ref': this.mouseupNode.id, 'mgmt-interface': true,
+ name: 'eth_' + this.sharedService.randomString(),
+ 'virtual-interface': { type: 'VIRTIO' },
+ type: 'EXTERNAL'
+ });
+ if (vduDetails['internal-connection-point'] === undefined) {
+ vduDetails['internal-connection-point'] = [];
+ }
+ if (vduDetails['monitoring-param'] === undefined) {
+ vduDetails['monitoring-param'] = [];
+ }
+ if (vduDetails['vm-flavor'] === undefined) {
+ vduDetails['vm-flavor'] = {};
+ }
+ }
+ });
+ this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
+ this.deselectPath();
+ } else if (this.mousedownNode.nodeTypeRef === 'vdu' && this.mouseupNode.nodeTypeRef === 'intvl') {
+ const setIntCP: string = 'intcp_' + this.sharedService.randomString();
+ this.vnfdPackageDetails['internal-vld'].forEach((vldInternal: InternalVLD) => {
+ if (vldInternal.id === this.mouseupNode.id) {
+ if (vldInternal['internal-connection-point'] === undefined) { vldInternal['internal-connection-point'] = []; }
+ vldInternal['internal-connection-point'].push({ 'id-ref': setIntCP });
+ }
+ });
+ this.vnfdPackageDetails.vdu.forEach((vduDetails: VDU) => {
+ if (vduDetails.id === this.mousedownNode.id) {
+ if (vduDetails.interface === undefined) {
+ vduDetails.interface = [];
+ }
+ vduDetails.interface.push({
+ 'internal-connection-point-ref': setIntCP, name: 'int_' + setIntCP, type: 'INTERNAL', 'virtual-interface': { type: 'VIRTIO' }
+ });
+ if (vduDetails['internal-connection-point'] === undefined) {
+ vduDetails['internal-connection-point'] = [];
+ }
+ vduDetails['internal-connection-point'].push({
+ id: setIntCP, name: setIntCP, 'short-name': setIntCP, type: 'VPORT'
+ });
+ }
+ });
+ this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
+ this.deselectPath();
+ }
+ else {
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.INVALIDSELECTION'));
+ this.deselectPath();
+ }
+ this.resetMouseActions();
+ this.currentSelectedNode = null;
+ }
+ /** Events handles when mousemove it will capture the selected node data @private */
+ private mousemove(): void {
+ if (!this.mousedownNode) { return; }
+ this.dragLine.attr('d',
+ `M${this.mousedownNode.x},${this.mousedownNode.y}L${d3.mouse(d3.event.currentTarget)[0]},${d3.mouse(d3.event.currentTarget)[1]}`);
+ }
+ /** reset Mouse varaibles @private */
+ private resetMouseActions(): void {
+ this.mousedownNode = null;
+ this.mouseupNode = null;
+ }
+ /** Key press event @private */
+ private keyDown(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ if (this.lastKeyDown !== -1) { return; }
+ this.lastKeyDown = d3.event.keyCode;
+ if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) {
+ this.gNetwork.call(d3.drag().on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended));
+ this.gVirutualDeploymentUnit.call(d3.drag().on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended));
+ this.gConnectionPoint.call(d3.drag().on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended));
+ this.gIntConnectionPoint.call(d3.drag().on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended));
+ this.svg.classed('ctrl', true);
+ }
+ }
+ /** Key realse event @private */
+ private keyUp(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.lastKeyDown = -1;
+ if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) {
+ this.gNetwork.on('.drag', null);
+ this.gVirutualDeploymentUnit.on('.drag', null);
+ this.gConnectionPoint.on('.drag', null);
+ this.gIntConnectionPoint.on('.drag', null);
+ this.svg.classed('ctrl', false);
+ }
+ }
+ /** Mosue Drag Line false if it is not satisfied @private */
+ private deselectPath(): void {
+ this.dragLine.classed('hidden', true).attr('d', 'M0,0L0,0');
+ }
+ /** Events handles when Shift Click to perform create cp @private */
+ // tslint:disable-next-line: no-any
+ private singleClick(nodeSelected: any, d: VNFDNODE): void {
+ this.selectedNode(nodeSelected, d);
+ }
+ /** Get confirmation Before Deleting the Node in Topology @private */
+ private getDeleteNodeConfirmation(d: VNFDNODE): void {
+ const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
+ modalRef.componentInstance.topologyType = 'Delete';
+ modalRef.componentInstance.topologyname = d.name;
+ if (d.nodeTypeRef === 'vdu') {
+ modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.VDU';
+ } else if (d.nodeTypeRef === 'intvl') {
+ modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.INTVL';
+ } else if (d.nodeTypeRef === 'cp') {
+ modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.CONNECTIONPOINT';
+ } else if (d.nodeTypeRef === 'intcp') {
+ modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.INTCP';
+ }
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.deleteNode(d);
+ }
+ }).catch();
+ }
+ /** Delete nodes @private */
+ private deleteNode(d: VNFDNODE): void {
+ const deletedNode: VNFDNODE = d;
+ this.nodes.forEach((node: VNFDNODE) => {
+ if (node.id === d.id) {
+ if (deletedNode.nodeTypeRef === 'cp') {
+ if (this.vnfdPackageDetails.vdu !== undefined) {
+ this.vnfdPackageDetails.vdu.forEach((vduDetails: VDU) => {
+ if (vduDetails.interface !== undefined) {
+ const interfacePos: number = vduDetails.interface.map((e: VNFDInterface) => { return e['external-connection-point-ref']; })
+ .indexOf(d.id);
+ if (interfacePos >= 0) {
+ vduDetails.interface.splice(interfacePos, 1);
+ }
+ }
+ });
+ }
+ const cpPos: number = this.vnfdPackageDetails['connection-point'].map((e: CONNECTIONPOINT) => { return e.name; })
+ .indexOf(d.id);
+ if (cpPos >= 0) {
+ this.vnfdPackageDetails['connection-point'].splice(cpPos, 1);
+ }
+ } else if (deletedNode.nodeTypeRef === 'intcp') {
+ this.vnfdPackageDetails.vdu.forEach((vduDetails: VDU) => {
+ // Delete Interface
+ const interfacePos: number = vduDetails.interface.map((e: VNFDInterface) => { return e['internal-connection-point-ref']; })
+ .indexOf(d.id);
+ if (interfacePos >= 0) {
+ vduDetails.interface.splice(interfacePos, 1);
+ }
+ // Delete Internal CP
+ const interCPPos: number = vduDetails['internal-connection-point']
+ .map((e: VDUInternalConnectionPoint) => { return e.id; })
+ .indexOf(d.id);
+ if (interCPPos >= 0) {
+ vduDetails['internal-connection-point'].splice(interCPPos, 1);
+ }
+ });
+ if (this.vnfdPackageDetails['internal-vld'] !== undefined && this.vnfdPackageDetails['internal-vld'].length > 0) {
+ this.vnfdPackageDetails['internal-vld'].forEach((internalVLD: InternalVLD) => {
+ const interfacePos: number = internalVLD['internal-connection-point']
+ .map((e: VLDInternalConnectionPoint) => { return e['id-ref']; }).indexOf(d.id);
+ if (interfacePos >= 0) {
+ internalVLD['internal-connection-point'].splice(interfacePos, 1);
+ }
+ });
+ }
+ } else if (deletedNode.nodeTypeRef === 'intvl') {
+ const intvlPos: number = this.vnfdPackageDetails['internal-vld']
+ .map((e: InternalVLD) => { return e.name; }).indexOf(d.id);
+ if (intvlPos >= 0) {
+ this.vnfdPackageDetails['internal-vld'].splice(intvlPos, 1);
+ }
+ } else if (deletedNode.nodeTypeRef === 'vdu') {
+ const internalCPList: string[] = [];
+ if (deletedNode.interface !== undefined && deletedNode.interface.length > 0) {
+ deletedNode.interface.forEach((interfaceNode: InternalVLD) => {
+ if (interfaceNode['internal-connection-point-ref'] !== undefined) {
+ internalCPList.push(interfaceNode['internal-connection-point-ref']);
+ }
+ });
+ }
+ internalCPList.forEach((list: string) => {
+ if (this.vnfdPackageDetails['internal-vld'] !== undefined && this.vnfdPackageDetails['internal-vld'].length > 0) {
+ this.vnfdPackageDetails['internal-vld'].forEach((internalVLD: InternalVLD) => {
+ const interfacePos: number = internalVLD['internal-connection-point']
+ .map((e: VLDInternalConnectionPoint) => { return e['id-ref']; }).indexOf(list);
+ if (interfacePos >= 0) {
+ internalVLD['internal-connection-point'].splice(interfacePos, 1);
+ }
+ });
+ }
+ });
+ const vduPos: number = this.vnfdPackageDetails.vdu.map((e: VDU) => { return e.id; }).indexOf(d.id);
+ if (vduPos >= 0) {
+ this.vnfdPackageDetails.vdu.splice(vduPos, 1);
+ }
+ } else {
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.INVALIDSELECTION'));
+ }
+ this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
+ }
+ });
+ }
+ /** Get confirmation before deleting the ink in Topology @private */
+ private getDeleteLinkConfirmation(d: Tick): void {
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.YOUCANNOTDELETELINK'));
+ }
+ /** Selected nodes @private */
+ // tslint:disable-next-line: no-any
+ private selectedNode(nodeSeleced: any, d: VNFDNODE): void {
+ const alreadyIsActive: boolean = nodeSeleced.select('#' + d.id).classed(this.activeNode);
+ this.deselectAllNodes();
+ nodeSeleced.select('#' + d.id).classed(this.activeNode, !alreadyIsActive);
+ if (d.nodeTypeRef === 'vdu' && !alreadyIsActive) {
+ this.vnfdPackageDetails.vdu.forEach((res: VDU) => {
+ if (res.name === d.id) {
+ this.showRightSideInfo = 'vduInfo';
+ this.vduInfo = res;
+ }
+ });
+ } else if (d.nodeTypeRef === 'intvl' && !alreadyIsActive) {
+ this.vnfdPackageDetails['internal-vld'].forEach((res: InternalVLD) => {
+ if (res.name === d.id) {
+ this.showRightSideInfo = 'intvlInfo';
+ this.intvlInfo = res;
+ this.intvlInfo.shortName = res['short-name'];
+ this.intvlInfo.ipProfileRef = res['ip-profile-ref'];
+ }
+ });
+ } else if (d.nodeTypeRef === 'cp' && !alreadyIsActive) {
+ this.vnfdPackageDetails['connection-point'].forEach((res: CONNECTIONPOINT) => {
+ if (res.name === d.id) {
+ this.showRightSideInfo = 'cpInfo';
+ this.cpInfo = res;
+ }
+ });
+ }
+ else if (d.nodeTypeRef === 'intcp' && !alreadyIsActive) {
+ this.intcpInfo = d;
+ this.showRightSideInfo = 'intcpInfo';
+ this.intcpInfo.shortName = d['short-name'];
+ } else {
+ this.showRightSideInfo = 'vnfdInfo';
+ }
+ }
+ /** De-select all the selected nodes @private */
+ private deselectAllNodes(): void {
+ this.network.select('image').classed(this.activeNode, false);
+ this.virutualDeploymentUnit.select('image').classed(this.activeNode, false);
+ this.connectionPoint.select('image').classed(this.activeNode, false);
+ this.intConnectionPoint.select('image').classed(this.activeNode, false);
+ }
+ /** Events handles when dragstarted @private */
+ private dragstarted(d: COMPOSERNODES): void {
+ d.fx = d.x;
+ d.fy = d.y;
+ }
+ /** Events handles when dragged @private */
+ private dragged(d: COMPOSERNODES): void {
+ d.fx = d.x = d3.event.x;
+ d.fy = d.y = d3.event.y;
+ }
+ /** Events handles when dragended @private */
+ private dragended(d: COMPOSERNODES): void {
+ if (this.forceSimulationActive) {
+ d.fx = null;
+ d.fy = null;
+ } else {
+ d.fx = d.x;
+ d.fy = d.y;
+ this.forceSimulationActive = false;
+ }
+ }
+ /** Events handles when node double click @private */
+ private onNodedblClickToggleSidebar(): void {
+ this.sideBarOpened = false;
+ }
+ /** Events handles when node single click @private */
+ private onNodeClickToggleSidebar(): void {
+ this.sideBarOpened = true;
+ }
+}
diff --git a/src/app/packages/show-content/ShowContentComponent.html b/src/app/packages/show-content/ShowContentComponent.html
new file mode 100644
index 0000000..dbb0935
--- /dev/null
+++ b/src/app/packages/show-content/ShowContentComponent.html
@@ -0,0 +1,44 @@
+<!--
+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: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+-->
+<div class="modal-header">
+ <h4 class="modal-title" id="modal-basic-title">{{'FILES' | translate}} {{'IN' | translate}}
+ {{(params.id)?params.id:'-'}}</h4>
+ <button class="button-xs" type="button" class="close" aria-label="Close" (click)="activeModal.dismiss('Cross click')">
+ <i class="fas fa-times-circle text-danger"></i>
+ </button>
+</div>
+<div class="modal-body">
+ <table class="table table-bored show-content" *ngIf="contents else noData">
+ <tr>
+ <th># {{'FILES' | translate}} {{'NAME' | translate}}</th>
+ <th>{{'ACTIONS' | translate }}</th>
+ </tr>
+ <tr *ngFor="let data of contents">
+ <td>- {{(data)?data:'-'}}</td>
+ <td>
+ <button class="btn btn-xs">
+ <i class="far fa-folder-open icons"></i>
+ </button>
+ </td>
+ </tr>
+ </table>
+ <ng-template #noData>{{'NODATAERROR' | translate}}</ng-template>
+</div>
+<div class="modal-footer">
+ <button type="button" class="btn btn-danger" (click)="activeModal.close()">{{'CANCEL' | translate}}</button>
+</div>
\ No newline at end of file
diff --git a/src/app/packages/show-content/ShowContentComponent.scss b/src/app/packages/show-content/ShowContentComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/packages/show-content/ShowContentComponent.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: 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/packages/show-content/ShowContentComponent.ts b/src/app/packages/show-content/ShowContentComponent.ts
new file mode 100644
index 0000000..e2431e8
--- /dev/null
+++ b/src/app/packages/show-content/ShowContentComponent.ts
@@ -0,0 +1,82 @@
+/*
+ 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: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+*/
+/**
+ * @file Show content modal
+ */
+import { Component, Injector, Input, OnInit } from '@angular/core';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { ERRORDATA, URLPARAMS } from 'CommonModel';
+import { environment } from 'environment';
+import { RestService } from 'RestService';
+
+/** Shows json data in the information modal */
+const defaults: {} = { 'text/json': '' };
+
+/**
+ * Creating component
+ * @Component takes ShowContentComponent.html as template url
+ */
+@Component({
+ selector: 'app-show-content',
+ templateUrl: './ShowContentComponent.html',
+ styleUrls: ['./ShowContentComponent.scss']
+})
+/** Exporting a class @exports ShowContentComponent */
+export class ShowContentComponent implements OnInit {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Instance for active modal service @public */
+ public activeModal: NgbActiveModal;
+
+ /** Contains files information @public */
+ public contents: {}[];
+
+ /** Input contains component objects @public */
+ @Input() public params: URLPARAMS;
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.activeModal = this.injector.get(NgbActiveModal);
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate @public
+ */
+ public ngOnInit(): void {
+ if (this.params.page === 'nsd') {
+ this.restService.getResource(environment.NSDESCRIPTORS_URL + '/' + this.params.id + '/artifacts/artifactPath')
+ .subscribe((nsd: {}[]) => {
+ this.contents = nsd;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ });
+ } else if (this.params.page === 'vnfd') {
+ this.restService.getResource(environment.VNFPACKAGES_URL + '/' + this.params.id + '/artifacts/artifactPath')
+ .subscribe((vnfd: {}[]) => {
+ this.contents = vnfd;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ });
+ }
+ }
+}
diff --git a/src/app/packages/vnf-packages/VNFPackagesComponent.html b/src/app/packages/vnf-packages/VNFPackagesComponent.html
new file mode 100644
index 0000000..2d7298d
--- /dev/null
+++ b/src/app/packages/vnf-packages/VNFPackagesComponent.html
@@ -0,0 +1,44 @@
+<!--
+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: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+-->
+<div class="row d-flex flex-row justify-content-between">
+ <div class="d-flex align-items-center header-style">VNF {{'PACKAGES' | translate}}</div>
+ <span class="button">
+ <button class="btn btn-primary" type="button" placement="top" container="body" ngbTooltip="{{'PAGE.VNFPACKAGE.ADDVNFPACKAGE' | translate}}"
+ (click)="composeVNFPackage()">
+ <i class="fas fa-plus-circle" aria-hidden="true"></i> {{'PAGE.VNFPACKAGE.ADDVNFPACKAGE' | translate}}
+ </button>
+ </span>
+</div>
+<div class="row">
+ <div class="dropzone mt-2" appDrag (click)="fileInput.click()" (files)="filesDropped($event)">
+ <input hidden type="file" #fileInput (change)="filesDropped($event.target.files)">
+ <div class="text-wrapper">
+ <div class="text-center file-drop-title">
+ <i class="fas fa-upload"></i> {{'DROPFILES' | translate}}</div>
+ </div>
+ </div>
+</div>
+<div class="row mt-2 mb-0 form-group justify-content-end list-utilites-actions">
+ <page-per-row class="mr-2" (pagePerRow)="onChange($event)"></page-per-row>
+ <page-reload></page-reload>
+</div>
+<div class="smarttable-style bg-white mt-1">
+ <ng2-smart-table [ngClass]="checkDataClass" [settings]="settings" [source]="dataSource" (userRowSelect)="onUserRowSelect($event)">
+ </ng2-smart-table>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/packages/vnf-packages/VNFPackagesComponent.scss b/src/app/packages/vnf-packages/VNFPackagesComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/packages/vnf-packages/VNFPackagesComponent.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: 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/packages/vnf-packages/VNFPackagesComponent.ts b/src/app/packages/vnf-packages/VNFPackagesComponent.ts
new file mode 100644
index 0000000..13707e1
--- /dev/null
+++ b/src/app/packages/vnf-packages/VNFPackagesComponent.ts
@@ -0,0 +1,275 @@
+/*
+ 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: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+*/
+/**
+ * @file VNF Package details Component.
+ */
+import { HttpHeaders } from '@angular/common/http';
+import { Component, ElementRef, Injector, OnInit, ViewChild } from '@angular/core';
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { APIURLHEADER, ERRORDATA } from 'CommonModel';
+import { ComposePackages } from 'ComposePackages';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import { LocalDataSource } from 'ng2-smart-table';
+import { RestService } from 'RestService';
+import { Subscription } from 'rxjs';
+import { SharedService } from 'SharedService';
+import { VNFData, VNFDDetails } from 'VNFDModel';
+import { VNFPackagesActionComponent } from 'VNFPackagesAction';
+
+/**
+ * Creating component
+ * @Component takes VNFPackagesComponent.html as template url
+ */
+@Component({
+ selector: 'app-vnf-packages',
+ templateUrl: './VNFPackagesComponent.html',
+ styleUrls: ['./VNFPackagesComponent.scss']
+})
+/** Exporting a class @exports VNFPackagesComponent */
+export class VNFPackagesComponent implements OnInit {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Data of smarttable populate through LocalDataSource @public */
+ public dataSource: LocalDataSource = new LocalDataSource();
+
+ /** handle translate @public */
+ public translateService: TranslateService;
+
+ /** Columns list of the smart table @public */
+ public columnLists: object = {};
+
+ /** Settings for smarttable to populate the table with columns @public */
+ public settings: object = {};
+
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = true;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Class for empty and present data @public */
+ public checkDataClass: string;
+
+ /** Element ref for fileInput @public */
+ @ViewChild('fileInput', { static: true }) public fileInput: ElementRef;
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ /** dataService to pass the data from one component to another @private */
+ private dataService: DataService;
+
+ /** Formation of appropriate Data for LocalDatasource @private */
+ private vnfData: VNFData[] = [];
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** variables contains file information */
+ private fileData: string | ArrayBuffer;
+
+ /** Controls the header form @private */
+ private headers: HttpHeaders;
+
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ /** Instance of subscriptions @private */
+ private generateDataSub: Subscription;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.dataService = this.injector.get(DataService);
+ this.sharedService = this.injector.get(SharedService);
+ this.translateService = this.injector.get(TranslateService);
+ this.notifierService = this.injector.get(NotifierService);
+ this.modalService = this.injector.get(NgbModal);
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ this.generateColumns();
+ this.generateSettings();
+ this.generateData();
+ this.headers = new HttpHeaders({
+ 'Content-Type': 'application/gzip',
+ Accept: 'application/json',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); });
+ }
+
+ /** smart table Header Colums @public */
+ public generateColumns(): void {
+ this.columnLists = {
+ shortName: { title: this.translateService.instant('SHORTNAME'), width: '15%', sortDirection: 'asc' },
+ identifier: { title: this.translateService.instant('IDENTIFIER'), width: '20%' },
+ type: {
+ title: this.translateService.instant('TYPE'),
+ filter: {
+ type: 'list',
+ config: {
+ selectText: 'Select',
+ list: [
+ { value: 'vnfd', title: 'VNF' },
+ { value: 'pnfd', title: 'PNF' },
+ { value: 'hnfd', title: 'HNF' }
+ ]
+ }
+ },
+ width: '10%'
+ },
+ description: { title: this.translateService.instant('DESCRIPTION'), width: '20%' },
+ vendor: { title: this.translateService.instant('VENDOR'), width: '10%' },
+ version: { title: this.translateService.instant('VERSION'), width: '10%' },
+ Actions: {
+ name: 'Action', width: '15%', filter: false, sort: false, type: 'custom',
+ title: this.translateService.instant('ACTIONS'),
+ valuePrepareFunction: (cell: VNFData, row: VNFData): VNFData => row, renderComponent: VNFPackagesActionComponent
+ }
+ };
+ }
+
+ /** smart table Data Settings @public */
+ public generateSettings(): void {
+ this.settings = {
+ edit: {
+ editButtonContent: '<i class="fa fa-edit" title="Edit"></i>',
+ confirmSave: true
+ },
+ delete: {
+ deleteButtonContent: '<i class="far fa-trash-alt" title="delete"></i>',
+ confirmDelete: true
+ },
+ columns: this.columnLists,
+ actions: {
+ add: false,
+ edit: false,
+ delete: false,
+ position: 'right'
+ },
+ attr: this.sharedService.tableClassConfig(),
+ pager: this.sharedService.paginationPagerConfig(),
+ noDataMessage: this.translateService.instant('NODATAMSG')
+ };
+ }
+
+ /** smart table listing manipulation @public */
+ public onChange(perPageValue: number): void {
+ this.dataSource.setPaging(1, perPageValue, true);
+ }
+
+ /** OnUserRowSelect Function @public */
+ public onUserRowSelect(event: MessageEvent): void {
+ Object.assign(event.data, { page: 'vnf-package' });
+ this.dataService.changeMessage(event.data);
+ }
+
+ /** Drag and drop feature and fetchind the details of files @public */
+ public filesDropped(files: FileList): void {
+ if (files && files.length === 1) {
+ this.isLoadingResults = true;
+ this.sharedService.getFileString(files, 'gz').then((fileContent: ArrayBuffer): void => {
+ const apiURLHeader: APIURLHEADER = {
+ url: environment.VNFPACKAGESCONTENT_URL,
+ httpOptions: { headers: this.headers }
+ };
+ this.saveFileData(apiURLHeader, fileContent);
+ }).catch((err: string): void => {
+ this.isLoadingResults = false;
+ if (err === 'typeError') {
+ this.notifierService.notify('error', this.translateService.instant('GZFILETYPEERRROR'));
+ } else {
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ }
+ });
+ } else if (files && files.length > 1) {
+ this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION'));
+ }
+ }
+
+ /** Post the droped files and reload the page @public */
+ public saveFileData(urlHeader: APIURLHEADER, fileData: {}): void {
+ this.fileInput.nativeElement.value = null;
+ this.restService.postResource(urlHeader, fileData).subscribe((result: {}) => {
+ this.notifierService.notify('success', this.translateService.instant('PAGE.VNFPACKAGE.CREATEDSUCCESSFULLY'));
+ this.generateData();
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'post');
+ this.isLoadingResults = false;
+ });
+ }
+
+ /** Generate nsData object from loop and return for the datasource @public */
+ public generatevnfdData(vnfdpackagedata: VNFDDetails): VNFData {
+ return {
+ shortName: vnfdpackagedata['short-name'],
+ identifier: vnfdpackagedata._id,
+ type: vnfdpackagedata._admin.type,
+ description: vnfdpackagedata.description,
+ vendor: vnfdpackagedata.vendor,
+ version: vnfdpackagedata.version
+ };
+ }
+ /** Handle compose new ns package method @public */
+ public composeVNFPackage(): void {
+ this.modalService.open(ComposePackages, { backdrop: 'static' }).componentInstance.params = { page: 'vnf-package' };
+ }
+
+ /**
+ * Lifecyle hook which get trigger on component destruction
+ */
+ public ngOnDestroy(): void {
+ this.generateDataSub.unsubscribe();
+ }
+
+ /** Fetching the data from server to Load in the smarttable @protected */
+ protected generateData(): void {
+ this.isLoadingResults = true;
+ this.restService.getResource(environment.VNFPACKAGESCONTENT_URL).subscribe((vnfdPackageData: VNFDDetails[]) => {
+ this.vnfData = [];
+ vnfdPackageData.forEach((vnfdpackagedata: VNFDDetails) => {
+ const vnfDataObj: VNFData = this.generatevnfdData(vnfdpackagedata);
+ this.vnfData.push(vnfDataObj);
+ });
+ if (this.vnfData.length > 0) {
+ this.checkDataClass = 'dataTables_present';
+ } else {
+ this.checkDataClass = 'dataTables_empty';
+ }
+ this.dataSource.load(this.vnfData).then((data: boolean) => {
+ this.isLoadingResults = false;
+ }).catch(() => {
+ this.isLoadingResults = false;
+ });
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ this.isLoadingResults = false;
+ });
+ }
+}