From 4cf3ade10a3e03fe2845155027663518ab491511 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Fri, 26 Jul 2019 11:00:11 -0400 Subject: [PATCH] RESTify the remaining components --- client-api/pvc-api.py | 479 +++++++++++++++++++++++------------------- docs/manuals/api.md | 30 +-- 2 files changed, 278 insertions(+), 231 deletions(-) diff --git a/client-api/pvc-api.py b/client-api/pvc-api.py index 45408437..3e23913f 100755 --- a/client-api/pvc-api.py +++ b/client-api/pvc-api.py @@ -137,7 +137,7 @@ def api_auth_logout(): # @api.route('/api/v1/node', methods=['GET']) @authenticator -def api_node(): +def api_node_root(): # Get name limit if 'limit' in flask.request.values: limit = flask.request.values['limit'] @@ -148,7 +148,7 @@ def api_node(): @api.route('/api/v1/node/', methods=['GET']) @authenticator -def api_node_root(node): +def api_node_element(node): # Same as specifying /node?limit=NODE return pvcapi.node_list(node) @@ -190,26 +190,26 @@ def api_node_domain_state(node): # @api.route('/api/v1/vm', methods=['GET', 'POST']) @authenticator -def api_vm(): +def api_vm_root(): if flask.request.method == 'GET': # Get node limit if 'node' in flask.request.values: node = flask.request.values['node'] else: node = None - + # Get state limit if 'state' in flask.request.values: state = flask.request.values['state'] else: state = None - + # Get name limit if 'limit' in flask.request.values: limit = flask.request.values['limit'] else: limit = None - + return pvcapi.vm_list(node, state, limit) if flask.request.method == 'POST': @@ -218,24 +218,24 @@ def api_vm(): libvirt_xml = flask.request.values['xml'] else: return flask.jsonify({"message":"ERROR: A Libvirt XML document must be specified."}), 400 - + # Get node name if 'node' in flask.request.values: node = flask.request.values['node'] else: node = None - + # Get target selector if 'selector' in flask.request.values: selector = flask.request.values['selector'] else: selector = None - + return pvcapi.vm_define(vm, libvirt_xml, node, selector) @api.route('/api/v1/vm/', methods=['GET', 'POST', 'PUT', 'DELETE']) @authenticator -def api_vm_root(vm): +def api_vm_element(vm): if flask.request.method == 'GET': # Same as specifying /vm?limit=VM return pvcapi.vm_list(None, None, vm, is_fuzzy=False) @@ -246,12 +246,12 @@ def api_vm_root(vm): if flask.request.method == 'PUT': libvirt_xml = flask.request.data - + if 'restart' in flask.request.values and flask.request.values['restart']: flag_restart = True else: flag_restart = False - + return pvcapi.vm_modify(vm, flag_restart, libvirt_xml) if flask.request.method == 'DELETE': @@ -285,13 +285,13 @@ def api_vm_state(vm): def api_vm_node(vm): if flask.request.method == 'GET': return "Test", 200 - + if flask.request.method == 'POST': if 'action' in flask.request.values: action = flask.request.values['action'] else: flask.abort(400) - + # Get node name if 'node' in flask.request.values: node = flask.request.values['node'] @@ -331,14 +331,14 @@ def api_vm_node(vm): # @api.route('/api/v1/network', methods=['GET', 'POST']) @authenticator -def api_net(): +def api_net_root(): if flask.request.method == 'GET': # Get name limit if 'limit' in flask.request.values: limit = flask.request.values['limit'] else: limit = None - + return pvcapi.net_list(limit) if flask.request.method == 'POST': @@ -347,13 +347,13 @@ def api_net(): vni = flask.request.values['vni'] else: return flask.jsonify({"message":"ERROR: A VNI must be specified for the virtual network."}), 520 - + # Get network description if 'description' in flask.request.values: description = flask.request.values['vni'] else: return flask.jsonify({"message":"ERROR: A VNI must be specified for the virtual network."}), 520 - + # Get network type if 'nettype' in flask.request.values: nettype = flask.request.values['nettype'] @@ -361,62 +361,62 @@ def api_net(): return flask.jsonify({"message":"ERROR: A valid nettype must be specified: 'managed' or 'bridged'."}), 520 else: return flask.jsonify({"message":"ERROR: A nettype must be specified for the virtual network."}), 520 - + # Get network domain if 'domain' in flask.request.values: domain = flask.request.values['domain'] else: domain = None - + # Get ipv4 network if 'ip4_network' in flask.request.values: ip4_network = flask.request.values['ip4_network'] else: ip4_network = None - + # Get ipv4 gateway if 'ip4_gateway' in flask.request.values: ip4_gateway = flask.request.values['ip4_gateway'] else: ip4_gateway = None - + # Get ipv6 network if 'ip6_network' in flask.request.values: ip6_network = flask.request.values['ip6_network'] else: ip6_network = None - + # Get ipv6 gateway if 'ip6_gateway' in flask.request.values: ip6_gateway = flask.request.values['ip6_gateway'] else: ip6_gateway = None - + # Get ipv4 DHCP flag if 'dhcp4' in flask.request.values and flask.request.values['dhcp4']: dhcp4_flag = True else: dhcp4_flag = False - + # Get ipv4 DHCP start if 'dhcp4_start' in flask.request.values: dhcp4_start = flask.request.values['dhcp4_start'] else: dhcp4_start = None - + # Get ipv4 DHCP end if 'dhcp4_end' in flask.request.values: dhcp4_end = flask.request.values['dhcp4_end'] else: dhcp4_end = None - + return pvcapi.net_add(vni, description, nettype, domain, ip4_network, ip4_gateway, ip6_network, ip6_gateway, dhcp4_flag, dhcp4_start, dhcp4_end) @api.route('/api/v1/network/', methods=['GET', 'PUT', 'DELETE']) @authenticator -def api_net_root(network): +def api_net_element(network): # Same as specifying /network?limit=NETWORK if flask.request.method == 'GET': return pvcapi.net_list(network) @@ -427,55 +427,55 @@ def api_net_root(network): description = flask.request.values['description'] else: description = None - + # Get network domain if 'domain' in flask.request.values: domain = flask.request.values['domain'] else: domain = None - + # Get ipv4 network if 'ip4_network' in flask.request.values: ip4_network = flask.request.values['ip4_network'] else: ip4_network = None - + # Get ipv4 gateway if 'ip4_gateway' in flask.request.values: ip4_gateway = flask.request.values['ip4_gateway'] else: ip4_gateway = None - + # Get ipv6 network if 'ip6_network' in flask.request.values: ip6_network = flask.request.values['ip6_network'] else: ip6_network = None - + # Get ipv6 gateway if 'ip6_gateway' in flask.request.values: ip6_gateway = flask.request.values['ip6_gateway'] else: ip6_gateway = None - + # Get ipv4 DHCP flag if 'dhcp4' in flask.request.values and flask.request.values['dhcp4']: dhcp4_flag = True else: dhcp4_flag = False - + # Get ipv4 DHCP start if 'dhcp4_start' in flask.request.values: dhcp4_start = flask.request.values['dhcp4_start'] else: dhcp4_start = None - + # Get ipv4 DHCP end if 'dhcp4_end' in flask.request.values: dhcp4_end = flask.request.values['dhcp4_end'] else: dhcp4_end = None - + return pvcapi.net_modify(network, description, domain, ip4_network, ip4_gateway, ip6_network, ip6_gateway, @@ -486,20 +486,20 @@ def api_net_root(network): @api.route('/api/v1/network//lease', methods=['GET', 'POST']) @authenticator -def api_net_lease(network): +def api_net_lease_root(network): if flask.request.method == 'GET': # Get name limit if 'limit' in flask.request.values: limit = flask.request.values['limit'] else: limit = None - + # Get static-only flag if 'static' in flask.request.values and flask.request.values['static']: flag_static = True else: flag_static = False - + return pvcapi.net_dhcp_list(network, limit. flag_static) if flask.request.method == 'POST': @@ -513,18 +513,18 @@ def api_net_lease(network): ipaddress = flask.request.values['ipaddress'] else: return flask.jsonify({"message":"ERROR: An IP address must be specified for the lease."}), 400 - + # Get lease hostname if 'hostname' in flask.request.values: hostname = flask.request.values['hostname'] else: hostname = None - + return pvcapi.net_dhcp_add(network, ipaddress, lease, hostname) @api.route('/api/v1/network//lease/', methods=['GET', 'DELETE']) @authenticator -def api_net_lease_root(network, lease): +def api_net_lease_element(network, lease): if flask.request.method == 'GET': # Same as specifying /network?limit=NETWORK return pvcapi.net_dhcp_list(network, lease, False) @@ -534,14 +534,14 @@ def api_net_lease_root(network, lease): @api.route('/api/v1/network//acl', methods=['GET', 'POST']) @authenticator -def api_net_acl(network): +def api_net_acl_root(network): if flask.request.method == 'GET': # Get name limit if 'limit' in flask.request.values: limit = flask.request.values['limit'] else: limit = None - + # Get direction limit if 'direction' in flask.request.values: direction = flask.request.values['direction'] @@ -549,7 +549,7 @@ def api_net_acl(network): return flash.jsonify({"message":"ERROR: Direction must be either 'in' or 'out'; for both, do not specify a direction."}), 400 else: direction = None - + return pvcapi.net_acl_list(network, limit, direction) if flask.request.method == 'POST': @@ -558,7 +558,7 @@ def api_net_acl(network): description = flask.request.values['description'] else: return flask.jsonify({"message":"ERROR: A description must be provided."}), 400 - + # Get rule direction if 'direction' in flask.request.values: direction = flask.request.values['limit'] @@ -566,24 +566,24 @@ def api_net_acl(network): return flask.jsonify({"message":"ERROR: Direction must be either 'in' or 'out'."}), 400 else: return flask.jsonify({"message":"ERROR: A direction must be specified for the ACL."}), 400 - + # Get rule data if 'rule' in flask.request.values: rule = flask.request.values['rule'] else: return flask.jsonify({"message":"ERROR: A valid NFT rule line must be specified for the ACL."}), 400 - + # Get order value if 'order' in flask.request.values: order = flask.request.values['order'] else: order = None - + return pvcapi.net_acl_add(network, direction, acl, rule, order) @api.route('/api/v1/network//acl/', methods=['GET', 'DELETE']) @authenticator -def api_net_acl_info(network, acl): +def api_net_acl_element(network, acl): if flask.request.method == 'GET': # Same as specifying /network?limit=NETWORK return pvcapi.net_acl_list(network, acl, None) @@ -596,7 +596,7 @@ def api_net_acl_info(network, acl): return flask.jsonify({"message":"ERROR: Direction must be either 'in' or 'out'."}), 400 else: return flask.jsonify({"message":"ERROR: A direction must be specified for the ACL."}), 400 - + return pvcapi.net_acl_remove(network, direction, acl) # @@ -622,196 +622,243 @@ def api_ceph_status(): def api_ceph_radosdf(): return pvcapi.ceph_radosdf() -@api.route('/api/v1/storage/ceph/osd', methods=['GET']) +@api.route('/api/v1/storage/ceph/cluster-option', methods=['GET', 'POST']) @authenticator -def api_ceph_osd(): - # Get name limit - if 'limit' in flask.request.values: - limit = flask.request.values['limit'] - else: - limit = None +def api_ceph_cluster_option(): + if flask.request.method == 'GET': + pass - return pvcapi.ceph_osd_list(limit) + if flask.request.method == 'POST': + # Get action + if 'action' in flask.request.values: + action = flask.request.values['action'] + if not 'set' in action and not 'unset' in action: + return flask.jsonify({"message":"ERROR: Action must be one of: set, unset"}), 400 + else: + return flask.jsonify({"message":"ERROR: An action must be specified."}), 400 + # Get option + if 'option' in flask.request.values: + option = flask.request.values['option'] + else: + return flask.jsonify({"message":"ERROR: An option must be specified."}), 400 -@api.route('/api/v1/storage/ceph/osd/set', methods=['POST']) + if action == 'set': + return pvcapi.ceph_osd_set(option) + if action == 'unset': + return pvcapi.ceph_osd_unset(option) + +@api.route('/api/v1/storage/ceph/osd', methods=['GET', 'POST']) @authenticator -def api_ceph_osd_set(): - # Get OSD option - if 'option' in flask.request.options: - option = flask.request.options['option'] - else: - return flask.jsonify({"message":"ERROR: An OSD option must be specified."}), 400 +def api_ceph_osd_root(): + if flask.request.method == 'GET': + # Get name limit + if 'limit' in flask.request.values: + limit = flask.request.values['limit'] + else: + limit = None - return pvcapi.ceph_osd_set(option) + return pvcapi.ceph_osd_list(limit) -@api.route('/api/v1/storage/ceph/osd/unset', methods=['POST']) + if flask.request.method == 'POST': + # Get OSD node + if 'node' in flask.request.values: + node = flask.request.values['node'] + else: + return flask.jsonify({"message":"ERROR: A node must be specified."}), 400 + + # Get OSD device + if 'device' in flask.request.values: + device = flask.request.values['device'] + else: + return flask.jsonify({"message":"ERROR: A block device must be specified."}), 400 + + # Get OSD weight + if 'weight' in flask.request.values: + weight = flask.request.values['weight'] + else: + return flask.jsonify({"message":"ERROR: An OSD weight must be specified."}), 400 + + return pvcapi.ceph_osd_add(node, device, weight) + +@api.route('/api/v1/storage/ceph/osd/', methods=['GET', 'DELETE']) @authenticator -def api_ceph_osd_unset(): - # Get OSD option - if 'option' in flask.request.options: - option = flask.request.options['option'] - else: - return flask.jsonify({"message":"ERROR: An OSD option must be specified."}), 400 +def api_ceph_osd_element(osd): + if flask.request.method == 'GET': + # Same as specifying /osd?limit=OSD + return pvcapi.ceph_osd_list(osd) - return pvcapi.ceph_osd_unset(option) + if flask.request.method == 'DELETE': + # Verify yes-i-really-mean-it flag + if not 'yes_i_really_mean_it' in flask.request.values: + return flask.jsonify({"message":"ERROR: This command can have unintended consequences and should not be automated; if you're sure you know what you're doing, resend with the argument 'yes_i_really_mean_it'."}), 400 -@api.route('/api/v1/storage/ceph/osd/', methods=['GET']) + return pvcapi.ceph_osd_remove(osd) + +@api.route('/api/v1/storage/ceph/osd//state', methods=['GET', 'POST']) @authenticator -def api_ceph_osd_info(osd): - # Same as specifying /osd?limit=OSD - return pvcapi.ceph_osd_list(osd) +def api_ceph_osd_state(osd): + if flask.request.method == 'GET': + pass -@api.route('/api/v1/storage/ceph/osd//add', methods=['POST']) + if flask.request.method == 'POST': + if 'state' in flask.request.values: + state = flask.request.values['state'] + if not 'in' in state and not 'out' in state: + return flask.jsonify({"message":"ERROR: State must be one of: in, out."}), 400 + else: + return flask.jsonify({"message":"ERROR: A state must be specified."}), 400 + + if state == 'in': + return pvcapi.ceph_osd_in(osd) + if state == 'out': + return pvcapi.ceph_osd_out(osd) + +@api.route('/api/v1/storage/ceph/pool', methods=['GET', 'POST']) @authenticator -def api_ceph_osd_add(node): - # Get OSD device - if 'device' in flask.request.devices: - device = flask.request.devices['device'] - else: - return flask.jsonify({"message":"ERROR: A block device must be specified."}), 400 +def api_ceph_pool_root(): + if flask.request.method == 'GET': + # Get name limit + if 'limit' in flask.request.values: + limit = flask.request.values['limit'] + else: + limit = None + + return pvcapi.ceph_pool_list(limit) - # Get OSD weight - if 'weight' in flask.request.weights: - weight = flask.request.weights['weight'] - else: - return flask.jsonify({"message":"ERROR: An OSD weight must be specified."}), 400 + if flask.request.method == 'POST': + # Get pool name + if 'pool' in flask.request.values: + pool = flask.request.values['pool'] + else: + return flask.jsonify({"message":"ERROR: A pool name must be specified."}), 400 + + # Get placement groups + if 'pgs' in flask.request.values: + pgs = flask.request.values['pgs'] + else: + # We default to a very small number; DOCUMENT THIS + pgs = 128 + + return pvcapi.ceph_pool_add(pool, pgs) - return pvcapi.ceph_osd_add(node, device, weight) - -@api.route('/api/v1/storage/ceph/osd//remove', methods=['POST']) +@api.route('/api/v1/storage/ceph/pool/', methods=['GET', 'DELETE']) @authenticator -def api_ceph_osd_remove(osd): - # Verify yes-i-really-mean-it flag - if not 'flag_yes_i_really_mean_it' in flask.request.values: - return flask.jsonify({"message":"ERROR: This command can have unintended consequences and should not be automated; if you're sure you know what you're doing, resend with the argument 'flag_yes_i_really_mean_it'."}), 599 +def api_ceph_pool_element(pool): + if flask.request.method == 'GET': + # Same as specifying /pool?limit=POOL + return pvcapi.ceph_pool_list(pool) - return pvcapi.ceph_osd_remove(osd) + if flask.request.method == 'DELETE': + # Verify yes-i-really-mean-it flag + if not 'yes_i_really_mean_it' in flask.request.values: + return flask.jsonify({"message":"ERROR: This command can have unintended consequences and should not be automated; if you're sure you know what you're doing, resend with the argument 'yes_i_really_mean_it'."}), 400 + + return pvcapi.ceph_pool_remove(pool) -@api.route('/api/v1/storage/ceph/osd//in', methods=['POST']) +@api.route('/api/v1/storage/ceph/volume', methods=['GET', 'POST']) @authenticator -def api_ceph_osd_in(osd): - return pvcapi.ceph_osd_in(osd) +def api_ceph_volume_root(): + if flask.request.method == 'GET': + # Get pool limit + if 'pool' in flask.request.values: + pool = flask.request.values['pool'] + else: + pool = None + + # Get name limit + if 'limit' in flask.request.values: + limit = flask.request.values['limit'] + else: + limit = None + + return pvcapi.ceph_volume_list(pool, limit) -@api.route('/api/v1/storage/ceph/osd//out', methods=['POST']) + if flask.request.method == 'POST': + # Get volume name + if 'volume' in flask.request.values: + volume = flask.request.values['volume'] + else: + return flask.jsonify({"message":"ERROR: A volume name must be specified."}), 400 + + # Get volume pool + if 'pool' in flask.request.values: + pool = flask.request.values['pool'] + else: + return flask.jsonify({"message":"ERROR: A pool name must be spcified."}), 400 + + # Get volume size + if 'size' in flask.request.values: + size = flask.request.values['size'] + else: + return flask.jsonify({"message":"ERROR: A volume size in bytes (or with an M/G/T suffix) must be specified."}), 400 + + return pvcapi.ceph_volume_add(pool, volume, size) + +@api.route('/api/v1/storage/ceph/volume//', methods=['GET', 'DELETE']) @authenticator -def api_ceph_osd_out(osd): - return pvcapi.ceph_osd_out(osd) +def api_ceph_volume_element(pool, volume): + if flask.request.method == 'GET': + # Same as specifying /volume?limit=VOLUME + return pvcapi.ceph_volume_list(pool, volume) -@api.route('/api/v1/storage/ceph/pool', methods=['GET']) + if flask.request.method == 'DELETE': + return pvcapi.ceph_volume_remove(pool, volume) + +@api.route('/api/v1/storage/ceph/volume/snapshot', methods=['GET', 'POST']) @authenticator -def api_ceph_pool(): - # Get name limit - if 'limit' in flask.request.values: - limit = flask.request.values['limit'] - else: - limit = None +def api_ceph_volume_snapshot_root(): + if flask.request.method == 'GET': + # Get pool limit + if 'pool' in flask.request.values: + pool = flask.request.values['pool'] + else: + pool = None + + # Get volume limit + if 'volume' in flask.request.values: + volume = flask.request.values['volume'] + else: + volume = None + + # Get name limit + if 'limit' in flask.request.values: + limit = flask.request.values['limit'] + else: + limit = None + + return pvcapi.ceph_volume_snapshot_list(pool, volume, limit) - return pvcapi.ceph_pool_list(limit) + if flask.request.method == 'POST': + # Get snapshot name + if 'snapshot' in flask.request.values: + snapshot = flask.request.values['snapshot'] + else: + return flask.jsonify({"message":"ERROR: A snapshot name must be specified."}), 400 + + # Get volume name + if 'volume' in flask.request.values: + volume = flask.request.values['volume'] + else: + return flask.jsonify({"message":"ERROR: A volume name must be specified."}), 400 + + # Get volume pool + if 'pool' in flask.request.values: + pool = flask.request.values['pool'] + else: + return flask.jsonify({"message":"ERROR: A pool name must be spcified."}), 400 + + return pvcapi.ceph_volume_snapshot_add(pool, volume, snapshot) -@api.route('/api/v1/storage/ceph/pool/', methods=['GET']) + +@api.route('/api/v1/storage/ceph/volume/snapshot///', methods=['GET', 'DELETE']) @authenticator -def api_ceph_pool_info(pool): - # Same as specifying /pool?limit=POOL - return pvcapi.ceph_pool_list(pool) +def api_ceph_volume_snapshot_element(pool, volume, snapshot): + if flask.request.method == 'GET': + # Same as specifying /snapshot?limit=VOLUME + return pvcapi.ceph_volume_snapshot_list(pool, volume, snapshot) -@api.route('/api/v1/storage/ceph/pool//add', methods=['POST']) -@authenticator -def api_ceph_pool_add(pool): - # Get placement groups - if 'pgs' in flask.request.values: - pgs = flask.request.values['pgs'] - else: - # We default to a very small number; DOCUMENT THIS - pgs = 128 - - return pvcapi.ceph_pool_add(pool, pgs) - -@api.route('/api/v1/storage/ceph/pool//remove', methods=['POST']) -@authenticator -def api_ceph_pool_remove(pool): - # Verify yes-i-really-mean-it flag - if not 'flag_yes_i_really_mean_it' in flask.request.values: - return flask.jsonify({"message":"ERROR: This command can have unintended consequences and should not be automated; if you're sure you know what you're doing, resend with the argument 'flag_yes_i_really_mean_it'."}), 599 - - return pvcapi.ceph_pool_remove(pool) - -@api.route('/api/v1/storage/ceph/volume', methods=['GET']) -@authenticator -def api_ceph_volume(): - # Get pool limit - if 'pool' in flask.request.values: - pool = flask.request.values['pool'] - else: - pool = None - - # Get name limit - if 'limit' in flask.request.values: - limit = flask.request.values['limit'] - else: - limit = None - - return pvcapi.ceph_volume_list(pool, limit) - -@api.route('/api/v1/storage/ceph/volume//', methods=['GET']) -@authenticator -def api_ceph_volume_info(pool, volume): - # Same as specifying /volume?limit=VOLUME - return pvcapi.ceph_osd_list(pool, osd) - -@api.route('/api/v1/storage/ceph/volume///add', methods=['POST']) -@authenticator -def api_ceph_volume_add(pool, volume): - # Get volume size - if 'size' in flask.request.values: - size = flask.request.values['size'] - else: - return flask.jsonify({"message":"ERROR: A volume size in bytes (or with an M/G/T suffix) must be specified."}), 400 - - return pvcapi.ceph_volume_add(pool, volume, size) - -@api.route('/api/v1/storage/ceph/volume///remove', methods=['POST']) -@authenticator -def api_ceph_volume_remove(pool, volume): - return pvcapi.ceph_volume_remove(pool, volume) - -@api.route('/api/v1/storage/ceph/volume/snapshot', methods=['GET']) -@authenticator -def api_ceph_volume_snapshot(): - # Get pool limit - if 'pool' in flask.request.values: - pool = flask.request.values['pool'] - else: - pool = None - - # Get volume limit - if 'volume' in flask.request.values: - volume = flask.request.values['volume'] - else: - volume = None - - # Get name limit - if 'limit' in flask.request.values: - limit = flask.request.values['limit'] - else: - limit = None - - return pvcapi.ceph_volume_snapshot_list(pool, volume, limit) - -@api.route('/api/v1/storage/ceph/volume/snapshot///', methods=['GET']) -@authenticator -def api_ceph_volume_snapshot_info(pool, volume, snapshot): - # Same as specifying /snapshot?limit=VOLUME - return pvcapi.ceph_snapshot_list(pool, volume, snapshot) - -@api.route('/api/v1/storage/ceph/volume/snapshot////add', methods=['POST']) -@authenticator -def api_ceph_volume_snapshot_add(pool, volume, snapshot): - return pvcapi.ceph_volume_snapshot_add(pool, volume, snapshot) - -@api.route('/api/v1/storage/ceph/volume/snapshot////remove', methods=['POST']) -@authenticator -def api_ceph_volume_snapshot_remove(pool, volume, snapshot): - return pvcapi.ceph_volume_snapshot_remove(pool, volume, snapshot) + if flask.request.method == 'DELETE': + return pvcapi.ceph_volume_snapshot_remove(pool, volume, snapshot) # # Entrypoint diff --git a/docs/manuals/api.md b/docs/manuals/api.md index d5099173..bfef60a2 100644 --- a/docs/manuals/api.md +++ b/docs/manuals/api.md @@ -399,7 +399,7 @@ This endpoint is an alias for `/api/v1/storage/ceph`. Return a JSON document containing information about the current Ceph cluster utilization. The JSON element `ceph_data` contains the raw output of a `rados df` command. #### `/api/v1/storage/ceph/cluster-option` - * Methods: `POST` + * Methods: `GET`, `POST` * Mandatory values: `action`, `option` * Optional values: N/A @@ -418,7 +418,7 @@ Return a JSON document containing information about all Ceph OSDs in the storage * Mandatory values: `node`, `device`, `weight` * Optional values: N/A -Add a new Ceph OSD to PVC node with name ``. `device` must be a valid block device on the specified ``, e.g. `/dev/sdb`. `weight` must be a valid Ceph OSD weight, usually `1.0` if all OSD disks are the same size. +Add a new Ceph OSD to PVC node ``. `device` must be a valid block device on the specified ``, e.g. `/dev/sdb`. `weight` must be a valid Ceph OSD weight, usually `1.0` if all OSD disks are the same size. #### `/api/v1/storage/ceph/osd/` * Methods: `GET`, `DELETE` @@ -467,7 +467,7 @@ Return a JSON document containing information about all Ceph RBD pools in the st * Mandatory values: `pool`, `pgs` * Optional values: N/A -Add a new Ceph RBD pool with name `` to the storage cluster. `pgs` must be a valid number of Placement Groups for the pool, taking into account the number of OSDs and the replication of the pool (`copies=3`). `256` is a safe and sane number of PGs for 3 nodes and 2 disks per node. This value can be grown later via `ceph` commands as required. +Add a new Ceph RBD pool `` to the storage cluster. `pgs` must be a valid number of Placement Groups for the pool, taking into account the number of OSDs and the replication of the pool (`copies=3`). `256` is a safe and sane number of PGs for 3 nodes and 2 disks per node. This value can be grown later via `ceph` commands as required. #### `/api/v1/storage/ceph/pool/` * Methods: `GET`, `DELETE` @@ -476,13 +476,13 @@ Add a new Ceph RBD pool with name `` to the storage cluster. `pgs` must be * Mandatory values: N/A * Optional values: N/A -Return a JSON document containing information about Ceph RBD pool with name ``. The output is identical to `/api/v1/storage/ceph/pool?limit=` without fuzzy regex matching. +Return a JSON document containing information about Ceph RBD pool ``. The output is identical to `/api/v1/storage/ceph/pool?limit=` without fuzzy regex matching. ###### `DELETE` * Mandatory values: `yes_i_really_mean_it` * Optional values: N/A -Remove a Ceph RBD pool with name `` from the storage cluster. +Remove a Ceph RBD pool `` from the storage cluster. **NOTE:** This is a command with potentially dangerous unintended consequences that should not be scripted. To acknowledge the danger, the `yes_i_really_mean_it` must be set or the endpoint will return a failure. @@ -495,13 +495,13 @@ Remove a Ceph RBD pool with name `` from the storage cluster. * Mandatory values: N/A * Optional values: `pool`, `limit` -Return a JSON document containing information about all Ceph RBD volumes in the storage cluster. If `pool` is specified, return a JSON document containing information about all Ceph RBD volumes in Ceph RBD pool with name `pool`. If `limit` is specified, return a JSON document containing information about all Ceph RBD volumes with names matching `limit` as fuzzy regex. +Return a JSON document containing information about all Ceph RBD volumes in the storage cluster. If `pool` is specified, return a JSON document containing information about all Ceph RBD volumes in Ceph RBD pool `pool`. If `limit` is specified, return a JSON document containing information about all Ceph RBD volumes with names matching `limit` as fuzzy regex. ###### `POST` - * Mandatory values: `pool`, `size` + * Mandatory values: `volume`, `pool`, `size` * Optional values: N/A -Add a new Ceph RBD volume with name `` to Ceph RBD pool with name ``. `size` must be a valid size, in bytes or a single-character metric prefix of bytes, e.g. `1073741824` (1GB), `4096M`, or `20G`. +Add a new Ceph RBD volume `` to Ceph RBD pool ``. `size` must be a valid size, in bytes or a single-character metric prefix of bytes, e.g. `1073741824` (1GB), `4096M`, or `20G`. #### `/api/v1/storage/ceph/volume//` * Methods: `GET`, `DELETE` @@ -510,13 +510,13 @@ Add a new Ceph RBD volume with name `` to Ceph RBD pool with name `` in Ceph RBD pool with name ``. The output is identical to `/api/v1/storage/ceph/volume?pool=&limit=` without fuzzy regex matching. +Return a JSON document containing information about Ceph RBD volume `` in Ceph RBD pool ``. The output is identical to `/api/v1/storage/ceph/volume?pool=&limit=` without fuzzy regex matching. ###### `DELETE` * Mandatory values: N/A * Optional values: N/A -Remove a Ceph RBD volume with name `` from Ceph RBD pool ``. +Remove a Ceph RBD volume `` from Ceph RBD pool ``. #### `/api/v1/storage/ceph/volume/snapshot` * Methods: `GET`, `POST` @@ -525,15 +525,15 @@ Remove a Ceph RBD volume with name `` from Ceph RBD pool ``. * Mandatory values: N/A * Optional values: `pool`, `volume`, `limit` -Return a JSON document containing information about all Ceph RBD volume snapshots in the storage cluster. If `pool` is specified, return a JSON document containing information about all Ceph RBD volume snapshots in Ceph RBD pool with name `pool`. If `volume` is specified, return a JSON document containing information about all Ceph RBD volume snapshots of Ceph RBD volume with name `volume`. If `limit` is specified, return a JSON document containing information about all Ceph RBD volume snapshots with names matching `limit` as fuzzy regex. +Return a JSON document containing information about all Ceph RBD volume snapshots in the storage cluster. If `pool` is specified, return a JSON document containing information about all Ceph RBD volume snapshots in Ceph RBD pool `pool`. If `volume` is specified, return a JSON document containing information about all Ceph RBD volume snapshots of Ceph RBD volume `volume`. If `limit` is specified, return a JSON document containing information about all Ceph RBD volume snapshots with names matching `limit` as fuzzy regex. The various limit options can be combined freely, e.g. one can specify a `volume` without `pool`, which would match all snapshots of the named volume(s) regardless of pool, or a `pool` and `limit` without a `volume`, which would match all named snapshots on any volume in `pool`. ###### `POST` - * Mandatory values: `snapshot`, `pool`, `volume` + * Mandatory values: `snapshot`, `volume`, `pool` * Optional values: N/A -Add a new Ceph RBD volume snapshot with name `` of Ceph RBD volume with name `` on Ceph RBD pool with name ``. +Add a new Ceph RBD volume snapshot `snapshot` of Ceph RBD volume `volume` on Ceph RBD pool `pool`. #### `/api/v1/storage/ceph/volume/snapshot///` * Methods: `GET`, `DELETE` @@ -542,10 +542,10 @@ Add a new Ceph RBD volume snapshot with name `` of Ceph RBD volume wit * Mandatory values: N/A * Optional values: N/A -Return a JSON document containing information about Ceph RBD volume snapshot with name `` of Ceph RBD volume with name `` in Ceph RBD pool with name ``. The output is identical to `/api/v1/storage/ceph/volume?pool=&volume=&limit=` without fuzzy regex matching. +Return a JSON document containing information about Ceph RBD volume snapshot `` of Ceph RBD volume `` in Ceph RBD pool ``. The output is identical to `/api/v1/storage/ceph/volume?pool=&volume=&limit=` without fuzzy regex matching. ###### `DELETE` * Mandatory values: N/A * Optional values: N/A -Remove a Ceph RBD volume snapshot with name `` of Ceph RBD volume with name `` on Ceph RBD pool with name ``. +Remove a Ceph RBD volume snapshot `` of Ceph RBD volume `` on Ceph RBD pool ``.