summaryrefslogtreecommitdiffstats
path: root/retiolum/scripts/adv_graphgen/tinc_stats/Log2JSON.py
blob: 644cbc636c8746b4a0a37e09d9e16e341e14af96 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#!/usr/bin/python
import subprocess
import os
import re
import sys
import json


TINC_NETWORK =os.environ.get("TINC_NETWORK","retiolum")

# is_legacy is the parameter which defines if the tinc config files are handled old fashioned (parse from syslog), 
# or if the new and hip tincctl should be used


# Tags and Delimiters
TINC_TAG="tinc.%s" % TINC_NETWORK
BEGIN_NODES = "Nodes:"
END_NODES = "End of nodes."
BEGIN_SUBNET = "Subnet list:"
END_SUBNET = "End of subnet list"
BEGIN_EDGES = "Edges:"
END_EDGES = "End of edges."
def usage():
  from sys import argv,exit
  print("""usage: %s
This tool dumps all tinc node informations as json

ENVIRONMENT VARIABLES:
  TINC_NETWORK   The tinc network to dump 
                      (default: retiolum)
  LOG_FILE       If legacy tinc is used, defines the log file where tinc stats are dumped in
                      (default: /var/log/everything.log)
""" % argv[0])
  exit(1)
def debug(func):
  from functools import wraps
  @wraps(func)
  def with_debug(*args,**kwargs):
    print( func.__name__ + " (args: %s | kwargs %s)"% (args,kwargs))
    return func(*args,**kwargs)
  return with_debug


def get_tinc_log_file():
  # TODO parse logfile from somewhere
  return os.environ.get("LOG_FILE","/var/log/everything.log")


def parse_tinc_stats():
  import subprocess
  from time import sleep
  from distutils.spawn import find_executable as which
  #newest tinc
  if which("tinc"):
    return parse_new_input("tinc")
  #new tinc
  elif which("tincctl"):
    return parse_new_input("tincctl")
  #old tinc
  elif which("tincd"):
    # TODO refactor me
    subprocess.call(["pkill","-SIGUSR2", "tincd"])
    sleep(1)
    return parse_input(get_tinc_block(get_tinc_log_file()))
  #no tinc
  else:
    raise Exception("no tinc executable found!")
  
#@debug
def get_tinc_block(log_file):
  """ returns an iterateable block from the given log file (syslog) 
      This function became obsolete with the introduction of tincctl
  """
  from BackwardsReader import BackwardsReader
  tinc_block = []
  in_block = False
  bf = BackwardsReader(log_file)
  BOL = re.compile(".*tinc.%s\[[0-9]+\]: " % TINC_NETWORK)
  while True:
    line = bf.readline()
    if not line:
      raise Exception("end of file at log file? This should not happen!")
    line = BOL.sub('',line).strip()

    if END_SUBNET in line:
      in_block = True

    if not in_block:
      continue
    tinc_block.append(line)

    if BEGIN_NODES in line:
      break
  return reversed(tinc_block)

def parse_new_input(tinc_bin):
  nodes = {} 
  pnodes = subprocess.Popen([tinc_bin,"-n",TINC_NETWORK,"dump","reachable","nodes"], stdout=subprocess.PIPE).communicate()[0]
  #pnodes = subprocess.check_output(["tincctl","-n",TINC_NETWORK,"dump","reachable","nodes"])
  for line in pnodes.split('\n'):
    if not line: continue
    l = line.split()
    nodes[l[0]]= { 'external-ip': l[2], 'external-port' : l[4] }
  psubnets = subprocess.check_output([tinc_bin,"-n",TINC_NETWORK,"dump","subnets"])
  for line in psubnets.split('\n'):
    if not line: continue
    l = line.split()
    try:
      if not nodes[l[2]].get('internal-ip',False):
         nodes[l[2]]['internal-ip'] = []
      nodes[l[2]]['internal-ip'].append(l[0].split('#')[0])
    except KeyError:
      pass # node does not exist (presumably)
  pedges = subprocess.check_output([tinc_bin,"-n",TINC_NETWORK,"dump","edges"])
  for line in pedges.split('\n'):
    if not line: continue
    l = line.split()
    try:
      if not nodes[l[0]].has_key('to') :
        nodes[l[0]]['to'] = []
      nodes[l[0]]['to'].append(
          {'name':l[2],'addr':l[4],'port':l[6],'weight' : l[10] })
    except KeyError:
      pass #node does not exist
  return nodes

#@debug
def parse_input(log_data):
  nodes={}
  for line in log_data:
    if BEGIN_NODES in line :
      nodes={}
      for line in log_data:
        if END_NODES in line :
          break
        l = line.replace('\n','').split() #TODO unhack me
        nodes[l[0]]= { 'external-ip': l[2], 'external-port' : l[4] }
    if BEGIN_SUBNET in line :
      for line in log_data:
        if END_SUBNET in line :
          break
        l = line.replace('\n','').split() 
        if not nodes[l[2]].get('internal-ip',False):
           nodes[l[2]]['internal-ip'] = []
        nodes[l[2]]['internal-ip'].append(l[0].split('#')[0])
    if BEGIN_EDGES in line :
      edges = {}
      for line in log_data:
        if END_EDGES in line :
          break
        l = line.replace('\n','').split() 
        if not nodes[l[0]].has_key('to') :
          nodes[l[0]]['to'] = []
        nodes[l[0]]['to'].append(
            {'name':l[2],'addr':l[4],'port':l[6],'weight' : l[10] })
  return nodes


if __name__ == '__main__':
  # TODO refactor me
  from sys import argv
  if len(argv) > 1:
    usage()
  else:
    print json.dumps(parse_tinc_stats())