Adding Common files for NetApp ElementSW release (#43727)

* MVP2 Post ElementSW OSRB sync

* Revert "MVP2 Post ElementSW OSRB sync"

This reverts commit c13db2ad962cd56bffce052c2891c558a2240c72.
This commit is contained in:
Chris Archibald 2018-08-10 09:33:08 -07:00 committed by John R Barker
parent 7a4517a067
commit 479408330e
5 changed files with 349 additions and 18 deletions

View file

@ -33,6 +33,9 @@ from ansible.module_utils.six.moves.urllib.error import HTTPError
from ansible.module_utils.urls import open_url from ansible.module_utils.urls import open_url
from ansible.module_utils.api import basic_auth_argument_spec from ansible.module_utils.api import basic_auth_argument_spec
import os
import ssl
HAS_NETAPP_LIB = False HAS_NETAPP_LIB = False
try: try:
from netapp_lib.api.zapi import zapi from netapp_lib.api.zapi import zapi
@ -81,7 +84,9 @@ def na_ontap_host_argument_spec():
hostname=dict(required=True, type='str'), hostname=dict(required=True, type='str'),
username=dict(required=True, type='str', aliases=['user']), username=dict(required=True, type='str', aliases=['user']),
password=dict(required=True, type='str', aliases=['pass'], no_log=True), password=dict(required=True, type='str', aliases=['pass'], no_log=True),
https=dict(required=False, type='bool', default=False) https=dict(required=False, type='bool', default=False),
validate_certs=dict(required=False, type='bool', default=True),
http_port=dict(required=False, type='int')
) )
@ -114,6 +119,8 @@ def setup_na_ontap_zapi(module, vserver=None):
username = module.params['username'] username = module.params['username']
password = module.params['password'] password = module.params['password']
https = module.params['https'] https = module.params['https']
validate_certs = module.params['validate_certs']
port = module.params['http_port']
if HAS_NETAPP_LIB: if HAS_NETAPP_LIB:
# set up zapi # set up zapi
@ -123,14 +130,22 @@ def setup_na_ontap_zapi(module, vserver=None):
if vserver: if vserver:
server.set_vserver(vserver) server.set_vserver(vserver)
# Todo : Replace hard-coded values with configurable parameters. # Todo : Replace hard-coded values with configurable parameters.
server.set_api_version(major=1, minor=21) server.set_api_version(major=1, minor=110)
# default is HTTP # default is HTTP
if https is True: if https:
server.set_port(443) if port is None:
server.set_transport_type('HTTPS') port = 443
transport_type = 'HTTPS'
# HACK to bypass certificate verification
if validate_certs is True:
if not os.environ.get('PYTHONHTTPSVERIFY', '') and getattr(ssl, '_create_unverified_context', None):
ssl._create_default_https_context = ssl._create_unverified_context
else: else:
server.set_port(80) if port is None:
server.set_transport_type('HTTP') port = 80
transport_type = 'HTTP'
server.set_transport_type(transport_type)
server.set_port(port)
server.set_server_type('FILER') server.set_server_type('FILER')
return server return server
else: else:
@ -150,7 +165,7 @@ def setup_ontap_zapi(module, vserver=None):
if vserver: if vserver:
server.set_vserver(vserver) server.set_vserver(vserver)
# Todo : Replace hard-coded values with configurable parameters. # Todo : Replace hard-coded values with configurable parameters.
server.set_api_version(major=1, minor=21) server.set_api_version(major=1, minor=110)
server.set_port(80) server.set_port(80)
server.set_server_type('FILER') server.set_server_type('FILER')
server.set_transport_type('HTTP') server.set_transport_type('HTTP')

View file

@ -0,0 +1,156 @@
# This code is part of Ansible, but is an independent component.
# This particular file snippet, and this file snippet only, is BSD licensed.
HAS_SF_SDK = False
try:
import solidfire.common
HAS_SF_SDK = True
except:
HAS_SF_SDK = False
def has_sf_sdk():
return HAS_SF_SDK
class NaElementSWModule(object):
def __init__(self, elem):
self.elem_connect = elem
self.parameters = dict()
def get_volume(self, volume_id):
"""
Return volume details if volume exists for given volume_id
:param volume_id: volume ID
:type volume_id: int
:return: Volume dict if found, None if not found
:rtype: dict
"""
volume_list = self.elem_connect.list_volumes(volume_ids=[volume_id])
for volume in volume_list.volumes:
if volume.volume_id == volume_id:
if str(volume.delete_time) == "":
return volume
return None
def get_volume_id(self, vol_name, account_id):
"""
Return volume id from the given (valid) account_id if found
Return None if not found
:param vol_name: Name of the volume
:type vol_name: str
:param account_id: Account ID
:type account_id: int
:return: Volume ID of the first matching volume if found. None if not found.
:rtype: int
"""
volume_list = self.elem_connect.list_volumes_for_account(account_id=account_id)
for volume in volume_list.volumes:
if volume.name == vol_name:
# return volume_id
if str(volume.delete_time) == "":
return volume.volume_id
return None
def volume_id_exists(self, volume_id):
"""
Return volume_id if volume exists for given volume_id
:param volume_id: volume ID
:type volume_id: int
:return: Volume ID if found, None if not found
:rtype: int
"""
volume_list = self.elem_connect.list_volumes(volume_ids=[volume_id])
for volume in volume_list.volumes:
if volume.volume_id == volume_id:
if str(volume.delete_time) == "":
return volume.volume_id
return None
def volume_exists(self, volume, account_id):
"""
Return volume_id if exists, None if not found
:param volume: Volume ID or Name
:type volume: str
:param account_id: Account ID (valid)
:type account_id: int
:return: Volume ID if found, None if not found
"""
# If volume is an integer, get_by_id
if str(volume).isdigit():
volume_id = int(volume)
try:
if self.volume_id_exists(volume_id):
return volume_id
except solidfire.common.ApiServerError:
# don't fail, continue and try get_by_name
pass
# get volume by name
volume_id = self.get_volume_id(volume, account_id)
return volume_id
def get_snapshot(self, snapshot_id, volume_id):
"""
Return snapshot details if found
:param snapshot_id: Snapshot ID or Name
:type snapshot_id: str
:param volume_id: Account ID (valid)
:type volume_id: int
:return: Snapshot dict if found, None if not found
:rtype: dict
"""
# mandate src_volume_id although not needed by sdk
snapshot_list = self.elem_connect.list_snapshots(
volume_id=volume_id)
for snapshot in snapshot_list.snapshots:
# if actual id is provided
if str(snapshot_id).isdigit() and snapshot.snapshot_id == int(snapshot_id):
return snapshot
# if snapshot name is provided
elif snapshot.name == snapshot_id:
return snapshot
return None
def account_exists(self, account):
"""
Return account_id if account exists for given account id or name
Raises an exception if account does not exist
:param account: Account ID or Name
:type account: str
:return: Account ID if found, None if not found
"""
# If account is an integer, get_by_id
if account.isdigit():
account_id = int(account)
try:
result = self.elem_connect.get_account_by_id(account_id=account_id)
if result.account.account_id == account_id:
return account_id
except solidfire.common.ApiServerError:
# don't fail, continue and try get_by_name
pass
# get account by name, the method returns an Exception if account doesn't exist
result = self.elem_connect.get_account_by_name(username=account)
return result.account.account_id
def set_element_attributes(self, source):
"""
Return telemetry attributes for the current execution
:param source: name of the module
:type source: str
:return: a dict containing telemetry attributes
"""
attributes = {}
attributes['config-mgmt'] = 'ansible'
attributes['event-source'] = source
return(attributes)

View file

@ -0,0 +1,149 @@
# This code is part of Ansible, but is an independent component.
# This particular file snippet, and this file snippet only, is BSD licensed.
# Modules you write using this snippet, which is embedded dynamically by Ansible
# still belong to the author of the module, and may assign their own license
# to the complete work.
#
# Copyright (c) 2018, Laurent Nicolas <laurentn@netapp.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
''' Support class for NetApp ansible modules '''
def cmp(a, b):
"""
Python 3 does not have a cmp function, this will do the cmp.
:param a: first object to check
:param b: second object to check
:return:
"""
return (a > b) - (a < b)
class NetAppModule(object):
'''
Common class for NetApp modules
set of support functions to derive actions based
on the current state of the system, and a desired state
'''
def __init__(self):
self.log = list()
self.changed = False
self.parameters = {'name': 'not intialized'}
def set_parameters(self, ansible_params):
self.parameters = dict()
for param in ansible_params:
if ansible_params[param] is not None:
self.parameters[param] = ansible_params[param]
return self.parameters
def get_cd_action(self, current, desired):
''' takes a desired state and a current state, and return an action:
create, delete, None
eg:
is_present = 'absent'
some_object = self.get_object(source)
if some_object is not None:
is_present = 'present'
action = cd_action(current=is_present, desired = self.desired.state())
'''
if 'state' in desired:
desired_state = desired['state']
else:
desired_state = 'present'
if current is None and desired_state == 'absent':
return None
if current is not None and desired_state == 'present':
return None
# change in state
self.changed = True
if current is not None:
return 'delete'
return 'create'
@staticmethod
def check_keys(current, desired):
''' TODO: raise an error if keys do not match
with the exception of:
new_name, state in desired
'''
pass
def get_modified_attributes(self, current, desired):
''' takes two lists of attributes and return a list of attributes that are
not in the desired state
It is expected that all attributes of interest are listed in current and
desired.
NOTE: depending on the attribute, the caller may need to do a modify or a
different operation (eg move volume if the modified attribute is an
aggregate name)
'''
# if the object does not exist, we can't modify it
modified = dict()
if current is None:
return modified
# error out if keys do not match
self.check_keys(current, desired)
# collect changed attributes
for key, value in current.items():
if key in desired and desired[key] is not None:
if type(value) is list:
value.sort()
desired[key].sort()
if cmp(value, desired[key]) != 0:
modified[key] = desired[key]
if modified:
self.changed = True
return modified
def is_rename_action(self, source, target):
''' takes a source and target object, and returns True
if a rename is required
eg:
source = self.get_object(source_name)
target = self.get_object(target_name)
action = is_rename_action(source, target)
:return: None for error, True for rename action, False otherwise
'''
if source is None and target is None:
# error, do nothing
# cannot rename an non existent resource
# alternatively we could create B
return None
if source is not None and target is not None:
# error, do nothing
# idempotency (or) new_name_is_already_in_use
# alternatively we could delete B and rename A to B
return False
if source is None and target is not None:
# do nothing, maybe the rename was already done
return False
# source is not None and target is None:
# rename is in order
self.changed = True
return True

View file

@ -39,7 +39,7 @@ options:
required: true required: true
description: description:
- This can be a Cluster-scoped or SVM-scoped account, depending on whether a Cluster-level or SVM-level API is required. - This can be a Cluster-scoped or SVM-scoped account, depending on whether a Cluster-level or SVM-level API is required.
For more information, please read the documentation U(https://goo.gl/BRu78Z). For more information, please read the documentation U(https://mysupport.netapp.com/NOW/download/software/nmsdk/9.4/).
aliases: ['user'] aliases: ['user']
password: password:
required: true required: true
@ -48,9 +48,20 @@ options:
aliases: ['pass'] aliases: ['pass']
https: https:
description: description:
- Enable and disabled https - Enable and disable https
type: bool type: bool
default: false default: false
validate_certs:
description:
- If set to C(False), the SSL certificates will not be validated.
- This should only set to C(False) used on personally controlled sites using self-signed certificates.
default: true
type: bool
http_port:
description:
- Override the default port (80 or 443) with this port
type: int
requirements: requirements:
- A physical or virtual clustered Data ONTAP system. The modules were developed with Clustered Data ONTAP 9.3 - A physical or virtual clustered Data ONTAP system. The modules were developed with Clustered Data ONTAP 9.3
@ -74,7 +85,7 @@ options:
required: true required: true
description: description:
- This can be a Cluster-scoped or SVM-scoped account, depending on whether a Cluster-level or SVM-level API is required. - This can be a Cluster-scoped or SVM-scoped account, depending on whether a Cluster-level or SVM-level API is required.
For more information, please read the documentation U(https://goo.gl/BRu78Z). For more information, please read the documentation U(https://mysupport.netapp.com/NOW/download/software/nmsdk/9.4/).
aliases: ['user'] aliases: ['user']
password: password:
required: true required: true
@ -101,17 +112,21 @@ options:
username: username:
required: true required: true
description: description:
- Please ensure that the user has the adequate permissions. For more information, please read the official documentation U(https://goo.gl/ddJa4Q). - Please ensure that the user has the adequate permissions. For more information, please read the official documentation
U(https://mysupport.netapp.com/documentation/docweb/index.html?productID=62636&language=en-US).
aliases: ['user']
password: password:
required: true required: true
description: description:
- Password for the specified user. - Password for the specified user.
aliases: ['pass']
requirements: requirements:
- solidfire-sdk-python (1.1.0.92) - The modules were developed with SolidFire 10.1
- solidfire-sdk-python (1.1.0.92) or greater. Install using 'pip install solidfire-sdk-python'
notes: notes:
- The modules prefixed with C(sf\\_) are built to support the SolidFire storage platform. - The modules prefixed with na\\_elementsw are built to support the SolidFire storage platform.
""" """

View file

@ -1126,11 +1126,7 @@ lib/ansible/modules/storage/netapp/netapp_e_volume_copy.py E323
lib/ansible/modules/storage/netapp/netapp_e_volume_copy.py E324 lib/ansible/modules/storage/netapp/netapp_e_volume_copy.py E324
lib/ansible/modules/storage/netapp/netapp_e_volume_copy.py E325 lib/ansible/modules/storage/netapp/netapp_e_volume_copy.py E325
lib/ansible/modules/storage/netapp/netapp_e_volume_copy.py E326 lib/ansible/modules/storage/netapp/netapp_e_volume_copy.py E326
lib/ansible/modules/storage/netapp/sf_account_manager.py E322
lib/ansible/modules/storage/netapp/sf_check_connections.py E322
lib/ansible/modules/storage/netapp/sf_snapshot_schedule_manager.py E322
lib/ansible/modules/storage/netapp/sf_snapshot_schedule_manager.py E325 lib/ansible/modules/storage/netapp/sf_snapshot_schedule_manager.py E325
lib/ansible/modules/storage/netapp/sf_volume_access_group_manager.py E322
lib/ansible/modules/storage/netapp/sf_volume_manager.py E322 lib/ansible/modules/storage/netapp/sf_volume_manager.py E322
lib/ansible/modules/storage/netapp/sf_volume_manager.py E325 lib/ansible/modules/storage/netapp/sf_volume_manager.py E325
lib/ansible/modules/storage/purestorage/purefb_fs.py E324 lib/ansible/modules/storage/purestorage/purefb_fs.py E324