Merge pull request #275 from GNS3/As-user-I-can-add-QEMU-VM

As user i can add qemu vm Fixes: #272 #273 #280 #281 #282 #283 #284 #285 #286
This commit is contained in:
ziajka 2019-02-26 09:58:48 +01:00 committed by GitHub
commit f3e2e8bf28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
283 changed files with 11614 additions and 89 deletions

View File

@ -1,3 +1,11 @@
// This file is required by the index.html file and will // This file is required by the index.html file and will
// be executed in the renderer process for that window. // be executed in the renderer process for that window.
// All of the Node.js APIs are available in this process. // All of the Node.js APIs are available in this process.
let shell = require('electron').shell
document.addEventListener('click', function (event) {
if (event.target.tagName === 'A' && event.target.href.startsWith('http')) {
event.preventDefault()
shell.openExternal(event.target.href)
}
})

View File

@ -2,13 +2,54 @@ import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { ProjectMapComponent } from './components/project-map/project-map.component'; import { ProjectMapComponent } from './components/project-map/project-map.component';
import { ServersComponent } from "./components/servers/servers.component"; import { ServersComponent } from './components/servers/servers.component';
import { ProjectsComponent } from "./components/projects/projects.component"; import { ProjectsComponent } from './components/projects/projects.component';
import { DefaultLayoutComponent } from "./layouts/default-layout/default-layout.component"; import { DefaultLayoutComponent } from './layouts/default-layout/default-layout.component';
import { SettingsComponent } from "./components/settings/settings.component"; import { SettingsComponent } from './components/settings/settings.component';
import { LocalServerComponent } from "./components/local-server/local-server.component"; import { LocalServerComponent } from './components/local-server/local-server.component';
import { PreferencesComponent } from './components/preferences/preferences.component';
import { QemuPreferencesComponent } from './components/preferences/qemu/qemu-preferences/qemu-preferences.component';
import { QemuVmTemplatesComponent } from './components/preferences/qemu/qemu-vm-templates/qemu-vm-templates.component';
import { QemuVmTemplateDetailsComponent } from './components/preferences/qemu/qemu-vm-template-details/qemu-vm-template-details.component';
import { AddQemuVmTemplateComponent } from './components/preferences/qemu/add-qemu-vm-template/add-qemu-vm-template.component';
import { GeneralPreferencesComponent } from './components/preferences/general/general-preferences.component';
import { VpcsPreferencesComponent } from './components/preferences/vpcs/vpcs-preferences/vpcs-preferences.component';
import { VpcsTemplatesComponent } from './components/preferences/vpcs/vpcs-templates/vpcs-templates.component';
import { AddVpcsTemplateComponent } from './components/preferences/vpcs/add-vpcs-template/add-vpcs-template.component';
import { VpcsTemplateDetailsComponent } from './components/preferences/vpcs/vpcs-template-details/vpcs-template-details.component';
import { VirtualBoxPreferencesComponent } from './components/preferences/virtual-box/virtual-box-preferences/virtual-box-preferences.component';
import { VirtualBoxTemplatesComponent } from './components/preferences/virtual-box/virtual-box-templates/virtual-box-templates.component';
import { VirtualBoxTemplateDetailsComponent } from './components/preferences/virtual-box/virtual-box-template-details/virtual-box-template-details.component';
import { AddVirtualBoxTemplateComponent } from './components/preferences/virtual-box/add-virtual-box-template/add-virtual-box-template.component';
import { BuiltInPreferencesComponent } from './components/preferences/built-in/built-in-preferences.component';
import { EthernetHubsTemplatesComponent } from './components/preferences/built-in/ethernet-hubs/ethernet-hubs-templates/ethernet-hubs-templates.component';
import { EthernetHubsAddTemplateComponent } from './components/preferences/built-in/ethernet-hubs/ethernet-hubs-add-template/ethernet-hubs-add-template.component';
import { EthernetHubsTemplateDetailsComponent } from './components/preferences/built-in/ethernet-hubs/ethernet-hubs-template-details/ethernet-hubs-template-details.component';
import { CloudNodesTemplatesComponent } from './components/preferences/built-in/cloud-nodes/cloud-nodes-templates/cloud-nodes-templates.component';
import { CloudNodesAddTemplateComponent } from './components/preferences/built-in/cloud-nodes/cloud-nodes-add-template/cloud-nodes-add-template.component';
import { CloudNodesTemplateDetailsComponent } from './components/preferences/built-in/cloud-nodes/cloud-nodes-template-details/cloud-nodes-template-details.component';
import { EthernetSwitchesTemplatesComponent } from './components/preferences/built-in/ethernet-switches/ethernet-switches-templates/ethernet-switches-templates.component';
import { EthernetSwitchesAddTemplateComponent } from './components/preferences/built-in/ethernet-switches/ethernet-switches-add-template/ethernet-switches-add-template.component';
import { EthernetSwitchesTemplateDetailsComponent } from './components/preferences/built-in/ethernet-switches/ethernet-switches-template-details/ethernet-switches-template-details.component';
import { DynamipsPreferencesComponent } from './components/preferences/dynamips/dynamips-preferences/dynamips-preferences.component';
import { IosTemplatesComponent } from './components/preferences/dynamips/ios-templates/ios-templates.component';
import { InstalledSoftwareComponent } from './components/installed-software/installed-software.component'; import { InstalledSoftwareComponent } from './components/installed-software/installed-software.component';
import { IosTemplateDetailsComponent } from './components/preferences/dynamips/ios-template-details/ios-template-details.component';
import { AddIosTemplateComponent } from './components/preferences/dynamips/add-ios-template/add-ios-template.component';
import { VmwarePreferencesComponent } from './components/preferences/vmware/vmware-preferences/vmware-preferences.component';
import { VmwareTemplatesComponent } from './components/preferences/vmware/vmware-templates/vmware-templates.component';
import { VmwareTemplateDetailsComponent } from './components/preferences/vmware/vmware-template-details/vmware-template-details.component';
import { AddVmwareTemplateComponent } from './components/preferences/vmware/add-vmware-template/add-vmware-template.component';
import { DockerTemplatesComponent } from './components/preferences/docker/docker-templates/docker-templates.component';
import { AddDockerTemplateComponent } from './components/preferences/docker/add-docker-template/add-docker-template.component';
import { DockerTemplateDetailsComponent } from './components/preferences/docker/docker-template-details/docker-template-details.component';
import { IouTemplatesComponent } from './components/preferences/ios-on-unix/iou-templates/iou-templates.component';
import { AddIouTemplateComponent } from './components/preferences/ios-on-unix/add-iou-template/add-iou-template.component';
import { IouTemplateDetailsComponent } from './components/preferences/ios-on-unix/iou-template-details/iou-template-details.component';
import { CopyQemuVmTemplateComponent } from './components/preferences/qemu/copy-qemu-vm-template/copy-qemu-vm-template.component';
import { CopyIosTemplateComponent } from './components/preferences/dynamips/copy-ios-template/copy-ios-template.component';
import { CopyDockerTemplateComponent } from './components/preferences/docker/copy-docker-template/copy-docker-template.component';
import { CopyIouTemplateComponent } from './components/preferences/ios-on-unix/copy-iou-template/copy-iou-template.component';
const routes: Routes = [ const routes: Routes = [
{ {
@ -21,6 +62,58 @@ const routes: Routes = [
{ path: 'server/:server_id/projects', component: ProjectsComponent }, { path: 'server/:server_id/projects', component: ProjectsComponent },
{ path: 'settings', component: SettingsComponent }, { path: 'settings', component: SettingsComponent },
{ path: 'installed-software', component: InstalledSoftwareComponent }, { path: 'installed-software', component: InstalledSoftwareComponent },
{ path: 'server/:server_id/preferences', component: PreferencesComponent },
// { path: 'server/:server_id/preferences/general', component: GeneralPreferencesComponent },
{ path: 'server/:server_id/preferences/builtin', component: BuiltInPreferencesComponent},
{ path: 'server/:server_id/preferences/builtin/ethernet-hubs', component: EthernetHubsTemplatesComponent },
{ path: 'server/:server_id/preferences/builtin/ethernet-hubs/addtemplate', component: EthernetHubsAddTemplateComponent },
{ path: 'server/:server_id/preferences/builtin/ethernet-hubs/:template_id', component: EthernetHubsTemplateDetailsComponent },
{ path: 'server/:server_id/preferences/builtin/ethernet-switches', component: EthernetSwitchesTemplatesComponent },
{ path: 'server/:server_id/preferences/builtin/ethernet-switches/addtemplate', component: EthernetSwitchesAddTemplateComponent },
{ path: 'server/:server_id/preferences/builtin/ethernet-switches/:template_id', component: EthernetSwitchesTemplateDetailsComponent },
{ path: 'server/:server_id/preferences/builtin/cloud-nodes', component: CloudNodesTemplatesComponent },
{ path: 'server/:server_id/preferences/builtin/cloud-nodes/addtemplate', component: CloudNodesAddTemplateComponent },
{ path: 'server/:server_id/preferences/builtin/cloud-nodes/:template_id', component: CloudNodesTemplateDetailsComponent },
//{ path: 'server/:server_id/preferences/dynamips', component: DynamipsPreferencesComponent },
{ path: 'server/:server_id/preferences/dynamips/templates', component: IosTemplatesComponent },
{ path: 'server/:server_id/preferences/dynamips/templates/addtemplate', component: AddIosTemplateComponent },
{ path: 'server/:server_id/preferences/dynamips/templates/:template_id', component: IosTemplateDetailsComponent },
{ path: 'server/:server_id/preferences/dynamips/templates/:template_id/copy', component: CopyIosTemplateComponent },
// { path: 'server/:server_id/preferences/qemu', component: QemuPreferencesComponent },
{ path: 'server/:server_id/preferences/qemu/templates', component: QemuVmTemplatesComponent },
{ path: 'server/:server_id/preferences/qemu/templates/:template_id/copy', component: CopyQemuVmTemplateComponent },
{ path: 'server/:server_id/preferences/qemu/templates/:template_id', component: QemuVmTemplateDetailsComponent },
{ path: 'server/:server_id/preferences/qemu/addtemplate', component: AddQemuVmTemplateComponent },
// { path: 'server/:server_id/preferences/vpcs', component: VpcsPreferencesComponent },
{ path: 'server/:server_id/preferences/vpcs/templates', component: VpcsTemplatesComponent },
{ path: 'server/:server_id/preferences/vpcs/templates/:template_id', component: VpcsTemplateDetailsComponent},
{ path: 'server/:server_id/preferences/vpcs/addtemplate', component: AddVpcsTemplateComponent },
// { path: 'server/:server_id/preferences/virtualbox', component: VirtualBoxPreferencesComponent },
{ path: 'server/:server_id/preferences/virtualbox/templates', component: VirtualBoxTemplatesComponent },
{ path: 'server/:server_id/preferences/virtualbox/templates/:template_id', component: VirtualBoxTemplateDetailsComponent },
{ path: 'server/:server_id/preferences/virtualbox/addtemplate', component: AddVirtualBoxTemplateComponent },
// { path: 'server/:server_id/preferences/vmware', component: VmwarePreferencesComponent },
{ path: 'server/:server_id/preferences/vmware/templates', component: VmwareTemplatesComponent },
{ 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/docker/templates', component: DockerTemplatesComponent },
{ 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/addtemplate', component: AddDockerTemplateComponent },
{ path: 'server/:server_id/preferences/iou/templates', component: IouTemplatesComponent },
{ path: 'server/:server_id/preferences/iou/templates/:template_id', component: IouTemplateDetailsComponent },
{ path: 'server/:server_id/preferences/iou/templates/:template_id/copy', component: CopyIouTemplateComponent },
{ path: 'server/:server_id/preferences/iou/addtemplate', component: AddIouTemplateComponent }
] ]
}, },
{ path: 'server/:server_id/project/:project_id', component: ProjectMapComponent } { path: 'server/:server_id/project/:project_id', component: ProjectMapComponent }

View File

@ -35,7 +35,8 @@ import { ProgressDialogComponent } from './common/progress-dialog/progress-dialo
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { ProjectMapComponent } from './components/project-map/project-map.component'; import { ProjectMapComponent } from './components/project-map/project-map.component';
import { ServersComponent, AddServerDialogComponent } from './components/servers/servers.component'; import { ServersComponent } from './components/servers/servers.component';
import { AddServerDialogComponent } from './components/servers/add-server-dialog/add-server-dialog.component';
import { ContextMenuComponent } from './components/project-map/context-menu/context-menu.component'; import { ContextMenuComponent } from './components/project-map/context-menu/context-menu.component';
import { StartNodeActionComponent } from './components/project-map/context-menu/actions/start-node-action/start-node-action.component'; import { StartNodeActionComponent } from './components/project-map/context-menu/actions/start-node-action/start-node-action.component';
import { StopNodeActionComponent } from './components/project-map/context-menu/actions/stop-node-action/stop-node-action.component'; import { StopNodeActionComponent } from './components/project-map/context-menu/actions/stop-node-action/stop-node-action.component';
@ -92,9 +93,76 @@ import { InstallSoftwareComponent } from './components/installed-software/instal
import { StyleEditorDialogComponent } from './components/project-map/drawings-editors/style-editor/style-editor.component'; import { StyleEditorDialogComponent } from './components/project-map/drawings-editors/style-editor/style-editor.component';
import { EditTextActionComponent } from './components/project-map/context-menu/actions/edit-text-action/edit-text-action.component'; import { EditTextActionComponent } from './components/project-map/context-menu/actions/edit-text-action/edit-text-action.component';
import { TextEditorDialogComponent } from './components/project-map/drawings-editors/text-editor/text-editor.component'; import { TextEditorDialogComponent } from './components/project-map/drawings-editors/text-editor/text-editor.component';
import { PreferencesComponent } from './components/preferences/preferences.component';
import { QemuPreferencesComponent } from './components/preferences/qemu/qemu-preferences/qemu-preferences.component';
import { ServerSettingsService } from './services/server-settings.service';
import { QemuVmTemplatesComponent } from './components/preferences/qemu/qemu-vm-templates/qemu-vm-templates.component';
import { AddQemuVmTemplateComponent } from './components/preferences/qemu/add-qemu-vm-template/add-qemu-vm-template.component';
import { QemuVmTemplateDetailsComponent } from './components/preferences/qemu/qemu-vm-template-details/qemu-vm-template-details.component';
import { QemuService } from './services/qemu.service';
import { GeneralPreferencesComponent } from './components/preferences/general/general-preferences.component';
import { VpcsPreferencesComponent } from './components/preferences/vpcs/vpcs-preferences/vpcs-preferences.component';
import { VpcsTemplatesComponent } from './components/preferences/vpcs/vpcs-templates/vpcs-templates.component';
import { VpcsService } from './services/vpcs.service';
import { AddVpcsTemplateComponent } from './components/preferences/vpcs/add-vpcs-template/add-vpcs-template.component';
import { VpcsTemplateDetailsComponent } from './components/preferences/vpcs/vpcs-template-details/vpcs-template-details.component';
import { TemplateMocksService } from './services/template-mocks.service';
import { VirtualBoxPreferencesComponent } from './components/preferences/virtual-box/virtual-box-preferences/virtual-box-preferences.component';
import { VirtualBoxTemplatesComponent } from './components/preferences/virtual-box/virtual-box-templates/virtual-box-templates.component';
import { VirtualBoxService } from './services/virtual-box.service';
import { VirtualBoxTemplateDetailsComponent } from './components/preferences/virtual-box/virtual-box-template-details/virtual-box-template-details.component';
import { AddVirtualBoxTemplateComponent } from './components/preferences/virtual-box/add-virtual-box-template/add-virtual-box-template.component';
import { BuiltInPreferencesComponent } from './components/preferences/built-in/built-in-preferences.component';
import { EthernetHubsTemplatesComponent } from './components/preferences/built-in/ethernet-hubs/ethernet-hubs-templates/ethernet-hubs-templates.component';
import { BuiltInTemplatesService } from './services/built-in-templates.service';
import { EthernetHubsAddTemplateComponent } from './components/preferences/built-in/ethernet-hubs/ethernet-hubs-add-template/ethernet-hubs-add-template.component';
import { EthernetHubsTemplateDetailsComponent } from './components/preferences/built-in/ethernet-hubs/ethernet-hubs-template-details/ethernet-hubs-template-details.component';
import { CloudNodesTemplatesComponent } from './components/preferences/built-in/cloud-nodes/cloud-nodes-templates/cloud-nodes-templates.component';
import { CloudNodesAddTemplateComponent } from './components/preferences/built-in/cloud-nodes/cloud-nodes-add-template/cloud-nodes-add-template.component';
import { CloudNodesTemplateDetailsComponent } from './components/preferences/built-in/cloud-nodes/cloud-nodes-template-details/cloud-nodes-template-details.component';
import { EthernetSwitchesTemplatesComponent } from './components/preferences/built-in/ethernet-switches/ethernet-switches-templates/ethernet-switches-templates.component';
import { EthernetSwitchesAddTemplateComponent } from './components/preferences/built-in/ethernet-switches/ethernet-switches-add-template/ethernet-switches-add-template.component';
import { EthernetSwitchesTemplateDetailsComponent } from './components/preferences/built-in/ethernet-switches/ethernet-switches-template-details/ethernet-switches-template-details.component';
import { DynamipsPreferencesComponent } from './components/preferences/dynamips/dynamips-preferences/dynamips-preferences.component';
import { IosTemplatesComponent } from './components/preferences/dynamips/ios-templates/ios-templates.component';
import { IosService } from './services/ios.service';
import { SymbolsComponent } from './components/preferences/common/symbols/symbols.component';
import { InstalledSoftwareService } from './services/installed-software.service'; import { InstalledSoftwareService } from './services/installed-software.service';
import { ExternalSoftwareDefinitionService } from './services/external-software-definition.service'; import { ExternalSoftwareDefinitionService } from './services/external-software-definition.service';
import { PlatformService } from './services/platform.service'; import { PlatformService } from './services/platform.service';
import { IosTemplateDetailsComponent } from './components/preferences/dynamips/ios-template-details/ios-template-details.component';
import { AddIosTemplateComponent } from './components/preferences/dynamips/add-ios-template/add-ios-template.component';
import { IosConfigurationService } from './services/ios-configuration.service';
import { QemuConfigurationService } from './services/qemu-configuration.service';
import { VirtualBoxConfigurationService } from './services/virtual-box-configuration.service';
import { VpcsConfigurationService } from './services/vpcs-configuration.service';
import { BuiltInTemplatesConfigurationService } from './services/built-in-templates-configuration.service';
import { VmwarePreferencesComponent } from './components/preferences/vmware/vmware-preferences/vmware-preferences.component';
import { VmwareTemplatesComponent } from './components/preferences/vmware/vmware-templates/vmware-templates.component';
import { VmwareService } from './services/vmware.service';
import { VmwareConfigurationService } from './services/vmware-configuration.service';
import { VmwareTemplateDetailsComponent } from './components/preferences/vmware/vmware-template-details/vmware-template-details.component';
import { AddVmwareTemplateComponent } from './components/preferences/vmware/add-vmware-template/add-vmware-template.component';
import { DeleteConfirmationDialogComponent } from './components/preferences/common/delete-confirmation-dialog/delete-confirmation-dialog.component';
import { DeleteTemplateComponent } from './components/preferences/common/delete-template-component/delete-template.component';
import { DockerService } from './services/docker.service';
import { DockerTemplatesComponent } from './components/preferences/docker/docker-templates/docker-templates.component';
import { DockerConfigurationService } from './services/docker-configuration.service';
import { AddDockerTemplateComponent } from './components/preferences/docker/add-docker-template/add-docker-template.component';
import { DockerTemplateDetailsComponent } from './components/preferences/docker/docker-template-details/docker-template-details.component';
import { IouTemplatesComponent } from './components/preferences/ios-on-unix/iou-templates/iou-templates.component';
import { IouService } from './services/iou.service';
import { AddIouTemplateComponent } from './components/preferences/ios-on-unix/add-iou-template/add-iou-template.component';
import { IouConfigurationService } from './services/iou-configuration.service';
import { IouTemplateDetailsComponent } from './components/preferences/ios-on-unix/iou-template-details/iou-template-details.component';
import { CopyQemuVmTemplateComponent } from './components/preferences/qemu/copy-qemu-vm-template/copy-qemu-vm-template.component';
import { CopyIosTemplateComponent } from './components/preferences/dynamips/copy-ios-template/copy-ios-template.component';
import { CopyIouTemplateComponent } from './components/preferences/ios-on-unix/copy-iou-template/copy-iou-template.component';
import { CopyDockerTemplateComponent } from './components/preferences/docker/copy-docker-template/copy-docker-template.component';
import { EmptyTemplatesListComponent } from './components/preferences/common/empty-templates-list/empty-templates-list.component';
import { SymbolsMenuComponent } from './components/preferences/common/symbols-menu/symbols-menu.component';
import { SearchFilter } from './filters/searchFilter.pipe';
import { RecentlyOpenedProjectService } from './services/recentlyOpenedProject.service';
import { ServerManagementService } from './services/server-management.service'; import { ServerManagementService } from './services/server-management.service';
if (environment.production) { if (environment.production) {
@ -132,6 +200,7 @@ if (environment.production) {
EditTextActionComponent, EditTextActionComponent,
ProjectMapShortcutsComponent, ProjectMapShortcutsComponent,
SettingsComponent, SettingsComponent,
PreferencesComponent,
LocalServerComponent, LocalServerComponent,
ProgressComponent, ProgressComponent,
ServerDiscoveryComponent, ServerDiscoveryComponent,
@ -149,7 +218,54 @@ if (environment.production) {
InterfaceLabelDraggedComponent, InterfaceLabelDraggedComponent,
InstallSoftwareComponent, InstallSoftwareComponent,
StyleEditorDialogComponent, StyleEditorDialogComponent,
TextEditorDialogComponent TextEditorDialogComponent,
QemuPreferencesComponent,
QemuVmTemplatesComponent,
AddQemuVmTemplateComponent,
QemuVmTemplateDetailsComponent,
GeneralPreferencesComponent,
VpcsPreferencesComponent,
VpcsTemplatesComponent,
AddVpcsTemplateComponent,
VpcsTemplateDetailsComponent,
VirtualBoxPreferencesComponent,
VirtualBoxTemplatesComponent,
VirtualBoxTemplateDetailsComponent,
AddVirtualBoxTemplateComponent,
BuiltInPreferencesComponent,
EthernetHubsTemplatesComponent,
EthernetHubsAddTemplateComponent,
EthernetHubsTemplateDetailsComponent,
CloudNodesTemplatesComponent,
CloudNodesAddTemplateComponent,
CloudNodesTemplateDetailsComponent,
EthernetSwitchesTemplatesComponent,
EthernetSwitchesAddTemplateComponent,
EthernetSwitchesTemplateDetailsComponent,
DynamipsPreferencesComponent,
IosTemplatesComponent,
IosTemplateDetailsComponent,
AddIosTemplateComponent,
SymbolsComponent,
VmwarePreferencesComponent,
VmwareTemplatesComponent,
VmwareTemplateDetailsComponent,
AddVmwareTemplateComponent,
DeleteConfirmationDialogComponent,
DeleteTemplateComponent,
DockerTemplatesComponent,
AddDockerTemplateComponent,
DockerTemplateDetailsComponent,
IouTemplatesComponent,
AddIouTemplateComponent,
IouTemplateDetailsComponent,
CopyQemuVmTemplateComponent,
CopyIosTemplateComponent,
CopyIouTemplateComponent,
CopyDockerTemplateComponent,
EmptyTemplatesListComponent,
SymbolsMenuComponent,
SearchFilter
], ],
imports: [ imports: [
BrowserModule, BrowserModule,
@ -196,9 +312,28 @@ if (environment.production) {
ServerDatabase, ServerDatabase,
ProjectNameValidator, ProjectNameValidator,
ToolsService, ToolsService,
ServerSettingsService,
QemuService,
VpcsService,
TemplateMocksService,
VirtualBoxService,
BuiltInTemplatesService,
IosService,
InstalledSoftwareService, InstalledSoftwareService,
ExternalSoftwareDefinitionService, ExternalSoftwareDefinitionService,
PlatformService, PlatformService,
IosConfigurationService,
QemuConfigurationService,
VirtualBoxConfigurationService,
VpcsConfigurationService,
BuiltInTemplatesConfigurationService,
VmwareService,
VmwareConfigurationService,
DockerService,
DockerConfigurationService,
IouService,
IouConfigurationService,
RecentlyOpenedProjectService,
ServerManagementService ServerManagementService
], ],
entryComponents: [ entryComponents: [
@ -210,7 +345,9 @@ if (environment.production) {
ImportProjectDialogComponent, ImportProjectDialogComponent,
ConfirmationDialogComponent, ConfirmationDialogComponent,
StyleEditorDialogComponent, StyleEditorDialogComponent,
TextEditorDialogComponent TextEditorDialogComponent,
SymbolsComponent,
DeleteConfirmationDialogComponent
], ],
bootstrap: [AppComponent] bootstrap: [AppComponent]
}) })

View File

@ -3,7 +3,7 @@
<h1>Installed software</h1> <h1>Installed software</h1>
</div> </div>
<div class="default-content"> <div class="default-content">
<div class="example-container mat-elevation-z8"> <div class="container mat-elevation-z8">
<mat-table #table [dataSource]="dataSource"> <mat-table #table [dataSource]="dataSource">
<ng-container matColumnDef="name"> <ng-container matColumnDef="name">

View File

@ -0,0 +1,23 @@
<div class="content">
<div class="default-header">
<div class="row">
<h1 class="col">Built-in preferences</h1>
<button class="top-button" class="cancel-button" routerLink="/server/{{serverId}}/preferences" mat-button>Back</button>
</div>
</div>
<div class="default-content">
<div class="listcontainer mat-elevation-z8">
<mat-nav-list>
<mat-list-item routerLink="/server/{{serverId}}/preferences/builtin/ethernet-hubs">
Ethernet hubs
</mat-list-item>
<mat-list-item routerLink="/server/{{serverId}}/preferences/builtin/ethernet-switches">
Ethernet switches
</mat-list-item>
<mat-list-item routerLink="/server/{{serverId}}/preferences/builtin/cloud-nodes">
Cloud nodes
</mat-list-item>
</mat-nav-list>
</div>
</div>
</div>

View File

@ -0,0 +1,44 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MatCheckboxModule, MatIconModule, MatToolbarModule, MatMenuModule } from '@angular/material';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { ActivatedRoute } from '@angular/router';
import { CommonModule } from '@angular/common';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { RouterTestingModule } from '@angular/router/testing';
import { MockedActivatedRoute } from '../preferences.component.spec';
import { BuiltInPreferencesComponent } from './built-in-preferences.component';
describe('BuiltInPreferencesComponent', () => {
let component: BuiltInPreferencesComponent;
let fixture: ComponentFixture<BuiltInPreferencesComponent>;
let activatedRoute = new MockedActivatedRoute().get();
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
providers: [
{
provide: ActivatedRoute, useValue: activatedRoute
}
],
declarations: [
BuiltInPreferencesComponent
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(BuiltInPreferencesComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should set correct server id', () => {
expect(component.serverId).toBe('1');
});
});

View File

@ -0,0 +1,20 @@
import { Component, OnInit } from "@angular/core";
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-built-in-preferences',
templateUrl: './built-in-preferences.component.html',
styleUrls: ['./built-in-preferences.component.scss', '../preferences.component.scss']
})
export class BuiltInPreferencesComponent implements OnInit {
public serverId: string = "";
constructor(
private route: ActivatedRoute
) {}
ngOnInit() {
this.serverId = this.route.snapshot.paramMap.get("server_id");
}
}

View File

@ -0,0 +1,20 @@
<div class="content">
<div class="default-header">
<div class="row">
<h1 class="col">New cloud node template</h1>
</div>
</div>
<div class="default-content">
<mat-card class="matCard">
<form [formGroup]="formGroup">
<mat-form-field class="form-field">
<input formControlName="templateName" matInput type="text" [(ngModel)]="templateName" placeholder="Template name">
</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,87 @@
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
import { MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule } from '@angular/material';
import { CommonModule } from '@angular/common';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
import { ActivatedRoute } from '@angular/router';
import { of } from 'rxjs';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { Server } from '../../../../../models/server';
import { CloudTemplate } from '../../../../../models/templates/cloud-template';
import { CloudNodesAddTemplateComponent } from './cloud-nodes-add-template.component';
import { MockedServerService } from '../../../../../services/server.service.spec';
import { MockedToasterService } from '../../../../../services/toaster.service.spec';
import { MockedActivatedRoute } from '../../../preferences.component.spec';
import { ServerService } from '../../../../../services/server.service';
import { BuiltInTemplatesService } from '../../../../../services/built-in-templates.service';
import { ToasterService } from '../../../../../services/toaster.service';
import { TemplateMocksService } from '../../../../../services/template-mocks.service';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
export class MockedBuiltInTemplatesService {
public addTemplate(server: Server, cloudTemplate: CloudTemplate) {
return of(cloudTemplate);
}
}
describe('CloudNodesAddTemplateComponent', () => {
let component: CloudNodesAddTemplateComponent;
let fixture: ComponentFixture<CloudNodesAddTemplateComponent>;
let mockedServerService = new MockedServerService;
let mockedBuiltInTemplatesService = new MockedBuiltInTemplatesService;
let mockedToasterService = new MockedToasterService;
let activatedRoute = new MockedActivatedRoute().get();
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [FormsModule, ReactiveFormsModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
providers: [
{
provide: ActivatedRoute, useValue: activatedRoute
},
{ provide: ServerService, useValue: mockedServerService },
{ provide: BuiltInTemplatesService, useValue: mockedBuiltInTemplatesService },
{ provide: ToasterService, useValue: mockedToasterService},
{ provide: TemplateMocksService, useClass: TemplateMocksService }
],
declarations: [
CloudNodesAddTemplateComponent
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CloudNodesAddTemplateComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should call add template', () => {
spyOn(mockedBuiltInTemplatesService, 'addTemplate').and.returnValue(of({} as CloudTemplate));
component.templateName = "sample name";
component.server = {id: 1} as Server;
component.formGroup.controls['templateName'].setValue('template name');
component.addTemplate();
expect(mockedBuiltInTemplatesService.addTemplate).toHaveBeenCalled();
});
it('should not call add template when template name is empty', () => {
spyOn(mockedBuiltInTemplatesService, 'addTemplate').and.returnValue(of({} as CloudTemplate));
spyOn(mockedToasterService, 'error');
component.templateName = "";
component.server = {id: 1} as Server;
component.addTemplate();
expect(mockedBuiltInTemplatesService.addTemplate).not.toHaveBeenCalled();
expect(mockedToasterService.error).toHaveBeenCalled();
});
});

View File

@ -0,0 +1,66 @@
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 { BuiltInTemplatesService } from '../../../../../services/built-in-templates.service';
import { CloudTemplate } from '../../../../../models/templates/cloud-template';
import { FormGroup, FormBuilder, FormControl, Validators } from '@angular/forms';
@Component({
selector: 'app-cloud-nodes-add-template',
templateUrl: './cloud-nodes-add-template.component.html',
styleUrls: ['./cloud-nodes-add-template.component.scss', '../../../preferences.component.scss']
})
export class CloudNodesAddTemplateComponent implements OnInit {
server: Server;
templateName: string = '';
formGroup: FormGroup;
constructor(
private route: ActivatedRoute,
private serverService: ServerService,
private builtInTemplatesService: BuiltInTemplatesService,
private router: Router,
private toasterService: ToasterService,
private templateMocksService: TemplateMocksService,
private formBuilder: FormBuilder
) {
this.formGroup = this.formBuilder.group({
templateName: new FormControl('', 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', 'builtin', 'cloud-nodes']);
}
addTemplate() {
if (!this.formGroup.invalid) {
let cloudTemplate: CloudTemplate;
this.templateMocksService.getCloudNodeTemplate().subscribe((template: CloudTemplate) => {
cloudTemplate = template;
});
cloudTemplate.template_id = uuid();
cloudTemplate.name = this.templateName;
this.builtInTemplatesService.addTemplate(this.server, cloudTemplate).subscribe((cloudNodeTemplate) => {
this.goBack();
});
} else {
this.toasterService.error(`Fill all required fields`);
}
}
}

View File

@ -0,0 +1,173 @@
<div class="content" [ngClass]="{ shadowed: isSymbolSelectionOpened}">
<div class="default-header">
<div class="row">
<h1 class="col">Cloud configuration</h1>
</div>
</div>
<div class="default-content" *ngIf="cloudNodeTemplate">
<mat-accordion>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
General
</mat-panel-title>
</mat-expansion-panel-header>
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="cloudNodeTemplate.name"
placeholder="Template name">
</mat-form-field>
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="cloudNodeTemplate.default_name_format"
placeholder="Default name format">
</mat-form-field>
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="cloudNodeTemplate.symbol"
placeholder="Symbol">
</mat-form-field>
<button mat-button class="symbolSelectionButton" (click)="chooseSymbol()">Choose symbol</button><br/><br/>
<mat-form-field class="form-field">
<mat-select
placeholder="Category"
[(ngModel)]="cloudNodeTemplate.category">
<mat-option *ngFor="let category of categories" [value]="category[1]">
{{category[0]}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="select">
<mat-select
placeholder="Console type"
[(ngModel)]="cloudNodeTemplate.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)]="cloudNodeTemplate.remote_console_host"
placeholder="Console host">
</mat-form-field>
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="cloudNodeTemplate.remote_console_port"
placeholder="Console port">
</mat-form-field>
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="cloudNodeTemplate.remote_console_http_path"
placeholder="Console HTTP path">
</mat-form-field>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
Ethernet interfaces
</mat-panel-title>
</mat-expansion-panel-header>
<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 " [value]="type">
{{type}}
</mat-option>
</mat-select>
</mat-form-field>
<button mat-button class="form-field" (click)="onAddEthernetInterface()">Add</button>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
TAP interfaces
</mat-panel-title>
</mat-expansion-panel-header>
<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-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
UDP tunnels
</mat-panel-title>
</mat-expansion-panel-header>
<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>
<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>
</mat-expansion-panel>
</mat-accordion>
<div class="buttons-bar">
<button class="cancel-button" (click)="goBack()" mat-button>Cancel</button>
<button mat-raised-button color="primary" (click)="onSave()">Save</button><br/>
</div>
</div>
</div>
<app-symbols-menu *ngIf="isSymbolSelectionOpened && cloudNodeTemplate" [server]="server" [symbol]="cloudNodeTemplate.symbol" (symbolChangedEmitter)="symbolChanged($event)"></app-symbols-menu>

View File

@ -0,0 +1,77 @@
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
import { MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, MatTableModule } from '@angular/material';
import { CommonModule } from '@angular/common';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
import { ActivatedRoute } from '@angular/router';
import { of } from 'rxjs';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { Server } from '../../../../../models/server';
import { CloudTemplate } from '../../../../../models/templates/cloud-template';
import { CloudNodesTemplateDetailsComponent } from './cloud-nodes-template-details.component';
import { MockedServerService } from '../../../../../services/server.service.spec';
import { MockedToasterService } from '../../../../../services/toaster.service.spec';
import { MockedActivatedRoute } from '../../../preferences.component.spec';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ServerService } from '../../../../../services/server.service';
import { ToasterService } from '../../../../../services/toaster.service';
import { BuiltInTemplatesService } from '../../../../../services/built-in-templates.service';
import { BuiltInTemplatesConfigurationService } from '../../../../../services/built-in-templates-configuration.service';
export class MockedBuiltInTemplatesService {
public getTemplate(server: Server, template_id: string) {
return of({ports_mapping: []} as CloudTemplate);
}
public saveTemplate(server: Server, cloudTemplate: CloudTemplate) {
return of(cloudTemplate);
}
}
describe('CloudNodesTemplateDetailsComponent', () => {
let component: CloudNodesTemplateDetailsComponent;
let fixture: ComponentFixture<CloudNodesTemplateDetailsComponent>;
let mockedServerService = new MockedServerService;
let mockedBuiltInTemplatesService = new MockedBuiltInTemplatesService;
let mockedToasterService = new MockedToasterService;
let activatedRoute = new MockedActivatedRoute().get();
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [FormsModule, ReactiveFormsModule, MatTableModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
providers: [
{
provide: ActivatedRoute, useValue: activatedRoute
},
{ provide: ServerService, useValue: mockedServerService },
{ provide: BuiltInTemplatesService, useValue: mockedBuiltInTemplatesService },
{ provide: ToasterService, useValue: mockedToasterService },
{ provide: BuiltInTemplatesConfigurationService, useClass: BuiltInTemplatesConfigurationService }
],
declarations: [
CloudNodesTemplateDetailsComponent
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CloudNodesTemplateDetailsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should call save template', () => {
spyOn(mockedBuiltInTemplatesService, 'saveTemplate').and.returnValue(of({} as CloudTemplate));
component.cloudNodeTemplate = {ports_mapping: []} as CloudTemplate;
component.onSave();
expect(mockedBuiltInTemplatesService.saveTemplate).toHaveBeenCalled();
});
});

View File

@ -0,0 +1,131 @@
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 { BuiltInTemplatesService } from '../../../../../services/built-in-templates.service';
import { CloudTemplate } from '../../../../../models/templates/cloud-template';
import { PortsMappingEntity } from '../../../../../models/ethernetHub/ports-mapping-enity';
import { BuiltInTemplatesConfigurationService } from '../../../../../services/built-in-templates-configuration.service';
@Component({
selector: 'app-cloud-nodes-template-details',
templateUrl: './cloud-nodes-template-details.component.html',
styleUrls: ['./cloud-nodes-template-details.component.scss', '../../../preferences.component.scss']
})
export class CloudNodesTemplateDetailsComponent implements OnInit {
server: Server;
cloudNodeTemplate: CloudTemplate;
isSymbolSelectionOpened: boolean = false;
categories = [];
consoleTypes: string[] = [];
tapInterface: string = '';
ethernetInterface: string = '';
ethernetInterfaces: string[] = ['Ethernet 2', 'Ethernet 3'];
portsMappingEthernet: PortsMappingEntity[] = [];
portsMappingTap: PortsMappingEntity[] = [];
portsMappingUdp: PortsMappingEntity[] = [];
newPort: PortsMappingEntity;
displayedColumns: string[] = ['name', 'lport', 'rhost', 'rport'];
dataSourceUdp: PortsMappingEntity[] = [];
constructor(
private route: ActivatedRoute,
private serverService: ServerService,
private builtInTemplatesService: BuiltInTemplatesService,
private toasterService: ToasterService,
private builtInTemplatesConfigurationService: BuiltInTemplatesConfigurationService,
private router: Router
) {
this.newPort = {
name: '',
port_number: 0,
};
}
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.getConfiguration();
this.builtInTemplatesService.getTemplate(this.server, template_id).subscribe((cloudNodeTemplate: CloudTemplate) => {
this.cloudNodeTemplate = cloudNodeTemplate;
this.portsMappingEthernet = this.cloudNodeTemplate.ports_mapping
.filter((elem) => elem.type === 'ethernet');
this.portsMappingTap = this.cloudNodeTemplate.ports_mapping
.filter((elem) => elem.type === 'tap');
this.portsMappingUdp = this.cloudNodeTemplate.ports_mapping
.filter((elem) => elem.type === 'udp');
this.dataSourceUdp = this.portsMappingUdp;
});
});
}
goBack() {
this.router.navigate(['/server', this.server.id, 'preferences', 'builtin', 'cloud-nodes']);
}
getConfiguration() {
this.categories = this.builtInTemplatesConfigurationService.getCategoriesForCloudNodes();
this.consoleTypes = this.builtInTemplatesConfigurationService.getConsoleTypesForCloudNodes();
}
onAddEthernetInterface() {
if (this.ethernetInterface) {
this.portsMappingEthernet.push({
interface: this.ethernetInterface,
name: this.ethernetInterface,
port_number: 0,
type: "ethernet"
});
}
}
onAddTapInterface() {
if (this.tapInterface) {
this.portsMappingTap.push({
interface: this.tapInterface,
name: this.tapInterface,
port_number: 0,
type: "tap"
});
}
}
onAddUdpInterface() {
this.portsMappingUdp.push(this.newPort);
this.dataSourceUdp = [...this.portsMappingUdp];
this.newPort = {
name: '',
port_number: 0,
};
}
onSave() {
this.cloudNodeTemplate.ports_mapping = [...this.portsMappingEthernet, ...this.portsMappingTap];
this.builtInTemplatesService.saveTemplate(this.server, this.cloudNodeTemplate).subscribe((cloudNodeTemplate: CloudTemplate) => {
this.toasterService.success("Changes saved");
});
}
chooseSymbol() {
this.isSymbolSelectionOpened = !this.isSymbolSelectionOpened;
}
symbolChanged(chosenSymbol: string) {
this.isSymbolSelectionOpened = !this.isSymbolSelectionOpened;
this.cloudNodeTemplate.symbol = chosenSymbol;
}
}

View File

@ -0,0 +1,32 @@
<div class="content">
<div class="default-header">
<div class="row">
<h1 class="col">Cloud nodes templates</h1>
<button *ngIf="server" class="top-button" class="cancel-button" routerLink="/server/{{server.id}}/preferences/builtin" mat-button>Back</button>
<button *ngIf="server" class="top-button" routerLink="/server/{{server.id}}/preferences/builtin/cloud-nodes/addtemplate" mat-raised-button color="primary">Add cloud node template</button>
</div>
</div>
<app-empty-templates-list *ngIf="!cloudNodesTemplates.length"></app-empty-templates-list>
<div class="default-content" *ngIf="cloudNodesTemplates.length">
<div class="listcontainer mat-elevation-z8">
<mat-nav-list *ngIf="server">
<div class="list-item" *ngFor='let template of cloudNodesTemplates'>
<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,57 @@
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
import { MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule } from '@angular/material';
import { CommonModule } from '@angular/common';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
import { ActivatedRoute } from '@angular/router';
import { of } from 'rxjs';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { Server } from '../../../../../models/server';
import { CloudTemplate } from '../../../../../models/templates/cloud-template';
import { MockedServerService } from '../../../../../services/server.service.spec';
import { MockedActivatedRoute } from '../../../preferences.component.spec';
import { ServerService } from '../../../../../services/server.service';
import { CloudNodesTemplatesComponent } from './cloud-nodes-templates.component';
import { BuiltInTemplatesService } from '../../../../../services/built-in-templates.service';
export class MockedBuiltInTemplatesService {
public getTemplates(server: Server) {
return of([{} as CloudTemplate]);
}
}
describe('CloudNodesTemplatesComponent', () => {
let component: CloudNodesTemplatesComponent;
let fixture: ComponentFixture<CloudNodesTemplatesComponent>;
let mockedServerService = new MockedServerService;
let mockedBuiltInTemplatesService = new MockedBuiltInTemplatesService;
let activatedRoute = new MockedActivatedRoute().get();
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
providers: [
{
provide: ActivatedRoute, useValue: activatedRoute
},
{ provide: ServerService, useValue: mockedServerService },
{ provide: BuiltInTemplatesService, useValue: mockedBuiltInTemplatesService }
],
declarations: [
CloudNodesTemplatesComponent
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CloudNodesTemplatesComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,47 @@
import { Component, OnInit, ViewChild } from "@angular/core";
import { Server } from '../../../../../models/server';
import { ActivatedRoute } from '@angular/router';
import { ServerService } from '../../../../../services/server.service';
import { BuiltInTemplatesService } from '../../../../../services/built-in-templates.service';
import { CloudTemplate } from '../../../../../models/templates/cloud-template';
import { DeleteTemplateComponent } from '../../../common/delete-template-component/delete-template.component';
@Component({
selector: 'app-cloud-nodes-templates',
templateUrl: './cloud-nodes-templates.component.html',
styleUrls: ['./cloud-nodes-templates.component.scss', '../../../preferences.component.scss']
})
export class CloudNodesTemplatesComponent implements OnInit {
server: Server;
cloudNodesTemplates: CloudTemplate[] = [];
@ViewChild(DeleteTemplateComponent) deleteComponent: DeleteTemplateComponent;
constructor(
private route: ActivatedRoute,
private serverService: ServerService,
private builtInTemplatesService: BuiltInTemplatesService
) {}
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.builtInTemplatesService.getTemplates(this.server).subscribe((cloudNodesTemplates: CloudTemplate[]) => {
this.cloudNodesTemplates = cloudNodesTemplates.filter((elem) => elem.template_type === "cloud" && !elem.builtin);
});
}
deleteTemplate(template: CloudTemplate) {
this.deleteComponent.deleteItem(template.name, template.template_id);
}
onDeleteEvent() {
this.getTemplates();
}
}

View File

@ -0,0 +1,23 @@
<div class="content">
<div class="default-header">
<div class="row">
<h1 class="col">New Ethernet hub template</h1>
</div>
</div>
<div class="default-content">
<mat-card class="matCard">
<form [formGroup]="formGroup">
<mat-form-field class="form-field">
<input formControlName="templateName" matInput type="text" [(ngModel)]="templateName" placeholder="Template name">
</mat-form-field>
<mat-form-field class="form-field">
<input formControlName="numberOfPorts" matInput type="text" [(ngModel)]="numberOfPorts" placeholder="Number of ports">
</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,102 @@
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
import { MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule } from '@angular/material';
import { CommonModule } from '@angular/common';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
import { ActivatedRoute } from '@angular/router';
import { of } from 'rxjs';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { Server } from '../../../../../models/server';
import { MockedServerService } from '../../../../../services/server.service.spec';
import { MockedToasterService } from '../../../../../services/toaster.service.spec';
import { MockedActivatedRoute } from '../../../preferences.component.spec';
import { ServerService } from '../../../../../services/server.service';
import { BuiltInTemplatesService } from '../../../../../services/built-in-templates.service';
import { ToasterService } from '../../../../../services/toaster.service';
import { TemplateMocksService } from '../../../../../services/template-mocks.service';
import { EthernetHubTemplate } from '../../../../../models/templates/ethernet-hub-template';
import { EthernetHubsAddTemplateComponent } from './ethernet-hubs-add-template.component';
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
export class MockedBuiltInTemplatesService {
public addTemplate(server: Server, ethernetHubTemplate: EthernetHubTemplate) {
return of(ethernetHubTemplate);
}
}
describe('EthernetHubsAddTemplateComponent', () => {
let component: EthernetHubsAddTemplateComponent;
let fixture: ComponentFixture<EthernetHubsAddTemplateComponent>;
let mockedServerService = new MockedServerService;
let mockedBuiltInTemplatesService = new MockedBuiltInTemplatesService;
let mockedToasterService = new MockedToasterService;
let activatedRoute = new MockedActivatedRoute().get();
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [FormsModule, ReactiveFormsModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
providers: [
{
provide: ActivatedRoute, useValue: activatedRoute
},
{ provide: ServerService, useValue: mockedServerService },
{ provide: BuiltInTemplatesService, useValue: mockedBuiltInTemplatesService },
{ provide: ToasterService, useValue: mockedToasterService},
{ provide: TemplateMocksService, useClass: TemplateMocksService }
],
declarations: [
EthernetHubsAddTemplateComponent
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(EthernetHubsAddTemplateComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should call add template', () => {
spyOn(mockedBuiltInTemplatesService, 'addTemplate').and.returnValue(of({} as EthernetHubTemplate));
component.templateName = "sample name";
component.numberOfPorts = 2;
component.server = {id: 1} as Server;
component.formGroup.controls['templateName'].setValue('template name');
component.formGroup.controls['numberOfPorts'].setValue('1');
component.addTemplate();
expect(mockedBuiltInTemplatesService.addTemplate).toHaveBeenCalled();
});
it('should not call add template when template name is empty', () => {
spyOn(mockedBuiltInTemplatesService, 'addTemplate').and.returnValue(of({} as EthernetHubTemplate));
spyOn(mockedToasterService, 'error');
component.templateName = "";
component.numberOfPorts = 2;
component.server = {id: 1} as Server;
component.addTemplate();
expect(mockedBuiltInTemplatesService.addTemplate).not.toHaveBeenCalled();
expect(mockedToasterService.error).toHaveBeenCalled();
});
it('should not call add template when number of ports is missing', () => {
spyOn(mockedBuiltInTemplatesService, 'addTemplate').and.returnValue(of({} as EthernetHubTemplate));
spyOn(mockedToasterService, 'error');
component.templateName = "sample name";
component.server = {id: 1} as Server;
component.addTemplate();
expect(mockedBuiltInTemplatesService.addTemplate).not.toHaveBeenCalled();
expect(mockedToasterService.error).toHaveBeenCalled();
});
});

View File

@ -0,0 +1,75 @@
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 { BuiltInTemplatesService } from '../../../../../services/built-in-templates.service';
import { EthernetHubTemplate } from '../../../../../models/templates/ethernet-hub-template';
import { FormBuilder, FormGroup, FormControl, Validators } from '@angular/forms';
@Component({
selector: 'app-ethernet-hubs-add-template',
templateUrl: './ethernet-hubs-add-template.component.html',
styleUrls: ['./ethernet-hubs-add-template.component.scss', '../../../preferences.component.scss']
})
export class EthernetHubsAddTemplateComponent implements OnInit {
server: Server;
numberOfPorts: number;
templateName: string = '';
formGroup: FormGroup;
constructor(
private route: ActivatedRoute,
private serverService: ServerService,
private builtInTemplatesService: BuiltInTemplatesService,
private router: Router,
private toasterService: ToasterService,
private templateMocksService: TemplateMocksService,
private formBuilder: FormBuilder
) {
this.formGroup = this.formBuilder.group({
templateName: new FormControl('', Validators.required),
numberOfPorts: new FormControl('', 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', 'builtin', 'ethernet-hubs']);
}
addTemplate() {
if (!this.formGroup.invalid) {
let ethernetHubTemplate: EthernetHubTemplate;
this.templateMocksService.getEthernetHubTemplate().subscribe((template: EthernetHubTemplate) => {
ethernetHubTemplate = template;
});
ethernetHubTemplate.template_id = uuid();
ethernetHubTemplate.name = this.templateName;
for(let i=0; i<this.numberOfPorts; i++){
ethernetHubTemplate.ports_mapping.push({
name: `Ethernet${i}`,
port_number: i
});
}
this.builtInTemplatesService.addTemplate(this.server, ethernetHubTemplate).subscribe(() => {
this.goBack();
});
} else {
this.toasterService.error(`Fill all required fields`);
}
}
}

View File

@ -0,0 +1,57 @@
<div class="content" [ngClass]="{ shadowed: isSymbolSelectionOpened}">
<div class="default-header">
<div class="row">
<h1 class="col">Ethernet hub settings</h1>
</div>
</div>
<div class="default-content" *ngIf="ethernetHubTemplate">
<mat-card>
<form [formGroup]="inputForm">
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="ethernetHubTemplate.name"
formControlName="templateName"
placeholder="Template name">
</mat-form-field>
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="ethernetHubTemplate.default_name_format"
formControlName="defaultName"
placeholder="Default name format">
</mat-form-field>
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="ethernetHubTemplate.symbol"
formControlName="symbol"
placeholder="Symbol">
</mat-form-field>
<button mat-button class="symbolSelectionButton" (click)="chooseSymbol()">Choose symbol</button><br/><br/>
<mat-form-field class="form-field">
<mat-select
placeholder="Category"
[ngModelOptions]="{standalone: true}"
[(ngModel)]="ethernetHubTemplate.category">
<mat-option *ngFor="let category of categories" [value]="category[1]">
{{category[0]}}
</mat-option>
</mat-select>
</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 class="buttons-bar">
<button class="cancel-button" (click)="goBack()" mat-button>Cancel</button>
<button mat-raised-button color="primary" (click)="onSave()">Save</button><br/>
</div>
</div>
</div>
<app-symbols-menu *ngIf="isSymbolSelectionOpened && ethernetHubTemplate" [server]="server" [symbol]="ethernetHubTemplate.symbol" (symbolChangedEmitter)="symbolChanged($event)"></app-symbols-menu>

View File

@ -0,0 +1,120 @@
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
import { MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, MatTableModule } from '@angular/material';
import { CommonModule } from '@angular/common';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
import { ActivatedRoute } from '@angular/router';
import { of } from 'rxjs';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { Server } from '../../../../../models/server';
import { MockedServerService } from '../../../../../services/server.service.spec';
import { MockedToasterService } from '../../../../../services/toaster.service.spec';
import { MockedActivatedRoute } from '../../../preferences.component.spec';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ServerService } from '../../../../../services/server.service';
import { ToasterService } from '../../../../../services/toaster.service';
import { BuiltInTemplatesService } from '../../../../../services/built-in-templates.service';
import { EthernetHubTemplate } from '../../../../../models/templates/ethernet-hub-template';
import { EthernetHubsTemplateDetailsComponent } from './ethernet-hubs-template-details.component';
import { BuiltInTemplatesConfigurationService } from '../../../../../services/built-in-templates-configuration.service';
export class MockedBuiltInTemplatesService {
public getTemplate(server: Server, template_id: string) {
return of({ports_mapping: []} as EthernetHubTemplate);
}
public saveTemplate(server: Server, cloudTemplate: EthernetHubTemplate) {
return of(cloudTemplate);
}
}
describe('EthernetHubsTemplateDetailsComponent', () => {
let component: EthernetHubsTemplateDetailsComponent;
let fixture: ComponentFixture<EthernetHubsTemplateDetailsComponent>;
let mockedServerService = new MockedServerService;
let mockedBuiltInTemplatesService = new MockedBuiltInTemplatesService;
let mockedToasterService = new MockedToasterService;
let activatedRoute = new MockedActivatedRoute().get();
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [FormsModule, ReactiveFormsModule, MatTableModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
providers: [
{
provide: ActivatedRoute, useValue: activatedRoute
},
{ provide: ServerService, useValue: mockedServerService },
{ provide: BuiltInTemplatesService, useValue: mockedBuiltInTemplatesService },
{ provide: ToasterService, useValue: mockedToasterService},
{ provide: BuiltInTemplatesConfigurationService, useClass: BuiltInTemplatesConfigurationService }
],
declarations: [
EthernetHubsTemplateDetailsComponent
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(EthernetHubsTemplateDetailsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should call save template', () => {
spyOn(mockedBuiltInTemplatesService, 'saveTemplate').and.returnValue(of({} as EthernetHubTemplate));
component.numberOfPorts = 2;
component.inputForm.controls['templateName'].setValue('template name');
component.inputForm.controls['defaultName'].setValue('default name');
component.inputForm.controls['symbol'].setValue('symbol');
component.ethernetHubTemplate = {ports_mapping: []} as EthernetHubTemplate;
component.onSave();
expect(mockedBuiltInTemplatesService.saveTemplate).toHaveBeenCalled();
});
it('should not call save template when template name is empty', () => {
spyOn(mockedBuiltInTemplatesService, 'saveTemplate').and.returnValue(of({} as EthernetHubTemplate));
component.numberOfPorts = 2;
component.inputForm.controls['templateName'].setValue('');
component.inputForm.controls['defaultName'].setValue('default name');
component.inputForm.controls['symbol'].setValue('symbol');
component.ethernetHubTemplate = {ports_mapping: []} as EthernetHubTemplate;
component.onSave();
expect(mockedBuiltInTemplatesService.saveTemplate).not.toHaveBeenCalled();
});
it('should not call save template when default name is empty', () => {
spyOn(mockedBuiltInTemplatesService, 'saveTemplate').and.returnValue(of({} as EthernetHubTemplate));
component.numberOfPorts = 2;
component.inputForm.controls['templateName'].setValue('template name');
component.inputForm.controls['defaultName'].setValue('');
component.inputForm.controls['symbol'].setValue('symbol');
component.ethernetHubTemplate = {ports_mapping: []} as EthernetHubTemplate;
component.onSave();
expect(mockedBuiltInTemplatesService.saveTemplate).not.toHaveBeenCalled();
});
it('should call save template when symbol path is empty', () => {
spyOn(mockedBuiltInTemplatesService, 'saveTemplate').and.returnValue(of({} as EthernetHubTemplate));
component.numberOfPorts = 2;
component.inputForm.controls['templateName'].setValue('template name');
component.inputForm.controls['defaultName'].setValue('default name');
component.inputForm.controls['symbol'].setValue('');
component.ethernetHubTemplate = {ports_mapping: []} as EthernetHubTemplate;
component.onSave();
expect(mockedBuiltInTemplatesService.saveTemplate).not.toHaveBeenCalled();
});
});

View File

@ -0,0 +1,86 @@
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 { EthernetHubTemplate } from '../../../../../models/templates/ethernet-hub-template';
import { BuiltInTemplatesService } from '../../../../../services/built-in-templates.service';
import { BuiltInTemplatesConfigurationService } from '../../../../../services/built-in-templates-configuration.service';
@Component({
selector: 'app-ethernet-hubs-template-details',
templateUrl: './ethernet-hubs-template-details.component.html',
styleUrls: ['./ethernet-hubs-template-details.component.scss', '../../../preferences.component.scss']
})
export class EthernetHubsTemplateDetailsComponent implements OnInit {
server: Server;
ethernetHubTemplate: EthernetHubTemplate;
numberOfPorts: number;
inputForm: FormGroup;
isSymbolSelectionOpened: boolean = false;
categories = [];
constructor(
private route: ActivatedRoute,
private serverService: ServerService,
private builtInTemplatesService: BuiltInTemplatesService,
private toasterService: ToasterService,
private formBuilder: FormBuilder,
private builtInTemplatesConfigurationService: BuiltInTemplatesConfigurationService,
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.categories = this.builtInTemplatesConfigurationService.getCategoriesForEthernetHubs();
this.builtInTemplatesService.getTemplate(this.server, template_id).subscribe((ethernetHubTemplate: EthernetHubTemplate) => {
this.ethernetHubTemplate = ethernetHubTemplate;
this.numberOfPorts = this.ethernetHubTemplate.ports_mapping.length;
});
});
}
goBack() {
this.router.navigate(['/server', this.server.id, 'preferences', 'builtin', 'ethernet-hubs']);
}
onSave() {
if (this.inputForm.invalid || ! this.numberOfPorts) {
this.toasterService.error(`Fill all required fields`);
} else {
this.ethernetHubTemplate.ports_mapping = [];
for(let i=0; i<this.numberOfPorts; i++){
this.ethernetHubTemplate.ports_mapping.push({
name: `Ethernet${i}`,
port_number: i
});
}
this.builtInTemplatesService.saveTemplate(this.server, this.ethernetHubTemplate).subscribe((ethernetHubTemplate: EthernetHubTemplate) => {
this.toasterService.success("Changes saved");
});
}
}
chooseSymbol() {
this.isSymbolSelectionOpened = !this.isSymbolSelectionOpened;
}
symbolChanged(chosenSymbol: string) {
this.isSymbolSelectionOpened = !this.isSymbolSelectionOpened;
this.ethernetHubTemplate.symbol = chosenSymbol;
}
}

View File

@ -0,0 +1,32 @@
<div class="content">
<div class="default-header">
<div class="row">
<h1 class="col">Ethernet hubs templates</h1>
<button *ngIf="server" class="top-button" class="cancel-button" routerLink="/server/{{server.id}}/preferences/builtin" mat-button>Back</button>
<button *ngIf="server" class="top-button" routerLink="/server/{{server.id}}/preferences/builtin/ethernet-hubs/addtemplate" mat-raised-button color="primary">Add Ethernet hub template</button>
</div>
</div>
<app-empty-templates-list *ngIf="!ethernetHubsTemplates.length"></app-empty-templates-list>
<div class="default-content" *ngIf="ethernetHubsTemplates.length">
<div class="listcontainer mat-elevation-z8">
<mat-nav-list *ngIf="server">
<div class="list-item" *ngFor='let template of ethernetHubsTemplates'>
<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,57 @@
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
import { MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule } from '@angular/material';
import { CommonModule } from '@angular/common';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
import { ActivatedRoute } from '@angular/router';
import { of } from 'rxjs';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { Server } from '../../../../../models/server';
import { MockedServerService } from '../../../../../services/server.service.spec';
import { MockedActivatedRoute } from '../../../preferences.component.spec';
import { ServerService } from '../../../../../services/server.service';
import { BuiltInTemplatesService } from '../../../../../services/built-in-templates.service';
import { EthernetHubTemplate } from '../../../../../models/templates/ethernet-hub-template';
import { EthernetHubsTemplatesComponent } from './ethernet-hubs-templates.component';
export class MockedBuiltInTemplatesService {
public getTemplates(server: Server) {
return of([{} as EthernetHubTemplate]);
}
}
describe('EthernetHubsTemplatesComponent', () => {
let component: EthernetHubsTemplatesComponent;
let fixture: ComponentFixture<EthernetHubsTemplatesComponent>;
let mockedServerService = new MockedServerService;
let mockedBuiltInTemplatesService = new MockedBuiltInTemplatesService;
let activatedRoute = new MockedActivatedRoute().get();
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
providers: [
{
provide: ActivatedRoute, useValue: activatedRoute
},
{ provide: ServerService, useValue: mockedServerService },
{ provide: BuiltInTemplatesService, useValue: mockedBuiltInTemplatesService }
],
declarations: [
EthernetHubsTemplatesComponent
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(EthernetHubsTemplatesComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,47 @@
import { Component, OnInit, ViewChild } from "@angular/core";
import { Server } from '../../../../../models/server';
import { ActivatedRoute } from '@angular/router';
import { ServerService } from '../../../../../services/server.service';
import { EthernetHubTemplate } from '../../../../../models/templates/ethernet-hub-template';
import { BuiltInTemplatesService } from '../../../../../services/built-in-templates.service';
import { DeleteTemplateComponent } from '../../../common/delete-template-component/delete-template.component';
@Component({
selector: 'app-ethernet-hubs-templates',
templateUrl: './ethernet-hubs-templates.component.html',
styleUrls: ['./ethernet-hubs-templates.component.scss', '../../../preferences.component.scss']
})
export class EthernetHubsTemplatesComponent implements OnInit {
server: Server;
ethernetHubsTemplates: EthernetHubTemplate[] = [];
@ViewChild(DeleteTemplateComponent) deleteComponent: DeleteTemplateComponent;
constructor(
private route: ActivatedRoute,
private serverService: ServerService,
private builtInTemplatesService: BuiltInTemplatesService
) {}
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.builtInTemplatesService.getTemplates(this.server).subscribe((ethernetHubsTemplates: EthernetHubTemplate[]) => {
this.ethernetHubsTemplates = ethernetHubsTemplates.filter((elem) => elem.template_type === "ethernet_hub" && !elem.builtin);
});
}
deleteTemplate(template: EthernetHubTemplate) {
this.deleteComponent.deleteItem(template.name, template.template_id);
}
onDeleteEvent() {
this.getTemplates();
}
}

View File

@ -0,0 +1,20 @@
<div class="content">
<div class="default-header">
<div class="row">
<h1 class="col">New Ethernet switch template</h1>
</div>
</div>
<div class="default-content">
<mat-card class="matCard">
<form [formGroup]="formGroup">
<mat-form-field class="form-field">
<input formControlName="templateName" matInput type="text" [(ngModel)]="templateName" placeholder="Template name">
</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,101 @@
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
import { MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule } from '@angular/material';
import { CommonModule } from '@angular/common';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
import { ActivatedRoute } from '@angular/router';
import { of } from 'rxjs';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { Server } from '../../../../../models/server';
import { MockedServerService } from '../../../../../services/server.service.spec';
import { MockedToasterService } from '../../../../../services/toaster.service.spec';
import { MockedActivatedRoute } from '../../../preferences.component.spec';
import { ServerService } from '../../../../../services/server.service';
import { BuiltInTemplatesService } from '../../../../../services/built-in-templates.service';
import { ToasterService } from '../../../../../services/toaster.service';
import { TemplateMocksService } from '../../../../../services/template-mocks.service';
import { EthernetSwitchTemplate } from '../../../../../models/templates/ethernet-switch-template';
import { EthernetSwitchesAddTemplateComponent } from './ethernet-switches-add-template.component';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
export class MockedBuiltInTemplatesService {
public addTemplate(server: Server, ethernetHubTemplate: EthernetSwitchTemplate) {
return of(ethernetHubTemplate);
}
}
describe('EthernetSwitchesAddTemplateComponent', () => {
let component: EthernetSwitchesAddTemplateComponent;
let fixture: ComponentFixture<EthernetSwitchesAddTemplateComponent>;
let mockedServerService = new MockedServerService;
let mockedBuiltInTemplatesService = new MockedBuiltInTemplatesService;
let mockedToasterService = new MockedToasterService;
let activatedRoute = new MockedActivatedRoute().get();
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [FormsModule, ReactiveFormsModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
providers: [
{
provide: ActivatedRoute, useValue: activatedRoute
},
{ provide: ServerService, useValue: mockedServerService },
{ provide: BuiltInTemplatesService, useValue: mockedBuiltInTemplatesService },
{ provide: ToasterService, useValue: mockedToasterService},
{ provide: TemplateMocksService, useClass: TemplateMocksService }
],
declarations: [
EthernetSwitchesAddTemplateComponent
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(EthernetSwitchesAddTemplateComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should call add template', () => {
spyOn(mockedBuiltInTemplatesService, 'addTemplate').and.returnValue(of({} as EthernetSwitchTemplate));
component.templateName = "sample name";
component.numberOfPorts = 2;
component.server = {id: 1} as Server;
component.formGroup.controls['templateName'].setValue('template name');
component.formGroup.controls['numberOfPorts'].setValue('1');
component.addTemplate();
expect(mockedBuiltInTemplatesService.addTemplate).toHaveBeenCalled();
});
it('should not call add template when template name is empty', () => {
spyOn(mockedBuiltInTemplatesService, 'addTemplate').and.returnValue(of({} as EthernetSwitchTemplate));
spyOn(mockedToasterService, 'error');
component.formGroup.controls['numberOfPorts'].setValue('1');
component.server = {id: 1} as Server;
component.addTemplate();
expect(mockedBuiltInTemplatesService.addTemplate).not.toHaveBeenCalled();
expect(mockedToasterService.error).toHaveBeenCalled();
});
it('should not call add template when number of ports is missing', () => {
spyOn(mockedBuiltInTemplatesService, 'addTemplate').and.returnValue(of({} as EthernetSwitchTemplate));
spyOn(mockedToasterService, 'error');
component.formGroup.controls['templateName'].setValue('template name');
component.server = {id: 1} as Server;
component.addTemplate();
expect(mockedBuiltInTemplatesService.addTemplate).not.toHaveBeenCalled();
expect(mockedToasterService.error).toHaveBeenCalled();
});
});

View File

@ -0,0 +1,78 @@
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 { BuiltInTemplatesService } from '../../../../../services/built-in-templates.service';
import { EthernetSwitchTemplate } from '../../../../../models/templates/ethernet-switch-template';
import { FormGroup, FormBuilder, FormControl, Validators } from '@angular/forms';
@Component({
selector: 'app-ethernet-switches-add-template',
templateUrl: './ethernet-switches-add-template.component.html',
styleUrls: ['./ethernet-switches-add-template.component.scss', '../../../preferences.component.scss']
})
export class EthernetSwitchesAddTemplateComponent implements OnInit {
server: Server;
numberOfPorts: number;
templateName: string = '';
formGroup: FormGroup;
constructor(
private route: ActivatedRoute,
private serverService: ServerService,
private builtInTemplatesService: BuiltInTemplatesService,
private router: Router,
private toasterService: ToasterService,
private templateMocksService: TemplateMocksService,
private formBuilder: FormBuilder
) {
this.formGroup = this.formBuilder.group({
templateName: new FormControl('', Validators.required),
numberOfPorts: new FormControl('', 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', 'builtin', 'ethernet-switches']);
}
addTemplate() {
if (!this.formGroup.invalid) {
let ethernetSwitchTemplate: EthernetSwitchTemplate;
this.templateMocksService.getEthernetSwitchTemplate().subscribe((template: EthernetSwitchTemplate) => {
ethernetSwitchTemplate = template;
});
ethernetSwitchTemplate.template_id = uuid();
ethernetSwitchTemplate.name = this.templateName;
for(let i=0; i<this.numberOfPorts; i++){
ethernetSwitchTemplate.ports_mapping.push({
ethertype: '',
name: `Ethernet${i}`,
port_number: i,
type: 'access',
vlan: 1
});
}
this.builtInTemplatesService.addTemplate(this.server, ethernetSwitchTemplate).subscribe((ethernetSwitchTemplate) => {
this.goBack();
});
} else {
this.toasterService.error(`Fill all required fields`);
}
}
}

View File

@ -0,0 +1,131 @@
<div class="content" [ngClass]="{ shadowed: isSymbolSelectionOpened}">
<div class="default-header">
<div class="row">
<h1 class="col">Ethernet switch configuration</h1>
</div>
</div>
<div class="default-content" *ngIf="ethernetSwitchTemplate">
<mat-accordion>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
General settings
</mat-panel-title>
</mat-expansion-panel-header>
<form [formGroup]="inputForm">
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="ethernetSwitchTemplate.name"
formControlName="templateName"
placeholder="Template name">
</mat-form-field>
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="ethernetSwitchTemplate.default_name_format"
formControlName="defaultName"
placeholder="Default name format">
</mat-form-field>
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="ethernetSwitchTemplate.symbol"
formControlName="symbol"
placeholder="Symbol">
</mat-form-field>
<button mat-button class="symbolSelectionButton" (click)="chooseSymbol()">Choose symbol</button><br/><br/>
<mat-form-field class="form-field">
<mat-select
placeholder="Category"
[ngModelOptions]="{standalone: true}"
[(ngModel)]="ethernetSwitchTemplate.category">
<mat-option *ngFor="let category of categories" [value]="category[1]">
{{category[0]}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="select">
<mat-select
placeholder="Console type"
[ngModelOptions]="{standalone: true}"
[(ngModel)]="ethernetSwitchTemplate.console_type">
<mat-option *ngFor="let type of consoleTypes" [value]="type">
{{type}}
</mat-option>
</mat-select>
</mat-form-field>
</form>
</mat-expansion-panel>
<mat-expansion-panel *ngIf="newPort">
<mat-expansion-panel-header>
<mat-panel-title>
Port settings
</mat-panel-title>
</mat-expansion-panel-header>
<table class="table" mat-table [dataSource]="dataSource">
<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-accordion>
<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 && ethernetSwitchTemplate" [server]="server" [symbol]="ethernetSwitchTemplate.symbol" (symbolChangedEmitter)="symbolChanged($event)"></app-symbols-menu>

View File

@ -0,0 +1,116 @@
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
import { MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, MatTableModule } from '@angular/material';
import { CommonModule } from '@angular/common';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
import { ActivatedRoute } from '@angular/router';
import { of } from 'rxjs';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { Server } from '../../../../../models/server';
import { MockedServerService } from '../../../../../services/server.service.spec';
import { MockedToasterService } from '../../../../../services/toaster.service.spec';
import { MockedActivatedRoute } from '../../../preferences.component.spec';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ServerService } from '../../../../../services/server.service';
import { ToasterService } from '../../../../../services/toaster.service';
import { BuiltInTemplatesService } from '../../../../../services/built-in-templates.service';
import { EthernetSwitchTemplate } from '../../../../../models/templates/ethernet-switch-template';
import { EthernetSwitchesTemplateDetailsComponent } from './ethernet-switches-template-details.component';
import { BuiltInTemplatesConfigurationService } from '../../../../../services/built-in-templates-configuration.service';
export class MockedBuiltInTemplatesService {
public getTemplate(server: Server, template_id: string) {
return of({ports_mapping: []} as EthernetSwitchTemplate);
}
public saveTemplate(server: Server, cloudTemplate: EthernetSwitchTemplate) {
return of(cloudTemplate);
}
}
describe('EthernetSwitchesTemplateDetailsComponent', () => {
let component: EthernetSwitchesTemplateDetailsComponent;
let fixture: ComponentFixture<EthernetSwitchesTemplateDetailsComponent>;
let mockedServerService = new MockedServerService;
let mockedBuiltInTemplatesService = new MockedBuiltInTemplatesService;
let mockedToasterService = new MockedToasterService;
let activatedRoute = new MockedActivatedRoute().get();
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [FormsModule, ReactiveFormsModule, MatTableModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
providers: [
{
provide: ActivatedRoute, useValue: activatedRoute
},
{ provide: ServerService, useValue: mockedServerService },
{ provide: BuiltInTemplatesService, useValue: mockedBuiltInTemplatesService },
{ provide: ToasterService, useValue: mockedToasterService},
{ provide: BuiltInTemplatesConfigurationService, useClass: BuiltInTemplatesConfigurationService }
],
declarations: [
EthernetSwitchesTemplateDetailsComponent
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(EthernetSwitchesTemplateDetailsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should call save template', () => {
spyOn(mockedBuiltInTemplatesService, 'saveTemplate').and.returnValue(of({} as EthernetSwitchTemplate));
component.inputForm.controls['templateName'].setValue('template name');
component.inputForm.controls['defaultName'].setValue('default name');
component.inputForm.controls['symbol'].setValue('symbol');
component.ethernetSwitchTemplate = {ports_mapping: []} as EthernetSwitchTemplate;
component.onSave();
expect(mockedBuiltInTemplatesService.saveTemplate).toHaveBeenCalled();
});
it('should not call save template when template name is empty', () => {
spyOn(mockedBuiltInTemplatesService, 'saveTemplate').and.returnValue(of({} as EthernetSwitchTemplate));
component.inputForm.controls['templateName'].setValue('');
component.inputForm.controls['defaultName'].setValue('default name');
component.inputForm.controls['symbol'].setValue('symbol');
component.ethernetSwitchTemplate = {ports_mapping: []} as EthernetSwitchTemplate;
component.onSave();
expect(mockedBuiltInTemplatesService.saveTemplate).not.toHaveBeenCalled();
});
it('should not call save template when default name is empty', () => {
spyOn(mockedBuiltInTemplatesService, 'saveTemplate').and.returnValue(of({} as EthernetSwitchTemplate));
component.inputForm.controls['templateName'].setValue('template name');
component.inputForm.controls['defaultName'].setValue('');
component.inputForm.controls['symbol'].setValue('symbol');
component.ethernetSwitchTemplate = {ports_mapping: []} as EthernetSwitchTemplate;
component.onSave();
expect(mockedBuiltInTemplatesService.saveTemplate).not.toHaveBeenCalled();
});
it('should call save template when symbol path is empty', () => {
spyOn(mockedBuiltInTemplatesService, 'saveTemplate').and.returnValue(of({} as EthernetSwitchTemplate));
component.inputForm.controls['templateName'].setValue('template name');
component.inputForm.controls['defaultName'].setValue('default name');
component.inputForm.controls['symbol'].setValue('');
component.ethernetSwitchTemplate = {ports_mapping: []} as EthernetSwitchTemplate;
component.onSave();
expect(mockedBuiltInTemplatesService.saveTemplate).not.toHaveBeenCalled();
});
});

View File

@ -0,0 +1,109 @@
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 { BuiltInTemplatesService } from '../../../../../services/built-in-templates.service';
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';
@Component({
selector: 'app-ethernet-switches-template-details',
templateUrl: './ethernet-switches-template-details.component.html',
styleUrls: ['./ethernet-switches-template-details.component.scss', '../../../preferences.component.scss']
})
export class EthernetSwitchesTemplateDetailsComponent implements OnInit {
server: Server;
ethernetSwitchTemplate: EthernetSwitchTemplate;
inputForm: FormGroup;
ethernetPorts: PortsMappingEntity[] = [];
dataSource: PortsMappingEntity[] = [];
newPort: PortsMappingEntity;
isSymbolSelectionOpened: boolean = false;
categories = [];
consoleTypes: string[] = [];
portTypes: string[] = [];
etherTypes: string[] = [];
displayedColumns: string[] = ['port_number', 'vlan', 'type', 'ethertype'];
constructor(
private route: ActivatedRoute,
private serverService: ServerService,
private builtInTemplatesService: BuiltInTemplatesService,
private toasterService: ToasterService,
private formBuilder: FormBuilder,
private builtInTemplatesConfigurationService: BuiltInTemplatesConfigurationService,
private router: Router
){
this.inputForm = this.formBuilder.group({
templateName: new FormControl('', Validators.required),
defaultName: new FormControl('', Validators.required),
symbol: new FormControl('', Validators.required)
});
this.newPort = {
name: '',
port_number: 0,
};
}
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.getConfiguration();
this.builtInTemplatesService.getTemplate(this.server, template_id).subscribe((ethernetSwitchTemplate: EthernetSwitchTemplate) => {
this.ethernetSwitchTemplate = ethernetSwitchTemplate;
this.ethernetPorts = this.ethernetSwitchTemplate.ports_mapping;
this.dataSource = this.ethernetSwitchTemplate.ports_mapping;
});
});
}
getConfiguration() {
this.categories = this.builtInTemplatesConfigurationService.getCategoriesForEthernetSwitches();
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() {
this.router.navigate(['/server', this.server.id, 'preferences', 'builtin', 'ethernet-switches']);
}
onSave() {
if (this.inputForm.invalid) {
this.toasterService.error(`Fill all required fields`);
} else {
this.builtInTemplatesService.saveTemplate(this.server, this.ethernetSwitchTemplate).subscribe((ethernetSwitchTemplate: EthernetSwitchTemplate) => {
this.toasterService.success("Changes saved");
});
}
}
chooseSymbol() {
this.isSymbolSelectionOpened = !this.isSymbolSelectionOpened;
}
symbolChanged(chosenSymbol: string) {
this.isSymbolSelectionOpened = !this.isSymbolSelectionOpened;
this.ethernetSwitchTemplate.symbol = chosenSymbol;
}
}

View File

@ -0,0 +1,32 @@
<div class="content">
<div class="default-header">
<div class="row">
<h1 class="col">Ethernet switches templates</h1>
<button *ngIf="server" class="top-button" class="cancel-button" routerLink="/server/{{server.id}}/preferences/builtin" mat-button>Back</button>
<button *ngIf="server" class="top-button" routerLink="/server/{{server.id}}/preferences/builtin/ethernet-switches/addtemplate" mat-raised-button color="primary">Add Ethernet switch template</button>
</div>
</div>
<app-empty-templates-list *ngIf="!ethernetSwitchesTemplates.length"></app-empty-templates-list>
<div class="default-content" *ngIf="ethernetSwitchesTemplates.length">
<div class="listcontainer mat-elevation-z8">
<mat-nav-list *ngIf="server">
<div class="list-item" *ngFor='let template of ethernetSwitchesTemplates'>
<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,57 @@
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
import { MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule } from '@angular/material';
import { CommonModule } from '@angular/common';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
import { ActivatedRoute } from '@angular/router';
import { of } from 'rxjs';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { Server } from '../../../../../models/server';
import { MockedServerService } from '../../../../../services/server.service.spec';
import { MockedActivatedRoute } from '../../../preferences.component.spec';
import { ServerService } from '../../../../../services/server.service';
import { BuiltInTemplatesService } from '../../../../../services/built-in-templates.service';
import { EthernetSwitchesTemplatesComponent } from './ethernet-switches-templates.component';
import { EthernetSwitchTemplate } from '../../../../../models/templates/ethernet-switch-template';
export class MockedBuiltInTemplatesService {
public getTemplates(server: Server) {
return of([{} as EthernetSwitchTemplate]);
}
}
describe('EthernetSwitchesTemplatesComponent', () => {
let component: EthernetSwitchesTemplatesComponent;
let fixture: ComponentFixture<EthernetSwitchesTemplatesComponent>;
let mockedServerService = new MockedServerService;
let mockedBuiltInTemplatesService = new MockedBuiltInTemplatesService;
let activatedRoute = new MockedActivatedRoute().get();
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
providers: [
{
provide: ActivatedRoute, useValue: activatedRoute
},
{ provide: ServerService, useValue: mockedServerService },
{ provide: BuiltInTemplatesService, useValue: mockedBuiltInTemplatesService }
],
declarations: [
EthernetSwitchesTemplatesComponent
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(EthernetSwitchesTemplatesComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,47 @@
import { Component, OnInit, ViewChild } from "@angular/core";
import { Server } from '../../../../../models/server';
import { ActivatedRoute } from '@angular/router';
import { ServerService } from '../../../../../services/server.service';
import { BuiltInTemplatesService } from '../../../../../services/built-in-templates.service';
import { EthernetSwitchTemplate } from '../../../../../models/templates/ethernet-switch-template';
import { DeleteTemplateComponent } from '../../../common/delete-template-component/delete-template.component';
@Component({
selector: 'app-ethernet-switches-templates',
templateUrl: './ethernet-switches-templates.component.html',
styleUrls: ['./ethernet-switches-templates.component.scss', '../../../preferences.component.scss']
})
export class EthernetSwitchesTemplatesComponent implements OnInit {
server: Server;
ethernetSwitchesTemplates: EthernetSwitchTemplate[] = [];
@ViewChild(DeleteTemplateComponent) deleteComponent: DeleteTemplateComponent;
constructor(
private route: ActivatedRoute,
private serverService: ServerService,
private builtInTemplatesService: BuiltInTemplatesService
) {}
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.builtInTemplatesService.getTemplates(this.server).subscribe((ethernetSwitchesTemplates: EthernetSwitchTemplate[]) => {
this.ethernetSwitchesTemplates = ethernetSwitchesTemplates.filter((elem) => elem.template_type === "ethernet_switch" && !elem.builtin);
});
}
deleteTemplate(template: EthernetSwitchTemplate) {
this.deleteComponent.deleteItem(template.name, template.template_id);
}
onDeleteEvent() {
this.getTemplates();
}
}

View File

@ -0,0 +1,15 @@
<div class="dialogcontainer">
<h1 mat-dialog-title>Delete template</h1>
<div mat-dialog-content>Are you sure you want to delete template {{templateName}}?</div>
<div class="spacer"></div>
<div mat-dialog-actions>
<button mat-button class="cancelButton" (click)="onNoClick()" color="accent">
No
</button>
<button mat-button class="confirmButton" (click)="onYesClick()" tabindex="2" mat-raised-button color="primary">
Yes
</button>
</div>
</div>

View File

@ -0,0 +1,9 @@
.dialogcontainer {
height: 100%;
display: flex;
flex-direction: column;
}
.spacer {
flex-grow: 1;
}

View File

@ -0,0 +1,29 @@
import { Component, OnInit, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
@Component({
selector: 'app-delete-confirmation-dialog',
templateUrl: './delete-confirmation-dialog.component.html',
styleUrls: ['./delete-confirmation-dialog.component.scss']
})
export class DeleteConfirmationDialogComponent implements OnInit {
templateName: string = '';
constructor(
public dialogRef: MatDialogRef<DeleteConfirmationDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any
) {
this.templateName = data['templateName'];
}
ngOnInit() {}
onNoClick(): void {
this.dialogRef.close(false);
}
onYesClick(): void {
this.dialogRef.close(true);
}
}

View File

@ -0,0 +1,43 @@
import { Component, Input, Output, EventEmitter } from "@angular/core";
import { TemplateService } from '../../../../services/template.service';
import { MatDialog } from '@angular/material';
import { DeleteConfirmationDialogComponent } from '../delete-confirmation-dialog/delete-confirmation-dialog.component';
import { ToasterService } from '../../../../services/toaster.service';
import { Server } from '../../../../models/server';
@Component({
selector: 'app-delete-template',
templateUrl: './delete-template.component.html',
styleUrls: ['./delete-template.component.scss']
})
export class DeleteTemplateComponent {
@Input() server: Server;
@Output() deleteEvent = new EventEmitter<string>();
constructor(
private templateService: TemplateService,
private dialog: MatDialog,
private toasterService: ToasterService
) {}
deleteItem(templateName, templateId) {
const dialogRef = this.dialog.open(DeleteConfirmationDialogComponent, {
width: '300px',
height: '250px',
data: {
templateName: templateName
}
});
dialogRef.afterClosed().subscribe((answer: boolean) => {
if (answer) {
this.templateService.deleteTemplate(this.server, templateId).subscribe((answer: boolean) => {
if(answer) {
this.deleteEvent.emit(templateId);
this.toasterService.success(`Template ${templateName} deleted.`);
}
});
}
});
}
}

View File

@ -0,0 +1,7 @@
<div class="default-content">
<mat-card class="matCard">
<h6 class="header">
{{emptyTemplatesListMessage}}
</h6>
</mat-card>
</div>

View File

@ -0,0 +1,4 @@
.header {
text-align: center;
color: darkgray;
}

View File

@ -0,0 +1,18 @@
import { Component, Input } from "@angular/core";
@Component({
selector: 'app-empty-templates-list',
templateUrl: './empty-templates-list.component.html',
styleUrls: ['./empty-templates-list.component.scss']
})
export class EmptyTemplatesListComponent {
@Input() textMessage: string;
emptyTemplatesListMessage: string = 'Empty templates list';
constructor(){
if (this.textMessage) {
this.emptyTemplatesListMessage = this.textMessage;
}
}
}

View File

@ -0,0 +1,12 @@
<div class="content" class="configurator">
<div class="default-header">
<div class="row">
<h1 class="col">Symbol selection</h1>
<button class="top-button" class="cancel-button" (click)="cancelChooseSymbol()" mat-button>Cancel</button>
<button class="top-button" (click)="chooseSymbol()" mat-raised-button color="primary">Choose symbol</button>
</div>
</div>
<div class="default-content">
<app-symbols [server]="server" [symbol]="symbol" (symbolChanged)="symbolChanged($event)"></app-symbols>
</div>
</div>

View File

@ -0,0 +1,28 @@
import { Component, Input, Output, EventEmitter } from "@angular/core";
import { Server } from '../../../../models/server';
@Component({
selector: 'app-symbols-menu',
templateUrl: './symbols-menu.component.html',
styleUrls: ['./symbols-menu.component.scss', '../../preferences.component.scss']
})
export class SymbolsMenuComponent {
@Input() server: Server;
@Input() symbol: string;
@Output() symbolChangedEmitter = new EventEmitter<string>();
chosenSymbol: string = '';
symbolChanged(chosenSymbol: string) {
this.chosenSymbol = chosenSymbol;
}
chooseSymbol() {
this.symbolChangedEmitter.emit(this.chosenSymbol);
}
cancelChooseSymbol() {
this.symbolChangedEmitter.emit(this.symbol);
}
}

View File

@ -0,0 +1,10 @@
<mat-card>
<input matInput type="text" [(ngModel)]="searchText" placeholder="Search by filename"><br/><br/>
<div class="wrapper">
<div class="buttonWrapper" *ngFor="let symbol of symbols | 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="http://127.0.0.1:3080/v2/symbols/{{symbol.symbol_id}}/raw"/>
</button>
</div>
</div>
</mat-card>

View File

@ -0,0 +1,38 @@
.buttonWrapper {
width: 85px;
height: 85px;
display: flex;
align-items: center;
margin-left: auto;
margin-right: auto;
outline: none;
}
.button {
background: border-box;
border-width: 0px;
outline: none;
}
.buttonSelected {
border-width: 3px;
border-color: #0097a7;
width: 77px;
}
.image {
width: 65px;
height: 65px;
}
.imageSelected {
margin-left: -3px;
}
.wrapper {
display: grid;
grid-template-rows: auto;
grid-template-columns: repeat(7, 1fr);
grid-row-gap: 3em;
grid-column-gap: 1em;
}

View File

@ -0,0 +1,58 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MatCheckboxModule, MatIconModule, MatToolbarModule, MatMenuModule } from '@angular/material';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { of } from 'rxjs';
import { CommonModule } from '@angular/common';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { RouterTestingModule } from '@angular/router/testing';
import { SymbolsComponent } from './symbols.component';
import { SymbolService } from '../../../../services/symbol.service';
import { HttpClientModule } from '@angular/common/http';
import { SearchFilter } from '../../../../filters/searchFilter.pipe';
export class MockedSymbolService {
public list() {
return of([]);
}
}
describe('Symbols component', () => {
let component: SymbolsComponent;
let fixture: ComponentFixture<SymbolsComponent>;
let mockedSymbolsService = new MockedSymbolService;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [HttpClientModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
providers: [
{
provide: SymbolService, useValue: mockedSymbolsService
}
],
declarations: [
SymbolsComponent,
SearchFilter
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SymbolsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should emit event when symbol selected', () => {
spyOn(component.symbolChanged, 'emit');
component.setSelected('id');
expect(component.symbolChanged.emit).toHaveBeenCalled();
expect(component.isSelected).toBe('id');
});
});

View File

@ -0,0 +1,37 @@
import { Component, OnInit, Input, Output, EventEmitter } from "@angular/core";
import { SymbolService } from '../../../../services/symbol.service';
import { Server } from '../../../../models/server';
import { Symbol } from '../../../../models/symbol';
@Component({
selector: 'app-symbols',
templateUrl: './symbols.component.html',
styleUrls: ['./symbols.component.scss']
})
export class SymbolsComponent implements OnInit {
@Input() server: Server;
@Input() symbol: string;
@Output() symbolChanged = new EventEmitter<string>();
symbols: Symbol[] = [];
isSelected: string = '';
searchText: string = '';
constructor(
private symbolService: SymbolService
) {}
ngOnInit() {
this.isSelected = this.symbol;
this.symbolService.list(this.server).subscribe((symbols: Symbol[]) => {
this.symbols = symbols;
});
}
setSelected(symbol_id: string) {
this.isSelected = symbol_id;
this.symbolChanged.emit(this.isSelected);
}
}

View File

@ -0,0 +1,98 @@
<div class="content">
<div class="default-header">
<div class="row">
<h1 class="col">New Docker container template</h1>
</div>
</div>
<div class="default-content">
<div class="container mat-elevation-z8">
<mat-vertical-stepper [linear]="true">
<mat-step label="Server type">
<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="2" (click)="setServerType('gns3 vm')" checked>Run this Docker container on the GNS3 VM</mat-radio-button>
</mat-radio-group>
</mat-step>
<mat-step label="Docker Virtual Machine">
<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}"
[(ngModel)]="selectedImage">
<mat-option *ngFor="let image of dockerImages" [value]="image">
{{image.filename}}
</mat-option>
</mat-select>
<div *ngIf="newImageSelected">
<mat-form-field class="form-field">
<input
matInput
type="text"
[(ngModel)]="dockerTemplate.image"
formControlName="filename"
placeholder="Image name"/>
</mat-form-field>
</div>
</form>
</mat-step>
<mat-step label="Container name">
<form [formGroup]="containerNameForm">
<mat-form-field class="form-field">
<input
matInput
type="text"
[(ngModel)]="dockerTemplate.name"
formControlName="templateName"
placeholder="Container name"/>
</mat-form-field>
</form>
</mat-step>
<mat-step label="Network adapters">
<form [formGroup]="networkAdaptersForm">
<mat-form-field class="form-field">
<input
matInput
type="number"
[(ngModel)]="dockerTemplate.adapters"
formControlName="adapters"
placeholder="Adapters"/>
</mat-form-field>
</form>
</mat-step>
<mat-step label="Start command">
<mat-form-field class="form-field">
<input
matInput
type="text"
[(ngModel)]="dockerTemplate.start_command"
placeholder="Start command"/>
</mat-form-field>
</mat-step>
<mat-step label="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>
</mat-form-field>
</mat-step>
</mat-vertical-stepper>
</div>
<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,111 @@
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
import { MatInputModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, MatSelectModule, MatFormFieldModule, MatAutocompleteModule, MatTableModule } from '@angular/material';
import { CommonModule } from '@angular/common';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
import { ActivatedRoute } from '@angular/router';
import { of } from 'rxjs';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { MockedServerService } from '../../../../services/server.service.spec';
import { ServerService } from '../../../../services/server.service';
import { Server } from '../../../../models/server';
import { ToasterService } from '../../../../services/toaster.service';
import { TemplateMocksService } from '../../../../services/template-mocks.service';
import { MockedToasterService } from '../../../../services/toaster.service.spec';
import { MockedActivatedRoute } from '../../preferences.component.spec';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { DockerTemplate } from '../../../../models/templates/docker-template';
import { AddDockerTemplateComponent } from './add-docker-template.component';
import { DockerService } from '../../../../services/docker.service';
import { DockerConfigurationService } from '../../../../services/docker-configuration.service';
export class MockedDockerService {
public addTemplate(server: Server, dockerTemplate: DockerTemplate) {
return of(dockerTemplate);
}
}
describe('AddDockerTemplateComponent', () => {
let component: AddDockerTemplateComponent;
let fixture: ComponentFixture<AddDockerTemplateComponent>;
let mockedServerService = new MockedServerService;
let mockedDockerService = new MockedDockerService;
let mockedToasterService = new MockedToasterService;
let activatedRoute = new MockedActivatedRoute().get();
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [ FormsModule, MatTableModule, MatAutocompleteModule, MatFormFieldModule, MatInputModule, ReactiveFormsModule, MatSelectModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
providers: [
{
provide: ActivatedRoute, useValue: activatedRoute
},
{ provide: ServerService, useValue: mockedServerService },
{ provide: DockerService, useValue: mockedDockerService },
{ provide: ToasterService, useValue: mockedToasterService},
{ provide: TemplateMocksService, useClass: TemplateMocksService },
{ provide: DockerConfigurationService, useClass: DockerConfigurationService }
],
declarations: [
AddDockerTemplateComponent
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AddDockerTemplateComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should call add template', () => {
spyOn(mockedDockerService, 'addTemplate').and.returnValue(of({} as DockerTemplate));
component.virtualMachineForm.controls['filename'].setValue('sample name');
component.containerNameForm.controls['templateName'].setValue('template name');
component.networkAdaptersForm.controls['adapters'].setValue(1);
component.server = {id: 1} as Server;
component.addTemplate();
expect(mockedDockerService.addTemplate).toHaveBeenCalled();
});
it('should not call add template when file name is missing', () => {
spyOn(mockedDockerService, 'addTemplate').and.returnValue(of({} as DockerTemplate));
component.containerNameForm.controls['templateName'].setValue('template name');
component.networkAdaptersForm.controls['adapters'].setValue(1);
component.server = {id: 1} as Server;
component.addTemplate();
expect(mockedDockerService.addTemplate).not.toHaveBeenCalled();
});
it('should not call add template when template name is missing', () => {
spyOn(mockedDockerService, 'addTemplate').and.returnValue(of({} as DockerTemplate));
component.virtualMachineForm.controls['filename'].setValue('sample name');
component.networkAdaptersForm.controls['adapters'].setValue(1);
component.server = {id: 1} as Server;
component.addTemplate();
expect(mockedDockerService.addTemplate).not.toHaveBeenCalled();
});
it('should not call add template when adapters field is empty', () => {
spyOn(mockedDockerService, 'addTemplate').and.returnValue(of({} as DockerTemplate));
component.virtualMachineForm.controls['filename'].setValue('sample name');
component.containerNameForm.controls['templateName'].setValue('template name');
component.server = {id: 1} as Server;
component.addTemplate();
expect(mockedDockerService.addTemplate).not.toHaveBeenCalled();
});
});

View File

@ -0,0 +1,98 @@
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 { FormGroup, FormBuilder, FormControl, Validators } from '@angular/forms';
import { v4 as uuid } from 'uuid';
import { TemplateMocksService } from '../../../../services/template-mocks.service';
import { DockerTemplate } from '../../../../models/templates/docker-template';
import { DockerService } from '../../../../services/docker.service';
import { DockerConfigurationService } from '../../../../services/docker-configuration.service';
import { DockerImage } from '../../../../models/docker/docker-image';
@Component({
selector: 'app-add-docker-template',
templateUrl: './add-docker-template.component.html',
styleUrls: ['./add-docker-template.component.scss', '../../preferences.component.scss']
})
export class AddDockerTemplateComponent implements OnInit {
server: Server;
dockerTemplate: DockerTemplate;
consoleTypes: string[] = [];
isGns3VmChosen: boolean = false;
isRemoteComputerChosen: boolean = false;
dockerImages: DockerImage[] = [];
newImageSelected: boolean = false;
virtualMachineForm: FormGroup;
containerNameForm: FormGroup;
networkAdaptersForm: FormGroup;
constructor(
private route: ActivatedRoute,
private serverService: ServerService,
private dockerService: DockerService,
private toasterService: ToasterService,
private router: Router,
private formBuilder: FormBuilder,
private templateMocksService: TemplateMocksService,
private configurationService: DockerConfigurationService
) {
this.dockerTemplate = new DockerTemplate();
this.virtualMachineForm = this.formBuilder.group({
filename: new FormControl('', Validators.required)
});
this.containerNameForm = this.formBuilder.group({
templateName: new FormControl('', Validators.required)
});
this.networkAdaptersForm = this.formBuilder.group({
adapters: new FormControl('', 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;
this.consoleTypes = this.configurationService.getConsoleTypes();
this.templateMocksService.getDockerTemplate().subscribe((dockerTemplate: DockerTemplate) => {
this.dockerTemplate = dockerTemplate;
})
});
}
setServerType(serverType: string) {
if (serverType === 'gns3 vm') {
this.isGns3VmChosen = true;
} else {
this.isRemoteComputerChosen = true;
}
}
setDiskImage(value: string) {
this.newImageSelected = value === "newImage";
}
goBack() {
this.router.navigate(['/server', this.server.id, 'preferences', 'docker', 'templates']);
}
addTemplate() {
if (!this.virtualMachineForm.invalid && !this.containerNameForm.invalid && !this.networkAdaptersForm.invalid) {
this.dockerTemplate.template_id = uuid();
this.dockerService.addTemplate(this.server, this.dockerTemplate).subscribe((template: DockerTemplate) => {
this.goBack();
});
} else {
this.toasterService.error(`Fill all required fields`);
}
}
}

View File

@ -0,0 +1,24 @@
<div class="content">
<div class="default-header">
<div class="row">
<h1 class="col">Copy Docker container template</h1>
</div>
</div>
<div class="default-content">
<div class="container mat-elevation-z8">
<form [formGroup]="templateNameForm">
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="templateName"
placeholder="Name"
formControlName="templateName"/>
</mat-form-field>
</form>
</div>
<div class="buttons-bar">
<button mat-button class="cancel-button" (click)="goBack()">Cancel</button>
<button mat-raised-button color="primary" (click)="addTemplate()">Copy template</button>
</div>
</div>
</div>

View File

@ -0,0 +1,66 @@
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 { DockerTemplate } from '../../../../models/templates/docker-template';
import { DockerService } from '../../../../services/docker.service';
import { FormGroup, FormBuilder, FormControl, Validators } from '@angular/forms';
@Component({
selector: 'app-copy-docker-template',
templateUrl: './copy-docker-template.component.html',
styleUrls: ['./copy-docker-template.component.scss', '../../preferences.component.scss']
})
export class CopyDockerTemplateComponent implements OnInit {
server: Server;
templateName: string = '';
dockerTemplate: DockerTemplate;
templateNameForm: FormGroup;
constructor(
private route: ActivatedRoute,
private serverService: ServerService,
private dockerService: DockerService,
private toasterService: ToasterService,
private router: Router,
private formBuilder: FormBuilder
) {
this.templateNameForm = this.formBuilder.group({
templateName: 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.dockerService.getTemplate(this.server, template_id).subscribe((dockerTemplate: DockerTemplate) => {
this.dockerTemplate = dockerTemplate;
this.templateName = `Copy of ${this.dockerTemplate.name}`;
})
});
}
goBack() {
this.router.navigate(['/server', this.server.id, 'preferences', 'docker', 'templates']);
}
addTemplate() {
if (!this.templateNameForm.invalid) {
this.dockerTemplate.template_id = uuid();
this.dockerTemplate.name = this.templateName;
this.dockerService.addTemplate(this.server, this.dockerTemplate).subscribe((template: DockerTemplate) => {
this.goBack();
});
} else {
this.toasterService.error(`Fill all required fields`);
}
}
}

View File

@ -0,0 +1,96 @@
<div class="content" [ngClass]="{ shadowed: isSymbolSelectionOpened}">
<div class="default-header">
<div class="row">
<h1 class="col">Docker image configuration</h1>
</div>
</div>
<div class="default-content" *ngIf="dockerTemplate">
<mat-accordion>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
General settings
</mat-panel-title>
</mat-expansion-panel-header>
<form [formGroup]="generalSettingsForm">
<mat-form-field class="form-field">
<input formControlName="templateName" matInput type="text" [(ngModel)]="dockerTemplate.name" placeholder="Template name">
</mat-form-field>
<mat-form-field class="form-field">
<input formControlName="defaultName" matInput type="text" [(ngModel)]="dockerTemplate.default_name_format" placeholder="Default name format">
</mat-form-field>
<mat-form-field class="form-field">
<mat-select [ngModelOptions]="{standalone: true}" placeholder="Category" [(ngModel)]="dockerTemplate.category">
<mat-option *ngFor="let category of categories" [value]="category[1]">
{{category[0]}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="form-field">
<input formControlName="symbol" matInput type="text" [(ngModel)]="dockerTemplate.symbol" placeholder="Symbol">
</mat-form-field>
<button mat-button class="symbolSelectionButton" (click)="chooseSymbol()">Choose symbol</button><br/><br/>
<mat-form-field class="form-field">
<input matInput type="text" [ngModelOptions]="{standalone: true}" [(ngModel)]="dockerTemplate.start_command" placeholder="Start command">
</mat-form-field>
<mat-form-field class="form-field">
<input formControlName="adapter" matInput type="number" [(ngModel)]="dockerTemplate.adapters" placeholder="Adapters">
</mat-form-field>
<mat-form-field class="select">
<mat-select [ngModelOptions]="{standalone: true}" placeholder="Console type" [(ngModel)]="dockerTemplate.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)]="dockerTemplate.console_auto_start">
Auto start console
</mat-checkbox>
<mat-form-field class="select">
<mat-select [ngModelOptions]="{standalone: true}" placeholder="VNC console resolution" [(ngModel)]="dockerTemplate.console_resolution">
<mat-option *ngFor="let resolution of consoleResolutions" [value]="resolution">
{{resolution}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="form-field">
<input [ngModelOptions]="{standalone: true}" matInput type="number" [(ngModel)]="dockerTemplate.console_http_port" placeholder="HTTP port in the container">
</mat-form-field>
<mat-form-field class="form-field">
<input [ngModelOptions]="{standalone: true}" matInput type="text" [(ngModel)]="dockerTemplate.console_http_path" placeholder="HTTP path">
</mat-form-field>
</form>
<h6>Environment</h6>
<mat-form-field class="form-field">
<textarea matInput type="text" [(ngModel)]="dockerTemplate.environment"></textarea>
</mat-form-field>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
Advanced
</mat-panel-title>
</mat-expansion-panel-header>
<h6>Extra hosts</h6>
<mat-form-field class="form-field">
<textarea matInput type="text" [(ngModel)]="dockerTemplate.extra_hosts"></textarea>
</mat-form-field>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
Usage
</mat-panel-title>
</mat-expansion-panel-header>
<mat-form-field class="form-field">
<textarea matInput type="text" [(ngModel)]="dockerTemplate.usage"></textarea>
</mat-form-field>
</mat-expansion-panel>
</mat-accordion>
<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 && dockerTemplate" [server]="server" [symbol]="dockerTemplate.symbol" (symbolChangedEmitter)="symbolChanged($event)"></app-symbols-menu>

View File

@ -0,0 +1,80 @@
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
import { MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule } from '@angular/material';
import { CommonModule } from '@angular/common';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
import { ActivatedRoute } from '@angular/router';
import { of } from 'rxjs';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { MockedServerService } from '../../../../services/server.service.spec';
import { ServerService } from '../../../../services/server.service';
import { Server } from '../../../../models/server';
import { MockedToasterService } from '../../../../services/toaster.service.spec';
import { ToasterService } from '../../../../services/toaster.service';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MockedActivatedRoute } from '../../preferences.component.spec';
import { DockerTemplate } from '../../../../models/templates/docker-template';
import { DockerTemplateDetailsComponent } from './docker-template-details.component';
import { DockerService } from '../../../../services/docker.service';
import { DockerConfigurationService } from '../../../../services/docker-configuration.service';
export class MockedDockerService {
public getTemplate(server: Server, template_id: string) {
return of({} as DockerTemplate);
}
public saveTemplate(server: Server, dockerTemplate: DockerTemplate) {
return of(dockerTemplate);
}
}
describe('DockerTemplateDetailsComponent', () => {
let component: DockerTemplateDetailsComponent;
let fixture: ComponentFixture<DockerTemplateDetailsComponent>;
let mockedServerService = new MockedServerService;
let mockedDockerService = new MockedDockerService;
let mockedToasterService = new MockedToasterService;
let activatedRoute = new MockedActivatedRoute().get();
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [FormsModule, ReactiveFormsModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
providers: [
{
provide: ActivatedRoute, useValue: activatedRoute
},
{ provide: ServerService, useValue: mockedServerService },
{ provide: DockerService, useValue: mockedDockerService },
{ provide: ToasterService, useValue: mockedToasterService },
{ provide: DockerConfigurationService, useClass: DockerConfigurationService }
],
declarations: [
DockerTemplateDetailsComponent
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(DockerTemplateDetailsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should call save template', () => {
spyOn(mockedDockerService, 'saveTemplate').and.returnValue(of({} as DockerTemplate));
component.generalSettingsForm.controls['templateName'].setValue('template name');
component.generalSettingsForm.controls['defaultName'].setValue('default name');
component.generalSettingsForm.controls['adapter'].setValue(1);
component.generalSettingsForm.controls['symbol'].setValue('symbol path');
component.onSave();
expect(mockedDockerService.saveTemplate).toHaveBeenCalled();
});
});

View File

@ -0,0 +1,89 @@
import { Component, OnInit } from "@angular/core";
import { ActivatedRoute, ParamMap, Router } from '@angular/router';
import { ServerService } from '../../../../services/server.service';
import { Server } from '../../../../models/server';
import { ToasterService } from '../../../../services/toaster.service';
import { CustomAdapter } from '../../../../models/qemu/qemu-custom-adapter';
import { DockerTemplate } from '../../../../models/templates/docker-template';
import { DockerService } from '../../../../services/docker.service';
import { DockerConfigurationService } from '../../../../services/docker-configuration.service';
import { FormGroup, FormBuilder, FormControl, Validators } from '@angular/forms';
@Component({
selector: 'app-docker-template-details',
templateUrl: './docker-template-details.component.html',
styleUrls: ['./docker-template-details.component.scss', '../../preferences.component.scss']
})
export class DockerTemplateDetailsComponent implements OnInit {
server: Server;
dockerTemplate: DockerTemplate;
isSymbolSelectionOpened: boolean = false;
consoleTypes: string[] = [];
consoleResolutions: string[] = [];
categories = [];
adapters: CustomAdapter[] = [];
displayedColumns: string[] = ['adapter_number', 'port_name'];
generalSettingsForm: FormGroup;
constructor(
private route: ActivatedRoute,
private serverService: ServerService,
private dockerService: DockerService,
private toasterService: ToasterService,
private configurationService: DockerConfigurationService,
private formBuilder: FormBuilder,
private router: Router
){
this.generalSettingsForm = this.formBuilder.group({
templateName: new FormControl('', Validators.required),
defaultName: new FormControl('', Validators.required),
adapter: 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.getConfiguration();
this.dockerService.getTemplate(this.server, template_id).subscribe((dockerTemplate: DockerTemplate) => {
this.dockerTemplate = dockerTemplate;
});
});
}
getConfiguration(){
this.consoleTypes = this.configurationService.getConsoleTypes();
this.categories = this.configurationService.getCategories();
this.consoleResolutions = this.configurationService.getConsoleResolutions();
}
goBack() {
this.router.navigate(['/server', this.server.id, 'preferences', 'docker', 'templates']);
}
onSave(){
if (this.generalSettingsForm.invalid) {
this.toasterService.error(`Fill all required fields`);
} else {
this.dockerService.saveTemplate(this.server, this.dockerTemplate).subscribe((savedTemplate: DockerTemplate) => {
this.toasterService.success("Changes saved");
});
}
}
chooseSymbol() {
this.isSymbolSelectionOpened = !this.isSymbolSelectionOpened;
}
symbolChanged(chosenSymbol: string) {
this.dockerTemplate.symbol = chosenSymbol;
}
}

View File

@ -0,0 +1,35 @@
<div class="content">
<div class="default-header">
<div class="row">
<h1 class="col">Docker container 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/docker/addtemplate" mat-raised-button color="primary">Add Docker container template</button>
</div>
</div>
<app-empty-templates-list *ngIf="!dockerTemplates.length"></app-empty-templates-list>
<div class="default-content" *ngIf="dockerTemplates.length">
<div class="listcontainer mat-elevation-z8">
<mat-nav-list *ngIf="server">
<div class="list-item" *ngFor='let template of dockerTemplates'>
<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>
<button mat-menu-item (click)="copyTemplate(template)">
<mat-icon>content_copy</mat-icon><span>Copy</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,55 @@
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
import { MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule } from '@angular/material';
import { CommonModule } from '@angular/common';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
import { ActivatedRoute } from '@angular/router';
import { of } from 'rxjs';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { MockedServerService } from '../../../../services/server.service.spec';
import { ServerService } from '../../../../services/server.service';
import { Server } from '../../../../models/server';
import { MockedActivatedRoute } from '../../preferences.component.spec';
import { DockerTemplate } from '../../../../models/templates/docker-template';
import { DockerTemplatesComponent } from './docker-templates.component';
import { DockerService } from '../../../../services/docker.service';
export class MockedDockerService {
public getTemplates(server: Server) {
return of([{} as DockerTemplate]);
}
}
describe('DockerTemplatesComponent', () => {
let component: DockerTemplatesComponent;
let fixture: ComponentFixture<DockerTemplatesComponent>;
let mockedServerService = new MockedServerService;
let mockedDockerService = new MockedDockerService;
let activatedRoute = new MockedActivatedRoute().get();
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
providers: [
{ provide: ActivatedRoute, useValue: activatedRoute },
{ provide: ServerService, useValue: mockedServerService },
{ provide: DockerService, useValue: mockedDockerService }
],
declarations: [
DockerTemplatesComponent
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(DockerTemplatesComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,52 @@
import { Component, OnInit, ViewChild } from "@angular/core";
import { Server } from '../../../../models/server';
import { ActivatedRoute, Router } from '@angular/router';
import { ServerService } from '../../../../services/server.service';
import { DeleteTemplateComponent } from '../../common/delete-template-component/delete-template.component';
import { DockerTemplate } from '../../../../models/templates/docker-template';
import { DockerService } from '../../../../services/docker.service';
@Component({
selector: 'app-docker-templates',
templateUrl: './docker-templates.component.html',
styleUrls: ['./docker-templates.component.scss', '../../preferences.component.scss']
})
export class DockerTemplatesComponent implements OnInit {
server: Server;
dockerTemplates: DockerTemplate[] = [];
@ViewChild(DeleteTemplateComponent) deleteComponent: DeleteTemplateComponent;
constructor(
private route: ActivatedRoute,
private serverService: ServerService,
private dockerService: DockerService,
private router: Router
) {}
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.dockerService.getTemplates(this.server).subscribe((dockerTemplates: DockerTemplate[]) => {
this.dockerTemplates = dockerTemplates.filter((elem) => elem.template_type === 'docker' && !elem.builtin);
});
}
deleteTemplate(template: DockerTemplate) {
this.deleteComponent.deleteItem(template.name, template.template_id);
}
onDeleteEvent() {
this.getTemplates();
}
copyTemplate(template: DockerTemplate) {
this.router.navigate(['/server', this.server.id, 'preferences', 'docker', 'templates', template.template_id, 'copy']);
}
}

View File

@ -0,0 +1,132 @@
<div class="content">
<div class="default-header">
<div class="row">
<h1 class="col">New IOS router</h1>
</div>
</div>
<div class="default-content" *ngIf="iosTemplate">
<div class="container mat-elevation-z8">
<mat-vertical-stepper [linear]="true">
<mat-step label="IOS image">
<form [formGroup]="iosImageForm">
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="iosTemplate.image"
formControlName="imageName"
placeholder="IOS image"/>
</mat-form-field>
</form>
</mat-step>
<mat-step label="Name and platform">
<form [formGroup]="iosNameForm">
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="iosTemplate.name"
formControlName="templateName"
placeholder="Name"/>
</mat-form-field>
<mat-form-field class="form-field">
<mat-select
placeholder="Platform"
(selectionChange)="onPlatformChosen($event)"
formControlName="platform"
[(ngModel)]="iosTemplate.platform">
<mat-option *ngFor="let platform of platforms" [value]="platform">
{{platform}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="form-field" *ngIf="chassis[iosTemplate.platform]">
<mat-select
placeholder="Chassis"
(selectionChange)="onChassisChosen($event)"
formControlName="chassis"
[(ngModel)]="iosTemplate.chassis">
<mat-option *ngFor="let chassis of chassis[iosTemplate.platform]" [value]="chassis">
{{chassis}}
</mat-option>
</mat-select>
</mat-form-field>
</form>
<mat-checkbox
*ngIf="platformsWithEtherSwitchRouterOption[iosTemplate.platform]"
[(ngModel)]="isEtherSwitchRouter">
This is an EtherSwitch router
</mat-checkbox>
</mat-step>
<mat-step label="Memory">
<form [formGroup]="iosMemoryForm">
<mat-form-field class="form-field">
<input
matInput type="number"
[(ngModel)]="iosTemplate.ram"
formControlName="memory"
value="defaultRam[iosTemplate.platform]"
placeholder="Default RAM"/>
</mat-form-field>
<mat-label>
<a href="{{ciscoUrl}}">Check for minimum and maximum RAM requirement</a>
</mat-label>
</form>
</mat-step>
<mat-step label="Network adapters">
<div *ngIf="iosTemplate.chassis && chassis[iosTemplate.platform]">
<div *ngFor="let index of [0,1,2,3,4,5,6,7]">
<mat-select
placeholder="Slot {{index}}"
[(ngModel)]="networkAdaptersForTemplate[index]"
[ngModelOptions]="{standalone: true}"
*ngIf="networkAdapters[iosTemplate.chassis][index]">
<mat-option *ngFor="let option of networkAdapters[iosTemplate.chassis][index]" [value]="option">
{{option}}
</mat-option>
</mat-select>
</div>
</div>
<div *ngIf="iosTemplate.platform && !chassis[iosTemplate.platform]">
<div *ngFor="let index of [0,1,2,3,4,5,6,7]">
<mat-select
placeholder="Slot {{index}}"
[(ngModel)]="networkAdaptersForTemplate[index]"
[ngModelOptions]="{standalone: true}"
*ngIf="networkAdaptersForPlatform[iosTemplate.platform][index]">
<mat-option *ngFor="let option of networkAdaptersForPlatform[iosTemplate.platform][index]" [value]="option">
{{option}}
</mat-option>
</mat-select>
</div>
</div>
</mat-step>
<mat-step label="WIC modules">
<div *ngIf="iosTemplate.platform && networkModules[iosTemplate.platform]">
<div *ngFor="let index of [0,1,2,3]">
<mat-select
placeholder="WIC {{index}}"
[(ngModel)]="networkModulesForTemplate[index]"
[ngModelOptions]="{standalone: true}"
*ngIf="networkModules[iosTemplate.platform][index]">
<mat-option *ngFor="let option of networkModules[iosTemplate.platform][index]" [value]="option">
{{option}}
</mat-option>
</mat-select>
</div>
</div>
</mat-step>
<mat-step label="Idle-PC">
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="iosTemplate.idlepc"
placeholder="Idle-PC"/>
</mat-form-field>
</mat-step>
</mat-vertical-stepper>
</div>
<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,120 @@
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
import { MatInputModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, MatSelectModule, MatFormFieldModule, MatAutocompleteModule, MatTableModule } from '@angular/material';
import { CommonModule } from '@angular/common';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
import { ActivatedRoute } from '@angular/router';
import { of } from 'rxjs';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { MockedServerService } from '../../../../services/server.service.spec';
import { ServerService } from '../../../../services/server.service';
import { Server } from '../../../../models/server';
import { ToasterService } from '../../../../services/toaster.service';
import { TemplateMocksService } from '../../../../services/template-mocks.service';
import { MockedToasterService } from '../../../../services/toaster.service.spec';
import { MockedActivatedRoute } from '../../preferences.component.spec';
import { IosTemplate } from '../../../../models/templates/ios-template';
import { AddIosTemplateComponent } from './add-ios-template.component';
import { IosService } from '../../../../services/ios.service';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { IosConfigurationService } from '../../../../services/ios-configuration.service';
export class MockedIosService {
public addTemplate(server: Server, iosTemplate: IosTemplate) {
return of(iosTemplate);
}
}
describe('AddIosTemplateComponent', () => {
let component: AddIosTemplateComponent;
let fixture: ComponentFixture<AddIosTemplateComponent>;
let mockedServerService = new MockedServerService;
let mockedIosService = new MockedIosService;
let mockedToasterService = new MockedToasterService;
let activatedRoute = new MockedActivatedRoute().get();
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [ FormsModule, MatTableModule, MatAutocompleteModule, MatFormFieldModule, MatInputModule, ReactiveFormsModule, MatSelectModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
providers: [
{
provide: ActivatedRoute, useValue: activatedRoute
},
{ provide: ServerService, useValue: mockedServerService },
{ provide: IosService, useValue: mockedIosService },
{ provide: ToasterService, useValue: mockedToasterService},
{ provide: TemplateMocksService, useClass: TemplateMocksService },
{ provide: IosConfigurationService, useClass: IosConfigurationService }
],
declarations: [
AddIosTemplateComponent
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AddIosTemplateComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should call add template', () => {
spyOn(mockedIosService, 'addTemplate').and.returnValue(of({} as IosTemplate));
component.iosImageForm.controls['imageName'].setValue('image name');
component.iosNameForm.controls['templateName'].setValue('template name');
component.iosNameForm.controls['platform'].setValue('platform');
component.iosNameForm.controls['chassis'].setValue('chassis');
component.iosMemoryForm.controls['memory'].setValue(0);
component.server = {id: 1} as Server;
component.addTemplate();
expect(mockedIosService.addTemplate).toHaveBeenCalled();
});
it('should not call add template when template name is not defined', () => {
spyOn(mockedIosService, 'addTemplate').and.returnValue(of({} as IosTemplate));
component.iosImageForm.controls['imageName'].setValue('image name');
component.iosNameForm.controls['templateName'].setValue('');
component.iosNameForm.controls['platform'].setValue('platform');
component.iosNameForm.controls['chassis'].setValue('chassis');
component.iosMemoryForm.controls['memory'].setValue(0);
component.server = {id: 1} as Server;
component.addTemplate();
expect(mockedIosService.addTemplate).not.toHaveBeenCalled();
});
it('should not call add template when image name is not defined', () => {
spyOn(mockedIosService, 'addTemplate').and.returnValue(of({} as IosTemplate));
component.iosNameForm.controls['templateName'].setValue('template name');
component.iosNameForm.controls['platform'].setValue('platform');
component.iosNameForm.controls['chassis'].setValue('chassis');
component.iosMemoryForm.controls['memory'].setValue(0);
component.server = {id: 1} as Server;
component.addTemplate();
expect(mockedIosService.addTemplate).not.toHaveBeenCalled();
});
it('should not call add template when memory is not defined', () => {
spyOn(mockedIosService, 'addTemplate').and.returnValue(of({} as IosTemplate));
component.iosImageForm.controls['imageName'].setValue('image name');
component.iosNameForm.controls['templateName'].setValue('template name');
component.iosNameForm.controls['platform'].setValue('platform');
component.iosNameForm.controls['chassis'].setValue('chassis');
component.server = {id: 1} as Server;
component.addTemplate();
expect(mockedIosService.addTemplate).not.toHaveBeenCalled();
});
});

View File

@ -0,0 +1,160 @@
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 { IosTemplate } from '../../../../models/templates/ios-template';
import { IosService } from '../../../../services/ios.service';
import { v4 as uuid } from 'uuid';
import { TemplateMocksService } from '../../../../services/template-mocks.service';
import { IosConfigurationService } from '../../../../services/ios-configuration.service';
@Component({
selector: 'app-add-ios-template',
templateUrl: './add-ios-template.component.html',
styleUrls: ['./add-ios-template.component.scss', '../../preferences.component.scss']
})
export class AddIosTemplateComponent implements OnInit {
server: Server;
iosTemplate: IosTemplate;
isEtherSwitchRouter: boolean = false;
iosImageForm: FormGroup;
iosNameForm: FormGroup;
iosMemoryForm: FormGroup;
networkAdaptersForTemplate: string[] = [];
networkModulesForTemplate: string[] = [];
platforms: string[] = [];
platformsWithEtherSwitchRouterOption = {};
platformsWithChassis = {};
chassis = {};
defaultRam = {};
defaultNvram = {};
networkAdapters = {};
networkAdaptersForPlatform = {};
networkModules = {};
ciscoUrl: string = "https://cfn.cloudapps.cisco.com/ITDIT/CFN/jsp/SearchBySoftware.jsp";
constructor(
private route: ActivatedRoute,
private serverService: ServerService,
private iosService: IosService,
private toasterService: ToasterService,
private formBuilder: FormBuilder,
private router: Router,
private templateMocksService: TemplateMocksService,
private iosConfigurationService: IosConfigurationService
) {
this.iosTemplate = new IosTemplate();
this.iosImageForm = this.formBuilder.group({
imageName: new FormControl(null, [Validators.required])
});
this.iosNameForm = this.formBuilder.group({
templateName: new FormControl(null, [Validators.required]),
platform: new FormControl(null, [Validators.required]),
chassis: new FormControl(null, [Validators.required])
});
this.iosMemoryForm = this.formBuilder.group({
memory: 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;
this.templateMocksService.getIosTemplate().subscribe((iosTemplate: IosTemplate) => {
this.iosTemplate = iosTemplate;
this.networkModules = this.iosConfigurationService.getNetworkModules();
this.networkAdaptersForPlatform = this.iosConfigurationService.getNetworkAdaptersForPlatform();
this.networkAdapters = this.iosConfigurationService.getNetworkAdapters();
this.platforms = this.iosConfigurationService.getAvailablePlatforms();
this.platformsWithEtherSwitchRouterOption = this.iosConfigurationService.getPlatformsWithEtherSwitchRouterOption();
this.platformsWithChassis = this.iosConfigurationService.getPlatformsWithChassis();
this.chassis = this.iosConfigurationService.getChassis();
this.defaultRam = this.iosConfigurationService.getDefaultRamSettings();
});
});
}
addTemplate() {
if (!this.iosImageForm.invalid && !this.iosNameForm.invalid && !this.iosMemoryForm.invalid) {
this.iosTemplate.template_id = uuid();
if (this.isEtherSwitchRouter) {
this.iosTemplate.symbol = ":/symbols/multilayer_switch.svg";
this.iosTemplate.category = "switch";
}
if (this.networkAdaptersForTemplate.length>0) this.completeAdaptersData();
if (this.networkModulesForTemplate.length>0) this.completeModulesData();
this.iosService.addTemplate(this.server, this.iosTemplate).subscribe((template: IosTemplate) => {
this.goBack();
});
} else {
this.toasterService.error(`Fill all required fields`);
}
}
completeAdaptersData() {
if (this.chassis[this.iosTemplate.platform]) {
if(Object.keys(this.networkAdapters[this.iosTemplate.platform])){
for(let i=0; i<Object.keys(this.networkAdapters[this.iosTemplate.platform]).length; i++){
if(!this.networkAdaptersForTemplate[i]) this.networkAdaptersForTemplate[i] = '';
}
}
} else {
if(Object.keys(this.networkAdaptersForPlatform[this.iosTemplate.platform])){
for(let i=0; i<Object.keys(this.networkAdaptersForPlatform[this.iosTemplate.platform]).length; i++){
if(!this.networkAdaptersForTemplate[i]) this.networkAdaptersForTemplate[i] = '';
}
}
}
if (this.networkAdaptersForTemplate[0]) this.iosTemplate.slot0 = this.networkAdaptersForTemplate[0];
if (this.networkAdaptersForTemplate[1]) this.iosTemplate.slot1 = this.networkAdaptersForTemplate[1];
if (this.networkAdaptersForTemplate[2]) this.iosTemplate.slot2 = this.networkAdaptersForTemplate[2];
if (this.networkAdaptersForTemplate[3]) this.iosTemplate.slot3 = this.networkAdaptersForTemplate[3];
if (this.networkAdaptersForTemplate[4]) this.iosTemplate.slot4 = this.networkAdaptersForTemplate[4];
if (this.networkAdaptersForTemplate[5]) this.iosTemplate.slot5 = this.networkAdaptersForTemplate[5];
if (this.networkAdaptersForTemplate[6]) this.iosTemplate.slot6 = this.networkAdaptersForTemplate[6];
if (this.networkAdaptersForTemplate[7]) this.iosTemplate.slot7 = this.networkAdaptersForTemplate[7];
}
completeModulesData() {
if(Object.keys(this.networkModules[this.iosTemplate.platform])){
for(let i=0; i<Object.keys(this.networkModules[this.iosTemplate.platform]).length; i++){
if(!this.networkModulesForTemplate[i]) this.networkModulesForTemplate[i] = '';
}
}
if (this.networkModulesForTemplate[0]) this.iosTemplate.wic0 = this.networkModulesForTemplate[0];
if (this.networkModulesForTemplate[1]) this.iosTemplate.wic1 = this.networkModulesForTemplate[1];
if (this.networkModulesForTemplate[2]) this.iosTemplate.wic2 = this.networkModulesForTemplate[2];
}
goBack() {
this.router.navigate(['/server', this.server.id, 'preferences', 'dynamips', 'templates']);
}
onPlatformChosen() {
this.iosTemplate.chassis = '';
this.networkAdaptersForTemplate = [];
this.networkModulesForTemplate = [];
}
onChassisChosen() {
this.networkAdaptersForTemplate = [];
}
}

View File

@ -0,0 +1,24 @@
<div class="content">
<div class="default-header">
<div class="row">
<h1 class="col">Copy IOS router template</h1>
</div>
</div>
<div class="default-content">
<mat-card class="matCard">
<form [formGroup]="formGroup">
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="templateName"
placeholder="Name"
formControlName="templateName"/>
</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()">Copy template</button>
</div>
</div>
</div>

View File

@ -0,0 +1,66 @@
import { Component, OnInit } from "@angular/core";
import { Server } from '../../../../models/server';
import { ActivatedRoute, ParamMap, Router } from '@angular/router';
import { ServerService } from '../../../../services/server.service';
import { ToasterService } from '../../../../services/toaster.service';
import { v4 as uuid } from 'uuid';
import { IosTemplate } from '../../../../models/templates/ios-template';
import { IosService } from '../../../../services/ios.service';
import { FormGroup, FormBuilder, FormControl, Validators } from '@angular/forms';
@Component({
selector: 'app-copy-ios-template',
templateUrl: './copy-ios-template.component.html',
styleUrls: ['./copy-ios-template.component.scss', '../../preferences.component.scss']
})
export class CopyIosTemplateComponent implements OnInit {
server: Server;
templateName: string = '';
iosTemplate: IosTemplate;
formGroup: FormGroup;
constructor(
private route: ActivatedRoute,
private serverService: ServerService,
private iosService: IosService,
private toasterService: ToasterService,
private router: Router,
private formBuilder: FormBuilder
) {
this.formGroup = this.formBuilder.group({
templateName: 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.iosService.getTemplate(this.server, template_id).subscribe((iosTemplate: IosTemplate) => {
this.iosTemplate = iosTemplate;
this.templateName = `Copy of ${this.iosTemplate.name}`;
})
});
}
goBack() {
this.router.navigate(['/server', this.server.id, 'preferences', 'dynamips', 'templates']);
}
addTemplate() {
if (!this.formGroup.invalid) {
this.iosTemplate.template_id = uuid();
this.iosTemplate.name = this.templateName;
this.iosService.addTemplate(this.server, this.iosTemplate).subscribe((template: IosTemplate) => {
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">Dynamips preferences</h1>
</div>
</div>
<div class="default-content">
<mat-form-field class="form-field">
<input matInput type="text" [(ngModel)]="dynamipsPath" placeholder="Path to Dynamips"/>
</mat-form-field>
</div>
</div>

View File

@ -0,0 +1,8 @@
.top-button {
height: 36px;
margin-top: 22px
}
.form-field {
width: 100%;
}

View File

@ -0,0 +1,61 @@
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
import { MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule } from '@angular/material';
import { CommonModule } from '@angular/common';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
import { ActivatedRoute } from '@angular/router';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { MockedServerService } from '../../../../services/server.service.spec';
import { ServerService } from '../../../../services/server.service';
import { MockedActivatedRoute } from '../../preferences.component.spec';
import { ServerSettingsService } from '../../../../services/server-settings.service';
import { HttpClientModule } from '@angular/common/http';
import { MockedServerSettingsService } from '../../../../services/server-settings.service.spec';
import { MockedToasterService } from '../../../../services/toaster.service.spec';
import { ToasterService } from '../../../../services/toaster.service';
import { DynamipsPreferencesComponent } from './dynamips-preferences.component';
describe('DynamipsPreferencesComponent', () => {
let component: DynamipsPreferencesComponent;
let fixture: ComponentFixture<DynamipsPreferencesComponent>;
let mockedServerService = new MockedServerService;
let activatedRoute = new MockedActivatedRoute().get();
let mockedServerSettingsService = new MockedServerSettingsService();
let mockedToasterService = new MockedToasterService();
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [HttpClientModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
providers: [
{
provide: ActivatedRoute, useValue: activatedRoute
},
{ provide: ServerService, useValue: mockedServerService },
{ provide: ServerSettingsService, useValue: mockedServerSettingsService },
{ provide: ToasterService, useValue: mockedToasterService }
],
declarations: [
DynamipsPreferencesComponent
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(DynamipsPreferencesComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should clear path when restore defaults called', () => {
component.dynamipsPath = 'Non empty';
component.restoreDefaults();
expect(component.dynamipsPath).toBe('');
});
});

View File

@ -0,0 +1,33 @@
import { Component, OnInit } from "@angular/core";
import { ServerSettingsService } from '../../../../services/server-settings.service';
import { ActivatedRoute } from '@angular/router';
import { Server } from '../../../../models/server';
import { ServerService } from '../../../../services/server.service';
@Component({
selector: 'app-dynamips-preferences',
templateUrl: './dynamips-preferences.component.html',
styleUrls: ['./dynamips-preferences.component.scss']
})
export class DynamipsPreferencesComponent implements OnInit {
server: Server;
dynamipsPath: string;
constructor(
private route: ActivatedRoute,
private serverService: ServerService,
private serverSettingsService: ServerSettingsService
) {}
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.dynamipsPath = '';
}
}

View File

@ -0,0 +1,198 @@
<div class="content" [ngClass]="{ shadowed: isSymbolSelectionOpened }">
<div class="default-header">
<div class="row">
<h1 class="col">Dynamips IOS Router configuration</h1>
</div>
</div>
<div class="default-content" *ngIf="iosTemplate">
<mat-accordion>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
General settings
</mat-panel-title>
</mat-expansion-panel-header>
<form [formGroup]="generalSettingsForm">
<mat-form-field class="form-field">
<input matInput type="text" formControlName="templateName" [(ngModel)]="iosTemplate.name" placeholder="Template name">
</mat-form-field>
<mat-form-field class="form-field">
<input matInput type="text" formControlName="defaultName" [(ngModel)]="iosTemplate.default_name_format" placeholder="Default name format">
</mat-form-field>
<mat-label>Platform - {{iosTemplate.platform}}</mat-label><br/><br/>
<mat-label>Chassis - {{iosTemplate.chassis}}</mat-label><br/><br/>
<mat-form-field class="form-field">
<input matInput type="text" formControlName="symbol" [(ngModel)]="iosTemplate.symbol" placeholder="Symbol">
</mat-form-field>
<button mat-button class="symbolSelectionButton" (click)="chooseSymbol()">Choose symbol</button><br/><br/>
<mat-form-field class="form-field">
<mat-select [ngModelOptions]="{standalone: true}" placeholder="Category" [(ngModel)]="iosTemplate.category">
<mat-option *ngFor="let category of categories" [value]="category[1]">
{{category[0]}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="form-field">
<input matInput type="text" formControlName="path" [(ngModel)]="iosTemplate.image" placeholder="IOS image path">
</mat-form-field>
<mat-form-field class="form-field">
<input matInput type="text" formControlName="initialConfig" [(ngModel)]="iosTemplate.startup_config" placeholder="Initial startup-config">
</mat-form-field>
<mat-form-field class="form-field">
<input [ngModelOptions]="{standalone: true}" matInput type="text" [(ngModel)]="iosTemplate.private_config" placeholder="Initial private-config">
</mat-form-field>
</form>
<mat-form-field class="select">
<mat-select placeholder="Console type" [(ngModel)]="iosTemplate.console_type">
<mat-option *ngFor="let type of consoleTypes" [value]="type">
{{type}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-checkbox [(ngModel)]="iosTemplate.console_auto_start">
Auto start console
</mat-checkbox>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
Memories and disks
</mat-panel-title>
</mat-expansion-panel-header>
<form [formGroup]="memoryForm">
<mat-form-field class="form-field">
<input matInput type="number" formControlName="ram" [(ngModel)]="iosTemplate.ram" placeholder="RAM size">
</mat-form-field>
<mat-form-field class="form-field">
<input matInput type="number" formControlName="nvram" [(ngModel)]="iosTemplate.nvram" placeholder="NVRAM size">
</mat-form-field>
<mat-form-field class="form-field">
<input matInput type="number" formControlName="iomemory" [(ngModel)]="iosTemplate.iomem" placeholder="I/O memory">
</mat-form-field>
<mat-form-field class="form-field">
<input matInput type="number" formControlName="disk0" [(ngModel)]="iosTemplate.disk0" placeholder="PCMCIA disk0">
</mat-form-field>
<mat-form-field class="form-field">
<input matInput type="number" formControlName="disk1" [(ngModel)]="iosTemplate.disk1" placeholder="PCMCIA disk1">
</mat-form-field>
</form>
<mat-checkbox [(ngModel)]="iosTemplate.auto_delete_disks">
Automatically delete NVRAM and disk files
</mat-checkbox>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
Slots
</mat-panel-title>
</mat-expansion-panel-header>
<h6>Adapters</h6>
<div *ngIf="iosTemplate.chassis && chassis[iosTemplate.platform]">
<div *ngFor="let index of [0,1,2,3,4,5,6,7]">
<mat-select
placeholder="Slot {{index}}"
[(ngModel)]="networkAdaptersForTemplate[index]"
[ngModelOptions]="{standalone: true}"
*ngIf="networkAdapters[iosTemplate.chassis][index]">
<mat-option *ngFor="let option of networkAdapters[iosTemplate.chassis][index]" [value]="option">
{{option}}
</mat-option>
</mat-select>
</div>
</div>
<div *ngIf="iosTemplate.platform && !chassis[iosTemplate.platform]">
<div *ngFor="let index of [0,1,2,3,4,5,6,7]">
<mat-select
placeholder="Slot {{index}}"
[(ngModel)]="networkAdaptersForTemplate[index]"
[ngModelOptions]="{standalone: true}"
*ngIf="networkAdaptersForPlatform[iosTemplate.platform][index]">
<mat-option *ngFor="let option of networkAdaptersForPlatform[iosTemplate.platform][index]" [value]="option">
{{option}}
</mat-option>
</mat-select>
</div>
</div><br/><br/>
<h6>WICs</h6>
<div *ngIf="iosTemplate.wic0 || iosTemplate.wic0===''">
<mat-select
placeholder="WIC 0"
[(ngModel)]="iosTemplate.wic0"
[ngModelOptions]="{standalone: true}">
<mat-option *ngFor="let option of networkModules[iosTemplate.platform][0]" [value]="option">
{{option}}
</mat-option>
</mat-select>
</div>
<div *ngIf="iosTemplate.wic1 || iosTemplate.wic1===''">
<mat-select
placeholder="WIC 1"
[(ngModel)]="iosTemplate.wic1"
[ngModelOptions]="{standalone: true}">
<mat-option *ngFor="let option of networkModules[iosTemplate.platform][1]" [value]="option">
{{option}}
</mat-option>
</mat-select>
</div>
<div *ngIf="iosTemplate.wic2 || iosTemplate.wic2===''">
<mat-select
placeholder="WIC 2"
[(ngModel)]="iosTemplate.wic2"
[ngModelOptions]="{standalone: true}">
<mat-option *ngFor="let option of networkModules[iosTemplate.platform][2]" [value]="option">
{{option}}
</mat-option>
</mat-select>
</div>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
Advanced
</mat-panel-title>
</mat-expansion-panel-header>
<form [formGroup]="advancedForm">
<mat-form-field class="form-field">
<input matInput formControlName="systemId" type="text" [(ngModel)]="iosTemplate.system_id" placeholder="System ID">
</mat-form-field>
<mat-form-field class="form-field">
<input [ngModelOptions]="{standalone: true}" matInput type="text" [(ngModel)]="iosTemplate.mac_addr" placeholder="Base MAC">
</mat-form-field>
<mat-form-field class="form-field">
<input [ngModelOptions]="{standalone: true}" matInput type="text" [(ngModel)]="iosTemplate.idlepc" placeholder="Idle-PC">
</mat-form-field>
<mat-form-field class="form-field">
<input matInput formControlName="idlemax" type="number" [(ngModel)]="iosTemplate.idlemax" placeholder="Idlemax">
</mat-form-field>
<mat-form-field class="form-field">
<input matInput formControlName="idlesleep" type="number" [(ngModel)]="iosTemplate.idlesleep" placeholder="Idlesleep">
</mat-form-field>
<mat-form-field class="form-field">
<input matInput formControlName="execarea" type="number" [(ngModel)]="iosTemplate.exec_area" placeholder="Exec area">
</mat-form-field>
</form>
<mat-checkbox [(ngModel)]="iosTemplate.mmap">
Enable mmap support
</mat-checkbox><br/><br/>
<mat-checkbox [(ngModel)]="iosTemplate.sparsemem">
Enable sparse memory supoport
</mat-checkbox>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
Usage
</mat-panel-title>
</mat-expansion-panel-header>
<mat-form-field class="form-field">
<textarea matInput type="text" [(ngModel)]="iosTemplate.usage"></textarea>
</mat-form-field>
</mat-expansion-panel>
</mat-accordion>
<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 && iosTemplate" [server]="server" [symbol]="iosTemplate.symbol" (symbolChangedEmitter)="symbolChanged($event)"></app-symbols-menu>

View File

@ -0,0 +1,90 @@
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
import { MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule } from '@angular/material';
import { CommonModule } from '@angular/common';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
import { ActivatedRoute } from '@angular/router';
import { of } from 'rxjs';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { MockedServerService } from '../../../../services/server.service.spec';
import { ServerService } from '../../../../services/server.service';
import { Server } from '../../../../models/server';
import { MockedToasterService } from '../../../../services/toaster.service.spec';
import { ToasterService } from '../../../../services/toaster.service';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MockedActivatedRoute } from '../../preferences.component.spec';
import { IosTemplate } from '../../../../models/templates/ios-template';
import { IosTemplateDetailsComponent } from './ios-template-details.component';
import { IosService } from '../../../../services/ios.service';
import { IosConfigurationService } from '../../../../services/ios-configuration.service';
export class MockedIosService {
public getTemplate(server: Server, template_id: string) {
return of({} as IosTemplate);
}
public saveTemplate(server: Server, iosTemplate: IosTemplate) {
return of(iosTemplate);
}
}
describe('IosTemplateDetailsComponent', () => {
let component: IosTemplateDetailsComponent;
let fixture: ComponentFixture<IosTemplateDetailsComponent>;
let mockedServerService = new MockedServerService;
let mockedIosService = new MockedIosService;
let mockedToasterService = new MockedToasterService;
let activatedRoute = new MockedActivatedRoute().get();
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [FormsModule, ReactiveFormsModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
providers: [
{
provide: ActivatedRoute, useValue: activatedRoute
},
{ provide: ServerService, useValue: mockedServerService },
{ provide: IosService, useValue: mockedIosService },
{ provide: ToasterService, useValue: mockedToasterService },
{ provide: IosConfigurationService, useClass: IosConfigurationService }
],
declarations: [
IosTemplateDetailsComponent
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(IosTemplateDetailsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should call save template', () => {
spyOn(mockedIosService, 'saveTemplate').and.returnValue(of({} as IosTemplate));
component.generalSettingsForm.controls['templateName'].setValue('template name');
component.generalSettingsForm.controls['defaultName'].setValue('default name');
component.generalSettingsForm.controls['symbol'].setValue('symbol');
component.generalSettingsForm.controls['path'].setValue('path');
component.generalSettingsForm.controls['initialConfig'].setValue('txt');
component.memoryForm.controls['ram'].setValue('0');
component.memoryForm.controls['nvram'].setValue('0');
component.memoryForm.controls['iomemory'].setValue('0');
component.memoryForm.controls['disk0'].setValue('0');
component.memoryForm.controls['disk1'].setValue('0');
component.advancedForm.controls['systemId'].setValue('0');
component.advancedForm.controls['idlemax'].setValue('0');
component.advancedForm.controls['idlesleep'].setValue('0');
component.advancedForm.controls['execarea'].setValue('0');
component.onSave();
expect(mockedIosService.saveTemplate).toHaveBeenCalled();
});
});

View File

@ -0,0 +1,145 @@
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 { IosTemplate } from '../../../../models/templates/ios-template';
import { IosService } from '../../../../services/ios.service';
import { IosConfigurationService } from '../../../../services/ios-configuration.service';
@Component({
selector: 'app-ios-template-details',
templateUrl: './ios-template-details.component.html',
styleUrls: ['./ios-template-details.component.scss', '../../preferences.component.scss']
})
export class IosTemplateDetailsComponent implements OnInit {
server: Server;
iosTemplate: IosTemplate;
isSymbolSelectionOpened: boolean = false;
networkAdaptersForTemplate: string[] = [];
platforms: string[] = [];
consoleTypes: string[] = [];
platformsWithEtherSwitchRouterOption = {};
platformsWithChassis = {};
chassis = {};
defaultRam = {};
defaultNvram = {};
networkAdapters = {};
networkAdaptersForPlatform = {};
networkModules = {};
generalSettingsForm: FormGroup;
memoryForm: FormGroup;
advancedForm: FormGroup;
constructor(
private route: ActivatedRoute,
private serverService: ServerService,
private iosService: IosService,
private toasterService: ToasterService,
private formBuilder: FormBuilder,
private iosConfigurationService: IosConfigurationService,
private router: Router
) {
this.generalSettingsForm = this.formBuilder.group({
templateName: new FormControl('', Validators.required),
defaultName: new FormControl('', Validators.required),
symbol: new FormControl('', Validators.required),
path: new FormControl('', Validators.required),
initialConfig: new FormControl('', Validators.required)
});
this.memoryForm = this.formBuilder.group({
ram: new FormControl('', Validators.required),
nvram: new FormControl('', Validators.required),
iomemory: new FormControl('', Validators.required),
disk0: new FormControl('', Validators.required),
disk1: new FormControl('', Validators.required),
});
this.advancedForm = this.formBuilder.group({
systemId: new FormControl('', Validators.required),
idlemax: new FormControl('', Validators.required),
idlesleep: new FormControl('', Validators.required),
execarea: 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.getConfiguration();
this.iosService.getTemplate(this.server, template_id).subscribe((iosTemplate: IosTemplate) => {
this.iosTemplate = iosTemplate;
this.fillAdaptersData();
});
});
}
getConfiguration() {
this.networkModules = this.iosConfigurationService.getNetworkModules();
this.networkAdaptersForPlatform = this.iosConfigurationService.getNetworkAdaptersForPlatform();
this.networkAdapters = this.iosConfigurationService.getNetworkAdapters();
this.platforms = this.iosConfigurationService.getAvailablePlatforms();
this.platformsWithEtherSwitchRouterOption = this.iosConfigurationService.getPlatformsWithEtherSwitchRouterOption();
this.platformsWithChassis = this.iosConfigurationService.getPlatformsWithChassis();
this.chassis = this.iosConfigurationService.getChassis();
this.defaultRam = this.iosConfigurationService.getDefaultRamSettings();
this.consoleTypes = this.iosConfigurationService.getConsoleTypes();
}
fillAdaptersData() {
if (this.iosTemplate.slot0) this.networkAdaptersForTemplate[0] = this.iosTemplate.slot0;
if (this.iosTemplate.slot1) this.networkAdaptersForTemplate[1] = this.iosTemplate.slot1;
if (this.iosTemplate.slot2) this.networkAdaptersForTemplate[2] = this.iosTemplate.slot2;
if (this.iosTemplate.slot3) this.networkAdaptersForTemplate[3] = this.iosTemplate.slot3;
if (this.iosTemplate.slot4) this.networkAdaptersForTemplate[4] = this.iosTemplate.slot4;
if (this.iosTemplate.slot5) this.networkAdaptersForTemplate[5] = this.iosTemplate.slot5;
if (this.iosTemplate.slot6) this.networkAdaptersForTemplate[6] = this.iosTemplate.slot6;
if (this.iosTemplate.slot7) this.networkAdaptersForTemplate[7] = this.iosTemplate.slot7;
}
completeAdaptersData() {
if (this.networkAdaptersForTemplate[0]) this.iosTemplate.slot0 = this.networkAdaptersForTemplate[0];
if (this.networkAdaptersForTemplate[1]) this.iosTemplate.slot1 = this.networkAdaptersForTemplate[1];
if (this.networkAdaptersForTemplate[2]) this.iosTemplate.slot2 = this.networkAdaptersForTemplate[2];
if (this.networkAdaptersForTemplate[3]) this.iosTemplate.slot3 = this.networkAdaptersForTemplate[3];
if (this.networkAdaptersForTemplate[4]) this.iosTemplate.slot4 = this.networkAdaptersForTemplate[4];
if (this.networkAdaptersForTemplate[5]) this.iosTemplate.slot5 = this.networkAdaptersForTemplate[5];
if (this.networkAdaptersForTemplate[6]) this.iosTemplate.slot6 = this.networkAdaptersForTemplate[6];
if (this.networkAdaptersForTemplate[7]) this.iosTemplate.slot7 = this.networkAdaptersForTemplate[7];
}
onSave() {
if (this.generalSettingsForm.invalid || this.memoryForm.invalid || this.advancedForm.invalid) {
this.toasterService.error(`Fill all required fields`);
} else {
this.completeAdaptersData();
this.iosService.saveTemplate(this.server, this.iosTemplate).subscribe((iosTemplate: IosTemplate) => {
this.toasterService.success("Changes saved");
});
}
}
goBack() {
this.router.navigate(['/server', this.server.id, 'preferences', 'dynamips', 'templates']);
}
chooseSymbol() {
this.isSymbolSelectionOpened = !this.isSymbolSelectionOpened;
}
symbolChanged(chosenSymbol: string) {
this.isSymbolSelectionOpened = !this.isSymbolSelectionOpened;
this.iosTemplate.symbol = chosenSymbol;
}
}

View File

@ -0,0 +1,35 @@
<div class="content">
<div class="default-header">
<div class="row">
<h1 class="col">IOS routers 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>
</div>
<app-empty-templates-list *ngIf="!iosTemplates.length"></app-empty-templates-list>
<div class="default-content" *ngIf="iosTemplates.length">
<div class="listcontainer mat-elevation-z8">
<mat-nav-list *ngIf="server">
<div class="list-item" *ngFor='let template of iosTemplates'>
<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>
<button mat-menu-item (click)="copyTemplate(template)">
<mat-icon>content_copy</mat-icon><span>Copy</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,57 @@
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
import { MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule } from '@angular/material';
import { CommonModule } from '@angular/common';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
import { ActivatedRoute } from '@angular/router';
import { of } from 'rxjs';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { MockedServerService } from '../../../../services/server.service.spec';
import { ServerService } from '../../../../services/server.service';
import { Server } from '../../../../models/server';
import { MockedActivatedRoute } from '../../preferences.component.spec';
import { IosTemplate } from '../../../../models/templates/ios-template';
import { IosTemplatesComponent } from './ios-templates.component';
import { IosService } from '../../../../services/ios.service';
export class MockedIosService {
public getTemplates(server: Server) {
return of([{} as IosTemplate]);
}
}
describe('IosTemplatesComponent', () => {
let component: IosTemplatesComponent;
let fixture: ComponentFixture<IosTemplatesComponent>;
let mockedServerService = new MockedServerService;
let mockedIosService = new MockedIosService;
let activatedRoute = new MockedActivatedRoute().get();
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
providers: [
{
provide: ActivatedRoute, useValue: activatedRoute
},
{ provide: ServerService, useValue: mockedServerService },
{ provide: IosService, useValue: mockedIosService }
],
declarations: [
IosTemplatesComponent
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(IosTemplatesComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,53 @@
import { Component, OnInit, ViewChild } from "@angular/core";
import { Server } from '../../../../models/server';
import { ActivatedRoute, Router } from '@angular/router';
import { ServerService } from '../../../../services/server.service';
import { IosService } from '../../../../services/ios.service';
import { IosTemplate } from '../../../../models/templates/ios-template';
import { DeleteTemplateComponent } from '../../common/delete-template-component/delete-template.component';
import { VpcsTemplate } from '../../../../models/templates/vpcs-template';
@Component({
selector: 'app-ios-templates',
templateUrl: './ios-templates.component.html',
styleUrls: ['./ios-templates.component.scss', '../../preferences.component.scss']
})
export class IosTemplatesComponent implements OnInit {
server: Server;
iosTemplates: IosTemplate[] = [];
@ViewChild(DeleteTemplateComponent) deleteComponent: DeleteTemplateComponent;
constructor(
private route: ActivatedRoute,
private serverService: ServerService,
private iosService: IosService,
private router: Router
) {}
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.iosService.getTemplates(this.server).subscribe((templates: IosTemplate[]) => {
this.iosTemplates = templates.filter((elem) => elem.template_type === 'dynamips' && !elem.builtin);
});
}
deleteTemplate(template: VpcsTemplate) {
this.deleteComponent.deleteItem(template.name, template.template_id);
}
onDeleteEvent() {
this.getTemplates();
}
copyTemplate(template: IosTemplate) {
this.router.navigate(['/server', this.server.id, 'preferences', 'dynamips', 'templates', template.template_id, 'copy']);
}
}

Some files were not shown because too many files have changed in this diff Show More