3 """ Representation for connd state as returned by tell-connd --status
5 Christian Herdtweck, Intra2net, January 2015
10 # 16/01/15 Christian Herdtweck: started creation
13 from re import match as regexp
17 default_tell_connd_binary = '/usr/intranator/bin/tell-connd'
20 ONLINE_STATE_ALWAYS_ONLINE = 'always online'
21 ONLINE_STATE_ALWAYS_OFFLINE = 'always offline'
22 ONLINE_STATE_DIAL_ON_COMMAND = 'dial on command'
23 ONLINE_STATE_DIAL_ON_DEMAND = 'dial on demand'
26 SUBSYS_DYNDNS = 'dyndns'
29 SUBSYS_SOCKS = 'socks'
31 SUBSYS_WEBPROXY = 'webproxy'
32 SUBSYS_PINGCHECK = 'pingcheck'
33 SUBSYS_IPONLINE = 'iponline'
34 ALL_SUBSYS = (SUBSYS_DNS, SUBSYS_DYNDNS, SUBSYS_MAIL, SUBSYS_NTP, \
35 SUBSYS_SOCKS, SUBSYS_VPN, SUBSYS_WEBPROXY, SUBSYS_PINGCHECK, \
39 """ representation of connd's status as returned by tell-connd --status """
42 default_provider = None
45 subsys_disabled = None
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))
56 def complete_str(self):
58 parts = ['ConndState: online mode = "{0}" (default provider: {1})\n'.format(\
59 self.online_mode, self.default_provider), ]
63 parts.append(' subsys: online: ')
64 if self.subsys_online:
65 for subsys in self.subsys_online:
66 parts.append(subsys + ' ')
69 parts.append('; offline: ')
70 if self.subsys_offline:
71 for subsys in self.subsys_offline:
72 parts.append(subsys + ' ')
75 parts.append('; disabled: ')
76 if self.subsys_disabled:
77 for subsys in self.subsys_disabled:
78 parts.append(subsys + ' ')
84 parts.append(' conns: ')
86 name, info, actions = self.connections[0]
87 parts.append('{0}: {1}, {2}\n'.format(name, info, actions))
89 parts.append('None\n')
90 for name, info, actions in self.connections[1:]:
92 parts.append(' {0}: {1}, {2}\n'.format(name, info, actions))
96 parts.append(' actions: ')
98 parts.append(self.actions[0] + '\n')
100 parts.append('None\n')
101 for action in self.actions[1:]:
103 parts.append(' {0}\n'.format(action))
107 parts.append(' IPs: ')
109 parts.append(self.online_ips[0])
110 for ip in self.online_ips[1:]:
111 parts.append(', {0}'.format(ip))
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))
128 return ''.join(parts)
129 #end: ConndState.complete_str
133 def run_tell_connd(tell_connd_binary=default_tell_connd_binary):
134 """ run tell-connd --status, return output iterator and return code
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
141 output = subprocess.check_output([tell_connd_binary, '--status'], \
142 stderr=subprocess.STDOUT, universal_newlines=True, shell=False, timeout=timeout)
143 return EX_OK, output.splitlines()
144 except subprocess.CalledProcessError as cpe: # non-zero return status
145 output = ['tell-connd exited with status {0}'.format(cpe.returncode), ]
146 output.extend( cpe.output.splitlines() )
147 return cpe.returncode, output
148 except subprocess.TimeoutExpired as te:
149 output = ['tell-connd timed out after {0}s. Returning -1'.format(te.timeout), ]
150 output.extend( te.output.splitlines() )
152 except Exception as e:
155 #end: ConndState.run_tell_connd
159 def get_state(tell_connd_binary=default_tell_connd_binary):
160 """ get actual state from tell-connd --status
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
168 err_code, all_lines = ConndState.run_tell_connd(tell_connd_binary)
169 if err_code != EX_OK:
170 return err_code, all_lines
172 output = iter(all_lines)
175 line = next(output).strip()
176 state.online_mode = regexp('online mode\s*:\s*(.+)$', line).groups()[0]
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) )
179 line = next(output).strip()
180 state.default_provider = regexp('default provider\s*:\s*(.*)$', \
182 if len(state.default_provider) == 0:
183 state.default_provider = None
184 line = next(output).strip()
185 assert( len(line) == 0 )
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()
192 for subsys in state.subsys_online:
193 assert(subsys in ALL_SUBSYS)
194 line = next(output).strip()
195 state.subsys_offline = regexp( 'offline\s*:\s*(.*)$', line).groups()[0].split()
196 for subsys in state.subsys_offline:
197 assert(subsys in ALL_SUBSYS)
198 line = next(output).strip()
199 state.subsys_disabled = regexp('disabled\s*:\s*(.*)$', line).groups()[0].split()
200 for subsys in state.subsys_disabled:
201 assert(subsys in ALL_SUBSYS)
202 line = next(output).strip()
203 assert( len(line) == 0 )
206 state.connections = []
207 line = next(output).strip()
208 assert( line == 'connection map:' )
215 if line == 'end of connection map':
217 conn_name, conn_info = regexp('\[\s*(.+)\s*\]\s*:\s*\(\s*(.*)\s*\)', line).groups()
220 conn_actions = regexp('actions\s*:\s*\[\s*(.+)\s*\]', line).groups()
221 state.connections.append( (conn_name, conn_info, conn_actions) )
225 line = next(output).strip()
226 assert( len(line) == 0 )
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() == '-':
233 line = next(output).strip()
234 assert( len(line) == 0 )
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 )
246 state.connected_vpns = []
247 line = next(output).strip()
248 assert( line == 'vpns connected:' )
253 elif line == 'end of list of connected vpns':
256 state.connected_vpns.append(line)
260 line = next(output).strip()
261 assert( len(line) == 0 )
262 line = next(output).strip()
263 assert( line == 'Done.' )
266 #end: ConndState.get_state
268 #end: class ConndState
272 state = ConndState.get_state()
274 print(state.complete_str())
277 """ Main function, called when running file as script; runs test()
283 if __name__ == '__main__':