mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2025-11-18 03:26:17 +00:00
Update flask_server.py
This commit is contained in:
@@ -446,12 +446,58 @@ def extract_vmid_from_interface(interface_name):
|
|||||||
pass
|
pass
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
|
# Global variable to cache the Proxmox node name
|
||||||
|
_proxmox_node_cache = None
|
||||||
|
_proxmox_node_cache_time = 0
|
||||||
|
CACHE_TTL = 300 # 5 minutes cache
|
||||||
|
|
||||||
|
def get_proxmox_node_name():
|
||||||
|
"""Get the actual Proxmox node name from the API (cached for 5 minutes)
|
||||||
|
This is more reliable than socket.gethostname() which may return FQDN"""
|
||||||
|
global _proxmox_node_cache, _proxmox_node_cache_time
|
||||||
|
|
||||||
|
# Return cached value if still valid
|
||||||
|
current_time = time.time()
|
||||||
|
if _proxmox_node_cache and (current_time - _proxmox_node_cache_time) < CACHE_TTL:
|
||||||
|
return _proxmox_node_cache
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Get node list from Proxmox
|
||||||
|
result = subprocess.run(
|
||||||
|
['pvesh', 'get', '/nodes', '--output-format', 'json'],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=5
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.returncode == 0:
|
||||||
|
nodes = json.loads(result.stdout)
|
||||||
|
|
||||||
|
# In a single-node setup or local node, there should be one node marked as local
|
||||||
|
# Or we can match by IP address
|
||||||
|
if nodes and len(nodes) > 0:
|
||||||
|
# For now, use the first node (usually the local one in single-node setups)
|
||||||
|
# In multi-node clusters, pvesh runs on the local node so the first result is typically local
|
||||||
|
node_name = nodes[0].get('node')
|
||||||
|
if node_name:
|
||||||
|
# Cache the result
|
||||||
|
_proxmox_node_cache = node_name
|
||||||
|
_proxmox_node_cache_time = current_time
|
||||||
|
return node_name
|
||||||
|
|
||||||
|
# Fallback to hostname if API call fails
|
||||||
|
return socket.gethostname().split('.')[0] # Use short hostname as fallback
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# Fallback to short hostname (without domain)
|
||||||
|
return socket.gethostname().split('.')[0]
|
||||||
|
|
||||||
def get_vm_lxc_names():
|
def get_vm_lxc_names():
|
||||||
"""Get VM and LXC names from Proxmox API (only from local node)"""
|
"""Get VM and LXC names from Proxmox API (only from local node)"""
|
||||||
vm_lxc_map = {}
|
vm_lxc_map = {}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
local_node = socket.gethostname()
|
local_node = get_proxmox_node_name()
|
||||||
|
|
||||||
result = subprocess.run(['pvesh', 'get', '/cluster/resources', '--type', 'vm', '--output-format', 'json'],
|
result = subprocess.run(['pvesh', 'get', '/cluster/resources', '--type', 'vm', '--output-format', 'json'],
|
||||||
capture_output=True, text=True, timeout=10)
|
capture_output=True, text=True, timeout=10)
|
||||||
@@ -1139,8 +1185,6 @@ def get_pcie_link_speed(disk_name):
|
|||||||
pass
|
pass
|
||||||
return pcie_info
|
return pcie_info
|
||||||
|
|
||||||
# get_pcie_link_speed function definition ends here
|
|
||||||
|
|
||||||
def get_smart_data(disk_name):
|
def get_smart_data(disk_name):
|
||||||
"""Get SMART data for a specific disk - Enhanced with multiple device type attempts"""
|
"""Get SMART data for a specific disk - Enhanced with multiple device type attempts"""
|
||||||
smart_data = {
|
smart_data = {
|
||||||
@@ -1645,7 +1689,7 @@ def get_smart_data(disk_name):
|
|||||||
def get_proxmox_storage():
|
def get_proxmox_storage():
|
||||||
"""Get Proxmox storage information using pvesh (filtered by local node)"""
|
"""Get Proxmox storage information using pvesh (filtered by local node)"""
|
||||||
try:
|
try:
|
||||||
local_node = socket.gethostname()
|
local_node = get_proxmox_node_name()
|
||||||
|
|
||||||
result = subprocess.run(['pvesh', 'get', '/cluster/resources', '--type', 'storage', '--output-format', 'json'],
|
result = subprocess.run(['pvesh', 'get', '/cluster/resources', '--type', 'storage', '--output-format', 'json'],
|
||||||
capture_output=True, text=True, timeout=10)
|
capture_output=True, text=True, timeout=10)
|
||||||
@@ -2197,7 +2241,7 @@ def get_proxmox_vms():
|
|||||||
all_vms = []
|
all_vms = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
local_node = socket.gethostname()
|
local_node = get_proxmox_node_name()
|
||||||
# print(f"[v0] Local node detected: {local_node}")
|
# print(f"[v0] Local node detected: {local_node}")
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -4674,24 +4718,20 @@ def api_network_summary():
|
|||||||
pass
|
pass
|
||||||
return jsonify({'error': str(e)}), 500
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
@app.route('/api/network/<interface_name>/metrics', methods=['GET'])
|
@app.route('/api/network/interfaces/<interface_name>/metrics', methods=['GET'])
|
||||||
@require_auth
|
@require_auth
|
||||||
def api_network_interface_metrics(interface_name):
|
def get_interface_metrics(interface_name):
|
||||||
"""Get historical metrics (RRD data) for a specific network interface"""
|
"""Get historical metrics for a specific network interface"""
|
||||||
try:
|
try:
|
||||||
timeframe = request.args.get('timeframe', 'day') # hour, day, week, month, year
|
timeframe = request.args.get('timeframe', 'day')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Validate timeframe
|
|
||||||
valid_timeframes = ['hour', 'day', 'week', 'month', 'year']
|
valid_timeframes = ['hour', 'day', 'week', 'month', 'year']
|
||||||
|
|
||||||
if timeframe not in valid_timeframes:
|
if timeframe not in valid_timeframes:
|
||||||
# print(f"[v0] ERROR: Invalid timeframe: {timeframe}")
|
# print(f"[v0] ERROR: Invalid timeframe: {timeframe}")
|
||||||
pass
|
pass
|
||||||
return jsonify({'error': f'Invalid timeframe. Must be one of: {", ".join(valid_timeframes)}'}), 400
|
return jsonify({'error': f'Invalid timeframe. Must be one of: {", ".join(valid_timeframes)}'}), 400
|
||||||
|
|
||||||
# Get local node name
|
local_node = get_proxmox_node_name()
|
||||||
local_node = socket.gethostname()
|
|
||||||
|
|
||||||
|
|
||||||
# Determine interface type and get appropriate RRD data
|
# Determine interface type and get appropriate RRD data
|
||||||
@@ -4763,6 +4803,52 @@ def api_vms():
|
|||||||
"""Get virtual machine information"""
|
"""Get virtual machine information"""
|
||||||
return jsonify(get_proxmox_vms())
|
return jsonify(get_proxmox_vms())
|
||||||
|
|
||||||
|
@app.route('/api/vms/<int:vmid>', methods=['GET'])
|
||||||
|
@require_auth
|
||||||
|
def get_vm_config(vmid):
|
||||||
|
"""Get detailed configuration for a specific VM/LXC"""
|
||||||
|
try:
|
||||||
|
node = get_proxmox_node_name()
|
||||||
|
|
||||||
|
result = subprocess.run(
|
||||||
|
['pvesh', 'get', f'/nodes/{node}/qemu/{vmid}/config', '--output-format', 'json'],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=10
|
||||||
|
)
|
||||||
|
|
||||||
|
vm_type = 'qemu'
|
||||||
|
|
||||||
|
if result.returncode != 0:
|
||||||
|
# Try LXC if QEMU config failed
|
||||||
|
result = subprocess.run(
|
||||||
|
['pvesh', 'get', f'/nodes/{node}/lxc/{vmid}/config', '--output-format', 'json'],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=10
|
||||||
|
)
|
||||||
|
if result.returncode == 0:
|
||||||
|
vm_type = 'lxc'
|
||||||
|
else:
|
||||||
|
# print(f"[v0] ERROR: VM/LXC {vmid} config not found")
|
||||||
|
pass
|
||||||
|
return jsonify({'error': f'Configuration for VM/LXC {vmid} not found'}), 404
|
||||||
|
|
||||||
|
# Parse the configuration
|
||||||
|
config = json.loads(result.stdout)
|
||||||
|
|
||||||
|
# Add VMID and type to the config for convenience
|
||||||
|
config['vmid'] = vmid
|
||||||
|
config['type'] = vm_type
|
||||||
|
|
||||||
|
return jsonify(config)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# print(f"Error getting VM config: {e}")
|
||||||
|
pass
|
||||||
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/vms/<int:vmid>/metrics', methods=['GET'])
|
@app.route('/api/vms/<int:vmid>/metrics', methods=['GET'])
|
||||||
@require_auth
|
@require_auth
|
||||||
def api_vm_metrics(vmid):
|
def api_vm_metrics(vmid):
|
||||||
@@ -4823,11 +4909,13 @@ def api_vm_metrics(vmid):
|
|||||||
'data': rrd_data
|
'data': rrd_data
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
|
# print(f"[v0] ERROR: Failed to get RRD data for VM/LXC {vmid}: {rrd_result.stderr}")
|
||||||
|
pass
|
||||||
return jsonify({'error': f'Failed to get RRD data: {rrd_result.stderr}'}), 500
|
return jsonify({'error': f'Failed to get RRD data: {rrd_result.stderr}'}), 500
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
# print(f"Error getting VM metrics: {e}")
|
||||||
|
pass
|
||||||
return jsonify({'error': str(e)}), 500
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
@app.route('/api/node/metrics', methods=['GET'])
|
@app.route('/api/node/metrics', methods=['GET'])
|
||||||
@@ -4847,7 +4935,7 @@ def api_node_metrics():
|
|||||||
return jsonify({'error': f'Invalid timeframe. Must be one of: {", ".join(valid_timeframes)}'}), 400
|
return jsonify({'error': f'Invalid timeframe. Must be one of: {", ".join(valid_timeframes)}'}), 400
|
||||||
|
|
||||||
# Get local node name
|
# Get local node name
|
||||||
local_node = socket.gethostname()
|
local_node = get_proxmox_node_name() # Use the cached/resolved node name
|
||||||
# print(f"[v0] Local node: {local_node}")
|
# print(f"[v0] Local node: {local_node}")
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -4886,10 +4974,13 @@ def api_node_metrics():
|
|||||||
'data': rrd_data
|
'data': rrd_data
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
|
# print(f"[v0] ERROR: Failed to get RRD data for node {local_node}: {rrd_result.stderr}")
|
||||||
|
pass
|
||||||
return jsonify({'error': f'Failed to get RRD data: {rrd_result.stderr}'}), 500
|
return jsonify({'error': f'Failed to get RRD data: {rrd_result.stderr}'}), 500
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
# print(f"Error getting node metrics: {e}")
|
||||||
|
pass
|
||||||
return jsonify({'error': str(e)}), 500
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
@app.route('/api/logs', methods=['GET'])
|
@app.route('/api/logs', methods=['GET'])
|
||||||
@@ -4956,6 +5047,8 @@ def api_logs():
|
|||||||
continue
|
continue
|
||||||
return jsonify({'logs': logs, 'total': len(logs)})
|
return jsonify({'logs': logs, 'total': len(logs)})
|
||||||
else:
|
else:
|
||||||
|
# print(f"[API] Error executing journalctl: {result.stderr}")
|
||||||
|
pass
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'error': 'journalctl not available or failed',
|
'error': 'journalctl not available or failed',
|
||||||
'logs': [],
|
'logs': [],
|
||||||
@@ -5022,6 +5115,8 @@ def api_logs_download():
|
|||||||
download_name=f'proxmox_{filename}'
|
download_name=f'proxmox_{filename}'
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
# print(f"[API] Error generating log file: {result.stderr}")
|
||||||
|
pass
|
||||||
return jsonify({'error': 'Failed to generate log file'}), 500
|
return jsonify({'error': 'Failed to generate log file'}), 500
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -5195,6 +5290,8 @@ def api_notifications_download():
|
|||||||
download_name=f'notification_{timestamp.replace(":", "_").replace(" ", "_")}.log'
|
download_name=f'notification_{timestamp.replace(":", "_").replace(" ", "_")}.log'
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
# print(f"[API] Error generating notification log file: {result.stderr}")
|
||||||
|
pass
|
||||||
return jsonify({'error': 'Failed to generate log file'}), 500
|
return jsonify({'error': 'Failed to generate log file'}), 500
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -5483,7 +5580,8 @@ def api_prometheus():
|
|||||||
try:
|
try:
|
||||||
metrics = []
|
metrics = []
|
||||||
timestamp = int(datetime.now().timestamp() * 1000)
|
timestamp = int(datetime.now().timestamp() * 1000)
|
||||||
node = socket.gethostname()
|
# Use get_proxmox_node_name() for the node label
|
||||||
|
node = get_proxmox_node_name()
|
||||||
|
|
||||||
# Get system data
|
# Get system data
|
||||||
cpu_usage = psutil.cpu_percent(interval=0.5)
|
cpu_usage = psutil.cpu_percent(interval=0.5)
|
||||||
|
|||||||
Reference in New Issue
Block a user