You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metron.apache.org by sa...@apache.org on 2019/07/21 09:19:53 UTC

[metron] branch feature/METRON-1856-parser-aggregation updated: METRON-2124 [UI] Move status information and start/stop to the Aggregate level (sardell) closes apache/metron#1418

This is an automated email from the ASF dual-hosted git repository.

sardell pushed a commit to branch feature/METRON-1856-parser-aggregation
in repository https://gitbox.apache.org/repos/asf/metron.git


The following commit(s) were added to refs/heads/feature/METRON-1856-parser-aggregation by this push:
     new 9d7efa0  METRON-2124 [UI] Move status information and start/stop to the Aggregate level (sardell) closes apache/metron#1418
9d7efa0 is described below

commit 9d7efa00c906d5c31e08ae89022f073a57c8d9dc
Author: sardell <sh...@gmail.com>
AuthorDate: Sun Jul 21 11:19:36 2019 +0200

    METRON-2124 [UI] Move status information and start/stop to the Aggregate level (sardell) closes apache/metron#1418
---
 .../sensor-parser-list.component.html              |  98 ++++++-
 .../sensor-parser-list.component.spec.ts           | 300 ++++++++++++---------
 .../sensor-parser-list.component.ts                |  40 ++-
 3 files changed, 293 insertions(+), 145 deletions(-)

diff --git a/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.html b/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.html
index 726cf77..d55618c 100644
--- a/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.html
+++ b/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.html
@@ -52,8 +52,8 @@
     </tr>
     </thead>
     <tbody>
-    <tr *ngFor="let sensor of sensors;" (click)="onSensorRowSelect(sensor, $event)" [ngClass]="{'active': (selectedSensors.indexOf(sensor) != -1 || selectedSensor == sensor)}">
-      <td>{{ sensor.sensorName }}</td>
+          'active': isSelected(sensor),
+          'parent': sensor.isGroup,
       <td>{{ getParserType(sensor.config) }}</td>
       <td [ngClass]="{'warning-text': (sensor.status == 'Stopped' || sensor.status == 'Disabled')}">{{ sensor.status }}</td>
       <td>{{ sensor.latency }}</td>
@@ -61,19 +61,93 @@
       <td>{{ sensor.modifiedByDate }}</td>
       <td>{{ sensor.modifiedBy }}</td>
       <td class="icon-container">
-          <i  data-toggle="tooltip" title="Operation in progress" class="fa fa-circle-o-notch fa-spin fa-lg fa-fw" [hidden]="!sensor.config['startStopInProgress']"></i>
+        <i
+          data-toggle="tooltip"
+          title="Operation in progress"
+          class="fa fa-circle-o-notch fa-spin fa-lg fa-fw"
+          [hidden]="!sensor.startStopInProgress"
+        >
+        </i>
+        <i
+          data-toggle="tooltip"
+          title="Stop parser topology"
+          class="fa fa-stop fa-lg"
+          aria-hidden="true"
+          data-qe-id="stop-parser-button"
+          [hidden]="!isStoppable(sensor)"
+          (click)="onStopSensor(sensor, $event)"
+        >
+        </i>
+        <i
+          data-toggle="tooltip"
+          title="Start parser topology"
+          class="fa fa-play fa-lg"
+          aria-hidden="true"
+          data-qe-id="start-parser-button"
+          [hidden]="!isStartable(sensor)"
+          (click)="onStartSensor(sensor, $event)"
+        >
+        </i>
 
-          <i  data-toggle="tooltip" title="Stop parser topology" class="fa fa-stop fa-lg" aria-hidden="true" [hidden]="((sensor.status != 'Running' && sensor.status != 'Disabled') || sensor.config['startStopInProgress'])" (click)="onStopSensor(sensor, $event)"></i>
-          <i  data-toggle="tooltip" title="Disable parser topology" class="fa fa-ban fa-lg" aria-hidden="true" [hidden]="(sensor.status != 'Running'  || sensor.config['startStopInProgress'])" (click)="onDisableSensor(sensor, $event)"></i>
+        <i
+          data-toggle="tooltip"
+          title="Disable parser topology"
+          class="fa fa-ban fa-lg"
+          aria-hidden="true"
+          data-qe-id="disable-parser-button"
+          [hidden]="!isDisableable(sensor)"
+          (click)="onDisableSensor(sensor, $event)"
+        >
+        </i>
 
-          <i  data-toggle="tooltip" title="Start parser topology" class="fa fa-play fa-lg" aria-hidden="true" [hidden]="(sensor.status != 'Stopped' || sensor.config['startStopInProgress'])" (click)="onStartSensor(sensor, $event)"></i>
-          <i  data-toggle="tooltip" title="Enable parser topology" class="fa fa-check-circle-o fa-lg" aria-hidden="true" [hidden]="(sensor.status != 'Disabled' || sensor.config['startStopInProgress'])" (click)="onEnableSensor(sensor, $event)"></i>
-
-          <i  data-toggle="tooltip" title="Edit parser topology" class="fa fa-pencil fa-lg" aria-hidden="true" (click)="navigateToSensorEdit(sensor, $event)"></i>
-
-          <i  data-toggle="tooltip" title="Delete parser configuration" class="fa fa-trash-o fa-lg" aria-hidden="true" (click)="deleteSensor($event, [sensor])"></i>
+        <i
+          data-toggle="tooltip"
+          title="Enable parser topology"
+          class="fa fa-check-circle-o fa-lg"
+          aria-hidden="true"
+          data-qe-id="enable-parser-button"
+          [hidden]="!isEnableable(sensor)"
+          (click)="onEnableSensor(sensor, $event)"
+        >
+        </i>
+        <i
+          data-toggle="tooltip"
+          title="Edit parser topology"
+          class="fa fa-pencil fa-lg"
+          aria-hidden="true"
+          data-qe-id="edit-parser-button"
+          [hidden]="sensor.isDeleted"
+          (click)="navigateToSensorEdit(sensor, $event)"
+        >
+        </i>
+        <i
+          data-toggle="tooltip"
+          title="Delete parser configuration"
+          class="fa fa-trash-o fa-lg"
+          aria-hidden="true"
+          data-qe-id="delete-parser-button"
+          [hidden]="sensor.isDeleted"
+          (click)="onDeleteItem(sensor, $event)"
+        >
+        </i>
+      </td>
+      <td>
+        <div data-qe-id="sensor-select" [hidden]="isDeletedOrPhantom(sensor)">
+          <input
+            id="{{ sensor.config.getName() }}"
+            class="fontawesome-checkbox"
+            type="checkbox"
+            name="{{sensor.config.getName()}}"
+            (change)="onRowSelected(sensor, $event)"
+            (click)="$event.stopPropagation()"
+            [checked]="isSelected(sensor)"
+          >
+          <label
+            attr.for="{{ sensor.config.getName() }}"
+            (click)="$event.stopPropagation()"
+          ></label>
+        </div>
       </td>
-      <td><input id="{{ sensor.sensorName }}" class="fontawesome-checkbox" type="checkbox" name="{{sensor.sensorName}}" (click)="onRowSelected(sensor, $event)"><label attr.for="{{ sensor.sensorName }}"></label></td>
     </tr>
     </tbody>
   </table>
diff --git a/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.spec.ts b/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.spec.ts
index eb4dbff..755daa5 100644
--- a/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.spec.ts
+++ b/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.spec.ts
@@ -22,7 +22,7 @@ import { HttpClient, HttpResponse } from '@angular/common/http';
 import { DebugElement, Inject } from '@angular/core';
 import { By } from '@angular/platform-browser';
 import { Router, NavigationStart } from '@angular/router';
-import { Observable } from 'rxjs';
+import { Observable, of } from 'rxjs';
 import { SensorParserListComponent } from './sensor-parser-list.component';
 import { SensorParserConfigService } from '../../service/sensor-parser-config.service';
 import { MetronAlerts } from '../../shared/metron-alerts';
@@ -150,12 +150,61 @@ describe('Component: SensorParserList', () => {
   let metronAlerts: MetronAlerts;
   let metronDialog: MetronDialogBox;
   let dialogEl: DebugElement;
+  let sensors = [
+    {
+      config: new ParserConfigModel('TestConfigId01'),
+      status: {
+        status: 'KILLED'
+      },
+      isGroup: false,
+      isDeleted: false,
+    },
+    {
+      config: new ParserConfigModel('TestConfigId02'),
+      status: {
+        status: 'INACTIVE'
+      },
+      isGroup: false,
+      isDeleted: false,
+    },
+    {
+      config: new ParserConfigModel('TestConfigId03'),
+      status: {
+        status: 'ACTIVE'
+      },
+      isGroup: false,
+      isDeleted: false,
+    },
+    {
+      config: new ParserConfigModel('TestConfigId04'),
+      status: {
+        status: 'ACTIVE'
+      },
+      isDeleted: true
+    },
+    {
+      config: new ParserConfigModel('TestConfigId05'),
+      status: {
+        status: 'ACTIVE'
+      },
+      isPhantom: true,
+      isDeleted: false,
+    }
+  ];
 
   beforeEach(async(() => {
     TestBed.configureTestingModule({
       imports: [SensorParserListModule],
       providers: [
         { provide: HttpClient },
+        { provide: Store, useValue: {
+          pipe: () => {
+            return of(sensors)
+          },
+          dispatch: () => {
+
+          }
+        } },
         { provide: Location, useClass: SpyLocation },
         { provide: AuthenticationService, useClass: MockAuthenticationService },
         {
@@ -639,135 +688,138 @@ describe('Component: SensorParserList', () => {
     })
   );
 
-  it('sort', async(() => {
-    let component: SensorParserListComponent = fixture.componentInstance;
+  it('isStoppable() should return true unless a sensor is KILLED', async(() => {
+    const component = Object.create( SensorParserListComponent.prototype );
+    const sensorParserConfig1 = new ParserConfigModel('TestConfigId01');
+    let sensor: ParserMetaInfoModel = { config: sensorParserConfig1, status: new TopologyStatus() };
 
-    component.sensors = [
-      Object.assign(new SensorParserConfigHistory(), {
-        sensorName: 'abc',
-        config: {
-          parserClassName: 'org.apache.metron.parsers.GrokParser',
-          sensorTopic: 'abc'
-        },
-        createdBy: 'raghu',
-        modifiedBy: 'abc',
-        createdDate: '2016-11-25 09:09:12',
-        modifiedByDate: '2016-11-25 09:09:12'
-      }),
-      Object.assign(new SensorParserConfigHistory(), {
-        sensorName: 'plm',
-        config: {
-          parserClassName: 'org.apache.metron.parsers.Bro',
-          sensorTopic: 'plm'
-        },
-        createdBy: 'raghu',
-        modifiedBy: 'plm',
-        createdDate: '2016-11-25 12:39:21',
-        modifiedByDate: '2016-11-25 12:39:21'
-      }),
-      Object.assign(new SensorParserConfigHistory(), {
-        sensorName: 'xyz',
-        config: {
-          parserClassName: 'org.apache.metron.parsers.GrokParser',
-          sensorTopic: 'xyz'
-        },
-        createdBy: 'raghu',
-        modifiedBy: 'xyz',
-        createdDate: '2016-11-25 12:44:03',
-        modifiedByDate: '2016-11-25 12:44:03'
-      })
-    ];
-
-    component.onSort({ sortBy: 'sensorName', sortOrder: Sort.ASC });
-    expect(component.sensors[0].sensorName).toEqual('abc');
-    expect(component.sensors[1].sensorName).toEqual('plm');
-    expect(component.sensors[2].sensorName).toEqual('xyz');
-
-    component.onSort({ sortBy: 'sensorName', sortOrder: Sort.DSC });
-    expect(component.sensors[0].sensorName).toEqual('xyz');
-    expect(component.sensors[1].sensorName).toEqual('plm');
-    expect(component.sensors[2].sensorName).toEqual('abc');
-
-    component.onSort({ sortBy: 'parserClassName', sortOrder: Sort.ASC });
-    expect(component.sensors[0].config.parserClassName).toEqual(
-      'org.apache.metron.parsers.Bro'
-    );
-    expect(component.sensors[1].config.parserClassName).toEqual(
-      'org.apache.metron.parsers.GrokParser'
-    );
-    expect(component.sensors[2].config.parserClassName).toEqual(
-      'org.apache.metron.parsers.GrokParser'
-    );
+    sensor.status.status = 'KILLED';
+    expect(component.isStoppable(sensor)).toBe(false);
 
-    component.onSort({ sortBy: 'parserClassName', sortOrder: Sort.DSC });
-    expect(component.sensors[0].config.parserClassName).toEqual(
-      'org.apache.metron.parsers.GrokParser'
-    );
-    expect(component.sensors[1].config.parserClassName).toEqual(
-      'org.apache.metron.parsers.GrokParser'
-    );
-    expect(component.sensors[2].config.parserClassName).toEqual(
-      'org.apache.metron.parsers.Bro'
-    );
+    sensor.status.status = 'ACTIVE';
+    expect(component.isStoppable(sensor)).toBe(true);
+
+    sensor.status.status = 'INACTIVE';
+    expect(component.isStoppable(sensor)).toBe(true);
+  }));
 
-    component.onSort({ sortBy: 'modifiedBy', sortOrder: Sort.ASC });
-    expect(component.sensors[0].modifiedBy).toEqual('abc');
-    expect(component.sensors[1].modifiedBy).toEqual('plm');
-    expect(component.sensors[2].modifiedBy).toEqual('xyz');
+  it('isStartable() should return true only when a parser is KILLED', async(() => {
+    const component = Object.create( SensorParserListComponent.prototype );
+    const sensorParserConfig1 = new ParserConfigModel('TestConfigId01');
+    let sensor: ParserMetaInfoModel = { config: sensorParserConfig1, status: new TopologyStatus() };
 
-    component.onSort({ sortBy: 'modifiedBy', sortOrder: Sort.DSC });
-    expect(component.sensors[0].modifiedBy).toEqual('xyz');
-    expect(component.sensors[1].modifiedBy).toEqual('plm');
-    expect(component.sensors[2].modifiedBy).toEqual('abc');
+    sensor.status.status = 'KILLED';
+    expect(component.isStartable(sensor)).toBe(true);
+
+    sensor.status.status = 'ACTIVE';
+    expect(component.isStartable(sensor)).toBe(false);
+
+    sensor.status.status = 'INACTIVE';
+    expect(component.isStartable(sensor)).toBe(false);
   }));
 
-  it('sort', async(() => {
-    let component: SensorParserListComponent = fixture.componentInstance;
+  it('isEnableable() should return true only when a parser is ACTIVE', async(() => {
+    const component = Object.create( SensorParserListComponent.prototype );
+    const sensorParserConfig1 = new ParserConfigModel('TestConfigId01');
+    let sensor: ParserMetaInfoModel = { config: sensorParserConfig1, status: new TopologyStatus() };
 
-    component.sensors = [
-      Object.assign(new SensorParserConfigHistory(), {
-        sensorName: 'abc',
-        config: {
-          parserClassName: 'org.apache.metron.parsers.GrokParser',
-          sensorTopic: 'abc'
-        },
-        createdBy: 'raghu',
-        modifiedBy: 'abc',
-        createdDate: '2016-11-25 09:09:12',
-        modifiedByDate: '2016-11-25 09:09:12'
-      })
-    ];
-
-    component.sensorsStatus = [
-      Object.assign(new TopologyStatus(), {
-        name: 'abc',
-        status: 'ACTIVE',
-        latency: '10',
-        throughput: '23'
-      })
-    ];
-
-    component.updateSensorStatus();
-    expect(component.sensors[0]['status']).toEqual('Running');
-    expect(component.sensors[0]['latency']).toEqual('10ms');
-    expect(component.sensors[0]['throughput']).toEqual('23kb/s');
-
-    component.sensorsStatus[0].status = 'KILLED';
-    component.updateSensorStatus();
-    expect(component.sensors[0]['status']).toEqual('Stopped');
-    expect(component.sensors[0]['latency']).toEqual('-');
-    expect(component.sensors[0]['throughput']).toEqual('-');
-
-    component.sensorsStatus[0].status = 'INACTIVE';
-    component.updateSensorStatus();
-    expect(component.sensors[0]['status']).toEqual('Disabled');
-    expect(component.sensors[0]['latency']).toEqual('-');
-    expect(component.sensors[0]['throughput']).toEqual('-');
-
-    component.sensorsStatus = [];
-    component.updateSensorStatus();
-    expect(component.sensors[0]['status']).toEqual('Stopped');
-    expect(component.sensors[0]['latency']).toEqual('-');
-    expect(component.sensors[0]['throughput']).toEqual('-');
+    sensor.status.status = 'KILLED';
+    expect(component.isEnableable(sensor)).toBe(false);
+
+    sensor.status.status = 'ACTIVE';
+    expect(component.isEnableable(sensor)).toBe(false);
+
+    sensor.status.status = 'INACTIVE';
+    expect(component.isEnableable(sensor)).toBe(true);
+  }));
+
+  it('isDisableable() should return true only when a parser is INACTIVE', async(() => {
+    const component = Object.create( SensorParserListComponent.prototype );
+    const sensorParserConfig1 = new ParserConfigModel('TestConfigId01');
+    let sensor: ParserMetaInfoModel = { config: sensorParserConfig1, status: new TopologyStatus() };
+
+    sensor.status.status = 'KILLED';
+    expect(component.isDisableable(sensor)).toBe(false);
+
+    sensor.status.status = 'ACTIVE';
+    expect(component.isDisableable(sensor)).toBe(true);
+
+    sensor.status.status = 'INACTIVE';
+    expect(component.isDisableable(sensor)).toBe(false);
+  }));
+
+  it('isDeletedOrPhantom() should return true if a parser is deleted or a phantom', async(() => {
+    const component = Object.create( SensorParserListComponent.prototype );
+    const sensorParserConfig1 = new ParserConfigModel('TestConfigId01');
+    let sensor: ParserMetaInfoModel = { config: sensorParserConfig1, status: new TopologyStatus() };
+
+    expect(component.isDeletedOrPhantom(sensor)).toBe(false);
+
+    sensor.isDeleted = true;
+    expect(component.isDeletedOrPhantom(sensor)).toBe(true);
+
+    sensor.isDeleted = false;
+    sensor.isPhantom = true;
+    expect(component.isDeletedOrPhantom(sensor)).toBe(true);
+  }));
+
+  it('should hide parser controls when they cannot be used', async(() => {
+    fixture.detectChanges();
+
+    const stopButtons = fixture.debugElement.queryAll(By.css('[data-qe-id="stop-parser-button"]'));
+    const startButtons = fixture.debugElement.queryAll(By.css('[data-qe-id="start-parser-button"]'));
+    const enableButtons = fixture.debugElement.queryAll(By.css('[data-qe-id="enable-parser-button"]'));
+    const disableButtons = fixture.debugElement.queryAll(By.css('[data-qe-id="disable-parser-button"]'));
+    const selectWrappers = fixture.debugElement.queryAll(By.css('[data-qe-id="sensor-select"]'));
+    const editButtons = fixture.debugElement.queryAll(By.css('[data-qe-id="edit-parser-button"]'));
+    const deleteButtons = fixture.debugElement.queryAll(By.css('[data-qe-id="delete-parser-button"]'));
+
+    // !KILLED status should show stop button
+    expect(stopButtons[0].properties.hidden).toBe(true);
+    expect(stopButtons[1].properties.hidden).toBe(false);
+    expect(stopButtons[2].properties.hidden).toBe(false);
+    expect(stopButtons[3].properties.hidden).toBe(true);
+    expect(stopButtons[4].properties.hidden).toBe(true);
+
+    // KILLED status should only show start button
+    expect(startButtons[0].properties.hidden).toBe(false);
+    expect(startButtons[1].properties.hidden).toBe(true);
+    expect(startButtons[2].properties.hidden).toBe(true);
+    expect(startButtons[3].properties.hidden).toBe(true);
+    expect(startButtons[4].properties.hidden).toBe(true);
+
+    // ACTIVE status should hide enable buttons
+    expect(enableButtons[0].properties.hidden).toBe(true);
+    expect(enableButtons[1].properties.hidden).toBe(false);
+    expect(enableButtons[2].properties.hidden).toBe(true);
+    expect(enableButtons[3].properties.hidden).toBe(true);
+    expect(enableButtons[4].properties.hidden).toBe(true);
+
+    // INACTIVE status should hide disable buttons
+    expect(disableButtons[0].properties.hidden).toBe(true);
+    expect(disableButtons[1].properties.hidden).toBe(true);
+    expect(disableButtons[2].properties.hidden).toBe(false);
+    expect(disableButtons[3].properties.hidden).toBe(true);
+    expect(disableButtons[4].properties.hidden).toBe(true);
+
+    // Edit button should hide if a parser or group is deleted
+    expect(editButtons[0].properties.hidden).toBe(false);
+    expect(editButtons[1].properties.hidden).toBe(false);
+    expect(editButtons[2].properties.hidden).toBe(false);
+    expect(editButtons[3].properties.hidden).toBe(true);
+    expect(editButtons[4].properties.hidden).toBe(false);
+
+    expect(deleteButtons[0].properties.hidden).toBe(false);
+    expect(deleteButtons[1].properties.hidden).toBe(false);
+    expect(deleteButtons[2].properties.hidden).toBe(false);
+    expect(deleteButtons[3].properties.hidden).toBe(true);
+    expect(deleteButtons[4].properties.hidden).toBe(false);
+
+    // select checkbox should hide if parser is deleted or a phantom
+    expect(selectWrappers[0].properties.hidden).toBe(false);
+    expect(selectWrappers[1].properties.hidden).toBe(false);
+    expect(selectWrappers[2].properties.hidden).toBe(false);
+    expect(selectWrappers[3].properties.hidden).toBe(true);
+    expect(selectWrappers[4].properties.hidden).toBe(true);
   }));
 });
diff --git a/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.ts b/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.ts
index 2694ab4..fd32cc8 100644
--- a/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.ts
+++ b/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.ts
@@ -316,6 +316,9 @@ export class SensorParserListComponent implements OnInit {
         this.onDisableSensor(sensor, null);
       }
     }
+
+  isSelected(sensor: ParserMetaInfoModel) {
+    return this.selectedSensors.includes(sensor.config.getName());
   }
 
   onDisableSensor(sensor: SensorParserConfigHistory, event) {
@@ -330,17 +333,36 @@ export class SensorParserListComponent implements OnInit {
         this.toggleStartStopInProgress(sensor);
       });
 
-    if (event !== null) {
-      event.stopPropagation();
-    }
+  isStoppable(sensor: ParserMetaInfoModel) {
+    return sensor.status.status && sensor.status.status !== 'KILLED'
+      && !sensor.startStopInProgress
+      && this.isRootElement(sensor)
+      && !this.isDeletedOrPhantom(sensor);
   }
 
-  onEnableSensors() {
-    for (let sensor of this.selectedSensors) {
-      if (sensor['status'] === 'Disabled') {
-        this.onEnableSensor(sensor, null);
-      }
-    }
+  isStartable(sensor: ParserMetaInfoModel) {
+    return (!sensor.status.status || sensor.status.status === 'KILLED')
+      && !sensor.startStopInProgress
+      && this.isRootElement(sensor)
+      && !this.isDeletedOrPhantom(sensor);
+  }
+
+  isEnableable(sensor: ParserMetaInfoModel) {
+    return sensor.status.status === 'INACTIVE'
+      && !sensor.startStopInProgress
+      && this.isRootElement(sensor)
+      && !this.isDeletedOrPhantom(sensor);
+  }
+
+  isDisableable(sensor: ParserMetaInfoModel) {
+    return sensor.status.status === 'ACTIVE'
+      && !sensor.startStopInProgress
+      && this.isRootElement(sensor)
+      && !this.isDeletedOrPhantom(sensor);
+  }
+
+  isDeletedOrPhantom(sensor: ParserMetaInfoModel) {
+    return !!sensor.isDeleted || !!sensor.isPhantom;
   }
 
   onEnableSensor(sensor: SensorParserConfigHistory, event) {