some terror with structures

This commit is contained in:
2025-12-01 02:19:22 +03:00
parent 4cfadd14e0
commit 52cd77735c
7 changed files with 171 additions and 12 deletions
+6
View File
@@ -0,0 +1,6 @@
name,"type","interfaces","ip","endpoints"
PC1,"host","int1","",""
PC2,"host","int2","",""
int1,"interface","","10.0.12.1/24",""
int2,"interface","","10.0.12.2/24",""
link,"wire","","","int1,int2"
1 name type interfaces ip endpoints
2 PC1 host int1
3 PC2 host int2
4 int1 interface 10.0.12.1/24
5 int2 interface 10.0.12.2/24
6 link wire int1,int2
+16
View File
@@ -0,0 +1,16 @@
links:
- endpoints:
- PC1
- PC2
meta:
id: /home/krosh/Documents/Github/network-diagrams-tool/host-host.csv
name: /home/krosh/Documents/Github/network-diagrams-tool/host-host.csv
nodes:
- interfaces:
- ip: 10.0.12.1/24
name: PC1
role: host
- interfaces:
- ip: 10.0.12.2/24
name: PC2
role: host
+21
View File
@@ -0,0 +1,21 @@
meta:
id: "lab-02-basic-utils"
name: "Basic Network Utilities & Traffic Monitoring"
description: "Two hosts connected directly to each other."
links:
- endpoints: ["PC1", "PC2"]
nodes:
- name: PC1
role: host
interfaces:
- ip: "10.0.12.1/24"
- name: PC2
role: host
interfaces:
- ip: "10.0.12.2/24"
# Optional:
# - "ip link set dev eth1 down"
Regular → Executable
+3 -1
View File
@@ -1,4 +1,6 @@
from parse import parse_csv_to_structure
import sys
from structures import Host, Wire
def check_correct(data):
@@ -14,7 +16,7 @@ def check_correct(data):
if __name__ == "__main__":
data = parse_csv_to_structure("input.csv")
data = parse_csv_to_structure(sys.argv[1])
if check_correct(data):
print("yay!")
print(data)
+81
View File
@@ -0,0 +1,81 @@
from check_correct import check_correct
from parse import parse_csv_to_structure
import sys
from structures import Host, Wire, Interface
import yaml
"""
meta:
id: "lab-02-basic-utils"
name: "Basic Network Utilities & Traffic Monitoring"
description: "Two hosts connected directly to each other."
links:
- endpoints: ["PC1", "PC2"]
nodes:
- name: PC1
role: host
interfaces:
- ip: "10.0.12.1/24"
- name: PC2
role: host
interfaces:
- ip: "10.0.12.2/24"
"""
def make_res(data, name):
res = {
"meta": {
"id": name,
"name": name,
},
"links": [],
"nodes": [],
}
host_int = {}
for key, value in data.items():
if value.get("type") == "host":
value["name"] = key
host = Host(value)
node_entry = {
"name": host.name,
"role": "host",
"interfaces": [
{"ip": Interface(data[iface]).ip_address}
for iface in host.interfaces
],
}
res["nodes"].append(node_entry)
host_int[host.interfaces[0]] = host.name
for key, value in data.items():
if value.get("type") == "wire":
wire = Wire(value)
link_entry = {
"endpoints": [
host_int[wire.endpoints[0]],
host_int[wire.endpoints[1]],
]
}
res["links"].append(link_entry)
return res
if __name__ == "__main__":
data = parse_csv_to_structure(sys.argv[1])
print(data)
if check_correct(data):
print("Data is correct.")
res = make_res(data, sys.argv[1])
print(res)
with open("output.yaml", "w") as f:
yaml.dump(res, f)
+6 -5
View File
@@ -1,24 +1,25 @@
import csv
def parse_csv_to_structure(path):
result = {}
with open(path, newline='') as f:
with open(path, newline="") as f:
reader = csv.reader(f)
rows = list(reader)
headers = rows[0][1:]
for row in rows[1:]:
obj = row[0].strip()
if not obj:
continue
result[obj] = {}
for header, value in zip(headers, row[1:]):
value = value.strip()
if value:
result[obj][header] = value
+38 -6
View File
@@ -6,6 +6,7 @@ from typing import List, Optional, Dict
# Interface Classes
# ===========================
@dataclass
class Interface:
name: str
@@ -14,6 +15,13 @@ class Interface:
speed: Optional[str] = None
duplex: Optional[str] = None
def __init__(self, fields):
self.name = fields.get("name")
self.ip_address = fields.get("ip")
self.mac_address = fields.get("mac")
self.speed = fields.get("speed")
self.duplex = fields.get("duplex")
def __post_init__(self):
if not isinstance(self.name, str) or not self.name:
raise ValueError("Interface 'name' must be a non-empty string")
@@ -27,15 +35,20 @@ class VirtualInterface(Interface):
def __post_init__(self):
super().__post_init__()
if not isinstance(self.vlan_id, int) or self.vlan_id < 0:
raise ValueError("VirtualInterface 'vlan_id' must be a non-negative integer")
raise ValueError(
"VirtualInterface 'vlan_id' must be a non-negative integer"
)
if not isinstance(self.parent_physical, Interface):
raise TypeError("VirtualInterface 'parent_physical' must reference an Interface")
raise TypeError(
"VirtualInterface 'parent_physical' must reference an Interface"
)
# ===========================
# Network / Subnet
# ===========================
@dataclass
class Network:
cidr: str
@@ -53,12 +66,18 @@ class Network:
# Device Base Class (Abstract)
# ===========================
@dataclass
class Device:
name: str
mgmt_ip: Optional[str] = None
interfaces: List[Interface] = field(default_factory=list)
def __init__(self, fields):
self.name = fields.get("name")
self.mgmt_ip = fields.get("mgmtIP")
self.interfaces = [f for f in fields.get("interfaces").split(",") if f]
def __post_init__(self):
if not isinstance(self.name, str) or not self.name:
raise ValueError("Device 'name' must be a non-empty string")
@@ -70,10 +89,15 @@ class Device:
# Host / Router / Switch
# ===========================
@dataclass
class Host(Device):
operating_system: Optional[str] = None
def __init__(self, fields):
super().__init__(fields)
self.operating_system = fields.get("operatingSystem")
def __post_init__(self):
super().__post_init__()
if not isinstance(self.operating_system, str):
@@ -102,16 +126,24 @@ class Switch(Device):
# Physical Link (Wire)
# ===========================
@dataclass
class Wire:
type: str
id: str
endpoints: List[Interface] # Must contain exactly 2 interfaces
bandwidth: Optional[str] = None
def __init__(self, fields):
self.id = fields.get("name")
self.endpoints = [ep for ep in fields.get("endpoints").split(",") if ep]
self.bandwidth = fields.get("bandwidth")
def __post_init__(self):
if not isinstance(self.type, str) or not self.type:
raise ValueError("Wire 'type' must be a non-empty string")
if not isinstance(self.id, str) or not self.id:
raise ValueError("Wire 'id' must be a non-empty string")
if len(self.endpoints) != 2:
raise ValueError("Wire 'endpoints' must contain exactly 2 Interface objects")
raise ValueError(
"Wire 'endpoints' must contain exactly 2 Interface objects"
)
if not all(isinstance(i, Interface) for i in self.endpoints):
raise TypeError("Wire 'endpoints' must be Interface objects")