#!/usr/bin/env python3
#
# Copyright (C) 2018 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#

import sys
import os

import jinja2
import vyos.version
import ipaddress

from vyos.config import Config
from vyos import ConfigError

config_file_client = r'/etc/snmp/snmp.conf'
config_file_daemon = r'/etc/snmp/snmpd.conf'

# SNMPS template - be careful if you edit the template.
client_config_tmpl = """
### Autogenerated by snmp.py ###
{% if trap_source -%}
clientaddr {{ trap_source }}
{% endif %}

"""

# SNMPS template - be careful if you edit the template.
daemon_config_tmpl = """
### Autogenerated by snmp.py ###
# non configurable defaults
sysObjectID 1.3.6.1.4.1.44641
sysServices 14
# maybe needed by lldpd
master agentx
agentXPerms 0755 0755
# add hook to read IF-MIB::ifAlias from sysfs
pass .1.3.6.1.2.1.31.1.1.1.18 /opt/vyatta/sbin/if-mib-alias
# ospfd
smuxpeer .1.3.6.1.4.1.3317.1.2.2
# bgpd
smuxpeer .1.3.6.1.4.1.3317.1.2.5
# ripd
smuxpeer .1.3.6.1.4.1.3317.1.2.3
# mribd
smuxpeer .1.3.6.1.4.1.3317.1.2.9
# mribd
smuxpeer .1.3.6.1.2.1.83
# pimd
smuxpeer .1.3.6.1.4.1.3317.1.2.8
# pimd
smuxpeer .1.3.6.1.2.1.157
smuxsocket localhost

# linkUp/Down configure the Event MIB tables to monitor
# the ifTable for network interfaces being taken up or down
# for making internal queries to retrieve any necessary information
# create an internal snmpv3 user of the form 'vyattaxxxxxxxxxxxxxxxx'
## TODO!!!!
iquerySecName vyatta3392514e4189da84

# Modified from the default linkUpDownNotification
# to include more OIDs and poll more frequently
notificationEvent  linkUpTrap    linkUp   ifIndex ifDescr ifType ifAdminStatus ifOperStatus
notificationEvent  linkDownTrap  linkDown ifIndex ifDescr ifType ifAdminStatus ifOperStatus
monitor  -r 10 -e linkUpTrap   "Generate linkUp" ifOperStatus != 2
monitor  -r 10 -e linkDownTrap "Generate linkDown" ifOperStatus == 2

########################
# configurable section #
########################

# Version
sysDescr VyOS {{ version }}

{% if description -%}
# Description
SysDescr {{ description }}
{% endif %}

# Listen
agentaddress unix:/run/snmpd.socket{% for li in listen_on %},{{ li }}{% endfor %}


# SNMP communities
{% if communities -%}
{% for c in communities %}
{% if c.network -%}
{% for network in c.network %}
{{ c.authorization }}community {{ c.name }} {{ network }}
{% endfor %}
{% else %}
{{ c.authorization }}community {{ c.name }}
{% endif %}
{% endfor %}
{% endif %}

{% if contact -%}
# system contact information
SysContact {{ contact }}
{% endif %}

{% if location -%}
# system location information
SysLocation {{ location }}
{% endif %}

{% if smux_peers -%}
# additional smux peers
{% for sp in smux_peers %}
smuxpeer {{ sp }}
{% endfor %}
{% endif %}

"""

default_config_data = {
    'listen_on': [],
    'location' : '',
    'description' : '',
    'contact' : '',
    'communities': [],
    'trap_source': '',
    'smux_peers': []
}

def get_config():
    snmp = default_config_data
    conf = Config()
    if not conf.exists('service snmp'):
        return None
    else:
        conf.set_level('service snmp')

    version_data = vyos.version.get_version_data()
    snmp.setdefault('version', version_data['version'])

    if conf.exists('community'):
        for name in conf.list_nodes('community'):
            community = {
                'name': name,
                'authorization': 'ro',
                'network': []
            }

            if conf.exists('community {0} authorization'.format(name)):
                community['authorization'] = conf.return_value('community {0} authorization'.format(name))

            if conf.exists('community {0} network'.format(name)):
                community['network'] = conf.return_values('community {0} network'.format(name))

            snmp['communities'].append(community)

    if conf.exists('contact'):
        snmp['contact'] = conf.return_value('contact')

    if conf.exists('description'):
        snmp['description'] = conf.return_value('description')

    if conf.exists('listen-address'):
        for addr in conf.list_nodes('listen-address'):
            listen = ''
            port = '161'
            if conf.exists('listen-address {0} port'.format(addr)):
                port = conf.return_value('listen-address {0} port'.format(addr))

            if ipaddress.ip_address(addr).version == 4:
                # udp:127.0.0.1:161
                listen = 'udp:' + addr + ':' + port
            elif ipaddress.ip_address(addr).version == 6:
                # udp6:[::1]:161
                listen = 'udp6:' + '[' + addr + ']' + ':' + port
            else:
                raise ConfigError('Invalid IP address version')

            snmp['listen_on'].append(listen)

    if conf.exists('location'):
        snmp['location'] = conf.return_value('location')

    if conf.exists('smux-peer'):
        snmp['smux_peers'] = conf.return_values('smux-peer')

    if conf.exists('trap-source'):
        snmp['trap_source'] = conf.return_value('trap-source')

    return snmp

def verify(snmp):
    return None

def generate(snmp):
    if snmp is None:
        return None

    tmpl = jinja2.Template(client_config_tmpl, trim_blocks=True)
    config_text = tmpl.render(snmp)
    with open(config_file_client, 'w') as f:
        f.write(config_text)

    tmpl = jinja2.Template(daemon_config_tmpl, trim_blocks=True)
    config_text = tmpl.render(snmp)
    with open(config_file_daemon, 'w') as f:
        f.write(config_text)

    return None

def apply(snmp):
    if snmp is not None:
        os.system("sudo systemctl restart snmpd.service")
    else:
        # SNMP is removed in the commit
        os.system("sudo systemctl stop snmpd.service")
        os.unlink(config_file_client)
        os.unlink(config_file_daemon)

    return None

if __name__ == '__main__':
    try:
        c = get_config()
        verify(c)
        generate(c)
        apply(c)
    except ConfigError as e:
        print(e)
        sys.exit(1)
