From f5c3fe4494228a98ba79a0007409692b3c12e63c Mon Sep 17 00:00:00 2001 From: Christian Herdtweck Date: Wed, 21 Jan 2015 11:37:48 +0100 Subject: [PATCH] improved tell-connd state representation * use constants for online states and subsystems * add function to output complete state * deal with exceptions from running binary --- test/connd_state.py | 144 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 files changed, 130 insertions(+), 14 deletions(-) diff --git a/test/connd_state.py b/test/connd_state.py index 0d0b903..1f11e12 100755 --- a/test/connd_state.py +++ b/test/connd_state.py @@ -13,9 +13,26 @@ import subprocess from re import match as regexp from os import EX_OK -tell_connd_binary = '/usr/intranator/bin/tell-connd' +# constants +default_tell_connd_binary = '/usr/intranator/bin/tell-connd' timeout = 1 +ONLINE_STATE_ALWAYS_ONLINE = 'always online' +ONLINE_STATE_ALWAYS_OFFLINE = 'always offline' +ONLINE_STATE_DIAL_ON_COMMAND = 'dial on command' +ONLINE_STATE_DIAL_ON_DEMAND = 'dial on demand' + +SUBSYS_DNS = 'dns' +SUBSYS_DYNDNS = 'dyndns' +SUBSYS_MAIL = 'mail' +SUBSYS_NTP = 'ntp' +SUBSYS_SOCKS = 'socks' +SUBSYS_VPN = 'vpn' +SUBSYS_WEBPROXY = 'webproxy' +SUBSYS_PINGCHECK = 'pingcheck' +ALL_SUBSYS = (SUBSYS_DNS, SUBSYS_DYNDNS, SUBSYS_MAIL, SUBSYS_NTP, \ + SUBSYS_SOCKS, SUBSYS_VPN, SUBSYS_WEBPROXY, SUBSYS_PINGCHECK) + class ConndState: """ representation of connd's status as returned by tell-connd --status """ @@ -34,34 +51,129 @@ class ConndState: self.online_mode, self.default_provider, len(self.connections), \ len(self.online_ips), len(self.connected_vpns)) + def complete_str(self): + # general + parts = ['ConndState: online mode = "{0}" (default provider: {1})\n'.format(\ + self.online_mode, self.default_provider), ] + + # subsys + # ' connctns: + parts.append(' subsys: online: ') + if self.subsys_online: + for subsys in self.subsys_online: + parts.append(subsys + ' ') + else: + parts.append('None ') + parts.append('; offline: ') + if self.subsys_offline: + for subsys in self.subsys_offline: + parts.append(subsys + ' ') + else: + parts.append('None ') + parts.append('; disabled: ') + if self.subsys_disabled: + for subsys in self.subsys_disabled: + parts.append(subsys + ' ') + else: + parts.append('None') + parts.append('\n') + + # connections + parts.append(' conns: ') + if self.connections: + name, info, actions = self.connections[0] + parts.append('{0}: {1}, {2}\n'.format(name, info, actions)) + else: + parts.append('None\n') + for name, info, actions in self.connections[1:]: + # ' connctns: + parts.append(' {0}: {1}, {2}\n'.format(name, info, actions)) + + # actions + # ' connctns: + parts.append(' actions: ') + if self.actions: + parts.append(self.actions[0] + '\n') + else: + parts.append('None\n') + for action in self.actions[1:]: + # ' connctns: + parts.append(' {0}\n'.format(action)) + + # online IPs + # ' connctns: + parts.append(' IPs: ') + if self.online_ips: + parts.append(self.online_ips[0]) + for ip in self.online_ips[1:]: + parts.append(', {0}'.format(ip)) + else: + parts.append('None') + parts.append('\n') + + # VPNs + # ' connctns: + parts.append(' VPNs: ') + if self.connected_vpns: + parts.append(self.connected_vpns[0]) + for vpn in self.connected_vpns[1:]: + parts.append(', {0}'.format(vpn)) + else: + parts.append('None') + parts.append('\n') + + + return ''.join(parts) + #end: ConndState.complete_str + @staticmethod - def run_tell_connd(): - """ run tell-connd --status, return output iterator """ + def run_tell_connd(tell_connd_binary=default_tell_connd_binary): + """ run tell-connd --status, return output iterator and return code + + catches all it can, so should usually return (output, return_code) + where output = [line1, line2, ...] + if return_code != 0, output's first line(s) is error message + """ try: output = subprocess.check_output([tell_connd_binary, '--status'], \ stderr=subprocess.STDOUT, universal_newlines=True, shell=False, timeout=timeout) - return output.splitlines(), EX_OK + return EX_OK, output.splitlines() except subprocess.CalledProcessError as cpe: # non-zero return status - warn('tell-connd exited with status {0}'.format(cpe.returncode)) - return cpe.output.splitlines(), cpe.returncode + output = ['tell-connd exited with status {0}'.format(cpe.returncode), ] + output.extend( cpe.output.splitlines() ) + return cpe.returncode, output except subprocess.TimeoutExpired as te: - warn('tell-connd timed out after {0}s. Returning -1'.format(te.timeout)) - return te.output.splitlines(), -1 + output = ['tell-connd timed out after {0}s. Returning -1'.format(te.timeout), ] + output.extend( te.output.splitlines() ) + return -1, output + except Exception as e: + output = [str(e),] + return -1, output #end: ConndState.run_tell_connd @staticmethod - def get_state(): + def get_state(tell_connd_binary=default_tell_connd_binary): + """ get actual state from tell-connd --status + + returns (err_code, output_lines) if something goes wrong running binary; + raises assertion if output from tell-connd does not match expected format + """ state = ConndState() - all_lines, err_code = ConndState.run_tell_connd() + err_code, all_lines = ConndState.run_tell_connd(tell_connd_binary) + if err_code != EX_OK: + return err_code, all_lines + output = iter(all_lines) # first section line = next(output).strip() state.online_mode = regexp('online mode\s*:\s*(.+)$', line).groups()[0] + assert( state.online_mode in (ONLINE_STATE_DIAL_ON_DEMAND, ONLINE_STATE_DIAL_ON_COMMAND, \ + ONLINE_STATE_ALWAYS_OFFLINE, ONLINE_STATE_ALWAYS_ONLINE) ) line = next(output).strip() state.default_provider = regexp('default provider\s*:\s*(.*)$', \ line).groups()[0] @@ -75,10 +187,16 @@ class ConndState: assert( line == 'subsys' ) line = next(output).strip() state.subsys_online = regexp( 'online\s*:\s*(.*)$', line).groups()[0].split() + for subsys in state.subsys_online: + assert(subsys in ALL_SUBSYS) line = next(output).strip() state.subsys_offline = regexp( 'offline\s*:\s*(.*)$', line).groups()[0].split() + for subsys in state.subsys_offline: + assert(subsys in ALL_SUBSYS) line = next(output).strip() state.subsys_disabled = regexp('disabled\s*:\s*(.*)$', line).groups()[0].split() + for subsys in state.subsys_disabled: + assert(subsys in ALL_SUBSYS) line = next(output).strip() assert( len(line) == 0 ) @@ -97,7 +215,7 @@ class ConndState: conn_name, conn_info = regexp('\[\s*(.+)\s*\]\s*:\s*\(\s*(.*)\s*\)', line).groups() expect_new = False else: - conn_actions = regexp('actions\s*:\s*\[\s*(.+)\s*\]', line) + conn_actions = regexp('actions\s*:\s*\[\s*(.+)\s*\]', line).groups() state.connections.append( (conn_name, conn_info, conn_actions) ) expect_new = True #end: for lines @@ -151,9 +269,7 @@ class ConndState: def test(): state = ConndState.get_state() print(state) - print(state.subsys_online) - print(state.subsys_offline) - print(state.subsys_disabled) + print(state.complete_str()) def main(): """ Main function, called when running file as script; runs test() -- 1.7.1