discovery

This commit is contained in:
insanity 2018-06-21 18:35:24 +09:00
parent 853141dd0f
commit aa810aef68
26 changed files with 1971 additions and 81 deletions

View File

@ -24,7 +24,7 @@ export const Anim = [
trigger('discoveryResultAnim', [
transition('void => *', [
query('*', style({ opacity: 0 }), { optional: true }),
query('*', stagger('30ms', [
query('*', stagger('60ms', [
animate('0.08s ease-in', keyframes([
style({ opacity: 0, transform: 'translateX(-95%)', offset: 0 }),
style({ opacity: .5, transform: 'translateX(95px)', offset: 0.3 }),

View File

@ -0,0 +1,83 @@
<of-error-message [error]="error$ | async" [closable]="false"></of-error-message>
<of-block-progressbar [target]="content" [pending]="pending$ | async"></of-block-progressbar>
<p-panel #content [showHeader]="false" class="block-panel">
<p-messages [(value)]="msgs"></p-messages>
<p-tree [value]="zoneNode" layout="vertical">
<!-- ZONE node template -->
<ng-template let-node pTemplate="ZONE">
<!-- 이미 저장된 Infra인 Node-->
<div *ngIf="node.data.date">
<p-toggleButton [disabled]="node.data.target" onLabel="{{node.label}}" offLabel="{{node.label}}" onIcon="fa-check" offIcon="fa-square"
[style]="{'width':'200px'}" (onChange)="onTargetSelect($event, node)"></p-toggleButton> {{node.data.date | date: 'yy/MM/dd'}}
</div>
</ng-template>
<!-- HOST node template -->
<ng-template let-node pTemplate="HOST">
<!-- 이미 저장된 Infra인 Node-->
<div *ngIf="node.data.date">
<p-toggleButton [disabled]="node.data.target" onLabel="{{node.label}}" offLabel="{{node.label}}" onIcon="fa-check" offIcon="fa-square"
[style]="{'width':'200px'}" (onChange)="onTargetSelect($event, node)"></p-toggleButton> {{node.data.date | date: 'yy/MM/dd'}}
</div>
<div *ngIf="!node.data.date" @discoveryResultAnim>
<p-toggleButton onLabel="{{node.label}}" offLabel="{{node.label}}" onIcon="fa-check" offIcon="fa-square" [style]="{'width':'200px'}"
(onChange)="onTargetSelect($event, node)"></p-toggleButton> New!!
</div>
<!-- <div @discoveryResultAnim>
<div *ngIf="checkHighlight(node.label, 0) else unhighlightHost">
<p-toggleButton [disabled]="checkExistTarget(node.data.target)" onLabel="{{node.label}}" offLabel="{{node.label}}" onIcon="fa-check"
offIcon="fa-square" [style]="{'width':'200px'}" (onChange)="onTargetSelect($event, node)"></p-toggleButton>
</div>
<ng-template #unhighlightHost>
<p-toggleButton [disabled]="checkExistTarget(node.data.target)" onLabel="{{node.label}}" offLabel="{{node.label}}" onIcon="fa-check"
offIcon="fa-square" [style]="{'width':'200px', 'opacity': '0.2'}" (onChange)="onTargetSelect($event, node)"></p-toggleButton>
</ng-template>
</div> -->
</ng-template>
<!-- SERVICE node template -->
<ng-template let-node pTemplate="SERVICE">
<!-- 이미 저장된 Infra인 Node-->
<div *ngIf="node.data.date">
<p-toggleButton [disabled]="node.data.target" onLabel="{{node.label}}" offLabel="{{node.label}}" onIcon="fa-check" offIcon="fa-square"
[style]="{'width':'400px'}" (onChange)="onTargetSelect($event, node)"></p-toggleButton> {{node.data.date | date: 'yy/MM/dd'}}
</div>
<div *ngIf="!node.data.date" @discoveryResultAnim>
<p-toggleButton onLabel="{{node.label}}" offLabel="{{node.label}}" onIcon="fa-check" offIcon="fa-square" [style]="{'width':'200px'}"
(onChange)="onTargetSelect($event, node)"></p-toggleButton> New!!
</div>
<!-- <div @discoveryResultAnim>
<div *ngIf="checkHighlight(node.data.name, 1) else unhighlightServ">
<p-toggleButton [disabled]="checkExistTarget(node.data.target)" onLabel="{{node.label}} {{node.data.portType}}" offLabel="{{node.label}} {{node.data.portType}} {{node.data.portNumber}}"
onIcon="fa-check" offIcon="fa-square" [style]="{'width':'300px'}" (onChange)="onTargetSelect($event, node)"></p-toggleButton>
</div>
<ng-template #unhighlightServ>
<p-toggleButton [disabled]="checkExistTarget(node.data.target)" onLabel="{{node.label}} {{node.data.portType}}" offLabel="{{node.label}} {{node.data.portType}} {{node.data.portNumber}}"
onIcon="fa-check" offIcon="fa-square" [style]="{'width':'300px', 'opacity': '0.2'}" (onChange)="onTargetSelect($event, node)"></p-toggleButton>
</ng-template>
</div> -->
</ng-template>
</p-tree>
<button class="ui-button-width-fit ui-float-right ui-top-space-10" [disabled]="selectedItems.length === 0" type="button"
label="Save" icon="ui-icon-close" pButton (click)="saveTargets()"></button>
</p-panel>
<p-dialog header="Title" [(visible)]="targetSaved" [modal]="true" [responsive]="true" [width]="600" [minWidth]="200" [minY]="70"
[closeOnEscape]="false">
<span>Target 지정이 완료되었습니다. 이어서 Sensor를 등록하시면 좋겠다능</span>
<p-footer>
<button type="button" pButton label="메인으로"></button>
<button type="button" pButton label="Target으로"></button>
<button type="button" pButton label="InfraMap으로"></button>
</p-footer>
</p-dialog>

View File

@ -0,0 +1,420 @@
import {
Component,
Input,
OnChanges,
SimpleChanges,
} from '@angular/core';
import { Host, Service } from '@overflow/commons-typescript/model/discovery';
import { TreeNode, Message, Tree } from 'primeng/primeng';
import { ProbeHost } from '@overflow/commons-typescript/model/probe';
import { Anim } from './animation';
import { TargetService } from '@overflow/target/service/target.service';
import { InfraService, InfraHost, Infra, MetaInfraTypeEnum, toMetaInfraTypeEnum, toMetaInfraType, InfraZone, Target, Page, PageParams } from '@overflow/commons-typescript';
import { InfraService as InfraManageService } from '../../infra/service/infra.service';
import { Observable, of, Subscription } from 'rxjs';
import { catchError, map, tap, take } from 'rxjs/operators';
@Component({
selector: 'of-discovery-infra-tree',
templateUrl: './discovery-infra-tree.component.html',
animations: Anim,
providers: [
TargetService,
InfraManageService
]
})
export class DiscoveryInfraTreeComponent implements OnChanges {
@Input() probeHost: ProbeHost;
@Input() filterWord: string;
@Input() filterServices: Map<string, boolean>;
targets: Target[];
infraZones: InfraZone[];
infraHosts: InfraHost[];
infraServices: InfraService[];
zoneNode: TreeNode[];
hostNode: TreeNode[];
selectedItems: TreeNode[] = [];
msgs: Message[];
savedInfras: Infra[];
discoveredHosts: Host[] = [];
discoveredServices: Service[] = [];
infraSaved: boolean;
targetSaved: boolean;
pending$: Observable<boolean>;
error$: Observable<any>;
constructor(
private infraManageService: InfraManageService,
private targetService: TargetService
) {
}
ngOnChanges(changes: SimpleChanges) {
if (changes['probeHost']) {
this.getTargets();
}
}
getInfras() {
const pageParams: PageParams = {
pageNo: 0,
countPerPage: 99999,
sortCol: 'id',
sortDirection: 'descending'
};
this.infraManageService.readAllByProbeID(this.probeHost.probe.id, pageParams)
.pipe(
tap(() => {
this.pending$ = of(true);
}),
map((infraPage: Page<Infra>) => {
this.generateTree(infraPage.content);
}),
catchError(error => {
this.error$ = of(error);
return of();
}),
tap(() => {
this.pending$ = of(false);
}),
take(1),
).subscribe();
}
getTargets() {
const pageParams: PageParams = {
pageNo: 0,
countPerPage: 99999,
sortCol: 'id',
sortDirection: 'descending'
};
this.targetService.readAllByProbeID(this.probeHost.probe.id, pageParams)
.pipe(
tap(() => {
this.pending$ = of(true);
}),
map((targetPage: Page<Target>) => {
this.targets = targetPage.content;
this.getInfras();
}),
catchError(error => {
this.error$ = of(error);
return of();
}),
tap(() => {
this.pending$ = of(false);
}),
take(1),
).subscribe();
}
generateTree(infras: Infra[]) {
this.zoneNode = [];
this.hostNode = [];
this.infraZones = [];
this.infraHosts = [];
this.infraServices = [];
for (const infra of infras) {
if (infra.metaInfraType.key === toMetaInfraType(MetaInfraTypeEnum.ZONE).key) {
this.infraZones.push(infra);
} else if (infra.metaInfraType.key === toMetaInfraType(MetaInfraTypeEnum.HOST).key) {
this.infraHosts.push(infra);
} else if (infra.metaInfraType.key === toMetaInfraType(MetaInfraTypeEnum.SERVICE).key) {
this.infraServices.push(infra);
}
}
this.infraZones.forEach(infraZone => {
this.addInfraZone(infraZone);
});
this.infraHosts.forEach(infraHost => {
this.addInfraHost(infraHost);
});
this.infraServices.forEach(infraService => {
this.addInfraService(infraService);
});
}
addInfraZone(infraZone: InfraZone) {
const target: Target = this.checkAlreadyTarget(infraZone.id);
this.zoneNode.push({
label: this.probeHost.probe.cidr,
type: 'ZONE',
data: {
target: target,
date: infraZone.createDate,
object: infraZone,
infraID: infraZone.id,
},
children: this.hostNode,
expanded: true
});
}
addInfraHost(infraHost: InfraHost) {
const target: Target = this.checkAlreadyTarget(infraHost.id);
const ip = infraHost.infraHostIPs[0].address
const idx = this.findHostIndex(ip);
this.hostNode.splice(idx, 0, {
type: 'HOST',
label: ip,
data: {
target: target,
date: infraHost.createDate,
ip: this.convertIPtoNumber(ip),
object: infraHost,
infraID: infraHost.id,
},
expanded: true,
children: []
});
}
addInfraService(infraService: InfraService) {
console.log(infraService);
const target: Target = this.checkAlreadyTarget(infraService.id);
const targetHostNode = this.findHostNodeByService(infraService.infraHostPort.infraHostIP.address);
// const idx = this.findServiceIndex(targetHostNode.children, infraService.metaTargetServiceType.name);
targetHostNode.children.push({
type: 'SERVICE',
label: 'metaTargetServiceType이 현재 null ' + ' (' + infraService.infraHostPort.port + ')',
data: {
target: target,
date: infraService.createDate,
portType: infraService.infraHostPort.metaPortType.name,
portNumber: infraService.infraHostPort.port,
object: infraService,
infraID: infraService.id,
},
});
}
checkAlreadyTarget(infraID: number): Target {
return this.targets.find(target => target.infra.id === infraID);
}
discoveryStarted(startedAt: Date) {
console.log('Discovery Started at: ' + startedAt);
this.pending$ = of(true);
}
discoveryStopped(stoppedAt: Date) {
console.log('Discovery Stopped at: ' + stoppedAt);
this.saveDiscoveredInfras();
this.pending$ = of(false);
}
saveDiscoveredInfras() {
this.infraManageService.registDiscoverd(
this.probeHost.probe.id,
this.discoveredHosts,
this.discoveredServices)
.pipe(
tap(() => {
this.infraSaved = false;
this.pending$ = of(true);
}),
map((infras: Infra[]) => {
if (infras) {
this.savedInfras = infras;
this.msgs = [];
this.msgs.push({
severity: 'success',
summary: infras.length + '개의 Infra가 새로 저장되었습니다. 모니터링 대상(들)을 선택 후 저장하세요.',
});
}
this.pending$ = of(false);
}),
catchError(error => {
this.error$ = of(error);
return of();
}),
tap(() => {
this.infraSaved = true;
}),
take(1),
).subscribe();
}
addHost(host: Host) {
let exist = false;
this.infraHosts.forEach(infraHost => {
if (infraHost.infraHostIPs[0].address === host.address) {
exist = true;
return;
}
});
if (exist) {
return;
}
const idx = this.findHostIndex(host.address);
this.hostNode.splice(idx, 0, {
type: 'HOST',
label: host.address,
data: {
target: null,
date: null,
ip: this.convertIPtoNumber(host.address),
object: host,
infraID: null,
},
expanded: true,
children: []
});
this.discoveredHosts.push(host);
}
addService(service: Service) {
let exist = false;
this.infraServices.forEach(infraService => {
if (//infraService.metaTargetServiceType.name === service.name &&
infraService.infraHostPort.port === service.port.portNumber &&
infraService.infraHostPort.infraHostIP.address === service.port.host.address
) {
exist = true;
return;
}
});
if (exist) {
return;
}
const targetHostNode = this.findHostNodeByService(service.port.host.address);
// const idx = this.findServiceIndex(targetHostNode.children, infraService.metaTargetServiceType.name);
targetHostNode.children.push({
type: 'SERVICE',
label: service.name + ' (' + service.port.portNumber + ')',
data: {
target: null,
date: null,
portType: service.port.metaPortType.name,
portNumber: service.port.portNumber,
object: service,
InfraID: null,
},
});
this.discoveredServices.push(service);
}
findHostIndex(hostIP: string): number {
let index = 0;
this.hostNode.forEach(node => {
if (node.data.ip < this.convertIPtoNumber(hostIP)) {
index++;
}
});
return index;
}
findServiceIndex(serviceNodes: TreeNode[], serviceName: string) {
let index = 0;
serviceNodes.forEach(node => {
// if (node.data.portNumber < service.port.portNumber) {
// index++;
// }
if (node.data.name.toUpperCase().localeCompare(serviceName.toUpperCase()) === -1) {
index++;
}
});
return index;
}
findHostNodeByService(serviceAddress: string) {
let targetHost = null;
this.hostNode.forEach((value, i) => {
if (value.data.ip === this.convertIPtoNumber(serviceAddress)) {
targetHost = value;
return;
}
});
return targetHost;
}
convertIPtoNumber(ip: string) {
return ip.split('.').map((octet, index, array) => {
return parseInt(octet) * Math.pow(256, (array.length - index - 1));
}).reduce((prev, curr) => {
return prev + curr;
});
}
checkHighlight(label: string, type: number) {
let highlight = true;
if (this.filterWord && (label.toUpperCase().indexOf(this.filterWord.toUpperCase()) < 0)) {
highlight = false;
}
if (type === 1 && this.filterServices[label] === false) {
highlight = false;
}
return highlight;
}
onTargetSelect(e, node: TreeNode) {
if (e.checked) {
this.selectedItems.push(node);
} else {
const index = this.selectedItems.indexOf(node);
this.selectedItems.splice(index, 1);
}
}
saveTargets() {
const targets: Target[] = [];
this.selectedItems.forEach(node => {
const infraID = node.data.infraID;
if (null === infraID) { // 새로 발견된 Host / Service
}
// const infra: Infra = value;
// let name: string;
// if (value.metaInfraType === toMetaInfraType(MetaInfraTypeEnum.ZONE)) {
// const infraZone: InfraZone = value;
// name = infraZone.network;
// } else if (value.metaInfraType === toMetaInfraType(MetaInfraTypeEnum.HOST)) {
// const infraHost: InfraHost = value;
// name = infraHost.infraHostIPs[0].address;
// } else if (value.metaInfraType === toMetaInfraType(MetaInfraTypeEnum.SERVICE)) {
// const infraService: InfraService = value;
// name = infraService.metaInfraType.name;
// }
const target: Target = {
infra: {
id: infraID
},
name: '',
sensorCount: 0,
};
targets.push(target);
});
console.log(targets);
// this.targetService.registAll(targets, this.probeHost.probe.id)
// .pipe(
// tap(() => {
// }),
// map((targets: Target[]) => {
// if (targets) {
// this.targetSaved = true;
// }
// }),
// catchError(error => {
// this.error$ = of(error);
// return of();
// }),
// tap(() => {
// }),
// take(1),
// ).subscribe();
}
}

View File

@ -1,7 +1,7 @@
<h1>Discovery</h1>
<div class="ui-g">
<div class="ui-g-12">
<of-probe-selector [probeHostID]="probeHostID" (select)="selectedProbe=$event"></of-probe-selector>
<of-probe-selector [probeHostID]="probeHostID" (select)="selectedProbe = $event"></of-probe-selector>
<of-probe-summary *ngIf="selectedProbe" @discoveryFilterAnim [probeHost]="selectedProbe"></of-probe-summary>
</div>
</div>
@ -10,19 +10,19 @@
<p-accordion [multiple]="true">
<p-accordionTab header="Discovery Settings" [selected]="true">
<span *ngIf="!selectedProbe">Choose a Probe to perform Discovery.</span>
<of-discovery-search-config *ngIf="!requested && selectedProbe" @discoveryFilterAnim [probeHost]="selectedProbe" (requestDiscovery)="onRequestDiscovery($event)"></of-discovery-search-config>
<of-discovery-search-config *ngIf="startDate === null && selectedProbe" @discoveryFilterAnim [probeHost]="selectedProbe"
(requestDiscovery)="onRequestDiscovery($event)"></of-discovery-search-config>
<of-discovery-request-summary *ngIf="discoverZone" @discoveryFilterAnim [discoverZone]="discoverZone"></of-discovery-request-summary>
</p-accordionTab>
<p-accordionTab header="Filter" [selected]="requested" [disabled]="!requested">
<of-discovery-search-filter #discoveryFilter (search)="filterWord=$event" (serviceSelect)="filterServices=$event"></of-discovery-search-filter>
<p-accordionTab header="Filter" [selected]="startDate !== null" [disabled]="startDate === null">
<of-discovery-search-filter #filter (search)="filterWord=$event" (serviceSelect)="filterServices=$event"></of-discovery-search-filter>
</p-accordionTab>
</p-accordion>
</div>
<div class="ui-g-12 ui-md-9">
<!-- <button class="ui-button-danger ui-button-width-fit" type="button" label="Stop" icon="ui-icon-close" pButton (click)="onRequestStop()"></button> -->
<of-discovery-result #discoveryResult *ngIf="requested else info" [probeHost]="selectedProbe" [filterWord]="filterWord" [filterServices]="filterServices"
[finished]="finished"></of-discovery-result>
<of-discovery-infra-tree #infraTree *ngIf="selectedProbe" [probeHost]="selectedProbe" [filterWord]="filterWord" [filterServices]="filterServices"></of-discovery-infra-tree>
<!-- <of-discovery-result *ngIf="requested else info" [probeHost]="selectedProbe" [filterWord]="filterWord" [filterServices]="filterServices"></of-discovery-result> -->
</div>
</div>

View File

@ -1,10 +1,10 @@
import {
Component, Input, OnDestroy, ViewChild,
Component, Input, OnDestroy, OnInit, ViewChild,
} from '@angular/core';
import { Observable, of, Subscription } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { catchError, map, tap, take } from 'rxjs/operators';
import { ProbeHost } from '@overflow/commons-typescript/model/probe';
import { DiscoverZone, Zone, Host, Port, Service } from '@overflow/commons-typescript/model/discovery';
@ -12,40 +12,46 @@ import { DiscoverZone, Zone, Host, Port, Service } from '@overflow/commons-types
import { Anim } from './animation';
import { DiscoveryService } from '../service/discovery.service';
import { DiscoverySubscriber, DiscoveryNotify } from '../subscriber/discovery.subscriber';
import { SearchResultComponent } from './search-result.component';
import { SearchFilterComponent } from './search-filter.component';
import { DiscoveryInfraTreeComponent } from './discovery-infra-tree.component';
@Component({
selector: 'of-discovery',
templateUrl: './discovery.component.html',
animations: Anim,
})
export class DiscoveryComponent implements OnDestroy {
export class DiscoveryComponent implements OnInit, OnDestroy {
@Input() probeHostID;
pending$: Observable<boolean>;
error$: Observable<any>;
discoverySubscription: Subscription;
selectedProbe: ProbeHost;
requested: boolean;
discoverZone: DiscoverZone;
finished: boolean;
startDate: Date;
stopDate: Date;
filterWord: string;
filterServices: Service[];
@ViewChild('discoveryResult') discoveryResult: SearchResultComponent;
@ViewChild('discoveryFilter') discoveryFilter: SearchFilterComponent;
@ViewChild('infraTree') infraTree: DiscoveryInfraTreeComponent;
@ViewChild('filter') filter: SearchFilterComponent;
constructor(
private discoveryService: DiscoveryService,
private discoverySubscriber: DiscoverySubscriber,
) {
this.discoverySubscription = null;
this.finished = false;
this.startDate = null;
this.stopDate = null;
}
ngOnInit(): void {
}
ngOnDestroy(): void {
if (null !== this.discoverySubscription) {
this.discoverySubscription.unsubscribe();
@ -53,9 +59,7 @@ export class DiscoveryComponent implements OnDestroy {
}
onRequestDiscovery(dz: DiscoverZone) {
this.requested = true;
this.discoverZone = dz;
const zone: Zone = {
network: this.selectedProbe.infraHost.infraZone.network, // this.selectedProbe.probe.cidr
address: this.selectedProbe.infraHost.infraZone.address.split('/')[0],
@ -71,16 +75,16 @@ export class DiscoveryComponent implements OnDestroy {
switch (discoveryNotify.method) {
case 'DiscoveryService.discoveryStart': {
const startDate = discoveryNotify.params as Date;
this.startDate = startDate;
this.infraTree.discoveryStarted(startDate);
break;
}
case 'DiscoveryService.discoveryStop': {
const stopDate = discoveryNotify.params as Date;
this.finished = true;
this.stopDate = stopDate;
this.discoverySubscription.unsubscribe();
this.discoverySubscription = null;
this.infraTree.discoveryStopped(stopDate);
break;
}
case 'DiscoveryService.discoveredZone': {
@ -89,7 +93,7 @@ export class DiscoveryComponent implements OnDestroy {
}
case 'DiscoveryService.discoveredHost': {
const host = discoveryNotify.params as Host;
this.discoveryResult.addHost(host);
this.infraTree.addHost(host);
break;
}
case 'DiscoveryService.discoveredPort': {
@ -99,8 +103,8 @@ export class DiscoveryComponent implements OnDestroy {
}
case 'DiscoveryService.discoveredService': {
const service = discoveryNotify.params as Service;
this.discoveryFilter.addService(service);
this.discoveryResult.addService(service);
this.infraTree.addService(service);
this.filter.addService(service);
break;
}
default: {
@ -114,9 +118,4 @@ export class DiscoveryComponent implements OnDestroy {
).subscribe();
}
onRequestStop() {
this.discoverZone = null;
this.requested = false;
this.discoveryService.stopDiscovery(this.selectedProbe.probe.probeKey);
}
}

View File

@ -2,16 +2,16 @@ import { ServiceSelectorComponent } from './service-selector.component';
import { DiscoveryComponent } from './discovery.component';
import { SearchConfigComponent } from './search-config.component';
import { SearchFilterComponent } from './search-filter.component';
import { SearchResultComponent } from './search-result.component';
import { IpInputComponent } from './ip-input.component';
import { RequestSummaryComponent } from './request-summary.component';
import { DiscoveryInfraTreeComponent } from './discovery-infra-tree.component';
export const COMPONENTS = [
ServiceSelectorComponent,
DiscoveryComponent,
SearchConfigComponent,
SearchFilterComponent,
SearchResultComponent,
IpInputComponent,
RequestSummaryComponent,
DiscoveryInfraTreeComponent
];

View File

@ -4,7 +4,7 @@
<div class="ui-g">
<div *ngFor="let service of services">
<p-toggleButton offLabel="{{service.name}}" onLabel="{{service.name}}" [style]="{'width':'100px'}" (onChange)="onServiceFilter($event, service)"
[(ngModel)]="filterServices[service.serviceName]"></p-toggleButton>
[(ngModel)]="filterServices[service.name]"></p-toggleButton>
<!-- <p-checkbox [(ngModel)]="filterServices" label="{{service.serviceName}}" value="{{service.serviceName}}"></p-checkbox> -->

View File

@ -1,8 +1,6 @@
import {
Component, Input,
SimpleChanges,
OnInit,
OnChanges,
} from '@angular/core';
import { Host, Port, Service } from '@overflow/commons-typescript/model/discovery';
import { TreeNode, Message, Tree } from 'primeng/primeng';
@ -23,12 +21,11 @@ import { catchError, map, tap, take } from 'rxjs/operators';
InfraRegistService
]
})
export class SearchResultComponent implements OnInit, OnChanges {
export class SearchResultComponent implements OnInit {
@Input() probeHost: ProbeHost;
@Input() filterWord: string;
@Input() filterServices: Map<string, boolean>;
@Input() finished: boolean;
infras: Infra[];
existTargets: Target[];
@ -62,7 +59,16 @@ export class SearchResultComponent implements OnInit, OnChanges {
children: this.hostNode,
expanded: true
});
this.getExistTarget();
// this.getExistTarget();
}
discoveryStarted(startedAt: Date) {
console.log('Discovery Started at: ' + startedAt);
}
discoveryStopped(stoppedAt: Date) {
console.log('Discovery Stopped at: ' + stoppedAt);
this.saveDiscoveredInfras();
}
getExistTarget() {
@ -89,11 +95,11 @@ export class SearchResultComponent implements OnInit, OnChanges {
).subscribe();
}
ngOnChanges(changes: SimpleChanges): void {
if (changes['finished'] && changes['finished'].currentValue === true) {
this.saveDiscoveredInfras();
}
}
// ngOnChanges(changes: SimpleChanges): void {
// if (changes['finished'] && changes['finished'].currentValue === true) {
// this.saveDiscoveredInfras();
// }
// }
displayInform() {
this.msgs = [];
@ -107,9 +113,9 @@ export class SearchResultComponent implements OnInit, OnChanges {
this.hostNode = [];
for (const infra of this.infras) {
switch (infra.metaInfraType) {
case toMetaInfraType(MetaInfraTypeEnum.ZONE):
const infraZone: InfraZone = infra;
break;
// case toMetaInfraType(MetaInfraTypeEnum.ZONE):
// const infraZone: InfraZone = infra;
// break;
case toMetaInfraType(MetaInfraTypeEnum.HOST):
const infraHost: InfraHost = infra;
this.addInfraHost(infraHost);

View File

@ -0,0 +1,35 @@
import { trigger, state, transition, style, animate, query, stagger, keyframes } from '@angular/animations';
export const Anim = [
trigger('discoveryFilterAnim', [
transition('void => *', [
query('*', style({ opacity: 0 }), { optional: true }),
query('*', stagger('3ms', [
animate('0.08s ease-in', keyframes([
style({ opacity: 0, transform: 'translateY(-75%)', offset: 0 }),
style({ opacity: .5, transform: 'translateY(35px)', offset: 0.3 }),
style({ opacity: 1, transform: 'translateY(0)', offset: 1.0 }),
]))]), { optional: true }),
]),
// transition('* => void', [
// query('*', style({ opacity: 1 }), { optional: true }),
// query('*', stagger('5ms', [
// animate('0.08s ease-in', keyframes([
// style({ opacity: 1, transform: 'translateY(0)', offset: 0 }),
// style({ opacity: .5, transform: 'translateY(35px)', offset: 0.3 }),
// style({ opacity: 0, transform: 'translateY(-75%)', offset: 1.0 }),
// ]))]), { optional: true }),
// ])
]),
trigger('discoveryResultAnim', [
transition('void => *', [
query('*', style({ opacity: 0 }), { optional: true }),
query('*', stagger('30ms', [
animate('0.08s ease-in', keyframes([
style({ opacity: 0, transform: 'translateX(-95%)', offset: 0 }),
style({ opacity: .5, transform: 'translateX(95px)', offset: 0.3 }),
style({ opacity: 1, transform: 'translateX(0)', offset: 1.0 }),
]))]), { optional: true }),
]),
]),
];

View File

@ -0,0 +1,34 @@
<h1>Discovery</h1>
<div class="ui-g">
<div class="ui-g-12">
<of-probe-selector [probeHostID]="probeHostID" (select)="selectedProbe=$event"></of-probe-selector>
<of-probe-summary *ngIf="selectedProbe" @discoveryFilterAnim [probeHost]="selectedProbe"></of-probe-summary>
</div>
</div>
<div class="ui-g">
<div class="ui-g-12 ui-md-3">
<p-accordion [multiple]="true">
<p-accordionTab header="Discovery Settings" [selected]="true">
<span *ngIf="!selectedProbe">Choose a Probe to perform Discovery.</span>
<of-discovery-search-config *ngIf="!requested && selectedProbe" @discoveryFilterAnim [probeHost]="selectedProbe" (requestDiscovery)="onRequestDiscovery($event)"></of-discovery-search-config>
<of-discovery-request-summary *ngIf="discoverZone" @discoveryFilterAnim [discoverZone]="discoverZone"></of-discovery-request-summary>
</p-accordionTab>
<p-accordionTab header="Filter" [selected]="requested" [disabled]="!requested">
<of-discovery-search-filter #discoveryFilter (search)="filterWord=$event" (serviceSelect)="filterServices=$event"></of-discovery-search-filter>
</p-accordionTab>
</p-accordion>
</div>
<div class="ui-g-12 ui-md-9">
<!-- <button class="ui-button-danger ui-button-width-fit" type="button" label="Stop" icon="ui-icon-close" pButton (click)="onRequestStop()"></button> -->
<of-discovery-result #discoveryResult *ngIf="requested else info" [probeHost]="selectedProbe" [filterWord]="filterWord" [filterServices]="filterServices"></of-discovery-result>
</div>
</div>
<ng-template #info>
<p-panel [showHeader]="false">
<div style="min-height: 98px">
Network Discovery 설명 페이지
</div>
</p-panel>
</ng-template>

View File

@ -0,0 +1,121 @@
import {
Component, Input, OnDestroy, ViewChild,
} from '@angular/core';
import { Observable, of, Subscription } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { ProbeHost } from '@overflow/commons-typescript/model/probe';
import { DiscoverZone, Zone, Host, Port, Service } from '@overflow/commons-typescript/model/discovery';
import { Anim } from './animation';
import { DiscoveryService } from '../service/discovery.service';
import { DiscoverySubscriber, DiscoveryNotify } from '../subscriber/discovery.subscriber';
import { SearchResultComponent } from './search-result.component';
import { SearchFilterComponent } from './search-filter.component';
@Component({
selector: 'of-discovery',
templateUrl: './discovery.component.html',
animations: Anim,
})
export class DiscoveryComponent implements OnDestroy {
@Input() probeHostID;
pending$: Observable<boolean>;
error$: Observable<any>;
discoverySubscription: Subscription;
selectedProbe: ProbeHost;
requested: boolean;
discoverZone: DiscoverZone;
filterWord: string;
filterServices: Service[];
startDate: Date;
stopDate: Date;
@ViewChild('discoveryResult') discoveryResult: SearchResultComponent;
@ViewChild('discoveryFilter') discoveryFilter: SearchFilterComponent;
constructor(
private discoveryService: DiscoveryService,
private discoverySubscriber: DiscoverySubscriber,
) {
this.discoverySubscription = null;
}
ngOnDestroy(): void {
if (null !== this.discoverySubscription) {
this.discoverySubscription.unsubscribe();
}
}
onRequestDiscovery(dz: DiscoverZone) {
this.requested = true;
this.discoverZone = dz;
const zone: Zone = {
network: this.selectedProbe.infraHost.infraZone.network, // this.selectedProbe.probe.cidr
address: this.selectedProbe.infraHost.infraZone.address.split('/')[0],
iface: this.selectedProbe.infraHost.infraZone.iface,
mac: this.selectedProbe.infraHost.infraZone.mac,
metaIPType: this.selectedProbe.infraHost.infraZone.metaIPType,
};
this.discoveryService.discoverHost(this.selectedProbe.probe.probeKey, zone, dz.discoverHost);
this.discoverySubscription = this.discoverySubscriber.observable().pipe(
map((discoveryNotify: DiscoveryNotify) => {
switch (discoveryNotify.method) {
case 'DiscoveryService.discoveryStart': {
const startDate = discoveryNotify.params as Date;
this.discoveryResult.discoveryStarted(startDate);
break;
}
case 'DiscoveryService.discoveryStop': {
const stopDate = discoveryNotify.params as Date;
this.discoveryResult.discoveryStopped(stopDate);
this.discoverySubscription.unsubscribe();
this.discoverySubscription = null;
break;
}
case 'DiscoveryService.discoveredZone': {
const _zone = discoveryNotify.params as Zone;
break;
}
case 'DiscoveryService.discoveredHost': {
const host = discoveryNotify.params as Host;
this.discoveryResult.addHost(host);
break;
}
case 'DiscoveryService.discoveredPort': {
const port = discoveryNotify.params as Port;
// this.discoveryResult.addPort(port);
break;
}
case 'DiscoveryService.discoveredService': {
const service = discoveryNotify.params as Service;
this.discoveryFilter.addService(service);
this.discoveryResult.addService(service);
break;
}
default: {
break;
}
}
}),
catchError(error => {
return of();
}),
).subscribe();
}
onRequestStop() {
this.discoverZone = null;
this.requested = false;
this.discoveryService.stopDiscovery(this.selectedProbe.probe.probeKey);
}
}

View File

@ -0,0 +1,17 @@
import { ServiceSelectorComponent } from './service-selector.component';
import { DiscoveryComponent } from './discovery.component';
import { SearchConfigComponent } from './search-config.component';
import { SearchFilterComponent } from './search-filter.component';
import { SearchResultComponent } from './search-result.component';
import { IpInputComponent } from './ip-input.component';
import { RequestSummaryComponent } from './request-summary.component';
export const COMPONENTS = [
ServiceSelectorComponent,
DiscoveryComponent,
SearchConfigComponent,
SearchFilterComponent,
SearchResultComponent,
IpInputComponent,
RequestSummaryComponent,
];

View File

@ -0,0 +1,11 @@
<div class="ui-inputgroup">
<span style="margin-right: 18px">{{title}} </span>
<input [disabled]="fixed >= 0"type="text" pInputText placeholder="127" maxlength="3" [(ngModel)]="first" value="{{first}}" (keyup)="onIpInput($event)">
<span class="ui-inputgroup-addon" style="min-width: 5px !important">.</span>
<input [disabled]="fixed > 1" type="text" pInputText placeholder="0" maxlength="3" [(ngModel)]="second" value="{{second}}" (keyup)="onIpInput($event)">
<span class="ui-inputgroup-addon" style="min-width: 5px !important">.</span>
<input [disabled]="fixed > 2" type="text" pInputText placeholder="0" maxlength="3" [(ngModel)]="third" value="{{third}}" (keyup)="onIpInput($event)">
<span class="ui-inputgroup-addon" style="min-width: 5px !important">.</span>
<input type="text" pInputText placeholder="1" maxlength="3" [(ngModel)]="fourth" value="{{fourth}}" (keyup)="onIpInput($event)">
</div>

View File

@ -0,0 +1,57 @@
import {
AfterContentInit,
Component,
EventEmitter,
Input,
Output,
OnInit,
} from '@angular/core';
@Component({
selector: 'of-ip-input',
templateUrl: './ip-input.component.html',
})
export class IpInputComponent implements OnInit, AfterContentInit {
first: string;
second: string;
third: string;
fourth: string;
@Input() hostIp: string;
@Input() title: string;
@Input() fixed: number;
@Output() inputIp = new EventEmitter();
constructor(
) {
}
ngOnInit() {
if (this.hostIp !== '' && this.hostIp !== null && this.hostIp !== undefined) {
const tempIp = this.hostIp.split('.');
this.first = tempIp[0];
this.second = tempIp[1];
this.third = tempIp[2];
this.fourth = tempIp[3];
}
}
ngAfterContentInit() {
}
onIpInput(event) {
if (
(this.first !== '' && this.first !== undefined) &&
(this.second !== '' && this.second !== undefined) &&
(this.third !== '' && this.third !== undefined) &&
(this.fourth !== '' && this.fourth !== undefined) ) {
event.value = this.first + '.' + this.second + '.' + this.third + '.' + this.fourth;
this.inputIp.emit(event);
} else {
return;
}
}
}

View File

@ -0,0 +1,24 @@
<div *ngIf="discoverZone" class="ui-key-value ui-left-info2">
<div class="ui-g">
<of-key-value [key]="'Host Range'" [value]="hostRange"></of-key-value>
<of-key-value [key]="'Port Range'" [value]="portRange"></of-key-value>
<div class="of-key-value-div">
<span>Port type</span>
<span class="ng-star-inserted ui-nopad">
<span *ngIf="discoverZone.discoverHost.discoverPort.includeTCP">TCP</span>
<span *ngIf="discoverZone.discoverHost.discoverPort.includeUDP">UDP</span>
</span>
</div>
<div *ngIf="services && services.length > 0" class="ui-g-12 ui-top-border-1" style="text-align: right">
<p-overlayPanel #op>
<div *ngFor="let service of services">
{{service}}
</div>
</p-overlayPanel>
<a style="cursor: pointer" (click)="op.toggle($event)">{{services.length}} services has selected.</a>
</div>
</div>
</div>

View File

@ -0,0 +1,33 @@
import {
Component, Input, OnChanges, SimpleChanges,
} from '@angular/core';
import { DiscoverZone } from '@overflow/commons-typescript/model/discovery';
@Component({
selector: 'of-discovery-request-summary',
templateUrl: './request-summary.component.html',
})
export class RequestSummaryComponent implements OnChanges {
@Input() discoverZone: DiscoverZone;
hostRange: string;
hostExclude: string;
portRange: string;
portExclude: string;
services: string[];
constructor(
) {
}
ngOnChanges(changes: SimpleChanges): void {
this.hostRange = this.discoverZone.discoverHost.firstScanRange
+ ' ~ '
+ this.discoverZone.discoverHost.lastScanRange;
this.portRange = this.discoverZone.discoverHost.discoverPort.firstScanRange
+ ' ~ '
+ this.discoverZone.discoverHost.discoverPort.lastScanRange;
this.services = this.discoverZone.discoverHost.discoverPort.discoverService.includeServices;
}
}

View File

@ -0,0 +1,106 @@
<div class="ui-g" *ngIf="probeHost">
<div>
<label>IP Version</label>
</div>
<div class="ui-g-12 ui-bottom-space-20">
<div class="ui-g-6 ui-g-nopad">
<p-radioButton name="group1" value="V4" label="IPv4" [(ngModel)]="ipType"></p-radioButton>
</div>
<div class="ui-g-6 ui-g-nopad">
<p-radioButton [disabled]="!SUPPORT_V6" name="group1" value="V6" label="IPv6" [(ngModel)]="ipVer"></p-radioButton>
</div>
</div>
<div *ngIf="ipVer === 'v4' else V6" class="ui-g-12">
<div>
<div>
<p-checkbox value="host" label="Host" [(ngModel)]="hostChecked" binary="true" [disabled]="true"></p-checkbox>
</div>
<label>Range</label>
</div>
<div class="ui-g-12">
<of-ip-input [hostIp]="startIP" (inputIp)="onInputIP($event, 0)" [title]="'From'" [fixed]="fixedIPrange"></of-ip-input>
</div>
<div class="ui-g-12">
<of-ip-input [hostIp]="endIP" (inputIp)="onInputIP($event, 1)" [title]="'To'" [fixed]="fixedIPrange"></of-ip-input>
</div>
<div class="ui-g-12">
<p-multiSelect (onFocus)="onIPExcludeFocus()" [options]="ipArray" optionLabel="ip" [(ngModel)]="excludeIPs" [panelStyle]="{minWidth:'12em'}"
selectedItemsLabel="{0} hosts are excluded." maxSelectedLabels="2" defaultLabel="Add exclude IP"></p-multiSelect>
</div>
<div *ngIf="ipErrMsg" class="ui-message ui-messages-error ui-corner-all">
{{ipErrMsg}}
</div>
</div>
<ng-template #V6>
<!-- TODO -->
</ng-template>
<div class="ui-g-12">
<div>
<p-checkbox #portCheckbox value="port" label="Port" [(ngModel)]="portChecked" binary="true" (onChange)="onPortCheckChange(serviceCheckbox, $event)"></p-checkbox>
</div>
</div>
<div class="ui-g-12 ui-bottom-space-20">
<div class="ui-g-6 ui-g-nopad">
TCP &nbsp;
<p-inputSwitch [(ngModel)]="tcpChecked" [disabled]="!portChecked" binary="true" (onChange)="onPortTypeCheck(portCheckbox, serviceCheckbox)"></p-inputSwitch>
</div>
<div class="ui-g-6 ui-g-nopad">
UDP &nbsp;
<p-inputSwitch [(ngModel)]="udpChecked" [disabled]="!portChecked" binary="true" (onChange)="onPortTypeCheck(portCheckbox, serviceCheckbox)"></p-inputSwitch>
</div>
</div>
<div class="ui-g-12">
<label>Range</label>
</div>
<div class="ui-g-12 ui-g-nopad ui-bottom-space-20">
<div class="ui-g-6">
<input #startPort id="float-input" type="number" maxlength="5" min="1" max="65535" pInputText [disabled]="!portChecked" value="1"
(keyup)="onInputPort(startPort.value, 0)">
</div>
<div class="ui-g-6">
<input #endPort id="float-input" type="number" maxlength="5" min="1" max="65535" pInputText [disabled]="!portChecked" value="1024"
(keyup)="onInputPort(endPort.value, 1)">
</div>
<div class="ui-g-12 ui-g-nopad" style="margin-left: 0.5em">
<a style="cursor: pointer" (click)="portExcludeDisplay = true">
<span *ngIf="excludePorts.length === 0">Add Exclude Ports</span>
<span *ngIf="excludePorts.length > 0">{{excludePorts.length}} ports are excluded.</span>
</a>
</div>
</div>
<div *ngIf="portErrMsg" class="ui-message ui-messages-error ui-corner-all">
{{portErrMsg}}
</div>
<div class="ui-g-12">
<div>
<p-checkbox #serviceCheckbox value="service" label="Service" [(ngModel)]="serviceChecked" (onChange)="onServiceCheckChange(portCheckbox, $event)"
binary="true"></p-checkbox>
</div>
</div>
<div class="ui-g-12 ui-g-nopad">
<of-service-selector [disabled]="!serviceChecked" (change)="includeServices = $event"></of-service-selector>
<!-- <of-meta-service-type-selector [disabled]="!serviceChecked" (change)="includeServices = $event"></of-meta-service-type-selector> -->
</div>
<div class="ui-g-12" dir="rtl">
<button type="submit" [disabled]="!validation" label="Discovery Start" pButton class="ui-button-width-fit" (click)="onRequestDiscovery()"></button>
</div>
</div>
<p-dialog [(visible)]="portExcludeDisplay" [modal]="true" [responsive]="true" [width]="400">
<p-header>
Add Ports to exclude.
</p-header>
<p-chips [(ngModel)]="portChips" [addOnTab]="true" [allowDuplicate]="false" (onAdd)="onAddExcludePort($event)" (onRemove)="onRemoveExcludePort($event)"
[max]="20" placeholder="e.g. 1025~65535"></p-chips>
<p-footer>
<button type="button" pButton icon="fa-check" (click)="portExcludeDisplay=false" label="Done"></button>
</p-footer>
</p-dialog>

View File

@ -0,0 +1,311 @@
import {
Component, EventEmitter, Input,
Output, OnChanges, SimpleChanges
} from '@angular/core';
import { ProbeHost } from '@overflow/commons-typescript/model/probe';
import * as CIDR from 'ip-cidr';
import * as ipRangeCheck from 'ip-range-check';
import { DiscoverPort, DiscoverService, DiscoverZone } from '@overflow/commons-typescript/model/discovery';
import { Checkbox } from 'primeng/primeng';
import { MetaIPTypeEnum, toMetaIPType } from '@overflow/commons-typescript/model/meta';
@Component({
selector: 'of-discovery-search-config',
templateUrl: './search-config.component.html',
})
export class SearchConfigComponent implements OnChanges {
SUPPORT_V6 = false;
@Input() probeHost: ProbeHost;
@Output() requestDiscovery = new EventEmitter<DiscoverZone>();
ipType: MetaIPTypeEnum;
startIP: string;
endIP: string;
startPort: string;
endPort: string;
excludeIPs = [];
excludePorts = [];
includeServices = [];
hostChecked = true;
portChecked = true;
serviceChecked = true;
tcpChecked = true;
udpChecked = true;
validation = false;
ipErrMsg: string;
portErrMsg: string;
fixedIPrange: number;
ipArray = [];
portExcludeDisplay = false;
portChips = [];
constructor(
) {
}
ngOnChanges(changes: SimpleChanges): void {
if (changes['probeHost']) {
this.initByProbe();
}
this.validation = true;
}
initByProbe() {
const cidr = new CIDR(this.probeHost.probe.cidr);
if (!cidr.isValid()) {
return;
}
this.ipType = MetaIPTypeEnum.V4;
this.startIP = cidr.start();
this.endIP = cidr.end();
this.startPort = '1';
this.endPort = '1024';
// TODO: Initialize services
this.checkEditableIPrange(cidr);
}
onIPExcludeFocus() {
const cidr = new CIDR(this.probeHost.probe.cidr);
this.ipArray = cidr.toArray().filter((value) => {
return this.inRange(value) || this.excludeIPs.find(obj => obj.ip === value);
}).map(value => {
return { ip: value };
});
}
inRange(value) {
const min = this.startIP;
const max = this.endIP;
if (this.ipToNum(min) <= this.ipToNum(value) && this.ipToNum(max) >= this.ipToNum(value)) {
return true;
}
return false;
}
ipToNum(ip) {
return Number(
ip.split('.')
.map(d => ('000' + d).substr(-3))
.join('')
);
}
onRequestDiscovery() {
let discoverPort: DiscoverPort = null;
let discoverService: DiscoverService = null;
if (this.serviceChecked) {
const services = [];
for (const service of this.includeServices) {
services.push(service.key);
}
discoverService = {
includeServices: services,
};
}
if (this.portChecked) {
discoverPort = {
firstScanRange: Number(this.startPort),
lastScanRange: Number(this.endPort),
includeTCP: this.tcpChecked,
includeUDP: this.udpChecked,
excludePorts: this.excludePorts,
discoverService: discoverService
};
}
const discoverZone: DiscoverZone = {
discoverHost: {
metaIPType: toMetaIPType(this.ipType),
firstScanRange: this.startIP,
lastScanRange: this.endIP,
discoverPort: discoverPort,
excludeHosts: this.excludeIPs,
},
};
console.log(discoverZone);
this.requestDiscovery.emit(discoverZone);
}
checkEditableIPrange(cidr) {
const startIPArray = cidr.addressStart.parsedAddress;
const endIPArray = cidr.addressEnd.parsedAddress;
let count = 0;
endIPArray.forEach((element, idx) => {
if (element === startIPArray[idx]) {
count++;
}
});
this.fixedIPrange = count;
}
onPortCheckChange(serviceCheckbox: Checkbox, checked: boolean) {
if (!checked) {
serviceCheckbox.checked = false;
this.serviceChecked = false;
} else {
if (!this.tcpChecked && !this.udpChecked) {
this.tcpChecked = true;
this.udpChecked = true;
}
}
}
onServiceCheckChange(portCheckbox: Checkbox, checked: boolean) {
if (checked) {
portCheckbox.checked = true;
this.portChecked = true;
}
}
onPortTypeCheck(portCheckbox: Checkbox, serviceCheckbox: Checkbox): void {
if (!this.tcpChecked && !this.udpChecked) {
this.portChecked = false;
portCheckbox.checked = false;
this.serviceChecked = false;
serviceCheckbox.checked = false;
}
}
onInputIP(e, idx: number) {
const value = e.value;
if (idx === 0) {
this.startIP = value;
} else {
this.endIP = value;
}
this.ipErrMsg = this.validateIP(value, idx);
if (this.ipErrMsg) {
this.validation = false;
} else {
this.validation = true;
}
}
validateIP(value: string, idx): string {
if (!this.isValidIPregex(value)) {
return 'Not valid IP format.';
}
if (!ipRangeCheck(value, this.probeHost.probe.cidr)) {
return 'Not valid IP range.';
}
const ipArray = [this.startIP, this.endIP];
const sortedIpArray = this.sortIP([this.startIP, this.endIP]);
if (ipArray[0] !== sortedIpArray[0]) {
return 'Not valiad range';
}
return null;
}
isValidIPregex(ip: string): boolean {
if (this.ipType === MetaIPTypeEnum.V4) {
return /^(?=\d+\.\d+\.\d+\.\d+$)(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])\.?){4}$/.test(ip);
}
return false;
}
sortIP(ipAddressArray) {
return ipAddressArray.sort((a, b) => {
a = a.split('.');
b = b.split('.');
for (let i = 0; i < a.length; i++) {
// tslint:disable-next-line:radix
if ((a[i] = parseInt(a[i])) < (b[i] = parseInt(b[i]))) {
return -1;
} else if (a[i] > b[i]) {
return 1;
}
}
return 0;
});
}
onInputPort(portNum: string, idx: number) {
if (idx === 0) {
this.startPort = portNum;
} else {
this.endPort = portNum;
}
this.portErrMsg = this.validatePort(portNum);
if (this.portErrMsg) {
this.validation = false;
} else {
this.validation = true;
}
}
validatePort(portNum): string {
if (Number(this.startPort) > Number(this.endPort)) {
return 'Not valid port range.';
}
if (Number(portNum) <= 0 || Number(portNum) > 65535) {
return 'Not valid port range.';
}
return null;
}
onAddExcludePort(event) {
const port = event.value.replace(/\s/g, '');
if (port.indexOf('~') > 0) { // e.g. 1~3000
const splited = port.split('~');
this.portChips.pop();
const from = Number(splited[0]);
const to = Number(splited[1]);
if (this.checkInvalidPort(from)
|| this.checkInvalidPort(to)
|| !Number.isInteger(from)
|| !Number.isInteger(to)) {
return;
}
const chipItem = 'All ' + from + ' ~ ' + to;
this.portChips.push(chipItem);
for (let i = from; i <= to; i++) {
this.excludePorts.push(String(i));
}
} else {
const num = Number(event.value);
if (!Number.isInteger(num) || this.checkInvalidPort(num) || this.checkExistExcludePort(event.value)) {
this.portChips.pop();
return;
}
this.excludePorts.push(event.value);
}
}
onRemoveExcludePort(event) {
if (event.value.indexOf('~') > 0) {
// e.g. 'All 1 ~ 30'
const splited = event.value.replace(/\s/g, '').replace('All', '').split('~');
for (let i = Number(splited[0]); i <= Number(splited[1]); i++) {
console.log(String(i));
const index = this.excludePorts.indexOf(String(i));
this.excludePorts.splice(index, 1);
}
return;
}
const idx = this.excludePorts.indexOf(event.value);
this.excludePorts.splice(idx, 1);
}
checkExistExcludePort(portNum) {
return this.excludePorts.find((value) => {
return value === portNum;
});
}
checkInvalidPort(port) {
return port <= 0 || port > 65535;
}
}

View File

@ -0,0 +1,15 @@
<div class="ui-g">
<input type="text" pInputText placeholder="Search..." [(ngModel)]="filterWord" (keyup)="onSearch($event)">
<div class="ui-g">
<div *ngFor="let service of services">
<p-toggleButton offLabel="{{service.name}}" onLabel="{{service.name}}" [style]="{'width':'100px'}" (onChange)="onServiceFilter($event, service)"
[(ngModel)]="filterServices[service.serviceName]"></p-toggleButton>
<!-- <p-checkbox [(ngModel)]="filterServices" label="{{service.serviceName}}" value="{{service.serviceName}}"></p-checkbox> -->
</div>
</div>
</div>

View File

@ -0,0 +1,68 @@
import {
Component,
OnInit,
Output,
EventEmitter
} from '@angular/core';
import { Anim } from './animation';
import { Service } from '@overflow/commons-typescript/model/discovery';
@Component({
selector: 'of-discovery-search-filter',
templateUrl: './search-filter.component.html',
animations: Anim
})
export class SearchFilterComponent implements OnInit {
services: Service[] = [];
filterWord: string;
filterServices = new Map<string, boolean>();
@Output() search = new EventEmitter<string>();
@Output() serviceSelect = new EventEmitter<Map<string, boolean>>();
constructor(
) {
}
ngOnInit() {
}
onSearch(e) {
if (e.key !== 'Enter') {
return;
}
this.search.emit(this.filterWord);
}
addService(service: Service) {
if (service.name.indexOf('Not Supported Service') >= 0) {
const tempName = service.name.split('Perhaps ')[1].split('[')[0];
service.name = '*' + tempName;
}
let exist = false;
this.services.forEach(value => {
if (value.name === service.name) {
exist = true;
return;
}
});
if (!exist) {
this.services.push(service);
this.filterServices[service.name] = true;
}
this.serviceSelect.emit(this.filterServices);
}
onServiceFilter(e, service: Service) {
// if (e.checked) {
// this.filterServices.push(service);
// } else {
// const index = this.filterServices.indexOf(service);
// this.filterServices.splice(index, 1);
// }
this.serviceSelect.emit(this.filterServices);
}
}

View File

@ -0,0 +1,61 @@
<of-block-progressbar [target]="content" [pending]="!finished"></of-block-progressbar>
<p-panel #content [showHeader]="false" class="block-panel">
<p-messages [(value)]="msgs"></p-messages>
<p-tree [value]="zoneNode" layout="vertical">
<!-- ZONE node template -->
<ng-template let-node pTemplate="ZONE">
<div>
{{node.label}}
</div>
</ng-template>
<!-- HOST node template -->
<ng-template let-node pTemplate="HOST">
<div @discoveryResultAnim>
<div *ngIf="checkHighlight(node.label, 0) else unhighlightHost">
<p-toggleButton [disabled]="checkExistTarget(node.data.target)" onLabel="{{node.label}}" offLabel="{{node.label}}" onIcon="fa-check"
offIcon="fa-square" [style]="{'width':'200px'}" (onChange)="onTargetSelect($event, node)"></p-toggleButton>
</div>
<ng-template #unhighlightHost>
<p-toggleButton [disabled]="checkExistTarget(node.data.target)" onLabel="{{node.label}}" offLabel="{{node.label}}" onIcon="fa-check"
offIcon="fa-square" [style]="{'width':'200px', 'opacity': '0.2'}" (onChange)="onTargetSelect($event, node)"></p-toggleButton>
</ng-template>
</div>
</ng-template>
<!-- SERVICE node template -->
<ng-template let-node pTemplate="SERVICE">
<div @discoveryResultAnim>
<div *ngIf="checkHighlight(node.data.name, 1) else unhighlightServ">
<p-toggleButton [disabled]="checkExistTarget(node.data.target)" onLabel="{{node.label}} {{node.data.portType}}" offLabel="{{node.label}} {{node.data.portType}} {{node.data.portNumber}}"
onIcon="fa-check" offIcon="fa-square" [style]="{'width':'300px'}" (onChange)="onTargetSelect($event, node)"></p-toggleButton>
</div>
<ng-template #unhighlightServ>
<p-toggleButton [disabled]="checkExistTarget(node.data.target)" onLabel="{{node.label}} {{node.data.portType}}" offLabel="{{node.label}} {{node.data.portType}} {{node.data.portNumber}}"
onIcon="fa-check" offIcon="fa-square" [style]="{'width':'300px', 'opacity': '0.2'}" (onChange)="onTargetSelect($event, node)"></p-toggleButton>
</ng-template>
</div>
</ng-template>
</p-tree>
<button class="ui-button-width-fit ui-float-right ui-top-space-10" [disabled]="selectedItems.length === 0" type="button"
label="Save" icon="ui-icon-close" pButton (click)="saveTargets()"></button>
</p-panel>
<p-dialog header="Title" [(visible)]="targetSaved" [modal]="true" [responsive]="true" [width]="600" [minWidth]="200" [minY]="70"
[closeOnEscape]="false">
<span>Target 지정이 완료되었습니다. 이어서 Sensor를 등록하시면 좋겠다능</span>
<p-footer>
<button type="button" pButton label="메인으로"></button>
<button type="button" pButton label="Target으로"></button>
<button type="button" pButton label="InfraMap으로"></button>
</p-footer>
</p-dialog>

View File

@ -0,0 +1,348 @@
import {
Component, Input,
OnInit,
} from '@angular/core';
import { Host, Port, Service } from '@overflow/commons-typescript/model/discovery';
import { TreeNode, Message, Tree } from 'primeng/primeng';
import { ProbeHost } from '@overflow/commons-typescript/model/probe';
import { Anim } from './animation';
import { TargetService } from '@overflow/target/service/target.service';
import { InfraService, InfraHost, MetaTargetHostTypeEnum, toMetaTargetHostType, Infra, MetaInfraTypeEnum, toMetaInfraTypeEnum, toMetaInfraType, InfraZone, Target, Page, PageParams } from '@overflow/commons-typescript';
import { InfraService as InfraRegistService } from '../../infra/service/infra.service';
import { Observable, of, Subscription } from 'rxjs';
import { catchError, map, tap, take } from 'rxjs/operators';
@Component({
selector: 'of-discovery-result',
templateUrl: './search-result.component.html',
animations: Anim,
providers: [
TargetService,
InfraRegistService
]
})
export class SearchResultComponent implements OnInit {
@Input() probeHost: ProbeHost;
@Input() filterWord: string;
@Input() filterServices: Map<string, boolean>;
infras: Infra[];
existTargets: Target[];
discoverySubscription: Subscription;
zoneNode: TreeNode[] = [];
hostNode: TreeNode[] = [];
selectedItems = [];
msgs: Message[];
error$: Observable<any>;
discoveredHosts: Host[] = [];
discoveredServices: Service[] = [];
infraSaved: boolean;
targetSaved: boolean;
pending$: Observable<boolean>;
constructor(
private infraRegistService: InfraRegistService,
private targetService: TargetService
) {
}
ngOnInit(): void {
this.zoneNode.push({
label: this.probeHost.probe.cidr,
type: 'ZONE',
data: {
},
children: this.hostNode,
expanded: true
});
// this.getExistTarget();
}
discoveryStarted(startedAt: Date) {
console.log('Discovery Started at: ' + startedAt);
}
discoveryStopped(stoppedAt: Date) {
console.log('Discovery Stopped at: ' + stoppedAt);
this.saveDiscoveredInfras();
}
getExistTarget() {
const pageParams: PageParams = {
pageNo: 0,
countPerPage: 99999,
sortCol: 'id',
sortDirection: 'descending'
};
this.targetService.readAllByProbeID(this.probeHost.probe.id, pageParams)
.pipe(
tap(() => {
}),
map((targetPage: Page<Target>) => {
this.existTargets = targetPage.content;
}),
catchError(error => {
this.error$ = of(error);
return of();
}),
tap(() => {
}),
take(1),
).subscribe();
}
// ngOnChanges(changes: SimpleChanges): void {
// if (changes['finished'] && changes['finished'].currentValue === true) {
// this.saveDiscoveredInfras();
// }
// }
displayInform() {
this.msgs = [];
this.msgs.push({
severity: 'success',
summary: 'Discovery가 완료되었습니다. 모니터링 대상(들)을 선택 후 저장하세요.',
});
}
rerenderInfras() {
this.hostNode = [];
for (const infra of this.infras) {
switch (infra.metaInfraType) {
// case toMetaInfraType(MetaInfraTypeEnum.ZONE):
// const infraZone: InfraZone = infra;
// break;
case toMetaInfraType(MetaInfraTypeEnum.HOST):
const infraHost: InfraHost = infra;
this.addInfraHost(infraHost);
break;
case toMetaInfraType(MetaInfraTypeEnum.SERVICE):
const infraService: InfraService = infra;
this.addInfraService(infraService)
break;
default:
break;
}
}
}
saveDiscoveredInfras() {
this.infraRegistService.registDiscoverd(
this.probeHost.probe.id,
this.discoveredHosts,
this.discoveredServices)
.pipe(
tap(() => {
this.infraSaved = false;
this.pending$ = of(true);
}),
map((infras: Infra[]) => {
if (infras) {
this.infras = infras;
this.rerenderInfras();
this.displayInform();
}
}),
catchError(error => {
this.error$ = of(error);
return of();
}),
tap(() => {
this.infraSaved = true;
this.pending$ = of(false);
}),
take(1),
).subscribe();
}
addHost(host: Host) {
const idx = this.findHostIndex(host);
this.hostNode.splice(idx, 0, {
type: 'HOST',
label: host.address,
data: {
ip: this.convertIPtoNumber(host.address),
mac: host.mac,
target: host
},
expanded: true,
children: []
});
this.discoveredHosts.push(host);
}
addService(service: Service) {
const targetHostNode = this.findHostNodeByService(service.port.host.address);
const idx = this.findServiceIndex(targetHostNode.children, service.name);
targetHostNode.children.splice(idx, 0, {
type: 'SERVICE',
label: service.name + ' (' + service.port.portNumber + ')',
data: {
name: service.name,
portType: service.port.metaPortType.key,
portNumber: service.port.portNumber,
target: service
},
});
this.discoveredServices.push(service);
}
addInfraHost(infraHost: InfraHost) {
const host: Host = {
address: infraHost.infraHostIPs[0].address,
}
const idx = this.findHostIndex(host);
this.hostNode.splice(idx, 0, {
type: 'HOST',
label: host.address,
data: {
ip: this.convertIPtoNumber(host.address),
target: infraHost
},
expanded: true,
children: []
});
}
addInfraService(infraService: InfraService) {
const targetHostNode = this.findHostNodeByService(infraService.infraHostPort.infraHostIP.address);
const idx = this.findServiceIndex(targetHostNode.children, infraService.metaTargetServiceType.name);
targetHostNode.children.splice(idx, 0, {
type: 'SERVICE',
label: infraService.metaTargetServiceType.name + ' (' + infraService.infraHostPort.port + ')',
data: {
name: infraService.metaTargetServiceType.name,
portType: infraService.infraHostPort.metaPortType.name,
portNumber: infraService.infraHostPort.port,
target: infraService
},
});
}
onTargetSelect(e, node: TreeNode) {
const data = node.data.target;
if (e.checked) {
this.selectedItems.push(data);
} else {
const index = this.selectedItems.indexOf(data);
this.selectedItems.splice(index, 1);
}
}
findHostIndex(host: Host): number {
let index = 0;
this.hostNode.forEach(node => {
if (node.data.ip < this.convertIPtoNumber(host.address)) {
index++;
}
});
return index;
}
findServiceIndex(serviceNodes: TreeNode[], serviceName: string) {
let index = 0;
serviceNodes.forEach(node => {
// if (node.data.portNumber < service.port.portNumber) {
// index++;
// }
if (node.data.name.toUpperCase().localeCompare(serviceName.toUpperCase()) === -1) {
index++;
}
});
return index;
}
findHostNodeByService(serviceAddress: string) {
let targetHost = null;
this.hostNode.forEach((value, i) => {
if (value.data.ip === this.convertIPtoNumber(serviceAddress)) {
targetHost = value;
return;
}
});
return targetHost;
}
convertIPtoNumber(ip: string) {
return ip.split('.').map((octet, index, array) => {
return parseInt(octet) * Math.pow(256, (array.length - index - 1));
}).reduce((prev, curr) => {
return prev + curr;
});
}
checkHighlight(label: string, type: number) {
let highlight = true;
if (this.filterWord && (label.toUpperCase().indexOf(this.filterWord.toUpperCase()) < 0)) {
highlight = false;
}
if (type === 1 && this.filterServices[label] === false) {
highlight = false;
}
return highlight;
}
checkExistTarget(infra: Infra): boolean {
if (!this.infraSaved) {
return false;
}
for (const target of this.existTargets) {
if (target.infra.id === infra.id) {
return true;
}
}
return false;
}
saveTargets() {
const targets: Target[] = [];
this.selectedItems.forEach(value => {
const infra: Infra = value;
let name: string;
if (value.metaInfraType === toMetaInfraType(MetaInfraTypeEnum.ZONE)) {
const infraZone: InfraZone = value;
name = infraZone.network;
} else if (value.metaInfraType === toMetaInfraType(MetaInfraTypeEnum.HOST)) {
const infraHost: InfraHost = value;
name = infraHost.infraHostIPs[0].address;
} else if (value.metaInfraType === toMetaInfraType(MetaInfraTypeEnum.SERVICE)) {
const infraService: InfraService = value;
name = infraService.metaInfraType.name;
}
const target: Target = {
infra: {
id: infra.id
},
name: name,
sensorCount: 0,
};
targets.push(target);
});
this.targetService.registAll(targets, this.probeHost.probe.id)
.pipe(
tap(() => {
}),
map((targets: Target[]) => {
if (targets) {
this.targetSaved = true;
}
}),
catchError(error => {
this.error$ = of(error);
return of();
}),
tap(() => {
}),
take(1),
).subscribe();
}
}

View File

@ -0,0 +1,27 @@
<div *ngIf="!disabled">
<of-error-message [error]="error$ | async" [closable]="false"></of-error-message>
<of-block-progressbar [target]="content" [pending]="pending$ | async"></of-block-progressbar>
<p-panel #content [showHeader]="false" class="block-panel">
<div class="ui-g" dir="rtl">
<button pButton type="button" class="ui-button-secondary ui-button-width-fit ui-s-button" label="Unselect All" style="margin-bottom: 3px;"
(click)="onUnselectAll()"></button>
<button pButton type="button" class="ui-button-secondary ui-button-width-fit ui-s-button" label="Select All" style="margin-bottom: 3px;"
(click)="onSelectAll()"></button>
</div>
<p-table selectionMode="multiple" [scrollable]="true" scrollHeight="200px" [value]="metaCrawlers" [(selection)]="includeServices"
dataKey="id" (onRowSelect)="onSelect($event.data)" (onRowUnselect)="onUnselect($event.data)">
<ng-template pTemplate="body" let-rowData let-columns="columns">
<tr [pSelectableRow]="rowData">
<!-- <td>
<p-tableCheckbox [value]="rowData"></p-tableCheckbox>
</td> -->
<td>
<p-tableCheckbox [value]="rowData"></p-tableCheckbox>
{{rowData.name}}
</td>
</tr>
</ng-template>
</p-table>
</p-panel>
</div>

View File

@ -0,0 +1,90 @@
import {
Component,
OnInit,
Input,
AfterContentInit,
Output,
EventEmitter,
OnDestroy
} from '@angular/core';
import { Store, select, StateObservable } from '@ngrx/store';
import { Observable, of } from 'rxjs';
import { catchError, exhaustMap, map, tap, take } from 'rxjs/operators';
import { RPCClientError } from '@loafer/ng-rpc';
import { MetaCrawler } from '@overflow/commons-typescript/model/meta';
import { MetaCrawlerService } from '@overflow/meta/service/meta-crawler.service';
@Component({
selector: 'of-service-selector',
templateUrl: './service-selector.component.html',
})
export class ServiceSelectorComponent implements OnInit, AfterContentInit, OnDestroy {
includeServices: MetaCrawler[];
@Output() change = new EventEmitter<MetaCrawler[]>();
@Input() disabled: boolean;
metaCrawlers: MetaCrawler[];
pending$: Observable<boolean>;
error$: Observable<any>;
constructor(
protected store: Store<any>,
protected metaCrawlerService: MetaCrawlerService,
) {
this.includeServices = [];
}
ngOnInit() {
this.metaCrawlerService.readAll()
.pipe(
tap(() => {
this.pending$ = of(true);
}),
map((metaCrawlers: MetaCrawler[]) => {
this.metaCrawlers = metaCrawlers;
}),
catchError(error => {
this.error$ = of(error);
return of(null);
}),
tap(() => {
this.pending$ = of(false);
this.includeServices = this.metaCrawlers.slice();
this.change.emit(this.includeServices);
}),
take(1),
).subscribe();
}
ngOnDestroy() {
}
ngAfterContentInit() {
// this.listStore.dispatch(new ListStore.ReadAll());
}
onSelectAll() {
this.includeServices = [];
this.metaCrawlers.forEach((value) => {
this.includeServices.push(value);
});
this.change.emit(this.includeServices);
}
onUnselectAll() {
this.includeServices = [];
this.change.emit(this.includeServices);
}
onSelect(crawler: MetaCrawler) {
this.change.emit(this.includeServices);
}
onUnselect(crawler: MetaCrawler) {
this.change.emit(this.includeServices);
}
}

View File

@ -3,10 +3,9 @@ import {
} from '@angular/core';
import { InfraService, InfraHost, InfraZone, Infra } from '@overflow/commons-typescript/model/infra';
import { Observable, of } from 'rxjs';
import { catchError, exhaustMap, map, tap, take } from 'rxjs/operators';
import { catchError, map, tap, take } from 'rxjs/operators';
import { TreeNode, MenuItem, ContextMenu } from 'primeng/primeng';
import { PageParams, Page } from '@overflow/commons-typescript/core/model';
import { ProbeHost, MetaInfraTypeEnum } from '@overflow/commons-typescript';
import { ProbeHost, MetaInfraTypeEnum, toMetaInfraType } from '@overflow/commons-typescript';
import { InfraService as InfraManageService } from '../service/infra.service';
@Component({
@ -17,7 +16,10 @@ export class InfraTreeComponent implements OnInit, OnChanges {
@Input() probeHost: ProbeHost;
infras: Infra[];
infraZones: InfraZone[];
infraHosts: InfraHost[];
infraServices: InfraService[];
pending$: Observable<boolean>;
error$: Observable<any>;
@ -46,7 +48,11 @@ export class InfraTreeComponent implements OnInit, OnChanges {
this.contextMenuZone = [
{ label: 'Zone Menu', command: (event) => this.cmZone.hide() },
{ separator: true },
{ label: 'Discovery', icon: 'fa-plus', command: (event) => alert('Discovery') },
{
label: 'Discovery', icon: 'fa-plus', command: (event) => {
alert('discovery');
}
},
];
this.contextMenuHost = [
{ label: 'Host Menu', command: (event) => this.cmHost.hide() },
@ -66,26 +72,36 @@ export class InfraTreeComponent implements OnInit, OnChanges {
if (changes['probeHost'].isFirstChange) {
this.zoneNode = [];
this.hostNode = [];
this.getInfras();
this.getInfraWithInfraTypeKey(toMetaInfraType(MetaInfraTypeEnum.ZONE).key);
this.getInfraWithInfraTypeKey(toMetaInfraType(MetaInfraTypeEnum.HOST).key);
this.getInfraWithInfraTypeKey(toMetaInfraType(MetaInfraTypeEnum.SERVICE).key);
}
}
getInfras() {
const pageParams: PageParams = {
pageNo: 0,
countPerPage: 99999,
sortCol: 'id',
sortDirection: 'descending'
};
this.infraService.readAllByProbeID(this.probeHost.probe.id, pageParams)
getInfraWithInfraTypeKey(metaInfraTypeKey: string) {
this.infraService.readAllByMetaInfraTypeKeyAndProbeID(
metaInfraTypeKey, this.probeHost.probe.id)
.pipe(
tap(() => {
this.pending$ = of(true);
}),
map((infraPage: Page<Infra>) => {
this.infras = infraPage.content;
console.log(this.infras);
this.generateTreeData(this.infras);
map((infras: Infra[]) => {
switch (metaInfraTypeKey) {
case toMetaInfraType(MetaInfraTypeEnum.ZONE).key:
this.infraZones = infras;
break;
case toMetaInfraType(MetaInfraTypeEnum.HOST).key:
this.infraHosts = infras;
break;
case toMetaInfraType(MetaInfraTypeEnum.SERVICE).key:
this.infraServices = infras;
break;
default:
break;
}
if (this.infraZones && this.infraHosts && this.infraServices) {
this.generateTreeData();
}
}),
catchError(error => {
this.error$ = of(error);
@ -98,21 +114,24 @@ export class InfraTreeComponent implements OnInit, OnChanges {
).subscribe();
}
generateTreeData(infras: Infra[]) {
infras.forEach(infra => {
switch (infra.metaInfraType.key) {
case MetaInfraTypeEnum.ZONE:
this.addZone(infra);
break;
case MetaInfraTypeEnum.HOST:
this.addHost(infra);
break;
case MetaInfraTypeEnum.HOST:
this.addService(infra);
break;
default:
break;
generateTreeData() {
if (null === this.infraZones) {
return;
}
this.infraZones.forEach(infraZone => {
this.addZone(infraZone);
});
if (null === this.infraHosts) {
return;
}
this.infraHosts.forEach(infraHost => {
this.addHost(infraHost);
});
if (null === this.infraServices) {
return;
}
this.infraServices.forEach(infraService => {
this.addService(infraService);
});
}

View File

@ -48,4 +48,9 @@ export class InfraService {
public readAllInfraZoneByProbeDomainID(probeDomainID: number): Observable<Infra[]> {
return this.rpcService.call<Infra[]>('InfraService.readAllInfraZoneByProbeDomainID', probeDomainID);
}
public readAllByMetaInfraTypeKeyAndProbeID(metaInfraTypeKey: string, probeID: number): Observable<Infra[]> {
return this.rpcService.call<Infra[]>('InfraService.readAllByMetaInfraTypeKeyAndProbeID', metaInfraTypeKey, probeID);
}
}