ensure local hypervisor dir when new hypervisor key accepted. apply soc.dyanno.hypervisor when hypervisor key accepted

This commit is contained in:
Josh Patterson
2025-03-05 08:51:10 -05:00
parent 8047e196fe
commit 2c5861a0c2
5 changed files with 187 additions and 6 deletions

View File

@@ -163,6 +163,7 @@ def _validate_image_checksum(path, expected_sha256):
IMAGE_URL = "https://yum.oracle.com/templates/OracleLinux/OL9/u5/x86_64/OL9U5_x86_64-kvm-b253.qcow2"
IMAGE_SHA256 = "3b00bbbefc8e78dd28d9f538834fb9e2a03d5ccdc2cadf2ffd0036c0a8f02021"
IMAGE_PATH = "/nsm/libvirt/boot/OL9U5_x86_64-kvm-b253.qcow2"
MANAGER_HOSTNAME = socket.gethostname()
def _download_image():
"""
@@ -332,6 +333,135 @@ def _check_vm_exists(vm_name: str) -> bool:
log.info("MAIN: VM %s already exists", vm_name)
return exists
def _ensure_hypervisor_host_dir(minion_id: str = None):
"""
Ensure the hypervisor host directory exists.
This function creates the directory structure for a hypervisor host if it doesn't exist.
The path is: /opt/so/saltstack/local/salt/hypervisor/hosts/<hypervisorHostname>
Args:
minion_id (str, optional): Salt minion ID of the hypervisor.
Returns:
bool: True if directory exists or was created successfully, False otherwise
"""
if not minion_id:
log.warning("HOSTDIR: No minion_id provided, skipping host directory creation")
return True
try:
# Extract hostname from minion_id by removing role suffix (anything after first underscore)
hostname = minion_id.split('_', 1)[0] if '_' in minion_id else minion_id
# Define the directory path
host_dir = f'/opt/so/saltstack/local/salt/hypervisor/hosts/{hostname}'
# Check if directory exists and create it if it doesn't
if os.path.exists(host_dir):
log.info(f"HOSTDIR: Hypervisor host directory already exists: {host_dir}")
# Create the VMs file if it doesn't exist
vms_file = f'/opt/so/saltstack/local/salt/hypervisor/hosts/{hostname}VMs'
if not os.path.exists(vms_file):
with salt.utils.files.fopen(vms_file, 'w') as f:
f.write('[]')
log.info(f"HOSTDIR: Created empty VMs file: {vms_file}")
# Set proper ownership for the VMs file
try:
socore_uid = pwd.getpwnam('socore').pw_uid
socore_gid = pwd.getpwnam('socore').pw_gid
os.chown(vms_file, socore_uid, socore_gid)
log.info(f"HOSTDIR: Set ownership to socore:socore for {vms_file}")
except (KeyError, Exception) as e:
log.warning(f"HOSTDIR: Failed to set ownership for VMs file: {str(e)}")
return True
# Create all necessary parent directories
os.makedirs(host_dir, exist_ok=True)
log.info(f"HOSTDIR: Created hypervisor host directory: {host_dir}")
# Create the VMs file with an empty JSON array
vms_file = f'/opt/so/saltstack/local/salt/hypervisor/hosts/{hostname}VMs'
with salt.utils.files.fopen(vms_file, 'w') as f:
f.write('[]')
log.info(f"HOSTDIR: Created empty VMs file: {vms_file}")
# Set proper ownership (socore:socore)
try:
socore_uid = pwd.getpwnam('socore').pw_uid
socore_gid = pwd.getpwnam('socore').pw_gid
os.chown(host_dir, socore_uid, socore_gid)
os.chown(vms_file, socore_uid, socore_gid)
# Also set ownership for parent directories if they were just created
parent_dir = os.path.dirname(host_dir) # /opt/so/saltstack/local/salt/hypervisor/hosts
if os.path.exists(parent_dir):
os.chown(parent_dir, socore_uid, socore_gid)
parent_dir = os.path.dirname(parent_dir) # /opt/so/saltstack/local/salt/hypervisor
if os.path.exists(parent_dir):
os.chown(parent_dir, socore_uid, socore_gid)
log.info(f"HOSTDIR: Set ownership to socore:socore for {host_dir} and {vms_file}")
except KeyError:
log.warning("HOSTDIR: socore user not found, skipping ownership change")
except Exception as e:
log.warning(f"HOSTDIR: Failed to set ownership: {str(e)}")
return True
except Exception as e:
log.error(f"HOSTDIR: Error creating hypervisor host directory: {str(e)}")
return False
def _apply_dyanno_hypervisor_state():
"""
Apply the soc.dyanno.hypervisor state on the salt master.
This function applies the soc.dyanno.hypervisor state on the salt master
to update the hypervisor annotation and ensure all hypervisor host directories exist.
Returns:
bool: True if state was applied successfully, False otherwise
"""
try:
log.info("DYANNO: Applying soc.dyanno.hypervisor state on salt master")
# Initialize the LocalClient
local = salt.client.LocalClient()
# Target the salt master (localhost) to apply the soc.dyanno.hypervisor state
target = MANAGER_HOSTNAME + '_*'
state_result = local.cmd(target, 'state.apply', ['soc.dyanno.hypervisor'], tgt_type='glob', concurrent=True)
log.debug(f"DYANNO: state_result: {state_result}")
# Check if state was applied successfully
if state_result:
success = True
for minion, states in state_result.items():
if not isinstance(states, dict):
log.error(f"DYANNO: Unexpected result format from {minion}: {states}")
success = False
continue
for state_id, state_data in states.items():
if not state_data.get('result', False):
log.error(f"DYANNO: State {state_id} failed on {minion}: {state_data.get('comment', 'No comment')}")
success = False
if success:
log.info("DYANNO: Successfully applied soc.dyanno.hypervisor state")
return True
else:
log.error("DYANNO: Failed to apply soc.dyanno.hypervisor state")
return False
else:
log.error("DYANNO: No response from salt master when applying soc.dyanno.hypervisor state")
return False
except Exception as e:
log.error(f"DYANNO: Error applying soc.dyanno.hypervisor state: {str(e)}")
return False
def setup_environment(vm_name: str = 'sool9', disk_size: str = '220G', minion_id: str = None):
"""
Main entry point to set up the hypervisor environment.
@@ -380,6 +510,43 @@ def setup_environment(vm_name: str = 'sool9', disk_size: str = '220G', minion_id
'vm_result': None
}
# Ensure hypervisor host directory exists
if not _ensure_hypervisor_host_dir(minion_id):
return {
'success': False,
'error': 'Failed to create hypervisor host directory',
'vm_result': None
}
# Initialize the LocalClient
local = salt.client.LocalClient()
# Add retry logic for mine.update
max_retries = 10
retry_delay = 3
mine_update_success = False
for attempt in range(1, max_retries + 1):
mine_update_result = local.cmd(minion_id, 'mine.update')
log.debug(f"DYANNO: mine_update_result: {mine_update_result}")
# Check if mine.update was successful
if mine_update_result and all(mine_update_result.values()):
log.info(f"DYANNO: mine.update successful on attempt {attempt}")
mine_update_success = True
break
else:
log.warning(f"DYANNO: mine.update failed on attempt {attempt}/{max_retries}, retrying in {retry_delay} seconds...")
time.sleep(retry_delay)
if not mine_update_success:
log.error(f"DYANNO: mine.update failed after {max_retries} attempts")
# Apply the soc.dyanno.hypervisor state on the salt master
if not _apply_dyanno_hypervisor_state():
log.warning("MAIN: Failed to apply soc.dyanno.hypervisor state, continuing with setup")
# We don't return an error here as we want to continue with the setup process
log.info("MAIN: Starting setup_environment in setup_hypervisor runner")
# Check if environment is already set up
@@ -548,9 +715,6 @@ def create_vm(vm_name: str, disk_size: str = '220G'):
log.error("CREATEVM: Failed to read SSH public key: %s", str(e))
return {'success': False, 'error': 'Failed to read SSH public key'}
# Get hostname for repo configuration
manager_hostname = socket.gethostname()
# Read and encode GPG keys
keys_dir = '/opt/so/saltstack/default/salt/repo/client/files/oracle/keys'
oracle_key = _read_and_encode_key(os.path.join(keys_dir, 'RPM-GPG-KEY-oracle'))
@@ -607,7 +771,7 @@ write_files:
content: |
[securityonion]
name=Security Onion Repo
baseurl=https://{manager_hostname}/repo
baseurl=https://{MANAGER_HOSTNAME}/repo
enabled=1
gpgcheck=1
sslverify=0