SystemTableParser to support multi-line

Enhancing the algorithm used by the SystemTableParser
to support broken lines and multi-line parsing.

Change-Id: Iacce397078f88ff4c6360ff907f1dff4f034abf2
This commit is contained in:
croy
2025-02-03 17:01:31 -05:00
parent df24154568
commit ee3a0836eb
3 changed files with 113 additions and 33 deletions

View File

@@ -42,7 +42,6 @@ class SystemHostLabelAssignOutput:
# {'Property': 'host_uuid', 'Value': '4feff42d-bc8d-4006-9922-a7fa10a6ee19'}
# {'Property': 'label_key', 'Value': 'kube-cpu-mgr-policy'}
# {'Property': 'label_value', 'Value': 'static'}
# {'Property': 'Property', 'Value': 'Value'} ###### SEPERATOR ROW ######
# {'Property': 'uuid', 'Value': '0260240f-bcca-41f5-9f32-b3acc030dfb0'}
# {'Property': 'host_uuid', 'Value': '4feff42d-bc8d-4006-9922-a7fa10a6ee19'}
# {'Property': 'label_key', 'Value': 'kube-topology-mgr-policy'}
@@ -51,13 +50,10 @@ class SystemHostLabelAssignOutput:
system_host_label_object = SystemHostLabelObject()
for value in output_values:
# A middle row with 'Property' means that we are changing entry.
if value['Property'] == 'Property':
self.system_host_labels.append(system_host_label_object)
if value['Property'] == 'uuid': # Every time we hit a uuid, we are encountering a new object.
system_host_label_object = SystemHostLabelObject()
if value['Property'] == 'uuid':
system_host_label_object.set_uuid(value['Value'])
self.system_host_labels.append(system_host_label_object)
if value['Property'] == 'host_uuid':
system_host_label_object.set_host_uuid(value['Value'])
@@ -68,8 +64,6 @@ class SystemHostLabelAssignOutput:
if value['Property'] == 'label_value':
system_host_label_object.set_label_value(value['Value'])
self.system_host_labels.append(system_host_label_object)
def get_all_host_labels(self) -> [SystemHostLabelObject]:
"""
Gets all the host label objects that were assigned

View File

@@ -5,6 +5,21 @@ from framework.logging.automation_logger import get_logger
class SystemTableParser:
"""
Class for System table parsing
Sample Table:
'+--------------------------------------+---------+-----------+---------------+\n'
'| uuid | name | ptp_insta | parameters |\n'
'| | | nce_name | |\n'
'+--------------------------------------+---------+-----------+---------------+\n'
"| 0000c96e-6dab-48c2-875a-48af194c893c | n4_p2 | ptp4 | ['masterOnly= |\n"
"| | | | 1'] |\n"
'| | | | |\n'
'| 24003e49-f9c4-4794-970e-506fa5c215c0 | n1_if | clock1 | [] |\n'
'| 51e06821-b045-4a6e-854b-6bd829b5c9e2 | ptp1if1 | ptp1 | [] |\n'
"| a689d398-329f-46b4-a99f-23b9a2417c27 | n5_p2 | ptp5 | ['masterOnly= |\n"
"| | | | 1'] |\n"
'| | | | |\n'
'+--------------------------------------+---------+-----------+---------------+\n'
"""
def __init__(self, system_output):
@@ -19,28 +34,60 @@ class SystemTableParser:
headers = []
output_values_list = []
found_headers = False
last_output_value = None
is_in_headers_block = False
is_in_content_block = False
number_of_columns = -1
for line in self.system_output:
# output that we care about like headers and actual output have | separators
if line.__contains__('|'):
# find the headers first
if not found_headers:
headers = line.split('|')[1:-1]
found_headers = True
else:
output_values = {}
values = line.split('|')[1:-1]
if len(headers) != len(values):
get_logger().log_error(
f"Number of headers was {len(headers)} "
f"but the number of values was {len(values)}. "
f"Full output was {self.system_output}"
)
raise KeywordException("Number of headers and values do not match")
index = 0
for header in headers:
# create dictionary with header and value
output_values[header.strip()] = values[index].strip()
index = index + 1
output_values_list.append(output_values)
# We have hit a separator which enters Headers, Content, or Ends the table
if line.__contains__('+'):
# Find out in which part of the table we are, based on the "+----" separator.
if not is_in_headers_block and not is_in_content_block: # First separator -> Enter Headers
is_in_headers_block = True
is_in_content_block = False
number_of_columns = line.count('+') - 1
headers = [""] * number_of_columns
continue # This is a separator line, don't try to parse anything.
elif is_in_headers_block and not is_in_content_block: # Second separator -> Go to Content
is_in_headers_block = False
is_in_content_block = True
continue # This is a separator line, don't try to parse anything.
else: # Last separator -> The table is complete
is_in_headers_block = False
is_in_content_block = False
# Build the list of headers, which could be multi-lined.
if is_in_headers_block:
headers_line = line.split('|')[1:-1]
if len(headers_line) != number_of_columns:
get_logger().log_error(f"Number of headers should be {number_of_columns} based on the number of '+' but the number of values was {len(headers_line)}.")
raise KeywordException("Number of headers and + separator do not match expected value")
for i in range(number_of_columns):
headers[i] += headers_line[i].strip()
# Build the list of values, which could be multi-lined.
if is_in_content_block:
values_line = line.split('|')[1:-1]
if len(values_line) != number_of_columns:
get_logger().log_error(f"Number of values should be {number_of_columns} based on the number of '+' but the number of values was {len(values_line)}.")
raise KeywordException("Number of headers and values do not match expected value")
# If there is a value in the first column, then this is a new entry.
if values_line[0].strip():
# Build a dictionary of the Header:Value
last_output_value = {}
for i in range(number_of_columns):
last_output_value[headers[i]] = values_line[i].strip()
output_values_list.append(last_output_value)
else: # Otherwise, this is the continuation of the previous line.
for i in range(number_of_columns):
last_output_value[headers[i]] += values_line[i].strip()
return output_values_list

View File

@@ -164,6 +164,21 @@ system_application_upload = [
"Please use 'system application-list' or 'system application-show hello-kitty' to view the current progress.\n",
]
system_host_if_ptp_remove_wrapped_output = [
'+--------------------------------------+---------+-----------+---------------+\n',
'| uuid | name | ptp_insta | parameters |\n',
'| | | nce_name | |\n',
'+--------------------------------------+---------+-----------+---------------+\n',
"| 0000c96e-6dab-48c2-875a-48af194c893c | n4_p2 | ptp4 | ['masterOnly= |\n",
"| | | | 1'] |\n",
'| | | | |\n',
'| 24003e49-f9c4-4794-970e-506fa5c215c0 | n1_if | clock1 | [] |\n',
'| 51e06821-b045-4a6e-854b-6bd829b5c9e2 | ptp1if1 | ptp1 | [] |\n',
"| a689d398-329f-46b4-a99f-23b9a2417c27 | n5_p2 | ptp5 | ['masterOnly= |\n",
"| | | | 1'] |\n",
'| | | | |\n',
'+--------------------------------------+---------+-----------+---------------+\n',
]
def test_system_parser():
"""
@@ -218,7 +233,7 @@ def test_system_parser_error():
SystemTableParser(system_output_error).get_output_values_list()
assert False, "There should be an exception when parsing the output."
except KeywordException as e:
assert e.args[0] == 'Number of headers and values do not match'
assert e.args[0] == 'Number of headers and + separator do not match expected value'
def test_system_host_output():
@@ -253,7 +268,7 @@ def test_system_host_output_error():
SystemHostOutput(system_output_error).get_hosts()
assert False, "There should be an exception when we parse the output."
except KeywordException as e:
assert e.args[0] == 'Number of headers and values do not match'
assert e.args[0] == 'Number of headers and + separator do not match expected value'
def test_system_application_output():
@@ -276,6 +291,30 @@ def test_system_application_output():
assert application.get_status() == 'applied'
assert application.get_progress() == 'completed'
def test_system_table_parser_with_wrapped_table_entry():
"""
Test the system vertical parser with a table that has a column wrapped.
Returns:
"""
system_vertical_table_parser = SystemTableParser(system_host_if_ptp_remove_wrapped_output)
list_of_values = system_vertical_table_parser.get_output_values_list()
assert len(list_of_values) == 4
first_entry = list_of_values[0]
assert len(first_entry.keys()) == 4
assert first_entry['uuid'] == '0000c96e-6dab-48c2-875a-48af194c893c'
assert first_entry['name'] == 'n4_p2'
assert first_entry['ptp_instance_name'] == 'ptp4'
assert first_entry['parameters'] == "['masterOnly=1']"
second_entry = list_of_values[1]
assert len(second_entry.keys()) == 4
assert second_entry['uuid'] == '24003e49-f9c4-4794-970e-506fa5c215c0'
assert second_entry['name'] == 'n1_if'
assert second_entry['ptp_instance_name'] == 'clock1'
assert second_entry['parameters'] == "[]"
def test_system_host_label_assign_output():
"""