-
+
@if (this.PermissionsService.hasPermissionObservable(['services', 'browser'])|async) {
@@ -618,7 +618,7 @@
-
+
@if (this.PermissionsService.hasPermissionObservable(['services', 'browser'])|async) {
@@ -632,7 +632,6 @@
}
-
import('./pages/wizards/broadcom-proxy/broadcom-proxy.component').then(m => BroadcomProxyComponent)
+ },
+];
diff --git a/src/app/modules/broadcomproxy_module/pages/wizards/broadcom-proxy/broadcom-proxy-wizard.interface.ts b/src/app/modules/broadcomproxy_module/pages/wizards/broadcom-proxy/broadcom-proxy-wizard.interface.ts
new file mode 100644
index 000000000..2554f6894
--- /dev/null
+++ b/src/app/modules/broadcomproxy_module/pages/wizards/broadcom-proxy/broadcom-proxy-wizard.interface.ts
@@ -0,0 +1,110 @@
+import { WizardGet, WizardPost } from '../../../../../pages/wizards/wizards.interface';
+
+// WIZARD GET
+export interface BroadcomProxyWizardGet extends WizardGet {
+ interfaceServicetemplate: InterfaceServicetemplate
+}
+
+
+export interface InterfaceServicetemplate {
+ id: number
+ uuid: string
+ template_name: string
+ name: string
+ container_id: number
+ servicetemplatetype_id: number
+ check_period_id: number
+ notify_period_id: number
+ description: string
+ command_id: number
+ check_command_args: string
+ checkcommand_info: string
+ eventhandler_command_id: number
+ timeperiod_id: number
+ check_interval: number
+ retry_interval: number
+ max_check_attempts: number
+ first_notification_delay: number
+ notification_interval: number
+ notify_on_warning: number
+ notify_on_unknown: number
+ notify_on_critical: number
+ notify_on_recovery: number
+ notify_on_flapping: number
+ notify_on_downtime: number
+ flap_detection_enabled: number
+ flap_detection_on_ok: number
+ flap_detection_on_warning: number
+ flap_detection_on_unknown: number
+ flap_detection_on_critical: number
+ low_flap_threshold: number
+ high_flap_threshold: number
+ process_performance_data: number
+ freshness_checks_enabled: number
+ freshness_threshold: any
+ passive_checks_enabled: number
+ event_handler_enabled: number
+ active_checks_enabled: number
+ retain_status_information: number
+ retain_nonstatus_information: number
+ notifications_enabled: number
+ notes: string
+ priority: number
+ tags: string
+ service_url: any
+ sla_relevant: number
+ is_volatile: number
+ check_freshness: number
+ created: string
+ modified: string
+ check_command: {
+ id: number
+ name: string
+ command_line: string
+ command_type: number
+ human_args: any
+ uuid: string
+ description: string
+ commandarguments: {
+ id: number
+ command_id: number
+ name: string
+ human_name: string
+ created: string
+ modified: string
+ }[]
+ }
+ servicetemplatecommandargumentvalues: Servicecommandargumentvalue[]
+}
+
+export interface Servicecommandargumentvalue {
+ commandargument: Commandargument
+ commandargument_id: number
+ created: string
+ id: number
+ modified: string
+ servicetemplate_id: number
+ value: string
+}
+
+export interface Commandargument {
+ command_id: number
+ created: string
+ human_name: string
+ id: number
+ modified: string
+ name: string
+}
+
+
+// WIZARD POST
+export interface BroadcomProxyWizardPost extends WizardPost {
+ authPassword: string
+ authProtocol: string
+ privacyPassword: string
+ privacyProtocol: string
+ securityLevel: string
+ securityName: string
+ snmpCommunity: string
+ snmpVersion: string
+}
diff --git a/src/app/modules/broadcomproxy_module/pages/wizards/broadcom-proxy/broadcom-proxy-wizard.service.ts b/src/app/modules/broadcomproxy_module/pages/wizards/broadcom-proxy/broadcom-proxy-wizard.service.ts
new file mode 100644
index 000000000..70c56ac34
--- /dev/null
+++ b/src/app/modules/broadcomproxy_module/pages/wizards/broadcom-proxy/broadcom-proxy-wizard.service.ts
@@ -0,0 +1,39 @@
+import { Injectable } from '@angular/core';
+import { catchError, map, Observable, of } from 'rxjs';
+import { WizardsService } from '../../../../../pages/wizards/wizards.service';
+import { GenericResponseWrapper, GenericValidationError } from '../../../../../generic-responses';
+import { BroadcomProxyWizardGet, BroadcomProxyWizardPost } from './broadcom-proxy-wizard.interface';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class BroadcomProxyWizardService extends WizardsService {
+
+ public fetch(hostId: number): Observable {
+ return this.http.get(`${this.proxyPath}/broadcom_proxy_module/wizards/broadcomProxy/${hostId}.json?angular=true`).pipe(
+ map((data: BroadcomProxyWizardGet): BroadcomProxyWizardGet => {
+ return data;
+ })
+ );
+ }
+
+ public submit(post: BroadcomProxyWizardPost): Observable {
+ return this.http.post(`${this.proxyPath}/broadcom_proxy_module/wizards/broadcomProxy.json?angular=true`, post)
+ .pipe(
+ map(data => {
+ return {
+ success: true,
+ data: null
+ };
+ }),
+ catchError((error: any) => {
+ const err = error.error.error as GenericValidationError;
+ return of({
+ success: false,
+ data: err
+ });
+ })
+ );
+
+ }
+}
diff --git a/src/app/components/charts/host-radialbar-chart/host-radialbar-chart.component.css b/src/app/modules/broadcomproxy_module/pages/wizards/broadcom-proxy/broadcom-proxy.component.css
similarity index 100%
rename from src/app/components/charts/host-radialbar-chart/host-radialbar-chart.component.css
rename to src/app/modules/broadcomproxy_module/pages/wizards/broadcom-proxy/broadcom-proxy.component.css
diff --git a/src/app/modules/broadcomproxy_module/pages/wizards/broadcom-proxy/broadcom-proxy.component.html b/src/app/modules/broadcomproxy_module/pages/wizards/broadcom-proxy/broadcom-proxy.component.html
new file mode 100644
index 000000000..9cd208b5c
--- /dev/null
+++ b/src/app/modules/broadcomproxy_module/pages/wizards/broadcom-proxy/broadcom-proxy.component.html
@@ -0,0 +1,218 @@
+
+
+
+
+
+
+
+ {{ t('Home') }}
+
+
+
+
+ {{ t('Wizards') }}
+
+
+
+ {{ t('Broadcom Proxy') }}
+
+
+
+
+
+
+
+ {{ t('Configuration Wizard: Broadcom Proxy') }}
+
+
+
+
+
+
+
+
+ {{ t('Host Information') }}
+
+
+
+
+
+ {{ t('Configure SNMP for Broadcom Proxy Monitoring') }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('Next') }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('SNMP Server Settings') }}
+
+
+
+
+
+ {{ t('SNMP Version') }}
+
+
+
+
+
+
+
+
+
+ {{ t('Security level') }}
+
+
+
+
+
+ {{ t('Communication with authentication and privacy. The protocols used for Authentication are MD5 and SHA and for Privacy, DES (Data Encryption Standard) and AES (Advanced Encryption Standard).') }}
+ {{ t('Communication with authentication and without privacy. The protocols used for Authentication are MD5 and SHA (Secure Hash Algorithm).') }}
+ {{ t('Communication without authentication and privacy.') }}
+
+
+
+
+
+
+ {{ t('Auth Protocol') }}
+
+
+
+
+
+
+
+
+ {{ t('Security name') }}
+
+
+
+
+
+
+
+ {{ t('Auth password') }}
+
+
+
+
+
+
+
+ {{ t('Privacy Protocol') }}
+
+
+
+
+
+
+
+
+ {{ t('Privacy password') }}
+
+
+
+
+
+
+
+ {{ t('Community') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/modules/broadcomproxy_module/pages/wizards/broadcom-proxy/broadcom-proxy.component.spec.ts b/src/app/modules/broadcomproxy_module/pages/wizards/broadcom-proxy/broadcom-proxy.component.spec.ts
new file mode 100644
index 000000000..f8169a62b
--- /dev/null
+++ b/src/app/modules/broadcomproxy_module/pages/wizards/broadcom-proxy/broadcom-proxy.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { BroadcomProxyComponent } from './broadcom-proxy.component';
+
+describe('BroadcomProxyComponent', () => {
+ let component: BroadcomProxyComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [BroadcomProxyComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(BroadcomProxyComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/modules/broadcomproxy_module/pages/wizards/broadcom-proxy/broadcom-proxy.component.ts b/src/app/modules/broadcomproxy_module/pages/wizards/broadcom-proxy/broadcom-proxy.component.ts
new file mode 100644
index 000000000..70039d806
--- /dev/null
+++ b/src/app/modules/broadcomproxy_module/pages/wizards/broadcom-proxy/broadcom-proxy.component.ts
@@ -0,0 +1,98 @@
+import { ChangeDetectionStrategy, Component, inject, ViewChild } from '@angular/core';
+import { WizardsAbstractComponent } from '../../../../../pages/wizards/wizards-abstract/wizards-abstract.component';
+import { SelectKeyValueString } from '../../../../../layouts/primeng/select.interface';
+import {
+ WizardsDynamicfieldsComponent
+} from '../../../../../components/wizards/wizards-dynamicfields/wizards-dynamicfields.component';
+import { RouterLink } from '@angular/router';
+import { FaIconComponent } from '@fortawesome/angular-fontawesome';
+import {
+ CardBodyComponent,
+ CardComponent,
+ CardHeaderComponent,
+ CardTitleDirective,
+ FormControlDirective,
+ FormLabelDirective
+} from '@coreui/angular';
+import { TranslocoDirective, TranslocoPipe } from '@jsverse/transloco';
+import { RequiredIconComponent } from '../../../../../components/required-icon/required-icon.component';
+import { BackButtonDirective } from '../../../../../directives/back-button.directive';
+import { BroadcomProxyWizardPost } from './broadcom-proxy-wizard.interface';
+import { BroadcomProxyWizardService } from './broadcom-proxy-wizard.service';
+import { SelectComponent } from '../../../../../layouts/primeng/select/select/select.component';
+import { FormErrorDirective } from '../../../../../layouts/coreui/form-error.directive';
+import { FormFeedbackComponent } from '../../../../../layouts/coreui/form-feedback/form-feedback.component';
+import { FormsModule } from '@angular/forms';
+import { NgIf } from '@angular/common';
+
+@Component({
+ selector: 'oitc-broadcom-proxy',
+ imports: [
+ RouterLink,
+ FaIconComponent,
+ CardComponent,
+ CardHeaderComponent,
+ CardBodyComponent,
+ BackButtonDirective,
+ TranslocoPipe,
+ RequiredIconComponent,
+ FormLabelDirective,
+ SelectComponent,
+ FormErrorDirective,
+ FormFeedbackComponent,
+ FormsModule,
+ FormControlDirective,
+ WizardsDynamicfieldsComponent,
+ TranslocoDirective,
+ CardTitleDirective,
+ NgIf
+ ],
+ templateUrl: './broadcom-proxy.component.html',
+ styleUrl: './broadcom-proxy.component.css',
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class BroadcomProxyComponent extends WizardsAbstractComponent {
+ @ViewChild(WizardsDynamicfieldsComponent) childComponentLocal!: WizardsDynamicfieldsComponent;
+ protected override WizardService: BroadcomProxyWizardService = inject(BroadcomProxyWizardService);
+ public checked: boolean = false;
+
+ protected override post: BroadcomProxyWizardPost = {
+// Default fields from the base wizard
+ host_id: 0,
+ services: [],
+// Fields for the wizard
+ authPassword: '',
+ authProtocol: 'md5',
+ privacyPassword: '',
+ privacyProtocol: 'des',
+ securityLevel: '1',
+ securityName: '',
+ snmpCommunity: '',
+ snmpVersion: '2'
+ } as BroadcomProxyWizardPost;
+ protected snmpVersions: SelectKeyValueString[] = [
+ {value: '1', key: 'SNMP V 1'},
+ {value: '2', key: 'SNMP V 2c'},
+ {value: '3', key: 'SNMP V 3'},
+ ]
+
+
+ protected securityLevels: SelectKeyValueString[] = [
+ {key: 'authPriv', value: '1'},
+ {key: 'authNoPriv', value: '2'},
+ {key: 'noAuthNoPriv', value: '3'},
+ ];
+ protected authProtocols: SelectKeyValueString[] = [
+ {key: 'MD5', value: 'md5'},
+ {key: 'SHA', value: 'sha'},
+ ];
+ protected privacyProtocols: SelectKeyValueString[] = [
+ {key: 'DES', value: 'des'},
+ {key: 'AES', value: 'aes'},
+ {key: 'AES128', value: 'aes128'},
+ {key: '3DES', value: '3des'},
+ {key: '3DESDE', value: '3desde'},
+ ];
+
+}
+
diff --git a/src/app/modules/cisco_module/cisco_module.routes.ts b/src/app/modules/cisco_module/cisco_module.routes.ts
new file mode 100644
index 000000000..8d42dd2a3
--- /dev/null
+++ b/src/app/modules/cisco_module/cisco_module.routes.ts
@@ -0,0 +1,14 @@
+import { Routes } from '@angular/router';
+import { CiscoNetworkComponent } from './pages/wizards/cisco-network/cisco-network.component';
+import { CiscoWlcComponent } from './pages/wizards/cisco-wlc/cisco-wlc.component';
+
+export const ciscoModuleRoutes: Routes = [
+ {
+ path: 'cisco_module/wizards/:hostId/cisco_network',
+ loadComponent: () => import('./pages/wizards/cisco-network/cisco-network.component').then(m => CiscoNetworkComponent)
+ },
+ {
+ path: 'cisco_module/wizards/:hostId/cisco_wlc',
+ loadComponent: () => import('./pages/wizards/cisco-wlc/cisco-wlc.component').then(m => CiscoWlcComponent)
+ },
+];
diff --git a/src/app/modules/cisco_module/pages/wizards/cisco-network/cisco-network-wizard.interface.ts b/src/app/modules/cisco_module/pages/wizards/cisco-network/cisco-network-wizard.interface.ts
new file mode 100644
index 000000000..9e9f57b88
--- /dev/null
+++ b/src/app/modules/cisco_module/pages/wizards/cisco-network/cisco-network-wizard.interface.ts
@@ -0,0 +1,110 @@
+import { WizardGet, WizardPost } from '../../../../../pages/wizards/wizards.interface';
+
+// WIZARD GET
+export interface CiscoNetworkWizardGet extends WizardGet {
+ interfaceServicetemplate: InterfaceServicetemplate
+}
+
+
+export interface InterfaceServicetemplate {
+ id: number
+ uuid: string
+ template_name: string
+ name: string
+ container_id: number
+ servicetemplatetype_id: number
+ check_period_id: number
+ notify_period_id: number
+ description: string
+ command_id: number
+ check_command_args: string
+ checkcommand_info: string
+ eventhandler_command_id: number
+ timeperiod_id: number
+ check_interval: number
+ retry_interval: number
+ max_check_attempts: number
+ first_notification_delay: number
+ notification_interval: number
+ notify_on_warning: number
+ notify_on_unknown: number
+ notify_on_critical: number
+ notify_on_recovery: number
+ notify_on_flapping: number
+ notify_on_downtime: number
+ flap_detection_enabled: number
+ flap_detection_on_ok: number
+ flap_detection_on_warning: number
+ flap_detection_on_unknown: number
+ flap_detection_on_critical: number
+ low_flap_threshold: number
+ high_flap_threshold: number
+ process_performance_data: number
+ freshness_checks_enabled: number
+ freshness_threshold: any
+ passive_checks_enabled: number
+ event_handler_enabled: number
+ active_checks_enabled: number
+ retain_status_information: number
+ retain_nonstatus_information: number
+ notifications_enabled: number
+ notes: string
+ priority: number
+ tags: string
+ service_url: any
+ sla_relevant: number
+ is_volatile: number
+ check_freshness: number
+ created: string
+ modified: string
+ check_command: {
+ id: number
+ name: string
+ command_line: string
+ command_type: number
+ human_args: any
+ uuid: string
+ description: string
+ commandarguments: {
+ id: number
+ command_id: number
+ name: string
+ human_name: string
+ created: string
+ modified: string
+ }[]
+ }
+ servicetemplatecommandargumentvalues: Servicecommandargumentvalue[]
+}
+
+export interface Servicecommandargumentvalue {
+ commandargument: Commandargument
+ commandargument_id: number
+ created: string
+ id: number
+ modified: string
+ servicetemplate_id: number
+ value: string
+}
+
+export interface Commandargument {
+ command_id: number
+ created: string
+ human_name: string
+ id: number
+ modified: string
+ name: string
+}
+
+
+// WIZARD POST
+export interface CiscoNetworkWizardPost extends WizardPost {
+ authPassword: string
+ authProtocol: string
+ privacyPassword: string
+ privacyProtocol: string
+ securityLevel: string
+ securityName: string
+ snmpCommunity: string
+ snmpVersion: string
+}
diff --git a/src/app/modules/cisco_module/pages/wizards/cisco-network/cisco-network-wizard.service.ts b/src/app/modules/cisco_module/pages/wizards/cisco-network/cisco-network-wizard.service.ts
new file mode 100644
index 000000000..7e7078c66
--- /dev/null
+++ b/src/app/modules/cisco_module/pages/wizards/cisco-network/cisco-network-wizard.service.ts
@@ -0,0 +1,39 @@
+import { Injectable } from '@angular/core';
+import { catchError, map, Observable, of } from 'rxjs';
+import { WizardsService } from '../../../../../pages/wizards/wizards.service';
+import { GenericResponseWrapper, GenericValidationError } from '../../../../../generic-responses';
+import { CiscoNetworkWizardGet, CiscoNetworkWizardPost } from './cisco-network-wizard.interface';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class CiscoNetworkWizardService extends WizardsService {
+
+ public fetch(hostId: number): Observable {
+ return this.http.get(`${this.proxyPath}/cisco_module/wizards/cisco_network/${hostId}.json?angular=true`).pipe(
+ map((data: CiscoNetworkWizardGet): CiscoNetworkWizardGet => {
+ return data;
+ })
+ );
+ }
+
+ public submit(post: CiscoNetworkWizardPost): Observable {
+ return this.http.post(`${this.proxyPath}/cisco_module/wizards/cisco_network.json?angular=true`, post)
+ .pipe(
+ map(data => {
+ return {
+ success: true,
+ data: null
+ };
+ }),
+ catchError((error: any) => {
+ const err = error.error.error as GenericValidationError;
+ return of({
+ success: false,
+ data: err
+ });
+ })
+ );
+
+ }
+}
diff --git a/src/app/components/charts/service-radialbar-chart/service-radialbar-chart.component.css b/src/app/modules/cisco_module/pages/wizards/cisco-network/cisco-network.component.css
similarity index 100%
rename from src/app/components/charts/service-radialbar-chart/service-radialbar-chart.component.css
rename to src/app/modules/cisco_module/pages/wizards/cisco-network/cisco-network.component.css
diff --git a/src/app/modules/cisco_module/pages/wizards/cisco-network/cisco-network.component.html b/src/app/modules/cisco_module/pages/wizards/cisco-network/cisco-network.component.html
new file mode 100644
index 000000000..87cebd080
--- /dev/null
+++ b/src/app/modules/cisco_module/pages/wizards/cisco-network/cisco-network.component.html
@@ -0,0 +1,218 @@
+
+
+
+
+
+
+
+ {{ t('Home') }}
+
+
+
+
+ {{ t('Wizards') }}
+
+
+
+ {{ t('Cisco Network') }}
+
+
+
+
+
+
+
+ {{ t('Configuration Wizard: Cisco Network') }}
+
+
+
+
+
+
+
+
+ {{ t('Host Information') }}
+
+
+
+
+
+ {{ t('Configure SNMP for Cisco Network Monitoring') }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('Next') }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('SNMP Server Settings') }}
+
+
+
+
+
+ {{ t('SNMP Version') }}
+
+
+
+
+
+
+
+
+
+ {{ t('Security level') }}
+
+
+
+
+
+ {{ t('Communication with authentication and privacy. The protocols used for Authentication are MD5 and SHA and for Privacy, DES (Data Encryption Standard) and AES (Advanced Encryption Standard).') }}
+ {{ t('Communication with authentication and without privacy. The protocols used for Authentication are MD5 and SHA (Secure Hash Algorithm).') }}
+ {{ t('Communication without authentication and privacy.') }}
+
+
+
+
+
+
+ {{ t('Auth Protocol') }}
+
+
+
+
+
+
+
+
+ {{ t('Security name') }}
+
+
+
+
+
+
+
+ {{ t('Auth password') }}
+
+
+
+
+
+
+
+ {{ t('Privacy Protocol') }}
+
+
+
+
+
+
+
+
+ {{ t('Privacy password') }}
+
+
+
+
+
+
+
+ {{ t('Community') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/components/charts/host-radialbar-chart/host-radialbar-chart.component.spec.ts b/src/app/modules/cisco_module/pages/wizards/cisco-network/cisco-network.component.spec.ts
similarity index 50%
rename from src/app/components/charts/host-radialbar-chart/host-radialbar-chart.component.spec.ts
rename to src/app/modules/cisco_module/pages/wizards/cisco-network/cisco-network.component.spec.ts
index 7ce1036e3..3e4f1f55a 100644
--- a/src/app/components/charts/host-radialbar-chart/host-radialbar-chart.component.spec.ts
+++ b/src/app/modules/cisco_module/pages/wizards/cisco-network/cisco-network.component.spec.ts
@@ -1,18 +1,18 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { HostRadialbarChartComponent } from './host-radialbar-chart.component';
+import { CiscoNetworkComponent } from './cisco-network.component';
-describe('HostRadialbarChartComponent', () => {
- let component: HostRadialbarChartComponent;
- let fixture: ComponentFixture;
+describe('CiscoNetworkComponent', () => {
+ let component: CiscoNetworkComponent;
+ let fixture: ComponentFixture;
beforeEach(async () => {
await TestBed.configureTestingModule({
- imports: [HostRadialbarChartComponent]
+ imports: [CiscoNetworkComponent]
})
.compileComponents();
- fixture = TestBed.createComponent(HostRadialbarChartComponent);
+ fixture = TestBed.createComponent(CiscoNetworkComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/src/app/modules/cisco_module/pages/wizards/cisco-network/cisco-network.component.ts b/src/app/modules/cisco_module/pages/wizards/cisco-network/cisco-network.component.ts
new file mode 100644
index 000000000..eaef94118
--- /dev/null
+++ b/src/app/modules/cisco_module/pages/wizards/cisco-network/cisco-network.component.ts
@@ -0,0 +1,98 @@
+import { ChangeDetectionStrategy, Component, inject, ViewChild } from '@angular/core';
+import { WizardsAbstractComponent } from '../../../../../pages/wizards/wizards-abstract/wizards-abstract.component';
+import { SelectKeyValueString } from '../../../../../layouts/primeng/select.interface';
+import {
+ WizardsDynamicfieldsComponent
+} from '../../../../../components/wizards/wizards-dynamicfields/wizards-dynamicfields.component';
+import { RouterLink } from '@angular/router';
+import { FaIconComponent } from '@fortawesome/angular-fontawesome';
+import {
+ CardBodyComponent,
+ CardComponent,
+ CardHeaderComponent,
+ CardTitleDirective,
+ FormControlDirective,
+ FormLabelDirective
+} from '@coreui/angular';
+import { TranslocoDirective, TranslocoPipe } from '@jsverse/transloco';
+import { RequiredIconComponent } from '../../../../../components/required-icon/required-icon.component';
+import { BackButtonDirective } from '../../../../../directives/back-button.directive';
+import { CiscoNetworkWizardPost } from './cisco-network-wizard.interface';
+import { CiscoNetworkWizardService } from './cisco-network-wizard.service';
+import { SelectComponent } from '../../../../../layouts/primeng/select/select/select.component';
+import { FormErrorDirective } from '../../../../../layouts/coreui/form-error.directive';
+import { FormFeedbackComponent } from '../../../../../layouts/coreui/form-feedback/form-feedback.component';
+import { FormsModule } from '@angular/forms';
+import { NgIf } from '@angular/common';
+
+@Component({
+ selector: 'oitc-cisco-network',
+ imports: [
+ RouterLink,
+ FaIconComponent,
+ CardComponent,
+ CardHeaderComponent,
+ CardBodyComponent,
+ BackButtonDirective,
+ TranslocoPipe,
+ RequiredIconComponent,
+ FormLabelDirective,
+ SelectComponent,
+ FormErrorDirective,
+ FormFeedbackComponent,
+ FormsModule,
+ FormControlDirective,
+ WizardsDynamicfieldsComponent,
+ TranslocoDirective,
+ CardTitleDirective,
+ NgIf
+ ],
+ templateUrl: './cisco-network.component.html',
+ styleUrl: './cisco-network.component.css',
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class CiscoNetworkComponent extends WizardsAbstractComponent {
+ @ViewChild(WizardsDynamicfieldsComponent) childComponentLocal!: WizardsDynamicfieldsComponent;
+ protected override WizardService: CiscoNetworkWizardService = inject(CiscoNetworkWizardService);
+ public checked: boolean = false;
+
+ protected override post: CiscoNetworkWizardPost = {
+// Default fields from the base wizard
+ host_id: 0,
+ services: [],
+// Fields for the wizard
+ authPassword: '',
+ authProtocol: 'md5',
+ privacyPassword: '',
+ privacyProtocol: 'des',
+ securityLevel: '1',
+ securityName: '',
+ snmpCommunity: '',
+ snmpVersion: '2'
+ } as CiscoNetworkWizardPost;
+ protected snmpVersions: SelectKeyValueString[] = [
+ {value: '1', key: 'SNMP V 1'},
+ {value: '2', key: 'SNMP V 2c'},
+ {value: '3', key: 'SNMP V 3'},
+ ]
+
+
+ protected securityLevels: SelectKeyValueString[] = [
+ {key: 'authPriv', value: '1'},
+ {key: 'authNoPriv', value: '2'},
+ {key: 'noAuthNoPriv', value: '3'},
+ ];
+ protected authProtocols: SelectKeyValueString[] = [
+ {key: 'MD5', value: 'md5'},
+ {key: 'SHA', value: 'sha'},
+ ];
+ protected privacyProtocols: SelectKeyValueString[] = [
+ {key: 'DES', value: 'des'},
+ {key: 'AES', value: 'aes'},
+ {key: 'AES128', value: 'aes128'},
+ {key: '3DES', value: '3des'},
+ {key: '3DESDE', value: '3desde'},
+ ];
+
+}
+
diff --git a/src/app/modules/cisco_module/pages/wizards/cisco-wlc/cisco-wlc-wizard.interface.ts b/src/app/modules/cisco_module/pages/wizards/cisco-wlc/cisco-wlc-wizard.interface.ts
new file mode 100644
index 000000000..c5e156fcb
--- /dev/null
+++ b/src/app/modules/cisco_module/pages/wizards/cisco-wlc/cisco-wlc-wizard.interface.ts
@@ -0,0 +1,137 @@
+import { WizardGet, WizardPost } from '../../../../../pages/wizards/wizards.interface';
+import { GenericValidationError } from '../../../../../generic-responses';
+
+// WIZARD GET
+export interface CiscoWlcWizardGet extends WizardGet {
+ interfaceServicetemplate: InterfaceServicetemplate
+}
+
+
+export interface InterfaceServicetemplate {
+ id: number
+ uuid: string
+ template_name: string
+ name: string
+ container_id: number
+ servicetemplatetype_id: number
+ check_period_id: number
+ notify_period_id: number
+ description: string
+ command_id: number
+ check_command_args: string
+ checkcommand_info: string
+ eventhandler_command_id: number
+ timeperiod_id: number
+ check_interval: number
+ retry_interval: number
+ max_check_attempts: number
+ first_notification_delay: number
+ notification_interval: number
+ notify_on_warning: number
+ notify_on_unknown: number
+ notify_on_critical: number
+ notify_on_recovery: number
+ notify_on_flapping: number
+ notify_on_downtime: number
+ flap_detection_enabled: number
+ flap_detection_on_ok: number
+ flap_detection_on_warning: number
+ flap_detection_on_unknown: number
+ flap_detection_on_critical: number
+ low_flap_threshold: number
+ high_flap_threshold: number
+ process_performance_data: number
+ freshness_checks_enabled: number
+ freshness_threshold: any
+ passive_checks_enabled: number
+ event_handler_enabled: number
+ active_checks_enabled: number
+ retain_status_information: number
+ retain_nonstatus_information: number
+ notifications_enabled: number
+ notes: string
+ priority: number
+ tags: string
+ service_url: any
+ sla_relevant: number
+ is_volatile: number
+ check_freshness: number
+ created: string
+ modified: string
+ check_command: {
+ id: number
+ name: string
+ command_line: string
+ command_type: number
+ human_args: any
+ uuid: string
+ description: string
+ commandarguments: {
+ id: number
+ command_id: number
+ name: string
+ human_name: string
+ created: string
+ modified: string
+ }[]
+ }
+ servicetemplatecommandargumentvalues: Servicecommandargumentvalue[]
+}
+
+export interface Servicecommandargumentvalue {
+ commandargument: Commandargument
+ commandargument_id: number
+ created: string
+ id: number
+ modified: string
+ servicetemplate_id: number
+ value: string
+}
+
+export interface Commandargument {
+ command_id: number
+ created: string
+ human_name: string
+ id: number
+ modified: string
+ name: string
+}
+
+
+// WIZARD POST
+export interface CiscoWlcWizardPost extends WizardPost {
+ authPassword: string
+ authProtocol: string
+ interfaces: N0[]
+ privacyPassword: string
+ privacyProtocol: string
+ securityLevel: string
+ securityName: string
+ snmpCommunity: string
+ snmpVersion: string
+}
+
+export interface N0 {
+ createService: boolean
+ description: string
+ host_id: number
+ name: string
+ servicecommandargumentvalues: Servicecommandargumentvalue[]
+ servicetemplate_id: number
+}
+
+// SNMP Discovery
+export interface SnmpDiscovery {
+ interfaces: Interface[]
+ success: boolean
+ errors: GenericValidationError | undefined
+ _csrfToken: any
+}
+
+export interface Interface {
+ key: number
+ value: {
+ number: string
+ name: string
+ }
+}
diff --git a/src/app/modules/cisco_module/pages/wizards/cisco-wlc/cisco-wlc-wizard.service.ts b/src/app/modules/cisco_module/pages/wizards/cisco-wlc/cisco-wlc-wizard.service.ts
new file mode 100644
index 000000000..9f40391fe
--- /dev/null
+++ b/src/app/modules/cisco_module/pages/wizards/cisco-wlc/cisco-wlc-wizard.service.ts
@@ -0,0 +1,38 @@
+import { Injectable } from '@angular/core';
+import { catchError, map, Observable, of } from 'rxjs';
+import { WizardsService } from '../../../../../pages/wizards/wizards.service';
+import { GenericResponseWrapper, GenericValidationError } from '../../../../../generic-responses';
+import { CiscoWlcWizardGet, CiscoWlcWizardPost } from './cisco-wlc-wizard.interface';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class CiscoWlcWizardService extends WizardsService {
+ public fetch(hostId: number): Observable {
+ return this.http.get(`${this.proxyPath}/cisco_module/wizards/cisco_wlc/${hostId}.json?angular=true`).pipe(
+ map((data: CiscoWlcWizardGet): CiscoWlcWizardGet => {
+ return data;
+ })
+ );
+ }
+
+ public submit(post: CiscoWlcWizardPost): Observable {
+ return this.http.post(`${this.proxyPath}/cisco_module/wizards/cisco_wlc.json?angular=true`, post)
+ .pipe(
+ map(data => {
+ return {
+ success: true,
+ data: null
+ };
+ }),
+ catchError((error: any) => {
+ const err = error.error.error as GenericValidationError;
+ return of({
+ success: false,
+ data: err
+ });
+ })
+ );
+ }
+
+}
diff --git a/src/app/modules/cisco_module/pages/wizards/cisco-wlc/cisco-wlc.component.css b/src/app/modules/cisco_module/pages/wizards/cisco-wlc/cisco-wlc.component.css
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/app/modules/cisco_module/pages/wizards/cisco-wlc/cisco-wlc.component.html b/src/app/modules/cisco_module/pages/wizards/cisco-wlc/cisco-wlc.component.html
new file mode 100644
index 000000000..e6fb10e64
--- /dev/null
+++ b/src/app/modules/cisco_module/pages/wizards/cisco-wlc/cisco-wlc.component.html
@@ -0,0 +1,217 @@
+
+
+
+
+
+
+
+ {{ t('Home') }}
+
+
+
+
+ {{ t('Wizards') }}
+
+
+
+ {{ t('Cisco WLC') }}
+
+
+
+
+
+
+
+ {{ t('Configuration Wizard: Cisco WLC') }}
+
+
+
+
+
+
+
+
+ {{ t('Host Information') }}
+
+
+
+
+
+ {{ t('Configure SNMP for Cisco WLC') }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('Next') }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('SNMP Server Settings') }}
+
+
+
+
+
+ {{ t('SNMP Version') }}
+
+
+
+
+
+
+
+
+
+ {{ t('Security level') }}
+
+
+
+
+
+ {{ t('Communication with authentication and privacy. The protocols used for Authentication are MD5 and SHA and for Privacy, DES (Data Encryption Standard) and AES (Advanced Encryption Standard).') }}
+ {{ t('Communication with authentication and without privacy. The protocols used for Authentication are MD5 and SHA (Secure Hash Algorithm).') }}
+ {{ t('Communication without authentication and privacy.') }}
+
+
+
+
+
+
+ {{ t('Auth Protocol') }}
+
+
+
+
+
+
+
+
+ {{ t('Security name') }}
+
+
+
+
+
+
+
+ {{ t('Auth password') }}
+
+
+
+
+
+
+
+ {{ t('Privacy Protocol') }}
+
+
+
+
+
+
+
+
+ {{ t('Privacy password') }}
+
+
+
+
+
+
+
+ {{ t('Community') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/modules/cisco_module/pages/wizards/cisco-wlc/cisco-wlc.component.spec.ts b/src/app/modules/cisco_module/pages/wizards/cisco-wlc/cisco-wlc.component.spec.ts
new file mode 100644
index 000000000..59c208bee
--- /dev/null
+++ b/src/app/modules/cisco_module/pages/wizards/cisco-wlc/cisco-wlc.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { CiscoWlcComponent } from './cisco-wlc.component';
+
+describe('CiscoWlcComponent', () => {
+ let component: CiscoWlcComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [CiscoWlcComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(CiscoWlcComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/modules/cisco_module/pages/wizards/cisco-wlc/cisco-wlc.component.ts b/src/app/modules/cisco_module/pages/wizards/cisco-wlc/cisco-wlc.component.ts
new file mode 100644
index 000000000..1d184dabb
--- /dev/null
+++ b/src/app/modules/cisco_module/pages/wizards/cisco-wlc/cisco-wlc.component.ts
@@ -0,0 +1,102 @@
+import { ChangeDetectionStrategy, Component, inject, ViewChild } from '@angular/core';
+import { WizardsAbstractComponent } from '../../../../../pages/wizards/wizards-abstract/wizards-abstract.component';
+import { SelectKeyValueString } from '../../../../../layouts/primeng/select.interface';
+import { CiscoWlcWizardService } from './cisco-wlc-wizard.service';
+import { CiscoWlcWizardPost } from './cisco-wlc-wizard.interface';
+import { RouterLink } from '@angular/router';
+import { FaIconComponent } from '@fortawesome/angular-fontawesome';
+import {
+ CardBodyComponent,
+ CardComponent,
+ CardHeaderComponent,
+ CardTitleDirective,
+ FormControlDirective,
+ FormLabelDirective
+} from '@coreui/angular';
+import { TranslocoDirective, TranslocoPipe } from '@jsverse/transloco';
+import { RequiredIconComponent } from '../../../../../components/required-icon/required-icon.component';
+import { SelectComponent } from '../../../../../layouts/primeng/select/select/select.component';
+import { FormFeedbackComponent } from '../../../../../layouts/coreui/form-feedback/form-feedback.component';
+import { FormErrorDirective } from '../../../../../layouts/coreui/form-error.directive';
+import { FormsModule } from '@angular/forms';
+import { NgIf } from '@angular/common';
+import {
+ WizardsDynamicfieldsComponent
+} from '../../../../../components/wizards/wizards-dynamicfields/wizards-dynamicfields.component';
+import { ProgressBarModule } from 'primeng/progressbar';
+import { BackButtonDirective } from '../../../../../directives/back-button.directive';
+
+@Component({
+ selector: 'oitc-cisco-wlc',
+ imports: [
+ RouterLink,
+ FaIconComponent,
+ CardComponent,
+ CardHeaderComponent,
+ CardBodyComponent,
+ TranslocoPipe,
+ RequiredIconComponent,
+ SelectComponent,
+ FormLabelDirective,
+ FormControlDirective,
+ NgIf,
+ WizardsDynamicfieldsComponent,
+ TranslocoDirective,
+ ProgressBarModule,
+ CardTitleDirective,
+ BackButtonDirective,
+ FormFeedbackComponent,
+ FormErrorDirective,
+ FormsModule
+ ],
+ templateUrl: './cisco-wlc.component.html',
+ styleUrl: './cisco-wlc.component.css',
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class CiscoWlcComponent extends WizardsAbstractComponent {
+ @ViewChild(WizardsDynamicfieldsComponent) childComponentLocal!: WizardsDynamicfieldsComponent;
+ protected override WizardService: CiscoWlcWizardService = inject(CiscoWlcWizardService);
+ public checked: boolean = false;
+ public accordionClosed: boolean = true;
+
+ protected override post: CiscoWlcWizardPost = {
+// Default fields from the base wizard
+ host_id: 0,
+ services: [],
+// Fields for the wizard
+ authPassword: '',
+ authProtocol: 'md5',
+ interfaces: [],
+ privacyPassword: '',
+ privacyProtocol: 'des',
+ securityLevel: '1',
+ securityName: '',
+ snmpCommunity: '',
+ snmpVersion: '2'
+ } as CiscoWlcWizardPost;
+ protected snmpVersions: SelectKeyValueString[] = [
+ {value: '1', key: 'SNMP V 1'},
+ {value: '2', key: 'SNMP V 2c'},
+ {value: '3', key: 'SNMP V 3'},
+ ]
+ protected searchedTags: string[] = [];
+
+
+ protected securityLevels: SelectKeyValueString[] = [
+ {key: 'authPriv', value: '1'},
+ {key: 'authNoPriv', value: '2'},
+ {key: 'noAuthNoPriv', value: '3'},
+ ];
+ protected authProtocols: SelectKeyValueString[] = [
+ {key: 'MD5', value: 'md5'},
+ {key: 'SHA', value: 'sha'},
+ ];
+ protected privacyProtocols: SelectKeyValueString[] = [
+ {key: 'DES', value: 'des'},
+ {key: 'AES', value: 'aes'},
+ {key: 'AES128', value: 'aes128'},
+ {key: '3DES', value: '3des'},
+ {key: '3DESDE', value: '3desde'},
+ ];
+
+}
diff --git a/src/app/modules/import_module/import_module.routes.ts b/src/app/modules/import_module/import_module.routes.ts
index 3834e3ceb..3c4a41467 100644
--- a/src/app/modules/import_module/import_module.routes.ts
+++ b/src/app/modules/import_module/import_module.routes.ts
@@ -33,6 +33,9 @@ export const importModuleRoutes: Routes = [{
}, {
path: 'import_module/ExternalMonitorings/edit/:id',
loadComponent: () => import('./pages/externalmonitorings/external-monitorings-edit/external-monitorings-edit.component').then(m => m.ExternalMonitoringsEditComponent)
+}, {
+ path: 'import_module/ExternalMonitorings/flowchiefNodeSelection/:id',
+ loadComponent: () => import('./pages/externalmonitorings/flowchief-node-selection/flowchief-node-selection.component').then(m => m.FlowchiefNodeSelectionComponent)
}, {
path: 'import_module/ImportedFiles/index',
loadComponent: () => import('./pages/importedfiles/imported-files-index/imported-files-index.component').then(m => m.ImportedFilesIndexComponent)
diff --git a/src/app/modules/import_module/pages/externalmonitorings/external-monitoring-systems.enum.ts b/src/app/modules/import_module/pages/externalmonitorings/external-monitoring-systems.enum.ts
new file mode 100644
index 000000000..5f6858a82
--- /dev/null
+++ b/src/app/modules/import_module/pages/externalmonitorings/external-monitoring-systems.enum.ts
@@ -0,0 +1,6 @@
+export enum ExternalMonitoringSystems {
+ Icinga2 = 'icinga2',
+ OpManager = 'opmanager',
+ PRTG = 'prtg',
+ FlowChief = 'flowchief'
+}
diff --git a/src/app/modules/import_module/pages/externalmonitorings/external-monitorings-add/external-monitorings-add.component.html b/src/app/modules/import_module/pages/externalmonitorings/external-monitorings-add/external-monitorings-add.component.html
index 540d24b3d..7f7545bb8 100644
--- a/src/app/modules/import_module/pages/externalmonitorings/external-monitorings-add/external-monitorings-add.component.html
+++ b/src/app/modules/import_module/pages/externalmonitorings/external-monitorings-add/external-monitorings-add.component.html
@@ -94,7 +94,7 @@ {{ t('Create new external monitoring system') }}
[showClear]="false">
- @if (post.system_type === 'icinga2') {
+ @if (post.system_type === ExternalMonitoringSystems.Icinga2) {
{{ t('Synchronize status information from Icinga 2 with openITCOCKPIT.') }}
}
diff --git a/src/app/modules/import_module/pages/externalmonitorings/external-monitorings-add/external-monitorings-add.component.ts b/src/app/modules/import_module/pages/externalmonitorings/external-monitorings-add/external-monitorings-add.component.ts
index a57301fdd..ffa133549 100644
--- a/src/app/modules/import_module/pages/externalmonitorings/external-monitorings-add/external-monitorings-add.component.ts
+++ b/src/app/modules/import_module/pages/externalmonitorings/external-monitorings-add/external-monitorings-add.component.ts
@@ -2,19 +2,18 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnDestro
import { BackButtonDirective } from '../../../../../directives/back-button.directive';
import { TranslocoDirective, TranslocoService } from '@jsverse/transloco';
import {
- CardBodyComponent,
- CardComponent,
- CardFooterComponent,
- CardHeaderComponent,
- CardTitleDirective,
- ContainerComponent,
- FormControlDirective,
- FormDirective,
- FormLabelDirective,
- NavComponent,
- NavItemComponent
+ CardBodyComponent,
+ CardComponent,
+ CardFooterComponent,
+ CardHeaderComponent,
+ CardTitleDirective,
+ ContainerComponent,
+ FormControlDirective,
+ FormDirective,
+ FormLabelDirective,
+ NavComponent,
+ NavItemComponent
} from '@coreui/angular';
-import { CoreuiComponent } from '../../../../../layouts/coreui/coreui.component';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { FormErrorDirective } from '../../../../../layouts/coreui/form-error.directive';
import { FormFeedbackComponent } from '../../../../../layouts/coreui/form-feedback/form-feedback.component';
@@ -32,6 +31,7 @@ import { SelectKeyValue } from '../../../../../layouts/primeng/select.interface'
import { ContainersLoadContainersByStringParams } from '../../../../../pages/containers/containers.interface';
import {
ExternalMonitoringConfig,
+ ExternalMonitoringConfigFlowChief,
ExternalMonitoringConfigIcinga2,
ExternalMonitoringConfigOpmanager,
ExternalMonitoringConfigPrtg,
@@ -54,37 +54,38 @@ import { DynamicalFormFields } from '../../../../../components/dynamical-form-fi
import {
DynamicalFormFieldsComponent
} from '../../../../../components/dynamical-form-fields/dynamical-form-fields.component';
+import { ExternalMonitoringSystems } from '../external-monitoring-systems.enum';
@Component({
selector: 'oitc-external-monitorings-add',
imports: [
- BackButtonDirective,
- CardBodyComponent,
- CardComponent,
- CardFooterComponent,
- CardHeaderComponent,
- CardTitleDirective,
- FaIconComponent,
- FormControlDirective,
- FormDirective,
- FormErrorDirective,
- FormFeedbackComponent,
- FormLabelDirective,
- FormsModule,
- NavComponent,
- NavItemComponent,
- PermissionDirective,
- ReactiveFormsModule,
- RequiredIconComponent,
- TranslocoDirective,
- XsButtonDirective,
- RouterLink,
- NgIf,
- SelectComponent,
- ContainerComponent,
- NgForOf,
- DynamicalFormFieldsComponent
-],
+ BackButtonDirective,
+ CardBodyComponent,
+ CardComponent,
+ CardFooterComponent,
+ CardHeaderComponent,
+ CardTitleDirective,
+ FaIconComponent,
+ FormControlDirective,
+ FormDirective,
+ FormErrorDirective,
+ FormFeedbackComponent,
+ FormLabelDirective,
+ FormsModule,
+ NavComponent,
+ NavItemComponent,
+ PermissionDirective,
+ ReactiveFormsModule,
+ RequiredIconComponent,
+ TranslocoDirective,
+ XsButtonDirective,
+ RouterLink,
+ NgIf,
+ SelectComponent,
+ ContainerComponent,
+ NgForOf,
+ DynamicalFormFieldsComponent
+ ],
templateUrl: './external-monitorings-add.component.html',
styleUrl: './external-monitorings-add.component.css',
changeDetection: ChangeDetectionStrategy.OnPush
@@ -106,15 +107,19 @@ export class ExternalMonitoringsAddComponent implements OnInit, OnDestroy {
protected readonly ExternalMonitoringTypes = [
{
- key: 'icinga2',
+ key: ExternalMonitoringSystems.FlowChief,
+ value: this.TranslocoService.translate('FlowChief')
+ },
+ {
+ key: ExternalMonitoringSystems.Icinga2,
value: this.TranslocoService.translate('Icinga 2')
},
{
- key: 'opmanager',
+ key: ExternalMonitoringSystems.OpManager,
value: this.TranslocoService.translate('ManageEngine OpManager')
},
{
- key: 'prtg',
+ key: ExternalMonitoringSystems.PRTG,
value: this.TranslocoService.translate('Paessler PRTG System')
}
];
@@ -182,18 +187,25 @@ export class ExternalMonitoringsAddComponent implements OnInit, OnDestroy {
.subscribe((result: ExternalMonitoringConfig) => {
this.errors = null;
switch (this.post.system_type) {
- case 'icinga2':
+ case ExternalMonitoringSystems.Icinga2:
const icinga2 = result.config.config as ExternalMonitoringConfigIcinga2;
this.post.json_data = icinga2;
break;
- case 'opmanager':
+
+ case ExternalMonitoringSystems.OpManager:
const opmanager = result.config.config as ExternalMonitoringConfigOpmanager;
this.post.json_data = opmanager;
break;
- case 'prtg':
+
+ case ExternalMonitoringSystems.PRTG:
const prtg = result.config.config as ExternalMonitoringConfigPrtg;
this.post.json_data = prtg;
break;
+
+ case ExternalMonitoringSystems.FlowChief:
+ const flowChief = result.config.config as ExternalMonitoringConfigFlowChief;
+ this.post.json_data = flowChief;
+ break;
}
this.formFields = result.config.formFields;
@@ -204,4 +216,5 @@ export class ExternalMonitoringsAddComponent implements OnInit, OnDestroy {
}
protected readonly Object = Object;
+ protected readonly ExternalMonitoringSystems = ExternalMonitoringSystems;
}
diff --git a/src/app/modules/import_module/pages/externalmonitorings/external-monitorings-edit/external-monitorings-edit.component.html b/src/app/modules/import_module/pages/externalmonitorings/external-monitorings-edit/external-monitorings-edit.component.html
index d946b9eb4..caab22ec9 100644
--- a/src/app/modules/import_module/pages/externalmonitorings/external-monitorings-edit/external-monitorings-edit.component.html
+++ b/src/app/modules/import_module/pages/externalmonitorings/external-monitorings-edit/external-monitorings-edit.component.html
@@ -100,7 +100,7 @@
[showClear]="false">
- @if (post.system_type === 'icinga2') {
+ @if (post.system_type === ExternalMonitoringSystems.Icinga2) {
{{ t('Synchronize status information from Icinga 2 with openITCOCKPIT.') }}
}
@@ -123,6 +123,24 @@
[(ngModel)]="post.json_data[formField['ngModel']]">
+
+ @if (post.system_type === ExternalMonitoringSystems.FlowChief) {
+
+
+
+
+
+
+
+
+ }
+
}
diff --git a/src/app/modules/import_module/pages/externalmonitorings/external-monitorings-edit/external-monitorings-edit.component.ts b/src/app/modules/import_module/pages/externalmonitorings/external-monitorings-edit/external-monitorings-edit.component.ts
index 168947546..e774d2e3a 100644
--- a/src/app/modules/import_module/pages/externalmonitorings/external-monitorings-edit/external-monitorings-edit.component.ts
+++ b/src/app/modules/import_module/pages/externalmonitorings/external-monitorings-edit/external-monitorings-edit.component.ts
@@ -1,19 +1,21 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnDestroy, OnInit } from '@angular/core';
import { BackButtonDirective } from '../../../../../directives/back-button.directive';
import {
+ AlertComponent,
CardBodyComponent,
CardComponent,
CardFooterComponent,
CardHeaderComponent,
CardTitleDirective,
+ ColComponent,
ContainerComponent,
FormControlDirective,
FormDirective,
FormLabelDirective,
NavComponent,
- NavItemComponent
+ NavItemComponent,
+ RowComponent
} from '@coreui/angular';
-import { CoreuiComponent } from '../../../../../layouts/coreui/coreui.component';
import {
DynamicalFormFieldsComponent
} from '../../../../../components/dynamical-form-fields/dynamical-form-fields.component';
@@ -42,6 +44,7 @@ import { ExternalMonitoringConfig, ExternalMonitoringPost } from '../external-mo
import { PermissionsService } from '../../../../../permissions/permissions.service';
import { SystemnameService } from '../../../../../services/systemname.service';
import { FormLoaderComponent } from '../../../../../layouts/primeng/loading/form-loader/form-loader.component';
+import { ExternalMonitoringSystems } from '../external-monitoring-systems.enum';
@Component({
@@ -73,7 +76,10 @@ import { FormLoaderComponent } from '../../../../../layouts/primeng/loading/form
TranslocoDirective,
XsButtonDirective,
RouterLink,
- FormLoaderComponent
+ FormLoaderComponent,
+ RowComponent,
+ ColComponent,
+ AlertComponent
],
templateUrl: './external-monitorings-edit.component.html',
styleUrl: './external-monitorings-edit.component.css',
@@ -97,15 +103,19 @@ export class ExternalMonitoringsEditComponent implements OnInit, OnDestroy {
protected readonly ExternalMonitoringTypes = [
{
- key: 'icinga2',
+ key: ExternalMonitoringSystems.FlowChief,
+ value: this.TranslocoService.translate('FlowChief')
+ },
+ {
+ key: ExternalMonitoringSystems.Icinga2,
value: this.TranslocoService.translate('Icinga 2')
},
{
- key: 'opmanager',
+ key: ExternalMonitoringSystems.OpManager,
value: this.TranslocoService.translate('ManageEngine OpManager')
},
{
- key: 'prtg',
+ key: ExternalMonitoringSystems.PRTG,
value: this.TranslocoService.translate('Paessler PRTG System')
}
];
@@ -146,7 +156,7 @@ export class ExternalMonitoringsEditComponent implements OnInit, OnDestroy {
this.subscriptions.add(this.ExternalMonitoringsService.getEdit(this.id)
.subscribe((result) => {
//Fire on page load
- this.post = result.externalMonitoring;
+ this.post = result;
this.cdr.markForCheck();
this.loadContainers();
this.loadConfigFieldsBySystemType();
@@ -181,4 +191,6 @@ export class ExternalMonitoringsEditComponent implements OnInit, OnDestroy {
public ngOnDestroy(): void {
this.subscriptions.unsubscribe();
}
+
+ protected readonly ExternalMonitoringSystems = ExternalMonitoringSystems;
}
diff --git a/src/app/modules/import_module/pages/externalmonitorings/external-monitorings-index/external-monitorings-index.component.html b/src/app/modules/import_module/pages/externalmonitorings/external-monitorings-index/external-monitorings-index.component.html
index 15feca3b9..b1628e394 100644
--- a/src/app/modules/import_module/pages/externalmonitorings/external-monitorings-index/external-monitorings-index.component.html
+++ b/src/app/modules/import_module/pages/externalmonitorings/external-monitorings-index/external-monitorings-index.component.html
@@ -174,6 +174,14 @@
{{ t('Edit') }}
+ @if (externalMonitoring.system_type === ExternalMonitoringSystems.FlowChief && externalMonitoring.allowEdit) {
+
+
+ {{ t('Select FlowChef Nodes') }}
+
+ }
diff --git a/src/app/modules/import_module/pages/externalmonitorings/external-monitorings-index/external-monitorings-index.component.ts b/src/app/modules/import_module/pages/externalmonitorings/external-monitorings-index/external-monitorings-index.component.ts
index ba1fc50e4..06a0558ab 100644
--- a/src/app/modules/import_module/pages/externalmonitorings/external-monitorings-index/external-monitorings-index.component.ts
+++ b/src/app/modules/import_module/pages/externalmonitorings/external-monitorings-index/external-monitorings-index.component.ts
@@ -17,7 +17,6 @@ import {
RowComponent,
TableDirective
} from '@coreui/angular';
-import { CoreuiComponent } from '../../../../../layouts/coreui/coreui.component';
import { DebounceDirective } from '../../../../../directives/debounce.directive';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
@@ -52,6 +51,7 @@ import {
import { SelectAllComponent } from '../../../../../layouts/coreui/select-all/select-all.component';
import { DELETE_SERVICE_TOKEN } from '../../../../../tokens/delete-injection.token';
import { IndexPage } from '../../../../../pages.interface';
+import { ExternalMonitoringSystems } from '../external-monitoring-systems.enum';
@Component({
selector: 'oitc-external-monitorings-index',
@@ -94,7 +94,7 @@ import { IndexPage } from '../../../../../pages.interface';
TableDirective
],
providers: [
- { provide: DELETE_SERVICE_TOKEN, useClass: ExternalMonitoringsService } // Inject the ExternalMonitoringsService into the DeleteAllModalComponent
+ {provide: DELETE_SERVICE_TOKEN, useClass: ExternalMonitoringsService} // Inject the ExternalMonitoringsService into the DeleteAllModalComponent
],
templateUrl: './external-monitorings-index.component.html',
styleUrl: './external-monitorings-index.component.css',
@@ -204,4 +204,6 @@ export class ExternalMonitoringsIndexComponent implements OnInit, OnDestroy, Ind
this.load();
}
}
+
+ protected readonly ExternalMonitoringSystems = ExternalMonitoringSystems;
}
diff --git a/src/app/modules/import_module/pages/externalmonitorings/external-monitorings.interface.ts b/src/app/modules/import_module/pages/externalmonitorings/external-monitorings.interface.ts
index 210770f55..1bc19c7f6 100644
--- a/src/app/modules/import_module/pages/externalmonitorings/external-monitorings.interface.ts
+++ b/src/app/modules/import_module/pages/externalmonitorings/external-monitorings.interface.ts
@@ -1,6 +1,7 @@
import { PaginateOrScroll } from '../../../../layouts/coreui/paginator/paginator.interface';
import { DynamicalFormFields } from '../../../../components/dynamical-form-fields/dynamical-form-fields.interface';
import { SelectKeyValue } from '../../../../layouts/primeng/select.interface';
+import { ExternalMonitoringSystems } from './external-monitoring-systems.enum';
export interface ExternalMonitoringsIndexRoot extends PaginateOrScroll {
externalMonitorings: ExternalMonitoring[]
@@ -12,7 +13,7 @@ export interface ExternalMonitoring {
name: string
container_id: number
description: string
- system_type: string
+ system_type: ExternalMonitoringSystems
container: string
allowEdit: boolean
}
@@ -22,14 +23,10 @@ export interface ExternalMonitoringPost {
container_id: number | null
name: string
description: string
- system_type: string
+ system_type: ExternalMonitoringSystems | ''
json_data: any
}
-export interface ExternalMonitoringGet {
- externalMonitoring: ExternalMonitoringPost
-}
-
export interface ExternalMonitoringsIndexParams {
angular: true,
scroll: boolean,
@@ -58,11 +55,12 @@ export function getDefaultExternalMonitoringsIndexParams(): ExternalMonitoringsI
export interface ExternalMonitoringConfig {
config: {
- config: ExternalMonitoringConfigIcinga2 | ExternalMonitoringConfigOpmanager | ExternalMonitoringConfigPrtg
+ config: ExternalMonitoringConfigIcinga2 | ExternalMonitoringConfigOpmanager | ExternalMonitoringConfigPrtg | ExternalMonitoringConfigFlowChief
formFields: DynamicalFormFields
}
}
+// Server code: IcingaExternalMonitoringJson.php
export interface ExternalMonitoringConfigIcinga2 {
api_url: string
api_user: string
@@ -74,6 +72,7 @@ export interface ExternalMonitoringConfigIcinga2 {
receive_acknowledgements: number
}
+// Server code: OpManagerExternalMonitoringJson.php
export interface ExternalMonitoringConfigOpmanager {
api_url: string
api_key: string
@@ -82,6 +81,7 @@ export interface ExternalMonitoringConfigOpmanager {
polling_interval: number
}
+// Server code: PrtgExternalMonitoringJson.php
export interface ExternalMonitoringConfigPrtg {
api_url: string
api_token: string
@@ -93,6 +93,38 @@ export interface ExternalMonitoringConfigPrtg {
include_channels: number
}
-export interface ExternalMonitoringsAsList{
+// Server code: FlowChiefExternalMonitoringJson.php
+export interface ExternalMonitoringConfigFlowChief {
+ api_url: string
+ api_user: string
+ api_password: string
+ use_proxy: number
+ ignore_ssl_certificate: number
+ host_prefix: string
+}
+
+export interface ExternalMonitoringsAsList {
externalMonitorings: SelectKeyValue[]
}
+
+
+export interface ExternalMonitoringWithFlowchiefNodesMembershipPost extends ExternalMonitoringPost {
+ flowchief_nodes_membership: FlowchiefNodesMembership[]
+}
+
+export interface FlowchiefNodesMembership {
+ id?: number // auto increment of linking table
+ external_monitoring_id: number
+ flowchief_node_id: number
+ is_recursive: boolean
+ path?: string // only used by the Angular Frontend
+}
+
+/**********************
+ * Global action *
+ **********************/
+export interface FlowchiefNodesByStringParams {
+ externalMonitoringId: number,
+ 'filter[FlowchiefNodes.path]': string,
+ 'selected[]': number[],
+}
diff --git a/src/app/modules/import_module/pages/externalmonitorings/external-monitorings.service.ts b/src/app/modules/import_module/pages/externalmonitorings/external-monitorings.service.ts
index 9b2915f97..2bd505eee 100644
--- a/src/app/modules/import_module/pages/externalmonitorings/external-monitorings.service.ts
+++ b/src/app/modules/import_module/pages/externalmonitorings/external-monitorings.service.ts
@@ -1,16 +1,18 @@
import { inject, Injectable } from '@angular/core';
import {
ExternalMonitoringConfig,
- ExternalMonitoringGet,
ExternalMonitoringPost,
ExternalMonitoringsIndexParams,
- ExternalMonitoringsIndexRoot
+ ExternalMonitoringsIndexRoot,
+ ExternalMonitoringWithFlowchiefNodesMembershipPost,
+ FlowchiefNodesByStringParams
} from './external-monitorings.interface';
import { catchError, map, Observable, of } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { PROXY_PATH } from '../../../../tokens/proxy-path.token';
import { DeleteAllItem } from '../../../../layouts/coreui/delete-all-modal/delete-all.interface';
import { GenericIdResponse, GenericResponseWrapper, GenericValidationError } from '../../../../generic-responses';
+import { SelectKeyValue } from '../../../../layouts/primeng/select.interface';
@Injectable({
providedIn: 'root'
@@ -38,15 +40,17 @@ export class ExternalMonitoringsService {
return this.http.post(`${proxyPath}/import_module/external_monitorings/loadConfigFieldsBySystemType/${system_type}.json?angular=true`, {});
}
- public getEdit(id: number): Observable {
+ public getEdit(id: number): Observable {
const proxyPath = this.proxyPath;
- return this.http.get(`${proxyPath}/import_module/external_monitorings/edit/${id}.json`, {
+ return this.http.get<{
+ externalMonitoring: ExternalMonitoringPost
+ }>(`${proxyPath}/import_module/external_monitorings/edit/${id}.json`, {
params: {
angular: true
}
}).pipe(
map(data => {
- return data;
+ return data.externalMonitoring;
})
);
}
@@ -107,4 +111,52 @@ export class ExternalMonitoringsService {
const proxyPath = this.proxyPath;
return this.http.post(`${proxyPath}/import_module/external_monitorings/delete/${item.id}.json?angular=true`, {});
}
+
+ /***************************************
+ * flowchiefNodeSelection action *
+ ***************************************/
+ public getFlowchiefNodeSelection(externalMonitoringId: number): Observable {
+ const proxyPath = this.proxyPath;
+ return this.http.get<{
+ externalMonitoring: ExternalMonitoringWithFlowchiefNodesMembershipPost
+ }>(`${proxyPath}/import_module/external_monitorings/flowchiefNodeSelection/${externalMonitoringId}.json`).pipe(
+ map(data => {
+ return data.externalMonitoring;
+ })
+ );
+ }
+
+ public editFlowchiefNodeSelection(externalMonitoring: ExternalMonitoringWithFlowchiefNodesMembershipPost): Observable {
+ const proxyPath = this.proxyPath;
+ return this.http.post(`${proxyPath}/import_module/external_monitorings/flowchiefNodeSelection/${externalMonitoring.id}.json`, externalMonitoring)
+ .pipe(
+ map(data => {
+ // Return true on 200 Ok
+ return {
+ success: true,
+ data: data.externalMonitoring as GenericIdResponse
+ };
+ }),
+ catchError((error: any) => {
+ const err = error.error.error as GenericValidationError;
+ return of({
+ success: false,
+ data: err
+ });
+ })
+ );
+ }
+
+ public loadFlowchiefNodesByString(params: FlowchiefNodesByStringParams): Observable {
+ const proxyPath = this.proxyPath;
+ return this.http.get<{
+ nodes: SelectKeyValue[]
+ }>(`${proxyPath}/import_module/external_monitorings/loadFlowchiefNodesByString.json`, {
+ params: params as {}
+ }).pipe(
+ map(data => {
+ return data.nodes;
+ })
+ )
+ }
}
diff --git a/src/app/modules/import_module/pages/externalmonitorings/flowchief-node-selection/flowchief-node-selection.component.css b/src/app/modules/import_module/pages/externalmonitorings/flowchief-node-selection/flowchief-node-selection.component.css
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/app/modules/import_module/pages/externalmonitorings/flowchief-node-selection/flowchief-node-selection.component.html b/src/app/modules/import_module/pages/externalmonitorings/flowchief-node-selection/flowchief-node-selection.component.html
new file mode 100644
index 000000000..fd575623a
--- /dev/null
+++ b/src/app/modules/import_module/pages/externalmonitorings/flowchief-node-selection/flowchief-node-selection.component.html
@@ -0,0 +1,140 @@
+
+
+
+
+
+
+ {{ t('Home') }}
+
+
+
+
+ {{ t('Import Module') }}
+
+
+
+
+ {{ t('External Monitoring Systems') }}
+
+
+
+
+ {{ t('Select FlowChief nodes') }}
+
+
+
+
+
+
+ @if (post) {
+
+ }
+
diff --git a/src/app/modules/import_module/pages/externalmonitorings/flowchief-node-selection/flowchief-node-selection.component.spec.ts b/src/app/modules/import_module/pages/externalmonitorings/flowchief-node-selection/flowchief-node-selection.component.spec.ts
new file mode 100644
index 000000000..38db1197f
--- /dev/null
+++ b/src/app/modules/import_module/pages/externalmonitorings/flowchief-node-selection/flowchief-node-selection.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { FlowchiefNodeSelectionComponent } from './flowchief-node-selection.component';
+
+describe('FlowchiefNodeSelectionComponent', () => {
+ let component: FlowchiefNodeSelectionComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [FlowchiefNodeSelectionComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(FlowchiefNodeSelectionComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/modules/import_module/pages/externalmonitorings/flowchief-node-selection/flowchief-node-selection.component.ts b/src/app/modules/import_module/pages/externalmonitorings/flowchief-node-selection/flowchief-node-selection.component.ts
new file mode 100644
index 000000000..234d30359
--- /dev/null
+++ b/src/app/modules/import_module/pages/externalmonitorings/flowchief-node-selection/flowchief-node-selection.component.ts
@@ -0,0 +1,202 @@
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnDestroy, OnInit } from '@angular/core';
+import { NotyService } from '../../../../../layouts/coreui/noty.service';
+import { ActivatedRoute, Router, RouterLink } from '@angular/router';
+import { Subscription } from 'rxjs';
+import { ExternalMonitoringsService } from '../external-monitorings.service';
+import { HistoryService } from '../../../../../history.service';
+import { FormFeedbackComponent } from '../../../../../layouts/coreui/form-feedback/form-feedback.component';
+import {
+ AlertComponent,
+ AlertHeadingDirective,
+ CardBodyComponent,
+ CardComponent,
+ CardFooterComponent,
+ CardHeaderComponent,
+ CardTitleDirective,
+ FormCheckComponent,
+ FormCheckInputDirective,
+ FormCheckLabelDirective,
+ FormDirective,
+ FormLabelDirective,
+ NavComponent,
+ NavItemComponent,
+ TextColorDirective
+} from '@coreui/angular';
+import { MultiSelectComponent } from '../../../../../layouts/primeng/multi-select/multi-select/multi-select.component';
+import { TranslocoDirective, TranslocoService } from '@jsverse/transloco';
+import { BackButtonDirective } from '../../../../../directives/back-button.directive';
+import { FaIconComponent } from '@fortawesome/angular-fontawesome';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { PermissionDirective } from '../../../../../permissions/permission.directive';
+import { XsButtonDirective } from '../../../../../layouts/coreui/xsbutton-directive/xsbutton.directive';
+import { GenericIdResponse, GenericValidationError } from '../../../../../generic-responses';
+import { SelectKeyValue } from '../../../../../layouts/primeng/select.interface';
+import { FormLoaderComponent } from '../../../../../layouts/primeng/loading/form-loader/form-loader.component';
+import {
+ ExternalMonitoringWithFlowchiefNodesMembershipPost,
+ FlowchiefNodesByStringParams,
+ FlowchiefNodesMembership
+} from '../external-monitorings.interface';
+import { NgClass } from '@angular/common';
+import { CopyToClipboardComponent } from '../../../../../layouts/coreui/copy-to-clipboard/copy-to-clipboard.component';
+
+@Component({
+ selector: 'oitc-flowchief-node-selection',
+ imports: [
+ FormFeedbackComponent,
+ FormLabelDirective,
+ MultiSelectComponent,
+ TranslocoDirective,
+ BackButtonDirective,
+ CardBodyComponent,
+ CardComponent,
+ CardFooterComponent,
+ CardHeaderComponent,
+ CardTitleDirective,
+ FaIconComponent,
+ FormDirective,
+ FormsModule,
+ NavComponent,
+ NavItemComponent,
+ PermissionDirective,
+ ReactiveFormsModule,
+ XsButtonDirective,
+ RouterLink,
+ FormLoaderComponent,
+ FormCheckComponent,
+ FormCheckInputDirective,
+ FormCheckLabelDirective,
+ NgClass,
+ TextColorDirective,
+ CopyToClipboardComponent,
+ AlertComponent,
+ AlertHeadingDirective
+ ],
+ templateUrl: './flowchief-node-selection.component.html',
+ styleUrl: './flowchief-node-selection.component.css',
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class FlowchiefNodeSelectionComponent implements OnInit, OnDestroy {
+
+ public post?: ExternalMonitoringWithFlowchiefNodesMembershipPost;
+ public selectedNodes: number[] = [];
+ public flowchiefNodes: SelectKeyValue[] = [];
+ public errors: GenericValidationError | null = null;
+
+
+ private subscriptions: Subscription = new Subscription();
+
+ private readonly ExternalMonitoringsService = inject(ExternalMonitoringsService);
+ private readonly TranslocoService = inject(TranslocoService);
+ private readonly notyService = inject(NotyService);
+ private readonly HistoryService: HistoryService = inject(HistoryService);
+ private readonly router: Router = inject(Router);
+ private readonly route: ActivatedRoute = inject(ActivatedRoute);
+ private cdr = inject(ChangeDetectorRef);
+
+ public ngOnInit() {
+ this.route.queryParams.subscribe(params => {
+ const id = Number(this.route.snapshot.paramMap.get('id'));
+ this.loadExternalMonitoringFlowchiefConfig(id);
+ });
+ }
+
+ public ngOnDestroy(): void {
+ this.subscriptions.unsubscribe();
+ }
+
+ public loadExternalMonitoringFlowchiefConfig(id: number) {
+ this.subscriptions.add(this.ExternalMonitoringsService.getFlowchiefNodeSelection(id).subscribe(response => {
+ this.post = response;
+ this.selectedNodes = response.flowchief_nodes_membership.map(item => item.flowchief_node_id);
+
+ this.loadFlowchiefNodes('');
+ this.cdr.markForCheck();
+ }));
+ }
+
+ public loadFlowchiefNodes = (searchString: string): void => {
+ const params: FlowchiefNodesByStringParams = {
+ externalMonitoringId: this.post?.id || 0,
+ 'filter[FlowchiefNodes.path]': searchString,
+ 'selected[]': this.selectedNodes
+ }
+
+ this.subscriptions.add(this.ExternalMonitoringsService.loadFlowchiefNodesByString(params)
+ .subscribe((result) => {
+ this.flowchiefNodes = result;
+ this.cdr.markForCheck();
+ })
+ );
+ };
+
+ public updateNodesMembership() {
+ if (!this.post) {
+ return;
+ }
+
+ // Cleanup if a node got deselected
+ const flowchief_nodes_membership: FlowchiefNodesMembership[] = [];
+ const alreadyInMembership: number[] = [];
+
+ this.post.flowchief_nodes_membership.forEach((flowchief_node) => {
+ if (this.selectedNodes.includes(flowchief_node.flowchief_node_id)) {
+ flowchief_nodes_membership.push(flowchief_node);
+ }
+ alreadyInMembership.push(flowchief_node.flowchief_node_id);
+ });
+
+ // All selected nodes to membership (so we can sret is_recursive properly)
+ this.selectedNodes.forEach((nodeId) => {
+ let externalMonitoringId = 0;
+ if (this.post && this.post.id) {
+ // This is just to make TypeScript happy
+ externalMonitoringId = this.post.id;
+ }
+
+ if (!alreadyInMembership.includes(nodeId)) {
+
+ const node = this.flowchiefNodes.find(n => n.key === nodeId);
+ if (node) {
+ flowchief_nodes_membership.push({
+ flowchief_node_id: nodeId,
+ external_monitoring_id: externalMonitoringId,
+ is_recursive: false,
+ path: node.value // only used by the Angular Frontend
+ });
+ }
+ }
+ });
+
+ this.post.flowchief_nodes_membership = flowchief_nodes_membership;
+ }
+
+ public submit() {
+ if (!this.post) {
+ return;
+ }
+
+ this.subscriptions.add(this.ExternalMonitoringsService.editFlowchiefNodeSelection(this.post)
+ .subscribe((result) => {
+ this.cdr.markForCheck();
+ if (result.success) {
+ const response = result.data as GenericIdResponse;
+ const title = this.TranslocoService.translate('FlowChief Nodes');
+ const msg = this.TranslocoService.translate('updated successfully');
+ const url = ['import_module', 'ExternalMonitorings', 'flowchiefNodeSelection', response.id];
+
+ this.notyService.genericSuccess(msg, title, url);
+ this.HistoryService.navigateWithFallback(['/import_module/ExternalMonitorings/index']);
+ this.notyService.scrollContentDivToTop();
+ return;
+ }
+
+ // Error
+ const errorResponse = result.data as GenericValidationError;
+ this.notyService.genericError();
+ if (result) {
+ this.errors = errorResponse;
+ }
+ }));
+ }
+}
diff --git a/src/app/modules/import_module/pages/importedhosts/imported-hosts-edit/imported-hosts-edit.component.html b/src/app/modules/import_module/pages/importedhosts/imported-hosts-edit/imported-hosts-edit.component.html
index b2b792118..fd4db054c 100644
--- a/src/app/modules/import_module/pages/importedhosts/imported-hosts-edit/imported-hosts-edit.component.html
+++ b/src/app/modules/import_module/pages/importedhosts/imported-hosts-edit/imported-hosts-edit.component.html
@@ -140,6 +140,29 @@
+
+
+ {{ t('Tags') }}
+
+
+
+
+
+
+
+ {{ t('Press return to separate tags') }}
+
+
+
{{ t('Container') }}
diff --git a/src/app/modules/import_module/pages/importedhosts/imported-hosts-edit/imported-hosts-edit.component.ts b/src/app/modules/import_module/pages/importedhosts/imported-hosts-edit/imported-hosts-edit.component.ts
index b6761e942..a268a5514 100644
--- a/src/app/modules/import_module/pages/importedhosts/imported-hosts-edit/imported-hosts-edit.component.ts
+++ b/src/app/modules/import_module/pages/importedhosts/imported-hosts-edit/imported-hosts-edit.component.ts
@@ -46,6 +46,7 @@ import { DebounceDirective } from '../../../../../directives/debounce.directive'
import {
RegexHelperTooltipComponent
} from '../../../../../layouts/coreui/regex-helper-tooltip/regex-helper-tooltip.component';
+import { NgSelectModule } from '@ng-select/ng-select';
@Component({
selector: 'oitc-imported-hosts-edit',
@@ -85,7 +86,8 @@ import {
InputGroupComponent,
InputGroupTextDirective,
RegexHelperTooltipComponent,
- CardFooterComponent
+ CardFooterComponent,
+ NgSelectModule
],
templateUrl: './imported-hosts-edit.component.html',
styleUrl: './imported-hosts-edit.component.css',
@@ -94,6 +96,7 @@ import {
export class ImportedHostsEditComponent implements OnInit, OnDestroy {
public post!: ImportedHostPost;
+ public tagsForSelect: string[] = [];
public errors: GenericValidationError | null = null;
public containers: SelectKeyValue[] = [];
@@ -130,7 +133,9 @@ export class ImportedHostsEditComponent implements OnInit, OnDestroy {
public loadImportedHost(id: number): void {
this.subscriptions.add(this.ImportedhostsService.getEdit(id).subscribe((response) => {
this.post = response;
-
+ if (this.post.tags) {
+ this.tagsForSelect = this.post.tags.split(',');
+ }
this.loadContainer();
this.loadElements();
@@ -172,6 +177,7 @@ export class ImportedHostsEditComponent implements OnInit, OnDestroy {
}
public submit() {
+ this.post.tags = this.tagsForSelect.join(',');
this.subscriptions.add(this.ImportedhostsService.saveImportedHostEdit(this.post)
.subscribe((result) => {
this.cdr.markForCheck();
diff --git a/src/app/modules/import_module/pages/importedhosts/importedhosts.interface.ts b/src/app/modules/import_module/pages/importedhosts/importedhosts.interface.ts
index f65c855c7..3f193d290 100644
--- a/src/app/modules/import_module/pages/importedhosts/importedhosts.interface.ts
+++ b/src/app/modules/import_module/pages/importedhosts/importedhosts.interface.ts
@@ -285,6 +285,7 @@ export interface ImportedHostPost {
identifier: string
name: string
description: string
+ tags: string
address: string
container_id: number
hosttemplate_id: number
diff --git a/src/app/modules/import_module/pages/importedhosts/importedhosts.service.ts b/src/app/modules/import_module/pages/importedhosts/importedhosts.service.ts
index 13a3713c0..9f87c8bc7 100644
--- a/src/app/modules/import_module/pages/importedhosts/importedhosts.service.ts
+++ b/src/app/modules/import_module/pages/importedhosts/importedhosts.service.ts
@@ -132,7 +132,7 @@ export class ImportedhostsService {
// Return true on 200 Ok
return {
success: true,
- data: data as GenericResponse // Containers a ImportedHostPost
+ data: data.importedhost as GenericResponse // Containers a ImportedHostPost
};
}),
catchError((error: any) => {
diff --git a/src/app/modules/map_module/pages/mapeditors/mapeditors-view/mapeditors-view.component.html b/src/app/modules/map_module/pages/mapeditors/mapeditors-view/mapeditors-view.component.html
index 4ee4fd80f..f08489619 100644
--- a/src/app/modules/map_module/pages/mapeditors/mapeditors-view/mapeditors-view.component.html
+++ b/src/app/modules/map_module/pages/mapeditors/mapeditors-view/mapeditors-view.component.html
@@ -104,8 +104,7 @@
@if (map) {
-
-
+
}
diff --git a/src/app/modules/map_module/pages/mapeditors/mapeditors-view/mapeditors-view.component.ts b/src/app/modules/map_module/pages/mapeditors/mapeditors-view/mapeditors-view.component.ts
index 090e0814f..ae6dc7238 100644
--- a/src/app/modules/map_module/pages/mapeditors/mapeditors-view/mapeditors-view.component.ts
+++ b/src/app/modules/map_module/pages/mapeditors/mapeditors-view/mapeditors-view.component.ts
@@ -8,7 +8,7 @@ import {
OnInit
} from '@angular/core';
import { interval, Subscription } from 'rxjs';
-import { ActivatedRoute, RouterLink } from '@angular/router';
+import { ActivatedRoute, Router, RouterLink } from '@angular/router';
import { GenericValidationError } from '../../../../../generic-responses';
import { PermissionsService } from '../../../../../permissions/permissions.service';
import { MapeditorsService } from '../mapeditors.service';
@@ -27,7 +27,6 @@ import { PermissionDirective } from '../../../../../permissions/permission.direc
import { BackButtonDirective } from '../../../../../directives/back-button.directive';
import { NgClass, NgIf } from '@angular/common';
import { XsButtonDirective } from '../../../../../layouts/coreui/xsbutton-directive/xsbutton.directive';
-import { parseInt } from 'lodash';
import { MapViewComponent } from '../../../components/map-view/map-view.component';
import { SidebarService } from '../../../../../layouts/coreui/coreui-navbar/sidebar.service';
@@ -61,14 +60,14 @@ export class MapeditorsViewComponent implements OnInit, OnDestroy, AfterViewInit
private MapeditorsService: MapeditorsService = inject(MapeditorsService);
public PermissionsService: PermissionsService = inject(PermissionsService);
readonly sidebarService: SidebarService = inject(SidebarService);
- private route = inject(ActivatedRoute);
+ public readonly route = inject(ActivatedRoute);
+ public readonly router = inject(Router);
public errors: GenericValidationError | null = null;
private cdr = inject(ChangeDetectorRef);
public map!: MapeditorsViewMap;
- private init = true;
protected mapId: number = 0;
- protected rotate: null | string | string[] = null;
+ protected rotate: string[] = [];
protected fullscreen: boolean = false;
private rotationInterval: number = 0;
@@ -79,49 +78,49 @@ export class MapeditorsViewComponent implements OnInit, OnDestroy, AfterViewInit
private refreshInterval: number = 0;
public ngOnInit(): void {
- this.mapId = Number(this.route.snapshot.paramMap.get('id'));
- this.fullscreen = (this.route.snapshot.paramMap.get('fullscreen') === 'true');
- let rotationParam = this.route.snapshot.paramMap.get('rotation');
- if (rotationParam != null) this.rotate = rotationParam;
- let intervalParam = this.route.snapshot.paramMap.get('interval');
- if (intervalParam != null) {
- this.rotationInterval = parseInt(intervalParam, 10) * 1000;
- this.intervalParam = parseInt(intervalParam, 10);
- }
- this.loadMapDetails();
- if (this.rotate !== null && this.rotationInterval > 0) {
- if (typeof this.rotate === "string") {
- this.rotate = this.rotate.split(',');
+ this.subscriptions.add(this.route.params.subscribe(params => {
+ this.mapId = Number(params['id'] ?? 0);
+ this.fullscreen = (params['fullscreen'] ?? 'false') === 'true';
+
+ let rotationParam = params['rotation']
+ if (rotationParam) {
+ this.rotate = String(rotationParam).split(',');
}
- this.intervalSubscription = interval(this.rotationInterval).subscribe(() => {
- this.rotationPosition++;
- if (this.rotate != null && (this.rotationPosition > this.rotate.length)) {
- this.rotationPosition = 1;
- }
+ let intervalParam = Number(params['interval'] ?? 0);
+ if (isNaN(intervalParam)) {
+ intervalParam = 90;
+ }
+ this.rotationInterval = intervalParam * 1000;
+
+
+ // Load the current map / first map of a rotation
+ this.loadMapDetails();
+ this.cdr.markForCheck();
+
+ if (this.rotate.length > 0) {
+ // Handle map rotations
+ this.intervalSubscription = interval(this.rotationInterval).subscribe(() => {
+ this.rotationPosition++;
+ if (this.rotate != null && (this.rotationPosition > this.rotate.length)) {
+ this.rotationPosition = 1;
+ }
- if (this.rotate != null) {
this.mapId = Number(this.rotate[this.rotationPosition - 1]);
- }
- this.loadMapDetails();
- this.cdr.markForCheck();
- });
- }
- this.subscriptions.add(this.route.params.subscribe(params => {
- // reload page to display new map
- if (params['id'] !== undefined && params['id'] !== null && params['id'] !== '' && params['id'] !== this.mapId.toString()) {
- window.location.reload();
+ this.loadMapDetails();
+ this.cdr.markForCheck();
+ });
}
}));
+
+
}
public ngOnDestroy(): void {
this.subscriptions.unsubscribe();
- if (this.intervalSubscription) {
- this.intervalSubscription.unsubscribe();
- }
+ this.intervalSubscription.unsubscribe();
this.toggleFullscreenMode(false, true);
this.cdr.markForCheck();
}
@@ -138,7 +137,6 @@ export class MapeditorsViewComponent implements OnInit, OnDestroy, AfterViewInit
if (this.refreshInterval !== 0 && this.refreshInterval < 5000) {
this.refreshInterval = 5000;
}
- this.init = false;
this.cdr.markForCheck();
}));
};
@@ -166,5 +164,4 @@ export class MapeditorsViewComponent implements OnInit, OnDestroy, AfterViewInit
this.cdr.markForCheck();
}
- protected readonly Array = Array;
}
diff --git a/src/app/modules/mshyperv_module/mshyperv_module.routes.ts b/src/app/modules/mshyperv_module/mshyperv_module.routes.ts
new file mode 100644
index 000000000..6681491d0
--- /dev/null
+++ b/src/app/modules/mshyperv_module/mshyperv_module.routes.ts
@@ -0,0 +1,9 @@
+import { Routes } from '@angular/router';
+import { HyperVComponent } from './pages/wizards/hyper-v/hyper-v.component';
+
+export const mshypervModuleRoutes: Routes = [
+ {
+ path: 'mshyperv_module/wizards/hyperv/:hostId',
+ loadComponent: () => import('./pages/wizards/hyper-v/hyper-v.component').then(m => HyperVComponent),
+ },
+];
diff --git a/src/app/modules/mshyperv_module/pages/wizards/hyper-v/hyper-v-wizard.interface.ts b/src/app/modules/mshyperv_module/pages/wizards/hyper-v/hyper-v-wizard.interface.ts
new file mode 100644
index 000000000..d34eff84a
--- /dev/null
+++ b/src/app/modules/mshyperv_module/pages/wizards/hyper-v/hyper-v-wizard.interface.ts
@@ -0,0 +1,15 @@
+import { WizardGet, WizardPost } from '../../../../../pages/wizards/wizards.interface';
+
+// WIZARD GET
+export interface HyperVWizardGet extends WizardGet {
+ winRmUser: string
+ winRmPw: string
+ winRmAuthMode: string
+}
+
+// WIZARD POST
+export interface HyperVWizardPost extends WizardPost {
+ winRmUser: string
+ winRmPw: string
+ winRmAuthMode: string
+}
diff --git a/src/app/modules/mshyperv_module/pages/wizards/hyper-v/hyper-v-wizard.service.ts b/src/app/modules/mshyperv_module/pages/wizards/hyper-v/hyper-v-wizard.service.ts
new file mode 100644
index 000000000..8b32cc571
--- /dev/null
+++ b/src/app/modules/mshyperv_module/pages/wizards/hyper-v/hyper-v-wizard.service.ts
@@ -0,0 +1,39 @@
+import { Injectable } from '@angular/core';
+import { catchError, map, Observable, of } from 'rxjs';
+import { WizardsService } from '../../../../../pages/wizards/wizards.service';
+import { GenericResponseWrapper, GenericValidationError } from '../../../../../generic-responses';
+import { HyperVWizardGet, HyperVWizardPost } from './hyper-v-wizard.interface';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class HyperVWizardService extends WizardsService {
+
+ public fetch(hostId: number): Observable {
+ return this.http.get(`${this.proxyPath}/hyperv_module/wizards/hyperv/${hostId}.json?angular=true`).pipe(
+ map((data: HyperVWizardGet): HyperVWizardGet => {
+ return data;
+ })
+ );
+ }
+
+ public submit(post: HyperVWizardPost): Observable {
+ return this.http.post(`${this.proxyPath}/hyperv_module/wizards/hyperv.json?angular=true`, post)
+ .pipe(
+ map(data => {
+ return {
+ success: true,
+ data: null
+ };
+ }),
+ catchError((error: any) => {
+ const err = error.error.error as GenericValidationError;
+ return of({
+ success: false,
+ data: err
+ });
+ })
+ );
+ }
+
+}
diff --git a/src/app/modules/mshyperv_module/pages/wizards/hyper-v/hyper-v.component.css b/src/app/modules/mshyperv_module/pages/wizards/hyper-v/hyper-v.component.css
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/app/modules/mshyperv_module/pages/wizards/hyper-v/hyper-v.component.html b/src/app/modules/mshyperv_module/pages/wizards/hyper-v/hyper-v.component.html
new file mode 100644
index 000000000..bde311e41
--- /dev/null
+++ b/src/app/modules/mshyperv_module/pages/wizards/hyper-v/hyper-v.component.html
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
+ {{ t('Home') }}
+
+
+
+
+ {{ t('Wizards') }}
+
+
+
+ {{ t('Hyper-V') }}
+
+
+
+
+
+
+
+ {{ t('Configuration Wizard: Hyper-V') }}
+
+
+
+
+
+
+
+ {{ t('Host Information') }}
+
+
+
+
+
+ {{ t('Configure services per Hyper-V VM') }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('Next') }}
+
+
+
+
+
+
+
+
+
+
+ {{ t('Hyper-V Settings') }}
+
+
+
+
+
+ {{ t('WinRM User') }}
+
+
+
+
+
+
+
+ {{ t('WinRM Password') }}
+
+
+
+
+
+
+
+ {{ t('WinRM AuthMode') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/modules/mshyperv_module/pages/wizards/hyper-v/hyper-v.component.spec.ts b/src/app/modules/mshyperv_module/pages/wizards/hyper-v/hyper-v.component.spec.ts
new file mode 100644
index 000000000..16c19cecc
--- /dev/null
+++ b/src/app/modules/mshyperv_module/pages/wizards/hyper-v/hyper-v.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { HyperVComponent } from './hyper-v.component';
+
+describe('HyperVComponent', () => {
+ let component: HyperVComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [HyperVComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(HyperVComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/modules/mshyperv_module/pages/wizards/hyper-v/hyper-v.component.ts b/src/app/modules/mshyperv_module/pages/wizards/hyper-v/hyper-v.component.ts
new file mode 100644
index 000000000..08395c56e
--- /dev/null
+++ b/src/app/modules/mshyperv_module/pages/wizards/hyper-v/hyper-v.component.ts
@@ -0,0 +1,78 @@
+import { ChangeDetectionStrategy, Component, inject, ViewChild } from '@angular/core';
+import { WizardsAbstractComponent } from '../../../../../pages/wizards/wizards-abstract/wizards-abstract.component';
+import { BackButtonDirective } from '../../../../../directives/back-button.directive';
+import {
+ CardBodyComponent,
+ CardComponent,
+ CardHeaderComponent,
+ CardTitleDirective,
+ FormControlDirective,
+ FormLabelDirective
+} from '@coreui/angular';
+import { FaIconComponent } from '@fortawesome/angular-fontawesome';
+import { FormErrorDirective } from '../../../../../layouts/coreui/form-error.directive';
+import { FormFeedbackComponent } from '../../../../../layouts/coreui/form-feedback/form-feedback.component';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { TranslocoDirective, TranslocoPipe } from '@jsverse/transloco';
+import {
+ WizardsDynamicfieldsComponent
+} from '../../../../../components/wizards/wizards-dynamicfields/wizards-dynamicfields.component';
+import { HyperVWizardService } from './hyper-v-wizard.service';
+import { HyperVWizardGet, HyperVWizardPost } from './hyper-v-wizard.interface';
+import { RouterLink } from '@angular/router';
+import { SelectComponent } from '../../../../../layouts/primeng/select/select/select.component';
+import { SelectKeyValueString } from '../../../../../layouts/primeng/select.interface';
+
+@Component({
+ selector: 'oitc-hyper-v',
+ imports: [
+ BackButtonDirective,
+ CardBodyComponent,
+ CardComponent,
+ CardHeaderComponent,
+ CardTitleDirective,
+ FaIconComponent,
+ FormControlDirective,
+ FormErrorDirective,
+ FormFeedbackComponent,
+ FormLabelDirective,
+ ReactiveFormsModule,
+ TranslocoDirective,
+ TranslocoPipe,
+ WizardsDynamicfieldsComponent,
+ FormsModule,
+ RouterLink,
+ SelectComponent
+ ],
+ templateUrl: './hyper-v.component.html',
+ styleUrl: './hyper-v.component.css',
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class HyperVComponent extends WizardsAbstractComponent {
+
+ @ViewChild(WizardsDynamicfieldsComponent) childComponentLocal!: WizardsDynamicfieldsComponent;
+ protected override WizardService: HyperVWizardService = inject(HyperVWizardService);
+ public checked: boolean = false;
+
+ protected winRmAuthModes: SelectKeyValueString[] = [
+ {value: 'basic', key: 'basic'},
+ {value: 'credssp', key: 'credssp'},
+ ]
+
+ protected override post: HyperVWizardPost = {
+ winRmUser: '',
+ winRmPw: '',
+ winRmAuthMode: '',
+// Default fields from the base wizard
+ host_id: 0,
+ services: [],
+ } as HyperVWizardPost;
+
+ protected override wizardLoad(result: HyperVWizardGet): void {
+ this.post.winRmUser = result.winRmUser;
+ this.post.winRmPw = result.winRmPw;
+ this.post.winRmAuthMode = result.winRmAuthMode;
+ super.wizardLoad(result);
+ }
+
+}
diff --git a/src/app/modules/scm_module/pages/resourcegroups/resourcegroups-add/resourcegroups-add.component.html b/src/app/modules/scm_module/pages/resourcegroups/resourcegroups-add/resourcegroups-add.component.html
index 403d2eb3d..c19eb379a 100644
--- a/src/app/modules/scm_module/pages/resourcegroups/resourcegroups-add/resourcegroups-add.component.html
+++ b/src/app/modules/scm_module/pages/resourcegroups/resourcegroups-add/resourcegroups-add.component.html
@@ -84,6 +84,103 @@ {{ t('Create new resource group') }}
[(ngModel)]="post.description"
oitcFormError [errors]="errors" errorField="description">
+
+
+
+
+ {{ t('Deadline') }}
+
+
+
+
+
+
+ {{ t('Determines the time at which a status had to be transmitted (hh:mm).') }}
+
+ @if (serverTimezone) {
+
+ {{ t('Server timezone is:') }}
+
+ {{ serverTimezone.server_timezone }}.
+
+ {{ t('Current server time:') }}
+
+ {{ serverTimezone.server_time }}
+
+
+ }
+
+
+
+
+ {{ t('Reminder time') }}
+
+
+
+
+
+
+ {{ t('Sends a reminder email to all users if no status had been transmitted before the deadline exceeded (in minutes).') }}
+
+
+
+
+
+
+ @if (this.PermissionsService.hasPermissionObservable(['timeperiods', 'edit'])|async) {
+ @if (post.timeperiod_id && post.timeperiod_id > 0) {
+
+ {{ t('Time period') }}
+
+ } @else {
+
+ {{ t('Time period') }}
+
+ }
+ } @else {
+ {{ t('Time period') }}
+ }
+
+
+
+
+
+
+ @if (this.PermissionsService.hasPermissionObservable(['timeperiods', 'viewdetails'])|async) {
+ @if (post.timeperiod_id) {
+
+
+ }
+ 0">
+ } @else {
+
+ }
+
+
+
+
+
diff --git a/src/app/modules/scm_module/pages/resourcegroups/resourcegroups-add/resourcegroups-add.component.ts b/src/app/modules/scm_module/pages/resourcegroups/resourcegroups-add/resourcegroups-add.component.ts
index b2195ed9b..ed9f45e90 100644
--- a/src/app/modules/scm_module/pages/resourcegroups/resourcegroups-add/resourcegroups-add.component.ts
+++ b/src/app/modules/scm_module/pages/resourcegroups/resourcegroups-add/resourcegroups-add.component.ts
@@ -27,7 +27,7 @@ import { AsyncPipe } from '@angular/common';
import { PermissionDirective } from '../../../../../permissions/permission.directive';
import { RequiredIconComponent } from '../../../../../components/required-icon/required-icon.component';
import { SelectComponent } from '../../../../../layouts/primeng/select/select/select.component';
-import { TranslocoDirective, TranslocoService } from '@jsverse/transloco';
+import { TranslocoDirective, TranslocoPipe, TranslocoService } from '@jsverse/transloco';
import { XsButtonDirective } from '../../../../../layouts/coreui/xsbutton-directive/xsbutton.directive';
import { Router, RouterLink } from '@angular/router';
import { Subscription } from 'rxjs';
@@ -45,6 +45,13 @@ import { UsersService } from '../../../../../pages/users/users.service';
import { MultiSelectComponent } from '../../../../../layouts/primeng/multi-select/multi-select/multi-select.component';
import { ResourcegroupsSubmitType } from '../resourcegroups.enum';
import { MailinglistsService } from '../../mailinglists/mailinglists.service';
+import { TimeperiodsService } from '../../../../../pages/timeperiods/timeperiods.service';
+import { TimezoneConfiguration, TimezoneService } from '../../../../../services/timezone.service';
+import { ScmSettingsIndex } from '../../scmsettings/scmsettings.interface';
+import { ScmsettingsService } from '../../scmsettings/scmsettings.service';
+import {
+ TimeperiodDetailsTooltipComponent
+} from '../../../../sla_module/components/timeperiod-details-tooltip/timeperiod-details-tooltip.component';
@Component({
selector: 'oitc-resourcegroups-add',
@@ -79,7 +86,9 @@ import { MailinglistsService } from '../../mailinglists/mailinglists.service';
DropdownMenuDirective,
DropdownToggleDirective,
InputGroupComponent,
- InputGroupTextDirective
+ InputGroupTextDirective,
+ TimeperiodDetailsTooltipComponent,
+ TranslocoPipe
],
templateUrl: './resourcegroups-add.component.html',
styleUrl: './resourcegroups-add.component.css',
@@ -91,6 +100,9 @@ export class ResourcegroupsAddComponent implements OnInit, OnDestroy {
private readonly ContainersService = inject(ContainersService);
private readonly UsersService = inject(UsersService);
private readonly MailinglistsService = inject(MailinglistsService);
+ private readonly TimeperiodsService: TimeperiodsService = inject(TimeperiodsService);
+ private readonly ScmsettingsService: ScmsettingsService = inject(ScmsettingsService);
+ private readonly TimezoneService = inject(TimezoneService);
private readonly notyService = inject(NotyService);
private readonly HistoryService: HistoryService = inject(HistoryService);
public post = this.getClearForm();
@@ -98,6 +110,9 @@ export class ResourcegroupsAddComponent implements OnInit, OnDestroy {
public PermissionsService: PermissionsService = inject(PermissionsService);
private ResourcegroupsService = inject(ResourcegroupsService);
public containers: SelectKeyValue[] = [];
+ public timeperiods: SelectKeyValue[] = [];
+ public serverTimezone: TimezoneConfiguration | null = null;
+
public users: SelectKeyValue[] = [];
public managers: SelectKeyValue[] = [];
public region_managers: SelectKeyValue[] = [];
@@ -117,6 +132,21 @@ export class ResourcegroupsAddComponent implements OnInit, OnDestroy {
public ngOnInit(): void {
this.loadContainers();
+ this.load();
+ }
+
+ private load(): void {
+ this.subscriptions.add(this.ScmsettingsService.loadScmSettings()
+ .subscribe((result: ScmSettingsIndex) => {
+ this.post.reminder_time = result.scm_settings.reminder_time;
+ this.post.deadline = result.scm_settings.deadline;
+ this.cdr.markForCheck();
+ }));
+
+ this.subscriptions.add(this.TimezoneService.getTimezoneConfiguration().subscribe(data => {
+ this.serverTimezone = data;
+ this.cdr.markForCheck();
+ }));
}
public loadContainers = (): void => {
@@ -151,10 +181,21 @@ export class ResourcegroupsAddComponent implements OnInit, OnDestroy {
}));
}
+ private loadTimeperiods() {
+ if (this.post.container.parent_id === null) {
+ return;
+ }
+ this.subscriptions.add(this.TimeperiodsService.loadTimeperiodsByContainerId(this.post.container.parent_id).subscribe((result) => {
+ this.timeperiods = result;
+ this.cdr.markForCheck();
+ }));
+ }
+
public onContainerChange() {
this.loadUsers();
this.loadMailinglists();
+ this.loadTimeperiods();
this.cdr.markForCheck();
}
@@ -165,6 +206,9 @@ export class ResourcegroupsAddComponent implements OnInit, OnDestroy {
public getClearForm(): ResourcegroupsPost {
return {
description: '',
+ timeperiod_id: null,
+ reminder_time: 15,
+ deadline: '',
container: {
parent_id: null,
name: ''
diff --git a/src/app/modules/scm_module/pages/resourcegroups/resourcegroups-edit/resourcegroups-edit.component.html b/src/app/modules/scm_module/pages/resourcegroups/resourcegroups-edit/resourcegroups-edit.component.html
index 256f2e64c..35de61e09 100644
--- a/src/app/modules/scm_module/pages/resourcegroups/resourcegroups-edit/resourcegroups-edit.component.html
+++ b/src/app/modules/scm_module/pages/resourcegroups/resourcegroups-edit/resourcegroups-edit.component.html
@@ -87,6 +87,101 @@ {{ t('Edit resource group:') }}
[(ngModel)]="post.description"
oitcFormError [errors]="errors" errorField="description">
+
+
+
+ {{ t('Deadline') }}
+
+
+
+
+
+
+ {{ t('Determines the time at which a status had to be transmitted (hh:mm).') }}
+
+ @if (serverTimezone) {
+
+ {{ t('Server timezone is:') }}
+
+ {{ serverTimezone.server_timezone }}.
+
+ {{ t('Current server time:') }}
+
+ {{ serverTimezone.server_time }}
+
+
+ }
+
+
+
+
+ {{ t('Reminder time') }}
+
+
+
+
+
+
+ {{ t('Sends a reminder email to all users if no status had been transmitted before the deadline exceeded (in minutes).') }}
+
+
+
+
+
+
+ @if (this.PermissionsService.hasPermissionObservable(['timeperiods', 'edit'])|async) {
+ @if (post.timeperiod_id && post.timeperiod_id > 0) {
+
+ {{ t('Time period') }}
+
+ } @else {
+
+ {{ t('Time period') }}
+
+ }
+ } @else {
+ {{ t('Time period') }}
+ }
+
+
+
+
+
+
+ @if (this.PermissionsService.hasPermissionObservable(['timeperiods', 'viewdetails'])|async) {
+ @if (post.timeperiod_id) {
+
+
+ }
+ 0">
+ } @else {
+
+ }
+
+
+
+
diff --git a/src/app/modules/scm_module/pages/resourcegroups/resourcegroups-edit/resourcegroups-edit.component.ts b/src/app/modules/scm_module/pages/resourcegroups/resourcegroups-edit/resourcegroups-edit.component.ts
index 84db09d28..64f2f0ca1 100644
--- a/src/app/modules/scm_module/pages/resourcegroups/resourcegroups-edit/resourcegroups-edit.component.ts
+++ b/src/app/modules/scm_module/pages/resourcegroups/resourcegroups-edit/resourcegroups-edit.component.ts
@@ -27,7 +27,7 @@ import { AsyncPipe, NgIf } from '@angular/common';
import { PermissionDirective } from '../../../../../permissions/permission.directive';
import { RequiredIconComponent } from '../../../../../components/required-icon/required-icon.component';
import { SelectComponent } from '../../../../../layouts/primeng/select/select/select.component';
-import { TranslocoDirective, TranslocoService } from '@jsverse/transloco';
+import { TranslocoDirective, TranslocoPipe, TranslocoService } from '@jsverse/transloco';
import { XsButtonDirective } from '../../../../../layouts/coreui/xsbutton-directive/xsbutton.directive';
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
import { Subscription } from 'rxjs';
@@ -46,6 +46,11 @@ import { MultiSelectComponent } from '../../../../../layouts/primeng/multi-selec
import { FormLoaderComponent } from '../../../../../layouts/primeng/loading/form-loader/form-loader.component';
import { ResourcegroupsSubmitType } from '../resourcegroups.enum';
import { MailinglistsService } from '../../mailinglists/mailinglists.service';
+import { TimeperiodsService } from '../../../../../pages/timeperiods/timeperiods.service';
+import { TimezoneConfiguration, TimezoneService } from '../../../../../services/timezone.service';
+import {
+ TimeperiodDetailsTooltipComponent
+} from '../../../../sla_module/components/timeperiod-details-tooltip/timeperiod-details-tooltip.component';
@Component({
selector: 'oitc-resourcegroups-edit',
@@ -82,7 +87,9 @@ import { MailinglistsService } from '../../mailinglists/mailinglists.service';
DropdownMenuDirective,
DropdownToggleDirective,
InputGroupComponent,
- InputGroupTextDirective
+ InputGroupTextDirective,
+ TimeperiodDetailsTooltipComponent,
+ TranslocoPipe
],
templateUrl: './resourcegroups-edit.component.html',
styleUrl: './resourcegroups-edit.component.css',
@@ -95,6 +102,8 @@ export class ResourcegroupsEditComponent implements OnInit, OnDestroy {
private readonly ContainersService = inject(ContainersService);
private readonly UsersService = inject(UsersService);
private readonly MailinglistsService = inject(MailinglistsService);
+ private readonly TimeperiodsService: TimeperiodsService = inject(TimeperiodsService);
+ private readonly TimezoneService = inject(TimezoneService);
private readonly notyService = inject(NotyService);
private readonly HistoryService: HistoryService = inject(HistoryService);
public post!: ResourcegroupsPost;
@@ -102,6 +111,8 @@ export class ResourcegroupsEditComponent implements OnInit, OnDestroy {
public PermissionsService: PermissionsService = inject(PermissionsService);
private ResourcegroupsService = inject(ResourcegroupsService);
public containers: SelectKeyValue[] = [];
+ public timeperiods: SelectKeyValue[] = [];
+ public serverTimezone: TimezoneConfiguration | null = null;
public users: SelectKeyValue[] = [];
public managers: SelectKeyValue[] = [];
@@ -122,6 +133,14 @@ export class ResourcegroupsEditComponent implements OnInit, OnDestroy {
ngOnInit(): void {
this.id = Number(this.route.snapshot.paramMap.get('id'));
this.loadResourcegroup();
+ this.load();
+ }
+
+ private load(): void {
+ this.subscriptions.add(this.TimezoneService.getTimezoneConfiguration().subscribe(data => {
+ this.serverTimezone = data;
+ this.cdr.markForCheck();
+ }));
}
public loadResourcegroup() {
@@ -133,6 +152,7 @@ export class ResourcegroupsEditComponent implements OnInit, OnDestroy {
this.loadContainers();
this.loadUsers();
this.loadMailinglists();
+ this.loadTimeperiods();
}));
}
@@ -168,10 +188,21 @@ export class ResourcegroupsEditComponent implements OnInit, OnDestroy {
}));
}
+ private loadTimeperiods() {
+ if (this.post.container.parent_id === null) {
+ return;
+ }
+ this.subscriptions.add(this.TimeperiodsService.loadTimeperiodsByContainerId(this.post.container.parent_id).subscribe((result) => {
+ this.timeperiods = result;
+ this.cdr.markForCheck();
+ }));
+ }
+
public onContainerChange() {
this.loadUsers();
this.loadMailinglists();
+ this.loadTimeperiods();
this.cdr.markForCheck();
}
@@ -182,6 +213,9 @@ export class ResourcegroupsEditComponent implements OnInit, OnDestroy {
public getClearForm(): ResourcegroupsPost {
return {
description: '',
+ timeperiod_id: null,
+ reminder_time: 15,
+ deadline: '',
container: {
parent_id: null,
name: ''
diff --git a/src/app/modules/scm_module/pages/resourcegroups/resourcegroups-index/resourcegroups-index.component.css b/src/app/modules/scm_module/pages/resourcegroups/resourcegroups-index/resourcegroups-index.component.css
index e69de29bb..7f0bf1564 100644
--- a/src/app/modules/scm_module/pages/resourcegroups/resourcegroups-index/resourcegroups-index.component.css
+++ b/src/app/modules/scm_module/pages/resourcegroups/resourcegroups-index/resourcegroups-index.component.css
@@ -0,0 +1,3 @@
+.weekdays-ul-padding-inline-start{
+ padding-inline-start: 1rem;
+}
diff --git a/src/app/modules/scm_module/pages/resourcegroups/resourcegroups-index/resourcegroups-index.component.html b/src/app/modules/scm_module/pages/resourcegroups/resourcegroups-index/resourcegroups-index.component.html
index aecf45a7a..e5b03c32e 100644
--- a/src/app/modules/scm_module/pages/resourcegroups/resourcegroups-index/resourcegroups-index.component.html
+++ b/src/app/modules/scm_module/pages/resourcegroups/resourcegroups-index/resourcegroups-index.component.html
@@ -139,11 +139,7 @@
{{ t('Description') }}
-
- {{ t('Created') }}
-
- {{ t('Last update') }}
-
+
@@ -151,9 +147,17 @@
{{ t('Resource summary') }}
+
+
+
{{ t('Users') }}
+
+ {{ t('Created') }}
+
+ {{ t('Last update') }}
+
@@ -184,28 +188,6 @@
{{ resourcegroup.description }}
-
- {{ resourcegroup.created }}
-
-
- @if (resourcegroup.last_update) {
- {{ resourcegroup.last_update }}
- }
- @if (!resourcegroup.last_update) {
-
- {{ t('n/a') }}
-
- }
- @if (resourcegroup.last_update_failed) {
-
-
-
- }
-
-
-
@if (resourcegroup.resource_count > 0 && ( PermissionsService.hasPermissionObservable(['scmmodule', 'resources', 'index'])|async )) {
}
+
+
+
+
+ {{ t('Reminder time') }}
+ {{ resourcegroup.reminder_time }}
+ {{ t('minute(s)') }}
+
+
+
+ {{ t('Deadline') }}
+ {{ resourcegroup.deadline }}
+
+ @if (resourcegroup.timeperiod_id && ( this.PermissionsService.hasPermissionObservable(['timeperiods', 'viewdetails'])|async )) {
+
+
+ {{ resourcegroup.timeperiod?.name }}
+
+
+ @for (
+ timerange of resourcegroup.timeperiod?.timeperiod_timeranges; track $index) {
+
+
+
+
+ {{ weekdays[timerange.day] }}:
+
+
+ {{ timerange.start }} -
+ {{ timerange.end }}
+
+
+
+ }
+ @if (resourcegroup.timeperiod?.timeperiod_timeranges?.length === 0) {
+
+
+
+ }
+ @if (resourcegroup.timeperiod?.calendar) {
+
+
+
+
+
+ {{ resourcegroup.timeperiod?.calendar?.name }}
+
+
+
+
+ }
+
+ }
@if (resourcegroup.region_managers.length > 0 || resourcegroup.mailinglist_region_managers.length > 0) {
@@ -359,6 +403,26 @@
}
+
+ {{ resourcegroup.created }}
+
+
+ @if (resourcegroup.last_update) {
+ {{ resourcegroup.last_update }}
+ }
+ @if (!resourcegroup.last_update) {
+
+ {{ t('n/a') }}
+
+ }
+ @if (resourcegroup.last_update_failed) {
+
+
+
+ }
+
{
// Here, params is an object containing the current query parameters.
// You can do something with these parameters here.
let resourcegroupId = params['id'] || params['id'];
+
if (resourcegroupId) {
this.params['filter[Resourcegroups.id][]'] = [].concat(resourcegroupId); // make sure we always get an array
}
diff --git a/src/app/modules/scm_module/pages/resourcegroups/resourcegroups.interface.ts b/src/app/modules/scm_module/pages/resourcegroups/resourcegroups.interface.ts
index 774d63b08..e9e14b74f 100644
--- a/src/app/modules/scm_module/pages/resourcegroups/resourcegroups.interface.ts
+++ b/src/app/modules/scm_module/pages/resourcegroups/resourcegroups.interface.ts
@@ -8,6 +8,7 @@ import {
import { Resource } from '../resources/resources.interface';
import { GenericIdAndName } from '../../../../generic.interfaces';
import { getUserDate } from '../../../../services/timezone.service';
+import { Timeperiod } from '../../../../pages/timeperiods/timeperiods.interface';
export interface ResourcegroupsIndex extends PaginateOrScroll {
all_resourcegroups: Resourcegroup[]
@@ -18,6 +19,10 @@ export interface Resourcegroup {
id: number
container_id: number
description: string
+ deadline: string
+ timeperiod_id: number | null
+ timeperiod?: Timeperiod
+ reminder_time: number
last_state: number
last_update: string
last_update_failed: boolean
@@ -73,6 +78,9 @@ export interface ResourcegroupsGet {
export interface ResourcegroupsPost {
id?: number
description: string
+ timeperiod_id: number | null
+ reminder_time: number
+ deadline: string
container: {
parent_id: number | null
name: string
diff --git a/src/app/modules/scm_module/pages/scmsettings/scm-settings-index/scm-settings-index.component.html b/src/app/modules/scm_module/pages/scmsettings/scm-settings-index/scm-settings-index.component.html
index 8276a8fdc..2ddbc5dd1 100644
--- a/src/app/modules/scm_module/pages/scmsettings/scm-settings-index/scm-settings-index.component.html
+++ b/src/app/modules/scm_module/pages/scmsettings/scm-settings-index/scm-settings-index.component.html
@@ -57,7 +57,7 @@
- {{ t('Deadline') }}
+ {{ t('Default deadline') }}
- {{ t('Reminder time') }}
+ {{ t('Default reminder time') }}
- {{ timeperiod.name }}
+ {{ timeperiod?.name }}
@@ -26,32 +26,37 @@
-
-
- @if (day.value.length > 0) {
-
- {{ label.messageExists }}
-
- } @else {
-
- {{ label.messageNotExists }}
-
- }
-
-
-
-
- @if (day.value.length > 0) {
-
-
- {{ timerange.start + ' - ' + timerange.end }}
-
-
- } @else {
-
- }
-
-
+ @if (weekDays) {
+
+ @for (day of weekDays | keyvalue; track $index) {
+
+ @if (day.value.length > 0) {
+
+ {{ label.messageExists }}
+
+ } @else {
+
+ {{ label.messageNotExists }}
+
+ }
+
+ @if (day.value.length > 0) {
+
+ @for (timerange of day.value; track $index) {
+
+ {{ timerange.start + ' - ' + timerange.end }}
+
+ }
+
+ } @else {
+
+ }
+
+
+ }
+
+ }
diff --git a/src/app/modules/sla_module/components/timeperiod-details-tooltip/timeperiod-details-tooltip.component.ts b/src/app/modules/sla_module/components/timeperiod-details-tooltip/timeperiod-details-tooltip.component.ts
index 19db62e21..f3fdb0169 100644
--- a/src/app/modules/sla_module/components/timeperiod-details-tooltip/timeperiod-details-tooltip.component.ts
+++ b/src/app/modules/sla_module/components/timeperiod-details-tooltip/timeperiod-details-tooltip.component.ts
@@ -6,7 +6,6 @@ import {
Input,
OnChanges,
OnDestroy,
- OnInit,
SimpleChanges
} from '@angular/core';
import { Subscription } from 'rxjs';
@@ -16,25 +15,24 @@ import { Timeperiod, TimeperiodRoot, WeekDays } from './timeperiod-details-toolt
import { TranslocoDirective, TranslocoService } from '@jsverse/transloco';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { BadgeComponent, PopoverDirective, TableDirective } from '@coreui/angular';
-import { KeyValuePipe, NgForOf, NgIf } from '@angular/common';
+import { KeyValuePipe } from '@angular/common';
+import _ from 'lodash';
@Component({
selector: 'oitc-timeperiod-details-tooltip',
imports: [
- FaIconComponent,
- TranslocoDirective,
- PopoverDirective,
- TableDirective,
- BadgeComponent,
- NgIf,
- NgForOf,
- KeyValuePipe
-],
+ FaIconComponent,
+ TranslocoDirective,
+ PopoverDirective,
+ TableDirective,
+ BadgeComponent,
+ KeyValuePipe
+ ],
templateUrl: './timeperiod-details-tooltip.component.html',
styleUrl: './timeperiod-details-tooltip.component.css',
changeDetection: ChangeDetectionStrategy.OnPush
})
-export class TimeperiodDetailsTooltipComponent implements OnInit, OnDestroy, OnChanges {
+export class TimeperiodDetailsTooltipComponent implements OnDestroy, OnChanges {
@Input() timeperiodId: number = 0;
@@ -43,7 +41,7 @@ export class TimeperiodDetailsTooltipComponent implements OnInit, OnDestroy, OnC
private readonly TranslocoService = inject(TranslocoService);
private readonly TimeperiodDetailsTooltipService = inject(TimeperiodDetailsTooltipService);
public isLoading: boolean = true;
- public timeperiod: Timeperiod = {} as Timeperiod;
+ public timeperiod?: Timeperiod;
public weekDays: WeekDays = {} as WeekDays;
public label = {
messageExists: this.TranslocoService.translate('Yes'),
@@ -56,16 +54,6 @@ export class TimeperiodDetailsTooltipComponent implements OnInit, OnDestroy, OnC
this.loadTimeperiodDetails();
}
-
- public ngOnInit() {
- this.subscriptions.add(this.route.queryParams.subscribe(params => {
- // Here, params is an object containing the current query parameters.
- // You can do something with these parameters here.
- //console.log(params);
- this.loadTimeperiodDetails();
- }));
- }
-
public ngOnDestroy() {
this.subscriptions.unsubscribe();
}
@@ -97,6 +85,11 @@ export class TimeperiodDetailsTooltipComponent implements OnInit, OnDestroy, OnC
});
}
+ for (let day in this.weekDays) {
+ this.weekDays[day] = _.sortBy(this.weekDays[day], [function (o) {
+ return o.start;
+ }]);
+ }
}));
}
this.cdr.markForCheck();
diff --git a/src/app/pages/agentconnector/agentconfig.interface.ts b/src/app/pages/agentconnector/agentconfig.interface.ts
index 14c405622..2e1c96fb4 100644
--- a/src/app/pages/agentconnector/agentconfig.interface.ts
+++ b/src/app/pages/agentconnector/agentconfig.interface.ts
@@ -21,6 +21,7 @@ export interface AgentConfigStringFields {
autossl_crt_file: string
autossl_key_file: string
autossl_ca_file: string
+ tls_security_level: 'lax' | 'intermediate' | 'modern'
config_version: string
}
@@ -33,6 +34,7 @@ export interface AgentConfigBoolFields {
push_enable_webserver: boolean
push_webserver_use_https: boolean
use_autossl: boolean
+ verify_autossl_expiry: boolean
use_https: boolean
use_https_verify: boolean
cpustats: boolean
diff --git a/src/app/pages/agentconnector/agentconnector-config/agentconnector-config.component.html b/src/app/pages/agentconnector/agentconnector-config/agentconnector-config.component.html
index 5840e876e..484973b80 100644
--- a/src/app/pages/agentconnector/agentconnector-config/agentconnector-config.component.html
+++ b/src/app/pages/agentconnector/agentconnector-config/agentconnector-config.component.html
@@ -393,6 +393,25 @@
*ngIf="connectionType === AgentconnectorConnectionTypes.AutoTls">
{{ t('openITCOCKPIT automatically generate a TLS certificate for authentication and encryption purpose. Auto-TLS is only available in Pull mode.') }}
+
+ @if (connectionType === AgentconnectorConnectionTypes.AutoTls) {
+
+
+
+
+ {{ t('Verify expiry of Auto-TLS certificate') }}
+
+
+ {{ t('The generated client certificate is valid for 10 years. If enabled, openITCOCKPIT will not be able to query the Agent after the certificate has been expired.') }}
+
+
+
+ }
+
{{ t('Start the Agents web server with own TLS certificates from Let\'s Encrypt for example.') }}
@@ -448,6 +467,29 @@
+
+ @if (connectionType === AgentconnectorConnectionTypes.AutoTls || connectionType === AgentconnectorConnectionTypes.Https || config.bool.enable_push_mode) {
+
+
+ {{ t('TLS security level') }}
+
+
+ {{ t('Lax - Compatible with very old systems (TLS 1.2 no restrictions on cipher suites)') }}
+
+ {{ t('Intermediate - General-purpose (TLS 1.2, restrict usage of cipher suites to a secure subset)') }}
+
+ {{ t('Modern - Most secure (TLS 1.3 only)') }}
+
+
+
+ }
diff --git a/src/app/pages/browsers/browsers-index/browsers-index.component.html b/src/app/pages/browsers/browsers-index/browsers-index.component.html
index f9fd13865..6dda48d47 100644
--- a/src/app/pages/browsers/browsers-index/browsers-index.component.html
+++ b/src/app/pages/browsers/browsers-index/browsers-index.component.html
@@ -159,14 +159,12 @@
@if (statusCounts && statusCounts.hoststatusCount.length === 3) {
-
-
+
+
+
+
@if (PermissionsService.hasPermissionObservable(['hosts', 'index'])|async) {
@@ -235,15 +233,11 @@
}
@if (statusCounts && statusCounts.servicestatusCount.length === 4) {
-
-
-
-
+
+
+
+
@if (PermissionsService.hasPermissionObservable(['services', 'index'])|async) {
diff --git a/src/app/pages/browsers/browsers-index/browsers-index.component.ts b/src/app/pages/browsers/browsers-index/browsers-index.component.ts
index e79ea3175..0ccd6c726 100644
--- a/src/app/pages/browsers/browsers-index/browsers-index.component.ts
+++ b/src/app/pages/browsers/browsers-index/browsers-index.component.ts
@@ -200,7 +200,8 @@ import { TitleService } from '../../../services/title.service';
ServicePieChartComponent,
ColumnsConfigExportModalComponent,
ColumnsConfigImportModalComponent,
- AsyncPipe
+ AsyncPipe,
+ ServicePieChartComponent
],
templateUrl: './browsers-index.component.html',
styleUrl: './browsers-index.component.css',
diff --git a/src/app/pages/dashboards/dashboards-index/dashboards-index.component.html b/src/app/pages/dashboards/dashboards-index/dashboards-index.component.html
index 45422bdb6..d4dc75519 100644
--- a/src/app/pages/dashboards/dashboards-index/dashboards-index.component.html
+++ b/src/app/pages/dashboards/dashboards-index/dashboards-index.component.html
@@ -67,6 +67,7 @@
{{ t('Dashboard') }}
@@ -170,8 +171,8 @@
+ [widgetInput]="widget"
+ [isReadonly]="isReadonly">
diff --git a/src/app/pages/dashboards/widgets/hosts-piechart-widget/hosts-piechart-widget.component.css b/src/app/pages/dashboards/widgets/hosts-piechart-widget/hosts-piechart-widget.component.css
index e69de29bb..66254c08a 100644
--- a/src/app/pages/dashboards/widgets/hosts-piechart-widget/hosts-piechart-widget.component.css
+++ b/src/app/pages/dashboards/widgets/hosts-piechart-widget/hosts-piechart-widget.component.css
@@ -0,0 +1,3 @@
+.h-85 {
+ height: 85% !important;
+}
diff --git a/src/app/pages/dashboards/widgets/hosts-piechart-widget/hosts-piechart-widget.component.html b/src/app/pages/dashboards/widgets/hosts-piechart-widget/hosts-piechart-widget.component.html
index 19504d8ec..071d52e1f 100644
--- a/src/app/pages/dashboards/widgets/hosts-piechart-widget/hosts-piechart-widget.component.html
+++ b/src/app/pages/dashboards/widgets/hosts-piechart-widget/hosts-piechart-widget.component.html
@@ -1,63 +1,62 @@
-
+
+ @if (!statusCounts) {
+
+ }
+ @if (statusCounts && statusCounts.hoststatusCount.length === 3) {
+
-
diff --git a/src/app/pages/dashboards/widgets/hosts-piechart-widget/hosts-piechart-widget.component.ts b/src/app/pages/dashboards/widgets/hosts-piechart-widget/hosts-piechart-widget.component.ts
index 07b5afa35..9dae0f472 100644
--- a/src/app/pages/dashboards/widgets/hosts-piechart-widget/hosts-piechart-widget.component.ts
+++ b/src/app/pages/dashboards/widgets/hosts-piechart-widget/hosts-piechart-widget.component.ts
@@ -1,80 +1,82 @@
-import { ChangeDetectionStrategy, Component, inject, ViewChild } from '@angular/core';
-import { BaseWidgetComponent } from '../base-widget/base-widget.component';
-import { KtdGridLayout, KtdResizeEnd } from '@katoid/angular-grid-layout';
-import { AsyncPipe, NgIf } from '@angular/common';
-import { FaIconComponent } from '@fortawesome/angular-fontawesome';
-import { TranslocoDirective } from '@jsverse/transloco';
+import { ChangeDetectionStrategy, Component, effect, inject, input, OnDestroy } from '@angular/core';
+import { NgxEchartsDirective, provideEchartsCore } from 'ngx-echarts';
+import * as echarts from 'echarts/core';
+import 'echarts/theme/dark.js';
+import { EChartsOption } from 'echarts';
+
+import { LegendComponent, PolarComponent, TitleComponent, TooltipComponent } from 'echarts/components';
+import { PieChart } from 'echarts/charts';
+import { BlockLoaderComponent } from '../../../../layouts/primeng/loading/block-loader/block-loader.component';
+import { AsyncPipe } from '@angular/common';
import { RouterLink } from '@angular/router';
import { StatuscountResponse } from '../../../browsers/browsers.interface';
-import { ApexGrid, ChartComponent } from "ng-apexcharts";
-import { ChartOptions } from '../../../../components/charts/host-pie-chart/host-pie-chart.component';
+import { FaIconComponent } from '@fortawesome/angular-fontawesome';
+import { BaseWidgetComponent } from '../base-widget/base-widget.component';
+import { PieChartMetric } from '../../../../components/charts/charts.interface';
import { LayoutService } from '../../../../layouts/coreui/layout.service';
-import { BlockLoaderComponent } from '../../../../layouts/primeng/loading/block-loader/block-loader.component';
+import { TranslocoDirective } from '@jsverse/transloco';
+import { KtdGridLayout } from '@katoid/angular-grid-layout';
+
+echarts.use([PieChart, LegendComponent, TitleComponent, TooltipComponent, PolarComponent]);
@Component({
selector: 'oitc-hosts-piechart-widget',
imports: [
+ NgxEchartsDirective,
+ BlockLoaderComponent,
AsyncPipe,
- FaIconComponent,
- NgIf,
- TranslocoDirective,
RouterLink,
- ChartComponent,
- BlockLoaderComponent
+ FaIconComponent,
+ TranslocoDirective
+ ],
+ providers: [
+ provideEchartsCore({echarts}),
],
templateUrl: './hosts-piechart-widget.component.html',
styleUrl: './hosts-piechart-widget.component.css',
changeDetection: ChangeDetectionStrategy.OnPush
})
-export class HostsPiechartWidgetComponent extends BaseWidgetComponent {
+export class HostsPiechartWidgetComponent extends BaseWidgetComponent implements OnDestroy {
+ public widgetHeight: number = 0;
- public statusCounts?: StatuscountResponse;
+ public title = input
('Host availability');
+ public showLegend = input(true);
+ public chartData = input([]);
+ public scaleSize = input(20);
+ public scale = input(true);
+
+
+ public theme: string = '';
+ public chartOption: EChartsOption = {};
- @ViewChild("chart") chart?: ChartComponent;
- public chartOptions!: Partial;
+ public echartsInstance: any;
private readonly LayoutService = inject(LayoutService);
- public apexGridOptions: ApexGrid = {
- padding: {
- top: 10,
- right: 0,
- bottom: 0,
- left: 0
- },
- };
+ public statusCounts?: StatuscountResponse;
public constructor() {
super();
-
- // Subscribe to the color mode changes (drop down menu in header)
this.subscriptions.add(this.LayoutService.theme$.subscribe((theme) => {
- //console.log('Change in theme detected', theme);
- if (this.chart && this.chartOptions) {
-
- // Read the background color value from the CSS variable and update the chart
- let cuiSecondaryBg = getComputedStyle(document.documentElement).getPropertyValue('--cui-secondary-bg').trim();
-
- //this.chartOptions.plotOptions?.radialBar?.track?.background = cuiSecondaryBg;
- this.chart.updateOptions({
- plotOptions: {
- radialBar: {
- track: {
- background: cuiSecondaryBg
- }
- }
- }
- });
+ this.theme = '';
+ if (theme === 'dark') {
+ this.theme = 'dark';
}
+
this.cdr.markForCheck();
}));
+
+ effect(() => {
+ this.renderChart();
+ this.cdr.markForCheck();
+ });
}
public override load() {
this.subscriptions.add(this.WidgetsService.loadStatusCount()
.subscribe((result) => {
this.statusCounts = result;
- this.updateChart();
+ this.renderChart();
this.cdr.markForCheck();
})
);
@@ -84,89 +86,129 @@ export class HostsPiechartWidgetComponent extends BaseWidgetComponent {
super.ngOnDestroy();
}
- private updateChart() {
+ onChartInit(ec: any) {
+ this.echartsInstance = ec;
+ this.cdr.markForCheck();
+ }
+
+ private renderChart() {
if (!this.statusCounts) {
return;
}
-
- let cuiSecondaryBg = getComputedStyle(document.documentElement).getPropertyValue('--cui-secondary-bg').trim();
- this.chartOptions = {
- series: [
- this.statusCounts.hoststatusCountPercentage[0],
- this.statusCounts.hoststatusCountPercentage[1],
- this.statusCounts.hoststatusCountPercentage[2],
- ],
- chart: {
- height: 180,
- offsetY: -20,
- type: "radialBar",
-
- // The sparkline option remove all paddings
- // https://github.com/apexcharts/apexcharts.js/issues/1272#issuecomment-591388290
- sparkline: {
- enabled: true
- }
+ let labels = [
+ this.TranslocoService.translate('Unreachable'),
+ this.TranslocoService.translate('Down'),
+ this.TranslocoService.translate('Up')
+ ];
+
+ this.chartOption = {
+ polar: {
+ radius: [30, '80%'],
},
- plotOptions: {
- radialBar: {
- offsetY: 0,
- startAngle: -180,
- endAngle: 180,
- hollow: {
- margin: 5,
- size: "30%",
- background: "transparent",
- image: undefined,
- },
- track: {
- show: true,
- background: cuiSecondaryBg,
- opacity: 0.5,
- dropShadow: {
- enabled: false,
- top: 2,
- left: 0,
- color: '#999999',
- opacity: 0.2,
- blur: 2
- }
- },
- dataLabels: {
- name: {
- show: false
- },
- value: {
- show: true
- }
- }
- },
+ backgroundColor: 'transparent',
+ angleAxis: {
+ show: false,
+ max: 100,
+ startAngle: 270
},
- colors: ["#00bc4c", "#bf0000", "#6b737c"],
- labels: [
- this.TranslocoService.translate('Up'),
- this.TranslocoService.translate('Down'),
- this.TranslocoService.translate('Unreachable')
- ],
- legend: {
- show: false
+ radiusAxis: {
+ type: 'category',
+ show: false,
+ data: labels
},
- responsive: [
+ tooltip: {
+ show: true,
+ formatter: '{b} ({c}%)'
+ },
+ series: [
{
- breakpoint: 480,
- options: {
- legend: {
- show: false
+ type: 'bar',
+ barWidth: '50%',
+ data: [
+ {
+ value: this.statusCounts.hoststatusCountPercentage[2],
+ itemStyle: {
+ color: {
+ type: 'linear',
+ x: 0.5,
+ y: 0.5,
+ x2: 1.5,
+ y2: 1.5,
+ colorStops: [
+ {
+ offset: 0.1,
+ color: '#6b737c' // color at 0%
+ },
+ {
+ offset: 1,
+ color: '#4f555a' // color at 100%
+ }
+ ],
+ global: false // default is false
+ }
+ }
+ },
+ {
+ value: this.statusCounts.hoststatusCountPercentage[1],
+ itemStyle: {
+ color: {
+ type: 'linear',
+ x: 0.5,
+ y: 0.5,
+ x2: 1.5,
+ y2: 1.5,
+ colorStops: [
+ {
+ offset: 0.1,
+ color: '#CC0000' // color at 0%
+ },
+ {
+ offset: 1,
+ color: '#c0022e' // color at 100%
+ }
+ ],
+ global: false // default is false
+ }
+ }
+ },
+ {
+ value: this.statusCounts.hoststatusCountPercentage[0],
+ itemStyle: {
+ color: {
+ type: 'linear',
+ x: 0.5,
+ y: 0.5,
+ x2: 1.5,
+ y2: 1.5,
+ colorStops: [
+ {
+ offset: 0.1,
+ color: '#00bc4c' // color at 0%
+ },
+ {
+ offset: 1,
+ color: '#039a3f' // color at 100%
+ }
+ ],
+ global: false // default is false
+ },
+ borderWidth: 1
+ }
}
- }
+ ],
+
+ colorBy: 'series',
+ showBackground: true,
+ backgroundStyle: {
+ color: 'rgba(180, 180, 180, 0.1)',
+ borderRadius: [5, 5, 0, 0]
+ },
+
+ coordinateSystem: 'polar'
}
]
};
- }
-
- public override resizeWidget(event?: KtdResizeEnd) {
- if (this.chart) {
- this.chart.updateOptions(this.chartOptions);
- }
+ this.cdr.markForCheck();
}
public override layoutUpdate(event: KtdGridLayout) {
diff --git a/src/app/pages/dashboards/widgets/services-piechart-widget/services-piechart-widget.component.css b/src/app/pages/dashboards/widgets/services-piechart-widget/services-piechart-widget.component.css
index e69de29bb..66254c08a 100644
--- a/src/app/pages/dashboards/widgets/services-piechart-widget/services-piechart-widget.component.css
+++ b/src/app/pages/dashboards/widgets/services-piechart-widget/services-piechart-widget.component.css
@@ -0,0 +1,3 @@
+.h-85 {
+ height: 85% !important;
+}
diff --git a/src/app/pages/dashboards/widgets/services-piechart-widget/services-piechart-widget.component.html b/src/app/pages/dashboards/widgets/services-piechart-widget/services-piechart-widget.component.html
index 3430bf716..a0a86a442 100644
--- a/src/app/pages/dashboards/widgets/services-piechart-widget/services-piechart-widget.component.html
+++ b/src/app/pages/dashboards/widgets/services-piechart-widget/services-piechart-widget.component.html
@@ -1,76 +1,74 @@
-
+
-
+ @if (!statusCounts) {
+
+ }
+ @if (statusCounts && statusCounts.servicestatusCount.length === 4) {
+
diff --git a/src/app/pages/dashboards/widgets/services-piechart-widget/services-piechart-widget.component.ts b/src/app/pages/dashboards/widgets/services-piechart-widget/services-piechart-widget.component.ts
index f48fa8581..2a631cb06 100644
--- a/src/app/pages/dashboards/widgets/services-piechart-widget/services-piechart-widget.component.ts
+++ b/src/app/pages/dashboards/widgets/services-piechart-widget/services-piechart-widget.component.ts
@@ -1,79 +1,82 @@
-import { ChangeDetectionStrategy, Component, inject, ViewChild } from '@angular/core';
-import { BaseWidgetComponent } from '../base-widget/base-widget.component';
-import { KtdGridLayout, KtdResizeEnd } from '@katoid/angular-grid-layout';
-import { AsyncPipe, NgIf } from '@angular/common';
-import { FaIconComponent } from '@fortawesome/angular-fontawesome';
-import { TranslocoDirective } from '@jsverse/transloco';
-import { StatuscountResponse } from '../../../browsers/browsers.interface';
+import { ChangeDetectionStrategy, Component, effect, inject, input, OnDestroy } from '@angular/core';
+import { NgxEchartsDirective, provideEchartsCore } from 'ngx-echarts';
+import * as echarts from 'echarts/core';
+import 'echarts/theme/dark.js';
+import { EChartsOption } from 'echarts';
+
+import { LegendComponent, PolarComponent, TitleComponent, TooltipComponent } from 'echarts/components';
+import { PieChart } from 'echarts/charts';
+import { BlockLoaderComponent } from '../../../../layouts/primeng/loading/block-loader/block-loader.component';
+import { AsyncPipe } from '@angular/common';
import { RouterLink } from '@angular/router';
-import { ApexGrid, ChartComponent } from 'ng-apexcharts';
-import { ChartOptions } from '../../../../components/charts/host-pie-chart/host-pie-chart.component';
+import { StatuscountResponse } from '../../../browsers/browsers.interface';
+import { FaIconComponent } from '@fortawesome/angular-fontawesome';
+import { BaseWidgetComponent } from '../base-widget/base-widget.component';
+import { PieChartMetric } from '../../../../components/charts/charts.interface';
import { LayoutService } from '../../../../layouts/coreui/layout.service';
-import { BlockLoaderComponent } from '../../../../layouts/primeng/loading/block-loader/block-loader.component';
+import { TranslocoDirective } from '@jsverse/transloco';
+import { KtdGridLayout } from '@katoid/angular-grid-layout';
+
+echarts.use([PieChart, LegendComponent, TitleComponent, TooltipComponent, PolarComponent]);
@Component({
selector: 'oitc-services-piechart-widget',
imports: [
+ NgxEchartsDirective,
+ BlockLoaderComponent,
AsyncPipe,
- FaIconComponent,
- NgIf,
- TranslocoDirective,
RouterLink,
- ChartComponent,
- BlockLoaderComponent
+ FaIconComponent,
+ TranslocoDirective
+ ],
+ providers: [
+ provideEchartsCore({echarts}),
],
templateUrl: './services-piechart-widget.component.html',
styleUrl: './services-piechart-widget.component.css',
changeDetection: ChangeDetectionStrategy.OnPush
})
-export class ServicesPiechartWidgetComponent extends BaseWidgetComponent {
- public statusCounts?: StatuscountResponse;
+export class ServicesPiechartWidgetComponent extends BaseWidgetComponent implements OnDestroy {
+ public widgetHeight: number = 0;
+
+ public title = input
('Services availability');
+ public showLegend = input(true);
+ public chartData = input([]);
+ public scaleSize = input(20);
+ public scale = input(true);
+
+
+ public theme: string = '';
+ public chartOption: EChartsOption = {};
- @ViewChild("chart") chart?: ChartComponent;
- public chartOptions!: Partial;
+ public echartsInstance: any;
private readonly LayoutService = inject(LayoutService);
- public apexGridOptions: ApexGrid = {
- padding: {
- top: 10,
- right: 0,
- bottom: 0,
- left: 0
- },
- };
+ public statusCounts?: StatuscountResponse;
public constructor() {
super();
-
- // Subscribe to the color mode changes (drop down menu in header)
this.subscriptions.add(this.LayoutService.theme$.subscribe((theme) => {
- //console.log('Change in theme detected', theme);
- if (this.chart && this.chartOptions) {
-
- // Read the background color value from the CSS variable and update the chart
- let cuiSecondaryBg = getComputedStyle(document.documentElement).getPropertyValue('--cui-secondary-bg').trim();
-
- //this.chartOptions.plotOptions?.radialBar?.track?.background = cuiSecondaryBg;
- this.chart.updateOptions({
- plotOptions: {
- radialBar: {
- track: {
- background: cuiSecondaryBg
- }
- }
- }
- });
+ this.theme = '';
+ if (theme === 'dark') {
+ this.theme = 'dark';
}
+
this.cdr.markForCheck();
}));
+
+ effect(() => {
+ this.renderChart();
+ this.cdr.markForCheck();
+ });
}
public override load() {
this.subscriptions.add(this.WidgetsService.loadStatusCount()
.subscribe((result) => {
this.statusCounts = result;
- this.updateChart();
+ this.renderChart();
this.cdr.markForCheck();
})
);
@@ -83,91 +86,153 @@ export class ServicesPiechartWidgetComponent extends BaseWidgetComponent {
super.ngOnDestroy();
}
- private updateChart() {
+ onChartInit(ec: any) {
+ this.echartsInstance = ec;
+ this.cdr.markForCheck();
+ }
+
+ private renderChart() {
if (!this.statusCounts) {
return;
}
+ let labels = [
+ this.TranslocoService.translate('Unknown'),
+ this.TranslocoService.translate('Critical'),
+ this.TranslocoService.translate('Warning'),
+ this.TranslocoService.translate('Ok'),
+ ];
- let cuiSecondaryBg = getComputedStyle(document.documentElement).getPropertyValue('--cui-secondary-bg').trim();
- this.chartOptions = {
- series: [
- this.statusCounts.servicestatusCountPercentage[0],
- this.statusCounts.servicestatusCountPercentage[1],
- this.statusCounts.servicestatusCountPercentage[2],
- this.statusCounts.servicestatusCountPercentage[3],
- ],
- chart: {
- height: 180,
- offsetY: -20,
- type: "radialBar",
-
- // The sparkline option remove all paddings
- // https://github.com/apexcharts/apexcharts.js/issues/1272#issuecomment-591388290
- sparkline: {
- enabled: true
- },
+ this.chartOption = {
+ polar: {
+ radius: [20, '80%'],
},
- plotOptions: {
- radialBar: {
- offsetY: 0,
- startAngle: -180,
- endAngle: 180,
- hollow: {
- margin: 5,
- size: "30%",
- background: "transparent",
- image: undefined,
- },
- track: {
- show: true,
- background: cuiSecondaryBg,
- opacity: 0.5,
- dropShadow: {
- enabled: false,
- top: 2,
- left: 0,
- color: '#999999',
- opacity: 0.2,
- blur: 2
- }
- },
- dataLabels: {
- name: {
- show: false
- },
- value: {
- show: true
- }
- }
- },
+ backgroundColor: 'transparent',
+ angleAxis: {
+ show: false,
+ max: 100,
+ startAngle: 270
},
- colors: ["#00bc4c", '#efaf2f', "#bf0000", "#6b737c"],
- labels: [
- this.TranslocoService.translate('Ok'),
- this.TranslocoService.translate('Warning'),
- this.TranslocoService.translate('Critical'),
- this.TranslocoService.translate('Unknown'),
- ],
- legend: {
- show: false
+ radiusAxis: {
+ type: 'category',
+ show: false,
+ data: labels
},
- responsive: [
+ tooltip: {
+ show: true,
+ formatter: '{b} ({c}%)'
+ },
+ series: [
{
- breakpoint: 480,
- options: {
- legend: {
- show: false
+ type: 'bar',
+ barWidth: '50%',
+ data: [
+ {
+ value: this.statusCounts.servicestatusCountPercentage[3],
+ itemStyle: {
+ color: {
+ type: 'linear',
+ x: 0.5,
+ y: 0.5,
+ x2: 1.5,
+ y2: 1.5,
+ colorStops: [
+ {
+ offset: 0.1,
+ color: '#6b737c' // color at 0%
+ },
+ {
+ offset: 1,
+ color: '#4f555a' // color at 100%
+ }
+ ],
+ global: false // default is false
+ }
+ }
+ },
+ {
+ value: this.statusCounts.servicestatusCountPercentage[2],
+ itemStyle: {
+ color: {
+ type: 'linear',
+ x: 0.5,
+ y: 0.5,
+ x2: 1.5,
+ y2: 1.5,
+ colorStops: [
+ {
+ offset: 0.1,
+ color: '#CC0000' // color at 0%
+ },
+ {
+ offset: 1,
+ color: '#c0022e' // color at 100%
+ }
+ ],
+ global: false // default is false
+ }
+ }
+ },
+ {
+ value: this.statusCounts.servicestatusCountPercentage[1],
+ itemStyle: {
+ color: {
+ type: 'linear',
+ x: 0.5,
+ y: 0.5,
+ x2: 1.5,
+ y2: 1.5,
+ colorStops: [
+ {
+ offset: 0.1,
+ color: '#ffbb33' // color at 0%
+ },
+ {
+ offset: 1,
+ color: '#ffbb33' // color at 100%
+ }
+ ],
+ global: false // default is false
+ }
+ }
+ },
+ {
+ value: this.statusCounts.servicestatusCountPercentage[0],
+ itemStyle: {
+ color: {
+ type: 'linear',
+ x: 0.5,
+ y: 0.5,
+ x2: 1.5,
+ y2: 1.5,
+ colorStops: [
+ {
+ offset: 0.1,
+ color: '#00bc4c' // color at 0%
+ },
+ {
+ offset: 1,
+ color: '#039a3f' // color at 100%
+ }
+ ],
+ global: false // default is false
+ },
+ borderWidth: 1
+ }
}
- }
+ ],
+
+ colorBy: 'series',
+ showBackground: true,
+ backgroundStyle: {
+ color: 'rgba(180, 180, 180, 0.1)',
+ borderRadius: [5, 5, 0, 0]
+ },
+
+ coordinateSystem: 'polar'
}
]
};
- }
-
- public override resizeWidget(event?: KtdResizeEnd) {
- if (this.chart) {
- this.chart.updateOptions(this.chartOptions);
- }
+ this.cdr.markForCheck();
}
public override layoutUpdate(event: KtdGridLayout) {
diff --git a/src/app/pages/hostgroups/hostgroups-append/hostgroups-append.component.html b/src/app/pages/hostgroups/hostgroups-append/hostgroups-append.component.html
index 696c45272..36f773491 100644
--- a/src/app/pages/hostgroups/hostgroups-append/hostgroups-append.component.html
+++ b/src/app/pages/hostgroups/hostgroups-append/hostgroups-append.component.html
@@ -54,8 +54,12 @@
[(ngModel)]="post.Hostgroup.id"
optionValue="key"
optionLabel="value"
- [options]="hostgroups">
+ [options]="hostgroups"
+ [searchCallback]="loadHostgroups"
+ oitcFormError [errors]="errors" errorField="hostgroup_id">
+
diff --git a/src/app/pages/hostgroups/hostgroups-append/hostgroups-append.component.ts b/src/app/pages/hostgroups/hostgroups-append/hostgroups-append.component.ts
index 7c531a929..26ed5602f 100644
--- a/src/app/pages/hostgroups/hostgroups-append/hostgroups-append.component.ts
+++ b/src/app/pages/hostgroups/hostgroups-append/hostgroups-append.component.ts
@@ -2,24 +2,19 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnDestro
import { BackButtonDirective } from '../../../directives/back-button.directive';
import { HistoryService } from '../../../history.service';
import {
- AlertComponent,
- CardBodyComponent,
- CardComponent,
- CardFooterComponent,
- CardHeaderComponent,
- CardTitleDirective,
- FormDirective,
- FormLabelDirective,
- NavComponent,
- NavItemComponent
+ AlertComponent,
+ CardBodyComponent,
+ CardComponent,
+ CardFooterComponent,
+ CardHeaderComponent,
+ CardTitleDirective,
+ FormDirective,
+ FormLabelDirective,
+ NavComponent,
+ NavItemComponent
} from '@coreui/angular';
-import { CoreuiComponent } from '../../../layouts/coreui/coreui.component';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
-
-
import { FormsModule } from '@angular/forms';
-
-
import { PaginatorModule } from 'primeng/paginator';
import { PermissionDirective } from '../../../permissions/permission.directive';
import { RequiredIconComponent } from '../../../components/required-icon/required-icon.component';
@@ -30,40 +25,45 @@ import { HostgroupsService } from '../hostgroups.service';
import { HostgroupAppend, HostgroupsLoadHostgroupsByStringParams } from '../hostgroups.interface';
import { Subscription } from 'rxjs';
import { SelectKeyValue } from '../../../layouts/primeng/select.interface';
-import { ActivatedRoute, Router, RouterLink } from '@angular/router';
+import { ActivatedRoute, RouterLink } from '@angular/router';
import { NotyService } from '../../../layouts/coreui/noty.service';
-import { GenericResponseWrapper } from '../../../generic-responses';
+import { GenericIdResponse, GenericValidationError } from '../../../generic-responses';
+import { FormErrorDirective } from '../../../layouts/coreui/form-error.directive';
+import { FormFeedbackComponent } from '../../../layouts/coreui/form-feedback/form-feedback.component';
+
@Component({
selector: 'oitc-hostgroups-append',
imports: [
- BackButtonDirective,
- CardBodyComponent,
- CardComponent,
- CardFooterComponent,
- CardHeaderComponent,
- CardTitleDirective,
- FaIconComponent,
- FormDirective,
- FormLabelDirective,
- FormsModule,
- NavComponent,
- NavItemComponent,
- PaginatorModule,
- PermissionDirective,
- RequiredIconComponent,
- SelectComponent,
- TranslocoDirective,
- XsButtonDirective,
- AlertComponent,
- RouterLink
-],
+ BackButtonDirective,
+ CardBodyComponent,
+ CardComponent,
+ CardFooterComponent,
+ CardHeaderComponent,
+ CardTitleDirective,
+ FaIconComponent,
+ FormDirective,
+ FormLabelDirective,
+ FormErrorDirective,
+ FormsModule,
+ NavComponent,
+ NavItemComponent,
+ PaginatorModule,
+ PermissionDirective,
+ RequiredIconComponent,
+ SelectComponent,
+ TranslocoDirective,
+ XsButtonDirective,
+ AlertComponent,
+ RouterLink,
+ FormFeedbackComponent
+ ],
templateUrl: './hostgroups-append.component.html',
styleUrl: './hostgroups-append.component.css',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class HostgroupsAppendComponent implements OnInit, OnDestroy {
- private readonly Subscription: Subscription = new Subscription();
+ private readonly subscriptions: Subscription = new Subscription();
private readonly HostgroupsService: HostgroupsService = inject(HostgroupsService);
private readonly notyService: NotyService = inject(NotyService);
private readonly TranslocoService: TranslocoService = inject(TranslocoService);
@@ -78,34 +78,57 @@ export class HostgroupsAppendComponent implements OnInit, OnDestroy {
}
};
protected hostgroups: SelectKeyValue[] = [];
+ public errors: GenericValidationError | null = null;
private cdr = inject(ChangeDetectorRef);
+ public ngOnInit() {
+ this.loadHostgroups('');
+ this.cdr.markForCheck();
+ }
+
+ ngOnDestroy(): void {
+ this.subscriptions.unsubscribe();
+ }
+
+ protected loadHostgroups = (search: string) => {
+ this.subscriptions.add(this.HostgroupsService.loadHostgroupsByString({
+ 'filter[Containers.name]': search
+ } as HostgroupsLoadHostgroupsByStringParams).subscribe((data: SelectKeyValue[]) => {
+ this.hostgroups = data;
+ this.cdr.markForCheck();
+ }));
+ }
+
protected submit(): void {
const hostIds = this.route.snapshot.paramMap.get('hostids');
if (hostIds) {
this.post.Hostgroup.hosts._ids = hostIds.split(',').map(Number);
}
- this.Subscription.add(this.HostgroupsService.appendHosts(this.post).subscribe((result: GenericResponseWrapper) => {
- this.cdr.markForCheck();
- const title = this.TranslocoService.translate('Host group');
- const msg = this.TranslocoService.translate('saved successfully');
- const url = ['hostgroups', 'edit', this.post.Hostgroup.id];
+ this.subscriptions.add(this.HostgroupsService.appendHosts(this.post)
+ .subscribe((result) => {
+ this.cdr.markForCheck();
+ if (result.success) {
+ const response = result.data as GenericIdResponse;
- this.notyService.genericSuccess(msg, title, url);
- this.HistoryService.navigateWithFallback(['/hostgroups/index']);
- }));
- }
+ const title = this.TranslocoService.translate('Append hosts to host group');
+ const msg = this.TranslocoService.translate(' successfully');
+ const url = ['hostgroups', 'edit', response.id];
- public ngOnInit() {
- this.Subscription.add(this.HostgroupsService.loadHostgroupsByString({} as HostgroupsLoadHostgroupsByStringParams).subscribe((data: SelectKeyValue[]) => {
- this.hostgroups = data;
- this.cdr.markForCheck();
- }));
- }
+ this.notyService.genericSuccess(msg, title, url);
- ngOnDestroy(): void {
- this.Subscription.unsubscribe();
- }
+ this.notyService.scrollContentDivToTop();
+ this.HistoryService.navigateWithFallback(['/hosts/index']);
+ return;
+ }
+ // Error
+ const errorResponse = result.data as GenericValidationError;
+ this.notyService.genericError();
+ if (result) {
+ this.errors = errorResponse;
+ }
+ })
+ );
+ }
}
diff --git a/src/app/pages/hostgroups/hostgroups.service.ts b/src/app/pages/hostgroups/hostgroups.service.ts
index d1e894c4c..62c77eed1 100644
--- a/src/app/pages/hostgroups/hostgroups.service.ts
+++ b/src/app/pages/hostgroups/hostgroups.service.ts
@@ -24,7 +24,12 @@ import {
} from "./hostgroups.interface";
import { HttpClient } from "@angular/common/http";
import { PROXY_PATH } from "../../tokens/proxy-path.token";
-import { GenericIdResponse, GenericResponseWrapper, GenericValidationError } from "../../generic-responses";
+import {
+ GenericActionErrorResponse,
+ GenericIdResponse,
+ GenericResponseWrapper,
+ GenericValidationError
+} from "../../generic-responses";
import { DeleteAllItem } from "../../layouts/coreui/delete-all-modal/delete-all.interface";
import { SelectKeyValue } from '../../layouts/primeng/select.interface';
@@ -243,7 +248,27 @@ export class HostgroupsService {
public appendHosts(param: HostgroupAppend): Observable
{
const proxyPath: string = this.proxyPath;
- return this.http.post(`${proxyPath}/hostgroups/append/.json?angular=true`, param);
+ return this.http.post(`${proxyPath}/hostgroups/append/.json?angular=true`, param)
+ .pipe(
+ map(data => {
+ // Return true on 200 Ok
+ return {
+ success: true,
+ data: data as GenericIdResponse
+ };
+ }),
+ catchError((error: any) => {
+ const err = error.error.message as GenericActionErrorResponse;
+ return of({
+ success: false,
+ data: {
+ hostgroup_id: {
+ err
+ }
+ }
+ });
+ })
+ );
}
public loadHostgroupsByContainerId(containerId: number, selected: any[], resolveContainerIds: boolean = true): Observable {
diff --git a/src/app/pages/hosts/hosts-add/hosts-add.component.html b/src/app/pages/hosts/hosts-add/hosts-add.component.html
index cd21e4900..8253602e8 100644
--- a/src/app/pages/hosts/hosts-add/hosts-add.component.html
+++ b/src/app/pages/hosts/hosts-add/hosts-add.component.html
@@ -166,7 +166,7 @@ {{ t('Create new host') }}
{{
t('A host with the name {0} already exists. Duplicate host names could lead to confusion.', {
- '0': post.name
+ '0': hostnameCheckedForDuplicates
})
}}
diff --git a/src/app/pages/hosts/hosts-add/hosts-add.component.ts b/src/app/pages/hosts/hosts-add/hosts-add.component.ts
index f91905cc9..e03a1f8c7 100644
--- a/src/app/pages/hosts/hosts-add/hosts-add.component.ts
+++ b/src/app/pages/hosts/hosts-add/hosts-add.component.ts
@@ -135,6 +135,9 @@ export class HostsAddComponent implements OnInit, OnDestroy {
isHostnameInUse: false
};
+ public hostnameCheckedForDuplicates: string = '';
+
+
public hosttemplates: SelectKeyValue[] = [];
public hostgroups: SelectKeyValue[] = [];
public timeperiods: SelectKeyValue[] = [];
@@ -417,6 +420,8 @@ export class HostsAddComponent implements OnInit, OnDestroy {
this.subscriptions.add(this.HostsService.checkForDuplicateHostname(this.post.name)
.subscribe((result) => {
this.data.isHostnameInUse = result;
+ // Ensure that the host name in the warning box will not change until the user changes the name again
+ this.hostnameCheckedForDuplicates = this.post.name;
this.cdr.markForCheck();
})
);
diff --git a/src/app/pages/hosts/hosts-browser/hosts-browser.component.html b/src/app/pages/hosts/hosts-browser/hosts-browser.component.html
index 4a207cd34..449f726ec 100644
--- a/src/app/pages/hosts/hosts-browser/hosts-browser.component.html
+++ b/src/app/pages/hosts/hosts-browser/hosts-browser.component.html
@@ -387,6 +387,62 @@
{{ t('The state of this host is currently flapping!') }}
+
+ @if (result && result.plannedDowntimes && result.plannedDowntimes.length > 0) {
+
+
+
+
+
+
+ {{ t('Upcoming planned maintenance periods for the host') }}
+
+
+
+
+
+
+ {{ t('User') }}
+ {{ t('Start time') }}
+ {{ t('End time') }}
+ {{ t('Comment') }}
+ {{ t('Cancel') }}
+
+
+
+ @for (downtime of result.plannedDowntimes; track $index) {
+
+ {{ downtime.authorName }}
+ {{ downtime.scheduledStartTime }}
+ {{ downtime.scheduledEndTime }}
+ {{ downtime.commentData }}
+
+ @if (downtime.allowEdit && downtime.isCancellable) {
+
+
+
+ {{ t('Cancel') }}
+
+
+ }
+
+
+ }
+
+
+
+
+
+
+ }
+
diff --git a/src/app/pages/hosts/hosts-edit-details/hosts-edit-details.component.html b/src/app/pages/hosts/hosts-edit-details/hosts-edit-details.component.html
index 5eee13abe..0bbad5b92 100644
--- a/src/app/pages/hosts/hosts-edit-details/hosts-edit-details.component.html
+++ b/src/app/pages/hosts/hosts-edit-details/hosts-edit-details.component.html
@@ -471,21 +471,14 @@
- {{ t('Update sharing') }}
+ {{ t('Update host details') }}
-
{{ t('Cancel') }}
-
-
-
-
-
-
-
diff --git a/src/app/pages/hosts/hosts-edit/hosts-edit.component.html b/src/app/pages/hosts/hosts-edit/hosts-edit.component.html
index bb9ece641..f70c17bd4 100644
--- a/src/app/pages/hosts/hosts-edit/hosts-edit.component.html
+++ b/src/app/pages/hosts/hosts-edit/hosts-edit.component.html
@@ -204,7 +204,7 @@ {{ t('Edit host:') }}
{{
t('A host with the name {0} already exists. Duplicate host names could lead to confusion.', {
- '0': post.name
+ '0': hostnameCheckedForDuplicates
})
}}
diff --git a/src/app/pages/hosts/hosts-edit/hosts-edit.component.ts b/src/app/pages/hosts/hosts-edit/hosts-edit.component.ts
index 022cb02fb..fd7ac6433 100644
--- a/src/app/pages/hosts/hosts-edit/hosts-edit.component.ts
+++ b/src/app/pages/hosts/hosts-edit/hosts-edit.component.ts
@@ -1,26 +1,26 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnDestroy, OnInit } from '@angular/core';
import {
- AlertComponent,
- CardBodyComponent,
- CardComponent,
- CardFooterComponent,
- CardHeaderComponent,
- CardTitleDirective,
- DropdownComponent,
- DropdownItemDirective,
- DropdownMenuDirective,
- DropdownToggleDirective,
- FormCheckComponent,
- FormCheckInputDirective,
- FormCheckLabelDirective,
- FormControlDirective,
- FormDirective,
- FormLabelDirective,
- InputGroupComponent,
- InputGroupTextDirective,
- NavComponent,
- NavItemComponent
+ AlertComponent,
+ CardBodyComponent,
+ CardComponent,
+ CardFooterComponent,
+ CardHeaderComponent,
+ CardTitleDirective,
+ DropdownComponent,
+ DropdownItemDirective,
+ DropdownMenuDirective,
+ DropdownToggleDirective,
+ FormCheckComponent,
+ FormCheckInputDirective,
+ FormCheckLabelDirective,
+ FormControlDirective,
+ FormDirective,
+ FormLabelDirective,
+ InputGroupComponent,
+ InputGroupTextDirective,
+ NavComponent,
+ NavItemComponent
} from '@coreui/angular';
import { BackButtonDirective } from '../../../directives/back-button.directive';
import {
@@ -69,59 +69,59 @@ import { HistoryService } from '../../../history.service';
@Component({
selector: 'oitc-hosts-edit',
imports: [
- AlertComponent,
- BackButtonDirective,
- CardBodyComponent,
- CardComponent,
- CardFooterComponent,
- CardHeaderComponent,
- CardTitleDirective,
- CheckAttemptsInputComponent,
- DropdownComponent,
- DropdownItemDirective,
- DropdownMenuDirective,
- DropdownToggleDirective,
- FaIconComponent,
- FormCheckComponent,
- FormCheckInputDirective,
- FormCheckLabelDirective,
- FormControlDirective,
- FormDirective,
- FormErrorDirective,
- FormFeedbackComponent,
- FormLabelDirective,
- FormsModule,
- HumanTimeComponent,
- InputGroupComponent,
- InputGroupTextDirective,
- IntervalInputComponent,
- LabelLinkComponent,
- MacrosComponent,
- MultiSelectComponent,
- NavComponent,
- NavItemComponent,
- NgForOf,
- NgIf,
- NgSelectModule,
- PermissionDirective,
- PriorityComponent,
- ReactiveFormsModule,
- RequiredIconComponent,
- SelectComponent,
- TemplateDiffBtnComponent,
- TemplateDiffComponent,
- TranslocoDirective,
- TranslocoPipe,
- TrueFalseDirective,
- XsButtonDirective,
- RouterLink,
- ObjectUuidComponent,
- NgClass,
- FakeSelectComponent,
- UiBlockerComponent,
- FormLoaderComponent,
- AsyncPipe
-],
+ AlertComponent,
+ BackButtonDirective,
+ CardBodyComponent,
+ CardComponent,
+ CardFooterComponent,
+ CardHeaderComponent,
+ CardTitleDirective,
+ CheckAttemptsInputComponent,
+ DropdownComponent,
+ DropdownItemDirective,
+ DropdownMenuDirective,
+ DropdownToggleDirective,
+ FaIconComponent,
+ FormCheckComponent,
+ FormCheckInputDirective,
+ FormCheckLabelDirective,
+ FormControlDirective,
+ FormDirective,
+ FormErrorDirective,
+ FormFeedbackComponent,
+ FormLabelDirective,
+ FormsModule,
+ HumanTimeComponent,
+ InputGroupComponent,
+ InputGroupTextDirective,
+ IntervalInputComponent,
+ LabelLinkComponent,
+ MacrosComponent,
+ MultiSelectComponent,
+ NavComponent,
+ NavItemComponent,
+ NgForOf,
+ NgIf,
+ NgSelectModule,
+ PermissionDirective,
+ PriorityComponent,
+ ReactiveFormsModule,
+ RequiredIconComponent,
+ SelectComponent,
+ TemplateDiffBtnComponent,
+ TemplateDiffComponent,
+ TranslocoDirective,
+ TranslocoPipe,
+ TrueFalseDirective,
+ XsButtonDirective,
+ RouterLink,
+ ObjectUuidComponent,
+ NgClass,
+ FakeSelectComponent,
+ UiBlockerComponent,
+ FormLoaderComponent,
+ AsyncPipe
+ ],
templateUrl: './hosts-edit.component.html',
styleUrl: './hosts-edit.component.css',
changeDetection: ChangeDetectionStrategy.OnPush
@@ -155,6 +155,9 @@ export class HostsEditComponent implements OnInit, OnDestroy {
disableInheritance: false
};
+ public hostnameCheckedForDuplicates: string = '';
+
+
public hosttemplates: SelectKeyValue[] = [];
public hostgroups: SelectKeyValue[] = [];
public timeperiods: SelectKeyValue[] = [];
@@ -410,6 +413,8 @@ export class HostsEditComponent implements OnInit, OnDestroy {
this.subscriptions.add(this.HostsService.checkForDuplicateHostname(this.post.name, [this.id])
.subscribe((result) => {
this.data.isHostnameInUse = result;
+ // Ensure that the host name in the warning box will not change until the user changes the name again
+ this.hostnameCheckedForDuplicates = this.post.name;
this.cdr.markForCheck();
})
);
diff --git a/src/app/pages/hosts/hosts-index/hosts-index.component.html b/src/app/pages/hosts/hosts-index/hosts-index.component.html
index bcc46d0f1..d64588917 100644
--- a/src/app/pages/hosts/hosts-index/hosts-index.component.html
+++ b/src/app/pages/hosts/hosts-index/hosts-index.component.html
@@ -1088,7 +1088,6 @@
-
}
-
-
@@ -1182,17 +1179,16 @@
{{ t('Copy') }}
-
-
-
-
-
- {{ t('Delete') }}
-
-
-
+ @if (host.Host.allow_edit && ( PermissionsService.hasPermissionObservable(['hosts', 'delete'])|async )) {
+
+
+
+
+ {{ t('Delete') }}
+
+
+ }
@@ -1254,7 +1250,7 @@