diff --git a/docs/writing-requirements.rst b/docs/writing-requirements.rst index 05ba4cbfc3690e22fa7039d7cc076be0408e435c..b30ec9408365d27dd0f516257badfe6922cbecd0 100644 --- a/docs/writing-requirements.rst +++ b/docs/writing-requirements.rst @@ -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``. diff --git a/mc_openapi/__main__.py b/mc_openapi/__main__.py index 9f8607ab5db3a02a87e56c0ecaba90f86c22e0f6..e715bbb30fc6c81ee11d05c60809bd7d03156155 100644 --- a/mc_openapi/__main__.py +++ b/mc_openapi/__main__.py @@ -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,14 +156,20 @@ else: for k, v in dmc.intermediate_model.items() } - # Parse - try: - synth_domlr_parser = Parser(SynthesisDOMLRTransformer) - synth_user_reqs, user_reqs_strings = synth_domlr_parser.parse(user_reqs, for_synthesis=True) - except Exception as e: - print(e, file=sys.stderr) - print("Failed to parse the DOMLR.", file=sys.stderr) - exit(-1) + if user_reqs: + try: + 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 @@ -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 diff --git a/mc_openapi/assets/doml_meta_v2.1.1.yaml b/mc_openapi/assets/doml_meta_v2.1.1.yaml index 010918c26d9d30828a30c18662efbdf9a4dbcf62..df4d72d197d0de585dc74c85c9c487fc59b4dc98 100644 --- a/mc_openapi/assets/doml_meta_v2.1.1.yaml +++ b/mc_openapi/assets/doml_meta_v2.1.1.yaml @@ -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 diff --git a/mc_openapi/assets/doml_meta_v2.1.yaml b/mc_openapi/assets/doml_meta_v2.1.yaml index a1c19170cc8f86926f8944678399945e04fbde50..cba1f52489abb62aa049084c03073473715d3b81 100644 --- a/mc_openapi/assets/doml_meta_v2.1.yaml +++ b/mc_openapi/assets/doml_meta_v2.1.yaml @@ -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 diff --git a/mc_openapi/doml_mc/domlr_parser/exceptions.py b/mc_openapi/doml_mc/domlr_parser/exceptions.py index cc213f52608c70fc0d814150a18ac2839bf171bd..a37ed1f8d1f9c41ebe72a6d86359e16aa18eef0d 100644 --- a/mc_openapi/doml_mc/domlr_parser/exceptions.py +++ b/mc_openapi/doml_mc/domlr_parser/exceptions.py @@ -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" diff --git a/mc_openapi/doml_mc/domlr_parser/parser.py b/mc_openapi/doml_mc/domlr_parser/parser.py index e193b24f6f3e3fe811c7c8648571fd9794428066..c2f751a5470835ba679b479a2fc6f81efa6a3492 100644 --- a/mc_openapi/doml_mc/domlr_parser/parser.py +++ b/mc_openapi/doml_mc/domlr_parser/parser.py @@ -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: diff --git a/mc_openapi/doml_mc/domlr_parser/utils.py b/mc_openapi/doml_mc/domlr_parser/utils.py index ab42f7ec46a99a02f97ee2fdb4aab2fdcc6bd903..b52a2ae01a01ce7472fb18276a0f4fb0b0c6ab57 100644 --- a/mc_openapi/doml_mc/domlr_parser/utils.py +++ b/mc_openapi/doml_mc/domlr_parser/utils.py @@ -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) @@ -158,10 +158,9 @@ class SynthesisRefHandler: raise f"Attribute {rel_name} not present in the metamodel!" 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 + tokens = rel.replace("abstract", "infrastructure").split(".") + 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}" diff --git a/mc_openapi/doml_mc/mc.py b/mc_openapi/doml_mc/mc.py index f50936a58f8c5a4c17b5c85bec98d391a84ab2e6..48be8aa743af9e25070475d397e9c55790012432 100644 --- a/mc_openapi/doml_mc/mc.py +++ b/mc_openapi/doml_mc/mc.py @@ -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) diff --git a/tests/doml/v2.1.1/nginx_func_req.domlx b/tests/doml/v2.1.1/nginx_func_req.domlx index 06e934aef363c4f9ebb82bf224ccaec8a4829967..f0f4e5af2424f404c8532f60487286bcf46367fb 100644 --- a/tests/doml/v2.1.1/nginx_func_req.domlx +++ b/tests/doml/v2.1.1/nginx_func_req.domlx @@ -55,5 +55,5 @@ <configurations name="conf"> <deployments component="//@application/@components.0" node="//@infrastructure/@groups.0/@machineDefinition"/> </configurations> - <functionalRequirements name="req_ext" description="```
 > "example requirement to test"
 # Expr to parse
 not ( 
 vm is class infrastructure.VirtualMachine
 and
 vm is not class infrastructure.Storage
 or
 vm is not class infrastructure.Storage
 implies
 vm is class infrastructure.Storage
 )
 iff
 not exists iface, apple (
 forall orange (
 vm has association infrastructure.ComputingNode->ifaces iface
 or
 vm has association infrastructure.ComputingNode->ifaces iface
 )
 and
 vm has attribute infrastructure.ComputingNode->os Os1
 )
 ---
 "Virtual Machine {vm} has no iface"
	```"/> + <functionalRequirements name="req_ext" description="+ "All Virtual Machines have a Interface and at least 4 CPUs" 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: "A vm does lacks an associated interface or has less than 4 CPUs""/> </commons:DOMLModel>