#!/usr/bin/python

"""Munin plugin to monitor libreswan ipsec servers
Copyright 2017, Kim B. Heino, b@bbbs.net, Foobar Oy
Copyright 2017, Paul Wouters <paul@nohats.ca>
License GPLv2+

This plugin requires Munin config /etc/munin/plugin-conf.d/libreswan:

[libreswan]
user root


#%# capabilities=autoconf
#%# family=auto
"""

from __future__ import print_function, unicode_literals
import subprocess
import sys
from collections import defaultdict, OrderedDict

def ddict ():
    return defaultdict(ddict)

def ddict2dict(d):
    for k, v in d.items():
        if isinstance(v, dict):
            d[k] = ddict2dict(v)
    return dict(d)

def get_stats():
	"""Get statistics"""
	# Get status output
	try:
		pipe = subprocess.Popen(
			['/usr/sbin/ipsec', 'whack', '--globalstatus'],
			stdout=subprocess.PIPE,
			stderr=subprocess.STDOUT)
		output = pipe.communicate()[0]
	except OSError:
		return {}

	vpn = ddict()

	for line in output.splitlines():
		if '=' not in line:
			continue

	for line in output.splitlines():

		prefix, val = line.split("=")
		prefixes = prefix.split(".")
		length = len(prefixes)
		if length == 3:
			vpn[prefixes[0]][prefixes[1]][prefixes[2]] = int(val)
		elif length == 4:
			vpn[prefixes[0]][prefixes[1]][prefixes[2]][prefixes[3]] = int(val)
		elif length == 5:
			vpn[prefixes[0]][prefixes[1]][prefixes[2]][prefixes[3]][prefixes[4]] = int(val)
		elif length == 6:
			vpn[prefixes[0]][prefixes[1]][prefixes[2]][prefixes[3]][prefixes[4]][prefixes[5]] = int(val)
		else:
			print("# ignored:{}".format(line))
			
	myvpn = ddict2dict(vpn)
	#print(myvpn)
	return myvpn

def print_option(values, config):

	print("multigraph vpn_ipsec_types")
	if config:
		print("graph_title IPsec SA Types")
		print("graph_vlabel total")
		print("graph_category vpn")
		print("graph_args --base 1000 --lower-limit 0")

	for entry in values["total"]["ipsec"]["type"].keys():
		if config:
			print("{}.label {}".format(entry, entry))
			print("{}.type DERIVE".format(entry))
			print("{}.min 0".format(entry))
		else:
			print("{}.value {}".format(entry, values["total"]["ipsec"]["type"][entry]))

	print("multigraph vpn_ipsec_encr")
	if config:
		print("graph_title IPsec SA ENCR")
		print("graph_vlabel total")
		print("graph_category vpn")
		print("graph_args --base 1000 --lower-limit 0")

	for entry in values["total"]["ipsec"]["encr"].keys():
		if config:
			print("{}.label {}".format(entry, entry))
			print("{}.type DERIVE".format(entry))
			print("{}.min 0".format(entry))
		else:
			print("{}.value {}".format(entry, values["total"]["ipsec"]["encr"][entry]))

	print("multigraph vpn_ipsec_integ")
	if config:
		print("graph_title IPsec SA INTEG")
		print("graph_vlabel total")
		print("graph_category vpn")
		print("graph_args --base 1000 --lower-limit 0")

	for entry in values["total"]["ipsec"]["integ"].keys():
		if config:
			print("{}.label {}".format(entry, entry))
			print("{}.type DERIVE".format(entry))
			print("{}.min 0".format(entry))
		else:
			print("{}.value {}".format(entry, values["total"]["ipsec"]["integ"][entry]))

	print("multigraph vpn_current")
	if config:
		print("graph_title Current States")
		print("graph_vlabel current")
		print("graph_category vpn")
		print("graph_args --base 1000 --lower-limit 0")

	for entry in values["current"]["states"].keys():
		if entry == "enumerate":
			continue
		if entry == "iketype":
			continue

		if config:
			print("{}.label {}".format(entry, entry))
			print("{}.type GAUGE".format(entry))
			print("{}.min 0".format(entry))
		else:
			print("{}.value {}".format(entry, values["current"]["states"][entry]))

	print("multigraph vpn_iketype")
	if config:
		print("graph_title Current IKE types")
		print("graph_vlabel iketypes")
		print("graph_category vpn")
		print("graph_args --base 1000 --lower-limit 0")

	for entry in values["current"]["states"]["iketype"].keys():

		if config:
			print("{}.label {}".format(entry, entry))
			print("{}.type GAUGE".format(entry))
			print("{}.min 0".format(entry))
		else:
			print("{}.value {}".format(entry, values["current"]["states"]["iketype"][entry]))

	print("multigraph vpn_state_kind")
	if config:
		print("graph_title Current pluto states")
		print("graph_vlabel pluto_states")
		print("graph_category vpn")
		print("graph_args --base 1000 --lower-limit 0")

	for entry in values["current"]["states"]["enumerate"].keys():

		if config:
			print("{}.label {}".format(entry, entry))
			print("{}.type GAUGE".format(entry))
			print("{}.min 0".format(entry))
		else:
			print("{}.value {}".format(entry, values["current"]["states"]["enumerate"][entry]))

	print("multigraph vpn_state_transition_func")
	if config:
		print("graph_title Pluto STFs")
		print("graph_vlabel total")
		print("graph_category vpn")
		print("graph_args --base 1000 --lower-limit 0")

	for entry in values["total"]["pluto"]["stf"].keys():
		if config:
			print("{}.label {}".format(entry, entry))
			print("{}.type DERIVE".format(entry))
			print("{}.min 0".format(entry))
		else:
			print("{}.value {}".format(entry, values["total"]["pluto"]["stf"][entry]))

	print("multigraph vpn_traffic_ipsec")
	if config:
		print("graph_title Total IPsec Traffic")
		print("graph_category vpn")
		print("graph_order down up")
		print("graph_args --base 1000")
		print("graph_vlabel bytes in (-) / out (+) per ${graph_period}")
		print("down.label received")
		print("down.type DERIVE")
		print("down.graph no")
		print("down.cdef down,8,*")
		print("down.min 0")
		print("up.label bps")
		print("up.type DERIVE")
		print("up.negative down")
		print("up.cdef up,8,*")
		print("up.min 0")

	for entry in values["total"]["ipsec"]["traffic"].keys():

		if entry == "in":
			orig = "down"
		else:
			orig = "up"
		if config:
			print("{}.label {}".format(orig, entry))
			print("{}.type DERIVE".format(orig))
			print("{}.min 0".format(orig))
		else:
			print("{}.value {}".format(orig, values["total"]["ipsec"]["traffic"][entry]))

	print("multigraph vpn_traffic_ike")
	if config:
		print("graph_title Total IKE Traffic")
		print("graph_category vpn")
		print("graph_order down up")
		print("graph_args --base 1000")
		print("graph_vlabel bytes in (-) / out (+) per ${graph_period}")
		print("down.label received")
		print("down.type DERIVE")
		print("down.graph no")
		print("down.cdef down,8,*")
		print("down.min 0")
		print("up.label bps")
		print("up.type DERIVE")
		print("up.negative down")
		print("up.cdef up,8,*")
		print("up.min 0")

	for entry in values["total"]["ike"]["traffic"].keys():

		if entry == "in":
			orig = "down"
		else:
			orig = "up"
		if config:
			print("{}.label {}".format(orig, entry))
			print("{}.type DERIVE".format(orig))
			print("{}.min 0".format(orig))
		else:
			print("{}.value {}".format(orig, values["total"]["ike"]["traffic"][entry]))

	print("multigraph vpn_dpd")
	if config:
		print("graph_title Total DPD Traffic")
		print("graph_vlabel dpd_traffic")
		print("graph_category vpn")
		print("graph_args --base 1000 --lower-limit 0")

	for entry in values["total"]["ike"]["dpd"].keys():

		if config:
			print("{}.label {}".format(entry, entry))
			print("{}.type DERIVE".format(entry))
			print("{}.min 0".format(entry))
		else:
			print("{}.value {}".format(entry, values["total"]["ike"]["dpd"][entry]))

	print("multigraph vpn_ike")
	if config:
		print("graph_title Total IKE Sessions")
		print("graph_vlabel ike_traffic")
		print("graph_category vpn")
		print("graph_args --base 1000 --lower-limit 0")

	for entry in ( "ikev2_ok", "ikev2_fail", "ikev1_ok", "ikev1_fail"):

		if config:
			print("{}.label {}".format(entry, entry))
			print("{}.type DERIVE".format(entry))
			print("{}.min 0".format(entry))
		else:
			if "2" in entry:
				k3 = "ikev2"
			else:
				k3 = "ikev1"
			if "fail" in entry:
				k4 = "failed"
			else:
				k4 = "established"
			print("{}.value {}".format(entry, values["total"]["ike"][k3][k4]))

	print("multigraph vpn_ikev1_sent_notifies")
	if config:
		print("graph_title IKEv1 sent NOTIFIES")
		print("graph_vlabel total")
		print("graph_category vpn")
		print("graph_args --base 1000 --lower-limit 0")

	for entry in values["total"]["ikev1"]["sent"]["notifies"]["error"].keys():
		if config:
			print("{}.label {}".format(entry, entry))
			print("{}.type DERIVE".format(entry))
			print("{}.min 0".format(entry))
		else:
			print("{}.value {}".format(entry, values["total"]["ikev1"]["sent"]["notifies"]["error"][entry]))
	
	print("multigraph vpn_ikev2_sent_notifies")
	if config:
		print("graph_title IKEv2 sent NOTIFIES")
		print("graph_vlabel total")
		print("graph_category vpn")
		print("graph_args --base 1000 --lower-limit 0")

	for entry in values["total"]["ikev2"]["sent"]["notifies"]["error"].keys():
		if config:
			print("{}.label {}".format(entry, entry))
			print("{}.type DERIVE".format(entry))
			print("{}.min 0".format(entry))
		else:
			print("{}.value {}".format(entry, values["total"]["ikev2"]["sent"]["notifies"]["error"][entry]))

	print("multigraph vpn_ikev1_recv_notifies")
	if config:
		print("graph_title IKEv1 recv NOTIFIES")
		print("graph_vlabel total")
		print("graph_category vpn")
		print("graph_args --base 1000 --lower-limit 0")

	for entry in values["total"]["ikev1"]["recv"]["notifies"]["error"].keys():
		if config:
			print("{}.label {}".format(entry, entry))
			print("{}.type DERIVE".format(entry))
			print("{}.min 0".format(entry))
		else:
			print("{}.value {}".format(entry, values["total"]["ikev1"]["recv"]["notifies"]["error"][entry]))
	
	print("multigraph vpn_ikev2_recv_notifies")
	if config:
		print("graph_title IKEv2 recv NOTIFIES")
		print("graph_vlabel total")
		print("graph_category vpn")
		print("graph_args --base 1000 --lower-limit 0")

	for entry in values["total"]["ikev2"]["recv"]["notifies"]["error"].keys():
		if config:
			print("{}.label {}".format(entry, entry))
			print("{}.type DERIVE".format(entry))
			print("{}.min 0".format(entry))
		else:
			print("{}.value {}".format(entry, values["total"]["ikev2"]["recv"]["notifies"]["error"][entry]))


	# down from here it is all crypto params

	print("multigraph vpn_ikev1_encr")
	if config:
		print("graph_title IKEv1 ENCR")
		print("graph_vlabel total")
		print("graph_category vpn")
		print("graph_args --base 1000 --lower-limit 0")

	for entry in values["total"]["ikev1"]["encr"].keys():
		if config:
			print("{}.label {}".format(entry, entry))
			print("{}.type DERIVE".format(entry))
			print("{}.min 0".format(entry))
		else:
			print("{}.value {}".format(entry, values["total"]["ikev1"]["encr"][entry]))

	print("multigraph vpn_ikev2_encr")
	if config:
		print("graph_title IKEv2 ENCR")
		print("graph_vlabel total")
		print("graph_category vpn")
		print("graph_args --base 1000 --lower-limit 0")

	for entry in values["total"]["ikev2"]["encr"].keys():
		if config:
			print("{}.label {}".format(entry, entry))
			print("{}.type DERIVE".format(entry))
			print("{}.min 0".format(entry))
		else:
			print("{}.value {}".format(entry, values["total"]["ikev2"]["encr"][entry]))

	print("multigraph vpn_ikev1_integ")
	if config:
		print("graph_title IKEv1 INTEG")
		print("graph_vlabel total")
		print("graph_category vpn")
		print("graph_args --base 1000 --lower-limit 0")

	for entry in values["total"]["ikev1"]["integ"].keys():
		if config:
			print("{}.label {}".format(entry, entry))
			print("{}.type DERIVE".format(entry))
			print("{}.min 0".format(entry))
		else:
			print("{}.value {}".format(entry, values["total"]["ikev1"]["integ"][entry]))

	print("multigraph vpn_ikev2_integ")
	if config:
		print("graph_title IKEv2 INTEG")
		print("graph_vlabel total")
		print("graph_category vpn")
		print("graph_args --base 1000 --lower-limit 0")

	for entry in values["total"]["ikev2"]["integ"].keys():
		if config:
			print("{}.label {}".format(entry, entry))
			print("{}.type DERIVE".format(entry))
			print("{}.min 0".format(entry))
		else:
			print("{}.value {}".format(entry, values["total"]["ikev2"]["integ"][entry]))

	print("multigraph vpn_ikev1_group")
	if config:
		print("graph_title IKEv1 GROUP")
		print("graph_vlabel total")
		print("graph_category vpn")
		print("graph_args --base 1000 --lower-limit 0")

	for entry in values["total"]["ikev1"]["group"].keys():
		if config:
			print("{}.label {}".format(entry, entry))
			print("{}.type DERIVE".format(entry))
			print("{}.min 0".format(entry))
		else:
			print("{}.value {}".format(entry, values["total"]["ikev1"]["group"][entry]))

	print("multigraph vpn_ikev2_group")
	if config:
		print("graph_title IKEv2 GROUP")
		print("graph_vlabel total")
		print("graph_category vpn")
		print("graph_args --base 1000 --lower-limit 0")

	for entry in values["total"]["ikev2"]["group"].keys():
		if config:
			print("{}.label {}".format(entry, entry))
			print("{}.type DERIVE".format(entry))
			print("{}.min 0".format(entry))
		else:
			print("{}.value {}".format(entry, values["total"]["ikev2"]["group"][entry]))

	print("multigraph vpn_ikev2_recv_badgroup_in")
	if config:
		print("graph_title IKEv2 recv INVALID GROUP")
		print("graph_vlabel total")
		print("graph_category vpn")
		print("graph_args --base 1000 --lower-limit 0")

	for entry in values["total"]["ikev2"]["recv"]["invalidke"]["using"].keys():
		if config:
			print("{}.label {}".format(entry, entry))
			print("{}.type DERIVE".format(entry))
			print("{}.min 0".format(entry))
		else:
			print("{}.value {}".format(entry, values["total"]["ikev2"]["recv"]["invalidke"]["using"][entry]))

	print("multigraph vpn_ikev2_recv_badgroup_out")
	if config:
		print("graph_title IKEv2 recv-sent INVALID GROUP")
		print("graph_vlabel total")
		print("graph_category vpn")
		print("graph_args --base 1000 --lower-limit 0")

	for entry in values["total"]["ikev2"]["recv"]["invalidke"]["suggesting"].keys():
		if config:
			print("{}.label {}".format(entry, entry))
			print("{}.type DERIVE".format(entry))
			print("{}.min 0".format(entry))
		else:
			print("{}.value {}".format(entry, values["total"]["ikev2"]["recv"]["invalidke"]["suggesting"][entry]))

	print("multigraph vpn_ikev2_sent_badgroup_in")
	if config:
		print("graph_title IKEv2 sent-recv INVALID GROUP")
		print("graph_vlabel total")
		print("graph_category vpn")
		print("graph_args --base 1000 --lower-limit 0")

	for entry in values["total"]["ikev2"]["sent"]["invalidke"]["using"].keys():
		if config:
			print("{}.label {}".format(entry, entry))
			print("{}.type DERIVE".format(entry))
			print("{}.min 0".format(entry))
		else:
			print("{}.value {}".format(entry, values["total"]["ikev2"]["sent"]["invalidke"]["using"][entry]))

	print("multigraph vpn_ikev2_sent_badgroup_out")
	if config:
		print("graph_title IKEv2 sent-sent INVALID GROUP")
		print("graph_vlabel total")
		print("graph_category vpn")
		print("graph_args --base 1000 --lower-limit 0")

	for entry in values["total"]["ikev2"]["sent"]["invalidke"]["suggesting"].keys():
		if config:
			print("{}.label {}".format(entry, entry))
			print("{}.type DERIVE".format(entry))
			print("{}.min 0".format(entry))
		else:
			print("{}.value {}".format(entry, values["total"]["ikev2"]["sent"]["invalidke"]["suggesting"][entry]))

def main(args):
	"""Main program"""
	values = get_stats()
	if len(args) > 1 and args[1] == 'autoconf':
		print('yes' if values else 'no')
	elif len(args) > 1 and args[1] == 'config':
		print_option(values, True)
	else:
		print_option(values, False)

if __name__ == '__main__':
    main(sys.argv)
