diff options
Diffstat (limited to 'lib/ansible/modules/storage/netapp/netapp_e_flashcache.py')
-rw-r--r-- | lib/ansible/modules/storage/netapp/netapp_e_flashcache.py | 415 |
1 files changed, 0 insertions, 415 deletions
diff --git a/lib/ansible/modules/storage/netapp/netapp_e_flashcache.py b/lib/ansible/modules/storage/netapp/netapp_e_flashcache.py deleted file mode 100644 index 6aba25ab99..0000000000 --- a/lib/ansible/modules/storage/netapp/netapp_e_flashcache.py +++ /dev/null @@ -1,415 +0,0 @@ -#!/usr/bin/python - -# (c) 2016, 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: netapp_e_flashcache -author: Kevin Hulquest (@hulquest) -version_added: '2.2' -short_description: NetApp E-Series manage SSD caches -description: -- Create or remove SSD caches on a NetApp E-Series storage array. -options: - api_username: - required: true - description: - - The username to authenticate with the SANtricity WebServices Proxy or embedded REST API. - api_password: - required: true - description: - - The password to authenticate with the SANtricity WebServices Proxy or embedded REST API. - api_url: - required: true - description: - - The url to the SANtricity WebServices Proxy or embedded REST API. - validate_certs: - required: false - default: true - description: - - Should https certificates be validated? - type: bool - ssid: - required: true - description: - - The ID of the array to manage (as configured on the web services proxy). - state: - required: true - description: - - Whether the specified SSD cache should exist or not. - choices: ['present', 'absent'] - default: present - name: - required: true - description: - - The name of the SSD cache to manage - io_type: - description: - - The type of workload to optimize the cache for. - choices: ['filesystem','database','media'] - default: filesystem - disk_count: - description: - - The minimum number of disks to use for building the cache. The cache will be expanded if this number exceeds the number of disks already in place - size_unit: - description: - - The unit to be applied to size arguments - choices: ['bytes', 'b', 'kb', 'mb', 'gb', 'tb', 'pb', 'eb', 'zb', 'yb'] - default: gb - cache_size_min: - description: - - The minimum size (in size_units) of the ssd cache. The cache will be expanded if this exceeds the current size of the cache. -''' - -EXAMPLES = """ - - name: Flash Cache - netapp_e_flashcache: - ssid: "{{ ssid }}" - api_url: "{{ netapp_api_url }}" - api_username: "{{ netapp_api_username }}" - api_password: "{{ netapp_api_password }}" - validate_certs: "{{ netapp_api_validate_certs }}" - name: SSDCacheBuiltByAnsible -""" - -RETURN = """ -msg: - description: Success message - returned: success - type: str - sample: json for newly created flash cache -""" -import json -import logging -import sys -import traceback - -from ansible.module_utils.api import basic_auth_argument_spec -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six.moves import reduce -from ansible.module_utils.six.moves.urllib.error import HTTPError -from ansible.module_utils._text import to_native -from ansible.module_utils.urls import open_url - - -def request(url, data=None, headers=None, method='GET', use_proxy=True, - force=False, last_mod_time=None, timeout=10, validate_certs=True, - url_username=None, url_password=None, http_agent=None, force_basic_auth=True, ignore_errors=False): - try: - r = open_url(url=url, data=data, headers=headers, method=method, use_proxy=use_proxy, - force=force, last_mod_time=last_mod_time, timeout=timeout, validate_certs=validate_certs, - url_username=url_username, url_password=url_password, http_agent=http_agent, - force_basic_auth=force_basic_auth) - except HTTPError as err: - r = err.fp - - try: - raw_data = r.read() - if raw_data: - data = json.loads(raw_data) - else: - raw_data = None - except Exception: - if ignore_errors: - pass - else: - raise Exception(raw_data) - - resp_code = r.getcode() - - if resp_code >= 400 and not ignore_errors: - raise Exception(resp_code, data) - else: - return resp_code, data - - -class NetAppESeriesFlashCache(object): - def __init__(self): - self.name = None - self.log_mode = None - self.log_path = None - self.api_url = None - self.api_username = None - self.api_password = None - self.ssid = None - self.validate_certs = None - self.disk_count = None - self.size_unit = None - self.cache_size_min = None - self.io_type = None - self.driveRefs = None - self.state = None - self._size_unit_map = dict( - bytes=1, - b=1, - kb=1024, - mb=1024 ** 2, - gb=1024 ** 3, - tb=1024 ** 4, - pb=1024 ** 5, - eb=1024 ** 6, - zb=1024 ** 7, - yb=1024 ** 8 - ) - - argument_spec = basic_auth_argument_spec() - argument_spec.update(dict( - api_username=dict(type='str', required=True), - api_password=dict(type='str', required=True, no_log=True), - api_url=dict(type='str', required=True), - state=dict(default='present', choices=['present', 'absent'], type='str'), - ssid=dict(required=True, type='str'), - name=dict(required=True, type='str'), - disk_count=dict(type='int'), - disk_refs=dict(type='list'), - cache_size_min=dict(type='int'), - io_type=dict(default='filesystem', choices=['filesystem', 'database', 'media']), - size_unit=dict(default='gb', choices=['bytes', 'b', 'kb', 'mb', 'gb', 'tb', 'pb', 'eb', 'zb', 'yb'], - type='str'), - criteria_disk_phy_type=dict(choices=['sas', 'sas4k', 'fibre', 'fibre520b', 'scsi', 'sata', 'pata'], - type='str'), - log_mode=dict(type='str'), - log_path=dict(type='str'), - )) - self.module = AnsibleModule( - argument_spec=argument_spec, - required_if=[ - - ], - mutually_exclusive=[ - - ], - # TODO: update validation for various selection criteria - supports_check_mode=True - ) - - self.__dict__.update(self.module.params) - - # logging setup - self._logger = logging.getLogger(self.__class__.__name__) - self.debug = self._logger.debug - - if self.log_mode == 'file' and self.log_path: - logging.basicConfig(level=logging.DEBUG, filename=self.log_path) - elif self.log_mode == 'stderr': - logging.basicConfig(level=logging.DEBUG, stream=sys.stderr) - - self.post_headers = dict(Accept="application/json") - self.post_headers['Content-Type'] = 'application/json' - - def get_candidate_disks(self, disk_count, size_unit='gb', capacity=None): - self.debug("getting candidate disks...") - - drives_req = dict( - driveCount=disk_count, - sizeUnit=size_unit, - driveType='ssd', - ) - - if capacity: - drives_req['targetUsableCapacity'] = capacity - - (rc, drives_resp) = request(self.api_url + "/storage-systems/%s/drives" % (self.ssid), - data=json.dumps(drives_req), headers=self.post_headers, method='POST', - url_username=self.api_username, url_password=self.api_password, - validate_certs=self.validate_certs) - - if rc == 204: - self.module.fail_json(msg='Cannot find disks to match requested criteria for ssd cache') - - disk_ids = [d['id'] for d in drives_resp] - bytes = reduce(lambda s, d: s + int(d['usableCapacity']), drives_resp, 0) - - return (disk_ids, bytes) - - def create_cache(self): - (disk_ids, bytes) = self.get_candidate_disks(disk_count=self.disk_count, size_unit=self.size_unit, - capacity=self.cache_size_min) - - self.debug("creating ssd cache...") - - create_fc_req = dict( - driveRefs=disk_ids, - name=self.name - ) - - (rc, self.resp) = request(self.api_url + "/storage-systems/%s/flash-cache" % (self.ssid), - data=json.dumps(create_fc_req), headers=self.post_headers, method='POST', - url_username=self.api_username, url_password=self.api_password, - validate_certs=self.validate_certs) - - def update_cache(self): - self.debug('updating flash cache config...') - update_fc_req = dict( - name=self.name, - configType=self.io_type - ) - - (rc, self.resp) = request(self.api_url + "/storage-systems/%s/flash-cache/configure" % (self.ssid), - data=json.dumps(update_fc_req), headers=self.post_headers, method='POST', - url_username=self.api_username, url_password=self.api_password, - validate_certs=self.validate_certs) - - def delete_cache(self): - self.debug('deleting flash cache...') - (rc, self.resp) = request(self.api_url + "/storage-systems/%s/flash-cache" % (self.ssid), method='DELETE', - url_username=self.api_username, url_password=self.api_password, - validate_certs=self.validate_certs, ignore_errors=True) - - @property - def needs_more_disks(self): - if len(self.cache_detail['driveRefs']) < self.disk_count: - self.debug("needs resize: current disk count %s < requested requested count %s", - len(self.cache_detail['driveRefs']), self.disk_count) - return True - - @property - def needs_less_disks(self): - if len(self.cache_detail['driveRefs']) > self.disk_count: - self.debug("needs resize: current disk count %s < requested requested count %s", - len(self.cache_detail['driveRefs']), self.disk_count) - return True - - @property - def current_size_bytes(self): - return int(self.cache_detail['fcDriveInfo']['fcWithDrives']['usedCapacity']) - - @property - def requested_size_bytes(self): - if self.cache_size_min: - return self.cache_size_min * self._size_unit_map[self.size_unit] - else: - return 0 - - @property - def needs_more_capacity(self): - if self.current_size_bytes < self.requested_size_bytes: - self.debug("needs resize: current capacity %sb is less than requested minimum %sb", - self.current_size_bytes, self.requested_size_bytes) - return True - - @property - def needs_resize(self): - return self.needs_more_disks or self.needs_more_capacity or self.needs_less_disks - - def resize_cache(self): - # increase up to disk count first, then iteratively add disks until we meet requested capacity - - # TODO: perform this calculation in check mode - current_disk_count = len(self.cache_detail['driveRefs']) - proposed_new_disks = 0 - - proposed_additional_bytes = 0 - proposed_disk_ids = [] - - if self.needs_more_disks: - proposed_disk_count = self.disk_count - current_disk_count - - (disk_ids, bytes) = self.get_candidate_disks(disk_count=proposed_disk_count) - proposed_additional_bytes = bytes - proposed_disk_ids = disk_ids - - while self.current_size_bytes + proposed_additional_bytes < self.requested_size_bytes: - proposed_new_disks += 1 - (disk_ids, bytes) = self.get_candidate_disks(disk_count=proposed_new_disks) - proposed_disk_ids = disk_ids - proposed_additional_bytes = bytes - - add_drives_req = dict( - driveRef=proposed_disk_ids - ) - - self.debug("adding drives to flash-cache...") - (rc, self.resp) = request(self.api_url + "/storage-systems/%s/flash-cache/addDrives" % (self.ssid), - data=json.dumps(add_drives_req), headers=self.post_headers, method='POST', - url_username=self.api_username, url_password=self.api_password, - validate_certs=self.validate_certs) - - elif self.needs_less_disks and self.driveRefs: - rm_drives = dict(driveRef=self.driveRefs) - (rc, self.resp) = request(self.api_url + "/storage-systems/%s/flash-cache/removeDrives" % (self.ssid), - data=json.dumps(rm_drives), headers=self.post_headers, method='POST', - url_username=self.api_username, url_password=self.api_password, - validate_certs=self.validate_certs) - - def apply(self): - result = dict(changed=False) - (rc, cache_resp) = request(self.api_url + "/storage-systems/%s/flash-cache" % (self.ssid), - url_username=self.api_username, url_password=self.api_password, - validate_certs=self.validate_certs, ignore_errors=True) - - if rc == 200: - self.cache_detail = cache_resp - else: - self.cache_detail = None - - if rc not in [200, 404]: - raise Exception( - "Unexpected error code %s fetching flash cache detail. Response data was %s" % (rc, cache_resp)) - - if self.state == 'present': - if self.cache_detail: - # TODO: verify parameters against detail for changes - if self.cache_detail['name'] != self.name: - self.debug("CHANGED: name differs") - result['changed'] = True - if self.cache_detail['flashCacheBase']['configType'] != self.io_type: - self.debug("CHANGED: io_type differs") - result['changed'] = True - if self.needs_resize: - self.debug("CHANGED: resize required") - result['changed'] = True - else: - self.debug("CHANGED: requested state is 'present' but cache does not exist") - result['changed'] = True - else: # requested state is absent - if self.cache_detail: - self.debug("CHANGED: requested state is 'absent' but cache exists") - result['changed'] = True - - if not result['changed']: - self.debug("no changes, exiting...") - self.module.exit_json(**result) - - if self.module.check_mode: - self.debug("changes pending in check mode, exiting early...") - self.module.exit_json(**result) - - if self.state == 'present': - if not self.cache_detail: - self.create_cache() - else: - if self.needs_resize: - self.resize_cache() - - # run update here as well, since io_type can't be set on creation - self.update_cache() - - elif self.state == 'absent': - self.delete_cache() - - # TODO: include other details about the storage pool (size, type, id, etc) - self.module.exit_json(changed=result['changed'], **self.resp) - - -def main(): - sp = NetAppESeriesFlashCache() - try: - sp.apply() - except Exception as e: - sp.debug("Exception in apply(): \n%s", to_native(e)) - sp.module.fail_json(msg="Failed to create flash cache. Error[%s]" % to_native(e), - exception=traceback.format_exc()) - - -if __name__ == '__main__': - main() |