Moved strmmsi_to_mmsi and mmsi_to_strmmsi from common module to ntools
[ais.git] / bin / inputs / config.py
1 # -*- coding: utf-8 -*-
2 '''
3 Peers definition
4 '''
5
6 from __future__ import division
7 import logging
8 import pprint
9 from ais.ntools import str_split_column_ipv6, strmmsi_to_mmsi
10
11 __all__ = [
12     'peers_get_config',
13     'source_get_infoin',
14     'get_hidden_mmsi',
15     ]
16
17 CONFIG_FILENAME = '/etc/ais/config'
18 HIDDENMMSI_FILENAME = '/etc/ais/hidden'
19
20 SOURCES = {}
21
22 __source_normalized__ = False
23 def peers_get_config():
24     '''
25     SOURCES normalization & checks
26     '''
27     global __source_normalized__
28     if __source_normalized__:
29         return SOURCES
30
31     config_file = file(CONFIG_FILENAME)
32     for lineno_e, line in enumerate(config_file.readlines()):
33         lineno = lineno_e + 1
34         #logging.debug('considering line #%s: %s', lineno, repr(line))
35         line = line.strip(' \r\n')
36         if len(line)==0 or line[0]=='#':
37             logging.debug('ignoring line #%s: %s', lineno, repr(line))
38             continue
39         spl = line.split(' ', 2)
40         assert len(spl) == 3, \
41             'line #%s: There should be at least 3 strings sepatated by spaces' % \
42             lineno
43         id4, io, definition = spl
44         assert len(id4)==4, 'source name (first parameter) must be 4 char long.'
45         logging.debug('%s %s %s', id4, io, definition)
46         if id4 not in SOURCES:
47             SOURCES[id4] = {}
48         if io == 'in':
49             assert len(spl)==3, \
50                 'line #%s: missing information after \'in\'' % \
51                 lineno
52             SOURCES[id4]['in'] = definition
53         elif io ==  'out':
54             assert len(spl)==3, \
55                 'line #%s: missing information after \'out\'' % \
56                 lineno
57             if not SOURCES[id4].has_key('out'):
58                 SOURCES[id4]['out'] = []
59             spl = definition.split(' ')
60             SOURCES[id4]['out'].append(spl)
61         elif io ==  'name':
62             SOURCES[id4]['name'] = definition
63         else:
64             raise AssertionError( \
65                 "line #%s: second parameter must be either 'in' or 'out'.\r%s" % \
66                 (lineno, line))
67
68     logging.debug('SOURCES \n%s', pprint.pformat(SOURCES))
69
70     logging.debug('SOURCES (raw):\n%s', pprint.pformat(SOURCES))
71     for id4, settings in SOURCES.iteritems():
72         assert len(id4) == 4, \
73             'Invalid ID %s in SOURCES. It should be 4 characters long.'
74
75         if 'name' not in settings:
76             settings['name'] =id4
77         try:
78             in_ = settings['in']
79         except KeyError:
80             continue
81     
82         assert type(in_) == str, \
83             "SOURCES['%s']['in'] should be of type str, not %s'." % \
84             (id4, type(in_))
85         in_ = settings.get('in')
86         spl = str_split_column_ipv6(in_)
87         protocol, parameters = spl[0], spl[1:]
88         assert parameters, \
89             "SOURCES['%s']['in'] should contain parameters after %s" % \
90             (id4, protocol)
91            
92         if protocol == 'udp':
93             assert len(parameters) == 4, \
94                 "SOURCES['%s']['in'] udp: protocol requires 4 parameters." % \
95                 id4
96             host_local = parameters[0]
97             if host_local == '*':
98                 host_local = ''
99             port_local = parameters[1]
100             assert port_local.isdigit(), \
101                 "SOURCES['%s']['in'] udp: illegal local port" % \
102                 id4
103             port_local = int(port_local)
104             host_remote = parameters[2]
105             if host_remote == '*':
106                 host_remote = ''
107             port_remote = parameters[3]
108             source_port_discreminate = False
109             if port_remote == '*':
110                 port_remote = ''
111             elif port_remote == '!':
112                 port_remote = ''
113                 source_port_discreminate = True
114             else:
115                 assert port_remote.isdigit(), \
116                     "SOURCES['%s']['in'] udp: illegal remote port" % \
117                     id4
118                 port_remote = int(port_remote)
119          
120             SOURCES[id4]['in'] = (protocol.title(), host_local, port_local, host_remote, port_remote, source_port_discreminate)
121
122         elif protocol == 'serial':
123             assert len(parameters) == 1, \
124                 "SOURCES['%s']['in'] serial: protocol requires 1 parameter." % \
125                 id4
126             SOURCES[id4]['in'] = tuple(['Serial'] + parameters)
127
128         elif protocol == 'tcpout':
129             assert len(parameters) == 2, \
130                 "SOURCES['%s']['in'] tcpout: protocol requires 2 parameters." % \
131                 id4
132             host = parameters[0]
133             port = int(parameters[1])
134             SOURCES[id4]['in'] = ('TcpOut', host, port)
135
136         else:
137             logging.error(
138                 "Unsupported source protocol '%s' for SOURCES['%s']: %s",
139                 protocol, id4, in_)
140     __source_normalized__ = True
141     logging.debug('SOURCES (normalised):\n%s', pprint.pformat(SOURCES))
142     return SOURCES
143
144
145
146 def source_get_infoin(*args):
147     '''
148     Gets a list of matching info in SOURCES[*]['in']:
149     example: get_source_infoin('Udp', '', 1234)
150       might return [ ['SRC1', '12.23.34.45', '', False],
151                      ['SRC2', '98.87.76.65', '', True]]
152     '''
153     result = []
154     for id4, source_info in peers_get_config().iteritems():
155         rawin = source_info.get('in', None)
156         if not rawin:
157             continue
158         if args == rawin[:len(args)]:
159             newmatch = [id4] + [rawin[i] for i in range(len(args), len(rawin))]
160             result.append(newmatch)
161     return result
162         
163 __hidden_mmsi__ = None
164 def get_hidden_mmsi():
165     global __hidden_mmsi__
166     if __hidden_mmsi__ is None:
167         __hidden_mmsi__ = []
168         try:
169             lines = file(HIDDENMMSI_FILENAME).read().split('\n')
170         except IOError, err:
171             if err.errno == 2: # No such file or directory
172                 logging.info('No hidden ship')
173                 return __hidden_mmsi__
174             else:
175                 raise
176         for line in lines:
177             line = line.strip()
178             if len(line)==0 or line[0] == '#':
179                 continue  # ignore empty lines & comments
180             mmsi = strmmsi_to_mmsi(line)
181             __hidden_mmsi__.append(mmsi)
182     return __hidden_mmsi__
183
184