Merge pull request #504 from GNS3/node-configurator

Configurators for nodes & template preferences for TraceNG & Qemu image creator
This commit is contained in:
piotrpekala7 2019-09-25 08:42:29 +02:00 committed by GitHub
commit b55d5ff59f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
78 changed files with 3478 additions and 141 deletions

View File

@ -53,6 +53,10 @@ import { CopyIouTemplateComponent } from './components/preferences/ios-on-unix/c
import { ListOfSnapshotsComponent } from './components/snapshots/list-of-snapshots/list-of-snapshots.component'; import { ListOfSnapshotsComponent } from './components/snapshots/list-of-snapshots/list-of-snapshots.component';
import { ConsoleComponent } from './components/settings/console/console.component'; import { ConsoleComponent } from './components/settings/console/console.component';
import { HelpComponent } from './components/help/help.component'; import { HelpComponent } from './components/help/help.component';
import { TracengPreferencesComponent } from './components/preferences/traceng/traceng-preferences/traceng-preferences.component';
import { TracengTemplatesComponent } from './components/preferences/traceng/traceng-templates/traceng-templates.component';
import { AddTracengTemplateComponent } from './components/preferences/traceng/add-traceng/add-traceng-template.component';
import { TracengTemplateDetailsComponent } from './components/preferences/traceng/traceng-template-details/traceng-template-details.component';
const routes: Routes = [ const routes: Routes = [
{ {
@ -111,6 +115,11 @@ const routes: Routes = [
{ path: 'server/:server_id/preferences/vmware/templates/:template_id', component: VmwareTemplateDetailsComponent }, { path: 'server/:server_id/preferences/vmware/templates/:template_id', component: VmwareTemplateDetailsComponent },
{ path: 'server/:server_id/preferences/vmware/addtemplate', component: AddVmwareTemplateComponent }, { path: 'server/:server_id/preferences/vmware/addtemplate', component: AddVmwareTemplateComponent },
// { path: 'server/:server_id/preferences/traceng', component: TracengPreferencesComponent },
// { path: 'server/:server_id/preferences/traceng/templates', component: TracengTemplatesComponent },
// { path: 'server/:server_id/preferences/traceng/templates/:template_id', component: TracengTemplateDetailsComponent },
// { path: 'server/:server_id/preferences/traceng/addtemplate', component: AddTracengTemplateComponent },
{ path: 'server/:server_id/preferences/docker/templates', component: DockerTemplatesComponent }, { path: 'server/:server_id/preferences/docker/templates', component: DockerTemplatesComponent },
{ path: 'server/:server_id/preferences/docker/templates/:template_id', component: DockerTemplateDetailsComponent }, { path: 'server/:server_id/preferences/docker/templates/:template_id', component: DockerTemplateDetailsComponent },
{ path: 'server/:server_id/preferences/docker/templates/:template_id/copy', component: CopyDockerTemplateComponent }, { path: 'server/:server_id/preferences/docker/templates/:template_id/copy', component: CopyDockerTemplateComponent },

View File

@ -211,6 +211,30 @@ import { ProjectsFilter } from './filters/projectsFilter.pipe';
import { ComputeService } from './services/compute.service'; import { ComputeService } from './services/compute.service';
import { ReloadNodeActionComponent } from './components/project-map/context-menu/actions/reload-node-action/reload-node-action.component'; import { ReloadNodeActionComponent } from './components/project-map/context-menu/actions/reload-node-action/reload-node-action.component';
import { SuspendNodeActionComponent } from './components/project-map/context-menu/actions/suspend-node-action/suspend-node-action.component'; import { SuspendNodeActionComponent } from './components/project-map/context-menu/actions/suspend-node-action/suspend-node-action.component';
import { ConfigActionComponent } from './components/project-map/context-menu/actions/config-action/config-action.component';
import { ConfiguratorDialogVpcsComponent } from './components/project-map/node-editors/configurator/vpcs/configurator-vpcs.component';
import { ConfiguratorDialogEthernetHubComponent } from './components/project-map/node-editors/configurator/ethernet_hub/configurator-ethernet-hub.component';
import { ConfiguratorDialogEthernetSwitchComponent } from './components/project-map/node-editors/configurator/ethernet-switch/configurator-ethernet-switch.component';
import { PortsComponent } from './components/preferences/common/ports/ports.component';
import { ConfiguratorDialogSwitchComponent } from './components/project-map/node-editors/configurator/switch/configurator-switch.component';
import { ConfiguratorDialogVirtualBoxComponent } from './components/project-map/node-editors/configurator/virtualbox/configurator-virtualbox.component';
import { CustomAdaptersTableComponent } from './components/preferences/common/custom-adapters-table/custom-adapters-table.component';
import { ConfiguratorDialogQemuComponent } from './components/project-map/node-editors/configurator/qemu/configurator-qemu.component';
import { ConfiguratorDialogCloudComponent } from './components/project-map/node-editors/configurator/cloud/configurator-cloud.component';
import { UdpTunnelsComponent } from './components/preferences/common/udp-tunnels/udp-tunnels.component';
import { ConfiguratorDialogAtmSwitchComponent } from './components/project-map/node-editors/configurator/atm_switch/configurator-atm-switch.component';
import { ConfiguratorDialogVmwareComponent } from './components/project-map/node-editors/configurator/vmware/configurator-vmware.component';
import { ConfiguratorDialogIouComponent } from './components/project-map/node-editors/configurator/iou/configurator-iou.component';
import { ConfiguratorDialogIosComponent } from './components/project-map/node-editors/configurator/ios/configurator-ios.component';
import { ConfiguratorDialogDockerComponent } from './components/project-map/node-editors/configurator/docker/configurator-docker.component';
import { ConfiguratorDialogNatComponent } from './components/project-map/node-editors/configurator/nat/configurator-nat.component';
import { ConfiguratorDialogTracengComponent } from './components/project-map/node-editors/configurator/traceng/configurator-traceng.component';
import { AddTracengTemplateComponent } from './components/preferences/traceng/add-traceng/add-traceng-template.component';
import { TracengPreferencesComponent } from './components/preferences/traceng/traceng-preferences/traceng-preferences.component';
import { TracengTemplatesComponent } from './components/preferences/traceng/traceng-templates/traceng-templates.component';
import { TracengService } from './services/traceng.service';
import { TracengTemplateDetailsComponent } from './components/preferences/traceng/traceng-template-details/traceng-template-details.component';
import { QemuImageCreatorComponent } from './components/project-map/node-editors/configurator/qemu/qemu-image-creator/qemu-image-creator.component';
if (environment.production) { if (environment.production) {
Raven.config('https://b2b1cfd9b043491eb6b566fd8acee358@sentry.io/842726', { Raven.config('https://b2b1cfd9b043491eb6b566fd8acee358@sentry.io/842726', {
@ -349,7 +373,30 @@ if (environment.production) {
ChangeSymbolActionComponent, ChangeSymbolActionComponent,
EditProjectDialogComponent, EditProjectDialogComponent,
ReloadNodeActionComponent, ReloadNodeActionComponent,
SuspendNodeActionComponent SuspendNodeActionComponent,
ConfigActionComponent,
ConfiguratorDialogVpcsComponent,
ConfiguratorDialogEthernetHubComponent,
ConfiguratorDialogEthernetSwitchComponent,
PortsComponent,
ConfiguratorDialogSwitchComponent,
ConfiguratorDialogVirtualBoxComponent,
CustomAdaptersTableComponent,
ConfiguratorDialogQemuComponent,
ConfiguratorDialogCloudComponent,
UdpTunnelsComponent,
ConfiguratorDialogAtmSwitchComponent,
ConfiguratorDialogVmwareComponent,
ConfiguratorDialogIouComponent,
ConfiguratorDialogIosComponent,
ConfiguratorDialogDockerComponent,
ConfiguratorDialogNatComponent,
ConfiguratorDialogTracengComponent,
AddTracengTemplateComponent,
TracengPreferencesComponent,
TracengTemplatesComponent,
TracengTemplateDetailsComponent,
QemuImageCreatorComponent
], ],
imports: [ imports: [
BrowserModule, BrowserModule,
@ -428,7 +475,8 @@ if (environment.production) {
RotationValidator, RotationValidator,
MapSettingsService, MapSettingsService,
InfoService, InfoService,
ComputeService ComputeService,
TracengService
], ],
entryComponents: [ entryComponents: [
AddServerDialogComponent, AddServerDialogComponent,
@ -449,7 +497,22 @@ if (environment.production) {
SaveProjectDialogComponent, SaveProjectDialogComponent,
InfoDialogComponent, InfoDialogComponent,
ChangeSymbolDialogComponent, ChangeSymbolDialogComponent,
EditProjectDialogComponent EditProjectDialogComponent,
ConfiguratorDialogVpcsComponent,
ConfiguratorDialogEthernetHubComponent,
ConfiguratorDialogEthernetSwitchComponent,
ConfiguratorDialogSwitchComponent,
ConfiguratorDialogVirtualBoxComponent,
ConfiguratorDialogQemuComponent,
ConfiguratorDialogCloudComponent,
ConfiguratorDialogAtmSwitchComponent,
ConfiguratorDialogVmwareComponent,
ConfiguratorDialogIouComponent,
ConfiguratorDialogIosComponent,
ConfiguratorDialogDockerComponent,
ConfiguratorDialogNatComponent,
ConfiguratorDialogTracengComponent,
QemuImageCreatorComponent
], ],
bootstrap: [AppComponent] bootstrap: [AppComponent]
}) })

View File

@ -1,12 +1,71 @@
import { Label } from './label'; import { Label } from './label';
import { Port } from '../../models/port'; import { Port } from '../../models/port';
import { CustomAdapter } from '../../models/qemu/qemu-custom-adapter';
export class PortsMapping {
name: string;
interface?: string;
port_number: number;
type?: string;
}
export class Properties {
adapter_type: string;
adapters: number;
headless: boolean;
linked_clone: boolean;
on_close: string;
ram: number;
nvram: number;
usage: string;
use_any_adapter: boolean;
vmname: string;
ports_mapping: PortsMapping[];
mappings: any;
bios_image: string;
bios_image_md5sum?: any;
boot_priority: string;
cdrom_image: string;
cdrom_image_md5sum?: any;
cpu_throttling: number;
cpus: number;
hda_disk_image: string;
hda_disk_image_md5sum: string;
hda_disk_interface: string;
hdb_disk_image: string;
hdb_disk_image_md5sum?: any;
hdb_disk_interface: string;
hdc_disk_image: string;
hdc_disk_image_md5sum?: any;
hdc_disk_interface: string;
hdd_disk_image: string;
hdd_disk_image_md5sum?: any;
hdd_disk_interface: string;
initrd: string;
initrd_md5sum?: any;
kernel_command_line: string;
kernel_image: string;
kernel_image_md5sum?: any;
legacy_networking: boolean;
mac_address: string;
options: string;
platform: string;
process_priority: string;
qemu_path: string;
environment: string;
extra_hosts: string;
}
export class Node { export class Node {
command_line: string; command_line: string;
compute_id: string; compute_id: string;
console: number; console: number;
console_auto_start: boolean;
console_host: string; console_host: string;
console_type: string; console_type: string;
custom_adapters?: CustomAdapter[];
ethernet_adapters?: any;
serial_adapters?: any;
first_port_name: string; first_port_name: string;
height: number; height: number;
label: Label; label: Label;
@ -18,6 +77,7 @@ export class Node {
port_segment_size: number; port_segment_size: number;
ports: Port[]; ports: Port[];
project_id: string; project_id: string;
properties: Properties;
status: string; status: string;
symbol: string; symbol: string;
symbol_url: string; // @TODO: full URL to symbol, move to MapNode once converters are moved to app module symbol_url: string; // @TODO: full URL to symbol, move to MapNode once converters are moved to app module

View File

@ -82,7 +82,7 @@
placeholder="Ethernet interface" placeholder="Ethernet interface"
[ngModelOptions]="{standalone: true}" [ngModelOptions]="{standalone: true}"
[(ngModel)]="ethernetInterface"> [(ngModel)]="ethernetInterface">
<mat-option *ngFor="let type of " [value]="type"> <mat-option *ngFor="let type of ethernetInterfaces" [value]="type">
{{type}} {{type}}
</mat-option> </mat-option>
</mat-select> </mat-select>

View File

@ -57,69 +57,13 @@
</mat-form-field> </mat-form-field>
</form> </form>
</mat-expansion-panel> </mat-expansion-panel>
<mat-expansion-panel *ngIf="newPort"> <mat-expansion-panel>
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-title> <mat-panel-title>
Port settings Port settings
</mat-panel-title> </mat-panel-title>
</mat-expansion-panel-header> </mat-expansion-panel-header>
<table class="table" mat-table [dataSource]="dataSource"> <app-ports #ports [ethernetPorts]="ethernetSwitchTemplate.ports_mapping"></app-ports>
<ng-container matColumnDef="port_number">
<th mat-header-cell *matHeaderCellDef> Port number </th>
<td mat-cell *matCellDef="let element"> {{element.port_number}} </td>
</ng-container>
<ng-container matColumnDef="vlan">
<th mat-header-cell *matHeaderCellDef> VLAN </th>
<td mat-cell *matCellDef="let element"> {{element.vlan}} </td>
</ng-container>
<ng-container matColumnDef="type">
<th mat-header-cell *matHeaderCellDef> Type </th>
<td mat-cell *matCellDef="let element"> {{element.type}} </td>
</ng-container>
<ng-container matColumnDef="ethertype">
<th mat-header-cell *matHeaderCellDef> EtherType </th>
<td mat-cell *matCellDef="let element"> {{element.ethertype}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table><br/>
<mat-form-field class="form-field">
<input
matInput type="number"
[(ngModel)]="newPort.port_number"
placeholder="Port">
</mat-form-field>
<mat-form-field class="form-field">
<input
matInput type="number"
[(ngModel)]="newPort.vlan"
placeholder="VLAN">
</mat-form-field>
<mat-form-field class="select">
<mat-select
placeholder="Type"
[ngModelOptions]="{standalone: true}"
[(ngModel)]="newPort.type">
<mat-option *ngFor="let type of portTypes" [value]="type">
{{type}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="select">
<mat-select
placeholder="EtherType"
[ngModelOptions]="{standalone: true}"
[(ngModel)]="newPort.ethertype">
<mat-option *ngFor="let type of etherTypes" [value]="type">
{{type}}
</mat-option>
</mat-select>
</mat-form-field>
<button mat-button class="form-field" (click)="onAdd()">Add</button>
</mat-expansion-panel> </mat-expansion-panel>
</mat-accordion> </mat-accordion>
<div class="buttons-bar"> <div class="buttons-bar">

View File

@ -17,6 +17,7 @@ import { BuiltInTemplatesService } from '../../../../../services/built-in-templa
import { EthernetSwitchTemplate } from '../../../../../models/templates/ethernet-switch-template'; import { EthernetSwitchTemplate } from '../../../../../models/templates/ethernet-switch-template';
import { EthernetSwitchesTemplateDetailsComponent } from './ethernet-switches-template-details.component'; import { EthernetSwitchesTemplateDetailsComponent } from './ethernet-switches-template-details.component';
import { BuiltInTemplatesConfigurationService } from '../../../../../services/built-in-templates-configuration.service'; import { BuiltInTemplatesConfigurationService } from '../../../../../services/built-in-templates-configuration.service';
import { PortsComponent } from '../../../common/ports/ports.component';
export class MockedBuiltInTemplatesService { export class MockedBuiltInTemplatesService {
public getTemplate(server: Server, template_id: string) { public getTemplate(server: Server, template_id: string) {
@ -68,6 +69,7 @@ describe('EthernetSwitchesTemplateDetailsComponent', () => {
it('should call save template', () => { it('should call save template', () => {
spyOn(mockedBuiltInTemplatesService, 'saveTemplate').and.returnValue(of({} as EthernetSwitchTemplate)); spyOn(mockedBuiltInTemplatesService, 'saveTemplate').and.returnValue(of({} as EthernetSwitchTemplate));
component.portsComponent = {ethernetPorts: []} as PortsComponent;
component.inputForm.controls['templateName'].setValue('template name'); component.inputForm.controls['templateName'].setValue('template name');
component.inputForm.controls['defaultName'].setValue('default name'); component.inputForm.controls['defaultName'].setValue('default name');
component.inputForm.controls['symbol'].setValue('symbol'); component.inputForm.controls['symbol'].setValue('symbol');
@ -102,7 +104,7 @@ describe('EthernetSwitchesTemplateDetailsComponent', () => {
expect(mockedBuiltInTemplatesService.saveTemplate).not.toHaveBeenCalled(); expect(mockedBuiltInTemplatesService.saveTemplate).not.toHaveBeenCalled();
}); });
it('should call save template when symbol path is empty', () => { it('should not call save template when symbol path is empty', () => {
spyOn(mockedBuiltInTemplatesService, 'saveTemplate').and.returnValue(of({} as EthernetSwitchTemplate)); spyOn(mockedBuiltInTemplatesService, 'saveTemplate').and.returnValue(of({} as EthernetSwitchTemplate));
component.inputForm.controls['templateName'].setValue('template name'); component.inputForm.controls['templateName'].setValue('template name');
component.inputForm.controls['defaultName'].setValue('default name'); component.inputForm.controls['defaultName'].setValue('default name');

View File

@ -1,4 +1,4 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnInit, ViewChild } from "@angular/core";
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { ServerService } from '../../../../../services/server.service'; import { ServerService } from '../../../../../services/server.service';
import { Server } from '../../../../../models/server'; import { Server } from '../../../../../models/server';
@ -6,8 +6,8 @@ import { ToasterService } from '../../../../../services/toaster.service';
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms'; import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
import { BuiltInTemplatesService } from '../../../../../services/built-in-templates.service'; import { BuiltInTemplatesService } from '../../../../../services/built-in-templates.service';
import { EthernetSwitchTemplate } from '../../../../../models/templates/ethernet-switch-template'; import { EthernetSwitchTemplate } from '../../../../../models/templates/ethernet-switch-template';
import { PortsMappingEntity } from '../../../../../models/ethernetHub/ports-mapping-enity';
import { BuiltInTemplatesConfigurationService } from '../../../../../services/built-in-templates-configuration.service'; import { BuiltInTemplatesConfigurationService } from '../../../../../services/built-in-templates-configuration.service';
import { PortsComponent } from '../../../common/ports/ports.component';
@Component({ @Component({
@ -16,20 +16,13 @@ import { BuiltInTemplatesConfigurationService } from '../../../../../services/bu
styleUrls: ['./ethernet-switches-template-details.component.scss', '../../../preferences.component.scss'] styleUrls: ['./ethernet-switches-template-details.component.scss', '../../../preferences.component.scss']
}) })
export class EthernetSwitchesTemplateDetailsComponent implements OnInit { export class EthernetSwitchesTemplateDetailsComponent implements OnInit {
@ViewChild(PortsComponent, {static: false}) portsComponent: PortsComponent;
server: Server; server: Server;
ethernetSwitchTemplate: EthernetSwitchTemplate; ethernetSwitchTemplate: EthernetSwitchTemplate;
inputForm: FormGroup; inputForm: FormGroup;
ethernetPorts: PortsMappingEntity[] = [];
dataSource: PortsMappingEntity[] = [];
newPort: PortsMappingEntity;
isSymbolSelectionOpened: boolean = false; isSymbolSelectionOpened: boolean = false;
categories = []; categories = [];
consoleTypes: string[] = []; consoleTypes: string[] = [];
portTypes: string[] = [];
etherTypes: string[] = [];
displayedColumns: string[] = ['port_number', 'vlan', 'type', 'ethertype'];
constructor( constructor(
private route: ActivatedRoute, private route: ActivatedRoute,
@ -45,11 +38,6 @@ export class EthernetSwitchesTemplateDetailsComponent implements OnInit {
defaultName: new FormControl('', Validators.required), defaultName: new FormControl('', Validators.required),
symbol: new FormControl('', Validators.required) symbol: new FormControl('', Validators.required)
}); });
this.newPort = {
name: '',
port_number: 0,
};
} }
ngOnInit() { ngOnInit() {
@ -61,8 +49,6 @@ export class EthernetSwitchesTemplateDetailsComponent implements OnInit {
this.getConfiguration(); this.getConfiguration();
this.builtInTemplatesService.getTemplate(this.server, template_id).subscribe((ethernetSwitchTemplate: EthernetSwitchTemplate) => { this.builtInTemplatesService.getTemplate(this.server, template_id).subscribe((ethernetSwitchTemplate: EthernetSwitchTemplate) => {
this.ethernetSwitchTemplate = ethernetSwitchTemplate; this.ethernetSwitchTemplate = ethernetSwitchTemplate;
this.ethernetPorts = this.ethernetSwitchTemplate.ports_mapping;
this.dataSource = this.ethernetSwitchTemplate.ports_mapping;
}); });
}); });
} }
@ -70,18 +56,6 @@ export class EthernetSwitchesTemplateDetailsComponent implements OnInit {
getConfiguration() { getConfiguration() {
this.categories = this.builtInTemplatesConfigurationService.getCategoriesForEthernetSwitches(); this.categories = this.builtInTemplatesConfigurationService.getCategoriesForEthernetSwitches();
this.consoleTypes = this.builtInTemplatesConfigurationService.getConsoleTypesForEthernetSwitches(); this.consoleTypes = this.builtInTemplatesConfigurationService.getConsoleTypesForEthernetSwitches();
this.portTypes = this.builtInTemplatesConfigurationService.getPortTypesForEthernetSwitches();
this.etherTypes = this.builtInTemplatesConfigurationService.getEtherTypesForEthernetSwitches();
}
onAdd() {
this.ethernetPorts.push(this.newPort);
this.dataSource = [...this.ethernetPorts];
this.newPort = {
name: '',
port_number: 0,
};
} }
goBack() { goBack() {
@ -92,6 +66,7 @@ export class EthernetSwitchesTemplateDetailsComponent implements OnInit {
if (this.inputForm.invalid) { if (this.inputForm.invalid) {
this.toasterService.error(`Fill all required fields`); this.toasterService.error(`Fill all required fields`);
} else { } else {
this.ethernetSwitchTemplate.ports_mapping = this.portsComponent.ethernetPorts;
this.builtInTemplatesService.saveTemplate(this.server, this.ethernetSwitchTemplate).subscribe((ethernetSwitchTemplate: EthernetSwitchTemplate) => { this.builtInTemplatesService.saveTemplate(this.server, this.ethernetSwitchTemplate).subscribe((ethernetSwitchTemplate: EthernetSwitchTemplate) => {
this.toasterService.success("Changes saved"); this.toasterService.success("Changes saved");
}); });

View File

@ -0,0 +1,35 @@
<table class="table" mat-table [dataSource]="adapters">
<ng-container matColumnDef="adapter_number">
<th mat-header-cell *matHeaderCellDef> Adapter number </th>
<td mat-cell *matCellDef="let element"> Adapter {{element.adapter_number}} </td>
</ng-container>
<ng-container matColumnDef="port_name">
<th mat-header-cell *matHeaderCellDef> Port name </th>
<td mat-cell *matCellDef="let element"> Ethernet {{element.adapter_number}} </td>
</ng-container>
<ng-container matColumnDef="adapter_type">
<th mat-header-cell *matHeaderCellDef> Adapter type </th>
<td mat-cell *matCellDef="let element; let i = index;">
<mat-select placeholder="Type" [(ngModel)]="element.adapter_type">
<mat-option *ngFor="let type of networkTypes" [value]="type">
{{type}}
</mat-option>
</mat-select>
</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef> Actions </th>
<td mat-cell *matCellDef="let element">
<button mat-icon-button matTooltip="Delete adapter" (click)="delete(element)">
<mat-icon aria-label="Delete adapter">delete</mat-icon>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<button mat-button class="form-field" (click)="onAdd()">Add</button>

View File

@ -0,0 +1,28 @@
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { CustomAdapter } from '../../../../models/qemu/qemu-custom-adapter';
@Component({
selector: 'app-custom-adapters-table',
templateUrl: './custom-adapters-table.component.html',
styleUrls: ['../../preferences.component.scss']
})
export class CustomAdaptersTableComponent {
@Input() networkTypes = [];
@Input() displayedColumns: string[] = [];
@Input() adapters: CustomAdapter[] = [];
public numberOfAdapters: number;
onAdd() {
let adapter: CustomAdapter = {
adapter_number: this.adapters.length,
adapter_type: this.networkTypes[0]
}
this.adapters = this.adapters.concat([adapter]);
}
delete(adapter: CustomAdapter) {
this.adapters = this.adapters.filter(elem => elem!== adapter);
}
}

View File

@ -6,31 +6,12 @@
</div> </div>
<div class="default-content"> <div class="default-content">
<div class="container mat-elevation-z8"> <div class="container mat-elevation-z8">
<table class="table" mat-table [dataSource]="adapters"> <app-custom-adapters-table
<ng-container matColumnDef="adapter_number"> #customAdapters
<th mat-header-cell *matHeaderCellDef> Adapter number </th> [networkTypes]="networkTypes"
<td mat-cell *matCellDef="let element"> Adapter {{element.adapter_number}} </td> [displayedColumns]="displayedColumns"
</ng-container> [adapters]="adapters"
></app-custom-adapters-table>
<ng-container matColumnDef="port_name">
<th mat-header-cell *matHeaderCellDef> Port name </th>
<td mat-cell *matCellDef="let element"> Ethernet {{element.adapter_number}} </td>
</ng-container>
<ng-container matColumnDef="adapter_type">
<th mat-header-cell *matHeaderCellDef> Adapter type </th>
<td mat-cell *matCellDef="let element; let i = index;">
<mat-select placeholder="Type" [(ngModel)]="adapters[i].adapter_type">
<mat-option *ngFor="let type of networkTypes" [value]="type[0]">
{{type[1]}} ({{type[0]}})
</mat-option>
</mat-select>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
</div> </div>
<div class="buttons-bar"> <div class="buttons-bar">
<button mat-button (click)="cancelConfigureCustomAdapters()">Cancel</button> <button mat-button (click)="cancelConfigureCustomAdapters()">Cancel</button>

View File

@ -4,6 +4,7 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { NO_ERRORS_SCHEMA } from '@angular/core'; import { NO_ERRORS_SCHEMA } from '@angular/core';
import { CustomAdaptersComponent } from './custom-adapters.component'; import { CustomAdaptersComponent } from './custom-adapters.component';
import { CustomAdaptersTableComponent } from '../custom-adapters-table/custom-adapters-table.component';
describe('Custom adapters component', () => { describe('Custom adapters component', () => {
let component: CustomAdaptersComponent; let component: CustomAdaptersComponent;
@ -31,6 +32,7 @@ describe('Custom adapters component', () => {
it('should emit event when apply clicked', () => { it('should emit event when apply clicked', () => {
spyOn(component.saveConfigurationEmitter, 'emit'); spyOn(component.saveConfigurationEmitter, 'emit');
component.customAdapters = {adapters: []} as CustomAdaptersTableComponent;
component.configureCustomAdapters(); component.configureCustomAdapters();

View File

@ -1,5 +1,6 @@
import { Component, Input, Output, EventEmitter } from '@angular/core'; import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core';
import { CustomAdapter } from '../../../../models/qemu/qemu-custom-adapter'; import { CustomAdapter } from '../../../../models/qemu/qemu-custom-adapter';
import { CustomAdaptersTableComponent } from '../custom-adapters-table/custom-adapters-table.component';
@Component({ @Component({
@ -13,14 +14,30 @@ export class CustomAdaptersComponent {
@Output() closeConfiguratorEmitter = new EventEmitter<boolean>(); @Output() closeConfiguratorEmitter = new EventEmitter<boolean>();
@Output() saveConfigurationEmitter = new EventEmitter<CustomAdapter[]>(); @Output() saveConfigurationEmitter = new EventEmitter<CustomAdapter[]>();
@ViewChild("customAdapters", {static: false}) customAdapters: CustomAdaptersTableComponent;
public adapters: CustomAdapter[]; public adapters: CustomAdapter[];
public numberOfAdapters: number; public numberOfAdapters: number;
constructor() {
console.log(this.networkTypes);
}
cancelConfigureCustomAdapters(){ cancelConfigureCustomAdapters(){
this.closeConfiguratorEmitter.emit(false); this.closeConfiguratorEmitter.emit(false);
} }
configureCustomAdapters(){ configureCustomAdapters(){
this.adapters = [];
console.log(this.customAdapters);
this.customAdapters.adapters.forEach(n => {
this.adapters.push({
adapter_number: n.adapter_number,
adapter_type: n.adapter_type
})
});
this.saveConfigurationEmitter.emit(this.adapters); this.saveConfigurationEmitter.emit(this.adapters);
} }
} }

View File

@ -0,0 +1,66 @@
<table class="table" mat-table [dataSource]="ethernetPorts">
<ng-container matColumnDef="port_number">
<th mat-header-cell *matHeaderCellDef> Port number </th>
<td mat-cell *matCellDef="let element"> {{element.port_number}} </td>
</ng-container>
<ng-container matColumnDef="vlan">
<th mat-header-cell *matHeaderCellDef> VLAN </th>
<td mat-cell *matCellDef="let element"> {{element.vlan}} </td>
</ng-container>
<ng-container matColumnDef="type">
<th mat-header-cell *matHeaderCellDef> Type </th>
<td mat-cell *matCellDef="let element"> {{element.type}} </td>
</ng-container>
<ng-container matColumnDef="ethertype">
<th mat-header-cell *matHeaderCellDef> EtherType </th>
<td mat-cell *matCellDef="let element"> {{element.ethertype}} </td>
</ng-container>
<ng-container matColumnDef="action">
<th mat-header-cell *matHeaderCellDef> Actions </th>
<td mat-cell *matCellDef="let element">
<button mat-icon-button matTooltip="Delete port" (click)="delete(element)">
<mat-icon aria-label="Delete port">delete</mat-icon>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table><br/>
<mat-form-field class="form-field">
<input
matInput type="number"
[(ngModel)]="newPort.port_number"
placeholder="Port">
</mat-form-field>
<mat-form-field class="form-field">
<input
matInput type="number"
[(ngModel)]="newPort.vlan"
placeholder="VLAN">
</mat-form-field>
<mat-form-field class="select">
<mat-select
placeholder="Type"
[ngModelOptions]="{standalone: true}"
[(ngModel)]="newPort.type">
<mat-option *ngFor="let type of portTypes" [value]="type">
{{type}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="select">
<mat-select
placeholder="EtherType"
[ngModelOptions]="{standalone: true}"
[(ngModel)]="newPort.ethertype">
<mat-option *ngFor="let type of etherTypes" [value]="type">
{{type}}
</mat-option>
</mat-select>
</mat-form-field>
<button mat-button class="form-field" (click)="onAdd()">Add</button>

View File

@ -0,0 +1,48 @@
import { Component, OnInit, Input, Output, EventEmitter } from "@angular/core";
import { Server } from '../../../../models/server';
import { PortsMappingEntity } from '../../../../models/ethernetHub/ports-mapping-enity';
import { BuiltInTemplatesConfigurationService } from '../../../../services/built-in-templates-configuration.service';
@Component({
selector: 'app-ports',
templateUrl: './ports.component.html',
styleUrls: ['../../preferences.component.scss']
})
export class PortsComponent implements OnInit {
@Input() ethernetPorts: PortsMappingEntity[] = [];
newPort: PortsMappingEntity = {
name: '',
port_number: 0,
};
portTypes: string[] = [];
etherTypes: string[] = [];
displayedColumns: string[] = ['port_number', 'vlan', 'type', 'ethertype', 'action'];
constructor(
private builtInTemplatesConfigurationService: BuiltInTemplatesConfigurationService
) {}
ngOnInit() {
this.getConfiguration();
}
getConfiguration() {
this.etherTypes = this.builtInTemplatesConfigurationService.getEtherTypesForEthernetSwitches();
this.portTypes = this.builtInTemplatesConfigurationService.getPortTypesForEthernetSwitches();
}
onAdd() {
this.ethernetPorts.push(this.newPort);
this.newPort = {
name: '',
port_number: 0,
};
}
delete(port: PortsMappingEntity) {
this.ethernetPorts = this.ethernetPorts.filter(n => n !== port);
}
}

View File

@ -0,0 +1,59 @@
<table *ngIf="dataSourceUdp.length" class="table" mat-table [dataSource]="dataSourceUdp">
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef> Name </th>
<td mat-cell *matCellDef="let element"> {{element.name}} </td>
</ng-container>
<ng-container matColumnDef="rport">
<th mat-header-cell *matHeaderCellDef> Local port </th>
<td mat-cell *matCellDef="let element"> {{element.rport}} </td>
</ng-container>
<ng-container matColumnDef="rhost">
<th mat-header-cell *matHeaderCellDef> Type </th>
<td mat-cell *matCellDef="let element"> {{element.rhost}} </td>
</ng-container>
<ng-container matColumnDef="lport">
<th mat-header-cell *matHeaderCellDef> Remote port </th>
<td mat-cell *matCellDef="let element"> {{element.lport}} </td>
</ng-container>
<ng-container matColumnDef="action">
<th mat-header-cell *matHeaderCellDef> Actions </th>
<td mat-cell *matCellDef="let element">
<button mat-icon-button matTooltip="Delete port" (click)="delete(element)">
<mat-icon aria-label="Delete port">delete</mat-icon>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<br *ngIf="dataSourceUdp.length"/>
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="newPort.name"
placeholder="Name">
</mat-form-field>
<mat-form-field class="form-field">
<input
matInput type="number"
[(ngModel)]="newPort.lport"
placeholder="Local port">
</mat-form-field>
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="newPort.rhost"
placeholder="Remote host">
</mat-form-field>
<mat-form-field class="form-field">
<input
matInput type="number"
[(ngModel)]="newPort.rport"
placeholder="Remote port">
</mat-form-field>
<button mat-button class="form-field" (click)="onAddUdpInterface()">Add</button>

View File

@ -0,0 +1,47 @@
import { Component, OnInit, Input, Output, EventEmitter } from "@angular/core";
import { Server } from '../../../../models/server';
import { PortsMappingEntity } from '../../../../models/ethernetHub/ports-mapping-enity';
import { BuiltInTemplatesConfigurationService } from '../../../../services/built-in-templates-configuration.service';
@Component({
selector: 'app-udp-tunnels',
templateUrl: './udp-tunnels.component.html',
styleUrls: ['../../preferences.component.scss']
})
export class UdpTunnelsComponent implements OnInit {
@Input() dataSourceUdp: PortsMappingEntity[] = [];
displayedColumns: string[] = ['name', 'lport', 'rhost', 'rport', 'action'];
newPort: PortsMappingEntity = {
name: '',
port_number: 0,
};
portTypes: string[] = [];
etherTypes: string[] = [];
constructor(
private builtInTemplatesConfigurationService: BuiltInTemplatesConfigurationService
) {}
ngOnInit() {
this.getConfiguration();
}
getConfiguration() {
this.etherTypes = this.builtInTemplatesConfigurationService.getEtherTypesForEthernetSwitches();
this.portTypes = this.builtInTemplatesConfigurationService.getPortTypesForEthernetSwitches();
}
onAddUdpInterface() {
this.dataSourceUdp = this.dataSourceUdp.concat([this.newPort]);
this.newPort = {
name: '',
port_number: 0,
};
}
delete(port: PortsMappingEntity) {
this.dataSourceUdp = this.dataSourceUdp.filter(n => n !== port);
}
}

View File

@ -31,6 +31,9 @@
<mat-list-item routerLink="/server/{{serverId}}/preferences/docker/templates"> <mat-list-item routerLink="/server/{{serverId}}/preferences/docker/templates">
Docker Docker
</mat-list-item> </mat-list-item>
<!-- <mat-list-item routerLink="/server/{{serverId}}/preferences/traceng/templates">
TraceNG
</mat-list-item> -->
</mat-nav-list> </mat-nav-list>
</div> </div>
</div> </div>

View File

@ -31,7 +31,7 @@ export class QemuVmTemplateDetailsComponent implements OnInit {
binaries: QemuBinary[] = []; binaries: QemuBinary[] = [];
activateCpuThrottling: boolean = true; activateCpuThrottling: boolean = true;
isConfiguratorOpened: boolean = false; isConfiguratorOpened: boolean = false;
displayedColumns: string[] = ['adapter_number', 'port_name', 'adapter_type']; displayedColumns: string[] = ['adapter_number', 'port_name', 'adapter_type', 'actions'];
generalSettingsForm: FormGroup; generalSettingsForm: FormGroup;
@ViewChild("customAdaptersConfigurator", {static: false}) @ViewChild("customAdaptersConfigurator", {static: false})

View File

@ -0,0 +1,23 @@
<div class="content">
<div class="default-header">
<div class="row">
<h1 class="col">New VPCS node template</h1>
</div>
</div>
<div class="default-content">
<mat-card class="matCard">
<form [formGroup]="templateNameForm">
<mat-form-field class="form-field">
<input matInput formControlName="templateName" type="text" placeholder="Template name">
</mat-form-field>
<mat-form-field class="form-field">
<input matInput formControlName="ipAddress" type="text" placeholder="IP address">
</mat-form-field>
</form>
</mat-card>
<div class="buttons-bar">
<button mat-button class="cancel-button" (click)="goBack()">Cancel</button>
<button mat-raised-button color="primary" (click)="addTemplate()">Add template</button>
</div>
</div>
</div>

View File

@ -0,0 +1,67 @@
import { Component, OnInit } from "@angular/core";
import { Server } from '../../../../models/server';
import { ActivatedRoute, Router } from '@angular/router';
import { ServerService } from '../../../../services/server.service';
import { ToasterService } from '../../../../services/toaster.service';
import { v4 as uuid } from 'uuid';
import { TemplateMocksService } from '../../../../services/template-mocks.service';
import { FormGroup, FormBuilder, FormControl, Validators } from '@angular/forms';
import { TracengService } from '../../../../services/traceng.service';
import { TracengTemplate } from '../../../../models/templates/traceng-template';
@Component({
selector: 'app-add-traceng-template',
templateUrl: './add-traceng-template.component.html',
styleUrls: ['./add-traceng-template.component.scss', '../../preferences.component.scss']
})
export class AddTracengTemplateComponent implements OnInit {
server: Server;
templateName: string = '';
ipAddress: string = '';
templateNameForm: FormGroup
constructor(
private route: ActivatedRoute,
private serverService: ServerService,
private tracengService: TracengService,
private router: Router,
private toasterService: ToasterService,
private templateMocksService: TemplateMocksService,
private formBuilder: FormBuilder
) {
this.templateNameForm = this.formBuilder.group({
templateName: new FormControl(null, [Validators.required]),
ipAddress: new FormControl(null, [Validators.required])
});
}
ngOnInit() {
const server_id = this.route.snapshot.paramMap.get("server_id");
this.serverService.get(parseInt(server_id, 10)).then((server: Server) => {
this.server = server;
});
}
goBack() {
this.router.navigate(['/server', this.server.id, 'preferences', 'traceng', 'templates']);
}
addTemplate() {
if (!this.templateNameForm.invalid) {
this.templateName = this.templateNameForm.get('templateName').value;
this.ipAddress = this.templateNameForm.get('ipAddress').value;
let tracengTemplate: TracengTemplate = this.templateMocksService.getTracengTemplate();
tracengTemplate.template_id = uuid();
tracengTemplate.name = this.templateName;
tracengTemplate.ip_address = this.ipAddress;
this.tracengService.addTemplate(this.server, tracengTemplate).subscribe(() => {
this.goBack();
});
} else {
this.toasterService.error(`Fill all required fields`);
}
}
}

View File

@ -0,0 +1,12 @@
<div class="content">
<div class="default-header">
<div class="row">
<h1 class="col">TraceNG preferences</h1>
</div>
</div>
<div class="default-content">
<mat-form-field class="form-field">
<input matInput type="text" [(ngModel)]="tracengExecutable" placeholder="Path to TraceNG executable"/>
</mat-form-field>
</div>
</div>

View File

@ -0,0 +1,3 @@
.form-field {
width: 100%;
}

View File

@ -0,0 +1,32 @@
import { Component, OnInit } from "@angular/core";
import { ActivatedRoute } from '@angular/router';
import { Server } from '../../../../models/server';
import { ServerService } from '../../../../services/server.service';
@Component({
selector: 'app-traceng-preferences',
templateUrl: './traceng-preferences.component.html',
styleUrls: ['./traceng-preferences.component.scss']
})
export class TracengPreferencesComponent implements OnInit {
server: Server;
tracengExecutable: string;
constructor(
private route: ActivatedRoute,
private serverService: ServerService
) {}
ngOnInit() {
const server_id = this.route.snapshot.paramMap.get("server_id");
this.serverService.get(parseInt(server_id, 10)).then((server: Server) => {
this.server = server;
});
}
restoreDefaults(){
this.tracengExecutable = '';
}
}

View File

@ -0,0 +1,40 @@
<div class="content" [ngClass]="{ shadowed: isSymbolSelectionOpened }">
<div class="default-header">
<div class="row">
<h1 class="col">TraceNG device configuration</h1>
</div>
</div>
<div class="default-content" *ngIf="tracengTemplate">
<mat-card class="matCard">
<form [formGroup]="inputForm">
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="tracengTemplate.name"
formControlName="templateName"
placeholder="Template name">
</mat-form-field>
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="tracengTemplate.default_name_format"
formControlName="defaultName"
placeholder="Default name format">
</mat-form-field>
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="tracengTemplate.symbol"
formControlName="symbol"
placeholder="Symbol">
</mat-form-field>
<button mat-button class="symbolSelectionButton" (click)="chooseSymbol()">Choose symbol</button><br/><br/>
</form>
</mat-card>
<div class="buttons-bar">
<button class="cancel-button" (click)="goBack()" mat-button>Cancel</button>
<button mat-raised-button color="primary" (click)="onSave()">Save</button>
</div>
</div>
</div>
<app-symbols-menu *ngIf="isSymbolSelectionOpened && tracengTemplate" [server]="server" [symbol]="tracengTemplate.symbol" (symbolChangedEmitter)="symbolChanged($event)"></app-symbols-menu>

View File

@ -0,0 +1,71 @@
import { Component, OnInit } from "@angular/core";
import { ActivatedRoute, Router } from '@angular/router';
import { ServerService } from '../../../../services/server.service';
import { Server } from '../../../../models/server';
import { ToasterService } from '../../../../services/toaster.service';
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
import { TracengService } from '../../../../services/traceng.service';
import { TracengTemplate } from '../../../../models/templates/traceng-template';
@Component({
selector: 'app-traceng-template-details',
templateUrl: './traceng-template-details.component.html',
styleUrls: ['./traceng-template-details.component.scss', '../../preferences.component.scss']
})
export class TracengTemplateDetailsComponent implements OnInit {
server: Server;
tracengTemplate: TracengTemplate;
inputForm: FormGroup;
isSymbolSelectionOpened: boolean = false;
constructor(
private route: ActivatedRoute,
private serverService: ServerService,
private tracengService: TracengService,
private toasterService: ToasterService,
private formBuilder: FormBuilder,
private router: Router
) {
this.inputForm = this.formBuilder.group({
templateName: new FormControl('', Validators.required),
defaultName: new FormControl('', Validators.required),
symbol: new FormControl('', Validators.required)
});
}
ngOnInit() {
const server_id = this.route.snapshot.paramMap.get("server_id");
const template_id = this.route.snapshot.paramMap.get("template_id");
this.serverService.get(parseInt(server_id, 10)).then((server: Server) => {
this.server = server;
this.tracengService.getTemplate(this.server, template_id).subscribe((tracengTemplate: TracengTemplate) => {
this.tracengTemplate = tracengTemplate;
});
});
}
goBack() {
this.router.navigate(['/server', this.server.id, 'preferences', 'traceng', 'templates']);
}
onSave() {
if (this.inputForm.invalid) {
this.toasterService.error(`Fill all required fields`);
} else {
this.tracengService.saveTemplate(this.server, this.tracengTemplate).subscribe((tracengTemplate: TracengTemplate) => {
this.toasterService.success("Changes saved");
});
}
}
chooseSymbol() {
this.isSymbolSelectionOpened = !this.isSymbolSelectionOpened;
}
symbolChanged(chosenSymbol: string) {
this.isSymbolSelectionOpened = !this.isSymbolSelectionOpened;
this.tracengTemplate.symbol = chosenSymbol;
}
}

View File

@ -0,0 +1,32 @@
<div class="content">
<div class="default-header">
<div class="row">
<h1 class="col">TraceNG node templates</h1>
<button *ngIf="server" class="top-button" class="cancel-button" routerLink="/server/{{server.id}}/preferences" mat-button>Back</button>
<button *ngIf="server" class="top-button" routerLink="/server/{{server.id}}/preferences/traceng/addtemplate" mat-raised-button color="primary">Add TraceNG template</button>
</div>
</div>
<app-empty-templates-list *ngIf="!tracengTemplates.length"></app-empty-templates-list>
<div class="default-content" *ngIf="tracengTemplates.length">
<div class="listcontainer mat-elevation-z8">
<mat-nav-list *ngIf="server">
<div class="list-item" *ngFor='let template of tracengTemplates'>
<mat-list-item class="template-name" routerLink="{{template.template_id}}">{{template.name}}</mat-list-item>
<button mat-button class="menu-button" [matMenuTriggerFor]="menu">
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #menu="matMenu">
<button mat-menu-item (click)="deleteTemplate(template)">
<mat-icon>delete</mat-icon><span>Delete</span>
</button>
</mat-menu>
</div>
</mat-nav-list>
</div>
</div>
</div>
<app-delete-template
#deleteComponent
[server]="server"
(deleteEvent)="onDeleteEvent()">
</app-delete-template>

View File

@ -0,0 +1,46 @@
import { Component, OnInit, ViewChild } from "@angular/core";
import { Server } from '../../../../models/server';
import { ActivatedRoute } from '@angular/router';
import { ServerService } from '../../../../services/server.service';
import { DeleteTemplateComponent } from '../../common/delete-template-component/delete-template.component';
import { TracengTemplate } from '../../../../models/templates/traceng-template';
import { TracengService } from '../../../../services/traceng.service';
@Component({
selector: 'app-traceng-templates',
templateUrl: './traceng-templates.component.html',
styleUrls: ['./traceng-templates.component.scss', '../../preferences.component.scss']
})
export class TracengTemplatesComponent implements OnInit {
server: Server;
tracengTemplates: TracengTemplate[] = [];
@ViewChild(DeleteTemplateComponent, {static: false}) deleteComponent: DeleteTemplateComponent;
constructor(
private route: ActivatedRoute,
private serverService: ServerService,
private tracengService: TracengService
) {}
ngOnInit() {
const server_id = this.route.snapshot.paramMap.get("server_id");
this.serverService.get(parseInt(server_id, 10)).then((server: Server) => {
this.server = server;
this.getTemplates();
});
}
getTemplates() {
this.tracengService.getTemplates(this.server).subscribe((tracengTemplates: TracengTemplate[]) => {
this.tracengTemplates = tracengTemplates.filter((elem) => elem.template_type === 'traceng' && !elem.builtin);
});
}
deleteTemplate(template: TracengTemplate) {
this.deleteComponent.deleteItem(template.name, template.template_id);
}
onDeleteEvent() {
this.getTemplates();
}
}

View File

@ -24,7 +24,7 @@ export class VirtualBoxTemplateDetailsComponent implements OnInit {
onCloseOptions = []; onCloseOptions = [];
categories = []; categories = [];
networkTypes = []; networkTypes = [];
displayedColumns: string[] = ['adapter_number', 'port_name', 'adapter_type']; displayedColumns: string[] = ['adapter_number', 'port_name', 'adapter_type', 'actions'];
isConfiguratorOpened: boolean = false; isConfiguratorOpened: boolean = false;
generalSettingsForm: FormGroup; generalSettingsForm: FormGroup;
networkForm: FormGroup networkForm: FormGroup

View File

@ -20,7 +20,7 @@ export class VmwareTemplateDetailsComponent implements OnInit {
server: Server; server: Server;
vmwareTemplate: VmwareTemplate; vmwareTemplate: VmwareTemplate;
generalSettingsForm: FormGroup; generalSettingsForm: FormGroup;
displayedColumns: string[] = ['adapter_number', 'port_name', 'adapter_type']; displayedColumns: string[] = ['adapter_number', 'port_name', 'adapter_type', 'actions'];
isConfiguratorOpened: boolean = false; isConfiguratorOpened: boolean = false;
isSymbolSelectionOpened: boolean = false; isSymbolSelectionOpened: boolean = false;
consoleTypes: string[] = []; consoleTypes: string[] = [];

View File

@ -0,0 +1,4 @@
<button mat-menu-item (click)="configureNode()">
<mat-icon>settings_applications</mat-icon>
<span>Configure</span>
</button>

View File

@ -0,0 +1,71 @@
import { Component, Input, OnInit, OnChanges } from '@angular/core';
import { Server } from '../../../../../models/server';
import { Node } from '../../../../../cartography/models/node';
import { MatDialog, MatDialogRef } from '@angular/material';
import { ConfiguratorDialogVpcsComponent } from '../../../node-editors/configurator/vpcs/configurator-vpcs.component';
import { ConfiguratorDialogEthernetHubComponent } from '../../../node-editors/configurator/ethernet_hub/configurator-ethernet-hub.component';
import { ConfiguratorDialogEthernetSwitchComponent } from '../../../node-editors/configurator/ethernet-switch/configurator-ethernet-switch.component';
import { ConfiguratorDialogSwitchComponent } from '../../../node-editors/configurator/switch/configurator-switch.component';
import { ConfiguratorDialogVirtualBoxComponent } from '../../../node-editors/configurator/virtualbox/configurator-virtualbox.component';
import { ConfiguratorDialogQemuComponent } from '../../../node-editors/configurator/qemu/configurator-qemu.component';
import { ConfiguratorDialogCloudComponent } from '../../../node-editors/configurator/cloud/configurator-cloud.component';
import { ConfiguratorDialogAtmSwitchComponent } from '../../../node-editors/configurator/atm_switch/configurator-atm-switch.component';
import { ConfiguratorDialogVmwareComponent } from '../../../node-editors/configurator/vmware/configurator-vmware.component';
import { ConfiguratorDialogIouComponent } from '../../../node-editors/configurator/iou/configurator-iou.component';
import { ConfiguratorDialogIosComponent } from '../../../node-editors/configurator/ios/configurator-ios.component';
import { ConfiguratorDialogDockerComponent } from '../../../node-editors/configurator/docker/configurator-docker.component';
import { ConfiguratorDialogNatComponent } from '../../../node-editors/configurator/nat/configurator-nat.component';
import { ConfiguratorDialogTracengComponent } from '../../../node-editors/configurator/traceng/configurator-traceng.component';
@Component({
selector: 'app-config-node-action',
templateUrl: './config-action.component.html'
})
export class ConfigActionComponent {
@Input() server: Server;
@Input() node: Node;
private conf = {
autoFocus: false,
width: '800px'
};
dialogRef;
constructor(private dialog: MatDialog) {}
configureNode() {
if (this.node.node_type === 'vpcs') {
this.dialogRef = this.dialog.open(ConfiguratorDialogVpcsComponent, this.conf);
} else if (this.node.node_type === 'ethernet_hub') {
this.dialogRef = this.dialog.open(ConfiguratorDialogEthernetHubComponent, this.conf);
} else if (this.node.node_type === 'ethernet_switch') {
this.dialogRef = this.dialog.open(ConfiguratorDialogEthernetSwitchComponent, this.conf);
} else if (this.node.node_type === 'cloud') {
this.dialogRef = this.dialog.open(ConfiguratorDialogCloudComponent, this.conf);
} else if (this.node.node_type === 'dynamips') {
this.dialogRef = this.dialog.open(ConfiguratorDialogIosComponent, this.conf);
} else if (this.node.node_type === 'iou') {
this.dialogRef = this.dialog.open(ConfiguratorDialogIouComponent, this.conf);
} else if (this.node.node_type === 'qemu') {
this.dialogRef = this.dialog.open(ConfiguratorDialogQemuComponent, this.conf);
} else if (this.node.node_type === 'virtualbox') {
this.dialogRef = this.dialog.open(ConfiguratorDialogVirtualBoxComponent, this.conf);
} else if (this.node.node_type === 'vmware') {
this.dialogRef = this.dialog.open(ConfiguratorDialogVmwareComponent, this.conf);
} else if (this.node.node_type === 'docker') {
this.dialogRef = this.dialog.open(ConfiguratorDialogDockerComponent, this.conf);
} else if (this.node.node_type === 'nat') {
this.dialogRef = this.dialog.open(ConfiguratorDialogNatComponent, this.conf);
} else if (this.node.node_type === 'frame_relay_switch') {
this.dialogRef = this.dialog.open(ConfiguratorDialogSwitchComponent, this.conf);
} else if (this.node.node_type === 'atm_switch') {
this.dialogRef = this.dialog.open(ConfiguratorDialogAtmSwitchComponent, this.conf);
} else if (this.node.node_type === 'traceng') {
this.dialogRef = this.dialog.open(ConfiguratorDialogTracengComponent, this.conf);
}
let instance = this.dialogRef.componentInstance;
instance.server = this.server;
instance.node = this.node;
}
}

View File

@ -5,6 +5,10 @@
[server]="server" [server]="server"
[node]="nodes[0]" [node]="nodes[0]"
></app-show-node-action> ></app-show-node-action>
<app-config-node-action *ngIf="nodes.length===1"
[server]="server"
[node]="nodes[0]"
></app-config-node-action>
<app-start-node-action *ngIf="nodes.length && labels.length===0" [server]="server" [nodes]="nodes"></app-start-node-action> <app-start-node-action *ngIf="nodes.length && labels.length===0" [server]="server" [nodes]="nodes"></app-start-node-action>
<app-suspend-node-action *ngIf="nodes.length && labels.length===0" [server]="server" [nodes]="nodes"></app-suspend-node-action> <app-suspend-node-action *ngIf="nodes.length && labels.length===0" [server]="server" [nodes]="nodes"></app-suspend-node-action>
<app-stop-node-action *ngIf="nodes.length && labels.length===0" [server]="server" [nodes]="nodes"></app-stop-node-action> <app-stop-node-action *ngIf="nodes.length && labels.length===0" [server]="server" [nodes]="nodes"></app-stop-node-action>

View File

@ -0,0 +1,109 @@
<h1 mat-dialog-title>Configurator for node {{name}}</h1>
<div class="modal-form-container">
<div class="content">
<div class="default-content">
<mat-card>
<table *ngIf="nodeMappingsDataSource.length" class="table" mat-table [dataSource]="nodeMappingsDataSource">
<ng-container matColumnDef="portIn">
<th mat-header-cell *matHeaderCellDef> Port : VPI : VCI </th>
<td mat-cell *matCellDef="let element"> {{element.portIn}} </td>
</ng-container>
<ng-container matColumnDef="portOut">
<th mat-header-cell *matHeaderCellDef> Port : VPI : VCI </th>
<td mat-cell *matCellDef="let element"> {{element.portOut}} </td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef> Actions </th>
<td mat-cell *matCellDef="let element">
<button mat-icon-button matTooltip="Delete port" (click)="delete(element)">
<mat-icon aria-label="Delete port">delete</mat-icon>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table><br/>
<form [formGroup]="nameForm">
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="node.name"
formControlName="name"
placeholder="Name">
</mat-form-field>
</form>
<mat-checkbox [(ngModel)]="useVpiOnly">
Use VPI only (VP tunnel)
</mat-checkbox>
<form [formGroup]="inputForm">
Source
<mat-form-field class="form-field">
<input
matInput type="number"
[(ngModel)]="sourcePort"
formControlName="sourcePort"
placeholder="Port">
</mat-form-field>
</form>
<form [formGroup]="abstractForm">
<mat-form-field class="form-field">
<input
matInput type="number"
[(ngModel)]="sourceVpi"
formControlName="sourceVpi"
placeholder="VPI">
</mat-form-field>
</form>
<form [formGroup]="inputForm">
<mat-form-field class="form-field">
<input
matInput type="number"
[(ngModel)]="sourceVci"
formControlName="sourceVci"
placeholder="VCI">
</mat-form-field>
Destination
<mat-form-field class="form-field">
<input
matInput type="number"
[(ngModel)]="destinationPort"
formControlName="destinationPort"
placeholder="Port">
</mat-form-field>
</form>
<form [formGroup]="abstractForm">
<mat-form-field class="form-field">
<input
matInput type="number"
[(ngModel)]="destinationVpi"
formControlName="destinationVpi"
placeholder="VPI">
</mat-form-field>
</form>
<form [formGroup]="inputForm">
<mat-form-field class="form-field">
<input
matInput type="number"
[(ngModel)]="destinationVci"
formControlName="destinationVci"
placeholder="VCI">
</mat-form-field>
</form>
<button mat-button class="form-field" (click)="add()">Add</button>
</mat-card>
</div>
</div>
</div>
<div mat-dialog-actions>
<button mat-button (click)="onCancelClick()" color="accent">Cancel</button>
<button mat-button (click)="onSaveClick()" tabindex="2" mat-raised-button color="primary">Apply</button>
</div>

View File

@ -0,0 +1,164 @@
import { Component, OnInit, Input } from "@angular/core";
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
import { Node } from '../../../../../cartography/models/node';
import { Server } from '../../../../../models/server';
import { NodeService } from '../../../../../services/node.service';
import { ToasterService } from '../../../../../services/toaster.service';
import { MatDialogRef } from '@angular/material';
@Component({
selector: 'app-configurator-atm-switch',
templateUrl: './configurator-atm-switch.component.html',
styleUrls: ['../configurator.component.scss', '../../../../preferences/preferences.component.scss']
})
export class ConfiguratorDialogAtmSwitchComponent implements OnInit {
server: Server;
node: Node;
name: string;
nameForm: FormGroup;
inputForm: FormGroup;
abstractForm: FormGroup;
consoleTypes: string[] = [];
nodeMappings = new Map<string, string>();
nodeMappingsDataSource: NodeMapping[] = [];
dataSource = [];
displayedColumns = ['portIn', 'portOut', 'actions']
sourcePort: string = '';
sourceVpi: string = '';
sourceVci: string = '';
destinationPort: string = '';
destinationVpi: string = '';
destinationVci: string = '';
useVpiOnly: boolean = false;
constructor(
public dialogRef: MatDialogRef<ConfiguratorDialogAtmSwitchComponent>,
public nodeService: NodeService,
private toasterService: ToasterService,
private formBuilder: FormBuilder
) {
this.nameForm = this.formBuilder.group({
name: new FormControl('', Validators.required),
});
this.inputForm = this.formBuilder.group({
sourcePort: new FormControl('', Validators.required),
sourceVci: new FormControl('', Validators.required),
destinationPort: new FormControl('', Validators.required),
destinationVci: new FormControl('', Validators.required),
});
this.abstractForm = this.formBuilder.group({
sourceVpi: new FormControl('', Validators.required),
destinationVpi: new FormControl('', Validators.required)
});
}
ngOnInit() {
this.nodeService.getNode(this.server, this.node).subscribe((node: Node) => {
this.node = node;
this.name = node.name;
let mappings = node.properties.mappings;
Object.keys(mappings).forEach(key => {
this.nodeMappings.set(key, mappings[key]);
});
this.nodeMappings.forEach((value: string, key: string) => {
this.nodeMappingsDataSource.push({
portIn: key,
portOut: value
});
});
});
}
delete(elem: NodeMapping) {
this.nodeMappingsDataSource = this.nodeMappingsDataSource.filter(n => n !== elem);
}
add() {
if (this.inputForm.valid) {
let nodeMapping: NodeMapping;
if (!this.useVpiOnly) {
if (this.abstractForm.valid) {
nodeMapping = {
portIn: `${this.sourcePort}:${this.sourceVpi}:${this.sourceVci}`,
portOut: `${this.destinationPort}:${this.destinationVpi}:${this.destinationVci}`
};
if (this.nodeMappingsDataSource.filter(n => n.portIn === nodeMapping.portIn).length > 0) {
this.toasterService.error('Mapping already defined.');
} else {
this.nodeMappingsDataSource = this.nodeMappingsDataSource.concat([nodeMapping]);
this.clearUserInput();
}
} else {
this.toasterService.error('Fill all required fields.');
}
} else {
nodeMapping = {
portIn: `${this.sourcePort}:${this.sourceVci}`,
portOut: `${this.destinationPort}:${this.destinationVci}`
};
if (this.nodeMappingsDataSource.filter(n => n.portIn === nodeMapping.portIn).length > 0) {
this.toasterService.error('Mapping already defined.');
} else {
this.nodeMappingsDataSource = this.nodeMappingsDataSource.concat([nodeMapping]);
this.clearUserInput();
}
}
} else {
this.toasterService.error('Fill all required fields.');
}
}
clearUserInput() {
this.sourcePort = '0';
this.sourceVpi = '0';
this.sourceVci = '0';
this.destinationPort = '0';
this.destinationVpi = '0';
this.sourceVci = '0';
}
strMapToObj(strMap) {
let obj = Object.create(null);
for (let [k,v] of strMap) {
obj[k] = v;
}
return obj;
}
onSaveClick() {
if (this.nameForm.valid) {
this.nodeMappings.clear();
this.nodeMappingsDataSource.forEach(elem => {
this.nodeMappings.set(elem.portIn, elem.portOut);
});
this.node.properties.mappings = Array.from(this.nodeMappings).reduce((obj, [key, value]) => (Object.assign(obj, { [key]: value })), {});
this.nodeService.updateNode(this.server, this.node).subscribe(() => {
this.toasterService.success(`Node ${this.node.name} updated.`);
this.onCancelClick();
});
} else {
this.toasterService.error(`Fill all required fields.`);
}
}
onCancelClick() {
this.dialogRef.close();
}
}
export interface NodeMapping {
portIn: string,
portOut: string
}

View File

@ -0,0 +1,89 @@
<h1 mat-dialog-title>Configurator for node {{name}}</h1>
<div class="modal-form-container">
<div class="content">
<div class="default-content">
<mat-card class="matCard">
<mat-tab-group *ngIf="name">
<mat-tab label="Ethernet interfaces">
<div class="form-field" *ngFor="let port of portsMappingEthernet">
<div class="form-field">{{port.name}}</div><br/><br/>
</div>
<mat-form-field class="select">
<mat-select
placeholder="Ethernet interface"
[ngModelOptions]="{standalone: true}"
[(ngModel)]="ethernetInterface">
<mat-option *ngFor="let type of ethernetInterfaces" [value]="type">
{{type}}
</mat-option>
</mat-select>
</mat-form-field>
<button mat-button class="form-field" (click)="onAddEthernetInterface()">Add</button>
</mat-tab>
<mat-tab label="TAP interfaces">
<div class="form-field" *ngFor="let port of portsMappingTap">
<div class="form-field">{{port.name}}</div><br/><br/>
</div>
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="tapInterface"
placeholder="TAP interface">
</mat-form-field>
<button mat-button class="form-field" (click)="onAddTapInterface()">Add</button>
</mat-tab>
<mat-tab label="UDP tunnels">
<app-udp-tunnels #udpTunnels [dataSourceUdp]="portsMappingUdp" ></app-udp-tunnels>
</mat-tab>
<mat-tab label="Miscellaneous">
<form [formGroup]="generalSettingsForm">
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="node.name"
formControlName="name"
placeholder="Name">
</mat-form-field>
</form>
<mat-form-field class="select">
<mat-select
placeholder="Console type"
[(ngModel)]="node.console_type">
<mat-option *ngFor="let type of consoleTypes" [value]="type">
{{type}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="node.properties.remote_console_host"
placeholder="Console host">
</mat-form-field>
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="node.properties.remote_console_port"
placeholder="Console port">
</mat-form-field>
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="node.properties.remote_console_http_path"
placeholder="Console HTTP path">
</mat-form-field>
</mat-tab>
</mat-tab-group>
</mat-card>
</div>
</div>
</div>
<div mat-dialog-actions>
<button mat-button (click)="onCancelClick()" color="accent">Cancel</button>
<button mat-button (click)="onSaveClick()" tabindex="2" mat-raised-button color="primary">Apply</button>
</div>

View File

@ -0,0 +1,105 @@
import { Component, OnInit, Input, ViewChild } from "@angular/core";
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
import { Node } from '../../../../../cartography/models/node';
import { Server } from '../../../../../models/server';
import { NodeService } from '../../../../../services/node.service';
import { ToasterService } from '../../../../../services/toaster.service';
import { MatDialogRef } from '@angular/material';
import { CustomAdaptersTableComponent } from '../../../../../components/preferences/common/custom-adapters-table/custom-adapters-table.component';
import { QemuBinary } from '../../../../../models/qemu/qemu-binary';
import { BuiltInTemplatesConfigurationService } from '../../../../../services/built-in-templates-configuration.service';
import { PortsMappingEntity } from '../../../../../models/ethernetHub/ports-mapping-enity';
import { UdpTunnelsComponent } from '../../../../../components/preferences/common/udp-tunnels/udp-tunnels.component';
@Component({
selector: 'app-configurator-cloud',
templateUrl: './configurator-cloud.component.html',
styleUrls: ['../configurator.component.scss']
})
export class ConfiguratorDialogCloudComponent implements OnInit {
server: Server;
node: Node;
name: string;
generalSettingsForm: FormGroup;
consoleTypes: string[] = [];
binaries: QemuBinary[] = [];
onCloseOptions = [];
bootPriorities = [];
diskInterfaces: string[] = [];
portsMappingEthernet: PortsMappingEntity[] = [];
portsMappingTap: PortsMappingEntity[] = [];
portsMappingUdp: PortsMappingEntity[] = [];
displayedColumns: string[] = ['adapter_number', 'port_name', 'adapter_type', 'actions'];
networkTypes = [];
tapInterface: string = '';
ethernetInterface: string = '';
ethernetInterfaces: string[] = ['Ethernet 2', 'Ethernet 3'];
@ViewChild("udpTunnels", {static: false}) udpTunnels: UdpTunnelsComponent;
constructor(
public dialogRef: MatDialogRef<ConfiguratorDialogCloudComponent>,
public nodeService: NodeService,
private toasterService: ToasterService,
private formBuilder: FormBuilder,
private builtInTemplatesConfigurationService: BuiltInTemplatesConfigurationService,
) {
this.generalSettingsForm = this.formBuilder.group({
name: new FormControl('', Validators.required)
});
}
ngOnInit() {
this.nodeService.getNode(this.server, this.node).subscribe((node: Node) => {
this.node = node;
this.name = node.name;
this.getConfiguration();
this.portsMappingEthernet = this.node.properties.ports_mapping
.filter((elem) => elem.type === 'ethernet');
this.portsMappingTap = this.node.properties.ports_mapping
.filter((elem) => elem.type === 'tap');
this.portsMappingUdp = this.node.properties.ports_mapping
.filter((elem) => elem.type === 'udp');
})
}
getConfiguration() {
this.consoleTypes = this.builtInTemplatesConfigurationService.getConsoleTypesForCloudNodes();
}
onAddTapInterface() {
if (this.tapInterface) {
this.portsMappingTap.push({
interface: this.tapInterface,
name: this.tapInterface,
port_number: 0,
type: "tap"
});
}
}
onSaveClick() {
if (this.generalSettingsForm.valid) {
this.portsMappingUdp = this.udpTunnels.dataSourceUdp;
this.node.properties.ports_mapping = this.portsMappingUdp.concat(this.portsMappingEthernet).concat(this.portsMappingTap);
this.nodeService. updateNode(this.server, this.node).subscribe(() => {
this.toasterService.success(`Node ${this.node.name} updated.`);
this.onCancelClick();
});
} else {
this.toasterService.error(`Fill all required fields.`);
}
}
onCancelClick() {
this.dialogRef.close();
}
}

View File

@ -0,0 +1,52 @@
.form-field {
width: 100%;
}
.configButton {
width: 100%;
}
.select {
width: 100%;
}
.default-content {
max-height: 400px;
overflow-y: scroll;
scrollbar-color: darkgrey #263238;
scrollbar-width: thin;
}
.file-button {
width: 18%;
}
.create-button {
width: 100%;
}
.file-name-form-field {
padding-left: 2%;
width: 80%;
}
.nonvisible {
display: none;
}
mat-radio-button {
margin-right: 10px;
}
::-webkit-scrollbar {
width: 0.5em;
}
::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
}
::-webkit-scrollbar-thumb {
background-color: darkgrey;
outline: 1px solid #263238;
}

View File

@ -0,0 +1,54 @@
<h1 mat-dialog-title>Configurator for node {{name}}</h1>
<div class="modal-form-container">
<div class="content">
<div class="default-content">
<mat-card class="matCard">
<mat-tab-group *ngIf="name">
<mat-tab label="General settings">
<br/><form [formGroup]="generalSettingsForm">
<mat-form-field class="form-field">
<input matInput formControlName="name" type="text" [(ngModel)]="node.name" placeholder="Name">
</mat-form-field>
<mat-form-field class="form-field">
<input formControlName="adapter" matInput type="number" [(ngModel)]="node.properties.adapters" placeholder="Adapters">
</mat-form-field>
<mat-form-field class="select">
<mat-select [ngModelOptions]="{standalone: true}" placeholder="Console type" [(ngModel)]="node.console_type">
<mat-option *ngFor="let type of consoleTypes" [value]="type">
{{type}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-checkbox [ngModelOptions]="{standalone: true}" [(ngModel)]="node.console_auto_start">
Auto start console
</mat-checkbox>
</form>
<h6>Environment</h6>
<mat-form-field class="form-field">
<textarea matInput type="text" [(ngModel)]="node.properties.environment"></textarea>
</mat-form-field>
</mat-tab>
<mat-tab label="Advanced">
<h6>Extra hosts</h6>
<mat-form-field class="form-field">
<textarea matInput type="text" [(ngModel)]="node.properties.extra_hosts"></textarea>
</mat-form-field>
</mat-tab>
<mat-tab label="Usage">
<mat-form-field class="form-field">
<textarea matInput type="text" [(ngModel)]="node.properties.usage"></textarea>
</mat-form-field>
</mat-tab>
</mat-tab-group>
</mat-card>
</div>
</div>
</div>
<div mat-dialog-actions>
<button mat-button (click)="onCancelClick()" color="accent">Cancel</button>
<button mat-button (click)="onSaveClick()" tabindex="2" mat-raised-button color="primary">Apply</button>
</div>

View File

@ -0,0 +1,63 @@
import { Component, OnInit, Input, ViewChild } from "@angular/core";
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
import { Node } from '../../../../../cartography/models/node';
import { Server } from '../../../../../models/server';
import { NodeService } from '../../../../../services/node.service';
import { ToasterService } from '../../../../../services/toaster.service';
import { MatDialogRef } from '@angular/material';
import { DockerConfigurationService } from '../../../../../services/docker-configuration.service';
@Component({
selector: 'app-configurator-docker',
templateUrl: './configurator-docker.component.html',
styleUrls: ['../configurator.component.scss']
})
export class ConfiguratorDialogDockerComponent implements OnInit {
server: Server;
node: Node;
name: string;
generalSettingsForm: FormGroup;
consoleTypes: string[] = [];
constructor(
public dialogRef: MatDialogRef<ConfiguratorDialogDockerComponent>,
public nodeService: NodeService,
private toasterService: ToasterService,
private formBuilder: FormBuilder,
private dockerConfigurationService: DockerConfigurationService
) {
this.generalSettingsForm = this.formBuilder.group({
name: new FormControl('', Validators.required),
adapter: new FormControl('', Validators.required)
});
}
ngOnInit() {
this.nodeService.getNode(this.server, this.node).subscribe((node: Node) => {
this.node = node;
this.name = node.name;
this.getConfiguration();
})
}
getConfiguration() {
this.consoleTypes = this.dockerConfigurationService.getConsoleTypes();
}
onSaveClick() {
if (this.generalSettingsForm.valid) {
this.nodeService.updateNode(this.server, this.node).subscribe(() => {
this.toasterService.success(`Node ${this.node.name} updated.`);
this.onCancelClick();
});
} else {
this.toasterService.error(`Fill all required fields.`);
}
}
onCancelClick() {
this.dialogRef.close();
}
}

View File

@ -0,0 +1,35 @@
<h1 mat-dialog-title>Configurator for node {{name}}</h1>
<div class="modal-form-container">
<div class="content">
<div class="default-content" *ngIf="node">
<mat-card class="matCard">
<form [formGroup]="inputForm">
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="node.name"
formControlName="name"
placeholder="Name">
</mat-form-field>
<mat-form-field class="select">
<mat-select
placeholder="Console type"
[ngModelOptions]="{standalone: true}"
[(ngModel)]="node.console_type">
<mat-option *ngFor="let type of consoleTypes" [value]="type">
{{type}}
</mat-option>
</mat-select>
</mat-form-field>
</form>
<app-ports *ngIf="node.properties" #ports [ethernetPorts]="node.properties.ports_mapping"></app-ports>
</mat-card>
</div>
</div>
</div>
<div mat-dialog-actions>
<button mat-button (click)="onCancelClick()" color="accent">Cancel</button>
<button mat-button (click)="onSaveClick()" tabindex="2" mat-raised-button color="primary">Apply</button>
</div>

View File

@ -0,0 +1,64 @@
import { Component, OnInit, Input, ViewChild } from "@angular/core";
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
import { Node } from '../../../../../cartography/models/node';
import { Server } from '../../../../../models/server';
import { NodeService } from '../../../../../services/node.service';
import { ToasterService } from '../../../../../services/toaster.service';
import { MatDialogRef } from '@angular/material';
import { BuiltInTemplatesConfigurationService } from '../../../../../services/built-in-templates-configuration.service';
import { PortsComponent } from '../../../../../components/preferences/common/ports/ports.component';
@Component({
selector: 'app-configurator-ethernet-switch',
templateUrl: './configurator-ethernet-switch.component.html',
styleUrls: ['../configurator.component.scss']
})
export class ConfiguratorDialogEthernetSwitchComponent implements OnInit {
@ViewChild(PortsComponent, {static: false}) portsComponent: PortsComponent;
server: Server;
node: Node;
name: string;
inputForm: FormGroup;
consoleTypes: string[] = [];
constructor(
public dialogRef: MatDialogRef<ConfiguratorDialogEthernetSwitchComponent>,
public nodeService: NodeService,
private toasterService: ToasterService,
private formBuilder: FormBuilder,
private ethernetSwitchesConfigurationService: BuiltInTemplatesConfigurationService
) {
this.inputForm = this.formBuilder.group({
name: new FormControl('', Validators.required)
});
}
ngOnInit() {
this.nodeService.getNode(this.server, this.node).subscribe((node: Node) => {
this.node = node;
this.name = this.node.name;
this.getConfiguration();
})
}
getConfiguration() {
this.consoleTypes = this.ethernetSwitchesConfigurationService.getConsoleTypesForEthernetSwitches();
}
onSaveClick() {
if (this.inputForm.valid) {
this.node.properties.ports_mapping = this.portsComponent.ethernetPorts;
this.nodeService.updateNode(this.server, this.node).subscribe(() => {
this.toasterService.success(`Node ${this.node.name} updated.`);
this.onCancelClick();
});
} else {
this.toasterService.error(`Fill all required fields.`);
}
}
onCancelClick() {
this.dialogRef.close();
}
}

View File

@ -0,0 +1,31 @@
<h1 mat-dialog-title>Configurator for node {{name}}</h1>
<div class="modal-form-container">
<div class="content">
<div class="default-content">
<mat-card class="matCard">
<form [formGroup]="inputForm">
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="node.name"
formControlName="name"
placeholder="Name">
</mat-form-field>
<mat-form-field class="form-field">
<input
matInput type="number"
[(ngModel)]="numberOfPorts"
[ngModelOptions]="{standalone: true}"
placeholder="Number of ports">
</mat-form-field>
</form>
</mat-card>
</div>
</div>
</div>
<div mat-dialog-actions>
<button mat-button (click)="onCancelClick()" color="accent">Cancel</button>
<button mat-button (click)="onSaveClick()" tabindex="2" mat-raised-button color="primary">Apply</button>
</div>

View File

@ -0,0 +1,73 @@
import { Component, OnInit, Input } from "@angular/core";
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
import { VpcsConfigurationService } from '../../../../../services/vpcs-configuration.service';
import { Node } from '../../../../../cartography/models/node';
import { Server } from '../../../../../models/server';
import { NodeService } from '../../../../../services/node.service';
import { ToasterService } from '../../../../../services/toaster.service';
import { MatDialogRef } from '@angular/material';
@Component({
selector: 'app-configurator-ethernet-hub',
templateUrl: './configurator-ethernet-hub.component.html',
styleUrls: ['../configurator.component.scss']
})
export class ConfiguratorDialogEthernetHubComponent implements OnInit {
server: Server;
node: Node;
numberOfPorts: number;
inputForm: FormGroup;
consoleTypes: string[] = [];
categories = [];
name: string;
constructor(
public dialogRef: MatDialogRef<ConfiguratorDialogEthernetHubComponent>,
public nodeService: NodeService,
private toasterService: ToasterService,
private formBuilder: FormBuilder,
private vpcsConfigurationService: VpcsConfigurationService
) {
this.inputForm = this.formBuilder.group({
name: new FormControl('', Validators.required)
});
}
ngOnInit() {
this.nodeService.getNode(this.server, this.node).subscribe((node: Node) => {
this.node = node;
this.name = this.node.name;
this.numberOfPorts = this.node.ports.length;
this.getConfiguration();
})
}
getConfiguration() {
this.consoleTypes = this.vpcsConfigurationService.getConsoleTypes();
this.categories = this.vpcsConfigurationService.getCategories();
}
onSaveClick() {
if (this.inputForm.valid) {
this.node.properties.ports_mapping = [];
for(let i=0; i<this.numberOfPorts; i++){
this.node.properties.ports_mapping.push({
name: `Ethernet${i}`,
port_number: i
});
}
this.nodeService.updateNode(this.server, this.node).subscribe(() => {
this.toasterService.success(`Node ${this.node.name} updated.`);
this.onCancelClick();
});
} else {
this.toasterService.error(`Fill all required fields.`)
}
}
onCancelClick() {
this.dialogRef.close();
}
}

View File

@ -0,0 +1,51 @@
<h1 mat-dialog-title>Configurator for node {{name}}</h1>
<div class="modal-form-container">
<div class="content">
<div class="default-content">
<mat-card class="matCard">
<mat-tab-group *ngIf="name">
<mat-tab label="General settings">
<br/><form [formGroup]="generalSettingsForm">
<mat-form-field class="form-field">
<input matInput type="text" formControlName="name" [(ngModel)]="node.name" placeholder="Name">
</mat-form-field>
</form>
<mat-form-field class="select">
<mat-select placeholder="Console type" [(ngModel)]="node.console_type">
<mat-option *ngFor="let type of consoleTypes" [value]="type">
{{type}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-checkbox [(ngModel)]="node.console_auto_start">
Auto start console
</mat-checkbox><br/>
</mat-tab>
<mat-tab label="Memories and disks">
<br/><form [formGroup]="memoryForm">
<mat-form-field class="form-field">
<input matInput type="number" formControlName="ram" [(ngModel)]="node.properties.ram" placeholder="RAM size">
</mat-form-field>
<mat-form-field class="form-field">
<input matInput type="number" formControlName="nvram" [(ngModel)]="node.properties.nvram" placeholder="NVRAM size">
</mat-form-field>
</form>
</mat-tab>
<mat-tab label="Usage">
<mat-form-field class="form-field">
<textarea matInput type="text" [(ngModel)]="node.properties.usage"></textarea>
</mat-form-field>
</mat-tab>
</mat-tab-group>
</mat-card>
</div>
</div>
</div>
<div mat-dialog-actions>
<button mat-button (click)="onCancelClick()" color="accent">Cancel</button>
<button mat-button (click)="onSaveClick()" tabindex="2" mat-raised-button color="primary">Apply</button>
</div>

View File

@ -0,0 +1,67 @@
import { Component, OnInit, Input, ViewChild } from "@angular/core";
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
import { Node } from '../../../../../cartography/models/node';
import { Server } from '../../../../../models/server';
import { NodeService } from '../../../../../services/node.service';
import { ToasterService } from '../../../../../services/toaster.service';
import { MatDialogRef } from '@angular/material';
import { IosConfigurationService } from '../../../../../services/ios-configuration.service';
@Component({
selector: 'app-configurator-ios',
templateUrl: './configurator-ios.component.html',
styleUrls: ['../configurator.component.scss']
})
export class ConfiguratorDialogIosComponent implements OnInit {
server: Server;
node: Node;
name: string;
generalSettingsForm: FormGroup;
memoryForm: FormGroup;
consoleTypes: string[] = [];
constructor(
public dialogRef: MatDialogRef<ConfiguratorDialogIosComponent>,
public nodeService: NodeService,
private toasterService: ToasterService,
private formBuilder: FormBuilder,
private configurationService: IosConfigurationService
) {
this.generalSettingsForm = this.formBuilder.group({
name: new FormControl('', Validators.required)
});
this.memoryForm = this.formBuilder.group({
ram: new FormControl('', Validators.required),
nvram: new FormControl('', Validators.required)
});
}
ngOnInit() {
this.nodeService.getNode(this.server, this.node).subscribe((node: Node) => {
this.node = node;
this.name = node.name;
this.getConfiguration();
});
}
getConfiguration() {
this.consoleTypes = this.configurationService.getConsoleTypes();
}
onSaveClick() {
if (this.generalSettingsForm.valid && this.memoryForm.valid) {
this.nodeService. updateNode(this.server, this.node).subscribe(() => {
this.toasterService.success(`Node ${this.node.name} updated.`);
this.onCancelClick();
});
} else {
this.toasterService.error(`Fill all required fields.`);
}
}
onCancelClick() {
this.dialogRef.close();
}
}

View File

@ -0,0 +1,57 @@
<h1 mat-dialog-title>Configurator for node {{name}}</h1>
<div class="modal-form-container">
<div class="content">
<div class="default-content">
<mat-card class="matCard">
<mat-tab-group *ngIf="name">
<mat-tab label="General settings">
<br/><form [formGroup]="generalSettingsForm">
<mat-form-field class="form-field">
<input matInput type="text" formControlName="name" [(ngModel)]="node.name" placeholder="Name">
</mat-form-field>
</form>
<mat-form-field class="select">
<mat-select placeholder="Console type" [(ngModel)]="node.console_type">
<mat-option *ngFor="let type of consoleTypes" [value]="type">
{{type}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-checkbox [(ngModel)]="node.console_auto_start">
Auto start console
</mat-checkbox><br/>
<mat-form-field class="form-field">
<input matInput type="number" [(ngModel)]="node.properties.ram" placeholder="RAM size">
</mat-form-field>
<mat-form-field class="form-field">
<input matInput type="number" [(ngModel)]="node.properties.nvram" placeholder="NVRAM size">
</mat-form-field>
</mat-tab>
<mat-tab label="Network">
<br/><form [formGroup]="networkForm">
<mat-form-field class="form-field">
<input matInput formControlName="ethernetAdapters" type="number" [(ngModel)]="node.ethernet_adapters" placeholder="Ethernet adapters">
</mat-form-field>
<mat-form-field class="form-field">
<input matInput formControlName="serialAdapters" type="number" [(ngModel)]="node.serial_adapters" placeholder="Serial adapters">
</mat-form-field>
</form>
</mat-tab>
<mat-tab label="Usage">
<mat-form-field class="form-field">
<textarea matInput type="text" [(ngModel)]="node.properties.usage"></textarea>
</mat-form-field>
</mat-tab>
</mat-tab-group>
</mat-card>
</div>
</div>
</div>
<div mat-dialog-actions>
<button mat-button (click)="onCancelClick()" color="accent">Cancel</button>
<button mat-button (click)="onSaveClick()" tabindex="2" mat-raised-button color="primary">Apply</button>
</div>

View File

@ -0,0 +1,67 @@
import { Component, OnInit, Input, ViewChild } from "@angular/core";
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
import { Node } from '../../../../../cartography/models/node';
import { Server } from '../../../../../models/server';
import { NodeService } from '../../../../../services/node.service';
import { ToasterService } from '../../../../../services/toaster.service';
import { MatDialogRef } from '@angular/material';
import { IouConfigurationService } from '../../../../../services/iou-configuration.service';
@Component({
selector: 'app-configurator-iou',
templateUrl: './configurator-iou.component.html',
styleUrls: ['../configurator.component.scss']
})
export class ConfiguratorDialogIouComponent implements OnInit {
server: Server;
node: Node;
name: string;
generalSettingsForm: FormGroup;
networkForm: FormGroup;
consoleTypes: string[] = [];
constructor(
public dialogRef: MatDialogRef<ConfiguratorDialogIouComponent>,
public nodeService: NodeService,
private toasterService: ToasterService,
private formBuilder: FormBuilder,
private configurationService: IouConfigurationService
) {
this.generalSettingsForm = this.formBuilder.group({
name: new FormControl('', Validators.required)
});
this.networkForm = this.formBuilder.group({
ethernetAdapters: new FormControl('', Validators.required),
serialAdapters: new FormControl('', Validators.required)
});
}
ngOnInit() {
this.nodeService.getNode(this.server, this.node).subscribe((node: Node) => {
this.node = node;
this.name = node.name;
this.getConfiguration();
});
}
getConfiguration() {
this.consoleTypes = this.configurationService.getConsoleTypes();
}
onSaveClick() {
if (this.generalSettingsForm.valid && this.networkForm.valid) {
this.nodeService. updateNode(this.server, this.node).subscribe(() => {
this.toasterService.success(`Node ${this.node.name} updated.`);
this.onCancelClick();
});
} else {
this.toasterService.error(`Fill all required fields.`);
}
}
onCancelClick() {
this.dialogRef.close();
}
}

View File

@ -0,0 +1,24 @@
<h1 mat-dialog-title>Configurator for node {{name}}</h1>
<div class="modal-form-container">
<div class="content">
<div class="default-content">
<mat-card class="matCard">
<form [formGroup]="generalSettingsForm">
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="node.name"
formControlName="name"
placeholder="Name">
</mat-form-field>
</form>
</mat-card>
</div>
</div>
</div>
<div mat-dialog-actions>
<button mat-button (click)="onCancelClick()" color="accent">Cancel</button>
<button mat-button (click)="onSaveClick()" tabindex="2" mat-raised-button color="primary">Apply</button>
</div>

View File

@ -0,0 +1,53 @@
import { Component, OnInit, Input, ViewChild } from "@angular/core";
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
import { Node } from '../../../../../cartography/models/node';
import { Server } from '../../../../../models/server';
import { NodeService } from '../../../../../services/node.service';
import { ToasterService } from '../../../../../services/toaster.service';
import { MatDialogRef } from '@angular/material';
@Component({
selector: 'app-configurator-nat',
templateUrl: './configurator-nat.component.html',
styleUrls: ['../configurator.component.scss']
})
export class ConfiguratorDialogNatComponent implements OnInit {
server: Server;
node: Node;
name: string;
generalSettingsForm: FormGroup;
constructor(
public dialogRef: MatDialogRef<ConfiguratorDialogNatComponent>,
public nodeService: NodeService,
private toasterService: ToasterService,
private formBuilder: FormBuilder
) {
this.generalSettingsForm = this.formBuilder.group({
name: new FormControl('', Validators.required)
});
}
ngOnInit() {
this.nodeService.getNode(this.server, this.node).subscribe((node: Node) => {
this.node = node;
this.name = node.name;
})
}
onSaveClick() {
if (this.generalSettingsForm.valid) {
this.nodeService.updateNode(this.server, this.node).subscribe(() => {
this.toasterService.success(`Node ${this.node.name} updated.`);
this.onCancelClick();
});
} else {
this.toasterService.error(`Fill all required fields.`);
}
}
onCancelClick() {
this.dialogRef.close();
}
}

View File

@ -0,0 +1,276 @@
<h1 mat-dialog-title>Configurator for node {{name}}</h1>
<div class="modal-form-container">
<div class="content">
<div class="default-content">
<mat-card class="matCard">
<mat-tab-group *ngIf="name">
<mat-tab label="General settings">
<br/><form [formGroup]="generalSettingsForm">
<mat-form-field class="form-field">
<input matInput type="text" formControlName="name" [(ngModel)]="node.name" placeholder="Name">
</mat-form-field>
<mat-form-field class="form-field">
<input matInput type="number" formControlName="ram" [(ngModel)]="node.properties.ram" placeholder="RAM">
</mat-form-field>
</form>
<mat-form-field class="form-field">
<input matInput type="number" [(ngModel)]="node.properties.cpus" placeholder="vCPUs">
</mat-form-field>
<mat-form-field class="form-field">
<mat-select placeholder="Qemu binary" [(ngModel)]="node.properties.qemu_path">
<mat-option *ngFor="let binary of binaries" [value]="binary.path">
{{binary.path}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="form-field">
<mat-select placeholder="Boot priority" [(ngModel)]="node.properties.boot_priority">
<mat-option *ngFor="let priority of bootPriorities" [value]="priority[1]">
{{priority[0]}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="form-field">
<mat-select placeholder="On close" [(ngModel)]="node.properties.on_close">
<mat-option *ngFor="let option of onCloseOptions" [value]="option[1]">
{{option[0]}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="select">
<mat-select placeholder="Console type" [(ngModel)]="node.console_type">
<mat-option *ngFor="let type of consoleTypes" [value]="type">
{{type}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-checkbox [(ngModel)]="node.properties.console_auto_start">
Auto start console
</mat-checkbox>
</mat-tab>
<mat-tab label="HDD">
<mat-card>
<!-- to do -->
<button mat-raised-button color="primary" (click)="openQemuImageCreator()" class="create-button">Create Qemu image</button>
<mat-card-title></mat-card-title>
<mat-card-subtitle>
HDA (Primary Master)
</mat-card-subtitle>
<mat-card-content>
<mat-select placeholder="Disk image" [(ngModel)]="node.properties.hda_disk_image">
<mat-option *ngFor="let image of qemuImages" [value]="image.filename">
{{image.filename}}
</mat-option>
</mat-select>
<mat-form-field class="form-field">
<mat-select placeholder="Disk interface" [(ngModel)]="node.properties.hda_disk_interface">
<mat-option *ngFor="let interface of diskInterfaces" [value]="interface">
{{interface}}
</mat-option>
</mat-select>
</mat-form-field>
</mat-card-content>
</mat-card>
<mat-card>
<mat-card-title></mat-card-title>
<mat-card-subtitle>
HDB (Primary Slave)
</mat-card-subtitle>
<mat-card-content>
<mat-select placeholder="Disk image" [(ngModel)]="node.properties.hdb_disk_image">
<mat-option *ngFor="let image of qemuImages" [value]="image.filename">
{{image.filename}}
</mat-option>
</mat-select>
<mat-form-field class="form-field">
<mat-select placeholder="Disk interface" [(ngModel)]="node.properties.hdb_disk_interface">
<mat-option *ngFor="let interface of diskInterfaces" [value]="interface">
{{interface}}
</mat-option>
</mat-select>
</mat-form-field>
</mat-card-content>
</mat-card>
<mat-card>
<mat-card-title></mat-card-title>
<mat-card-subtitle>
HDC (Secondary Master)
</mat-card-subtitle>
<mat-card-content>
<mat-select placeholder="Disk image" [(ngModel)]="node.properties.hdc_disk_image">
<mat-option *ngFor="let image of qemuImages" [value]="image.filename">
{{image.filename}}
</mat-option>
</mat-select>
<mat-form-field class="form-field">
<mat-select placeholder="Disk interface" [(ngModel)]="node.properties.hdc_disk_interface">
<mat-option *ngFor="let interface of diskInterfaces" [value]="interface">
{{interface}}
</mat-option>
</mat-select>
</mat-form-field>
</mat-card-content>
</mat-card>
<mat-card>
<mat-card-title></mat-card-title>
<mat-card-subtitle>
HDD (Secondary Slave)
</mat-card-subtitle>
<mat-card-content>
<mat-select placeholder="Disk image" [(ngModel)]="node.properties.hdd_disk_image">
<mat-option *ngFor="let image of qemuImages" [value]="image.filename">
{{image.filename}}
</mat-option>
</mat-select>
<mat-form-field class="form-field">
<mat-select placeholder="Disk interface" [(ngModel)]="node.properties.hdd_disk_interface">
<mat-option *ngFor="let interface of diskInterfaces" [value]="interface">
{{interface}}
</mat-option>
</mat-select>
</mat-form-field>
</mat-card-content>
</mat-card>
</mat-tab>
<mat-tab label="CD/DVD">
<div>
<button mat-raised-button color="primary" (click)="filecdrom.click()" class="file-button">Browse</button>
<input
type="file"
#filecdrom
class="nonvisible"
(change)="uploadCdromImageFile($event)"/>
<mat-form-field class="file-name-form-field">
<input
matInput
type="text"
[(ngModel)]="node.properties.cdrom_image"
placeholder="Image"/>
</mat-form-field>
</div>
</mat-tab>
<mat-tab label="Network">
<br/><mat-checkbox [(ngModel)]="node.properties.legacy_networking">
Use the legacy networking mode
</mat-checkbox>
<app-custom-adapters-table
#customAdapters
[networkTypes]="networkTypes"
[displayedColumns]="displayedColumns"
[adapters]="node.ports"
></app-custom-adapters-table>
</mat-tab>
<mat-tab label="Advanced">
<mat-card>
<mat-card-title></mat-card-title>
<mat-card-subtitle>
Linux boot specific settings
</mat-card-subtitle>
<mat-card-content>
<div>
<button mat-raised-button color="primary" (click)="fileinitrd.click()" class="file-button">Browse</button>
<input
type="file"
#fileinitrd
class="nonvisible"
(change)="uploadInitrdFile($event)"/>
<mat-form-field class="file-name-form-field">
<input
matInput
type="text"
[(ngModel)]="node.properties.initrd"
placeholder="Initial RAM disk (initrd)"/>
</mat-form-field>
</div>
<div>
<button mat-raised-button color="primary" (click)="filekerenelimage.click()" class="file-button">Browse</button>
<input
type="file"
#filekernelimage
class="nonvisible"
(change)="uploadKernelImageFile($event)"/>
<mat-form-field class="file-name-form-field">
<input
matInput
type="text"
[(ngModel)]="node.properties.kernel_image"
placeholder="Kernel image"/>
</mat-form-field>
</div>
<mat-form-field class="form-field">
<input matInput type="text" [(ngModel)]="node.properties.kernel_command_line" placeholder="Kernel command line">
</mat-form-field>
</mat-card-content>
</mat-card>
<mat-card>
<mat-card-title></mat-card-title>
<mat-card-subtitle>
Bios
</mat-card-subtitle>
<mat-card-content>
<div>
<button mat-raised-button color="primary" (click)="filebios.click()" class="file-button">Browse</button>
<input
type="file"
#filebios
class="nonvisible"
(change)="uploadBiosFile($event)"/>
<mat-form-field class="file-name-form-field">
<input
matInput
type="text"
[(ngModel)]="node.properties.bios_image"
placeholder="Bios image"/>
</mat-form-field>
</div>
</mat-card-content>
</mat-card>
<mat-card>
<mat-card-title></mat-card-title>
<mat-card-subtitle>
Optimization
</mat-card-subtitle>
<mat-card-content>
<mat-checkbox [(ngModel)]="activateCpuThrottling">
Activate CPU throttling
</mat-checkbox>
<mat-form-field *ngIf="activateCpuThrottling" class="form-field">
<input matInput type="number" [(ngModel)]="node.properties.cpu_throttling" placeholder="Perecentage of CPU allowed">
</mat-form-field>
<mat-form-field class="form-field">
<mat-select placeholder="Process priority" [(ngModel)]="node.properties.process_priority">
<mat-option *ngFor="let priority of priorities" [value]="priority">
{{priority}}
</mat-option>
</mat-select>
</mat-form-field>
</mat-card-content>
</mat-card>
<mat-card>
<mat-card-title></mat-card-title>
<mat-card-subtitle>
Additional settings
</mat-card-subtitle>
<mat-card-content>
<mat-form-field class="form-field">
<input matInput type="text" [(ngModel)]="node.properties.options" placeholder="Options">
</mat-form-field>
</mat-card-content>
</mat-card>
</mat-tab>
<mat-tab label="Usage">
<mat-form-field class="form-field">
<textarea matInput type="text" [(ngModel)]="node.properties.usage"></textarea>
</mat-form-field>
</mat-tab>
</mat-tab-group>
</mat-card>
</div>
</div>
</div>
<div mat-dialog-actions>
<button mat-button (click)="onCancelClick()" color="accent">Cancel</button>
<button mat-button (click)="onSaveClick()" tabindex="2" mat-raised-button color="primary">Apply</button>
</div>

View File

@ -0,0 +1,131 @@
import { Component, OnInit, Input, ViewChild } from "@angular/core";
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
import { Node } from '../../../../../cartography/models/node';
import { Server } from '../../../../../models/server';
import { NodeService } from '../../../../../services/node.service';
import { ToasterService } from '../../../../../services/toaster.service';
import { MatDialogRef, MatDialog } from '@angular/material';
import { CustomAdaptersTableComponent } from '../../../../../components/preferences/common/custom-adapters-table/custom-adapters-table.component';
import { QemuService } from '../../../../../services/qemu.service';
import { QemuConfigurationService } from '../../../../../services/qemu-configuration.service';
import { QemuBinary } from '../../../../../models/qemu/qemu-binary';
import { QemuImageCreatorComponent } from './qemu-image-creator/qemu-image-creator.component';
import { QemuImage } from '../../../../../models/qemu/qemu-image';
@Component({
selector: 'app-configurator-qemu',
templateUrl: './configurator-qemu.component.html',
styleUrls: ['../configurator.component.scss']
})
export class ConfiguratorDialogQemuComponent implements OnInit {
server: Server;
node: Node;
name: string;
generalSettingsForm: FormGroup;
consoleTypes: string[] = [];
binaries: QemuBinary[] = [];
onCloseOptions = [];
bootPriorities = [];
diskInterfaces: string[] = [];
displayedColumns: string[] = ['adapter_number', 'port_name', 'adapter_type', 'actions'];
networkTypes = [];
qemuImages: QemuImage[] = [];
private conf = {
autoFocus: false,
width: '800px'
};
dialogRefQemuImageCreator;
@ViewChild("customAdapters", {static: false}) customAdapters: CustomAdaptersTableComponent;
constructor(
private dialog: MatDialog,
public dialogRef: MatDialogRef<ConfiguratorDialogQemuComponent>,
public nodeService: NodeService,
private toasterService: ToasterService,
private formBuilder: FormBuilder,
private qemuService: QemuService,
private qemuConfigurationService: QemuConfigurationService
) {
this.generalSettingsForm = this.formBuilder.group({
name: new FormControl('', Validators.required),
ram: new FormControl('', Validators.required)
});
}
ngOnInit() {
this.nodeService.getNode(this.server, this.node).subscribe((node: Node) => {
this.node = node;
this.name = node.name;
this.getConfiguration();
})
this.qemuService.getBinaries(this.server).subscribe((qemuBinaries: QemuBinary[]) => {
this.binaries = qemuBinaries;
});
this.qemuService.getImages(this.server).subscribe((qemuImages: QemuImage[]) => {
this.qemuImages = qemuImages;
});
}
openQemuImageCreator() {
this.dialogRefQemuImageCreator = this.dialog.open(QemuImageCreatorComponent, this.conf);
let instance = this.dialogRefQemuImageCreator.componentInstance;
instance.server = this.server;
}
uploadCdromImageFile(event){
this.node.properties.cdrom_image = event.target.files[0].name;
}
uploadInitrdFile(event){
this.node.properties.initrd = event.target.files[0].name;
}
uploadKernelImageFile(event){
this.node.properties.kernel_image = event.target.files[0].name;
}
uploadBiosFile(event){
this.node.properties.bios_image = event.target.files[0].name;
}
getConfiguration() {
this.consoleTypes = this.qemuConfigurationService.getConsoleTypes();
this.onCloseOptions = this.qemuConfigurationService.getOnCloseOptions();
this.qemuConfigurationService.getNetworkTypes().forEach(n => {
this.networkTypes.push(n[0]);
});
this.bootPriorities = this.qemuConfigurationService.getBootPriorities();
this.diskInterfaces = this.qemuConfigurationService.getDiskInterfaces();
}
onSaveClick() {
if (this.generalSettingsForm.valid) {
this.node.custom_adapters = [];
this.customAdapters.adapters.forEach(n => {
this.node.custom_adapters.push({
adapter_number: n.adapter_number,
adapter_type: n.adapter_type
})
});
this.node.properties.adapters = this.node.custom_adapters.length;
this.nodeService. updateNodeWithCustomAdapters(this.server, this.node).subscribe(() => {
this.toasterService.success(`Node ${this.node.name} updated.`);
this.onCancelClick();
});
} else {
this.toasterService.error(`Fill all required fields.`);
}
}
onCancelClick() {
this.dialogRef.close();
}
}

View File

@ -0,0 +1,107 @@
<h1 mat-dialog-title>Qemu image configurator</h1>
<div class="modal-form-container">
<div class="content">
<div class="default-content">
<mat-card class="matCard">
<form [formGroup]="inputForm">
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="qemuImg.qemu_img"
formControlName="qemu_img"
placeholder="Qemu image path">
</mat-form-field>
</form>
<mat-form-field class="form-field">
<mat-select placeholder="Image format" [(ngModel)]="qemuImg.format">
<mat-option *ngFor="let format of formatOptions" [value]="format">
{{format}}
</mat-option>
</mat-select>
</mat-form-field>
<div *ngIf="qemuImg.format==='qcow2'">
Size options
<mat-form-field class="form-field">
<mat-select placeholder="Image format" [(ngModel)]="qemuImg.preallocation">
<mat-option *ngFor="let preallocation of preallocationsOptions" [value]="preallocation">
{{preallocation}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="form-field">
<mat-select placeholder="Cluster size" [(ngModel)]="qemuImg.cluster_size">
<mat-option *ngFor="let clusterSize of clusterSizeOptions" [value]="clusterSize.value">
{{clusterSize.name}}
</mat-option>
</mat-select>
</mat-form-field>
Refcounts
<mat-form-field class="form-field">
<mat-select placeholder="Lazy refcounts" [(ngModel)]="qemuImg.lazy_refcounts">
<mat-option *ngFor="let lazyRefcount of lazyRefcountsOptions" [value]="lazyRefcount">
{{lazyRefcount}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="form-field">
<mat-select placeholder="Refcount entry size" [(ngModel)]="qemuImg.refcount_bits">
<mat-option *ngFor="let clusterSize of clusterSizeOptions" [value]="clusterSize.value">
{{clusterSize.name}}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div *ngIf="qemuImg.format==='vhd' || qemuImg.format==='vdi'">
<mat-radio-group aria-label="Image file sizing mode" class="radio-selection">
<mat-radio-button value="1" (click)="setSubformat('dynamic')" checked>Dynamic</mat-radio-button>
<mat-radio-button value="2" (click)="setSubformat('fixed')">Fixed</mat-radio-button>
</mat-radio-group>
</div>
<div *ngIf="qemuImg.format==='vmdk'">
Adapter type<br/>
<mat-radio-group aria-label="Adapter type" class="radio-selection">
<mat-radio-button value="1" (click)="setAdapterType('ide')" checked>IDE</mat-radio-button>
<mat-radio-button value="2" (click)="setAdapterType('lsilogic')">LSI Logic</mat-radio-button>
<mat-radio-button value="3" (click)="setAdapterType('buslogic')">BusLogic</mat-radio-button>
<mat-radio-button value="4" (click)="setAdapterType('legacyESX')">Legacy (ESX)</mat-radio-button>
</mat-radio-group><br/><br/>
Image file sizing mode<br/>
<mat-radio-group aria-label="Image file sizing mode" class="radio-selection">
<mat-radio-button value="1" (click)="setSubformat('streamOptimized')" checked>Stream optimized</mat-radio-button>
<mat-radio-button value="2" (click)="setSubformat('twoGbMaxExtentSparse')">Split every 2GB (sparse)</mat-radio-button>
<mat-radio-button value="3" (click)="setSubformat('twoGbMaxExtentFlat')">Split every 2GB (flat)</mat-radio-button>
<mat-radio-button value="4" (click)="setSubformat('monolithicSparse')">Monolithic sparse</mat-radio-button>
<mat-radio-button value="5" (click)="setSubformat('monolithicFlat')">Monolithic flat</mat-radio-button>
</mat-radio-group><br/><br/>
<mat-select placeholder="Zeroed grain" [(ngModel)]="qemuImg.zeroed_grain">
<mat-option *ngFor="let option of zeroedGrainOptions" [value]="option">
{{option}}
</mat-option>
</mat-select>
</div>
<form [formGroup]="inputForm">
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="qemuImg.path"
formControlName="path"
placeholder="File location">
</mat-form-field>
<mat-form-field class="form-field">
<input
matInput type="number"
[(ngModel)]="qemuImg.size"
formControlName="size"
placeholder="Disk size">
</mat-form-field>
</form>
</mat-card>
</div>
</div>
</div>
<div mat-dialog-actions>
<button mat-button (click)="onCancelClick()" color="accent">Cancel</button>
<button mat-button (click)="onSaveClick()" tabindex="2" mat-raised-button color="primary">Apply</button>
</div>

View File

@ -0,0 +1,121 @@
import { Component, OnInit, Input, ViewChild } from "@angular/core";
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
import { Server } from '../../../../../../models/server';
import { NodeService } from '../../../../../../services/node.service';
import { ToasterService } from '../../../../../../services/toaster.service';
import { MatDialogRef } from '@angular/material';
import { QemuService } from '../../../../../../services/qemu.service';
import { QemuImg } from '../../../../../../models/qemu/qemu-img';
@Component({
selector: 'app-qemu-image-creator',
templateUrl: './qemu-image-creator.component.html',
styleUrls: ['../../configurator.component.scss']
})
export class QemuImageCreatorComponent implements OnInit {
server: Server;
qemuImg: QemuImg;
formatOptions: string[] = ['qcow2', 'qcow', 'vhd', 'vdi', 'vmdk', 'raw'];
preallocationsOptions: string[] = ['off', 'metadata', 'falloc', 'full'];
clusterSizeOptions: ClusterSize[] = [
{
name: '512',
value: 512
},
{
name: '1k',
value: 1024
},
{
name: '2k',
value: 2048
},
{
name: '4k',
value: 4096
},
{
name: '8k',
value: 8192
},
{
name: '16k',
value: 16384
},
{
name: '32k',
value: 32768
},
{
name: '64k',
value: 65536
},
{
name: '128k',
value: 131072
},
{
name: '256k',
value: 262144
},
{
name: '512k',
value: 524288
},
{
name: '1024k',
value: 1048576
},
{
name: '2048k',
value: 2097152
}
];
lazyRefcountsOptions: string[] = ['off', 'on'];
refcountBitsOptions: number[] = [1,2,4,8,16,32,64];
zeroedGrainOptions: string[] = ['on', 'off'];
inputForm: FormGroup;
constructor(
public dialogRef: MatDialogRef<QemuImageCreatorComponent>,
public nodeService: NodeService,
private toasterService: ToasterService,
private formBuilder: FormBuilder,
private qemuService: QemuService
) {
this.inputForm = this.formBuilder.group({
qemu_img: new FormControl('', Validators.required),
path: new FormControl('', Validators.required),
size: new FormControl('', Validators.required)
});
}
ngOnInit() {
this.qemuImg = {} as QemuImg;
}
setSubformat(subformat: string) {
this.qemuImg.subformat = subformat;
}
onSaveClick() {
if (this.inputForm.valid && this.qemuImg.format) {
this.qemuService.addImage(this.server, this.qemuImg).subscribe(() => {
this.dialogRef.close();
});
} else {
this.toasterService.error('Fill all required fields.')
}
}
onCancelClick() {
this.dialogRef.close();
}
}
export interface ClusterSize {
name: string;
value: number;
}

View File

@ -0,0 +1,83 @@
<h1 mat-dialog-title>Configurator for node {{name}}</h1>
<div class="modal-form-container">
<div class="content">
<div class="default-content">
<mat-card>
<table *ngIf="nodeMappingsDataSource.length" class="table" mat-table [dataSource]="nodeMappingsDataSource">
<ng-container matColumnDef="portIn">
<th mat-header-cell *matHeaderCellDef> Port : DLCI</th>
<td mat-cell *matCellDef="let element"> {{element.portIn}} </td>
</ng-container>
<ng-container matColumnDef="portOut">
<th mat-header-cell *matHeaderCellDef> Port : DLCI </th>
<td mat-cell *matCellDef="let element"> {{element.portOut}} </td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef> Actions </th>
<td mat-cell *matCellDef="let element">
<button mat-icon-button matTooltip="Delete port" (click)="delete(element)">
<mat-icon aria-label="Delete port">delete</mat-icon>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table><br/>
<form [formGroup]="nameForm">
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="node.name"
formControlName="name"
placeholder="Name">
</mat-form-field>
</form>
<form [formGroup]="inputForm">
Source
<mat-form-field class="form-field">
<input
matInput type="number"
[(ngModel)]="sourcePort"
formControlName="sourcePort"
placeholder="Port">
</mat-form-field>
<mat-form-field class="form-field">
<input
matInput type="number"
[(ngModel)]="sourceDlci"
formControlName="sourceDlci"
placeholder="DLCI">
</mat-form-field>
Destination
<mat-form-field class="form-field">
<input
matInput type="number"
[(ngModel)]="destinationPort"
formControlName="destinationPort"
placeholder="Port">
</mat-form-field>
<mat-form-field class="form-field">
<input
matInput type="number"
[(ngModel)]="destinationDlci"
formControlName="destinationDlci"
placeholder="DLCI">
</mat-form-field>
</form>
<button mat-button class="form-field" (click)="add()">Add</button>
</mat-card>
</div>
</div>
</div>
<div mat-dialog-actions>
<button mat-button (click)="onCancelClick()" color="accent">Cancel</button>
<button mat-button (click)="onSaveClick()" tabindex="2" mat-raised-button color="primary">Apply</button>
</div>

View File

@ -0,0 +1,133 @@
import { Component, OnInit, Input } from "@angular/core";
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
import { Node } from '../../../../../cartography/models/node';
import { Server } from '../../../../../models/server';
import { NodeService } from '../../../../../services/node.service';
import { ToasterService } from '../../../../../services/toaster.service';
import { MatDialogRef } from '@angular/material';
@Component({
selector: 'app-configurator-switch',
templateUrl: './configurator-switch.component.html',
styleUrls: ['../configurator.component.scss', '../../../../preferences/preferences.component.scss']
})
export class ConfiguratorDialogSwitchComponent implements OnInit {
server: Server;
node: Node;
name: string;
nameForm: FormGroup;
inputForm: FormGroup;
consoleTypes: string[] = [];
nodeMappings = new Map<string, string>();
nodeMappingsDataSource: NodeMapping[] = [];
dataSource = [];
displayedColumns = ['portIn', 'portOut', 'actions']
sourcePort: string = '';
sourceDlci: string = '';
destinationPort: string = '';
destinationDlci: string = '';
constructor(
public dialogRef: MatDialogRef<ConfiguratorDialogSwitchComponent>,
public nodeService: NodeService,
private toasterService: ToasterService,
private formBuilder: FormBuilder
) {
this.nameForm = this.formBuilder.group({
name: new FormControl('', Validators.required),
});
this.inputForm = this.formBuilder.group({
sourcePort: new FormControl('', Validators.required),
sourceDlci: new FormControl('', Validators.required),
destinationPort: new FormControl('', Validators.required),
destinationDlci: new FormControl('', Validators.required),
});
}
ngOnInit() {
this.nodeService.getNode(this.server, this.node).subscribe((node: Node) => {
this.node = node;
this.name = node.name;
let mappings = node.properties.mappings;
Object.keys(mappings).forEach(key => {
this.nodeMappings.set(key, mappings[key]);
});
this.nodeMappings.forEach((value: string, key: string) => {
this.nodeMappingsDataSource.push({
portIn: key,
portOut: value
});
});
});
}
delete(elem: NodeMapping) {
this.nodeMappingsDataSource = this.nodeMappingsDataSource.filter(n => n !== elem);
}
add() {
if (this.inputForm.valid) {
let nodeMapping: NodeMapping = {
portIn: `${this.sourcePort}:${this.sourceDlci}`,
portOut: `${this.destinationPort}:${this.destinationDlci}`
};
if (this.nodeMappingsDataSource.filter(n => n.portIn === nodeMapping.portIn).length > 0) {
this.toasterService.error('Mapping already defined.');
} else {
this.nodeMappingsDataSource = this.nodeMappingsDataSource.concat([nodeMapping]);
this.clearUserInput();
}
} else {
this.toasterService.error('Fill all required fields.');
}
}
clearUserInput() {
this.sourcePort = '0';
this.sourceDlci = '0';
this.destinationPort = '0';
this.destinationDlci = '0';
}
strMapToObj(strMap) {
let obj = Object.create(null);
for (let [k,v] of strMap) {
obj[k] = v;
}
return obj;
}
onSaveClick() {
if (this.nameForm.valid) {
this.nodeMappings.clear();
this.nodeMappingsDataSource.forEach(elem => {
this.nodeMappings.set(elem.portIn, elem.portOut);
});
this.node.properties.mappings = Array.from(this.nodeMappings).reduce((obj, [key, value]) => (Object.assign(obj, { [key]: value })), {});
this.nodeService.updateNode(this.server, this.node).subscribe(() => {
this.toasterService.success(`Node ${this.node.name} updated.`);
this.onCancelClick();
});
} else {
this.toasterService.error(`Fill all required fields.`);
}
}
onCancelClick() {
this.dialogRef.close();
}
}
export interface NodeMapping {
portIn: string,
portOut: string
}

View File

@ -0,0 +1,24 @@
<h1 mat-dialog-title>Configurator for node {{name}}</h1>
<div class="modal-form-container">
<div class="content">
<div class="default-content">
<mat-card class="matCard">
<form [formGroup]="generalSettingsForm">
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="node.name"
formControlName="name"
placeholder="Name">
</mat-form-field>
</form>
</mat-card>
</div>
</div>
</div>
<div mat-dialog-actions>
<button mat-button (click)="onCancelClick()" color="accent">Cancel</button>
<button mat-button (click)="onSaveClick()" tabindex="2" mat-raised-button color="primary">Apply</button>
</div>

View File

@ -0,0 +1,53 @@
import { Component, OnInit, Input, ViewChild } from "@angular/core";
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
import { Node } from '../../../../../cartography/models/node';
import { Server } from '../../../../../models/server';
import { NodeService } from '../../../../../services/node.service';
import { ToasterService } from '../../../../../services/toaster.service';
import { MatDialogRef } from '@angular/material';
@Component({
selector: 'app-configurator-traceng',
templateUrl: './configurator-traceng.component.html',
styleUrls: ['../configurator.component.scss']
})
export class ConfiguratorDialogTracengComponent implements OnInit {
server: Server;
node: Node;
name: string;
generalSettingsForm: FormGroup;
constructor(
public dialogRef: MatDialogRef<ConfiguratorDialogTracengComponent>,
public nodeService: NodeService,
private toasterService: ToasterService,
private formBuilder: FormBuilder
) {
this.generalSettingsForm = this.formBuilder.group({
name: new FormControl('', Validators.required)
});
}
ngOnInit() {
this.nodeService.getNode(this.server, this.node).subscribe((node: Node) => {
this.node = node;
this.name = node.name;
})
}
onSaveClick() {
if (this.generalSettingsForm.valid) {
this.nodeService.updateNode(this.server, this.node).subscribe(() => {
this.toasterService.success(`Node ${this.node.name} updated.`);
this.onCancelClick();
});
} else {
this.toasterService.error(`Fill all required fields.`);
}
}
onCancelClick() {
this.dialogRef.close();
}
}

View File

@ -0,0 +1,63 @@
<h1 mat-dialog-title>Configurator for node {{name}}</h1>
<div class="modal-form-container">
<div class="content">
<div class="default-content">
<mat-card class="matCard">
<mat-tab-group *ngIf="name">
<mat-tab label="General settings">
<br/><form [formGroup]="generalSettingsForm">
<mat-form-field class="form-field">
<input matInput formControlName="name" type="text" [(ngModel)]="node.name" placeholder="Name">
</mat-form-field>
<mat-form-field class="select">
<mat-select [ngModelOptions]="{standalone: true}" placeholder="Console type" [(ngModel)]="node.console_type">
<mat-option *ngFor="let type of consoleTypes" [value]="type">
{{type}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-checkbox [ngModelOptions]="{standalone: true}" [(ngModel)]="node.console_auto_start">
Auto start console
</mat-checkbox>
<mat-form-field class="form-field">
<input matInput formControlName="ram" type="number" [(ngModel)]="node.properties.ram" placeholder="RAM">
</mat-form-field>
<mat-form-field class="form-field">
<mat-select [ngModelOptions]="{standalone: true}" placeholder="On close" [(ngModel)]="node.properties.on_close">
<mat-option *ngFor="let option of onCloseOptions" [value]="option[1]">
{{option[0]}}
</mat-option>
</mat-select>
</mat-form-field>
</form>
<mat-checkbox [(ngModel)]="node.properties.headless">
Start VM in headless mode
</mat-checkbox>
</mat-tab>
<mat-tab label="Network">
<br/><mat-checkbox [(ngModel)]="node.properties.use_any_adapter">
Allow GNS3 to use any configured VirtualBox adapter
</mat-checkbox>
<app-custom-adapters-table
#customAdapters
[networkTypes]="networkTypes"
[displayedColumns]="displayedColumns"
[adapters]="node.ports"
></app-custom-adapters-table>
</mat-tab>
<mat-tab label="Usage">
<mat-form-field class="form-field">
<textarea matInput type="text" [(ngModel)]="node.properties.usage"></textarea>
</mat-form-field>
</mat-tab>
</mat-tab-group>
</mat-card>
</div>
</div>
</div>
<div mat-dialog-actions>
<button mat-button (click)="onCancelClick()" color="accent">Cancel</button>
<button mat-button (click)="onSaveClick()" tabindex="2" mat-raised-button color="primary">Apply</button>
</div>

View File

@ -0,0 +1,81 @@
import { Component, OnInit, Input, ViewChild } from "@angular/core";
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
import { Node } from '../../../../../cartography/models/node';
import { Server } from '../../../../../models/server';
import { NodeService } from '../../../../../services/node.service';
import { ToasterService } from '../../../../../services/toaster.service';
import { MatDialogRef } from '@angular/material';
import { VirtualBoxConfigurationService } from '../../../../../services/virtual-box-configuration.service';
import { CustomAdaptersTableComponent } from '../../../../../components/preferences/common/custom-adapters-table/custom-adapters-table.component';
@Component({
selector: 'app-configurator-virtualbox',
templateUrl: './configurator-virtualbox.component.html',
styleUrls: ['../configurator.component.scss']
})
export class ConfiguratorDialogVirtualBoxComponent implements OnInit {
server: Server;
node: Node;
name: string;
generalSettingsForm: FormGroup;
consoleTypes: string[] = [];
onCloseOptions = [];
displayedColumns: string[] = ['adapter_number', 'port_name', 'adapter_type', 'actions'];
networkTypes = [];
@ViewChild("customAdapters", {static: false}) customAdapters: CustomAdaptersTableComponent;
constructor(
public dialogRef: MatDialogRef<ConfiguratorDialogVirtualBoxComponent>,
public nodeService: NodeService,
private toasterService: ToasterService,
private formBuilder: FormBuilder,
private virtualBoxConfigurationService: VirtualBoxConfigurationService
) {
this.generalSettingsForm = this.formBuilder.group({
name: new FormControl('', Validators.required),
ram: new FormControl('', Validators.required)
});
}
ngOnInit() {
this.nodeService.getNode(this.server, this.node).subscribe((node: Node) => {
this.node = node;
this.name = node.name;
this.getConfiguration();
})
}
getConfiguration() {
this.consoleTypes = this.virtualBoxConfigurationService.getConsoleTypes();
this.onCloseOptions = this.virtualBoxConfigurationService.getOnCloseoptions();
this.networkTypes = this.virtualBoxConfigurationService.getNetworkTypes();
}
onSaveClick() {
if (this.generalSettingsForm.valid) {
this.node.custom_adapters = [];
this.customAdapters.adapters.forEach(n => {
this.node.custom_adapters.push({
adapter_number: n.adapter_number,
adapter_type: n.adapter_type
})
});
this.node.properties.adapters = this.node.custom_adapters.length;
this.nodeService.updateNodeWithCustomAdapters(this.server, this.node).subscribe(() => {
this.toasterService.success(`Node ${this.node.name} updated.`);
this.onCancelClick();
});
} else {
this.toasterService.error(`Fill all required fields.`);
}
}
onCancelClick() {
this.dialogRef.close();
}
}

View File

@ -0,0 +1,63 @@
<h1 mat-dialog-title>Configurator for node {{name}}</h1>
<div class="modal-form-container">
<div class="content">
<div class="default-content">
<mat-card class="matCard">
<mat-tab-group *ngIf="name">
<mat-tab label="General settings">
<br/><form [formGroup]="generalSettingsForm">
<mat-form-field class="form-field">
<input matInput formControlName="name" type="text" [(ngModel)]="node.name" placeholder="Name">
</mat-form-field>
<mat-form-field class="select">
<mat-select [ngModelOptions]="{standalone: true}" placeholder="Console type" [(ngModel)]="node.console_type">
<mat-option *ngFor="let type of consoleTypes" [value]="type">
{{type}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-checkbox [ngModelOptions]="{standalone: true}" [(ngModel)]="node.console_auto_start">
Auto start console
</mat-checkbox>
<mat-form-field class="form-field">
<mat-select [ngModelOptions]="{standalone: true}" placeholder="On close" [(ngModel)]="node.properties.on_close">
<mat-option *ngFor="let option of onCloseOptions" [value]="option[1]">
{{option[0]}}
</mat-option>
</mat-select>
</mat-form-field>
</form>
<mat-checkbox [(ngModel)]="node.properties.headless">
Start VM in headless mode
</mat-checkbox><br/>
<mat-checkbox [(ngModel)]="node.properties.linked_clone">
Use a linked base VM (experimental)
</mat-checkbox>
</mat-tab>
<mat-tab label="Network">
<br/><mat-checkbox [(ngModel)]="node.properties.use_any_adapter">
Allow GNS3 to override non custom VMware adapter
</mat-checkbox>
<app-custom-adapters-table
#customAdapters
[networkTypes]="networkTypes"
[displayedColumns]="displayedColumns"
[adapters]="node.ports"
></app-custom-adapters-table>
</mat-tab>
<mat-tab label="Usage">
<mat-form-field class="form-field">
<textarea matInput type="text" [(ngModel)]="node.properties.usage"></textarea>
</mat-form-field>
</mat-tab>
</mat-tab-group>
</mat-card>
</div>
</div>
</div>
<div mat-dialog-actions>
<button mat-button (click)="onCancelClick()" color="accent">Cancel</button>
<button mat-button (click)="onSaveClick()" tabindex="2" mat-raised-button color="primary">Apply</button>
</div>

View File

@ -0,0 +1,80 @@
import { Component, OnInit, Input, ViewChild } from "@angular/core";
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
import { Node } from '../../../../../cartography/models/node';
import { Server } from '../../../../../models/server';
import { NodeService } from '../../../../../services/node.service';
import { ToasterService } from '../../../../../services/toaster.service';
import { MatDialogRef } from '@angular/material';
import { CustomAdaptersTableComponent } from '../../../../../components/preferences/common/custom-adapters-table/custom-adapters-table.component';
import { VmwareConfigurationService } from '../../../../../services/vmware-configuration.service';
@Component({
selector: 'app-configurator-vmware',
templateUrl: './configurator-vmware.component.html',
styleUrls: ['../configurator.component.scss']
})
export class ConfiguratorDialogVmwareComponent implements OnInit {
server: Server;
node: Node;
name: string;
generalSettingsForm: FormGroup;
consoleTypes: string[] = [];
onCloseOptions = [];
displayedColumns: string[] = ['adapter_number', 'port_name', 'adapter_type', 'actions'];
networkTypes = [];
@ViewChild("customAdapters", {static: false}) customAdapters: CustomAdaptersTableComponent;
constructor(
public dialogRef: MatDialogRef<ConfiguratorDialogVmwareComponent>,
public nodeService: NodeService,
private toasterService: ToasterService,
private formBuilder: FormBuilder,
private vmwareConfigurationService: VmwareConfigurationService
) {
this.generalSettingsForm = this.formBuilder.group({
name: new FormControl('', Validators.required)
});
}
ngOnInit() {
this.nodeService.getNode(this.server, this.node).subscribe((node: Node) => {
this.node = node;
this.name = node.name;
this.getConfiguration();
})
}
getConfiguration() {
this.consoleTypes = this.vmwareConfigurationService.getConsoleTypes();
this.onCloseOptions = this.vmwareConfigurationService.getOnCloseoptions();
this.networkTypes = this.vmwareConfigurationService.getNetworkTypes();
}
onSaveClick() {
if (this.generalSettingsForm.valid) {
this.node.custom_adapters = [];
this.customAdapters.adapters.forEach(n => {
this.node.custom_adapters.push({
adapter_number: n.adapter_number,
adapter_type: n.adapter_type
})
});
this.node.properties.adapters = this.node.custom_adapters.length;
this.nodeService.updateNodeWithCustomAdapters(this.server, this.node).subscribe(() => {
this.toasterService.success(`Node ${this.node.name} updated.`);
this.onCancelClick();
});
} else {
this.toasterService.error(`Fill all required fields.`);
}
}
onCancelClick() {
this.dialogRef.close();
}
}

View File

@ -0,0 +1,39 @@
<h1 mat-dialog-title>Configurator for node {{name}}</h1>
<div class="modal-form-container">
<div class="content">
<div class="default-content">
<mat-card class="matCard">
<form [formGroup]="inputForm">
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="node.name"
formControlName="name"
placeholder="Name">
</mat-form-field>
<mat-form-field class="select">
<mat-select
placeholder="Console type"
[ngModelOptions]="{standalone: true}"
[(ngModel)]="node.console_type">
<mat-option *ngFor="let type of consoleTypes" [value]="type">
{{type}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-checkbox
[ngModelOptions]="{standalone: true}"
[(ngModel)]="node.console_auto_start">
Auto start console
</mat-checkbox>
</form>
</mat-card>
</div>
</div>
</div>
<div mat-dialog-actions>
<button mat-button (click)="onCancelClick()" color="accent">Cancel</button>
<button mat-button (click)="onSaveClick()" tabindex="2" mat-raised-button color="primary">Apply</button>
</div>

View File

@ -0,0 +1,61 @@
import { Component, OnInit, Input } from "@angular/core";
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
import { VpcsConfigurationService } from '../../../../../services/vpcs-configuration.service';
import { Node } from '../../../../../cartography/models/node';
import { Server } from '../../../../../models/server';
import { NodeService } from '../../../../../services/node.service';
import { ToasterService } from '../../../../../services/toaster.service';
import { MatDialogRef } from '@angular/material';
@Component({
selector: 'app-configurator-vpcs',
templateUrl: './configurator-vpcs.component.html',
styleUrls: ['../configurator.component.scss']
})
export class ConfiguratorDialogVpcsComponent implements OnInit {
server: Server;
node: Node;
name: string;
inputForm: FormGroup;
consoleTypes: string[] = [];
constructor(
public dialogRef: MatDialogRef<ConfiguratorDialogVpcsComponent>,
public nodeService: NodeService,
private toasterService: ToasterService,
private formBuilder: FormBuilder,
private vpcsConfigurationService: VpcsConfigurationService
) {
this.inputForm = this.formBuilder.group({
name: new FormControl('', Validators.required)
});
}
ngOnInit() {
this.nodeService.getNode(this.server, this.node).subscribe((node: Node) => {
this.node = node;
this.name = node.name;
this.getConfiguration();
})
}
getConfiguration() {
this.consoleTypes = this.vpcsConfigurationService.getConsoleTypes();
}
onSaveClick() {
if (this.inputForm.valid) {
this.nodeService.updateNode(this.server, this.node).subscribe(() => {
this.toasterService.success(`Node ${this.node.name} updated.`);
this.onCancelClick();
});
} else {
this.toasterService.error(`Fill all required fields.`);
}
}
onCancelClick() {
this.dialogRef.close();
}
}

View File

@ -1,5 +1,6 @@
export class Port { export class Port {
adapter_number: number; adapter_number: number;
adapter_type: string;
link_type: string; link_type: string;
name: string; name: string;
port_number: number; port_number: number;

View File

@ -0,0 +1,12 @@
export class QemuImg {
cluster_size: number;
format: string;
lazy_refcounts: string;
path: string;
preallocation: string;
qemu_img: string;
refcount_bits: number;
size: number;
subformat: string;
zeroed_grain: string;
}

View File

@ -0,0 +1,12 @@
export interface TracengTemplate {
builtin: boolean;
category: string;
compute_id: string;
console_type: string;
default_name_format: string;
ip_address: string;
name: string;
symbol: string;
template_id: string;
template_type: string;
}

View File

@ -86,6 +86,25 @@ export class NodeService {
}); });
} }
updateNode(server: Server, node: Node): Observable<Node> {
return this.httpServer.put<Node>(server, `/projects/${node.project_id}/nodes/${node.node_id}`, {
console_type: node.console_type,
console_auto_start: node.console_auto_start,
name: node.name,
properties: node.properties
});
}
updateNodeWithCustomAdapters(server: Server, node: Node): Observable<Node> {
return this.httpServer.put<Node>(server, `/projects/${node.project_id}/nodes/${node.node_id}`, {
console_type: node.console_type,
console_auto_start: node.console_auto_start,
custom_adapters: node.custom_adapters,
name: node.name,
properties: node.properties
});
}
delete(server: Server, node: Node) { delete(server: Server, node: Node) {
return this.httpServer.delete<Node>(server, `/projects/${node.project_id}/nodes/${node.node_id}`); return this.httpServer.delete<Node>(server, `/projects/${node.project_id}/nodes/${node.node_id}`);
} }
@ -99,6 +118,10 @@ export class NodeService {
}); });
} }
getNode(server: Server, node: Node) {
return this.httpServer.get(server, `/projects/${node.project_id}/nodes/${node.node_id}`)
}
getConfiguration(server: Server, node: Node) { getConfiguration(server: Server, node: Node) {
let urlPath: string = `/projects/${node.project_id}/nodes/${node.node_id}` let urlPath: string = `/projects/${node.project_id}/nodes/${node.node_id}`

View File

@ -11,26 +11,48 @@ export class QemuConfigurationService {
} }
getNetworkTypes() { getNetworkTypes() {
let networkTypes = [["e1000", "Intel Gigabit Ethernet"], // needs extending of custom adapter component
["i82550", "Intel i82550 Ethernet"], // let networkTypes = [["e1000", "Intel Gigabit Ethernet"],
["i82551", "Intel i82551 Ethernet"], // ["i82550", "Intel i82550 Ethernet"],
["i82557a", "Intel i82557A Ethernet"], // ["i82551", "Intel i82551 Ethernet"],
["i82557b", "Intel i82557B Ethernet"], // ["i82557a", "Intel i82557A Ethernet"],
["i82557c", "Intel i82557C Ethernet"], // ["i82557b", "Intel i82557B Ethernet"],
["i82558a", "Intel i82558A Ethernet"], // ["i82557c", "Intel i82557C Ethernet"],
["i82558b", "Intel i82558B Ethernet"], // ["i82558a", "Intel i82558A Ethernet"],
["i82559a", "Intel i82559A Ethernet"], // ["i82558b", "Intel i82558B Ethernet"],
["i82559b", "Intel i82559B Ethernet"], // ["i82559a", "Intel i82559A Ethernet"],
["i82559c", "Intel i82559C Ethernet"], // ["i82559b", "Intel i82559B Ethernet"],
["i82559er", "Intel i82559ER Ethernet"], // ["i82559c", "Intel i82559C Ethernet"],
["i82562", "Intel i82562 Ethernet"], // ["i82559er", "Intel i82559ER Ethernet"],
["i82801", "Intel i82801 Ethernet"], // ["i82562", "Intel i82562 Ethernet"],
["ne2k_pci", "NE2000 Ethernet"], // ["i82801", "Intel i82801 Ethernet"],
["pcnet", "AMD PCNet Ethernet"], // ["ne2k_pci", "NE2000 Ethernet"],
["rtl8139", "Realtek 8139 Ethernet"], // ["pcnet", "AMD PCNet Ethernet"],
["virtio", "Legacy paravirtualized Network I/O"], // ["rtl8139", "Realtek 8139 Ethernet"],
["virtio-net-pci", "Paravirtualized Network I/O"], // ["virtio", "Legacy paravirtualized Network I/O"],
["vmxnet3", "VMWare Paravirtualized Ethernet v3"]]; // ["virtio-net-pci", "Paravirtualized Network I/O"],
// ["vmxnet3", "VMWare Paravirtualized Ethernet v3"]];
let networkTypes = ["e1000", "Intel Gigabit Ethernet",
"i82550",
"i82551",
"i82557a",
"i82557b",
"i82557c",
"i82558a",
"i82558b",
"i82559a",
"i82559b",
"i82559c",
"i82559er",
"i82562",
"i82801",
"ne2k_pci",
"pcnet",
"rtl8139",
"virtio",
"virtio-net-pci",
"vmxnet3"];
return networkTypes; return networkTypes;
} }

View File

@ -5,6 +5,7 @@ import { QemuTemplate } from '../models/templates/qemu-template';
import { Server } from '../models/server'; import { Server } from '../models/server';
import { QemuBinary } from '../models/qemu/qemu-binary'; import { QemuBinary } from '../models/qemu/qemu-binary';
import { QemuImage } from '../models/qemu/qemu-image'; import { QemuImage } from '../models/qemu/qemu-image';
import { QemuImg } from '../models/qemu/qemu-img';
@Injectable() @Injectable()
export class QemuService { export class QemuService {
@ -26,6 +27,10 @@ export class QemuService {
return this.httpServer.get<QemuImage[]>(server, '/compute/qemu/images') as Observable<QemuImage[]>; return this.httpServer.get<QemuImage[]>(server, '/compute/qemu/images') as Observable<QemuImage[]>;
} }
addImage(server: Server, qemuImg: QemuImg): Observable<QemuImg> {
return this.httpServer.post<QemuImg>(server, '/compute/qemu/img', qemuImg) as Observable<QemuImg>;
}
addTemplate(server: Server, qemuTemplate: QemuTemplate): Observable<QemuTemplate> { addTemplate(server: Server, qemuTemplate: QemuTemplate): Observable<QemuTemplate> {
return this.httpServer.post<QemuTemplate>(server, `/templates`, qemuTemplate) as Observable<QemuTemplate>; return this.httpServer.post<QemuTemplate>(server, `/templates`, qemuTemplate) as Observable<QemuTemplate>;
} }

View File

@ -11,9 +11,27 @@ import { VmwareTemplate } from '../models/templates/vmware-template';
import { DockerTemplate } from '../models/templates/docker-template'; import { DockerTemplate } from '../models/templates/docker-template';
import { CustomAdapter } from '../models/qemu/qemu-custom-adapter'; import { CustomAdapter } from '../models/qemu/qemu-custom-adapter';
import { IouTemplate } from '../models/templates/iou-template'; import { IouTemplate } from '../models/templates/iou-template';
import { TracengTemplate } from '../models/templates/traceng-template';
@Injectable() @Injectable()
export class TemplateMocksService { export class TemplateMocksService {
getTracengTemplate() : TracengTemplate {
let template: TracengTemplate = {
builtin: false,
category: 'guest',
compute_id: 'local',
console_type: 'none',
default_name_format: 'TraceNG{0}',
ip_address: '',
name: '',
symbol: ':/symbols/classic/traceng.svg',
template_id: '',
template_type: 'traceng'
};
return template;
}
getQemuTemplate() : Observable<QemuTemplate> { getQemuTemplate() : Observable<QemuTemplate> {
let template : QemuTemplate = { let template : QemuTemplate = {
adapter_type: 'e1000', adapter_type: 'e1000',

View File

View File

@ -0,0 +1,27 @@
import { Injectable } from "@angular/core";
import { HttpServer } from './http-server.service';
import { Server } from '../models/server';
import { Observable } from 'rxjs';
import { HttpHeaders } from '@angular/common/http';
import { TracengTemplate } from '../models/templates/traceng-template';
@Injectable()
export class TracengService {
constructor(private httpServer: HttpServer) {}
getTemplates(server: Server): Observable<TracengTemplate[]> {
return this.httpServer.get<TracengTemplate[]>(server, '/templates') as Observable<TracengTemplate[]>;
}
getTemplate(server: Server, template_id: string): Observable<TracengTemplate> {
return this.httpServer.get<TracengTemplate>(server, `/templates/${template_id}`) as Observable<TracengTemplate>;
}
addTemplate(server: Server, vpcsTemplate: TracengTemplate): Observable<TracengTemplate> {
return this.httpServer.post<TracengTemplate>(server, `/templates`, vpcsTemplate) as Observable<TracengTemplate>;
}
saveTemplate(server: Server, vpcsTemplate: TracengTemplate): Observable<TracengTemplate> {
return this.httpServer.put<TracengTemplate>(server, `/templates/${vpcsTemplate.template_id}`, vpcsTemplate) as Observable<TracengTemplate>;
}
}