Added resizing based on %values. Included support for VG|PVS|FREE

This commit is contained in:
Jonas Vermeulen 2015-12-25 14:27:48 +01:00 committed by Matt Clay
parent 2095aa5100
commit 2149a7b1f6

View file

@ -42,8 +42,8 @@ options:
- The size of the logical volume, according to lvcreate(8) --size, by - The size of the logical volume, according to lvcreate(8) --size, by
default in megabytes or optionally with one of [bBsSkKmMgGtTpPeE] units; or default in megabytes or optionally with one of [bBsSkKmMgGtTpPeE] units; or
according to lvcreate(8) --extents as a percentage of [VG|PVS|FREE]; according to lvcreate(8) --extents as a percentage of [VG|PVS|FREE];
resizing is not supported with percentages. Float values must begin Float values must begin with a digit.
with a digit. Resizing using percentage values was not supported prior to 2.1.
state: state:
choices: [ "present", "absent" ] choices: [ "present", "absent" ]
default: present default: present
@ -88,6 +88,15 @@ EXAMPLES = '''
# Extend the logical volume to 1024m. # Extend the logical volume to 1024m.
- lvol: vg=firefly lv=test size=1024 - lvol: vg=firefly lv=test size=1024
# Extend the logical volume to consume all remaining space in the volume group
- lvol: vg=firefly lv=test size=+100%FREE
# Extend the logical volume to take all remaining space of the PVs
- lvol: vg=firefly lv=test size=100%PVS
# Resize the logical volume to % of VG
- lvol: vg-firefly lv=test size=80%VG force=yes
# Reduce the logical volume to 512m # Reduce the logical volume to 512m
- lvol: vg=firefly lv=test size=512 force=yes - lvol: vg=firefly lv=test size=512 force=yes
@ -99,23 +108,35 @@ EXAMPLES = '''
''' '''
import re import re
import logging
decimal_point = re.compile(r"(\.|,)") decimal_point = re.compile(r"(\d+)")
def mkversion(major, minor, patch): def mkversion(major, minor, patch):
return (1000 * 1000 * int(major)) + (1000 * int(minor)) + int(patch) return (1000 * 1000 * int(major)) + (1000 * int(minor)) + int(patch)
def parse_lvs(data): def parse_lvs(data):
lvs = [] lvs = []
for line in data.splitlines(): for line in data.splitlines():
parts = line.strip().split(';') parts = line.strip().split(';')
lvs.append({ lvs.append({
'name': parts[0], 'name': parts[0],
'size': int(decimal_point.split(parts[1])[0]), 'size': int(decimal_point.match(parts[1]).group(1))
}) })
return lvs return lvs
def parse_vgs(data):
vgs = []
for line in data.splitlines():
parts = line.strip().split(';')
vgs.append({
'name': parts[0],
'size': int(decimal_point.match(parts[1]).group(1)),
'free': int(decimal_point.match(parts[2]).group(1)),
'ext_size': int(decimal_point.match(parts[3]).group(1))
})
return vgs
def get_lvm_version(module): def get_lvm_version(module):
ver_cmd = module.get_bin_path("lvm", required=True) ver_cmd = module.get_bin_path("lvm", required=True)
@ -198,6 +219,21 @@ def main():
else: else:
unit = size_unit unit = size_unit
# Get information on volume group requested
vgs_cmd = module.get_bin_path("vgs", required=True)
rc, current_vgs, err = module.run_command(
"%s --noheadings -o vg_name,size,free,vg_extent_size --units %s --separator ';' %s" % (vgs_cmd, unit, vg))
if rc != 0:
if state == 'absent':
module.exit_json(changed=False, stdout="Volume group %s does not exist." % vg, stderr=False)
else:
module.fail_json(msg="Volume group %s does not exist." % vg, rc=rc, err=err)
vgs = parse_vgs(current_vgs)
this_vg = vgs[0]
# Get information on logical volume requested
lvs_cmd = module.get_bin_path("lvs", required=True) lvs_cmd = module.get_bin_path("lvs", required=True)
rc, current_lvs, err = module.run_command( rc, current_lvs, err = module.run_command(
"lvs --noheadings --nosuffix -o lv_name,size --units %s --separator ';' %s" % (unit, vg)) "lvs --noheadings --nosuffix -o lv_name,size --units %s --separator ';' %s" % (unit, vg))
@ -261,17 +297,58 @@ def main():
module.fail_json(msg="Failed to remove logical volume %s" % (lv), rc=rc, err=err) module.fail_json(msg="Failed to remove logical volume %s" % (lv), rc=rc, err=err)
elif size_opt == 'l': elif size_opt == 'l':
module.exit_json(changed=False, msg="Resizing extents with percentage not supported.") ### Resize LV based on % value
tool = None
size_free = this_vg['free']
if size_whole == 'VG' or size_whole == 'PVS':
size_requested = size_percent * this_vg['size'] / 100
else: # size_whole == 'FREE':
size_requested = size_percent * this_vg['free'] / 100
if '+' in size:
size_requested += this_lv['size']
if this_lv['size'] < size_requested:
if (size_free > 0) and (('+' not in size) or (size_free >= (size_requested - this_lv['size']))):
tool = module.get_bin_path("lvextend", required=True)
else:
module.fail_json(msg="Logical Volume %s could not be extended. Not enough free space left (%s%s required / %s%s available)" % (this_lv['name'], (size_requested - this_lv['size']), unit, size_free, unit))
elif this_lv['size'] > size_requested + this_vg['ext_size']: # more than an extent too large
if size_requested == 0:
module.fail_json(msg="Sorry, no shrinking of %s to 0 permitted." % (this_lv['name']))
elif not force:
module.fail_json(msg="Sorry, no shrinking of %s without force=yes" % (this_lv['name']))
else:
tool = module.get_bin_path("lvreduce", required=True)
tool = '%s %s' % (tool, '--force')
if tool:
if module.check_mode:
changed = True
else:
cmd = "%s -%s %s%s %s/%s" % (tool, size_opt, size, size_unit, vg, this_lv['name'])
rc, out, err = module.run_command(cmd)
if "Reached maximum COW size" in out:
module.fail_json(msg="Unable to resize %s to %s%s" % (lv, size, size_unit), rc=rc, err=err, out=out)
elif rc == 0:
changed = True
msg="Volume %s resized to %s%s" % (this_lv['name'], size_requested, unit)
elif "matches existing size" in err:
module.exit_json(changed=False, vg=vg, lv=this_lv['name'], size=this_lv['size'])
else:
module.fail_json(msg="Unable to resize %s to %s%s" % (lv, size, size_unit), rc=rc, err=err)
else: else:
### resize LV ### resize LV based on absolute values
tool = None tool = None
if int(size) > this_lv['size']: if int(size) > this_lv['size']:
tool = module.get_bin_path("lvextend", required=True) tool = module.get_bin_path("lvextend", required=True)
elif int(size) < this_lv['size']: elif int(size) < this_lv['size']:
if int(size) == 0:
module.fail_json(msg="Sorry, no shrinking of %s to 0 permitted." % (this_lv['name']))
if not force: if not force:
module.fail_json(msg="Sorry, no shrinking of %s without force=yes." % (this_lv['name'])) module.fail_json(msg="Sorry, no shrinking of %s without force=yes." % (this_lv['name']))
tool = module.get_bin_path("lvreduce", required=True) else:
tool = '%s %s' % (tool, '--force') tool = module.get_bin_path("lvreduce", required=True)
tool = '%s %s' % (tool, '--force')
if tool: if tool:
if module.check_mode: if module.check_mode: