]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mgr/dashboard: rbd: image list implementation
authorRicardo Dias <rdias@suse.com>
Fri, 16 Mar 2018 13:12:33 +0000 (13:12 +0000)
committerRicardo Dias <rdias@suse.com>
Fri, 13 Apr 2018 14:58:46 +0000 (15:58 +0100)
Signed-off-by: Ricardo Dias <rdias@suse.com>
src/pybind/mgr/dashboard/controllers/rbd.py
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/pool-detail/pool-detail.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/pool-detail/pool-detail.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/pool.service.ts
src/pybind/mgr/dashboard/services/ceph_service.py

index 5303fb4bfe830dea67a1a06f7b32e92cb5b93d31..974958ae7fa5cb0b09bcf76a4891d4d8d4edff0d 100644 (file)
@@ -7,6 +7,7 @@ import rbd
 
 from . import ApiController, AuthRequired, RESTController
 from .. import mgr
+from ..services.ceph_service import CephService
 from ..tools import ViewCache
 
 
@@ -26,10 +27,6 @@ class Rbd(RESTController):
         rbd.RBD_FEATURE_OPERATIONS: "operations",
     }
 
-    def __init__(self):
-        super(Rbd, self).__init__()
-        self.rbd = None
-
     @staticmethod
     def _format_bitmask(features):
         """
@@ -65,46 +62,99 @@ class Rbd(RESTController):
                 res = key | res
         return res
 
+    def _rbd_image(self, ioctx, pool_name, image_name):
+        img = rbd.Image(ioctx, image_name)
+        stat = img.stat()
+        stat['name'] = image_name
+        stat['id'] = img.id()
+        stat['pool_name'] = pool_name
+        features = img.features()
+        stat['features'] = features
+        stat['features_name'] = self._format_bitmask(features)
+
+        # the following keys are deprecated
+        del stat['parent_pool']
+        del stat['parent_name']
+
+        stat['timestamp'] = "{}Z".format(img.create_timestamp().isoformat())
+
+        stat['stripe_count'] = img.stripe_count()
+        stat['stripe_unit'] = img.stripe_unit()
+
+        data_pool_name = CephService.get_pool_name_from_id(img.data_pool_id())
+        if data_pool_name == pool_name:
+            data_pool_name = None
+        stat['data_pool'] = data_pool_name
+
+        try:
+            parent_info = img.parent_info()
+            stat['parent'] = {
+                'pool_name': parent_info[0],
+                'image_name': parent_info[1],
+                'snap_name': parent_info[2]
+            }
+        except rbd.ImageNotFound:
+            # no parent image
+            stat['parent'] = None
+
+        # snapshots
+        stat['snapshots'] = []
+        for snap in img.list_snaps():
+            snap['timestamp'] = "{}Z".format(img.get_snap_timestamp(snap['id']).isoformat())
+            snap['is_protected'] = img.is_protected_snap(snap['name'])
+            snap['children'] = []
+            img.set_snap(snap['name'])
+            for child_pool_name, child_image_name in img.list_children():
+                snap['children'].append({
+                    'pool_name': child_pool_name,
+                    'image_name': child_image_name
+                })
+            stat['snapshots'].append(snap)
+
+        return stat
+
     @ViewCache()
-    def _rbd_list(self, pool_name):
+    def _rbd_pool_list(self, pool_name):
+        rbd_inst = rbd.RBD()
         ioctx = mgr.rados.open_ioctx(pool_name)
-        self.rbd = rbd.RBD()
-        names = self.rbd.list(ioctx)
+        names = rbd_inst.list(ioctx)
         result = []
         for name in names:
-            i = rbd.Image(ioctx, name)
-            stat = i.stat()
-            stat['name'] = name
-            stat['id'] = i.id()
-            features = i.features()
-            stat['features'] = features
-            stat['features_name'] = self._format_bitmask(features)
-
-            # the following keys are deprecated
-            del stat['parent_pool']
-            del stat['parent_name']
-
             try:
-                parent_info = i.parent_info()
-                parent = "{}@{}".format(parent_info[0], parent_info[1])
-                if parent_info[0] != pool_name:
-                    parent = "{}/{}".format(parent_info[0], parent)
-                stat['parent'] = parent
+                stat = self._rbd_image(ioctx, pool_name, name)
             except rbd.ImageNotFound:
-                pass
+                # may have been removed in the meanwhile
+                continue
             result.append(stat)
         return result
 
-    def get(self, pool_name):
+    def _rbd_list(self, pool_name=None):
+        if pool_name:
+            pools = [pool_name]
+        else:
+            pools = [p['pool_name'] for p in CephService.get_pool_list('rbd')]
+
+        result = []
+        for pool in pools:
+            # pylint: disable=unbalanced-tuple-unpacking
+            status, value = self._rbd_pool_list(pool)
+            result.append({'status': status, 'value': value, 'pool_name': pool})
+        return result
+
+    def list(self, pool_name=None):
         # pylint: disable=unbalanced-tuple-unpacking
-        status, value = self._rbd_list(pool_name)
-        if status == ViewCache.VALUE_EXCEPTION:
-            raise value
-        return {'status': status, 'value': value}
+        return self._rbd_list(pool_name)
+
+    def get(self, pool_name, image_name):
+        ioctx = mgr.rados.open_ioctx(pool_name)
+        try:
+            return self._rbd_image(ioctx, pool_name, image_name)
+        except rbd.ImageNotFound:
+            raise cherrypy.HTTPError(404)
 
     def create(self, data):
-        if not self.rbd:
-            self.rbd = rbd.RBD()
+        # pylint: disable=too-many-locals
+        rbd_inst = rbd.RBD()
 
         # Get input values
         name = data.get('name')
@@ -127,7 +177,7 @@ class Rbd(RESTController):
         ioctx = mgr.rados.open_ioctx(pool_name)
 
         try:
-            self.rbd.create(ioctx, name, size, order=order, old_format=False,
+            rbd_inst.create(ioctx, name, size, order=order, old_format=False,
                             features=feature_bitmask, stripe_unit=stripe_unit,
                             stripe_count=stripe_count, data_pool=data_pool)
         except rbd.OSError as e:
index 1bdd5a22fbe70b79aac99018c26c42ef9140d2b0..5a1b0bb5ebcfb0d83ddd79341319ef55aa697b16 100644 (file)
@@ -16,3 +16,9 @@
           [columns]="columns"
           (fetchData)="loadImages()">
 </cd-table>
+
+<ng-template #parentTpl
+             let-value="value">
+  <span *ngIf="value">{{ value.pool_name }}/{{ value.image_name }}@{{ value.snap_name }}</span>
+  <span *ngIf="!value">-</span>
+</ng-template>
index 56480770761bac96274a60da3084751dcc8f8923..018758a3a32b8ce0a35c98d3cdfd1d809a6286db 100644 (file)
@@ -1,4 +1,4 @@
-import { Component, OnDestroy, OnInit } from '@angular/core';
+import { Component, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
 import { ActivatedRoute } from '@angular/router';
 
 import { PoolService } from '../../../shared/api/pool.service';
@@ -13,6 +13,8 @@ import { DimlessPipe } from '../../../shared/pipes/dimless.pipe';
   styleUrls: ['./pool-detail.component.scss']
 })
 export class PoolDetailComponent implements OnInit, OnDestroy {
+  @ViewChild('parentTpl') parentTpl: TemplateRef<any>;
+
   name: string;
   images: any;
   columns: CdTableColumn[];
@@ -30,6 +32,7 @@ export class PoolDetailComponent implements OnInit, OnDestroy {
       {
         name: 'Name',
         prop: 'name',
+        cellTemplate: this.parentTpl,
         flexGrow: 2
       },
       {
@@ -61,6 +64,7 @@ export class PoolDetailComponent implements OnInit, OnDestroy {
       {
         name: 'Parent',
         prop: 'parent',
+        cellTemplate: this.parentTpl,
         flexGrow: 2
       }
     ];
@@ -81,8 +85,8 @@ export class PoolDetailComponent implements OnInit, OnDestroy {
   loadImages() {
     this.poolService.rbdPoolImages(this.name).then(
       resp => {
-        this.viewCacheStatus = resp.status;
-        this.images = resp.value;
+        this.viewCacheStatus = resp[0].status;
+        this.images = resp[0].value;
       },
       () => {
         this.viewCacheStatus = ViewCacheStatus.ValueException;
index 8ac6de9d5b4e3ae4f0a6ce7b9287fddf55dc26fc..e6a996595f055ff781195627078fa1102f1cf449 100644 (file)
@@ -8,7 +8,7 @@ export class PoolService {
   }
 
   rbdPoolImages(pool) {
-    return this.http.get(`api/rbd/${pool}`).toPromise().then((resp: any) => {
+    return this.http.get(`api/rbd?pool_name=${pool}`).toPromise().then((resp: any) => {
       return resp;
     });
   }
index 7c1d9928a30732ad03ffb0cb081456226e426c6f..6e91165a3b9134068d4b03962940cb2a79de722b 100644 (file)
@@ -113,6 +113,14 @@ class CephService(object):
             pools_w_stats.append(pool)
         return pools_w_stats
 
+    @classmethod
+    def get_pool_name_from_id(cls, pool_id):
+        pool_list = cls.get_pool_list()
+        for pool in pool_list:
+            if pool['pool'] == pool_id:
+                return pool['pool_name']
+        return None
+
     @classmethod
     def send_command(cls, srv_type, prefix, srv_spec='', **kwargs):
         """