added log output target UNDEFINED; return if no hosts defined
[pingcheck] / test / connd_state.py
CommitLineData
3daedf04
CH
1#!/usr/bin/env python3
2
3""" Representation for connd state as returned by tell-connd --status
4
5Christian Herdtweck, Intra2net, January 2015
6(c) Intra2net AG 2015
7"""
8
9# Version History
10# 16/01/15 Christian Herdtweck: started creation
11
12import subprocess
13from re import match as regexp
14from os import EX_OK
15
f5c3fe44
CH
16# constants
17default_tell_connd_binary = '/usr/intranator/bin/tell-connd'
3daedf04
CH
18timeout = 1
19
f5c3fe44
CH
20ONLINE_STATE_ALWAYS_ONLINE = 'always online'
21ONLINE_STATE_ALWAYS_OFFLINE = 'always offline'
22ONLINE_STATE_DIAL_ON_COMMAND = 'dial on command'
23ONLINE_STATE_DIAL_ON_DEMAND = 'dial on demand'
24
25SUBSYS_DNS = 'dns'
26SUBSYS_DYNDNS = 'dyndns'
27SUBSYS_MAIL = 'mail'
28SUBSYS_NTP = 'ntp'
29SUBSYS_SOCKS = 'socks'
30SUBSYS_VPN = 'vpn'
31SUBSYS_WEBPROXY = 'webproxy'
32SUBSYS_PINGCHECK = 'pingcheck'
a85f210b 33SUBSYS_IPONLINE = 'iponline'
f5c3fe44 34ALL_SUBSYS = (SUBSYS_DNS, SUBSYS_DYNDNS, SUBSYS_MAIL, SUBSYS_NTP, \
a85f210b
CH
35 SUBSYS_SOCKS, SUBSYS_VPN, SUBSYS_WEBPROXY, SUBSYS_PINGCHECK, \
36 SUBSYS_IPONLINE)
f5c3fe44 37
3daedf04
CH
38class ConndState:
39 """ representation of connd's status as returned by tell-connd --status """
40
41 online_mode = None
42 default_provider = None
43 subsys_online = None
44 subsys_offline = None
45 subsys_disabled = None
46 connections = None
47 actions = None
48 online_ips = None
49 connected_vpns = None
50
51 def __str__(self):
52 return '[ConndState: {0} (default {1}), {2} conn\'s, {3} ips, {4} vpns ]'.format(\
53 self.online_mode, self.default_provider, len(self.connections), \
54 len(self.online_ips), len(self.connected_vpns))
55
f5c3fe44
CH
56 def complete_str(self):
57 # general
58 parts = ['ConndState: online mode = "{0}" (default provider: {1})\n'.format(\
59 self.online_mode, self.default_provider), ]
60
61 # subsys
62 # ' connctns:
63 parts.append(' subsys: online: ')
64 if self.subsys_online:
65 for subsys in self.subsys_online:
66 parts.append(subsys + ' ')
67 else:
68 parts.append('None ')
69 parts.append('; offline: ')
70 if self.subsys_offline:
71 for subsys in self.subsys_offline:
72 parts.append(subsys + ' ')
73 else:
74 parts.append('None ')
75 parts.append('; disabled: ')
76 if self.subsys_disabled:
77 for subsys in self.subsys_disabled:
78 parts.append(subsys + ' ')
79 else:
80 parts.append('None')
81 parts.append('\n')
82
83 # connections
84 parts.append(' conns: ')
85 if self.connections:
86 name, info, actions = self.connections[0]
87 parts.append('{0}: {1}, {2}\n'.format(name, info, actions))
88 else:
89 parts.append('None\n')
90 for name, info, actions in self.connections[1:]:
91 # ' connctns:
92 parts.append(' {0}: {1}, {2}\n'.format(name, info, actions))
93
94 # actions
95 # ' connctns:
96 parts.append(' actions: ')
97 if self.actions:
98 parts.append(self.actions[0] + '\n')
99 else:
100 parts.append('None\n')
101 for action in self.actions[1:]:
102 # ' connctns:
103 parts.append(' {0}\n'.format(action))
104
105 # online IPs
106 # ' connctns:
107 parts.append(' IPs: ')
108 if self.online_ips:
109 parts.append(self.online_ips[0])
110 for ip in self.online_ips[1:]:
111 parts.append(', {0}'.format(ip))
112 else:
113 parts.append('None')
114 parts.append('\n')
115
116 # VPNs
117 # ' connctns:
118 parts.append(' VPNs: ')
119 if self.connected_vpns:
120 parts.append(self.connected_vpns[0])
121 for vpn in self.connected_vpns[1:]:
122 parts.append(', {0}'.format(vpn))
123 else:
124 parts.append('None')
125 parts.append('\n')
126
127
128 return ''.join(parts)
129 #end: ConndState.complete_str
130
3daedf04
CH
131
132 @staticmethod
f5c3fe44
CH
133 def run_tell_connd(tell_connd_binary=default_tell_connd_binary):
134 """ run tell-connd --status, return output iterator and return code
135
136 catches all it can, so should usually return (output, return_code)
137 where output = [line1, line2, ...]
138 if return_code != 0, output's first line(s) is error message
139 """
3daedf04
CH
140 try:
141 output = subprocess.check_output([tell_connd_binary, '--status'], \
142 stderr=subprocess.STDOUT, universal_newlines=True, shell=False, timeout=timeout)
f5c3fe44 143 return EX_OK, output.splitlines()
3daedf04 144 except subprocess.CalledProcessError as cpe: # non-zero return status
f5c3fe44
CH
145 output = ['tell-connd exited with status {0}'.format(cpe.returncode), ]
146 output.extend( cpe.output.splitlines() )
147 return cpe.returncode, output
3daedf04 148 except subprocess.TimeoutExpired as te:
f5c3fe44
CH
149 output = ['tell-connd timed out after {0}s. Returning -1'.format(te.timeout), ]
150 output.extend( te.output.splitlines() )
151 return -1, output
152 except Exception as e:
153 output = [str(e),]
154 return -1, output
3daedf04
CH
155 #end: ConndState.run_tell_connd
156
157
158 @staticmethod
f5c3fe44
CH
159 def get_state(tell_connd_binary=default_tell_connd_binary):
160 """ get actual state from tell-connd --status
161
162 returns (err_code, output_lines) if something goes wrong running binary;
163 raises assertion if output from tell-connd does not match expected format
164 """
3daedf04
CH
165
166 state = ConndState()
167
f5c3fe44
CH
168 err_code, all_lines = ConndState.run_tell_connd(tell_connd_binary)
169 if err_code != EX_OK:
170 return err_code, all_lines
171
3daedf04
CH
172 output = iter(all_lines)
173
174 # first section
175 line = next(output).strip()
176 state.online_mode = regexp('online mode\s*:\s*(.+)$', line).groups()[0]
f5c3fe44
CH
177 assert( state.online_mode in (ONLINE_STATE_DIAL_ON_DEMAND, ONLINE_STATE_DIAL_ON_COMMAND, \
178 ONLINE_STATE_ALWAYS_OFFLINE, ONLINE_STATE_ALWAYS_ONLINE) )
3daedf04
CH
179 line = next(output).strip()
180 state.default_provider = regexp('default provider\s*:\s*(.*)$', \
181 line).groups()[0]
182 if len(state.default_provider) == 0:
183 state.default_provider = None
184 line = next(output).strip()
185 assert( len(line) == 0 )
186
187 # subsys
188 line = next(output).strip()
189 assert( line == 'subsys' )
190 line = next(output).strip()
191 state.subsys_online = regexp( 'online\s*:\s*(.*)$', line).groups()[0].split()
f5c3fe44
CH
192 for subsys in state.subsys_online:
193 assert(subsys in ALL_SUBSYS)
3daedf04
CH
194 line = next(output).strip()
195 state.subsys_offline = regexp( 'offline\s*:\s*(.*)$', line).groups()[0].split()
f5c3fe44
CH
196 for subsys in state.subsys_offline:
197 assert(subsys in ALL_SUBSYS)
3daedf04
CH
198 line = next(output).strip()
199 state.subsys_disabled = regexp('disabled\s*:\s*(.*)$', line).groups()[0].split()
f5c3fe44
CH
200 for subsys in state.subsys_disabled:
201 assert(subsys in ALL_SUBSYS)
3daedf04
CH
202 line = next(output).strip()
203 assert( len(line) == 0 )
204
205 # connection map
206 state.connections = []
207 line = next(output).strip()
208 assert( line == 'connection map:' )
209 expect_new = True
210 for line in output:
211 line = line.strip()
212 if len(line) == 0:
213 continue
214 if expect_new:
215 if line == 'end of connection map':
216 break
217 conn_name, conn_info = regexp('\[\s*(.+)\s*\]\s*:\s*\(\s*(.*)\s*\)', line).groups()
218 expect_new = False
219 else:
f5c3fe44 220 conn_actions = regexp('actions\s*:\s*\[\s*(.+)\s*\]', line).groups()
3daedf04
CH
221 state.connections.append( (conn_name, conn_info, conn_actions) )
222 expect_new = True
223 #end: for lines
224 assert( expect_new )
225 line = next(output).strip()
226 assert( len(line) == 0 )
227
228 # actions
229 line = next(output).strip()
230 state.actions = regexp('actions\s*:\s*(.*)', line).groups()[0].split()
231 if len(state.actions) == 1 and state.actions[0].strip() == '-':
232 state.actions = []
233 line = next(output).strip()
234 assert( len(line) == 0 )
235
236 # online IPs
237 line = next(output).strip()
238 state.online_ips = regexp('list of online ips\s*:\s*(.*)', \
239 line).groups()[0].split()
240 if len(state.online_ips) == 1 and state.online_ips[0].strip() == 'NONE':
241 state.online_ips = []
242 line = next(output).strip()
243 assert( len(line) == 0 )
244
245 # VPNs
246 state.connected_vpns = []
247 line = next(output).strip()
248 assert( line == 'vpns connected:' )
249 for line in output:
250 line = line.strip()
251 if len(line) == 0:
252 continue
253 elif line == 'end of list of connected vpns':
254 break
255 else:
256 state.connected_vpns.append(line)
257 #end: for lines
258
259 # done
260 line = next(output).strip()
261 assert( len(line) == 0 )
262 line = next(output).strip()
263 assert( line == 'Done.' )
264
265 return state
266 #end: ConndState.get_state
267
268#end: class ConndState
269
270
271def test():
272 state = ConndState.get_state()
273 print(state)
f5c3fe44 274 print(state.complete_str())
3daedf04
CH
275
276def main():
277 """ Main function, called when running file as script; runs test()
278 """
279 test()
280#end: function main
281
282
283if __name__ == '__main__':
284 main()
285