From: Ricardo Dias Date: Fri, 16 Mar 2018 13:12:33 +0000 (+0000) Subject: mgr/dashboard: rbd: image list implementation X-Git-Tag: v13.1.0~234^2~29 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=f4a6863759734505acef0be7b20f028a08f8f768;p=ceph-ci.git mgr/dashboard: rbd: image list implementation Signed-off-by: Ricardo Dias --- diff --git a/src/pybind/mgr/dashboard/controllers/rbd.py b/src/pybind/mgr/dashboard/controllers/rbd.py index 5303fb4bfe8..974958ae7fa 100644 --- a/src/pybind/mgr/dashboard/controllers/rbd.py +++ b/src/pybind/mgr/dashboard/controllers/rbd.py @@ -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: diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/pool-detail/pool-detail.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/pool-detail/pool-detail.component.html index 1bdd5a22fbe..5a1b0bb5ebc 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/pool-detail/pool-detail.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/pool-detail/pool-detail.component.html @@ -16,3 +16,9 @@ [columns]="columns" (fetchData)="loadImages()"> + + + {{ value.pool_name }}/{{ value.image_name }}@{{ value.snap_name }} + - + diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/pool-detail/pool-detail.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/pool-detail/pool-detail.component.ts index 56480770761..018758a3a32 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/pool-detail/pool-detail.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/pool-detail/pool-detail.component.ts @@ -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; + 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; diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/pool.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/pool.service.ts index 8ac6de9d5b4..e6a996595f0 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/pool.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/pool.service.ts @@ -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; }); } diff --git a/src/pybind/mgr/dashboard/services/ceph_service.py b/src/pybind/mgr/dashboard/services/ceph_service.py index 7c1d9928a30..6e91165a3b9 100644 --- a/src/pybind/mgr/dashboard/services/ceph_service.py +++ b/src/pybind/mgr/dashboard/services/ceph_service.py @@ -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): """