diff options
Diffstat (limited to 'compass-tasks/log_analyzor/line_matcher.py')
-rw-r--r-- | compass-tasks/log_analyzor/line_matcher.py | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/compass-tasks/log_analyzor/line_matcher.py b/compass-tasks/log_analyzor/line_matcher.py new file mode 100644 index 0000000..ada9ed6 --- /dev/null +++ b/compass-tasks/log_analyzor/line_matcher.py @@ -0,0 +1,206 @@ +# 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 isinstance(progress, (int, long, 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_) |