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
|
from parse import parse_csv_to_structure
|
||||||
|
import sys
|
||||||
|
from structures import Host, Wire
|
||||||
|
|
||||||
|
|
||||||
def check_correct(data):
|
def check_correct(data):
|
||||||
@@ -14,7 +16,7 @@ def check_correct(data):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
data = parse_csv_to_structure("input.csv")
|
data = parse_csv_to_structure(sys.argv[1])
|
||||||
if check_correct(data):
|
if check_correct(data):
|
||||||
print("yay!")
|
print("yay!")
|
||||||
print(data)
|
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
|
import csv
|
||||||
|
|
||||||
|
|
||||||
def parse_csv_to_structure(path):
|
def parse_csv_to_structure(path):
|
||||||
result = {}
|
result = {}
|
||||||
|
|
||||||
with open(path, newline='') as f:
|
with open(path, newline="") as f:
|
||||||
reader = csv.reader(f)
|
reader = csv.reader(f)
|
||||||
rows = list(reader)
|
rows = list(reader)
|
||||||
|
|
||||||
|
|||||||
+38
-6
@@ -6,6 +6,7 @@ from typing import List, Optional, Dict
|
|||||||
# Interface Classes
|
# Interface Classes
|
||||||
# ===========================
|
# ===========================
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Interface:
|
class Interface:
|
||||||
name: str
|
name: str
|
||||||
@@ -14,6 +15,13 @@ class Interface:
|
|||||||
speed: Optional[str] = None
|
speed: Optional[str] = None
|
||||||
duplex: 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):
|
def __post_init__(self):
|
||||||
if not isinstance(self.name, str) or not self.name:
|
if not isinstance(self.name, str) or not self.name:
|
||||||
raise ValueError("Interface 'name' must be a non-empty string")
|
raise ValueError("Interface 'name' must be a non-empty string")
|
||||||
@@ -27,15 +35,20 @@ class VirtualInterface(Interface):
|
|||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
super().__post_init__()
|
super().__post_init__()
|
||||||
if not isinstance(self.vlan_id, int) or self.vlan_id < 0:
|
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):
|
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
|
# Network / Subnet
|
||||||
# ===========================
|
# ===========================
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Network:
|
class Network:
|
||||||
cidr: str
|
cidr: str
|
||||||
@@ -53,12 +66,18 @@ class Network:
|
|||||||
# Device Base Class (Abstract)
|
# Device Base Class (Abstract)
|
||||||
# ===========================
|
# ===========================
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Device:
|
class Device:
|
||||||
name: str
|
name: str
|
||||||
mgmt_ip: Optional[str] = None
|
mgmt_ip: Optional[str] = None
|
||||||
interfaces: List[Interface] = field(default_factory=list)
|
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):
|
def __post_init__(self):
|
||||||
if not isinstance(self.name, str) or not self.name:
|
if not isinstance(self.name, str) or not self.name:
|
||||||
raise ValueError("Device 'name' must be a non-empty string")
|
raise ValueError("Device 'name' must be a non-empty string")
|
||||||
@@ -70,10 +89,15 @@ class Device:
|
|||||||
# Host / Router / Switch
|
# Host / Router / Switch
|
||||||
# ===========================
|
# ===========================
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Host(Device):
|
class Host(Device):
|
||||||
operating_system: Optional[str] = None
|
operating_system: Optional[str] = None
|
||||||
|
|
||||||
|
def __init__(self, fields):
|
||||||
|
super().__init__(fields)
|
||||||
|
self.operating_system = fields.get("operatingSystem")
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
super().__post_init__()
|
super().__post_init__()
|
||||||
if not isinstance(self.operating_system, str):
|
if not isinstance(self.operating_system, str):
|
||||||
@@ -102,16 +126,24 @@ class Switch(Device):
|
|||||||
# Physical Link (Wire)
|
# Physical Link (Wire)
|
||||||
# ===========================
|
# ===========================
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Wire:
|
class Wire:
|
||||||
type: str
|
id: str
|
||||||
endpoints: List[Interface] # Must contain exactly 2 interfaces
|
endpoints: List[Interface] # Must contain exactly 2 interfaces
|
||||||
bandwidth: Optional[str] = None
|
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):
|
def __post_init__(self):
|
||||||
if not isinstance(self.type, str) or not self.type:
|
if not isinstance(self.id, str) or not self.id:
|
||||||
raise ValueError("Wire 'type' must be a non-empty string")
|
raise ValueError("Wire 'id' must be a non-empty string")
|
||||||
if len(self.endpoints) != 2:
|
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):
|
if not all(isinstance(i, Interface) for i in self.endpoints):
|
||||||
raise TypeError("Wire 'endpoints' must be Interface objects")
|
raise TypeError("Wire 'endpoints' must be Interface objects")
|
||||||
|
|||||||
Reference in New Issue
Block a user