# -*- coding: utf-8 -*-
+import json
import logging
import os
from collections import defaultdict
+from typing import Any, Dict
import cephfs
import cherrypy
from ..services.ceph_service import CephService
from ..services.cephfs import CephFS as CephFS_
from ..services.exception import handle_cephfs_error
-from ..tools import ViewCache
-from . import APIDoc, APIRouter, EndpointDoc, RESTController, UIRouter, allow_empty_body
+from ..tools import ViewCache, str_to_bool
+from . import APIDoc, APIRouter, DeletePermission, Endpoint, EndpointDoc, \
+ RESTController, UIRouter, UpdatePermission, allow_empty_body
GET_QUOTAS_SCHEMA = {
'max_bytes': (int, ''),
logger = logging.getLogger("controllers.rgw")
+# pylint: disable=R0904
@APIRouter('/cephfs', Scope.CEPHFS)
@APIDoc("Cephfs Management API", "Cephfs")
class CephFS(RESTController):
fsmap = mgr.get("fs_map")
return fsmap['filesystems']
+ def create(self, name: str, service_spec: Dict[str, Any]):
+ service_spec_str = '1 '
+ if 'labels' in service_spec['placement']:
+ for label in service_spec['placement']['labels']:
+ service_spec_str += f'label:{label},'
+ service_spec_str = service_spec_str[:-1]
+ if 'hosts' in service_spec['placement']:
+ for host in service_spec['placement']['hosts']:
+ service_spec_str += f'{host},'
+ service_spec_str = service_spec_str[:-1]
+
+ error_code, _, err = mgr.remote('volumes', '_cmd_fs_volume_create', None,
+ {'name': name, 'placement': service_spec_str})
+ if error_code != 0:
+ raise RuntimeError(
+ f'Error creating volume {name} with placement {str(service_spec)}: {err}')
+ return f'Volume {name} created successfully'
+
+ @EndpointDoc("Remove CephFS Volume",
+ parameters={
+ 'name': (str, 'File System Name'),
+ })
+ @allow_empty_body
+ @Endpoint('DELETE')
+ @DeletePermission
+ def remove(self, name):
+ error_code, _, err = mgr.remote('volumes', '_cmd_fs_volume_rm', None,
+ {'vol_name': name,
+ 'yes-i-really-mean-it': "--yes-i-really-mean-it"})
+ if error_code != 0:
+ raise DashboardException(
+ msg=f'Error deleting volume {name}: {err}',
+ component='cephfs')
+ return f'Volume {name} removed successfully'
+
+ @EndpointDoc("Rename CephFS Volume",
+ parameters={
+ 'name': (str, 'Existing FS Name'),
+ 'new_name': (str, 'New FS Name'),
+ })
+ @allow_empty_body
+ @UpdatePermission
+ @Endpoint('PUT')
+ def rename(self, name: str, new_name: str):
+ error_code, _, err = mgr.remote('volumes', '_cmd_fs_volume_rename', None,
+ {'vol_name': name, 'new_vol_name': new_name,
+ 'yes_i_really_mean_it': True})
+ if error_code != 0:
+ raise DashboardException(
+ msg=f'Error renaming volume {name} to {new_name}: {err}',
+ component='cephfs')
+ return f'Volume {name} renamed successfully to {new_name}'
+
def get(self, fs_id):
fs_id = self.fs_id_to_int(fs_id)
return self.fs_status(fs_id)
except (cephfs.PermissionError, cephfs.ObjectNotFound): # pragma: no cover
paths = []
return paths
+
+
+@APIRouter('/cephfs/subvolume', Scope.CEPHFS)
+@APIDoc('CephFS Subvolume Management API', 'CephFSSubvolume')
+class CephFSSubvolume(RESTController):
+
+ def get(self, vol_name: str, group_name: str = ""):
+ params = {'vol_name': vol_name}
+ if group_name:
+ params['group_name'] = group_name
+ error_code, out, err = mgr.remote(
+ 'volumes', '_cmd_fs_subvolume_ls', None, params)
+ if error_code != 0:
+ raise DashboardException(
+ f'Failed to list subvolumes for volume {vol_name}: {err}'
+ )
+ subvolumes = json.loads(out)
+ for subvolume in subvolumes:
+ params['sub_name'] = subvolume['name']
+ error_code, out, err = mgr.remote('volumes', '_cmd_fs_subvolume_info', None,
+ params)
+ if error_code != 0:
+ raise DashboardException(
+ f'Failed to get info for subvolume {subvolume["name"]}: {err}'
+ )
+ subvolume['info'] = json.loads(out)
+ return subvolumes
+
+ @RESTController.Resource('GET')
+ def info(self, vol_name: str, subvol_name: str, group_name: str = ""):
+ params = {'vol_name': vol_name, 'sub_name': subvol_name}
+ if group_name:
+ params['group_name'] = group_name
+ error_code, out, err = mgr.remote('volumes', '_cmd_fs_subvolume_info', None,
+ params)
+ if error_code != 0:
+ raise DashboardException(
+ f'Failed to get info for subvolume {subvol_name}: {err}'
+ )
+ return json.loads(out)
+
+ def create(self, vol_name: str, subvol_name: str, **kwargs):
+ error_code, _, err = mgr.remote('volumes', '_cmd_fs_subvolume_create', None, {
+ 'vol_name': vol_name, 'sub_name': subvol_name, **kwargs})
+ if error_code != 0:
+ raise DashboardException(
+ f'Failed to create subvolume {subvol_name}: {err}'
+ )
+
+ return f'Subvolume {subvol_name} created successfully'
+
+ def set(self, vol_name: str, subvol_name: str, size: str, group_name: str = ""):
+ params = {'vol_name': vol_name, 'sub_name': subvol_name}
+ if size:
+ params['new_size'] = size
+ if group_name:
+ params['group_name'] = group_name
+ error_code, _, err = mgr.remote('volumes', '_cmd_fs_subvolume_resize', None,
+ params)
+ if error_code != 0:
+ raise DashboardException(
+ f'Failed to update subvolume {subvol_name}: {err}'
+ )
+
+ return f'Subvolume {subvol_name} updated successfully'
+
+ def delete(self, vol_name: str, subvol_name: str, group_name: str = "",
+ retain_snapshots: bool = False):
+ params = {'vol_name': vol_name, 'sub_name': subvol_name}
+ if group_name:
+ params['group_name'] = group_name
+ retain_snapshots = str_to_bool(retain_snapshots)
+ if retain_snapshots:
+ params['retain_snapshots'] = 'True'
+ error_code, _, err = mgr.remote(
+ 'volumes', '_cmd_fs_subvolume_rm', None, params)
+ if error_code != 0:
+ raise DashboardException(
+ msg=f'Failed to remove subvolume {subvol_name}: {err}',
+ component='cephfs')
+ return f'Subvolume {subvol_name} removed successfully'
+
+
+@APIRouter('/cephfs/subvolume/group', Scope.CEPHFS)
+@APIDoc("Cephfs Subvolume Group Management API", "CephfsSubvolumeGroup")
+class CephFSSubvolumeGroups(RESTController):
+
+ def get(self, vol_name):
+ if not vol_name:
+ raise DashboardException(
+ f'Error listing subvolume groups for {vol_name}')
+ error_code, out, err = mgr.remote('volumes', '_cmd_fs_subvolumegroup_ls',
+ None, {'vol_name': vol_name})
+ if error_code != 0:
+ raise DashboardException(
+ f'Error listing subvolume groups for {vol_name}')
+ subvolume_groups = json.loads(out)
+ for group in subvolume_groups:
+ error_code, out, err = mgr.remote('volumes', '_cmd_fs_subvolumegroup_info',
+ None, {'vol_name': vol_name,
+ 'group_name': group['name']})
+ if error_code != 0:
+ raise DashboardException(
+ f'Failed to get info for subvolume group {group["name"]}: {err}'
+ )
+ group['info'] = json.loads(out)
+ return subvolume_groups
+
+ @RESTController.Resource('GET')
+ def info(self, vol_name: str, group_name: str):
+ error_code, out, err = mgr.remote('volumes', '_cmd_fs_subvolumegroup_info', None, {
+ 'vol_name': vol_name, 'group_name': group_name})
+ if error_code != 0:
+ raise DashboardException(
+ f'Failed to get info for subvolume group {group_name}: {err}'
+ )
+ return json.loads(out)
+
+ def create(self, vol_name: str, group_name: str, **kwargs):
+ error_code, _, err = mgr.remote('volumes', '_cmd_fs_subvolumegroup_create', None, {
+ 'vol_name': vol_name, 'group_name': group_name, **kwargs})
+ if error_code != 0:
+ raise DashboardException(
+ f'Failed to create subvolume group {group_name}: {err}'
+ )
+
+ def set(self, vol_name: str, group_name: str, size: str):
+ if not size:
+ return f'Failed to update subvolume group {group_name}, size was not provided'
+ error_code, _, err = mgr.remote('volumes', '_cmd_fs_subvolumegroup_resize', None, {
+ 'vol_name': vol_name, 'group_name': group_name, 'new_size': size})
+ if error_code != 0:
+ raise DashboardException(
+ f'Failed to update subvolume group {group_name}: {err}'
+ )
+ return f'Subvolume group {group_name} updated successfully'
+
+ def delete(self, vol_name: str, group_name: str):
+ error_code, _, err = mgr.remote(
+ 'volumes', '_cmd_fs_subvolumegroup_rm', None, {
+ 'vol_name': vol_name, 'group_name': group_name})
+ if error_code != 0:
+ raise DashboardException(
+ f'Failed to delete subvolume group {group_name}: {err}'
+ )
+ return f'Subvolume group {group_name} removed successfully'