In Part 3, we discussed using the python-ipfabric SDK to interact with IP Fabric's API - did you know that you can also create diagrams using the API? Today we will be using the python-ipfabric-diagrams SDK. Since diagramming is a coding heavy topic, I am also working on a webinar to show live examples and more advanced features such as turning layers off and ungrouping links.
Find a coding example on GitLab at 2022-05-20-api-programmability-part-4-diagramming.
There are four options for returning data in the IPFDiagram
class.
Each of these methods has five input parameters, and only the first one is required:
This is the most basic diagram as it takes a single IP address. The imports will differ depending on the type of graph.
# 1_host2gateway.py
from ipfabric_diagrams import IPFDiagram, Host2GW
if __name__ == '__main__':
diagram = IPFDiagram(base_url='https://demo3.ipfabric.io/', token='token', verify=False)
with open('1_host2gateway.png', 'wb') as f:
f.write(diagram.diagram_png(Host2GW(startingPoint='10.241.1.108')))
The Network class accepts 3 input parameters. If no parameters are defined, this will create a graph similar to going to the UI and Diagrams > Network.
site name
or a List of site names
.# 2_network.py
from ipfabric_diagrams import IPFDiagram, Network, Layout
if __name__ == '__main__':
diagram = IPFDiagram(base_url='https://demo3.ipfabric.io/', token='token', verify=False)
with open('2_1_network.png', 'wb') as f:
f.write(diagram.diagram_png(Network(sites='MPLS', all_network=True)))
with open('2_2_network.png', 'wb') as f:
f.write(diagram.diagram_png(Network(sites=['LAB01', 'HWLAB'], all_network=False)))
with open('2_3_network.png', 'wb') as f:
f.write(diagram.diagram_png(
Network(sites='L71', all_network=False, layouts=[Layout(path='L71', layout='upwardTree')])
))
Before moving on to Unicast and Multicast let's take a look at how to overlay a snapshot comparison or specific intent rule onto your graph. You can apply this to any type of graph.
# 3_network_overlay.py
from ipfabric_diagrams import IPFDiagram, Network, Overlay
if __name__ == '__main__':
diagram = IPFDiagram(base_url='https://demo3.ipfabric.io/', token='token', verify=False)
with open('3_1_network_snap_overlay.png', 'wb') as f:
f.write(diagram.diagram_png(Network(sites='MPLS', all_network=False),
overlay=Overlay(snapshotToCompare='$prev')))
To overlay an Intent Rule you must first get the ID of the rule to submit. In this example, we are using the ipfabric package to load the intents and get a rule by name. Find more examples of extracting intent rule IDs here.
# 3_network_overlay.py
from ipfabric import IPFClient
from ipfabric_diagrams import IPFDiagram, Network, Overlay
if __name__ == '__main__':
diagram = IPFDiagram(base_url='https://demo3.ipfabric.io/', token='token', verify=False)
# Get intent rule ID
ipf = IPFClient(base_url='https://demo3.ipfabric.io/', token='token', verify=False)
ipf.intent.load_intent()
intent_rule_id = ipf.intent.intent_by_name['NTP Reachable Sources'].intent_id
with open('3_2_network_intent_overlay.png', 'wb') as f:
f.write(diagram.diagram_png(Network(sites=['L71'], all_network=False),
overlay=Overlay(intentRuleId=intent_rule_id)))
The next two examples make it a bit clearer why we first create a python object and then pass it into the diagramming functions. The amount of options required are quite lengthy, and this keeps your code cleaner and provides great type hints (see below). Additionally, it has many built-in checks to ensure you provide the correct data before submitting the payload to IP Fabric and returning an error.
For all valid ICMP types please refer to icmp.py.
# 5_unicast_path_lookup.py
from ipfabric_diagrams import IPFDiagram, Unicast
from ipfabric_diagrams import icmp
if __name__ == '__main__':
diagram = IPFDiagram(base_url='https://demo3.ipfabric.io/', token='token', verify=False)
unicast_icmp = Unicast(
startingPoint='10.47.117.112',
destinationPoint='10.66.123.117',
protocol='icmp',
icmp=icmp.ECHO_REQUEST, # Dict is also valid: {'type': 0, 'code': 0}
ttl=64,
securedPath=False # UI Option 'Security Rules'; True == 'Drop'; False == 'Continue'
)
with open('5_1_unicast_icmp.png', 'wb') as f:
f.write(diagram.diagram_png(unicast_icmp))
TCP and UDP accept srcPorts
and dstPorts
which can be a single port number, a comma-separated list, a range of ports separated by a -
, or any combination of them. The applications
, srcRegions
, and dstRegions
arguments are used for Zone Firewall rule checks and these default to any (.*
).
# 5_unicast_path_lookup.py
from ipfabric_diagrams import IPFDiagram, Unicast, OtherOptions, Algorithm, EntryPoint
if __name__ == '__main__':
diagram = IPFDiagram(base_url='https://demo3.ipfabric.io/', token='token', verify=False)
unicast_tcp = Unicast(
startingPoint='10.47.117.112',
destinationPoint='10.66.123.117',
protocol='tcp',
srcPorts='1024,2048-4096',
dstPorts='80,443',
otherOptions=OtherOptions(applications='(web|http|https)', tracked=False),
srcRegions='US',
dstRegions='CZ',
ttl=64,
securedPath=False
)
with open(path.join('path_lookup', '5_2_unicast_tcp.png'), 'wb') as f:
f.write(diagram.diagram_png(unicast_tcp))
with open(path.join('path_lookup', '5_3_unicast_tcp_swap_src_dst.png'), 'wb') as f:
f.write(diagram.diagram_png(unicast_tcp, unicast_swap_src_dst=True))
# Subnet Example
unicast_subnet = Unicast(
startingPoint='10.38.115.0/24',
destinationPoint='10.66.126.0/24',
protocol='tcp',
srcPorts='1025',
dstPorts='22',
securedPath=False
)
with open(path.join('path_lookup', '5_4_unicast_subnet.png'), 'wb') as f:
f.write(diagram.diagram_png(unicast_subnet))
This is a new graphing feature in version 4.3 and above that allows you to specify a device and interface a packet enters your network. Perhaps you have a firewall rule to allow a certain IP address or subnet and want to verify that this is functioning correctly. The sn
value is the IP Fabric unique serial number, iface
is the intName
or Interface
column (not to be confused with Original Name
), and the hostname
is also required. The easiest way to collect this information is from the Inventory > Interfaces table. The sn
is not a visible column in the UI, but is available from the API.
# Example pulling Interface Inventory table
from ipfabric import IPFClient
ipf = IPFClient(base_url='https://demo3.ipfabric.io/', token='token', verify=False)
interfaces = ipf.inventory.interfaces.all(columns=['sn', 'hostname', 'intName'])
# 5_unicast_path_lookup.py
from ipfabric_diagrams import IPFDiagram, Unicast, Algorithm, EntryPoint
if __name__ == '__main__':
diagram = IPFDiagram(base_url='https://demo3.ipfabric.io/', token='token', verify=False)
# User Defined Entry Point Example
unicast_entry_point = Unicast(
startingPoint='1.0.0.1',
destinationPoint='10.66.126.0/24',
protocol='tcp',
srcPorts='1025',
dstPorts='22',
securedPath=True,
firstHopAlgorithm=Algorithm(entryPoints=[
EntryPoint(sn='test', iface='eth0', hostname='test'),
dict(sn='test', iface='eth0', hostname='test') # You can also use a dictionary
])
)
Multicast is very similar to Unicast except some of the parameter names have changed. You can also specify a receiver
IP address but this is optional.
# 7_multicast.py
from ipfabric_diagrams import IPFDiagram, Multicast
if __name__ == '__main__':
diagram = IPFDiagram(base_url='https://demo3.ipfabric.io/', token='token', verify=False)
multicast = Multicast(
source='10.33.230.2',
group='233.1.1.1',
receiver='10.33.244.200', # Optional
protocol='tcp',
srcPorts='1024,2048-4096',
dstPorts='80,443',
)
with open('7_multicast.png', 'wb') as f:
f.write(diagram.diagram_png(multicast))
One of the great advantages of using this package is returning a Python object instead of returning the raw JSON. This allows a user to more easily understand the complex textual data returned by IP Fabric that represents how the edges (links) connect to the nodes (devices, clouds, etc.) and the decisions a packet may take. You can accomplish this via the JSON output but returning an object provides type hints along with the ability to export the model as a JSON schema. Please note that the model is not the exact same as the JSON output and some structure has been changed for ease of use. It also dynamically links some internal objects to eliminate the need to do extra lookups and references.
# 6_json_vs_model.py
from ipfabric_diagrams.output_models.graph_result import GraphResult
if __name__ == '__main__':
print(GraphResult.schema_json(indent=2))
"""
{
"title": "GraphResult",
"type": "object",
"properties": {
"nodes": {
"title": "Nodes",
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/Node"
}
},
"edges": {
"title": "Edges",
"type": "object",
"additionalProperties": {
"anyOf": [
{
"$ref": "#/definitions/NetworkEdge"
},
{
"$ref": "#/definitions/PathLookupEdge"
}
]
}
},
...
"""
The ability to create diagrams using the API allows for greater automation and integration into other applications. Many of our customers use this feature to create chatbots to speed up troubleshooting, as shown below. This example is from the Network to Code nautobot-plugin-chatops-ipfabric plugin.
Another useful feature is performing a Path Lookup and parsing the JSON output to ensure traffic is flowing apart of a review process. We have partnered with Itential to demonstrate how using their low-code automation platform can automate ServiceNow requests and ensure that a newly deployed IP has access to correct network services during a Change Control review. Keep an eye out for a video of this exciting demonstration!
If you have any questions, comments, or bug requests please send an email to [email protected] or open an issue request on the GitLab repository.
In Part 3, we discussed using the python-ipfabric SDK to interact with IP Fabric's API - did you know that you can also create diagrams using the API? Today we will be using the python-ipfabric-diagrams SDK. Since diagramming is a coding heavy topic, I am also working on a webinar to show live examples and more advanced features such as turning layers off and ungrouping links.
Find a coding example on GitLab at 2022-05-20-api-programmability-part-4-diagramming.
There are four options for returning data in the IPFDiagram
class.
Each of these methods has five input parameters, and only the first one is required:
This is the most basic diagram as it takes a single IP address. The imports will differ depending on the type of graph.
# 1_host2gateway.py
from ipfabric_diagrams import IPFDiagram, Host2GW
if __name__ == '__main__':
diagram = IPFDiagram(base_url='https://demo3.ipfabric.io/', token='token', verify=False)
with open('1_host2gateway.png', 'wb') as f:
f.write(diagram.diagram_png(Host2GW(startingPoint='10.241.1.108')))
The Network class accepts 3 input parameters. If no parameters are defined, this will create a graph similar to going to the UI and Diagrams > Network.
site name
or a List of site names
.# 2_network.py
from ipfabric_diagrams import IPFDiagram, Network, Layout
if __name__ == '__main__':
diagram = IPFDiagram(base_url='https://demo3.ipfabric.io/', token='token', verify=False)
with open('2_1_network.png', 'wb') as f:
f.write(diagram.diagram_png(Network(sites='MPLS', all_network=True)))
with open('2_2_network.png', 'wb') as f:
f.write(diagram.diagram_png(Network(sites=['LAB01', 'HWLAB'], all_network=False)))
with open('2_3_network.png', 'wb') as f:
f.write(diagram.diagram_png(
Network(sites='L71', all_network=False, layouts=[Layout(path='L71', layout='upwardTree')])
))
Before moving on to Unicast and Multicast let's take a look at how to overlay a snapshot comparison or specific intent rule onto your graph. You can apply this to any type of graph.
# 3_network_overlay.py
from ipfabric_diagrams import IPFDiagram, Network, Overlay
if __name__ == '__main__':
diagram = IPFDiagram(base_url='https://demo3.ipfabric.io/', token='token', verify=False)
with open('3_1_network_snap_overlay.png', 'wb') as f:
f.write(diagram.diagram_png(Network(sites='MPLS', all_network=False),
overlay=Overlay(snapshotToCompare='$prev')))
To overlay an Intent Rule you must first get the ID of the rule to submit. In this example, we are using the ipfabric package to load the intents and get a rule by name. Find more examples of extracting intent rule IDs here.
# 3_network_overlay.py
from ipfabric import IPFClient
from ipfabric_diagrams import IPFDiagram, Network, Overlay
if __name__ == '__main__':
diagram = IPFDiagram(base_url='https://demo3.ipfabric.io/', token='token', verify=False)
# Get intent rule ID
ipf = IPFClient(base_url='https://demo3.ipfabric.io/', token='token', verify=False)
ipf.intent.load_intent()
intent_rule_id = ipf.intent.intent_by_name['NTP Reachable Sources'].intent_id
with open('3_2_network_intent_overlay.png', 'wb') as f:
f.write(diagram.diagram_png(Network(sites=['L71'], all_network=False),
overlay=Overlay(intentRuleId=intent_rule_id)))
The next two examples make it a bit clearer why we first create a python object and then pass it into the diagramming functions. The amount of options required are quite lengthy, and this keeps your code cleaner and provides great type hints (see below). Additionally, it has many built-in checks to ensure you provide the correct data before submitting the payload to IP Fabric and returning an error.
For all valid ICMP types please refer to icmp.py.
# 5_unicast_path_lookup.py
from ipfabric_diagrams import IPFDiagram, Unicast
from ipfabric_diagrams import icmp
if __name__ == '__main__':
diagram = IPFDiagram(base_url='https://demo3.ipfabric.io/', token='token', verify=False)
unicast_icmp = Unicast(
startingPoint='10.47.117.112',
destinationPoint='10.66.123.117',
protocol='icmp',
icmp=icmp.ECHO_REQUEST, # Dict is also valid: {'type': 0, 'code': 0}
ttl=64,
securedPath=False # UI Option 'Security Rules'; True == 'Drop'; False == 'Continue'
)
with open('5_1_unicast_icmp.png', 'wb') as f:
f.write(diagram.diagram_png(unicast_icmp))
TCP and UDP accept srcPorts
and dstPorts
which can be a single port number, a comma-separated list, a range of ports separated by a -
, or any combination of them. The applications
, srcRegions
, and dstRegions
arguments are used for Zone Firewall rule checks and these default to any (.*
).
# 5_unicast_path_lookup.py
from ipfabric_diagrams import IPFDiagram, Unicast, OtherOptions, Algorithm, EntryPoint
if __name__ == '__main__':
diagram = IPFDiagram(base_url='https://demo3.ipfabric.io/', token='token', verify=False)
unicast_tcp = Unicast(
startingPoint='10.47.117.112',
destinationPoint='10.66.123.117',
protocol='tcp',
srcPorts='1024,2048-4096',
dstPorts='80,443',
otherOptions=OtherOptions(applications='(web|http|https)', tracked=False),
srcRegions='US',
dstRegions='CZ',
ttl=64,
securedPath=False
)
with open(path.join('path_lookup', '5_2_unicast_tcp.png'), 'wb') as f:
f.write(diagram.diagram_png(unicast_tcp))
with open(path.join('path_lookup', '5_3_unicast_tcp_swap_src_dst.png'), 'wb') as f:
f.write(diagram.diagram_png(unicast_tcp, unicast_swap_src_dst=True))
# Subnet Example
unicast_subnet = Unicast(
startingPoint='10.38.115.0/24',
destinationPoint='10.66.126.0/24',
protocol='tcp',
srcPorts='1025',
dstPorts='22',
securedPath=False
)
with open(path.join('path_lookup', '5_4_unicast_subnet.png'), 'wb') as f:
f.write(diagram.diagram_png(unicast_subnet))
This is a new graphing feature in version 4.3 and above that allows you to specify a device and interface a packet enters your network. Perhaps you have a firewall rule to allow a certain IP address or subnet and want to verify that this is functioning correctly. The sn
value is the IP Fabric unique serial number, iface
is the intName
or Interface
column (not to be confused with Original Name
), and the hostname
is also required. The easiest way to collect this information is from the Inventory > Interfaces table. The sn
is not a visible column in the UI, but is available from the API.
# Example pulling Interface Inventory table
from ipfabric import IPFClient
ipf = IPFClient(base_url='https://demo3.ipfabric.io/', token='token', verify=False)
interfaces = ipf.inventory.interfaces.all(columns=['sn', 'hostname', 'intName'])
# 5_unicast_path_lookup.py
from ipfabric_diagrams import IPFDiagram, Unicast, Algorithm, EntryPoint
if __name__ == '__main__':
diagram = IPFDiagram(base_url='https://demo3.ipfabric.io/', token='token', verify=False)
# User Defined Entry Point Example
unicast_entry_point = Unicast(
startingPoint='1.0.0.1',
destinationPoint='10.66.126.0/24',
protocol='tcp',
srcPorts='1025',
dstPorts='22',
securedPath=True,
firstHopAlgorithm=Algorithm(entryPoints=[
EntryPoint(sn='test', iface='eth0', hostname='test'),
dict(sn='test', iface='eth0', hostname='test') # You can also use a dictionary
])
)
Multicast is very similar to Unicast except some of the parameter names have changed. You can also specify a receiver
IP address but this is optional.
# 7_multicast.py
from ipfabric_diagrams import IPFDiagram, Multicast
if __name__ == '__main__':
diagram = IPFDiagram(base_url='https://demo3.ipfabric.io/', token='token', verify=False)
multicast = Multicast(
source='10.33.230.2',
group='233.1.1.1',
receiver='10.33.244.200', # Optional
protocol='tcp',
srcPorts='1024,2048-4096',
dstPorts='80,443',
)
with open('7_multicast.png', 'wb') as f:
f.write(diagram.diagram_png(multicast))
One of the great advantages of using this package is returning a Python object instead of returning the raw JSON. This allows a user to more easily understand the complex textual data returned by IP Fabric that represents how the edges (links) connect to the nodes (devices, clouds, etc.) and the decisions a packet may take. You can accomplish this via the JSON output but returning an object provides type hints along with the ability to export the model as a JSON schema. Please note that the model is not the exact same as the JSON output and some structure has been changed for ease of use. It also dynamically links some internal objects to eliminate the need to do extra lookups and references.
# 6_json_vs_model.py
from ipfabric_diagrams.output_models.graph_result import GraphResult
if __name__ == '__main__':
print(GraphResult.schema_json(indent=2))
"""
{
"title": "GraphResult",
"type": "object",
"properties": {
"nodes": {
"title": "Nodes",
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/Node"
}
},
"edges": {
"title": "Edges",
"type": "object",
"additionalProperties": {
"anyOf": [
{
"$ref": "#/definitions/NetworkEdge"
},
{
"$ref": "#/definitions/PathLookupEdge"
}
]
}
},
...
"""
The ability to create diagrams using the API allows for greater automation and integration into other applications. Many of our customers use this feature to create chatbots to speed up troubleshooting, as shown below. This example is from the Network to Code nautobot-plugin-chatops-ipfabric plugin.
Another useful feature is performing a Path Lookup and parsing the JSON output to ensure traffic is flowing apart of a review process. We have partnered with Itential to demonstrate how using their low-code automation platform can automate ServiceNow requests and ensure that a newly deployed IP has access to correct network services during a Change Control review. Keep an eye out for a video of this exciting demonstration!
If you have any questions, comments, or bug requests please send an email to [email protected] or open an issue request on the GitLab repository.