text: # 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) 2020, NetApp Ansible Team <ng-ansibleteam@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.
  
  from __future__ import absolute_import, division, print_function
  
  __metaclass__ = type
  
  import json
  import mimetypes
  import os
  import random
  
  from pprint import pformat
  from ansible.module_utils import six
  from ansible.module_utils.basic import AnsibleModule, missing_required_lib
  from ansible.module_utils.six.moves.urllib.error import HTTPError, URLError
  from ansible.module_utils.urls import open_url
  from ansible.module_utils.api import basic_auth_argument_spec
  from ansible.module_utils._text import to_native
  
  COLLECTION_VERSION = "21.9.0"
  
  try:
      import requests
  
      HAS_REQUESTS = True
  except ImportError:
      HAS_REQUESTS = False
  
  import ssl
  
  try:
      from urlparse import urlparse, urlunparse
  except ImportError:
      from urllib.parse import urlparse, urlunparse
  
  
  POW2_BYTE_MAP = dict(
      # Here, 1 kb = 1024
      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,
  )
  
  
  def na_storagegrid_host_argument_spec():
  
      return dict(
          api_url=dict(required=True, type="str"),
          validate_certs=dict(required=False, type="bool", default=True),
          auth_token=dict(required=True, type="str", no_log=True),
      )
  
  
  class SGRestAPI(object):
      def __init__(self, module, timeout=60):
          self.module = module
          self.auth_token = self.module.params["auth_token"]
          self.api_url = self.module.params["api_url"]
          self.verify = self.module.params["validate_certs"]
          self.timeout = timeout
          self.check_required_library()
          self.sg_version = dict(major=-1, minor=-1, full="", valid=False)
  
      def check_required_library(self):
          if not HAS_REQUESTS:
              self.module.fail_json(msg=missing_required_lib("requests"))
  
      def send_request(self, method, api, params, json=None):
          """send http request and process reponse, including error conditions"""
          url = "%s/%s" % (self.api_url, api)
          status_code = None
          content = None
          json_dict = None
          json_error = None
          error_details = None
          headers = {
              "Content-type": "application/json",
              "Authorization": self.auth_token,
              "Cache-Control": "no-cache",
          }
  
          def get_json(response):
              """extract json, and error message if present"""
              try:
                  json = response.json()
  
              except ValueError:
                  return None, None
              success_code = [200, 201, 202, 204]
              if response.status_code not in success_code:
                  error = json.get("message")
              else:
                  error = None
              return json, error
  
          try:
              response = requests.request(
                  method,
                  url,
                  headers=headers,
                  timeout=self.timeout,
                  json=json,
                  verify=self.verify,
                  params=params,
              )
              status_code = response.status_code
              # If the response was successful, no Exception will be raised
              json_dict, json_error = get_json(response)
          except requests.exceptions.HTTPError as err:
              __, json_error = get_json(response)
              if json_error is None:
                  error_details = str(err)
          except requests.exceptions.ConnectionError as err:
              error_details = str(err)
          except Exception as err:
              error_details = str(err)
          if json_error is not None:
              error_details = json_error
  
          return json_dict, error_details
  
      # If an error was reported in the json payload, it is handled below
      def get(self, api, params=None):
          method = "GET"
          return self.send_request(method, api, params)
  
      def post(self, api, data, params=None):
          method = "POST"
          return self.send_request(method, api, params, json=data)
  
      def patch(self, api, data, params=None):
          method = "PATCH"
          return self.send_request(method, api, params, json=data)
  
      def put(self, api, data, params=None):
          method = "PUT"
          return self.send_request(method, api, params, json=data)
  
      def delete(self, api, data, params=None):
          method = "DELETE"
          return self.send_request(method, api, params, json=data)
  
      def get_sg_product_version(self, api_root="grid"):
          method = "GET"
          api = "api/v3/%s/config/product-version" % api_root
          message, error = self.send_request(method, api, params={})
          if error:
              self.module.fail_json(msg=error)
          self.set_version(message)
  
      def set_version(self, message):
          try:
              product_version = message.get("data", "not found").get("productVersion", "not_found")
          except AttributeError:
              self.sg_version["valid"] = False
              return
  
          self.sg_version["major"], self.sg_version["minor"] = list(map(int, product_version.split(".")[0:2]))
          self.sg_version["full"] = product_version
          self.sg_version["valid"] = True
  
      def get_sg_version(self):
          if self.sg_version["valid"]:
              return self.sg_version["major"], self.sg_version["minor"]
          return -1, -1
  
      def meets_sg_minimum_version(self, minimum_major, minimum_minor):
          return self.get_sg_version() >= (minimum_major, minimum_minor)
  
      def requires_sg_version(self, module_or_option, version):
          return "%s requires StorageGRID %s or later." % (module_or_option, version)
  
      def fail_if_not_sg_minimum_version(self, module_or_option, minimum_major, minimum_minor):
          version = self.get_sg_version()
          if version < (minimum_major, minimum_minor):
              msg = "Error: " + self.requires_sg_version(module_or_option, "%d.%d" % (minimum_major, minimum_minor))
              msg += "  Found: %s.%s." % version
              self.module.fail_json(msg=msg)
  