some terror with structures
This commit is contained in:
@@ -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"
|
||||
|
+16
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
+2
-1
@@ -1,9 +1,10 @@
|
||||
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)
|
||||
|
||||
|
||||
+38
-6
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user