/lib/galaxy/webapps/reports/controllers/system.py

https://bitbucket.org/cistrome/cistrome-harvard/ · Python · 203 lines · 192 code · 3 blank · 8 comment · 43 complexity · 82bdb4cb61c8ca6757fba511e2a79f8c MD5 · raw file

  1. import os
  2. import logging
  3. from datetime import datetime, timedelta
  4. from galaxy.web.base.controller import BaseUIController, web
  5. from decimal import Decimal
  6. from galaxy import model, util
  7. from galaxy.model.orm import and_, desc, eagerload
  8. log = logging.getLogger( __name__ )
  9. class System( BaseUIController ):
  10. @web.expose
  11. def index( self, trans, **kwd ):
  12. params = util.Params( kwd )
  13. message = ''
  14. if params.userless_histories_days:
  15. userless_histories_days = params.userless_histories_days
  16. else:
  17. userless_histories_days = '60'
  18. if params.deleted_histories_days:
  19. deleted_histories_days = params.deleted_histories_days
  20. else:
  21. deleted_histories_days = '60'
  22. if params.deleted_datasets_days:
  23. deleted_datasets_days = params.deleted_datasets_days
  24. else:
  25. deleted_datasets_days = '60'
  26. file_path, disk_usage, datasets, file_size_str = self.disk_usage( trans, **kwd )
  27. if 'action' in kwd:
  28. if kwd['action'] == "userless_histories":
  29. userless_histories_days, message = self.userless_histories( trans, **kwd )
  30. elif kwd['action'] == "deleted_histories":
  31. deleted_histories_days, message = self.deleted_histories( trans, **kwd )
  32. elif kwd['action'] == "deleted_datasets":
  33. deleted_datasets_days, message = self.deleted_datasets( trans, **kwd )
  34. return trans.fill_template( '/webapps/reports/system.mako',
  35. file_path=file_path,
  36. disk_usage=disk_usage,
  37. datasets=datasets,
  38. file_size_str=file_size_str,
  39. userless_histories_days=userless_histories_days,
  40. deleted_histories_days=deleted_histories_days,
  41. deleted_datasets_days=deleted_datasets_days,
  42. message=message,
  43. nice_size=nice_size )
  44. def userless_histories( self, trans, **kwd ):
  45. """The number of userless histories and associated datasets that have not been updated for the specified number of days."""
  46. params = util.Params( kwd )
  47. message = ''
  48. if params.userless_histories_days:
  49. userless_histories_days = int( params.userless_histories_days )
  50. cutoff_time = datetime.utcnow() - timedelta( days=userless_histories_days )
  51. history_count = 0
  52. dataset_count = 0
  53. for history in trans.sa_session.query( model.History ) \
  54. .filter( and_( model.History.table.c.user_id == None,
  55. model.History.table.c.deleted == True,
  56. model.History.table.c.update_time < cutoff_time ) ):
  57. for dataset in history.datasets:
  58. if not dataset.deleted:
  59. dataset_count += 1
  60. history_count += 1
  61. message = "%d userless histories ( including a total of %d datasets ) have not been updated for at least %d days." %( history_count, dataset_count, userless_histories_days )
  62. else:
  63. message = "Enter the number of days."
  64. return str( userless_histories_days ), message
  65. def deleted_histories( self, trans, **kwd ):
  66. """
  67. The number of histories that were deleted more than the specified number of days ago, but have not yet been purged.
  68. Also included is the number of datasets associated with the histories.
  69. """
  70. params = util.Params( kwd )
  71. message = ''
  72. if params.deleted_histories_days:
  73. deleted_histories_days = int( params.deleted_histories_days )
  74. cutoff_time = datetime.utcnow() - timedelta( days=deleted_histories_days )
  75. history_count = 0
  76. dataset_count = 0
  77. disk_space = 0
  78. histories = trans.sa_session.query( model.History ) \
  79. .filter( and_( model.History.table.c.deleted == True,
  80. model.History.table.c.purged == False,
  81. model.History.table.c.update_time < cutoff_time ) ) \
  82. .options( eagerload( 'datasets' ) )
  83. for history in histories:
  84. for hda in history.datasets:
  85. if not hda.dataset.purged:
  86. dataset_count += 1
  87. try:
  88. disk_space += hda.dataset.file_size
  89. except:
  90. pass
  91. history_count += 1
  92. message = "%d histories ( including a total of %d datasets ) were deleted more than %d days ago, but have not yet been purged, " \
  93. "disk space: %s." % ( history_count, dataset_count, deleted_histories_days, nice_size( disk_space, True ) )
  94. else:
  95. message = "Enter the number of days."
  96. return str( deleted_histories_days ), message
  97. def deleted_datasets( self, trans, **kwd ):
  98. """The number of datasets that were deleted more than the specified number of days ago, but have not yet been purged."""
  99. params = util.Params( kwd )
  100. message = ''
  101. if params.deleted_datasets_days:
  102. deleted_datasets_days = int( params.deleted_datasets_days )
  103. cutoff_time = datetime.utcnow() - timedelta( days=deleted_datasets_days )
  104. dataset_count = 0
  105. disk_space = 0
  106. for dataset in trans.sa_session.query( model.Dataset ) \
  107. .filter( and_( model.Dataset.table.c.deleted == True,
  108. model.Dataset.table.c.purged == False,
  109. model.Dataset.table.c.update_time < cutoff_time ) ):
  110. dataset_count += 1
  111. try:
  112. disk_space += dataset.file_size
  113. except:
  114. pass
  115. message = "%d datasets were deleted more than %d days ago, but have not yet been purged," \
  116. " disk space: %s." % ( dataset_count, deleted_datasets_days, nice_size( disk_space, True ))
  117. else:
  118. message = "Enter the number of days."
  119. return str( deleted_datasets_days ), message
  120. @web.expose
  121. def dataset_info( self, trans, **kwd ):
  122. params = util.Params( kwd )
  123. message = ''
  124. dataset = trans.sa_session.query( model.Dataset ).get( trans.security.decode_id( kwd.get( 'id', '' ) ) )
  125. # Get all associated hdas and lddas that use the same disk file.
  126. associated_hdas = trans.sa_session.query( trans.model.HistoryDatasetAssociation ) \
  127. .filter( and_( trans.model.HistoryDatasetAssociation.deleted == False,
  128. trans.model.HistoryDatasetAssociation.dataset_id == dataset.id ) ) \
  129. .all()
  130. associated_lddas = trans.sa_session.query( trans.model.LibraryDatasetDatasetAssociation ) \
  131. .filter( and_( trans.model.LibraryDatasetDatasetAssociation.deleted == False,
  132. trans.model.LibraryDatasetDatasetAssociation.dataset_id == dataset.id ) ) \
  133. .all()
  134. return trans.fill_template( '/webapps/reports/dataset_info.mako',
  135. dataset=dataset,
  136. associated_hdas=associated_hdas,
  137. associated_lddas=associated_lddas,
  138. message=message )
  139. def get_disk_usage( self, file_path ):
  140. df_cmd = 'df -h ' + file_path
  141. is_sym_link = os.path.islink( file_path )
  142. file_system = disk_size = disk_used = disk_avail = disk_cap_pct = mount = None
  143. df_file = os.popen( df_cmd )
  144. while True:
  145. df_line = df_file.readline()
  146. df_line = df_line.strip()
  147. if df_line:
  148. df_line = df_line.lower()
  149. if 'filesystem' in df_line or 'proc' in df_line:
  150. continue
  151. elif is_sym_link:
  152. if ':' in df_line and '/' in df_line:
  153. mount = df_line
  154. else:
  155. try:
  156. disk_size, disk_used, disk_avail, disk_cap_pct, file_system = df_line.split()
  157. break
  158. except:
  159. pass
  160. else:
  161. try:
  162. file_system, disk_size, disk_used, disk_avail, disk_cap_pct, mount = df_line.split()
  163. break
  164. except:
  165. pass
  166. else:
  167. break # EOF
  168. df_file.close()
  169. return ( file_system, disk_size, disk_used, disk_avail, disk_cap_pct, mount )
  170. @web.expose
  171. def disk_usage( self, trans, **kwd ):
  172. file_path = trans.app.config.file_path
  173. disk_usage = self.get_disk_usage( file_path )
  174. min_file_size = 2**32 # 4 Gb
  175. file_size_str = nice_size( min_file_size )
  176. datasets = trans.sa_session.query( model.Dataset ) \
  177. .filter( and_( model.Dataset.table.c.purged == False,
  178. model.Dataset.table.c.file_size > min_file_size ) ) \
  179. .order_by( desc( model.Dataset.table.c.file_size ) )
  180. return file_path, disk_usage, datasets, file_size_str
  181. def nice_size(size, include_bytes=False):
  182. """Returns a readably formatted string with the size"""
  183. niced = False
  184. nice_string = "%s bytes" % size
  185. try:
  186. nsize = Decimal(size)
  187. for x in ['bytes','KB','MB','GB']:
  188. if nsize.compare(Decimal("1024.0")) == Decimal("-1"):
  189. nice_string = "%3.1f %s" % (nsize, x)
  190. niced = True
  191. break
  192. nsize /= Decimal("1024.0")
  193. if not niced:
  194. nice_string = "%3.1f %s" % (nsize, 'TB')
  195. niced = True
  196. if include_bytes and x != 'bytes':
  197. nice_string = "%s (%s bytes)" % (nice_string, size)
  198. except:
  199. pass
  200. return nice_string