limit.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  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 re
  21. import core.log
  22. import core.db.loader
  23. import core.db.post
  24. import core.db.utils
  25. import core.export.cout
  26. import core.warn
  27. import core.cmdparser
  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. (options, args) = parser.parse_args(tool_args)
  42. log_plugin.configure(options)
  43. db_plugin.configure(options)
  44. warn_plugin.configure(options)
  45. loader_prev = core.db.loader.Loader()
  46. if db_plugin.dbfile_prev != None:
  47. loader_prev.open_database(db_plugin.dbfile_prev)
  48. loader = core.db.loader.Loader()
  49. loader.open_database(db_plugin.dbfile)
  50. warn_plugin.verify_namespaces(loader.iterate_namespace_names())
  51. for each in loader.iterate_namespace_names():
  52. warn_plugin.verify_fields(each, loader.get_namespace(each).iterate_field_names())
  53. # Check for versions consistency
  54. for each in loader.iterate_properties():
  55. if db_plugin.dbfile_prev != None:
  56. prev = loader_prev.get_property(each.name)
  57. if prev != each.value:
  58. logging.warn("Previous data has got different metadata:")
  59. logging.warn(" - identification of change trends can be not reliable")
  60. logging.warn(" - use 'info' tool to get more details")
  61. break
  62. paths = None
  63. if len(args) == 0:
  64. paths = [""]
  65. else:
  66. paths = args
  67. # Try to optimise iterative change scans
  68. modified_file_ids = None
  69. if warn_plugin.mode != warn_plugin.MODE_ALL:
  70. modified_file_ids = get_list_of_modified_files(loader, loader_prev)
  71. for path in paths:
  72. logging.info("Processing: " + re.sub(r'''[\\]''', "/", path))
  73. for limit in warn_plugin.iterate_limits():
  74. logging.info("Applying limit: " + str(limit))
  75. filters = [limit.filter]
  76. if modified_file_ids != None:
  77. filters.append(('file_id', 'IN', modified_file_ids))
  78. selected_data = loader.load_selected_data(limit.namespace,
  79. fields = [limit.field],
  80. path=path,
  81. filters = filters)
  82. if selected_data == None:
  83. logging.error("Specified path '" + path + "' is invalid (not found in the database records)")
  84. exit_code += 1
  85. continue
  86. for select_data in selected_data:
  87. is_modified = None
  88. diff = None
  89. file_data = loader.load_file_data(select_data.get_path())
  90. file_data_prev = loader_prev.load_file_data(select_data.get_path())
  91. if file_data_prev != None:
  92. if file_data.get_checksum() == file_data_prev.get_checksum():
  93. diff = 0
  94. is_modified = False
  95. else:
  96. matcher = core.db.utils.FileRegionsMatcher(file_data, file_data_prev)
  97. prev_id = matcher.get_prev_id(select_data.get_region().get_id())
  98. if matcher.is_matched(select_data.get_region().get_id()):
  99. if matcher.is_modified(select_data.get_region().get_id()):
  100. is_modified = True
  101. else:
  102. is_modified = False
  103. diff = core.db.loader.DiffData(select_data,
  104. file_data_prev.get_region(prev_id)).get_data(limit.namespace, limit.field)
  105. if warn_plugin.is_mode_matched(limit.limit, select_data.get_data(limit.namespace, limit.field), diff, is_modified):
  106. exit_code += 1
  107. region_cursor = 0
  108. region_name = ""
  109. if select_data.get_region() != None:
  110. region_cursor = select_data.get_region().cursor
  111. region_name = select_data.get_region().name
  112. report_limit_exceeded(select_data.get_path(),
  113. region_cursor,
  114. limit.namespace,
  115. limit.field,
  116. region_name,
  117. select_data.get_data(limit.namespace, limit.field),
  118. diff,
  119. limit.limit,
  120. is_modified)
  121. return exit_code
  122. def get_list_of_modified_files(loader, loader_prev):
  123. logging.info("Identifying changed files...")
  124. old_files_map = {}
  125. for each in loader_prev.iterate_file_data():
  126. old_files_map[each.get_path()] = each.get_checksum()
  127. if len(old_files_map) == 0:
  128. return None
  129. modified_file_ids = []
  130. for each in loader.iterate_file_data():
  131. if len(modified_file_ids) > 1000: # If more than 1000 files changed, skip optimisation
  132. return None
  133. if (each.get_path() not in old_files_map.keys()) or old_files_map[each.get_path()] != each.get_checksum():
  134. modified_file_ids.append(str(each.get_id()))
  135. old_files_map = None
  136. if len(modified_file_ids) != 0:
  137. modified_file_ids = " , ".join(modified_file_ids)
  138. modified_file_ids = "(" + modified_file_ids + ")"
  139. return modified_file_ids
  140. return None
  141. def report_limit_exceeded(path, cursor, namespace, field, region_name, stat_level, trend_value, stat_limit, is_modified):
  142. message = "Metric '" + namespace + "/" + field + "' for region '" + region_name + "' exceeds the limit."
  143. details = [("Metric name", namespace + "/" + field),
  144. ("Region name", region_name),
  145. ("Metric value", stat_level),
  146. ("Modified", is_modified),
  147. ("Change trend", '{0:{1}}'.format(trend_value, '+' if trend_value else '')),
  148. ("Limit", stat_limit)]
  149. core.export.cout.cout(path, cursor, core.export.cout.SEVERITY_WARNING, message, details)