Skip to content

Commit

Permalink
Merge pull request #60 from frederic-loui/d2-export
Browse files Browse the repository at this point in the history
D2 export
  • Loading branch information
bortok authored Jul 16, 2023
2 parents 0c529d8 + 8461c82 commit a159766
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 11 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ nrx39
nrx39-dev
tests/*/test/*
tests/*/graphite/*
tests/*/d2/*
tests/main.conf
tmp
demo
13 changes: 11 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ lint:
pylint nrx/*.py

test-local: test-dc1 test-dc2 test-colo test-site1 test-h88
test: test-dc1-cyjs-2-clab test-dc2-cyjs-2-cml test-site1-cyjs-2-clab test-dc1-cyjs-2-graphite test-dc2-cyjs-2-graphite test-h88-cyjs-2-clab
test: test-dc1-cyjs-2-clab test-dc2-cyjs-2-cml test-site1-cyjs-2-clab test-dc1-cyjs-2-graphite test-dc2-cyjs-2-graphite test-h88-cyjs-2-clab test-dc1-cyjs-2-d2

test-dc1: test-dc1-nb-2-cyjs-current test-dc1-nb-2-cyjs-latest test-dc1-cyjs-2-clab test-dc1-cyjs-2-graphite
test-dc1: test-dc1-nb-2-cyjs-current test-dc1-nb-2-cyjs-latest test-dc1-cyjs-2-clab test-dc1-cyjs-2-graphite test-dc1-cyjs-2-d2
test-dc2: test-dc2-nb-2-cyjs-current test-dc2-nb-2-cyjs-latest test-dc2-cyjs-2-cml test-dc2-cyjs-2-graphite
test-colo: test-colo-nb-2-cyjs-current test-colo-nb-2-cyjs-latest
test-site1: test-site1-nb-2-cyjs-current test-site1-nb-2-cyjs-latest test-site1-cyjs-2-clab
Expand Down Expand Up @@ -48,6 +48,15 @@ test-dc1-cyjs-2-graphite:
for f in *; do echo Comparing file $$f ...; diff $$f ../data/$$f || exit 1; done
@echo

test-dc1-cyjs-2-d2:
@echo "#################################################################"
@echo "# DC1: read from CYJS and export as d2"
@echo "#################################################################"
mkdir -p tests/dc1/d2 && cd tests/dc1/d2 && rm -rf * && \
../../../nrx.py -c ../nrx.conf -i cyjs -f ../data/dc1.cyjs -o d2 -d && \
for f in *; do echo Comparing file $$f ...; diff $$f ../data/$$f || exit 1; done
@echo

test-dc2-nb-2-cyjs-current:
@echo "#################################################################"
@echo "# DC2: read from NetBox current version and export as CYJS"
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

* Topology file for [Containerlab](https://containerlab.dev) tool for container-based networking labs
* Topology file for [Cisco Modeling Labs](https://developer.cisco.com/modeling-labs/) platform for network simulation
* Topology data for visualization using [Graphite](https://github.com/netreplica/graphite)
* Topology data for visualization using [Graphite](https://github.com/netreplica/graphite) or [D2](https://d2lang.com/)
* Graph data as a JSON file in [Cytoscape](https://cytoscape.org/) format [CYJS](http://manual.cytoscape.org/en/stable/Supported_Network_File_Formats.html#cytoscape-js-json)

It can also read the topology graph previously saved as a CYJS file to convert it into other formats.
Expand Down Expand Up @@ -57,7 +57,7 @@ Export capabilities:
* Exports the graph as a Containerlab topology definition file in YAML format
* Exports the graph as a Cisco Modeling Labs (CML) topology definition file in YAML format
* Exported device configurations will be used as `startup-config` for Containerlab and CML
* Exports the graph in JSON format for visualization with Graphite
* Exports the graph in formats for visualization with Graphite or D2
* Uses NetBox Device Platform `slug` field to identify node templates when rendering the export file
* Creates mapping between real interface names and interface names used by the supported lab tools
* Calculates `level` and `rank` values for each node based on Device Role to help visualize the topology
Expand Down Expand Up @@ -136,7 +136,7 @@ optional arguments:
-h, --help show this help message and exit
-c, --config CONFIG configuration file
-i, --input INPUT input source: netbox (default) | cyjs
-o, --output OUTPUT output format: cyjs | gml | clab | cml | graphite
-o, --output OUTPUT output format: cyjs | gml | clab | cml | graphite | d2
-a, --api API netbox API URL
-s, --site SITE netbox site to export
-t, --tags TAGS netbox tags to export, for multiple tags use a comma-separated list: tag1,tag2,tag3 (uses AND logic)
Expand Down
15 changes: 10 additions & 5 deletions nrx/nrx.py
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,9 @@ def _write_topology(self, topo):
if self.config['output_format'] == 'graphite':
# <topology-name>.graphite.json
topo_file = f"{self.topology['name']}.{self.config['output_format']}.json"
elif self.config['output_format'] == 'd2':
# <topology-name>.d2
topo_file = f"{self.topology['name']}.{self.config['output_format']}"
else:
# <topology-name>.clab.yaml or <topology-name>.cml.yaml
topo_file = f"{self.topology['name']}.{self.config['output_format']}.yaml"
Expand Down Expand Up @@ -650,11 +653,13 @@ def _print_motd(self, topo):
print(f"{topo_dict['motd']}")
elif self.config['output_format'] == 'clab':
print(f"To deploy this topology, run: sudo -E clab dep -t {self.files_path}/{self.topology['name']}.clab.yaml")
elif self.config['output_format'] == 'd2':
print(f"To visualize this D2 topology, open https://play.d2lang.com and paste content of the file: {self.files_path}/{self.topology['name']}.d2")

def _render_interface_map(self, node):
"""Render interface mapping file for a node"""
if self.config['output_format'] == 'graphite':
# No need to render interface maps for Graphite
if self.config['output_format'] in ['graphite', 'd2']:
# No need to render interface maps
return None
if 'name' in node and node['name'] in self.device_interfaces_map:
d = node['name']
Expand Down Expand Up @@ -713,7 +718,7 @@ def arg_input_check(s):

def arg_output_check(s):
"""Check if output format is supported"""
allowed_values = ['gml', 'cyjs', 'clab', 'cml', 'graphite']
allowed_values = ['gml', 'cyjs', 'clab', 'cml', 'graphite', 'd2']
if s in allowed_values:
return s
raise argparse.ArgumentTypeError(f"output format has to be one of {allowed_values}")
Expand All @@ -724,7 +729,7 @@ def parse_args():
parser.add_argument('-c', '--config', required=False, help='configuration file')
parser.add_argument('-i', '--input', required=False, help='input source: netbox (default) | cyjs',
default='netbox', type=arg_input_check,)
parser.add_argument('-o', '--output', required=False, help='output format: cyjs | gml | clab | cml | graphite',
parser.add_argument('-o', '--output', required=False, help='output format: cyjs | gml | clab | cml | graphite | d2',
type=arg_output_check, )
parser.add_argument('-a', '--api', required=False, help='netbox API URL')
parser.add_argument('-s', '--site', required=False, help='netbox site to export')
Expand Down Expand Up @@ -884,7 +889,7 @@ def main():
else:
topo.build_from_graph(nb_network.graph())

if config['output_format'] in ['clab', 'cml', 'graphite']:
if config['output_format'] in ['clab', 'cml', 'graphite', 'd2']:
topo.export_topology()
else:
if nb_network is None:
Expand Down
2 changes: 1 addition & 1 deletion templates
98 changes: 98 additions & 0 deletions tests/dc1/data/dc1.d2
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
title: "" {
near: top-center
grid-columns: 2
grid-gap: 0
width: 1000

Topology.width: 500
Topology.style.font-size: 32
dc1.width: 500
dc1.style.bold: false
dc1.style.font-size: 32
}

direction: right

style: {
fill: transparent
}

dc1-leaf-1: {
shape: circle
}
dc1-spine-1: {
shape: circle
}
dc1-spine-2: {
shape: circle
}
dc1-leaf-2: {
shape: circle
}
dc1-leaf-3: {
shape: circle
}
dc1-leaf-4: {
shape: circle
}
dc1-srv-1: {
shape: circle
}
dc1-srv-2: {
shape: circle
}
dc1-srv-3: {
shape: circle
}
dc1-srv-4: {
shape: circle
}

dc1-leaf-1 -- dc1-spine-1: {
source-arrowhead.label: ethernet-1/49
target-arrowhead.label: Ethernet1/1
}
dc1-leaf-1 -- dc1-spine-2: {
source-arrowhead.label: ethernet-1/50
target-arrowhead.label: Ethernet1/1
}
dc1-leaf-2 -- dc1-spine-1: {
source-arrowhead.label: ethernet-1/49
target-arrowhead.label: Ethernet2/1
}
dc1-leaf-2 -- dc1-spine-2: {
source-arrowhead.label: ethernet-1/50
target-arrowhead.label: Ethernet2/1
}
dc1-leaf-3 -- dc1-spine-1: {
source-arrowhead.label: ethernet-1/49
target-arrowhead.label: Ethernet3/1
}
dc1-leaf-3 -- dc1-spine-2: {
source-arrowhead.label: ethernet-1/50
target-arrowhead.label: Ethernet3/1
}
dc1-leaf-4 -- dc1-spine-1: {
source-arrowhead.label: ethernet-1/49
target-arrowhead.label: Ethernet4/1
}
dc1-leaf-4 -- dc1-spine-2: {
source-arrowhead.label: ethernet-1/50
target-arrowhead.label: Ethernet4/1
}
dc1-leaf-1 -- dc1-srv-1: {
source-arrowhead.label: ethernet-1/1
target-arrowhead.label: Gig-E 1
}
dc1-leaf-2 -- dc1-srv-2: {
source-arrowhead.label: ethernet-1/1
target-arrowhead.label: Gig-E 1
}
dc1-leaf-3 -- dc1-srv-3: {
source-arrowhead.label: ethernet-1/1
target-arrowhead.label: Gig-E 1
}
dc1-leaf-4 -- dc1-srv-4: {
source-arrowhead.label: ethernet-1/1
target-arrowhead.label: Gig-E 1
}

0 comments on commit a159766

Please sign in to comment.