diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 30493562..a92cf507 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -169,6 +169,10 @@ import { DateFilter } from './filters/dateFilter.pipe'; import { NameFilter } from './filters/nameFilter.pipe'; import { CustomAdaptersComponent } from './components/preferences/common/custom-adapters/custom-adapters.component'; import { NodesMenuComponent } from './components/project-map/nodes-menu/nodes-menu.component'; +import { PacketFiltersDialogComponent } from './components/project-map/packet-capturing/packet-filters/packet-filters.component'; +import { HelpDialogComponent } from './components/project-map/help-dialog/help-dialog.component'; +import { StartCaptureActionComponent } from './components/project-map/context-menu/actions/start-capture/start-capture-action.component'; +import { StartCaptureDialogComponent } from './components/project-map/packet-capturing/start-capture/start-capture.component'; if (environment.production) { Raven.config('https://b2b1cfd9b043491eb6b566fd8acee358@sentry.io/842726', { @@ -203,6 +207,8 @@ if (environment.production) { EditStyleActionComponent, EditTextActionComponent, DeleteActionComponent, + PacketFiltersActionComponent, + StartCaptureActionComponent, ProjectMapShortcutsComponent, SettingsComponent, PreferencesComponent, @@ -224,6 +230,7 @@ if (environment.production) { InstallSoftwareComponent, StyleEditorDialogComponent, TextEditorDialogComponent, + PacketFiltersDialogComponent, QemuPreferencesComponent, QemuVmTemplatesComponent, AddQemuVmTemplateComponent, @@ -257,6 +264,8 @@ if (environment.production) { VmwareTemplateDetailsComponent, AddVmwareTemplateComponent, DeleteConfirmationDialogComponent, + HelpDialogComponent, + StartCaptureDialogComponent, DeleteTemplateComponent, DockerTemplatesComponent, AddDockerTemplateComponent, @@ -355,9 +364,12 @@ if (environment.production) { ImportProjectDialogComponent, ConfirmationDialogComponent, StyleEditorDialogComponent, + PacketFiltersDialogComponent, TextEditorDialogComponent, SymbolsComponent, - DeleteConfirmationDialogComponent + DeleteConfirmationDialogComponent, + HelpDialogComponent, + StartCaptureDialogComponent ], bootstrap: [AppComponent] }) diff --git a/src/app/cartography/converters/map/link-to-map-link-converter.ts b/src/app/cartography/converters/map/link-to-map-link-converter.ts index c2946f08..f06d0172 100644 --- a/src/app/cartography/converters/map/link-to-map-link-converter.ts +++ b/src/app/cartography/converters/map/link-to-map-link-converter.ts @@ -15,6 +15,7 @@ export class LinkToMapLinkConverter implements Converter { mapLink.captureFileName = link.capture_file_name; mapLink.captureFilePath = link.capture_file_path; mapLink.capturing = link.capturing; + mapLink.filters = link.filters; mapLink.linkType = link.link_type; mapLink.nodes = link.nodes.map(linkNode => this.linkNodeToMapLinkNode.convert(linkNode, { link_id: link.link_id })); mapLink.projectId = link.project_id; diff --git a/src/app/cartography/converters/map/map-link-to-link-converter.ts b/src/app/cartography/converters/map/map-link-to-link-converter.ts index e7db39dc..1c640e61 100644 --- a/src/app/cartography/converters/map/map-link-to-link-converter.ts +++ b/src/app/cartography/converters/map/map-link-to-link-converter.ts @@ -15,6 +15,7 @@ export class MapLinkToLinkConverter implements Converter { link.capture_file_name = mapLink.captureFileName; link.capture_file_path = mapLink.captureFilePath; link.capturing = mapLink.capturing; + link.filters = mapLink.filters; link.link_type = mapLink.linkType; link.nodes = mapLink.nodes.map(mapLinkNode => this.mapLinkNodeToMapLinkNode.convert(mapLinkNode)); link.project_id = mapLink.projectId; diff --git a/src/app/cartography/models/map/map-link.ts b/src/app/cartography/models/map/map-link.ts index 0a0fb3a8..aaf9e555 100644 --- a/src/app/cartography/models/map/map-link.ts +++ b/src/app/cartography/models/map/map-link.ts @@ -1,12 +1,14 @@ import { MapLinkNode } from './map-link-node'; import { MapNode } from './map-node'; import { Indexed } from '../../datasources/map-datasource'; +import { Filter } from '../../../models/filter'; export class MapLink implements Indexed { id: string; captureFileName: string; captureFilePath: string; capturing: boolean; + filters?: Filter; linkType: string; nodes: MapLinkNode[]; projectId: string; diff --git a/src/app/cartography/widgets/link.ts b/src/app/cartography/widgets/link.ts index 1c88c5ec..252706e0 100644 --- a/src/app/cartography/widgets/link.ts +++ b/src/app/cartography/widgets/link.ts @@ -32,6 +32,19 @@ export class LinkWidget implements Widget { return `translate (${translation.dx}, ${translation.dy})`; }); + link_body.select('.svg-icon').remove(); + + link_body + .filter(l => { return l.filters.frequency_drop }) + .append('g') + .attr('class', 'svg-icon') + .attr('transform', link => { + return `translate (${(link.source.x + link.target.x)/2}, ${(link.source.y + link.target.y)/2})` + }) + .attr('viewBox', '0 0 20 20') + .append('path') + .attr('d', "M18.125,15.804l-4.038-4.037c0.675-1.079,1.012-2.308,1.01-3.534C15.089,4.62,12.199,1.75,8.584,1.75C4.815,1.75,1.982,4.726,2,8.286c0.021,3.577,2.908,6.549,6.578,6.549c1.241,0,2.417-0.347,3.44-0.985l4.032,4.026c0.167,0.166,0.43,0.166,0.596,0l1.479-1.478C18.292,16.234,18.292,15.968,18.125,15.804 M8.578,13.99c-3.198,0-5.716-2.593-5.733-5.71c-0.017-3.084,2.438-5.686,5.74-5.686c3.197,0,5.625,2.493,5.64,5.624C14.242,11.548,11.621,13.99,8.578,13.99 M16.349,16.981l-3.637-3.635c0.131-0.11,0.721-0.695,0.876-0.884l3.642,3.639L16.349,16.981z"); + const serial_link_widget = new SerialLinkWidget(); serial_link_widget.draw(link_body_merge); diff --git a/src/app/components/project-map/context-menu/actions/packet-filters-action/packet-filters-action.component.html b/src/app/components/project-map/context-menu/actions/packet-filters-action/packet-filters-action.component.html new file mode 100644 index 00000000..ea808178 --- /dev/null +++ b/src/app/components/project-map/context-menu/actions/packet-filters-action/packet-filters-action.component.html @@ -0,0 +1,4 @@ + diff --git a/src/app/components/project-map/context-menu/actions/packet-filters-action/packet-filters-action.component.spec.ts b/src/app/components/project-map/context-menu/actions/packet-filters-action/packet-filters-action.component.spec.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/app/components/project-map/context-menu/actions/packet-filters-action/packet-filters-action.component.ts b/src/app/components/project-map/context-menu/actions/packet-filters-action/packet-filters-action.component.ts new file mode 100644 index 00000000..54dca3e0 --- /dev/null +++ b/src/app/components/project-map/context-menu/actions/packet-filters-action/packet-filters-action.component.ts @@ -0,0 +1,30 @@ +import { Component, Input } from "@angular/core"; +import { Link } from '../../../../../models/link'; +import { Server } from '../../../../../models/server'; +import { Project } from '../../../../../models/project'; +import { MatDialog } from '@angular/material'; +import { PacketFiltersDialogComponent } from '../../../packet-capturing/packet-filters/packet-filters.component'; + +@Component({ + selector: 'app-packet-filters-action', + templateUrl: './packet-filters-action.component.html' +}) +export class PacketFiltersActionComponent { + @Input() server: Server; + @Input() project: Project; + @Input() link: Link; + + constructor(private dialog: MatDialog) {} + + openPacketFilters() { + const dialogRef = this.dialog.open(PacketFiltersDialogComponent, { + width: '900px', + height: '400px', + autoFocus: false + }); + let instance = dialogRef.componentInstance; + instance.server = this.server; + instance.project = this.project; + instance.link = this.link; + } +} diff --git a/src/app/components/project-map/context-menu/actions/start-capture/start-capture-action.component.html b/src/app/components/project-map/context-menu/actions/start-capture/start-capture-action.component.html new file mode 100644 index 00000000..551336cf --- /dev/null +++ b/src/app/components/project-map/context-menu/actions/start-capture/start-capture-action.component.html @@ -0,0 +1,4 @@ + diff --git a/src/app/components/project-map/context-menu/actions/start-capture/start-capture-action.component.spec.ts b/src/app/components/project-map/context-menu/actions/start-capture/start-capture-action.component.spec.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/app/components/project-map/context-menu/actions/start-capture/start-capture-action.component.ts b/src/app/components/project-map/context-menu/actions/start-capture/start-capture-action.component.ts new file mode 100644 index 00000000..726e42bf --- /dev/null +++ b/src/app/components/project-map/context-menu/actions/start-capture/start-capture-action.component.ts @@ -0,0 +1,26 @@ +import { Component, Input } from '@angular/core'; +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'; + +@Component({ + selector: 'app-start-capture-action', + templateUrl: './start-capture-action.component.html' +}) +export class StartCaptureActionComponent { + @Input() server: Server; + @Input() link: Link; + + constructor(private dialog: MatDialog) {} + + startCapture() { + const dialogRef = this.dialog.open(StartCaptureDialogComponent, { + width: '400px', + autoFocus: false + }); + let instance = dialogRef.componentInstance; + instance.server = this.server; + instance.link = this.link; + } +} diff --git a/src/app/components/project-map/context-menu/context-menu.component.html b/src/app/components/project-map/context-menu/context-menu.component.html index 0060c9f1..9617c80e 100644 --- a/src/app/components/project-map/context-menu/context-menu.component.html +++ b/src/app/components/project-map/context-menu/context-menu.component.html @@ -26,6 +26,17 @@ [nodes]="nodes" [drawings]="drawings" > + + {{title}} + + + +
+ +
diff --git a/src/app/components/project-map/help-dialog/help-dialog.component.scss b/src/app/components/project-map/help-dialog/help-dialog.component.scss new file mode 100644 index 00000000..f382464c --- /dev/null +++ b/src/app/components/project-map/help-dialog/help-dialog.component.scss @@ -0,0 +1,7 @@ +.message { + margin-bottom: 10px; +} + +.description { + color: #b0bec5; +} diff --git a/src/app/components/project-map/help-dialog/help-dialog.component.spec.ts b/src/app/components/project-map/help-dialog/help-dialog.component.spec.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/app/components/project-map/help-dialog/help-dialog.component.ts b/src/app/components/project-map/help-dialog/help-dialog.component.ts new file mode 100644 index 00000000..7145c03c --- /dev/null +++ b/src/app/components/project-map/help-dialog/help-dialog.component.ts @@ -0,0 +1,21 @@ +import { Component, Input } from '@angular/core'; +import { MatDialogRef } from '@angular/material'; +import { Message } from '../../../models/message'; + +@Component({ + selector: 'app-help-dialog', + templateUrl: './help-dialog.component.html', + styleUrls: ['./help-dialog.component.scss'] +}) +export class HelpDialogComponent { + @Input() title: string; + @Input() messages: Message[]; + + constructor( + public dialogRef: MatDialogRef, + ) {} + + onCloseClick() { + this.dialogRef.close(); + } +} diff --git a/src/app/components/project-map/packet-capturing/packet-filters/packet-filters.component.html b/src/app/components/project-map/packet-capturing/packet-filters/packet-filters.component.html new file mode 100644 index 00000000..6bb4b4ce --- /dev/null +++ b/src/app/components/project-map/packet-capturing/packet-filters/packet-filters.component.html @@ -0,0 +1,42 @@ +

Packet filters

+ + + +
+ + + +
+ +
diff --git a/src/app/components/project-map/packet-capturing/packet-filters/packet-filters.component.scss b/src/app/components/project-map/packet-capturing/packet-filters/packet-filters.component.scss new file mode 100644 index 00000000..4048e8c8 --- /dev/null +++ b/src/app/components/project-map/packet-capturing/packet-filters/packet-filters.component.scss @@ -0,0 +1,59 @@ +.item { + height: 25px; + font-size: 10pt; + margin-bottom: 10px; +} + +.item-name { + margin-bottom: 10px; +} + +.item-value { + width: 100%; + margin-bottom: 10px; +} + +.input-field { + width: 100%; + margin-top: 10px; +} + +.divider { + width: fit-content; + flex: 1 1 auto; +} + +.input-color { + padding: 0px; + border-width: 0px; + width: 100%; + background-color: transparent; + outline: none; +} + +input:focus { + outline: none; +} + +input[type="color"] { + -webkit-appearance: none; + border: none; + height: 25px; +} + +input[type="color"]::-webkit-color-swatch-wrapper { + padding: 0; +} + +input[type="color"]::-webkit-color-swatch { + border: none; +} + +.modal-form-container { + display: flex; + flex-direction: column; +} + +.modal-form-container > * { + width: 100%; +} diff --git a/src/app/components/project-map/packet-capturing/packet-filters/packet-filters.component.spec.ts b/src/app/components/project-map/packet-capturing/packet-filters/packet-filters.component.spec.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/app/components/project-map/packet-capturing/packet-filters/packet-filters.component.ts b/src/app/components/project-map/packet-capturing/packet-filters/packet-filters.component.ts new file mode 100644 index 00000000..19cf6e7f --- /dev/null +++ b/src/app/components/project-map/packet-capturing/packet-filters/packet-filters.component.ts @@ -0,0 +1,96 @@ +import { Component, OnInit } from '@angular/core'; +import { Link } from '../../../../models/link'; +import { Server } from '../../../../models/server'; +import { Project } from '../../../../models/project'; +import { MatDialogRef, MatDialog } from '@angular/material'; +import { LinkService } from '../../../../services/link.service'; +import { FilterDescription } from '../../../../models/filter-description'; +import { HelpDialogComponent } from '../../help-dialog/help-dialog.component'; +import { Message } from '../../../../models/message'; +import { Filter } from '../../../../models/filter'; + +@Component({ + selector: 'app-packet-filters', + templateUrl: './packet-filters.component.html', + styleUrls: ['./packet-filters.component.scss'] +}) +export class PacketFiltersDialogComponent implements OnInit{ + server: Server; + project: Project; + link: Link; + filters: Filter; + availableFilters: FilterDescription[]; + + constructor( + private dialogRef: MatDialogRef, + private linkService: LinkService, + private dialog: MatDialog + ) {} + + ngOnInit(){ + this.linkService.getLink(this.server, this.link.project_id, this.link.link_id).subscribe((link: Link) => { + this.link = link; + this.filters = { + bpf: [], + corrupt: [0], + delay: [0, 0], + frequency_drop: [0], + packet_loss: [0] + }; + + if (this.link.filters) { + this.filters.bpf = this.link.filters.bpf ? this.link.filters.bpf : []; + this.filters.corrupt = this.link.filters.corrupt ? this.link.filters.corrupt : [0]; + this.filters.delay = this.link.filters.delay ? this.link.filters.delay : [0, 0]; + this.filters.frequency_drop = this.link.filters.frequency_drop ? this.link.filters.frequency_drop : [0]; + this.filters.packet_loss = this.link.filters.packet_loss ? this.link.filters.packet_loss : [0]; + } + }); + + this.linkService.getAvailableFilters(this.server, this.link).subscribe((availableFilters: FilterDescription[]) => { + this.availableFilters = availableFilters; + }); + } + + onNoClick() { + this.dialogRef.close(); + } + + onResetClick() { + this.link.filters = { + bpf: [], + corrupt: [0], + delay: [0, 0], + frequency_drop: [0], + packet_loss: [0] + }; + + this.linkService.updateLink(this.server, this.link).subscribe((link: Link) => { + this.dialogRef.close(); + }); + } + + onYesClick() { + this.link.filters = this.filters; + this.linkService.updateLink(this.server, this.link).subscribe((link: Link) => { + this.dialogRef.close(); + }); + } + + onHelpClick() { + const dialogRef = this.dialog.open(HelpDialogComponent, { + width: '500px', + autoFocus: false + }); + let instance = dialogRef.componentInstance; + instance.title = 'Help for filters'; + let messages: Message[] = []; + this.availableFilters.forEach((filter: FilterDescription) => { + messages.push({ + name: filter.name, + description: filter.description + }); + }); + instance.messages = messages; + } +} diff --git a/src/app/components/project-map/packet-capturing/start-capture/start-capture.component.html b/src/app/components/project-map/packet-capturing/start-capture/start-capture.component.html new file mode 100644 index 00000000..22a2a4af --- /dev/null +++ b/src/app/components/project-map/packet-capturing/start-capture/start-capture.component.html @@ -0,0 +1,24 @@ +

Packet capture

+ + + +
+ + +
diff --git a/src/app/components/project-map/packet-capturing/start-capture/start-capture.component.scss b/src/app/components/project-map/packet-capturing/start-capture/start-capture.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/components/project-map/packet-capturing/start-capture/start-capture.component.spec.ts b/src/app/components/project-map/packet-capturing/start-capture/start-capture.component.spec.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/app/components/project-map/packet-capturing/start-capture/start-capture.component.ts b/src/app/components/project-map/packet-capturing/start-capture/start-capture.component.ts new file mode 100644 index 00000000..13314544 --- /dev/null +++ b/src/app/components/project-map/packet-capturing/start-capture/start-capture.component.ts @@ -0,0 +1,56 @@ +import { Component, OnInit } from '@angular/core'; +import { Server } from '../../../../models/server'; +import { Link } from '../../../../models/link'; +import { MatDialogRef } from '@angular/material'; +import { PacketFiltersDialogComponent } from '../packet-filters/packet-filters.component'; +import { LinkService } from '../../../../services/link.service'; +import { CapturingSettings } from '../../../../models/capturingSettings'; + +@Component({ + selector: 'app-start-capture', + templateUrl: './start-capture.component.html', + styleUrls: ['./start-capture.component.scss'] +}) +export class StartCaptureDialogComponent implements OnInit { + server: Server; + link: Link; + linkTypes = []; + + linkType: string; + fileName: string; + + constructor( + private dialogRef: MatDialogRef, + private linkService: LinkService + ) {} + + ngOnInit() { + if (this.link.link_type === 'ethernet') { + this.linkTypes = [ + ["Ethernet", "DLT_EN10MB"] + ]; + } else { + this.linkTypes = [ + ["Cisco HDLC", "DLT_C_HDLC"], + ["Cisco PPP", "DLT_PPP_SERIAL"], + ["Frame Relay", "DLT_FRELAY"], + ["ATM", "DLT_ATM_RFC1483"] + ]; + } + } + + onYesClick() { + let captureSettings: CapturingSettings = { + capture_file_name: this.fileName, + data_link_type: this.linkType + }; + + this.linkService.startCaptureOnLink(this.server, this.link, captureSettings).subscribe(() => { + this.dialogRef.close(); + }); + } + + onNoClick() { + this.dialogRef.close(); + } +} diff --git a/src/app/components/snapshots/snapshot-menu-item/snapshot-menu-item.component.ts b/src/app/components/snapshots/snapshot-menu-item/snapshot-menu-item.component.ts index 78a806b8..a346e751 100644 --- a/src/app/components/snapshots/snapshot-menu-item/snapshot-menu-item.component.ts +++ b/src/app/components/snapshots/snapshot-menu-item/snapshot-menu-item.component.ts @@ -48,11 +48,6 @@ export class SnapshotMenuItemComponent implements OnInit { (created_snapshot: Snapshot) => { this.toaster.success(`Snapshot '${snapshot.name}' has been created.`); progress.close(); - }, - response => { - const error = response.json(); - this.toaster.error(`Cannot create snapshot: ${error.message}`); - progress.close(); } ); diff --git a/src/app/material.imports.ts b/src/app/material.imports.ts index 9dc2260b..fdc33412 100644 --- a/src/app/material.imports.ts +++ b/src/app/material.imports.ts @@ -19,7 +19,8 @@ import { MatTooltipModule, MatStepperModule, MatRadioModule, - MatGridListModule + MatGridListModule, + MatTabsModule } from '@angular/material'; export const MATERIAL_IMPORTS = [ @@ -43,5 +44,6 @@ export const MATERIAL_IMPORTS = [ MatTooltipModule, MatStepperModule, MatRadioModule, - MatGridListModule + MatGridListModule, + MatTabsModule ]; diff --git a/src/app/models/capturingSettings.ts b/src/app/models/capturingSettings.ts new file mode 100644 index 00000000..7cf85ba8 --- /dev/null +++ b/src/app/models/capturingSettings.ts @@ -0,0 +1,4 @@ +export class CapturingSettings { + capture_file_name: string; + data_link_type: string; +} diff --git a/src/app/models/filter-description.ts b/src/app/models/filter-description.ts new file mode 100644 index 00000000..77238e27 --- /dev/null +++ b/src/app/models/filter-description.ts @@ -0,0 +1,14 @@ +export class FilterDescription { + description: string; + name: string; + parameters: Parameter[]; + type: string; +} + +interface Parameter { + maximum?: number; + minimum?: number; + name: string; + type: string; + unit?: string; +} diff --git a/src/app/models/filter.ts b/src/app/models/filter.ts new file mode 100644 index 00000000..5a27c67a --- /dev/null +++ b/src/app/models/filter.ts @@ -0,0 +1,7 @@ +export class Filter { + bpf?: string[]; + corrupt?: number[]; + delay?: number[]; + frequency_drop?: number[]; + packet_loss?: number[]; +} diff --git a/src/app/models/link.ts b/src/app/models/link.ts index d33d3845..afa7dc47 100644 --- a/src/app/models/link.ts +++ b/src/app/models/link.ts @@ -1,10 +1,12 @@ import { Node } from '../cartography/models/node'; import { LinkNode } from './link-node'; +import { Filter } from './filter'; export class Link { capture_file_name: string; capture_file_path: string; capturing: boolean; + filters?: Filter; link_id: string; link_type: string; nodes: LinkNode[]; diff --git a/src/app/models/message.ts b/src/app/models/message.ts new file mode 100644 index 00000000..3d21b1cc --- /dev/null +++ b/src/app/models/message.ts @@ -0,0 +1,4 @@ +export class Message { + name?: string; + description: string; +} diff --git a/src/app/services/link.service.ts b/src/app/services/link.service.ts index 37d2ebfa..89735923 100644 --- a/src/app/services/link.service.ts +++ b/src/app/services/link.service.ts @@ -7,16 +7,13 @@ import { HttpServer } from './http-server.service'; import { Port } from '../models/port'; import { Link } from '../models/link'; import { LinkNode } from '../models/link-node'; +import { FilterDescription } from '../models/filter-description'; +import { CapturingSettings } from '../models/capturingSettings'; @Injectable() export class LinkService { constructor(private httpServer: HttpServer) {} - deleteLink(server: Server, link: Link) { - //return this.httpServer.delete(server, `/compute/projects/${link.project_id}/vpcs/nodes/${link.nodes[0].node_id}/adapters/0/ports/0/nio`) - return this.httpServer.delete(server, `/projects/${link.project_id}/links/${link.link_id}`) - } - createLink(server: Server, source_node: Node, source_port: Port, target_node: Node, target_port: Port) { return this.httpServer.post(server, `/projects/${source_node.project_id}/links`, { nodes: [ @@ -34,6 +31,22 @@ export class LinkService { }); } + getLink(server: Server, projectId: string, linkId: string) { + return this.httpServer.get(server, `/projects/${projectId}/links/${linkId}`); + } + + deleteLink(server: Server, link: Link) { + return this.httpServer.delete(server, `/projects/${link.project_id}/links/${link.link_id}`) + } + + updateLink(server: Server, link: Link) { + return this.httpServer.put(server, `/projects/${link.project_id}/links/${link.link_id}`, link); + } + + getAvailableFilters(server: Server, link: Link) { + return this.httpServer.get(server, `/projects/${link.project_id}/links/${link.link_id}/available_filters`); + } + updateNodes(server: Server, link: Link, nodes: LinkNode[]) { const requestNodes = nodes.map(linkNode => { return { @@ -52,4 +65,16 @@ export class LinkService { return this.httpServer.put(server, `/projects/${link.project_id}/links/${link.link_id}`, { nodes: requestNodes }); } + + startCaptureOnLink(server: Server, link: Link, settings: CapturingSettings) { + return this.httpServer.post(server, `/projects/${link.project_id}/links/${link.link_id}/start_capture`, settings); + } + + stopCaptureOnLink(server: Server, link: Link) { + return this.httpServer.post(server, `/projects/${link.project_id}/links/${link.link_id}/stop_capture`, {}); + } + + streamPcap(server: Server, link: Link) { + return this.httpServer.get(server, `/projects/${link.project_id}/links/${link.link_id}/pcap`) + } }