Dark Mode

Settings

MACM Validity Checks

This is a list of syntactic and semantic checks to validate the correctness of a MACM model.

Check Table

Check ID Name Description Query Activated

1

Asset Type Validity Per ogni nodo: - Il valore della proprietà `asset type` deve appartenere all’insieme T dei tipi ammessi. - La coppia `(PrimaryLabel, SecondaryLabel)` associata a quel valore di asset_type deve corrispondere ai valori (primary_label, secondary_label) del nodo. CALL apoc.trigger.add( 'check_asset_type_labels', ' UNWIND coalesce($createdNodes, []) AS n WITH n WHERE n.type IS NOT NULL WITH n, split(n.type, ".")[0] AS plFromType, labels(n) AS lbls CALL apoc.util.validate( NOT (plFromType IN lbls), apoc.text.format( "/* %s type=%s expects primary label %s but labels are [%s] */", [coalesce(n.name,"<no name>"), n.type, plFromType, apoc.text.join(lbls, ",")] ), [] ) WITH n, plFromType, lbls, ["Party","CSP","HW","Network","Service","Virtual","SystemLayer","Data"] AS macro WITH n, plFromType, [l IN lbls WHERE l <> plFromType AND NOT l IN macro] AS rest WITH n, plFromType, rest, [ {pl:"Party",sl:"Human",types:["Party.Human"]}, {pl:"Party",sl:"LegalEntity",types:["Party.LegalEntity"]}, {pl:"Party",sl:"Group",types:["Party.Group"]}, {pl:"CSP",sl:null,types:["CSP"]}, {pl:"HW",sl:"MEC",types:["HW.MEC"]}, {pl:"HW",sl:"HW.GCS",types:["HW.GCS"]}, {pl:"HW",sl:"UE",types:["HW.UE"]}, {pl:"HW",sl:"Chassis",types:["HW.Chassis"]}, {pl:"HW",sl:"Raspberry",types:["HW.Raspberry"]}, {pl:"HW",sl:"Router",types:["HW.Router"]}, {pl:"HW",sl:"IoT",types:["HW.IoT.Device","HW.IoT.Gateway"]}, {pl:"HW",sl:"Device",types:["HW.Device","HW.HDI"]}, {pl:"HW",sl:"Server",types:["HW.Server"]}, {pl:"HW",sl:"Microcontroller",types:["HW.Microcontroller"]}, {pl:"HW",sl:"SOC",types:["HW.SOC"]}, {pl:"HW",sl:"PC",types:["HW.PC","HW.PC.LoginNode","HW.PC.DataStorageDisk","HW.PC.SchedulerNode","HW.PC.ComputeNode"]}, {pl:"Network",sl:null,types:["Network"]}, {pl:"Network",sl:"WAN",types:["Network.WAN"]}, {pl:"Network",sl:"LAN",types:["Network.LAN","Network.Wired","Network.WiFi","Network.Virtual"]}, {pl:"Network",sl:"PAN",types:["Network.PAN"]}, {pl:"Network",sl:"5G",types:["Network.RAN","Network.Core"]}, {pl:"SystemLayer",sl:"OS",types:["SystemLayer.OS"]}, {pl:"SystemLayer",sl:"Firmware",types:["SystemLayer.Firmware"]}, {pl:"SystemLayer",sl:"HyperVisor",types:["SystemLayer.HyperVisor"]}, {pl:"SystemLayer",sl:"ContainerRuntime",types:["SystemLayer.ContainerRuntime"]}, {pl:"Virtual",sl:"VM",types:["Virtual.VM"]}, {pl:"Virtual",sl:"Container",types:["Virtual.Container"]}, {pl:"Service",sl:"5G",types:["Service.5G.RAN","Service.5G.AMF","Service.5G.AUSF","Service.5G.NEF","Service.5G.NRF","Service.5G.NSSF","Service.5G.NWDAF","Service.5G.PCF","Service.5G.UDM","Service.5G.UPF"]}, {pl:"Service",sl:"App",types:["Service.App","Service.Browser","Service.MQTTClient","Service.QueueClient"]}, {pl:"Service",sl:null,types:["Service"]}, {pl:"Service",sl:"Server",types:["Service.JobScheduler","Service.SSH","Service.Web","Service.API","Service.DB","Service.NoSQLDB","Service.IDProvider","Service.MQTTBroker","Service.RPCBind","Service.CA","Service.QueueServer"]} ] AS mapping WITH n, plFromType, rest, [m IN mapping WHERE m.pl = plFromType AND n.type IN m.types | m.sl] AS expectedSLsRaw WITH n, plFromType, rest, [x IN expectedSLsRaw WHERE x IS NOT NULL] AS expectedSome, any(x IN expectedSLsRaw WHERE x IS NULL) AS noneAllowed, size(expectedSLsRaw) AS hasAnyMapping CALL apoc.util.validate( hasAnyMapping = 0, apoc.text.format( "/* %s type=%s PL=%s is not covered by mapping */", [coalesce(n.name,"<no name>"), n.type, plFromType] ), [] ) WITH n, plFromType, rest, expectedSome, noneAllowed, (CASE WHEN noneAllowed THEN "[]" ELSE "[" + apoc.text.join(expectedSome, ",") + "]" END) AS expectedStr, apoc.text.join(rest, ",") AS restStr CALL apoc.util.validate( (noneAllowed AND size(rest) <> 0) OR (NOT noneAllowed AND (size(rest) <> 1 OR NOT rest[0] IN expectedSome)), apoc.text.format( "/* %s type=%s PL=%s expected SL(s)=%s but secondary labels are [%s] */", [coalesce(n.name,"<no name>"), n.type, plFromType, expectedStr, restStr] ), [] ) RETURN 0; ', {phase:'before'} ); Yes

2

Single Hosting/Providing per Asset Each asset must be hosted or provided by **at most** one other node. No service can have multiple hosts or provider. CALL apoc.trigger.add( 'check_single_host_provide_per_asset', ' MATCH (hoster)-[r]->(s) WHERE type(r) IN ["hosts","provides"] WITH s, COUNT(DISTINCT hoster) AS numHosts WHERE numHosts > 1 WITH collect(s.name) AS violations CALL apoc.util.validate( size(violations) > 0, "/*Rule 2 violation: the following service has more than a host/provider:" + apoc.text.join (violations, ",") + "*/", [] ) RETURN true ', {phase:'before'} ); Yes

3

Mandatory host/provide per service Ogni nodo di tipo Service deve essere collegato da esattamente un altro nodo tramite una relazione :hosts o :provides. In altre parole - Nessun Service può essere “orfano” (0 host/provide); - Nessun Service può avere due o più host/provide contemporaneamente. CALL apoc.trigger.add( 'check_mandatory_host_provide_for_service', ' MATCH (s:Service) OPTIONAL MATCH (hoster)-[r]->(s) WHERE type(r) IN ["hosts","provides"] WITH s, COUNT(DISTINCT hoster) AS numHosts WHERE numHosts < 1 WITH collect(s.name) AS violations CALL apoc.util.validate( size(violations) > 0, "/*Rule 3 violation: the following service do not have a host/provider:" + apoc.text.join (violations, ",") + "*/", [] ) RETURN true ', {phase:'before'} ); Yes

4

Alternate path for uses Ogni relazione uses tra due nodi deve avere almeno un percorso alternativo che li colleghi senza usare altre uses o interacts. CALL apoc.trigger.add( 'check_alternate_path_for_uses', ' MATCH (a)-[r:uses]->(b) WHERE NOT EXISTS { MATCH p=(a)-[*]-(b) WHERE ALL(rel IN relationships(p) WHERE type(rel) <> "uses") } WITH collect(a.name + "->" + b.name) AS violazioni CALL apoc.util.validate( size(violazioni) > 0, "/*Rule 4 violation: uses relationships without alternative path: " + apoc.text.join(violazioni, ", ") + "*/", [] ) RETURN true ', {phase: 'before'} ); Yes

5

SystemLayer hosting SystemLayer node validity Solo un nodo SoftLayer di tipo sistema operativo può ospitare nodi SoftLayer che rappresentano ambienti di containerizzazione o macchine virtuali; qualsiasi altra combinazione non è permessa. CALL apoc.trigger.add( 'check_SystemLayer_hosts_SystemLayer', ' MATCH (src)-[r:hosts]->(dst) WITH head(labels(src)) AS primarySrc, head(labels(dst)) AS primaryDst, src, dst WHERE primarySrc = "SystemLayer" AND primaryDst = "SystemLayer" AND NOT ( src.type = "SystemLayer.OS" AND dst.type IN ["SystemLayer.ContainerRuntime", "SystemLayer.HyperVisor"] ) WITH collect(src.name + " hosts " + dst.name) AS violazioni CALL apoc.util.validate( size(violazioni) > 0, "/*Rule 5 violation: hosting relationships between SystemLayer nodes not valid: " + apoc.text.join(violazioni, ", ")+"*/", [] ) RETURN true ', {phase: 'before'} ); Yes

6

SystemLayer hosting Virtual node validity Quando un nodo SoftLayer ospita un nodo Virtual, il nodo SoftLayer deve essere di tipo ContainerRuntime o HyperVisor. Se il nodo SoftLayer è di tipo ContainerRuntime, allora il nodo `Virtual` ospitato deve essere un Virtual.Container (cioè un container virtuale). Se il nodo SoftLayer è di tipo HyperVisor, allora il nodo Virtual ospitato deve essere una Virtual.VM (una macchina virtuale). CALL apoc.trigger.add( 'check_SystemLayer_hosts_virtual', ' MATCH (src)-[r:hosts]->(dst) WITH head(labels(src)) AS primarySrc, head(labels(dst)) AS primaryDst, src, dst WHERE primarySrc = "SystemLayer" AND primaryDst = "Virtual" AND NOT ( (src.type = "SystemLayer.ContainerRuntime" AND dst.type = "Virtual.Container") OR (src.type = "SystemLayer.HyperVisor" AND dst.type = "Virtual.VM") ) WITH collect(src.name + " hosts " + dst.name) AS violazioni CALL apoc.util.validate( size(violazioni) > 0, "/*Rule 6 violation: hosting relationships between SystemLayer and Virtual nodes not valid: " + apoc.text.join(violazioni, ", ")+"*/", [] ) RETURN true ', {phase: 'before'} ); Yes

7

SystemLayer hosting Service node validity I nodi hardware possono hostare solo servizi di tipo SoftLayer.Firmware o SoftLayer.OS. CALL apoc.trigger.add( 'check_SystemLayer_hosts_services', ' MATCH (src)-[r:hosts]->(dst) WHERE head(labels(src)) = "SystemLayer" AND head(labels(dst)) = "Service" AND NOT src.type IN ["SystemLayer.Firmware", "SystemLayer.OS"] WITH collect(src.name + " (type: " + src.type + ")" + " hosts " + dst.name) AS violazioni CALL apoc.util.validate( size(violazioni) > 0, "/*Rule 7 violation: hosting relationships between SystemLayer and Service nodes not valid: " + apoc.text.join(violazioni, ", ") + "*/", [] ) RETURN true ', {phase: 'before'} ); Yes

8

Virtual hosting SystemLayer node validity Quando un nodo Virtual hosts un nodo SoftLayer, il nodo SoftLayer ospitato deve avere asset type uguale a SoftLayer.OS oppure SoftLayer.Firmware,altrimenti viene generato un errore. CALL apoc.trigger.add( 'check_virtual_hosts_SystemLayer', ' MATCH (src)-[r:hosts]->(dst) WITH head(labels(src)) AS primarySrc, head(labels(dst)) AS primaryDst, src, dst WHERE primarySrc = "Virtual" AND primaryDst = "SystemLayer" AND NOT (dst.type IN ["SystemLayer.OS", "SystemLayer.Firmware"]) WITH collect(src.name + " hosts " + dst.name) AS violazioni CALL apoc.util.validate( size(violazioni) > 0, "/*Rule 8 violation: hosting relationships between Virtual and SystemLayer nodes not valid: " + apoc.text.join(violazioni, ", ") + "*/", [] ) RETURN true ', {phase: 'before'} ); Yes

9

Hardware hosting SystemLayer node validity Se un nodo HW (hardware) hosts un nodo SoftLayer, il nodo SoftLayer ospitato non può avere asset type SoftLayer.ContainerRuntime,altrimenti viene generato un errore. CALL apoc.trigger.add( 'check_hw_hosts_SystemLayer', ' MATCH (src)-[r:hosts]->(dst) WITH head(labels(src)) AS primarySrc, head(labels(dst)) AS primaryDst, src, dst WHERE primarySrc = "HW" AND primaryDst = "SystemLayer" AND dst.type = "SystemLayer.ContainerRuntime" WITH collect(src.name + " hosts " + dst.name) AS violazioni CALL apoc.util.validate( size(violazioni) > 0, "/*Rule 9 violation: hosting relationships between HW and SystemLayer nodes not valid (ContainerRuntime not allowed): " + apoc.text.join(violazioni, ", ") + "*/", [] ) RETURN true ', {phase: 'before'} ); Yes

10

Graph Connectivity Il grafo deve essere connesso. CALL apoc.trigger.add( 'check_graph_connectivity_global', ' // scegliamo un nodo a caso come sorgente MATCH (start) WITH start LIMIT 1 // percorsi di lunghezza 0.. → lo start è incluso nei reachable MATCH (start)-[*0..]-(reachable) WITH start, collect(DISTINCT id(reachable)) AS reachIds // tutti i nodi del grafo MATCH (n) WITH start, reachIds, collect(id(n)) AS allIds WITH start, reachIds, allIds, [x IN allIds WHERE NOT x IN reachIds] AS missingIds // nodi non raggiungibili OPTIONAL MATCH (m) WHERE id(m) IN missingIds WITH start, [m IN collect(m) | coalesce(m.name, head(labels(m)), toString(id(m)))] AS notReachable CALL apoc.util.validate( size(notReachable) > 0, "/*Connectivity violation: the graph is not connected. Start node: " + coalesce(start.name, head(labels(start)), toString(id(start))) + ". Unreachable nodes: " + apoc.text.join(notReachable, ", ") + "*/", [] ) RETURN true ', {phase: 'before'} ); Yes

11

Single host/provide per SystemLayer Ogni nodo di tipo SystemLayer deve essere collegato da esattamente un altro nodo tramite una relazione :hosts o :provides. In altre parole - Nessun SystemLayer può essere “orfano” (0 host/provide); - Nessun SystemLayer può avere due o più host/provide contemporaneamente. CALL apoc.trigger.add( 'check_mandatory_host_provide_for_systemlayer', ' MATCH (s:SystemLayer) OPTIONAL MATCH (hoster)-[r]->(s) WHERE type(r) IN ["hosts","provides"] WITH s, COUNT(DISTINCT hoster) AS numHosts WHERE numHosts < 1 WITH collect(s.name) AS violations CALL apoc.util.validate( size(violations) > 0, "/*Rule 12 violation: the following SystemLayer do not have any host/provider:" + apoc.text.join (violations, ",") + "*/", [] ) RETURN true ', {phase:'before'} ); Yes

12

Single host/provide per Virtual Each `Virtual` node must be hosted or provided by **exactly one** other node. No Virtual can be orphaned or have multiple hosts. CALL apoc.trigger.add( 'check_mandatory_host_provide_for_virtual', ' MATCH (s:Virtual) OPTIONAL MATCH (hoster)-[r]->(s) WHERE type(r) IN ["hosts","provides"] WITH s, COUNT(DISTINCT hoster) AS numHosts WHERE numHosts < 1 WITH collect(s.name) AS violations CALL apoc.util.validate( size(violations) > 0, "/*Rule 13 violation: the following Virtual do not have any host/provider:" + apoc.text.join (violations, ",") + "*/", [] ) RETURN true ', {phase:'before'} ); Yes

13

No cycles allowed for hosts relationship Ensures acyclicity of the :hosts relation, preventing recursive or circular hosting dependencies among system assets. CALL apoc.trigger.add( 'check_hosts_acyclic_with_cycles', ' MATCH p = (n)-[:hosts*1..]->(n) WITH DISTINCT p, [x IN nodes(p) | toString(coalesce(x.name, id(x)))] AS lst WITH CASE WHEN size(lst) > 1 AND lst[0] = lst[size(lst)-1] THEN lst[0..size(lst)-1] ELSE lst END AS core WHERE size(core) > 0 WITH [i IN range(0, size(core)-1) | apoc.text.join(core[i..] + core[..i] + [core[i]], " -> ") ] AS rots WITH apoc.coll.min(rots) AS cycleCanon WITH apoc.coll.toSet(collect(cycleCanon)) AS cycles CALL apoc.util.validate( size(cycles) > 0, "/*Constraint violation: cycles detected in :hosts hierarchy:\\n" + apoc.text.join(cycles[0..20], "\\n") + "*/", [] ) RETURN true ', {phase:'before'} ); Yes

14

Relationship validity patterns $\delta(SourcePrimaryLabel,TargetPrimaryLabel)=RelationshipType$ CALL apoc.trigger.add( 'check_allowed_relationships', ' // relazioni ammesse (macro-primarie) WITH [ ["Party", "interacts", "Service"], ["Party", "interacts", "HW"], ["Party", "interacts", "Network"], ["Party", "interacts", "Party"], ["Party", "interacts", "Virtual"], ["Party", "interacts", "SystemLayer"], ["Party", "interacts", "CSP"], ["Service", "uses", "Service"], ["Service", "uses", "Virtual"], ["Service", "hosts", "Service"], ["Virtual", "hosts", "SystemLayer"], ["SystemLayer", "hosts", "SystemLayer"], ["SystemLayer", "hosts", "Virtual"], ["SystemLayer", "hosts", "Service"], ["SystemLayer", "hosts", "Network"], ["SystemLayer", "uses", "HW"], ["HW", "hosts", "HW"], ["HW", "hosts", "SystemLayer"], ["CSP", "provides", "Service"], ["CSP", "provides", "Network"], ["CSP", "provides", "HW"], ["CSP", "provides", "Virtual"], ["CSP", "provides", "SystemLayer"], ["Network", "connects", "Network"], ["Network", "connects", "Virtual"], ["Network", "connects", "HW"], ["Network", "connects", "CSP"] ] AS allowed, coalesce($createdRelationships, []) AS rels, // solo le relazioni toccate nel tx ["Party","CSP","HW","Network","Service","Virtual","SystemLayer","Data"] AS macro UNWIND rels AS r WITH allowed, macro, startNode(r) AS src, type(r) AS relType, endNode(r) AS dst // primary label robusta: prima macro presente nelle label del nodo WITH allowed, relType, [m IN macro WHERE m IN labels(src)][0] AS primarySrc, [m IN macro WHERE m IN labels(dst)][0] AS primaryDst WITH allowed, primarySrc, relType, primaryDst WHERE NOT any(rule IN allowed WHERE rule[0]=primarySrc AND rule[1]=relType AND rule[2]=primaryDst) WITH collect(coalesce(primarySrc,"<no-PL>") + "-" + relType + "->" + coalesce(primaryDst,"<no-PL>")) AS violations CALL apoc.util.validate( size(violations) > 0, "/*Constraint violated: Unauthorized relationship(s): " + apoc.text.join(violations, ", ")+"*/", [] ) RETURN true; ', {phase:'before'} ); Yes
Check ID Name Description Query Activated