Skip to content
Snippets Groups Projects
Unverified Commit 198e0164 authored by Andrea Franchini's avatar Andrea Franchini
Browse files

Add support for Requirements in DOML

Fix minor bugs and typos
TODO: Refactor __main__.py
parent 9dd80794
No related branches found
No related tags found
No related merge requests found
......@@ -23,10 +23,10 @@ Every user requirement file is a list of requirement::
exists iface (
vm has abstract.ComputingNode.ifaces iface
and
vm has abstract.ComputingNode.cpu_cores >= 4
vm has abstract.ComputingNode.cpu_count >= 4
)
)
error: "A vm does lacks an associated interface or has less than 4 cpu cores"
error: "A vm does lacks an associated interface or has less than 4 CPUs"
Rules in 1 minute
-----------------
......@@ -88,7 +88,7 @@ The syntax is the following:
- Example:
- ``vm has abstract.ComputingNode.ifaces iface`` is an **Association**, as it puts in relationship the element ``vm`` with the element ``iface``.
- ``vm has abstract.ComputingNode.cpu_cores >= 4`` is an **Attribute** Relationship, as it compares a property (``cpu_cores``) of the element ``vm`` with a constant number.
- ``vm has abstract.ComputingNode.cpu_count >= 4`` is an **Attribute** Relationship, as it compares a property (``cpu_count``) of the element ``vm`` with a constant number.
- Classes: ``class <class name>``
- They represent a kind of element in the architecture.
- Classes follow this naming structure ``<package>.<class>``
......@@ -98,8 +98,8 @@ The syntax is the following:
- Comparisons: ``>``, ``>=``, ``<``, ``<=``, ``==``, ``!=``
- You can compare attributes with constants, or attributes with attributes.
- Example:
- ``vm has abstract.ComputingNode.cpu_cores >= 4`` compares attribute ``cpu_cores`` with a numeric constant.
- ``vm1 has abstract.ComputingNode.cpu_cores >= vm2 abstract.ComputingNode.cpu_cores`` compares attribute ``cpu_cores`` of ``vm1`` with the one of ``vm2``.
- ``vm has abstract.ComputingNode.cpu_count >= 4`` compares attribute ``cpu_count`` with a numeric constant.
- ``vm1 has abstract.ComputingNode.cpu_count >= vm2 abstract.ComputingNode.cpu_count`` compares attribute ``cpu_count`` of ``vm1`` with the one of ``vm2``.
......
......@@ -17,6 +17,7 @@ from mc_openapi.doml_mc.imc import RequirementStore
from mc_openapi.doml_mc.intermediate_model.metamodel import MetaModelDocs
from mc_openapi.doml_mc.mc import ModelChecker
from mc_openapi.doml_mc.mc_result import MCResult
from mc_openapi.doml_mc.xmi_parser.doml_model import get_pyecore_model
parser = argparse.ArgumentParser()
......@@ -67,22 +68,56 @@ else:
# Config the model checker (setup metamodels and intermediate models)
dmc = ModelChecker(doml_xmi, doml_ver)
# Store of Requirements and unique string constants
user_req_store = RequirementStore()
user_req_str_consts = []
synth_user_reqs = []
synth_user_reqs_strings = []
try:
domlr_parser = Parser(DOMLRTransformer)
if args.synth:
synth_domlr_parser = Parser(SynthesisDOMLRTransformer)
except Exception as e:
print(e, file=sys.stderr)
print("Failed to setup DOMLR Parser")
exit(-1)
user_reqs = None
if reqs_path:
with open(reqs_path, "r") as reqsf:
# Read the user requirements written in DSL
user_reqs = reqsf.read()
# Parse them
try:
domlr_parser = Parser(DOMLRTransformer)
user_req_store, user_req_str_consts = domlr_parser.parse(user_reqs)
except Exception as e:
print(e, file=sys.stderr)
print("Failed to parse the DOMLR.", file=sys.stderr)
exit(-1)
if doml_ver == DOMLVersion.V2_1_1:
model = get_pyecore_model(doml_xmi, DOMLVersion.V2_1_1)
func_reqs = model.functionalRequirements.items
for req in func_reqs:
req_name: str = req.name
req_text: str = req.description
req_text = req_text.replace("```", "")
doml_req_store, doml_req_str_consts = domlr_parser.parse(req_text)
user_req_store += doml_req_store
user_req_str_consts += doml_req_str_consts
if args.synth:
synth_doml_req_store, synth_doml_req_str_consts = synth_domlr_parser.parse(req_text, for_synthesis=True)
synth_user_reqs.append(synth_doml_req_store)
synth_user_reqs_strings += synth_doml_req_str_consts
# Remove possible duplicates
user_req_str_consts = list(set(user_req_str_consts))
if not args.synth:
try:
# Check satisfiability
......@@ -121,15 +156,21 @@ else:
for k, v in dmc.intermediate_model.items()
}
# Parse
if user_reqs:
try:
synth_domlr_parser = Parser(SynthesisDOMLRTransformer)
synth_user_reqs, user_reqs_strings = synth_domlr_parser.parse(user_reqs, for_synthesis=True)
ext_domlr_reqs, ext_domlr_reqs_strings = synth_domlr_parser.parse(user_reqs, for_synthesis=True)
synth_user_reqs.append(ext_domlr_reqs)
synth_user_reqs_strings += ext_domlr_reqs_strings
except Exception as e:
print(e, file=sys.stderr)
print("Failed to parse the DOMLR.", file=sys.stderr)
exit(-1)
# Remove duplicated strings
print(synth_user_reqs_strings)
synth_user_reqs_strings = list(set(synth_user_reqs_strings))
state = State()
# Parse MM and IM
state = init_data(
......@@ -138,14 +179,14 @@ else:
metamodel=mm,
)
reqs = [synth_user_reqs]
reqs = synth_user_reqs
if not args.skip_common:
reqs.append(builtin_requirements)
state = solve(
state,
requirements=reqs,
strings=user_reqs_strings,
strings=synth_user_reqs_strings,
max_tries=args.tries
)
# Update state
......
......@@ -144,7 +144,7 @@ application:
associations:
exposedInterfaces:
class: application_SoftwareInterface
mutiplicity: "0..*"
multiplicity: "0..*"
SoftwareInterface:
superclass: application_ApplicationComponent
attributes:
......@@ -277,7 +277,7 @@ infrastructure:
class: infrastructure_ComputingNode
multiplicity: "0..1"
iface:
class: infrastructure_NewtorkInterface
class: infrastructure_NetworkInterface
multiplicity: "0..1"
Container:
superclass: infrastructure_ComputingNode
......
......@@ -84,10 +84,10 @@ application:
associations:
exposedInterfaces:
class: application_SoftwareInterface
mutiplicity: "0..*"
multiplicity: "0..*"
consumedInterfaces:
class: application_SoftwareInterface
mutiplicity: "0..*"
multiplicity: "0..*"
SaaS:
superclass: application_ApplicationComponent
attributes:
......@@ -97,7 +97,7 @@ application:
associations:
exposedInterfaces:
class: application_SoftwareInterface
mutiplicity: "0..*"
multiplicity: "0..*"
SoftwareInterface:
superclass: application_ApplicationComponent
attributes:
......@@ -212,7 +212,7 @@ infrastructure:
class: infrastructure_ComputingNode
multiplicity: "0..1"
iface:
class: infrastructure_NewtorkInterface
class: infrastructure_NetworkInterface
multiplicity: "0..1"
Container:
superclass: infrastructure_ComputingNode
......
......@@ -6,7 +6,7 @@ class RequirementException(Exception):
class RequirementMissingKeyException(RequirementException):
def __init__(self, key_type: str, key: str, close_matches: list[str], *args: object) -> None:
super().__init__(*args)
fix_syntax = lambda x: x.replace("_", ".").replace("::", "->")
fix_syntax = lambda x: x.replace("_", ".", 1).replace("::", "->")
key = fix_syntax(key)
close_matches = list(map(fix_syntax, close_matches))
self.message = f"Error: no {key_type} found named '{key}'.\n"
......
......@@ -381,7 +381,6 @@ class SynthesisDOMLRTransformer(Transformer):
# start
def requirements(self, args) -> list[tuple]:
# TODO: Transform Requirement into
return args
def requirement(self, args) -> tuple:
......
......@@ -99,8 +99,8 @@ class RefHandler:
if rel is not None:
return rel, RefHandler.ATTRIBUTE
else:
close_matches = get_close_matches(rel_name, enc.associations.keys())
raise RequirementMissingKeyException("association", rel_name, close_matches)
close_matches = get_close_matches(rel_name, list(enc.associations.keys()) + list(enc.attributes.keys()))
raise RequirementMissingKeyException("relationship", rel_name, close_matches)
def get_association_rel(enc: SMTEncoding, a: ExprRef, rel: DatatypeRef, b: ExprRef) -> DatatypeRef:
return enc.association_rel(a, rel, b)
......@@ -159,9 +159,8 @@ class SynthesisRefHandler:
def _convert_rel_str(rel: str) -> str:
tokens = rel.replace("abstract", "infrastructure").split(".")
ret = tokens[0]
if len(tokens) >= 2:
ret += "_" + tokens[1]
if len(tokens) >= 3:
ret += "::" + tokens[2]
return ret
if len(tokens) == 2:
return tokens[0] + "_" + tokens[1]
if len(tokens) == 3:
return tokens[0] + "_" + tokens[1] + "::" + tokens[2]
raise f"Bad relationship name: {rel}"
......@@ -55,17 +55,23 @@ class ModelChecker:
return imc.check_requirements(rs)
def split_reqs(n_reqs: int, n_split: int):
slice_size = n_reqs // n_split
slice_size = max(n_reqs // n_split, 1)
rto = 0
while rto < n_reqs:
rfrom = rto
rto = min(rfrom + slice_size, n_reqs)
yield rfrom, rto
try:
with parallel_backend('loky', n_jobs=threads):
results = Parallel(timeout=timeout)(delayed(worker)(rfrom, rto) for rfrom, rto in split_reqs(len(req_store), threads))
# Uncomment for ease of debug
# results =[ worker(0, len(req_store) )]
ret = MCResults([])
for res in results:
ret.add_results(res)
......
......@@ -55,5 +55,5 @@
<configurations name="conf">
<deployments component="//@application/@components.0" node="//@infrastructure/@groups.0/@machineDefinition"/>
</configurations>
<functionalRequirements name="req_ext" description="```&#xA; > &quot;example requirement to test&quot;&#xA; # Expr to parse&#xA; not ( &#xA; vm is class infrastructure.VirtualMachine&#xA; and&#xA; vm is not class infrastructure.Storage&#xA; or&#xA; vm is not class infrastructure.Storage&#xA; implies&#xA; vm is class infrastructure.Storage&#xA; )&#xA; iff&#xA; not exists iface, apple (&#xA; forall orange (&#xA; vm has association infrastructure.ComputingNode->ifaces iface&#xA; or&#xA; vm has association infrastructure.ComputingNode->ifaces iface&#xA; )&#xA; and&#xA; vm has attribute infrastructure.ComputingNode->os Os1&#xA; )&#xA; ---&#xA; &quot;Virtual Machine {vm} has no iface&quot;&#xA;&#x9;```"/>
<functionalRequirements name="req_ext" description="+ &quot;All Virtual Machines have a Interface and at least 4 CPUs&quot; forall vm (vm is class abstract.VirtualMachine implies exists iface (vm has abstract.ComputingNode.ifaces iface and vm has abstract.ComputingNode.cpu_count >= 4 )) error: &quot;A vm does lacks an associated interface or has less than 4 CPUs&quot;"/>
</commons:DOMLModel>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment