mirror of
https://github.com/Security-Onion-Solutions/securityonion.git
synced 2026-04-21 12:12:26 +02:00
progress and hw tracking for soc hypervisor dynamic annotations
This commit is contained in:
@@ -6,9 +6,12 @@
|
||||
|
||||
{# Build enhanced HYPERVISORS structure #}
|
||||
{% set HYPERVISORS = {} %}
|
||||
{% do salt.log.info('salt/hypervisor/map.jinja: NODES content: ' ~ NODES | tojson) %}
|
||||
{% for role, hypervisors in NODES.items() %}
|
||||
{% do salt.log.info('salt/hypervisor/map.jinja: Processing role: ' ~ role) %}
|
||||
{% do HYPERVISORS.update({role: {}}) %}
|
||||
{% for hypervisor, config in hypervisors.items() %}
|
||||
{% do salt.log.info('salt/hypervisor/map.jinja: Processing hypervisor: ' ~ hypervisor ~ ' with config: ' ~ config | tojson) %}
|
||||
{# Get model from cached grains using Salt runner #}
|
||||
{% set grains = salt.saltutil.runner('cache.grains', tgt=hypervisor ~ '_*', tgt_type='glob') %}
|
||||
{% set model = '' %}
|
||||
@@ -18,17 +21,54 @@
|
||||
{% endif %}
|
||||
{% set model_config = DEFAULTS.hypervisor.model.get(model, {}) %}
|
||||
|
||||
{# Get VM list and states #}
|
||||
{# Get VM list from VMs file #}
|
||||
{% set vms = {} %}
|
||||
{% import_json 'hypervisor/hosts/' ~ hypervisor ~ 'VMs' as vm_list %}
|
||||
{% set vm_list = [] %}
|
||||
{% set vm_list_file = 'hypervisor/hosts/' ~ hypervisor ~ 'VMs' %}
|
||||
{% do salt.log.info('salt/hypervisor/map.jinja: VM list file: ' ~ vm_list_file) %}
|
||||
{% import_json vm_list_file as vm_list %}
|
||||
{% if vm_list %}
|
||||
{% do salt.log.info('salt/hypervisor/map.jinja: VM list content: ' ~ vm_list | tojson) %}
|
||||
{% else %}
|
||||
{# we won't get here if the vm_list_file doesn't exist because we will get TemplateNotFound on the import_json #}
|
||||
{% do salt.log.info('salt/hypervisor/map.jinja: VM list empty: ' ~ vm_list_file) %}
|
||||
{% endif %}
|
||||
|
||||
{# Load state for each VM #}
|
||||
{# Load status and configuration for each VM #}
|
||||
{% for vm in vm_list %}
|
||||
{# Get VM details from list entry #}
|
||||
{% set hostname = vm.get('hostname', '') %}
|
||||
{% set role = vm.get('role', '') %}
|
||||
{% if hostname and role %}
|
||||
{% import_json 'hypervisor/hosts/' ~ hypervisor ~ '/' ~ hostname ~ '_' ~ role as vm_state %}
|
||||
{% do vms.update({hostname: vm_state}) %}
|
||||
{% do salt.log.info('salt/hypervisor/map.jinja: Processing VM - hostname: ' ~ hostname ~ ', role: ' ~ role) %}
|
||||
|
||||
{# Load VM configuration from config file #}
|
||||
{% set vm_file = 'hypervisor/hosts/' ~ hypervisor ~ '/' ~ hostname ~ '_' ~ role %}
|
||||
{% do salt.log.info('salt/hypervisor/map.jinja: VM config file: ' ~ vm_file) %}
|
||||
{% import_json vm_file as vm_state %}
|
||||
{% if vm_state %}
|
||||
{% do salt.log.info('salt/hypervisor/map.jinja: VM config content: ' ~ vm_state | tojson) %}
|
||||
{% set vm_data = {'config': vm_state.config} %}
|
||||
|
||||
{# Load VM status from status file #}
|
||||
{% set status_file = vm_file ~ '.status' %}
|
||||
{% do salt.log.info('salt/hypervisor/map.jinja: VM status file: ' ~ status_file) %}
|
||||
{% import_json status_file as status_data %}
|
||||
{% if status_data %}
|
||||
{% do salt.log.info('salt/hypervisor/map.jinja: VM status content: ' ~ status_data | tojson) %}
|
||||
{% do vm_data.update({'status': status_data}) %}
|
||||
{% else %}
|
||||
{% do salt.log.info('salt/hypervisor/map.jinja: Status file empty: ' ~ status_file) %}
|
||||
{% do vm_data.update({
|
||||
'status': {
|
||||
'status': '',
|
||||
'details': null,
|
||||
'timestamp': ''
|
||||
}
|
||||
}) %}
|
||||
{% endif %}
|
||||
{% do vms.update({hostname: vm_data}) %}
|
||||
{% else %}
|
||||
{% do salt.log.info('salt/hypervisor/map.jinja: Config file empty: ' ~ vm_file) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ used during VM provisioning and hardware reconfiguration tasks.
|
||||
-v, --vm Name of the virtual machine to modify.
|
||||
-c, --cpu Number of virtual CPUs to assign.
|
||||
-m, --memory Amount of memory to assign in MiB.
|
||||
-p, --pci PCI hardware ID(s) to passthrough to the VM (e.g., 0000:c7:00.0). Can be specified multiple times.
|
||||
-p, --pci PCI hardware ID(s) to passthrough to the VM (e.g., 0000:00:1f.2). Can be specified multiple times.
|
||||
Format: domain:bus:device.function
|
||||
-s, --start Start the VM after modification.
|
||||
|
||||
@@ -124,16 +124,34 @@ The `so-kvm-modify-hardware` script modifies hardware parameters of KVM virtual
|
||||
- Both file and console logging are enabled for real-time monitoring
|
||||
- Log entries include timestamps and severity levels
|
||||
- Detailed error messages are logged for troubleshooting
|
||||
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import libvirt
|
||||
import logging
|
||||
import socket
|
||||
import xml.etree.ElementTree as ET
|
||||
from io import StringIO
|
||||
from so_vm_utils import start_vm, stop_vm
|
||||
from so_logging_utils import setup_logging
|
||||
import subprocess
|
||||
|
||||
# Get hypervisor name from local hostname
|
||||
HYPERVISOR = socket.gethostname()
|
||||
|
||||
# Custom log handler to capture output
|
||||
class StringIOHandler(logging.Handler):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.strio = StringIO()
|
||||
|
||||
def emit(self, record):
|
||||
msg = self.format(record)
|
||||
self.strio.write(msg + '\n')
|
||||
|
||||
def get_value(self):
|
||||
return self.strio.getvalue()
|
||||
|
||||
def parse_arguments():
|
||||
parser = argparse.ArgumentParser(description='Modify hardware parameters of a KVM virtual machine.')
|
||||
@@ -226,12 +244,15 @@ def redefine_vm(conn, new_xml_desc, logger):
|
||||
|
||||
def main():
|
||||
# Set up logging using the so_logging_utils library
|
||||
string_handler = StringIOHandler()
|
||||
string_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
|
||||
logger = setup_logging(
|
||||
logger_name='so-kvm-modify-hardware',
|
||||
log_file_path='/opt/so/log/hypervisor/so-kvm-modify-hardware.log',
|
||||
log_level=logging.INFO,
|
||||
format_str='%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
logger.addHandler(string_handler)
|
||||
|
||||
try:
|
||||
args = parse_arguments()
|
||||
@@ -247,6 +268,15 @@ def main():
|
||||
conn = libvirt.open(None)
|
||||
except libvirt.libvirtError as e:
|
||||
logger.error(f"Failed to open connection to libvirt: {e}")
|
||||
try:
|
||||
subprocess.run([
|
||||
'so-salt-emit-vm-deployment-status-event',
|
||||
'-v', vm_name,
|
||||
'-H', HYPERVISOR,
|
||||
'-s', 'Hardware Configuration Failed'
|
||||
], check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
logger.error(f"Failed to emit failure status event: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
# Stop VM if running
|
||||
@@ -262,16 +292,57 @@ def main():
|
||||
if start_vm_flag:
|
||||
dom = conn.lookupByName(vm_name)
|
||||
start_vm(dom, logger)
|
||||
logger.info(f"VM '{vm_name}' started successfully.")
|
||||
else:
|
||||
logger.info("VM start flag not provided; VM will remain stopped.")
|
||||
|
||||
# Close connection
|
||||
conn.close()
|
||||
|
||||
# Send success status event
|
||||
try:
|
||||
subprocess.run([
|
||||
'so-salt-emit-vm-deployment-status-event',
|
||||
'-v', vm_name,
|
||||
'-H', HYPERVISOR,
|
||||
'-s', 'Hardware Configuration'
|
||||
], check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
logger.error(f"Failed to emit success status event: {e}")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logger.error("Operation cancelled by user.")
|
||||
error_msg = "Operation cancelled by user"
|
||||
logger.error(error_msg)
|
||||
try:
|
||||
subprocess.run([
|
||||
'so-salt-emit-vm-deployment-status-event',
|
||||
'-v', vm_name,
|
||||
'-H', HYPERVISOR,
|
||||
'-s', 'Hardware Configuration Failed'
|
||||
], check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
logger.error(f"Failed to emit failure status event: {e}")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
logger.error(f"An error occurred: {e}")
|
||||
error_msg = str(e)
|
||||
if "Failed to open connection to libvirt" in error_msg:
|
||||
error_msg = f"Failed to connect to libvirt: {error_msg}"
|
||||
elif "Failed to redefine VM" in error_msg:
|
||||
error_msg = f"Failed to apply hardware changes: {error_msg}"
|
||||
elif "Failed to modify VM XML" in error_msg:
|
||||
error_msg = f"Failed to update hardware configuration: {error_msg}"
|
||||
else:
|
||||
error_msg = f"An error occurred: {error_msg}"
|
||||
logger.error(error_msg)
|
||||
try:
|
||||
subprocess.run([
|
||||
'so-salt-emit-vm-deployment-status-event',
|
||||
'-v', vm_name,
|
||||
'-h', HYPERVISOR,
|
||||
'-s', 'Hardware Configuration Failed'
|
||||
], check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
logger.error(f"Failed to emit failure status event: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -118,7 +118,6 @@ The `so-qcow2-modify-network` script modifies network configuration within a QCO
|
||||
- Image mount/unmount operations
|
||||
- Validation failures
|
||||
- File access errors
|
||||
|
||||
"""
|
||||
|
||||
import argparse
|
||||
@@ -127,20 +126,41 @@ import re
|
||||
import sys
|
||||
import logging
|
||||
import os
|
||||
import socket
|
||||
import ipaddress
|
||||
import configparser
|
||||
import uuid
|
||||
from io import StringIO
|
||||
import libvirt
|
||||
from so_logging_utils import setup_logging
|
||||
import subprocess
|
||||
|
||||
# Get hypervisor name from local hostname
|
||||
HYPERVISOR = socket.gethostname()
|
||||
|
||||
# Custom log handler to capture output
|
||||
class StringIOHandler(logging.Handler):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.strio = StringIO()
|
||||
|
||||
def emit(self, record):
|
||||
msg = self.format(record)
|
||||
self.strio.write(msg + '\n')
|
||||
|
||||
def get_value(self):
|
||||
return self.strio.getvalue()
|
||||
|
||||
# Set up logging using the so_logging_utils library
|
||||
string_handler = StringIOHandler()
|
||||
string_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
|
||||
logger = setup_logging(
|
||||
logger_name='so-qcow2-modify-network',
|
||||
log_file_path='/opt/so/log/hypervisor/so-qcow2-modify-network.log',
|
||||
log_level=logging.INFO,
|
||||
format_str='%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
logger.addHandler(string_handler)
|
||||
|
||||
NETWORK_CONFIG_DIR = "/etc/NetworkManager/system-connections"
|
||||
|
||||
@@ -403,6 +423,7 @@ def parse_arguments():
|
||||
parser = argparse.ArgumentParser(description="Modify IPv4 settings in a QCOW2 image for a specified network interface.")
|
||||
parser.add_argument("-I", "--image", required=True, help="Path to the QCOW2 image.")
|
||||
parser.add_argument("-i", "--interface", required=True, help="Network interface to modify (e.g., enp1s0).")
|
||||
parser.add_argument("-n", "--vm-name", required=True, help="Full name of the VM (hostname_role).")
|
||||
group = parser.add_mutually_exclusive_group(required=True)
|
||||
group.add_argument("--dhcp4", action="store_true", help="Configure interface for DHCP (IPv4).")
|
||||
group.add_argument("--static4", action="store_true", help="Configure interface for static IPv4 settings.")
|
||||
@@ -448,15 +469,47 @@ def main():
|
||||
modify_network_config(args.image, args.interface, mode, args.ip4, args.gw4, args.dns4, args.search4)
|
||||
logger.info("Network configuration update completed successfully")
|
||||
|
||||
# Send success status event
|
||||
try:
|
||||
subprocess.run([
|
||||
'so-salt-emit-vm-deployment-status-event',
|
||||
'-v', args.vm_name,
|
||||
'-H', HYPERVISOR,
|
||||
'-s', 'IP Configuration'
|
||||
], check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
logger.error(f"Failed to emit success status event: {e}")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logger.error("Operation cancelled by user.")
|
||||
error_msg = "Operation cancelled by user"
|
||||
logger.error(error_msg)
|
||||
try:
|
||||
subprocess.run([
|
||||
'so-salt-emit-vm-deployment-status-event',
|
||||
'-v', args.vm_name,
|
||||
'-H', HYPERVISOR,
|
||||
'-s', 'IP Configuration Failed'
|
||||
], check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
logger.error(f"Failed to emit failure status event: {e}")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
if "base domain is running" in str(e):
|
||||
error_msg = str(e)
|
||||
if "base domain is running" in error_msg:
|
||||
logger.error("Cannot proceed: Base domain must not be running when modifying network configuration")
|
||||
error_msg = "Base domain must not be running when modifying network configuration"
|
||||
else:
|
||||
logger.error(f"An error occurred: {e}")
|
||||
try:
|
||||
subprocess.run([
|
||||
'so-salt-emit-vm-deployment-status-event',
|
||||
'-v', args.vm_name,
|
||||
'-H', HYPERVISOR,
|
||||
'-s', 'IP Configuration Failed'
|
||||
], check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
logger.error(f"Failed to emit failure status event: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user