Option to upload appliance added

This commit is contained in:
piotrpekala7 2020-06-02 03:15:45 +02:00
parent d15b4db9fe
commit 25b1edd178
7 changed files with 157 additions and 7 deletions

View File

@ -277,6 +277,7 @@ import { WebConsoleFullWindowComponent } from './components/web-console-full-win
import { ConsoleGuard } from './guards/console-guard';
import { NewTemplateDialogComponent } from './components/project-map/new-template-dialog/new-template-dialog.component';
import { ApplianceService } from './services/appliances.service';
import { DataSourceFilter } from './filters/dataSourceFilter';
if (environment.production) {
Raven.config('https://b2b1cfd9b043491eb6b566fd8acee358@sentry.io/842726', {
@ -390,6 +391,7 @@ if (environment.production) {
SearchFilter,
DateFilter,
NameFilter,
DataSourceFilter,
TemplateFilter,
ProjectsFilter,
ListOfSnapshotsComponent,

View File

@ -11,13 +11,14 @@
<div>
<button mat-button matStepperNext>Next</button>
<button mat-button (click)="onCloseClick()">Cancel</button>
</div>
</mat-step>
<mat-step [stepControl]="secondFormGroup">
<ng-template matStepLabel>{{actionTitle}}</ng-template>
<mat-card>
<mat-card [hidden]="!(action==='install')">
<div class="tableHeader">
<mat-form-field class="filter-field">
<input matInput [(ngModel)]="searchText" placeholder="Filter">
@ -34,8 +35,9 @@
class="mat-table"
#table
matSort
multiTemplateDataRows
(matSortChange)= "sortData($event)"
[dataSource]="appliances | namefilter: searchText">
[dataSource]="dataSource | datasourcefilter: searchText">
<ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef mat-sort-header> Name </mat-header-cell>
<mat-cell *matCellDef="let row"> {{row.name}} </mat-cell>
@ -63,10 +65,43 @@
</button>
</mat-cell>
</ng-container> -->
<!-- <ng-container matColumnDef="expandedDetail">
<mat-cell *matCellDef="let detail">
The symbol for {{detail.element}}
</mat-cell>
</ng-container> -->
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
<!-- <mat-row
*matRowDef="let row; columns: displayedColumns;"
matRipple
class="element-row"
[class.expanded]="expandedElement == row"
(click)="expandedElement = row">
</mat-row>
<mat-row
*matRowDef="let row; columns: ['expandedDetail']; when: isExpansionDetailRow"
[@detailExpand]="row.element == expandedElement ? 'expanded' : 'collapsed'"
style="overflow: hidden">
</mat-row> -->
</mat-table>
<mat-paginator [pageSizeOptions]="[5, 10, 20, 50, 100]" showFirstLastButtons></mat-paginator>
</mat-card>
<mat-card [hidden]="action==='install'">
<input
type="file"
accept=".gns3appliance, .gns3a"
class="non-visible"
#file
(change)="addAppliance($event)"
ng2FileSelect
[uploader]="uploader"/>
<button mat-raised-button color="primary" (click)="file.click()">Click to import appliance</button>
</mat-card>
</mat-step>

View File

@ -9,3 +9,31 @@
.filter-field {
width: 100%;
}
.example-container {
display: flex;
flex-direction: column;
max-height: 500px;
min-width: 300px;
}
.mat-table {
overflow: auto;
max-height: 500px;
}
.element-row {
position: relative;
}
.element-row:not(.expanded) {
cursor: pointer;
}
.element-row:not(.expanded):hover {
background: #f5f5f5;
}
.element-row.expanded {
border-bottom-color: transparent;
}

View File

@ -1,20 +1,32 @@
import { Component, Input, OnInit, ChangeDetectorRef } from '@angular/core';
import { MatDialogRef, Sort } from '@angular/material';
import { Component, Input, OnInit, ChangeDetectorRef, ViewChild } from '@angular/core';
import { MatDialogRef, Sort, MatTableDataSource, MatPaginator } from '@angular/material';
import { Server } from '../../../models/server';
import { Node } from '../../../cartography/models/node';
import { Project } from '../../../models/project';
import { ApplianceService } from '../../../services/appliances.service';
import { Appliance } from '../../../models/appliance';
import { animate, state, style, transition, trigger } from '@angular/animations';
import { FileUploader, FileItem, ParsedResponseHeaders } from 'ng2-file-upload';
import { ToasterService } from '../../../services/toaster.service';
@Component({
selector: 'app-new-template-dialog',
templateUrl: './new-template-dialog.component.html',
styleUrls: ['./new-template-dialog.component.scss']
styleUrls: ['./new-template-dialog.component.scss'],
animations: [
trigger('detailExpand', [
state('collapsed', style({ height: '0px', minHeight: '0', visibility: 'hidden' })),
state('expanded', style({ height: '*', visibility: 'visible' })),
transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
]),
],
})
export class NewTemplateDialogComponent implements OnInit {
@Input() server: Server;
@Input() project: Project;
uploader: FileUploader;
public action: string = 'install';
public actionTitle: string = 'Install appliance from server';
@ -26,10 +38,15 @@ export class NewTemplateDialogComponent implements OnInit {
public category: string = 'all categories';
public displayedColumns: string[] = ['name', 'emulator', 'vendor'];
public dataSource: MatTableDataSource<Appliance>;
@ViewChild(MatPaginator, {static: true}) paginator: MatPaginator;
constructor(
public dialogRef: MatDialogRef<NewTemplateDialogComponent>,
private applianceService: ApplianceService,
private changeDetector: ChangeDetectorRef
private changeDetector: ChangeDetectorRef,
private toasterService: ToasterService
) {}
ngOnInit() {
@ -42,7 +59,49 @@ export class NewTemplateDialogComponent implements OnInit {
if (appliance.qemu) appliance.emulator = 'Qemu';
});
this.allAppliances = appliances;
this.dataSource = new MatTableDataSource(this.allAppliances);
this.dataSource.paginator = this.paginator;
});
this.uploader = new FileUploader({});
this.uploader.onAfterAddingFile = file => {
file.withCredentials = false;
};
this.uploader.onErrorItem = (item: FileItem, response: string, status: number, headers: ParsedResponseHeaders) => {
this.toasterService.error('An error has occured');
};
this.uploader.onSuccessItem = (item: FileItem, response: string, status: number, headers: ParsedResponseHeaders) => {
this.toasterService.success('Appliance imported succesfully');
};
}
addAppliance(event): void {
let name = event.target.files[0].name.split('-')[0];
let fileName = event.target.files[0].name;
let file = event.target.files[0];
let fileReader: FileReader = new FileReader();
let emulator;
fileReader.onloadend = () => {
let appliance = JSON.parse(fileReader.result as string);
if (appliance.docker) emulator = 'docker';
if (appliance.dynamips) emulator = 'dynamips';
if (appliance.iou) emulator = 'iou';
if (appliance.qemu) emulator = 'qemu';
const url = this.applianceService.getUploadPath(this.server, emulator, fileName);
this.uploader.queue.forEach(elem => (elem.url = url));
const itemToUpload = this.uploader.queue[0];
(itemToUpload as any).options.disableMultipart = true;
this.uploader.uploadItem(itemToUpload);
};
fileReader.readAsText(file);
}
filterAppliances(event) {
@ -55,6 +114,9 @@ export class NewTemplateDialogComponent implements OnInit {
} else {
this.appliances = temporaryAppliances.filter(t => t.category === this.category);
}
this.dataSource = new MatTableDataSource(this.appliances);
this.dataSource.paginator = this.paginator;
}
setAction(action: string) {

View File

@ -0,0 +1,17 @@
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'datasourcefilter'
})
export class DataSourceFilter implements PipeTransform {
transform(items: any, searchText: string): any[] {
if(!items) return [];
if(!searchText) return items;
searchText = searchText.toLowerCase();
return items.filteredData.filter( item => {
return item.name.toLowerCase().includes(searchText);
});
}
}

View File

@ -23,10 +23,12 @@ import {
MatTabsModule,
MatTreeModule,
MatBottomSheetModule,
MatChipsModule
MatChipsModule,
MatPaginatorModule
} from '@angular/material';
export const MATERIAL_IMPORTS = [
MatPaginatorModule,
MatButtonModule,
MatMenuModule,
MatCardModule,

View File

@ -13,4 +13,8 @@ export class ApplianceService {
getAppliances(server: Server): Observable<Appliance[]> {
return this.httpServer.get<Appliance[]>(server, '/appliances') as Observable<Appliance[]>;
}
getUploadPath(server: Server, emulator: string, filename: string) {
return `http://${server.host}:${server.port}/v2/${emulator}/images/${filename}`;
}
}