From 09bf881b4f457f731c5a49e88822bc731eda2c96 Mon Sep 17 00:00:00 2001 From: Ruben Pollan Date: Fri, 17 Mar 2017 00:34:12 +0100 Subject: [feat] report the real status of the VPN --- src/leap/bitmask/cli/command.py | 23 +++++++++++++++++++++ src/leap/bitmask/cli/mail.py | 20 +----------------- src/leap/bitmask/cli/vpn.py | 6 +++++- src/leap/bitmask/core/mail_services.py | 27 ++----------------------- src/leap/bitmask/util.py | 27 +++++++++++++++++++++++++ src/leap/bitmask/vpn/_control.py | 2 +- src/leap/bitmask/vpn/_status.py | 37 +++++++++++++++++++++++++++------- src/leap/bitmask/vpn/fw/firewall.py | 16 ++++++++++----- src/leap/bitmask/vpn/process.py | 4 ++-- src/leap/bitmask/vpn/service.py | 9 ++++++--- src/leap/bitmask/vpn/vpn.py | 17 ++++++---------- 11 files changed, 114 insertions(+), 74 deletions(-) diff --git a/src/leap/bitmask/cli/command.py b/src/leap/bitmask/cli/command.py index 068f19b5..4bee325d 100644 --- a/src/leap/bitmask/cli/command.py +++ b/src/leap/bitmask/cli/command.py @@ -51,6 +51,29 @@ def default_dict_printer(result): print(Fore.RESET + key.ljust(10) + color + value + Fore.RESET) +def print_status(status, depth=0): + for name, v in [(' status', status)] + status['childrenStatus'].items(): + line = Fore.RESET + name.ljust(12) + if v['status'] in ('on', 'starting'): + line += Fore.GREEN + elif v['status'] == 'failure': + line += Fore.RED + line += v['status'] + if v['error']: + line += Fore.RED + " (" + v['error'] + ")" + line += Fore.RESET + print(line) + + for k, v in status.items(): + if k in ('status', 'childrenStatus', 'error'): + continue + if k == 'up': + k = '↑↑↑ ' + elif k == 'down': + k = '↓↓↓ ' + print(Fore.RESET + k.ljust(12) + Fore.CYAN + str(v) + Fore.RESET) + + class Command(object): """A generic command dispatcher. Any command in the class attribute `commands` will be dispached and diff --git a/src/leap/bitmask/cli/mail.py b/src/leap/bitmask/cli/mail.py index fd44383b..1624606a 100644 --- a/src/leap/bitmask/cli/mail.py +++ b/src/leap/bitmask/cli/mail.py @@ -57,22 +57,4 @@ SUBCOMMANDS: uid = self.cfg.get('bonafide', 'active', default=None) self.data += ['status', uid] - return self._send(self._print_status) - - def _print_status(self, status, depth=0): - for name, v in [('mail', status)] + status['childrenStatus'].items(): - line = Fore.RESET + name.ljust(10) - if v['status'] in ('on', 'starting'): - line += Fore.GREEN - elif v['status'] == 'failure': - line += Fore.RED - line += v['status'] - if v['error']: - line += Fore.RED + " (" + v['error'] + ")" - line += Fore.RESET - print(line) - - for k, v in status.items(): - if k in ('status', 'childrenStatus', 'error'): - continue - print(Fore.RESET + k.ljust(10) + Fore.CYAN + str(v) + Fore.RESET) + return self._send(command.print_status) diff --git a/src/leap/bitmask/cli/vpn.py b/src/leap/bitmask/cli/vpn.py index 69825159..e413e89d 100644 --- a/src/leap/bitmask/cli/vpn.py +++ b/src/leap/bitmask/cli/vpn.py @@ -43,7 +43,7 @@ SUBCOMMANDS: '''.format(name=command.appname) - commands = ['stop', 'status', 'install', 'uninstall', + commands = ['stop', 'install', 'uninstall', 'enable', 'disable'] def start(self, raw_args): @@ -68,6 +68,10 @@ SUBCOMMANDS: return self._send(command.default_dict_printer) + def status(self, raw_args): + self.data += ['status'] + return self._send(command.print_status) + def check(self, raw_args): parser = argparse.ArgumentParser( description='Bitmask VPN check', diff --git a/src/leap/bitmask/core/mail_services.py b/src/leap/bitmask/core/mail_services.py index 019a1048..70e7b490 100644 --- a/src/leap/bitmask/core/mail_services.py +++ b/src/leap/bitmask/core/mail_services.py @@ -47,7 +47,7 @@ from leap.bitmask.mail.imap import service as imap_service from leap.bitmask.mail.smtp import service as smtp_service from leap.bitmask.mail.incoming.service import IncomingMail from leap.bitmask.mail.incoming.service import INCOMING_CHECK_PERIOD -from leap.bitmask.util import get_gpg_bin_path +from leap.bitmask.util import get_gpg_bin_path, merge_status from leap.soledad.client.api import Soledad from leap.bitmask.core.uuid_map import UserMap @@ -599,30 +599,7 @@ class StandardMailService(service.MultiService, HookableService): 'keymanager': keymanager.status(userid), 'incoming': incoming_status } - - def key(service): - status = childrenStatus[service] - level = { - "on": 0, - "off": 1, - "starting": 10, - "stopping": 11, - "failure": 100 - } - return level.get(status["status"], -1) - - service = max(childrenStatus, key=key) - - status = childrenStatus[service]["status"] - error = childrenStatus[service]["error"] - - res = {} - for s in childrenStatus.values(): - res.update(s) - res['status'] = status - res['error'] = error - res['childrenStatus'] = childrenStatus - defer.returnValue(res) + defer.returnValue(merge_status(childrenStatus)) def get_token(self): active_user = self._active_user diff --git a/src/leap/bitmask/util.py b/src/leap/bitmask/util.py index 3a7bacd2..05f1c5b3 100644 --- a/src/leap/bitmask/util.py +++ b/src/leap/bitmask/util.py @@ -45,6 +45,33 @@ def here(module=None): return dirname(__file__) +def merge_status(children): + + def key(service): + status = children[service] + level = { + "on": 0, + "off": 1, + "starting": 10, + "stopping": 11, + "failure": 100 + } + return level.get(status["status"], -1) + + service = max(children, key=key) + + status = children[service]["status"] + error = children[service]["error"] + + res = {} + for s in children.values(): + res.update(s) + res['status'] = status + res['error'] = error + res['childrenStatus'] = children + return res + + def get_gpg_bin_path(): """ Return the path to gpg binary. diff --git a/src/leap/bitmask/vpn/_control.py b/src/leap/bitmask/vpn/_control.py index 8dfe4c64..a4909346 100644 --- a/src/leap/bitmask/vpn/_control.py +++ b/src/leap/bitmask/vpn/_control.py @@ -145,7 +145,7 @@ class VPNControl(object): @property def status(self): if not self._vpnproc: - return 'OFFLINE' + return {'status': 'off', 'error': None} return self._vpnproc.status @property diff --git a/src/leap/bitmask/vpn/_status.py b/src/leap/bitmask/vpn/_status.py index a0e38420..7c8ff6b0 100644 --- a/src/leap/bitmask/vpn/_status.py +++ b/src/leap/bitmask/vpn/_status.py @@ -2,6 +2,8 @@ from itertools import chain, repeat from twisted.logger import Logger from ._human import bytes2human +from leap.common.events import catalog, emit_async + logger = Logger() @@ -24,7 +26,8 @@ class VPNStatus(object): } def __init__(self): - self.status = 'OFFLINE' + self._status = 'off' + self.errcode = None self._traffic_down = None self._traffic_up = None @@ -49,8 +52,14 @@ class VPNStatus(object): self.set_status(status, errcode) def set_status(self, status, errcode): - self.status = status + if status in ("AUTH", "WAIT"): + status = "starting" + elif status == "CONNECTED": + status = "on" + + self._status = status self.errcode = errcode + emit_async(catalog.VPN_STATUS_CHANGED) def set_traffic_status(self, status): up, down = status @@ -58,16 +67,30 @@ class VPNStatus(object): self._traffic_down = down def get_traffic_status(self): - return {'down': bytes2human(self._traffic_down), - 'up': bytes2human(self._traffic_up)} + down = None + up = None + if self._traffic_down: + down = bytes2human(self._traffic_down) + if self._traffic_up: + up = bytes2human(self._traffic_up) + return {'down': down, 'up': up} + + @property + def status(self): + status = self.get_traffic_status() + status.update({ + 'status': self._status, + 'error': self.errcode + }) + return status def _status_codes(self, event): # TODO check good transitions # TODO check valid states _table = { - "network_unreachable": ('OFFLINE', 'network unreachable'), - "process_restart_tls": ('RESTARTING', 'restart tls'), - "initialization_completed": ('ONLINE', None) + "network_unreachable": ('off', 'network unreachable'), + "process_restart_tls": ('starting', 'restart tls'), + "initialization_completed": ('on', None) } return _table.get(event.lower()) diff --git a/src/leap/bitmask/vpn/fw/firewall.py b/src/leap/bitmask/vpn/fw/firewall.py index 5eace20a..8b71a9fd 100644 --- a/src/leap/bitmask/vpn/fw/firewall.py +++ b/src/leap/bitmask/vpn/fw/firewall.py @@ -23,6 +23,7 @@ import commands import subprocess from leap.bitmask.vpn.constants import IS_MAC +from leap.common.events import catalog, emit_async class FirewallManager(object): @@ -44,7 +45,6 @@ class FirewallManager(object): :param remotes: the gateway(s) that we will allow :type remotes: list """ - self.status = 'OFF' self._remotes = remotes def start(self, restart=False): @@ -66,12 +66,11 @@ class FirewallManager(object): # FIXME -- use a processprotocol exitCode = subprocess.call(cmd + gateways) + emit_async(catalog.VPN_STATUS_CHANGED) if exitCode == 0: - self.status = 'ON' return True else: - self.status = 'OFF' return False # def tear_down_firewall(self): @@ -85,11 +84,10 @@ class FirewallManager(object): exitCode = subprocess.call(["pkexec", self.BITMASK_ROOT, "firewall", "stop"]) + emit_async(catalog.VPN_STATUS_CHANGED) if exitCode == 0: - self.status = 'OFF' return True else: - self.status = 'ON' return False def is_up(self): @@ -104,3 +102,11 @@ class FirewallManager(object): output = commands.getstatusoutput(cmd)[0] return output != 256 + + @property + def status(self): + status = 'off' + if self.is_up(): + status = 'on' + + return {'status': status, 'error': None} diff --git a/src/leap/bitmask/vpn/process.py b/src/leap/bitmask/vpn/process.py index cb67eff3..6096a473 100644 --- a/src/leap/bitmask/vpn/process.py +++ b/src/leap/bitmask/vpn/process.py @@ -136,9 +136,9 @@ class VPNProcess(protocol.ProcessProtocol, _management.VPNManagement): internet_error.ProcessDone, internet_error.ProcessTerminated) if err == internet_error.ProcessDone: - status, errmsg = 'OFFLINE', None + status, errmsg = 'off', None elif err == internet_error.ProcessTerminated: - status, errmsg = 'ABORTED', failure.value.exitCode + status, errmsg = 'failure', failure.value.exitCode if errmsg: logger.debug("processExited, status %d" % (errmsg,)) else: diff --git a/src/leap/bitmask/vpn/service.py b/src/leap/bitmask/vpn/service.py index b5d7e523..2c7c69ce 100644 --- a/src/leap/bitmask/vpn/service.py +++ b/src/leap/bitmask/vpn/service.py @@ -80,11 +80,14 @@ class VPNService(HookableService): return {'result': 'stopped'} def do_status(self): + status = { + 'status': 'off', + 'error': None, + 'childrenStatus': {} + } if self._vpn: status = self._vpn.get_status() - else: - status = {'VPN': 'OFF'} - status['domain'] = self._domain + status['domain'] = self._domain return status def do_check(self, domain): diff --git a/src/leap/bitmask/vpn/vpn.py b/src/leap/bitmask/vpn/vpn.py index e19f6629..dc3062af 100644 --- a/src/leap/bitmask/vpn/vpn.py +++ b/src/leap/bitmask/vpn/vpn.py @@ -18,6 +18,7 @@ from colorama import Fore +from leap.bitmask.util import merge_status from leap.bitmask.vpn.manager import TunnelManager from leap.bitmask.vpn.fw.firewall import FirewallManager @@ -68,14 +69,8 @@ class VPNManager(object): return True def get_status(self): - vpn_status = self._vpn.status - # TODO use firewall.is_up instead - fw_status = self._firewall.status - - result = {'VPN': vpn_status, - 'firewall': fw_status} - if vpn_status == 'CONNECTED': - traffic = self._vpn.traffic_status - result['↑↑↑'] = traffic['up'] - result['↓↓↓'] = traffic['down'] - return result + childrenStatus = { + "vpn": self._vpn.status, + "firewall": self._firewall.status + } + return merge_status(childrenStatus) -- cgit v1.2.3