def nfs_export_add(req, host, path, options=None, chown=None, export_path=None): if not isinstance(options, list): if options is not None and len(options) > 0: options = [options] else: options = [] if export_path is not None: raise TargetdError( TargetdError.NFS_NO_SUPPORT, "separate export path not supported at " "this time") bit_opt = 0 key_opt = {} for o in options: if '=' in o: k, v = o.split('=') key_opt[k] = v else: bit_opt |= Export.bool_option[o] if chown is not None: if not allow_chown: raise TargetdError( TargetdError.NO_SUPPORT, "Chown is disabled. Consult manual before enabling it.") items = chown.split(':') try: uid = int(items[0]) gid = -1 if len(items) > 1: gid = int(items[1]) os.chown(path, uid, gid) except ValueError as e: raise TargetdError(TargetdError.INVALID_ARGUMENT, "Wrong chown arguments: {}".format(e)) try: Nfs.export_add(host, path, bit_opt, key_opt) except ValueError as e: raise TargetdError(TargetdError.INVALID_ARGUMENT, "{}".format(e)) return dict(host=host, path=path)
def _get_ss_by_uuid(req, fs_uuid, ss_uuid, fs_ht=None): if fs_ht is None: fs_ht = _get_fs_by_uuid(req, fs_uuid) for s in ss(req, fs_uuid, fs_ht): if s['uuid'] == ss_uuid: return s raise TargetdError(TargetdError.NOT_FOUND_SS, "snapshot not found")
def fs_create(req, pool_name, name, size_bytes): full_path = os.path.join(pool_name, fs_path, name) if not os.path.exists(full_path): invoke([fs_cmd, 'subvolume', 'create', full_path]) else: raise TargetdError(TargetdError.EXISTS_FS_NAME, 'FS already exists (Btrfs)')
def _invoke_retries(command, throw_exception): # TODO take out this loop, used to handle bug in btrfs # ERROR: Failed to lookup path for root 0 - No such file or directory for i in range(0, 5): result, out, err = invoke(command, False) if result == 0: return result, out, err elif result == 19: time.sleep(1) continue else: raise TargetdError(-303, "Unexpected exit code %d" % result) raise TargetdError( -303, "Unable to execute command after " "multiple retries %s" % (str(command)))
def initialize(config_dict): global pools global allow_chown allow_chown = config_dict['allow_chown'] all_fs_pools = list(config_dict['fs_pools']) for mount in all_fs_pools: if not os.path.exists(mount): raise TargetdError(TargetdError.NOT_FOUND_FS, 'The fs_pool {0} does not exist'.format(mount)) for info in Mount.mounted_filesystems(): if info[Mount.MOUNT_POINT] in all_fs_pools: filesystem = info[Mount.FS_TYPE] if filesystem in pool_modules: # forward both mountpoint and device to the backend as ZFS prefers its own devices (pool/volume) and # btrfs prefers mount points (/mnt/btrfs). Otherwise ZFS or btrfs needs to ask mounted_filesystems again pools[filesystem].append({ "mount": info[Mount.MOUNT_POINT], "device": info[Mount.DEVICE] }) else: raise TargetdError( TargetdError.NO_SUPPORT, 'Unsupported filesystem {0} for pool {1}'.format( info[2], info[1])) for modname, mod in pool_modules.items(): mod.fs_initialize(config_dict, pools[modname]) return dict( fs_list=fs, fs_destroy=fs_destroy, fs_create=fs_create, fs_clone=fs_clone, ss_list=ss, fs_snapshot=fs_snapshot, fs_snapshot_delete=fs_snapshot_delete, nfs_export_auth_list=nfs_export_auth_list, nfs_export_list=nfs_export_list, nfs_export_add=nfs_export_add, nfs_export_remove=nfs_export_remove, )
def pool_check(pool_name): """ pool_name *cannot* be trusted, funcs taking a pool param must call this or to ensure passed-in pool name is one targetd has been configured to use. """ if pool_name not in pools: raise TargetdError(-110, "Invalid filesystem pool")
def fs_create(req, pool_name, name, size_bytes): pool_check(pool_name) full_path = os.path.join(pool_name, fs_path, name) if not os.path.exists(full_path): invoke([fs_cmd, 'subvolume', 'create', full_path]) else: raise TargetdError(-53, 'FS already exists')
def _json_payload(payload): assert payload.get('jsonrpc') == "2.0" if 'error' in payload: error_code = int(payload['error']['code']) if error_code <= 0: raise TargetdError(error_code, payload['error'].get('message', '')) else: raise Exception("Invalid error code %d, should be negative!" % error_code) else: return payload['result']
def pool_module(pool_name): """ Determines the module responsible for the given pool :param pool_name: the pool to determine this for :return: the module responsible for it """ for modname, mod in pool_modules.items(): if mod.has_fs_pool(pool_name): return mod raise TargetdError(TargetdError.INVALID_POOL, "Invalid pool (%s)" % pool_name)
def nfs_export_remove(req, host, path): found = False for e in Nfs.exports(): if e.host == host and e.path == path: Nfs.export_remove(e) found = True if not found: raise TargetdError(-400, "NFS export to remove not found %s:%s", (host, path))
def nfs_export_remove(req, host, path): found = False for e in Nfs.exports(): if e.host == host and e.path == path: Nfs.export_remove(e) found = True if not found: raise TargetdError( TargetdError.NOT_FOUND_NFS_EXPORT, "NFS export to remove not found %s:%s" % (host, path))
def fs_snapshot(req, pool, name, dest_ss_name): source_path = os.path.join(pool, fs_path, name) dest_base = os.path.join(pool, ss_path, name) dest_path = os.path.join(dest_base, dest_ss_name) create_sub_volume(dest_base) if os.path.exists(dest_path): raise TargetdError(TargetdError.EXISTS_FS_NAME, "Snapshot already exists with that name (Btrfs)") invoke([fs_cmd, 'subvolume', 'snapshot', '-r', source_path, dest_path])
def fs_clone(req, fs_uuid, dest_fs_name, snapshot_id): fs_ht = _get_fs_by_uuid(req, fs_uuid) if not fs_ht: raise TargetdError(-104, "fs_uuid not found") if snapshot_id: snapshot = _get_ss_by_uuid(req, fs_uuid, snapshot_id) if not snapshot: raise TargetdError(-112, "snapshot not found") source = os.path.join(fs_ht['pool'], ss_path, fs_ht['name'], snapshot['name']) dest = os.path.join(fs_ht['pool'], fs_path, dest_fs_name) else: source = os.path.join(fs_ht['pool'], fs_path, fs_ht['name']) dest = os.path.join(fs_ht['pool'], fs_path, dest_fs_name) if os.path.exists(dest): raise TargetdError(-51, "Filesystem with that name exists") invoke([fs_cmd, 'subvolume', 'snapshot', source, dest])
def fs_clone(req, pool, name, dest_fs_name, snapshot_name=None): if snapshot_name is not None: source = os.path.join(pool, ss_path, name, snapshot_name) dest = os.path.join(pool, fs_path, dest_fs_name) else: source = os.path.join(pool, fs_path, name) dest = os.path.join(pool, fs_path, dest_fs_name) if os.path.exists(dest): raise TargetdError(TargetdError.EXISTS_CLONE_NAME, "Filesystem with that name exists (Btrfs)") invoke([fs_cmd, 'subvolume', 'snapshot', source, dest])
def fs_snapshot(req, fs_uuid, dest_ss_name): fs_ht = _get_fs_by_uuid(req, fs_uuid) if fs_ht: source_path = os.path.join(fs_ht['pool'], fs_path, fs_ht['name']) dest_base = os.path.join(fs_ht['pool'], ss_path, fs_ht['name']) dest_path = os.path.join(dest_base, dest_ss_name) create_sub_volume(dest_base) if os.path.exists(dest_path): raise TargetdError(-53, "Snapshot already exists with that name") invoke([fs_cmd, 'subvolume', 'snapshot', '-r', source_path, dest_path])
def nfs_export_add(req, host, path, export_path, options): if export_path is not None: raise TargetdError( -401, "separate export path not supported at " "this time") bit_opt = 0 key_opt = {} for o in options: if '=' in o: k, v = o.split('=') key_opt[k] = v else: bit_opt |= Export.bool_option[o] Nfs.export_add(host, path, bit_opt, key_opt)
def rpc(method, params=None, data=None): global id_num auth_info = HTTPBasicAuth(user, password) if not data: data = json.dumps( dict(id=id_num, method=method, params=params, jsonrpc="2.0")).encode('utf-8') id_num += 1 url = "%s://%s:%s%s" % (proto, host, port, rpc_path) r = requests.post(url, data=data, auth=auth_info, verify=cert_file) if r.status_code == 200: # JSON RPC error return _json_payload(r.json()) else: # Transport error raise TargetdError(r.status_code, str(r))
def _get_fs_by_uuid(req, fs_uuid): for f in fs(req): if f['uuid'] == fs_uuid: return f raise TargetdError(TargetdError.NOT_FOUND_FS, "fs_uuid not found")