compass-core/compass/log_analyzor/line_matcher.py
Lei Lei 3fe0d59558 Add integration test for update_progress.py
Change-Id: Ie7a359b6028dafc706c2213b8745e45c9bf414a2
2014-12-29 14:58:26 -08:00

207 lines
7.4 KiB
Python

# Copyright 2014 Huawei Technologies Co. Ltd
#
# 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 to get the progress when found match with a line of the log."""
import logging
import re
from abc import ABCMeta
from compass.utils import util
class ProgressCalculator(object):
"""base class to generate progress."""
__metaclass__ = ABCMeta
@classmethod
def update_progress(
cls, progress_data, message,
severity, log_history
):
"""Update progress with the given progress_data, message and severity.
:param progress_data: installing progress.
:type progress_data: float between 0 to 1.
:param message: installing progress message.
:param severity: installing message severity.
:param progress: :class:`Progress` instance to update
"""
# the progress is only updated when the new progress
# is greater than the stored progress or the progress
# to update is the same but the message is different.
if (
progress_data > log_history['percentage'] or (
progress_data == log_history['percentage'] and
message != log_history['message']
)
):
log_history['percentage'] = progress_data
if message:
log_history['message'] = message
if severity:
log_history['severity'] = severity
logging.debug('update progress to %s', log_history)
else:
logging.debug('ignore update progress %s to %s',
progress_data, log_history)
def update(self, message, severity, log_history):
"""vritual method to update progress by message and severity.
:param message: installing message.
:param severity: installing severity.
"""
raise NotImplementedError(str(self))
def __repr__(self):
return self.__class__.__name__
class IncrementalProgress(ProgressCalculator):
"""Class to increment the progress."""
def __init__(self, min_progress,
max_progress, incremental_ratio):
super(IncrementalProgress, self).__init__()
if not 0.0 <= min_progress <= max_progress <= 1.0:
raise IndexError(
'%s restriction is not mat: 0.0 <= min_progress(%s)'
' <= max_progress(%s) <= 1.0' % (
self.__class__.__name__, min_progress, max_progress))
if not 0.0 <= incremental_ratio <= 1.0:
raise IndexError(
'%s restriction is not mat: '
'0.0 <= incremental_ratio(%s) <= 1.0' % (
self.__class__.__name__, incremental_ratio))
self.min_progress_ = min_progress
self.max_progress_ = max_progress
self.incremental_progress_ = (
incremental_ratio * (max_progress - min_progress))
def __str__(self):
return '%s[%s:%s:%s]' % (
self.__class__.__name__,
self.min_progress_,
self.max_progress_,
self.incremental_progress_
)
def update(self, message, severity, log_history):
"""update progress from message and severity."""
progress_data = max(
self.min_progress_,
min(
self.max_progress_,
log_history['percentage'] + self.incremental_progress_
)
)
self.update_progress(progress_data,
message, severity, log_history)
class RelativeProgress(ProgressCalculator):
"""class to update progress to the given relative progress."""
def __init__(self, progress):
super(RelativeProgress, self).__init__()
if not 0.0 <= progress <= 1.0:
raise IndexError(
'%s restriction is not mat: 0.0 <= progress(%s) <= 1.0' % (
self.__class__.__name__, progress))
self.progress_ = progress
def __str__(self):
return '%s[%s]' % (self.__class__.__name__, self.progress_)
def update(self, message, severity, log_history):
"""update progress from message and severity."""
self.update_progress(
self.progress_, message, severity, log_history)
class SameProgress(ProgressCalculator):
"""class to update message and severity for progress."""
def update(self, message, severity, log_history):
"""update progress from the message and severity."""
self.update_progress(log_history['percentage'], message,
severity, log_history)
class LineMatcher(object):
"""Progress matcher for each line."""
def __init__(self, pattern, progress=None,
message_template='', severity=None,
unmatch_sameline_next_matcher_name='',
unmatch_nextline_next_matcher_name='',
match_sameline_next_matcher_name='',
match_nextline_next_matcher_name=''):
self.regex_ = re.compile(pattern)
if not progress:
self.progress_ = SameProgress()
elif isinstance(progress, ProgressCalculator):
self.progress_ = progress
elif util.is_instance(progress, [int, float]):
self.progress_ = RelativeProgress(progress)
else:
raise TypeError(
'progress unsupport type %s: %s' % (
type(progress), progress))
self.message_template_ = message_template
self.severity_ = severity
self.unmatch_sameline_ = unmatch_sameline_next_matcher_name
self.unmatch_nextline_ = unmatch_nextline_next_matcher_name
self.match_sameline_ = match_sameline_next_matcher_name
self.match_nextline_ = match_nextline_next_matcher_name
def __repr__(self):
return '%r[pattern:%r, message_template:%r, severity:%r]' % (
self.__class__.__name__, self.regex_.pattern,
self.message_template_, self.severity_)
def update_progress(self, line, log_history):
"""Update progress by the line.
:param line: one line in log file to indicate the installing progress.
.. note::
The line may be partial if the latest line of the log file is
not the whole line. But the whole line may be resent
in the next run.
:param progress: the :class:`Progress` instance to update.
"""
mat = self.regex_.search(line)
if not mat:
return (
self.unmatch_sameline_,
self.unmatch_nextline_)
try:
message = self.message_template_ % mat.groupdict()
except Exception as error:
logging.error('failed to get message %s %% %s in line matcher %s',
self.message_template_, mat.groupdict(), self)
raise error
self.progress_.update(message, self.severity_, log_history)
return (
self.match_sameline_,
self.match_nextline_)