Implement server diagnostics

This outputs a handy summary of your server configuration and version
strings etc, which is useful for bug reporting.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
This commit is contained in:
Sebastian Spaeth 2011-06-30 15:18:03 +02:00
parent 6b3f429c81
commit 3885acf87d
4 changed files with 89 additions and 15 deletions

View File

@ -13,6 +13,9 @@ others.
New Features New Features
------------ ------------
* add a --info command line switch that outputs useful information about
the server and the configuration for all enabled accounts.
Changes Changes
------- -------

View File

@ -82,6 +82,9 @@ class Account(CustomConfig.ConfigHelperMixin):
def __str__(self): def __str__(self):
return self.name return self.name
def getaccountmeta(self):
return os.path.join(self.metadatadir, 'Account-' + self.name)
def getsection(self): def getsection(self):
return 'Account ' + self.getname() return 'Account ' + self.getname()
@ -154,8 +157,16 @@ class Account(CustomConfig.ConfigHelperMixin):
self.quicknum = 0 self.quicknum = 0
return 1 return 1
return 0 return 0
def serverdiagnostics(self):
"""Output diagnostics for all involved repositories"""
remote_repo = Repository(self, 'remote')
local_repo = Repository(self, 'local')
#status_repo = Repository(self, 'status')
self.ui.serverdiagnostics(remote_repo, 'Remote')
self.ui.serverdiagnostics(local_repo, 'Local')
#self.ui.serverdiagnostics(statusrepos, 'Status')
class SyncableAccount(Account): class SyncableAccount(Account):
"""A syncable email account connecting 2 repositories """A syncable email account connecting 2 repositories
@ -233,9 +244,6 @@ class SyncableAccount(Account):
if looping and self.sleeper() >= 2: if looping and self.sleeper() >= 2:
looping = 0 looping = 0
def getaccountmeta(self):
return os.path.join(self.metadatadir, 'Account-' + self.name)
def sync(self): def sync(self):
"""Synchronize the account once, then return """Synchronize the account once, then return

View File

@ -44,9 +44,12 @@ class OfflineImap:
""" """
def run(self): def run(self):
"""Parse the commandline and invoke everything""" """Parse the commandline and invoke everything"""
# next line also sets self.config # next line also sets self.config and self.ui
options = self.parse_cmd_options() options, args = self.parse_cmd_options()
self.sync(options) if options.diagnostics:
self.serverdiagnostics(options)
else:
self.sync(options)
def parse_cmd_options(self): def parse_cmd_options(self):
parser = OptionParser(version=offlineimap.__version__, parser = OptionParser(version=offlineimap.__version__,
@ -143,6 +146,13 @@ class OfflineImap:
"not usable. Possible interface choices are: %s " % "not usable. Possible interface choices are: %s " %
", ".join(UI_LIST.keys())) ", ".join(UI_LIST.keys()))
parser.add_option("--info",
action="store_true", dest="diagnostics",
default=False,
help="Output information on the configured email repositories"
". Useful for debugging and bug reporting. Use in conjunction wit"
"h the -a option to limit the output to a single account")
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
#read in configuration file #read in configuration file
@ -265,7 +275,7 @@ class OfflineImap:
config.getdefaultint('Repository ' + reposname, config.getdefaultint('Repository ' + reposname,
'maxconnections', 2)) 'maxconnections', 2))
self.config = config self.config = config
return options return (options, args)
def sync(self, options): def sync(self, options):
"""Invoke the correct single/multithread syncing """Invoke the correct single/multithread syncing
@ -276,7 +286,7 @@ class OfflineImap:
# die immediately # die immediately
self.ui.terminate(errormsg="terminating...") self.ui.terminate(errormsg="terminating...")
signal.signal(signal.SIGTERM, sigterm_handler) signal.signal(signal.SIGTERM, sigterm_handler)
try: try:
pidfd = open(self.config.getmetadatadir() + "/pid", "w") pidfd = open(self.config.getmetadatadir() + "/pid", "w")
pidfd.write(str(os.getpid()) + "\n") pidfd.write(str(os.getpid()) + "\n")
@ -305,11 +315,7 @@ class OfflineImap:
self.ui.terminate(1, errormsg = errormsg) self.ui.terminate(1, errormsg = errormsg)
if account not in syncaccounts: if account not in syncaccounts:
syncaccounts.append(account) syncaccounts.append(account)
server = None
remoterepos = None
localrepos = None
def sig_handler(sig, frame): def sig_handler(sig, frame):
if sig == signal.SIGUSR1 or sig == signal.SIGHUP: if sig == signal.SIGUSR1 or sig == signal.SIGHUP:
# tell each account to stop sleeping # tell each account to stop sleeping
@ -370,3 +376,13 @@ class OfflineImap:
accountname) accountname)
threading.currentThread().name = "Account sync %s" % accountname threading.currentThread().name = "Account sync %s" % accountname
account.syncrunner() account.syncrunner()
def serverdiagnostics(self, options):
activeaccounts = self.config.get("general", "accounts")
if options.accounts:
activeaccounts = options.accounts
activeaccounts = activeaccounts.split(",")
allaccounts = accounts.AccountListGenerator(self.config)
for account in allaccounts:
if account.name not in activeaccounts: continue
account.serverdiagnostics()

View File

@ -322,6 +322,53 @@ class UIBase:
s._msg("Deleting flag %s from %d messages on %s" % \ s._msg("Deleting flag %s from %d messages on %s" % \
(", ".join(flags), len(uidlist), dest)) (", ".join(flags), len(uidlist), dest))
def serverdiagnostics(self, repository, type):
"""Connect to repository and output useful information for debugging"""
conn = None
self._msg("%s repository '%s': type '%s'" % (type, repository.name,
self.getnicename(repository)))
try:
if hasattr(repository, 'gethost'): # IMAP
self._msg("Host: %s Port: %s SSL: %s" % (repository.gethost(),
repository.getport(),
repository.getssl()))
try:
conn = repository.imapserver.acquireconnection()
except OfflineImapError, e:
self._msg("Failed to connect. Reason %s" % e)
else:
if 'ID' in conn.capabilities:
self._msg("Server supports ID extension.")
#TODO: Debug and make below working, it hangs Gmail
#res_type, response = conn.id((
# 'name', offlineimap.__productname__,
# 'version', offlineimap.__version__))
#self._msg("Server ID: %s %s" % (res_type, response[0]))
self._msg("Server welcome string: %s" % str(conn.welcome))
self._msg("Server capabilities: %s\n" % str(conn.capabilities))
repository.imapserver.releaseconnection(conn)
if type != 'Status':
folderfilter = repository.getconf('folderfilter', None)
if folderfilter:
self._msg("folderfilter= %s\n" % folderfilter)
folderincludes = repository.getconf('folderincludes', None)
if folderincludes:
self._msg("folderincludes= %s\n" % folderincludes)
nametrans = repository.getconf('nametrans', None)
if nametrans:
self._msg("nametrans= %s\n" % nametrans)
folders = repository.getfolders()
foldernames = [(f.name, f.getvisiblename()) for f in folders]
folders = []
for name, visiblename in foldernames:
if name == visiblename: folders.append(name)
else: folders.append("%s -> %s" % (name, visiblename))
self._msg("Folderlist: %s\n" % str(folders))
finally:
if conn: #release any existing IMAP connection
repository.imapserver.close()
################################################## Threads ################################################## Threads
def getThreadDebugLog(s, thread): def getThreadDebugLog(s, thread):