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