#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# |             ____ _               _        __  __ _  __           |
# |            / ___| |__   ___  ___| | __   |  \/  | |/ /           |
# |           | |   | '_ \ / _ \/ __| |/ /   | |\/| | ' /            |
# |           | |___| | | |  __/ (__|   <    | |  | | . \            |
# |            \____|_| |_|\___|\___|_|\_\___|_|  |_|_|\_\           |
# |                                                                  |
# | Copyright Mathias Kettner 2014             mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk is free software;  you can redistribute it and/or modify it
# under the  terms of the  GNU General Public License  as published by
# the Free Software Foundation in version 2.  check_mk is  distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY;  with-
# out even the implied warranty of  MERCHANTABILITY  or  FITNESS FOR A
# PARTICULAR PURPOSE. See the  GNU General Public License for more de-
# tails. You should have  received  a copy of the  GNU  General Public
# License along with GNU Make; see the file  COPYING.  If  not,  write
# to the Free Software Foundation, Inc., 51 Franklin St,  Fifth Floor,
# Boston, MA 02110-1301 USA.

import sys, getopt

try:
    from NaElement import *
    from NaServer import *
except Exception, e:
    sys.stderr.write("Unable to import the files NaServer.py/NaElement.py.\nThese files are "\
                     "provided by the NetApp Managability SDK and must be put into "\
                     "the sites folder ~/local/lib/python.\nDetailed error: %s\n" % e)
    sys.exit(1)


# Use this block if you want to use TLS instead of SSL authentification

#import ssl
#from functools import wraps
#def sslwrap(func):
#    @wraps(func)
#    def bar(*args, **kw):
#        kw['ssl_version'] = ssl.PROTOCOL_TLSv1
#        return func(*args, **kw)
#    return bar
#
#ssl.wrap_socket = sslwrap(ssl.wrap_socket)


def usage():
    sys.stderr.write("""Check_MK NetApp Agent

USAGE: agent_netapp [OPTIONS] HOST

ARGUMENTS:
  HOST                          Host name or IP address of NetApp Filer

OPTIONS:
  -h, --help                    Show this help message and exit
  -u USER, --user USER          Username for NetApp login
  -s SECRET, --secret SECRET    Secret/Password for NetApp login
  -p, --port port               Alternative port number (default is 443 for the https connection)
  --debug                       Debug mode: let Python exceptions come through
  --xml,                        Dump xml messages into agent output
  -t, --timeout SECS            Set the network timeout to the NetApp filer to SECS seconds.
                                This is also used when connecting the agent (option -a).
                                Default is 60 seconds. Note: the timeout is not only
                                applied to the connection, but also to each individual
                                subquery.
  --nocounters volumes          (clustermode only), skip counters for the given element
                                right now only "volumes" is supported

Required user permissions
#########################

General:
 cf-status
 diagnosis-status-get
 perf-object-get-instances
 perf-object-instance-list-info-iter
 storage-shelf-environment-list-info
 system-get-info
 system-get-version

7-Mode specific:
 aggr-list-info
 disk-list-info
 license-v2-list-info
 net-ifconfig-get
 snapvault-secondary-relationship-status-list-iter-end
 snapvault-secondary-relationship-status-list-iter-next
 snapvault-secondary-relationship-status-list-iter-start
 vfiler-list-info
 volume-list-info

Cluster-Mode specific:
 aggr-get-iter
 fcp-adapter-get-iter
 fcp-interface-get-iter
 net-interface-get-iter
 net-port-get-iter
 snapmirror-get-iter
 storage-disk-get-iter
 storage-shelf-environment-list-info
 system-get-node-info-iter
 system-get-node-info-iter
 system-get-node-info-iter
 system-node-get-iter
 volume-get-iter
 vserver-get-iter
""")


# NetApp credentials
user   = None
secret = None

opt_timeout     = 120
opt_debug       = False
opt_dump_xml    = False
opt_no_counters = []

short_options = 'hu:s:t:'
long_options  = ['help', 'debug', 'user=', 'secret=', 'timeout=', 'xml', "nocounters="]

try:
    opts, args = getopt.getopt(sys.argv[1:], short_options, long_options)
except getopt.GetoptError, err:
    sys.stderr.write("%s\n" % err)
    sys.exit(1)

for o,a in opts:
    if o in [ '--debug' ]:
        opt_debug = True
    elif o in [ '-u', '--user' ]:
        user = a
    elif o in [ '-s', '--secret' ]:
        secret = a
    elif o in [ '--nocounters']:
        opt_no_counters = a.split(",")
    elif o in [ '--xml']:
        opt_dump_xml = True
    elif o in [ '-t', '--timeout' ]:
        opt_timeout = int(a)
    elif o in [ '-h', '--help' ]:
        usage()
        sys.exit(0)

if len(args) == 1:
    host_address = args[0]
elif not args:
    sys.stderr.write("ERROR: No host given.\n")
    sys.exit(1)
else:
    sys.stderr.write("ERROR: Please specify exactly one host.\n")
    sys.exit(1)



#.
#   .--Format-Fctns--------------------------------------------------------.
#   |   _____                          _        _____    _                 |
#   |  |  ___|__  _ __ _ __ ___   __ _| |_     |  ___|__| |_ _ __  ___     |
#   |  | |_ / _ \| '__| '_ ` _ \ / _` | __|____| |_ / __| __| '_ \/ __|    |
#   |  |  _| (_) | |  | | | | | | (_| | ||_____|  _| (__| |_| | | \__ \    |
#   |  |_|  \___/|_|  |_| |_| |_|\__,_|\__|    |_|  \___|\__|_| |_|___/    |
#   |                                                                      |
#   +----------------------------------------------------------------------+

def create_dict(instances, custom_key = [], is_counter = True):
    if not instances:
        return {}

    result = {}
    for instance in instances.children_get():
        values = {}
        if is_counter:
            for node in instance.child_get("counters").children_get():
                values[node.child_get_string("name")] = node.child_get_string("value")
        else:
            for node in instance.children_get():
                values[node.element["name"]] = node.element["content"]

        if custom_key:
            if type(custom_key) == list:
                tokens = []
                for name in custom_key:
                    tokens.append(values[name])
                key = "|".join(tokens)
            else:
                key = values[custom_key]
        else:
            # Used to identify counters
            key = instance.child_get_string("name")
        result[key] = values
    return result

# Format config as one liner. Might add extra info identified by config_key
def format_config(instances, prefix, config_key, config_report = "all",
                  config_scale = {}, config_rename = {},
                  extra_info = {}, extra_info_report = "all",
                  delimeter = "\t", skip_missing_config_key = False):

    result = []
    values = {}

    def collect_values(node, namespace = ""):
        for entry in node.children_get():
            collect_values(entry, namespace + node.element["name"] + ".")

        if node.element["content"]:
            values["%s%s" % (namespace, node.element["name"])] = node.element["content"]


    for instance in instances.children_get():
        values = {}
        for node in instance.children_get():
            collect_values(node)

        line = []
        if type(config_key) == list:
            instance_key = []
            for entry in config_key:
               instance_key.append(values.get(entry))
            instance_key = ".".join(instance_key)
        else:
            if skip_missing_config_key and config_key not in values:
                # The config_key is not available in the xml node
                # Looks like the information is only available through the other node
                continue

            instance_key = values.get(config_key, config_key)
            if config_key in values:
                del values[config_key]
        line.append("%s %s" % (prefix, instance_key))
        for key, value in values.items():
            if config_report == "all" or key in config_report:
                if key in config_scale:
                    value = int(value) * config_scale[key]
                key = config_rename.get(key, key)
                line.append("%s %s" % (key, value))

        if instance_key in extra_info:
            for key, value in extra_info[instance_key].items():
                if value and (extra_info_report == "all" or key in extra_info_report):
                    line.append("%s %s" % (key, value))

        result.append(("%s" % delimeter).join(map(lambda x: x.encode("utf-8"), line)))
    return "\n".join(result)

# Format instance without subnodes as key/value lines
def format_as_key_value(plain_instance, prefix = "", report = "all", delimeter = "\t"):
    result = []
    for node in plain_instance.children_get():
        if report == "all" or node.element["name"] in report:
            if node.element["content"]:
                result.append("%s%s%s%s" % (prefix, node.element["name"], delimeter, node.element["content"]))
    return "\n".join(result)

# Output a single dictionary
def format_dict(the_dict, prefix = "", report = "all", delimeter = "\t", as_line = False):
    result = []

    values = {}
    for key, value in the_dict.items():
        if report == "all" or key in report:
            values[key] = value

    if as_line:
        line = []
        if prefix:
            line.append(prefix)
        for key, value in values.items():
            line.append("%s %s" % (key, value))
        return ("%s" % delimeter).join(line)
    else:
        for key, value in values.items():
            result.append("%s%s%s%s" % (prefix, key, delimeter, value))
    return "\n".join(result)



#.
#   .--Query-Helpers-------------------------------------------------------.
#   |  ___                              _   _      _                       |
#   | / _ \ _   _  ___ _ __ _   _      | | | | ___| |_ __   ___ _ __ ___   |
#   || | | | | | |/ _ \ '__| | | |_____| |_| |/ _ \ | '_ \ / _ \ '__/ __|  |
#   || |_| | |_| |  __/ |  | |_| |_____|  _  |  __/ | |_) |  __/ |  \__ \  |
#   | \__\_\\__,_|\___|_|   \__, |     |_| |_|\___|_| .__/ \___|_|  |___/  |
#   |                       |___/                   |_|                    |
#   +----------------------------------------------------------------------+

nodes = []
def query_nodes(what, node_attribute = "node-name"):
    global nodes, section_errors
    if not nodes:
        node_query = NaElement("system-get-node-info-iter")
        node_list = server.invoke_elem(node_query)
        nodes = []
        for instance in node_list.child_get("attributes-list").children_get():
            nodes.append(instance.child_get_string("system-name"))

    results = {}
    for node in nodes:
        query = NaElement(what)
        query.child_add_string(node_attribute, node)
        response = server.invoke_elem(query)
        if response.results_status() == "failed":
            section_errors.append("In class %s: %s" % (what, response.results_reason()))
            continue

        results["%s.%s" % (what, node)] = response

    return results

section_errors = []
def query(what, return_toplevel_node = False):
    # HACK: if "what" endswith get-iter, add max_records = 5000
    # This approach is way easier than reading the tag, invoke another
    # command and merge all answers together
    if what.endswith("get-iter"):
        response = server.invoke(what, "max-records", 5000)
    else:
        response = server.invoke(what)

    if response.results_status() == "failed":
        section_errors.append("In class %s: %s" % (what, response.results_reason()))
    else:
        if return_toplevel_node:
            return response
        else:
            data = response.children_get()
            if data:
                return data[0]

def query_counters(what):
    instance_uuids = []

    counter_query = NaElement("perf-object-get-instances")
    counter_query.child_add_string("objectname", what)

    # In clustermode there is no "get all" command for performance counters
    # We need to determine the instance names first and add them to the query
    if netapp_mode == "clustermode":
        instance_query = NaElement("perf-object-instance-list-info-iter")
        instance_query.child_add_string("objectname", what)

        instance_query_response = server.invoke_elem(instance_query)
        instance_list = instance_query_response.child_get("attributes-list")
        if instance_list:
            for instance_data in instance_list.children_get():
                instance_uuids.append(instance_data.child_get_string("uuid"))
            if not instance_uuids:
                # Nothing to query..
                return

            instances_to_query = NaElement("instance-uuids")
            for uuid in instance_uuids:
                instances_to_query.child_add_string("instance-uuid", uuid)
            counter_query.child_add(instances_to_query)
        else:
            return

    # Query counters
    response = server.invoke_elem(counter_query)
    if response.results_status() == "failed":
        section_errors.append("In counter %s: %s" % (what, response.results_reason()))
    else:
        return response.child_get("instances")

#.
#   .--Main----------------------------------------------------------------.
#   |                        __  __       _                                |
#   |                       |  \/  | __ _(_)_ __                           |
#   |                       | |\/| |/ _` | | '_ \                          |
#   |                       | |  | | (_| | | | | |                         |
#   |                       |_|  |_|\__,_|_|_| |_|                         |
#   |                                                                      |
#   +----------------------------------------------------------------------+

# Login
try:
    server = NaServer(host_address,1,8)
    server.set_admin_user(user, secret)
    server.set_timeout(opt_timeout)
    server.set_transport_type("HTTPS")
    server.set_server_cert_verification(False)
    if opt_dump_xml:
        server.set_debug_style("NA_PRINT_DONT_PARSE")
except Exception, e:
    if opt_debug:
        raise
    sys.stderr.write("Cannot connect to NetApp Server. Maybe you provided wrong " \
                     "credentials. Please check your connection settings and try " \
                     "again.")
    sys.exit(1)

# Determine if this filer is running 7mode or clustermode
version_info = query("system-get-version", return_toplevel_node = True)
if not version_info:
    sys.stderr.write(",".join(section_errors))
    sys.exit(1)

clustered_info = version_info.child_get_string("is-clustered")
if clustered_info:
    netapp_mode  = clustered_info.lower() == "false" and "7mode" or "clustermode"
else:
    # Looks like the is-clustered attribute is not set, e.g. NetApp 7-Mode Version 8.0
    version_string = version_info.child_get_string("version").lower()
    # TODO: Needs improvement. Unfortunately the version info string does not provide
    # exact info whether its a 7mode or a clustermode system
    # Possible approach: Query a class which does not exist in 7-mode and evaluate response
    if "NetApp Release 7.3.5.1".lower() in version_string:
        netapp_mode  = "7mode"
    else:
        netapp_mode  = "7-mode" in version_string and "7mode" or "clustermode"


#.
#   .--Clustermode Settings------------------------------------------------.
#   |       ____ _           _                                _            |
#   |      / ___| |_   _ ___| |_ ___ _ __ _ __ ___   ___   __| | ___       |
#   |     | |   | | | | / __| __/ _ \ '__| '_ ` _ \ / _ \ / _` |/ _ \      |
#   |     | |___| | |_| \__ \ ||  __/ |  | | | | | | (_) | (_| |  __/      |
#   |      \____|_|\__,_|___/\__\___|_|  |_| |_| |_|\___/ \__,_|\___|      |
#   |                                                                      |
#   |                ____       _   _   _                                  |
#   |               / ___|  ___| |_| |_(_)_ __   __ _ ___                  |
#   |               \___ \ / _ \ __| __| | '_ \ / _` / __|                 |
#   |                ___) |  __/ |_| |_| | | | | (_| \__ \                 |
#   |               |____/ \___|\__|\__|_|_| |_|\__, |___/                 |
#   |                                           |___/                      |
#   +----------------------------------------------------------------------+

if netapp_mode == "clustermode":
    # VServer
    vservers = query("vserver-get-iter")
    if vservers:
        print "<<<netapp_api_vs_status:sep(9)>>>"
        vserver_dict = create_dict(vservers, custom_key = [ "vserver-name" ], is_counter = False)

        vserver_info = {}
        for key, values in vserver_dict.items():
            if "state" in values:
                vserver_info[key] = values.get("state")
        print format_dict(vserver_info)

    # VServer Stats
    print "<<<netapp_api_vs_traffic:sep(9)>>>"
    for what in ["lif:vserver", "fcp_lif:vserver", "iscsi_lif:vserver", "cifs:vserver", "nfsv3", "nfsv4", "nfsv4_1"]:
        result = query_counters(what)
        if result:
            result_dict = create_dict(result)
            for key, value in result_dict.items():
                print format_dict(value, prefix = "protocol %s" % what, as_line = True)

    # Interfaces
    interfaces  = query("net-interface-get-iter")
    ports       = query("net-port-get-iter")
    if_counters = query_counters("lif")

    if interfaces:
        print "<<<netapp_api_if:sep(9)>>>"
        port_dict = create_dict(ports, custom_key = ["node", "port"], is_counter = False)
        if_dict   = create_dict(if_counters, custom_key = "instance_name")
        map_if_nodeport_to_name = {}
        for key, values in if_dict.items():
            map_if_nodeport_to_name[(values["node_name"], values["current_port"])] = key

        mod_port_dict = {}
        for key, values in port_dict.items():
            port_key = map_if_nodeport_to_name.get((values["node"], values["port"]))
            if port_key:
                mod_port_dict[port_key] = values

        # NetApp clustermode reports sent_data instead of send_data..
        for key, values in if_dict.items():
            for old, new in [ ("sent_data",    "send_data"),
                              ("sent_packet",  "send_packet"),
                              ("sent_errors",  "send_errors") ]:
                values[new] = values[old]
                del values[old]

        extra_info = if_dict
        for key, values in mod_port_dict.items():
            extra_info[key].update(values)

        print format_config(interfaces, "interface", "interface-name",
                            extra_info = extra_info,
                            extra_info_report = [ "recv_data", "send_data",
                                                  "recv_mcasts", "send_mcasts",
                                                  "recv_errors", "send_errors",
                                                  "instance_name", "link-status", "operational-speed"])


    # Fibrechannels
    fcp_counters = query_counters("fcp_lif")
    fcp_ports    = query("fcp-interface-get-iter")
    fcp_adapter  = query("fcp-adapter-get-iter")

    if fcp_counters:
        print "<<<netapp_api_fcp:sep(9)>>>"
        port_dict = create_dict(fcp_adapter, custom_key = "port-name", is_counter = False)
        fcp_counter_dict = create_dict(fcp_counters, custom_key = "instance_name")

        port_extra_info = {}
        for key, values in fcp_counter_dict.items():
            if values["port_wwpn"] in port_dict:
                values.update(port_dict[values["port_wwpn"]])

        print format_config(fcp_ports, "fcp", "interface-name",
                            extra_info = fcp_counter_dict)


    # CPU Util for both nodes
    node_info = query("system-get-node-info-iter")
    system_info = query("system-node-get-iter")
    if node_info and system_info:
        print "<<<netapp_api_cpu:sep(9)>>>"
        print format_config(node_info, "cpu-info", "system-name", config_report = ["number-of-processors"],
                                                                  config_rename = {"number-of-processors": "num_processors"})
        print format_config(system_info, "cpu-info", "node", config_scale = {"cpu-busytime": 1000000},
                                                             config_report = ["cpu-busytime", "nvram-battery-status"],
                                                             config_rename = {"cpu-busytime": "cpu_busy"})

    # Cluster info
    # TODO: check is missing
    cluster_status = query_nodes("cf-status", node_attribute = "node")
    ha_partners = {} # Used later on by environmental sensors
    if cluster_status:
        print "<<<netapp_api_cm_cluster:sep(9)>>>"
        for node, entry in cluster_status.items():
            # Small trick improve formatting
            container = NaElement("container")
            container.child_add(entry)
            partner_name = entry.child_get_string("partner-name")
            if partner_name:
                ha_partners[node] = partner_name
            print format_config(container, "cluster", node.split(".",1)[1])

    # Disk
    disks = query("storage-disk-get-iter")
    if disks:
        print "<<<netapp_api_disk:sep(9)>>>"
        print format_config(disks, "disk", "disk-uid",
                                    config_report = [ "disk-inventory-info.shelf-bay",
                                                      "disk-inventory-info.serial-number",
                                                      "disk-inventory-info.vendor",
                                                      "disk-raid-info.container-type",
                                                      "disk-raid-info.position",
                                                      "disk-raid-info.used-blocks",
                                                      "disk-raid-info.physical-blocks" ],
                                    config_scale  = { "disk-raid-info.physical-blocks": 4096,
                                                      "disk-raid-info.used-blocks": 4096 },
                                    config_rename = { "disk-inventory-info.shelf-bay": "bay",
                                                      "disk-inventory-info.serial-number": "serial-number",
                                                      "disk-inventory-info.vendor": "vendor-id",
                                                      "disk-raid-info.container-type": "raid-state",
                                                      "disk-raid-info.position": "raid-type",
                                                      "disk-raid-info.used-blocks": "used-space",
                                                      "disk-raid-info.physical-blocks": "physical-space"}
        )

    # Volumes
    volumes = query("volume-get-iter")
    if "volumes" in opt_no_counters:
        volume_counters = None
    else:
        volume_counters = query_counters("volume")
    if volumes:
        print "<<<netapp_api_volumes:sep(9)>>>"
        print format_config(volumes, "volume", "volume-id-attributes.instance-uuid",
                                    config_report = [ "volume-space-attributes.size-available",
                                                      "volume-space-attributes.size-total",
                                                      "volume-state-attributes.state",
                                                      "volume-id-attributes.name",
                                                      "volume-id-attributes.node",
                                                      "volume-id-attributes.msid",
                                                      "volume-inode-attributes.files-total",
                                                      "volume-inode-attributes.files-used" ],
                                    config_rename = { "volume-space-attributes.size-available": "size-available",
                                                      "volume-space-attributes.size-total"    : "size-total",
                                                      "volume-state-attributes.state"         : "state",
                                                      "volume-id-attributes.name"             : "name",
                                                      "volume-id-attributes.msid"             : "msid",
                                                      "volume-id-attributes.node"             : "node",
                                                      "volume-inode-attributes.files-total"   : "files-total",
                                                      "volume-inode-attributes.files-used"    : "files-used" },
                                    extra_info = create_dict(volume_counters, custom_key = [ "instance_uuid" ]),
                                    extra_info_report = sum(map(lambda x: ["%s" % x, "nfs_%s" % x, "cifs_%s" % x, "san_%s" % x, "fcp_%s" % x, "iscsi_%s" % x],
                                                        sum(map(lambda x: ["read_%s" % x, "write_%s" % x], ["data", "latency"]), [])), []) + [ "instance_name" ],
                                    skip_missing_config_key = True
        )

    # Aggregations
    aggregations = query("aggr-get-iter")
    if aggregations:
        print "<<<netapp_api_aggr:sep(9)>>>"
        print format_config(aggregations, "aggregation", "aggregate-name",
                                    config_report = [ "aggr-space-attributes.size-available",
                                                      "aggr-space-attributes.size-total"],
                                    config_rename = { "aggr-space-attributes.size-available": "size-available",
                                                      "aggr-space-attributes.size-total"    : "size-total"})


    # Diagnosis status
    diag_status = query("diagnosis-status-get")
    if diag_status:
        print "<<<netapp_api_status>>>"
        print format_config(diag_status, "status", "status")

    # NetApp System Version/Info
    system_version = query("system-get-version", return_toplevel_node = True)
    system_info    = query("system-get-node-info-iter")
    if system_version:
        print "<<<netapp_api_info:sep(9)>>>"
        print format_as_key_value(system_version)
        if system_info:
            child_dict = create_dict(system_info, custom_key = "system-name", is_counter = False)
            for key, values in child_dict.items():
                print format_dict(values, prefix = "node %s" % key, as_line = True)

    # Snapmirror / Snapvault lag-time
    snapmirror_info = query("snapmirror-get-iter")
    if snapmirror_info:
        print "<<<netapp_api_snapvault:sep(9)>>>"
        print format_config(snapmirror_info, "snapvault", "destination-volume",
                                    config_report = [ "destination-volume-node",
                                                      "policy",
                                                      "mirror-state",
                                                      "source-vserver",
                                                      "lag-time",
                                                      "relationship-status" ],
                                    config_rename = { "destination-volume-node": "destination-system",
                                                      "mirror-state": "state",
                                                      "source-vserver": "source-system",
                                                      "relationship-status": "status" }
        )

    # Environmental sensors
    environment_info = query_nodes("storage-shelf-environment-list-info")
    processed_nodes = []
    if environment_info:
        for node, values in environment_info.items():
            if node in processed_nodes:
                continue
            channel_list = values.child_get("shelf-environ-channel-list")
            for channel in channel_list.children_get():                                    # cycle channel list
                shelf_list = channel.child_get("shelf-environ-shelf-list")
                for shelf in channel.child_get("shelf-environ-shelf-list").children_get(): # cycle shelf list
                    shelf_id = shelf.child_get_string("shelf-id")
                    for what, section in [ ("power-supply-list",    "netapp_api_psu"),
                                           ("cooling-element-list", "netapp_api_fan"),
                                           ("temp-sensor-list",     "netapp_api_temp") ]:
                        print "<<<%s:sep(9)>>>" % section
                        node = shelf.child_get(what)
                        print format_config(node, what, shelf_id)
            # HA partners always report redundant environmental data.
            # (Node1/Node2) <--Cluster--> (Node3/Node4)
            # We skip data from Node2 and Node4.
            processed_nodes.append(node)
            if node in ha_partners:
                processed_nodes.append(ha_partners[node])


#.
#   .--7Mode Settings------------------------------------------------------.
#   |                    _____ __  __           _                          |
#   |                   |___  |  \/  | ___   __| | ___                     |
#   |                      / /| |\/| |/ _ \ / _` |/ _ \                    |
#   |                     / / | |  | | (_) | (_| |  __/                    |
#   |                    /_/  |_|  |_|\___/ \__,_|\___|                    |
#   |                                                                      |
#   |                ____       _   _   _                                  |
#   |               / ___|  ___| |_| |_(_)_ __   __ _ ___                  |
#   |               \___ \ / _ \ __| __| | '_ \ / _` / __|                 |
#   |                ___) |  __/ |_| |_| | | | | (_| \__ \                 |
#   |               |____/ \___|\__|\__|_|_| |_|\__, |___/                 |
#   |                                           |___/                      |
#   +----------------------------------------------------------------------+
else:
    # Interfaces
    interfaces  = query("net-ifconfig-get")
    if_counters = query_counters("ifnet")
    if interfaces:
        print "<<<netapp_api_if:sep(9)>>>"
        print format_config(interfaces, "interface", "interface-name",
                            extra_info = create_dict(if_counters), extra_info_report = [ "recv_data", "send_data",
                                                                                         "recv_mcasts", "send_mcasts",
                                                                                         "recv_errors", "send_errors",
                                                                                         "instance_name", "mediatype"])
    # TODO: Fibrechannel interfaces

    # CPU
    system_counters = query_counters("system")
    if system_counters:
        print "<<<netapp_api_cpu:sep(9)>>>"
        dict_counters = create_dict(system_counters)
        print format_dict(dict_counters.get("system"), report = ["cpu_busy", "num_processors"])


    # Volumes
    volumes = query("volume-list-info")
    if "volumes" in opt_no_counters:
        volume_counters = None
    else:
        volume_counters = query_counters("volume")
    if volumes:
        print "<<<netapp_api_volumes:sep(9)>>>"
        print format_config(volumes, "volume", "name",
                            config_report = ["name", "volume-info", "size-total", "size-available",
                                             "volumes", "files-total", "files-used", "state"],
                            extra_info = create_dict(volume_counters),
                            extra_info_report = sum(map(lambda x: ["%s" % x, "nfs_%s" % x, "cifs_%s" % x, "san_%s" % x, "fcp_%s" % x, "iscsi_%s" % x],
                                                sum(map(lambda x: ["read_%s" % x, "write_%s" % x], ["data", "latency"]), [])), []) + [ "instance_name" ])


    # Aggregation
    aggregations = query("aggr-list-info")
    if aggregations:
        print "<<<netapp_api_aggr:sep(9)>>>"
        print format_config(aggregations, "aggregation", "name",
                            config_report = ["name", "size-total", "size-available"])

    # Snapshot info
    print "<<<netapp_api_snapshots:sep(9)>>>"
    for volume in volumes.children_get():
        container = NaElement("container")
        container.child_add(volume)
        print format_config(container, "volume_snapshot", "name",
                    config_report = ["name", "size-total", "snapshot-percent-reserved", "state",
                                     "snapshot-blocks-reserved", "reserve-used-actual"])

    # Protocols
    print "<<<netapp_api_protocol:sep(9)>>>"
    for what, key in [ ("nfsv3", "nfs"), ("nfsv4", "nfsv4"), ("iscsi", "iscsi"), ("cifs", "cifs"), ("fcp", "fcp") ]:
        protocol_counters = query_counters(what)
        if protocol_counters:
            protocol_dict = create_dict(protocol_counters)
            print format_dict(protocol_dict[key],
                              report = [ "instance_name", "%s_read_ops" % what, "%s_write_ops" % what ],
                              prefix = "protocol %s" % key, as_line = True)

    # Diagnosis status
    diag_status = query("diagnosis-status-get")
    if diag_status:
        print "<<<netapp_api_status>>>"
        print format_config(diag_status, "status", "status")

    # 7Mode Cluster info
    cluster_status = query("cf-status", return_toplevel_node = True)
    if cluster_status:
        print "<<<netapp_api_cluster:sep(9)>>>"
        print format_as_key_value(cluster_status)


    # Disks
    disk_info = query("disk-list-info")
    if disk_info:
        print "<<<netapp_api_disk:sep(9)>>>"
        print format_config(disk_info, "disk", "disk-uid",
                                config_report = [ "raid-state", "raid-type", "physical-space", "bay",
                                                  "raid-type", "used-space", "serial-number", "disk-uid",
                                                  "disk-model", "vendor-id" ]
        )

    # VFiler
    vfiler_info  = query("vfiler-list-info")
    vfiler_names = []
    if vfiler_info:
        print "<<<netapp_api_vf_status:sep(9)>>>"
        for vfiler in vfiler_info.children_get():
            name = vfiler.child_get_string("name")
            vfiler_names.append(name)
            response = server.invoke("vfiler-get-status", "vfiler", name)
            print "%s\t%s" % (name, response.child_get_string("status"))

    # Snapvaults
    if vfiler_names:
        print "<<<netapp_api_snapvault:sep(9)>>>"
        for vfiler in vfiler_names:
            server.set_vfiler(vfiler)
            response = server.invoke("snapvault-secondary-relationship-status-list-iter-start")
            records  = response.child_get_string("records")
            if not records or records == "0":
                continue
            tag = response.child_get_string("tag")
            response = server.invoke("snapvault-secondary-relationship-status-list-iter-next", "maximum", records, "tag", tag)
            print format_config(response.child_get("status-list"), "snapvault", "source-path",
                                    config_report = ["lag-time", "state", "status", "source-system", "destination-system"])
            server.invoke("snapvault-secondary-relationship-status-list-iter-end","tag", tag)
        server.set_vfiler("")

    # VFiler Counters
    vfiler_counters = query_counters("vfiler")
    if vfiler_counters:
        print "<<<netapp_api_vf_stats:sep(9)>>>"
        vfiler_dict = create_dict(vfiler_counters)
        for key, values in vfiler_dict.items():
            print format_dict(values, prefix = "vfiler %s" % key, as_line = True)

    # NetApp System Version/Info
    system_info    = query("system-get-info")
    system_version = query("system-get-version", return_toplevel_node = True)
    if system_info:
        print "<<<netapp_api_info:sep(9)>>>"
        print format_as_key_value(system_info)
        print format_as_key_value(system_version)

    # Sensors: Temp, Fan, PSU
    # Definition: all sensors are always monitored by one of the filers
    # We choose this filer by an alphanumerical compare
    system_name         = system_info.child_get_string("system-name")
    partner_system_name = system_info.child_get_string("partner-system-name")
    if not partner_system_name or system_name < partner_system_name:
        environ_info = query("storage-shelf-environment-list-info")
        if environ_info:
            for channel in environ_info.children_get():
                shelf_list = channel.child_get("shelf-environ-shelf-list")
                if shelf_list:
                    for shelf in shelf_list.children_get():
                        shelf_id = shelf.child_get_string("shelf-id")
                        for what, section in [ ("power-supply-list",    "netapp_api_psu"),
                                               ("cooling-element-list", "netapp_api_fan"),
                                               ("temp-sensor-list",     "netapp_api_temp") ]:
                            print "<<<%s:sep(9)>>>" % section
                            node = shelf.child_get(what)
                            print format_config(node, what, shelf_id)

    # License information
    print "<<<netapp_api_licenses:sep(9)>>>"
    licensev2_info = query("license-v2-list-info")
    if licensev2_info:
        print format_config(licensev2_info, "license", "package")

#
#print "<<<netapp_errors>>>"
#import pprint
#pprint.pprint(section_errors)
