acrn-hypervisor/misc/config_tools/scenario_config/pipeline.py
Junjie Mao 22a47fe795 config_tools: check XML file structures on load
This patch validates the structure of the XML files given before they are
further used by the rest of the configurator. With this validation process,
the configurator confirms the XML files are well-structured and can thus
access certain nodes and contents without checking their existence or data
types.

Upon validation failure, an alert will pop up informing the user that the
given XML file is ill-formed. No further details are given as of now
because we assume users should not care about the internal structure of
those files.

Tracked-On: #6691
Signed-off-by: Junjie Mao <junjie.mao@intel.com>
2022-05-19 09:31:46 +08:00

73 lines
2.4 KiB
Python

#!/usr/bin/env python3
#
# Copyright (C) 2022 Intel Corporation.
#
# SPDX-License-Identifier: BSD-3-Clause
#
class PipelineObject:
def __init__(self, **kwargs):
self.data = {}
for k,v in kwargs.items():
self.set(k, v)
def set(self, tag, data):
self.data[tag] = data
def get(self, tag):
return self.data[tag]
def has(self, tag):
return tag in self.data.keys()
def consume(self, tag):
return self.data.pop(tag, None)
def dump(self):
print(self.data)
class PipelineStage:
# The following three class variables defines the inputs and outputs of the stage. Each of them can be either a set
# or a string (which is interpreted as a unit set)
consumes = set() # Data consumed by this stage. Consumed data will be unavailable to later stages.
uses = set() # Data used but not consumed by this stage.
provides = set() # Data provided by this stage.
def run(self, obj):
raise NotImplementedError
class PipelineEngine:
def __init__(self, initial_data = []):
self.stages = []
self.initial_data = set(initial_data)
self.available_data = set(initial_data)
def add_stage(self, stage):
consumes = stage.consumes if isinstance(stage.consumes, set) else {stage.consumes}
uses = stage.uses if isinstance(stage.uses, set) else {stage.uses}
provides = stage.provides if isinstance(stage.provides, set) else {stage.provides}
all_uses = consumes.union(uses)
if not all_uses.issubset(self.available_data):
raise Exception(f"Data {all_uses - self.available_data} need by stage {stage.__class__.__name__} but not provided by the pipeline")
self.stages.append(stage)
self.available_data = self.available_data.difference(consumes).union(provides)
def add_stages(self, stages):
for stage in stages:
self.add_stage(stage)
def run(self, obj):
for tag in self.initial_data:
if not obj.has(tag):
raise AttributeError(f"Data {tag} is needed by the pipeline but not provided by the object")
for stage in self.stages:
stage.run(obj)
consumes = stage.consumes if isinstance(stage.consumes, set) else {stage.consumes}
for tag in consumes:
obj.consume(tag)