Add size validations for volume clones
Adds the same validations as a volume add or resize to volume clones, to ensure there is enough free space for them.
This commit is contained in:
parent
efc7434143
commit
a95e72008e
|
@ -5972,7 +5972,11 @@ class API_Storage_Ceph_Volume_Element_Clone(Resource):
|
||||||
"name": "new_volume",
|
"name": "new_volume",
|
||||||
"required": True,
|
"required": True,
|
||||||
"helptext": "A new volume name must be specified.",
|
"helptext": "A new volume name must be specified.",
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
"name": "force",
|
||||||
|
"required": False,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@Authenticator
|
@Authenticator
|
||||||
|
@ -5988,6 +5992,12 @@ class API_Storage_Ceph_Volume_Element_Clone(Resource):
|
||||||
type: string
|
type: string
|
||||||
required: true
|
required: true
|
||||||
description: The name of the new cloned volume
|
description: The name of the new cloned volume
|
||||||
|
- in: query
|
||||||
|
name: force
|
||||||
|
type: boolean
|
||||||
|
required: false
|
||||||
|
default: flase
|
||||||
|
description: Force action if clone volume size would violate 80% full soft cap on the pool
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: OK
|
description: OK
|
||||||
|
@ -6006,7 +6016,7 @@ class API_Storage_Ceph_Volume_Element_Clone(Resource):
|
||||||
id: Message
|
id: Message
|
||||||
"""
|
"""
|
||||||
return api_helper.ceph_volume_clone(
|
return api_helper.ceph_volume_clone(
|
||||||
pool, reqargs.get("new_volume", None), volume
|
pool, reqargs.get("new_volume", None), volume, reqargs.get("force", None)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1887,11 +1887,13 @@ def ceph_volume_add(zkhandler, pool, name, size, force_flag):
|
||||||
|
|
||||||
|
|
||||||
@ZKConnection(config)
|
@ZKConnection(config)
|
||||||
def ceph_volume_clone(zkhandler, pool, name, source_volume):
|
def ceph_volume_clone(zkhandler, pool, name, source_volume, force_flag):
|
||||||
"""
|
"""
|
||||||
Clone a Ceph RBD volume to a new volume on the PVC Ceph storage cluster.
|
Clone a Ceph RBD volume to a new volume on the PVC Ceph storage cluster.
|
||||||
"""
|
"""
|
||||||
retflag, retdata = pvc_ceph.clone_volume(zkhandler, pool, source_volume, name)
|
retflag, retdata = pvc_ceph.clone_volume(
|
||||||
|
zkhandler, pool, source_volume, name, force_flag=force_flag
|
||||||
|
)
|
||||||
|
|
||||||
if retflag:
|
if retflag:
|
||||||
retcode = 200
|
retcode = 200
|
||||||
|
|
|
@ -4237,13 +4237,25 @@ def cli_storage_volume_rename(pool, name, new_name):
|
||||||
@click.argument("pool")
|
@click.argument("pool")
|
||||||
@click.argument("name")
|
@click.argument("name")
|
||||||
@click.argument("new_name")
|
@click.argument("new_name")
|
||||||
def cli_storage_volume_clone(pool, name, new_name):
|
@click.option(
|
||||||
|
"-f",
|
||||||
|
"--force",
|
||||||
|
"force_flag",
|
||||||
|
is_flag=True,
|
||||||
|
default=False,
|
||||||
|
help="Force clone even if volume would violate 80% full safe free space.",
|
||||||
|
)
|
||||||
|
def cli_storage_volume_clone(pool, name, new_name, force_flag):
|
||||||
"""
|
"""
|
||||||
Clone a Ceph RBD volume with name NAME in pool POOL to name NEW_NAME in pool POOL.
|
Clone a Ceph RBD volume with name NAME in pool POOL to name NEW_NAME in pool POOL.
|
||||||
|
|
||||||
|
PVC will prevent the clone of a volume who's new size is greater than the available free space on the pool. This cannot be overridden.
|
||||||
|
|
||||||
|
PVC will prevent the clone of a volume who's new size is greater than the 80% full safe free space on the pool. This can be overridden with the "-f"/"--force" option but this may be dangerous!
|
||||||
"""
|
"""
|
||||||
|
|
||||||
retcode, retmsg = pvc.lib.storage.ceph_volume_clone(
|
retcode, retmsg = pvc.lib.storage.ceph_volume_clone(
|
||||||
CLI_CONFIG, pool, name, new_name
|
CLI_CONFIG, pool, name, new_name, force_flag=force_flag
|
||||||
)
|
)
|
||||||
finish(retcode, retmsg)
|
finish(retcode, retmsg)
|
||||||
|
|
||||||
|
|
|
@ -1294,15 +1294,15 @@ def ceph_volume_modify(
|
||||||
return retstatus, response.json().get("message", "")
|
return retstatus, response.json().get("message", "")
|
||||||
|
|
||||||
|
|
||||||
def ceph_volume_clone(config, pool, volume, new_volume):
|
def ceph_volume_clone(config, pool, volume, new_volume, force_flag=False):
|
||||||
"""
|
"""
|
||||||
Clone Ceph volume
|
Clone Ceph volume
|
||||||
|
|
||||||
API endpoint: POST /api/v1/storage/ceph/volume/{pool}/{volume}
|
API endpoint: POST /api/v1/storage/ceph/volume/{pool}/{volume}
|
||||||
API arguments: new_volume={new_volume
|
API arguments: new_volume={new_volume, force_flag={force_flag}
|
||||||
API schema: {"message":"{data}"}
|
API schema: {"message":"{data}"}
|
||||||
"""
|
"""
|
||||||
params = {"new_volume": new_volume}
|
params = {"new_volume": new_volume, "force_flag": force_flag}
|
||||||
response = call_api(
|
response = call_api(
|
||||||
config,
|
config,
|
||||||
"post",
|
"post",
|
||||||
|
|
|
@ -611,13 +611,39 @@ def add_volume(zkhandler, pool, name, size, force_flag=False):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def clone_volume(zkhandler, pool, name_src, name_new):
|
def clone_volume(zkhandler, pool, name_src, name_new, force_flag=False):
|
||||||
|
# 1. Verify the volume
|
||||||
if not verifyVolume(zkhandler, pool, name_src):
|
if not verifyVolume(zkhandler, pool, name_src):
|
||||||
return False, 'ERROR: No volume with name "{}" is present in pool "{}".'.format(
|
return False, 'ERROR: No volume with name "{}" is present in pool "{}".'.format(
|
||||||
name_src, pool
|
name_src, pool
|
||||||
)
|
)
|
||||||
|
|
||||||
# 1. Clone the volume
|
volume_stats_raw = zkhandler.read(("volume.stats", f"{pool}/{name_src}"))
|
||||||
|
volume_stats = dict(json.loads(volume_stats_raw))
|
||||||
|
size_bytes = volume_stats["size"]
|
||||||
|
pool_information = getPoolInformation(zkhandler, pool)
|
||||||
|
pool_total_free_bytes = int(pool_information["stats"]["free_bytes"])
|
||||||
|
if size_bytes >= pool_total_free_bytes:
|
||||||
|
return (
|
||||||
|
False,
|
||||||
|
f"ERROR: Clone volume size '{format_bytes_tohuman(size_bytes)}' is greater than the available free space in the pool ('{format_bytes_tohuman(pool_information['stats']['free_bytes'])}')",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check if we're greater than 80% utilization after the create; error if so unless we have the force flag
|
||||||
|
pool_total_bytes = (
|
||||||
|
int(pool_information["stats"]["used_bytes"]) + pool_total_free_bytes
|
||||||
|
)
|
||||||
|
pool_safe_total_bytes = int(pool_total_bytes * 0.80)
|
||||||
|
pool_safe_free_bytes = pool_safe_total_bytes - int(
|
||||||
|
pool_information["stats"]["used_bytes"]
|
||||||
|
)
|
||||||
|
if size_bytes >= pool_safe_free_bytes and not force_flag:
|
||||||
|
return (
|
||||||
|
False,
|
||||||
|
f"ERROR: Clone volume size '{format_bytes_tohuman(size_bytes)}' is greater than the safe free space in the pool ('{format_bytes_tohuman(pool_safe_free_bytes)}' for 80% full); retry with force to ignore this error",
|
||||||
|
)
|
||||||
|
|
||||||
|
# 2. Clone the volume
|
||||||
retcode, stdout, stderr = common.run_os_command(
|
retcode, stdout, stderr = common.run_os_command(
|
||||||
"rbd copy {}/{} {}/{}".format(pool, name_src, pool, name_new)
|
"rbd copy {}/{} {}/{}".format(pool, name_src, pool, name_new)
|
||||||
)
|
)
|
||||||
|
@ -629,13 +655,13 @@ def clone_volume(zkhandler, pool, name_src, name_new):
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# 2. Get volume stats
|
# 3. Get volume stats
|
||||||
retcode, stdout, stderr = common.run_os_command(
|
retcode, stdout, stderr = common.run_os_command(
|
||||||
"rbd info --format json {}/{}".format(pool, name_new)
|
"rbd info --format json {}/{}".format(pool, name_new)
|
||||||
)
|
)
|
||||||
volstats = stdout
|
volstats = stdout
|
||||||
|
|
||||||
# 3. Add the new volume to Zookeeper
|
# 4. Add the new volume to Zookeeper
|
||||||
zkhandler.write(
|
zkhandler.write(
|
||||||
[
|
[
|
||||||
(("volume", f"{pool}/{name_new}"), ""),
|
(("volume", f"{pool}/{name_new}"), ""),
|
||||||
|
|
Loading…
Reference in New Issue