]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mgr/dashboard: Improve iSCSI overview page
authorRicardo Marques <rimarques@suse.com>
Thu, 28 Mar 2019 22:49:44 +0000 (22:49 +0000)
committerRicardo Marques <rimarques@suse.com>
Thu, 4 Apr 2019 19:15:03 +0000 (20:15 +0100)
iSCSI overview page will now use information
obtained from ceph-iscsi.

Fixes: https://tracker.ceph.com/issues/39024
Signed-off-by: Ricardo Marques <rimarques@suse.com>
12 files changed:
src/pybind/mgr/dashboard/controllers/health.py
src/pybind/mgr/dashboard/controllers/iscsi.py
src/pybind/mgr/dashboard/controllers/tcmu_iscsi.py [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi/iscsi.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi/iscsi.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi/iscsi.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/iscsi.service.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/tcmu-iscsi.service.spec.ts [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/shared/api/tcmu-iscsi.service.ts [deleted file]
src/pybind/mgr/dashboard/frontend/src/locale/messages.xlf
src/pybind/mgr/dashboard/services/tcmu_service.py
src/pybind/mgr/dashboard/tests/test_tcmu_iscsi.py [deleted file]

index e3e3fc5939a9ab06ec10f0b913097c59e1e267c3..eaff5be7173ef4fc8961c51cbe30bf35e7ce398f 100644 (file)
@@ -8,7 +8,7 @@ from . import ApiController, Endpoint, BaseController
 from .. import mgr
 from ..security import Permission, Scope
 from ..services.ceph_service import CephService
-from ..services.tcmu_service import TcmuService
+from ..services.iscsi_cli import IscsiGatewaysConfig
 
 
 class HealthData(object):
@@ -123,7 +123,8 @@ class HealthData(object):
         return len(mgr.list_servers())
 
     def iscsi_daemons(self):
-        return TcmuService.get_iscsi_daemons_amount()
+        gateways = IscsiGatewaysConfig.get_gateways_config()['gateways']
+        return len(gateways) if gateways else 0
 
     def mgr_map(self):
         mgr_map = mgr.get('mgr_map')
index 5990e64c1184c61f921a035b745bcb6f6174d391..36a0823b75721ce4235cc51aad801c1726fe2b04 100644 (file)
@@ -17,6 +17,7 @@ from ..security import Scope
 from ..services.iscsi_client import IscsiClient
 from ..services.iscsi_cli import IscsiGatewaysConfig
 from ..services.rbd import format_bitmask
+from ..services.tcmu_service import TcmuService
 from ..exceptions import DashboardException
 from ..tools import TaskManager
 
@@ -71,6 +72,66 @@ class IscsiUi(BaseController):
             portals.append({'name': name, 'ip_addresses': ip_addresses['data']})
         return sorted(portals, key=lambda p: '{}.{}'.format(p['name'], p['ip_addresses']))
 
+    @Endpoint()
+    @ReadPermission
+    def overview(self):
+        result_gateways = []
+        result_images = []
+        gateways_names = IscsiGatewaysConfig.get_gateways_config()['gateways'].keys()
+        config = None
+        for gateway_name in gateways_names:
+            try:
+                config = IscsiClient.instance(gateway_name=gateway_name).get_config()
+                break
+            except RequestException:
+                pass
+
+        # Gateways info
+        for gateway_name in gateways_names:
+            gateway = {
+                'name': gateway_name,
+                'state': 'N/A',
+                'num_targets': 'N/A'
+            }
+            try:
+                IscsiClient.instance(gateway_name=gateway_name).ping()
+                gateway['state'] = 'up'
+            except RequestException:
+                gateway['state'] = 'down'
+            if config:
+                gateway['num_targets'] = len([target for _, target in config['targets'].items()
+                                              if gateway_name in target['portals']])
+            result_gateways.append(gateway)
+
+        # Images info
+        if config:
+            tcmu_info = TcmuService.get_iscsi_info()
+            for _, disk_config in config['disks'].items():
+                image = {
+                    'pool': disk_config['pool'],
+                    'image': disk_config['image'],
+                    'backstore': disk_config['backstore'],
+                    'optimized_since': None,
+                    'stats': None,
+                    'stats_history': None
+                }
+                tcmu_image_info = TcmuService.get_image_info(image['pool'],
+                                                             image['image'],
+                                                             tcmu_info)
+                if tcmu_image_info:
+                    if 'optimized_since' in tcmu_image_info:
+                        image['optimized_since'] = tcmu_image_info['optimized_since']
+                    if 'stats' in tcmu_image_info:
+                        image['stats'] = tcmu_image_info['stats']
+                    if 'stats_history' in tcmu_image_info:
+                        image['stats_history'] = tcmu_image_info['stats_history']
+                result_images.append(image)
+
+        return {
+            'gateways': sorted(result_gateways, key=lambda g: g['name']),
+            'images': sorted(result_images, key=lambda i: '{}/{}'.format(i['pool'], i['image']))
+        }
+
 
 @ApiController('/iscsi', Scope.ISCSI)
 class Iscsi(BaseController):
diff --git a/src/pybind/mgr/dashboard/controllers/tcmu_iscsi.py b/src/pybind/mgr/dashboard/controllers/tcmu_iscsi.py
deleted file mode 100644 (file)
index 1da816f..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import
-
-from . import ApiController, RESTController
-from ..security import Scope
-from ..services.tcmu_service import TcmuService
-
-
-@ApiController('/tcmuiscsi', Scope.ISCSI)
-class TcmuIscsi(RESTController):
-
-    def list(self):
-        return TcmuService.get_iscsi_info()
index 7cbfcbf6f37f311ba16196b1b7d6e2e0ba0f3dca..59604a2438302c3f3c40ad4f9eb5db663a374ee8 100644 (file)
@@ -1,12 +1,55 @@
 <cd-iscsi-tabs></cd-iscsi-tabs>
 
-<legend i18n>Daemons</legend>
-<cd-table [data]="daemons"
+<legend i18n>Gateways</legend>
+<cd-table [data]="gateways"
           (fetchData)="refresh()"
-          [columns]="daemonsColumns">
+          [columns]="gatewaysColumns">
 </cd-table>
 
 <legend i18n>Images</legend>
 <cd-table [data]="images"
           [columns]="imagesColumns">
 </cd-table>
+
+<ng-template #statusColorTpl
+             let-value="value">
+  <span class="label"
+        [ngClass]="{'label-success': 'up' == value, 'label-danger': 'down' == value}">{{ value }}</span>
+</ng-template>
+
+<ng-template #iscsiSparklineTpl
+             let-row="row"
+             let-value="value">
+  <span *ngIf="row.backstore === 'user:rbd'">
+    <cd-sparkline [data]="value"
+                  [isBinary]="row.cdIsBinary"></cd-sparkline>
+  </span>
+  <span *ngIf="row.backstore !== 'user:rbd'"
+        class="text-muted">
+    n/a
+  </span>
+</ng-template>
+
+<ng-template #iscsiPerSecondTpl
+             let-row="row"
+             let-value="value">
+  <span *ngIf="row.backstore === 'user:rbd'">
+    {{ value }} /s
+  </span>
+  <span *ngIf="row.backstore !== 'user:rbd'"
+        class="text-muted">
+    n/a
+  </span>
+</ng-template>
+
+<ng-template #iscsiRelativeDateTpl
+             let-row="row"
+             let-value="value">
+  <span *ngIf="row.backstore === 'user:rbd'">
+    {{ value | relativeDate }}
+  </span>
+  <span *ngIf="row.backstore !== 'user:rbd'"
+        class="text-muted">
+    n/a
+  </span>
+</ng-template>
index dcc1be8a1a0fb9b8179d127a297622527bad38b6..e9bae7f321395e29fe0fbed3daa8003356c07208 100644 (file)
@@ -4,7 +4,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
 import { of } from 'rxjs';
 
 import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper';
-import { TcmuIscsiService } from '../../../shared/api/tcmu-iscsi.service';
+import { IscsiService } from '../../../shared/api/iscsi.service';
 import { CephShortVersionPipe } from '../../../shared/pipes/ceph-short-version.pipe';
 import { DimlessPipe } from '../../../shared/pipes/dimless.pipe';
 import { ListPipe } from '../../../shared/pipes/list.pipe';
@@ -15,11 +15,11 @@ import { IscsiComponent } from './iscsi.component';
 describe('IscsiComponent', () => {
   let component: IscsiComponent;
   let fixture: ComponentFixture<IscsiComponent>;
-  let tcmuIscsiService: TcmuIscsiService;
+  let iscsiService: IscsiService;
   let tcmuiscsiData;
 
   const fakeService = {
-    tcmuiscsi: () => {
+    overview: () => {
       return new Promise(function() {
         return;
       });
@@ -36,7 +36,7 @@ describe('IscsiComponent', () => {
       FormatterService,
       RelativeDatePipe,
       ListPipe,
-      { provide: TcmuIscsiService, useValue: fakeService },
+      { provide: IscsiService, useValue: fakeService },
       i18nProviders
     ]
   });
@@ -44,12 +44,12 @@ describe('IscsiComponent', () => {
   beforeEach(() => {
     fixture = TestBed.createComponent(IscsiComponent);
     component = fixture.componentInstance;
-    tcmuIscsiService = TestBed.get(TcmuIscsiService);
+    iscsiService = TestBed.get(IscsiService);
     fixture.detectChanges();
     tcmuiscsiData = {
       images: []
     };
-    spyOn(tcmuIscsiService, 'tcmuiscsi').and.callFake(() => of(tcmuiscsiData));
+    spyOn(iscsiService, 'overview').and.callFake(() => of(tcmuiscsiData));
   });
 
   it('should create', () => {
index 0b9195ec417b5ef98b3fe5eedc5a25c4144812ae..fe64d02c385be6209f184ac143d985870f6454df 100644 (file)
@@ -1,12 +1,9 @@
-import { Component } from '@angular/core';
+import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
 
 import { I18n } from '@ngx-translate/i18n-polyfill';
 
-import { TcmuIscsiService } from '../../../shared/api/tcmu-iscsi.service';
-import { CellTemplate } from '../../../shared/enum/cell-template.enum';
-import { CephShortVersionPipe } from '../../../shared/pipes/ceph-short-version.pipe';
+import { IscsiService } from '../../../shared/api/iscsi.service';
 import { DimlessPipe } from '../../../shared/pipes/dimless.pipe';
-import { ListPipe } from '../../../shared/pipes/list.pipe';
 import { RelativeDatePipe } from '../../../shared/pipes/relative-date.pipe';
 
 @Component({
@@ -14,92 +11,91 @@ import { RelativeDatePipe } from '../../../shared/pipes/relative-date.pipe';
   templateUrl: './iscsi.component.html',
   styleUrls: ['./iscsi.component.scss']
 })
-export class IscsiComponent {
-  daemons = [];
-  daemonsColumns: any;
+export class IscsiComponent implements OnInit {
+  @ViewChild('statusColorTpl')
+  statusColorTpl: TemplateRef<any>;
+  @ViewChild('iscsiSparklineTpl')
+  iscsiSparklineTpl: TemplateRef<any>;
+  @ViewChild('iscsiPerSecondTpl')
+  iscsiPerSecondTpl: TemplateRef<any>;
+  @ViewChild('iscsiRelativeDateTpl')
+  iscsiRelativeDateTpl: TemplateRef<any>;
+
+  gateways = [];
+  gatewaysColumns: any;
   images = [];
   imagesColumns: any;
 
   constructor(
-    private tcmuIscsiService: TcmuIscsiService,
-    cephShortVersionPipe: CephShortVersionPipe,
-    dimlessPipe: DimlessPipe,
-    relativeDatePipe: RelativeDatePipe,
-    listPipe: ListPipe,
+    private iscsiService: IscsiService,
+    private dimlessPipe: DimlessPipe,
+    private relativeDatePipe: RelativeDatePipe,
     private i18n: I18n
-  ) {
-    this.daemonsColumns = [
-      {
-        name: this.i18n('Hostname'),
-        prop: 'server_hostname'
-      },
+  ) {}
+
+  ngOnInit() {
+    this.gatewaysColumns = [
       {
-        name: this.i18n('# Active/Optimized'),
-        prop: 'optimized_paths'
+        name: this.i18n('Name'),
+        prop: 'name'
       },
       {
-        name: this.i18n('# Active/Non-Optimized'),
-        prop: 'non_optimized_paths'
+        name: this.i18n('State'),
+        prop: 'state',
+        cellTemplate: this.statusColorTpl
       },
       {
-        name: this.i18n('Version'),
-        prop: 'version',
-        pipe: cephShortVersionPipe
+        name: this.i18n('# Targets'),
+        prop: 'num_targets'
       }
     ];
     this.imagesColumns = [
       {
         name: this.i18n('Pool'),
-        prop: 'pool_name'
+        prop: 'pool'
       },
       {
         name: this.i18n('Image'),
-        prop: 'name'
-      },
-      {
-        name: this.i18n('Active/Optimized'),
-        prop: 'optimized_paths',
-        pipe: listPipe
+        prop: 'image'
       },
       {
-        name: this.i18n('Active/Non-Optimized'),
-        prop: 'non_optimized_paths',
-        pipe: listPipe
+        name: this.i18n('Backstore'),
+        prop: 'backstore'
       },
       {
         name: this.i18n('Read Bytes'),
         prop: 'stats_history.rd_bytes',
-        cellTransformation: CellTemplate.sparkline
+        cellTemplate: this.iscsiSparklineTpl
       },
       {
         name: this.i18n('Write Bytes'),
         prop: 'stats_history.wr_bytes',
-        cellTransformation: CellTemplate.sparkline
+        cellTemplate: this.iscsiSparklineTpl
       },
       {
         name: this.i18n('Read Ops'),
         prop: 'stats.rd',
-        pipe: dimlessPipe,
-        cellTransformation: CellTemplate.perSecond
+        pipe: this.dimlessPipe,
+        cellTemplate: this.iscsiPerSecondTpl
       },
       {
         name: this.i18n('Write Ops'),
         prop: 'stats.wr',
-        pipe: dimlessPipe,
-        cellTransformation: CellTemplate.perSecond
+        pipe: this.dimlessPipe,
+        cellTemplate: this.iscsiPerSecondTpl
       },
       {
         name: this.i18n('A/O Since'),
         prop: 'optimized_since',
-        pipe: relativeDatePipe
+        cellTemplate: this.iscsiRelativeDateTpl
       }
     ];
   }
 
   refresh() {
-    this.tcmuIscsiService.tcmuiscsi().subscribe((resp: any) => {
-      this.daemons = resp.daemons;
-      this.images = resp.images;
+    this.iscsiService.overview().subscribe((overview: Array<any>) => {
+      this.gateways = overview['gateways'];
+      this.images = overview['images'];
       this.images.map((image) => {
         if (image.stats_history) {
           image.stats_history.rd_bytes = image.stats_history.rd_bytes.map((i) => i[1]);
index b829ea95dc644dde4ef70824021c0cbe4b87a85e..0c1ec456552420ba19a69cf56338e92ccdb45ef4 100644 (file)
@@ -101,4 +101,8 @@ export class IscsiService {
   updateDiscovery(auth) {
     return this.http.put(`api/iscsi/discoveryauth`, auth);
   }
+
+  overview() {
+    return this.http.get(`ui-api/iscsi/overview`);
+  }
 }
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/tcmu-iscsi.service.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/tcmu-iscsi.service.spec.ts
deleted file mode 100644 (file)
index 99cd424..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
-import { fakeAsync, TestBed, tick } from '@angular/core/testing';
-
-import { configureTestBed } from '../../../testing/unit-test-helper';
-import { TcmuIscsiService } from './tcmu-iscsi.service';
-
-describe('TcmuIscsiService', () => {
-  let service: TcmuIscsiService;
-  let httpTesting: HttpTestingController;
-
-  configureTestBed({
-    providers: [TcmuIscsiService],
-    imports: [HttpClientTestingModule]
-  });
-
-  beforeEach(() => {
-    service = TestBed.get(TcmuIscsiService);
-    httpTesting = TestBed.get(HttpTestingController);
-  });
-
-  afterEach(() => {
-    httpTesting.verify();
-  });
-
-  it('should be created', () => {
-    expect(service).toBeTruthy();
-  });
-
-  it('should call tcmuiscsi', fakeAsync(() => {
-    let result;
-    service.tcmuiscsi().subscribe((resp) => {
-      result = resp;
-    });
-    const req = httpTesting.expectOne('api/tcmuiscsi');
-    expect(req.request.method).toBe('GET');
-    req.flush(['foo']);
-    tick();
-    expect(result).toEqual(['foo']);
-  }));
-});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/tcmu-iscsi.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/tcmu-iscsi.service.ts
deleted file mode 100644 (file)
index 24a8e87..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-import { HttpClient } from '@angular/common/http';
-import { Injectable } from '@angular/core';
-
-import { ApiModule } from './api.module';
-
-@Injectable({
-  providedIn: ApiModule
-})
-export class TcmuIscsiService {
-  constructor(private http: HttpClient) {}
-
-  tcmuiscsi() {
-    return this.http.get('api/tcmuiscsi');
-  }
-}
index 54cc5273d6d4c71c17b8674bbefaa28a2424c131..4ffef753b6ba85afc85d38a0f987158b41ff54a1 100644 (file)
           <context context-type="sourcefile">app/ceph/block/mirroring/overview/overview.component.html</context>
           <context context-type="linenumber">5</context>
         </context-group>
-        <context-group purpose="location">
-          <context context-type="sourcefile">app/ceph/block/iscsi/iscsi.component.html</context>
-          <context context-type="linenumber">3</context>
-        </context-group>
       </trans-unit><trans-unit id="4d13a9cd5ed3dcee0eab22cb25198d43886942be" datatype="html">
         <source>Users</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/block/rbd-trash-move-modal/rbd-trash-move-modal.component.html</context>
           <context context-type="linenumber">40</context>
         </context-group>
+      </trans-unit><trans-unit id="8a9910cd114c1deb9af74f6f99b4173403965bf2" datatype="html">
+        <source>Gateways</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">app/ceph/block/iscsi/iscsi.component.html</context>
+          <context context-type="linenumber">3</context>
+        </context-group>
       </trans-unit><trans-unit id="e95ae009d0bdb45fcc656e8b65248cf7396080d5" datatype="html">
         <source>Overview</source>
         <context-group purpose="location">
           <context context-type="linenumber">1</context>
         </context-group>
       </trans-unit>
-      <trans-unit id="9a541ec1a4319fffc16ad3b3ab2c2b6d251a829d" datatype="html">
-        <source>Hostname</source>
+      <trans-unit id="873b72903b1858a9cd6c8967521030b4d7d1435b" datatype="html">
+        <source>State</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/ceph/block/iscsi/iscsi.component.ts</context>
           <context context-type="linenumber">1</context>
         </context-group>
         <context-group purpose="location">
-          <context context-type="sourcefile">src/app/ceph/block/mirroring/daemon-list/daemon-list.component.ts</context>
+          <context context-type="sourcefile">src/app/ceph/block/mirroring/image-list/image-list.component.ts</context>
           <context context-type="linenumber">1</context>
         </context-group>
         <context-group purpose="location">
-          <context context-type="sourcefile">src/app/ceph/cluster/hosts/hosts.component.ts</context>
+          <context context-type="sourcefile">src/app/ceph/block/mirroring/image-list/image-list.component.ts</context>
           <context context-type="linenumber">1</context>
         </context-group>
         <context-group purpose="location">
-          <context context-type="sourcefile">src/app/ceph/rgw/rgw-daemon-list/rgw-daemon-list.component.ts</context>
+          <context context-type="sourcefile">src/app/ceph/block/mirroring/image-list/image-list.component.ts</context>
           <context context-type="linenumber">1</context>
         </context-group>
-      </trans-unit>
-      <trans-unit id="32b73dd480452aed3c8df00055279a110b3e633c" datatype="html">
-        <source># Active/Optimized</source>
         <context-group purpose="location">
-          <context context-type="sourcefile">src/app/ceph/block/iscsi/iscsi.component.ts</context>
+          <context context-type="sourcefile">src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.ts</context>
           <context context-type="linenumber">1</context>
         </context-group>
-      </trans-unit>
-      <trans-unit id="26a3d69b872abf67663ba7473606ea32d6128531" datatype="html">
-        <source># Active/Non-Optimized</source>
         <context-group purpose="location">
-          <context context-type="sourcefile">src/app/ceph/block/iscsi/iscsi.component.ts</context>
+          <context context-type="sourcefile">src/app/ceph/cephfs/cephfs-detail/cephfs-detail.component.ts</context>
           <context context-type="linenumber">1</context>
         </context-group>
-      </trans-unit>
-      <trans-unit id="2cd86fc9c2dc1b4398514266a08e507140fe5ba8" datatype="html">
-        <source>Active/Optimized</source>
         <context-group purpose="location">
-          <context context-type="sourcefile">src/app/ceph/block/iscsi/iscsi.component.ts</context>
+          <context context-type="sourcefile">src/app/ceph/cluster/prometheus/prometheus-list/prometheus-list.component.ts</context>
           <context context-type="linenumber">1</context>
         </context-group>
       </trans-unit>
-      <trans-unit id="c74c467d73caf2b951bc1a1d52d6e93f9c5e795b" datatype="html">
-        <source>Active/Non-Optimized</source>
+      <trans-unit id="02b75464c0dc0cf49ea0208211b6fbcad3bf728a" datatype="html">
+        <source># Targets</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/ceph/block/iscsi/iscsi.component.ts</context>
           <context context-type="linenumber">1</context>
           <context context-type="linenumber">1</context>
         </context-group>
       </trans-unit>
-      <trans-unit id="36fad7995cadfb3a6f0d43ef2db280146b3ca022" datatype="html">
-        <source>Issue</source>
+      <trans-unit id="9a541ec1a4319fffc16ad3b3ab2c2b6d251a829d" datatype="html">
+        <source>Hostname</source>
         <context-group purpose="location">
-          <context context-type="sourcefile">src/app/ceph/block/mirroring/image-list/image-list.component.ts</context>
+          <context context-type="sourcefile">src/app/ceph/block/mirroring/daemon-list/daemon-list.component.ts</context>
           <context context-type="linenumber">1</context>
         </context-group>
-      </trans-unit>
-      <trans-unit id="873b72903b1858a9cd6c8967521030b4d7d1435b" datatype="html">
-        <source>State</source>
         <context-group purpose="location">
-          <context context-type="sourcefile">src/app/ceph/block/mirroring/image-list/image-list.component.ts</context>
+          <context context-type="sourcefile">src/app/ceph/cluster/hosts/hosts.component.ts</context>
           <context context-type="linenumber">1</context>
         </context-group>
         <context-group purpose="location">
-          <context context-type="sourcefile">src/app/ceph/block/mirroring/image-list/image-list.component.ts</context>
+          <context context-type="sourcefile">src/app/ceph/rgw/rgw-daemon-list/rgw-daemon-list.component.ts</context>
           <context context-type="linenumber">1</context>
         </context-group>
+      </trans-unit>
+      <trans-unit id="36fad7995cadfb3a6f0d43ef2db280146b3ca022" datatype="html">
+        <source>Issue</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/ceph/block/mirroring/image-list/image-list.component.ts</context>
           <context context-type="linenumber">1</context>
         </context-group>
-        <context-group purpose="location">
-          <context context-type="sourcefile">src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.ts</context>
-          <context context-type="linenumber">1</context>
-        </context-group>
-        <context-group purpose="location">
-          <context context-type="sourcefile">src/app/ceph/cephfs/cephfs-detail/cephfs-detail.component.ts</context>
-          <context context-type="linenumber">1</context>
-        </context-group>
-        <context-group purpose="location">
-          <context context-type="sourcefile">src/app/ceph/cluster/prometheus/prometheus-list/prometheus-list.component.ts</context>
-          <context context-type="linenumber">1</context>
-        </context-group>
       </trans-unit>
       <trans-unit id="15793f4cbc261bedbc60f7105533dde536a3f42b" datatype="html">
         <source>Progress</source>
index 02f6558580897412638f4a2ebd0ba474fe94dc59..d28a49543e9e0d0843230b59540c1de4bfbf6393 100644 (file)
@@ -86,13 +86,8 @@ class TcmuService(object):
         }
 
     @staticmethod
-    def get_iscsi_daemons_amount():
-        daemons = {}
-        for service in CephService.get_service_list(SERVICE_TYPE):
-            hostname = service['hostname']
-
-            daemon = daemons.get(hostname, None)
-            if daemon is None:
-                daemons[hostname] = True
-
-        return len(daemons)
+    def get_image_info(pool_name, image_name, get_iscsi_info):
+        for image in get_iscsi_info['images']:
+            if image['pool_name'] == pool_name and image['name'] == image_name:
+                return image
+        return None
diff --git a/src/pybind/mgr/dashboard/tests/test_tcmu_iscsi.py b/src/pybind/mgr/dashboard/tests/test_tcmu_iscsi.py
deleted file mode 100644 (file)
index cff68f1..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-from __future__ import absolute_import
-
-from . import ControllerTestCase
-from .. import mgr
-from ..controllers.tcmu_iscsi import TcmuIscsi
-
-mocked_servers = [{
-    'ceph_version': 'ceph version 13.0.0-5083- () mimic (dev)',
-    'hostname': 'ceph-dev1',
-    'services': [{'id': 'ceph-dev1:pool1/image1', 'type': 'tcmu-runner'}]
-}, {
-    'ceph_version': 'ceph version 13.0.0-5083- () mimic (dev)',
-    'hostname': 'ceph-dev2',
-    'services': [{'id': 'ceph-dev2:pool1/image1', 'type': 'tcmu-runner'}]
-}]
-
-mocked_metadata1 = {
-    'ceph_version': 'ceph version 13.0.0-5083- () mimic (dev)',
-    'pool_name': 'pool1',
-    'image_name': 'image1',
-    'image_id': '42',
-    'optimized_since': 1152121348,
-}
-
-mocked_metadata2 = {
-    'ceph_version': 'ceph version 13.0.0-5083- () mimic (dev)',
-    'pool_name': 'pool1',
-    'image_name': 'image1',
-    'image_id': '42',
-    'optimized_since': 0,
-}
-
-mocked_get_daemon_status = {
-    'lock_owner': 'true',
-}
-
-mocked_get_counter1 = {
-    'librbd-42-pool1-image1.lock_acquired_time': [[1152121348 * 1000000000,
-                                                   1152121348 * 1000000000]],
-    'librbd-42-pool1-image1.rd': [[0, 0], [1, 43]],
-    'librbd-42-pool1-image1.wr': [[0, 0], [1, 44]],
-    'librbd-42-pool1-image1.rd_bytes': [[0, 0], [1, 45]],
-    'librbd-42-pool1-image1.wr_bytes': [[0, 0], [1, 46]],
-}
-
-mocked_get_counter2 = {
-    'librbd-42-pool1-image1.lock_acquired_time': [[0, 0]],
-    'librbd-42-pool1-image1.rd': [],
-    'librbd-42-pool1-image1.wr': [],
-    'librbd-42-pool1-image1.rd_bytes': [],
-    'librbd-42-pool1-image1.wr_bytes': [],
-}
-
-
-def _get_counter(_daemon_type, daemon_name, _stat):
-    if daemon_name == 'ceph-dev1:pool1/image1':
-        return mocked_get_counter1
-    if daemon_name == 'ceph-dev2:pool1/image1':
-        return mocked_get_counter2
-    return Exception('invalid daemon name')
-
-
-class TcmuIscsiControllerTest(ControllerTestCase):
-
-    @classmethod
-    def setup_server(cls):
-        mgr.list_servers.return_value = mocked_servers
-        mgr.get_metadata.side_effect = [mocked_metadata1, mocked_metadata2]
-        mgr.get_daemon_status.return_value = mocked_get_daemon_status
-        mgr.get_counter.side_effect = _get_counter
-        mgr.url_prefix = ''
-        TcmuIscsi._cp_config['tools.authenticate.on'] = False  # pylint: disable=protected-access
-
-        cls.setup_controllers(TcmuIscsi, "/test")
-
-    def test_list(self):
-        self._get('/test/api/tcmuiscsi')
-        self.assertStatus(200)
-        self.assertJsonBody({
-            'daemons': [
-                {
-                    'server_hostname': 'ceph-dev1',
-                    'version': 'ceph version 13.0.0-5083- () mimic (dev)',
-                    'optimized_paths': 1, 'non_optimized_paths': 0},
-                {
-                    'server_hostname': 'ceph-dev2',
-                    'version': 'ceph version 13.0.0-5083- () mimic (dev)',
-                    'optimized_paths': 0, 'non_optimized_paths': 1}],
-            'images': [{
-                'device_id': 'pool1/image1',
-                'pool_name': 'pool1',
-                'name': 'image1',
-                'id': '42',
-                'optimized_paths': ['ceph-dev1'],
-                'non_optimized_paths': ['ceph-dev2'],
-                'optimized_daemon': 'ceph-dev1',
-                'optimized_since': 1152121348,
-                'stats': {
-                    'rd': 43.0,
-                    'wr': 44.0,
-                    'rd_bytes': 45.0,
-                    'wr_bytes': 46.0},
-                'stats_history': {
-                    'rd': [[1, 43]],
-                    'wr': [[1, 44]],
-                    'rd_bytes': [[1, 45]],
-                    'wr_bytes': [[1, 46]]}
-            }]
-        })