kopano-search-xapian-compact.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. #!/usr/bin/env python
  2. import fcntl
  3. import glob
  4. import grp
  5. import os
  6. import pwd
  7. import shutil
  8. import subprocess
  9. import sys
  10. import traceback
  11. import kopano
  12. """
  13. uses 'xapian-compact' to compact given database(s), specified using store GUID(s),
  14. or all databases found in the 'index_path' directory specified in search.cfg.
  15. """
  16. # TODO: purge removed users (stores)
  17. def _default(name):
  18. if name == '':
  19. return 'root'
  20. elif name is None:
  21. return 'kopano'
  22. else:
  23. return name
  24. def main():
  25. parser = kopano.parser() # select common cmd-line options
  26. options, args = parser.parse_args()
  27. config = kopano.Config(None, service='search', options=options)
  28. server = kopano.Server(options, config)
  29. # get index_path, run_as_user, run_as_group from search.cfg
  30. search_config = server.config
  31. if not search_config:
  32. print('ERROR: search config is not available')
  33. sys.exit(1)
  34. index_path = _default(search_config.get('index_path'))
  35. search_user = _default(search_config.get('run_as_user'))
  36. uid = pwd.getpwnam(search_user).pw_uid
  37. search_group = _default(search_config.get('run_as_group'))
  38. gid = grp.getgrnam(search_group).gr_gid
  39. if (uid, gid) != (os.getuid(), os.getgid()):
  40. print('ERROR: script should run under user/group as specified in search.cfg')
  41. sys.exit(1)
  42. cleaned_args = [arg for arg in sys.argv[:1] if not arg.startswith('-')]
  43. # find database(s) corresponding to given store GUID(s)
  44. dbpaths = []
  45. if len(cleaned_args) > 1:
  46. for arg in cleaned_args[1:]:
  47. dbpath = os.path.join(index_path, server.guid+'-'+arg)
  48. dbpaths.append(dbpath)
  49. else: # otherwise, select all databases for compaction
  50. dbpaths = []
  51. for path in glob.glob(os.path.join(index_path, server.guid+'-*')):
  52. if not path.endswith('.lock'):
  53. dbpaths.append(path)
  54. # loop and compact
  55. count = len(dbpaths)
  56. errors = 0
  57. for dbpath in dbpaths:
  58. try:
  59. if os.path.isdir(dbpath):
  60. if len(dbpath.split('-')) > 1:
  61. store = dbpath.split('-')[1]
  62. try:
  63. server.store(store)
  64. except kopano.NotFoundError as e:
  65. print('deleting:', dbpath)
  66. shutil.rmtree(dbpath)
  67. os.remove('%s.lock' % dbpath)
  68. continue
  69. print('compact:', dbpath)
  70. with open('%s.lock' % dbpath, 'w') as lockfile: # do not index at the same time
  71. fcntl.flock(lockfile.fileno(), fcntl.LOCK_EX)
  72. shutil.rmtree(dbpath + '.compact', ignore_errors=True)
  73. subprocess.call(['xapian-compact', dbpath, dbpath + '.compact'])
  74. # quickly swap in compacted database
  75. shutil.move(dbpath, dbpath + '.old')
  76. shutil.move(dbpath + '.compact', dbpath)
  77. shutil.rmtree(dbpath + '.old')
  78. else:
  79. print('ERROR: no such database: %s', dbpath)
  80. errors += 1
  81. print
  82. except Exception as e:
  83. print('ERROR')
  84. traceback.print_exc(e)
  85. errors += 1
  86. # summarize
  87. print('done compacting (%d processed, %d errors)' % (count, errors))
  88. if __name__ == '__main__':
  89. main()