From a2804c3de1470db98d8c6aa8a01e2de1aa1718a1 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 5 Sep 2012 07:42:10 +0900 Subject: app wide logging handler --- src/leap/app.py | 25 +++++++++++++++++-------- src/leap/baseapp/eip.py | 9 +++++++-- src/leap/baseapp/leap_app.py | 6 ++++++ src/leap/baseapp/mainwindow.py | 5 ++--- src/leap/eip/checks.py | 7 ++----- src/leap/eip/config.py | 2 -- src/leap/eip/eipconnection.py | 6 ++---- src/leap/eip/openvpnconnection.py | 6 +++--- 8 files changed, 39 insertions(+), 27 deletions(-) diff --git a/src/leap/app.py b/src/leap/app.py index db48701b..3864ffeb 100644 --- a/src/leap/app.py +++ b/src/leap/app.py @@ -6,9 +6,6 @@ from PyQt4.QtGui import (QApplication, QSystemTrayIcon, QMessageBox) from leap.baseapp.mainwindow import LeapWindow -logging.basicConfig() -logger = logging.getLogger(name=__name__) - def main(): """ @@ -20,17 +17,29 @@ def main(): parser, opts = leap_argparse.init_leapc_args() debug = getattr(opts, 'debug', False) - #XXX get debug level and set logger accordingly if debug: - logger.setLevel('DEBUG') - logger.debug('args: %s' % opts) + level = logging.DEBUG + else: + level = logging.WARNING + + logger = logging.getLogger(name='leap') + logger.setLevel(level) + console = logging.StreamHandler() + console.setLevel(level) + formatter = logging.Formatter( + '%(asctime)s ' + '- %(name)s - %(levelname)s - %(message)s') + console.setFormatter(formatter) + logger.addHandler(console) + logger.debug('args: %s' % opts) + logger.info('Starting app') app = QApplication(sys.argv) if not QSystemTrayIcon.isSystemTrayAvailable(): QMessageBox.critical(None, "Systray", - "I couldn't detect any \ -system tray on this system.") + "I couldn't detect" + "any system tray on this system.") sys.exit(1) if not debug: QApplication.setQuitOnLastWindowClosed(False) diff --git a/src/leap/baseapp/eip.py b/src/leap/baseapp/eip.py index 029ce0ba..e0da63a2 100644 --- a/src/leap/baseapp/eip.py +++ b/src/leap/baseapp/eip.py @@ -1,3 +1,4 @@ +import logging import time from PyQt4 import QtCore @@ -7,6 +8,8 @@ from leap.baseapp import constants from leap.eip import exceptions as eip_exceptions from leap.eip.eipconnection import EIPConnection +logger = logging.getLogger(name=__name__) + class EIPConductorApp(object): """ @@ -126,8 +129,10 @@ class EIPConductorApp(object): if self.conductor.with_errors: #XXX how to wait on pkexec??? #something better that this workaround, plz!! - time.sleep(2) - print('errors. disconnect.') + time.sleep(5) + #print('errors. disconnect.') + logger.debug('timeout') + logger.error('errors. disconnect') self.start_or_stopVPN() # is stop state = self.conductor.poll_connection_state() diff --git a/src/leap/baseapp/leap_app.py b/src/leap/baseapp/leap_app.py index def95da1..85644360 100644 --- a/src/leap/baseapp/leap_app.py +++ b/src/leap/baseapp/leap_app.py @@ -1,7 +1,11 @@ +import logging + from PyQt4 import QtGui from leap.gui import mainwindow_rc +logger = logging.getLogger(name=__name__) + class MainWindow(object): """ @@ -79,5 +83,7 @@ technolust") # TODO:make sure to shutdown all child process / threads # in conductor # XXX send signal instead? + logger.info('Shutting down') self.conductor.cleanup() + logger.info('Exiting') QtGui.qApp.quit() diff --git a/src/leap/baseapp/mainwindow.py b/src/leap/baseapp/mainwindow.py index ac7fe9c4..e87f5844 100644 --- a/src/leap/baseapp/mainwindow.py +++ b/src/leap/baseapp/mainwindow.py @@ -1,9 +1,6 @@ # vim: set fileencoding=utf-8 : #!/usr/bin/env python import logging -logging.basicConfig() -logger = logging.getLogger(name=__name__) -logger.setLevel(logging.DEBUG) from PyQt4 import QtCore from PyQt4 import QtGui @@ -13,6 +10,8 @@ from leap.baseapp.log import LogPane from leap.baseapp.systray import StatusAwareTrayIcon from leap.baseapp.leap_app import MainWindow +logger = logging.getLogger(name=__name__) + class LeapWindow(QtGui.QMainWindow, MainWindow, EIPConductorApp, diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 1b7c2e1b..4a2a9599 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -1,12 +1,7 @@ -#import json import logging import ssl import os -logging.basicConfig() -logger = logging.getLogger(name=__name__) -logger.setLevel(logging.DEBUG) - import requests from leap.base import constants as baseconstants @@ -17,6 +12,8 @@ from leap.eip import exceptions as eipexceptions from leap.eip import specs as eipspecs from leap.util.fileutil import mkdir_p +logger = logging.getLogger(name=__name__) + """ EIPConfigChecker ---------- diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 810a5a8d..f4b979ce 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -10,9 +10,7 @@ from leap.baseapp.permcheck import (is_pkexec_in_system, from leap.eip import exceptions as eip_exceptions from leap.eip import specs as eipspecs -logging.basicConfig() logger = logging.getLogger(name=__name__) -logger.setLevel('DEBUG') class EIPConfig(baseconfig.JSONLeapConfig): diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index 386b71be..3a6f4d49 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -4,14 +4,12 @@ EIP Connection Class from __future__ import (absolute_import,) import logging -logging.basicConfig() -logger = logging.getLogger(name=__name__) -logger.setLevel(logging.DEBUG) - from leap.eip.checks import EIPConfigChecker from leap.eip import exceptions as eip_exceptions from leap.eip.openvpnconnection import OpenVPNConnection +logger = logging.getLogger(name=__name__) + class EIPConnection(OpenVPNConnection): """ diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 32fa55b1..48252e10 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -7,9 +7,7 @@ import socket import time from functools import partial -logging.basicConfig() logger = logging.getLogger(name=__name__) -logger.setLevel(logging.DEBUG) from leap.base.connection import Connection from leap.util.coroutines import spawn_and_watch_process @@ -45,6 +43,7 @@ to be triggered for each one of them. :type watcher_cb: function :type signal_map: dict """ + logger.debug('init openvpn connection') self.debug = debug #print('conductor:%s' % debug) @@ -192,7 +191,8 @@ to be triggered for each one of them. # def forget_errors(self): - print('forgetting errors') + #print('forgetting errors') + logger.debug('forgetting errors') self.with_errors = False def connect_to_management(self): -- cgit v1.2.3 From 813a97957572aad97d50319db96b55a74b8ed307 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 5 Sep 2012 07:54:10 +0900 Subject: can log to logfile app.py --debug --logfile /tmp/foo.log --- src/leap/app.py | 10 ++++++++++ src/leap/util/leap_argparse.py | 4 ++++ 2 files changed, 14 insertions(+) diff --git a/src/leap/app.py b/src/leap/app.py index 3864ffeb..1ab806ca 100644 --- a/src/leap/app.py +++ b/src/leap/app.py @@ -17,6 +17,7 @@ def main(): parser, opts = leap_argparse.init_leapc_args() debug = getattr(opts, 'debug', False) + # XXX get severity from command line args if debug: level = logging.DEBUG else: @@ -31,6 +32,15 @@ def main(): '- %(name)s - %(levelname)s - %(message)s') console.setFormatter(formatter) logger.addHandler(console) + + logfile = getattr(opts, 'log_file', False) + if logfile: + logger.debug('setting logfile ', logfile) + fileh = logging.FileHandler(logfile) + fileh.setLevel(logging.DEBUG) + fileh.setFormatter(formatter) + logger.addHandler(fileh) + logger.debug('args: %s' % opts) logger.info('Starting app') diff --git a/src/leap/util/leap_argparse.py b/src/leap/util/leap_argparse.py index 9c355134..f329cf3e 100644 --- a/src/leap/util/leap_argparse.py +++ b/src/leap/util/leap_argparse.py @@ -11,6 +11,10 @@ Launches main LEAP Client""", epilog=epilog) action="store", dest="config_file", type=argparse.FileType('r'), help='optional config file') + parser.add_argument('--logfile', metavar="LOG FILE", nargs='?', + action="store", dest="log_file", + #type=argparse.FileType('w'), + help='optional log file') return parser -- cgit v1.2.3 From 535e584ba978a7a234a52df14f89197fbc3cea14 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 5 Sep 2012 07:59:35 +0900 Subject: openvpn messages log to eip.openvpn logger so we can get them to file / stdout even if our log viewer is not launched. --- src/leap/baseapp/eip.py | 1 - src/leap/baseapp/log.py | 8 +++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/leap/baseapp/eip.py b/src/leap/baseapp/eip.py index e0da63a2..856cb197 100644 --- a/src/leap/baseapp/eip.py +++ b/src/leap/baseapp/eip.py @@ -130,7 +130,6 @@ class EIPConductorApp(object): #XXX how to wait on pkexec??? #something better that this workaround, plz!! time.sleep(5) - #print('errors. disconnect.') logger.debug('timeout') logger.error('errors. disconnect') self.start_or_stopVPN() # is stop diff --git a/src/leap/baseapp/log.py b/src/leap/baseapp/log.py index 0c98eb94..3580e987 100644 --- a/src/leap/baseapp/log.py +++ b/src/leap/baseapp/log.py @@ -1,6 +1,10 @@ +import logging + from PyQt4 import QtGui from PyQt4 import QtCore +vpnlogger = logging.getLogger('leap.openvpn') + class LogPane(object): """ @@ -56,5 +60,7 @@ class LogPane(object): """ simple slot: writes new line to logger Pane. """ + msg = line[:-1] if self.debugmode: - self.logbrowser.append(line[:-1]) + self.logbrowser.append(msg) + vpnlogger.info(msg) -- cgit v1.2.3 From c190b7f66cc1977d0e058bfa2d8fc1a850326320 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 5 Sep 2012 10:23:24 +0900 Subject: missing_pkexec error converted to "auto" error. idea is that we define user messages in the exceptions, and queue them during (conductor) checks. user facing dialogs get constucted from exception attrs. if critical, log as such and exit. --- src/leap/app.py | 2 +- src/leap/baseapp/dialogs.py | 17 +++++------ src/leap/baseapp/eip.py | 59 +++++++++++++++++++++++++----------- src/leap/eip/eipconnection.py | 38 +++++++++++++---------- src/leap/eip/exceptions.py | 64 +++++++++++++++++++++------------------ src/leap/eip/openvpnconnection.py | 16 +++++++--- 6 files changed, 116 insertions(+), 80 deletions(-) diff --git a/src/leap/app.py b/src/leap/app.py index 1ab806ca..5849848c 100644 --- a/src/leap/app.py +++ b/src/leap/app.py @@ -35,7 +35,7 @@ def main(): logfile = getattr(opts, 'log_file', False) if logfile: - logger.debug('setting logfile ', logfile) + logger.debug('setting logfile to %s ', logfile) fileh = logging.FileHandler(logfile) fileh.setLevel(logging.DEBUG) fileh.setFormatter(formatter) diff --git a/src/leap/baseapp/dialogs.py b/src/leap/baseapp/dialogs.py index 4b1b5b62..d37a234c 100644 --- a/src/leap/baseapp/dialogs.py +++ b/src/leap/baseapp/dialogs.py @@ -4,7 +4,6 @@ from PyQt4.QtGui import (QDialog, QFrame, QPushButton, QLabel, QMessageBox) class ErrorDialog(QDialog): def __init__(self, parent=None): super(ErrorDialog, self).__init__(parent) - frameStyle = QFrame.Sunken | QFrame.Panel self.warningLabel = QLabel() self.warningLabel.setFrameStyle(frameStyle) @@ -15,19 +14,17 @@ class ErrorDialog(QDialog): "QMessageBox.warning()", msg, QMessageBox.NoButton, self) msgBox.addButton("&Ok", QMessageBox.AcceptRole) - msgBox.addButton("&Cancel", QMessageBox.RejectRole) if msgBox.exec_() == QMessageBox.AcceptRole: - self.warningLabel.setText("Save Again") - else: - self.warningLabel.setText("Continue") + pass + # do whatever we want to do after + # closing the dialog. we can pass that + # in the constructor def criticalMessage(self, msg, label): msgBox = QMessageBox(QMessageBox.Critical, "QMessageBox.critical()", msg, QMessageBox.NoButton, self) msgBox.addButton("&Ok", QMessageBox.AcceptRole) - msgBox.addButton("&Cancel", QMessageBox.RejectRole) - if msgBox.exec_() == QMessageBox.AcceptRole: - self.warningLabel.setText("Save Again") - else: - self.warningLabel.setText("Continue") + msgBox.exec_() + import sys + sys.exit() diff --git a/src/leap/baseapp/eip.py b/src/leap/baseapp/eip.py index 856cb197..dd88b7f5 100644 --- a/src/leap/baseapp/eip.py +++ b/src/leap/baseapp/eip.py @@ -12,6 +12,7 @@ logger = logging.getLogger(name=__name__) class EIPConductorApp(object): + # XXX EIPConductorMixin ? """ initializes an instance of EIPConnection, gathers errors, and passes status-change signals @@ -51,8 +52,38 @@ class EIPConductorApp(object): lambda: self.start_or_stopVPN()) def error_check(self): - - # XXX refactor (by #504) + logger.debug('error check') + + ##################################### + # XXX refactor in progress (by #504) + errq = self.conductor.error_queue + while errq.qsize() != 0: + logger.debug('%s errors left in conductor queue', errq.qsize()) + error = errq.get() + logger.error('%s: %s', error.__class__.__name__, error.message) + + if issubclass(error.__class__, eip_exceptions.EIPClientError): + if error.critical: + logger.critical(error.message) + logger.error('quitting') + + # XXX + # check headless = False before + # launching dialog. + # (for Qt tests) + + dialog = ErrorDialog() + if getattr(error, 'usermessage', None): + message = error.usermessage + else: + message = error.message + dialog.criticalMessage(message, 'error') + else: + logger.exception(error.message) + else: + import traceback + traceback.print_exc() + raise error if self.conductor.missing_definition is True: dialog = ErrorDialog() @@ -78,12 +109,14 @@ class EIPConductorApp(object): # os.kill of subprocess fails if we have # some of this errors. - if self.conductor.bad_provider is True: - dialog = ErrorDialog() - dialog.criticalMessage( - 'Bad provider entry. Check that remote_ip entry ' - 'has an IP under section [provider] in eip.cfg', - 'error') + # deprecated. + # get something alike. + #if self.conductor.bad_provider is True: + #dialog = ErrorDialog() + #dialog.criticalMessage( + #'Bad provider entry. Check that remote_ip entry ' + #'has an IP under section [provider] in eip.cfg', + #'error') if self.conductor.bad_keyfile_perms is True: dialog = ErrorDialog() @@ -91,16 +124,6 @@ class EIPConductorApp(object): 'The vpn keys file has bad permissions', 'error') - if self.conductor.missing_auth_agent is True: - dialog = ErrorDialog() - dialog.warningMessage( - 'We could not find any authentication ' - 'agent in your system.
' - 'Make sure you have ' - 'polkit-gnome-authentication-agent-1 ' - 'running and try again.', - 'error') - if self.conductor.missing_pkexec is True: dialog = ErrorDialog() dialog.warningMessage( diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index 3a6f4d49..e090f9a7 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -3,6 +3,7 @@ EIP Connection Class """ from __future__ import (absolute_import,) import logging +import Queue from leap.eip.checks import EIPConfigChecker from leap.eip import exceptions as eip_exceptions @@ -23,8 +24,8 @@ class EIPConnection(OpenVPNConnection): self.settingsfile = kwargs.get('settingsfile', None) self.logfile = kwargs.get('logfile', None) - # not used atm. but should. - self.error_queue = [] + # XXX USE THIS + self.error_queue = Queue.Queue() status_signals = kwargs.pop('status_signals', None) self.status = EIPConnectionStatus(callbacks=status_signals) @@ -36,7 +37,12 @@ class EIPConnection(OpenVPNConnection): """ run all eip checks previous to attempting a connection """ - self.config_checker.run_all(skip_download=skip_download) + logger.debug('running conductor checks') + try: + self.config_checker.run_all(skip_download=skip_download) + self.run_openvpn_checks() + except Exception as exc: + self.error_queue.put(exc) def connect(self): """ @@ -44,7 +50,6 @@ class EIPConnection(OpenVPNConnection): """ self.forget_errors() self._try_connection() - # XXX should capture errors? def disconnect(self): """ @@ -65,11 +70,11 @@ class EIPConnection(OpenVPNConnection): """ return self.status.current - def desired_connection_state(self): - """ - returns the desired_connection state - """ - return self.desired_con_state + #def desired_connection_state(self): + #""" + #returns the desired_connection state + #""" + #return self.desired_con_state def poll_connection_state(self): """ @@ -107,26 +112,27 @@ class EIPConnection(OpenVPNConnection): private method for disconnecting """ if self.subp is not None: + logger.debug('disconnecting...') self.subp.terminate() self.subp = None - # XXX signal state changes! :) - def _is_alive(self): - """ - don't know yet - """ - pass + #def _is_alive(self): + #""" + #don't know yet + #""" + #pass def _connect(self): """ entry point for connection cascade methods. """ - #conn_result = ConState.DISCONNECTED try: conn_result = self._try_connection() except eip_exceptions.UnrecoverableError as except_msg: logger.error("FATAL: %s" % unicode(except_msg)) conn_result = self.status.UNRECOVERABLE + + # XXX enqueue exceptions themselves instead? except Exception as except_msg: self.error_queue.append(except_msg) logger.error("Failed Connection: %s" % diff --git a/src/leap/eip/exceptions.py b/src/leap/eip/exceptions.py index 19a0e707..a30cd2a6 100644 --- a/src/leap/eip/exceptions.py +++ b/src/leap/eip/exceptions.py @@ -1,68 +1,72 @@ -class EIPNoCommandError(Exception): - pass - - -class ConnectionError(Exception): - """ - generic connection error - """ - pass - - class EIPClientError(Exception): """ base EIPClient exception """ - def __str__(self): - if len(self.args) >= 1: - return repr(self.args[0]) - else: - return ConnectionError + # Should inherit from LeapException + # and move basic attrs there + critical = False + + #def __str__(self): + #if len(self.args) >= 1: + #return repr(self.args[0]) + #else: + #return ConnectionError -class UnrecoverableError(EIPClientError): +class CriticalError(EIPClientError): """ we cannot do anything about it, sorry """ - # XXX we should catch this and raise - # to qtland, so we emit signal - # to translate whatever kind of error - # to user-friendly msg in dialog. - pass + critical = True -class MissingSocketError(Exception): +class EIPNoPolkitAuthAgentAvailable(CriticalError): + message = "No polkit authentication agent could be found" + usermessage = ("We could not find any authentication " + "agent in your system.
" + "Make sure you have " + "polkit-gnome-authentication-agent-1 " + "running and try again.") + +# Errors needing some work + + +class EIPNoPkexecAvailable(Exception): pass -class ConnectionRefusedError(Exception): +class EIPInitNoProviderError(Exception): pass -class EIPNoPkexecAvailable(Exception): +class EIPInitBadProviderError(Exception): pass -class EIPNoPolkitAuthAgentAvailable(Exception): +class EIPInitNoKeyFileError(Exception): pass -class EIPInitNoProviderError(Exception): +class EIPInitBadKeyFilePermError(Exception): pass -class EIPInitBadProviderError(Exception): +class EIPNoCommandError(Exception): pass +# Errors that probably we don't need anymore -class EIPInitNoKeyFileError(Exception): + +class MissingSocketError(Exception): pass -class EIPInitBadKeyFilePermError(Exception): +class ConnectionRefusedError(Exception): pass + + class EIPMissingDefaultProvider(Exception): pass diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 48252e10..4a6a495a 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -22,7 +22,6 @@ class OpenVPNConnection(Connection): All related to invocation of the openvpn binary """ - # Connection Methods def __init__(self, config_file=None, watcher_cb=None, @@ -45,7 +44,6 @@ to be triggered for each one of them. """ logger.debug('init openvpn connection') self.debug = debug - #print('conductor:%s' % debug) self.config_file = config_file self.watcher_cb = watcher_cb @@ -58,15 +56,18 @@ to be triggered for each one of them. self.port = None self.proto = None + ################################## # XXX move all error messages # into a more encapsulated object. self.missing_pkexec = False self.missing_auth_agent = False + self.bad_keyfile_perms = False self.missing_vpn_keyfile = False self.missing_provider = False self.missing_definition = False self.bad_provider = False + ################################# #XXX workaround for signaling #the ui that we don't know how to @@ -78,9 +79,6 @@ to be triggered for each one of them. # XXX get autostart from config self.autostart = True - #self._get_or_create_config() - self._set_ovpn_command() - self._check_vpn_keys() # # management init methods @@ -96,6 +94,11 @@ to be triggered for each one of them. self.port = port self.password = password + def run_openvpn_checks(self): + logger.debug('running openvpn checks') + self._set_ovpn_command() + self._check_vpn_keys() + def _set_ovpn_command(self): # XXX check also for command-line --command flag try: @@ -103,10 +106,13 @@ to be triggered for each one of them. debug=self.debug) except eip_exceptions.EIPNoPolkitAuthAgentAvailable: command = args = None + # XXX deprecate self.missing_auth_agent = True + raise except eip_exceptions.EIPNoPkexecAvailable: command = args = None self.missing_pkexec = True + raise # XXX if not command, signal error. self.command = command -- cgit v1.2.3 From 8148bc9c8c113c41fcb18b397669b1f13447c653 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 6 Sep 2012 02:27:04 +0900 Subject: more generic error handler in EipConductorAppMixin documentation of the Exception Hierarchy and attributes. also a bit of general cleanup around error handling in conductor. Hopefully to be polished an abstracted to leap.base with time. not all errors are converted (and the old with_errors/ignoring errors) are still there, but we should be using this style of handlers from now on. wrapping up with this pseudo-feature for now. as we work on individual features we can mimick the exceptions that are working. --- src/leap/baseapp/constants.py | 5 ++ src/leap/baseapp/dialogs.py | 19 +++++- src/leap/baseapp/eip.py | 150 +++++++++++++++++++++-------------------- src/leap/baseapp/leap_app.py | 2 +- src/leap/baseapp/log.py | 3 +- src/leap/baseapp/mainwindow.py | 20 +++--- src/leap/baseapp/systray.py | 2 +- src/leap/eip/eipconnection.py | 4 +- src/leap/eip/exceptions.py | 97 +++++++++++++++++++------- 9 files changed, 190 insertions(+), 112 deletions(-) diff --git a/src/leap/baseapp/constants.py b/src/leap/baseapp/constants.py index 763df23b..e312be21 100644 --- a/src/leap/baseapp/constants.py +++ b/src/leap/baseapp/constants.py @@ -1 +1,6 @@ +# This timer used for polling vpn manager state. + +# XXX what is an optimum polling interval? +# too little will be overkill, too much will +# miss transition states. TIMER_MILLISECONDS = 250.0 diff --git a/src/leap/baseapp/dialogs.py b/src/leap/baseapp/dialogs.py index d37a234c..d4acb09d 100644 --- a/src/leap/baseapp/dialogs.py +++ b/src/leap/baseapp/dialogs.py @@ -1,14 +1,25 @@ +import logging + from PyQt4.QtGui import (QDialog, QFrame, QPushButton, QLabel, QMessageBox) +logger = logging.getLogger(name=__name__) + class ErrorDialog(QDialog): - def __init__(self, parent=None): + def __init__(self, parent=None, errtype=None, msg=None, label=None): super(ErrorDialog, self).__init__(parent) frameStyle = QFrame.Sunken | QFrame.Panel self.warningLabel = QLabel() self.warningLabel.setFrameStyle(frameStyle) self.warningButton = QPushButton("QMessageBox.&warning()") + if msg is not None: + self.msg = msg + if label is not None: + self.label = label + if errtype == "critical": + self.criticalMessage(self.msg, self.label) + def warningMessage(self, msg, label): msgBox = QMessageBox(QMessageBox.Warning, "QMessageBox.warning()", msg, @@ -26,5 +37,11 @@ class ErrorDialog(QDialog): QMessageBox.NoButton, self) msgBox.addButton("&Ok", QMessageBox.AcceptRole) msgBox.exec_() + + # It's critical, so we exit. + # We should better emit a signal and connect it + # with the proper shutdownAndQuit method, but + # this suffices for now. + logger.info('Quitting') import sys sys.exit() diff --git a/src/leap/baseapp/eip.py b/src/leap/baseapp/eip.py index dd88b7f5..afdb7adc 100644 --- a/src/leap/baseapp/eip.py +++ b/src/leap/baseapp/eip.py @@ -11,8 +11,7 @@ from leap.eip.eipconnection import EIPConnection logger = logging.getLogger(name=__name__) -class EIPConductorApp(object): - # XXX EIPConductorMixin ? +class EIPConductorAppMixin(object): """ initializes an instance of EIPConnection, gathers errors, and passes status-change signals @@ -52,86 +51,90 @@ class EIPConductorApp(object): lambda: self.start_or_stopVPN()) def error_check(self): + """ + consumes the conductor error queue. + pops errors, and acts accordingly (launching user dialogs). + """ logger.debug('error check') ##################################### # XXX refactor in progress (by #504) + errq = self.conductor.error_queue while errq.qsize() != 0: logger.debug('%s errors left in conductor queue', errq.qsize()) error = errq.get() + + # redundant log, debugging the loop. logger.error('%s: %s', error.__class__.__name__, error.message) if issubclass(error.__class__, eip_exceptions.EIPClientError): - if error.critical: - logger.critical(error.message) - logger.error('quitting') - - # XXX - # check headless = False before - # launching dialog. - # (for Qt tests) - - dialog = ErrorDialog() - if getattr(error, 'usermessage', None): - message = error.usermessage - else: - message = error.message - dialog.criticalMessage(message, 'error') - else: - logger.exception(error.message) + self.handle_eip_error(error) + else: + # This is not quite working. FIXME import traceback traceback.print_exc() raise error - if self.conductor.missing_definition is True: - dialog = ErrorDialog() - dialog.criticalMessage( - 'The default ' - 'definition.json file cannot be found', - 'error') + if error.failfirst is True: + break - if self.conductor.missing_provider is True: - dialog = ErrorDialog() - dialog.criticalMessage( - 'Missing provider. Add a remote_ip entry ' - 'under section [provider] in eip.cfg', - 'error') + ############################################# + # old errors to check + # write test for them and them remove + # their corpses from here. - if self.conductor.missing_vpn_keyfile is True: - dialog = ErrorDialog() - dialog.criticalMessage( - 'Could not find the vpn keys file', - 'error') + #if self.conductor.missing_vpn_keyfile is True: + #dialog = ErrorDialog() + #dialog.criticalMessage( + #'Could not find the vpn keys file', + #'error') - # ... btw, review pending. - # os.kill of subprocess fails if we have - # some of this errors. + #if self.conductor.bad_keyfile_perms is True: + #dialog = ErrorDialog() + #dialog.criticalMessage( + #'The vpn keys file has bad permissions', + #'error') - # deprecated. - # get something alike. - #if self.conductor.bad_provider is True: + # deprecated. configchecker takes care of that. + #if self.conductor.missing_definition is True: #dialog = ErrorDialog() #dialog.criticalMessage( - #'Bad provider entry. Check that remote_ip entry ' - #'has an IP under section [provider] in eip.cfg', + #'The default ' + #'definition.json file cannot be found', #'error') - if self.conductor.bad_keyfile_perms is True: - dialog = ErrorDialog() - dialog.criticalMessage( - 'The vpn keys file has bad permissions', - 'error') + def handle_eip_error(self, error): + """ + check severity and launches + dialogs informing user about the errors. + in the future we plan to derive errors to + our log viewer. + """ - if self.conductor.missing_pkexec is True: + if getattr(error, 'usermessage', None): + message = error.usermessage + else: + message = error.message + + # XXX + # check headless = False before + # launching dialog. + # (so Qt tests can assert stuff) + + if error.critical: + logger.critical(error.message) + #critical error (non recoverable), + #we give user some info and quit. + #(critical error dialog will exit app) + ErrorDialog(errtype="critical", + msg=message, + label="critical error") + + else: dialog = ErrorDialog() - dialog.warningMessage( - 'We could not find pkexec in your ' - 'system.
Do you want to try ' - 'setuid workaround? ' - '(DOES NOTHING YET)', - 'error') + dialog.warningMessage(message, 'error') @QtCore.pyqtSlot() def statusUpdate(self): @@ -188,29 +191,30 @@ class EIPConductorApp(object): """ stub for running child process with vpn """ + if self.conductor.has_errors(): + logger.debug('not starting vpn; conductor has errors') + if self.eip_service_started is False: try: self.conductor.connect() - # XXX move this to error queue - except eip_exceptions.EIPNoCommandError: - dialog = ErrorDialog() - dialog.warningMessage( - 'No suitable openvpn command found. ' - '
(Might be a permissions problem)', - 'error') - if self.debugmode: - self.startStopButton.setText('&Disconnect') - self.eip_service_started = True - # XXX what is optimum polling interval? - # too little is overkill, too much - # will miss transition states.. + except eip_exceptions.EIPNoCommandError as exc: + self.handle_eip_error(exc) + + except Exception as err: + # raise generic exception (Bad Thing Happened?) + logger.exception(err) + else: + # no errors, so go on. + if self.debugmode: + self.startStopButton.setText('&Disconnect') + self.eip_service_started = True - # XXX decouple! (timer is init by icons class). - # should bring it here? - # to its own class? + # XXX decouple! (timer is init by icons class). + # we could bring Timer Init to this Mixin + # or to its own Mixin. + self.timer.start(constants.TIMER_MILLISECONDS) - self.timer.start(constants.TIMER_MILLISECONDS) return if self.eip_service_started is True: diff --git a/src/leap/baseapp/leap_app.py b/src/leap/baseapp/leap_app.py index 85644360..f91b2329 100644 --- a/src/leap/baseapp/leap_app.py +++ b/src/leap/baseapp/leap_app.py @@ -7,7 +7,7 @@ from leap.gui import mainwindow_rc logger = logging.getLogger(name=__name__) -class MainWindow(object): +class MainWindowMixin(object): """ create the main window for leap app diff --git a/src/leap/baseapp/log.py b/src/leap/baseapp/log.py index 3580e987..8a7f81c3 100644 --- a/src/leap/baseapp/log.py +++ b/src/leap/baseapp/log.py @@ -6,7 +6,7 @@ from PyQt4 import QtCore vpnlogger = logging.getLogger('leap.openvpn') -class LogPane(object): +class LogPaneMixin(object): """ a simple log pane that writes new lines as they come @@ -22,7 +22,6 @@ class LogPane(object): self.logbrowser = QtGui.QTextBrowser() startStopButton = QtGui.QPushButton("&Connect") - #startStopButton.clicked.connect(self.start_or_stopVPN) self.startStopButton = startStopButton logging_layout.addWidget(self.logbrowser) diff --git a/src/leap/baseapp/mainwindow.py b/src/leap/baseapp/mainwindow.py index e87f5844..10b23d9a 100644 --- a/src/leap/baseapp/mainwindow.py +++ b/src/leap/baseapp/mainwindow.py @@ -5,18 +5,18 @@ import logging from PyQt4 import QtCore from PyQt4 import QtGui -from leap.baseapp.eip import EIPConductorApp -from leap.baseapp.log import LogPane -from leap.baseapp.systray import StatusAwareTrayIcon -from leap.baseapp.leap_app import MainWindow +from leap.baseapp.eip import EIPConductorAppMixin +from leap.baseapp.log import LogPaneMixin +from leap.baseapp.systray import StatusAwareTrayIconMixin +from leap.baseapp.leap_app import MainWindowMixin logger = logging.getLogger(name=__name__) class LeapWindow(QtGui.QMainWindow, - MainWindow, EIPConductorApp, - StatusAwareTrayIcon, - LogPane): + MainWindowMixin, EIPConductorAppMixin, + StatusAwareTrayIconMixin, + LogPaneMixin): """ main window for the leap app. Initializes all of its base classes @@ -34,9 +34,9 @@ class LeapWindow(QtGui.QMainWindow, super(LeapWindow, self).__init__() if self.debugmode: self.createLogBrowser() - EIPConductorApp.__init__(self, opts=opts) - StatusAwareTrayIcon.__init__(self) - MainWindow.__init__(self) + EIPConductorAppMixin.__init__(self, opts=opts) + StatusAwareTrayIconMixin.__init__(self) + MainWindowMixin.__init__(self) # bind signals # XXX move to parent classes init?? diff --git a/src/leap/baseapp/systray.py b/src/leap/baseapp/systray.py index f3832473..c696ee74 100644 --- a/src/leap/baseapp/systray.py +++ b/src/leap/baseapp/systray.py @@ -4,7 +4,7 @@ from PyQt4 import QtGui from leap.gui import mainwindow_rc -class StatusAwareTrayIcon(object): +class StatusAwareTrayIconMixin(object): """ a mix of several functions needed to create a systray and make it diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index e090f9a7..5c54a986 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -24,7 +24,6 @@ class EIPConnection(OpenVPNConnection): self.settingsfile = kwargs.get('settingsfile', None) self.logfile = kwargs.get('logfile', None) - # XXX USE THIS self.error_queue = Queue.Queue() status_signals = kwargs.pop('status_signals', None) @@ -33,6 +32,9 @@ class EIPConnection(OpenVPNConnection): super(EIPConnection, self).__init__(*args, **kwargs) + def has_errors(self): + return True if self.error_queue.qsize != 0 else True + def run_checks(self, skip_download=False): """ run all eip checks previous to attempting a connection diff --git a/src/leap/eip/exceptions.py b/src/leap/eip/exceptions.py index a30cd2a6..3c8f6afb 100644 --- a/src/leap/eip/exceptions.py +++ b/src/leap/eip/exceptions.py @@ -1,23 +1,60 @@ +""" +Generic error hierarchy +Leap/EIP exceptions used for exception handling, +logging, and notifying user of errors +during leap operation. + +Exception hierarchy +------------------- +All EIP Errors must inherit from EIPClientError (note: move that to +a more generic LEAPClientBaseError). + +Exception attributes and their meaning/uses +------------------------------------------- + +* critical: if True, will abort execution prematurely, + after attempting any cleaning + action. + +* failfirst: breaks any error_check loop that is examining + the error queue. + +* message: the message that will be used in the __repr__ of the exception. + +* usermessage: the message that will be passed to user in ErrorDialogs + in Qt-land. + +TODO: + +* EIPClientError: + Should inherit from LeapException + and move basic attrs there + +* gettext / i18n for user messages. + +""" + + class EIPClientError(Exception): """ base EIPClient exception """ - # Should inherit from LeapException - # and move basic attrs there critical = False - #def __str__(self): - #if len(self.args) >= 1: - #return repr(self.args[0]) - #else: - #return ConnectionError - class CriticalError(EIPClientError): """ we cannot do anything about it, sorry """ critical = True + failfirst = True + + +class Warning(EIPClientError): + """ + just that, warnings + """ + pass class EIPNoPolkitAuthAgentAvailable(CriticalError): @@ -28,33 +65,53 @@ class EIPNoPolkitAuthAgentAvailable(CriticalError): "polkit-gnome-authentication-agent-1 " "running and try again.") -# Errors needing some work +class EIPNoPkexecAvailable(Warning): + message = "No pkexec binary found" + usermessage = ("We could not find pkexec in your " + "system.
Do you want to try " + "setuid workaround? " + "(DOES NOTHING YET)") + failfirst = True -class EIPNoPkexecAvailable(Exception): - pass +class EIPNoCommandError(EIPClientError): + message = "no suitable openvpn command found" + usermessage = ("No suitable openvpn command found. " + "
(Might be a permissions problem)") -class EIPInitNoProviderError(Exception): - pass +# +# errors still needing some love +# + +class EIPInitNoKeyFileError(CriticalError): + message = "No vpn keys found in the expected path" + usermessage = "We could not find your eip certs in the expected path" -class EIPInitBadProviderError(Exception): + +class EIPInitBadKeyFilePermError(Warning): + # I don't know if we should be telling user or not, + # we try to fix permissions and should only re-raise + # if permission check failed. pass -class EIPInitNoKeyFileError(Exception): +class EIPInitNoProviderError(EIPClientError): pass -class EIPInitBadKeyFilePermError(Exception): +class EIPInitBadProviderError(EIPClientError): pass -class EIPNoCommandError(Exception): +class EIPConfigurationError(EIPClientError): pass +# # Errors that probably we don't need anymore +# chase down for them and check. +# class MissingSocketError(Exception): @@ -65,11 +122,5 @@ class ConnectionRefusedError(Exception): pass - - class EIPMissingDefaultProvider(Exception): pass - - -class EIPConfigurationError(Exception): - pass -- cgit v1.2.3