]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/telemetry/telemetry.component.ts
import quincy beta 17.1.0
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / cluster / telemetry / telemetry.component.ts
index 8edb7b6a927d700989040dfb0060f4d954cfa768..882a2fe3c403637aafe50ca0d9faf4a7ca481846 100644 (file)
@@ -12,7 +12,6 @@ import { NotificationType } from '~/app/shared/enum/notification-type.enum';
 import { CdForm } from '~/app/shared/forms/cd-form';
 import { CdFormBuilder } from '~/app/shared/forms/cd-form-builder';
 import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
-import { CdValidators } from '~/app/shared/forms/cd-validators';
 import { NotificationService } from '~/app/shared/services/notification.service';
 import { TelemetryNotificationService } from '~/app/shared/services/telemetry-notification.service';
 
@@ -26,22 +25,28 @@ export class TelemetryComponent extends CdForm implements OnInit {
   licenseAgrmt = false;
   moduleEnabled: boolean;
   options: Object = {};
+  newConfig: Object = {};
+  configResp: object = {};
   previewForm: CdFormGroup;
   requiredFields = [
     'channel_basic',
     'channel_crash',
     'channel_device',
     'channel_ident',
+    'channel_perf',
     'interval',
     'proxy',
     'contact',
-    'description'
+    'description',
+    'organization'
   ];
+  contactInfofields = ['contact', 'description', 'organization'];
   report: object = undefined;
   reportId: number = undefined;
   sendToUrl = '';
   sendToDeviceUrl = '';
   step = 1;
+  showContactInfo: boolean;
 
   constructor(
     public actionLabels: ActionLabelsI18n,
@@ -66,10 +71,11 @@ export class TelemetryComponent extends CdForm implements OnInit {
         this.moduleEnabled = configResp['enabled'];
         this.sendToUrl = configResp['url'];
         this.sendToDeviceUrl = configResp['device_url'];
+        this.showContactInfo = configResp['channel_ident'];
         this.options = _.pick(resp[0], this.requiredFields);
-        const configs = _.pick(configResp, this.requiredFields);
+        this.configResp = _.pick(configResp, this.requiredFields);
         this.createConfigForm();
-        this.configForm.setValue(configs);
+        this.configForm.setValue(this.configResp);
         this.loadingReady();
       },
       (_error) => {
@@ -86,9 +92,71 @@ export class TelemetryComponent extends CdForm implements OnInit {
     this.configForm = this.formBuilder.group(controlsConfig);
   }
 
+  private replacer(key: string, value: any) {
+    // Display the arrays of keys 'ranges' and 'values' horizontally as they take up too much space
+    // and Stringify displays it in vertical by default.
+    if ((key === 'ranges' || key === 'values') && Array.isArray(value)) {
+      const elements = [];
+      for (let i = 0; i < value.length; i++) {
+        elements.push(JSON.stringify(value[i]));
+      }
+      return elements;
+    }
+    // Else, just return the value as is, without any formatting.
+    return value;
+  }
+
+  replacerTest(report: object) {
+    return JSON.stringify(report, this.replacer, 2);
+  }
+
+  private formatReport() {
+    let copy = {};
+    copy = JSON.parse(JSON.stringify(this.report));
+    const perf_keys = [
+      'perf_counters',
+      'stats_per_pool',
+      'stats_per_pg',
+      'io_rate',
+      'osd_perf_histograms',
+      'mempool',
+      'heap_stats',
+      'rocksdb_stats'
+    ];
+    for (let i = 0; i < perf_keys.length; i++) {
+      const key = perf_keys[i];
+      if (key in copy['report']) {
+        delete copy['report'][key];
+      }
+    }
+    return JSON.stringify(copy, null, 2);
+  }
+
+  formatReportTest(report: object) {
+    let copy = {};
+    copy = JSON.parse(JSON.stringify(report));
+    const perf_keys = [
+      'perf_counters',
+      'stats_per_pool',
+      'stats_per_pg',
+      'io_rate',
+      'osd_perf_histograms',
+      'mempool',
+      'heap_stats',
+      'rocksdb_stats'
+    ];
+    for (let i = 0; i < perf_keys.length; i++) {
+      const key = perf_keys[i];
+      if (key in copy) {
+        delete copy[key];
+      }
+    }
+    return JSON.stringify(copy, null, 2);
+  }
+
   private createPreviewForm() {
     const controls = {
-      report: JSON.stringify(this.report, null, 2),
+      report: this.formatReport(),
       reportId: this.reportId,
       licenseAgrmt: [this.licenseAgrmt, Validators.requiredTrue]
     };
@@ -99,14 +167,7 @@ export class TelemetryComponent extends CdForm implements OnInit {
     const result = [];
     switch (option.type) {
       case 'int':
-        result.push(CdValidators.number());
         result.push(Validators.required);
-        if (_.isNumber(option.min)) {
-          result.push(Validators.min(option.min));
-        }
-        if (_.isNumber(option.max)) {
-          result.push(Validators.max(option.max));
-        }
         break;
       case 'str':
         if (_.isNumber(option.min)) {
@@ -120,6 +181,23 @@ export class TelemetryComponent extends CdForm implements OnInit {
     return result;
   }
 
+  private updateReportFromConfig(updatedConfig: Object = {}) {
+    // update channels
+    const availableChannels: string[] = this.report['report']['channels_available'];
+    const updatedChannels = [];
+    for (const channel of availableChannels) {
+      const key = `channel_${channel}`;
+      if (updatedConfig[key]) {
+        updatedChannels.push(channel);
+      }
+    }
+    this.report['report']['channels'] = updatedChannels;
+    // update contactInfo
+    for (const contactInfofield of this.contactInfofields) {
+      this.report['report'][contactInfofield] = updatedConfig[contactInfofield];
+    }
+  }
+
   private getReport() {
     this.loadingStart();
 
@@ -127,6 +205,7 @@ export class TelemetryComponent extends CdForm implements OnInit {
       (resp: object) => {
         this.report = resp;
         this.reportId = resp['report']['report_id'];
+        this.updateReportFromConfig(this.newConfig);
         this.createPreviewForm();
         this.loadingReady();
         this.step++;
@@ -137,31 +216,29 @@ export class TelemetryComponent extends CdForm implements OnInit {
     );
   }
 
-  updateConfig() {
-    const config = {};
-    _.forEach(Object.values(this.options), (option) => {
+  toggleIdent() {
+    this.showContactInfo = !this.showContactInfo;
+  }
+
+  buildReport() {
+    this.newConfig = {};
+    for (const option of Object.values(this.options)) {
       const control = this.configForm.get(option.name);
-      // Append the option only if the value has been modified.
-      if (control.dirty && control.valid) {
-        config[option.name] = control.value;
-      }
-    });
-    this.mgrModuleService.updateConfig('telemetry', config).subscribe(
-      () => {
-        this.disableModule(
-          $localize`Your settings have been applied successfully. \
- Due to privacy/legal reasons the Telemetry module is now disabled until you \
- complete the next step and accept the license.`,
-          () => {
-            this.getReport();
-          }
-        );
-      },
-      () => {
-        // Reset the 'Submit' button.
+      // Append the option only if they are valid
+      if (control.valid) {
+        this.newConfig[option.name] = control.value;
+      } else {
         this.configForm.setErrors({ cdSubmitButton: true });
+        return;
       }
-    );
+    }
+    // reset contact info field  if ident channel is off
+    if (!this.newConfig['channel_ident']) {
+      for (const contactInfofield of this.contactInfofields) {
+        this.newConfig[contactInfofield] = '';
+      }
+    }
+    this.getReport();
   }
 
   disableModule(message: string = null, followUpFunc: Function = null) {
@@ -179,25 +256,52 @@ export class TelemetryComponent extends CdForm implements OnInit {
   }
 
   next() {
-    if (this.configForm.pristine) {
-      this.getReport();
-    } else {
-      this.updateConfig();
-    }
+    this.buildReport();
   }
 
   back() {
     this.step--;
   }
 
-  onSubmit() {
-    this.telemetryService.enable().subscribe(() => {
-      this.telemetryNotificationService.setVisibility(false);
-      this.notificationService.show(
-        NotificationType.success,
-        $localize`The Telemetry module has been configured and activated successfully.`
-      );
-      this.router.navigate(['']);
+  getChangedConfig() {
+    const updatedConfig = {};
+    _.forEach(this.requiredFields, (configField) => {
+      if (!_.isEqual(this.configResp[configField], this.newConfig[configField])) {
+        updatedConfig[configField] = this.newConfig[configField];
+      }
     });
+    return updatedConfig;
+  }
+
+  onSubmit() {
+    const updatedConfig = this.getChangedConfig();
+    const observables = [
+      this.telemetryService.enable(),
+      this.mgrModuleService.updateConfig('telemetry', updatedConfig)
+    ];
+
+    observableForkJoin(observables).subscribe(
+      () => {
+        this.telemetryNotificationService.setVisibility(false);
+        this.notificationService.show(
+          NotificationType.success,
+          $localize`The Telemetry module has been configured and activated successfully.`
+        );
+      },
+      () => {
+        this.telemetryNotificationService.setVisibility(false);
+        this.notificationService.show(
+          NotificationType.error,
+          $localize`An Error occurred while updating the Telemetry module configuration.\
+             Please Try again`
+        );
+        // Reset the 'Update' button.
+        this.previewForm.setErrors({ cdSubmitButton: true });
+      },
+      () => {
+        this.newConfig = {};
+        this.router.navigate(['']);
+      }
+    );
   }
 }