Blog

  • Home
  • Blog
  • Building a Frontend for Network Device Management using React, D3.js, and Streamlit

Building a Frontend for Network Device Management using React, D3.js, and Streamlit

Category: Networking Author: Kolyo Dimanov Posted: 05 Jun 2024 0 Comments

In this tutorial, we will build a frontend application to manage networking devices (such as switches and routers and links between them) using React. We'll enhance the user interface with visualizations using D3.js and create an interactive dashboard with Streamlit. This frontend will interact with the RESTful API we created previously.

Prerequisites

Before we start, ensure you have the following installed on your machine:
  • Node.js and npm (to set up the React application)
  • Python (to run Streamlit) and the following python packages:
    • streamlit
    • requests
    • Pandas
    • networkx
    • pyvis
  • The RESTful API for managing networking devices (from the previous blog post / Understanding RESTful Services)

Setting Up the React Application

First, create a new React application using Create React App:

npx create-react-app network-device-manager
cd network-device-manager

Next, install the required packages:

npm install axios d3

Creating the Device Management Component

Create a new folder named components inside the src directory and add a file named DeviceManager.js. This component will handle the CRUD operations for our devices.

// src/components/DeviceManager.js
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import DeviceChart from './DeviceChart';

const API_URL = 'http://127.0.0.1:5000';

const DeviceManager = () => {
 
const [network, setNetwork] = useState({ devices: [], links: [] });
 
const [loading, setLoading] = useState(true);
 
const [deviceType, setDeviceType] = useState('');
 
const [deviceName, setDeviceName] = useState('');
 
const [deviceIp, setDeviceIp] = useState('');
 
const [source, setSource] = useState('');
 
const [target, setTarget] = useState('');

 useEffect(() => {
   fetchNetwork();
 }, []);

 
const fetchNetwork = async () => {
  
try {
    
const response = await axios.get(`${API_URL}/network`);
     setNetwork(response.data);
   }
catch (error) {
    
console.error('Error fetching network:', error);
   }
finally {
     setLoading(
false);
   }
 };

 
const addDevice = async () => {
  
try {
    
const response = await axios.post(`${API_URL}/devices`, {
       type: deviceType,
       name: deviceName,
       ip_address: deviceIp
     });
     fetchNetwork();
// Refresh network data after adding a new device
   }
catch (error) {
    
console.error('Error adding device:', error);
   }
 };

 
const deleteDevice = async (id) => {
  
try {
    
await axios.delete(`${API_URL}/devices/${id}`);
     fetchNetwork();
// Refresh network data after deleting a device
   }
catch (error) {
    
console.error('Error deleting device:', error);
   }
 };

 
const addLink = async () => {
  
try {
    
const response = await axios.post(`${API_URL}/links`, {
       source:
parseInt(source),
       target:
parseInt(target)
     });
     fetchNetwork();
// Refresh network data after adding a link
   }
catch (error) {
    
console.error('Error adding link:', error);
   }
 };

 
if (loading) {
  
return <p>Loading...</p>;
 }

 
return (
   <div>
     <h2>Network Devices</h2>
     <DeviceChart network={network} />

     <h3>Add Device</h3>
     <input
       type="text"
       placeholder="Type"
       value={deviceType}
       onChange={(e) => setDeviceType(e.target.value)}
     />
     <input
       type="text"
       placeholder="Name"
       value={deviceName}
       onChange={(e) => setDeviceName(e.target.value)}
     />
     <input
       type="text"
       placeholder="IP Address"
       value={deviceIp}
       onChange={(e) => setDeviceIp(e.target.value)}
     />
     <button onClick={addDevice}>Add Device</button>

     <h3>Add Link</h3>
     <input
       type="text"
       placeholder="Source ID"
       value={source}
       onChange={(e) => setSource(e.target.value)}
     />
     <input
       type="text"
       placeholder="Target ID"
       value={target}
       onChange={(e) => setTarget(e.target.value)}
     />
     <button onClick={addLink}>Add Link</button>

     <h3>Delete Device</h3>
     {network.devices.map((device) => (
       <div key={device.id}>
         <span>{device.name} ({device.id})</span>
         <button onClick={() => deleteDevice(device.id)}>Delete</button>
       </div>
     ))}
   </div>
 );
};

export default DeviceManager;

Integrating D3.js for Visualization

Create a new file named DeviceChart.js inside the components folder. This component will visualize the devices using D3.js.

// src/components/DeviceChart.js
import React, { useRef, useEffect } from 'react';
import * as d3 from 'd3';

const DeviceChart = ({ network }) => {
 
const svgRef = useRef();

 useEffect(() => {
  
const width = 800;
  
const height = 600;

  
const svg = d3.select(svgRef.current)
     .attr(
'width', width)
     .attr(
'height', height);

   svg.selectAll(
'*').remove(); // Clear previous graph

  
// Create a simulation with devices
  
const simulation = d3.forceSimulation(network.devices)
     .force(
'link', d3.forceLink(network.links).id(d => d.id).distance(100))
     .force(
'charge', d3.forceManyBody().strength(-200))
     .force(
'center', d3.forceCenter(width / 2, height / 2));

  
// Create link elements
  
const link = svg.append('g')
     .selectAll(
'line')
     .data(network.links)
     .enter()
     .append(
'line')
     .attr(
'stroke', '#999')
     .attr(
'stroke-opacity', 0.6)
     .attr(
'stroke-width', 2);

  
// Create node elements
  
const node = svg.append('g')
     .selectAll(
'circle')
     .data(network.devices)
     .enter()
     .append(
'circle')
     .attr(
'r', 8)
     .attr(
'fill', 'blue')
     .call(drag(simulation));

   node.append(
'title')
     .text(d => d.name);

  
// Update positions on every tick
   simulation.on(
'tick', () => {
     link
       .attr(
'x1', d => d.source.x)
       .attr(
'y1', d => d.source.y)
       .attr(
'x2', d => d.target.x)
       .attr(
'y2', d => d.target.y);

     node
       .attr(
'cx', d => d.x)
       .attr(
'cy', d => d.y);
   });

  
function drag(simulation) {
    
function dragstarted(event) {
      
if (!event.active) simulation.alphaTarget(0.3).restart();
       event.subject.fx = event.subject.x;
       event.subject.fy = event.subject.y;
     }

    
function dragged(event) {
       event.subject.fx = event.x;
       event.subject.fy = event.y;
     }

    
function dragended(event) {
      
if (!event.active) simulation.alphaTarget(0);
       event.subject.fx =
null;
       event.subject.fy =
null;
     }

    
return d3.drag()
       .on(
'start', dragstarted)
       .on(
'drag', dragged)
       .on(
'end', dragended);
   }

 }, [network]);

 
return <svg ref={svgRef}></svg>;
};

export default DeviceChart;

Combining Components in App.js

Update the App.js file to include our DeviceManager and DeviceChart components.

// src/App.js
import React from 'react';
import DeviceManager from './components/DeviceManager';

function App() {
 
return (
  
<div className="App">
    
<header className="App-header">
      
<h1>Network Device Manager</h1>
    
</header>
    
<DeviceManager />
  
</div>
 );
}

export default App;

Running the React Application

To start the React application, run:

npm start

Your React application should now be running at http://localhost:3000/, where you can manage your network devices and view a simple bar chart of the devices.

Integrating Streamlit for Interactive Dashboards

Streamlit is a great tool for creating interactive dashboards in Python. We will create a Streamlit app to display device data and interact with the API. First, install Streamlit and the additional needed python libraries for the visualization of the network topology:

pip install streamlit

pip install requests

pip install Pandas

pip install networkx

pip install pyvis

Creating the Streamlit Dashboard

Create a new file named dashboard.py:

import streamlit as st

import requests

import pandas as pd

import networkx as nx

from pyvis.network import Network

import streamlit.components.v1 as components

 

# API URL

API_URL = 'http://127.0.0.1:5000'

 

# Helper function to fetch devices

def fetch_devices():

   response = requests.get(f'{API_URL}/devices')

   return response.json()

 

# Helper function to fetch links

def fetch_links():

   response = requests.get(f'{API_URL}/links')

   return response.json()

 

# Helper function to add a device

def add_device(device_type, device_name, device_ip):

   response = requests.post(f'{API_URL}/devices', json={

       'type': device_type,

       'name': device_name,

       'ip_address': device_ip

   })

   return response.json()

 

# Helper function to delete a device

def delete_device(device_id):

   response = requests.delete(f'{API_URL}/devices/{device_id}')

   return response.json()

 

# Helper function to add a link

def add_link(source_id, target_id):

   response = requests.post(f'{API_URL}/links', json={

       'source': source_id,

       'target': target_id

   })

   return response.json()

 

# Helper function to delete a link

def delete_link(link_id):

   response = requests.delete(f'{API_URL}/links/{link_id}')

   return response.json()

 

# Function to draw the network graph using PyVis

def draw_graph(devices, links):

   G = Network(height='600px', width='100%', notebook=True)

 

   for device in devices:

       G.add_node(device['id'], label=device['name'], title=device['ip_address'])

 

   for link in links:

       G.add_edge(link['source'], link['target'])

 

   G.save_graph('network.html')

   return 'network.html'

 

# Streamlit UI

st.title('Network Device Manager')

 

# Add Device

st.header('Add Device')

device_type = st.text_input('Device Type')

device_name = st.text_input('Device Name')

device_ip = st.text_input('Device IP Address')

if st.button('Add Device'):

   result = add_device(device_type, device_name, device_ip)

   st.write(result)

 

# Delete Device

st.header('Delete Device')

device_id = st.number_input('Device ID', min_value=1, step=1)

if st.button('Delete Device'):

   result = delete_device(device_id)

   st.write(result)

 

# Add Link

st.header('Add Link')

source_id = st.number_input('Source Device ID', min_value=1, step=1, key='source')

target_id = st.number_input('Target Device ID', min_value=1, step=1, key='target')

if st.button('Add Link'):

   result = add_link(source_id, target_id)

   st.write(result)

 

# Fetch and visualize the network

st.header('Network Visualization')

if st.button('Refresh Network'):

   devices = fetch_devices()

   links = fetch_links()

   st.write(pd.DataFrame(devices))

   st.write(pd.DataFrame(links))

   graph_path = draw_graph(devices, links)

   with open(graph_path, 'r') as f:

       html_content = f.read()

   components.html(html_content, height=600)

Running the Streamlit Application

To run your Streamlit application, use the following command:

streamlit run dashboard.py

Your Streamlit dashboard should now be running at http://localhost:8501/, providing an interactive interface to manage your network devices.

Conclusion

In this tutorial, we built a frontend application to manage networking devices using React, D3.js, and Streamlit. We created a React component for CRUD operations on the devices and links between the devices, integrated D3.js for data visualization, and set up a Streamlit dashboard for an interactive and user-friendly interface. This combination of tools provides a powerful and flexible way to manage and visualize network devices. Happy coding! At Mina-Soft, we specialize in building industrial-scale APIs and interactive frontend applications across various technology stacks. Our team of professionals is dedicated to delivering high-quality software tailored to meet the demands of modern enterprises. We pride ourselves on creating robust, scalable, and efficient solutions.

Screenshots of examples:

Streamlit dashboard for manipulation of network elements and links:
Streamlit Network Visualisation section:
React with D3.js simple visualization of the same functionality:




leave a comment


blog categories

Send us a message

If you want to say something to us, feel free to do it


I agree with Privacy policy