Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"python.analysis.extraPaths": [
"./plugins/modules"
]
}
2 changes: 1 addition & 1 deletion galaxy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace: ibm
name: storage_virtualize
version: 2.1.0
version: 2.2.0
readme: README.md
authors:
- Shilpi Jain (github.com/Shilpi-J)
Expand Down
4 changes: 4 additions & 0 deletions plugins/module_utils/ibm_svc_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
from ansible.module_utils.six.moves.urllib.parse import quote
from ansible.module_utils.six.moves.urllib.error import HTTPError

# from urllib import open_url
# from six import quote
# from six import HTTPError


def svc_argument_spec():
"""
Expand Down
129 changes: 100 additions & 29 deletions plugins/modules/ibm_svc_manage_mirrored_volume.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-

# Copyright (C) 2021 IBM CORPORATION
# Author(s): Rohit Kumar <[email protected]>
# Author(s): Rohit Kumar <[email protected]>, Ian R Wright <[email protected]>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import absolute_import, division, print_function
Expand Down Expand Up @@ -70,7 +70,10 @@
a "standard mirror" volume gets created.
- If a "standard" mirrored volume exists and either I(PoolA) or I(PoolB)
is specified, the mirrored volume gets converted to a standard volume.
choices: [ local hyperswap, standard ]
- Transform allows a volume to be changed between thin and thick/generic provisioning and automatically deletes the old copy after sync
- Transform also allows Volume Mirror to be used for migration between pools. A volume will move to a different pool (within the same IO Group for non-disruption)
and the original copy will be deleted after synchronization
choices: [ local hyperswap, standard, transform ]
type: str
thin:
description:
Expand Down Expand Up @@ -173,6 +176,18 @@
name: "vol1"
state: present
size: "{{new_size}}"
- name: Convert a thick (100% provisioned) volume to thin provisioned with an rsize of 10%
block:
- name: transform a thick volume to thin provisioned with rsize of 10%
clustername: "{{clustername}}"
username: "{{username}}"
password: "{{password}}"
log_path: /tmp/playbook.debug
name: "vol1"
state: present
type: transform
thin: true
rsize: "10%"
'''

RETURN = '''#'''
Expand All @@ -195,7 +210,8 @@ def __init__(self):
poolB=dict(type='str', required=False),
size=dict(type='str', required=False),
thin=dict(type='bool', required=False),
type=dict(type='str', required=False, choices=['local hyperswap', 'standard']),
# added a new "type" called transform for thick/thin conversion and migration between pools using volume mirroring
type=dict(type='str', required=False, choices=['local hyperswap', 'standard', 'transform']),
grainsize=dict(type='str', required=False),
rsize=dict(type='str', required=False),
compressed=dict(type='bool', required=False),
Expand All @@ -215,6 +231,8 @@ def __init__(self):
self.isdrp = False
self.expand_flag = False
self.shrink_flag = False
# added an autodelete flag for use with transform
self.autodelete = False

# logging setup
log_path = self.module.params.get('log_path')
Expand Down Expand Up @@ -273,12 +291,14 @@ def basic_checks(self, data):
self.module.fail_json(msg="PoolB does not exist")
if self.state == "present" and not self.type and not self.size:
self.module.fail_json(msg="missing required argument: type")
if self.poolA and self.poolB:
# having both pools within a site is permitted as long as transform is the type
if self.poolA and self.poolB and self.type != "transform":
if self.poolA == self.poolB:
self.module.fail_json(msg="poolA and poolB cannot be same")
self.module.fail_json(msg="poolA and poolB cannot be same unless you are transforming a volume")
siteA, siteB = self.discover_site_from_pools()
if siteA != siteB and self.type == "standard":
self.module.fail_json(msg="To create Standard Mirrored volume, provide pools belonging to same site.")
# tranformation does not work between sites/systems
if siteA != siteB and (self.type == "standard" or siteA != siteB and self.type == "transform"):
self.module.fail_json(msg="To create Standard Mirrored volume or transform a volume, provide pools belonging to same site.")
if not self.poolA and not self.poolB and self.state == "present" and not self.size:
self.module.fail_json(msg="Both poolA and poolB cannot be empty")
if self.type == "local hyperswap" and self.state != 'absent':
Expand Down Expand Up @@ -333,8 +353,17 @@ def discover_vdisk_type(self, data):

def discover_site_from_pools(self):
self.log("Entering function discover_site_from_pools")
poolA_site = self.poolA_data['site_name']
poolB_site = self.poolB_data['site_name']
# if using a single site for volume transformation, setting the other "site" to be the same as the one that does exist for purposes of
# getting along with the code
if self.poolA and not self.poolB:
poolA_site = self.poolA_data['site_name']
poolB_site = poolA_site
elif self.poolB and not self.poolA:
poolB_site = self.poolB_data['site_name']
poolA_site = poolB_site
else:
poolA_site = self.poolA_data['site_name']
poolB_site = self.poolB_data['site_name']
return poolA_site, poolB_site

def vdisk_probe(self, data):
Expand All @@ -357,11 +386,17 @@ def vdisk_probe(self, data):
self.changebysize = existing_size - size_in_bytes
self.shrink_flag = True
if self.poolA and self.poolB:
#Volume transformation is not allowed if there's already a mirror in place
if self.type == "transform":
if (self.vdisk_type == "local hyperswap" or self.vdisk_type == "standard pool") and self.type == "transform":
self.module.fail_json(msg="HyperSwap or Standard Mirror Volumes cannot be transformed")
elif self.poolA == self.discovered_standard_vol_pool or self.poolB == self.discovered_standard_vol_pool:
props +=['addvdiskcopy']
if self.vdisk_type == "local hyperswap" and self.type == "standard":
self.module.fail_json(msg="HyperSwap Volume cannot be converted to standard mirror")
if self.vdisk_type == "standard mirror" or self.vdisk_type == "local hyperswap":
if (self.poolA == self.discovered_poolA or self.poolA == self.discovered_poolB)\
and (self.poolB == self.discovered_poolA or self.poolB == self.discovered_poolB) and not resizevolume_flag:
and (self.poolB == self.discovered_poolA or self.poolB == self.discovered_poolB) and not resizevolume_flag:
return props
elif not resizevolume_flag:
self.module.fail_json(msg="Pools for Standard Mirror or HyperSwap volume cannot be updated")
Expand All @@ -379,20 +414,27 @@ def vdisk_probe(self, data):
elif self.vdisk_type and not self.type:
self.module.fail_json(msg="missing required argument: type")
elif not self.poolA or not self.poolB:
if self.vdisk_type == "standard":
# transformation is allowed within a single pool.
if self.type == "transform" and self.vdisk_type == "standard":
if (self.poolA == self.discovered_standard_vol_pool) or (self.poolB == self.discovered_standard_vol_pool):
props += ['addvdiskcopy']
elif self.vdisk_type == "standard" and self.type != "transform":
if self.poolA == self.discovered_standard_vol_pool or self.poolB == self.discovered_standard_vol_pool:
self.log("Standard Volume already exists, no modifications done")
return props
if self.poolA:
if self.poolA == self.discovered_poolA or self.poolA == self.discovered_poolB:
props += ['rmvolumecopy']
else:
self.module.fail_json(msg="One of the input pools must belong to the Volume")
# Changed this. Needed to make sure that it wasn't erroneously deciding that a volume needed to be removed when transform was set
if self.type != "transform" and self.vdisk_type == "standard":
if self.poolA == self.discovered_poolA or self.poolA == self.discovered_poolB:
props += ['rmvolumecopy']
else:
self.module.fail_json(msg="One of the input pools must belong to the Volume")
elif self.poolB:
if self.poolB == self.discovered_poolA or self.poolB == self.discovered_poolB:
props += ['rmvolumecopy']
else:
self.module.fail_json(msg="One of the input pools must belong to the Volume")
if self.type != "transform" and self.vdisk_type == "standard":
if self.poolB == self.discovered_poolA or self.poolB == self.discovered_poolB:
props += ['rmvolumecopy']
else:
self.module.fail_json(msg="One of the input pools must belong to the Volume")
if not (self.poolA or not self.poolB) and not self.size:
if (self.system_topology == "hyperswap" and self.type == "local hyperswap"):
self.module.fail_json(msg="Type must be standard if either PoolA or PoolB is not specified.")
Expand All @@ -413,7 +455,7 @@ def resizevolume(self):
if self.vdisk_type == "local hyperswap" and self.expand_flag:
cmd = "expandvolume"
elif self.vdisk_type == "local hyperswap" and self.shrink_flag:
self.module.fail_json(msg="Size of a HyperSwap Volume cannot be shrinked")
self.module.fail_json(msg="A HyperSwap Volume cannot be reduced in size")
elif self.vdisk_type == "standard mirror" and self.expand_flag:
cmd = "expandvdisksize"
elif self.vdisk_type == "standard mirror" and self.shrink_flag:
Expand Down Expand Up @@ -566,21 +608,31 @@ def addvdiskcopy(self):
self.module.fail_json(msg="Parameter 'size' cannot be passed while converting a standard volume to Mirror Volume")
siteA, siteB = self.discover_site_from_pools()
if siteA != siteB:
self.module.fail_json(msg="To create Standard Mirrored volume, provide pools belonging to same site.")
self.module.fail_json(msg="To create Standard Mirror or to Transform a volume, provide pools belonging to same site.")
if self.poolA and (self.poolB == self.discovered_standard_vol_pool and self.poolA != self.discovered_standard_vol_pool):
cmdopts['mdiskgrp'] = self.poolA
elif self.poolB and (self.poolA == self.discovered_standard_vol_pool and self.poolB != self.discovered_standard_vol_pool):
cmdopts['mdiskgrp'] = self.poolB
# if the type selected is "transform" we want to allow migration to other pools or transformation within the same pool
# by setting the mdiskgrp to be equal to the only pool available
elif self.type == "transform":
if self.poolA == self.discovered_standard_vol_pool:
cmdopts['mdiskgrp'] = self.poolA
else:
if self.poolB == self.discovered_standard_vol_pool:
cmdopts['mdiskgrp'] = self.poolB
else:
self.module.fail_json(msg="One of the input pools must belong to the volume")
if self.compressed:
if self.compressed == True:
cmdopts['compressed'] = self.compressed
if self.grainsize:
cmdopts['grainsize'] = self.grainsize
if self.thin and self.rsize:
cmdopts['rsize'] = self.rsize
elif self.thin:
cmdopts['rsize'] = "2%"
# Set autoexpand to true. Short of having an autoexpand setting this should be the case for any thin volume
cmdopts['autoexpand'] = True
elif self.rsize and not self.thin:
self.module.fail_json(msg="To configure 'rsize', parameter 'thin' should be passed and the value should be 'true'.")
if self.deduplicated:
Expand All @@ -591,6 +643,9 @@ def addvdiskcopy(self):
self.module.fail_json(msg="To configure 'deduplicated', parameter 'thin' should be passed and the value should be 'true.'")
if self.isdrp and self.thin:
cmdopts['autoexpand'] = True
# adding the autodelete flag into command options
if self.autodelete == True:
cmdopts['autodelete'] = True
if self.module.check_mode:
self.changed = True
return
Expand Down Expand Up @@ -635,18 +690,31 @@ def vdisk_update(self, modify):
self.addvolumecopy()
elif 'addvdiskcopy' in modify:
self.isdrpool()
if self.type == "transform":
self.autodelete = True
self.addvdiskcopy()
elif 'rmvolumecopy' in modify:
self.rmvolumecopy()
elif 'resizevolume' in modify:
self.resizevolume()

def isdrpool(self):
poolA_drp = self.poolA_data['data_reduction']
poolB_drp = self.poolB_data['data_reduction']
isdrpool_list = [poolA_drp, poolB_drp]
if "yes" in isdrpool_list:
self.isdrp = True
poolA_drp={}
poolB_drp={}
if self.poolA and self.poolB:
poolA_drp = self.poolA_data['data_reduction']
poolB_drp = self.poolB_data['data_reduction']
isdrpool_list = [poolA_drp, poolB_drp]
if "yes" in isdrpool_list:
self.isdrp = True
else:
if (self.poolA or self.poolB) and self.type == "transform":
if self.poolA:
poolA_drp = self.poolA_data['data_reduction']
elif self.poolB:
poolB_drp = self.poolB_data['data_reduction']
if "yes" in poolA_drp or poolB_drp:
self.isdrp = True

def volume_delete(self):
self.log("Entering function volume_delete")
Expand Down Expand Up @@ -712,7 +780,7 @@ def apply(self):
if not self.type:
self.module.fail_json(msg="missing required argument: type")
# create_vdisk_flag = self.discover_site_from_pools()
if self.type == "standard":
elif self.type == "standard":
self.isdrpool()
self.vdisk_create()
msg = "Standard Mirrored Volume %s has been created." % self.name
Expand All @@ -725,7 +793,10 @@ def apply(self):
else:
# This is where we would modify if required
self.vdisk_update(modify)
msg = "Volume [%s] has been modified." % self.name
if self.type == "transform":
msg = "Volume [%s] is being transformed. The original copy will be deleted after synchronization" % self.name
else:
msg = "Volume [%s] has been modified." % self.name
changed = True
elif self.state == 'absent':
self.volume_delete()
Expand Down
Loading