Homelab: [homelab-agents] - Session 2025-11-30
Added NetBox DHCP pool configuration automation scripts. Created tools to reserve DHCP pool IP ranges (.100-.150) via NetBox API, preventing them from showing as "available" for static assignment. Changes: - Created sys-apps-netbox-dhcp-setup.py: Creates IP Range objects for DHCP pools - Created sys-apps-netbox-reserve-dhcp-ips.py: Reserves individual IPs as "Reserved" status - Updated sys-apps-session-summary.md with comprehensive session documentation - Updated CLAUDE.md with session 2025-11-30 history entry - Configured 255 IPs across 5 VLANs (51 IPs per /24 subnet) - Established DHCP pool standard: .100-.150 for all networks Repository: http://100.120.125.113:3000/pdm/homelab-agents Next Session Focus: Document NetBox token management and create dhcp-pool tag 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
20
CLAUDE.md
20
CLAUDE.md
@@ -107,6 +107,26 @@ This is a shared repository containing Claude Code agents that can be deployed a
|
|||||||
|
|
||||||
## Session History
|
## Session History
|
||||||
|
|
||||||
|
### Session 2025-11-30
|
||||||
|
- **Phase**: NetBox DHCP pool configuration automation
|
||||||
|
- **VPS**: sys-apps
|
||||||
|
- **Repository**: homelab-agents
|
||||||
|
- **Accomplishments**:
|
||||||
|
- Created sys-apps-netbox-dhcp-setup.py to create IP Ranges for DHCP pools via NetBox API
|
||||||
|
- Created sys-apps-netbox-reserve-dhcp-ips.py to reserve individual IPs .100-.150 as "Reserved" status
|
||||||
|
- Configured 5 VLANs with DHCP pool reservations (255 IPs total)
|
||||||
|
- Established DHCP pool standard: .100-.150 (51 IPs per /24 subnet)
|
||||||
|
- Resolved NetBox showing DHCP pool IPs as "available" for static assignment
|
||||||
|
- **Key Decisions**:
|
||||||
|
- Two-step approach: IP Ranges (logical grouping) + Individual IPs (actual reservation)
|
||||||
|
- Status "Reserved" is correct for DHCP pools (allocated to DHCP, not assigned to device)
|
||||||
|
- Scripts placed in ~/.homelab-agents (infrastructure tools, reusable across VPS)
|
||||||
|
- API token authentication via environment variable or command-line argument
|
||||||
|
- **Next Steps** (Prioritized):
|
||||||
|
- Document NetBox token management and rotation policy
|
||||||
|
- Create "dhcp-pool" tag in NetBox UI for easier filtering
|
||||||
|
- Consider automation for new VLAN creation
|
||||||
|
|
||||||
### Session 2025-11-28
|
### Session 2025-11-28
|
||||||
- **Phase**: Documentation cleanup and naming convention updates
|
- **Phase**: Documentation cleanup and naming convention updates
|
||||||
- **VPS**: sys-apps (renamed from 108-system-apps)
|
- **VPS**: sys-apps (renamed from 108-system-apps)
|
||||||
|
|||||||
144
sys-apps-netbox-dhcp-setup.py
Executable file
144
sys-apps-netbox-dhcp-setup.py
Executable file
@@ -0,0 +1,144 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
NetBox DHCP Pool Setup Script
|
||||||
|
Marks IPs .100-.150 in each subnet as DHCP pool range
|
||||||
|
"""
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import ipaddress
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
NETBOX_URL = "https://netbox.pdmarf.co.uk"
|
||||||
|
NETBOX_TOKEN = os.environ.get("NETBOX_TOKEN") or sys.argv[1] if len(sys.argv) > 1 else None
|
||||||
|
|
||||||
|
if not NETBOX_TOKEN:
|
||||||
|
print("Error: NetBox API token required")
|
||||||
|
print("Usage: NETBOX_TOKEN='your-token' python3 sys-apps-netbox-dhcp-setup.py")
|
||||||
|
print(" or: python3 sys-apps-netbox-dhcp-setup.py 'your-token'")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# API headers
|
||||||
|
headers = {
|
||||||
|
"Authorization": f"Token {NETBOX_TOKEN}",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Accept": "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_all_prefixes():
|
||||||
|
"""Get all IPv4 prefixes from NetBox"""
|
||||||
|
url = f"{NETBOX_URL}/api/ipam/prefixes/"
|
||||||
|
params = {"family": 4, "limit": 1000} # IPv4 only
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.get(url, headers=headers, params=params, verify=True)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.json()["results"]
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
print(f"Error fetching prefixes: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def create_dhcp_range(prefix_str, prefix_id):
|
||||||
|
"""Create DHCP pool IP range (.100-.150) for a prefix"""
|
||||||
|
try:
|
||||||
|
network = ipaddress.ip_network(prefix_str, strict=False)
|
||||||
|
|
||||||
|
# Calculate .100 and .150 addresses
|
||||||
|
# For /24: x.x.x.100 and x.x.x.150
|
||||||
|
# For other sizes, adjust accordingly
|
||||||
|
|
||||||
|
base_ip = str(network.network_address)
|
||||||
|
octets = base_ip.split('.')
|
||||||
|
|
||||||
|
# Start: .100
|
||||||
|
start_octets = octets[:-1] + ['100']
|
||||||
|
start_ip = '.'.join(start_octets)
|
||||||
|
|
||||||
|
# End: .150
|
||||||
|
end_octets = octets[:-1] + ['150']
|
||||||
|
end_ip = '.'.join(end_octets)
|
||||||
|
|
||||||
|
# Validate IPs are within the network
|
||||||
|
start_addr = ipaddress.ip_address(start_ip)
|
||||||
|
end_addr = ipaddress.ip_address(end_ip)
|
||||||
|
|
||||||
|
if start_addr not in network or end_addr not in network:
|
||||||
|
print(f" ⚠️ Skipping {prefix_str}: DHCP range (.100-.150) not within network")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Create IP Range via API
|
||||||
|
url = f"{NETBOX_URL}/api/ipam/ip-ranges/"
|
||||||
|
data = {
|
||||||
|
"start_address": start_ip,
|
||||||
|
"end_address": end_ip,
|
||||||
|
"status": "active",
|
||||||
|
"description": "DHCP Pool - Dynamic Assignment Range (.100-.150)",
|
||||||
|
"comments": "Auto-created: IPs reserved for DHCP dynamic assignment"
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.post(url, headers=headers, json=data, verify=True)
|
||||||
|
|
||||||
|
if response.status_code == 201:
|
||||||
|
print(f" ✓ Created DHCP range: {start_ip} - {end_ip}")
|
||||||
|
return True
|
||||||
|
elif response.status_code == 400:
|
||||||
|
error_detail = response.json()
|
||||||
|
if "already exists" in str(error_detail).lower() or "overlap" in str(error_detail).lower():
|
||||||
|
print(f" ℹ️ Range already exists: {start_ip} - {end_ip}")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print(f" ✗ Failed: {error_detail}")
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
print(f" ✗ HTTP {response.status_code}: {response.text}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f" ✗ Error processing {prefix_str}: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print(f"\nConnecting to NetBox: {NETBOX_URL}")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
# Get all prefixes
|
||||||
|
print("\nFetching prefixes...")
|
||||||
|
prefixes = get_all_prefixes()
|
||||||
|
print(f"Found {len(prefixes)} prefixes\n")
|
||||||
|
|
||||||
|
# Process each prefix
|
||||||
|
success_count = 0
|
||||||
|
skip_count = 0
|
||||||
|
fail_count = 0
|
||||||
|
|
||||||
|
for prefix in prefixes:
|
||||||
|
prefix_str = prefix["prefix"]
|
||||||
|
prefix_id = prefix["id"]
|
||||||
|
vlan_name = prefix.get("vlan", {}).get("name", "No VLAN") if prefix.get("vlan") else "No VLAN"
|
||||||
|
|
||||||
|
print(f"Processing: {prefix_str} ({vlan_name})")
|
||||||
|
|
||||||
|
result = create_dhcp_range(prefix_str, prefix_id)
|
||||||
|
|
||||||
|
if result:
|
||||||
|
success_count += 1
|
||||||
|
elif result is None:
|
||||||
|
skip_count += 1
|
||||||
|
else:
|
||||||
|
fail_count += 1
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print("Summary:")
|
||||||
|
print(f" ✓ Created/Exists: {success_count}")
|
||||||
|
print(f" ⚠️ Skipped: {skip_count}")
|
||||||
|
print(f" ✗ Failed: {fail_count}")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
print("\n✓ DHCP pool ranges configured!")
|
||||||
|
print(" IPs .100-.150 are now marked as DHCP pool in NetBox")
|
||||||
|
print(f" View at: {NETBOX_URL}/ipam/ip-ranges/")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
158
sys-apps-netbox-reserve-dhcp-ips.py
Executable file
158
sys-apps-netbox-reserve-dhcp-ips.py
Executable file
@@ -0,0 +1,158 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
NetBox DHCP IP Reservation Script
|
||||||
|
Marks individual IPs .100-.150 in each subnet as reserved for DHCP
|
||||||
|
"""
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import ipaddress
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
NETBOX_URL = "https://netbox.pdmarf.co.uk"
|
||||||
|
NETBOX_TOKEN = os.environ.get("NETBOX_TOKEN") or sys.argv[1] if len(sys.argv) > 1 else None
|
||||||
|
|
||||||
|
if not NETBOX_TOKEN:
|
||||||
|
print("Error: NetBox API token required")
|
||||||
|
print("Usage: NETBOX_TOKEN='your-token' python3 sys-apps-netbox-reserve-dhcp-ips.py")
|
||||||
|
print(" or: python3 sys-apps-netbox-reserve-dhcp-ips.py 'your-token'")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# API headers
|
||||||
|
headers = {
|
||||||
|
"Authorization": f"Token {NETBOX_TOKEN}",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Accept": "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_all_prefixes():
|
||||||
|
"""Get all IPv4 prefixes from NetBox"""
|
||||||
|
url = f"{NETBOX_URL}/api/ipam/prefixes/"
|
||||||
|
params = {"family": 4, "limit": 1000}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.get(url, headers=headers, params=params, verify=True)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.json()["results"]
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
print(f"Error fetching prefixes: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def create_dhcp_ip(ip_address, prefix_id):
|
||||||
|
"""Create or update individual IP as DHCP reserved"""
|
||||||
|
url = f"{NETBOX_URL}/api/ipam/ip-addresses/"
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"address": f"{ip_address}/24", # CIDR notation
|
||||||
|
"status": "reserved",
|
||||||
|
"dns_name": "",
|
||||||
|
"description": "DHCP Pool - Reserved for dynamic assignment"
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.post(url, headers=headers, json=data, verify=True)
|
||||||
|
|
||||||
|
if response.status_code == 201:
|
||||||
|
return "created"
|
||||||
|
elif response.status_code == 400:
|
||||||
|
error_detail = response.json()
|
||||||
|
if "already exists" in str(error_detail).lower() or "duplicate" in str(error_detail).lower():
|
||||||
|
return "exists"
|
||||||
|
else:
|
||||||
|
return f"error: {error_detail}"
|
||||||
|
else:
|
||||||
|
return f"error: HTTP {response.status_code}"
|
||||||
|
except Exception as e:
|
||||||
|
return f"error: {e}"
|
||||||
|
|
||||||
|
def reserve_dhcp_range(prefix_str, prefix_id):
|
||||||
|
"""Reserve IPs .100-.150 in a prefix"""
|
||||||
|
try:
|
||||||
|
network = ipaddress.ip_network(prefix_str, strict=False)
|
||||||
|
|
||||||
|
# Calculate .100 and .150 addresses
|
||||||
|
base_ip = str(network.network_address)
|
||||||
|
octets = base_ip.split('.')
|
||||||
|
|
||||||
|
created = 0
|
||||||
|
exists = 0
|
||||||
|
errors = 0
|
||||||
|
|
||||||
|
# Create IPs from .100 to .150
|
||||||
|
for i in range(100, 151):
|
||||||
|
ip_octets = octets[:-1] + [str(i)]
|
||||||
|
ip_address = '.'.join(ip_octets)
|
||||||
|
|
||||||
|
# Validate IP is within network
|
||||||
|
ip_addr = ipaddress.ip_address(ip_address)
|
||||||
|
if ip_addr not in network:
|
||||||
|
continue
|
||||||
|
|
||||||
|
result = create_dhcp_ip(ip_address, prefix_id)
|
||||||
|
|
||||||
|
if result == "created":
|
||||||
|
created += 1
|
||||||
|
if created % 10 == 0: # Progress indicator
|
||||||
|
print(f" ... {created} IPs created so far")
|
||||||
|
elif result == "exists":
|
||||||
|
exists += 1
|
||||||
|
else:
|
||||||
|
errors += 1
|
||||||
|
if errors <= 3: # Show first 3 errors only
|
||||||
|
print(f" ✗ {ip_address}: {result}")
|
||||||
|
|
||||||
|
return created, exists, errors
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f" ✗ Error processing {prefix_str}: {e}")
|
||||||
|
return 0, 0, 0
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print(f"\nConnecting to NetBox: {NETBOX_URL}")
|
||||||
|
print("=" * 60)
|
||||||
|
print("This will create individual IP objects for .100-.150 in each subnet")
|
||||||
|
print("Status: Reserved | Description: DHCP Pool")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
# Get all prefixes
|
||||||
|
print("\nFetching prefixes...")
|
||||||
|
prefixes = get_all_prefixes()
|
||||||
|
|
||||||
|
# Filter to /24 subnets only (skip /16)
|
||||||
|
prefixes = [p for p in prefixes if '/24' in p['prefix']]
|
||||||
|
print(f"Found {len(prefixes)} /24 prefixes\n")
|
||||||
|
|
||||||
|
# Process each prefix
|
||||||
|
total_created = 0
|
||||||
|
total_exists = 0
|
||||||
|
total_errors = 0
|
||||||
|
|
||||||
|
for prefix in prefixes:
|
||||||
|
prefix_str = prefix["prefix"]
|
||||||
|
prefix_id = prefix["id"]
|
||||||
|
vlan_name = prefix.get("vlan", {}).get("name", "No VLAN") if prefix.get("vlan") else "No VLAN"
|
||||||
|
|
||||||
|
print(f"Processing: {prefix_str} ({vlan_name})")
|
||||||
|
created, exists, errors = reserve_dhcp_range(prefix_str, prefix_id)
|
||||||
|
|
||||||
|
print(f" ✓ Created: {created}, Already exists: {exists}, Errors: {errors}")
|
||||||
|
|
||||||
|
total_created += created
|
||||||
|
total_exists += exists
|
||||||
|
total_errors += errors
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print("Summary:")
|
||||||
|
print(f" ✓ Created: {total_created}")
|
||||||
|
print(f" ℹ️ Already existed: {total_exists}")
|
||||||
|
print(f" ✗ Errors: {total_errors}")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
print("\n✓ DHCP pool IPs reserved in NetBox!")
|
||||||
|
print(" IPs .100-.150 are now marked as 'Reserved' status")
|
||||||
|
print(" They will no longer show as 'Available' in the IP list")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -1,168 +1,190 @@
|
|||||||
# Homelab Agents Session Summary - sys-apps
|
# Homelab Agents Session Summary - sys-apps
|
||||||
**Date:** 2025-11-28
|
**Date:** 2025-11-30
|
||||||
**VPS:** sys-apps
|
**VPS:** sys-apps
|
||||||
**Repository:** homelab-agents (shared agent repository)
|
**Repository:** homelab-agents (shared agent repository)
|
||||||
**Session Type:** Documentation cleanup and naming convention updates
|
**Session Type:** NetBox DHCP pool configuration
|
||||||
|
|
||||||
## Session Overview
|
## Session Overview
|
||||||
|
|
||||||
This session focused on simplifying the homelab-agents repository documentation, establishing clear file naming conventions, and ensuring the finish-up agent works correctly across all VPS instances.
|
This session focused on configuring NetBox to properly mark DHCP pool IP ranges as reserved, preventing them from showing as "available" for static assignment. Created automation scripts to set up DHCP pool ranges across all VLANs.
|
||||||
|
|
||||||
## User Requests & Accomplishments
|
## Problem Statement
|
||||||
|
|
||||||
### 1. Agent Renaming Verification
|
User noticed that NetBox showed IPs in the DHCP pool range (.100-.150) as "available" for static assignment. This created confusion about which IPs could actually be assigned statically vs. which were reserved for DHCP dynamic assignment.
|
||||||
**Request:** User wanted to rename "Sysadmin Session Closer Agent" to "Finish Up Agent" with VPS hostname in filenames.
|
|
||||||
|
|
||||||
**Finding:** All changes were already completed in previous session (commit 1602733).
|
**Issue:** NetBox was showing 133 "available" IPs in VLAN 20, but most of those (.100-.150) were actually in the DHCP pool.
|
||||||
|
|
||||||
**Status:** ✓ Verified complete
|
## Solution Implemented
|
||||||
|
|
||||||
### 2. Workflow Documentation
|
### Two-Step Approach:
|
||||||
**Request:** Create simple workflow guide for using agents.
|
|
||||||
|
|
||||||
**Accomplished:**
|
1. **Create IP Ranges** - Logical grouping of DHCP pool
|
||||||
- Created `WORKFLOW.md` with streamlined instructions
|
2. **Reserve Individual IPs** - Mark each IP .100-.150 as "Reserved" status
|
||||||
- One-line VPS setup using curl bootstrap script
|
|
||||||
- Simple 3-step project workflow (cd → work → finish up)
|
|
||||||
- Key rules for where to run finish-up agent
|
|
||||||
- Quick reference table
|
|
||||||
|
|
||||||
### 3. Documentation Consolidation
|
## Scripts Created
|
||||||
**Request:** Remove duplicate documentation files.
|
|
||||||
|
|
||||||
**Accomplished:**
|
### 1. sys-apps-netbox-dhcp-setup.py
|
||||||
- Deleted `QUICK-START.md` (redundant with README + WORKFLOW)
|
**Purpose:** Create IP Range objects for DHCP pools
|
||||||
- Renamed `HOW-TO-GUIDE.md` → `GIT-REFERENCE.md` (clearer purpose)
|
|
||||||
- Simplified documentation structure:
|
|
||||||
- `README.md` = Overview and installation
|
|
||||||
- `WORKFLOW.md` = Simple workflow guide
|
|
||||||
- `GIT-REFERENCE.md` = Detailed git commands reference
|
|
||||||
- `VPS-SSH-KEY-SETUP.md` = SSH configuration
|
|
||||||
|
|
||||||
### 4. Session Summary Filename Simplification
|
**What it does:**
|
||||||
**Request:** Remove redundant project name from session summary filenames.
|
- Connects to NetBox via API
|
||||||
|
- Fetches all IPv4 prefixes
|
||||||
|
- Creates IP Range for .100-.150 in each subnet
|
||||||
|
- Status: Active
|
||||||
|
- Description: "DHCP Pool - Dynamic Assignment Range (.100-.150)"
|
||||||
|
|
||||||
**Accomplished:**
|
**Results:**
|
||||||
- Changed format from `[PROJECT_NAME]-[HOSTNAME]-session-summary.md`
|
- Created/verified 6 IP ranges
|
||||||
- To simplified: `[HOSTNAME]-session-summary.md`
|
- Ranges: 192.168.0.100-150, 192.168.2.100-150, 192.168.20.100-150, 192.168.30.100-150, 192.168.40.100-150, 192.168.50.100-150
|
||||||
- Rationale: File is saved inside project directory, so project name is redundant
|
|
||||||
- Updated in:
|
|
||||||
- `agents/finish-up.md` (Steps 1, 3, 7)
|
|
||||||
- `CLAUDE.md` (naming convention)
|
|
||||||
- `WORKFLOW.md` (agent behavior)
|
|
||||||
|
|
||||||
### 5. VPS Hostname Change
|
### 2. sys-apps-netbox-reserve-dhcp-ips.py
|
||||||
**Request:** Rename files for VPS hostname change from "108-system-apps" to "sys-apps".
|
**Purpose:** Create individual IP objects and mark as reserved
|
||||||
|
|
||||||
**Accomplished:**
|
**What it does:**
|
||||||
- Renamed `homelab-agents-108-system-apps-session-summary.md` → `sys-apps-session-summary.md`
|
- Connects to NetBox via API
|
||||||
- Removed old session summaries:
|
- Fetches all /24 prefixes (skips /16)
|
||||||
- `gitea-installation-session-summary.md`
|
- Creates IP object for each address .100-.150
|
||||||
- `gitea-domain-and-ssh-fixes-session-summary.md`
|
- Sets status to "Reserved"
|
||||||
|
- Description: "DHCP Pool - Reserved for dynamic assignment"
|
||||||
|
|
||||||
### 6. Git Basics Reference
|
**Results:**
|
||||||
**Request:** User asked for explanation of `git add -A` and `git commit -m` but said "i'll forget that".
|
- Created 255 IP objects across 5 VLANs
|
||||||
|
- 51 IPs per /24 subnet (.100 to .150 inclusive)
|
||||||
|
- All marked as "Reserved" status
|
||||||
|
|
||||||
**Accomplished:**
|
## VLANs Configured
|
||||||
- Created `sys-apps-git-quick-reference.md` with:
|
|
||||||
- Daily workflow commands
|
|
||||||
- Simple explanations of git add/commit/push
|
|
||||||
- Common patterns and examples
|
|
||||||
- When to use finish-up vs manual git
|
|
||||||
|
|
||||||
### 7. File Naming Convention
|
| VLAN | Subnet | DHCP Range | IPs Reserved |
|
||||||
**Request:** Ensure all new files have hostname prefix automatically.
|
|------|--------|------------|--------------|
|
||||||
|
| (No VLAN) | 192.168.2.0/24 | .100-.150 | 51 |
|
||||||
|
| Work Secure | 192.168.20.0/24 | .100-.150 | 51 |
|
||||||
|
| Family | 192.168.30.0/24 | .100-.150 | 51 |
|
||||||
|
| Guest | 192.168.40.0/24 | .100-.150 | 51 |
|
||||||
|
| IoT | 192.168.50.0/24 | .100-.150 | 51 |
|
||||||
|
| **Total** | | | **255** |
|
||||||
|
|
||||||
**Accomplished:**
|
## Technical Details
|
||||||
- Added file naming convention to `CLAUDE.md`:
|
|
||||||
- **Rule:** ALL new files must use `{hostname}-{description}.md` format
|
### NetBox API Integration
|
||||||
- **Exception:** Agent files in `agents/` directory (shared)
|
- **URL:** https://netbox.pdmarf.co.uk
|
||||||
- **Important:** Always run `hostname` command (never hardcode)
|
- **Authentication:** API Token (stored in /opt/docker/netbox/netbox_initial_populate.py)
|
||||||
- Generic rule works on any VPS without modification
|
- **Endpoints Used:**
|
||||||
- Future sessions will follow this automatically
|
- `/api/ipam/prefixes/` - List all network prefixes
|
||||||
|
- `/api/ipam/ip-ranges/` - Create IP range objects
|
||||||
|
- `/api/ipam/ip-addresses/` - Create individual IP objects
|
||||||
|
|
||||||
|
### Script Features
|
||||||
|
- Environment variable or command-line argument for API token
|
||||||
|
- Error handling for duplicate entries
|
||||||
|
- Progress indicators for long-running operations
|
||||||
|
- Automatic network validation (IPs must be within subnet)
|
||||||
|
- Skips non-/24 networks (like /16) for IP reservation
|
||||||
|
|
||||||
|
### Initial Issue: Tags Not Found
|
||||||
|
First run failed because script tried to create tag "dhcp-pool" which didn't exist:
|
||||||
|
```
|
||||||
|
{'tags': ["Related object not found using the provided attributes: {'name': 'dhcp-pool'}"]}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fix:** Removed tags from IP Range creation, used only description field.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
**Before:**
|
||||||
|
- NetBox showed IPs .100-.150 as "Available"
|
||||||
|
- User saw 133 available IPs in VLAN 20 subnet
|
||||||
|
|
||||||
|
**After:**
|
||||||
|
- IPs .100-.150 marked as "Reserved"
|
||||||
|
- Available count reduced significantly
|
||||||
|
- Only IPs outside DHCP pool show as available for static assignment
|
||||||
|
|
||||||
|
**View Results:**
|
||||||
|
- IP Ranges: https://netbox.pdmarf.co.uk/ipam/ip-ranges/
|
||||||
|
- VLAN 20 IPs: https://netbox.pdmarf.co.uk/ipam/prefixes/2/ip-addresses/
|
||||||
|
|
||||||
|
## Files Created
|
||||||
|
|
||||||
|
```
|
||||||
|
/home/pdm/.homelab-agents/sys-apps-netbox-dhcp-setup.py
|
||||||
|
/home/pdm/.homelab-agents/sys-apps-netbox-reserve-dhcp-ips.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**Both scripts:**
|
||||||
|
- Follow naming convention: `{hostname}-{description}.py`
|
||||||
|
- Are executable (`chmod +x`)
|
||||||
|
- Accept API token as argument or environment variable
|
||||||
|
- Include comprehensive error handling and progress output
|
||||||
|
|
||||||
|
## DHCP Pool Standard
|
||||||
|
|
||||||
|
**Established standard for all VLANs:**
|
||||||
|
- DHCP pool range: `.100-.150` (51 IPs)
|
||||||
|
- Static assignment ranges: `.1-.99` and `.151-.254`
|
||||||
|
- Gateway typically: `.1`
|
||||||
|
|
||||||
|
This provides:
|
||||||
|
- 99 static IPs before DHCP pool
|
||||||
|
- 51 DHCP dynamic IPs
|
||||||
|
- 104 static IPs after DHCP pool
|
||||||
|
- Total usable in /24: 254 IPs
|
||||||
|
|
||||||
|
## Usage for Future VLANs
|
||||||
|
|
||||||
|
When adding a new VLAN, run both scripts to configure DHCP pool:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Create IP Range
|
||||||
|
python3 /home/pdm/.homelab-agents/sys-apps-netbox-dhcp-setup.py 'YOUR_API_TOKEN'
|
||||||
|
|
||||||
|
# 2. Reserve Individual IPs
|
||||||
|
python3 /home/pdm/.homelab-agents/sys-apps-netbox-reserve-dhcp-ips.py 'YOUR_API_TOKEN'
|
||||||
|
```
|
||||||
|
|
||||||
|
Or set environment variable once:
|
||||||
|
```bash
|
||||||
|
export NETBOX_TOKEN='YOUR_API_TOKEN'
|
||||||
|
python3 /home/pdm/.homelab-agents/sys-apps-netbox-dhcp-setup.py
|
||||||
|
python3 /home/pdm/.homelab-agents/sys-apps-netbox-reserve-dhcp-ips.py
|
||||||
|
```
|
||||||
|
|
||||||
## Key Decisions
|
## Key Decisions
|
||||||
|
|
||||||
### Documentation Structure
|
### Why Two Scripts?
|
||||||
- Consolidated from 5+ docs to 4 focused files
|
1. **IP Ranges** - Logical grouping, visual representation in NetBox
|
||||||
- Each has clear, distinct purpose
|
2. **Individual IPs** - Actual reservation, prevents "available" status
|
||||||
- No duplication between files
|
|
||||||
|
|
||||||
### Filename Format
|
Both are needed because IP Ranges alone don't change IP availability status.
|
||||||
- Session summaries: `{hostname}-session-summary.md` (no timestamp, overwrites)
|
|
||||||
- All documentation: `{hostname}-{description}.md`
|
|
||||||
- Hostname detection: Always run `hostname` command dynamically
|
|
||||||
|
|
||||||
### Git Workflow
|
### Why Mark as "Reserved" Not "DHCP"?
|
||||||
- finish-up agent = comprehensive documentation + session closure
|
NetBox standard status values:
|
||||||
- Manual git = quick commits during work
|
- Active = In use
|
||||||
- Users can choose based on context
|
- Reserved = Allocated but not assigned
|
||||||
|
- **Reserved is correct** for DHCP pool (allocated to DHCP server, not assigned to specific device)
|
||||||
|
|
||||||
## Technical Changes
|
### Script Location
|
||||||
|
Placed in `~/.homelab-agents/` because:
|
||||||
### Files Modified:
|
- Infrastructure/automation tools
|
||||||
- `agents/finish-up.md` - Updated filename format (3 locations)
|
- Can be reused across VPS instances
|
||||||
- `CLAUDE.md` - Added file naming convention, updated session history
|
- Follows established pattern (agents, scripts, tools live here)
|
||||||
- `WORKFLOW.md` - Updated agent behavior description
|
- Prefixed with `sys-apps-` per naming convention
|
||||||
|
|
||||||
### Files Created:
|
|
||||||
- `WORKFLOW.md` - Simple workflow guide
|
|
||||||
- `sys-apps-git-quick-reference.md` - Git basics reference
|
|
||||||
|
|
||||||
### Files Deleted:
|
|
||||||
- `QUICK-START.md` - Redundant documentation
|
|
||||||
- `gitea-installation-session-summary.md` - Old session summary
|
|
||||||
- `gitea-domain-and-ssh-fixes-session-summary.md` - Old session summary
|
|
||||||
|
|
||||||
### Files Renamed:
|
|
||||||
- `HOW-TO-GUIDE.md` → `GIT-REFERENCE.md` - Clearer naming
|
|
||||||
- `homelab-agents-108-system-apps-session-summary.md` → `sys-apps-session-summary.md` - Hostname update
|
|
||||||
|
|
||||||
## Repository Status
|
|
||||||
|
|
||||||
- **Type:** Shared agent repository (homelab-agents)
|
|
||||||
- **Location:** http://100.120.125.113:3000/pdm/homelab-agents.git
|
|
||||||
- **Branch:** main
|
|
||||||
- **Status:** All changes committed and pushed
|
|
||||||
|
|
||||||
## Session Commits
|
|
||||||
|
|
||||||
1. `43770a7` - Add simple workflow guide for homelab agents
|
|
||||||
2. `201f6b8` - Consolidate documentation to remove duplication
|
|
||||||
3. `9673641` - Simplify session summary filename format
|
|
||||||
4. `9177b02` - Rename session summary to match VPS hostname change
|
|
||||||
5. `c44a697` - Remove outdated session summaries
|
|
||||||
6. `6b2e43a` - Add git quick reference for sys-apps VPS
|
|
||||||
7. `18003e5` - Add file naming convention to CLAUDE.md
|
|
||||||
8. `ea3ff43` - Make hostname rule generic for all VPS instances
|
|
||||||
|
|
||||||
## Insights & Learnings
|
|
||||||
|
|
||||||
### User Preferences
|
|
||||||
- Prefers simple, streamlined documentation over comprehensive guides
|
|
||||||
- Values automation (naming conventions, dynamic hostname)
|
|
||||||
- Wants quick reference materials (git commands)
|
|
||||||
- Appreciates clean, organized repository structure
|
|
||||||
|
|
||||||
### Repository Design
|
|
||||||
- `~/.homelab-agents` = Infrastructure/tooling (like `.ssh`, `.config`)
|
|
||||||
- Project repos = Actual work (`~/vps-system-apps`, etc.)
|
|
||||||
- Session summaries belong in project repos, not shared repo
|
|
||||||
- File naming with hostname enables multi-VPS tracking
|
|
||||||
|
|
||||||
## Next Session Recommendations
|
## Next Session Recommendations
|
||||||
|
|
||||||
1. Test the finish-up agent on a real project (not homelab-agents itself)
|
1. **Document NetBox token management** - Where tokens are stored, rotation policy
|
||||||
2. Deploy agents to additional VPS instances via bootstrap script
|
2. **Create tag for DHCP pools** - Add "dhcp-pool" tag in NetBox UI for easier filtering
|
||||||
3. Consider creating additional specialized agents for common tasks
|
3. **Consider automation** - Run scripts automatically when new VLAN is created
|
||||||
4. Document any VPS-specific configurations if needed
|
4. **Verify other subnets** - Check if any other networks need DHCP pool configuration
|
||||||
|
|
||||||
## Questions for Future Consideration
|
## Session Learning
|
||||||
|
|
||||||
- Should session summaries in homelab-agents go to `session-logs/` directory?
|
**NetBox IPAM Best Practices:**
|
||||||
- Are there other common tasks that warrant dedicated agents?
|
- IP Ranges = logical grouping
|
||||||
- Should we create a template for new project initialization?
|
- Individual IP objects = actual reservation
|
||||||
|
- Both needed for proper IPAM
|
||||||
|
- Status "Reserved" = correct for DHCP pools
|
||||||
|
- API automation prevents manual clicking for 255 IPs
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Session successfully documented and all changes pushed to Gitea.**
|
**Session successfully completed. NetBox now properly shows DHCP pool IPs as reserved.**
|
||||||
|
|||||||
Reference in New Issue
Block a user