limit.py 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. #
  2. # Metrix++, Copyright 2009-2013, Metrix++ Project
  3. # Link: http://metrixplusplus.sourceforge.net
  4. #
  5. # This file is a part of Metrix++ Tool.
  6. #
  7. # Metrix++ is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation, version 3 of the License.
  10. #
  11. # Metrix++ is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with Metrix++. If not, see <http://www.gnu.org/licenses/>.
  18. #
  19. import logging
  20. import core.log
  21. import core.db.loader
  22. import core.db.post
  23. import core.db.utils
  24. import core.cout
  25. import core.warn
  26. import core.cmdparser
  27. import tools.utils
  28. import core.api
  29. class Tool(core.api.ITool):
  30. def run(self, tool_args):
  31. return main(tool_args)
  32. def main(tool_args):
  33. exit_code = 0
  34. log_plugin = core.log.Plugin()
  35. db_plugin = core.db.post.Plugin()
  36. warn_plugin = core.warn.Plugin()
  37. parser = core.cmdparser.MultiOptionParser(usage="Usage: %prog limit [options] -- [path 1] ... [path N]")
  38. log_plugin.declare_configuration(parser)
  39. db_plugin.declare_configuration(parser)
  40. warn_plugin.declare_configuration(parser)
  41. parser.add_option("--hotspots", "--hs", default=None, help="If not set (none), all exceeded limits are printed."
  42. " If set, exceeded limits are sorted (the worst is the first) and only first HOTSPOTS limits are printed."
  43. " [default: %default]", type=int)
  44. parser.add_option("--disable-suppressions", "--ds", action="store_true", default=False,
  45. help = "If not set (none), all suppressions are ignored"
  46. " and associated warnings are printed. [default: %default]")
  47. (options, args) = parser.parse_args(tool_args)
  48. log_plugin.configure(options)
  49. db_plugin.configure(options)
  50. warn_plugin.configure(options)
  51. hotspots = options.__dict__['hotspots']
  52. no_suppress = options.__dict__['disable_suppressions']
  53. loader_prev = core.db.loader.Loader()
  54. if db_plugin.dbfile_prev != None:
  55. loader_prev.open_database(db_plugin.dbfile_prev)
  56. loader = core.db.loader.Loader()
  57. loader.open_database(db_plugin.dbfile)
  58. warn_plugin.verify_namespaces(loader.iterate_namespace_names())
  59. for each in loader.iterate_namespace_names():
  60. warn_plugin.verify_fields(each, loader.get_namespace(each).iterate_field_names())
  61. # Check for versions consistency
  62. if db_plugin.dbfile_prev != None:
  63. tools.utils.check_db_metadata(loader, loader_prev)
  64. paths = None
  65. if len(args) == 0:
  66. paths = [""]
  67. else:
  68. paths = args
  69. # Try to optimise iterative change scans
  70. modified_file_ids = None
  71. if warn_plugin.mode != warn_plugin.MODE_ALL:
  72. modified_file_ids = get_list_of_modified_files(loader, loader_prev)
  73. for path in paths:
  74. path = tools.utils.preprocess_path(path)
  75. for limit in warn_plugin.iterate_limits():
  76. logging.info("Applying limit: " + str(limit))
  77. filters = [limit.filter]
  78. if modified_file_ids != None:
  79. filters.append(('file_id', 'IN', modified_file_ids))
  80. sort_by = None
  81. limit_by = None
  82. if hotspots != None:
  83. sort_by = limit.field
  84. if limit.type == "max":
  85. sort_by = "-" + sort_by
  86. limit_by = hotspots
  87. selected_data = loader.load_selected_data(limit.namespace,
  88. fields = [limit.field],
  89. path=path,
  90. filters = filters,
  91. sort_by=sort_by,
  92. limit_by=limit_by)
  93. if selected_data == None:
  94. tools.utils.report_bad_path(path)
  95. exit_code += 1
  96. continue
  97. for select_data in selected_data:
  98. is_modified = None
  99. diff = None
  100. file_data = loader.load_file_data(select_data.get_path())
  101. file_data_prev = loader_prev.load_file_data(select_data.get_path())
  102. if file_data_prev != None:
  103. if file_data.get_checksum() == file_data_prev.get_checksum():
  104. diff = 0
  105. is_modified = False
  106. else:
  107. matcher = core.db.utils.FileRegionsMatcher(file_data, file_data_prev)
  108. prev_id = matcher.get_prev_id(select_data.get_region().get_id())
  109. if matcher.is_matched(select_data.get_region().get_id()):
  110. if matcher.is_modified(select_data.get_region().get_id()):
  111. is_modified = True
  112. else:
  113. is_modified = False
  114. diff = core.db.loader.DiffData(select_data,
  115. file_data_prev.get_region(prev_id)).get_data(limit.namespace, limit.field)
  116. if (warn_plugin.is_mode_matched(limit.limit,
  117. select_data.get_data(limit.namespace, limit.field),
  118. diff,
  119. is_modified) == False):
  120. continue
  121. is_sup = is_metric_suppressed(limit.namespace, limit.field, loader, select_data)
  122. if is_sup == True and no_suppress == False:
  123. continue
  124. exit_code += 1
  125. region_cursor = 0
  126. region_name = None
  127. if select_data.get_region() != None:
  128. region_cursor = select_data.get_region().cursor
  129. region_name = select_data.get_region().name
  130. report_limit_exceeded(select_data.get_path(),
  131. region_cursor,
  132. limit.namespace,
  133. limit.field,
  134. region_name,
  135. select_data.get_data(limit.namespace, limit.field),
  136. diff,
  137. limit.limit,
  138. is_modified,
  139. is_sup)
  140. return exit_code
  141. def get_list_of_modified_files(loader, loader_prev):
  142. logging.info("Identifying changed files...")
  143. old_files_map = {}
  144. for each in loader_prev.iterate_file_data():
  145. old_files_map[each.get_path()] = each.get_checksum()
  146. if len(old_files_map) == 0:
  147. return None
  148. modified_file_ids = []
  149. for each in loader.iterate_file_data():
  150. if len(modified_file_ids) > 1000: # If more than 1000 files changed, skip optimisation
  151. return None
  152. if (each.get_path() not in old_files_map.keys()) or old_files_map[each.get_path()] != each.get_checksum():
  153. modified_file_ids.append(str(each.get_id()))
  154. old_files_map = None
  155. if len(modified_file_ids) != 0:
  156. modified_file_ids = " , ".join(modified_file_ids)
  157. modified_file_ids = "(" + modified_file_ids + ")"
  158. return modified_file_ids
  159. return None
  160. def is_metric_suppressed(metric_namespace, metric_field, loader, select_data):
  161. data = loader.load_file_data(select_data.get_path())
  162. if select_data.get_region() != None:
  163. data = data.get_region(select_data.get_region().get_id())
  164. sup_data = data.get_data('std.suppress', 'list')
  165. else:
  166. sup_data = data.get_data('std.suppress.file', 'list')
  167. if sup_data != None and sup_data.find('[' + metric_namespace + ':' + metric_field + ']') != -1:
  168. return True
  169. return False
  170. def report_limit_exceeded(path, cursor, namespace, field, region_name,
  171. stat_level, trend_value, stat_limit,
  172. is_modified, is_suppressed):
  173. if region_name != None:
  174. message = "Metric '" + namespace + ":" + field + "' for region '" + region_name + "' exceeds the limit."
  175. else:
  176. message = "Metric '" + namespace + ":" + field + "' exceeds the limit."
  177. details = [("Metric name", namespace + ":" + field),
  178. ("Region name", region_name),
  179. ("Metric value", stat_level),
  180. ("Modified", is_modified),
  181. ("Change trend", '{0:{1}}'.format(trend_value, '+' if trend_value else '')),
  182. ("Limit", stat_limit),
  183. ("Suppressed", is_suppressed)]
  184. core.cout.notify(path, cursor, core.cout.SEVERITY_WARNING, message, details)