summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkali <kali@leap.se>2013-01-16 00:59:54 +0900
committerkali <kali@leap.se>2013-01-16 00:59:54 +0900
commit9c260efd6f9821f5d97cc426f501203bfc8a1fde (patch)
tree3a0a31077cb6cb1e4ec1bbcfc2cd08e6b08adf4b
parentf90f9df1d09e12ba64e9401530684d5a36220ad3 (diff)
parentbf39c45eddc62733fdb72b4f46cdb81ec649cb30 (diff)
Merge branch 'feature/openvpn_logs_mgmt_interface' into develop
-rw-r--r--src/leap/base/checks.py23
-rw-r--r--src/leap/base/network.py17
-rw-r--r--src/leap/base/tests/test_checks.py38
-rw-r--r--src/leap/baseapp/eip.py6
-rw-r--r--src/leap/baseapp/log.py6
-rw-r--r--src/leap/baseapp/network.py24
-rw-r--r--src/leap/eip/eipconnection.py6
-rw-r--r--src/leap/eip/openvpnconnection.py30
8 files changed, 127 insertions, 23 deletions
diff --git a/src/leap/base/checks.py b/src/leap/base/checks.py
index 4d4a5d8b..e5767018 100644
--- a/src/leap/base/checks.py
+++ b/src/leap/base/checks.py
@@ -12,6 +12,9 @@ from leap.base import exceptions
logger = logging.getLogger(name=__name__)
+#EVENTS OF NOTE
+EVENT_CONNECT_REFUSED = "[ECONNREFUSED]: Connection refused (code=111)"
+
class LeapNetworkChecker(object):
"""
@@ -34,6 +37,8 @@ class LeapNetworkChecker(object):
if self.provider_gateway:
checker.ping_gateway(self.provider_gateway)
+ checker.parse_log_and_react([], ())
+
def check_internet_connection(self):
try:
# XXX remove this hardcoded random ip
@@ -136,3 +141,21 @@ class LeapNetworkChecker(object):
return True
except socket.gaierror:
raise exceptions.CannotResolveDomainError
+
+ def parse_log_and_react(self, log, error_matrix=None):
+ """
+ compares the recent openvpn status log to
+ strings passed in and executes the callbacks passed in.
+ @param log: openvpn log
+ @type log: list of strings
+ @param error_matrix: tuples of strings and tuples of callbacks
+ @type error_matrix: tuples strings and call backs
+ """
+ for line in log:
+ # we could compile a regex here to save some cycles up -- kali
+ for each in error_matrix:
+ error, callbacks = each
+ if error in line:
+ for cb in callbacks:
+ if callable(cb):
+ cb()
diff --git a/src/leap/base/network.py b/src/leap/base/network.py
index 765d8ea0..d841e692 100644
--- a/src/leap/base/network.py
+++ b/src/leap/base/network.py
@@ -21,8 +21,8 @@ class NetworkCheckerThread(object):
connection.
"""
def __init__(self, *args, **kwargs):
+
self.status_signals = kwargs.pop('status_signals', None)
- #self.watcher_cb = kwargs.pop('status_signals', None)
self.error_cb = kwargs.pop(
'error_cb',
lambda exc: logger.error("%s", exc.message))
@@ -48,6 +48,7 @@ class NetworkCheckerThread(object):
(self.error_cb,))
def stop(self):
+ self.process_handle.join(timeout=0.1)
self.shutdown.set()
logger.debug("network checked stopped.")
@@ -59,6 +60,7 @@ class NetworkCheckerThread(object):
#here all the observers in fail_callbacks expect one positional argument,
#which is exception so we can try by passing a lambda with logger to
#check it works.
+
def _network_checks_thread(self, fail_callbacks):
#TODO: replace this with waiting for a signal from openvpn
while True:
@@ -69,11 +71,15 @@ class NetworkCheckerThread(object):
# XXX ??? why do we sleep here???
# aa: If the openvpn isn't up and running yet,
# let's give it a moment to breath.
+ #logger.error('NOT DEFAULT ROUTE!----')
+ # Instead of this, we should flag when the
+ # iface IS SUPPOSED to be up imo. -- kali
sleep(1)
fail_observer_dict = dict(((
observer,
process_events(observer)) for observer in fail_callbacks))
+
while not self.shutdown.is_set():
try:
self.checker.check_tunnel_default_interface()
@@ -83,11 +89,18 @@ class NetworkCheckerThread(object):
for obs in fail_observer_dict:
fail_observer_dict[obs].send(exc)
sleep(ROUTE_CHECK_INTERVAL)
+
#reset event
+ # I see a problem with this. You cannot stop it, it
+ # resets itself forever. -- kali
+
+ # XXX use QTimer for the recurrent triggers,
+ # and ditch the sleeps.
+ logger.debug('resetting event')
self.shutdown.clear()
def _launch_recurrent_network_checks(self, fail_callbacks):
- #we need to wrap the fail callback in a tuple
+ # XXX reimplement using QTimer -- kali
watcher = launch_thread(
self._network_checks_thread,
(fail_callbacks,))
diff --git a/src/leap/base/tests/test_checks.py b/src/leap/base/tests/test_checks.py
index 7a694f89..645e615c 100644
--- a/src/leap/base/tests/test_checks.py
+++ b/src/leap/base/tests/test_checks.py
@@ -37,6 +37,8 @@ class LeapNetworkCheckTest(BaseLeapTest):
"missing meth")
self.assertTrue(hasattr(checker, "ping_gateway"),
"missing meth")
+ self.assertTrue(hasattr(checker, "parse_log_and_react"),
+ "missing meth")
def test_checker_should_actually_call_all_tests(self):
checker = checks.LeapNetworkChecker()
@@ -45,6 +47,7 @@ class LeapNetworkCheckTest(BaseLeapTest):
self.assertTrue(mc.check_internet_connection.called, "not called")
self.assertTrue(mc.check_tunnel_default_interface.called, "not called")
self.assertTrue(mc.is_internet_up.called, "not called")
+ self.assertTrue(mc.parse_log_and_react.called, "not called")
# ping gateway only called if we pass provider_gw
checker = checks.LeapNetworkChecker(provider_gw="0.0.0.0")
@@ -54,6 +57,7 @@ class LeapNetworkCheckTest(BaseLeapTest):
self.assertTrue(mc.check_tunnel_default_interface.called, "not called")
self.assertTrue(mc.ping_gateway.called, "not called")
self.assertTrue(mc.is_internet_up.called, "not called")
+ self.assertTrue(mc.parse_log_and_react.called, "not called")
def test_get_default_interface_no_interface(self):
checker = checks.LeapNetworkChecker()
@@ -134,6 +138,40 @@ class LeapNetworkCheckTest(BaseLeapTest):
mock_ping.side_effect = exceptions.NoConnectionToGateway
checker.check_internet_connection()
+ def test_parse_log_and_react(self):
+ checker = checks.LeapNetworkChecker()
+ to_call = Mock()
+ log = [("leap.openvpn - INFO - Mon Nov 19 13:36:24 2012 "
+ "read UDPv4 [ECONNREFUSED]: Connection refused (code=111)"]
+ err_matrix = [(checks.EVENT_CONNECT_REFUSED, (to_call, ))]
+ checker.parse_log_and_react(log, err_matrix)
+ self.assertTrue(to_call.called)
+
+ log = [("2012-11-19 13:36:26,177 - leap.openvpn - INFO - "
+ "Mon Nov 19 13:36:24 2012 ERROR: Linux route delete command "
+ "failed: external program exited"),
+ ("2012-11-19 13:36:26,178 - leap.openvpn - INFO - "
+ "Mon Nov 19 13:36:24 2012 ERROR: Linux route delete command "
+ "failed: external program exited"),
+ ("2012-11-19 13:36:26,180 - leap.openvpn - INFO - "
+ "Mon Nov 19 13:36:24 2012 ERROR: Linux route delete command "
+ "failed: external program exited"),
+ ("2012-11-19 13:36:26,181 - leap.openvpn - INFO - "
+ "Mon Nov 19 13:36:24 2012 /sbin/ifconfig tun0 0.0.0.0"),
+ ("2012-11-19 13:36:26,182 - leap.openvpn - INFO - "
+ "Mon Nov 19 13:36:24 2012 Linux ip addr del failed: external "
+ "program exited with error stat"),
+ ("2012-11-19 13:36:26,183 - leap.openvpn - INFO - "
+ "Mon Nov 19 13:36:26 2012 SIGTERM[hard,] received, process"
+ "exiting"), ]
+ to_call.reset_mock()
+ checker.parse_log_and_react(log, err_matrix)
+ self.assertFalse(to_call.called)
+
+ to_call.reset_mock()
+ checker.parse_log_and_react([], err_matrix)
+ self.assertFalse(to_call.called)
+
@unittest.skipUnless(_uid == 0, "root only")
def test_ping_gateway(self):
checker = checks.LeapNetworkChecker()
diff --git a/src/leap/baseapp/eip.py b/src/leap/baseapp/eip.py
index 41f4c541..4fcbee3f 100644
--- a/src/leap/baseapp/eip.py
+++ b/src/leap/baseapp/eip.py
@@ -9,6 +9,7 @@ from leap.baseapp.dialogs import ErrorDialog
from leap.baseapp import constants
from leap.eip import exceptions as eip_exceptions
from leap.eip.eipconnection import EIPConnection
+from leap.base.checks import EVENT_CONNECT_REFUSED
logger = logging.getLogger(name=__name__)
@@ -174,6 +175,11 @@ class EIPConductorAppMixin(object):
self.tun_read_bytes.setText(tun_read)
self.tun_write_bytes.setText(tun_write)
+ # connection information via management interface
+ log = self.conductor.get_log()
+ error_matrix = [(EVENT_CONNECT_REFUSED, (self.start_or_stopVPN, ))]
+ self.network_checker.checker.parse_log_and_react(log, error_matrix)
+
@QtCore.pyqtSlot()
def start_or_stopVPN(self):
"""
diff --git a/src/leap/baseapp/log.py b/src/leap/baseapp/log.py
index 95cfc918..e6a767fb 100644
--- a/src/leap/baseapp/log.py
+++ b/src/leap/baseapp/log.py
@@ -11,6 +11,7 @@ class LogPaneMixin(object):
a simple log pane
that writes new lines as they come
"""
+ EXCLUDES = ('MANAGEMENT',)
def createLogBrowser(self):
"""
@@ -60,6 +61,7 @@ class LogPaneMixin(object):
simple slot: writes new line to logger Pane.
"""
msg = line[:-1]
- if self.debugmode:
+ if self.debugmode and all(map(lambda w: w not in msg,
+ LogPaneMixin.EXCLUDES)):
self.logbrowser.append(msg)
- vpnlogger.info(msg)
+ vpnlogger.info(msg)
diff --git a/src/leap/baseapp/network.py b/src/leap/baseapp/network.py
index a33265e5..a67f6340 100644
--- a/src/leap/baseapp/network.py
+++ b/src/leap/baseapp/network.py
@@ -17,6 +17,8 @@ class NetworkCheckerAppMixin(object):
initialize an instance of the Network Checker,
which gathers error and passes them on.
"""
+ ERR_NETERR = False
+
def __init__(self, *args, **kwargs):
provider = kwargs.pop('provider', None)
if provider:
@@ -41,11 +43,19 @@ class NetworkCheckerAppMixin(object):
slot that receives a network exceptions
and raises a user error message
"""
- logger.debug('handling network exception')
- logger.error(exc.message)
- dialog = ErrorDialog(parent=self)
+ # FIXME this should not HANDLE anything after
+ # the network check thread has been stopped.
- if exc.critical:
- dialog.criticalMessage(exc.usermessage, "network error")
- else:
- dialog.warningMessage(exc.usermessage, "network error")
+ logger.debug('handling network exception')
+ if not self.ERR_NETERR:
+ self.ERR_NETERR = True
+
+ logger.error(exc.message)
+ dialog = ErrorDialog(parent=self)
+ if exc.critical:
+ dialog.criticalMessage(exc.usermessage, "network error")
+ else:
+ dialog.warningMessage(exc.usermessage, "network error")
+
+ self.start_or_stopVPN()
+ self.network_checker.stop()
diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py
index 540e7558..20b45e36 100644
--- a/src/leap/eip/eipconnection.py
+++ b/src/leap/eip/eipconnection.py
@@ -27,6 +27,8 @@ class StatusMixIn(object):
# Should separate EIPConnectionStatus (self.status)
# from the OpenVPN state/status command and parsing.
+ ERR_CONNREFUSED = False
+
def connection_state(self):
"""
returns the current connection state
@@ -49,7 +51,9 @@ class StatusMixIn(object):
state = self.get_connection_state()
except eip_exceptions.ConnectionRefusedError:
# connection refused. might be not ready yet.
- logger.warning('connection refused')
+ if not self.ERR_CONNREFUSED:
+ logger.warning('connection refused')
+ self.ERR_CONNREFUSED = True
return
if not state:
#logger.debug('no state')
diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py
index b36b0b16..a36d99de 100644
--- a/src/leap/eip/openvpnconnection.py
+++ b/src/leap/eip/openvpnconnection.py
@@ -92,14 +92,17 @@ class OpenVPNManagement(object):
logger.error('socket error')
self._close_management_socket(announce=False)
return []
- buf = self.tn.read_until(b"END", 2)
- self._seek_to_eof()
- blist = buf.split('\r\n')
- if blist[-1].startswith('END'):
- del blist[-1]
- return blist
- else:
- return []
+ try:
+ buf = self.tn.read_until(b"END", 2)
+ self._seek_to_eof()
+ blist = buf.split('\r\n')
+ if blist[-1].startswith('END'):
+ del blist[-1]
+ return blist
+ else:
+ return []
+ except socket.error as exc:
+ logger.debug('socket error: %s' % exc.message)
def _send_short_command(self, cmd):
"""
@@ -329,12 +332,12 @@ to be triggered for each one of them.
#use _only_ signal_maps instead
logger.debug('_launch_openvpn called')
- logger.debug('watcher_cb: %s' % self.watcher_cb)
if self.watcher_cb is not None:
linewrite_callback = self.watcher_cb
else:
#XXX get logger instead
- linewrite_callback = lambda line: logger.debug('watcher: %s' % line)
+ linewrite_callback = lambda line: logger.debug(
+ 'watcher: %s' % line)
# the partial is not
# being applied now because we're not observing the process
@@ -342,7 +345,8 @@ to be triggered for each one of them.
# here since it will be handy for observing patterns in the
# thru-the-manager updates (with regex)
observers = (linewrite_callback,
- partial(lambda con_status, line: linewrite_callback, self.status))
+ partial(lambda con_status,
+ line: linewrite_callback, self.status))
subp, watcher = spawn_and_watch_process(
self.command,
self.args,
@@ -400,3 +404,7 @@ to be triggered for each one of them.
if process.name == "openvpn":
return process
return None
+
+ def get_log(self, lines=1):
+ log = self._send_command("log %s" % lines)
+ return log