Graham Paasch commited on
Commit
5f0d60a
·
1 Parent(s): e8329fa

Add Build Network tab with MCP integration

Browse files

- Added build_network_from_description MCP tool integration
- New Build Network tab with music festival example
- Users can describe networks in natural language
- Auto-configuration option for devices
- Real network building, not mock demos
- Updated network_ops.py with new function
- Full workflow: description → topology → configuration

Files changed (3) hide show
  1. .gitignore +1 -0
  2. agent/network_ops.py +52 -16
  3. app.py +83 -0
.gitignore CHANGED
@@ -2,3 +2,4 @@
2
  venv/
3
  __pycache__/
4
  *.pyc
 
 
2
  venv/
3
  __pycache__/
4
  *.pyc
5
+ *.log
agent/network_ops.py CHANGED
@@ -135,32 +135,68 @@ def configure_device(host: str, commands: List[str], username: str = "admin") ->
135
 
136
  def backup_device_config(host: str, device_name: str, username: str = "admin") -> Dict:
137
  """
138
- Backup device configuration
139
 
140
  Args:
141
- host: Device IP/hostname
142
- device_name: Friendly name for backup file
143
  username: SSH username
144
 
145
  Returns:
146
- Dict with backup path or error
147
  """
148
  client = get_mcp_client()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
 
 
 
 
 
 
 
 
 
 
150
  try:
151
- result = client.backup_config(host, device_name, username)
152
- return {
153
- "success": True,
154
- "host": host,
155
- "device": device_name,
156
- "backup_info": result
157
- }
 
 
158
  except Exception as e:
159
- return {
160
- "success": False,
161
- "host": host,
162
- "error": str(e)
163
- }
 
 
 
164
 
165
 
166
  # Keep existing NCS simulator integration
 
135
 
136
  def backup_device_config(host: str, device_name: str, username: str = "admin") -> Dict:
137
  """
138
+ Backup device configuration to timestamped file
139
 
140
  Args:
141
+ host: Device IP or hostname
142
+ device_name: Name for the backup
143
  username: SSH username
144
 
145
  Returns:
146
+ Dict with success status and backup info
147
  """
148
  client = get_mcp_client()
149
+ try:
150
+ result = client.call_tool(
151
+ "backup_device_config",
152
+ {
153
+ "host": host,
154
+ "device_name": device_name,
155
+ "username": username
156
+ }
157
+ )
158
+ return result
159
+ except Exception as e:
160
+ return {"success": False, "error": str(e)}
161
+
162
+
163
+ def build_network_from_description(description: str, project_name: str = "overgrowth", auto_configure: bool = True) -> Dict:
164
+ """
165
+ Build a complete network from natural language description
166
+
167
+ This calls the MCP tool that:
168
+ 1. Creates GNS3 topology based on description
169
+ 2. Auto-configures all devices (if auto_configure=True)
170
+ 3. Returns complete network info
171
 
172
+ Args:
173
+ description: Natural language description of network needs
174
+ project_name: GNS3 project name (default: overgrowth)
175
+ auto_configure: Whether to auto-configure devices (default: True)
176
+
177
+ Returns:
178
+ Dict with success status, topology info, and configuration results
179
+ """
180
+ client = get_mcp_client()
181
  try:
182
+ result = client.call_tool(
183
+ "build_network_from_description",
184
+ {
185
+ "description": description,
186
+ "project_name": project_name,
187
+ "auto_configure": auto_configure
188
+ }
189
+ )
190
+ return result
191
  except Exception as e:
192
+ return {"success": False, "error": str(e)}
193
+
194
+
195
+ def simulate_change(steps: List[Dict]) -> Tuple[List[Dict], List[Dict]]:
196
+ """
197
+ Wrapper for backward compatibility
198
+ """
199
+ return simulate_network_change_with_ncs(steps)
200
 
201
 
202
  # Keep existing NCS simulator integration
app.py CHANGED
@@ -14,6 +14,7 @@ from agent.network_ops import (
14
  get_device_configuration,
15
  configure_device,
16
  backup_device_config,
 
17
  )
18
  import json
19
 
@@ -253,6 +254,37 @@ def build_ui():
253
  synapse_btn = gr.Button("Refresh Log")
254
  synapse_md = gr.Markdown(label="Overgrowth Synapse Log")
255
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256
  with gr.Tab("Lab Management"):
257
  gr.Markdown("### 🌱 GNS3 Lab Control\nManage your actual lab infrastructure at lab.grahampaasch.com")
258
 
@@ -406,6 +438,52 @@ def build_ui():
406
  return f"✅ Backup completed for **{dev_name}**\n\n{result.get('backup_info', '')}"
407
  else:
408
  return f"❌ Backup failed: {result.get('error', 'unknown error')}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
409
 
410
  run_btn.click(
411
  fn=on_run,
@@ -471,6 +549,11 @@ def build_ui():
471
  inputs=[device_host, device_user, backup_name],
472
  outputs=[backup_result]
473
  )
 
 
 
 
 
474
 
475
  return demo
476
 
 
14
  get_device_configuration,
15
  configure_device,
16
  backup_device_config,
17
+ build_network_from_description,
18
  )
19
  import json
20
 
 
254
  synapse_btn = gr.Button("Refresh Log")
255
  synapse_md = gr.Markdown(label="Overgrowth Synapse Log")
256
 
257
+ with gr.Tab("Build Network"):
258
+ gr.Markdown("### 🌱 Build Network from Description\nDescribe your network needs in plain English - Overgrowth builds and configures it automatically.")
259
+
260
+ network_description = gr.Textbox(
261
+ label="Network Description",
262
+ placeholder=(
263
+ "Example: I'm running a big music festival with multiple stages. "
264
+ "I need networks for: main stage with audio/video systems, "
265
+ "food truck area with 8 POS terminals, VIP lounge with WiFi, "
266
+ "ticketing booth with 4 terminals, and backstage with artist WiFi. "
267
+ "All need secure management access and internet connectivity."
268
+ ),
269
+ lines=8,
270
+ )
271
+
272
+ with gr.Row():
273
+ build_project_name = gr.Textbox(
274
+ label="Project Name",
275
+ value="overgrowth",
276
+ scale=2
277
+ )
278
+ auto_config_checkbox = gr.Checkbox(
279
+ label="Auto-Configure Devices",
280
+ value=True,
281
+ scale=1
282
+ )
283
+
284
+ build_btn = gr.Button("🚀 Build Network", variant="primary", size="lg")
285
+ build_result = gr.Markdown(label="Build Results")
286
+ build_topology = gr.Code(label="Topology Details", language="json")
287
+
288
  with gr.Tab("Lab Management"):
289
  gr.Markdown("### 🌱 GNS3 Lab Control\nManage your actual lab infrastructure at lab.grahampaasch.com")
290
 
 
438
  return f"✅ Backup completed for **{dev_name}**\n\n{result.get('backup_info', '')}"
439
  else:
440
  return f"❌ Backup failed: {result.get('error', 'unknown error')}"
441
+
442
+ def build_network(description, project, auto_config):
443
+ if not description or not description.strip():
444
+ return "⚠️ Please provide a network description", ""
445
+
446
+ result_md = "### 🏗️ Building Network...\n\n"
447
+ result_md += f"**Description:** {description}\n\n"
448
+ result_md += f"**Project:** {project}\n\n"
449
+ result_md += "---\n\n"
450
+
451
+ result = build_network_from_description(description, project, auto_config)
452
+
453
+ if result.get('success'):
454
+ result_md += "## ✅ Network Built Successfully!\n\n"
455
+
456
+ # Parse the result
457
+ topology_info = result.get('topology', {})
458
+ config_info = result.get('configuration', {})
459
+
460
+ result_md += f"### 📊 Topology Created\n"
461
+ result_md += f"- **Nodes:** {topology_info.get('node_count', 'N/A')}\n"
462
+ result_md += f"- **Links:** {topology_info.get('link_count', 'N/A')}\n\n"
463
+
464
+ if auto_config and config_info:
465
+ result_md += f"### 🔧 Auto-Configuration\n"
466
+ result_md += f"- **Status:** {config_info.get('status', 'Completed')}\n"
467
+ result_md += f"- **Devices Configured:** {config_info.get('configured_count', 'N/A')}\n\n"
468
+
469
+ if config_info.get('details'):
470
+ result_md += "**Configuration Details:**\n"
471
+ result_md += f"```\n{config_info['details']}\n```\n\n"
472
+
473
+ result_md += "### 🎯 Next Steps\n"
474
+ result_md += "1. Go to **Lab Management** tab to view topology\n"
475
+ result_md += "2. Verify device configurations\n"
476
+ result_md += "3. Start testing your network\n"
477
+
478
+ # Return topology as formatted JSON
479
+ import json
480
+ topology_json = json.dumps(result, indent=2)
481
+ return result_md, topology_json
482
+ else:
483
+ error_msg = "## ❌ Build Failed\n\n"
484
+ error_msg += f"**Error:** {result.get('error', 'Unknown error')}\n\n"
485
+ error_msg += "Please check your description and try again."
486
+ return error_msg, ""
487
 
488
  run_btn.click(
489
  fn=on_run,
 
549
  inputs=[device_host, device_user, backup_name],
550
  outputs=[backup_result]
551
  )
552
+ build_btn.click(
553
+ fn=build_network,
554
+ inputs=[network_description, build_project_name, auto_config_checkbox],
555
+ outputs=[build_result, build_topology]
556
+ )
557
 
558
  return demo
559