class CephFS: mgr: Mgr mon: Mon def __init__(self): self.mgr = Mgr() self.mon = Mon() pass def create(self, name: str) -> None: cmd = { "prefix": "fs volume create", "name": name } try: res = self.mgr.call(cmd) except CephCommandError as e: raise CephFSError(e) from e # this command does not support json at this time, and will output # free-form text instead. We are not going to parse it, but we'll make # sure we've got something out of it. assert "result" in res assert len(res["result"]) > 0 def volume_ls(self) -> CephFSVolumeListModel: cmd = { "prefix": "fs volume ls", "format": "json" } try: res = self.mgr.call(cmd) except CephCommandError as e: raise CephFSError(e) from e return CephFSVolumeListModel( volumes=parse_obj_as(List[CephFSNameModel], res) ) def ls(self) -> List[CephFSListEntryModel]: cmd = { "prefix": "fs ls", "format": "json" } try: res = self.mon.call(cmd) except CephCommandError as e: raise CephFSError(e) from e return parse_obj_as(List[CephFSListEntryModel], res) def get_fs_info(self, name: str) -> CephFSListEntryModel: ls: List[CephFSListEntryModel] = self.ls() for fs in ls: if fs.name == name: return fs raise CephFSError(f"unknown filesystem {name}")
class Orchestrator: cluster: Mgr def __init__(self): self.cluster = Mgr() def call(self, cmd: Dict[str, Any]) -> Any: if "format" not in cmd: cmd["format"] = "json" return self.cluster.call(cmd) def host_ls(self) -> List[OrchHostListModel]: cmd = {"prefix": "orch host ls"} res = self.call(cmd) return parse_obj_as(List[OrchHostListModel], res) def devices_ls(self) -> List[OrchDevicesPerHostModel]: cmd = {"prefix": "orch device ls"} res = self.call(cmd) return parse_obj_as(List[OrchDevicesPerHostModel], res) def assimilate_all_devices(self) -> None: cmd = {"prefix": "orch apply osd", "all_available_devices": True} res = self.call(cmd) assert "result" in res def all_devices_assimilated(self) -> bool: res = self.devices_ls() for host in res: for dev in host.devices: if dev.available: return False return True def apply_mds(self, fsname: str) -> None: cmd = {"prefix": "orch apply mds", "fs_name": fsname} self.call(cmd) def get_public_key(self) -> str: cmd = {"prefix": "cephadm get-pub-key"} res = self.call(cmd) assert "result" in res return res["result"] def host_add(self, hostname: str, address: str) -> bool: assert hostname assert address cmd = { "prefix": "orch host add", "hostname": hostname, "addr": address } try: self.call(cmd) except CephCommandError: logger.error(f"host add > unable to add {hostname} {address}") return False return True
class NFSService: mgr: Mgr def __init__(self): self.mgr = Mgr() def _call(self, cmd: Dict[str, Any]) -> Any: try: return self.mgr.call(cmd) except CephCommandError as e: raise NFSError(e) from e def create(self, name: str, placement: Optional[str]) -> str: cmd = { 'prefix': 'nfs cluster create', 'type': 'cephfs', # TODO: pending https://github.com/ceph/ceph/pull/40411 'clusterid': name, } if placement: cmd['placement'] = placement return self._call(cmd)['result'] def update(self, name: str, placement: str) -> str: res = self._call({ 'prefix': 'nfs cluster update', 'clusterid': name, 'placement': placement, }) return res['result'] def delete(self, name: str) -> str: res = self._call({ 'prefix': 'nfs cluster delete', 'clusterid': name, }) return res['result'] def ls(self) -> List[str]: res = self._call({ 'prefix': 'nfs cluster ls', 'format': 'json', }) return res['result'].split() if res.get('result') else [] def info(self, name: Optional[str] = None) -> List[NFSServiceModel]: cmd = { 'prefix': 'nfs cluster info', 'format': 'json', } if name: cmd['clusterid'] = name res = self._call(cmd) ret: List[NFSServiceModel] = [] for name in res: daemons = parse_obj_as(List[NFSDaemonModel], res[name]) ret.append(NFSServiceModel(name=name, daemons=daemons)) return ret
class CephFS: mgr: Mgr mon: Mon def __init__(self): self.mgr = Mgr() self.mon = Mon() pass def create(self, name: str) -> None: cmd = {"prefix": "fs volume create", "name": name} try: # this is expected to be a silent command self.mgr.call(cmd) except CephCommandError as e: raise CephFSError(e) from e # schedule orchestrator to update the number of mds instances orch = Orchestrator() orch.apply_mds(name) def volume_ls(self) -> CephFSVolumeListModel: cmd = {"prefix": "fs volume ls", "format": "json"} try: res = self.mgr.call(cmd) except CephCommandError as e: raise CephFSError(e) from e return CephFSVolumeListModel( volumes=parse_obj_as(List[CephFSNameModel], res)) def ls(self) -> List[CephFSListEntryModel]: cmd = {"prefix": "fs ls", "format": "json"} try: res = self.mon.call(cmd) except CephCommandError as e: raise CephFSError(e) from e return parse_obj_as(List[CephFSListEntryModel], res) def get_fs_info(self, name: str) -> CephFSListEntryModel: ls: List[CephFSListEntryModel] = self.ls() for fs in ls: if fs.name == name: return fs raise CephFSError(f"unknown filesystem {name}")
class Orchestrator: cluster: Mgr def __init__(self): self.cluster = Mgr() def call(self, cmd: Dict[str, Any]) -> Any: if "format" not in cmd: cmd["format"] = "json" return self.cluster.call(cmd) def host_ls(self) -> List[OrchHostListModel]: cmd = {"prefix": "orch host ls"} res = self.call(cmd) return parse_obj_as(List[OrchHostListModel], res) def devices_ls(self) -> List[OrchDevicesPerHostModel]: cmd = {"prefix": "orch device ls"} res = self.call(cmd) return parse_obj_as(List[OrchDevicesPerHostModel], res) def assimilate_all_devices(self) -> None: cmd = { "prefix": "orch apply osd", "all_available_devices": True } res = self.call(cmd) assert "result" in res def all_devices_assimilated(self) -> bool: res = self.devices_ls() for host in res: for dev in host.devices: if dev.available: return False return True
class CephFS: mgr: Mgr mon: Mon def __init__(self): self.mgr = Mgr() self.mon = Mon() pass def create(self, name: str) -> None: cmd = {"prefix": "fs volume create", "name": name} try: # this is expected to be a silent command self.mgr.call(cmd) except CephCommandError as e: raise CephFSError(e) from e # schedule orchestrator to update the number of mds instances orch = Orchestrator() orch.apply_mds(name) def volume_ls(self) -> CephFSVolumeListModel: cmd = {"prefix": "fs volume ls", "format": "json"} try: res = self.mgr.call(cmd) except CephCommandError as e: raise CephFSError(e) from e return CephFSVolumeListModel( volumes=parse_obj_as(List[CephFSNameModel], res)) def ls(self) -> List[CephFSListEntryModel]: cmd = {"prefix": "fs ls", "format": "json"} try: res = self.mon.call(cmd) except CephCommandError as e: raise CephFSError(e) from e return parse_obj_as(List[CephFSListEntryModel], res) def get_fs_info(self, name: str) -> CephFSListEntryModel: ls: List[CephFSListEntryModel] = self.ls() for fs in ls: if fs.name == name: return fs raise CephFSError(f"unknown filesystem {name}") def authorize(self, fsname: str, clientid: str) -> CephFSAuthorizationModel: assert fsname and clientid cmd = { "prefix": "fs authorize", "filesystem": fsname, "entity": f"client.{fsname}-{clientid}", "caps": ["/", "rw"], "format": "json" } try: res = self.mon.call(cmd) except CephCommandError as e: raise CephFSError(str(e)) from e lst = parse_obj_as(List[CephFSAuthorizationModel], res) assert len(lst) == 1 return lst[0] def get_authorization(self, fsname: str, clientid: Optional[str]) -> CephFSAuthorizationModel: if not clientid: clientid = "default" cmd = { "prefix": "auth get", "entity": f"client.{fsname}-{clientid}", "format": "json" } try: res = self.mon.call(cmd) except CephCommandError as e: if e.rc == errno.ENOENT: raise CephFSNoAuthorizationError(e.message) raise CephFSError(str(e)) from e lst = parse_obj_as(List[CephFSAuthorizationModel], res) if len(lst) == 0: raise CephFSNoAuthorizationError() return lst[0]