Skip to content

Commit e8095e9

Browse files
committed
graphs: validate edges as well as nodes
1 parent 71d9e82 commit e8095e9

File tree

7 files changed

+50
-39
lines changed

7 files changed

+50
-39
lines changed

src/cli/graph.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,4 @@ def validate(graph: Path):
4242
"""
4343
Validate a <graph file> against the schema.
4444
"""
45-
print(rpc_call("graph_validate", {"graph_path": graph.as_posix()}))
45+
print(rpc_call("graph_validate", {"graph_path": Path(graph).as_posix()}))

src/schema/graph_schema.json

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"node": {
3+
"type": "object",
4+
"properties": {
5+
"degree": {"type": "number"},
6+
"x": {"type": "number"},
7+
"y": {"type": "number"},
8+
"version": {"type": "string"},
9+
"image": {"type": "string"},
10+
"bitcoin_config": {"type": "string", "default": ""},
11+
"tc_netem": {"type": "string"},
12+
"exporter": {"type": "boolean", "default": false},
13+
"collect_logs": {"type": "boolean", "default": false},
14+
"build_args": {"type": "string", "default": ""},
15+
"ln": {"type": "string"},
16+
"ln-image": {"type": "string"},
17+
"ln-cb-image": {"type": "string"}
18+
},
19+
"additionalProperties": false,
20+
"oneOf": [
21+
{"required": ["version"]},
22+
{"required": ["image"]}
23+
],
24+
"required": []
25+
},
26+
"edge": {
27+
"type": "object",
28+
"properties": {
29+
"channel": {"type": "string"}
30+
},
31+
"additionalProperties": false,
32+
"required": []
33+
}
34+
}

src/schema/node_schema.json

-24
This file was deleted.

src/warnet/server.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
from warnet.utils import (
2929
create_cycle_graph,
3030
gen_config_dir,
31-
load_schema,
3231
validate_graph_schema,
3332
)
3433
from warnet.warnet import Warnet
@@ -531,11 +530,10 @@ def graph_generate(
531530
raise ServerError(message=msg) from e
532531

533532
def graph_validate(self, graph_path: str) -> str:
534-
schema = load_schema()
535533
with open(graph_path) as f:
536-
graph = nx.parse_graphml(f.read(), node_type=int)
534+
graph = nx.parse_graphml(f.read(), node_type=int, force_multigraph=True)
537535
try:
538-
validate_graph_schema(schema, graph)
536+
validate_graph_schema(graph)
539537
except (jsonschema.ValidationError, jsonschema.SchemaError) as e:
540538
raise ServerError(message=f"Schema of {graph_path} is invalid: {e}") from e
541539
return f"Schema of {graph_path} is valid"

src/warnet/tank.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def _parse_version(self, version):
6161
def parse_graph_node(self, node):
6262
# Dynamically parse properties based on the schema
6363
graph_properties = {}
64-
for property, specs in self.warnet.node_schema["properties"].items():
64+
for property, specs in self.warnet.graph_schema["node"]["properties"].items():
6565
value = node.get(property, specs.get("default"))
6666
if property == "version":
6767
self._parse_version(value)

src/warnet/utils.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
WEIGHTED_TAGS = [
3434
tag for index, tag in enumerate(reversed(SUPPORTED_TAGS)) for _ in range(index + 1)
3535
]
36-
NODE_SCHEMA_PATH = SCHEMA / "node_schema.json"
36+
NODE_SCHEMA_PATH = SCHEMA / "graph_schema.json"
3737

3838

3939
class NonErrorFilter(logging.Filter):
@@ -500,9 +500,12 @@ def load_schema():
500500
return json.load(schema_file)
501501

502502

503-
def validate_graph_schema(node_schema: dict, graph: nx.Graph):
503+
def validate_graph_schema(graph: nx.Graph):
504504
"""
505505
Validate a networkx.Graph against the node schema
506506
"""
507-
for i in list(graph.nodes):
508-
validate(instance=graph.nodes[i], schema=node_schema)
507+
graph_schema = load_schema()
508+
for n in list(graph.nodes):
509+
validate(instance=graph.nodes[n], schema=graph_schema["node"])
510+
for e in list(graph.edges):
511+
validate(instance=graph.edges[e], schema=graph_schema["edge"])

src/warnet/warnet.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def __init__(self, config_dir, backend, network_name: str):
3636
self.tanks: list[Tank] = []
3737
self.deployment_file: Path | None = None
3838
self.backend = backend
39-
self.node_schema = load_schema()
39+
self.graph_schema = load_schema()
4040

4141
def _warnet_dict_representation(self) -> dict:
4242
repr = {}
@@ -106,8 +106,8 @@ def from_graph_file(
106106
with open(destination, "wb") as f:
107107
f.write(graph_file)
108108
self.network_name = network
109-
self.graph = networkx.parse_graphml(graph_file.decode("utf-8"), node_type=int)
110-
validate_graph_schema(self.node_schema, self.graph)
109+
self.graph = networkx.parse_graphml(graph_file.decode("utf-8"), node_type=int, force_multigraph=True)
110+
validate_graph_schema(self.graph)
111111
self.tanks_from_graph()
112112
logger.info(f"Created Warnet using directory {self.config_dir}")
113113
return self
@@ -116,7 +116,7 @@ def from_graph_file(
116116
def from_graph(cls, graph, backend="compose", network="warnet"):
117117
self = cls(Path(), backend, network)
118118
self.graph = graph
119-
validate_graph_schema(self.node_schema, self.graph)
119+
validate_graph_schema(self.graph)
120120
self.tanks_from_graph()
121121
logger.info(f"Created Warnet using directory {self.config_dir}")
122122
return self
@@ -128,7 +128,7 @@ def from_network(cls, network_name, backend="compose"):
128128
self.network_name = network_name
129129
# Get network graph edges from graph file (required for network restarts)
130130
self.graph = networkx.read_graphml(Path(self.config_dir / self.graph_name), node_type=int)
131-
validate_graph_schema(self.node_schema, self.graph)
131+
validate_graph_schema(self.graph)
132132
self.tanks_from_graph()
133133
for tank in self.tanks:
134134
tank._ipv4 = self.container_interface.get_tank_ipv4(tank.index)

0 commit comments

Comments
 (0)