1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00

Commit first set of 5 netapp modules (#40486)

* Commit first set of 5 netapp modules

* Documentation changes

* Fix module

* move future

* fix issues

* fix aggregate

* fix issues matt found

* fix redundant requires

* fix issues

* documentation change
This commit is contained in:
Chris Archibald 2018-05-24 12:59:18 -07:00 committed by Matt Davis
parent da578b8fff
commit 3bae9ed00c
5 changed files with 1382 additions and 0 deletions

View file

@ -0,0 +1,359 @@
#!/usr/bin/python
# (c) 2018, NetApp, Inc
# 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
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
module: na_ontap_aggregate
short_description: Manage NetApp ONTAP aggregates.
extends_documentation_fragment:
- netapp.na_ontap
version_added: '2.6'
author: Sumit Kumar (sumit4@netapp.com), Suhas Bangalore Shekar (bsuhas@netapp.com)
description:
- Create or destroy aggregates on NetApp cDOT.
options:
state:
description:
- Whether the specified aggregate should exist or not.
choices: ['present', 'absent']
default: 'present'
service_state:
description:
- Whether the specified aggregate should be enabled or disabled. Creates aggregate if doesnt exist.
choices: ['online', 'offline']
name:
required: true
description:
- The name of the aggregate to manage.
rename:
description:
- The name of the aggregate that replaces the current name.
nodes:
description:
- List of node for the aggregate
disk_count:
description:
- Number of disks to place into the aggregate, including parity disks.
- The disks in this newly-created aggregate come from the spare disk pool.
- The smallest disks in this pool join the aggregate first, unless the C(disk-size) argument is provided.
- Either C(disk-count) or C(disks) must be supplied. Range [0..2^31-1].
- Required when C(state=present).
unmount_volumes:
type: bool
description:
- If set to "TRUE", this option specifies that all of the volumes hosted by the given aggregate are to be unmounted
- before the offline operation is executed.
- By default, the system will reject any attempt to offline an aggregate that hosts one or more online volumes.
'''
EXAMPLES = """
- name: Create Aggregates
na_ontap_aggregate:
state: present
service_state: online
name: ansibleAggr
disk_count: 1
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
- name: Manage Aggregates
na_ontap_aggregate:
state: present
service_state: offline
unmount_volumes: true
name: ansibleAggr
disk_count: 1
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
- name: Rename Aggregates
na_ontap_aggregate:
state: present
service_state: online
name: ansibleAggr
rename: ansibleAggr2
disk_count: 1
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
- name: Delete Aggregates
na_ontap_aggregate:
state: absent
service_state: offline
unmount_volumes: true
name: ansibleAggr
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
"""
RETURN = """
"""
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
import ansible.module_utils.netapp as netapp_utils
HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
class NetAppOntapAggregate(object):
''' object initialize and class methods '''
def __init__(self):
self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
self.argument_spec.update(dict(
state=dict(required=False, choices=[
'present', 'absent'], default='present'),
service_state=dict(required=False, choices=['online', 'offline']),
name=dict(required=True, type='str'),
rename=dict(required=False, type='str'),
disk_count=dict(required=False, type='int', default=None),
nodes=dict(required=False, type='list'),
unmount_volumes=dict(required=False, type='bool'),
))
self.module = AnsibleModule(
argument_spec=self.argument_spec,
required_if=[
('service_state', 'offline', ['unmount_volumes'])
],
supports_check_mode=True
)
parameters = self.module.params
# set up state variables
self.state = parameters['state']
self.service_state = parameters['service_state']
self.name = parameters['name']
self.rename = parameters['rename']
self.disk_count = parameters['disk_count']
self.nodes = parameters['nodes']
self.unmount_volumes = parameters['unmount_volumes']
if HAS_NETAPP_LIB is False:
self.module.fail_json(
msg="the python NetApp-Lib module is required")
else:
self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
def get_aggr(self):
"""
Checks if aggregate exists.
:return:
True if aggregate found
False if aggregate is not found
:rtype: bool
"""
aggr_get_iter = netapp_utils.zapi.NaElement('aggr-get-iter')
query_details = netapp_utils.zapi.NaElement.create_node_with_children(
'aggr-attributes', **{'aggregate-name': self.name})
query = netapp_utils.zapi.NaElement('query')
query.add_child_elem(query_details)
aggr_get_iter.add_child_elem(query)
try:
result = self.server.invoke_successfully(aggr_get_iter,
enable_tunneling=False)
except netapp_utils.zapi.NaApiError as error:
# Error 13040 denotes an aggregate not being found.
if to_native(error.code) == "13040":
return False
else:
self.module.fail_json(msg=to_native(
error), exception=traceback.format_exc())
if (result.get_child_by_name('num-records') and
int(result.get_child_content('num-records')) >= 1):
return True
return False
def aggregate_online(self):
"""
enable aggregate (online).
"""
online_aggr = netapp_utils.zapi.NaElement.create_node_with_children(
'aggr-online', **{'aggregate': self.name,
'force-online': 'true'})
try:
self.server.invoke_successfully(online_aggr,
enable_tunneling=True)
return True
except netapp_utils.zapi.NaApiError as error:
if to_native(error.code) == "13060":
# Error 13060 denotes aggregate is already online
return False
else:
self.module.fail_json(msg='Error changing the state of aggregate %s to %s: %s' %
(self.name, self.service_state,
to_native(error)),
exception=traceback.format_exc())
def aggregate_offline(self):
"""
disable aggregate (offline).
"""
offline_aggr = netapp_utils.zapi.NaElement.create_node_with_children(
'aggr-offline', **{'aggregate': self.name,
'force-offline': 'false',
'unmount-volumes': str(self.unmount_volumes)})
try:
self.server.invoke_successfully(offline_aggr,
enable_tunneling=True)
return True
except netapp_utils.zapi.NaApiError as error:
if to_native(error.code) == "13042":
# Error 13042 denotes aggregate is already offline
return False
else:
self.module.fail_json(msg='Error changing the state of aggregate %s to %s: %s' %
(self.name, self.service_state,
to_native(error)),
exception=traceback.format_exc())
def create_aggr(self):
"""
create aggregate.
"""
aggr_create = netapp_utils.zapi.NaElement.create_node_with_children(
'aggr-create', **{'aggregate': self.name,
'disk-count': str(self.disk_count)})
if self.nodes is not None:
nodes_obj = netapp_utils.zapi.NaElement('nodes')
aggr_create.add_child_elem(nodes_obj)
for node in self.nodes:
nodes_obj.add_new_child('node-name', node)
try:
self.server.invoke_successfully(aggr_create,
enable_tunneling=False)
except netapp_utils.zapi.NaApiError as error:
self.module.fail_json(msg="Error provisioning aggregate %s: %s" % (self.name, to_native(error)),
exception=traceback.format_exc())
def delete_aggr(self):
"""
delete aggregate.
"""
aggr_destroy = netapp_utils.zapi.NaElement.create_node_with_children(
'aggr-destroy', **{'aggregate': self.name})
try:
self.server.invoke_successfully(aggr_destroy,
enable_tunneling=False)
except netapp_utils.zapi.NaApiError as error:
self.module.fail_json(msg="Error removing aggregate %s: %s" % (self.name, to_native(error)),
exception=traceback.format_exc())
def rename_aggregate(self):
"""
rename aggregate.
"""
aggr_rename = netapp_utils.zapi.NaElement.create_node_with_children(
'aggr-rename', **{'aggregate': self.name,
'new-aggregate-name':
self.rename})
try:
self.server.invoke_successfully(aggr_rename,
enable_tunneling=False)
except netapp_utils.zapi.NaApiError as error:
self.module.fail_json(msg="Error renaming aggregate %s: %s" % (self.name, to_native(error)),
exception=traceback.format_exc())
def apply(self):
'''Apply action to aggregate'''
changed = False
size_changed = False
aggregate_exists = self.get_aggr()
rename_aggregate = False
results = netapp_utils.get_cserver(self.server)
cserver = netapp_utils.setup_na_ontap_zapi(
module=self.module, vserver=results)
netapp_utils.ems_log_event("na_ontap_aggregate", cserver)
# check if anything needs to be changed (add/delete/update)
if aggregate_exists:
if self.state == 'absent':
changed = True
elif self.state == 'present':
if self.service_state:
changed = True
if self.rename is not None and self.name != \
self.rename:
rename_aggregate = True
changed = True
else:
if self.state == 'present':
# Aggregate does not exist, but requested state is present.
if (self.rename is None) and self.disk_count:
changed = True
if changed:
if self.module.check_mode:
pass
else:
if self.state == 'present':
if not aggregate_exists:
self.create_aggr()
if self.service_state == 'offline':
self.aggregate_offline()
else:
if self.service_state == 'online':
size_changed = self.aggregate_online()
elif self.service_state == 'offline':
size_changed = self.aggregate_offline()
if rename_aggregate:
self.rename_aggregate()
if not size_changed and not rename_aggregate:
changed = False
elif self.state == 'absent':
if self.service_state == 'offline':
self.aggregate_offline()
self.delete_aggr()
self.module.exit_json(changed=changed)
def main():
''' Create object and call apply '''
obj_aggr = NetAppOntapAggregate()
obj_aggr.apply()
if __name__ == '__main__':
main()

View file

@ -0,0 +1,234 @@
#!/usr/bin/python
# (c) 2018, NetApp, Inc
# 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
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
module: na_ontap_broadcast_domain_ports
short_description: Manage NetApp Ontap broadcast domain ports
extends_documentation_fragment:
- netapp.na_ontap
version_added: '2.6'
author:
- Chris Archibald (carchi@netapp.com), Kevin Hutton (khutton@netapp.com), Suhas Bangalore Shekar (bsuhas@netapp.com)
description:
- Modify Ontap broadcast domain ports
options:
state:
description:
- Whether the specified broadcast domain should exist or not.
choices: ['present', 'absent']
default: present
vserver:
description:
- The name of the vserver
required: true
broadcast_domain:
description:
- Specify the broadcast_domain name
required: true
ipspace:
description:
- Specify the ipspace for the broadcast domain
ports:
description:
- Specify the list of ports associated with this broadcast domain.
'''
EXAMPLES = """
- name: create broadcast domain ports
na_ontap_broadcast_domain_ports:
state=present
vserver={{ Vserver name }}
username={{ netapp_username }}
password={{ netapp_password }}
hostname={{ netapp_hostname }}
broadcast_domain=123kevin
ports=khutton-vsim1:e0d-13
- name: delete broadcast domain ports
na_ontap_broadcast_domain_ports:
state=absent
vserver={{ Vserver name }}
username={{ netapp_username }}
password={{ netapp_password }}
hostname={{ netapp_hostname }}
broadcast_domain=123kevin
ports=khutton-vsim1:e0d-13
"""
RETURN = """
"""
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
import ansible.module_utils.netapp as netapp_utils
HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
class NetAppOntapBroadcastDomainPorts(object):
"""
Create and Destroys Broadcast Domain Ports
"""
def __init__(self):
"""
Initialize the Ontap Net Route class
"""
self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
self.argument_spec.update(dict(
state=dict(required=False, choices=['present', 'absent'], default='present'),
vserver=dict(required=False, type='str'),
broadcast_domain=dict(required=True, type='str'),
ipspace=dict(required=False, type='str', default=None),
ports=dict(required=True, type='list'),
))
self.module = AnsibleModule(
argument_spec=self.argument_spec,
supports_check_mode=True
)
parameters = self.module.params
# set up state variables
self.state = parameters['state']
self.vserver = parameters['vserver']
self.broadcast_domain = parameters['broadcast_domain']
self.ipspace = parameters['ipspace']
self.ports = parameters['ports']
if HAS_NETAPP_LIB is False:
self.module.fail_json(msg="the python NetApp-Lib module is required")
else:
self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
return
def get_broadcast_domain_ports(self):
"""
Return details about the broadcast domain ports
:param:
name : broadcast domain name
:return: Details about the broadcast domain. None if not found.
:rtype: dict
"""
domain_get_iter = netapp_utils.zapi.NaElement('net-port-broadcast-domain-get-iter')
broadcast_domain_info = netapp_utils.zapi.NaElement('net-port-broadcast-domain-info')
broadcast_domain_info.add_new_child('broadcast-domain', self.broadcast_domain)
query = netapp_utils.zapi.NaElement('query')
query.add_child_elem(broadcast_domain_info)
domain_get_iter.add_child_elem(query)
result = self.server.invoke_successfully(domain_get_iter, True)
domain_exists = None
# check if broadcast domain exists
if result.get_child_by_name('num-records') and \
int(result.get_child_content('num-records')) == 1:
domain_info = result.get_child_by_name('attributes-list').get_child_by_name('net-port-broadcast-domain-info')
domain_name = domain_info.get_child_content('broadcast-domain')
domain_ports = domain_info.get_child_content('port-info')
domain_exists = {
'domain-name': domain_name,
'ports': domain_ports
}
return domain_exists
def create_broadcast_domain_ports(self):
"""
Creates new broadcast domain ports
"""
domain_obj = netapp_utils.zapi.NaElement('net-port-broadcast-domain-add-ports')
domain_obj.add_new_child("broadcast-domain", self.broadcast_domain)
if self.ipspace:
domain_obj.add_new_child("ipspace", self.ipspace)
if self.ports:
ports_obj = netapp_utils.zapi.NaElement('ports')
domain_obj.add_child_elem(ports_obj)
for port in self.ports:
ports_obj.add_new_child('net-qualified-port-name', port)
try:
self.server.invoke_successfully(domain_obj, True)
return True
except netapp_utils.zapi.NaApiError as error:
# Error 18605 denotes port already being used.
if to_native(error.code) == "18605":
return False
else:
self.module.fail_json(msg='Error creating port for broadcast domain %s: %s' %
(self.broadcast_domain, to_native(error)),
exception=traceback.format_exc())
def delete_broadcast_domain_ports(self):
"""
Deletes broadcast domain ports
"""
domain_obj = netapp_utils.zapi.NaElement('net-port-broadcast-domain-remove-ports')
domain_obj.add_new_child("broadcast-domain", self.broadcast_domain)
if self.ipspace:
domain_obj.add_new_child("ipspace", self.ipspace)
if self.ports:
ports_obj = netapp_utils.zapi.NaElement('ports')
domain_obj.add_child_elem(ports_obj)
for port in self.ports:
ports_obj.add_new_child('net-qualified-port-name', port)
try:
self.server.invoke_successfully(domain_obj, True)
return True
except netapp_utils.zapi.NaApiError as error:
# Error 13001 denotes port already not being used.
if to_native(error.code) == "13001":
return False
else:
self.module.fail_json(msg='Error deleting port for broadcast domain %s: %s' %
(self.broadcast_domain, to_native(error)),
exception=traceback.format_exc())
def apply(self):
"""
Run Module based on play book
"""
changed = False
broadcast_domain_details = self.get_broadcast_domain_ports()
broadcast_domain_exists = False
results = netapp_utils.get_cserver(self.server)
cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
netapp_utils.ems_log_event("na_ontap_broadcast_domain_ports", cserver)
if broadcast_domain_details:
broadcast_domain_exists = True
if self.state == 'absent': # delete
changed = True
elif self.state == 'present': # create
changed = True
else:
pass
if changed:
if self.module.check_mode:
pass
else:
if broadcast_domain_exists:
if self.state == 'present': # execute create
changed = self.create_broadcast_domain_ports()
elif self.state == 'absent': # execute delete
changed = self.delete_broadcast_domain_ports()
self.module.exit_json(changed=changed)
def main():
"""
Creates the NetApp Ontap Net Route object and runs the correct play task
"""
obj = NetAppOntapBroadcastDomainPorts()
obj.apply()
if __name__ == '__main__':
main()

View file

@ -0,0 +1,248 @@
#!/usr/bin/python
# (c) 2018, NetApp, Inc
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# import untangle
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
author: "Archana Ganesan (garchana@netapp.com), Suhas Bangalore Shekar (bsuhas@netapp.com)"
description:
- "Create or destroy or modify(path) cifs-share on ONTAP"
extends_documentation_fragment:
- netapp.na_ontap
module: na_ontap_cifs
options:
path:
description:
The file system path that is shared through this CIFS share. The path is the full, user visible path relative
to the vserver root, and it might be crossing junction mount points. The path is in UTF8 and uses forward
slash as directory separator
required: false
vserver:
description:
- "Vserver containing the CIFS share."
required: true
share_name:
description:
The name of the CIFS share. The CIFS share name is a UTF-8 string with the following characters being
illegal; control characters from 0x00 to 0x1F, both inclusive, 0x22 (double quotes)
required: true
state:
choices: ['present', 'absent']
description:
- "Whether the specified CIFS share should exist or not."
required: false
default: present
short_description: "Manage NetApp cifs-share"
version_added: "2.6"
'''
EXAMPLES = """
- name: Create CIFS share
na_ontap_cifs:
state: present
share_name: cifsShareName
path: /
vserver: vserverName
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
- name: Delete CIFS share
na_ontap_cifs:
state: absent
share_name: cifsShareName
vserver: vserverName
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
- name: Modify path CIFS share
na_ontap_cifs:
state: present
share_name: pb_test
vserver: vserverName
path: /
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
"""
RETURN = """
"""
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
import ansible.module_utils.netapp as netapp_utils
HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
class NetAppONTAPCifsShare(object):
"""
Methods to create/delete/modify(path) CIFS share
"""
def __init__(self):
self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
self.argument_spec.update(dict(
state=dict(required=False, type='str', choices=[
'present', 'absent'], default='present'),
share_name=dict(required=True, type='str'),
path=dict(required=False, type='str'),
vserver=dict(required=True, type='str')
))
self.module = AnsibleModule(
argument_spec=self.argument_spec,
required_if=[
('state', 'present', ['share_name', 'path'])
],
supports_check_mode=True
)
parameters = self.module.params
# set up state variables
self.state = parameters['state']
self.share_name = parameters['share_name']
self.path = parameters['path']
self.vserver = parameters['vserver']
if HAS_NETAPP_LIB is False:
self.module.fail_json(
msg="the python NetApp-Lib module is required")
else:
self.server = netapp_utils.setup_na_ontap_zapi(
module=self.module, vserver=self.vserver)
def get_cifs_share(self):
"""
Return details about the cifs-share
:param:
name : Name of the cifs-share
:return: Details about the cifs-share. None if not found.
:rtype: dict
"""
cifs_iter = netapp_utils.zapi.NaElement('cifs-share-get-iter')
cifs_info = netapp_utils.zapi.NaElement('cifs-share')
cifs_info.add_new_child('share-name', self.share_name)
query = netapp_utils.zapi.NaElement('query')
query.add_child_elem(cifs_info)
cifs_iter.add_child_elem(query)
result = self.server.invoke_successfully(cifs_iter, True)
return_value = None
print(result.to_string())
# check if query returns the expected cifs-share
if result.get_child_by_name('num-records') and \
int(result.get_child_content('num-records')) == 1:
cifs_acl = result.get_child_by_name('attributes-list').\
get_child_by_name('cifs-share')
return_value = {
'share': cifs_acl.get_child_content('share-name'),
'path': cifs_acl.get_child_content('path'),
}
return return_value
def create_cifs_share(self):
"""
Create CIFS share
"""
cifs_create = netapp_utils.zapi.NaElement.create_node_with_children(
'cifs-share-create', **{'share-name': self.share_name,
'path': self.path})
try:
self.server.invoke_successfully(cifs_create,
enable_tunneling=True)
except netapp_utils.zapi.NaApiError as error:
self.module.fail_json(msg='Error creating cifs-share %s: %s'
% (self.share_name, to_native(error)),
exception=traceback.format_exc())
def delete_cifs_share(self):
"""
Delete CIFS share
"""
cifs_delete = netapp_utils.zapi.NaElement.create_node_with_children(
'cifs-share-delete', **{'share-name': self.share_name})
try:
self.server.invoke_successfully(cifs_delete,
enable_tunneling=True)
except netapp_utils.zapi.NaApiError as error:
self.module.fail_json(msg='Error deleting cifs-share %s: %s'
% (self.share_name, to_native(error)),
exception=traceback.format_exc())
def modify_cifs_share(self):
"""
modilfy path for the given CIFS share
"""
cifs_modify = netapp_utils.zapi.NaElement.create_node_with_children(
'cifs-share-modify', **{'share-name': self.share_name,
'path': self.path})
try:
self.server.invoke_successfully(cifs_modify,
enable_tunneling=True)
except netapp_utils.zapi.NaApiError as error:
self.module.fail_json(msg='Error modifying cifs-share %s:%s'
% (self.share_name, to_native(error)),
exception=traceback.format_exc())
def apply(self):
'''Apply action to cifs share'''
changed = False
cifs_exists = False
netapp_utils.ems_log_event("na_ontap_cifs", self.server)
cifs_details = self.get_cifs_share()
if cifs_details:
cifs_exists = True
if self.state == 'absent': # delete
changed = True
elif self.state == 'present':
if cifs_details['path'] != self.path: # modify path
changed = True
else:
if self.state == 'present': # create
changed = True
if changed:
if self.module.check_mode:
pass
else:
if self.state == 'present': # execute create
if not cifs_exists:
self.create_cifs_share()
else: # execute modify path
self.modify_cifs_share()
elif self.state == 'absent': # execute delete
self.delete_cifs_share()
self.module.exit_json(changed=changed)
def main():
'''Execute action from playbook'''
cifs_obj = NetAppONTAPCifsShare()
cifs_obj.apply()
if __name__ == '__main__':
main()

View file

@ -0,0 +1,234 @@
#!/usr/bin/python
# (c) 2018, NetApp, Inc
# 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
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
author: "Archana Ganesan (garchana@netapp.com), Suhas Bangalore Shekar (bsuhas@netapp.com)"
description:
- "Create or destroy or modify cifs-share-access-controls on ONTAP"
extends_documentation_fragment:
- netapp.na_ontap
module: na_ontap_cifs_acl
options:
permission:
choices: ['no_access', 'read', 'change', 'full_control']
description:
-"The access rights that the user or group has on the defined CIFS share."
share_name:
description:
- "The name of the cifs-share-access-control to manage."
required: true
state:
choices: ['present', 'absent']
description:
- "Whether the specified CIFS share acl should exist or not."
default: present
vserver:
description:
- Name of the vserver to use.
required: true
user_or_group:
description:
- "The user or group name for which the permissions are listed."
required: true
short_description: "Manage NetApp cifs-share-access-control"
version_added: "2.6"
'''
EXAMPLES = """
- name: Create CIFS share acl
na_ontap_cifs_acl:
state: present
share_name: cifsShareName
user_or_group: Everyone
permission: read
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
- name: Modify CIFS share acl permission
na_ontap_cifs_acl:
state: present
share_name: cifsShareName
permission: change
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
"""
RETURN = """
"""
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
import ansible.module_utils.netapp as netapp_utils
HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
class NetAppONTAPCifsAcl(object):
"""
Methods to create/delete/modify CIFS share/user access-control
"""
def __init__(self):
self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
self.argument_spec.update(dict(
state=dict(required=False, type='str', choices=['present', 'absent'], default='present'),
vserver=dict(required=True, type='str'),
share_name=dict(required=True, type='str'),
user_or_group=dict(required=True, type='str'),
permission=dict(required=False, type='str', choices=['no_access', 'read', 'change', 'full_control'])
))
self.module = AnsibleModule(
argument_spec=self.argument_spec,
required_if=[
('state', 'present', ['share_name', 'user_or_group', 'permission'])
],
supports_check_mode=True
)
parameters = self.module.params
# set up state variables
self.state = parameters['state']
self.vserver = parameters['vserver']
self.share_name = parameters['share_name']
self.user_or_group = parameters['user_or_group']
self.permission = parameters['permission']
if HAS_NETAPP_LIB is False:
self.module.fail_json(msg="the python NetApp-Lib module is required")
else:
self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.vserver)
def get_cifs_acl(self):
"""
Return details about the cifs-share-access-control
:param:
name : Name of the cifs-share-access-control
:return: Details about the cifs-share-access-control. None if not found.
:rtype: dict
"""
cifs_acl_iter = netapp_utils.zapi.NaElement('cifs-share-access-control-get-iter')
cifs_acl_info = netapp_utils.zapi.NaElement('cifs-share-access-control')
cifs_acl_info.add_new_child('share', self.share_name)
cifs_acl_info.add_new_child('user-or-group', self.user_or_group)
query = netapp_utils.zapi.NaElement('query')
query.add_child_elem(cifs_acl_info)
cifs_acl_iter.add_child_elem(query)
result = self.server.invoke_successfully(cifs_acl_iter, True)
return_value = None
# check if query returns the expected cifs-share-access-control
if result.get_child_by_name('num-records') and \
int(result.get_child_content('num-records')) == 1:
cifs_acl = result.get_child_by_name('attributes-list').get_child_by_name('cifs-share-access-control')
return_value = {
'share': cifs_acl.get_child_content('share'),
'user-or-group': cifs_acl.get_child_content('user-or-group'),
'permission': cifs_acl.get_child_content('permission')
}
return return_value
def create_cifs_acl(self):
"""
Create access control for the given CIFS share/user-group
"""
cifs_acl_create = netapp_utils.zapi.NaElement.create_node_with_children(
'cifs-share-access-control-create', **{'share': self.share_name,
'user-or-group': self.user_or_group,
'permission': self.permission})
try:
self.server.invoke_successfully(cifs_acl_create,
enable_tunneling=True)
except netapp_utils.zapi.NaApiError as error:
self.module.fail_json(msg='Error creating cifs-share-access-control %s: %s'
% (self.share_name, to_native(error)),
exception=traceback.format_exc())
def delete_cifs_acl(self):
"""
Delete access control for the given CIFS share/user-group
"""
cifs_acl_delete = netapp_utils.zapi.NaElement.create_node_with_children(
'cifs-share-access-control-delete', **{'share': self.share_name,
'user-or-group': self.user_or_group})
try:
self.server.invoke_successfully(cifs_acl_delete,
enable_tunneling=True)
except netapp_utils.zapi.NaApiError as error:
self.module.fail_json(msg='Error deleting cifs-share-access-control %s: %s'
% (self.share_name, to_native(error)),
exception=traceback.format_exc())
def modify_cifs_acl_permission(self):
"""
Change permission for the given CIFS share/user-group
"""
cifs_acl_modify = netapp_utils.zapi.NaElement.create_node_with_children(
'cifs-share-access-control-modify', **{'share': self.share_name,
'user-or-group': self.user_or_group,
'permission': self.permission})
try:
self.server.invoke_successfully(cifs_acl_modify,
enable_tunneling=True)
except netapp_utils.zapi.NaApiError as error:
self.module.fail_json(msg='Error modifying cifs-share-access-control permission %s:%s'
% (self.share_name, to_native(error)),
exception=traceback.format_exc())
def apply(self):
"""
Apply action to cifs-share-access-control
"""
changed = False
cifs_acl_exists = False
netapp_utils.ems_log_event("na_ontap_cifs_acl", self.server)
cifs_acl_details = self.get_cifs_acl()
if cifs_acl_details:
cifs_acl_exists = True
if self.state == 'absent': # delete
changed = True
elif self.state == 'present':
if cifs_acl_details['permission'] != self.permission: # rename
changed = True
else:
if self.state == 'present': # create
changed = True
if changed:
if self.module.check_mode:
pass
else:
if self.state == 'present': # execute create
if not cifs_acl_exists:
self.create_cifs_acl()
else: # execute modify
self.modify_cifs_acl_permission()
elif self.state == 'absent': # execute delete
self.delete_cifs_acl()
self.module.exit_json(changed=changed)
def main():
"""
Execute action from playbook
"""
cifs_acl = NetAppONTAPCifsAcl()
cifs_acl.apply()
if __name__ == '__main__':
main()

View file

@ -0,0 +1,307 @@
#!/usr/bin/python
""" this is cifs_server module
(c) 2018, NetApp, Inc
# 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
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'
}
DOCUMENTATION = '''
---
module: na_ontap_cifs_server
short_description: cifs server configuration
extends_documentation_fragment:
- netapp.na_ontap
version_added: '2.6'
author: chhaya gunawat (chhayag@netapp.com)
description:
- Creating / deleting and modifying the CIF server .
options:
state:
description:
- Whether the specified cifs_server should exist or not.
default: present
choices: ['present', 'absent']
service_state:
description:
- CIFS Server Administrative Status.
choices: ['stopped', 'started']
cifs_server_name:
description:
- Specifies the cifs_server name.
required: true
admin_user_name:
description:
- Specifies the cifs server admin username.
admin_password:
description:
- Specifies the cifs server admin password.
domain:
description:
- The Fully Qualified Domain Name of the Windows Active Directory this CIFS server belongs to.
workgroup:
description:
- The NetBIOS name of the domain or workgroup this CIFS server belongs to.
vserver:
description:
- The name of the vserver to use.
required: true
'''
EXAMPLES = '''
- name: Create cifs_server
na_ontap_cifs_server:
state: present
vserver: svm1
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
- name: Delete cifs_server
na_ontap_cifs_server:
state: absent
cifs_server_name: data2
vserver: svm1
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
'''
RETURN = '''
'''
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
import ansible.module_utils.netapp as netapp_utils
HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
class NetAppOntapcifsServer(object):
"""
object to describe cifs_server info
"""
def __init__(self):
self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
self.argument_spec.update(dict(
state=dict(required=False, choices=['present', 'absent'], default='present'),
service_state=dict(required=False, choices=['stopped', 'started']),
cifs_server_name=dict(required=False, type='str'),
workgroup=dict(required=False, type='str', default=None),
domain=dict(required=False, type='str'),
admin_user_name=dict(required=False, type='str'),
admin_password=dict(required=False, type='str'),
vserver=dict(required=True, type='str'),
))
self.module = AnsibleModule(
argument_spec=self.argument_spec,
supports_check_mode=True
)
params = self.module.params
# set up state variables
self.state = params['state']
self.cifs_server_name = params['cifs_server_name']
self.workgroup = params['workgroup']
self.domain = params['domain']
self.vserver = params['vserver']
self.service_state = params['service_state']
self.admin_user_name = params['admin_user_name']
self.admin_password = params['admin_password']
if HAS_NETAPP_LIB is False:
self.module.fail_json(msg="the python NetApp-Lib module is required")
else:
self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.vserver)
def get_cifs_server(self):
"""
Return details about the CIFS-server
:param:
name : Name of the name of the cifs_server
:return: Details about the cifs_server. None if not found.
:rtype: dict
"""
cifs_server_info = netapp_utils.zapi.NaElement('cifs-server-get-iter')
cifs_server_attributes = netapp_utils.zapi.NaElement('cifs-server-config')
cifs_server_attributes.add_new_child('cifs-server', self.cifs_server_name)
query = netapp_utils.zapi.NaElement('query')
query.add_child_elem(cifs_server_attributes)
cifs_server_info.add_child_elem(query)
result = self.server.invoke_successfully(cifs_server_info, True)
return_value = None
if result.get_child_by_name('num-records') and \
int(result.get_child_content('num-records')) >= 1:
cifs_server_attributes = result.get_child_by_name('attributes-list').\
get_child_by_name('cifs-server-config')
return_value = {
'cifs_server_name': self.cifs_server_name,
'administrative-status': cifs_server_attributes.get_child_content('administrative-status')
}
return return_value
def create_cifs_server(self):
"""
calling zapi to create cifs_server
"""
options = {'cifs-server': self.cifs_server_name, 'administrative-status': 'up'
if self.service_state == 'started' else 'down'}
if self.workgroup is not None:
options['workgroup'] = self.workgroup
if self.domain is not None:
options['domain'] = self.domain
if self.admin_user_name is not None:
options['admin-username'] = self.admin_user_name
if self.admin_password is not None:
options['admin-password'] = self.admin_password
cifs_server_create = netapp_utils.zapi.NaElement.create_node_with_children(
'cifs-server-create', **options)
try:
self.server.invoke_successfully(cifs_server_create,
enable_tunneling=True)
except netapp_utils.zapi.NaApiError as exc:
self.module.fail_json(msg='Error Creating cifs_server %s: %s' %
(self.cifs_server_name, to_native(exc)), exception=traceback.format_exc())
def delete_cifs_server(self):
"""
calling zapi to create cifs_server
"""
if self.cifs_server_name == 'up':
self.modify_cifs_server(admin_status='down')
cifs_server_delete = netapp_utils.zapi.NaElement.create_node_with_children('cifs-server-delete')
try:
self.server.invoke_successfully(cifs_server_delete,
enable_tunneling=True)
except netapp_utils.zapi.NaApiError as exc:
self.module.fail_json(msg='Error deleting cifs_server %s: %s' % (self.cifs_server_name, to_native(exc)),
exception=traceback.format_exc())
def modify_cifs_server(self, admin_status):
"""
RModify the cifs_server.
"""
cifs_server_modify = netapp_utils.zapi.NaElement.create_node_with_children(
'cifs-server-modify', **{'cifs-server': self.cifs_server_name,
'administrative-status': admin_status, 'vserver': self.vserver})
try:
self.server.invoke_successfully(cifs_server_modify,
enable_tunneling=True)
except netapp_utils.zapi.NaApiError as e:
self.module.fail_json(msg='Error modifying cifs_server %s: %s' % (self.cifs_server_name, to_native(e)),
exception=traceback.format_exc())
def start_cifs_server(self):
"""
RModify the cifs_server.
"""
cifs_server_modify = netapp_utils.zapi.NaElement.create_node_with_children(
'cifs-server-start')
try:
self.server.invoke_successfully(cifs_server_modify,
enable_tunneling=True)
except netapp_utils.zapi.NaApiError as e:
self.module.fail_json(msg='Error modifying cifs_server %s: %s' % (self.cifs_server_name, to_native(e)),
exception=traceback.format_exc())
def stop_cifs_server(self):
"""
RModify the cifs_server.
"""
cifs_server_modify = netapp_utils.zapi.NaElement.create_node_with_children(
'cifs-server-stop')
try:
self.server.invoke_successfully(cifs_server_modify,
enable_tunneling=True)
except netapp_utils.zapi.NaApiError as e:
self.module.fail_json(msg='Error modifying cifs_server %s: %s' % (self.cifs_server_name, to_native(e)),
exception=traceback.format_exc())
def apply(self):
"""
calling all cifs_server features
"""
changed = False
cifs_server_exists = False
netapp_utils.ems_log_event("na_ontap_cifs_server", self.server)
cifs_server_detail = self.get_cifs_server()
if cifs_server_detail:
cifs_server_exists = True
if self.state == 'present':
administrative_status = cifs_server_detail['administrative-status']
if self.service_state == 'started' and administrative_status == 'down':
changed = True
if self.service_state == 'stopped' and administrative_status == 'up':
changed = True
else:
# we will delete the CIFs server
changed = True
else:
if self.state == 'present':
changed = True
if changed:
if self.module.check_mode:
pass
else:
if self.state == 'present':
if not cifs_server_exists:
self.create_cifs_server()
elif self.service_state == 'stopped':
self.stop_cifs_server()
elif self.service_state == 'started':
self.start_cifs_server()
elif self.state == 'absent':
self.delete_cifs_server()
self.module.exit_json(changed=changed)
def main():
cifs_server = NetAppOntapcifsServer()
cifs_server.apply()
if __name__ == '__main__':
main()