From d5f263bdd6e3a6464a427e9bdb7e4e1f2cc321c9 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Fri, 26 Jul 2019 14:24:22 -0400 Subject: [PATCH] Implement Ceph volume resize and rename in clients [1/2] Implements #44 --- client-api/api_lib/pvcapi.py | 34 +++++++++++++++++++++ client-api/pvc-api.py | 15 +++++++-- client-cli/pvc.py | 44 +++++++++++++++++++++++++++ client-common/ceph.py | 59 ++++++++++++++++++++++++++++++++++++ docs/manuals/api.md | 2 ++ 5 files changed, 152 insertions(+), 2 deletions(-) diff --git a/client-api/api_lib/pvcapi.py b/client-api/api_lib/pvcapi.py index 085e31b5..a3ac1672 100755 --- a/client-api/api_lib/pvcapi.py +++ b/client-api/api_lib/pvcapi.py @@ -803,6 +803,40 @@ def ceph_volume_add(pool, name, size): } return flask.jsonify(output), retcode +def ceph_volume_resize(pool, name, size): + """ + Resize an existing Ceph RBD volume in the PVC Ceph storage cluster. + """ + zk_conn = pvc_common.startZKConnection(config['coordinators']) + retflag, retdata = pvc_ceph.resize_volume(zk_conn, pool, name, size) + if retflag: + retcode = 200 + else: + retcode = 400 + + pvc_common.stopZKConnection(zk_conn) + output = { + 'message': retdata.replace('\"', '\'') + } + return flask.jsonify(output), retcode + +def ceph_volume_rename(pool, name, new_name): + """ + Rename a Ceph RBD volume in the PVC Ceph storage cluster. + """ + zk_conn = pvc_common.startZKConnection(config['coordinators']) + retflag, retdata = pvc_ceph.rename_volume(zk_conn, pool, name, new_name) + if retflag: + retcode = 200 + else: + retcode = 400 + + pvc_common.stopZKConnection(zk_conn) + output = { + 'message': retdata.replace('\"', '\'') + } + return flask.jsonify(output), retcode + def ceph_volume_remove(pool, name): """ Remove a Ceph RBD volume to the PVC Ceph storage cluster. diff --git a/client-api/pvc-api.py b/client-api/pvc-api.py index 5825a170..8b4f1ca8 100755 --- a/client-api/pvc-api.py +++ b/client-api/pvc-api.py @@ -799,8 +799,19 @@ def api_ceph_volume_element(pool, volume): return pvcapi.ceph_volume_list(pool, volume) if flask.request.method == 'PUT': - # TODO: #44 - flask.abort(501) + if 'size' in flask.request.values: + size = flask.request.values['size'] + + if 'name' in flask.request.values: + name = flask.request.values['name'] + + if size and not name: + return pvcapi.ceph_volume_resize(pool, volume, size) + + if name and not size: + return pvcapi.ceph_volume_rename(pool, volume, name) + + return flask.jsonify({"message":"ERROR: No name or size specified, or both specified; not changing anything."}), 400 if flask.request.method == 'DELETE': return pvcapi.ceph_volume_remove(pool, volume) diff --git a/client-cli/pvc.py b/client-cli/pvc.py index a6b0e7ca..9c5fb404 100755 --- a/client-cli/pvc.py +++ b/client-cli/pvc.py @@ -1482,6 +1482,48 @@ def ceph_volume_remove(pool, name, yes): retcode, retmsg = pvc_ceph.remove_volume(zk_conn, pool, name) cleanup(retcode, retmsg, zk_conn) +############################################################################### +# pvc storage ceph volume resize +############################################################################### +@click.command(name='resize', short_help='Resize RBD volume.') +@click.argument( + 'pool' +) +@click.argument( + 'name' +) +@click.argument( + 'size' +) +def ceph_volume_resize(pool, name, size): + """ + Resize an existing Ceph RBD volume with name NAME in pool POOL to size SIZE [in human units, e.g. 1024M, 20G, etc.]. + """ + zk_conn = pvc_common.startZKConnection(zk_host) + retcode, retmsg = pvc_ceph.resize_volume(zk_conn, pool, name, size) + cleanup(retcode, retmsg, zk_conn) + +############################################################################### +# pvc storage ceph volume rename +############################################################################### +@click.command(name='rename', short_help'Rename RBD volume.') +@click.argument( + 'pool' +) +@click.argument( + 'name' +) +@click.argument( + 'new_name' +) +def ceph_volume_rename(pool, name, new_name): + """ + Rename an existing Ceph RBD volume with name NAME in pool POOL to name NEW_NAME. + """ + zk_conn = pvc_common.startZKConnection(zk_host) + retcode, retmsg = pvc_ceph.rename_volume(zk_conn, pool, name, new_name) + cleanup(retcode, retmsg, zk_conn) + ############################################################################### # pvc storage ceph volume list ############################################################################### @@ -1753,6 +1795,8 @@ ceph_pool.add_command(ceph_pool_remove) ceph_pool.add_command(ceph_pool_list) ceph_volume.add_command(ceph_volume_add) +ceph_volume.add_command(ceph_volume_resize) +ceph_volume.add_command(ceph_volume_rename) ceph_volume.add_command(ceph_volume_remove) ceph_volume.add_command(ceph_volume_list) ceph_volume.add_command(ceph_volume_snapshot) diff --git a/client-common/ceph.py b/client-common/ceph.py index bc44ff71..c6205e97 100644 --- a/client-common/ceph.py +++ b/client-common/ceph.py @@ -966,6 +966,65 @@ def add_volume(zk_conn, pool, name, size): return success, message +def resize_volume(zk_conn, pool, name, size): + # Tell the cluster to resize the volume + databytes = format_bytes_fromhuman(size) + resize_volume_string = 'volume_resize {},{},{}'.format(pool, name, databytes) + zkhandler.writedata(zk_conn, {'/ceph/cmd': resize_volume_string}) + # Wait 1/2 second for the cluster to get the message and start working + time.sleep(0.5) + # Acquire a read lock, so we get the return exclusively + lock = zkhandler.readlock(zk_conn, '/ceph/cmd') + with lock: + try: + result = zkhandler.readdata(zk_conn, '/ceph/cmd').split()[0] + if result == 'success-volume_resize': + message = 'Resized RBD volume "{}" to size "{}" on pool "{}".'.format(name, size, pool) + success = True + else: + message = 'ERROR: Failed to resize volume; check node logs for details.' + success = False + except: + message = 'ERROR: Command ignored by node.' + success = False + + # Acquire a write lock to ensure things go smoothly + lock = zkhandler.writelock(zk_conn, '/ceph/cmd') + with lock: + time.sleep(1) + zkhandler.writedata(zk_conn, {'/ceph/cmd': ''}) + + return success, message + +def rename_volume(zk_conn, pool, name, new_name): + # Tell the cluster to rename + rename_volume_string = 'volume_rename {},{},{}'.format(pool, name, new_name) + zkhandler.writedata(zk_conn, {'/ceph/cmd': rename_volume_string}) + # Wait 1/2 second for the cluster to get the message and start working + time.sleep(0.5) + # Acquire a read lock, so we get the return exclusively + lock = zkhandler.readlock(zk_conn, '/ceph/cmd') + with lock: + try: + result = zkhandler.readdata(zk_conn, '/ceph/cmd').split()[0] + if result == 'success-volume_add': + message = 'Created new RBD volume "{}" of size "{}" on pool "{}".'.format(name, size, pool) + success = True + else: + message = 'ERROR: Failed to create new volume; check node logs for details.' + success = False + except: + message = 'ERROR: Command ignored by node.' + success = False + + # Acquire a write lock to ensure things go smoothly + lock = zkhandler.writelock(zk_conn, '/ceph/cmd') + with lock: + time.sleep(1) + zkhandler.writedata(zk_conn, {'/ceph/cmd': ''}) + + return success, message + def remove_volume(zk_conn, pool, name): if not verifyVolume(zk_conn, pool, name): return False, 'ERROR: No volume with name "{}" is present in pool {}.'.format(name, pool) diff --git a/docs/manuals/api.md b/docs/manuals/api.md index e07507fb..d926d0d2 100644 --- a/docs/manuals/api.md +++ b/docs/manuals/api.md @@ -524,6 +524,8 @@ Return a JSON document containing information about Ceph RBD volume `` i Change the configuration of the volume ``. If `name` is specified, rename the volume to the specified name. If `size` is specified, resize the volume to the specified size (see `POST /api/v1/storage/ceph/volume` for restrictions). +**NOTE:** Only one change operation (either `name` or `size`) may be completed in one operation. + ###### `DELETE` * Mandatory values: N/A * Optional values: N/A