├── .$output.xml.bkp ├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── drawio_network_plot.toml ├── drawio_network_plot ├── __init__.py └── drawio_network_plot.py ├── examples ├── .DS_Store ├── cisco_gather_cdp_and_plot │ ├── .DS_Store │ ├── cisco_gather_cdp_and_plot.py │ └── device_configurations │ │ ├── Leaf_1 │ │ ├── Leaf_2 │ │ ├── Leaf_3 │ │ ├── Router_1 │ │ ├── Router_2 │ │ ├── Spine_1 │ │ └── Spine_2 ├── example_1_output_snapshot.png └── output.xml ├── icons ├── Firewall.png ├── L2_switch.png ├── L3_switch.png ├── Router.png └── Server.png ├── output.xml ├── setup.py ├── test_temp.py └── tests ├── __init__.py └── unit_test.py /.$output.xml.bkp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 105 | __pypackages__/ 106 | 107 | # Celery stuff 108 | celerybeat-schedule 109 | celerybeat.pid 110 | 111 | # SageMath parsed files 112 | *.sage.py 113 | 114 | # Environments 115 | .env 116 | .venv 117 | env/ 118 | venv/ 119 | ENV/ 120 | env.bak/ 121 | venv.bak/ 122 | 123 | # Spyder project settings 124 | .spyderproject 125 | .spyproject 126 | 127 | # Rope project settings 128 | .ropeproject 129 | 130 | # mkdocs documentation 131 | /site 132 | 133 | # mypy 134 | .mypy_cache/ 135 | .dmypy.json 136 | dmypy.json 137 | 138 | # Pyre type checker 139 | .pyre/ 140 | 141 | # pytype static type analyzer 142 | .pytype/ 143 | 144 | # Cython debug symbols 145 | cython_debug/ 146 | 147 | # PyCharm 148 | # JetBrains specific template is maintainted in a separate JetBrains.gitignore that can 149 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 150 | # and can be added to the global gitignore or merged into this file. For a more nuclear 151 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 152 | #.idea/ 153 | 154 | # General 155 | .DS_Store 156 | .AppleDouble 157 | .LSOverride 158 | 159 | # Icon must end with two \r 160 | Icon 161 | 162 | # Thumbnails 163 | ._* 164 | 165 | # Files that might appear in the root of a volume 166 | .DocumentRevisions-V100 167 | .fseventsd 168 | .Spotlight-V100 169 | .TemporaryItems 170 | .Trashes 171 | .VolumeIcon.icns 172 | .com.apple.timemachine.donotpresent 173 | 174 | # Directories potentially created on remote AFP share 175 | .AppleDB 176 | .AppleDesktop 177 | Network Trash Folder 178 | Temporary Items 179 | .apdisk -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Amr ElHusseiny 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # drawio_network_plot 2 | ## Overview 3 | - Package is mainly created for Network Automation , can be used in conjunction with other libraries to generate DrawIO plot for your Network (Service Provider ot Data Centter Network). 4 | - Available devices : Router , L3_Switch , L2_switch , Firewall , Server ( More to be added ) 5 | - Output generated can be directly opened by DrawIO application (Desktop or Web version) 6 | ## Understanding the logic 7 | The "NetPlot" class is itself the plot , you initiate it and then you can do the following : 8 | - Add a Node or a Nodes list . 9 | - Add an Edge or a list of Edges . 10 | >Note : the edges has Source and Destination nodes , better to always put the Parent device in the Source node and the Child node in the Destination node , this will affect the way DrawIO does automatic Layouts . 11 | - After you open the file in DrawIO , you will see everything stacked on top of each other , simply go to "Arrange/Layout/Vertical Tree" and it will arrange the nodes and like the hirarichy of the Data center , you can also try out other layout options . 12 | ## Examples 13 | ### Example 1 : Datacenter Plot 14 | this example demonstrates how to use both the addition of object or list of objects at once : 15 | #### Code 16 | ```python 17 | 18 | from drawio_network_plot import NetPlot 19 | 20 | device_list = [ 21 | # Routers 22 | {'nodeName' : 'Router_1','nodeType' : 'router','nodeDescription' : 'External Peering Provider 1'}, 23 | {'nodeName' : 'Router_2','nodeType' : 'router','nodeDescription' : 'External Peering Provider 2'}, 24 | # Core 25 | {'nodeName' : 'Core_switch_1','nodeType' : 'l3_switch','nodeDescription' : 'Spine Switch 01'}, 26 | {'nodeName' : 'Core_switch_2','nodeType' : 'l3_switch','nodeDescription' : 'Spine Switch 02'}, 27 | # Firewalls 28 | {'nodeName' : 'FW_1','nodeType' : 'firewall','nodeDescription' : 'Firewall 01'}, 29 | {'nodeName' : 'FW_2','nodeType' : 'firewall','nodeDescription' : 'Firewall 02'}, 30 | # Leafs 31 | {'nodeName' : 'TOR_1','nodeType' : 'l2_switch','nodeDescription' : 'Leaf Switch 01'}, 32 | {'nodeName' : 'TOR_2','nodeType' : 'l2_switch','nodeDescription' : 'Leaf Switch 02'}, 33 | {'nodeName' : 'TOR_3','nodeType' : 'l2_switch','nodeDescription' : 'Leaf Switch 03'}, 34 | {'nodeName' : 'TOR_4','nodeType' : 'l2_switch','nodeDescription' : 'Leaf Switch 04'}, 35 | # Servers 36 | {'nodeName' : 'Server_1','nodeType' : 'server','nodeDescription' : 'Server 1'}, 37 | {'nodeName' : 'Server_2','nodeType' : 'server','nodeDescription' : 'Server 2'}, 38 | {'nodeName' : 'Server_3','nodeType' : 'server','nodeDescription' : 'Server 3'}, 39 | {'nodeName' : 'Server_4','nodeType' : 'server','nodeDescription' : 'Server 4'}, 40 | {'nodeName' : 'Server_5','nodeType' : 'server','nodeDescription' : 'Server 5'}, 41 | {'nodeName' : 'Server_6','nodeType' : 'server','nodeDescription' : 'Server 6'}, 42 | {'nodeName' : 'Server_7','nodeType' : 'server','nodeDescription' : 'Server 7'}, 43 | {'nodeName' : 'Server_8','nodeType' : 'server','nodeDescription' : 'Server 8'}, 44 | {'nodeName' : 'Server_9','nodeType' : 'server','nodeDescription' : 'Server 9'}, 45 | {'nodeName' : 'Server_10','nodeType' : 'server','nodeDescription' : 'Server 10'}, 46 | {'nodeName' : 'Server_11','nodeType' : 'server','nodeDescription' : 'Server 11'}, 47 | {'nodeName' : 'Server_12','nodeType' : 'server','nodeDescription' : 'Server 12'}, 48 | {'nodeName' : 'Server_13','nodeType' : 'server','nodeDescription' : 'Server 13'}, 49 | {'nodeName' : 'Server_14','nodeType' : 'server','nodeDescription' : 'Server 14'}, 50 | {'nodeName' : 'Server_15','nodeType' : 'server','nodeDescription' : 'Server 15'}, 51 | {'nodeName' : 'Server_16','nodeType' : 'server','nodeDescription' : 'Server 16'}, 52 | ] 53 | 54 | 55 | connection_list = [ 56 | # Router to Core 57 | {'sourceNodeID' : 'Router_1','destinationNodeID' : 'Core_switch_1'}, 58 | {'sourceNodeID' : 'Router_1','destinationNodeID' : 'Core_switch_2'}, 59 | {'sourceNodeID' : 'Router_2','destinationNodeID' : 'Core_switch_1'}, 60 | {'sourceNodeID' : 'Router_2','destinationNodeID' : 'Core_switch_2'}, 61 | # Core to FW 62 | {'sourceNodeID' : 'Core_switch_1','destinationNodeID' : 'FW_1'}, 63 | {'sourceNodeID' : 'Core_switch_2','destinationNodeID' : 'FW_2'}, 64 | # Core to TOR 65 | {'sourceNodeID' : 'Core_switch_1','destinationNodeID' : 'TOR_1'}, 66 | {'sourceNodeID' : 'Core_switch_1','destinationNodeID' : 'TOR_2'}, 67 | {'sourceNodeID' : 'Core_switch_1','destinationNodeID' : 'TOR_3'}, 68 | {'sourceNodeID' : 'Core_switch_1','destinationNodeID' : 'TOR_4'}, 69 | {'sourceNodeID' : 'Core_switch_2','destinationNodeID' : 'TOR_1'}, 70 | {'sourceNodeID' : 'Core_switch_2','destinationNodeID' : 'TOR_2'}, 71 | {'sourceNodeID' : 'Core_switch_2','destinationNodeID' : 'TOR_3'}, 72 | {'sourceNodeID' : 'Core_switch_2','destinationNodeID' : 'TOR_4'}, 73 | # TOR to Server 74 | {'sourceNodeID' : 'TOR_1','destinationNodeID' : 'Server_1'}, 75 | {'sourceNodeID' : 'TOR_2','destinationNodeID' : 'Server_2'}, 76 | {'sourceNodeID' : 'TOR_3','destinationNodeID' : 'Server_3'}, 77 | {'sourceNodeID' : 'TOR_4','destinationNodeID' : 'Server_4'}, 78 | {'sourceNodeID' : 'TOR_1','destinationNodeID' : 'Server_5'}, 79 | {'sourceNodeID' : 'TOR_2','destinationNodeID' : 'Server_6'}, 80 | {'sourceNodeID' : 'TOR_3','destinationNodeID' : 'Server_7'}, 81 | {'sourceNodeID' : 'TOR_4','destinationNodeID' : 'Server_8'}, 82 | {'sourceNodeID' : 'TOR_1','destinationNodeID' : 'Server_9'}, 83 | {'sourceNodeID' : 'TOR_2','destinationNodeID' : 'Server_10'}, 84 | {'sourceNodeID' : 'TOR_3','destinationNodeID' : 'Server_11'}, 85 | {'sourceNodeID' : 'TOR_4','destinationNodeID' : 'Server_12'}, 86 | {'sourceNodeID' : 'TOR_1','destinationNodeID' : 'Server_13'}, 87 | {'sourceNodeID' : 'TOR_2','destinationNodeID' : 'Server_14'}, 88 | {'sourceNodeID' : 'TOR_3','destinationNodeID' : 'Server_15'}, 89 | {'sourceNodeID' : 'TOR_4','destinationNodeID' : 'Server_16'}, 90 | ] 91 | # Initiating an Plot instance 92 | x = NetPlot() 93 | # Adding node by node and edge by edge 94 | x.addNode(nodeName='Router_17',nodeType='router') 95 | x.addNode(nodeName='Router_18',nodeType='router') 96 | # Adding lists of nodes and edges 97 | x.addLink('Router_17','Router_18') 98 | x.addLink('Router_17','Router_1') 99 | x.addNodeList(device_list) 100 | x.addLinkList(connection_list) 101 | 102 | # Output 103 | # 1- using method "display_xml" 104 | print(x.display_xml()) 105 | # 2- printing the class will automatically call the "display_xml" method 106 | print(x) 107 | # 3- Exporting to XML file directly 108 | x.exportXML('examples/output.xml') 109 | 110 | ``` 111 | #### Output 112 | ![Data Center Example output](examples/example_1_output_snapshot.png) -------------------------------------------------------------------------------- /drawio_network_plot.toml: -------------------------------------------------------------------------------- 1 | # drawio_network_plot.toml 2 | 3 | [build-system] 4 | requires = ["setuptools>=46.4.0", "wheel"] 5 | build-backend = "setuptools.build_meta" -------------------------------------------------------------------------------- /drawio_network_plot/__init__.py: -------------------------------------------------------------------------------- 1 | # __init__.py 2 | from .drawio_network_plot import NetPlot 3 | #import drawio_network_plot -------------------------------------------------------------------------------- /drawio_network_plot/drawio_network_plot.py: -------------------------------------------------------------------------------- 1 | import xml.etree.ElementTree as ET 2 | import logging 3 | 4 | 5 | class NetPlot(): 6 | ''' 7 | Creates the DrawIO Plot XML file , based on Jgraph template 8 | ''' 9 | def __init__(self): 10 | # initiating the blocks needed for the DrawIO XML template , standard in every plot 11 | self.mxfile = ET.Element('mxfile',host="Electron",agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/19.0.0 Chrome/100.0.4896.160 Electron/18.3.2 Safari/537.36",type="device") 12 | self.diagram = ET.SubElement(self.mxfile,'diagram',id="diagram_1",name="Page-1") 13 | self.mxGraphModel = ET.SubElement(self.diagram, 14 | 'mxGraphModel', 15 | grid="1", 16 | gridSize="10", 17 | guides="1", 18 | tooltips="1", 19 | connect="1", 20 | arrows="1", 21 | fold="1", 22 | page="1", 23 | pageScale="1", 24 | pageWidth="850", 25 | pageHeight="1100", 26 | math="0", 27 | shadow="0") 28 | self.root = ET.SubElement(self.mxGraphModel,'root') 29 | self.mxCellID0 = ET.SubElement(self.root,'mxCell' , id="0") 30 | # Each Node and Edge are a child to the Parent id "1" 31 | self.mxCellID1 = ET.SubElement(self.root,'mxCell' , id="1" , parent="0" , style=";html=1;") 32 | 33 | def _getMXgraphShape(self,nodeType): 34 | # Defining the shape that can be used , it uses shapes already shipped with DrawIO application (More shapes will be added) 35 | # we are also using the "nodeLevel" variable to set the hirarichy of the plot if standards are not adhered by in using library 36 | if nodeType == 'router': 37 | return {'style':'shape=mxgraph.cisco19.rect;prIcon=router;fillColor=#FAFAFA;strokeColor=#005073;html=1;', 38 | 'width':'50', 39 | 'height':'50', 40 | 'nodeLevel':'1' 41 | } 42 | elif nodeType == 'l2_switch': 43 | return {'style':'shape=mxgraph.cisco19.rect;prIcon=l2_switch;fillColor=#FAFAFA;strokeColor=#005073;html=1;', 44 | 'width':'50', 45 | 'height':'50', 46 | 'nodeLevel':'3'} 47 | elif nodeType == 'l3_switch': 48 | return {'style':'shape=mxgraph.cisco19.rect;prIcon=l3_switch;fillColor=#FAFAFA;strokeColor=#005073;html=1;', 49 | 'width':'50', 50 | 'height':'50', 51 | 'nodeLevel':'2'} 52 | elif nodeType == 'firewall': 53 | return {'style':'shape=mxgraph.cisco19.rect;prIcon=firewall;fillColor=#FAFAFA;strokeColor=#005073;html=1;', 54 | 'width':'64', 55 | 'height':'50', 56 | 'nodeLevel':'2'} 57 | elif nodeType == 'server': 58 | return {'style':'shape=mxgraph.cisco19.rect;prIcon=server;fillColor=#FAFAFA;strokeColor=#005073;html=1;', 59 | 'width':'27', 60 | 'height':'50', 61 | 'nodeLevel':'4'} 62 | else : 63 | return {'style':'shape=mxgraph.cisco19.rect;prIcon=server;fillColor=#FAFAFA;strokeColor=#005073;html=1;', 64 | 'width':'27', 65 | 'height':'50', 66 | 'nodeLevel':'4'} 67 | 68 | 69 | def addNode(self,nodeName,nodeDescription='',nodeType=''): 70 | try: 71 | shapeParameters = self._getMXgraphShape(nodeType) 72 | mxCell = ET.SubElement(self.root, 73 | 'mxCell', 74 | id=nodeName, 75 | value=nodeName, 76 | style=("verticalLabelPosition=bottom;" 77 | "html=1;" 78 | "verticalAlign=top;" 79 | "aspect=fixed;align=center;" 80 | "pointerEvents=1;" 81 | f"{shapeParameters['style']}" 82 | ""), 83 | parent="1", 84 | vertex="1") 85 | mxGeometry = ET.SubElement(mxCell, 'mxGeometry',width=shapeParameters['width'] ,height=shapeParameters['height'] ) 86 | mxGeometry.set('as','geometry') 87 | return 88 | except Exception as e: 89 | logging.error('Error in adding Node: {}'.format(e)) 90 | 91 | def addNodeList(self,nodeListOfDictionary): 92 | try: 93 | for node in nodeListOfDictionary: 94 | self.addNode(nodeName=node['nodeName'],nodeDescription=node['nodeDescription'],nodeType=node['nodeType']) 95 | return 96 | except Exception as e: 97 | logging.error('Error in adding Node List: {}'.format(e)) 98 | 99 | def addLink(self,sourceNodeID,destinationNodeID): 100 | try: 101 | for mxCell in self.root: 102 | if mxCell.attrib['id'] == sourceNodeID: 103 | logging.debug('Source Node ID {} found'.format(sourceNodeID)) 104 | break 105 | return 106 | except Exception as e: 107 | logging.error('Error in adding Link: {}'.format(e)) 108 | 109 | mxCell = ET.SubElement(self.root, 110 | 'mxCell', 111 | id=sourceNodeID+destinationNodeID, 112 | style="endFill=0;endArrow=none;", 113 | parent="1", 114 | source=sourceNodeID, 115 | target=destinationNodeID, 116 | edge="1") 117 | mxGeometry = ET.SubElement(mxCell, 'mxGeometry') 118 | mxGeometry.set('as','geometry') 119 | 120 | def addLinkList(self,linkListOfDictionary): 121 | try: 122 | for link in linkListOfDictionary: 123 | self.addLink(sourceNodeID=link['sourceNodeID'] , destinationNodeID=link['destinationNodeID'] ) 124 | return 125 | except Exception as e: 126 | logging.error('Error in adding Link List: {}'.format(e)) 127 | 128 | def display_xml(self): 129 | return ET.tostring(self.mxfile) 130 | 131 | def exportXML(self, filePath): 132 | with open(filePath,'wb') as file: 133 | tree = ET.ElementTree(self.mxfile) 134 | tree.write(file) 135 | return 136 | 137 | def __repr__(self): 138 | return str(self.display_xml()) -------------------------------------------------------------------------------- /examples/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amrelhusseiny/drawio_network_plot/bfdeaf8648092aaba33dba458952b2a98c394f6b/examples/.DS_Store -------------------------------------------------------------------------------- /examples/cisco_gather_cdp_and_plot/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amrelhusseiny/drawio_network_plot/bfdeaf8648092aaba33dba458952b2a98c394f6b/examples/cisco_gather_cdp_and_plot/.DS_Store -------------------------------------------------------------------------------- /examples/cisco_gather_cdp_and_plot/cisco_gather_cdp_and_plot.py: -------------------------------------------------------------------------------- 1 | from netmiko import ConnectHandler 2 | import re 3 | from drawio_network_plot.drawio_network_plot import NetPlot 4 | 5 | # ----------------- Getting live CDP Neigbors to gather links endpoints --------------------------- 6 | def retieve_lldp_neigbor_hostname(devices_list): 7 | list_of_cdp_neighborship = [] 8 | for device in devices_list: 9 | device_dictionary = { 10 | 'device_type': 'cisco_ios', 11 | 'host': device['ip_address'], 12 | 'username': 'automation', 13 | 'password' : '1234567', 14 | } 15 | net_connect = ConnectHandler(**device_dictionary) 16 | output = net_connect.send_command("show cdp neighbor") 17 | lines_list = output.splitlines() 18 | for line in lines_list: 19 | try: 20 | # regex to search the name of the device before the domain ID ".default" , then removing the word ".default" from the string 21 | link = { 22 | 'sourceNodeID' : device['nodeName'], 23 | 'destinationNodeID' : re.search('\S+\.default',line).group().replace('.default','') 24 | } 25 | # checking for duplication before adding new link : 26 | if {'sourceNodeID':link['destinationNodeID'],'destinationNodeID':link['sourceNodeID']} not in list_of_cdp_neighborship: 27 | list_of_cdp_neighborship.append(link) 28 | except: 29 | continue 30 | net_connect.disconnect() 31 | return list_of_cdp_neighborship 32 | 33 | def main(): 34 | # Lab Devices , must have the device type for the plotting library to work 35 | devices = [ 36 | {'nodeName':'Router_1','ip_address':'100.0.1.1','nodeType':'router','nodeDescription':'NA'}, 37 | {'nodeName':'Router_2','ip_address':'100.0.1.2','nodeType':'router','nodeDescription':'NA'}, 38 | {'nodeName':'Spine_1','ip_address':'100.0.2.1','nodeType':'l3_switch','nodeDescription':'NA'}, 39 | {'nodeName':'Spine_2','ip_address':'100.0.2.2','nodeType':'l3_switch','nodeDescription':'NA'}, 40 | {'nodeName':'Leaf_1','ip_address':'100.0.3.1','nodeType':'l2_switch','nodeDescription':'NA'}, 41 | {'nodeName':'Leaf_2','ip_address':'100.0.3.2','nodeType':'l2_switch','nodeDescription':'NA'}, 42 | {'nodeName':'Leaf_3','ip_address':'100.0.3.3','nodeType':'l2_switch','nodeDescription':'NA'} 43 | ] 44 | # Getting list of links for each device 45 | list_of_cdp_neighborship = retieve_lldp_neigbor_hostname(devices) 46 | for peering in list_of_cdp_neighborship: 47 | print(peering) 48 | 49 | # ------------------------------------------------------------------------------------------------------ 50 | # ------------------------------------------------------------------------------------------------------ 51 | # ----------------- Main Part : using library to generate XML DrawIIO format --------------------------- 52 | # Using the Plot library 53 | x = NetPlot() 54 | x.addNodeList(devices) 55 | x.addLinkList(list_of_cdp_neighborship) 56 | 57 | print(x.display_xml()) 58 | # - You can also directly generate an XML file using the built in function : 59 | # x.exportXML('examples/output.xml') 60 | # - You can add node by node and connection by connection : 61 | # x.addNode(nodeName='Router_18',nodeType='router') 62 | # x.addLink('Router_17','Router_18') 63 | # ------------------------------------------------------------------------------------------------------ 64 | # ------------------------------------------------------------------------------------------------------ 65 | # ------------------------------------------------------------------------------------------------------ 66 | 67 | if __name__ == "__main__": 68 | main() -------------------------------------------------------------------------------- /examples/cisco_gather_cdp_and_plot/device_configurations/Leaf_1: -------------------------------------------------------------------------------- 1 | Leaf_1#sho running-config 2 | Building configuration... 3 | 4 | Current configuration : 1827 bytes 5 | ! 6 | ! Last configuration change at 20:59:48 UTC Mon Sep 5 2022 7 | upgrade fpd auto 8 | version 15.3 9 | service timestamps debug datetime msec 10 | service timestamps log datetime msec 11 | no service password-encryption 12 | ! 13 | hostname Leaf_1 14 | ! 15 | boot-start-marker 16 | boot-end-marker 17 | ! 18 | aqm-register-fnf 19 | ! 20 | ! 21 | aaa new-model 22 | aaa local authentication default authorization default 23 | ! 24 | ! 25 | aaa authentication login default local 26 | aaa authentication login automation local 27 | ! 28 | ! 29 | ! 30 | ! 31 | ! 32 | aaa session-id common 33 | ! 34 | ! 35 | ! 36 | ! 37 | ! 38 | ! 39 | ip domain name default 40 | ip cef 41 | no ipv6 cef 42 | ! 43 | multilink bundle-name authenticated 44 | ! 45 | ! 46 | ! 47 | ! 48 | ! 49 | ! 50 | ! 51 | ! 52 | ! 53 | username automation password 0 1234567 54 | ! 55 | redundancy 56 | ! 57 | ! 58 | ! 59 | ! 60 | ! 61 | ! 62 | ! 63 | ! 64 | ! 65 | ! 66 | ! 67 | ! 68 | interface Loopback100 69 | ip address 100.0.3.1 255.255.255.255 70 | ip ospf 1 area 0 71 | ! 72 | interface FastEthernet0/0 73 | no ip address 74 | duplex full 75 | ! 76 | interface Ethernet1/0 77 | no ip address 78 | duplex full 79 | ! 80 | interface Ethernet1/1 81 | ip address 10.0.35.2 255.255.255.0 82 | ip ospf 1 area 0 83 | duplex full 84 | no shutdown 85 | ! 86 | interface Ethernet1/2 87 | no ip address 88 | duplex full 89 | ! 90 | interface Ethernet1/3 91 | no ip address 92 | duplex full 93 | ! 94 | interface Ethernet1/4 95 | ip address 10.0.45.2 255.255.255.0 96 | ip ospf 1 area 0 97 | no shutdown 98 | duplex full 99 | ! 100 | interface Ethernet1/5 101 | no ip address 102 | duplex full 103 | ! 104 | interface Ethernet1/6 105 | no ip address 106 | duplex full 107 | ! 108 | interface Ethernet1/7 109 | no ip address 110 | duplex full 111 | ! 112 | router ospf 1 113 | router-id 100.0.3.1 114 | network 10.0.0.0 0.0.0.255 area 0 115 | ! 116 | ip forward-protocol nd 117 | no ip http server 118 | no ip http secure-server 119 | ! 120 | ! 121 | ip route 0.0.0.0 0.0.0.0 192.168.1.1 122 | ! 123 | ! 124 | ! 125 | ! 126 | ! 127 | ! 128 | control-plane 129 | ! 130 | ! 131 | mgcp behavior rsip-range tgcp-only 132 | mgcp behavior comedia-role none 133 | mgcp behavior comedia-check-media-src disable 134 | mgcp behavior comedia-sdp-force disable 135 | ! 136 | mgcp profile default 137 | ! 138 | ! 139 | ! 140 | gatekeeper 141 | shutdown 142 | ! 143 | ! 144 | line con 0 145 | stopbits 1 146 | line aux 0 147 | stopbits 1 148 | line vty 0 4 149 | transport input all 150 | line vty 5 7 151 | transport input all 152 | ! 153 | ! 154 | end 155 | 156 | Leaf_1# -------------------------------------------------------------------------------- /examples/cisco_gather_cdp_and_plot/device_configurations/Leaf_2: -------------------------------------------------------------------------------- 1 | Leaf_2#sho running-config 2 | Building configuration... 3 | 4 | Current configuration : 1817 bytes 5 | ! 6 | ! Last configuration change at 21:00:11 UTC Mon Sep 5 2022 7 | upgrade fpd auto 8 | version 15.3 9 | service timestamps debug datetime msec 10 | service timestamps log datetime msec 11 | no service password-encryption 12 | ! 13 | hostname Leaf_2 14 | ! 15 | boot-start-marker 16 | boot-end-marker 17 | ! 18 | aqm-register-fnf 19 | ! 20 | ! 21 | aaa new-model 22 | aaa local authentication default authorization default 23 | ! 24 | ! 25 | aaa authentication login default local 26 | aaa authentication login automation local 27 | ! 28 | ! 29 | ! 30 | ! 31 | ! 32 | aaa session-id common 33 | ! 34 | ! 35 | ! 36 | ! 37 | ! 38 | ! 39 | ip domain name default 40 | ip cef 41 | no ipv6 cef 42 | ! 43 | multilink bundle-name authenticated 44 | ! 45 | ! 46 | ! 47 | ! 48 | ! 49 | ! 50 | ! 51 | ! 52 | ! 53 | username automation password 0 1234567 54 | ! 55 | redundancy 56 | ! 57 | ! 58 | ! 59 | ! 60 | ! 61 | ! 62 | ! 63 | ! 64 | ! 65 | ! 66 | ! 67 | ! 68 | interface Loopback100 69 | ip address 100.0.3.2 255.255.255.255 70 | ip ospf 1 area 0 71 | ! 72 | interface FastEthernet0/0 73 | no ip address 74 | duplex full 75 | ! 76 | interface Ethernet1/0 77 | no ip address 78 | duplex full 79 | ! 80 | interface Ethernet1/1 81 | no ip address 82 | duplex full 83 | ! 84 | interface Ethernet1/2 85 | ip address 10.0.36.2 255.255.255.0 86 | ip ospf 1 area 0 87 | duplex full 88 | ! 89 | interface Ethernet1/3 90 | no ip address 91 | duplex full 92 | ! 93 | interface Ethernet1/4 94 | no ip address 95 | duplex full 96 | ! 97 | interface Ethernet1/5 98 | ip address 10.0.46.2 255.255.255.0 99 | ip ospf 1 area 0 100 | duplex full 101 | ! 102 | interface Ethernet1/6 103 | no ip address 104 | duplex full 105 | ! 106 | interface Ethernet1/7 107 | no ip address 108 | duplex full 109 | ! 110 | router ospf 1 111 | router-id 100.0.3.2 112 | network 10.0.0.0 0.0.0.255 area 0 113 | ! 114 | ip forward-protocol nd 115 | no ip http server 116 | no ip http secure-server 117 | ! 118 | ! 119 | ip route 0.0.0.0 0.0.0.0 192.168.1.1 120 | ! 121 | ! 122 | ! 123 | ! 124 | ! 125 | ! 126 | control-plane 127 | ! 128 | ! 129 | mgcp behavior rsip-range tgcp-only 130 | mgcp behavior comedia-role none 131 | mgcp behavior comedia-check-media-src disable 132 | mgcp behavior comedia-sdp-force disable 133 | ! 134 | mgcp profile default 135 | ! 136 | ! 137 | ! 138 | gatekeeper 139 | shutdown 140 | ! 141 | ! 142 | line con 0 143 | stopbits 1 144 | line aux 0 145 | stopbits 1 146 | line vty 0 4 147 | transport input all 148 | line vty 5 7 149 | transport input all 150 | ! 151 | ! 152 | end 153 | 154 | Leaf_2# -------------------------------------------------------------------------------- /examples/cisco_gather_cdp_and_plot/device_configurations/Leaf_3: -------------------------------------------------------------------------------- 1 | Leaf_3#show running-config 2 | Building configuration... 3 | 4 | Current configuration : 1817 bytes 5 | ! 6 | ! Last configuration change at 21:00:39 UTC Mon Sep 5 2022 7 | upgrade fpd auto 8 | version 15.3 9 | service timestamps debug datetime msec 10 | service timestamps log datetime msec 11 | no service password-encryption 12 | ! 13 | hostname Leaf_3 14 | ! 15 | boot-start-marker 16 | boot-end-marker 17 | ! 18 | aqm-register-fnf 19 | ! 20 | ! 21 | aaa new-model 22 | aaa local authentication default authorization default 23 | ! 24 | ! 25 | aaa authentication login default local 26 | aaa authentication login automation local 27 | ! 28 | ! 29 | ! 30 | ! 31 | ! 32 | aaa session-id common 33 | ! 34 | ! 35 | ! 36 | ! 37 | ! 38 | ! 39 | ip domain name default 40 | ip cef 41 | no ipv6 cef 42 | ! 43 | multilink bundle-name authenticated 44 | ! 45 | ! 46 | ! 47 | ! 48 | ! 49 | ! 50 | ! 51 | ! 52 | ! 53 | username automation password 0 1234567 54 | ! 55 | redundancy 56 | ! 57 | ! 58 | ! 59 | ! 60 | ! 61 | ! 62 | ! 63 | ! 64 | ! 65 | ! 66 | ! 67 | ! 68 | interface Loopback100 69 | ip address 100.0.3.3 255.255.255.255 70 | ip ospf 1 area 0 71 | ! 72 | interface FastEthernet0/0 73 | no ip address 74 | duplex full 75 | ! 76 | interface Ethernet1/0 77 | no ip address 78 | duplex full 79 | ! 80 | interface Ethernet1/1 81 | no ip address 82 | duplex full 83 | ! 84 | interface Ethernet1/2 85 | no ip address 86 | duplex full 87 | ! 88 | interface Ethernet1/3 89 | ip address 10.0.37.2 255.255.255.0 90 | ip ospf 1 area 0 91 | duplex full 92 | ! 93 | interface Ethernet1/4 94 | no ip address 95 | duplex full 96 | ! 97 | interface Ethernet1/5 98 | no ip address 99 | duplex full 100 | ! 101 | interface Ethernet1/6 102 | ip address 10.0.47.2 255.255.255.0 103 | ip ospf 1 area 0 104 | duplex full 105 | ! 106 | interface Ethernet1/7 107 | no ip address 108 | duplex full 109 | ! 110 | router ospf 1 111 | router-id 100.0.3.3 112 | network 10.0.0.0 0.0.0.255 area 0 113 | ! 114 | ip forward-protocol nd 115 | no ip http server 116 | no ip http secure-server 117 | ! 118 | ! 119 | ip route 0.0.0.0 0.0.0.0 192.168.1.1 120 | ! 121 | ! 122 | ! 123 | ! 124 | ! 125 | ! 126 | control-plane 127 | ! 128 | ! 129 | mgcp behavior rsip-range tgcp-only 130 | mgcp behavior comedia-role none 131 | mgcp behavior comedia-check-media-src disable 132 | mgcp behavior comedia-sdp-force disable 133 | ! 134 | mgcp profile default 135 | ! 136 | ! 137 | ! 138 | gatekeeper 139 | shutdown 140 | ! 141 | ! 142 | line con 0 143 | stopbits 1 144 | line aux 0 145 | stopbits 1 146 | line vty 0 4 147 | transport input all 148 | line vty 5 7 149 | transport input all 150 | ! 151 | ! 152 | end 153 | -------------------------------------------------------------------------------- /examples/cisco_gather_cdp_and_plot/device_configurations/Router_1: -------------------------------------------------------------------------------- 1 | Router_1#show running-config 2 | Building configuration... 3 | 4 | Current configuration : 2101 bytes 5 | ! 6 | ! Last configuration change at 21:21:08 UTC Mon Sep 5 2022 7 | upgrade fpd auto 8 | version 15.3 9 | service timestamps debug datetime msec 10 | service timestamps log datetime msec 11 | no service password-encryption 12 | ! 13 | hostname Router_1 14 | ! 15 | boot-start-marker 16 | boot-end-marker 17 | ! 18 | aqm-register-fnf 19 | ! 20 | ! 21 | aaa new-model 22 | aaa local authentication default authorization default 23 | ! 24 | ! 25 | aaa authentication login default local 26 | aaa authentication login automation local 27 | ! 28 | ! 29 | ! 30 | ! 31 | ! 32 | aaa session-id common 33 | ! 34 | ! 35 | ! 36 | ! 37 | ! 38 | ! 39 | ip domain name default 40 | ip cef 41 | no ipv6 cef 42 | ! 43 | multilink bundle-name authenticated 44 | ! 45 | ! 46 | ! 47 | ! 48 | ! 49 | ! 50 | ! 51 | ! 52 | ! 53 | username automation password 0 1234567 54 | ! 55 | redundancy 56 | ! 57 | ! 58 | ! 59 | ! 60 | ! 61 | ! 62 | ! 63 | ! 64 | ! 65 | ! 66 | ! 67 | ! 68 | interface Loopback100 69 | ip address 100.0.1.1 255.255.255.255 70 | ip ospf 1 area 0 71 | ! 72 | interface FastEthernet0/0 73 | ip address 192.168.1.120 255.255.255.0 74 | ip nat outside 75 | ip virtual-reassembly in 76 | duplex full 77 | ! 78 | interface Ethernet1/0 79 | ip address 10.0.13.1 255.255.255.0 80 | ip nat inside 81 | ip virtual-reassembly in 82 | ip ospf 1 area 0 83 | duplex full 84 | ! 85 | interface Ethernet1/1 86 | no ip address 87 | duplex full 88 | ! 89 | interface Ethernet1/2 90 | no ip address 91 | duplex full 92 | ! 93 | interface Ethernet1/3 94 | no ip address 95 | duplex full 96 | ! 97 | interface Ethernet1/4 98 | no ip address 99 | duplex full 100 | ! 101 | interface Ethernet1/5 102 | no ip address 103 | duplex full 104 | ! 105 | interface Ethernet1/6 106 | no ip address 107 | duplex full 108 | ! 109 | interface Ethernet1/7 110 | no ip address 111 | duplex full 112 | ! 113 | router ospf 1 114 | router-id 100.0.1.1 115 | network 10.0.0.0 0.0.0.255 area 0 116 | network 0.0.0.0 255.255.255.255 area 0 117 | ! 118 | ip forward-protocol nd 119 | no ip http server 120 | no ip http secure-server 121 | ! 122 | ! 123 | ip nat inside source list 100 interface FastEthernet0/0 overload 124 | ip route 0.0.0.0 0.0.0.0 192.168.1.1 125 | ! 126 | ! 127 | ! 128 | access-list 100 permit ip 10.0.0.0 0.255.255.255 any 129 | access-list 100 remark == [Control To Local Network]== 130 | ! 131 | ! 132 | ! 133 | control-plane 134 | ! 135 | ! 136 | mgcp behavior rsip-range tgcp-only 137 | mgcp behavior comedia-role none 138 | mgcp behavior comedia-check-media-src disable 139 | mgcp behavior comedia-sdp-force disable 140 | ! 141 | mgcp profile default 142 | ! 143 | ! 144 | ! 145 | gatekeeper 146 | shutdown 147 | ! 148 | ! 149 | line con 0 150 | stopbits 1 151 | line aux 0 152 | stopbits 1 153 | line vty 0 4 154 | transport input all 155 | line vty 5 7 156 | transport input all 157 | ! 158 | ! 159 | end 160 | -------------------------------------------------------------------------------- /examples/cisco_gather_cdp_and_plot/device_configurations/Router_2: -------------------------------------------------------------------------------- 1 | Router_2#show running-config 2 | Building configuration... 3 | 4 | Current configuration : 1819 bytes 5 | ! 6 | ! Last configuration change at 20:58:19 UTC Mon Sep 5 2022 by automation 7 | upgrade fpd auto 8 | version 15.3 9 | service timestamps debug datetime msec 10 | service timestamps log datetime msec 11 | no service password-encryption 12 | ! 13 | hostname Router_2 14 | ! 15 | boot-start-marker 16 | boot-end-marker 17 | ! 18 | aqm-register-fnf 19 | ! 20 | ! 21 | aaa new-model 22 | aaa local authentication default authorization default 23 | ! 24 | ! 25 | aaa authentication login default local 26 | aaa authentication login automation local 27 | ! 28 | ! 29 | ! 30 | ! 31 | ! 32 | aaa session-id common 33 | ! 34 | ! 35 | ! 36 | ! 37 | ! 38 | ! 39 | ip domain name default 40 | ip cef 41 | no ipv6 cef 42 | ! 43 | multilink bundle-name authenticated 44 | ! 45 | ! 46 | ! 47 | ! 48 | ! 49 | ! 50 | ! 51 | ! 52 | ! 53 | username automation password 0 1234567 54 | ! 55 | redundancy 56 | ! 57 | ! 58 | ! 59 | ! 60 | ! 61 | ! 62 | ! 63 | ! 64 | ! 65 | ! 66 | ! 67 | ! 68 | interface Loopback100 69 | ip address 100.0.1.2 255.255.255.255 70 | ip ospf 1 area 0 71 | ! 72 | interface FastEthernet0/0 73 | ip address 192.168.1.121 255.255.255.0 74 | duplex full 75 | ! 76 | interface Ethernet1/0 77 | ip address 10.0.24.1 255.255.255.0 78 | ip ospf 1 area 0 79 | duplex full 80 | ! 81 | interface Ethernet1/1 82 | no ip address 83 | duplex full 84 | ! 85 | interface Ethernet1/2 86 | no ip address 87 | duplex full 88 | ! 89 | interface Ethernet1/3 90 | no ip address 91 | duplex full 92 | ! 93 | interface Ethernet1/4 94 | no ip address 95 | duplex full 96 | ! 97 | interface Ethernet1/5 98 | no ip address 99 | duplex full 100 | ! 101 | interface Ethernet1/6 102 | no ip address 103 | duplex full 104 | ! 105 | interface Ethernet1/7 106 | no ip address 107 | duplex full 108 | ! 109 | router ospf 1 110 | router-id 100.0.1.2 111 | network 10.0.0.0 0.0.0.255 area 0 112 | ! 113 | ip forward-protocol nd 114 | no ip http server 115 | no ip http secure-server 116 | ! 117 | ! 118 | ip route 0.0.0.0 0.0.0.0 192.168.1.1 119 | ! 120 | ! 121 | ! 122 | ! 123 | ! 124 | ! 125 | control-plane 126 | ! 127 | ! 128 | mgcp behavior rsip-range tgcp-only 129 | mgcp behavior comedia-role none 130 | mgcp behavior comedia-check-media-src disable 131 | mgcp behavior comedia-sdp-force disable 132 | ! 133 | mgcp profile default 134 | ! 135 | ! 136 | ! 137 | gatekeeper 138 | shutdown 139 | ! 140 | ! 141 | line con 0 142 | stopbits 1 143 | line aux 0 144 | stopbits 1 145 | line vty 0 4 146 | transport input all 147 | line vty 5 7 148 | transport input all 149 | ! 150 | ! 151 | end 152 | 153 | Router_2# -------------------------------------------------------------------------------- /examples/cisco_gather_cdp_and_plot/device_configurations/Spine_1: -------------------------------------------------------------------------------- 1 | Spine_1#show running-config 2 | Building configuration... 3 | 4 | Current configuration : 1896 bytes 5 | ! 6 | ! Last configuration change at 20:58:53 UTC Mon Sep 5 2022 7 | upgrade fpd auto 8 | version 15.3 9 | service timestamps debug datetime msec 10 | service timestamps log datetime msec 11 | no service password-encryption 12 | ! 13 | hostname Spine_1 14 | ! 15 | boot-start-marker 16 | boot-end-marker 17 | ! 18 | aqm-register-fnf 19 | ! 20 | ! 21 | aaa new-model 22 | aaa local authentication default authorization default 23 | ! 24 | ! 25 | aaa authentication login default local 26 | aaa authentication login automation local 27 | ! 28 | ! 29 | ! 30 | ! 31 | ! 32 | aaa session-id common 33 | ! 34 | ! 35 | ! 36 | ! 37 | ! 38 | ! 39 | ip domain name default 40 | ip cef 41 | no ipv6 cef 42 | ! 43 | multilink bundle-name authenticated 44 | ! 45 | ! 46 | ! 47 | ! 48 | ! 49 | ! 50 | ! 51 | ! 52 | ! 53 | username automation password 0 1234567 54 | ! 55 | redundancy 56 | ! 57 | ! 58 | ! 59 | ! 60 | ! 61 | ! 62 | ! 63 | ! 64 | ! 65 | ! 66 | ! 67 | ! 68 | interface Loopback100 69 | ip address 100.0.2.1 255.255.255.255 70 | ip ospf 1 area 0 71 | ! 72 | interface FastEthernet0/0 73 | no ip address 74 | duplex full 75 | ! 76 | interface Ethernet1/0 77 | ip address 10.0.13.2 255.255.255.0 78 | ip ospf 1 area 0 79 | duplex full 80 | ! 81 | interface Ethernet1/1 82 | ip address 10.0.35.1 255.255.255.0 83 | ip ospf 1 area 0 84 | duplex full 85 | ! 86 | interface Ethernet1/2 87 | ip address 10.0.36.1 255.255.255.0 88 | ip ospf 1 area 0 89 | duplex full 90 | ! 91 | interface Ethernet1/3 92 | ip address 10.0.37.1 255.255.255.0 93 | ip ospf 1 area 0 94 | duplex full 95 | ! 96 | interface Ethernet1/4 97 | no ip address 98 | duplex full 99 | ! 100 | interface Ethernet1/5 101 | no ip address 102 | duplex full 103 | ! 104 | interface Ethernet1/6 105 | no ip address 106 | duplex full 107 | ! 108 | interface Ethernet1/7 109 | no ip address 110 | duplex full 111 | ! 112 | router ospf 1 113 | router-id 100.0.2.1 114 | network 10.0.0.0 0.0.0.255 area 0 115 | ! 116 | ip forward-protocol nd 117 | no ip http server 118 | no ip http secure-server 119 | ! 120 | ! 121 | ip route 0.0.0.0 0.0.0.0 192.168.1.1 122 | ! 123 | ! 124 | ! 125 | ! 126 | ! 127 | ! 128 | control-plane 129 | ! 130 | ! 131 | mgcp behavior rsip-range tgcp-only 132 | mgcp behavior comedia-role none 133 | mgcp behavior comedia-check-media-src disable 134 | mgcp behavior comedia-sdp-force disable 135 | ! 136 | mgcp profile default 137 | ! 138 | ! 139 | ! 140 | gatekeeper 141 | shutdown 142 | ! 143 | ! 144 | line con 0 145 | stopbits 1 146 | line aux 0 147 | stopbits 1 148 | line vty 0 4 149 | transport input all 150 | line vty 5 7 151 | transport input all 152 | ! 153 | ! 154 | end 155 | 156 | Spine_1# -------------------------------------------------------------------------------- /examples/cisco_gather_cdp_and_plot/device_configurations/Spine_2: -------------------------------------------------------------------------------- 1 | Spine_2#show running-config 2 | Building configuration... 3 | 4 | Current configuration : 1896 bytes 5 | ! 6 | ! Last configuration change at 20:59:23 UTC Mon Sep 5 2022 7 | upgrade fpd auto 8 | version 15.3 9 | service timestamps debug datetime msec 10 | service timestamps log datetime msec 11 | no service password-encryption 12 | ! 13 | hostname Spine_2 14 | ! 15 | boot-start-marker 16 | boot-end-marker 17 | ! 18 | aqm-register-fnf 19 | ! 20 | ! 21 | aaa new-model 22 | aaa local authentication default authorization default 23 | ! 24 | ! 25 | aaa authentication login default local 26 | aaa authentication login automation local 27 | ! 28 | ! 29 | ! 30 | ! 31 | ! 32 | aaa session-id common 33 | ! 34 | ! 35 | ! 36 | ! 37 | ! 38 | ! 39 | ip domain name default 40 | ip cef 41 | no ipv6 cef 42 | ! 43 | multilink bundle-name authenticated 44 | ! 45 | ! 46 | ! 47 | ! 48 | ! 49 | ! 50 | ! 51 | ! 52 | ! 53 | username automation password 0 1234567 54 | ! 55 | redundancy 56 | ! 57 | ! 58 | ! 59 | ! 60 | ! 61 | ! 62 | ! 63 | ! 64 | ! 65 | ! 66 | ! 67 | ! 68 | interface Loopback100 69 | ip address 100.0.2.2 255.255.255.255 70 | ip ospf 1 area 0 71 | ! 72 | interface FastEthernet0/0 73 | no ip address 74 | duplex full 75 | ! 76 | interface Ethernet1/0 77 | ip address 10.0.24.2 255.255.255.0 78 | ip ospf 1 area 0 79 | duplex full 80 | ! 81 | interface Ethernet1/1 82 | no ip address 83 | duplex full 84 | ! 85 | interface Ethernet1/2 86 | no ip address 87 | duplex full 88 | ! 89 | interface Ethernet1/3 90 | no ip address 91 | duplex full 92 | ! 93 | interface Ethernet1/4 94 | ip address 10.0.45.1 255.255.255.0 95 | ip ospf 1 area 0 96 | duplex full 97 | ! 98 | interface Ethernet1/5 99 | ip address 10.0.46.1 255.255.255.0 100 | ip ospf 1 area 0 101 | duplex full 102 | ! 103 | interface Ethernet1/6 104 | ip address 10.0.47.1 255.255.255.0 105 | ip ospf 1 area 0 106 | duplex full 107 | ! 108 | interface Ethernet1/7 109 | no ip address 110 | duplex full 111 | ! 112 | router ospf 1 113 | router-id 100.0.2.2 114 | network 10.0.0.0 0.0.0.255 area 0 115 | ! 116 | ip forward-protocol nd 117 | no ip http server 118 | no ip http secure-server 119 | ! 120 | ! 121 | ip route 0.0.0.0 0.0.0.0 192.168.1.1 122 | ! 123 | ! 124 | ! 125 | ! 126 | ! 127 | ! 128 | control-plane 129 | ! 130 | ! 131 | mgcp behavior rsip-range tgcp-only 132 | mgcp behavior comedia-role none 133 | mgcp behavior comedia-check-media-src disable 134 | mgcp behavior comedia-sdp-force disable 135 | ! 136 | mgcp profile default 137 | ! 138 | ! 139 | ! 140 | gatekeeper 141 | shutdown 142 | ! 143 | ! 144 | line con 0 145 | stopbits 1 146 | line aux 0 147 | stopbits 1 148 | line vty 0 4 149 | transport input all 150 | line vty 5 7 151 | transport input all 152 | ! 153 | ! 154 | end 155 | 156 | Spine_2# -------------------------------------------------------------------------------- /examples/example_1_output_snapshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amrelhusseiny/drawio_network_plot/bfdeaf8648092aaba33dba458952b2a98c394f6b/examples/example_1_output_snapshot.png -------------------------------------------------------------------------------- /examples/output.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /icons/Firewall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amrelhusseiny/drawio_network_plot/bfdeaf8648092aaba33dba458952b2a98c394f6b/icons/Firewall.png -------------------------------------------------------------------------------- /icons/L2_switch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amrelhusseiny/drawio_network_plot/bfdeaf8648092aaba33dba458952b2a98c394f6b/icons/L2_switch.png -------------------------------------------------------------------------------- /icons/L3_switch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amrelhusseiny/drawio_network_plot/bfdeaf8648092aaba33dba458952b2a98c394f6b/icons/L3_switch.png -------------------------------------------------------------------------------- /icons/Router.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amrelhusseiny/drawio_network_plot/bfdeaf8648092aaba33dba458952b2a98c394f6b/icons/Router.png -------------------------------------------------------------------------------- /icons/Server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amrelhusseiny/drawio_network_plot/bfdeaf8648092aaba33dba458952b2a98c394f6b/icons/Server.png -------------------------------------------------------------------------------- /output.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # setup.py 2 | 3 | from setuptools import setup, find_packages 4 | 5 | VERSION = '0.0.1' 6 | DESCRIPTION = 'Plot Network using python , and output to DrawIO file' 7 | 8 | with open('README.md') as readme_file: 9 | LONG_DESCRIPTION = readme_file.read() 10 | 11 | # Setting up 12 | setup( 13 | # the name must match the folder name 'verysimplemodule' 14 | name="drawio_network_plot", 15 | version="0.0.02 Beta", 16 | author="Amr ElHusseiny", 17 | author_email="amroashram@hotmail.com", 18 | description=DESCRIPTION, 19 | long_description= LONG_DESCRIPTION , 20 | long_description_content_type='text/markdown', 21 | packages=find_packages(), 22 | install_requires=[], # No packages needed - all standard libraries 23 | keywords=['python', 'first package'], 24 | classifiers= ['Development Status :: 4 - Beta','Topic :: System :: Networking'], 25 | python_requires='>=3.6', 26 | ) -------------------------------------------------------------------------------- /test_temp.py: -------------------------------------------------------------------------------- 1 | from drawio_network_plot.drawio_network_plot import NetPlot 2 | 3 | device_list = [ 4 | # Routers 5 | {'nodeName' : 'Router_1','nodeType' : 'router','nodeDescription' : 'External Peering Provider 1'}, 6 | {'nodeName' : 'Router_2','nodeType' : 'router','nodeDescription' : 'External Peering Provider 2'}, 7 | # Core 8 | {'nodeName' : 'Core_switch_1','nodeType' : 'l3_switch','nodeDescription' : 'Spine Switch 01'}, 9 | {'nodeName' : 'Core_switch_2','nodeType' : 'l3_switch','nodeDescription' : 'Spine Switch 02'}, 10 | # Firewalls 11 | {'nodeName' : 'FW_1','nodeType' : 'firewall','nodeDescription' : 'Firewall 01'}, 12 | {'nodeName' : 'FW_2','nodeType' : 'firewall','nodeDescription' : 'Firewall 02'}, 13 | # Leafs 14 | {'nodeName' : 'TOR_1','nodeType' : 'l2_switch','nodeDescription' : 'Leaf Switch 01'}, 15 | {'nodeName' : 'TOR_2','nodeType' : 'l2_switch','nodeDescription' : 'Leaf Switch 02'}, 16 | {'nodeName' : 'TOR_3','nodeType' : 'l2_switch','nodeDescription' : 'Leaf Switch 03'}, 17 | {'nodeName' : 'TOR_4','nodeType' : 'l2_switch','nodeDescription' : 'Leaf Switch 04'}, 18 | # Servers 19 | {'nodeName' : 'Server_1','nodeType' : 'server','nodeDescription' : 'Server 1'}, 20 | {'nodeName' : 'Server_2','nodeType' : 'server','nodeDescription' : 'Server 2'}, 21 | {'nodeName' : 'Server_3','nodeType' : 'server','nodeDescription' : 'Server 3'}, 22 | {'nodeName' : 'Server_4','nodeType' : 'server','nodeDescription' : 'Server 4'}, 23 | {'nodeName' : 'Server_5','nodeType' : 'server','nodeDescription' : 'Server 5'}, 24 | {'nodeName' : 'Server_6','nodeType' : 'server','nodeDescription' : 'Server 6'}, 25 | {'nodeName' : 'Server_7','nodeType' : 'server','nodeDescription' : 'Server 7'}, 26 | {'nodeName' : 'Server_8','nodeType' : 'server','nodeDescription' : 'Server 8'}, 27 | {'nodeName' : 'Server_9','nodeType' : 'server','nodeDescription' : 'Server 9'}, 28 | {'nodeName' : 'Server_10','nodeType' : 'server','nodeDescription' : 'Server 10'}, 29 | {'nodeName' : 'Server_11','nodeType' : 'server','nodeDescription' : 'Server 11'}, 30 | {'nodeName' : 'Server_12','nodeType' : 'server','nodeDescription' : 'Server 12'}, 31 | {'nodeName' : 'Server_13','nodeType' : 'server','nodeDescription' : 'Server 13'}, 32 | {'nodeName' : 'Server_14','nodeType' : 'server','nodeDescription' : 'Server 14'}, 33 | {'nodeName' : 'Server_15','nodeType' : 'server','nodeDescription' : 'Server 15'}, 34 | {'nodeName' : 'Server_16','nodeType' : 'server','nodeDescription' : 'Server 16'}, 35 | ] 36 | 37 | 38 | connection_list = [ 39 | # Router to Core 40 | {'sourceNodeID' : 'Router_1','destinationNodeID' : 'Core_switch_1'}, 41 | {'sourceNodeID' : 'Router_1','destinationNodeID' : 'Core_switch_2'}, 42 | {'sourceNodeID' : 'Router_2','destinationNodeID' : 'Core_switch_1'}, 43 | {'sourceNodeID' : 'Router_2','destinationNodeID' : 'Core_switch_2'}, 44 | # Core to FW 45 | {'sourceNodeID' : 'Core_switch_1','destinationNodeID' : 'FW_1'}, 46 | {'sourceNodeID' : 'Core_switch_2','destinationNodeID' : 'FW_2'}, 47 | # Core to TOR 48 | {'sourceNodeID' : 'Core_switch_1','destinationNodeID' : 'TOR_1'}, 49 | {'sourceNodeID' : 'Core_switch_1','destinationNodeID' : 'TOR_2'}, 50 | {'sourceNodeID' : 'Core_switch_1','destinationNodeID' : 'TOR_3'}, 51 | {'sourceNodeID' : 'Core_switch_1','destinationNodeID' : 'TOR_4'}, 52 | {'sourceNodeID' : 'Core_switch_2','destinationNodeID' : 'TOR_1'}, 53 | {'sourceNodeID' : 'Core_switch_2','destinationNodeID' : 'TOR_2'}, 54 | {'sourceNodeID' : 'Core_switch_2','destinationNodeID' : 'TOR_3'}, 55 | {'sourceNodeID' : 'Core_switch_2','destinationNodeID' : 'TOR_4'}, 56 | # TOR to Server 57 | {'sourceNodeID' : 'TOR_1','destinationNodeID' : 'Server_1'}, 58 | {'sourceNodeID' : 'TOR_2','destinationNodeID' : 'Server_2'}, 59 | {'sourceNodeID' : 'TOR_3','destinationNodeID' : 'Server_3'}, 60 | {'sourceNodeID' : 'TOR_4','destinationNodeID' : 'Server_4'}, 61 | {'sourceNodeID' : 'TOR_1','destinationNodeID' : 'Server_5'}, 62 | {'sourceNodeID' : 'TOR_2','destinationNodeID' : 'Server_6'}, 63 | {'sourceNodeID' : 'TOR_3','destinationNodeID' : 'Server_7'}, 64 | {'sourceNodeID' : 'TOR_4','destinationNodeID' : 'Server_8'}, 65 | {'sourceNodeID' : 'TOR_1','destinationNodeID' : 'Server_9'}, 66 | {'sourceNodeID' : 'TOR_2','destinationNodeID' : 'Server_10'}, 67 | {'sourceNodeID' : 'TOR_3','destinationNodeID' : 'Server_11'}, 68 | {'sourceNodeID' : 'TOR_4','destinationNodeID' : 'Server_12'}, 69 | {'sourceNodeID' : 'TOR_1','destinationNodeID' : 'Server_13'}, 70 | {'sourceNodeID' : 'TOR_2','destinationNodeID' : 'Server_14'}, 71 | {'sourceNodeID' : 'TOR_3','destinationNodeID' : 'Server_15'}, 72 | {'sourceNodeID' : 'TOR_4','destinationNodeID' : 'Server_16'}, 73 | ] 74 | x = NetPlot() 75 | x.addNode(nodeName='Router_17',nodeType='router') 76 | x.addNode(nodeName='Router_18',nodeType='router') 77 | x.addLink('Router_17','Router_18') 78 | x.addLink('Router_17','Router_1') 79 | x.addNodeList(device_list) 80 | x.addLinkList(connection_list) 81 | # print(x.display_xml()) 82 | # print(x) 83 | x.exportXML('examples/output.xml') 84 | # for mxCell in x.root: 85 | # print(item.get('value')) 86 | # print(item.get('id')) 87 | # print(mxCell.attrib) 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # __init__.py -------------------------------------------------------------------------------- /tests/unit_test.py: -------------------------------------------------------------------------------- 1 | from drawio_network_plot import NetPlot 2 | 3 | device_list = [ 4 | # Routers 5 | {'nodeName' : 'Router_1','nodeType' : 'router','nodeDescription' : 'External Peering Provider 1'}, 6 | {'nodeName' : 'Router_2','nodeType' : 'router','nodeDescription' : 'External Peering Provider 2'}, 7 | # Core 8 | {'nodeName' : 'Core_switch_1','nodeType' : 'l3_switch','nodeDescription' : 'Spine Switch 01'}, 9 | {'nodeName' : 'Core_switch_2','nodeType' : 'l3_switch','nodeDescription' : 'Spine Switch 02'}, 10 | # Firewalls 11 | {'nodeName' : 'FW_1','nodeType' : 'firewall','nodeDescription' : 'Firewall 01'}, 12 | {'nodeName' : 'FW_2','nodeType' : 'firewall','nodeDescription' : 'Firewall 02'}, 13 | # Leafs 14 | {'nodeName' : 'TOR_1','nodeType' : 'l2_switch','nodeDescription' : 'Leaf Switch 01'}, 15 | {'nodeName' : 'TOR_2','nodeType' : 'l2_switch','nodeDescription' : 'Leaf Switch 02'}, 16 | {'nodeName' : 'TOR_3','nodeType' : 'l2_switch','nodeDescription' : 'Leaf Switch 03'}, 17 | {'nodeName' : 'TOR_4','nodeType' : 'l2_switch','nodeDescription' : 'Leaf Switch 04'}, 18 | # Servers 19 | {'nodeName' : 'Server_1','nodeType' : 'server','nodeDescription' : 'Server 1'}, 20 | {'nodeName' : 'Server_2','nodeType' : 'server','nodeDescription' : 'Server 2'}, 21 | {'nodeName' : 'Server_3','nodeType' : 'server','nodeDescription' : 'Server 3'}, 22 | {'nodeName' : 'Server_4','nodeType' : 'server','nodeDescription' : 'Server 4'}, 23 | {'nodeName' : 'Server_5','nodeType' : 'server','nodeDescription' : 'Server 5'}, 24 | {'nodeName' : 'Server_6','nodeType' : 'server','nodeDescription' : 'Server 6'}, 25 | {'nodeName' : 'Server_7','nodeType' : 'server','nodeDescription' : 'Server 7'}, 26 | {'nodeName' : 'Server_8','nodeType' : 'server','nodeDescription' : 'Server 8'}, 27 | {'nodeName' : 'Server_9','nodeType' : 'server','nodeDescription' : 'Server 9'}, 28 | {'nodeName' : 'Server_10','nodeType' : 'server','nodeDescription' : 'Server 10'}, 29 | {'nodeName' : 'Server_11','nodeType' : 'server','nodeDescription' : 'Server 11'}, 30 | {'nodeName' : 'Server_12','nodeType' : 'server','nodeDescription' : 'Server 12'}, 31 | {'nodeName' : 'Server_13','nodeType' : 'server','nodeDescription' : 'Server 13'}, 32 | {'nodeName' : 'Server_14','nodeType' : 'server','nodeDescription' : 'Server 14'}, 33 | {'nodeName' : 'Server_15','nodeType' : 'server','nodeDescription' : 'Server 15'}, 34 | {'nodeName' : 'Server_16','nodeType' : 'server','nodeDescription' : 'Server 16'}, 35 | ] 36 | 37 | 38 | connection_list = [ 39 | # Router to Core 40 | {'sourceNodeID' : 'Router_1','destinationNodeID' : 'Core_switch_1'}, 41 | {'sourceNodeID' : 'Router_1','destinationNodeID' : 'Core_switch_2'}, 42 | {'sourceNodeID' : 'Router_2','destinationNodeID' : 'Core_switch_1'}, 43 | {'sourceNodeID' : 'Router_2','destinationNodeID' : 'Core_switch_2'}, 44 | # Core to FW 45 | {'sourceNodeID' : 'Core_switch_1','destinationNodeID' : 'FW_1'}, 46 | {'sourceNodeID' : 'Core_switch_2','destinationNodeID' : 'FW_2'}, 47 | # Core to TOR 48 | {'sourceNodeID' : 'Core_switch_1','destinationNodeID' : 'TOR_1'}, 49 | {'sourceNodeID' : 'Core_switch_1','destinationNodeID' : 'TOR_2'}, 50 | {'sourceNodeID' : 'Core_switch_1','destinationNodeID' : 'TOR_3'}, 51 | {'sourceNodeID' : 'Core_switch_1','destinationNodeID' : 'TOR_4'}, 52 | {'sourceNodeID' : 'Core_switch_2','destinationNodeID' : 'TOR_1'}, 53 | {'sourceNodeID' : 'Core_switch_2','destinationNodeID' : 'TOR_2'}, 54 | {'sourceNodeID' : 'Core_switch_2','destinationNodeID' : 'TOR_3'}, 55 | {'sourceNodeID' : 'Core_switch_2','destinationNodeID' : 'TOR_4'}, 56 | # TOR to Server 57 | {'sourceNodeID' : 'TOR_1','destinationNodeID' : 'Server_1'}, 58 | {'sourceNodeID' : 'TOR_2','destinationNodeID' : 'Server_2'}, 59 | {'sourceNodeID' : 'TOR_3','destinationNodeID' : 'Server_3'}, 60 | {'sourceNodeID' : 'TOR_4','destinationNodeID' : 'Server_4'}, 61 | {'sourceNodeID' : 'TOR_1','destinationNodeID' : 'Server_5'}, 62 | {'sourceNodeID' : 'TOR_2','destinationNodeID' : 'Server_6'}, 63 | {'sourceNodeID' : 'TOR_3','destinationNodeID' : 'Server_7'}, 64 | {'sourceNodeID' : 'TOR_4','destinationNodeID' : 'Server_8'}, 65 | {'sourceNodeID' : 'TOR_1','destinationNodeID' : 'Server_9'}, 66 | {'sourceNodeID' : 'TOR_2','destinationNodeID' : 'Server_10'}, 67 | {'sourceNodeID' : 'TOR_3','destinationNodeID' : 'Server_11'}, 68 | {'sourceNodeID' : 'TOR_4','destinationNodeID' : 'Server_12'}, 69 | {'sourceNodeID' : 'TOR_1','destinationNodeID' : 'Server_13'}, 70 | {'sourceNodeID' : 'TOR_2','destinationNodeID' : 'Server_14'}, 71 | {'sourceNodeID' : 'TOR_3','destinationNodeID' : 'Server_15'}, 72 | {'sourceNodeID' : 'TOR_4','destinationNodeID' : 'Server_16'}, 73 | ] 74 | x = NetPlot() 75 | x.addNode(nodeName='Router_17',nodeType='router') 76 | x.addNode(nodeName='Router_18',nodeType='router') 77 | x.addLink('Router_17','Router_18') 78 | x.addLink('Router_17','Router_1') 79 | x.addNodeList(device_list) 80 | x.addLinkList(connection_list) 81 | # print(x.display_xml()) 82 | # print(x) 83 | x.exportXML('examples/output.xml') 84 | for mxCell in x.root: 85 | # print(item.get('value')) 86 | # print(item.get('id')) 87 | print(mxCell.attrib) 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | --------------------------------------------------------------------------------