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': 'host_uuid', 'Value': '4feff42d-bc8d-4006-9922-a7fa10a6ee19'}
# {'Property': 'label_key', 'Value': 'kube-cpu-mgr-policy'} # {'Property': 'label_key', 'Value': 'kube-cpu-mgr-policy'}
# {'Property': 'label_value', 'Value': 'static'} # {'Property': 'label_value', 'Value': 'static'}
# {'Property': 'Property', 'Value': 'Value'} ###### SEPERATOR ROW ######
# {'Property': 'uuid', 'Value': '0260240f-bcca-41f5-9f32-b3acc030dfb0'} # {'Property': 'uuid', 'Value': '0260240f-bcca-41f5-9f32-b3acc030dfb0'}
# {'Property': 'host_uuid', 'Value': '4feff42d-bc8d-4006-9922-a7fa10a6ee19'} # {'Property': 'host_uuid', 'Value': '4feff42d-bc8d-4006-9922-a7fa10a6ee19'}
# {'Property': 'label_key', 'Value': 'kube-topology-mgr-policy'} # {'Property': 'label_key', 'Value': 'kube-topology-mgr-policy'}
@@ -51,13 +50,10 @@ class SystemHostLabelAssignOutput:
system_host_label_object = SystemHostLabelObject() system_host_label_object = SystemHostLabelObject()
for value in output_values: for value in output_values:
# A middle row with 'Property' means that we are changing entry. if value['Property'] == 'uuid': # Every time we hit a uuid, we are encountering a new object.
if value['Property'] == 'Property':
self.system_host_labels.append(system_host_label_object)
system_host_label_object = SystemHostLabelObject() system_host_label_object = SystemHostLabelObject()
if value['Property'] == 'uuid':
system_host_label_object.set_uuid(value['Value']) system_host_label_object.set_uuid(value['Value'])
self.system_host_labels.append(system_host_label_object)
if value['Property'] == 'host_uuid': if value['Property'] == 'host_uuid':
system_host_label_object.set_host_uuid(value['Value']) system_host_label_object.set_host_uuid(value['Value'])
@@ -68,8 +64,6 @@ class SystemHostLabelAssignOutput:
if value['Property'] == 'label_value': if value['Property'] == 'label_value':
system_host_label_object.set_label_value(value['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]: def get_all_host_labels(self) -> [SystemHostLabelObject]:
""" """
Gets all the host label objects that were assigned 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 SystemTableParser:
""" """
Class for System table parsing 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): def __init__(self, system_output):
@@ -19,28 +34,60 @@ class SystemTableParser:
headers = [] headers = []
output_values_list = [] 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: for line in self.system_output:
# output that we care about like headers and actual output have | separators
if line.__contains__('|'): # We have hit a separator which enters Headers, Content, or Ends the table
# find the headers first if line.__contains__('+'):
if not found_headers:
headers = line.split('|')[1:-1] # Find out in which part of the table we are, based on the "+----" separator.
found_headers = True if not is_in_headers_block and not is_in_content_block: # First separator -> Enter Headers
else: is_in_headers_block = True
output_values = {} is_in_content_block = False
values = line.split('|')[1:-1] number_of_columns = line.count('+') - 1
if len(headers) != len(values): headers = [""] * number_of_columns
get_logger().log_error( continue # This is a separator line, don't try to parse anything.
f"Number of headers was {len(headers)} " elif is_in_headers_block and not is_in_content_block: # Second separator -> Go to Content
f"but the number of values was {len(values)}. " is_in_headers_block = False
f"Full output was {self.system_output}" is_in_content_block = True
) continue # This is a separator line, don't try to parse anything.
raise KeywordException("Number of headers and values do not match") else: # Last separator -> The table is complete
index = 0 is_in_headers_block = False
for header in headers: is_in_content_block = False
# create dictionary with header and value
output_values[header.strip()] = values[index].strip() # Build the list of headers, which could be multi-lined.
index = index + 1 if is_in_headers_block:
output_values_list.append(output_values) 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 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", "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(): def test_system_parser():
""" """
@@ -218,7 +233,7 @@ def test_system_parser_error():
SystemTableParser(system_output_error).get_output_values_list() SystemTableParser(system_output_error).get_output_values_list()
assert False, "There should be an exception when parsing the output." assert False, "There should be an exception when parsing the output."
except KeywordException as e: 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(): def test_system_host_output():
@@ -253,7 +268,7 @@ def test_system_host_output_error():
SystemHostOutput(system_output_error).get_hosts() SystemHostOutput(system_output_error).get_hosts()
assert False, "There should be an exception when we parse the output." assert False, "There should be an exception when we parse the output."
except KeywordException as e: 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(): def test_system_application_output():
@@ -276,6 +291,30 @@ def test_system_application_output():
assert application.get_status() == 'applied' assert application.get_status() == 'applied'
assert application.get_progress() == 'completed' 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(): def test_system_host_label_assign_output():
""" """