Skip to content

Commit 177273a

Browse files
committed
(gha) add DHCP pool validation tool with CI/CD integration
1 parent de737d8 commit 177273a

File tree

6 files changed

+473
-0
lines changed

6 files changed

+473
-0
lines changed

.github/workflows/dhcp-check.yaml

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
---
2+
name: "DHCP Pool Validation"
3+
permissions:
4+
contents: "read"
5+
6+
"on":
7+
pull_request:
8+
paths:
9+
- "hieradata/node/**/*.yaml"
10+
- "hieradata/node/**/*.yml"
11+
- "utils/dhcp-check.py"
12+
push:
13+
branches:
14+
- "production"
15+
paths:
16+
- "hieradata/node/**/*.yaml"
17+
- "hieradata/node/**/*.yml"
18+
- "utils/dhcp-check.py"
19+
20+
jobs:
21+
validate-dhcp-pools:
22+
runs-on: "ubuntu-latest"
23+
name: "Validate DHCP Pool Configurations"
24+
25+
steps:
26+
- name: "Checkout Repository"
27+
uses: "actions/checkout@v4"
28+
29+
- name: "Set up Python"
30+
uses: "actions/setup-python@v4"
31+
with:
32+
python-version: "3.11"
33+
34+
- name: "Install Python Dependencies"
35+
run: |
36+
python -m pip install --upgrade pip
37+
if [ -f utils/requirements-dhcp.txt ]; then
38+
pip install -r utils/requirements-dhcp.txt
39+
else
40+
pip install PyYAML
41+
fi
42+
43+
- name: "Run DHCP Pool Validation"
44+
run: |
45+
python utils/dhcp-check.py
46+
env:
47+
PYTHONPATH: "${{ github.workspace }}"
48+
49+
- name: "Check for DHCP Pool Changes"
50+
if: "github.event_name == 'pull_request'"
51+
run: |
52+
echo "## DHCP Pool Validation Results" >> $GITHUB_STEP_SUMMARY
53+
echo "" >> $GITHUB_STEP_SUMMARY
54+
55+
# Get list of changed DHCP-related files
56+
CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}..HEAD | grep -E 'hieradata/node/.*\.ya?ml$' | head -10)
57+
58+
if [ -z "$CHANGED_FILES" ]; then
59+
echo "✅ No DHCP configuration files were modified in this PR." >> $GITHUB_STEP_SUMMARY
60+
else
61+
echo "📝 **Modified DHCP Configuration Files:**" >> $GITHUB_STEP_SUMMARY
62+
echo '```' >> $GITHUB_STEP_SUMMARY
63+
echo "$CHANGED_FILES" >> $GITHUB_STEP_SUMMARY
64+
echo '```' >> $GITHUB_STEP_SUMMARY
65+
echo "" >> $GITHUB_STEP_SUMMARY
66+
echo "All files have been validated and passed DHCP pool checks ✅" >> $GITHUB_STEP_SUMMARY
67+
fi
68+
69+
- name: "Validation Success"
70+
run: |
71+
echo "🎉 All DHCP pool configurations are valid!"
72+
echo "✅ Network configurations meet ISC DHCPD requirements"
73+
echo "✅ No subnet/mask conflicts detected"
74+
echo "✅ Gateway and range validations passed"

utils/README-dhcp-check.md

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
# DHCP Pool Checker
2+
3+
A validation tool for DHCP pools defined in Hiera YAML files (Foreman/Puppet configuration management). This tool prevents invalid subnet/mask configurations from causing failures in ISC DHCPD by validating network configurations prior to deployment.
4+
5+
## Overview
6+
7+
The `dhcp-check.py` script validates DHCP pool configurations in Puppet/Foreman Hiera YAML files to ensure:
8+
9+
- Valid network and mask combinations
10+
- Correct gateway configurations (when present)
11+
- Valid IP ranges within subnet boundaries
12+
- No conflicts with network or broadcast addresses
13+
14+
## Features
15+
16+
- **Network Validation**: Ensures each `dhcp::pools` entry contains valid network and mask combinations
17+
- **Gateway Validation**: Validates that gateway IP addresses are within the subnet and do not conflict with network or broadcast addresses
18+
- **Range Validation**: Verifies that all IP ranges are within subnet boundaries and properly ordered
19+
- **Configurable Warning System**: Provides configurable handling of missing gateway configurations
20+
- **YAML Anchor Resolution**: Automatically resolves YAML files with duplicate anchor issues
21+
- **CI/CD Integration**: Designed for automated validation within GitHub Actions workflows
22+
23+
## Installation & Usage
24+
25+
### Prerequisites
26+
27+
```bash
28+
pip install pyyaml
29+
```
30+
31+
### Basic Usage
32+
33+
```bash
34+
# Validate all DHCP pools in the default directory (hieradata/node)
35+
python utils/dhcp-check.py
36+
37+
# Specify custom directory
38+
python utils/dhcp-check.py --dir hieradata/node
39+
40+
# Require gateways (treat missing gateway as an error)
41+
python utils/dhcp-check.py --require-gateway
42+
43+
# Suppress gateway warnings
44+
python utils/dhcp-check.py --no-warn-missing-gateway
45+
46+
# Strict mode (treat all warnings as errors)
47+
python utils/dhcp-check.py --strict
48+
```
49+
50+
### Command Line Options
51+
52+
| Option | Description |
53+
|--------|-------------|
54+
| `--dir PATH` | Directory containing node YAML files (default: `hieradata/node`) |
55+
| `--require-gateway` | Treat missing gateway as an error (fail validation) |
56+
| `--no-warn-missing-gateway` | Suppress warnings when the gateway is missing |
57+
| `--strict` | Treat warnings as errors (fail on any warnings) |
58+
59+
## Exit Codes
60+
61+
- **0**: No errors found (warnings do not affect exit code unless `--strict` is specified)
62+
- **1**: Validation errors found (invalid subnet, invalid range, invalid gateway)
63+
- **2**: Usage or environment errors (missing directory, invalid arguments)
64+
65+
## Example Output
66+
67+
### Successful Validation
68+
69+
![Successful validation](image-ok.png)
70+
71+
```bash
72+
ℹ️ hieradata/node/example1.yaml: no dhcp::pools, skipping.
73+
✔️ hieradata/node/dhcp-server.yaml: 3 pool(s) validated OK.
74+
⚠️ hieradata/node/dhcp-server.yaml [guest_network]: gateway is missing (warning only)
75+
76+
✅ All DHCP pools are valid.
77+
```
78+
79+
### Validation Errors
80+
81+
![Validation errors](image-wrong.png)
82+
83+
```bash
84+
❌ Errors:
85+
- hieradata/node/bad-server.yaml [main_pool]: network 192.168.1.5 is not the subnet base (should be 192.168.1.0/24)
86+
- hieradata/node/bad-server.yaml [guest_pool]: range 192.168.2.1-192.168.2.300 not inside 192.168.2.0/24
87+
- hieradata/node/bad-server.yaml [admin_pool]: gateway 10.0.0.255 is network/broadcast address
88+
```
89+
90+
## YAML Configuration Format
91+
92+
The tool validates `dhcp::pools` sections in your Hiera YAML files:
93+
94+
```yaml
95+
dhcp::pools:
96+
main_network:
97+
network: "192.168.1.0"
98+
mask: "255.255.255.0"
99+
gateway: "192.168.1.1"
100+
range:
101+
- "192.168.1.10 192.168.1.100"
102+
- "192.168.1.150 192.168.1.200"
103+
104+
guest_network:
105+
network: "10.0.0.0"
106+
mask: "255.255.255.0"
107+
# gateway is optional
108+
range:
109+
- "10.0.0.50 10.0.0.150"
110+
```
111+
112+
## Validation Rules
113+
114+
### Network & Mask
115+
116+
- Network address must be a valid IPv4 address
117+
- Mask must be a valid subnet mask in dotted decimal notation
118+
- Network must be the subnet base address (not a host address)
119+
120+
### Gateway (Optional)
121+
122+
- If present, must be a valid IPv4 address
123+
- Must be within the subnet range
124+
- Cannot be a network or broadcast address
125+
- If missing, generates a warning (unless `--no-warn-missing-gateway` is specified)
126+
127+
### Ranges
128+
129+
- Must be valid IPv4 addresses
130+
- Start address must be ≤ end address
131+
- Both addresses must be within the subnet
132+
- Cannot include network or broadcast addresses
133+
134+
## Troubleshooting
135+
136+
### YAML Anchor Issues
137+
138+
The tool automatically handles YAML files with duplicate anchor issues by:
139+
140+
1. Detecting duplicate anchor errors
141+
1. Stripping anchor definitions and references
142+
1. Re-parsing the cleaned YAML for validation
143+
144+
### Common Errors
145+
146+
- **"network X is not the subnet base"**: Use the actual network address (e.g., 192.168.1.0, not 192.168.1.5)
147+
- **"range X-Y not inside subnet"**: Ensure all IP ranges fall within the network/mask boundaries
148+
- **"gateway is network/broadcast address"**: Select a gateway IP address that is not the network or broadcast address (e.g., not .0 or .255 for /24 networks)
149+
150+
## Integration
151+
152+
### GitHub Actions
153+
154+
See `.github/workflows/dhcp-check.yaml` for automated validation on pull requests.
155+
156+
### Pre-commit Hooks
157+
158+
Add to your `.pre-commit-config.yaml`:
159+
160+
```yaml
161+
repos:
162+
- repo: local
163+
hooks:
164+
- id: dhcp-check
165+
name: DHCP Pool Validation
166+
entry: python utils/dhcp-check.py
167+
language: system
168+
pass_filenames: false
169+
```
170+
171+
## Contributing
172+
173+
When modifying DHCP configurations, follow these steps:
174+
175+
1. Run `python utils/dhcp-check.py` locally before committing
176+
1. Address any validation errors or warnings
177+
1. The GitHub workflow will automatically validate changes on PR
178+
179+
## License
180+
181+
This tool is part of the LSST control system and follows the same licensing terms.

0 commit comments

Comments
 (0)