wip
This commit is contained in:
parent
6dd38036e7
commit
097b3dbf09
34 changed files with 1719 additions and 520 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,8 +1,10 @@
|
|||
from flask import Blueprint, jsonify, request, abort
|
||||
from flask_login import login_required
|
||||
from app.core.models import Subnet, Server, App
|
||||
from app.core.models import Subnet, Server, App, Port
|
||||
from app.core.extensions import db
|
||||
from app.scripts.ip_scanner import scan
|
||||
import random
|
||||
import ipaddress
|
||||
|
||||
bp = Blueprint('api', __name__, url_prefix='/api')
|
||||
|
||||
|
@ -93,14 +95,24 @@ def get_servers():
|
|||
@bp.route('/servers/<int:server_id>', methods=['GET'])
|
||||
@login_required
|
||||
def get_server(server_id):
|
||||
"""Get details for a specific server"""
|
||||
"""Get a specific server"""
|
||||
server = Server.query.get_or_404(server_id)
|
||||
|
||||
apps = []
|
||||
for app in App.query.filter_by(server_id=server_id).all():
|
||||
for app in server.apps:
|
||||
ports = []
|
||||
for port in app.ports:
|
||||
ports.append({
|
||||
'id': port.id,
|
||||
'port_number': port.port_number,
|
||||
'protocol': port.protocol,
|
||||
'description': port.description
|
||||
})
|
||||
|
||||
apps.append({
|
||||
'id': app.id,
|
||||
'name': app.name,
|
||||
'ports': ports,
|
||||
'created_at': app.created_at.strftime('%Y-%m-%d %H:%M:%S')
|
||||
})
|
||||
|
||||
|
@ -110,9 +122,8 @@ def get_server(server_id):
|
|||
'ip_address': server.ip_address,
|
||||
'subnet_id': server.subnet_id,
|
||||
'documentation': server.documentation,
|
||||
'created_at': server.created_at.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'ports': server.ports,
|
||||
'apps': apps
|
||||
'apps': apps,
|
||||
'created_at': server.created_at.strftime('%Y-%m-%d %H:%M:%S')
|
||||
}
|
||||
|
||||
return jsonify(result)
|
||||
|
@ -196,4 +207,103 @@ def suggest_ports():
|
|||
return jsonify([
|
||||
{'port': 80, 'type': 'tcp', 'desc': 'HTTP'},
|
||||
{'port': 22, 'type': 'tcp', 'desc': 'SSH'}
|
||||
])
|
||||
])
|
||||
|
||||
@bp.route('/servers/<int:server_id>/suggest_port', methods=['GET'])
|
||||
@login_required
|
||||
def suggest_port(server_id):
|
||||
"""Suggest a random unused port for a server"""
|
||||
server = Server.query.get_or_404(server_id)
|
||||
|
||||
# Get all used ports for this server
|
||||
used_ports = []
|
||||
for app in server.apps:
|
||||
for port in app.ports:
|
||||
used_ports.append(port.port_number)
|
||||
|
||||
# Find an unused port in the dynamic/private port range
|
||||
available_port = None
|
||||
attempts = 0
|
||||
|
||||
while attempts < 50: # Try 50 times to find a random port
|
||||
# Random port between 10000 and 65535
|
||||
port = random.randint(10000, 65535)
|
||||
|
||||
if port not in used_ports:
|
||||
available_port = port
|
||||
break
|
||||
|
||||
attempts += 1
|
||||
|
||||
if available_port is None:
|
||||
# If no random port found, find first available in sequence
|
||||
for port in range(10000, 65536):
|
||||
if port not in used_ports:
|
||||
available_port = port
|
||||
break
|
||||
|
||||
return jsonify({'port': available_port})
|
||||
|
||||
@bp.route('/apps/<int:app_id>/ports', methods=['GET'])
|
||||
@login_required
|
||||
def get_app_ports(app_id):
|
||||
"""Get all ports for an app"""
|
||||
app = App.query.get_or_404(app_id)
|
||||
|
||||
ports = []
|
||||
for port in app.ports:
|
||||
ports.append({
|
||||
'id': port.id,
|
||||
'port_number': port.port_number,
|
||||
'protocol': port.protocol,
|
||||
'description': port.description
|
||||
})
|
||||
|
||||
return jsonify({'ports': ports})
|
||||
|
||||
@bp.route('/apps/<int:app_id>/ports', methods=['POST'])
|
||||
@login_required
|
||||
def add_app_port(app_id):
|
||||
"""Add a new port to an app"""
|
||||
app = App.query.get_or_404(app_id)
|
||||
|
||||
data = request.json
|
||||
if not data or 'port_number' not in data:
|
||||
return jsonify({'error': 'Missing port number'}), 400
|
||||
|
||||
port_number = data.get('port_number')
|
||||
protocol = data.get('protocol', 'TCP')
|
||||
description = data.get('description', '')
|
||||
|
||||
# Check if port already exists for this app
|
||||
existing_port = Port.query.filter_by(app_id=app_id, port_number=port_number).first()
|
||||
if existing_port:
|
||||
return jsonify({'error': 'Port already exists for this app'}), 400
|
||||
|
||||
new_port = Port(
|
||||
app_id=app_id,
|
||||
port_number=port_number,
|
||||
protocol=protocol,
|
||||
description=description
|
||||
)
|
||||
|
||||
db.session.add(new_port)
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({
|
||||
'id': new_port.id,
|
||||
'port_number': new_port.port_number,
|
||||
'protocol': new_port.protocol,
|
||||
'description': new_port.description
|
||||
})
|
||||
|
||||
@bp.route('/ports/<int:port_id>', methods=['DELETE'])
|
||||
@login_required
|
||||
def delete_port(port_id):
|
||||
"""Delete a port"""
|
||||
port = Port.query.get_or_404(port_id)
|
||||
|
||||
db.session.delete(port)
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({'success': True})
|
|
@ -8,7 +8,7 @@ bp = Blueprint('auth', __name__, url_prefix='/auth')
|
|||
|
||||
@bp.route('/login', methods=['GET', 'POST'])
|
||||
def login():
|
||||
# If already logged in, redirect to dashboard
|
||||
"""User login"""
|
||||
if current_user.is_authenticated:
|
||||
return redirect(url_for('dashboard.dashboard_home'))
|
||||
|
||||
|
@ -19,58 +19,64 @@ def login():
|
|||
|
||||
user = User.query.filter_by(email=email).first()
|
||||
|
||||
if user and user.check_password(password):
|
||||
login_user(user, remember=remember)
|
||||
next_page = request.args.get('next')
|
||||
if next_page:
|
||||
return redirect(next_page)
|
||||
return redirect(url_for('dashboard.dashboard_home'))
|
||||
if not user or not user.check_password(password):
|
||||
flash('Invalid email or password', 'danger')
|
||||
return render_template('auth/login.html', title='Login')
|
||||
|
||||
flash('Invalid email or password', 'danger')
|
||||
login_user(user, remember=remember)
|
||||
|
||||
next_page = request.args.get('next')
|
||||
if not next_page or not next_page.startswith('/'):
|
||||
next_page = url_for('dashboard.dashboard_home')
|
||||
|
||||
return redirect(next_page)
|
||||
|
||||
return render_template('auth/login.html', title='Login')
|
||||
|
||||
@bp.route('/register', methods=['GET', 'POST'])
|
||||
def register():
|
||||
# If already logged in, redirect to dashboard
|
||||
"""User registration"""
|
||||
if current_user.is_authenticated:
|
||||
return redirect(url_for('dashboard.dashboard_home'))
|
||||
|
||||
if request.method == 'POST':
|
||||
email = request.form.get('email')
|
||||
username = request.form.get('username')
|
||||
password = request.form.get('password')
|
||||
password_confirm = request.form.get('password_confirm')
|
||||
|
||||
# Check if email already exists
|
||||
existing_user = User.query.filter_by(email=email).first()
|
||||
if existing_user:
|
||||
flash('Email already registered', 'danger')
|
||||
# Validation
|
||||
if not email or not username or not password:
|
||||
flash('All fields are required', 'danger')
|
||||
return render_template('auth/register.html', title='Register')
|
||||
|
||||
# Check if passwords match
|
||||
if password != password_confirm:
|
||||
flash('Passwords do not match', 'danger')
|
||||
if User.query.filter_by(email=email).first():
|
||||
flash('Email already registered', 'danger')
|
||||
return render_template('auth/register.html', title='Register')
|
||||
|
||||
if User.query.filter_by(username=username).first():
|
||||
flash('Username already taken', 'danger')
|
||||
return render_template('auth/register.html', title='Register')
|
||||
|
||||
# Create new user
|
||||
user = User(email=email)
|
||||
user = User(email=email, username=username)
|
||||
user.set_password(password)
|
||||
|
||||
# Make first user an admin
|
||||
if User.query.count() == 0:
|
||||
user.is_admin = True
|
||||
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
|
||||
flash('Registration successful! Please log in.', 'success')
|
||||
return redirect(url_for('auth.login'))
|
||||
flash('Registration successful! You are now logged in.', 'success')
|
||||
|
||||
# Auto-login after registration
|
||||
login_user(user)
|
||||
|
||||
return redirect(url_for('dashboard.dashboard_home'))
|
||||
|
||||
return render_template('auth/register.html', title='Register')
|
||||
|
||||
@bp.route('/logout')
|
||||
@login_required
|
||||
def logout():
|
||||
"""User logout"""
|
||||
logout_user()
|
||||
flash('You have been logged out', 'info')
|
||||
return redirect(url_for('auth.login'))
|
|
@ -1,7 +1,7 @@
|
|||
from flask import Blueprint, render_template, redirect, url_for, request, flash, jsonify
|
||||
from flask_login import login_required, current_user
|
||||
import markdown
|
||||
from app.core.models import Server, App, Subnet
|
||||
from app.core.models import Server, App, Subnet, Port
|
||||
from app.core.extensions import db, limiter
|
||||
from datetime import datetime
|
||||
|
||||
|
@ -196,22 +196,50 @@ def app_new():
|
|||
server_id = request.form.get('server_id')
|
||||
documentation = request.form.get('documentation', '')
|
||||
|
||||
# Get port data from form
|
||||
port_numbers = request.form.getlist('port_numbers[]')
|
||||
protocols = request.form.getlist('protocols[]')
|
||||
port_descriptions = request.form.getlist('port_descriptions[]')
|
||||
|
||||
# Basic validation
|
||||
if not name or not server_id:
|
||||
flash('Please fill in all required fields', 'danger')
|
||||
return render_template(
|
||||
'dashboard/app_form.html',
|
||||
title='New Application',
|
||||
servers=servers,
|
||||
now=datetime.now()
|
||||
servers=servers
|
||||
)
|
||||
|
||||
# Create new app
|
||||
app = App(
|
||||
name=name,
|
||||
server_id=server_id,
|
||||
documentation=documentation
|
||||
)
|
||||
|
||||
db.session.add(app)
|
||||
db.session.flush() # Get the app ID without committing
|
||||
|
||||
# Add ports if provided
|
||||
for i in range(len(port_numbers)):
|
||||
if port_numbers[i] and port_numbers[i].strip():
|
||||
try:
|
||||
port_num = int(port_numbers[i])
|
||||
|
||||
# Get protocol and description, handling index errors
|
||||
protocol = protocols[i] if i < len(protocols) else 'TCP'
|
||||
description = port_descriptions[i] if i < len(port_descriptions) else ''
|
||||
|
||||
new_port = Port(
|
||||
app_id=app.id,
|
||||
port_number=port_num,
|
||||
protocol=protocol,
|
||||
description=description
|
||||
)
|
||||
db.session.add(new_port)
|
||||
except (ValueError, IndexError):
|
||||
continue
|
||||
|
||||
db.session.commit()
|
||||
|
||||
flash('Application created successfully', 'success')
|
||||
|
@ -220,8 +248,7 @@ def app_new():
|
|||
return render_template(
|
||||
'dashboard/app_form.html',
|
||||
title='New Application',
|
||||
servers=servers,
|
||||
now=datetime.now()
|
||||
servers=servers
|
||||
)
|
||||
|
||||
@bp.route('/app/<int:app_id>', methods=['GET'])
|
||||
|
@ -245,49 +272,69 @@ def app_view(app_id):
|
|||
def app_edit(app_id):
|
||||
"""Edit an existing application"""
|
||||
app = App.query.get_or_404(app_id)
|
||||
servers = Server.query.all()
|
||||
|
||||
if request.method == 'POST':
|
||||
name = request.form.get('name')
|
||||
server_id = request.form.get('server_id')
|
||||
documentation = request.form.get('documentation', '')
|
||||
|
||||
# Process ports
|
||||
ports = []
|
||||
port_numbers = request.form.getlist('port[]')
|
||||
port_types = request.form.getlist('port_type[]')
|
||||
port_descs = request.form.getlist('port_desc[]')
|
||||
# Get port data from form
|
||||
port_numbers = request.form.getlist('port_numbers[]')
|
||||
protocols = request.form.getlist('protocols[]')
|
||||
port_descriptions = request.form.getlist('port_descriptions[]')
|
||||
|
||||
for i in range(len(port_numbers)):
|
||||
if port_numbers[i]:
|
||||
port = {
|
||||
'port': int(port_numbers[i]),
|
||||
'type': port_types[i] if i < len(port_types) else 'tcp',
|
||||
'desc': port_descs[i] if i < len(port_descs) else '',
|
||||
'status': 'open'
|
||||
}
|
||||
ports.append(port)
|
||||
# Validate inputs
|
||||
if not all([name, server_id]):
|
||||
flash('All fields are required', 'danger')
|
||||
return render_template('dashboard/app_form.html',
|
||||
title='Edit Application',
|
||||
app=app,
|
||||
servers=servers,
|
||||
edit_mode=True)
|
||||
|
||||
# Update app
|
||||
app.name = name
|
||||
app.server_id = server_id
|
||||
app.documentation = documentation
|
||||
app.ports = ports
|
||||
|
||||
db.session.commit()
|
||||
# Delete existing ports and recreate them
|
||||
# This simplifies handling additions, deletions, and updates
|
||||
Port.query.filter_by(app_id=app.id).delete()
|
||||
|
||||
flash('Application updated successfully', 'success')
|
||||
return redirect(url_for('dashboard.app_view', app_id=app.id))
|
||||
# Add new ports
|
||||
for i in range(len(port_numbers)):
|
||||
if port_numbers[i] and port_numbers[i].strip():
|
||||
try:
|
||||
port_num = int(port_numbers[i])
|
||||
|
||||
# Get protocol and description, handling index errors
|
||||
protocol = protocols[i] if i < len(protocols) else 'TCP'
|
||||
description = port_descriptions[i] if i < len(port_descriptions) else ''
|
||||
|
||||
new_port = Port(
|
||||
app_id=app.id,
|
||||
port_number=port_num,
|
||||
protocol=protocol,
|
||||
description=description
|
||||
)
|
||||
db.session.add(new_port)
|
||||
except (ValueError, IndexError):
|
||||
continue
|
||||
|
||||
try:
|
||||
db.session.commit()
|
||||
flash(f'Application {name} has been updated', 'success')
|
||||
return redirect(url_for('dashboard.server_view', server_id=app.server_id))
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
flash(f'Error updating application: {str(e)}', 'danger')
|
||||
|
||||
# GET request - show form with current values
|
||||
servers = Server.query.all()
|
||||
|
||||
return render_template(
|
||||
'dashboard/app_edit.html',
|
||||
title=f'Edit Application - {app.name}',
|
||||
app=app,
|
||||
servers=servers,
|
||||
use_editor=True
|
||||
)
|
||||
return render_template('dashboard/app_form.html',
|
||||
title='Edit Application',
|
||||
app=app,
|
||||
servers=servers,
|
||||
edit_mode=True)
|
||||
|
||||
@bp.route('/app/<int:app_id>/delete', methods=['POST'])
|
||||
@login_required
|
||||
|
|
|
@ -5,6 +5,7 @@ from app.core.extensions import db
|
|||
from app.scripts.ip_scanner import scan
|
||||
import ipaddress
|
||||
from datetime import datetime
|
||||
import json
|
||||
|
||||
bp = Blueprint('ipam', __name__, url_prefix='/ipam')
|
||||
|
||||
|
@ -16,7 +17,10 @@ def ipam_home():
|
|||
|
||||
# Calculate usage for each subnet
|
||||
for subnet in subnets:
|
||||
subnet.usage_percent = subnet.used_ips / 254 * 100 if subnet.cidr.endswith('/24') else 0
|
||||
network = ipaddress.ip_network(subnet.cidr, strict=False)
|
||||
max_hosts = network.num_addresses - 2 if network.prefixlen < 31 else network.num_addresses
|
||||
used_count = Server.query.filter_by(subnet_id=subnet.id).count()
|
||||
subnet.usage_percent = (used_count / max_hosts) * 100 if max_hosts > 0 else 0
|
||||
|
||||
return render_template(
|
||||
'ipam/index.html',
|
||||
|
@ -32,42 +36,43 @@ def subnet_new():
|
|||
if request.method == 'POST':
|
||||
cidr = request.form.get('cidr')
|
||||
location = request.form.get('location')
|
||||
auto_scan = 'auto_scan' in request.form
|
||||
auto_scan = request.form.get('auto_scan') == 'on'
|
||||
|
||||
# Basic validation
|
||||
if not cidr or not location:
|
||||
flash('Please fill in all required fields', 'danger')
|
||||
return render_template(
|
||||
'ipam/subnet_form.html',
|
||||
title='New Subnet',
|
||||
now=datetime.now()
|
||||
title='New Subnet'
|
||||
)
|
||||
|
||||
# Check if valid CIDR
|
||||
# Validate CIDR format
|
||||
try:
|
||||
ipaddress.ip_network(cidr)
|
||||
ipaddress.ip_network(cidr, strict=False)
|
||||
except ValueError:
|
||||
flash('Invalid CIDR notation', 'danger')
|
||||
flash('Invalid CIDR format', 'danger')
|
||||
return render_template(
|
||||
'ipam/subnet_form.html',
|
||||
title='New Subnet',
|
||||
now=datetime.now()
|
||||
title='New Subnet'
|
||||
)
|
||||
|
||||
# Check if subnet already exists
|
||||
# Check if CIDR already exists
|
||||
if Subnet.query.filter_by(cidr=cidr).first():
|
||||
flash('Subnet already exists', 'danger')
|
||||
return render_template(
|
||||
'ipam/subnet_form.html',
|
||||
title='New Subnet',
|
||||
now=datetime.now()
|
||||
title='New Subnet'
|
||||
)
|
||||
|
||||
# Create new subnet with JSON string for active_hosts, not a Python list
|
||||
subnet = Subnet(
|
||||
cidr=cidr,
|
||||
location=location,
|
||||
active_hosts=json.dumps([]), # Convert empty list to JSON string
|
||||
last_scanned=None,
|
||||
auto_scan=auto_scan
|
||||
)
|
||||
|
||||
db.session.add(subnet)
|
||||
db.session.commit()
|
||||
|
||||
|
@ -76,70 +81,115 @@ def subnet_new():
|
|||
|
||||
return render_template(
|
||||
'ipam/subnet_form.html',
|
||||
title='New Subnet',
|
||||
now=datetime.now()
|
||||
title='New Subnet'
|
||||
)
|
||||
|
||||
@bp.route('/subnet/<int:subnet_id>')
|
||||
@login_required
|
||||
def subnet_view(subnet_id):
|
||||
"""View subnet details"""
|
||||
"""View a specific subnet"""
|
||||
subnet = Subnet.query.get_or_404(subnet_id)
|
||||
|
||||
# Get all servers in this subnet
|
||||
servers = Server.query.filter_by(subnet_id=subnet_id).all()
|
||||
|
||||
# Get network info
|
||||
network = ipaddress.ip_network(subnet.cidr)
|
||||
total_ips = network.num_addresses - 2 # Excluding network and broadcast addresses
|
||||
used_ips = len(servers)
|
||||
usage_percent = (used_ips / total_ips) * 100 if total_ips > 0 else 0
|
||||
# Parse CIDR for display
|
||||
network = ipaddress.ip_network(subnet.cidr, strict=False)
|
||||
subnet_info = {
|
||||
'network_address': str(network.network_address),
|
||||
'broadcast_address': str(network.broadcast_address),
|
||||
'netmask': str(network.netmask),
|
||||
'num_addresses': network.num_addresses,
|
||||
'host_range': f"{str(network.network_address + 1)} - {str(network.broadcast_address - 1)}" if network.prefixlen < 31 else subnet.cidr
|
||||
}
|
||||
|
||||
return render_template(
|
||||
'ipam/subnet_view.html',
|
||||
title=f'Subnet - {subnet.cidr}',
|
||||
title=subnet.cidr,
|
||||
subnet=subnet,
|
||||
subnet_info=subnet_info,
|
||||
servers=servers,
|
||||
total_ips=total_ips,
|
||||
used_ips=used_ips,
|
||||
usage_percent=usage_percent,
|
||||
now=datetime.now()
|
||||
)
|
||||
|
||||
@bp.route('/subnet/<int:subnet_id>/scan')
|
||||
@bp.route('/subnet/<int:subnet_id>/edit', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def subnet_edit(subnet_id):
|
||||
"""Edit a subnet"""
|
||||
subnet = Subnet.query.get_or_404(subnet_id)
|
||||
|
||||
if request.method == 'POST':
|
||||
cidr = request.form.get('cidr')
|
||||
location = request.form.get('location')
|
||||
auto_scan = request.form.get('auto_scan') == 'on'
|
||||
|
||||
# Validate inputs
|
||||
if not all([cidr, location]):
|
||||
flash('All fields are required', 'danger')
|
||||
return render_template('ipam/subnet_form.html',
|
||||
title='Edit Subnet',
|
||||
subnet=subnet,
|
||||
edit_mode=True)
|
||||
|
||||
# Validate CIDR format
|
||||
try:
|
||||
ipaddress.ip_network(cidr, strict=False)
|
||||
except ValueError:
|
||||
flash('Invalid CIDR format', 'danger')
|
||||
return render_template('ipam/subnet_form.html',
|
||||
title='Edit Subnet',
|
||||
subnet=subnet,
|
||||
edit_mode=True)
|
||||
|
||||
# Update subnet
|
||||
subnet.cidr = cidr
|
||||
subnet.location = location
|
||||
subnet.auto_scan = auto_scan
|
||||
|
||||
try:
|
||||
db.session.commit()
|
||||
flash(f'Subnet {cidr} has been updated', 'success')
|
||||
return redirect(url_for('ipam.subnet_view', subnet_id=subnet.id))
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
flash(f'Error updating subnet: {str(e)}', 'danger')
|
||||
|
||||
return render_template('ipam/subnet_form.html',
|
||||
title='Edit Subnet',
|
||||
subnet=subnet,
|
||||
edit_mode=True)
|
||||
|
||||
@bp.route('/subnet/<int:subnet_id>/delete', methods=['POST'])
|
||||
@login_required
|
||||
def subnet_delete(subnet_id):
|
||||
"""Delete a subnet"""
|
||||
subnet = Subnet.query.get_or_404(subnet_id)
|
||||
|
||||
# Check if subnet has servers
|
||||
servers_count = Server.query.filter_by(subnet_id=subnet_id).count()
|
||||
if servers_count > 0:
|
||||
flash(f'Cannot delete subnet {subnet.cidr}. It has {servers_count} servers assigned.', 'danger')
|
||||
return redirect(url_for('ipam.subnet_view', subnet_id=subnet_id))
|
||||
|
||||
db.session.delete(subnet)
|
||||
db.session.commit()
|
||||
|
||||
flash(f'Subnet {subnet.cidr} has been deleted', 'success')
|
||||
return redirect(url_for('ipam.ipam_home'))
|
||||
|
||||
@bp.route('/subnet/<int:subnet_id>/scan', methods=['POST'])
|
||||
@login_required
|
||||
def subnet_scan(subnet_id):
|
||||
"""Scan a subnet for active hosts"""
|
||||
"""Manually scan a subnet"""
|
||||
subnet = Subnet.query.get_or_404(subnet_id)
|
||||
|
||||
try:
|
||||
results = scan(subnet.cidr, save_results=True)
|
||||
flash(f'Scan completed for subnet {subnet.cidr}. Found {len(results)} active hosts.', 'success')
|
||||
# Call the scan function with manual_trigger=True
|
||||
scan(subnet, manual_trigger=True)
|
||||
db.session.commit()
|
||||
flash(f'Scan completed for subnet {subnet.cidr}', 'success')
|
||||
except Exception as e:
|
||||
flash(f'Error scanning subnet: {e}', 'danger')
|
||||
db.session.rollback()
|
||||
flash(f'Error scanning subnet: {str(e)}', 'danger')
|
||||
|
||||
return redirect(url_for('ipam.subnet_view', subnet_id=subnet_id))
|
||||
|
||||
@bp.route('/subnet/<int:subnet_id>/visualize')
|
||||
@login_required
|
||||
def subnet_visualize(subnet_id):
|
||||
"""Visualize IP usage in a subnet"""
|
||||
subnet = Subnet.query.get_or_404(subnet_id)
|
||||
servers = Server.query.filter_by(subnet_id=subnet_id).all()
|
||||
|
||||
# Create a dictionary of used IPs
|
||||
used_ips = {server.ip_address: server.hostname for server in servers}
|
||||
|
||||
# Get network info
|
||||
network = ipaddress.ip_network(subnet.cidr)
|
||||
total_ips = network.num_addresses - 2 # Excluding network and broadcast addresses
|
||||
used_ip_count = len(servers)
|
||||
|
||||
return render_template(
|
||||
'ipam/subnet_visualization.html',
|
||||
title=f'Subnet Visualization - {subnet.cidr}',
|
||||
subnet=subnet,
|
||||
network=network,
|
||||
used_ips=used_ips,
|
||||
total_ips=total_ips,
|
||||
used_ip_count=used_ip_count,
|
||||
now=datetime.now()
|
||||
)
|
||||
return redirect(url_for('ipam.subnet_view', subnet_id=subnet_id))
|
Loading…
Add table
Add a link
Reference in a new issue