text: #!/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_facts
  short_description: NetApp E-Series retrieve facts about NetApp E-Series storage arrays
  description:
      - The netapp_e_facts module returns a collection of facts regarding NetApp E-Series storage arrays.
  author:
      - Kevin Hulquest (@hulquest)
      - Nathan Swartz (@ndswartz)
  extends_documentation_fragment:
  - netapp.ontap.netapp.eseries
  
  '''
  
  EXAMPLES = """
  ---
  - name: Get array facts
    netapp_e_facts:
      ssid: "1"
      api_url: "https://192.168.1.100:8443/devmgr/v2"
      api_username: "admin"
      api_password: "adminpass"
      validate_certs: true
  """
  
  RETURN = """
      msg:
          description: Success message
          returned: on success
          type: str
          sample:
              - Gathered facts for storage array. Array ID [1].
              - Gathered facts for web services proxy.
      storage_array_facts:
          description: provides details about the array, controllers, management interfaces, hostside interfaces,
                       driveside interfaces, disks, storage pools, volumes, snapshots, and features.
          returned: on successful inquiry from from embedded web services rest api
          type: complex
          contains:
              netapp_controllers:
                  description: storage array controller list that contains basic controller identification and status
                  type: complex
                  sample:
                      - [{"name": "A", "serial": "021632007299", "status": "optimal"},
                         {"name": "B", "serial": "021632007300", "status": "failed"}]
              netapp_disks:
                  description: drive list that contains identification, type, and status information for each drive
                  type: complex
                  sample:
                      - [{"available": false,
                          "firmware_version": "MS02",
                          "id": "01000000500003960C8B67880000000000000000",
                          "media_type": "ssd",
                          "product_id": "PX02SMU080      ",
                          "serial_number": "15R0A08LT2BA",
                          "status": "optimal",
                          "tray_ref": "0E00000000000000000000000000000000000000",
                          "usable_bytes": "799629205504" }]
              netapp_driveside_interfaces:
                  description: drive side interface list that contains identification, type, and speed for each interface
                  type: complex
                  sample:
                      - [{ "controller": "A", "interface_speed": "12g", "interface_type": "sas" }]
                      - [{ "controller": "B", "interface_speed": "10g", "interface_type": "iscsi" }]
              netapp_enabled_features:
                  description: specifies the enabled features on the storage array.
                  returned: on success
                  type: complex
                  sample:
                      - [ "flashReadCache", "performanceTier", "protectionInformation", "secureVolume" ]
              netapp_host_groups:
                  description: specifies the host groups on the storage arrays.
                  returned: on success
                  type: complex
                  sample:
                      - [{ "id": "85000000600A098000A4B28D003610705C40B964", "name": "group1" }]
              netapp_hosts:
                  description: specifies the hosts on the storage arrays.
                  returned: on success
                  type: complex
                  sample:
                      - [{ "id": "8203800000000000000000000000000000000000",
                           "name": "host1",
                           "group_id": "85000000600A098000A4B28D003610705C40B964",
                           "host_type_index": 28,
                           "ports": [{ "type": "fc", "address": "1000FF7CFFFFFF01", "label": "FC_1" },
                                     { "type": "fc", "address": "1000FF7CFFFFFF00", "label": "FC_2" }]}]
              netapp_host_types:
                  description: lists the available host types on the storage array.
                  returned: on success
                  type: complex
                  sample:
                      - [{ "index": 0, "type": "FactoryDefault" },
                         { "index": 1, "type": "W2KNETNCL"},
                         { "index": 2, "type": "SOL" },
                         { "index": 5, "type": "AVT_4M" },
                         { "index": 6, "type": "LNX" },
                         { "index": 7, "type": "LnxALUA" },
                         { "index": 8, "type": "W2KNETCL" },
                         { "index": 9, "type": "AIX MPIO" },
                         { "index": 10, "type": "VmwTPGSALUA" },
                         { "index": 15, "type": "HPXTPGS" },
                         { "index": 17, "type": "SolTPGSALUA" },
                         { "index": 18, "type": "SVC" },
                         { "index": 22, "type": "MacTPGSALUA" },
                         { "index": 23, "type": "WinTPGSALUA" },
                         { "index": 24, "type": "LnxTPGSALUA" },
                         { "index": 25, "type": "LnxTPGSALUA_PM" },
                         { "index": 26, "type": "ONTAP_ALUA" },
                         { "index": 27, "type": "LnxTPGSALUA_SF" },
                         { "index": 28, "type": "LnxDHALUA" },
                         { "index": 29, "type": "ATTOClusterAllOS" }]
              netapp_hostside_interfaces:
                  description: host side interface list that contains identification, configuration, type, speed, and
                               status information for each interface
                  type: complex
                  sample:
                      - [{"iscsi":
                          [{ "controller": "A",
                              "current_interface_speed": "10g",
                              "ipv4_address": "10.10.10.1",
                              "ipv4_enabled": true,
                              "ipv4_gateway": "10.10.10.1",
                              "ipv4_subnet_mask": "255.255.255.0",
                              "ipv6_enabled": false,
                              "iqn": "iqn.1996-03.com.netapp:2806.600a098000a81b6d0000000059d60c76",
                              "link_status": "up",
                              "mtu": 9000,
                              "supported_interface_speeds": [ "10g" ] }]}]
              netapp_management_interfaces:
                  description: management interface list that contains identification, configuration, and status for
                               each interface
                  type: complex
                  sample:
                      - [{"alias": "ict-2800-A",
                          "channel": 1,
                          "controller": "A",
                          "dns_config_method": "dhcp",
                          "dns_servers": [],
                          "ipv4_address": "10.1.1.1",
                          "ipv4_address_config_method": "static",
                          "ipv4_enabled": true,
                          "ipv4_gateway": "10.113.1.1",
                          "ipv4_subnet_mask": "255.255.255.0",
                          "ipv6_enabled": false,
                          "link_status": "up",
                          "mac_address": "00A098A81B5D",
                          "name": "wan0",
                          "ntp_config_method": "disabled",
                          "ntp_servers": [],
                          "remote_ssh_access": false }]
              netapp_storage_array:
                  description: provides storage array identification, firmware version, and available capabilities
                  type: dict
                  sample:
                      - {"chassis_serial": "021540006043",
                         "firmware": "08.40.00.01",
                         "name": "ict-2800-11_40",
                         "wwn": "600A098000A81B5D0000000059D60C76",
                         "cacheBlockSizes": [4096,
                                             8192,
                                             16384,
                                             32768],
                         "supportedSegSizes": [8192,
                                               16384,
                                               32768,
                                               65536,
                                               131072,
                                               262144,
                                               524288]}
              netapp_storage_pools:
                  description: storage pool list that contains identification and capacity information for each pool
                  type: complex
                  sample:
                      - [{"available_capacity": "3490353782784",
                          "id": "04000000600A098000A81B5D000002B45A953A61",
                          "name": "Raid6",
                          "total_capacity": "5399466745856",
                          "used_capacity": "1909112963072" }]
              netapp_volumes:
                  description: storage volume list that contains identification and capacity information for each volume
                  type: complex
                  sample:
                      - [{"capacity": "5368709120",
                          "id": "02000000600A098000AAC0C3000002C45A952BAA",
                          "is_thin_provisioned": false,
                          "name": "5G",
                          "parent_storage_pool_id": "04000000600A098000A81B5D000002B45A953A61" }]
              netapp_workload_tags:
                  description: workload tag list
                  type: complex
                  sample:
                      - [{"id": "87e19568-43fb-4d8d-99ea-2811daaa2b38",
                          "name": "ftp_server",
                          "workloadAttributes": [{"key": "use",
                                                  "value": "general"}]}]
              netapp_volumes_by_initiators:
                  description: list of available volumes keyed by the mapped initiators.
                  type: complex
                  sample:
                     - {"192_168_1_1": [{"id": "02000000600A098000A4B9D1000015FD5C8F7F9E",
                                         "meta_data": {"filetype": "xfs", "public": true},
                                         "name": "some_volume",
                                         "workload_name": "test2_volumes",
                                         "wwn": "600A098000A4B9D1000015FD5C8F7F9E"}]}
              snapshot_images:
                  description: snapshot image list that contains identification, capacity, and status information for each
                               snapshot image
                  type: complex
                  sample:
                      - [{"active_cow": true,
                          "creation_method": "user",
                          "id": "34000000600A098000A81B5D00630A965B0535AC",
                          "pit_capacity": "5368709120",
                          "reposity_cap_utilization": "0",
                          "rollback_source": false,
                          "status": "optimal" }]
  """
  
  from re import match
  from pprint import pformat
  from ansible_collections.netapp.ontap.plugins.module_utils.netapp import NetAppESeriesModule
  
  
  class Facts(NetAppESeriesModule):
      def __init__(self):
          web_services_version = "02.00.0000.0000"
          super(Facts, self).__init__(ansible_options={},
                                      web_services_version=web_services_version,
                                      supports_check_mode=True)
  
      def get_controllers(self):
          """Retrieve a mapping of controller references to their labels."""
          controllers = list()
          try:
              rc, controllers = self.request('storage-systems/%s/graph/xpath-filter?query=/controller/id' % self.ssid)
          except Exception as err:
              self.module.fail_json(
                  msg="Failed to retrieve controller list! Array Id [%s]. Error [%s]."
                      % (self.ssid, str(err)))
  
          controllers.sort()
  
          controllers_dict = {}
          i = ord('A')
          for controller in controllers:
              label = chr(i)
              controllers_dict[controller] = label
              i += 1
  
          return controllers_dict
  
      def get_array_facts(self):
          """Extract particular facts from the storage array graph"""
          facts = dict(facts_from_proxy=(not self.is_embedded()), ssid=self.ssid)
          controller_reference_label = self.get_controllers()
          array_facts = None
  
          # Get the storage array graph
          try:
              rc, array_facts = self.request("storage-systems/%s/graph" % self.ssid)
          except Exception as error:
              self.module.fail_json(msg="Failed to obtain facts from storage array with id [%s]. Error [%s]" % (self.ssid, str(error)))
  
          facts['netapp_storage_array'] = dict(
              name=array_facts['sa']['saData']['storageArrayLabel'],
              chassis_serial=array_facts['sa']['saData']['chassisSerialNumber'],
              firmware=array_facts['sa']['saData']['fwVersion'],
              wwn=array_facts['sa']['saData']['saId']['worldWideName'],
              segment_sizes=array_facts['sa']['featureParameters']['supportedSegSizes'],
              cache_block_sizes=array_facts['sa']['featureParameters']['cacheBlockSizes'])
  
          facts['netapp_controllers'] = [
              dict(
                  name=controller_reference_label[controller['controllerRef']],
                  serial=controller['serialNumber'].strip(),
                  status=controller['status'],
              ) for controller in array_facts['controller']]
  
          facts['netapp_host_groups'] = [
              dict(
                  id=group['id'],
                  name=group['name']
              ) for group in array_facts['storagePoolBundle']['cluster']]
  
          facts['netapp_hosts'] = [
              dict(
                  group_id=host['clusterRef'],
                  hosts_reference=host['hostRef'],
                  id=host['id'],
                  name=host['name'],
                  host_type_index=host['hostTypeIndex'],
                  posts=host['hostSidePorts']
              ) for host in array_facts['storagePoolBundle']['host']]
  
          facts['netapp_host_types'] = [
              dict(
                  type=host_type['hostType'],
                  index=host_type['index']
              ) for host_type in array_facts['sa']['hostSpecificVals']
              if 'hostType' in host_type.keys() and host_type['hostType']
              # This conditional ignores zero-length strings which indicates that the associated host-specific NVSRAM region has been cleared.
          ]
          facts['snapshot_images'] = [
              dict(
                  id=snapshot['id'],
                  status=snapshot['status'],
                  pit_capacity=snapshot['pitCapacity'],
                  creation_method=snapshot['creationMethod'],
                  reposity_cap_utilization=snapshot['repositoryCapacityUtilization'],
                  active_cow=snapshot['activeCOW'],
                  rollback_source=snapshot['isRollbackSource']
              ) for snapshot in array_facts['highLevelVolBundle']['pit']]
  
          facts['netapp_disks'] = [
              dict(
                  id=disk['id'],
                  available=disk['available'],
                  media_type=disk['driveMediaType'],
                  status=disk['status'],
                  usable_bytes=disk['usableCapacity'],
                  tray_ref=disk['physicalLocation']['trayRef'],
                  product_id=disk['productID'],
                  firmware_version=disk['firmwareVersion'],
                  serial_number=disk['serialNumber'].lstrip()
              ) for disk in array_facts['drive']]
  
          facts['netapp_management_interfaces'] = [
              dict(controller=controller_reference_label[controller['controllerRef']],
                   name=iface['ethernet']['interfaceName'],
                   alias=iface['ethernet']['alias'],
                   channel=iface['ethernet']['channel'],
                   mac_address=iface['ethernet']['macAddr'],
                   remote_ssh_access=iface['ethernet']['rloginEnabled'],
                   link_status=iface['ethernet']['linkStatus'],
                   ipv4_enabled=iface['ethernet']['ipv4Enabled'],
                   ipv4_address_config_method=iface['ethernet']['ipv4AddressConfigMethod'].lower().replace("config", ""),
                   ipv4_address=iface['ethernet']['ipv4Address'],
                   ipv4_subnet_mask=iface['ethernet']['ipv4SubnetMask'],
                   ipv4_gateway=iface['ethernet']['ipv4GatewayAddress'],
                   ipv6_enabled=iface['ethernet']['ipv6Enabled'],
                   dns_config_method=iface['ethernet']['dnsProperties']['acquisitionProperties']['dnsAcquisitionType'],
                   dns_servers=(iface['ethernet']['dnsProperties']['acquisitionProperties']['dnsServers']
                                if iface['ethernet']['dnsProperties']['acquisitionProperties']['dnsServers'] else []),
                   ntp_config_method=iface['ethernet']['ntpProperties']['acquisitionProperties']['ntpAcquisitionType'],
                   ntp_servers=(iface['ethernet']['ntpProperties']['acquisitionProperties']['ntpServers']
                                if iface['ethernet']['ntpProperties']['acquisitionProperties']['ntpServers'] else [])
                   ) for controller in array_facts['controller'] for iface in controller['netInterfaces']]
  
          facts['netapp_hostside_interfaces'] = [
              dict(
                  fc=[dict(controller=controller_reference_label[controller['controllerRef']],
                           channel=iface['fibre']['channel'],
                           link_status=iface['fibre']['linkStatus'],
                           current_interface_speed=strip_interface_speed(iface['fibre']['currentInterfaceSpeed']),
                           maximum_interface_speed=strip_interface_speed(iface['fibre']['maximumInterfaceSpeed']))
                      for controller in array_facts['controller']
                      for iface in controller['hostInterfaces']
                      if iface['interfaceType'] == 'fc'],
                  ib=[dict(controller=controller_reference_label[controller['controllerRef']],
                           channel=iface['ib']['channel'],
                           link_status=iface['ib']['linkState'],
                           mtu=iface['ib']['maximumTransmissionUnit'],
                           current_interface_speed=strip_interface_speed(iface['ib']['currentSpeed']),
                           maximum_interface_speed=strip_interface_speed(iface['ib']['supportedSpeed']))
                      for controller in array_facts['controller']
                      for iface in controller['hostInterfaces']
                      if iface['interfaceType'] == 'ib'],
                  iscsi=[dict(controller=controller_reference_label[controller['controllerRef']],
                              iqn=iface['iscsi']['iqn'],
                              link_status=iface['iscsi']['interfaceData']['ethernetData']['linkStatus'],
                              ipv4_enabled=iface['iscsi']['ipv4Enabled'],
                              ipv4_address=iface['iscsi']['ipv4Data']['ipv4AddressData']['ipv4Address'],
                              ipv4_subnet_mask=iface['iscsi']['ipv4Data']['ipv4AddressData']['ipv4SubnetMask'],
                              ipv4_gateway=iface['iscsi']['ipv4Data']['ipv4AddressData']['ipv4GatewayAddress'],
                              ipv6_enabled=iface['iscsi']['ipv6Enabled'],
                              mtu=iface['iscsi']['interfaceData']['ethernetData']['maximumFramePayloadSize'],
                              current_interface_speed=strip_interface_speed(iface['iscsi']['interfaceData']
                                                                            ['ethernetData']['currentInterfaceSpeed']),
                              supported_interface_speeds=strip_interface_speed(iface['iscsi']['interfaceData']
                                                                               ['ethernetData']
                                                                               ['supportedInterfaceSpeeds']))
                         for controller in array_facts['controller']
                         for iface in controller['hostInterfaces']
                         if iface['interfaceType'] == 'iscsi'],
                  sas=[dict(controller=controller_reference_label[controller['controllerRef']],
                            channel=iface['sas']['channel'],
                            current_interface_speed=strip_interface_speed(iface['sas']['currentInterfaceSpeed']),
                            maximum_interface_speed=strip_interface_speed(iface['sas']['maximumInterfaceSpeed']),
                            link_status=iface['sas']['iocPort']['state'])
                       for controller in array_facts['controller']
                       for iface in controller['hostInterfaces']
                       if iface['interfaceType'] == 'sas'])]
  
          facts['netapp_driveside_interfaces'] = [
              dict(
                  controller=controller_reference_label[controller['controllerRef']],
                  interface_type=interface['interfaceType'],
                  interface_speed=strip_interface_speed(
                      interface[interface['interfaceType']]['maximumInterfaceSpeed']
                      if (interface['interfaceType'] == 'sata' or
                          interface['interfaceType'] == 'sas' or
                          interface['interfaceType'] == 'fibre')
                      else (
                          interface[interface['interfaceType']]['currentSpeed']
                          if interface['interfaceType'] == 'ib'
                          else (
                              interface[interface['interfaceType']]['interfaceData']['maximumInterfaceSpeed']
                              if interface['interfaceType'] == 'iscsi' else 'unknown'
                          ))),
              )
              for controller in array_facts['controller']
              for interface in controller['driveInterfaces']]
  
          facts['netapp_storage_pools'] = [
              dict(
                  id=storage_pool['id'],
                  name=storage_pool['name'],
                  available_capacity=storage_pool['freeSpace'],
                  total_capacity=storage_pool['totalRaidedSpace'],
                  used_capacity=storage_pool['usedSpace']
              ) for storage_pool in array_facts['volumeGroup']]
  
          all_volumes = list(array_facts['volume'])
  
          facts['netapp_volumes'] = [
              dict(
                  id=v['id'],
                  name=v['name'],
                  parent_storage_pool_id=v['volumeGroupRef'],
                  capacity=v['capacity'],
                  is_thin_provisioned=v['thinProvisioned'],
                  workload=v['metadata'],
              ) for v in all_volumes]
  
          workload_tags = None
          try:
              rc, workload_tags = self.request("storage-systems/%s/workloads" % self.ssid)
          except Exception as error:
              self.module.fail_json(msg="Failed to retrieve workload tags. Array [%s]." % self.ssid)
  
          facts['netapp_workload_tags'] = [
              dict(
                  id=workload_tag['id'],
                  name=workload_tag['name'],
                  attributes=workload_tag['workloadAttributes']
              ) for workload_tag in workload_tags]
  
          # Create a dictionary of volume lists keyed by host names
          facts['netapp_volumes_by_initiators'] = dict()
          for mapping in array_facts['storagePoolBundle']['lunMapping']:
              for host in facts['netapp_hosts']:
                  if mapping['mapRef'] == host['hosts_reference'] or mapping['mapRef'] == host['group_id']:
                      if host['name'] not in facts['netapp_volumes_by_initiators'].keys():
                          facts['netapp_volumes_by_initiators'].update({host['name']: []})
  
                      for volume in all_volumes:
                          if mapping['id'] in [volume_mapping['id'] for volume_mapping in volume['listOfMappings']]:
  
                              # Determine workload name if there is one
                              workload_name = ""
                              metadata = dict()
                              for volume_tag in volume['metadata']:
                                  if volume_tag['key'] == 'workloadId':
                                      for workload_tag in facts['netapp_workload_tags']:
                                          if volume_tag['value'] == workload_tag['id']:
                                              workload_name = workload_tag['name']
                                              metadata = dict((entry['key'], entry['value'])
                                                              for entry in workload_tag['attributes']
                                                              if entry['key'] != 'profileId')
  
                              facts['netapp_volumes_by_initiators'][host['name']].append(
                                  dict(name=volume['name'],
                                       id=volume['id'],
                                       wwn=volume['wwn'],
                                       workload_name=workload_name,
                                       meta_data=metadata))
  
          features = [feature for feature in array_facts['sa']['capabilities']]
          features.extend([feature['capability'] for feature in array_facts['sa']['premiumFeatures']
                           if feature['isEnabled']])
          features = list(set(features))  # ensure unique
          features.sort()
          facts['netapp_enabled_features'] = features
  
          return facts
  
      def get_facts(self):
          """Get the embedded or web services proxy information."""
          facts = self.get_array_facts()
  
          self.module.log("isEmbedded: %s" % self.is_embedded())
          self.module.log(pformat(facts))
  
          self.module.exit_json(msg="Gathered facts for storage array. Array ID: [%s]." % self.ssid,
                                storage_array_facts=facts)
  
  
  def strip_interface_speed(speed):
      """Converts symbol interface speeds to a more common notation. Example: 'speed10gig' -> '10g'"""
      if isinstance(speed, list):
          result = [match(r"speed[0-9]{1,3}[gm]", sp) for sp in speed]
          result = [sp.group().replace("speed", "") if result else "unknown" for sp in result if sp]
          result = ["auto" if match(r"auto", sp) else sp for sp in result]
      else:
          result = match(r"speed[0-9]{1,3}[gm]", speed)
          result = result.group().replace("speed", "") if result else "unknown"
          result = "auto" if match(r"auto", result.lower()) else result
      return result
  
  
  def main():
      facts = Facts()
      facts.get_facts()
  
  
  if __name__ == "__main__":
      main()
  