#!/usr/bin/env python2.7

''' An Ansible module to perform various OSPF tasks on Nexus 9000 '''

# Copyright (c) 2014 Matt Oswalt
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied. See the License for the specific language governing
# permissions and limitations under the License.

DOCUMENTATION = '''
---
module: nxapi_ospf
author: Matt Oswalt
version_added: 1.0
short_description: Verify OSPF on Nexus 9000 switches
description:
    - A module to verify OSPF operation on Nexus 9000 switches
requirements:
    - Mierdin's fork of nexus9000 https://github.com/Mierdin/nexus9000
options:
    host:
        description:
            - should be {{ inventory_hostname }}
        required: true  
    user:
        description:
            - login user-name 
        required: true
        default: "admin"
    passwd:
        description:
            - login password
        required: true               
    logfile:
        description:
            - location for log file
        required: false 
        default: None        
'''

EXAMPLES = '''
#TODO: Fill out examples
'''

#TODO: make var names pep8 compliant

#TODO: dependency on new library. Re-enable old line for current testing.
from cisco.nxapi.nxapiutils.nxapi_utils import NXAPI
#from nxos.nxapi.utils.nxapi_utils import NXAPI

import logging
import json

def getospfprops(thisNXAPI, ospfPort):
    """Connects to a switch and retrieves OSPF properties"""

    ospfReport = dict()
    ospfReport['details'] = dict()

    thisNXAPI.set_cmd('show license host-id')
    returnList = thisNXAPI.send_req()
    returnDict = json.loads(returnList[1])
    ospfReport['hostid'] = returnDict['ins_api']['outputs']['output'] \
            ['body']['host_id'][4:] #gets rid of the VDH=

    #TODO: Remove after demo
    ospfReport['hostid'] = 'XXXXXXXX' + ospfReport['hostid'][-3:]

    #checking to see if there is a neighbor on the other side
    #TODO: This is obviously catered for p2p links - consider 
        #if it will be important to handle multiple neighbors
        # on a link. (I'm thinking not because of the spine leaf topo)
    thisNXAPI.set_cmd('show ip ospf nei int ' + ospfPort)
    returnList = thisNXAPI.send_req()
    returnDict = json.loads(returnList[1])

    try:
        neighborDict = returnDict['ins_api']['outputs']['output']['body'] \
                ['TABLE_ctx']['ROW_ctx']['TABLE_nbr']['ROW_nbr']
        ospfReport['details']['state'] = neighborDict['state']
        ospfReport['details']['uptime'] = neighborDict['uptime']

    except KeyError: #This is thrown if there is no neighbor
        ospfReport['details']['nei_status'] = 'DOWN'
        ospfReport['details']['nei_uptime'] = 'none'


    #time to gather more info
    #TODO: statically setting instance name here. Need to make an arg (ideal) 
           # or loop through all potential instances (not ideal)
    thisNXAPI.set_cmd('show ip ospf CLOS int ' + ospfPort)
    returnList = thisNXAPI.send_req()
    returnDict = json.loads(returnList[1])
    iface = returnDict['ins_api']['outputs']['output']['body'] \
            ['TABLE_ctx']['ROW_ctx']['TABLE_intf']['ROW_intf']
            #TODO: Note that ROW_ctx would be a list if we weren't
                    #specifying the instance here.

    #TODO: This all assumes only one interface and 
    #one OSPF instance because I'm manually specifying 
    #both in the NXOS command. Verify this is best approach

    #TODO: Need to ensure MTU matches as well.
    #Here I'm just capturing all I can about this interface to be
    #compared with the other switch's interface.
    ospfReport['details']['ifname'] = iface['ifname']
    ospfReport['details']['admin_status'] = iface['admin_status']
    ospfReport['details']['proto_status'] = iface['proto_status']
    ospfReport['details']['addr'] = iface['addr']
    ospfReport['details']['masklen'] = iface['masklen']
    ospfReport['details']['hello_interval'] = iface['hello_interval']
    ospfReport['details']['dead_interval'] = iface['dead_interval']
    #TODO: There is also a "wait_interval" parameter - what is this?
    ospfReport['details']['auth_type'] = iface['auth_type']
    ospfReport['details']['area'] = iface['area']

    #gather area information now
    thisNXAPI.set_cmd('show ip ospf CLOS')
    #TODO: static process again, either make dynamic here, or loop through all
    returnList = thisNXAPI.send_req()
    returnDict = json.loads(returnList[1])

    try:
        #TODO: That stupid ROW issue (list or not list, etc) came up.
            #Statically defining path. pls re-visit and handle multiple areas
        areaDict = returnDict['ins_api']['outputs']['output']['body'] \
            ['TABLE_ctx']['ROW_ctx']['TABLE_area']['ROW_area']
        ospfReport['details']['area_stub'] = areaDict['stub']
        ospfReport['details']['area_nssa'] = areaDict['nssa']
        #ospfReport['area_auth'] = areaDict['auth_type'] # TODO: area auth?

    except TypeError:
        # ROW is not a dictionary, it is a list. This indicates multiple areas.
        areas = returnDict['ins_api']['outputs']['output']['body'] \
                 ['TABLE_ctx']['ROW_ctx']['TABLE_area']['ROW_area']

        for area in areas:
            if area['aname'] == ospfReport['details']['area']:
                ospfReport['details']['area_stub'] = area['stub']
                ospfReport['details']['area_nssa'] = area['nssa']
                #ospfReport['area_auth'] = area['auth_type'] # TODO: area auth?

    return ospfReport


def main():
    module = AnsibleModule(
        argument_spec=dict( \
            host=dict(required=True), \
            user=dict(required=False, default='admin'), \
            passwd=dict(required=False, default=None), \
            neighborInfo=dict(required=True, default=None), \
            logfile=dict(required=False, default=None)), \
        supports_check_mode=False)

    m_args = module.params
    m_results = dict(changed=False)

    #Configure logging
    logfile = module.params.get('logfile')
    if logfile is not None:
        logging.basicConfig(filename=logfile, level=logging.INFO, \
                format='--- NXAPI_OSPF --- ' + \
                ' %(asctime)s:%(name)s:%(message)s')

        logging.getLogger().name = 'CONFIG:'+m_args['host']

    logging.info("Connecting to host: {}".format(m_args['host']))

    logging.info(m_args['neighborInfo'])

    report = dict()

    thisNXAPI = NXAPI()
    thisNXAPI.set_target_url('http://' + m_args['host'] + '/ins')
    thisNXAPI.set_username(m_args['user'])
    thisNXAPI.set_password(m_args['passwd'])
    thisNXAPI.set_out_format('json')
    thisNXAPI.set_msg_type('cli_show')

    #Gather properties for local interface
    report['local'] = getospfprops(thisNXAPI, m_args['neighborInfo']['local_if'])


    remoteNXAPI = NXAPI()
    remoteNXAPI.set_target_url('http://' + m_args['neighborInfo']['mgmt_ipv4'] + '/ins')
    #TODO: Assumption that credentials are the same
    remoteNXAPI.set_username(m_args['user'])
    remoteNXAPI.set_password(m_args['passwd'])
    remoteNXAPI.set_out_format('json')
    remoteNXAPI.set_msg_type('cli_show')

    #Gather properties for remote interface
    report['remote'] = getospfprops(remoteNXAPI, m_args['neighborInfo']['remote_if'])

    m_results['ospf'] = dict()

    #Using the remote switch's host ID to key off of reports
    m_results['ospf'][report['remote']['hostid']] = report

    #m_results['args'] = m_args        # for debug
    module.exit_json(**m_results)

from ansible.module_utils.basic import *
main()


