diff --git a/UML.wsd b/UML.wsd deleted file mode 100644 index 0d00ee3..0000000 --- a/UML.wsd +++ /dev/null @@ -1,8 +0,0 @@ -@startuml - -class Device { - +id: string - +name: string - +model: string - +getInterfaces() -} \ No newline at end of file diff --git a/UML/UML.png b/UML/UML.png new file mode 100644 index 0000000..1aff8d4 Binary files /dev/null and b/UML/UML.png differ diff --git a/UML/UML.txt b/UML/UML.txt new file mode 100644 index 0000000..6fd6825 --- /dev/null +++ b/UML/UML.txt @@ -0,0 +1,79 @@ +@startuml + +' =========================== +' Abstract base class +' =========================== + +abstract class Device { + - name : String + - mgmtIP : String + - interfaces : Interface[*] +} + +Device "1" o-- "0..*" Interface : has > + +' =========================== +' Device Types +' =========================== + +class Host { + - operatingSystem : String +} + +class Router { + - routingTable : Map + - routingProtocols : List +} + +class Switch { + - macTable : Map + - vlanDatabase : Map +} + +Device <|-- Host +Device <|-- Router +Device <|-- Switch + +' =========================== +' Interfaces +' =========================== + +class Interface { + - name : String + - ipAddress : String + - macAddress : String + - speed : String + - duplex : String +} + +class VirtualInterface { + - vlanId : Integer + - parentPhysical : Interface +} + +Interface <|-- VirtualInterface + +' =========================== +' Physical Links +' =========================== + +class Wire { + - type : String + - endpoints : Interface[2] + - bandwidth : String +} + +Wire "1" -- "2" Interface : connects > + +' =========================== +' Optional: Networks / Subnets +' =========================== + +class Network { + - cidr : String + - gateway : String +} + +Network "1" o-- "0..*" Interface : membership > + +@enduml diff --git a/UML/structure.jpg b/UML/structure.jpg new file mode 100644 index 0000000..8f33bdb Binary files /dev/null and b/UML/structure.jpg differ diff --git a/input.csv b/examples/input.csv similarity index 100% rename from input.csv rename to examples/input.csv diff --git a/examples/template.yaml b/examples/template.yaml new file mode 100644 index 0000000..e08905b --- /dev/null +++ b/examples/template.yaml @@ -0,0 +1,160 @@ +# yaml-language-server: $schema=./topology-schema.json +schema: asvk.topology/1.0 +meta: + id: "string" + title: "string" + description: "string" + +provider: + name: virtualbox # virtualbox | kvm | container + defaults: + cpu: 1 + ram_mb: 512 + disk_gb: 8 + os_image: "debian-12-cloud.qcow2" + nic_model: virtio + +defaults: + routing: + stack: bird # bird | frr | linux + protocols_enabled: [] + switch: + impl: linux-bridge # linux-bridge | ovs + firewall: + impl: nftables # nftables | none + mgmt: + ssh_user: "lab" + ssh_key: "~/.ssh/id_rsa.pub" + +variables: # for templates + asn: 65000 + domain: "lab.local" + +networks: # L2 / L3 / VLAN trunks + - id: lan1 + type: l2 # l2 | l3 + mtu: 1500 + - id: lan13 + type: l2 + - id: trunk1 + type: l2 + vlan: + mode: trunk + allowed: [4, 8] + - id: mgmt + type: l3 + cidr: 192.168.100.0/24 + dhcp: false + +nodes: # nodes (VM/containers) + - name: R1 + role: router # router | switch | host + resources: + cpu: 1 + ram_mb: 512 + mgmt: + ip: 192.168.100.11/24 + gw: 192.168.100.1 + net: mgmt + interfaces: # order matters: eth1 ↔ adapter2 and so on + - name: eth1 + network: lan13 + addresses: ["10.0.13.1/24"] + - name: eth0 # MGMT + network: mgmt + routing: + stack: bird # inherited from defaults, can be omitted + protocols: + static: + routes: + - to: 0.0.0.0/0 + via: 10.0.13.3 + ospf: # if needed + enabled: false + rip: + enabled: false + services: # servers/daemons of the host + - name: sshd + enabled: true + +switching: # L2 config for switch nodes + - node: S1 + impl: linux-bridge # linux-bridge | ovs + bridges: + - name: br0 + ports: + - if: eth1 # to trunk1 + vlan: + mode: trunk + allowed: [4, 8] + - if: eth2 # access → VLAN 4 + vlan: + mode: access + vid: 4 + - if: eth3 # access → VLAN 8 + vlan: + mode: access + vid: 8 + svis: # VLAN management interfaces (SVI) + - vid: 4 + ifname: br0.4 + addresses: ["10.0.4.254/24"] + +firewall: # High-level ACL-> to nftables + - node: R2 + rules: + - id: allow-ospf + match: + in_if: eth1 + ip_proto: ospf + action: accept + - id: telnet-from-R1-to-S1 + match: + src: 10.0.13.1/32 + dst: 10.0.4.254/32 + l4: { proto: tcp, dport: 23 } + action: accept + - id: ftp-from-R3-to-S2 + match: + src: 10.0.13.3/32 + dst: 10.0.6.254/32 + l4: { proto: tcp, dports: [20, 21] } + action: accept + - id: drop-rest + action: drop + +profiles: # overlays - patches on top of the base description + - name: "all-frr" + description: "Switch all control-plane to FRR" + patches: + - op: replace + path: /defaults/routing/stack + value: frr + - name: "mix-bird-frr-ospf" + description: "BIRD by default, but OSPF on R2 via FRR" + patches: + - op: add + path: /nodes + where: + name: R2 + patch: + routing: + protocols: + ospf: + enabled: true + impl: frr + - name: "switch-ovs" + patches: + - op: replace + path: /defaults/switch/impl + value: ovs + +tests: # tests (expected result) + - name: ping-R1-to-R3 + from: R1 + to: 10.0.13.3 + expect: success + - name: ping-VLAN-4-R1-to-R3 + from: R1 + to: 10.0.4.1 + expect: success diff --git a/examples/topology-schema.json b/examples/topology-schema.json new file mode 100644 index 0000000..4baf645 --- /dev/null +++ b/examples/topology-schema.json @@ -0,0 +1,607 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://github.com/asvk-topology/schema/1.0", + "title": "ASVK Topology Configuration Schema", + "description": "JSON Schema for ASVK topology configuration files", + "type": "object", + "required": ["schema", "meta", "provider", "nodes"], + "properties": { + "schema": { + "type": "string", + "description": "Schema identifier", + "pattern": "^asvk\\.topology/[0-9]+\\.[0-9]+$", + "examples": ["asvk.topology/1.0"] + }, + "meta": { + "type": "object", + "description": "Metadata for the topology configuration", + "required": ["id", "title"], + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for the topology", + "pattern": "^[a-zA-Z0-9_-]+$" + }, + "title": { + "type": "string", + "description": "Human-readable name for the topology" + }, + "description": { + "type": "string", + "description": "Detailed description of the topology" + } + } + }, + "provider": { + "type": "object", + "description": "Provider configuration for VMs/containers", + "required": ["name"], + "properties": { + "name": { + "type": "string", + "description": "Provider name", + "enum": ["virtualbox", "kvm", "container"] + }, + "defaults": { + "type": "object", + "description": "Default resource allocation", + "properties": { + "cpu": { + "type": "integer", + "description": "Default CPU cores", + "minimum": 1, + "default": 1 + }, + "ram_mb": { + "type": "integer", + "description": "Default RAM in MB", + "minimum": 128, + "default": 512 + }, + "disk_gb": { + "type": "integer", + "description": "Default disk size in GB", + "minimum": 1, + "default": 8 + }, + "os_image": { + "type": "string", + "description": "Default OS image" + }, + "nic_model": { + "type": "string", + "description": "Default NIC model", + "enum": ["virtio", "e1000", "rtl8139"] + }, + "promiscuous": { + "type": "string", + "description": "Promiscuous mode setting", + "enum": ["deny", "allow-vms", "allow-all"] + } + } + }, + "images": { + "type": "object", + "description": "Custom images for different node types", + "properties": { + "router": { "type": "string" }, + "switch": { "type": "string" }, + "host": { "type": "string" } + } + } + } + }, + "defaults": { + "type": "object", + "description": "Default behavior and software configuration", + "properties": { + "routing": { + "type": "object", + "properties": { + "stack": { + "type": "string", + "enum": ["bird", "frr", "linux"], + "default": "bird" + }, + "protocols_enabled": { + "type": "array", + "items": { + "type": "string", + "enum": ["ospf", "rip", "bgp", "static"] + }, + "default": [] + } + } + }, + "switch": { + "type": "object", + "properties": { + "impl": { + "type": "string", + "enum": ["linux-bridge", "ovs"], + "default": "linux-bridge" + } + } + }, + "firewall": { + "type": "object", + "properties": { + "impl": { + "type": "string", + "enum": ["nftables", "none"], + "default": "nftables" + } + } + }, + "mgmt": { + "type": "object", + "properties": { + "ssh_user": { + "type": "string", + "default": "lab" + }, + "ssh_key": { + "type": "string", + "description": "Path to SSH public key" + } + } + } + } + }, + "variables": { + "type": "object", + "description": "Global variables that can be referenced in templates", + "additionalProperties": true + }, + "networks": { + "type": "array", + "description": "Network definitions (L2 domains, L3 networks, VLAN trunks)", + "items": { + "type": "object", + "required": ["id", "type"], + "properties": { + "id": { + "type": "string", + "description": "Unique network identifier", + "pattern": "^[a-zA-Z0-9_-]+$" + }, + "type": { + "type": "string", + "enum": ["l2", "l3"], + "description": "Network type" + }, + "mtu": { + "type": "integer", + "description": "MTU for the network", + "minimum": 68, + "maximum": 9000, + "default": 1500 + }, + "cidr": { + "type": "string", + "description": "CIDR notation for L3 networks", + "pattern": "^([0-9]{1,3}\\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$" + }, + "dhcp": { + "type": "boolean", + "description": "Enable DHCP on this network", + "default": false + }, + "vlan": { + "type": "object", + "description": "VLAN configuration for trunk ports", + "properties": { + "mode": { + "type": "string", + "enum": ["access", "trunk"] + }, + "allowed": { + "type": "array", + "items": { + "type": "integer", + "minimum": 1, + "maximum": 4094 + }, + "description": "Allowed VLAN IDs on trunk" + }, + "vid": { + "type": "integer", + "description": "Native VLAN ID for access ports", + "minimum": 1, + "maximum": 4094 + } + } + } + } + } + }, + "nodes": { + "type": "array", + "description": "Node definitions (VMs/containers)", + "items": { + "type": "object", + "required": ["name", "role"], + "properties": { + "name": { + "type": "string", + "description": "Node name", + "pattern": "^[a-zA-Z0-9_-]+$" + }, + "role": { + "type": "string", + "enum": ["router", "switch", "host"], + "description": "Node role/type" + }, + "image": { + "type": "string", + "description": "OS image for the node (can reference provider.images)" + }, + "resources": { + "type": "object", + "properties": { + "cpu": { + "type": "integer", + "minimum": 1 + }, + "ram_mb": { + "type": "integer", + "minimum": 128 + }, + "disk_gb": { + "type": "integer", + "minimum": 1 + } + } + }, + "mgmt": { + "type": "object", + "description": "Management network configuration", + "properties": { + "ip": { + "type": "string", + "pattern": "^([0-9]{1,3}\\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$" + }, + "gw": { + "type": "string", + "pattern": "^([0-9]{1,3}\\.){3}[0-9]{1,3}$" + }, + "net": { + "type": "string" + } + } + }, + "interfaces": { + "type": "array", + "description": "Network interfaces (order matters)", + "items": { + "type": "object", + "required": ["name"], + "properties": { + "name": { + "type": "string", + "pattern": "^(eth[0-9]+|lo)$" + }, + "network": { + "type": "string" + }, + "loopback": { + "type": "boolean", + "description": "Indicates if this is a loopback interface" + }, + "addresses": { + "type": "array", + "items": { + "type": "string", + "pattern": "^([0-9]{1,3}\\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$" + } + } + }, + "anyOf": [ + { + "required": ["network"] + }, + { + "required": ["loopback"], + "properties": { + "loopback": { "const": true } + } + } + ] + } + }, + "routing": { + "type": "object", + "properties": { + "stack": { + "type": "string", + "enum": ["bird", "frr", "linux"] + }, + "protocols": { + "type": "object", + "properties": { + "static": { + "type": "object", + "properties": { + "routes": { + "type": "array", + "items": { + "type": "object", + "required": ["to", "via"], + "properties": { + "to": { + "type": "string", + "pattern": "^([0-9]{1,3}\\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])|0\\.0\\.0\\.0/0$" + }, + "via": { + "type": "string", + "pattern": "^([0-9]{1,3}\\.){3}[0-9]{1,3}$" + } + } + } + } + } + }, + "ospf": { + "type": "object", + "properties": { + "enabled": { "type": "boolean", "default": false }, + "impl": { + "type": "string", + "enum": ["bird", "frr"] + } + } + }, + "rip": { + "type": "object", + "properties": { + "enabled": { "type": "boolean", "default": false } + } + }, + "bgp": { + "type": "object", + "properties": { + "enabled": { "type": "boolean", "default": false }, + "asn": { "type": "integer" } + } + } + } + } + } + }, + "services": { + "type": "array", + "items": { + "type": "object", + "required": ["name"], + "properties": { + "name": { "type": "string" }, + "enabled": { "type": "boolean", "default": true } + } + } + } + } + } + }, + "switching": { + "type": "array", + "description": "L2 switching configuration", + "items": { + "type": "object", + "required": ["node", "impl"], + "properties": { + "node": { + "type": "string", + "description": "Switch node name" + }, + "impl": { + "type": "string", + "enum": ["linux-bridge", "ovs"] + }, + "bridges": { + "type": "array", + "items": { + "type": "object", + "required": ["name"], + "properties": { + "name": { "type": "string" }, + "ports": { + "type": "array", + "items": { + "type": "object", + "required": ["if"], + "properties": { + "if": { + "type": "string", + "pattern": "^eth[0-9]+$" + }, + "vlan": { + "type": "object", + "properties": { + "mode": { + "type": "string", + "enum": ["access", "trunk"] + }, + "allowed": { + "type": "array", + "items": { + "type": "integer", + "minimum": 1, + "maximum": 4094 + } + }, + "vid": { + "type": "integer", + "minimum": 1, + "maximum": 4094 + } + } + } + } + } + }, + "svis": { + "type": "array", + "description": "Switched Virtual Interfaces (SVI)", + "items": { + "type": "object", + "required": ["vid", "ifname"], + "properties": { + "vid": { + "type": "integer", + "minimum": 1, + "maximum": 4094 + }, + "ifname": { + "type": "string", + "pattern": "^[a-zA-Z0-9._-]+$" + }, + "addresses": { + "type": "array", + "items": { + "type": "string", + "pattern": "^([0-9]{1,3}\\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$" + } + } + } + } + } + } + } + } + } + } + }, + "firewall": { + "type": "array", + "description": "Firewall configuration", + "items": { + "type": "object", + "required": ["node", "rules"], + "properties": { + "node": { "type": "string" }, + "rules": { + "type": "array", + "items": { + "type": "object", + "required": ["id", "action"], + "properties": { + "id": { + "type": "string", + "pattern": "^[a-zA-Z0-9_-]+$" + }, + "match": { + "type": "object", + "properties": { + "in_if": { "type": "string" }, + "out_if": { "type": "string" }, + "src": { + "type": "string", + "pattern": "^([0-9]{1,3}\\.){3}[0-9]{1,3}(/([0-9]|[1-2][0-9]|3[0-2]))?$" + }, + "dst": { + "type": "string", + "pattern": "^([0-9]{1,3}\\.){3}[0-9]{1,3}(/([0-9]|[1-2][0-9]|3[0-2]))?$" + }, + "ip_proto": { + "type": "string", + "enum": ["tcp", "udp", "icmp", "ospf", "all"] + }, + "l4": { + "type": "object", + "properties": { + "proto": { + "type": "string", + "enum": ["tcp", "udp"] + }, + "sport": { + "type": ["integer", "array"], + "items": { "type": "integer" }, + "minimum": 1, + "maximum": 65535 + }, + "dport": { + "type": ["integer", "array"], + "items": { "type": "integer" }, + "minimum": 1, + "maximum": 65535 + }, + "dports": { + "type": ["integer", "array"], + "items": { "type": "integer" }, + "minimum": 1, + "maximum": 65535 + } + } + } + } + }, + "action": { + "type": "string", + "enum": ["accept", "drop", "reject"] + } + } + } + } + } + } + }, + "profiles": { + "type": "array", + "description": "Configuration profiles/overlays", + "items": { + "type": "object", + "required": ["name", "patches"], + "properties": { + "name": { "type": "string" }, + "description": { "type": "string" }, + "patches": { + "type": "array", + "items": { + "type": "object", + "required": ["op", "path"], + "properties": { + "op": { + "type": "string", + "enum": ["add", "replace", "remove"] + }, + "path": { "type": "string" }, + "value": true, + "where": { + "type": "object", + "description": "Conditional matching for patches" + }, + "patch": true + } + } + } + } + } + }, + "tests": { + "type": "array", + "description": "Validation tests", + "items": { + "type": "object", + "required": ["name", "from", "to", "expect"], + "properties": { + "name": { "type": "string" }, + "from": { "type": "string" }, + "to": { + "type": ["string", "object"], + "properties": { + "ip": { + "type": "string", + "pattern": "^([0-9]{1,3}\\.){3}[0-9]{1,3}$" + }, + "node": { "type": "string" } + } + }, + "expect": { + "type": "string", + "pattern": "^(success|failure|timeout|via .*)$" + } + } + } + } + } +} diff --git a/check_correct.py b/src/check_correct.py similarity index 100% rename from check_correct.py rename to src/check_correct.py diff --git a/parse.py b/src/parse.py similarity index 100% rename from parse.py rename to src/parse.py diff --git a/structures.py b/src/structures.py similarity index 100% rename from structures.py rename to src/structures.py