Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added approaches to move loganalyzer to pytest. #1

Closed
wants to merge 3 commits into from

Conversation

yvolynets-mlnx
Copy link
Owner

Signed-off-by: Yuriy Volynets yuriyv@mellanox.com

Signed-off-by: Yuriy Volynets <yuriyv@mellanox.com>
Module "loganalyzer.py" with class "Loganalyzer".
"Loganalyzer" class - executes shell commands on DUT.
**"Loganalyzer" class interface:**
- __init__(match: list, expect: list, ignore: list)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While creating Loganalyzer instance, it would be better to pass the DUT information to it. DUT information is needed by subsequent methods.

- extract_syslog() - calls "extract_syslog" from "log_processor.py"
- upload_search_files(dest) - upload match, expect, ignore files to the DUT
- parse_summary_file() - convert generated on DUT "result.loganalysis.{TEST_NAME}..." file to json.
- parse_result_file() - convert generated on DUT "summary.loganalysis.{TEST_NAME}" file to json.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the methods that are internally used by Loganalyzer, we can consider hide them using the "_" prefix.

Usage example:

def test(localhost, ansible_adhoc, testbed):
loganalyzer = Loganalyzer(match="{PATH}/loganalyzer_common_match.txt", expect="{PATH}/loganalyzer_common_expect.txt", ignore="{PATH}/loganalyzer_common_ignore.txt")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The loganalyzer can load and copy the common match/expect/ignore files by default. And we can consider adding extra argument for skipping the common pattern files.

result = loganalyzer.analyze()
if result["summary"]["TOTAL MATCHES"] != 0:
loganalyzer.fetch_fail_artifacts(dst="test/{}".format(loganalyzer.unic_test_name))
pytest.fail("SOME_MESSAGE\n{}\n{}".format(result["summary"], result["match_messages"]))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When test failed, I think we need to keep the capability of generating dump and copy related files to sonic-mgmt container for further analysis. While generating dump, we can consider collecting logs within 1 hour by default. If more logs are needed, the time range should can be passed in by an extra argument.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"When test failed, I think we need to keep the capability of generating dump and copy related files to sonic-mgmt container for further analysis." Agree but maybe better will be to have this functionality in some pytest fixture which has access to the DUT?

loganalyzer.fetch_fail_artifacts(dst="test/{}".format(loganalyzer.unic_test_name))
pytest.fail("SOME_MESSAGE\n{}\n{}".format(result["summary"], result["match_messages"]))

#### Approach 2
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I vote for approach 1 because less work is required.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added new commit with udpdates but based on the approach 2. Because it will be more flexible and will require less work done on the DUT side.

- init() - Add start marker to the DUT syslog.
- analyze() - Extract syslog and copy it to ansible host. Analyze extracted syslog files localy. Return python dictionary object.
Return example:
{"summary":

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The formatting in rendered view is messed up

loganalyzer.init()

# Add search marker specific for None MAC test
loganalyzer.update_match(action="add", type="match", "Runtime error: can't parse mac address 'None'")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think action='pop' is needed. I would better recreate the loganalyzer object rather than keeping in mind which matches I removed and which not

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disagree. 1 - Probably this class will be wrapped in pytets fixture, so object recreation does not fit.
2 - We need interface to add/remove specific regexp entries to tune search, so such interface will be ok in such case.
3 - If there is a need we can add "extend" flag to "load_common_config()" which can clear config:

  • if "extend is True", load common configuration and add to the existed.
  • if "extend is False", clear previous configuration and load common.

loganalyzer.init()

# Add search marker specific for None MAC test
loganalyzer.update_match(action="add", type="match", "Runtime error: can't parse mac address 'None'")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add_match/add_expect/add_ignore seems to me better interface than passing a type.
BTW, 'type' is a bad name for key/variable in python

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it will be better but in such case also will be needed del_match/del_expect/del_ignore. So it increase number of methods for similar functionality.


from loganalyzer import Loganalyzer
def test(localhost, ansible_adhoc, testbed):
loganalyzer = Loganalyzer()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the default in this case, why load_common_config is a different method? What if the user will not call load_common_config/update_match?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this approach, when loganalyzer is initializing:
loganalyzer = Loganalyzer()

It does not have configured search regexp (does not know what markers to search in the logs).
As we have common regular expressions for the DUT (now defined in match, expect and ignore files) they can be loaded/read to the loganalyzer object by calling "loganalyzer.load_common_config()".

In most cases we will need common regexp to be loaded so this can be moved to the "init".

"update_match()" - not required to be used. It can be used if there is a need to remove or update (add test specific) configured regular expressions to search.

- init() - Add start marker to the DUT syslog.
- analyze() - Extract syslog and copy it to ansible host. Analyze extracted syslog files localy. Return python dictionary object.
Return example:
{"summary":

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a dict with one key. What is the benefit of having everything under key "summary"?

loganalyzer.init()

# Add search marker specific for None MAC test
loganalyzer.update_match(action="add", type="match", "Runtime error: can't parse mac address 'None'")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

having the option to load a list of regexp is good, but there are a lot of tests which have files with regexps, how can one load these files using "update_match"?

loganalyzer.init()

# Add search marker specific for None MAC test
loganalyzer.update_match(action="add", type="match", "Runtime error: can't parse mac address 'None'")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does update_match overrides common or adds incrementaly?
How can one use, lets say, common 'match'/'ignore' but specific 'expect'?

@stepanblyschak
Copy link

I vote for approach #2, because there is no haste, so we can do things better than they are right now

Signed-off-by: Yuriy Volynets <yuriyv@mellanox.com>
- __init__(ansible_host: ansible_host)
- load_common_config() - Load regular expressions from common configuration files: match, expect and ignore which are located in some configuration directory or with current module. Clear previous configured match, expect and ignore. Save loaded configuration.
- parse_regexp_file(file_path) - read and parse regular expressions from specified file. Return list of strings of defined regular expressions.
- run_cmd(callable, *args, **kwargs) - call function and analyze DUT logs, return the same result as "analyze" function
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why use a wrapper here rather than directly call the functions?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is analog to the "roles/test/tasks/run_command_with_log_analyzer.yml" - add start marker, execute function, add stop marker, analyze logs.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see. It looks good.

- analyze() - Extract syslog and copy it to ansible host. Analyze extracted syslog files localy. Return python dictionary object.
Return example:
{"counters": {"match": 1, "expected_match": 0, "expected_missing_match": 0},
"match_files": {"/tmp/syslog": {"match": 0, "expected_match": 32},
Copy link

@wangxin wangxin Jul 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the log analyzer may be called mutliple times, the logs may be copied to sonic-mgmt container multiple times. I think we need to consider putting the copied files under different temp folders to avoid conflicts.

After analyzing is done, should we cleanup the copied temp log files?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"I think we need to consider putting the copied files under different temp folders to avoid conflicts." - Yes it is planned like this.
"After analyzing is done, should we cleanup the copied temp log files?" - Yes we should clean up temp log file.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yvolynets-mlnx Can the loganalyzer print whole temp file with DEBUG level. I assume we will run with DEBUG on jenkins and this part of log that is analyzed will be visible in output. Useful I think

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depends on the DUT health, it colud take many lines. We need more comments about this to decide whether to add it automatically.


#### Development
Module "loganalyzer.py" with class "Loganalyzer". Class is intendent to do:
- Add start marker to the DUT syslog
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would the start marker be looked like? Should we consider haveing the start marker string contain testname? If so, testname need to be passed in to the init method as an argument.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Existed roles\test\files\tools\loganalyzer\loganalyzer.py will be reused, so logic with adding start/stop markers and processing the logs will be the same (will be reused). And yes, start marker is based on the test name.

- parse_regexp_file(file_path) - read and parse regular expressions from specified file. Return list of strings of defined regular expressions.
- run_cmd(callable, *args, **kwargs) - call function and analyze DUT logs, return the same result as "analyze" function
- init() - Add start marker to the DUT syslog.
- analyze() - Extract syslog and copy it to ansible host. Analyze extracted syslog files localy. Return python dictionary object.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extract syslog and copy it to sonic-mgmt container?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logs will be stored to the host where pytest was started. For now it will be sonic-mgmt container. It will decrease work which are doing on the device side.

"/tmp/syslog2": ["Message 1", "Message 2", "Message n"]
}
}
- save_full_log(file_path) - save downloaded DUT syslog logs to the Ansible host folder specified in 'path' input parameter.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Save log to DUT or to sonic-mgmt container?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To sonic-mgmt container

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

save_full_log(file_path) - will be better if this will download /tmp/syslog from the DUT to the Ansible host. Because temp logs which are composed by analyze method will be deleted.

Signed-off-by: Yuriy Volynets <yuriyv@mellanox.com>
"Loganalyzer" class interface:
- __init__(ansible_host: ansible_host, run_dir="/tmp")
- load_common_config() - Clear previous configured match, expect and ignore. Load regular expressions from common configuration files: match, expect and ignore which are located in some configuration directory or with current module. Save loaded configuration to the self.match_regex, self.expect_regex and self.ignore_regex attributes.
- parse_regexp_file(file_path) - Read and parse regular expressions from specified file. Return list of strings of defined regular expressions.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably a @classmethod ?

liat-grozovik pushed a commit to liat-grozovik/SONiC that referenced this pull request Jun 26, 2023
Update Interface-Link-bring-up-sequence.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants