mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2025-05-28 13:04:21 +00:00
Merge branch 'master' into Create-templates-from-appliances
This commit is contained in:
commit
b3109a8cc5
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gns3-web-ui",
|
||||
"version": "2020.2.0-beta.3",
|
||||
"version": "2020.2.0-beta.4",
|
||||
"author": {
|
||||
"name": "GNS3 Technology Inc.",
|
||||
"email": "developers@gns3.com"
|
||||
|
@ -1,6 +1,19 @@
|
||||
GNS3 WebUI is web implementation of user interface for GNS3 software.
|
||||
|
||||
Current version: GNS3 Web UI 2020.2.0-beta.2
|
||||
Current version: GNS3 Web UI 2020.2.0-beta.4
|
||||
|
||||
Bug Fixes
|
||||
- New port setting for GNS3 VM preferences
|
||||
- Option to auto-hide menu toolbar on the left side
|
||||
- Server type in template preferences
|
||||
- Error when selecting existing Docker image
|
||||
- Default values in templates
|
||||
- TypeError: Cannot read property 'message' of undefined
|
||||
- TypeError: e.error is undefined
|
||||
- TypeError: Cannot read property 'placements' of null
|
||||
- Creating IOS templates -> fix for platforms and network adapters
|
||||
|
||||
GNS3 Web UI 2020.2.0-beta.2
|
||||
|
||||
What's New
|
||||
- Drag & drop to add new nodes on topology
|
||||
|
@ -169,7 +169,7 @@ const routes: Routes = [
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes, { anchorScrolling: 'enabled', enableTracing: true, scrollPositionRestoration: 'enabled'})],
|
||||
imports: [RouterModule.forRoot(routes, { anchorScrolling: 'enabled', enableTracing: false, scrollPositionRestoration: 'enabled'})],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule {}
|
||||
|
@ -278,6 +278,8 @@ import { ConsoleGuard } from './guards/console-guard';
|
||||
import { NewTemplateDialogComponent } from './components/project-map/new-template-dialog/new-template-dialog.component';
|
||||
import { ApplianceService } from './services/appliances.service';
|
||||
import { DataSourceFilter } from './filters/dataSourceFilter';
|
||||
import { ChangeHostnameActionComponent } from './components/project-map/context-menu/actions/change-hostname/change-hostname-action.component';
|
||||
import { ChangeHostnameDialogComponent } from './components/project-map/change-hostname-dialog/change-hostname-dialog.component';
|
||||
|
||||
if (environment.production) {
|
||||
Raven.config('https://b2b1cfd9b043491eb6b566fd8acee358@sentry.io/842726', {
|
||||
@ -466,7 +468,9 @@ if (environment.production) {
|
||||
ConsoleWrapperComponent,
|
||||
HttpConsoleNewTabActionComponent,
|
||||
WebConsoleFullWindowComponent,
|
||||
NewTemplateDialogComponent
|
||||
NewTemplateDialogComponent,
|
||||
ChangeHostnameActionComponent,
|
||||
ChangeHostnameDialogComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
@ -607,7 +611,8 @@ if (environment.production) {
|
||||
ConfirmationBottomSheetComponent,
|
||||
ConfigDialogComponent,
|
||||
AdbutlerComponent,
|
||||
NewTemplateDialogComponent
|
||||
NewTemplateDialogComponent,
|
||||
ChangeHostnameDialogComponent
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
|
@ -18,21 +18,21 @@ export class StylesToFontConverter implements Converter<string, Font> {
|
||||
});
|
||||
|
||||
ast.children.forEach(child => {
|
||||
if (child.property === 'font-size') {
|
||||
if (child.property === 'font-size' && child.value && child.value.children) {
|
||||
child.value.children.forEach(value => {
|
||||
if (value.type === 'Dimension') {
|
||||
font.font_size = parseInt(value.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (child.property === 'font-family') {
|
||||
if (child.property === 'font-family' && child.value && child.value.children) {
|
||||
child.value.children.forEach(value => {
|
||||
if (value.type === 'Identifier') {
|
||||
font.font_family = value.name;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (child.property === 'font-weight') {
|
||||
if (child.property === 'font-weight' && child.value && child.value.children) {
|
||||
child.value.children.forEach(value => {
|
||||
if (value.type === 'Identifier') {
|
||||
font.font_weight = value.name;
|
||||
|
@ -11,7 +11,7 @@ export class CssFixer {
|
||||
|
||||
// fixes font-size when unit (pt|px) is not defined
|
||||
ast.children.forEach(child => {
|
||||
if (child.property === 'font-size') {
|
||||
if (child.property === 'font-size' && child.value && child.value.children) {
|
||||
child.value.children.forEach(value => {
|
||||
if (value.type === 'Number') {
|
||||
const fontSize = value.value.toString();
|
||||
|
@ -30,7 +30,7 @@ export class FontFixer {
|
||||
let isByIdentifier = true;
|
||||
|
||||
ast.children.forEach(child => {
|
||||
if (child.property === 'font-family') {
|
||||
if (child.property === 'font-family' && child.value && child.value.children) {
|
||||
child.value.children.forEach(value => {
|
||||
if (value.type === 'Identifier') {
|
||||
fontFamilyPointer = value;
|
||||
@ -41,7 +41,7 @@ export class FontFixer {
|
||||
}
|
||||
});
|
||||
}
|
||||
if (child.property === 'font-size') {
|
||||
if (child.property === 'font-size' && child.value && child.value.children) {
|
||||
child.value.children.forEach(value => {
|
||||
if (value.type === 'Dimension') {
|
||||
fontSizePointer = value;
|
||||
|
@ -54,6 +54,7 @@ export class Properties {
|
||||
qemu_path: string;
|
||||
environment: string;
|
||||
extra_hosts: string;
|
||||
replicate_network_connection_state: boolean;
|
||||
}
|
||||
|
||||
export class Node {
|
||||
|
@ -87,12 +87,14 @@ export class NodeWidget implements Widget {
|
||||
})
|
||||
.attr('xnode:href', (n: MapNode) => n.symbolUrl)
|
||||
.attr('width', (n: MapNode) => {
|
||||
if (!n.width) return 60
|
||||
return n.width
|
||||
if (!n.width) return 60;
|
||||
if (n.width > 64) return 64;
|
||||
return n.width;
|
||||
})
|
||||
.attr('height', (n: MapNode) => {
|
||||
if (!n.height) return 60
|
||||
return n.height
|
||||
if (!n.height) return 60;
|
||||
if (n.height > 64) return 64;
|
||||
return n.height;
|
||||
})
|
||||
.attr('x', (n: MapNode) => 0)
|
||||
.attr('y', (n: MapNode) => 0)
|
||||
|
@ -4,7 +4,8 @@ import { ToasterService } from '../../services/toaster.service';
|
||||
export class ToasterErrorHandler extends RavenErrorHandler {
|
||||
handleError(err: any): void {
|
||||
super.handleError(err);
|
||||
|
||||
if (!err) return;
|
||||
|
||||
const toasterService = this.injector.get(ToasterService);
|
||||
if (err.error && err.error.message) {
|
||||
toasterService.error(err.error.message);
|
||||
|
@ -24,22 +24,26 @@ export class DirectLinkComponent implements OnInit {
|
||||
const serverIp = this.route.snapshot.paramMap.get('server_ip');
|
||||
const serverPort = +this.route.snapshot.paramMap.get('server_port');
|
||||
const projectId = this.route.snapshot.paramMap.get('project_id');
|
||||
|
||||
this.serverService.serviceInitialized.subscribe(async (value: boolean) => {
|
||||
if (value) {
|
||||
const servers = await this.serverService.findAll();
|
||||
const server = servers.filter(server => server.host === serverIp && server.port === serverPort)[0];
|
||||
|
||||
const servers = await this.serverService.findAll();
|
||||
const server = servers.filter(server => server.host === serverIp && server.port === serverPort)[0];
|
||||
|
||||
if (server) {
|
||||
this.router.navigate(['/server', server.id, 'project', projectId]);
|
||||
} else {
|
||||
let serverToAdd: Server = new Server();
|
||||
serverToAdd.host = serverIp;
|
||||
serverToAdd.port = serverPort;
|
||||
serverToAdd.location = 'bundled';
|
||||
serverToAdd.name = serverIp;
|
||||
|
||||
this.serverService.create(serverToAdd).then((addedServer: Server) => {
|
||||
this.router.navigate(['/server', addedServer.id, 'project', projectId]);
|
||||
});
|
||||
}
|
||||
if (server) {
|
||||
this.router.navigate(['/server', server.id, 'project', projectId]);
|
||||
} else {
|
||||
let serverToAdd: Server = new Server();
|
||||
serverToAdd.host = serverIp;
|
||||
serverToAdd.port = serverPort;
|
||||
serverToAdd.location = 'bundled';
|
||||
serverToAdd.name = serverIp;
|
||||
|
||||
this.serverService.create(serverToAdd).then((addedServer: Server) => {
|
||||
this.router.navigate(['/server', addedServer.id, 'project', projectId]);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
<div class="default-content">
|
||||
<mat-card class="matCard">
|
||||
<mat-radio-group class="radio-group">
|
||||
<mat-radio-button class="radio-button" value="1" (click)="setServerType('local')" checked>Run the cloud node on your local computer</mat-radio-button>
|
||||
<mat-radio-button class="radio-button" value="1" (click)="setServerType('local')" checked>Run the cloud node locally</mat-radio-button>
|
||||
<mat-radio-button [disabled]="!isGns3VmAvailable" class="radio-button" value="2" (click)="setServerType('gns3 vm')">Run the cloud node on the GNS3 VM</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
<div class="default-content">
|
||||
<mat-card class="matCard">
|
||||
<mat-radio-group class="radio-group">
|
||||
<mat-radio-button class="radio-button" value="1" (click)="setServerType('local')" checked>Run the Ethernet Hub on your local computer</mat-radio-button>
|
||||
<mat-radio-button class="radio-button" value="1" (click)="setServerType('local')" checked>Run the Ethernet Hub locally</mat-radio-button>
|
||||
<mat-radio-button [disabled]="!isGns3VmAvailable" class="radio-button" value="2" (click)="setServerType('gns3 vm')">Run the Ethernet Hub on the GNS3 VM</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
|
||||
|
@ -7,10 +7,10 @@
|
||||
<div class="default-content">
|
||||
<mat-card class="matCard">
|
||||
<mat-radio-group class="radio-group">
|
||||
<mat-radio-button class="radio-button" value="1" (click)="setServerType('local')" checked>Run the Ethernet switch on your local computer</mat-radio-button>
|
||||
<mat-radio-button class="radio-button" value="1" (click)="setServerType('local')" checked>Run the Ethernet switch locally</mat-radio-button>
|
||||
<mat-radio-button [disabled]="!isGns3VmAvailable" class="radio-button" value="2" (click)="setServerType('gns3 vm')">Run the Ethernet switch on the GNS3 VM</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
|
||||
|
||||
<form [formGroup]="formGroup">
|
||||
<mat-form-field class="form-field">
|
||||
<input formControlName="templateName" matInput type="text" placeholder="Template name">
|
||||
|
@ -1,34 +1,32 @@
|
||||
<mat-card>
|
||||
<div class="menu">
|
||||
<mat-radio-group aria-label="Select an option" class="radio-selection">
|
||||
<mat-radio-button value="1" (click)="setFilter('all')" checked>All symbols</mat-radio-button>
|
||||
<mat-radio-button value="2" (click)="setFilter('builtin')">Built-in symbols</mat-radio-button>
|
||||
<mat-radio-button value="3" (click)="setFilter('custom')">Custom symbols</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
<input
|
||||
type="file"
|
||||
accept=".svg, .bmp, .jpeg, .jpg, .gif, .png"
|
||||
class="non-visible"
|
||||
#file
|
||||
(change)="uploadSymbolFile($event)"/>
|
||||
<button mat-button (click)="file.click()">
|
||||
<mat-icon>add</mat-icon>
|
||||
Add symbol
|
||||
<div class="menu">
|
||||
<mat-radio-group aria-label="Select an option" class="radio-selection">
|
||||
<mat-radio-button value="1" (click)="setFilter('all')" checked>All symbols</mat-radio-button>
|
||||
<mat-radio-button value="2" (click)="setFilter('builtin')">Built-in symbols</mat-radio-button>
|
||||
<mat-radio-button value="3" (click)="setFilter('custom')">Custom symbols</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
<input
|
||||
type="file"
|
||||
accept=".svg, .bmp, .jpeg, .jpg, .gif, .png"
|
||||
class="non-visible"
|
||||
#file
|
||||
(change)="uploadSymbolFile($event)"/>
|
||||
<button mat-button (click)="file.click()">
|
||||
<mat-icon>add</mat-icon>
|
||||
Add symbol
|
||||
</button>
|
||||
</div>
|
||||
<form>
|
||||
<mat-form-field class="example-full-width">
|
||||
<input matInput
|
||||
placeholder="Search by filename"
|
||||
[(ngModel)]="searchText"
|
||||
[ngModelOptions]="{standalone: true}">
|
||||
</mat-form-field>
|
||||
</form>
|
||||
<div class="wrapper">
|
||||
<div class="buttonWrapper" *ngFor="let symbol of filteredSymbols | filenamefilter: searchText">
|
||||
<button [ngClass]="{ buttonSelected: isSelected === symbol.symbol_id }" class="button" (click)="setSelected(symbol.symbol_id)">
|
||||
<img [ngClass]="{ imageSelected: isSelected === symbol.symbol_id }" class="image" [src]="getImageSourceForTemplate(symbol.symbol_id)"/>
|
||||
</button>
|
||||
</div>
|
||||
<form>
|
||||
<mat-form-field class="example-full-width">
|
||||
<input matInput
|
||||
placeholder="Search by filename"
|
||||
[(ngModel)]="searchText"
|
||||
[ngModelOptions]="{standalone: true}">
|
||||
</mat-form-field>
|
||||
</form>
|
||||
<div class="wrapper">
|
||||
<div class="buttonWrapper" *ngFor="let symbol of filteredSymbols | filenamefilter: searchText">
|
||||
<button [ngClass]="{ buttonSelected: isSelected === symbol.symbol_id }" class="button" (click)="setSelected(symbol.symbol_id)">
|
||||
<img [ngClass]="{ imageSelected: isSelected === symbol.symbol_id }" class="image" [src]="getImageSourceForTemplate(symbol.symbol_id)"/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card>
|
||||
</div>
|
||||
|
@ -7,23 +7,24 @@
|
||||
<div class="default-content">
|
||||
<div class="container mat-elevation-z8">
|
||||
<mat-vertical-stepper [linear]="true">
|
||||
<mat-step label="Server type">
|
||||
<mat-step label="Server type" [completed]="isGns3VmChosen || isLocalComputerChosen">
|
||||
<mat-radio-group class="radio-group">
|
||||
<!-- <mat-radio-button class="radio-button" value="1" (click)="setServerType('remote computer')">Run this Docker container on a remote computer</mat-radio-button> -->
|
||||
<mat-radio-button class="radio-button" value="1" (click)="setServerType('local')" checked>Run this Docker container on your local computer</mat-radio-button>
|
||||
<mat-radio-button class="radio-button" value="1" (click)="setServerType('local')" checked>Run this Docker container locally</mat-radio-button>
|
||||
<mat-radio-button [disabled]="!isGns3VmAvailable" class="radio-button" value="2" (click)="setServerType('gns3 vm')">Run this Docker container on the GNS3 VM</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</mat-step>
|
||||
<mat-step label="Docker Virtual Machine">
|
||||
|
||||
<mat-step label="Docker Virtual Machine" [completed]="selectedImage || virtualMachineForm.get('filename').value">
|
||||
<form [formGroup]="virtualMachineForm">
|
||||
<mat-radio-group class="radio-group">
|
||||
<mat-radio-button class="radio-button" value="1" (click)="setDiskImage('existingImage')" checked>Existing image</mat-radio-button>
|
||||
<mat-radio-button class="radio-button" value="2" (click)="setDiskImage('newImage')">New image</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
<mat-select
|
||||
*ngIf="!newImageSelected"
|
||||
placeholder="Image list"
|
||||
[ngModelOptions]="{standalone: true}"
|
||||
<mat-select
|
||||
*ngIf="!newImageSelected"
|
||||
placeholder="Image list"
|
||||
[ngModelOptions]="{standalone: true}"
|
||||
[(ngModel)]="selectedImage">
|
||||
<mat-option *ngFor="let image of dockerImages" [value]="image">
|
||||
{{image.image}}
|
||||
@ -41,7 +42,8 @@
|
||||
</div>
|
||||
</form>
|
||||
</mat-step>
|
||||
<mat-step label="Container name">
|
||||
|
||||
<mat-step label="Container name" [completed]="containerNameForm.get('templateName').value">
|
||||
<form [formGroup]="containerNameForm">
|
||||
<mat-form-field class="form-field">
|
||||
<input
|
||||
@ -53,7 +55,8 @@
|
||||
</mat-form-field>
|
||||
</form>
|
||||
</mat-step>
|
||||
<mat-step label="Network adapters">
|
||||
|
||||
<mat-step label="Network adapters" [completed]="networkAdaptersForm.get('adapters').value">
|
||||
<form [formGroup]="networkAdaptersForm">
|
||||
<mat-form-field class="form-field">
|
||||
<input
|
||||
@ -65,7 +68,8 @@
|
||||
</mat-form-field>
|
||||
</form>
|
||||
</mat-step>
|
||||
<mat-step label="Start command">
|
||||
|
||||
<mat-step label="Start command" [completed]="dockerTemplate.start_command">
|
||||
<mat-form-field class="form-field">
|
||||
<input
|
||||
matInput
|
||||
@ -74,16 +78,18 @@
|
||||
placeholder="Start command"/>
|
||||
</mat-form-field>
|
||||
</mat-step>
|
||||
<mat-step label="Console type">
|
||||
<mat-select
|
||||
placeholder="Console type"
|
||||
[ngModelOptions]="{standalone: true}"
|
||||
|
||||
<mat-step label="Console type" [completed]="dockerTemplate.console_type">
|
||||
<mat-select
|
||||
placeholder="Console type"
|
||||
[ngModelOptions]="{standalone: true}"
|
||||
[(ngModel)]="dockerTemplate.console_type">
|
||||
<mat-option *ngFor="let type of consoleTypes" [value]="type">
|
||||
{{type}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-step>
|
||||
|
||||
<mat-step label="Environment">
|
||||
<mat-form-field class="form-field">
|
||||
<textarea matInput type="text" [(ngModel)]="dockerTemplate.environment"></textarea>
|
||||
|
@ -25,6 +25,7 @@ export class AddDockerTemplateComponent implements OnInit {
|
||||
consoleTypes: string[] = [];
|
||||
isRemoteComputerChosen: boolean = false;
|
||||
dockerImages: DockerImage[] = [];
|
||||
selectedImage: DockerImage;
|
||||
newImageSelected: boolean = false;
|
||||
|
||||
virtualMachineForm: FormGroup;
|
||||
@ -49,15 +50,15 @@ export class AddDockerTemplateComponent implements OnInit {
|
||||
this.dockerTemplate = new DockerTemplate();
|
||||
|
||||
this.virtualMachineForm = this.formBuilder.group({
|
||||
filename: new FormControl('', Validators.required)
|
||||
filename: new FormControl(null, Validators.required)
|
||||
});
|
||||
|
||||
this.containerNameForm = this.formBuilder.group({
|
||||
templateName: new FormControl('', Validators.required)
|
||||
templateName: new FormControl(null, Validators.required)
|
||||
});
|
||||
|
||||
this.networkAdaptersForm = this.formBuilder.group({
|
||||
adapters: new FormControl('', Validators.required)
|
||||
adapters: new FormControl('1', Validators.required)
|
||||
});
|
||||
}
|
||||
|
||||
@ -101,11 +102,17 @@ export class AddDockerTemplateComponent implements OnInit {
|
||||
}
|
||||
|
||||
addTemplate() {
|
||||
if ((!this.virtualMachineForm.invalid || !this.newImageSelected) && !this.containerNameForm.invalid && !this.networkAdaptersForm.invalid) {
|
||||
if ((!this.virtualMachineForm.invalid || (!this.newImageSelected && this.selectedImage)) && !this.containerNameForm.invalid && !this.networkAdaptersForm.invalid) {
|
||||
this.dockerTemplate.template_id = uuid();
|
||||
this.dockerTemplate.image = this.virtualMachineForm.get('filename').value;
|
||||
|
||||
if (this.newImageSelected) {
|
||||
this.dockerTemplate.image = this.virtualMachineForm.get('filename').value;
|
||||
} else {
|
||||
this.dockerTemplate.image = this.selectedImage.image;
|
||||
}
|
||||
|
||||
this.dockerTemplate.name = this.containerNameForm.get('templateName').value;
|
||||
this.dockerTemplate.adapters = this.networkAdaptersForm.get('adapters').value;
|
||||
this.dockerTemplate.adapters = +this.networkAdaptersForm.get('adapters').value;
|
||||
this.dockerTemplate.compute_id = this.isGns3VmChosen ? 'vm' : 'local';
|
||||
|
||||
this.dockerService.addTemplate(this.server, this.dockerTemplate).subscribe((template: DockerTemplate) => {
|
||||
|
@ -7,14 +7,14 @@
|
||||
<div class="default-content" *ngIf="iosTemplate">
|
||||
<div class="container mat-elevation-z8">
|
||||
<mat-vertical-stepper [linear]="true">
|
||||
<mat-step label="Server type">
|
||||
<mat-step label="Server type" [completed]="isGns3VmChosen || isLocalComputerChosen">
|
||||
<mat-radio-group class="radio-group">
|
||||
<mat-radio-button class="radio-button" value="1" (click)="setServerType('local')" checked>Run this IOS router on your local computer</mat-radio-button>
|
||||
<mat-radio-button class="radio-button" value="1" (click)="setServerType('local')" checked>Run this IOS router locally</mat-radio-button>
|
||||
<mat-radio-button [disabled]="!isGns3VmAvailable" class="radio-button" value="2" (click)="setServerType('gns3 vm')">Run this IOS router on the GNS3 VM</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</mat-step>
|
||||
|
||||
<mat-step label="IOS image">
|
||||
<mat-step label="IOS image" [completed]="iosImageForm.get('imageName').value">
|
||||
<input
|
||||
type="file"
|
||||
accept=".bin, .image"
|
||||
@ -26,8 +26,8 @@
|
||||
<button mat-raised-button color="primary" (click)="file.click()">Click to add image</button> or create from existing one
|
||||
<form [formGroup]="iosImageForm">
|
||||
<mat-form-field class="form-field">
|
||||
<mat-select
|
||||
placeholder="Image"
|
||||
<mat-select
|
||||
placeholder="Image"
|
||||
(selectionChange)="onImageChosen($event)"
|
||||
formControlName="imageName">
|
||||
<mat-option *ngFor="let image of iosImages" [value]="image.filename">
|
||||
@ -37,17 +37,20 @@
|
||||
</mat-form-field>
|
||||
</form>
|
||||
</mat-step>
|
||||
<mat-step label="Name and platform">
|
||||
|
||||
<mat-step
|
||||
label="Name and platform"
|
||||
[completed]="iosNameForm.get('templateName').value && iosNameForm.get('platform').value && iosNameForm.get('chassis').value">
|
||||
<form [formGroup]="iosNameForm">
|
||||
<mat-form-field class="form-field">
|
||||
<input
|
||||
matInput type="text"
|
||||
<input
|
||||
matInput type="text"
|
||||
formControlName="templateName"
|
||||
placeholder="Name"/>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="form-field">
|
||||
<mat-select
|
||||
placeholder="Platform"
|
||||
<mat-select
|
||||
placeholder="Platform"
|
||||
(selectionChange)="onPlatformChosen($event)"
|
||||
formControlName="platform">
|
||||
<mat-option *ngFor="let platform of platforms" [value]="platform">
|
||||
@ -56,8 +59,8 @@
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="form-field" *ngIf="chassis[iosNameForm.get('platform').value]">
|
||||
<mat-select
|
||||
placeholder="Chassis"
|
||||
<mat-select
|
||||
placeholder="Chassis"
|
||||
(selectionChange)="onChassisChosen($event)"
|
||||
formControlName="chassis">
|
||||
<mat-option *ngFor="let chassis of chassis[iosNameForm.get('platform').value]" [value]="chassis">
|
||||
@ -66,17 +69,18 @@
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</form>
|
||||
<mat-checkbox
|
||||
*ngIf="platformsWithEtherSwitchRouterOption[iosTemplate.platform]"
|
||||
<mat-checkbox
|
||||
*ngIf="platformsWithEtherSwitchRouterOption[iosTemplate.platform]"
|
||||
[(ngModel)]="isEtherSwitchRouter">
|
||||
This is an EtherSwitch router
|
||||
</mat-checkbox>
|
||||
</mat-step>
|
||||
<mat-step label="Memory">
|
||||
|
||||
<mat-step label="Memory" [completed]="iosMemoryForm.get('memory').value">
|
||||
<form [formGroup]="iosMemoryForm">
|
||||
<mat-form-field class="form-field">
|
||||
<input
|
||||
matInput type="number"
|
||||
<input
|
||||
matInput type="number"
|
||||
formControlName="memory"
|
||||
value="defaultRam[iosNameForm.get('platform').value]"
|
||||
placeholder="Default RAM"/>
|
||||
@ -87,54 +91,57 @@
|
||||
</mat-label>
|
||||
</form>
|
||||
</mat-step>
|
||||
|
||||
<mat-step label="Network adapters">
|
||||
<div *ngIf="chassis[iosNameForm.get('platform').value]">
|
||||
<!-- <div *ngIf="chassis[iosNameForm.get('platform').value]">
|
||||
<div *ngFor="let index of [0,1,2,3,4,5,6,7]">
|
||||
<mat-select
|
||||
placeholder="Slot {{index}}"
|
||||
<mat-select
|
||||
placeholder="Slot {{index}}"
|
||||
[(ngModel)]="networkAdaptersForTemplate[index]"
|
||||
[ngModelOptions]="{standalone: true}"
|
||||
*ngIf="networkAdapters[iosNameForm.get('chassis').value] && networkAdapters[iosNameForm.get('chassis').value][index]">
|
||||
<mat-option *ngFor="let option of networkAdapters[iosNameForm.get('chassis').value][index]" [value]="option">
|
||||
{{option}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-select>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="chassis[iosNameForm.get('platform').value]">
|
||||
</div> -->
|
||||
<div *ngIf="selectedPlatform">
|
||||
<div *ngFor="let index of [0,1,2,3,4,5,6,7]">
|
||||
<mat-select
|
||||
placeholder="Slot {{index}}"
|
||||
<mat-select
|
||||
placeholder="Slot {{index}}"
|
||||
[(ngModel)]="networkAdaptersForTemplate[index]"
|
||||
[ngModelOptions]="{standalone: true}"
|
||||
*ngIf="networkAdaptersForPlatform[iosNameForm.get('platform').value] && networkAdaptersForPlatform[iosNameForm.get('platform').value][index]">
|
||||
<mat-option *ngFor="let option of networkAdaptersForPlatform[iosNameForm.get('platform').value][index]" [value]="option">
|
||||
{{option}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-select>
|
||||
</div>
|
||||
</div>
|
||||
</mat-step>
|
||||
|
||||
<mat-step label="WIC modules">
|
||||
<div *ngIf="iosNameForm.get('platform').value">
|
||||
<div *ngFor="let index of [0,1,2,3]">
|
||||
<mat-select
|
||||
placeholder="WIC {{index}}"
|
||||
<mat-select
|
||||
placeholder="WIC {{index}}"
|
||||
[(ngModel)]="networkModulesForTemplate[index]"
|
||||
[ngModelOptions]="{standalone: true}"
|
||||
*ngIf="networkModules[iosNameForm.get('platform').value] && networkModules[iosNameForm.get('platform').value][index]">
|
||||
<mat-option *ngFor="let option of networkModules[iosNameForm.get('platform').value][index]" [value]="option">
|
||||
{{option}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-select>
|
||||
</div>
|
||||
</div>
|
||||
</mat-step>
|
||||
|
||||
<mat-step label="Idle-PC">
|
||||
<mat-form-field class="form-field">
|
||||
<input
|
||||
matInput type="text"
|
||||
[(ngModel)]="iosTemplate.idlepc"
|
||||
<input
|
||||
matInput type="text"
|
||||
[(ngModel)]="iosTemplate.idlepc"
|
||||
placeholder="Idle-PC"/>
|
||||
</mat-form-field>
|
||||
</mat-step>
|
||||
|
@ -28,6 +28,7 @@ export class AddIosTemplateComponent implements OnInit {
|
||||
iosImageForm: FormGroup;
|
||||
iosNameForm: FormGroup;
|
||||
iosMemoryForm: FormGroup;
|
||||
selectedPlatform: string;
|
||||
|
||||
networkAdaptersForTemplate: string[] = [];
|
||||
networkModulesForTemplate: string[] = [];
|
||||
@ -187,8 +188,8 @@ export class AddIosTemplateComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(Object.keys(this.networkAdaptersForPlatform[this.iosTemplate.chassis])){
|
||||
for(let i=0; i<Object.keys(this.networkAdaptersForPlatform[this.iosTemplate.chassis]).length; i++){
|
||||
if(this.networkAdaptersForPlatform[this.iosNameForm.get('platform').value]){
|
||||
for(let i=0; i<Object.keys(this.networkAdaptersForPlatform[this.iosNameForm.get('platform').value]).length; i++){
|
||||
if(!this.networkAdaptersForTemplate[i]) this.networkAdaptersForTemplate[i] = '';
|
||||
}
|
||||
}
|
||||
@ -224,11 +225,23 @@ export class AddIosTemplateComponent implements OnInit {
|
||||
let name: string = this.iosImageForm.get("imageName").value.split('-')[0];
|
||||
this.iosNameForm.controls['templateName'].setValue(name);
|
||||
|
||||
if (this.platforms.includes(name.split('-')[0])) {
|
||||
if (name === 'c3620' || name === 'c3640' || name === 'c3660') {
|
||||
this.iosNameForm.controls['platform'].setValue('c3600');
|
||||
this.selectedPlatform = 'c3600';
|
||||
} else {
|
||||
this.iosNameForm.controls['platform'].setValue(name);
|
||||
this.iosNameForm.controls['chassis'].setValue('');
|
||||
this.iosMemoryForm.controls['memory'].setValue(this.defaultRam[name]);
|
||||
this.selectedPlatform = name;
|
||||
}
|
||||
|
||||
if (name === 'c1700') {
|
||||
this.iosNameForm.controls['chassis'].setValue('1720');
|
||||
} else if (name === 'c2600') {
|
||||
this.iosNameForm.controls['chassis'].setValue('2610');
|
||||
} else {
|
||||
this.iosNameForm.controls['chassis'].setValue('');
|
||||
}
|
||||
|
||||
this.iosMemoryForm.controls['memory'].setValue(this.defaultRam[name]);
|
||||
}
|
||||
|
||||
onPlatformChosen() {
|
||||
|
@ -1,7 +1,7 @@
|
||||
<div class="content">
|
||||
<div class="default-header">
|
||||
<div class="row">
|
||||
<h1 class="col">IOS routers templates</h1>
|
||||
<h1 class="col">IOS router 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/dynamips/templates/addtemplate" mat-raised-button color="primary">Add IOS router template</button>
|
||||
</div>
|
||||
|
@ -7,19 +7,21 @@
|
||||
<div class="default-content">
|
||||
<div class="container mat-elevation-z8">
|
||||
<mat-vertical-stepper [linear]="true">
|
||||
<mat-step label="Server type">
|
||||
<mat-step label="Server type" [completed]="isGns3VmChosen || isLocalComputerChosen">
|
||||
<mat-radio-group class="radio-group">
|
||||
<mat-radio-button class="radio-button" value="1" (click)="setServerType('local')" checked>Run this IOU device on your local computer</mat-radio-button>
|
||||
<mat-radio-button class="radio-button" value="1" (click)="setServerType('local')" checked>Run this IOU device locally</mat-radio-button>
|
||||
<mat-radio-button [disabled]="!isGns3VmAvailable" class="radio-button" value="2" (click)="setServerType('gns3 vm')">Run this IOU device on the GNS3 VM</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</mat-step>
|
||||
<mat-step label="Name">
|
||||
|
||||
<mat-step label="Name" [completed]="templateNameForm.get('templateName').value">
|
||||
<form [formGroup]="templateNameForm">
|
||||
<mat-form-field class="form-field">
|
||||
<input matInput placeholder="Name" type="text" formControlName="templateName"/>
|
||||
</mat-form-field>
|
||||
</form>
|
||||
</mat-step>
|
||||
|
||||
<mat-step label="Image">
|
||||
<form [formGroup]="imageForm">
|
||||
<mat-radio-group class="radio-group">
|
||||
@ -27,8 +29,8 @@
|
||||
<mat-radio-button class="radio-button" value="2" (click)="setDiskImage('newImage')">New image</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
<mat-form-field class="form-field">
|
||||
<mat-select
|
||||
placeholder="Type"
|
||||
<mat-select
|
||||
placeholder="Type"
|
||||
[(ngModel)]="selectedType"
|
||||
[ngModelOptions]="{standalone: true}">
|
||||
<mat-option *ngFor="let type of types" [value]="type">
|
||||
@ -37,8 +39,8 @@
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="form-field" *ngIf="!newImageSelected">
|
||||
<mat-select
|
||||
placeholder="IOU image"
|
||||
<mat-select
|
||||
placeholder="IOU image"
|
||||
[(ngModel)]="iouTemplate.path"
|
||||
[ngModelOptions]="{standalone: true}">
|
||||
<mat-option *ngFor="let image of iouImages" [value]="image.path">
|
||||
|
@ -49,7 +49,7 @@ export class AddIouTemplateComponent implements OnInit {
|
||||
this.iouTemplate = new IouTemplate();
|
||||
|
||||
this.templateNameForm = this.formBuilder.group({
|
||||
templateName: new FormControl('', Validators.required)
|
||||
templateName: new FormControl(null, Validators.required)
|
||||
});
|
||||
|
||||
this.imageForm = this.formBuilder.group({
|
||||
@ -133,6 +133,14 @@ export class AddIouTemplateComponent implements OnInit {
|
||||
this.iouTemplate.name = this.templateNameForm.get("templateName").value;
|
||||
if (this.newImageSelected) this.iouTemplate.path = this.imageForm.get("imageName").value;
|
||||
this.iouTemplate.compute_id = this.isGns3VmChosen ? 'vm' : 'local';
|
||||
|
||||
if (this.selectedType === 'L2 image') {
|
||||
this.iouTemplate.ethernet_adapters = 4;
|
||||
this.iouTemplate.serial_adapters = 0;
|
||||
} else if (this.selectedType === 'L3 image') {
|
||||
this.iouTemplate.ethernet_adapters = 2;
|
||||
this.iouTemplate.serial_adapters = 2;
|
||||
}
|
||||
|
||||
this.iouService.addTemplate(this.server, this.iouTemplate).subscribe((template: IouTemplate) => {
|
||||
this.goBack();
|
||||
|
@ -1,7 +1,7 @@
|
||||
<div class="content">
|
||||
<div class="default-header">
|
||||
<div class="row">
|
||||
<h1 class="col">IOU devices templates</h1>
|
||||
<h1 class="col">IOU device 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/iou/addtemplate" mat-raised-button color="primary">Add IOU device template</button>
|
||||
</div>
|
||||
|
@ -7,31 +7,29 @@
|
||||
<div class="default-content">
|
||||
<div class="container mat-elevation-z8">
|
||||
<mat-vertical-stepper [linear]="true">
|
||||
<mat-step label="Server type">
|
||||
<mat-step label="Server type" [completed]="isGns3VmChosen || isLocalComputerChosen">
|
||||
<mat-radio-group class="radio-group">
|
||||
<mat-radio-button class="radio-button" value="1" (click)="setServerType('local')" checked>Run this QEMU VM on your local computer</mat-radio-button>
|
||||
<mat-radio-button class="radio-button" value="1" (click)="setServerType('local')" checked>Run this QEMU VM locally</mat-radio-button>
|
||||
<mat-radio-button [disabled]="!isGns3VmAvailable" class="radio-button" value="2" (click)="setServerType('gns3 vm')">Run this QEMU VM on the GNS3 VM</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</mat-step>
|
||||
|
||||
<mat-step label="QEMU VM Name">
|
||||
<mat-step label="QEMU VM Name" [completed]="nameForm.get('templateName').value">
|
||||
<form [formGroup]="nameForm">
|
||||
<mat-form-field class="form-field">
|
||||
<input
|
||||
matInput type="text"
|
||||
<input
|
||||
matInput type="text"
|
||||
formControlName="templateName"
|
||||
placeholder="Please choose a descriptive name for your new QEMU virtual machine"
|
||||
ngDefaultContro/>
|
||||
</mat-form-field><br/>
|
||||
<mat-checkbox>
|
||||
This is a legacy ASA VM
|
||||
</mat-checkbox>
|
||||
</form>
|
||||
</mat-step>
|
||||
<mat-step label="QEMU binary and memory">
|
||||
|
||||
<mat-step label="QEMU binary and memory" [completed]="memoryForm.get('ramMemory').value && selectedBinary">
|
||||
<form [formGroup]="memoryForm">
|
||||
<mat-form-field class="form-field">
|
||||
<mat-select
|
||||
<mat-select
|
||||
placeholder="Qemu binary"
|
||||
[(ngModel)]="selectedBinary"
|
||||
[ngModelOptions]="{standalone: true}">
|
||||
@ -41,19 +39,20 @@
|
||||
</mat-select>
|
||||
</mat-form-field><br/>
|
||||
<mat-form-field class="form-field">
|
||||
<input
|
||||
matInput type="number"
|
||||
placeholder="RAM"
|
||||
<input
|
||||
matInput type="number"
|
||||
placeholder="RAM"
|
||||
formControlName="ramMemory"
|
||||
ngDefaultContro/>
|
||||
ngDefaultControl/>
|
||||
<span matSuffix>MB</span>
|
||||
</mat-form-field>
|
||||
</form>
|
||||
</mat-step>
|
||||
<mat-step label="Console type">
|
||||
|
||||
<mat-step label="Console type" [completed]="qemuTemplate.console_type">
|
||||
<mat-form-field class="form-field">
|
||||
<mat-select
|
||||
placeholder="Console type"
|
||||
<mat-select
|
||||
placeholder="Console type"
|
||||
[(ngModel)]="qemuTemplate.console_type"
|
||||
[ngModelOptions]="{standalone: true}" >
|
||||
<mat-option *ngFor="let type of consoleTypes" [value]="type">
|
||||
@ -62,16 +61,17 @@
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</mat-step>
|
||||
|
||||
<mat-step label="Disk image">
|
||||
<form [formGroup]="diskForm">
|
||||
<mat-radio-group class="radio-group">
|
||||
<mat-radio-button class="radio-button" value="1" (click)="setDiskImage('existingImage')" checked>Existing image</mat-radio-button>
|
||||
<mat-radio-button class="radio-button" value="2" (click)="setDiskImage('newImage')">New image</mat-radio-button>
|
||||
</mat-radio-group><br/><br/>
|
||||
<mat-select
|
||||
*ngIf="!newImageSelected"
|
||||
placeholder="Disk image (hda)"
|
||||
[ngModelOptions]="{standalone: true}"
|
||||
<mat-select
|
||||
*ngIf="!newImageSelected"
|
||||
placeholder="Disk image (hda)"
|
||||
[ngModelOptions]="{standalone: true}"
|
||||
[(ngModel)]="selectedImage">
|
||||
<mat-option *ngFor="let image of qemuImages" [value]="image">
|
||||
{{image.filename}}
|
||||
|
@ -56,11 +56,11 @@ export class AddQemuVmTemplateComponent implements OnInit {
|
||||
this.qemuTemplate = new QemuTemplate();
|
||||
|
||||
this.nameForm = this.formBuilder.group({
|
||||
templateName: new FormControl('', Validators.required)
|
||||
templateName: new FormControl(null, Validators.required)
|
||||
});
|
||||
|
||||
this.memoryForm = this.formBuilder.group({
|
||||
ramMemory: new FormControl('', Validators.required)
|
||||
ramMemory: new FormControl('256', Validators.required)
|
||||
});
|
||||
|
||||
this.diskForm = this.formBuilder.group({
|
||||
@ -98,6 +98,7 @@ export class AddQemuVmTemplateComponent implements OnInit {
|
||||
|
||||
this.qemuService.getBinaries(server).subscribe((qemuBinaries: QemuBinary[]) => {
|
||||
this.qemuBinaries = qemuBinaries;
|
||||
if (this.qemuBinaries[0]) this.selectedBinary = this.qemuBinaries[0];
|
||||
});
|
||||
|
||||
this.qemuService.getImages(server).subscribe((qemuImages: QemuImage[]) => {
|
||||
|
@ -7,7 +7,7 @@
|
||||
<div class="default-content">
|
||||
<mat-card class="matCard">
|
||||
<mat-radio-group class="radio-group">
|
||||
<mat-radio-button class="radio-button" value="1" (click)="setServerType('local')" checked>Run the VPCS node on your local computer</mat-radio-button>
|
||||
<mat-radio-button class="radio-button" value="1" (click)="setServerType('local')" checked>Run the VPCS node locally</mat-radio-button>
|
||||
<mat-radio-button [disabled]="!isGns3VmAvailable" class="radio-button" value="2" (click)="setServerType('gns3 vm')">Run the VPCS node on the GNS3 VM</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
|
||||
|
@ -0,0 +1,24 @@
|
||||
<h1 mat-dialog-title>Change hostname 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>
|
||||
</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>
|
@ -0,0 +1,3 @@
|
||||
.form-field {
|
||||
width: 100%;
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
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-change-hostname-dialog-component',
|
||||
templateUrl: './change-hostname-dialog.component.html',
|
||||
styleUrls: ['./change-hostname-dialog.component.scss']
|
||||
})
|
||||
export class ChangeHostnameDialogComponent implements OnInit {
|
||||
server: Server;
|
||||
node: Node;
|
||||
inputForm: FormGroup;
|
||||
name: string;
|
||||
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<ChangeHostnameDialogComponent>,
|
||||
public nodeService: NodeService,
|
||||
private toasterService: ToasterService,
|
||||
private formBuilder: FormBuilder
|
||||
) {
|
||||
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;
|
||||
})
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
<button mat-menu-item (click)="changeHostname()">
|
||||
<mat-icon>edit</mat-icon>
|
||||
<span>Change hostname</span>
|
||||
</button>
|
@ -0,0 +1,28 @@
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { Server } from '../../../../../models/server';
|
||||
import { Node } from '../../../../../cartography/models/node';
|
||||
import { MatDialog } from '@angular/material';
|
||||
import { ChangeHostnameDialogComponent } from '../../../change-hostname-dialog/change-hostname-dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-change-hostname-action',
|
||||
templateUrl: './change-hostname-action.component.html'
|
||||
})
|
||||
export class ChangeHostnameActionComponent implements OnInit {
|
||||
@Input() server: Server;
|
||||
@Input() node: Node;
|
||||
|
||||
constructor(private dialog: MatDialog) {}
|
||||
|
||||
ngOnInit() {}
|
||||
|
||||
changeHostname() {
|
||||
const dialogRef = this.dialog.open(ChangeHostnameDialogComponent, {
|
||||
autoFocus: false,
|
||||
disableClose: true
|
||||
});
|
||||
let instance = dialogRef.componentInstance;
|
||||
instance.server = this.server;
|
||||
instance.node = this.node;
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
<button mat-menu-item (click)="changeSymbol()">
|
||||
<mat-icon>edit</mat-icon>
|
||||
<mat-icon>find_replace</mat-icon>
|
||||
<span>Change symbol</span>
|
||||
</button>
|
||||
|
@ -40,6 +40,11 @@
|
||||
[server]="server"
|
||||
[node]="nodes[0]"
|
||||
></app-open-file-explorer-action>
|
||||
<app-change-hostname-action
|
||||
*ngIf="!projectService.isReadOnly(project) && nodes.length===1"
|
||||
[server]="server"
|
||||
[node]="nodes[0]"
|
||||
></app-change-hostname-action>
|
||||
<app-change-symbol-action
|
||||
*ngIf="!projectService.isReadOnly(project) && nodes.length===1"
|
||||
[server]="server"
|
||||
|
@ -155,6 +155,9 @@
|
||||
<br/><mat-checkbox [(ngModel)]="node.properties.legacy_networking">
|
||||
Use the legacy networking mode
|
||||
</mat-checkbox>
|
||||
<br/><mat-checkbox [(ngModel)]="node.properties.replicate_network_connection_state">
|
||||
Replicate network connection state
|
||||
</mat-checkbox>
|
||||
<app-custom-adapters-table
|
||||
#customAdapters
|
||||
[networkTypes]="networkTypes"
|
||||
|
@ -68,6 +68,7 @@ import { NotificationService } from '../../services/notification.service';
|
||||
import { ThemeService } from '../../services/theme.service';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { NewTemplateDialogComponent } from './new-template-dialog/new-template-dialog.component';
|
||||
import { NodeConsoleService } from '../../services/nodeConsole.service';
|
||||
|
||||
|
||||
@Component({
|
||||
@ -157,7 +158,8 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
||||
private bottomSheet: MatBottomSheet,
|
||||
private notificationService: NotificationService,
|
||||
private themeService: ThemeService,
|
||||
private title: Title
|
||||
private title: Title,
|
||||
private nodeConsoleService: NodeConsoleService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@ -435,6 +437,8 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
||||
if(!nodeAddedEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.progressService.activate();
|
||||
this.nodeService.createFromTemplate(this.server, this.project, nodeAddedEvent.template, nodeAddedEvent.x, nodeAddedEvent.y, nodeAddedEvent.server).subscribe((node: Node) => {
|
||||
// if (nodeAddedEvent.name !== nodeAddedEvent.template.name) {
|
||||
// node.name = nodeAddedEvent.name;
|
||||
@ -453,6 +457,8 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
||||
nodeAddedEvent.x = nodeAddedEvent.x + 50 < this.project.scene_width/2 ? nodeAddedEvent.x + 50 : nodeAddedEvent.x;
|
||||
nodeAddedEvent.y = nodeAddedEvent.y + 50 < this.project.scene_height/2 ? nodeAddedEvent.y + 50 : nodeAddedEvent.y;
|
||||
this.onNodeCreation(nodeAddedEvent);
|
||||
} else {
|
||||
this.progressService.deactivate();
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -864,7 +870,9 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
public ngOnDestroy() {
|
||||
this.nodeConsoleService.openConsoles = 0;
|
||||
this.title.setTitle('GNS3 Web UI');
|
||||
|
||||
this.drawingsDataSource.clear();
|
||||
this.nodesDataSource.clear();
|
||||
this.linksDataSource.clear();
|
||||
|
@ -33,7 +33,7 @@
|
||||
Start all nodes when this project is opened
|
||||
</mat-checkbox>
|
||||
|
||||
<mat-checkbox [ngModelOptions]="{standalone: true}" [(ngModel)]="!project.auto_close">
|
||||
<mat-checkbox [ngModelOptions]="{standalone: true}" [(ngModel)]="auto_close">
|
||||
Leave this project running in the background after closing
|
||||
</mat-checkbox>
|
||||
|
||||
|
@ -22,6 +22,8 @@ export class EditProjectDialogComponent implements OnInit {
|
||||
displayedColumns: string[] = ['name', 'value', 'actions'];
|
||||
variables: ProjectVariable[] = [];
|
||||
|
||||
auto_close: boolean;
|
||||
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<EditProjectDialogComponent>,
|
||||
private formBuilder: FormBuilder,
|
||||
@ -52,6 +54,7 @@ export class EditProjectDialogComponent implements OnInit {
|
||||
if (this.project.variables) {
|
||||
this.project.variables.forEach(n => this.variables.push(n));
|
||||
}
|
||||
this.auto_close = !this.project.auto_close;
|
||||
}
|
||||
|
||||
addVariable() {
|
||||
@ -83,6 +86,8 @@ export class EditProjectDialogComponent implements OnInit {
|
||||
this.project.grid_size = this.formGroup.get('nodeGridSize').value;
|
||||
this.project.variables = this.variables;
|
||||
|
||||
this.project.auto_close = !this.project.auto_close;
|
||||
|
||||
this.projectService.update(this.server, this.project).subscribe((project: Project) => {
|
||||
this.toasterService.success(`Project ${project.name} updated.`);
|
||||
this.onNoClick();
|
||||
|
@ -57,6 +57,7 @@ export class AddServerDialogComponent implements OnInit {
|
||||
|
||||
async numberOfLocalServers() {
|
||||
const servers = await this.serverService.findAll();
|
||||
|
||||
return servers.filter((server) => server.location === 'local').length;
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,11 @@ export class ServerDiscoveryComponent implements OnInit {
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.discoverFirstAvailableServer();
|
||||
this.serverService.serviceInitialized.subscribe(async (value: boolean) => {
|
||||
if (value) {
|
||||
this.discoverFirstAvailableServer();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
discoverFirstAvailableServer() {
|
||||
|
@ -40,24 +40,28 @@ export class ServersComponent implements OnInit, OnDestroy {
|
||||
this.isElectronApp = this.electronService.isElectronApp;
|
||||
const runningServersNames = this.serverManagement.getRunningServers();
|
||||
|
||||
this.serverService.findAll().then((servers: Server[]) => {
|
||||
servers.forEach((server) => {
|
||||
const serverIndex = runningServersNames.findIndex((serverName) => server.name === serverName);
|
||||
if(serverIndex >= 0) {
|
||||
server.status = 'running';
|
||||
}
|
||||
});
|
||||
|
||||
servers.forEach((server) => {
|
||||
this.serverService.checkServerVersion(server).subscribe(
|
||||
(serverInfo) => {
|
||||
if ((serverInfo.version.split('.')[1]>=2) && (serverInfo.version.split('.')[0]>=2)) {
|
||||
if (!this.serverDatabase.find(server.name)) this.serverDatabase.addServer(server);
|
||||
this.serverService.serviceInitialized.subscribe(async (value: boolean) => {
|
||||
if (value) {
|
||||
this.serverService.findAll().then((servers: Server[]) => {
|
||||
servers.forEach((server) => {
|
||||
const serverIndex = runningServersNames.findIndex((serverName) => server.name === serverName);
|
||||
if(serverIndex >= 0) {
|
||||
server.status = 'running';
|
||||
}
|
||||
},
|
||||
error => {}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
servers.forEach((server) => {
|
||||
this.serverService.checkServerVersion(server).subscribe(
|
||||
(serverInfo) => {
|
||||
if ((serverInfo.version.split('.')[1]>=2) && (serverInfo.version.split('.')[0]>=2)) {
|
||||
if (!this.serverDatabase.find(server.name)) this.serverDatabase.addServer(server);
|
||||
}
|
||||
},
|
||||
error => {}
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.dataSource = new ServerDataSource(this.serverDatabase);
|
||||
|
@ -62,7 +62,10 @@ export class IosConfigurationService {
|
||||
"c1700": 128,
|
||||
"c2600": 128,
|
||||
"c2691": 256,
|
||||
"c3600": 256,
|
||||
"c3600": 192,
|
||||
"c3620": 192,
|
||||
"c3640": 192,
|
||||
"c3660": 192,
|
||||
"c3725": 256,
|
||||
"c3745": 256,
|
||||
"c7200": 512
|
||||
@ -216,6 +219,33 @@ export class IosConfigurationService {
|
||||
}
|
||||
|
||||
getNetworkAdaptersForPlatform() {
|
||||
let networkAdaptersForPlatform = {};
|
||||
networkAdaptersForPlatform["c2691"] = {
|
||||
0: ["GT96100-FE"],
|
||||
1: this.c3700_nms
|
||||
};
|
||||
networkAdaptersForPlatform["c3725"] = {
|
||||
0: ["GT96100-FE"],
|
||||
1: this.c3700_nms,
|
||||
2: this.c3700_nms
|
||||
};
|
||||
networkAdaptersForPlatform["c3745"] = {
|
||||
0: ["GT96100-FE"],
|
||||
1: this.c3700_nms,
|
||||
2: this.c3700_nms,
|
||||
3: this.c3700_nms,
|
||||
4: this.c3700_nms
|
||||
};
|
||||
networkAdaptersForPlatform["c7200"] = {
|
||||
0: this.c7200_io,
|
||||
1: this.c7200_pas,
|
||||
2: this.c7200_pas,
|
||||
3: this.c7200_pas,
|
||||
4: this.c7200_pas,
|
||||
5: this.c7200_pas,
|
||||
6: this.c7200_pas
|
||||
};
|
||||
|
||||
return {
|
||||
"c2691": {
|
||||
0: ["GT96100-FE"],
|
||||
|
@ -1,28 +1,58 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Injectable, EventEmitter } from '@angular/core';
|
||||
import { IndexedDbService } from './indexed-db.service';
|
||||
import { Server } from '../models/server';
|
||||
import { Observable } from 'rxjs';
|
||||
import { HttpServer } from './http-server.service';
|
||||
import { ToasterService } from './toaster.service';
|
||||
|
||||
@Injectable()
|
||||
export class ServerService {
|
||||
private tablename = 'servers';
|
||||
private ready: Promise<any>;
|
||||
private isIncognitoMode: boolean = false;
|
||||
private serverIdsInIncognitoMode: string[] = [];
|
||||
public serviceInitialized: EventEmitter<boolean> = new EventEmitter<boolean>();
|
||||
|
||||
constructor(
|
||||
private indexedDbService: IndexedDbService,
|
||||
private httpServer: HttpServer
|
||||
private httpServer: HttpServer,
|
||||
private toasterService: ToasterService
|
||||
) {
|
||||
this.ready = indexedDbService.get().openDatabase(1, evt => {
|
||||
evt.currentTarget.result.createObjectStore(this.tablename, { keyPath: 'id', autoIncrement: true });
|
||||
this.indexedDbService.get().openDatabase(1).then(() => {
|
||||
this.ready = indexedDbService.get().openDatabase(1, evt => {
|
||||
evt.currentTarget.result.createObjectStore(this.tablename, { keyPath: 'id', autoIncrement: true });
|
||||
});
|
||||
}).catch(() => {
|
||||
this.isIncognitoMode = true;
|
||||
}).finally(() => {
|
||||
this.serviceInitialized.emit(true);
|
||||
});
|
||||
}
|
||||
|
||||
public get(id: number): Promise<Server> {
|
||||
if (this.isIncognitoMode) {
|
||||
let server: Server = JSON.parse(localStorage.getItem(`server-${id}`));
|
||||
let promise = new Promise<Server>(resolve => {
|
||||
resolve(server);
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
|
||||
return this.onReady(() => this.indexedDbService.get().getByKey(this.tablename, id)) as Promise<Server>;
|
||||
}
|
||||
|
||||
public create(server: Server) {
|
||||
if (this.isIncognitoMode) {
|
||||
server.id = this.serverIdsInIncognitoMode.length + 1;
|
||||
localStorage.setItem(`server-${server.id}`, JSON.stringify(server));
|
||||
this.serverIdsInIncognitoMode.push(`server-${server.id}`);
|
||||
|
||||
let promise = new Promise<Server>(resolve => {
|
||||
resolve(server);
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
|
||||
return this.onReady(() => {
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
this.indexedDbService
|
||||
@ -38,6 +68,16 @@ export class ServerService {
|
||||
}
|
||||
|
||||
public update(server: Server) {
|
||||
if (this.isIncognitoMode) {
|
||||
localStorage.removeItem(`server-${server.id}`);
|
||||
localStorage.setItem(`server-${server.id}`, JSON.stringify(server));
|
||||
|
||||
let promise = new Promise<Server>(resolve => {
|
||||
resolve(server);
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
|
||||
return this.onReady(() => {
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
this.indexedDbService
|
||||
@ -52,10 +92,32 @@ export class ServerService {
|
||||
}
|
||||
|
||||
public findAll() {
|
||||
if (this.isIncognitoMode) {
|
||||
let promise = new Promise<Server[]>(resolve => {
|
||||
let servers: Server[] = [];
|
||||
this.serverIdsInIncognitoMode.forEach(n => {
|
||||
let server: Server = JSON.parse(localStorage.getItem(n));
|
||||
servers.push(server);
|
||||
});
|
||||
resolve(servers);
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
|
||||
return this.onReady(() => this.indexedDbService.get().getAll(this.tablename)) as Promise<Server[]>;
|
||||
}
|
||||
|
||||
public delete(server: Server) {
|
||||
if (this.isIncognitoMode) {
|
||||
localStorage.removeItem(`server-${server.id}`);
|
||||
this.serverIdsInIncognitoMode = this.serverIdsInIncognitoMode.filter(n => n !== `server-${server.id}`);
|
||||
|
||||
let promise = new Promise(resolve => {
|
||||
resolve(server.id);
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
|
||||
return this.onReady(() => this.indexedDbService.get().delete(this.tablename, server.id));
|
||||
}
|
||||
|
||||
|
@ -229,7 +229,7 @@ export class TemplateMocksService {
|
||||
headless: false,
|
||||
linked_clone: false,
|
||||
name: '',
|
||||
on_close: 'power-off',
|
||||
on_close: 'power_off',
|
||||
port_name_format: 'Ethernet{0}',
|
||||
port_segment_size: 0,
|
||||
symbol: ':/symbols/vmware_guest.svg',
|
||||
|
Loading…
x
Reference in New Issue
Block a user