build: 🚀 add release please
This commit is contained in:
@@ -0,0 +1,72 @@
|
|||||||
|
name: Release Please
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release-please:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
release_created: ${{ steps.release.outputs.release_created }}
|
||||||
|
steps:
|
||||||
|
- uses: googleapis/release-please-action@v4
|
||||||
|
id: release
|
||||||
|
with:
|
||||||
|
manifest-file: .release-please-manifest.json
|
||||||
|
config-file: .release-please-config.json
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
if: ${{ steps.release.outputs.release_created }}
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
if: ${{ steps.release.outputs.release_created }}
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Log in to GitHub Container Registry
|
||||||
|
if: ${{ steps.release.outputs.release_created }}
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Read version
|
||||||
|
if: ${{ steps.release.outputs.release_created }}
|
||||||
|
id: version
|
||||||
|
run: |
|
||||||
|
VERSION=$(cat VERSION)
|
||||||
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
if: ${{ steps.release.outputs.release_created }}
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
ghcr.io/jdb-net/ipam:${{ env.VERSION }}
|
||||||
|
ghcr.io/jdb-net/ipam:latest
|
||||||
|
build-args: |
|
||||||
|
VERSION=${{ env.VERSION }}
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
name: Deploy to Kubernetes
|
||||||
|
needs: release-please
|
||||||
|
if: ${{ needs.release-please.outputs.release_created }}
|
||||||
|
runs-on: [ k3s-lan-01 ]
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Apply manifests
|
||||||
|
run: |
|
||||||
|
sudo kubectl replace -f deployment.yml --grace-period=60 --force
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
stages:
|
|
||||||
- buildandpush
|
|
||||||
- deploy
|
|
||||||
|
|
||||||
variables:
|
|
||||||
IMAGE_NAME: "$HARBOR_REGISTRY/public/ipam"
|
|
||||||
|
|
||||||
buildandpush:
|
|
||||||
stage: buildandpush
|
|
||||||
image: docker:latest
|
|
||||||
tags:
|
|
||||||
- docker
|
|
||||||
script:
|
|
||||||
- echo "$HARBOR_PASSWORD" | docker login $HARBOR_REGISTRY -u "$HARBOR_USERNAME" --password-stdin
|
|
||||||
- docker build -t $IMAGE_NAME:$IMAGE_TAG .
|
|
||||||
- docker tag $IMAGE_NAME:$IMAGE_TAG $IMAGE_NAME:latest
|
|
||||||
- docker push $IMAGE_NAME:$IMAGE_TAG
|
|
||||||
- docker push $IMAGE_NAME:latest
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
stage: deploy
|
|
||||||
tags:
|
|
||||||
- k3s-lan-01
|
|
||||||
script:
|
|
||||||
- sudo kubectl replace -f deployment.yml --grace-period=60 --force
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"packages": {
|
||||||
|
".": {
|
||||||
|
"release-type": "simple",
|
||||||
|
"version-file": "VERSION"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json"
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
".": "1.0.0"
|
||||||
|
}
|
||||||
@@ -0,0 +1,226 @@
|
|||||||
|
<div align="center">
|
||||||
|
<img src="https://projects.jdbnet.co.uk/ipam/img/favicon.png" alt="IPAM" width="200" />
|
||||||
|
|
||||||
|
# IP Address Management
|
||||||
|
</div>
|
||||||
|
|
||||||
|
A Flask-based web application for comprehensive IP Address Management (IPAM). Manage subnets, IP addresses, devices, DHCP pools, and physical rack infrastructure with an intuitive web interface.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Subnet Management**: Create and manage IP subnets with CIDR notation (supports /24 to /32)
|
||||||
|
- **IP Address Tracking**: Automatic IP address generation and tracking for each subnet
|
||||||
|
- **Device Management**: Track devices with types (Server, VM, Switch, Firewall, WiFi AP, Printer, Other)
|
||||||
|
- **IP Assignment**: Assign IP addresses to devices with automatic hostname updates
|
||||||
|
- **DHCP Pool Configuration**: Configure DHCP pools with start/end IP ranges and excluded IPs
|
||||||
|
- **Rack Management**: Physical infrastructure tracking with U positions and front/back sides
|
||||||
|
- **Site Organization**: Organize subnets and devices by site/location
|
||||||
|
- **Audit Logging**: Complete audit trail of all changes with user, action, details, and timestamps
|
||||||
|
- **User Management**: Multi-user support with secure password authentication
|
||||||
|
- **CSV Export**: Export subnet and rack data to CSV files
|
||||||
|
- **Device Statistics**: View device counts by type
|
||||||
|
- **Web Interface**: Modern, responsive web GUI built with Tailwind CSS and dark mode support
|
||||||
|
|
||||||
|
## Quick Start with Docker
|
||||||
|
|
||||||
|
### Docker Run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d \
|
||||||
|
--name ipam \
|
||||||
|
-p 5000:5000 \
|
||||||
|
-e MYSQL_HOST=10.10.2.27 \
|
||||||
|
-e MYSQL_USER=ipam \
|
||||||
|
-e MYSQL_PASSWORD=your_password \
|
||||||
|
-e MYSQL_DATABASE=ipam \
|
||||||
|
-e SECRET_KEY=your_secret_key \
|
||||||
|
-e NAME="Your Organization" \
|
||||||
|
-e LOGO_PNG="https://example.com/logo.png" \
|
||||||
|
ghcr.io/jdb-net/ipam:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker Compose
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
ipam:
|
||||||
|
image: ghcr.io/jdb-net/ipam:latest
|
||||||
|
container_name: ipam
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "5000:5000" # Web interface
|
||||||
|
environment:
|
||||||
|
- MYSQL_HOST=10.10.2.27
|
||||||
|
- MYSQL_USER=ipam
|
||||||
|
- MYSQL_PASSWORD=your_password
|
||||||
|
- MYSQL_DATABASE=ipam
|
||||||
|
- SECRET_KEY=your_secret_key
|
||||||
|
- NAME=Your Organization
|
||||||
|
- LOGO_PNG=https://example.com/logo.png
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
- `MYSQL_HOST`: MySQL/MariaDB host (default: localhost)
|
||||||
|
- `MYSQL_USER`: Database user (default: user)
|
||||||
|
- `MYSQL_PASSWORD`: Database password (default: password)
|
||||||
|
- `MYSQL_DATABASE`: Database name (default: ipam)
|
||||||
|
- `SECRET_KEY`: Flask secret key for sessions (**REQUIRED in production!**)
|
||||||
|
- `NAME`: Organization name displayed in header (default: JDB-NET)
|
||||||
|
- `LOGO_PNG`: URL or path to organization logo (default: JDB-NET logo)
|
||||||
|
|
||||||
|
### Database Setup
|
||||||
|
|
||||||
|
The application automatically initializes the database schema on first run. Ensure the database and user exist with appropriate permissions:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE DATABASE ipam;
|
||||||
|
CREATE USER 'ipam'@'%' IDENTIFIED BY 'your_password';
|
||||||
|
GRANT ALL PRIVILEGES ON ipam.* TO 'ipam'@'%';
|
||||||
|
FLUSH PRIVILEGES;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### First Login
|
||||||
|
|
||||||
|
1. Access the web interface at `http://your-server:5000`
|
||||||
|
2. Log in with the default credentials:
|
||||||
|
- Email: `admin@example.com`
|
||||||
|
- Password: `password`
|
||||||
|
3. **Change the default password immediately** via the Users page
|
||||||
|
|
||||||
|
### Managing Subnets
|
||||||
|
|
||||||
|
1. Navigate to "Admin" from the main menu
|
||||||
|
2. Click "Add Subnet" and fill in:
|
||||||
|
- **Name**: Friendly name for the subnet (e.g., "Office LAN")
|
||||||
|
- **CIDR**: Subnet in CIDR notation (e.g., `192.168.1.0/24`)
|
||||||
|
- **Site**: Site/location identifier
|
||||||
|
3. The system automatically generates all IP addresses in the subnet
|
||||||
|
|
||||||
|
### Adding Devices
|
||||||
|
|
||||||
|
1. Navigate to "Devices" from the main menu
|
||||||
|
2. Click "Add Device"
|
||||||
|
3. Enter device name and select device type
|
||||||
|
4. Click "Create Device"
|
||||||
|
|
||||||
|
### Assigning IP Addresses to Devices
|
||||||
|
|
||||||
|
1. Open a device from the Devices page
|
||||||
|
2. Select a subnet and available IP address
|
||||||
|
3. Click "Assign IP" - the hostname is automatically updated
|
||||||
|
|
||||||
|
### Configuring DHCP Pools
|
||||||
|
|
||||||
|
1. Open a subnet view
|
||||||
|
2. Click "Configure DHCP Pool"
|
||||||
|
3. Set the start and end IP addresses
|
||||||
|
4. Optionally specify excluded IPs (comma-separated)
|
||||||
|
5. IPs within the pool range are automatically marked as "DHCP"
|
||||||
|
|
||||||
|
### Managing Racks
|
||||||
|
|
||||||
|
1. Navigate to "Racks" from the main menu
|
||||||
|
2. Click "Add Rack" and specify:
|
||||||
|
- **Name**: Rack identifier
|
||||||
|
- **Site**: Site location
|
||||||
|
- **Height**: Rack height in U units
|
||||||
|
3. Open a rack to assign devices to specific U positions (front or back)
|
||||||
|
|
||||||
|
### Audit Log
|
||||||
|
|
||||||
|
View all changes and actions in the "Audit Log" section, with filtering by user, subnet, action type, or device name.
|
||||||
|
|
||||||
|
### Exporting Data
|
||||||
|
|
||||||
|
- **Subnet CSV**: Click "Export CSV" on any subnet page to download IP addresses with hostnames
|
||||||
|
- **Rack CSV**: Click "Export CSV" on any rack page to download rack layout information
|
||||||
|
|
||||||
|
## Kubernetes Deployment
|
||||||
|
|
||||||
|
The project includes a Kubernetes deployment manifest. See `deployment.yml` for details.
|
||||||
|
|
||||||
|
**Example Kubernetes deployment:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: ipam
|
||||||
|
namespace: ipam
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: ipam
|
||||||
|
image: ghcr.io/jdb-net/ipam:latest
|
||||||
|
ports:
|
||||||
|
- containerPort: 5000
|
||||||
|
env:
|
||||||
|
- name: SECRET_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: ipam-secrets
|
||||||
|
key: secret-key
|
||||||
|
- name: MYSQL_HOST
|
||||||
|
value: "mysql-service"
|
||||||
|
- name: MYSQL_USER
|
||||||
|
value: "ipam"
|
||||||
|
- name: MYSQL_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: ipam-secrets
|
||||||
|
key: mysql-password
|
||||||
|
- name: MYSQL_DATABASE
|
||||||
|
value: "ipam"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Notes
|
||||||
|
|
||||||
|
- **CHANGE THE DEFAULT ADMIN PASSWORD** immediately after first login
|
||||||
|
- **CHANGE THE SECRET_KEY** in production - use a strong random string (e.g., `openssl rand -hex 32`)
|
||||||
|
- Use strong passwords for database access
|
||||||
|
- Ensure database connections are secured (consider SSL/TLS for MySQL connections)
|
||||||
|
- Review audit logs regularly for unauthorized changes
|
||||||
|
- Limit database user permissions if possible (though CREATE/ALTER may be needed for schema initialization)
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Database Connection Issues
|
||||||
|
|
||||||
|
- Ensure MySQL/MariaDB is running and accessible from the container
|
||||||
|
- Check database credentials in environment variables
|
||||||
|
- Verify database and user exist with proper permissions
|
||||||
|
- Check network connectivity between container and database
|
||||||
|
- Ensure the database name matches exactly (case-sensitive on some systems)
|
||||||
|
|
||||||
|
### Application Not Starting
|
||||||
|
|
||||||
|
- Check container logs: `docker logs ipam`
|
||||||
|
- Verify all required environment variables are set
|
||||||
|
- Ensure port 5000 is not already in use
|
||||||
|
- Check that MySQL/MariaDB is reachable
|
||||||
|
|
||||||
|
### Subnet or IP Not Appearing
|
||||||
|
|
||||||
|
- Verify CIDR notation is correct (supports /24 to /32)
|
||||||
|
- Check subnet was created successfully (view in Admin page)
|
||||||
|
- Ensure you're logged in with appropriate permissions
|
||||||
|
- Check application logs for errors
|
||||||
|
|
||||||
|
### Device IP Assignment Issues
|
||||||
|
|
||||||
|
- Verify the IP address is available (not already assigned)
|
||||||
|
- Check that the IP is not in a DHCP pool range
|
||||||
|
- Ensure the device exists and is visible in the Devices list
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is provided as-is for IP Address Management.
|
||||||
@@ -17,9 +17,20 @@ app.config['MYSQL_DATABASE'] = os.environ.get('MYSQL_DATABASE', 'ipam')
|
|||||||
|
|
||||||
@app.context_processor
|
@app.context_processor
|
||||||
def inject_env_vars():
|
def inject_env_vars():
|
||||||
|
# Read version from VERSION file
|
||||||
|
version = 'unknown'
|
||||||
|
try:
|
||||||
|
version_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'VERSION')
|
||||||
|
if os.path.exists(version_file):
|
||||||
|
with open(version_file, 'r') as f:
|
||||||
|
version = f.read().strip()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'NAME': os.environ.get('NAME', 'JDB-NET'),
|
'NAME': os.environ.get('NAME', 'JDB-NET'),
|
||||||
'LOGO_PNG': os.environ.get('LOGO_PNG', 'https://assets.s3.jdbnet.co.uk/logo/128x128.png')
|
'LOGO_PNG': os.environ.get('LOGO_PNG', 'https://assets.s3.jdbnet.co.uk/logo/128x128.png'),
|
||||||
|
'VERSION': version
|
||||||
}
|
}
|
||||||
|
|
||||||
register_routes(app)
|
register_routes(app)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<header class="bg-zinc-800 shadow-md py-3 px-6 flex items-center justify-between relative">
|
<header class="bg-zinc-800 shadow-md py-3 px-6 flex items-center justify-between relative">
|
||||||
<a href="/" class="flex items-center space-x-3">
|
<a href="/" class="flex items-center space-x-3">
|
||||||
<img src="{{ LOGO_PNG }}" alt="Logo" class="h-8 rounded">
|
<img src="{{ LOGO_PNG }}" alt="Logo" class="h-8 rounded">
|
||||||
<span class="text-2xl font-bold text-white">{{ NAME }} IPAM</span>
|
<span class="text-2xl font-bold text-white">{{ NAME }} IPAM <span class="text-sm font-normal text-gray-300">v{{ VERSION }}</span></span>
|
||||||
</a>
|
</a>
|
||||||
<div class="hidden lg:block absolute left-1/2 transform -translate-x-1/2 text-white text-lg font-medium whitespace-nowrap">
|
<div class="hidden lg:block absolute left-1/2 transform -translate-x-1/2 text-white text-lg font-medium whitespace-nowrap">
|
||||||
{% if current_user_name %}Hello, {{ current_user_name }}{% endif %}
|
{% if current_user_name %}Hello, {{ current_user_name }}{% endif %}
|
||||||
|
|||||||
Reference in New Issue
Block a user