mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2025-04-24 13:05:48 +00:00
Merge branch 'master' into custom-html-adbutler-ads
This commit is contained in:
commit
d756ce9b87
@ -14,10 +14,11 @@ init:
|
||||
- git config --global core.autocrlf input
|
||||
|
||||
install:
|
||||
- ps: Install-Product node 11 x64
|
||||
- ps: Install-Product node 12 x64
|
||||
- yarn install
|
||||
|
||||
build_script:
|
||||
- cmd: set NODE_OPTIONS=--max-old-space-size=8092
|
||||
- yarn buildforelectron
|
||||
- "%PYTHON%\\python.exe -m pip install -r scripts\\requirements.txt"
|
||||
- "%PYTHON%\\python.exe scripts\\build.py download -a"
|
||||
|
60
package.json
60
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gns3-web-ui",
|
||||
"version": "2019.2.0-alpha.6dev",
|
||||
"version": "2019.2.0-alpha.8dev",
|
||||
"author": {
|
||||
"name": "GNS3 Technology Inc.",
|
||||
"email": "developers@gns3.com"
|
||||
@ -38,25 +38,26 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^8.1.2",
|
||||
"@angular/cdk": "^8.1.1",
|
||||
"@angular/common": "^8.1.2",
|
||||
"@angular/compiler": "^8.1.2",
|
||||
"@angular/core": "^8.1.2",
|
||||
"@angular/forms": "^8.1.2",
|
||||
"@angular/animations": "^8.2.8",
|
||||
"@angular/cdk": "^8.2.1",
|
||||
"@angular/common": "^8.2.8",
|
||||
"@angular/compiler": "^8.2.8",
|
||||
"@angular/core": "^8.2.8",
|
||||
"@angular/forms": "^8.2.8",
|
||||
"@angular/http": "^7.2.15",
|
||||
"@angular/material": "^8.1.1",
|
||||
"@angular/platform-browser": "^8.1.2",
|
||||
"@angular/platform-browser-dynamic": "^8.1.2",
|
||||
"@angular/router": "^8.1.2",
|
||||
"@angular/material": "^8.2.1",
|
||||
"@angular/platform-browser": "^8.2.8",
|
||||
"@angular/platform-browser-dynamic": "^8.2.8",
|
||||
"@angular/router": "^8.2.8",
|
||||
"angular-persistence": "^1.0.1",
|
||||
"angular2-hotkeys": "^2.1.4",
|
||||
"angular2-hotkeys": "^2.1.5",
|
||||
"angular2-indexeddb": "^1.2.3",
|
||||
"bootstrap": "4.3.1",
|
||||
"command-exists": "^1.2.8",
|
||||
"core-js": "^3.1.4",
|
||||
"css-tree": "^1.0.0-alpha.33",
|
||||
"core-js": "^3.2.1",
|
||||
"css-tree": "^1.0.0-alpha.34",
|
||||
"d3-ng2-service": "^2.1.0",
|
||||
"file-saver": "^2.0.2",
|
||||
"hammerjs": "^2.0.8",
|
||||
"ini": "^1.3.5",
|
||||
"material-design-icons": "^3.0.1",
|
||||
@ -65,24 +66,27 @@
|
||||
"node-fetch": "^2.6.0",
|
||||
"notosans-fontface": "^1.1.0",
|
||||
"raven-js": "^3.27.2",
|
||||
"rxjs": "^6.5.2",
|
||||
"rxjs-compat": "^6.5.2",
|
||||
"rxjs": "^6.5.3",
|
||||
"rxjs-compat": "^6.5.3",
|
||||
"save-svg-as-png": "^1.4.14",
|
||||
"svg-crowbar": "^0.2.3",
|
||||
"tree-kill": "^1.2.1",
|
||||
"typeface-roboto": "^0.0.75",
|
||||
"xterm": "^3.14.5",
|
||||
"yargs": "^13.3.0",
|
||||
"zone.js": "^0.9.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~0.801.2",
|
||||
"@angular/cli": "^8.1.2",
|
||||
"@angular/compiler-cli": "^8.1.2",
|
||||
"@angular/language-service": "^8.1.2",
|
||||
"@sentry/cli": "^1.47.0",
|
||||
"@sentry/electron": "^0.17.3",
|
||||
"@types/jasmine": "~3.3.15",
|
||||
"@types/jasminewd2": "~2.0.6",
|
||||
"@types/node": "~12.6.8",
|
||||
"codelyzer": "~5.1.0",
|
||||
"@angular-devkit/build-angular": "^0.801.3",
|
||||
"@angular/cli": "^8.3.6",
|
||||
"@angular/compiler-cli": "^8.2.8",
|
||||
"@angular/language-service": "^8.2.8",
|
||||
"@sentry/cli": "^1.47.2",
|
||||
"@sentry/electron": "^0.17.4",
|
||||
"@types/jasmine": "^3.3.16",
|
||||
"@types/jasminewd2": "^2.0.7",
|
||||
"@types/node": "^12.6.9",
|
||||
"codelyzer": "^5.1.2",
|
||||
"electron": "5.0.8",
|
||||
"electron-builder": "21.1.5",
|
||||
"jasmine-core": "~3.4.0",
|
||||
@ -99,8 +103,8 @@
|
||||
"popper.js": "^1.15.0",
|
||||
"prettier": "^1.18.2",
|
||||
"protractor": "~5.4.2",
|
||||
"replace": "^1.1.0",
|
||||
"ts-mockito": "^2.4.2",
|
||||
"replace": "^1.1.1",
|
||||
"ts-mockito": "^2.5.0",
|
||||
"ts-node": "~8.3.0",
|
||||
"tslint": "~5.18.0",
|
||||
"tslint-config-prettier": "^1.18.0",
|
||||
|
@ -3,14 +3,17 @@ GNS3 WebUI is web implementation of user interface for GNS3 software.
|
||||
Current version: 2019.2.0
|
||||
|
||||
What's New
|
||||
- Help section added with information about third party components
|
||||
- Showing progress when server starting
|
||||
- Possibility to edit interface & node labels by using context menu
|
||||
- Enhancements in moving elements on map
|
||||
- Context menu extended with option to duplicate
|
||||
- Main menu extended with option to lock all items on map
|
||||
- Editing interface labels on double click
|
||||
- Support for keyboard shortcuts
|
||||
- Menu extended with option to delete currently opened project, export & import project
|
||||
- Possibility to save current state of project
|
||||
- Ability to duplicate project from projects page
|
||||
- Node information dialog available from context menu
|
||||
- Topology summary widget on map view
|
||||
- Improvements in dialog styles
|
||||
|
||||
Bug Fixes
|
||||
- Removing issues with positioning interface labels while adding link between nodes on map
|
||||
- Removing issues with opening console
|
||||
- Context menu now is correctly placed
|
||||
- Entered text in text & style editor is now validated
|
||||
- Text validation in dialogs
|
||||
- Removing errors with creating WebSockets
|
||||
|
@ -53,6 +53,10 @@ import { CopyIouTemplateComponent } from './components/preferences/ios-on-unix/c
|
||||
import { ListOfSnapshotsComponent } from './components/snapshots/list-of-snapshots/list-of-snapshots.component';
|
||||
import { ConsoleComponent } from './components/settings/console/console.component';
|
||||
import { HelpComponent } from './components/help/help.component';
|
||||
import { TracengPreferencesComponent } from './components/preferences/traceng/traceng-preferences/traceng-preferences.component';
|
||||
import { TracengTemplatesComponent } from './components/preferences/traceng/traceng-templates/traceng-templates.component';
|
||||
import { AddTracengTemplateComponent } from './components/preferences/traceng/add-traceng/add-traceng-template.component';
|
||||
import { TracengTemplateDetailsComponent } from './components/preferences/traceng/traceng-template-details/traceng-template-details.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@ -111,6 +115,11 @@ const routes: Routes = [
|
||||
{ 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/traceng', component: TracengPreferencesComponent },
|
||||
// { path: 'server/:server_id/preferences/traceng/templates', component: TracengTemplatesComponent },
|
||||
// { path: 'server/:server_id/preferences/traceng/templates/:template_id', component: TracengTemplateDetailsComponent },
|
||||
// { path: 'server/:server_id/preferences/traceng/addtemplate', component: AddTracengTemplateComponent },
|
||||
|
||||
{ path: 'server/:server_id/preferences/docker/templates', component: DockerTemplatesComponent },
|
||||
{ path: 'server/:server_id/preferences/docker/templates/:template_id', component: DockerTemplateDetailsComponent },
|
||||
{ path: 'server/:server_id/preferences/docker/templates/:template_id/copy', component: CopyDockerTemplateComponent },
|
||||
|
@ -0,0 +1,3 @@
|
||||
mat-menu-panel {
|
||||
min-height: 0px;
|
||||
}
|
@ -189,9 +189,59 @@ import { NotificationBoxComponent } from './components/notification-box/notifica
|
||||
import { NonNegativeValidator } from './validators/non-negative-validator';
|
||||
import { RotationValidator } from './validators/rotation-validator';
|
||||
import { DuplicateActionComponent } from './components/project-map/context-menu/actions/duplicate-action/duplicate-action.component';
|
||||
import { MapSettingService } from './services/mapsettings.service';
|
||||
import { MapSettingsService } from './services/mapsettings.service';
|
||||
import { ProjectMapMenuComponent } from './components/project-map/project-map-menu/project-map-menu.component';
|
||||
import { HelpComponent } from './components/help/help.component';
|
||||
import { ConfigEditorDialogComponent } from './components/project-map/node-editors/config-editor/config-editor.component';
|
||||
import { EditConfigActionComponent } from './components/project-map/context-menu/actions/edit-config/edit-config-action.component';
|
||||
import { LogConsoleComponent } from './components/project-map/log-console/log-console.component';
|
||||
import { LogEventsDataSource } from './components/project-map/log-console/log-events-datasource';
|
||||
import { SaveProjectDialogComponent } from './components/projects/save-project-dialog/save-project-dialog.component';
|
||||
import { TopologySummaryComponent } from './components/topology-summary/topology-summary.component';
|
||||
import { ShowNodeActionComponent } from './components/project-map/context-menu/actions/show-node-action/show-node-action.component';
|
||||
import { InfoDialogComponent } from './components/project-map/info-dialog/info-dialog.component';
|
||||
import { InfoService } from './services/info.service';
|
||||
import { BringToFrontActionComponent } from './components/project-map/context-menu/actions/bring-to-front-action/bring-to-front-action.component';
|
||||
import { ExportConfigActionComponent } from './components/project-map/context-menu/actions/export-config/export-config-action.component';
|
||||
import { ImportConfigActionComponent } from './components/project-map/context-menu/actions/import-config/import-config-action.component';
|
||||
import { ConsoleDeviceActionBrowserComponent } from './components/project-map/context-menu/actions/console-device-action-browser/console-device-action-browser.component';
|
||||
import { ChangeSymbolDialogComponent } from './components/project-map/change-symbol-dialog/change-symbol-dialog.component';
|
||||
import { ChangeSymbolActionComponent } from './components/project-map/context-menu/actions/change-symbol/change-symbol-action.component';
|
||||
import { EditProjectDialogComponent } from './components/projects/edit-project-dialog/edit-project-dialog.component';
|
||||
import { ProjectsFilter } from './filters/projectsFilter.pipe';
|
||||
import { ComputeService } from './services/compute.service';
|
||||
import { ReloadNodeActionComponent } from './components/project-map/context-menu/actions/reload-node-action/reload-node-action.component';
|
||||
import { SuspendNodeActionComponent } from './components/project-map/context-menu/actions/suspend-node-action/suspend-node-action.component';
|
||||
import { ConfigActionComponent } from './components/project-map/context-menu/actions/config-action/config-action.component';
|
||||
import { ConfiguratorDialogVpcsComponent } from './components/project-map/node-editors/configurator/vpcs/configurator-vpcs.component';
|
||||
import { ConfiguratorDialogEthernetHubComponent } from './components/project-map/node-editors/configurator/ethernet_hub/configurator-ethernet-hub.component';
|
||||
import { ConfiguratorDialogEthernetSwitchComponent } from './components/project-map/node-editors/configurator/ethernet-switch/configurator-ethernet-switch.component';
|
||||
import { PortsComponent } from './components/preferences/common/ports/ports.component';
|
||||
import { ConfiguratorDialogSwitchComponent } from './components/project-map/node-editors/configurator/switch/configurator-switch.component';
|
||||
import { ConfiguratorDialogVirtualBoxComponent } from './components/project-map/node-editors/configurator/virtualbox/configurator-virtualbox.component';
|
||||
import { CustomAdaptersTableComponent } from './components/preferences/common/custom-adapters-table/custom-adapters-table.component';
|
||||
import { ConfiguratorDialogQemuComponent } from './components/project-map/node-editors/configurator/qemu/configurator-qemu.component';
|
||||
import { ConfiguratorDialogCloudComponent } from './components/project-map/node-editors/configurator/cloud/configurator-cloud.component';
|
||||
import { UdpTunnelsComponent } from './components/preferences/common/udp-tunnels/udp-tunnels.component';
|
||||
import { ConfiguratorDialogAtmSwitchComponent } from './components/project-map/node-editors/configurator/atm_switch/configurator-atm-switch.component';
|
||||
import { ConfiguratorDialogVmwareComponent } from './components/project-map/node-editors/configurator/vmware/configurator-vmware.component';
|
||||
import { ConfiguratorDialogIouComponent } from './components/project-map/node-editors/configurator/iou/configurator-iou.component';
|
||||
import { ConfiguratorDialogIosComponent } from './components/project-map/node-editors/configurator/ios/configurator-ios.component';
|
||||
import { ConfiguratorDialogDockerComponent } from './components/project-map/node-editors/configurator/docker/configurator-docker.component';
|
||||
import { ConfiguratorDialogNatComponent } from './components/project-map/node-editors/configurator/nat/configurator-nat.component';
|
||||
import { ConfiguratorDialogTracengComponent } from './components/project-map/node-editors/configurator/traceng/configurator-traceng.component';
|
||||
import { AddTracengTemplateComponent } from './components/preferences/traceng/add-traceng/add-traceng-template.component';
|
||||
import { TracengPreferencesComponent } from './components/preferences/traceng/traceng-preferences/traceng-preferences.component';
|
||||
import { TracengTemplatesComponent } from './components/preferences/traceng/traceng-templates/traceng-templates.component';
|
||||
import { TracengService } from './services/traceng.service';
|
||||
import { TracengTemplateDetailsComponent } from './components/preferences/traceng/traceng-template-details/traceng-template-details.component';
|
||||
import { QemuImageCreatorComponent } from './components/project-map/node-editors/configurator/qemu/qemu-image-creator/qemu-image-creator.component';
|
||||
import { ChooseNameDialogComponent } from './components/projects/choose-name-dialog/choose-name-dialog.component';
|
||||
import { PacketCaptureService } from './services/packet-capture.service';
|
||||
import { StartCaptureOnStartedLinkActionComponent } from './components/project-map/context-menu/actions/start-capture-on-started-link/start-capture-on-started-link.component';
|
||||
import { LockActionComponent } from './components/project-map/context-menu/actions/lock-action/lock-action.component';
|
||||
import { NavigationDialogComponent } from './components/projects/navigation-dialog/navigation-dialog.component';
|
||||
import { ScreenshotDialogComponent } from './components/project-map/screenshot-dialog/screenshot-dialog.component';
|
||||
|
||||
if (environment.production) {
|
||||
Raven.config('https://b2b1cfd9b043491eb6b566fd8acee358@sentry.io/842726', {
|
||||
@ -305,16 +355,61 @@ if (environment.production) {
|
||||
SearchFilter,
|
||||
DateFilter,
|
||||
NameFilter,
|
||||
ProjectsFilter,
|
||||
ListOfSnapshotsComponent,
|
||||
CustomAdaptersComponent,
|
||||
NodesMenuComponent,
|
||||
AdbutlerComponent,
|
||||
ConsoleDeviceActionComponent,
|
||||
ShowNodeActionComponent,
|
||||
ConsoleComponent,
|
||||
NodesMenuComponent,
|
||||
NotificationBoxComponent,
|
||||
ProjectMapMenuComponent,
|
||||
HelpComponent
|
||||
HelpComponent,
|
||||
ConfigEditorDialogComponent,
|
||||
EditConfigActionComponent,
|
||||
LogConsoleComponent,
|
||||
SaveProjectDialogComponent,
|
||||
TopologySummaryComponent,
|
||||
InfoDialogComponent,
|
||||
BringToFrontActionComponent,
|
||||
ExportConfigActionComponent,
|
||||
ImportConfigActionComponent,
|
||||
ConsoleDeviceActionBrowserComponent,
|
||||
ChangeSymbolDialogComponent,
|
||||
ChangeSymbolActionComponent,
|
||||
EditProjectDialogComponent,
|
||||
ReloadNodeActionComponent,
|
||||
SuspendNodeActionComponent,
|
||||
ConfigActionComponent,
|
||||
ConfiguratorDialogVpcsComponent,
|
||||
ConfiguratorDialogEthernetHubComponent,
|
||||
ConfiguratorDialogEthernetSwitchComponent,
|
||||
PortsComponent,
|
||||
ConfiguratorDialogSwitchComponent,
|
||||
ConfiguratorDialogVirtualBoxComponent,
|
||||
CustomAdaptersTableComponent,
|
||||
ConfiguratorDialogQemuComponent,
|
||||
ConfiguratorDialogCloudComponent,
|
||||
UdpTunnelsComponent,
|
||||
ConfiguratorDialogAtmSwitchComponent,
|
||||
ConfiguratorDialogVmwareComponent,
|
||||
ConfiguratorDialogIouComponent,
|
||||
ConfiguratorDialogIosComponent,
|
||||
ConfiguratorDialogDockerComponent,
|
||||
ConfiguratorDialogNatComponent,
|
||||
ConfiguratorDialogTracengComponent,
|
||||
AddTracengTemplateComponent,
|
||||
TracengPreferencesComponent,
|
||||
TracengTemplatesComponent,
|
||||
TracengTemplateDetailsComponent,
|
||||
QemuImageCreatorComponent,
|
||||
ChooseNameDialogComponent,
|
||||
StartCaptureOnStartedLinkActionComponent,
|
||||
LockActionComponent,
|
||||
NavigationDialogComponent,
|
||||
ScreenshotDialogComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
@ -354,6 +449,7 @@ if (environment.production) {
|
||||
LinksDataSource,
|
||||
NodesDataSource,
|
||||
SymbolsDataSource,
|
||||
LogEventsDataSource,
|
||||
SelectionManager,
|
||||
InRectangleHelper,
|
||||
DrawingsDataSource,
|
||||
@ -390,7 +486,11 @@ if (environment.production) {
|
||||
NodeCreatedLabelStylesFixer,
|
||||
NonNegativeValidator,
|
||||
RotationValidator,
|
||||
MapSettingService
|
||||
MapSettingsService,
|
||||
InfoService,
|
||||
ComputeService,
|
||||
TracengService,
|
||||
PacketCaptureService
|
||||
],
|
||||
entryComponents: [
|
||||
AddServerDialogComponent,
|
||||
@ -406,7 +506,30 @@ if (environment.production) {
|
||||
SymbolsComponent,
|
||||
DeleteConfirmationDialogComponent,
|
||||
HelpDialogComponent,
|
||||
StartCaptureDialogComponent
|
||||
StartCaptureDialogComponent,
|
||||
ConfigEditorDialogComponent,
|
||||
SaveProjectDialogComponent,
|
||||
InfoDialogComponent,
|
||||
ChangeSymbolDialogComponent,
|
||||
EditProjectDialogComponent,
|
||||
ConfiguratorDialogVpcsComponent,
|
||||
ConfiguratorDialogEthernetHubComponent,
|
||||
ConfiguratorDialogEthernetSwitchComponent,
|
||||
ConfiguratorDialogSwitchComponent,
|
||||
ConfiguratorDialogVirtualBoxComponent,
|
||||
ConfiguratorDialogQemuComponent,
|
||||
ConfiguratorDialogCloudComponent,
|
||||
ConfiguratorDialogAtmSwitchComponent,
|
||||
ConfiguratorDialogVmwareComponent,
|
||||
ConfiguratorDialogIouComponent,
|
||||
ConfiguratorDialogIosComponent,
|
||||
ConfiguratorDialogDockerComponent,
|
||||
ConfiguratorDialogNatComponent,
|
||||
ConfiguratorDialogTracengComponent,
|
||||
QemuImageCreatorComponent,
|
||||
ChooseNameDialogComponent,
|
||||
NavigationDialogComponent,
|
||||
ScreenshotDialogComponent
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
|
@ -58,6 +58,8 @@ import { DrawingAddingComponent } from './components/drawing-adding/drawing-addi
|
||||
import { MovingEventSource } from './events/moving-event-source';
|
||||
import { MovingCanvasDirective } from './directives/moving-canvas.directive';
|
||||
import { ZoomingCanvasDirective } from './directives/zooming-canvas.directive';
|
||||
import { EthernetLinkWidget } from './widgets/links/ethernet-link';
|
||||
import { SerialLinkWidget } from './widgets/links/serial-link';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, MatMenuModule, MatIconModule],
|
||||
@ -117,6 +119,8 @@ import { ZoomingCanvasDirective } from './directives/zooming-canvas.directive';
|
||||
MapSettingsManager,
|
||||
FontBBoxCalculator,
|
||||
StylesToFontConverter,
|
||||
EthernetLinkWidget,
|
||||
SerialLinkWidget,
|
||||
...D3_MAP_IMPORTS
|
||||
],
|
||||
exports: [D3MapComponent, ExperimentalMapComponent]
|
||||
|
@ -21,7 +21,7 @@ import { MapLabel } from '../../models/map/map-label';
|
||||
import { MapLinkNode } from '../../models/map/map-link-node';
|
||||
import { select } from 'd3-selection';
|
||||
import { MapLink } from '../../models/map/map-link';
|
||||
import { MapSettingService } from '../../../services/mapsettings.service';
|
||||
import { MapSettingsService } from '../../../services/mapsettings.service';
|
||||
|
||||
describe('DraggableSelectionComponent', () => {
|
||||
let component: DraggableSelectionComponent;
|
||||
@ -123,7 +123,7 @@ describe('DraggableSelectionComponent', () => {
|
||||
{ provide: DrawingsEventSource, useValue: drawingsEventSourceStub },
|
||||
{ provide: GraphDataManager, useValue: mockedGraphDataManager },
|
||||
{ provide: LinksEventSource, useValue: linksEventSourceStub },
|
||||
{ provide: MapSettingService, useClass: MapSettingService }
|
||||
{ provide: MapSettingsService, useClass: MapSettingsService }
|
||||
],
|
||||
declarations: [DraggableSelectionComponent]
|
||||
}).compileComponents();
|
||||
|
@ -17,7 +17,7 @@ import { LabelWidget } from '../../widgets/label';
|
||||
import { InterfaceLabelWidget } from '../../widgets/interface-label';
|
||||
import { MapLinkNode } from '../../models/map/map-link-node';
|
||||
import { LinksEventSource } from '../../events/links-event-source';
|
||||
import { MapSettingService } from '../../../services/mapsettings.service';
|
||||
import { MapSettingsService } from '../../../services/mapsettings.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-draggable-selection',
|
||||
@ -44,7 +44,7 @@ export class DraggableSelectionComponent implements OnInit, OnDestroy {
|
||||
private drawingsEventSource: DrawingsEventSource,
|
||||
private graphDataManager: GraphDataManager,
|
||||
private linksEventSource: LinksEventSource,
|
||||
private mapSettingsService: MapSettingService
|
||||
private mapSettingsService: MapSettingsService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@ -94,8 +94,10 @@ export class DraggableSelectionComponent implements OnInit, OnDestroy {
|
||||
).subscribe((evt: DraggableDrag<any>) => {
|
||||
if (!this.isMapLocked) {
|
||||
const selected = this.selectionManager.getSelected();
|
||||
const selectedNodes = selected.filter(item => item instanceof MapNode);
|
||||
// update nodes
|
||||
let mapNodes = selected.filter(item => item instanceof MapNode);
|
||||
const lockedNodes = mapNodes.filter((item: MapNode) => item.locked);
|
||||
const selectedNodes = mapNodes.filter((item: MapNode) => !item.locked);
|
||||
selectedNodes.forEach((node: MapNode) => {
|
||||
node.x += evt.dx;
|
||||
node.y += evt.dy;
|
||||
@ -116,52 +118,52 @@ export class DraggableSelectionComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
|
||||
// update drawings
|
||||
selected
|
||||
.filter(item => item instanceof MapDrawing)
|
||||
.forEach((drawing: MapDrawing) => {
|
||||
drawing.x += evt.dx;
|
||||
drawing.y += evt.dy;
|
||||
this.drawingsWidget.redrawDrawing(svg, drawing);
|
||||
});
|
||||
let mapDrawings = selected.filter(item => item instanceof MapDrawing);
|
||||
const selectedDrawings = mapDrawings.filter((item: MapDrawing) => !item.locked);
|
||||
selectedDrawings.forEach((drawing: MapDrawing) => {
|
||||
drawing.x += evt.dx;
|
||||
drawing.y += evt.dy;
|
||||
this.drawingsWidget.redrawDrawing(svg, drawing);
|
||||
});
|
||||
|
||||
// update labels
|
||||
selected
|
||||
.filter(item => item instanceof MapLabel)
|
||||
.forEach((label: MapLabel) => {
|
||||
const isParentNodeSelected = selectedNodes.filter(node => node.id === label.nodeId).length > 0;
|
||||
if (isParentNodeSelected) {
|
||||
return;
|
||||
}
|
||||
let mapLabels = selected.filter(item => item instanceof MapLabel);
|
||||
const selectedLabels = mapLabels.filter((item: MapLabel) => lockedNodes.filter((node) => node.id === item.nodeId).length === 0);
|
||||
selectedLabels.forEach((label: MapLabel) => {
|
||||
const isParentNodeSelected = selectedNodes.filter(node => node.id === label.nodeId).length > 0;
|
||||
if (isParentNodeSelected) {
|
||||
return;
|
||||
}
|
||||
|
||||
const node = this.graphDataManager.getNodes().filter(node => node.id === label.nodeId)[0];
|
||||
node.label.x += evt.dx;
|
||||
node.label.y += evt.dy;
|
||||
this.labelWidget.redrawLabel(svg, label);
|
||||
});
|
||||
const node = this.graphDataManager.getNodes().filter(node => node.id === label.nodeId)[0];
|
||||
node.label.x += evt.dx;
|
||||
node.label.y += evt.dy;
|
||||
this.labelWidget.redrawLabel(svg, label);
|
||||
});
|
||||
|
||||
// update interface labels
|
||||
selected
|
||||
.filter(item => item instanceof MapLinkNode)
|
||||
.forEach((interfaceLabel: MapLinkNode) => {
|
||||
const isParentNodeSelected = selectedNodes.filter(node => node.id === interfaceLabel.nodeId).length > 0;
|
||||
if (isParentNodeSelected) {
|
||||
return;
|
||||
}
|
||||
let mapLinkNodes = selected.filter(item => item instanceof MapLinkNode);
|
||||
const selectedLinkNodes = mapLinkNodes.filter((item: MapLinkNode) => lockedNodes.filter((node) => node.id === item.nodeId).length === 0);
|
||||
selectedLinkNodes.forEach((interfaceLabel: MapLinkNode) => {
|
||||
const isParentNodeSelected = selectedNodes.filter(node => node.id === interfaceLabel.nodeId).length > 0;
|
||||
if (isParentNodeSelected) {
|
||||
return;
|
||||
}
|
||||
|
||||
const link = this.graphDataManager
|
||||
.getLinks()
|
||||
.filter(link => link.nodes[0].id === interfaceLabel.id || link.nodes[1].id === interfaceLabel.id)[0];
|
||||
if (link.nodes[0].id === interfaceLabel.id) {
|
||||
link.nodes[0].label.x += evt.dx;
|
||||
link.nodes[0].label.y += evt.dy;
|
||||
}
|
||||
if (link.nodes[1].id === interfaceLabel.id) {
|
||||
link.nodes[1].label.x += evt.dx;
|
||||
link.nodes[1].label.y += evt.dy;
|
||||
}
|
||||
const link = this.graphDataManager
|
||||
.getLinks()
|
||||
.filter(link => link.nodes[0].id === interfaceLabel.id || link.nodes[1].id === interfaceLabel.id)[0];
|
||||
if (link.nodes[0].id === interfaceLabel.id) {
|
||||
link.nodes[0].label.x += evt.dx;
|
||||
link.nodes[0].label.y += evt.dy;
|
||||
}
|
||||
if (link.nodes[1].id === interfaceLabel.id) {
|
||||
link.nodes[1].label.x += evt.dx;
|
||||
link.nodes[1].label.y += evt.dy;
|
||||
}
|
||||
|
||||
this.linksWidget.redrawLink(svg, link);
|
||||
});
|
||||
this.linksWidget.redrawLink(svg, link);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@ -173,39 +175,41 @@ export class DraggableSelectionComponent implements OnInit, OnDestroy {
|
||||
).subscribe((evt: DraggableEnd<any>) => {
|
||||
if (!this.isMapLocked) {
|
||||
const selected = this.selectionManager.getSelected();
|
||||
const selectedNodes = selected.filter(item => item instanceof MapNode);
|
||||
|
||||
let mapNodes = selected.filter(item => item instanceof MapNode);
|
||||
const lockedNodes = mapNodes.filter((item: MapNode) => item.locked);
|
||||
const selectedNodes = mapNodes.filter((item: MapNode) => !item.locked);
|
||||
selectedNodes.forEach((item: MapNode) => {
|
||||
this.nodesEventSource.dragged.emit(new DraggedDataEvent<MapNode>(item, evt.dx, evt.dy));
|
||||
});
|
||||
|
||||
selected
|
||||
.filter(item => item instanceof MapDrawing)
|
||||
.forEach((item: MapDrawing) => {
|
||||
this.drawingsEventSource.dragged.emit(new DraggedDataEvent<MapDrawing>(item, evt.dx, evt.dy));
|
||||
});
|
||||
let mapDrawings = selected.filter(item => item instanceof MapDrawing);
|
||||
const selectedDrawings = mapDrawings.filter((item: MapDrawing) => !item.locked);
|
||||
selectedDrawings.forEach((item: MapDrawing) => {
|
||||
this.drawingsEventSource.dragged.emit(new DraggedDataEvent<MapDrawing>(item, evt.dx, evt.dy));
|
||||
});
|
||||
|
||||
selected
|
||||
.filter(item => item instanceof MapLabel)
|
||||
.forEach((label: MapLabel) => {
|
||||
const isParentNodeSelected = selectedNodes.filter(node => node.id === label.nodeId).length > 0;
|
||||
if (isParentNodeSelected) {
|
||||
return;
|
||||
}
|
||||
let mapLabels = selected.filter(item => item instanceof MapLabel);
|
||||
const selectedLabels = mapLabels.filter((item: MapLabel) => lockedNodes.filter((node) => node.id === item.nodeId).length === 0);
|
||||
selectedLabels.forEach((label: MapLabel) => {
|
||||
const isParentNodeSelected = selectedNodes.filter(node => node.id === label.nodeId).length > 0;
|
||||
if (isParentNodeSelected) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.nodesEventSource.labelDragged.emit(new DraggedDataEvent<MapLabel>(label, evt.dx, evt.dy));
|
||||
});
|
||||
this.nodesEventSource.labelDragged.emit(new DraggedDataEvent<MapLabel>(label, evt.dx, evt.dy));
|
||||
});
|
||||
|
||||
selected
|
||||
.filter(item => item instanceof MapLinkNode)
|
||||
.forEach((label: MapLinkNode) => {
|
||||
const isParentNodeSelected = selectedNodes.filter(node => node.id === label.nodeId).length > 0;
|
||||
if (isParentNodeSelected) {
|
||||
return;
|
||||
}
|
||||
this.linksEventSource.interfaceDragged.emit(new DraggedDataEvent<MapLinkNode>(label, evt.dx, evt.dy));
|
||||
});
|
||||
}
|
||||
let mapLinkNodes = selected.filter(item => item instanceof MapLinkNode);
|
||||
const selectedLinkNodes = mapLinkNodes.filter((item: MapLinkNode) => lockedNodes.filter((node) => node.id === item.nodeId).length === 0)
|
||||
selectedLinkNodes.forEach((label: MapLinkNode) => {
|
||||
const isParentNodeSelected = selectedNodes.filter(node => node.id === label.nodeId).length > 0;
|
||||
if (isParentNodeSelected) {
|
||||
return;
|
||||
}
|
||||
this.linksEventSource.interfaceDragged.emit(new DraggedDataEvent<MapLinkNode>(label, evt.dx, evt.dy));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ export class DrawingToMapDrawingConverter implements Converter<Drawing, MapDrawi
|
||||
mapDrawing.projectId = drawing.project_id;
|
||||
mapDrawing.rotation = drawing.rotation;
|
||||
mapDrawing.svg = drawing.svg;
|
||||
mapDrawing.locked = drawing.locked;
|
||||
mapDrawing.x = drawing.x;
|
||||
mapDrawing.y = drawing.y;
|
||||
mapDrawing.z = drawing.z;
|
||||
|
@ -14,6 +14,7 @@ export class MapDrawingToDrawingConverter implements Converter<MapDrawing, Drawi
|
||||
drawing.project_id = mapDrawing.projectId;
|
||||
drawing.rotation = mapDrawing.rotation;
|
||||
drawing.svg = mapDrawing.svg;
|
||||
drawing.locked = mapDrawing.locked;
|
||||
drawing.x = mapDrawing.x;
|
||||
drawing.y = mapDrawing.y;
|
||||
drawing.z = mapDrawing.z;
|
||||
|
@ -20,6 +20,7 @@ export class MapNodeToNodeConverter implements Converter<MapNode, Node> {
|
||||
node.first_port_name = mapNode.firstPortName;
|
||||
node.height = mapNode.height;
|
||||
node.label = mapNode.label ? this.mapLabelToLabel.convert(mapNode.label) : undefined;
|
||||
node.locked = mapNode.locked;
|
||||
node.name = mapNode.name;
|
||||
node.node_directory = mapNode.nodeDirectory;
|
||||
node.node_type = mapNode.nodeType;
|
||||
|
@ -28,13 +28,14 @@ export class NodeToMapNodeConverter implements Converter<Node, MapNode> {
|
||||
mapNode.consoleHost = node.console_host;
|
||||
mapNode.firstPortName = node.first_port_name;
|
||||
mapNode.height = node.height;
|
||||
mapNode.label = this.labelToMapLabel.convert(node.label, { node_id: node.node_id });
|
||||
mapNode.label = this.labelToMapLabel ? this.labelToMapLabel.convert(node.label, { node_id: node.node_id }) : undefined;
|
||||
mapNode.locked = node.locked;
|
||||
mapNode.name = node.name;
|
||||
mapNode.nodeDirectory = node.node_directory;
|
||||
mapNode.nodeType = node.node_type;
|
||||
mapNode.portNameFormat = node.port_name_format;
|
||||
mapNode.portSegmentSize = node.port_segment_size;
|
||||
mapNode.ports = node.ports.map(port => this.portToMapPort.convert(port));
|
||||
mapNode.ports = node.ports ? node.ports.map(port => this.portToMapPort.convert(port)) : [];
|
||||
mapNode.projectId = node.project_id;
|
||||
mapNode.status = node.status;
|
||||
mapNode.symbol = node.symbol;
|
||||
|
@ -34,31 +34,39 @@ export class GraphDataManager {
|
||||
) {}
|
||||
|
||||
public setNodes(nodes: Node[]) {
|
||||
const mapNodes = nodes.map(n => this.nodeToMapNode.convert(n));
|
||||
this.mapNodesDataSource.set(mapNodes);
|
||||
if (nodes) {
|
||||
const mapNodes = nodes.map(n => this.nodeToMapNode.convert(n));
|
||||
this.mapNodesDataSource.set(mapNodes);
|
||||
|
||||
this.assignDataToLinks();
|
||||
this.onDataUpdate();
|
||||
this.assignDataToLinks();
|
||||
this.onDataUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public setLinks(links: Link[]) {
|
||||
const mapLinks = links.map(l => this.linkToMapLink.convert(l));
|
||||
this.mapLinksDataSource.set(mapLinks);
|
||||
if (links) {
|
||||
const mapLinks = links.map(l => this.linkToMapLink.convert(l));
|
||||
this.mapLinksDataSource.set(mapLinks);
|
||||
|
||||
this.assignDataToLinks();
|
||||
this.onDataUpdate();
|
||||
this.assignDataToLinks();
|
||||
this.onDataUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public setDrawings(drawings: Drawing[]) {
|
||||
const mapDrawings = drawings.map(d => this.drawingToMapDrawing.convert(d));
|
||||
this.mapDrawingsDataSource.set(mapDrawings);
|
||||
|
||||
this.onDataUpdate();
|
||||
if (drawings) {
|
||||
const mapDrawings = drawings.map(d => this.drawingToMapDrawing.convert(d));
|
||||
this.mapDrawingsDataSource.set(mapDrawings);
|
||||
|
||||
this.onDataUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public setSymbols(symbols: Symbol[]) {
|
||||
const mapSymbols = symbols.map(s => this.symbolToMapSymbol.convert(s));
|
||||
this.mapSymbolsDataSource.set(mapSymbols);
|
||||
if (symbols) {
|
||||
const mapSymbols = symbols.map(s => this.symbolToMapSymbol.convert(s));
|
||||
this.mapSymbolsDataSource.set(mapSymbols);
|
||||
}
|
||||
}
|
||||
|
||||
public getNodes() {
|
||||
|
@ -5,6 +5,7 @@ export class Drawing {
|
||||
project_id: string;
|
||||
rotation: number;
|
||||
svg: string;
|
||||
locked: boolean;
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
|
@ -6,6 +6,7 @@ export class MapDrawing implements Indexed {
|
||||
projectId: string;
|
||||
rotation: number;
|
||||
svg: string;
|
||||
locked: boolean;
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
|
@ -12,6 +12,7 @@ export class MapNode implements Indexed {
|
||||
firstPortName: string;
|
||||
height: number;
|
||||
label: MapLabel;
|
||||
locked: boolean;
|
||||
name: string;
|
||||
nodeDirectory: string;
|
||||
nodeType: string;
|
||||
|
@ -1,15 +1,75 @@
|
||||
import { Label } from './label';
|
||||
import { Port } from '../../models/port';
|
||||
import { CustomAdapter } from '../../models/qemu/qemu-custom-adapter';
|
||||
|
||||
export class PortsMapping {
|
||||
name: string;
|
||||
interface?: string;
|
||||
port_number: number;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
export class Properties {
|
||||
adapter_type: string;
|
||||
adapters: number;
|
||||
headless: boolean;
|
||||
linked_clone: boolean;
|
||||
on_close: string;
|
||||
ram: number;
|
||||
nvram: number;
|
||||
usage: string;
|
||||
use_any_adapter: boolean;
|
||||
vmname: string;
|
||||
ports_mapping: PortsMapping[];
|
||||
mappings: any;
|
||||
bios_image: string;
|
||||
bios_image_md5sum?: any;
|
||||
boot_priority: string;
|
||||
cdrom_image: string;
|
||||
cdrom_image_md5sum?: any;
|
||||
cpu_throttling: number;
|
||||
cpus: number;
|
||||
hda_disk_image: string;
|
||||
hda_disk_image_md5sum: string;
|
||||
hda_disk_interface: string;
|
||||
hdb_disk_image: string;
|
||||
hdb_disk_image_md5sum?: any;
|
||||
hdb_disk_interface: string;
|
||||
hdc_disk_image: string;
|
||||
hdc_disk_image_md5sum?: any;
|
||||
hdc_disk_interface: string;
|
||||
hdd_disk_image: string;
|
||||
hdd_disk_image_md5sum?: any;
|
||||
hdd_disk_interface: string;
|
||||
initrd: string;
|
||||
initrd_md5sum?: any;
|
||||
kernel_command_line: string;
|
||||
kernel_image: string;
|
||||
kernel_image_md5sum?: any;
|
||||
legacy_networking: boolean;
|
||||
mac_address: string;
|
||||
options: string;
|
||||
platform: string;
|
||||
process_priority: string;
|
||||
qemu_path: string;
|
||||
environment: string;
|
||||
extra_hosts: string;
|
||||
}
|
||||
|
||||
export class Node {
|
||||
command_line: string;
|
||||
compute_id: string;
|
||||
console: number;
|
||||
console_auto_start: boolean;
|
||||
console_host: string;
|
||||
console_type: string;
|
||||
custom_adapters?: CustomAdapter[];
|
||||
ethernet_adapters?: any;
|
||||
serial_adapters?: any;
|
||||
first_port_name: string;
|
||||
height: number;
|
||||
label: Label;
|
||||
locked: boolean;
|
||||
name: string;
|
||||
node_directory: string;
|
||||
node_id: string;
|
||||
@ -18,6 +78,7 @@ export class Node {
|
||||
port_segment_size: number;
|
||||
ports: Port[];
|
||||
project_id: string;
|
||||
properties: Properties;
|
||||
status: string;
|
||||
symbol: string;
|
||||
symbol_url: string; // @TODO: full URL to symbol, move to MapNode once converters are moved to app module
|
||||
|
@ -16,16 +16,17 @@ export class InterfaceStatusWidget implements Widget {
|
||||
const link_group = select<SVGGElement, MapLink>(this);
|
||||
const link_path = link_group.select<SVGPathElement>('path');
|
||||
|
||||
const start_point: SVGPoint = link_path.node().getPointAtLength(45);
|
||||
const end_point: SVGPoint = link_path.node().getPointAtLength(link_path.node().getTotalLength() - 45);
|
||||
|
||||
let statuses = [];
|
||||
if (link_path.node()) {
|
||||
const start_point: SVGPoint = link_path.node().getPointAtLength(45);
|
||||
const end_point: SVGPoint = link_path.node().getPointAtLength(link_path.node().getTotalLength() - 45);
|
||||
|
||||
if (link_path.node().getTotalLength() > 2 * 45 + 10) {
|
||||
statuses = [
|
||||
new LinkStatus(start_point.x, start_point.y, l.source.status),
|
||||
new LinkStatus(end_point.x, end_point.y, l.target.status)
|
||||
];
|
||||
if (link_path.node().getTotalLength() > 2 * 45 + 10) {
|
||||
statuses = [
|
||||
new LinkStatus(start_point.x, start_point.y, l.source.status),
|
||||
new LinkStatus(end_point.x, end_point.y, l.target.status)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
const status_started = link_group
|
||||
|
@ -20,7 +20,9 @@ export class LinkWidget implements Widget {
|
||||
private multiLinkCalculatorHelper: MultiLinkCalculatorHelper,
|
||||
private interfaceLabelWidget: InterfaceLabelWidget,
|
||||
private interfaceStatusWidget: InterfaceStatusWidget,
|
||||
private selectionManager: SelectionManager
|
||||
private selectionManager: SelectionManager,
|
||||
private ethernetLinkWidget: EthernetLinkWidget,
|
||||
private serialLinkWidget: SerialLinkWidget
|
||||
) {}
|
||||
|
||||
public draw(view: SVGSelection) {
|
||||
@ -88,11 +90,8 @@ export class LinkWidget implements Widget {
|
||||
.attr('height', '48px')
|
||||
.attr("xlink:href", "assets/resources/images/filter.svg");
|
||||
|
||||
const serial_link_widget = new SerialLinkWidget();
|
||||
serial_link_widget.draw(link_body_merge);
|
||||
|
||||
const ethernet_link_widget = new EthernetLinkWidget();
|
||||
ethernet_link_widget.draw(link_body_merge);
|
||||
this.serialLinkWidget.draw(link_body_merge);
|
||||
this.ethernetLinkWidget.draw(link_body_merge);
|
||||
|
||||
link_body_merge
|
||||
.select<SVGPathElement>('path')
|
||||
|
@ -1,14 +1,19 @@
|
||||
import { path } from 'd3-path';
|
||||
|
||||
import { EventEmitter, Injectable } from '@angular/core';
|
||||
import { Widget } from '../widget';
|
||||
import { SVGSelection } from '../../models/types';
|
||||
import { MapLink } from '../../models/map/map-link';
|
||||
import { LinkContextMenu } from '../../events/event-source';
|
||||
|
||||
class EthernetLinkPath {
|
||||
constructor(public source: [number, number], public target: [number, number]) {}
|
||||
}
|
||||
|
||||
export class EthernetLinkWidget implements Widget {
|
||||
@Injectable() export class EthernetLinkWidget implements Widget {
|
||||
public onContextMenu = new EventEmitter<LinkContextMenu>();
|
||||
|
||||
constructor() {}
|
||||
|
||||
private linktoEthernetLink(link: MapLink) {
|
||||
return new EthernetLinkPath(
|
||||
[link.source.x + link.source.width / 2, link.source.y + link.source.height / 2],
|
||||
@ -27,9 +32,21 @@ export class EthernetLinkWidget implements Widget {
|
||||
const link_enter = link
|
||||
.enter()
|
||||
.append<SVGPathElement>('path')
|
||||
.attr('class', 'ethernet_link');
|
||||
.attr('class', 'ethernet_link')
|
||||
.on('contextmenu', (datum) => {
|
||||
let link: MapLink = datum as unknown as MapLink;
|
||||
const evt = event;
|
||||
this.onContextMenu.emit(new LinkContextMenu(evt, link));
|
||||
});
|
||||
|
||||
link_enter.attr('stroke', '#000').attr('stroke-width', '2');
|
||||
link_enter
|
||||
.attr('stroke', '#000')
|
||||
.attr('stroke-width', '2')
|
||||
.on('contextmenu', (datum) => {
|
||||
let link: MapLink = datum as unknown as MapLink;
|
||||
const evt = event;
|
||||
this.onContextMenu.emit(new LinkContextMenu(evt, link));
|
||||
});
|
||||
|
||||
const link_merge = link.merge(link_enter);
|
||||
|
||||
|
@ -3,6 +3,8 @@ import { path } from 'd3-path';
|
||||
import { Widget } from '../widget';
|
||||
import { SVGSelection } from '../../models/types';
|
||||
import { MapLink } from '../../models/map/map-link';
|
||||
import { Injectable, EventEmitter } from '@angular/core';
|
||||
import { LinkContextMenu } from '../../events/event-source';
|
||||
|
||||
class SerialLinkPath {
|
||||
constructor(
|
||||
@ -13,7 +15,11 @@ class SerialLinkPath {
|
||||
) {}
|
||||
}
|
||||
|
||||
export class SerialLinkWidget implements Widget {
|
||||
@Injectable() export class SerialLinkWidget implements Widget {
|
||||
public onContextMenu = new EventEmitter<LinkContextMenu>();
|
||||
|
||||
constructor() {}
|
||||
|
||||
private linkToSerialLink(link: MapLink) {
|
||||
const source = {
|
||||
x: link.source.x + link.source.width / 2,
|
||||
@ -55,7 +61,12 @@ export class SerialLinkWidget implements Widget {
|
||||
const link_enter = link
|
||||
.enter()
|
||||
.append<SVGPathElement>('path')
|
||||
.attr('class', 'serial_link');
|
||||
.attr('class', 'serial_link')
|
||||
.on('contextmenu', (datum) => {
|
||||
let link: MapLink = datum as unknown as MapLink;
|
||||
const evt = event;
|
||||
this.onContextMenu.emit(new LinkContextMenu(evt, link));
|
||||
});
|
||||
|
||||
link_enter
|
||||
.attr('stroke', '#B22222')
|
||||
|
@ -7,7 +7,7 @@ import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { Router } from '@angular/router';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
|
||||
class MockedRouter {
|
||||
export class MockedRouter {
|
||||
events: BehaviorSubject<boolean>;
|
||||
|
||||
constructor() {
|
||||
|
@ -48,6 +48,7 @@ describe('DrawingDraggedComponent', () => {
|
||||
};
|
||||
const mapDrawing: MapDrawing = {
|
||||
id: 'sampleId',
|
||||
locked: false,
|
||||
projectId: 'sampleprojectId',
|
||||
rotation: 0,
|
||||
svg: 'sampleSvg',
|
||||
|
@ -51,6 +51,7 @@ describe('DrawingResizedComponent', () => {
|
||||
};
|
||||
const mapDrawing: MapDrawing = {
|
||||
id: 'sampleId',
|
||||
locked: false,
|
||||
projectId: 'sampleprojectId',
|
||||
rotation: 0,
|
||||
svg: 'sampleSvg',
|
||||
|
@ -72,6 +72,7 @@ describe('LinkCreatedComponent', () => {
|
||||
firstPortName: 'sampleFirstPortName',
|
||||
height: 0,
|
||||
label: {} as MapLabel,
|
||||
locked: false,
|
||||
name: 'sampleName',
|
||||
nodeDirectory: 'sampleNodeDirectory',
|
||||
nodeType: 'sampleNodeType',
|
||||
|
@ -52,6 +52,7 @@ describe('NodeDraggedComponent', () => {
|
||||
firstPortName: 'sampleFirstPortName',
|
||||
height: 0,
|
||||
label: {} as MapLabel,
|
||||
locked: false,
|
||||
name: 'sampleName',
|
||||
nodeDirectory: 'sampleNodeDirectory',
|
||||
nodeType: 'sampleNodeType',
|
||||
|
@ -3,6 +3,19 @@
|
||||
<div class="default-content">
|
||||
<div class="container mat-elevation-z8">
|
||||
<mat-accordion>
|
||||
<mat-expansion-panel>
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title> Useful shortcuts </mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<mat-list>
|
||||
<mat-list-item> ctrl + + to zoom in </mat-list-item>
|
||||
<mat-list-item> ctrl + - to zoom out </mat-list-item>
|
||||
<mat-list-item> ctrl + 0 to reset zoom </mat-list-item>
|
||||
<mat-list-item> ctrl + a to select all items on map </mat-list-item>
|
||||
<mat-list-item> ctrl + shift + a to deselect all items on map </mat-list-item>
|
||||
<mat-list-item> ctrl + shift + s to go to preferences </mat-list-item>
|
||||
</mat-list>
|
||||
</mat-expansion-panel>
|
||||
<mat-expansion-panel>
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title> Third party components </mat-panel-title>
|
||||
|
@ -82,7 +82,7 @@
|
||||
placeholder="Ethernet interface"
|
||||
[ngModelOptions]="{standalone: true}"
|
||||
[(ngModel)]="ethernetInterface">
|
||||
<mat-option *ngFor="let type of " [value]="type">
|
||||
<mat-option *ngFor="let type of ethernetInterfaces" [value]="type">
|
||||
{{type}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
|
@ -57,69 +57,13 @@
|
||||
</mat-form-field>
|
||||
</form>
|
||||
</mat-expansion-panel>
|
||||
<mat-expansion-panel *ngIf="newPort">
|
||||
<mat-expansion-panel>
|
||||
<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>
|
||||
<app-ports #ports [ethernetPorts]="ethernetSwitchTemplate.ports_mapping"></app-ports>
|
||||
</mat-expansion-panel>
|
||||
</mat-accordion>
|
||||
<div class="buttons-bar">
|
||||
|
@ -17,6 +17,7 @@ import { BuiltInTemplatesService } from '../../../../../services/built-in-templa
|
||||
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';
|
||||
import { PortsComponent } from '../../../common/ports/ports.component';
|
||||
|
||||
export class MockedBuiltInTemplatesService {
|
||||
public getTemplate(server: Server, template_id: string) {
|
||||
@ -68,6 +69,7 @@ describe('EthernetSwitchesTemplateDetailsComponent', () => {
|
||||
|
||||
it('should call save template', () => {
|
||||
spyOn(mockedBuiltInTemplatesService, 'saveTemplate').and.returnValue(of({} as EthernetSwitchTemplate));
|
||||
component.portsComponent = {ethernetPorts: []} as PortsComponent;
|
||||
component.inputForm.controls['templateName'].setValue('template name');
|
||||
component.inputForm.controls['defaultName'].setValue('default name');
|
||||
component.inputForm.controls['symbol'].setValue('symbol');
|
||||
@ -102,7 +104,7 @@ describe('EthernetSwitchesTemplateDetailsComponent', () => {
|
||||
expect(mockedBuiltInTemplatesService.saveTemplate).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call save template when symbol path is empty', () => {
|
||||
it('should not call save template when symbol path is empty', () => {
|
||||
spyOn(mockedBuiltInTemplatesService, 'saveTemplate').and.returnValue(of({} as EthernetSwitchTemplate));
|
||||
component.inputForm.controls['templateName'].setValue('template name');
|
||||
component.inputForm.controls['defaultName'].setValue('default name');
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { Component, OnInit, ViewChild } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ServerService } from '../../../../../services/server.service';
|
||||
import { Server } from '../../../../../models/server';
|
||||
@ -6,8 +6,8 @@ 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';
|
||||
import { PortsComponent } from '../../../common/ports/ports.component';
|
||||
|
||||
|
||||
@Component({
|
||||
@ -16,20 +16,13 @@ import { BuiltInTemplatesConfigurationService } from '../../../../../services/bu
|
||||
styleUrls: ['./ethernet-switches-template-details.component.scss', '../../../preferences.component.scss']
|
||||
})
|
||||
export class EthernetSwitchesTemplateDetailsComponent implements OnInit {
|
||||
@ViewChild(PortsComponent, {static: false}) portsComponent: PortsComponent;
|
||||
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,
|
||||
@ -45,11 +38,6 @@ export class EthernetSwitchesTemplateDetailsComponent implements OnInit {
|
||||
defaultName: new FormControl('', Validators.required),
|
||||
symbol: new FormControl('', Validators.required)
|
||||
});
|
||||
|
||||
this.newPort = {
|
||||
name: '',
|
||||
port_number: 0,
|
||||
};
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
@ -61,8 +49,6 @@ export class EthernetSwitchesTemplateDetailsComponent implements OnInit {
|
||||
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;
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -70,18 +56,6 @@ export class EthernetSwitchesTemplateDetailsComponent implements OnInit {
|
||||
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() {
|
||||
@ -92,6 +66,7 @@ export class EthernetSwitchesTemplateDetailsComponent implements OnInit {
|
||||
if (this.inputForm.invalid) {
|
||||
this.toasterService.error(`Fill all required fields`);
|
||||
} else {
|
||||
this.ethernetSwitchTemplate.ports_mapping = this.portsComponent.ethernetPorts;
|
||||
this.builtInTemplatesService.saveTemplate(this.server, this.ethernetSwitchTemplate).subscribe((ethernetSwitchTemplate: EthernetSwitchTemplate) => {
|
||||
this.toasterService.success("Changes saved");
|
||||
});
|
||||
|
@ -0,0 +1,35 @@
|
||||
<table class="table" mat-table [dataSource]="adapters">
|
||||
<ng-container matColumnDef="adapter_number">
|
||||
<th mat-header-cell *matHeaderCellDef> Adapter number </th>
|
||||
<td mat-cell *matCellDef="let element"> Adapter {{element.adapter_number}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="port_name">
|
||||
<th mat-header-cell *matHeaderCellDef> Port name </th>
|
||||
<td mat-cell *matCellDef="let element"> Ethernet {{element.adapter_number}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="adapter_type">
|
||||
<th mat-header-cell *matHeaderCellDef> Adapter type </th>
|
||||
<td mat-cell *matCellDef="let element; let i = index;">
|
||||
<mat-select placeholder="Type" [(ngModel)]="element.adapter_type">
|
||||
<mat-option *ngFor="let type of networkTypes" [value]="type">
|
||||
{{type}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef> Actions </th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<button mat-icon-button matTooltip="Delete adapter" (click)="delete(element)">
|
||||
<mat-icon aria-label="Delete adapter">delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
<button mat-button class="form-field" (click)="onAdd()">Add</button>
|
@ -0,0 +1,28 @@
|
||||
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { CustomAdapter } from '../../../../models/qemu/qemu-custom-adapter';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-custom-adapters-table',
|
||||
templateUrl: './custom-adapters-table.component.html',
|
||||
styleUrls: ['../../preferences.component.scss']
|
||||
})
|
||||
export class CustomAdaptersTableComponent {
|
||||
@Input() networkTypes = [];
|
||||
@Input() displayedColumns: string[] = [];
|
||||
@Input() adapters: CustomAdapter[] = [];
|
||||
|
||||
public numberOfAdapters: number;
|
||||
|
||||
onAdd() {
|
||||
let adapter: CustomAdapter = {
|
||||
adapter_number: this.adapters.length,
|
||||
adapter_type: this.networkTypes[0]
|
||||
}
|
||||
this.adapters = this.adapters.concat([adapter]);
|
||||
}
|
||||
|
||||
delete(adapter: CustomAdapter) {
|
||||
this.adapters = this.adapters.filter(elem => elem!== adapter);
|
||||
}
|
||||
}
|
@ -6,31 +6,12 @@
|
||||
</div>
|
||||
<div class="default-content">
|
||||
<div class="container mat-elevation-z8">
|
||||
<table class="table" mat-table [dataSource]="adapters">
|
||||
<ng-container matColumnDef="adapter_number">
|
||||
<th mat-header-cell *matHeaderCellDef> Adapter number </th>
|
||||
<td mat-cell *matCellDef="let element"> Adapter {{element.adapter_number}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="port_name">
|
||||
<th mat-header-cell *matHeaderCellDef> Port name </th>
|
||||
<td mat-cell *matCellDef="let element"> Ethernet {{element.adapter_number}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="adapter_type">
|
||||
<th mat-header-cell *matHeaderCellDef> Adapter type </th>
|
||||
<td mat-cell *matCellDef="let element; let i = index;">
|
||||
<mat-select placeholder="Type" [(ngModel)]="adapters[i].adapter_type">
|
||||
<mat-option *ngFor="let type of networkTypes" [value]="type[0]">
|
||||
{{type[1]}} ({{type[0]}})
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
<app-custom-adapters-table
|
||||
#customAdapters
|
||||
[networkTypes]="networkTypes"
|
||||
[displayedColumns]="displayedColumns"
|
||||
[adapters]="adapters"
|
||||
></app-custom-adapters-table>
|
||||
</div>
|
||||
<div class="buttons-bar">
|
||||
<button mat-button (click)="cancelConfigureCustomAdapters()">Cancel</button>
|
||||
|
@ -4,6 +4,7 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { CustomAdaptersComponent } from './custom-adapters.component';
|
||||
import { CustomAdaptersTableComponent } from '../custom-adapters-table/custom-adapters-table.component';
|
||||
|
||||
describe('Custom adapters component', () => {
|
||||
let component: CustomAdaptersComponent;
|
||||
@ -31,6 +32,7 @@ describe('Custom adapters component', () => {
|
||||
|
||||
it('should emit event when apply clicked', () => {
|
||||
spyOn(component.saveConfigurationEmitter, 'emit');
|
||||
component.customAdapters = {adapters: []} as CustomAdaptersTableComponent;
|
||||
|
||||
component.configureCustomAdapters();
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core';
|
||||
import { CustomAdapter } from '../../../../models/qemu/qemu-custom-adapter';
|
||||
import { CustomAdaptersTableComponent } from '../custom-adapters-table/custom-adapters-table.component';
|
||||
|
||||
|
||||
@Component({
|
||||
@ -13,14 +14,30 @@ export class CustomAdaptersComponent {
|
||||
@Output() closeConfiguratorEmitter = new EventEmitter<boolean>();
|
||||
@Output() saveConfigurationEmitter = new EventEmitter<CustomAdapter[]>();
|
||||
|
||||
@ViewChild("customAdapters", {static: false}) customAdapters: CustomAdaptersTableComponent;
|
||||
|
||||
public adapters: CustomAdapter[];
|
||||
public numberOfAdapters: number;
|
||||
|
||||
constructor() {
|
||||
console.log(this.networkTypes);
|
||||
}
|
||||
|
||||
cancelConfigureCustomAdapters(){
|
||||
this.closeConfiguratorEmitter.emit(false);
|
||||
}
|
||||
|
||||
configureCustomAdapters(){
|
||||
this.adapters = [];
|
||||
console.log(this.customAdapters);
|
||||
|
||||
this.customAdapters.adapters.forEach(n => {
|
||||
this.adapters.push({
|
||||
adapter_number: n.adapter_number,
|
||||
adapter_type: n.adapter_type
|
||||
})
|
||||
});
|
||||
|
||||
this.saveConfigurationEmitter.emit(this.adapters);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,66 @@
|
||||
<table class="table" mat-table [dataSource]="ethernetPorts">
|
||||
<ng-container matColumnDef="port_number">
|
||||
<th mat-header-cell *matHeaderCellDef> Port number </th>
|
||||
<td mat-cell *matCellDef="let element"> {{element.port_number}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="vlan">
|
||||
<th mat-header-cell *matHeaderCellDef> VLAN </th>
|
||||
<td mat-cell *matCellDef="let element"> {{element.vlan}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="type">
|
||||
<th mat-header-cell *matHeaderCellDef> Type </th>
|
||||
<td mat-cell *matCellDef="let element"> {{element.type}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="ethertype">
|
||||
<th mat-header-cell *matHeaderCellDef> EtherType </th>
|
||||
<td mat-cell *matCellDef="let element"> {{element.ethertype}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="action">
|
||||
<th mat-header-cell *matHeaderCellDef> Actions </th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<button mat-icon-button matTooltip="Delete port" (click)="delete(element)">
|
||||
<mat-icon aria-label="Delete port">delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table><br/>
|
||||
<mat-form-field class="form-field">
|
||||
<input
|
||||
matInput type="number"
|
||||
[(ngModel)]="newPort.port_number"
|
||||
placeholder="Port">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="form-field">
|
||||
<input
|
||||
matInput type="number"
|
||||
[(ngModel)]="newPort.vlan"
|
||||
placeholder="VLAN">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="select">
|
||||
<mat-select
|
||||
placeholder="Type"
|
||||
[ngModelOptions]="{standalone: true}"
|
||||
[(ngModel)]="newPort.type">
|
||||
<mat-option *ngFor="let type of portTypes" [value]="type">
|
||||
{{type}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="select">
|
||||
<mat-select
|
||||
placeholder="EtherType"
|
||||
[ngModelOptions]="{standalone: true}"
|
||||
[(ngModel)]="newPort.ethertype">
|
||||
<mat-option *ngFor="let type of etherTypes" [value]="type">
|
||||
{{type}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<button mat-button class="form-field" (click)="onAdd()">Add</button>
|
@ -0,0 +1,48 @@
|
||||
import { Component, OnInit, Input, Output, EventEmitter } from "@angular/core";
|
||||
import { Server } from '../../../../models/server';
|
||||
import { PortsMappingEntity } from '../../../../models/ethernetHub/ports-mapping-enity';
|
||||
import { BuiltInTemplatesConfigurationService } from '../../../../services/built-in-templates-configuration.service';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-ports',
|
||||
templateUrl: './ports.component.html',
|
||||
styleUrls: ['../../preferences.component.scss']
|
||||
})
|
||||
export class PortsComponent implements OnInit {
|
||||
@Input() ethernetPorts: PortsMappingEntity[] = [];
|
||||
newPort: PortsMappingEntity = {
|
||||
name: '',
|
||||
port_number: 0,
|
||||
};
|
||||
|
||||
portTypes: string[] = [];
|
||||
etherTypes: string[] = [];
|
||||
displayedColumns: string[] = ['port_number', 'vlan', 'type', 'ethertype', 'action'];
|
||||
|
||||
constructor(
|
||||
private builtInTemplatesConfigurationService: BuiltInTemplatesConfigurationService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.getConfiguration();
|
||||
}
|
||||
|
||||
getConfiguration() {
|
||||
this.etherTypes = this.builtInTemplatesConfigurationService.getEtherTypesForEthernetSwitches();
|
||||
this.portTypes = this.builtInTemplatesConfigurationService.getPortTypesForEthernetSwitches();
|
||||
}
|
||||
|
||||
onAdd() {
|
||||
this.ethernetPorts.push(this.newPort);
|
||||
|
||||
this.newPort = {
|
||||
name: '',
|
||||
port_number: 0,
|
||||
};
|
||||
}
|
||||
|
||||
delete(port: PortsMappingEntity) {
|
||||
this.ethernetPorts = this.ethernetPorts.filter(n => n !== port);
|
||||
}
|
||||
}
|
@ -1,7 +1,31 @@
|
||||
<mat-card>
|
||||
<input matInput type="text" [(ngModel)]="searchText" placeholder="Search by filename"><br/><br/>
|
||||
<div class="menu">
|
||||
<mat-radio-group aria-label="Select an option" class="radio-selection">
|
||||
<mat-radio-button value="1" (click)="setFilter('all')" checked>All symbols</mat-radio-button>
|
||||
<mat-radio-button value="2" (click)="setFilter('builtin')">Built-in symbols</mat-radio-button>
|
||||
<mat-radio-button value="3" (click)="setFilter('custom')">Custom symbols</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
<input
|
||||
type="file"
|
||||
accept=".svg, .bmp, .jpeg, .jpg, .gif, .png"
|
||||
class="non-visible"
|
||||
#file
|
||||
(change)="uploadSymbolFile($event)"/>
|
||||
<button mat-button (click)="file.click()">
|
||||
<mat-icon>add</mat-icon>
|
||||
Add symbol
|
||||
</button>
|
||||
</div>
|
||||
<form>
|
||||
<mat-form-field class="example-full-width">
|
||||
<input matInput
|
||||
placeholder="Search by filename"
|
||||
[(ngModel)]="searchText"
|
||||
[ngModelOptions]="{standalone: true}">
|
||||
</mat-form-field>
|
||||
</form>
|
||||
<div class="wrapper">
|
||||
<div class="buttonWrapper" *ngFor="let symbol of symbols | filenamefilter: searchText">
|
||||
<div class="buttonWrapper" *ngFor="let symbol of filteredSymbols | filenamefilter: searchText">
|
||||
<button [ngClass]="{ buttonSelected: isSelected === symbol.symbol_id }" class="button" (click)="setSelected(symbol.symbol_id)">
|
||||
<img [ngClass]="{ imageSelected: isSelected === symbol.symbol_id }" class="image" src="http://127.0.0.1:3080/v2/symbols/{{symbol.symbol_id}}/raw"/>
|
||||
</button>
|
||||
|
@ -8,6 +8,11 @@
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.menu {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.button {
|
||||
background: border-box;
|
||||
border-width: 0px;
|
||||
@ -36,3 +41,19 @@
|
||||
grid-row-gap: 3em;
|
||||
grid-column-gap: 1em;
|
||||
}
|
||||
|
||||
.radio-selection {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.mat-radio-button ~ .mat-radio-button {
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.non-visible {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.example-full-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
@ -14,6 +14,10 @@ export class MockedSymbolService {
|
||||
public list() {
|
||||
return of([]);
|
||||
}
|
||||
|
||||
public raw() {
|
||||
return of('<svg></svg>')
|
||||
}
|
||||
}
|
||||
|
||||
describe('Symbols component', () => {
|
||||
|
@ -15,6 +15,7 @@ export class SymbolsComponent implements OnInit {
|
||||
@Output() symbolChanged = new EventEmitter<string>();
|
||||
|
||||
symbols: Symbol[] = [];
|
||||
filteredSymbols: Symbol[] = [];
|
||||
isSelected: string = '';
|
||||
searchText: string = '';
|
||||
|
||||
@ -24,14 +25,55 @@ export class SymbolsComponent implements OnInit {
|
||||
|
||||
ngOnInit() {
|
||||
this.isSelected = this.symbol;
|
||||
this.loadSymbols();
|
||||
}
|
||||
|
||||
this.symbolService.list(this.server).subscribe((symbols: Symbol[]) => {
|
||||
this.symbols = symbols;
|
||||
});
|
||||
setFilter(filter: string) {
|
||||
if (filter === 'all') {
|
||||
this.filteredSymbols = this.symbols;
|
||||
} else if (filter === 'builtin') {
|
||||
this.filteredSymbols = this.symbols.filter(elem => elem.builtin);
|
||||
} else {
|
||||
this.filteredSymbols = this.symbols.filter(elem => !elem.builtin);
|
||||
}
|
||||
}
|
||||
|
||||
setSelected(symbol_id: string) {
|
||||
this.isSelected = symbol_id;
|
||||
this.symbolChanged.emit(this.isSelected);
|
||||
}
|
||||
|
||||
loadSymbols() {
|
||||
this.symbolService.list(this.server).subscribe((symbols: Symbol[]) => {
|
||||
this.symbols = symbols;
|
||||
this.filteredSymbols = symbols;
|
||||
});
|
||||
}
|
||||
|
||||
public uploadSymbolFile(event) {
|
||||
this.readSymbolFile(event.target);
|
||||
}
|
||||
|
||||
private readSymbolFile(symbolInput) {
|
||||
let file: File = symbolInput.files[0];
|
||||
let fileName = symbolInput.files[0].name;
|
||||
let fileReader: FileReader = new FileReader();
|
||||
let imageToUpload = new Image();
|
||||
|
||||
fileReader.onloadend = () => {
|
||||
let image = fileReader.result;
|
||||
let svg = this.createSvgFileForImage(image, imageToUpload);
|
||||
this.symbolService.add(this.server, fileName, svg).subscribe(() => {
|
||||
this.loadSymbols();
|
||||
});
|
||||
}
|
||||
|
||||
imageToUpload.onload = () => { fileReader.readAsDataURL(file) };
|
||||
imageToUpload.src = window.URL.createObjectURL(file);
|
||||
}
|
||||
|
||||
private createSvgFileForImage(image: string|ArrayBuffer, imageToUpload: HTMLImageElement) {
|
||||
return `<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" height=\"${imageToUpload.height}\"
|
||||
width=\"${imageToUpload.width}\">\n<image height=\"${imageToUpload.height}\" width=\"${imageToUpload.width}\" xlink:href=\"${image}\"/>\n</svg>`
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,59 @@
|
||||
<table *ngIf="dataSourceUdp.length" class="table" mat-table [dataSource]="dataSourceUdp">
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef> Name </th>
|
||||
<td mat-cell *matCellDef="let element"> {{element.name}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="rport">
|
||||
<th mat-header-cell *matHeaderCellDef> Local port </th>
|
||||
<td mat-cell *matCellDef="let element"> {{element.rport}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="rhost">
|
||||
<th mat-header-cell *matHeaderCellDef> Type </th>
|
||||
<td mat-cell *matCellDef="let element"> {{element.rhost}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="lport">
|
||||
<th mat-header-cell *matHeaderCellDef> Remote port </th>
|
||||
<td mat-cell *matCellDef="let element"> {{element.lport}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="action">
|
||||
<th mat-header-cell *matHeaderCellDef> Actions </th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<button mat-icon-button matTooltip="Delete port" (click)="delete(element)">
|
||||
<mat-icon aria-label="Delete port">delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
<br *ngIf="dataSourceUdp.length"/>
|
||||
<mat-form-field class="form-field">
|
||||
<input
|
||||
matInput type="text"
|
||||
[(ngModel)]="newPort.name"
|
||||
placeholder="Name">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="form-field">
|
||||
<input
|
||||
matInput type="number"
|
||||
[(ngModel)]="newPort.lport"
|
||||
placeholder="Local port">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="form-field">
|
||||
<input
|
||||
matInput type="text"
|
||||
[(ngModel)]="newPort.rhost"
|
||||
placeholder="Remote host">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="form-field">
|
||||
<input
|
||||
matInput type="number"
|
||||
[(ngModel)]="newPort.rport"
|
||||
placeholder="Remote port">
|
||||
</mat-form-field>
|
||||
<button mat-button class="form-field" (click)="onAddUdpInterface()">Add</button>
|
@ -0,0 +1,47 @@
|
||||
import { Component, OnInit, Input, Output, EventEmitter } from "@angular/core";
|
||||
import { Server } from '../../../../models/server';
|
||||
import { PortsMappingEntity } from '../../../../models/ethernetHub/ports-mapping-enity';
|
||||
import { BuiltInTemplatesConfigurationService } from '../../../../services/built-in-templates-configuration.service';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-udp-tunnels',
|
||||
templateUrl: './udp-tunnels.component.html',
|
||||
styleUrls: ['../../preferences.component.scss']
|
||||
})
|
||||
export class UdpTunnelsComponent implements OnInit {
|
||||
@Input() dataSourceUdp: PortsMappingEntity[] = [];
|
||||
displayedColumns: string[] = ['name', 'lport', 'rhost', 'rport', 'action'];
|
||||
newPort: PortsMappingEntity = {
|
||||
name: '',
|
||||
port_number: 0,
|
||||
};
|
||||
portTypes: string[] = [];
|
||||
etherTypes: string[] = [];
|
||||
|
||||
constructor(
|
||||
private builtInTemplatesConfigurationService: BuiltInTemplatesConfigurationService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.getConfiguration();
|
||||
}
|
||||
|
||||
getConfiguration() {
|
||||
this.etherTypes = this.builtInTemplatesConfigurationService.getEtherTypesForEthernetSwitches();
|
||||
this.portTypes = this.builtInTemplatesConfigurationService.getPortTypesForEthernetSwitches();
|
||||
}
|
||||
|
||||
onAddUdpInterface() {
|
||||
this.dataSourceUdp = this.dataSourceUdp.concat([this.newPort]);
|
||||
|
||||
this.newPort = {
|
||||
name: '',
|
||||
port_number: 0,
|
||||
};
|
||||
}
|
||||
|
||||
delete(port: PortsMappingEntity) {
|
||||
this.dataSourceUdp = this.dataSourceUdp.filter(n => n !== port);
|
||||
}
|
||||
}
|
@ -31,6 +31,9 @@
|
||||
<mat-list-item routerLink="/server/{{serverId}}/preferences/docker/templates">
|
||||
Docker
|
||||
</mat-list-item>
|
||||
<!-- <mat-list-item routerLink="/server/{{serverId}}/preferences/traceng/templates">
|
||||
TraceNG
|
||||
</mat-list-item> -->
|
||||
</mat-nav-list>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -31,7 +31,7 @@ export class QemuVmTemplateDetailsComponent implements OnInit {
|
||||
binaries: QemuBinary[] = [];
|
||||
activateCpuThrottling: boolean = true;
|
||||
isConfiguratorOpened: boolean = false;
|
||||
displayedColumns: string[] = ['adapter_number', 'port_name', 'adapter_type'];
|
||||
displayedColumns: string[] = ['adapter_number', 'port_name', 'adapter_type', 'actions'];
|
||||
generalSettingsForm: FormGroup;
|
||||
|
||||
@ViewChild("customAdaptersConfigurator", {static: false})
|
||||
|
@ -0,0 +1,23 @@
|
||||
<div class="content">
|
||||
<div class="default-header">
|
||||
<div class="row">
|
||||
<h1 class="col">New VPCS node template</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="default-content">
|
||||
<mat-card class="matCard">
|
||||
<form [formGroup]="templateNameForm">
|
||||
<mat-form-field class="form-field">
|
||||
<input matInput formControlName="templateName" type="text" placeholder="Template name">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="form-field">
|
||||
<input matInput formControlName="ipAddress" type="text" placeholder="IP address">
|
||||
</mat-form-field>
|
||||
</form>
|
||||
</mat-card>
|
||||
<div class="buttons-bar">
|
||||
<button mat-button class="cancel-button" (click)="goBack()">Cancel</button>
|
||||
<button mat-raised-button color="primary" (click)="addTemplate()">Add template</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,67 @@
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { Server } from '../../../../models/server';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ServerService } from '../../../../services/server.service';
|
||||
import { ToasterService } from '../../../../services/toaster.service';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { TemplateMocksService } from '../../../../services/template-mocks.service';
|
||||
import { FormGroup, FormBuilder, FormControl, Validators } from '@angular/forms';
|
||||
import { TracengService } from '../../../../services/traceng.service';
|
||||
import { TracengTemplate } from '../../../../models/templates/traceng-template';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-add-traceng-template',
|
||||
templateUrl: './add-traceng-template.component.html',
|
||||
styleUrls: ['./add-traceng-template.component.scss', '../../preferences.component.scss']
|
||||
})
|
||||
export class AddTracengTemplateComponent implements OnInit {
|
||||
server: Server;
|
||||
templateName: string = '';
|
||||
ipAddress: string = '';
|
||||
templateNameForm: FormGroup
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private serverService: ServerService,
|
||||
private tracengService: TracengService,
|
||||
private router: Router,
|
||||
private toasterService: ToasterService,
|
||||
private templateMocksService: TemplateMocksService,
|
||||
private formBuilder: FormBuilder
|
||||
) {
|
||||
this.templateNameForm = this.formBuilder.group({
|
||||
templateName: new FormControl(null, [Validators.required]),
|
||||
ipAddress: new FormControl(null, [Validators.required])
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
const server_id = this.route.snapshot.paramMap.get("server_id");
|
||||
this.serverService.get(parseInt(server_id, 10)).then((server: Server) => {
|
||||
this.server = server;
|
||||
});
|
||||
}
|
||||
|
||||
goBack() {
|
||||
this.router.navigate(['/server', this.server.id, 'preferences', 'traceng', 'templates']);
|
||||
}
|
||||
|
||||
addTemplate() {
|
||||
if (!this.templateNameForm.invalid) {
|
||||
this.templateName = this.templateNameForm.get('templateName').value;
|
||||
this.ipAddress = this.templateNameForm.get('ipAddress').value;
|
||||
let tracengTemplate: TracengTemplate = this.templateMocksService.getTracengTemplate();
|
||||
|
||||
tracengTemplate.template_id = uuid();
|
||||
tracengTemplate.name = this.templateName;
|
||||
tracengTemplate.ip_address = this.ipAddress;
|
||||
|
||||
this.tracengService.addTemplate(this.server, tracengTemplate).subscribe(() => {
|
||||
this.goBack();
|
||||
});
|
||||
} else {
|
||||
this.toasterService.error(`Fill all required fields`);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
<div class="content">
|
||||
<div class="default-header">
|
||||
<div class="row">
|
||||
<h1 class="col">TraceNG preferences</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="default-content">
|
||||
<mat-form-field class="form-field">
|
||||
<input matInput type="text" [(ngModel)]="tracengExecutable" placeholder="Path to TraceNG executable"/>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,3 @@
|
||||
.form-field {
|
||||
width: 100%;
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Server } from '../../../../models/server';
|
||||
import { ServerService } from '../../../../services/server.service';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-traceng-preferences',
|
||||
templateUrl: './traceng-preferences.component.html',
|
||||
styleUrls: ['./traceng-preferences.component.scss']
|
||||
})
|
||||
export class TracengPreferencesComponent implements OnInit {
|
||||
server: Server;
|
||||
tracengExecutable: string;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private serverService: ServerService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
const server_id = this.route.snapshot.paramMap.get("server_id");
|
||||
|
||||
this.serverService.get(parseInt(server_id, 10)).then((server: Server) => {
|
||||
this.server = server;
|
||||
});
|
||||
}
|
||||
|
||||
restoreDefaults(){
|
||||
this.tracengExecutable = '';
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
<div class="content" [ngClass]="{ shadowed: isSymbolSelectionOpened }">
|
||||
<div class="default-header">
|
||||
<div class="row">
|
||||
<h1 class="col">TraceNG device configuration</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="default-content" *ngIf="tracengTemplate">
|
||||
<mat-card class="matCard">
|
||||
<form [formGroup]="inputForm">
|
||||
<mat-form-field class="form-field">
|
||||
<input
|
||||
matInput type="text"
|
||||
[(ngModel)]="tracengTemplate.name"
|
||||
formControlName="templateName"
|
||||
placeholder="Template name">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="form-field">
|
||||
<input
|
||||
matInput type="text"
|
||||
[(ngModel)]="tracengTemplate.default_name_format"
|
||||
formControlName="defaultName"
|
||||
placeholder="Default name format">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="form-field">
|
||||
<input
|
||||
matInput type="text"
|
||||
[(ngModel)]="tracengTemplate.symbol"
|
||||
formControlName="symbol"
|
||||
placeholder="Symbol">
|
||||
</mat-form-field>
|
||||
<button mat-button class="symbolSelectionButton" (click)="chooseSymbol()">Choose symbol</button><br/><br/>
|
||||
</form>
|
||||
</mat-card>
|
||||
<div class="buttons-bar">
|
||||
<button class="cancel-button" (click)="goBack()" mat-button>Cancel</button>
|
||||
<button mat-raised-button color="primary" (click)="onSave()">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<app-symbols-menu *ngIf="isSymbolSelectionOpened && tracengTemplate" [server]="server" [symbol]="tracengTemplate.symbol" (symbolChangedEmitter)="symbolChanged($event)"></app-symbols-menu>
|
@ -0,0 +1,71 @@
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ServerService } from '../../../../services/server.service';
|
||||
import { Server } from '../../../../models/server';
|
||||
import { ToasterService } from '../../../../services/toaster.service';
|
||||
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
|
||||
import { TracengService } from '../../../../services/traceng.service';
|
||||
import { TracengTemplate } from '../../../../models/templates/traceng-template';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-traceng-template-details',
|
||||
templateUrl: './traceng-template-details.component.html',
|
||||
styleUrls: ['./traceng-template-details.component.scss', '../../preferences.component.scss']
|
||||
})
|
||||
export class TracengTemplateDetailsComponent implements OnInit {
|
||||
server: Server;
|
||||
tracengTemplate: TracengTemplate;
|
||||
inputForm: FormGroup;
|
||||
isSymbolSelectionOpened: boolean = false;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private serverService: ServerService,
|
||||
private tracengService: TracengService,
|
||||
private toasterService: ToasterService,
|
||||
private formBuilder: FormBuilder,
|
||||
private router: Router
|
||||
) {
|
||||
this.inputForm = this.formBuilder.group({
|
||||
templateName: new FormControl('', Validators.required),
|
||||
defaultName: new FormControl('', Validators.required),
|
||||
symbol: new FormControl('', Validators.required)
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
const server_id = this.route.snapshot.paramMap.get("server_id");
|
||||
const template_id = this.route.snapshot.paramMap.get("template_id");
|
||||
this.serverService.get(parseInt(server_id, 10)).then((server: Server) => {
|
||||
this.server = server;
|
||||
|
||||
this.tracengService.getTemplate(this.server, template_id).subscribe((tracengTemplate: TracengTemplate) => {
|
||||
this.tracengTemplate = tracengTemplate;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
goBack() {
|
||||
this.router.navigate(['/server', this.server.id, 'preferences', 'traceng', 'templates']);
|
||||
}
|
||||
|
||||
onSave() {
|
||||
if (this.inputForm.invalid) {
|
||||
this.toasterService.error(`Fill all required fields`);
|
||||
} else {
|
||||
this.tracengService.saveTemplate(this.server, this.tracengTemplate).subscribe((tracengTemplate: TracengTemplate) => {
|
||||
this.toasterService.success("Changes saved");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
chooseSymbol() {
|
||||
this.isSymbolSelectionOpened = !this.isSymbolSelectionOpened;
|
||||
}
|
||||
|
||||
symbolChanged(chosenSymbol: string) {
|
||||
this.isSymbolSelectionOpened = !this.isSymbolSelectionOpened;
|
||||
this.tracengTemplate.symbol = chosenSymbol;
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
<div class="content">
|
||||
<div class="default-header">
|
||||
<div class="row">
|
||||
<h1 class="col">TraceNG node templates</h1>
|
||||
<button *ngIf="server" class="top-button" class="cancel-button" routerLink="/server/{{server.id}}/preferences" mat-button>Back</button>
|
||||
<button *ngIf="server" class="top-button" routerLink="/server/{{server.id}}/preferences/traceng/addtemplate" mat-raised-button color="primary">Add TraceNG template</button>
|
||||
</div>
|
||||
</div>
|
||||
<app-empty-templates-list *ngIf="!tracengTemplates.length"></app-empty-templates-list>
|
||||
<div class="default-content" *ngIf="tracengTemplates.length">
|
||||
<div class="listcontainer mat-elevation-z8">
|
||||
<mat-nav-list *ngIf="server">
|
||||
<div class="list-item" *ngFor='let template of tracengTemplates'>
|
||||
<mat-list-item class="template-name" routerLink="{{template.template_id}}">{{template.name}}</mat-list-item>
|
||||
<button mat-button class="menu-button" [matMenuTriggerFor]="menu">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item (click)="deleteTemplate(template)">
|
||||
<mat-icon>delete</mat-icon><span>Delete</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
</mat-nav-list>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<app-delete-template
|
||||
#deleteComponent
|
||||
[server]="server"
|
||||
(deleteEvent)="onDeleteEvent()">
|
||||
</app-delete-template>
|
@ -0,0 +1,46 @@
|
||||
import { Component, OnInit, ViewChild } from "@angular/core";
|
||||
import { Server } from '../../../../models/server';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { ServerService } from '../../../../services/server.service';
|
||||
import { DeleteTemplateComponent } from '../../common/delete-template-component/delete-template.component';
|
||||
import { TracengTemplate } from '../../../../models/templates/traceng-template';
|
||||
import { TracengService } from '../../../../services/traceng.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-traceng-templates',
|
||||
templateUrl: './traceng-templates.component.html',
|
||||
styleUrls: ['./traceng-templates.component.scss', '../../preferences.component.scss']
|
||||
})
|
||||
export class TracengTemplatesComponent implements OnInit {
|
||||
server: Server;
|
||||
tracengTemplates: TracengTemplate[] = [];
|
||||
@ViewChild(DeleteTemplateComponent, {static: false}) deleteComponent: DeleteTemplateComponent;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private serverService: ServerService,
|
||||
private tracengService: TracengService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
const server_id = this.route.snapshot.paramMap.get("server_id");
|
||||
this.serverService.get(parseInt(server_id, 10)).then((server: Server) => {
|
||||
this.server = server;
|
||||
this.getTemplates();
|
||||
});
|
||||
}
|
||||
|
||||
getTemplates() {
|
||||
this.tracengService.getTemplates(this.server).subscribe((tracengTemplates: TracengTemplate[]) => {
|
||||
this.tracengTemplates = tracengTemplates.filter((elem) => elem.template_type === 'traceng' && !elem.builtin);
|
||||
});
|
||||
}
|
||||
|
||||
deleteTemplate(template: TracengTemplate) {
|
||||
this.deleteComponent.deleteItem(template.name, template.template_id);
|
||||
}
|
||||
|
||||
onDeleteEvent() {
|
||||
this.getTemplates();
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@ export class VirtualBoxTemplateDetailsComponent implements OnInit {
|
||||
onCloseOptions = [];
|
||||
categories = [];
|
||||
networkTypes = [];
|
||||
displayedColumns: string[] = ['adapter_number', 'port_name', 'adapter_type'];
|
||||
displayedColumns: string[] = ['adapter_number', 'port_name', 'adapter_type', 'actions'];
|
||||
isConfiguratorOpened: boolean = false;
|
||||
generalSettingsForm: FormGroup;
|
||||
networkForm: FormGroup
|
||||
|
@ -20,7 +20,7 @@ export class VmwareTemplateDetailsComponent implements OnInit {
|
||||
server: Server;
|
||||
vmwareTemplate: VmwareTemplate;
|
||||
generalSettingsForm: FormGroup;
|
||||
displayedColumns: string[] = ['adapter_number', 'port_name', 'adapter_type'];
|
||||
displayedColumns: string[] = ['adapter_number', 'port_name', 'adapter_type', 'actions'];
|
||||
isConfiguratorOpened: boolean = false;
|
||||
isSymbolSelectionOpened: boolean = false;
|
||||
consoleTypes: string[] = [];
|
||||
|
@ -0,0 +1,12 @@
|
||||
<h1 mat-dialog-title>Change symbol for node: {{node.name}}</h1>
|
||||
|
||||
<div class="modal-form-container">
|
||||
<div class="symbolsWrapper">
|
||||
<app-symbols [server]="server" [symbol]="symbol" (symbolChanged)="symbolChanged($event)"></app-symbols>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div mat-dialog-actions>
|
||||
<button mat-button (click)="onCloseClick()" color="accent">Cancel</button>
|
||||
<button mat-button (click)="onSelectClick()" tabindex="2" mat-raised-button color="primary">Apply</button>
|
||||
</div>
|
@ -0,0 +1,19 @@
|
||||
.symbolsWrapper {
|
||||
height: 350px;
|
||||
overflow-y: scroll;
|
||||
scrollbar-color: darkgrey #263238;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 0.5em;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: darkgrey;
|
||||
outline: 1px solid #263238;
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { MatDialogRef } from '@angular/material';
|
||||
import { Message } from '../../../models/message';
|
||||
import { Server } from '../../../models/server';
|
||||
import { Node } from '../../../cartography/models/node';
|
||||
import { Symbol } from '../../../models/symbol';
|
||||
import { NodeService } from '../../../services/node.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-change-symbol-dialog',
|
||||
templateUrl: './change-symbol-dialog.component.html',
|
||||
styleUrls: ['./change-symbol-dialog.component.scss']
|
||||
})
|
||||
export class ChangeSymbolDialogComponent implements OnInit {
|
||||
@Input() server: Server;
|
||||
@Input() node: Node;
|
||||
symbol: string;
|
||||
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<ChangeSymbolDialogComponent>,
|
||||
private nodeService: NodeService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.symbol = this.node.symbol;
|
||||
}
|
||||
|
||||
symbolChanged(chosenSymbol: string) {
|
||||
this.symbol = chosenSymbol;
|
||||
}
|
||||
|
||||
onCloseClick() {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
onSelectClick() {
|
||||
this.nodeService.updateSymbol(this.server, this.node, this.symbol).subscribe(() => {
|
||||
this.onCloseClick()
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
<button mat-menu-item (click)="bringToFront()">
|
||||
<mat-icon>vertical_align_top</mat-icon>
|
||||
<span>Bring to front</span>
|
||||
</button>
|
@ -0,0 +1,66 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { BringToFrontActionComponent } from './bring-to-front-action.component';
|
||||
import { MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule } from '@angular/material';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { MockedDrawingService, MockedDrawingsDataSource, MockedNodesDataSource, MockedNodeService } from '../../../project-map.component.spec';
|
||||
import { DrawingService } from '../../../../../services/drawing.service';
|
||||
import { NodesDataSource } from '../../../../../cartography/datasources/nodes-datasource';
|
||||
import { DrawingsDataSource } from '../../../../../cartography/datasources/drawings-datasource';
|
||||
import { NodeService } from '../../../../../services/node.service';
|
||||
import { Node } from '../../../../../cartography/models/node';
|
||||
import { of } from 'rxjs';
|
||||
import { ComponentFactoryResolver } from '@angular/core';
|
||||
import { Drawing } from '../../../../../cartography/models/drawing';
|
||||
|
||||
describe('BringToFrontActionComponent', () => {
|
||||
let component: BringToFrontActionComponent;
|
||||
let fixture: ComponentFixture<BringToFrontActionComponent>;
|
||||
let drawingService = new MockedDrawingService();
|
||||
let drawingsDataSource = new MockedDrawingsDataSource();
|
||||
let nodeService = new MockedNodeService();
|
||||
let nodesDataSource = new MockedNodesDataSource();
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule],
|
||||
providers: [
|
||||
{ provide: DrawingService, useValue: drawingService },
|
||||
{ provide: DrawingsDataSource, useValue: drawingsDataSource },
|
||||
{ provide: NodeService, useValue: nodeService },
|
||||
{ provide: NodesDataSource, useValue: nodesDataSource },
|
||||
],
|
||||
declarations: [BringToFrontActionComponent]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(BringToFrontActionComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should call node service when bring to front action called', () => {
|
||||
spyOn(nodeService, 'update').and.returnValue(of());
|
||||
component.nodes = [{z: 0} as Node];
|
||||
component.drawings = [];
|
||||
|
||||
component.bringToFront();
|
||||
|
||||
expect(nodeService.update).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call drawing service when bring to front action called', () => {
|
||||
spyOn(drawingService, 'update').and.returnValue(of());
|
||||
component.nodes = [];
|
||||
component.drawings = [{z: 0} as Drawing];
|
||||
|
||||
component.bringToFront();
|
||||
|
||||
expect(drawingService.update).toHaveBeenCalled();
|
||||
});
|
||||
});
|
@ -0,0 +1,43 @@
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { Server } from '../../../../../models/server';
|
||||
import { Node } from '../../../../../cartography/models/node';
|
||||
import { NodesDataSource } from '../../../../../cartography/datasources/nodes-datasource';
|
||||
import { NodeService } from '../../../../../services/node.service';
|
||||
import { Drawing } from '../../../../../cartography/models/drawing';
|
||||
import { DrawingsDataSource } from '../../../../../cartography/datasources/drawings-datasource';
|
||||
import { DrawingService } from '../../../../../services/drawing.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-bring-to-front-action',
|
||||
templateUrl: './bring-to-front-action.component.html'
|
||||
})
|
||||
export class BringToFrontActionComponent implements OnInit {
|
||||
@Input() server: Server;
|
||||
@Input() nodes: Node[];
|
||||
@Input() drawings: Drawing[];
|
||||
|
||||
constructor(
|
||||
private nodesDataSource: NodesDataSource,
|
||||
private drawingsDataSource: DrawingsDataSource,
|
||||
private nodeService: NodeService,
|
||||
private drawingService: DrawingService
|
||||
) {}
|
||||
|
||||
ngOnInit() {}
|
||||
|
||||
bringToFront() {
|
||||
this.nodes.forEach((node) => {
|
||||
node.z = 100;
|
||||
this.nodesDataSource.update(node);
|
||||
|
||||
this.nodeService.update(this.server, node).subscribe((node: Node) => {});
|
||||
});
|
||||
|
||||
this.drawings.forEach((drawing) => {
|
||||
drawing.z = 100;
|
||||
this.drawingsDataSource.update(drawing);
|
||||
|
||||
this.drawingService.update(this.server, drawing).subscribe((drawing: Drawing) => {});
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
<button mat-menu-item (click)="changeSymbol()">
|
||||
<mat-icon>edit</mat-icon>
|
||||
<span>Change symbol</span>
|
||||
</button>
|
@ -0,0 +1,29 @@
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { Server } from '../../../../../models/server';
|
||||
import { Node } from '../../../../../cartography/models/node';
|
||||
import { MatDialog } from '@angular/material';
|
||||
import { ChangeSymbolDialogComponent } from '../../../change-symbol-dialog/change-symbol-dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-change-symbol-action',
|
||||
templateUrl: './change-symbol-action.component.html'
|
||||
})
|
||||
export class ChangeSymbolActionComponent implements OnInit {
|
||||
@Input() server: Server;
|
||||
@Input() node: Node;
|
||||
|
||||
constructor(private dialog: MatDialog) {}
|
||||
|
||||
ngOnInit() {}
|
||||
|
||||
changeSymbol() {
|
||||
const dialogRef = this.dialog.open(ChangeSymbolDialogComponent, {
|
||||
width: '1000px',
|
||||
height: '500px',
|
||||
autoFocus: false
|
||||
});
|
||||
let instance = dialogRef.componentInstance;
|
||||
instance.server = this.server;
|
||||
instance.node = this.node;
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
<button mat-menu-item (click)="configureNode()">
|
||||
<mat-icon>settings_applications</mat-icon>
|
||||
<span>Configure</span>
|
||||
</button>
|
@ -0,0 +1,71 @@
|
||||
import { Component, Input, OnInit, OnChanges } from '@angular/core';
|
||||
import { Server } from '../../../../../models/server';
|
||||
import { Node } from '../../../../../cartography/models/node';
|
||||
import { MatDialog, MatDialogRef } from '@angular/material';
|
||||
import { ConfiguratorDialogVpcsComponent } from '../../../node-editors/configurator/vpcs/configurator-vpcs.component';
|
||||
import { ConfiguratorDialogEthernetHubComponent } from '../../../node-editors/configurator/ethernet_hub/configurator-ethernet-hub.component';
|
||||
import { ConfiguratorDialogEthernetSwitchComponent } from '../../../node-editors/configurator/ethernet-switch/configurator-ethernet-switch.component';
|
||||
import { ConfiguratorDialogSwitchComponent } from '../../../node-editors/configurator/switch/configurator-switch.component';
|
||||
import { ConfiguratorDialogVirtualBoxComponent } from '../../../node-editors/configurator/virtualbox/configurator-virtualbox.component';
|
||||
import { ConfiguratorDialogQemuComponent } from '../../../node-editors/configurator/qemu/configurator-qemu.component';
|
||||
import { ConfiguratorDialogCloudComponent } from '../../../node-editors/configurator/cloud/configurator-cloud.component';
|
||||
import { ConfiguratorDialogAtmSwitchComponent } from '../../../node-editors/configurator/atm_switch/configurator-atm-switch.component';
|
||||
import { ConfiguratorDialogVmwareComponent } from '../../../node-editors/configurator/vmware/configurator-vmware.component';
|
||||
import { ConfiguratorDialogIouComponent } from '../../../node-editors/configurator/iou/configurator-iou.component';
|
||||
import { ConfiguratorDialogIosComponent } from '../../../node-editors/configurator/ios/configurator-ios.component';
|
||||
import { ConfiguratorDialogDockerComponent } from '../../../node-editors/configurator/docker/configurator-docker.component';
|
||||
import { ConfiguratorDialogNatComponent } from '../../../node-editors/configurator/nat/configurator-nat.component';
|
||||
import { ConfiguratorDialogTracengComponent } from '../../../node-editors/configurator/traceng/configurator-traceng.component';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-config-node-action',
|
||||
templateUrl: './config-action.component.html'
|
||||
})
|
||||
export class ConfigActionComponent {
|
||||
@Input() server: Server;
|
||||
@Input() node: Node;
|
||||
private conf = {
|
||||
autoFocus: false,
|
||||
width: '800px'
|
||||
};
|
||||
dialogRef;
|
||||
|
||||
constructor(private dialog: MatDialog) {}
|
||||
|
||||
configureNode() {
|
||||
if (this.node.node_type === 'vpcs') {
|
||||
this.dialogRef = this.dialog.open(ConfiguratorDialogVpcsComponent, this.conf);
|
||||
} else if (this.node.node_type === 'ethernet_hub') {
|
||||
this.dialogRef = this.dialog.open(ConfiguratorDialogEthernetHubComponent, this.conf);
|
||||
} else if (this.node.node_type === 'ethernet_switch') {
|
||||
this.dialogRef = this.dialog.open(ConfiguratorDialogEthernetSwitchComponent, this.conf);
|
||||
} else if (this.node.node_type === 'cloud') {
|
||||
this.dialogRef = this.dialog.open(ConfiguratorDialogCloudComponent, this.conf);
|
||||
} else if (this.node.node_type === 'dynamips') {
|
||||
this.dialogRef = this.dialog.open(ConfiguratorDialogIosComponent, this.conf);
|
||||
} else if (this.node.node_type === 'iou') {
|
||||
this.dialogRef = this.dialog.open(ConfiguratorDialogIouComponent, this.conf);
|
||||
} else if (this.node.node_type === 'qemu') {
|
||||
this.dialogRef = this.dialog.open(ConfiguratorDialogQemuComponent, this.conf);
|
||||
} else if (this.node.node_type === 'virtualbox') {
|
||||
this.dialogRef = this.dialog.open(ConfiguratorDialogVirtualBoxComponent, this.conf);
|
||||
} else if (this.node.node_type === 'vmware') {
|
||||
this.dialogRef = this.dialog.open(ConfiguratorDialogVmwareComponent, this.conf);
|
||||
} else if (this.node.node_type === 'docker') {
|
||||
this.dialogRef = this.dialog.open(ConfiguratorDialogDockerComponent, this.conf);
|
||||
} else if (this.node.node_type === 'nat') {
|
||||
this.dialogRef = this.dialog.open(ConfiguratorDialogNatComponent, this.conf);
|
||||
} else if (this.node.node_type === 'frame_relay_switch') {
|
||||
this.dialogRef = this.dialog.open(ConfiguratorDialogSwitchComponent, this.conf);
|
||||
} else if (this.node.node_type === 'atm_switch') {
|
||||
this.dialogRef = this.dialog.open(ConfiguratorDialogAtmSwitchComponent, this.conf);
|
||||
} else if (this.node.node_type === 'traceng') {
|
||||
this.dialogRef = this.dialog.open(ConfiguratorDialogTracengComponent, this.conf);
|
||||
}
|
||||
|
||||
let instance = this.dialogRef.componentInstance;
|
||||
instance.server = this.server;
|
||||
instance.node = this.node;
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
<button mat-menu-item (click)="openConsole()">
|
||||
<mat-icon>web_asset</mat-icon>
|
||||
<span>Console</span>
|
||||
</button>
|
@ -0,0 +1,43 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Node } from '../../../../../cartography/models/node';
|
||||
import { ToasterService } from '../../../../../services/toaster.service';
|
||||
import { NodeService } from '../../../../../services/node.service';
|
||||
import { Server } from '../../../../../models/server';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-console-device-action-browser',
|
||||
templateUrl: './console-device-action-browser.component.html'
|
||||
})
|
||||
export class ConsoleDeviceActionBrowserComponent {
|
||||
@Input() server: Server;
|
||||
@Input() node: Node;
|
||||
|
||||
constructor(
|
||||
private toasterService: ToasterService,
|
||||
private nodeService: NodeService
|
||||
) {}
|
||||
|
||||
openConsole() {
|
||||
this.nodeService.getNode(this.server, this.node).subscribe((node: Node) => {
|
||||
this.node = node;
|
||||
this.startConsole();
|
||||
});
|
||||
}
|
||||
|
||||
startConsole() {
|
||||
if (this.node.status !== "started") {
|
||||
this.toasterService.error("This node must be started before a console can be opened");
|
||||
} else {
|
||||
if (this.node.console_type === "telnet") {
|
||||
location.assign(`gns3+telnet://${this.node.console_host}:${this.node.console}?name=${this.node.name}&project_id=${this.node.project_id}&node_id=${this.node.node_id}`);
|
||||
} else if (this.node.console_type === "vnc") {
|
||||
location.assign(`gns3+vnc://${this.node.console_host}:${this.node.console}?name=${this.node.name}&project_id=${this.node.project_id}&node_id=${this.node.node_id}`);
|
||||
} else if(this.node.console_type === "spice") {
|
||||
location.assign(`gns3+spice://${this.node.console_host}:${this.node.console}?name=${this.node.name}&project_id=${this.node.project_id}&node_id=${this.node.node_id}`);
|
||||
} else {
|
||||
this.toasterService.error("Supported console types: telnet, vnc, spice.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -11,6 +11,8 @@ import { SettingsService } from '../../../../../services/settings.service';
|
||||
import { MockedSettingsService } from '../../../../../services/settings.service.spec';
|
||||
import { Node } from '../../../../../cartography/models/node';
|
||||
import { Server } from '../../../../../models/server';
|
||||
import { MockedNodeService } from '../../../project-map.component.spec';
|
||||
import { NodeService } from '../../../../../services/node.service';
|
||||
|
||||
|
||||
describe('ConsoleDeviceActionComponent', () => {
|
||||
@ -20,7 +22,8 @@ describe('ConsoleDeviceActionComponent', () => {
|
||||
let server: Server;
|
||||
let mockedSettingsService: MockedSettingsService;
|
||||
let mockedServerService: MockedServerService;
|
||||
let mockedToaster: MockedToasterService
|
||||
let mockedToaster: MockedToasterService;
|
||||
let mockedNodeService: MockedNodeService = new MockedNodeService();
|
||||
|
||||
beforeEach(() => {
|
||||
electronService = {
|
||||
@ -47,7 +50,8 @@ describe('ConsoleDeviceActionComponent', () => {
|
||||
{ provide: ElectronService, useValue: electronService },
|
||||
{ provide: ServerService, useValue: mockedServerService },
|
||||
{ provide: SettingsService, useValue: mockedSettingsService },
|
||||
{ provide: ToasterService, useValue: mockedToaster }
|
||||
{ provide: ToasterService, useValue: mockedToaster },
|
||||
{ provide: NodeService, useValue: mockedNodeService }
|
||||
],
|
||||
imports: [
|
||||
MatIconModule
|
||||
|
@ -5,6 +5,7 @@ import { ElectronService } from 'ngx-electron';
|
||||
import { ServerService } from '../../../../../services/server.service';
|
||||
import { SettingsService } from '../../../../../services/settings.service';
|
||||
import { ToasterService } from '../../../../../services/toaster.service';
|
||||
import { NodeService } from '../../../../../services/node.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-console-device-action',
|
||||
@ -18,19 +19,14 @@ export class ConsoleDeviceActionComponent implements OnInit {
|
||||
private electronService: ElectronService,
|
||||
private serverService: ServerService,
|
||||
private settingsService: SettingsService,
|
||||
private toasterService: ToasterService
|
||||
private toasterService: ToasterService,
|
||||
private nodeService: NodeService
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
ngOnInit() {}
|
||||
|
||||
async console() {
|
||||
let consoleCommand = this.settingsService.get<string>('console_command');
|
||||
|
||||
if(consoleCommand === undefined) {
|
||||
consoleCommand = `putty.exe -telnet \%h \%p -wt \"\%d\" -gns3 5 -skin 4`;
|
||||
}
|
||||
|
||||
let consoleCommand = this.settingsService.get<string>('console_command') ? this.settingsService.get<string>('console_command') : this.nodeService.getDefaultCommand();
|
||||
const startedNodes = this.nodes.filter(node => node.status === 'started');
|
||||
|
||||
if(startedNodes.length === 0) {
|
||||
|
@ -45,7 +45,9 @@ export class DuplicateActionComponent {
|
||||
})
|
||||
}
|
||||
|
||||
runningNodes = runningNodes.substring(0, runningNodes.length-2);
|
||||
this.toasterService.error(`Cannot duplicate node data for nodes: ${runningNodes}`);
|
||||
if (runningNodes.length > 0) {
|
||||
runningNodes = runningNodes.substring(0, runningNodes.length-2);
|
||||
this.toasterService.error(`Cannot duplicate node data for nodes: ${runningNodes}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,6 @@
|
||||
<button mat-menu-item
|
||||
*ngIf="node.node_type==='vpcs' || node.node_type==='iou' || node.node_type==='dynamips'"
|
||||
(click)="editConfig()">
|
||||
<mat-icon>settings</mat-icon>
|
||||
<span>Edit config</span>
|
||||
</button>
|
@ -0,0 +1,30 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Node } from '../../../../../cartography/models/node';
|
||||
import { Project } from '../../../../../models/project';
|
||||
import { Server } from '../../../../../models/server';
|
||||
import { ConfigEditorDialogComponent } from '../../../node-editors/config-editor/config-editor.component';
|
||||
import { MatDialog } from '@angular/material';
|
||||
|
||||
@Component({
|
||||
selector: 'app-edit-config-action',
|
||||
templateUrl: './edit-config-action.component.html'
|
||||
})
|
||||
export class EditConfigActionComponent {
|
||||
@Input() server: Server;
|
||||
@Input() project: Project;
|
||||
@Input() node: Node;
|
||||
|
||||
constructor(private dialog: MatDialog) {}
|
||||
|
||||
editConfig() {
|
||||
const dialogRef = this.dialog.open(ConfigEditorDialogComponent, {
|
||||
width: '600px',
|
||||
height: '500px',
|
||||
autoFocus: false
|
||||
});
|
||||
let instance = dialogRef.componentInstance;
|
||||
instance.server = this.server;
|
||||
instance.project = this.project;
|
||||
instance.node = this.node;
|
||||
}
|
||||
}
|
@ -20,7 +20,7 @@ export class EditStyleActionComponent implements OnInit {
|
||||
|
||||
editStyle() {
|
||||
const dialogRef = this.dialog.open(StyleEditorDialogComponent, {
|
||||
width: '300px',
|
||||
width: '800px',
|
||||
autoFocus: false
|
||||
});
|
||||
let instance = dialogRef.componentInstance;
|
||||
|
@ -0,0 +1,6 @@
|
||||
<button mat-menu-item
|
||||
*ngIf="node.node_type==='vpcs' || node.node_type==='dynamips' || node.node_type==='iou'"
|
||||
(click)="exportConfig()">
|
||||
<mat-icon>call_made</mat-icon>
|
||||
<span>Export config</span>
|
||||
</button>
|
@ -0,0 +1,37 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Node } from '../../../../../cartography/models/node';
|
||||
import { NodeService } from '../../../../../services/node.service';
|
||||
import { Server } from '../../../../../models/server';
|
||||
|
||||
@Component({
|
||||
selector: 'app-export-config-action',
|
||||
templateUrl: './export-config-action.component.html'
|
||||
})
|
||||
export class ExportConfigActionComponent {
|
||||
@Input() server: Server;
|
||||
@Input() node: Node;
|
||||
|
||||
constructor(
|
||||
private nodeService: NodeService
|
||||
) {}
|
||||
|
||||
exportConfig() {
|
||||
this.nodeService.getStartupConfiguration(this.server, this.node).subscribe((config: any) => {
|
||||
this.downloadByHtmlTag(config);
|
||||
});
|
||||
}
|
||||
|
||||
private downloadByHtmlTag(config: string) {
|
||||
const element = document.createElement('a');
|
||||
const fileType = 'text/plain';
|
||||
element.setAttribute('href', `data:${fileType};charset=utf-8,${encodeURIComponent(config)}`);
|
||||
if (this.node.node_type === 'vpcs') {
|
||||
element.setAttribute('download', `${this.node.name}_startup.vpc`);
|
||||
} else if (this.node.node_type === 'iou' || this.node.node_type === 'dynamips') {
|
||||
element.setAttribute('download', `${this.node.name}_startup.cfg`);
|
||||
}
|
||||
|
||||
var event = new MouseEvent("click");
|
||||
element.dispatchEvent(event);
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
<button mat-menu-item (click)="importConfig()">
|
||||
<mat-icon>call_received</mat-icon>
|
||||
<span>Import config</span>
|
||||
</button>
|
@ -0,0 +1,19 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Node } from '../../../../../cartography/models/node';
|
||||
import { NodeService } from '../../../../../services/node.service';
|
||||
import { Server } from '../../../../../models/server';
|
||||
|
||||
@Component({
|
||||
selector: 'app-import-config-action',
|
||||
templateUrl: './import-config-action.component.html'
|
||||
})
|
||||
export class ImportConfigActionComponent {
|
||||
@Input() server: Server;
|
||||
@Input() node: Node;
|
||||
|
||||
constructor() {}
|
||||
|
||||
importConfig() {
|
||||
//needs implementation
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
<button mat-menu-item (click)="lock()">
|
||||
<mat-icon>lock</mat-icon>
|
||||
<span>{{command}}</span>
|
||||
</button>
|
@ -0,0 +1,48 @@
|
||||
import { Component, OnInit, Input, OnDestroy, OnChanges } from '@angular/core';
|
||||
import { Server } from '../../../../../models/server';
|
||||
import { Node } from '../../../../../cartography/models/node';
|
||||
import { NodesDataSource } from '../../../../../cartography/datasources/nodes-datasource';
|
||||
import { NodeService } from '../../../../../services/node.service';
|
||||
import { Drawing } from '../../../../../cartography/models/drawing';
|
||||
import { DrawingsDataSource } from '../../../../../cartography/datasources/drawings-datasource';
|
||||
import { DrawingService } from '../../../../../services/drawing.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-lock-action',
|
||||
templateUrl: './lock-action.component.html'
|
||||
})
|
||||
export class LockActionComponent implements OnChanges {
|
||||
@Input() server: Server;
|
||||
@Input() nodes: Node[];
|
||||
@Input() drawings: Drawing[];
|
||||
command: string;
|
||||
|
||||
constructor(
|
||||
private nodesDataSource: NodesDataSource,
|
||||
private drawingsDataSource: DrawingsDataSource,
|
||||
private nodeService: NodeService,
|
||||
private drawingService: DrawingService
|
||||
) {}
|
||||
|
||||
ngOnChanges() {
|
||||
if (this.nodes.length === 1 && this.drawings.length === 0) {
|
||||
this.command = this.nodes[0].locked ? 'Unlock item' : 'Lock item';
|
||||
} else if (this.nodes.length === 0 && this.drawings.length === 1) {
|
||||
this.command = this.drawings[0].locked ? 'Unlock item' : 'Lock item';
|
||||
} else {
|
||||
this.command = 'Lock/unlock items';
|
||||
}
|
||||
}
|
||||
|
||||
lock() {
|
||||
this.nodes.forEach((node) => {
|
||||
node.locked = !node.locked;
|
||||
this.nodeService.updateNode(this.server, node).subscribe((node) => { this.nodesDataSource.update(node) });
|
||||
});
|
||||
|
||||
this.drawings.forEach((drawing) => {
|
||||
drawing.locked = ! drawing.locked;
|
||||
this.drawingService.update(this.server, drawing).subscribe((drawing) => { this.drawingsDataSource.update(drawing) });
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
<button mat-menu-item *ngIf="filteredNodes.length > 0" (click)="reloadNodes()">
|
||||
<mat-icon>refresh</mat-icon>
|
||||
<span>Reload</span>
|
||||
</button>
|
@ -0,0 +1,31 @@
|
||||
import { Component, Input, OnInit, OnChanges } from '@angular/core';
|
||||
import { Server } from '../../../../../models/server';
|
||||
import { NodeService } from '../../../../../services/node.service';
|
||||
import { Node } from '../../../../../cartography/models/node';
|
||||
|
||||
@Component({
|
||||
selector: 'app-reload-node-action',
|
||||
templateUrl: './reload-node-action.component.html'
|
||||
})
|
||||
export class ReloadNodeActionComponent implements OnInit {
|
||||
@Input() server: Server;
|
||||
@Input() nodes: Node[];
|
||||
|
||||
filteredNodes: Node[] = [];
|
||||
|
||||
constructor(private nodeService: NodeService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.nodes.forEach((node) => {
|
||||
if (node.node_type === 'vpcs' || node.node_type === 'qemu' || node.node_type === 'virtualbox' || node.node_type === 'vmware') {
|
||||
this.filteredNodes.push(node);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
reloadNodes() {
|
||||
this.filteredNodes.forEach((node) => {
|
||||
this.nodeService.reload(this.server, node).subscribe((n: Node) => {});
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
<button mat-menu-item (click)="showNode()">
|
||||
<mat-icon>info</mat-icon>
|
||||
<span>Show node information</span>
|
||||
</button>
|
@ -0,0 +1,26 @@
|
||||
import { Component, Input, OnInit, OnChanges } from '@angular/core';
|
||||
import { Node } from '../../../../../cartography/models/node';
|
||||
import { MatDialog } from '@angular/material';
|
||||
import { InfoDialogComponent } from '../../../info-dialog/info-dialog.component';
|
||||
import { Server } from '../../../../../models/server';
|
||||
|
||||
@Component({
|
||||
selector: 'app-show-node-action',
|
||||
templateUrl: './show-node-action.component.html'
|
||||
})
|
||||
export class ShowNodeActionComponent {
|
||||
@Input() node: Node;
|
||||
@Input() server: Server
|
||||
|
||||
constructor(private dialog: MatDialog) {}
|
||||
|
||||
showNode() {
|
||||
const dialogRef = this.dialog.open(InfoDialogComponent, {
|
||||
width: '600px',
|
||||
autoFocus: false
|
||||
});
|
||||
let instance = dialogRef.componentInstance;
|
||||
instance.node = this.node;
|
||||
instance.server = this.server;
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
<button mat-menu-item *ngIf="link.capturing" (click)="startCapture()">
|
||||
<mat-icon>search</mat-icon>
|
||||
<span>Start Wireshark</span>
|
||||
</button>
|
@ -0,0 +1,24 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Server } from '../../../../../models/server';
|
||||
import { Link } from '../../../../../models/link';
|
||||
import { Project } from '../../../../../models/project';
|
||||
import { PacketCaptureService } from '../../../../../services/packet-capture.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-start-capture-on-started-link-action',
|
||||
templateUrl: './start-capture-on-started-link.component.html'
|
||||
})
|
||||
export class StartCaptureOnStartedLinkActionComponent {
|
||||
@Input() server: Server;
|
||||
@Input() project: Project;
|
||||
@Input() link: Link;
|
||||
|
||||
constructor(
|
||||
private packetCaptureService: PacketCaptureService
|
||||
) {}
|
||||
|
||||
startCapture() {
|
||||
var splittedFileName = this.link.capture_file_name.split('.');
|
||||
this.packetCaptureService.startCapture(this.server, this.project, this.link, splittedFileName[0]);
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ import { Server } from '../../../../../models/server';
|
||||
import { Link } from '../../../../../models/link';
|
||||
import { MatDialog } from '@angular/material';
|
||||
import { StartCaptureDialogComponent } from '../../../packet-capturing/start-capture/start-capture.component';
|
||||
import { Project } from '../../../../../models/project';
|
||||
|
||||
@Component({
|
||||
selector: 'app-start-capture-action',
|
||||
@ -10,6 +11,7 @@ import { StartCaptureDialogComponent } from '../../../packet-capturing/start-cap
|
||||
})
|
||||
export class StartCaptureActionComponent {
|
||||
@Input() server: Server;
|
||||
@Input() project: Project;
|
||||
@Input() link: Link;
|
||||
|
||||
constructor(private dialog: MatDialog) {}
|
||||
@ -21,6 +23,7 @@ export class StartCaptureActionComponent {
|
||||
});
|
||||
let instance = dialogRef.componentInstance;
|
||||
instance.server = this.server;
|
||||
instance.project = this.project;
|
||||
instance.link = this.link;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,4 @@
|
||||
<button mat-menu-item *ngIf="isNodeWithStartedStatus" (click)="suspendNodes()">
|
||||
<mat-icon>pause</mat-icon>
|
||||
<span>Suspend</span>
|
||||
</button>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user