@RESTController.MethodMap(version=APIVersion(2, 0)) # type: ignore
def list(self, pool_name=None, offset: int = 0, limit: int = DEFAULT_LIMIT,
search: str = '', sort: str = ''):
- return self._rbd_list(pool_name, offset=offset, limit=limit, search=search, sort=sort)
+ return self._rbd_list(pool_name, offset=int(offset), limit=int(limit),
+ search=search, sort=sort)
@handle_rbd_error()
@handle_rados_error('pool')
+import { HttpHeaders } from '@angular/common/http';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
describe('ServiceDaemonListComponent', () => {
let component: ServiceDaemonListComponent;
let fixture: ComponentFixture<ServiceDaemonListComponent>;
+ let headers: HttpHeaders;
const daemons = [
{
of(getDaemonsByServiceName(component.serviceName))
);
- const paginate_obs = new PaginateObservable<any>(of(services));
+ headers = new HttpHeaders().set('X-Total-Count', '2');
+ const paginate_obs = new PaginateObservable<any>(of({ body: services, headers: headers }));
spyOn(cephServiceService, 'list').and.returnValue(paginate_obs);
context.pageInfo.offset = 0;
context.pageInfo.limit = -1;
+import { HttpHeaders } from '@angular/common/http';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
describe('ServicesComponent', () => {
let component: ServicesComponent;
let fixture: ComponentFixture<ServicesComponent>;
+ let headers: HttpHeaders;
const fakeAuthStorageService = {
getPermissions: () => {
component = fixture.componentInstance;
const orchService = TestBed.inject(OrchestratorService);
const cephServiceService = TestBed.inject(CephServiceService);
- const paginate_obs = new PaginateObservable<any>(of({ available: true }));
- spyOn(orchService, 'status').and.returnValue(paginate_obs);
- spyOn(cephServiceService, 'list').and.returnValue(of(services));
- fixture.detectChanges();
+ spyOn(orchService, 'status').and.returnValue(of({ available: true }));
+ headers = new HttpHeaders().set('X-Total-Count', '2');
+ const paginate_obs = new PaginateObservable<any>(of({ body: services, headers: headers }));
+
+ spyOn(cephServiceService, 'list').and.returnValue(paginate_obs);
});
it('should create', () => {
});
it('should return all services', () => {
- const context = new CdTableFetchDataContext(() => undefined)
+ const context = new CdTableFetchDataContext(() => undefined);
context.pageInfo.offset = 0;
- context.pageInfo.limit = -1
+ context.pageInfo.limit = -1;
component.getServices(context);
expect(component.services.length).toBe(2);
});
observable: Observable<Type>;
count: number;
- subscribe: any;
constructor(obs: Observable<Type>) {
this.observable = obs.pipe(
map((response: any) => {
}
toParams(): HttpParams {
- if (this.pageInfo.offset == NaN) {
+ if (Number.isNaN(this.pageInfo.offset)) {
this.pageInfo.offset = 0;
}
name: service_name
schema:
type: string
+ - default: 0
+ in: query
+ name: offset
+ schema:
+ type: integer
+ - default: 5
+ in: query
+ name: limit
+ schema:
+ type: integer
+ - default: ''
+ in: query
+ name: search
+ schema:
+ type: string
+ - default: +service_name
+ in: query
+ name: sort
+ schema:
+ type: string
responses:
'200':
content:
- application/vnd.ceph.api.v1.0+json:
+ application/vnd.ceph.api.v2.0+json:
type: object
description: OK
'400':
class ListPaginator:
+ # pylint: disable=W0102
def __init__(self, offset: int, limit: int, sort: str, search: str,
input_list: List[Any], default_sort: str,
searchable_params: List[str] = [], sortable_params: List[str] = []):
self.default_sort = default_sort
self.searchable_params = searchable_params
self.sortable_params = sortable_params
+ self.count = len(self.input_list)
def get_count(self):
- return len(self.input_list)
+ return self.count
def find_value(self, item: Dict[str, Any], key: str):
+ # dot separated keys to lookup nested values
keys = key.split('.')
value = item
- for key in keys:
- if key in value:
- value = value[key]
+ for nested_key in keys:
+ if nested_key in value:
+ value = value[nested_key]
else:
return ''
return value
if self.limit == -1:
end = len(self.input_list)
+ if not self.sort:
+ self.sort = self.default_sort
+
desc = self.sort[0] == '-'
sort_by = self.sort[1:]
+ if sort_by not in self.sortable_params:
+ sort_by = self.default_sort[1:]
+
# trim down by search
trimmed_list = []
if self.search:
else:
trimmed_list = self.input_list
- if sort_by not in self.sortable_params:
- sort_by = self.default_sort
-
def sort(item):
return self.find_value(item, sort_by)
- for item in sorted(trimmed_list, key=sort, reverse=desc)[self.offset:end]:
+ sorted_list = sorted(trimmed_list, key=sort, reverse=desc)
+ self.count = len(sorted_list)
+ for item in sorted_list[self.offset:end]:
yield item
import logging
from functools import wraps
-from typing import Any, Dict, List, Optional
+from typing import Any, Dict, List, Optional, Tuple
from ceph.deployment.service_spec import ServiceSpec
from orchestrator import DaemonDescription, DeviceLightLoc, HostSpec, \
service_type: Optional[str] = None,
service_name: Optional[str] = None,
offset: int = 0, limit: int = -1,
- sort: str = '+service_name', search: str = '') -> List[ServiceDescription]:
+ sort: str = '+service_name', search: str = '') -> Tuple[List[Dict[Any, Any]], int]:
services = self.api.describe_service(service_type, service_name)
services = [service.to_dict() for service in services.result]
paginator = ListPaginator(offset, limit, sort, search,
'status.last_refreshed', 'status.size'],
sortable_params=['service_name', 'status.running',
'status.last_refreshed', 'status.size'],
- default_sort='service_name')
+ default_sort='+service_name')
return list(paginator.list()), paginator.get_count()
@wait_api_result
from .. import mgr
from ..exceptions import DashboardException
from ..plugins.ttl_cache import ttl_cache
+from ._paginate import ListPaginator
from .ceph_service import CephService
try:
@classmethod
def rbd_pool_list(cls, pool_names: List[str], namespace: Optional[str] = None, offset: int = 0,
limit: int = 5, search: str = '', sort: str = ''):
- offset = int(offset)
- limit = int(limit)
- # let's use -1 to denotate we want ALL images for now. Iscsi currently gathers
- # all images therefore, we need this.
- if limit < -1:
- raise DashboardException(msg=f'Wrong limit value {limit}', code=400)
-
- refs = cls._rbd_pool_image_refs(pool_names, namespace)
- image_refs = []
- # transform to list so that we can count
- for ref in refs:
- if search in ref['name']:
- image_refs.append(ref)
- elif search in ref['pool_name']:
- image_refs.append(ref)
- elif search in ref['namespace']:
- image_refs.append(ref)
+ image_refs = cls._rbd_pool_image_refs(pool_names, namespace)
+ params = ['name', 'pool_name', 'namespace']
+ paginator = ListPaginator(offset, limit, sort, search, image_refs,
+ searchable_params=params, sortable_params=params,
+ default_sort='+name')
result = []
- end = offset + limit
- if len(sort) < 2:
- sort = '+name'
- descending = sort[0] == '-'
- sort_by = sort[1:]
- if sort_by not in ['name', 'pool_name', 'namespace']:
- sort_by = 'name'
- if limit == -1:
- end = len(image_refs)
- for image_ref in sorted(image_refs, key=lambda v: v[sort_by],
- reverse=descending)[offset:end]:
- ioctx = cls.get_ioctx(image_ref['pool_name'], namespace=image_ref['namespace'])
- try:
- stat = cls._rbd_image_stat(
- ioctx, image_ref['pool_name'], image_ref['namespace'], image_ref['name'])
- except rbd.ImageNotFound:
+ for image_ref in paginator.list():
+ with mgr.rados.open_ioctx(image_ref['pool_name']) as ioctx:
+ ioctx.set_namespace(image_ref['namespace'])
# Check if the RBD has been deleted partially. This happens for example if
# the deletion process of the RBD has been started and was interrupted.
+
try:
- stat = cls._rbd_image_stat_removing(
- ioctx, image_ref['pool_name'], image_ref['namespace'], image_ref['id'])
+ stat = cls._rbd_image_stat(
+ ioctx, image_ref['pool_name'], image_ref['namespace'], image_ref['name'])
except rbd.ImageNotFound:
- continue
- result.append(stat)
- return result, len(image_refs)
+ try:
+ stat = cls._rbd_image_stat_removing(
+ ioctx, image_ref['pool_name'], image_ref['namespace'], image_ref['id'])
+ except rbd.ImageNotFound:
+ continue
+ result.append(stat)
+ return result, paginator.get_count()
@classmethod
def get_image(cls, image_spec):