na_ontap_qtree / na_ontap_gather_facts: qtree new params and modify operation / new subsets (#55825)
* qtree new parameters and modify action * fixing pylint offenses * fixing shippable fails * added igroup_info gather_subset * added qos_policy_info / qos_adaptive_policy_info gather_subsets * pylint fixes * fixing shippable test failure * requiring option flexvol_name for na_ontap_qtree module
This commit is contained in:
parent
ba9fee6c37
commit
6f0ac90ec3
2 changed files with 216 additions and 85 deletions
|
@ -32,12 +32,12 @@ options:
|
||||||
description:
|
description:
|
||||||
- When supplied, this argument will restrict the facts collected
|
- When supplied, this argument will restrict the facts collected
|
||||||
to a given subset. Possible values for this argument include
|
to a given subset. Possible values for this argument include
|
||||||
"aggregate_info", "cluster_node_info", "lun_info", "net_ifgrp_info",
|
"aggregate_info", "cluster_node_info", "igroup_info", "lun_info", "net_ifgrp_info",
|
||||||
"net_interface_info", "net_port_info", "nvme_info", "nvme_interface_info",
|
"net_interface_info", "net_port_info", "nvme_info", "nvme_interface_info",
|
||||||
"nvme_namespace_info", "nvme_subsystem_info", "ontap_version",
|
"nvme_namespace_info", "nvme_subsystem_info", "ontap_version",
|
||||||
"security_key_manager_key_info", "security_login_account_info",
|
"qos_adaptive_policy_info", "qos_policy_info", "security_key_manager_key_info",
|
||||||
"storage_failover_info", "volume_info", "vserver_info",
|
"security_login_account_info", "storage_failover_info", "volume_info",
|
||||||
"vserver_login_banner_info", "vserver_motd_info"
|
"vserver_info", "vserver_login_banner_info", "vserver_motd_info"
|
||||||
Can specify a list of values to include a larger subset. Values can also be used
|
Can specify a list of values to include a larger subset. Values can also be used
|
||||||
with an initial C(M(!)) to specify that a specific subset should
|
with an initial C(M(!)) to specify that a specific subset should
|
||||||
not be collected.
|
not be collected.
|
||||||
|
@ -103,7 +103,10 @@ ontap_facts:
|
||||||
"vserver_login_banner_info": {...},
|
"vserver_login_banner_info": {...},
|
||||||
"vserver_motd_info": {...},
|
"vserver_motd_info": {...},
|
||||||
"vserver_info": {...},
|
"vserver_info": {...},
|
||||||
"ontap_version": {...}
|
"ontap_version": {...},
|
||||||
|
"igroup_info": {...},
|
||||||
|
"qos_policy_info": {...},
|
||||||
|
"qos_adaptive_policy_info": {...}
|
||||||
}'
|
}'
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
@ -128,6 +131,7 @@ HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
|
||||||
|
|
||||||
|
|
||||||
class NetAppONTAPGatherFacts(object):
|
class NetAppONTAPGatherFacts(object):
|
||||||
|
'''Class with gather facts methods'''
|
||||||
|
|
||||||
def __init__(self, module):
|
def __init__(self, module):
|
||||||
self.module = module
|
self.module = module
|
||||||
|
@ -278,6 +282,37 @@ class NetAppONTAPGatherFacts(object):
|
||||||
},
|
},
|
||||||
'min_version': '0',
|
'min_version': '0',
|
||||||
},
|
},
|
||||||
|
'igroup_info': {
|
||||||
|
'method': self.get_generic_get_iter,
|
||||||
|
'kwargs': {
|
||||||
|
'call': 'igroup-get-iter',
|
||||||
|
'attribute': 'initiator-group-info',
|
||||||
|
'field': ('vserver', 'initiator-group-name'),
|
||||||
|
'query': {'max-records': '1024'},
|
||||||
|
},
|
||||||
|
'min_version': '0',
|
||||||
|
},
|
||||||
|
'qos_policy_info': {
|
||||||
|
'method': self.get_generic_get_iter,
|
||||||
|
'kwargs': {
|
||||||
|
'call': 'qos-policy-group-get-iter',
|
||||||
|
'attribute': 'qos-policy-group-info',
|
||||||
|
'field': 'policy-group',
|
||||||
|
'query': {'max-records': '1024'},
|
||||||
|
},
|
||||||
|
'min_version': '0',
|
||||||
|
},
|
||||||
|
# supported in ONTAP 9.3 and onwards
|
||||||
|
'qos_adaptive_policy_info': {
|
||||||
|
'method': self.get_generic_get_iter,
|
||||||
|
'kwargs': {
|
||||||
|
'call': 'qos-adaptive-policy-group-get-iter',
|
||||||
|
'attribute': 'qos-adaptive-policy-group-info',
|
||||||
|
'field': 'policy-group',
|
||||||
|
'query': {'max-records': '1024'},
|
||||||
|
},
|
||||||
|
'min_version': '130',
|
||||||
|
},
|
||||||
# supported in ONTAP 9.4 and onwards
|
# supported in ONTAP 9.4 and onwards
|
||||||
'nvme_info': {
|
'nvme_info': {
|
||||||
'method': self.get_generic_get_iter,
|
'method': self.get_generic_get_iter,
|
||||||
|
@ -327,34 +362,41 @@ class NetAppONTAPGatherFacts(object):
|
||||||
self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
|
self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
|
||||||
|
|
||||||
def ontapi(self):
|
def ontapi(self):
|
||||||
|
'''Method to get ontapi version'''
|
||||||
|
|
||||||
api = 'system-get-ontapi-version'
|
api = 'system-get-ontapi-version'
|
||||||
api_call = netapp_utils.zapi.NaElement(api)
|
api_call = netapp_utils.zapi.NaElement(api)
|
||||||
try:
|
try:
|
||||||
results = self.server.invoke_successfully(api_call, enable_tunneling=False)
|
results = self.server.invoke_successfully(api_call, enable_tunneling=False)
|
||||||
ontapi_version = results.get_child_content('minor-version')
|
ontapi_version = results.get_child_content('minor-version')
|
||||||
return ontapi_version if ontapi_version is not None else '0'
|
return ontapi_version if ontapi_version is not None else '0'
|
||||||
except netapp_utils.zapi.NaApiError as e:
|
except netapp_utils.zapi.NaApiError as error:
|
||||||
self.module.fail_json(msg="Error calling API %s: %s" %
|
self.module.fail_json(msg="Error calling API %s: %s" %
|
||||||
(api, to_native(e)), exception=traceback.format_exc())
|
(api, to_native(error)), exception=traceback.format_exc())
|
||||||
|
|
||||||
def call_api(self, call, query=None):
|
def call_api(self, call, query=None):
|
||||||
|
'''Main method to run an API call'''
|
||||||
|
|
||||||
api_call = netapp_utils.zapi.NaElement(call)
|
api_call = netapp_utils.zapi.NaElement(call)
|
||||||
result = None
|
result = None
|
||||||
|
|
||||||
if query:
|
if query:
|
||||||
for k, v in query.items():
|
for key, val in query.items():
|
||||||
# Can v be nested?
|
# Can val be nested?
|
||||||
api_call.add_new_child(k, v)
|
api_call.add_new_child(key, val)
|
||||||
try:
|
try:
|
||||||
result = self.server.invoke_successfully(api_call, enable_tunneling=False)
|
result = self.server.invoke_successfully(api_call, enable_tunneling=False)
|
||||||
return result
|
return result
|
||||||
except netapp_utils.zapi.NaApiError as e:
|
except netapp_utils.zapi.NaApiError as error:
|
||||||
if call in ['security-key-manager-key-get-iter']:
|
if call in ['security-key-manager-key-get-iter']:
|
||||||
return result
|
return result
|
||||||
else:
|
else:
|
||||||
self.module.fail_json(msg="Error calling API %s: %s" % (call, to_native(e)), exception=traceback.format_exc())
|
self.module.fail_json(msg="Error calling API %s: %s"
|
||||||
|
% (call, to_native(error)), exception=traceback.format_exc())
|
||||||
|
|
||||||
def get_ifgrp_info(self):
|
def get_ifgrp_info(self):
|
||||||
|
'''Method to get network port ifgroups info'''
|
||||||
|
|
||||||
try:
|
try:
|
||||||
net_port_info = self.netapp_info['net_port_info']
|
net_port_info = self.netapp_info['net_port_info']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -372,14 +414,22 @@ class NetAppONTAPGatherFacts(object):
|
||||||
query = dict()
|
query = dict()
|
||||||
query['node'], query['ifgrp-name'] = ifgrp.split(':')
|
query['node'], query['ifgrp-name'] = ifgrp.split(':')
|
||||||
|
|
||||||
tmp = self.get_generic_get_iter('net-port-ifgrp-get', field=('node', 'ifgrp-name'), attribute='net-ifgrp-info', query=query, children='attributes')
|
tmp = self.get_generic_get_iter('net-port-ifgrp-get', field=('node', 'ifgrp-name'),
|
||||||
|
attribute='net-ifgrp-info', query=query)
|
||||||
net_ifgrp_info = net_ifgrp_info.copy()
|
net_ifgrp_info = net_ifgrp_info.copy()
|
||||||
net_ifgrp_info.update(tmp)
|
net_ifgrp_info.update(tmp)
|
||||||
return net_ifgrp_info
|
return net_ifgrp_info
|
||||||
|
|
||||||
def get_generic_get_iter(self, call, attribute=None, field=None, query=None, children='attributes-list'):
|
def get_generic_get_iter(self, call, attribute=None, field=None, query=None):
|
||||||
|
'''Method to run a generic get-iter call'''
|
||||||
|
|
||||||
generic_call = self.call_api(call, query)
|
generic_call = self.call_api(call, query)
|
||||||
|
|
||||||
|
if call == 'net-port-ifgrp-get':
|
||||||
|
children = 'attributes'
|
||||||
|
else:
|
||||||
|
children = 'attributes-list'
|
||||||
|
|
||||||
if generic_call is None:
|
if generic_call is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -394,25 +444,27 @@ class NetAppONTAPGatherFacts(object):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
for child in attributes_list.get_children():
|
for child in attributes_list.get_children():
|
||||||
d = xmltodict.parse(child.to_string(), xml_attribs=False)
|
dic = xmltodict.parse(child.to_string(), xml_attribs=False)
|
||||||
|
|
||||||
if attribute is not None:
|
if attribute is not None:
|
||||||
d = d[attribute]
|
dic = dic[attribute]
|
||||||
|
|
||||||
if isinstance(field, str):
|
if isinstance(field, str):
|
||||||
unique_key = _finditem(d, field)
|
unique_key = _finditem(dic, field)
|
||||||
out = out.copy()
|
out = out.copy()
|
||||||
out.update({unique_key: convert_keys(json.loads(json.dumps(d)))})
|
out.update({unique_key: convert_keys(json.loads(json.dumps(dic)))})
|
||||||
elif isinstance(field, tuple):
|
elif isinstance(field, tuple):
|
||||||
unique_key = ':'.join([_finditem(d, el) for el in field])
|
unique_key = ':'.join([_finditem(dic, el) for el in field])
|
||||||
out = out.copy()
|
out = out.copy()
|
||||||
out.update({unique_key: convert_keys(json.loads(json.dumps(d)))})
|
out.update({unique_key: convert_keys(json.loads(json.dumps(dic)))})
|
||||||
else:
|
else:
|
||||||
out.append(convert_keys(json.loads(json.dumps(d))))
|
out.append(convert_keys(json.loads(json.dumps(dic))))
|
||||||
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
def get_all(self, gather_subset):
|
def get_all(self, gather_subset):
|
||||||
|
'''Method to get all subsets'''
|
||||||
|
|
||||||
results = netapp_utils.get_cserver(self.server)
|
results = netapp_utils.get_cserver(self.server)
|
||||||
cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
|
cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
|
||||||
netapp_utils.ems_log_event("na_ontap_gather_facts", cserver)
|
netapp_utils.ems_log_event("na_ontap_gather_facts", cserver)
|
||||||
|
@ -430,6 +482,8 @@ class NetAppONTAPGatherFacts(object):
|
||||||
return self.netapp_info
|
return self.netapp_info
|
||||||
|
|
||||||
def get_subset(self, gather_subset, version):
|
def get_subset(self, gather_subset, version):
|
||||||
|
'''Method to get a single subset'''
|
||||||
|
|
||||||
runable_subsets = set()
|
runable_subsets = set()
|
||||||
exclude_subsets = set()
|
exclude_subsets = set()
|
||||||
usable_subsets = [key for key in self.fact_subsets.keys() if version >= self.fact_subsets[key]['min_version']]
|
usable_subsets = [key for key in self.fact_subsets.keys() if version >= self.fact_subsets[key]['min_version']]
|
||||||
|
@ -471,9 +525,9 @@ def __finditem(obj, key):
|
||||||
|
|
||||||
if key in obj:
|
if key in obj:
|
||||||
return obj[key]
|
return obj[key]
|
||||||
for dummy, v in obj.items():
|
for dummy, val in obj.items():
|
||||||
if isinstance(v, dict):
|
if isinstance(val, dict):
|
||||||
item = __finditem(v, key)
|
item = __finditem(val, key)
|
||||||
if item is not None:
|
if item is not None:
|
||||||
return item
|
return item
|
||||||
return None
|
return None
|
||||||
|
@ -487,18 +541,22 @@ def _finditem(obj, key):
|
||||||
raise KeyError(key)
|
raise KeyError(key)
|
||||||
|
|
||||||
|
|
||||||
def convert_keys(d):
|
def convert_keys(d_param):
|
||||||
|
'''Method to convert hyphen to underscore'''
|
||||||
|
|
||||||
out = {}
|
out = {}
|
||||||
if isinstance(d, dict):
|
if isinstance(d_param, dict):
|
||||||
for k, v in d.items():
|
for key, val in d_param.items():
|
||||||
v = convert_keys(v)
|
val = convert_keys(val)
|
||||||
out[k.replace('-', '_')] = v
|
out[key.replace('-', '_')] = val
|
||||||
else:
|
else:
|
||||||
return d
|
return d_param
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
'''Execute action'''
|
||||||
|
|
||||||
argument_spec = netapp_utils.na_ontap_host_argument_spec()
|
argument_spec = netapp_utils.na_ontap_host_argument_spec()
|
||||||
argument_spec.update(dict(
|
argument_spec.update(dict(
|
||||||
state=dict(default='info', choices=['info']),
|
state=dict(default='info', choices=['info']),
|
||||||
|
@ -520,10 +578,10 @@ def main():
|
||||||
gather_subset = module.params['gather_subset']
|
gather_subset = module.params['gather_subset']
|
||||||
if gather_subset is None:
|
if gather_subset is None:
|
||||||
gather_subset = ['all']
|
gather_subset = ['all']
|
||||||
v = NetAppONTAPGatherFacts(module)
|
gf_obj = NetAppONTAPGatherFacts(module)
|
||||||
g = v.get_all(gather_subset)
|
gf_all = gf_obj.get_all(gather_subset)
|
||||||
result = {'state': state, 'changed': False}
|
result = {'state': state, 'changed': False}
|
||||||
module.exit_json(ansible_facts={'ontap_facts': g}, **result)
|
module.exit_json(ansible_facts={'ontap_facts': gf_all}, **result)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -2,11 +2,9 @@
|
||||||
|
|
||||||
# (c) 2018-2019, NetApp, Inc
|
# (c) 2018-2019, NetApp, Inc
|
||||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
# 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
|
from __future__ import absolute_import, division, print_function
|
||||||
__metaclass__ = type
|
__metaclass__ = type
|
||||||
|
|
||||||
|
|
||||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||||
'status': ['preview'],
|
'status': ['preview'],
|
||||||
'supported_by': 'certified'}
|
'supported_by': 'certified'}
|
||||||
|
@ -46,12 +44,35 @@ options:
|
||||||
flexvol_name:
|
flexvol_name:
|
||||||
description:
|
description:
|
||||||
- The name of the FlexVol the qtree should exist on. Required when C(state=present).
|
- The name of the FlexVol the qtree should exist on. Required when C(state=present).
|
||||||
|
required: true
|
||||||
|
|
||||||
vserver:
|
vserver:
|
||||||
description:
|
description:
|
||||||
- The name of the vserver to use.
|
- The name of the vserver to use.
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
|
export_policy:
|
||||||
|
description:
|
||||||
|
- The name of the export policy to apply.
|
||||||
|
version_added: '2.9'
|
||||||
|
|
||||||
|
security_style:
|
||||||
|
description:
|
||||||
|
- The security style for the qtree.
|
||||||
|
choices: ['unix', 'ntfs', 'mixed']
|
||||||
|
version_added: '2.9'
|
||||||
|
|
||||||
|
oplocks:
|
||||||
|
description:
|
||||||
|
- Whether the oplocks should be enabled or not for the qtree.
|
||||||
|
choices: ['enabled', 'disabled']
|
||||||
|
version_added: '2.9'
|
||||||
|
|
||||||
|
unix_permissions:
|
||||||
|
description:
|
||||||
|
- File permissions bits of the qtree.
|
||||||
|
version_added: '2.9'
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
EXAMPLES = """
|
EXAMPLES = """
|
||||||
|
@ -81,26 +102,31 @@ RETURN = """
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils._text import to_native
|
from ansible.module_utils._text import to_native
|
||||||
import ansible.module_utils.netapp as netapp_utils
|
import ansible.module_utils.netapp as netapp_utils
|
||||||
|
from ansible.module_utils.netapp_module import NetAppModule
|
||||||
|
|
||||||
HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
|
HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
|
||||||
|
|
||||||
|
|
||||||
class NetAppOntapQTree(object):
|
class NetAppOntapQTree(object):
|
||||||
|
'''Class with qtree operations'''
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
|
self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
|
||||||
self.argument_spec.update(dict(
|
self.argument_spec.update(dict(
|
||||||
state=dict(required=False, choices=[
|
state=dict(required=False,
|
||||||
'present', 'absent'], default='present'),
|
choices=['present', 'absent'],
|
||||||
|
default='present'),
|
||||||
name=dict(required=True, type='str'),
|
name=dict(required=True, type='str'),
|
||||||
from_name=dict(required=False, type='str'),
|
from_name=dict(required=False, type='str'),
|
||||||
flexvol_name=dict(type='str'),
|
flexvol_name=dict(type='str'),
|
||||||
vserver=dict(required=True, type='str'),
|
vserver=dict(required=True, type='str'),
|
||||||
|
export_policy=dict(required=False, type='str'),
|
||||||
|
security_style=dict(required=False, choices=['unix', 'ntfs', 'mixed']),
|
||||||
|
oplocks=dict(required=False, choices=['enabled', 'disabled']),
|
||||||
|
unix_permissions=dict(required=False, type='str'),
|
||||||
))
|
))
|
||||||
|
|
||||||
self.module = AnsibleModule(
|
self.module = AnsibleModule(
|
||||||
|
@ -110,81 +136,93 @@ class NetAppOntapQTree(object):
|
||||||
],
|
],
|
||||||
supports_check_mode=True
|
supports_check_mode=True
|
||||||
)
|
)
|
||||||
|
self.na_helper = NetAppModule()
|
||||||
p = self.module.params
|
self.parameters = self.na_helper.set_parameters(self.module.params)
|
||||||
|
|
||||||
# set up state variables
|
|
||||||
self.state = p['state']
|
|
||||||
self.name = p['name']
|
|
||||||
self.from_name = p['from_name']
|
|
||||||
self.flexvol_name = p['flexvol_name']
|
|
||||||
self.vserver = p['vserver']
|
|
||||||
|
|
||||||
if HAS_NETAPP_LIB is False:
|
if HAS_NETAPP_LIB is False:
|
||||||
self.module.fail_json(
|
self.module.fail_json(
|
||||||
msg="the python NetApp-Lib module is required")
|
msg="the python NetApp-Lib module is required")
|
||||||
else:
|
else:
|
||||||
self.server = netapp_utils.setup_na_ontap_zapi(
|
self.server = netapp_utils.setup_na_ontap_zapi(
|
||||||
module=self.module, vserver=self.vserver)
|
module=self.module, vserver=self.parameters['vserver'])
|
||||||
|
|
||||||
def get_qtree(self, name=None):
|
def get_qtree(self, name=None):
|
||||||
"""
|
"""
|
||||||
Checks if the qtree exists.
|
Checks if the qtree exists.
|
||||||
|
:param:
|
||||||
|
name : qtree name
|
||||||
:return:
|
:return:
|
||||||
True if qtree found
|
Details about the qtree
|
||||||
False if qtree is not found
|
False if qtree is not found
|
||||||
:rtype: bool
|
:rtype: bool
|
||||||
"""
|
"""
|
||||||
if name is None:
|
if name is None:
|
||||||
name = self.name
|
name = self.parameters['name']
|
||||||
|
|
||||||
qtree_list_iter = netapp_utils.zapi.NaElement('qtree-list-iter')
|
qtree_list_iter = netapp_utils.zapi.NaElement('qtree-list-iter')
|
||||||
query_details = netapp_utils.zapi.NaElement.create_node_with_children(
|
query_details = netapp_utils.zapi.NaElement.create_node_with_children(
|
||||||
'qtree-info', **{'vserver': self.vserver,
|
'qtree-info', **{'vserver': self.parameters['vserver'],
|
||||||
'volume': self.flexvol_name,
|
'volume': self.parameters['flexvol_name'],
|
||||||
'qtree': name})
|
'qtree': name})
|
||||||
|
|
||||||
query = netapp_utils.zapi.NaElement('query')
|
query = netapp_utils.zapi.NaElement('query')
|
||||||
query.add_child_elem(query_details)
|
query.add_child_elem(query_details)
|
||||||
qtree_list_iter.add_child_elem(query)
|
qtree_list_iter.add_child_elem(query)
|
||||||
|
|
||||||
result = self.server.invoke_successfully(qtree_list_iter,
|
result = self.server.invoke_successfully(qtree_list_iter,
|
||||||
enable_tunneling=True)
|
enable_tunneling=True)
|
||||||
|
return_q = False
|
||||||
if (result.get_child_by_name('num-records') and
|
if (result.get_child_by_name('num-records') and
|
||||||
int(result.get_child_content('num-records')) >= 1):
|
int(result.get_child_content('num-records')) >= 1):
|
||||||
return True
|
return_q = {'export_policy': result['attributes-list']['qtree-info']['export-policy'],
|
||||||
else:
|
'unix_permissions': result['attributes-list']['qtree-info']['mode'],
|
||||||
return False
|
'oplocks': result['attributes-list']['qtree-info']['oplocks'],
|
||||||
|
'security_style': result['attributes-list']['qtree-info']['security-style']}
|
||||||
|
|
||||||
|
return return_q
|
||||||
|
|
||||||
def create_qtree(self):
|
def create_qtree(self):
|
||||||
|
"""
|
||||||
|
Create a qtree
|
||||||
|
"""
|
||||||
|
options = {'qtree': self.parameters['name'], 'volume': self.parameters['flexvol_name']}
|
||||||
|
if self.parameters.get('export_policy'):
|
||||||
|
options['export-policy'] = self.parameters['export_policy']
|
||||||
|
if self.parameters.get('security_style'):
|
||||||
|
options['security-style'] = self.parameters['security_style']
|
||||||
|
if self.parameters.get('oplocks'):
|
||||||
|
options['oplocks'] = self.parameters['oplocks']
|
||||||
|
if self.parameters.get('unix_permissions'):
|
||||||
|
options['mode'] = self.parameters['unix_permissions']
|
||||||
qtree_create = netapp_utils.zapi.NaElement.create_node_with_children(
|
qtree_create = netapp_utils.zapi.NaElement.create_node_with_children(
|
||||||
'qtree-create', **{'volume': self.flexvol_name,
|
'qtree-create', **options)
|
||||||
'qtree': self.name})
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.server.invoke_successfully(qtree_create,
|
self.server.invoke_successfully(qtree_create,
|
||||||
enable_tunneling=True)
|
enable_tunneling=True)
|
||||||
except netapp_utils.zapi.NaApiError as e:
|
except netapp_utils.zapi.NaApiError as error:
|
||||||
self.module.fail_json(msg="Error provisioning qtree %s: %s" % (self.name, to_native(e)),
|
self.module.fail_json(msg="Error provisioning qtree %s: %s"
|
||||||
|
% (self.parameters['name'], to_native(error)),
|
||||||
exception=traceback.format_exc())
|
exception=traceback.format_exc())
|
||||||
|
|
||||||
def delete_qtree(self):
|
def delete_qtree(self):
|
||||||
path = '/vol/%s/%s' % (self.flexvol_name, self.name)
|
"""
|
||||||
|
Delete a qtree
|
||||||
|
"""
|
||||||
|
path = '/vol/%s/%s' % (self.parameters['flexvol_name'], self.parameters['name'])
|
||||||
qtree_delete = netapp_utils.zapi.NaElement.create_node_with_children(
|
qtree_delete = netapp_utils.zapi.NaElement.create_node_with_children(
|
||||||
'qtree-delete', **{'qtree': path})
|
'qtree-delete', **{'qtree': path})
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.server.invoke_successfully(qtree_delete,
|
self.server.invoke_successfully(qtree_delete,
|
||||||
enable_tunneling=True)
|
enable_tunneling=True)
|
||||||
except netapp_utils.zapi.NaApiError as e:
|
except netapp_utils.zapi.NaApiError as error:
|
||||||
self.module.fail_json(msg="Error deleting qtree %s: %s" % (path, to_native(e)),
|
self.module.fail_json(msg="Error deleting qtree %s: %s" % (path, to_native(error)),
|
||||||
exception=traceback.format_exc())
|
exception=traceback.format_exc())
|
||||||
|
|
||||||
def rename_qtree(self):
|
def rename_qtree(self):
|
||||||
path = '/vol/%s/%s' % (self.flexvol_name, self.from_name)
|
"""
|
||||||
new_path = '/vol/%s/%s' % (self.flexvol_name, self.name)
|
Rename a qtree
|
||||||
|
"""
|
||||||
|
path = '/vol/%s/%s' % (self.parameters['flexvol_name'], self.parameters['from_name'])
|
||||||
|
new_path = '/vol/%s/%s' % (self.parameters['flexvol_name'], self.parameters['name'])
|
||||||
qtree_rename = netapp_utils.zapi.NaElement.create_node_with_children(
|
qtree_rename = netapp_utils.zapi.NaElement.create_node_with_children(
|
||||||
'qtree-rename', **{'qtree': path,
|
'qtree-rename', **{'qtree': path,
|
||||||
'new-qtree-name': new_path})
|
'new-qtree-name': new_path})
|
||||||
|
@ -192,25 +230,57 @@ class NetAppOntapQTree(object):
|
||||||
try:
|
try:
|
||||||
self.server.invoke_successfully(qtree_rename,
|
self.server.invoke_successfully(qtree_rename,
|
||||||
enable_tunneling=True)
|
enable_tunneling=True)
|
||||||
except netapp_utils.zapi.NaApiError as e:
|
except netapp_utils.zapi.NaApiError as error:
|
||||||
self.module.fail_json(msg="Error renaming qtree %s: %s" % (self.from_name, to_native(e)),
|
self.module.fail_json(msg="Error renaming qtree %s: %s"
|
||||||
|
% (self.parameters['from_name'], to_native(error)),
|
||||||
|
exception=traceback.format_exc())
|
||||||
|
|
||||||
|
def modify_qtree(self):
|
||||||
|
"""
|
||||||
|
Modify a qtree
|
||||||
|
"""
|
||||||
|
options = {'qtree': self.parameters['name'], 'volume': self.parameters['flexvol_name']}
|
||||||
|
if self.parameters.get('export_policy'):
|
||||||
|
options['export-policy'] = self.parameters['export_policy']
|
||||||
|
if self.parameters.get('security_style'):
|
||||||
|
options['security-style'] = self.parameters['security_style']
|
||||||
|
if self.parameters.get('oplocks'):
|
||||||
|
options['oplocks'] = self.parameters['oplocks']
|
||||||
|
if self.parameters.get('unix_permissions'):
|
||||||
|
options['mode'] = self.parameters['unix_permissions']
|
||||||
|
qtree_modify = netapp_utils.zapi.NaElement.create_node_with_children(
|
||||||
|
'qtree-modify', **options)
|
||||||
|
try:
|
||||||
|
self.server.invoke_successfully(qtree_modify, enable_tunneling=True)
|
||||||
|
except netapp_utils.zapi.NaApiError as error:
|
||||||
|
self.module.fail_json(msg='Error modifying qtree %s: %s'
|
||||||
|
% (self.parameters['name'], to_native(error)),
|
||||||
exception=traceback.format_exc())
|
exception=traceback.format_exc())
|
||||||
|
|
||||||
def apply(self):
|
def apply(self):
|
||||||
changed = False
|
'''Call create/delete/modify/rename operations'''
|
||||||
qtree_exists = False
|
# changed = False
|
||||||
rename_qtree = False
|
# rename_qtree = False
|
||||||
|
# modified_qtree = None
|
||||||
|
changed, rename_qtree, modified_qtree = False, False, None
|
||||||
netapp_utils.ems_log_event("na_ontap_qtree", self.server)
|
netapp_utils.ems_log_event("na_ontap_qtree", self.server)
|
||||||
qtree_detail = self.get_qtree()
|
qtree_detail = self.get_qtree()
|
||||||
if qtree_detail:
|
if qtree_detail:
|
||||||
qtree_exists = True
|
# delete or modify qtree
|
||||||
if self.state == 'absent': # delete
|
if self.parameters['state'] == 'absent': # delete
|
||||||
changed = True
|
changed = True
|
||||||
elif self.state == 'present':
|
else:
|
||||||
|
modified_qtree = self.na_helper.get_modified_attributes(
|
||||||
|
qtree_detail, self.parameters)
|
||||||
|
if modified_qtree is not None:
|
||||||
|
changed = True
|
||||||
|
elif self.parameters['state'] == 'present':
|
||||||
# create or rename qtree
|
# create or rename qtree
|
||||||
if self.from_name:
|
if self.parameters.get('from_name'):
|
||||||
if self.get_qtree(self.from_name) is None:
|
if self.get_qtree(self.parameters['from_name']) is None:
|
||||||
self.module.fail_json(msg="Error renaming qtree %s: does not exists" % self.from_name)
|
self.module.fail_json(
|
||||||
|
msg="Error renaming qtree %s: does not exists"
|
||||||
|
% self.parameters['from_name'])
|
||||||
else:
|
else:
|
||||||
changed = True
|
changed = True
|
||||||
rename_qtree = True
|
rename_qtree = True
|
||||||
|
@ -220,20 +290,23 @@ class NetAppOntapQTree(object):
|
||||||
if self.module.check_mode:
|
if self.module.check_mode:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
if self.state == 'present':
|
if self.parameters['state'] == 'present':
|
||||||
if rename_qtree:
|
if rename_qtree:
|
||||||
self.rename_qtree()
|
self.rename_qtree()
|
||||||
|
elif modified_qtree:
|
||||||
|
self.modify_qtree()
|
||||||
else:
|
else:
|
||||||
self.create_qtree()
|
self.create_qtree()
|
||||||
elif self.state == 'absent':
|
elif self.parameters['state'] == 'absent':
|
||||||
self.delete_qtree()
|
self.delete_qtree()
|
||||||
|
|
||||||
self.module.exit_json(changed=changed)
|
self.module.exit_json(changed=changed)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
v = NetAppOntapQTree()
|
'''Apply qtree operations from playbook'''
|
||||||
v.apply()
|
qtree_obj = NetAppOntapQTree()
|
||||||
|
qtree_obj.apply()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
Loading…
Reference in a new issue