# Copyright 2013 Mirantis, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. module Astute module LogParser class ParseDeployLogs < ParseNodeLogs attr_reader :deploy_type def deploy_type=(deploy_type) @deploy_type = deploy_type @nodes_patterns = {} end def get_pattern_for_node(node) role = node['role'] node_pattern = Patterns::get_default_pattern( "puppet-log-components-list-#{@deploy_type}-#{role}") node_pattern['path_prefix'] ||= PATH_PREFIX.to_s node_pattern['separator'] ||= SEPARATOR.to_s node_pattern end private def calculate(fo, node_pattern_spec) case node_pattern_spec['type'] when 'count-lines' progress = simple_line_counter(fo, node_pattern_spec) when 'components-list' progress = component_parser(fo, node_pattern_spec) end return progress end def simple_line_counter(fo, pattern_spec) # Pattern specification example: # pattern_spec = {'type' => 'count-lines', # 'endlog_patterns' => [{'pattern' => /Finished catalog run in [0-9]+\.[0-9]* seconds\n/, 'progress' => 1.0}], # 'expected_line_number' => 500} # Use custom separator if defined. separator = pattern_spec['separator'] counter = 0 end_of_scope = false previous_subchunk = '' until end_of_scope chunk = get_chunk(fo, pattern_spec['chunk_size']) break unless chunk # Trying to find separator on border between chunks. subchunk = chunk.slice((1-separator.size)..-1) # End of file reached. Exit from cycle. end_of_scope = true unless subchunk if subchunk and (subchunk + previous_subchunk).include?(separator) # Separator found on border between chunks. Exit from cycle. end_of_scope = true continue end pos = chunk.rindex(separator) if pos end_of_scope = true chunk = chunk.slice((pos + separator.size)..-1) end counter += chunk.count("\n") end number = pattern_spec['expected_line_number'] unless number Astute.logger.warn("Wrong pattern\n#{pattern_spec.pretty_inspect} defined for calculating progress via log.") return 0 end progress = counter.to_f / number progress = 1 if progress > 1 return progress end def component_parser(fo, pattern_spec) # Pattern specification example: # pattern_spec = {'type' => 'components-list', # 'chunk_size' => 40000, # 'components_list' => [ # {'name' => 'Horizon', 'weight' => 10, 'patterns' => [ # {'pattern' => '/Stage[main]/Horizon/Package[mod_wsgi]/ensure) created', 'progress' => 0.1}, # {'pattern' => '/Stage[main]/Horizon/File_line[horizon_redirect_rule]/ensure) created', 'progress' => 0.3}, # {'pattern' => '/Stage[main]/Horizon/File[/etc/openstack-dashboard/local_settings]/group)', 'progress' => 0.7}, # {'pattern' => '/Stage[main]/Horizon/Service[$::horizon::params::http_service]/ensure)'\ # ' ensure changed \'stopped\' to \'running\'', 'progress' => 1}, # ] # }, # ] # } # Use custom separator if defined. separator = pattern_spec['separator'] components_list = pattern_spec['components_list'] unless components_list Astute.logger.warn("Wrong pattern\n#{pattern_spec.pretty_inspect} defined for calculating progress via logs.") return 0 end chunk = get_chunk(fo, pos=pattern_spec['file_pos']) return 0 unless chunk pos = chunk.rindex(separator) chunk = chunk.slice((pos + separator.size)..-1) if pos block = chunk.split("\n") # Update progress of each component. while block.any? string = block.pop components_list.each do |component| matched_pattern = nil component['patterns'].each do |pattern| if pattern['regexp'] matched_pattern = pattern if string.match(pattern['pattern']) else matched_pattern = pattern if string.include?(pattern['pattern']) end break if matched_pattern end if matched_pattern and (not component['_progress'] or matched_pattern['progress'] > component['_progress']) component['_progress'] = matched_pattern['progress'] end end end # Calculate integral progress. weighted_components = components_list.select{|n| n['weight']} weight_sum = 0.0 if weighted_components.any? weighted_components.each{|n| weight_sum += n['weight']} weight_sum = weight_sum * components_list.length / weighted_components.length raise "Total weight of weighted components equal to zero." if weight_sum == 0 end nonweighted_delta = 1.0 / components_list.length progress = 0 components_list.each do |component| component['_progress'] = 0.0 unless component['_progress'] weight = component['weight'] if weight progress += component['_progress'] * weight / weight_sum else progress += component['_progress'] * nonweighted_delta end end return progress end end end end