[why]
Reduce the nesting of results and output yaml
[example]
es-jobs os=archlinux
[output]
kvcount.arch=aarch64: 43
kvcount.arch=x86_64: 2
kvcount.category=benchmark: 12
kvcount.category=functional: 33
kvcount.job_state=failed: 20
kvcount.job_state=finished: 25
kvcount.os=archlinux: 45
kvcount.suite=cci-depends: 28
kvcount.suite=cci-makepkg: 5
kvcount.suite=iperf: 3
kvcount.suite=iperf-walk-os-test: 9
kvcount.summary.any_error=1: 40
kvcount.summary.any_stderr=1: 44
kvcount.summary.any_warning=1: 23
kvcount.summary.success=1: 1
kvcount.tbox_group=vm-2p16g--wangyong: 1
kvcount.tbox_group=vm-2p16g--wcl: 1
kvcount.tbox_group=vm-2p16g.wangyong: 34
kvcount.tbox_group=vm-2p8g: 9
...
raw.id.[job_state=failed]: ["crystal.122405","crystal.122406","crystal.122331","crystal.122365","crystal.122404","crystal.122438","crystal.122607","crystal.122370","crystal.122375","crystal.122410","crystal.122900","crystal.122906","crystal.122271","crystal.122451","crystal.122835","crystal.122834","crystal.122232","crystal.122403","crystal.122453","crystal.122886"]
...
raw.id.[summary.any_error=1]: ["crystal.122436","crystal.122476","crystal.122803","crystal.122883","crystal.122910","crystal.122438","crystal.122452","crystal.122895","crystal.122446","crystal.122685","crystal.122687","crystal.122833","crystal.122451","crystal.122690","crystal.122719","crystal.122882","crystal.122890","crystal.122453","crystal.122443","crystal.122435","crystal.122432","crystal.122688","crystal.122689","crystal.122886"]
...
sum.stats.last_state.test.iperf.exit_code.127: 12
sum.stats.stderr./lkp/lkp/src/lib/env.sh:#:set_perf_path: 12
sum.stats.last_state.is_incomplete_run: 12
sum.stats.kmsg.timestamp:last: 45
sum.stats.last_state.exit_fail: 20
sum.stats.stderr.has_stderr: 44
...
raw.stats.sched_debug.cfs_rq:/.load.stddev: [524288.0,null,null,null,null,null,null,null,null,524288.0,524288.0,null,null,null,null,null,516608.0,null,1048576.0,null,null,null,null,null,null,null,524288.0,null,null,null,null,2104832.0,1572864.0,null,null,2097152.0,null,null,null,null,null,null,null,null,null]
...
avg.stats.sched_debug.cfs_rq:/.load.stddev: 1048576.0
avg.stats.softirqs.CPU1.NET_RX: 2.5833333333333335
avg.stats.slabinfo.kmalloc-512.active_objs: 1372.75
...
max.stats.sched_debug.cfs_rq:/.load.stddev: 2104832.0
max.stats.softirqs.CPU1.NET_RX: 6
max.stats.slabinfo.kmalloc-512.active_objs: 1616
...
Signed-off-by: Lu Kaiyi <2392863668(a)qq.com>
---
lib/es_jobs.rb | 213 ++++++++++++++++++++++++++++---------------------
1 file changed, 123 insertions(+), 90 deletions(-)
diff --git a/lib/es_jobs.rb b/lib/es_jobs.rb
index a90b1ba..c35b182 100644
--- a/lib/es_jobs.rb
+++ b/lib/es_jobs.rb
@@ -3,137 +3,170 @@
LKP_SRC = ENV['LKP_SRC'] || '/c/lkp-tests'
+KEYWORD = %w[suite os arch category job_state tbox_group upstream_repo summary.success
+ summary.any_fail summary.any_error summary.any_stderr summary.any_warning].freeze
+
+require 'json'
require "#{LKP_SRC}/lib/stats"
require_relative './es_query'
# deal jobs search from es
class ESJobs
def initialize(es_query, my_refine = [], fields = [], stats_filter = [])
- @es_query = es_query
- @es = ESQuery.new(ES_HOST, ES_PORT)
+ @jobs = query_jobs_from_es(es_query)
@refine = my_refine
@fields = fields
@stats_filter = stats_filter
- @stats_filter_result = {}
@refine_jobs = []
- @jobs = {}
- @stats_level = {
- 0 => 'stats.success',
- 1 => 'stats.unknown',
- 2 => 'stats.warning',
- 3 => 'stats.has_error'
- }
- set_defaults
- deal_jobs
+ set_jobs_summary
end
- def set_defaults
- query_result = @es.multi_field_query(@es_query)
- query_result['hits']['hits'].each do |job|
- @jobs[job['_id']] = job['_source']
- end
-
- @stats = {
- 'stats.count' => Hash.new(0),
- 'stats.sum' => Hash.new(0),
- 'stats.avg' => Hash.new(0)
- }
- @result = {}
- @fields.each do |field|
- @result[field] = []
- end
+ def query_jobs_from_es(items)
+ es = ESQuery.new(ES_HOST, ES_PORT)
+ result = es.multi_field_query items
+ jobs = result['hits']['hits']
+ jobs.map! { |job| job['_source'] }
+ return jobs
end
- def add_result_fields(job, level)
- return unless @refine.include?(level) || @refine.include?(-1)
-
- @refine_jobs << job['id']
- @fields.each do |field|
- value = job[field]
- if value
- value = job['id'] + '.' + value if field == 'job_state'
- @result[field] << value
+ def set_job_summary(stats, job)
+ summary_result = ''
+ stats.each_key do |stat|
+ # "stderr.warning:rubygems_will_be_installed_before_its_ruby_dependency": 1,
+ # "stderr._#warning_FORTIFY_SOURCE_requires_compiling_with_optimization(-O)": 1,
+ # "stderr.disk_src.c:#:#:warning:incompatible_implicit_declaration_of_built-in_function'strcpy'": 1,
+ if stat.match(/warning/i)
+ job['summary.any_warning'] = 1
+ summary_result = 'warning'
+ end
+ # "stderr.linux-perf": 1,
+ # "stderr.error:target_not_found:ruby-dev": 1,
+ # "stderr.error:could_not_open_file/var/lib/pacman/local/ldb-#:#-#/desc:Not_a_directory": 1,
+ if stat.match(/stderr\./i)
+ job['summary.any_stderr'] = 1
+ summary_result = 'stderr'
+ end
+ # "stderr.::Proceed_with_installation?[Y/n]error:target_not_found:liblzma-dev": 1,
+ # "stderr.==>ERROR:Failure_while_downloading_apache-cassandra": 1,
+ # "stderr.ftq.h:#:#:error:unknown_type_name'ticks'": 1,
+ # "last_state.test.iperf.exit_code.127": 1,
+ # "last_state.test.cci-makepkg.exit_code.1": 1,
+ if stat.match(/error|\.exit_code\./i)
+ job['summary.any_error'] = 1
+ summary_result = 'error'
+ end
+ if stat.match(/\.fail$/i)
+ job['summary.any_fail'] = 1
+ summary_result = 'fail'
end
-
- next unless job['stats']
-
- @result[field] << job['stats'][field] if job['stats'][field]
end
- end
-
- def deal_jobs
- stats_count = Hash.new(0)
- stats_jobs = {}
+ return unless summary_result.empty?
- @jobs.each do |job_id, job|
- level = deal_stats(job)
- add_result_fields(job, level)
+ job['summary.success'] = 1
+ end
- stat_key = @stats_level[level]
- stat_jobs_key = stat_key + '_jobs'
+ # set jobs summary fields information in place
+ def set_jobs_summary
+ @jobs.each do |job|
+ stats = job['stats']
+ next unless stats
- stats_count[stat_key] += 1
- stats_jobs[stat_jobs_key] ||= []
- stats_jobs[stat_jobs_key] << job_id
+ set_job_summary(stats, job)
end
-
- @stats['stats.count'].merge!(stats_count)
- @stats['stats.count'].merge!(stats_jobs)
end
- def deal_stats(job, level = 0)
- return 1 unless job['stats']
+ def get_all_metrics(jobs)
+ metrics = []
+ jobs.each do |job|
+ stats = job['stats']
+ next unless stats
- job['stats'].each do |key, value|
- match_stats_filter(key, value, job['id'])
- calculate_stat(key, value)
- level = get_stat_level(key, level)
+ metrics.concat(stats.keys)
end
- return level
+ metrics.uniq!
end
- def match_stats_filter(key, value, job_id)
- @stats_filter.each do |filter|
- next unless key.include?(filter)
+ def initialize_result_hash(metrics)
+ result = {
+ 'kvcount' => {},
+ 'raw.id' => {},
+ 'sum.stats' => {},
+ 'raw.stats' => {},
+ 'avg.stats' => {},
+ 'max.stats' => {},
+ 'min.stats' => {}
+ }
+ metrics.each { |metric| result['raw.stats'][metric] = [] }
+ result
+ end
- key = job_id + '.' + key
- @stats_filter_result[key] = value
+ def set_default_value(result, stats, metrics)
+ left_metrics = metrics - stats.keys
+ left_metrics.each { |metric| result['raw.stats'][metric] << nil }
- break
+ stats.each do |key, value|
+ result['raw.stats'][key] << value
end
end
- def calculate_stat(key, value)
- if function_stat?(key)
- return unless @fields.include?('stats.sum')
+ def kvcount(result, job)
+ KEYWORD.each do |keyword|
+ next unless job[keyword]
- @stats['stats.sum'][key] += value
- else
- return unless @fields.include?('stats.avg')
+ result['kvcount']["#{keyword}=#{job[keyword]}"] ||= 0
+ result['kvcount']["#{keyword}=#{job[keyword]}"] += 1
+ result['raw.id']["[#{keyword}=#{job[keyword]}]"] ||= []
+ result['raw.id']["[#{keyword}=#{job[keyword]}]"] << job['id']
+ end
+ end
- @stats['stats.avg'][key] = (@stats['stats.avg'][key] + value) / 2
+ def stats_count(result)
+ result['raw.stats'].each do |key, value|
+ if function_stat?(key)
+ result['sum.stats'][key] = value.compact.size
+ else
+ result['avg.stats'][key] = value.compact.sum / value.compact.size.to_f
+ result['max.stats'][key] = value.compact.max
+ result['min.stats'][key] = value.compact.min
+ end
end
end
- def get_stat_level(stat, level)
- return level if level >= 3
- return 3 if stat.match(/error|fail/i)
- return 2 if stat.match(/warn/i)
+ def query_jobs_state(jobs)
+ metrics = get_all_metrics(jobs)
+ result = initialize_result_hash(metrics)
+ jobs.each do |job|
+ stats = job['stats']
+ next unless stats
+
+ set_default_value(result, stats, metrics)
+ kvcount(result, job)
+ end
- return 0
+ stats_count(result)
+ result
end
- def output
- result = {
- 'stats.count' => @stats['stats.count']
- }
+ def output_yaml(prefix, result)
+ result.each do |key, value|
+ if prefix.empty?
+ prefix_key = "#{key}"
+ else
+ prefix_key = "#{prefix}.#{key}"
+ end
- @stats.each do |key, value|
- result[key] = value if @fields.include?(key)
+ if value.is_a? Hash
+ output_yaml(prefix_key, value)
+ else
+ puts "#{prefix_key}: #{value.to_json}"
+ end
end
+ end
- @result['stats_filter_result'] = @stats_filter_result unless @stats_filter.empty?
- @result.merge!(result)
- puts JSON.pretty_generate(@result)
+ def output
+ @result = query_jobs_state(@jobs)
+ @result['kvcount'] = @result['kvcount'].sort.to_h
+ @result['raw.id'] = @result['raw.id'].sort.to_h
+ output_yaml('', @result)
end
end
--
2.23.0