import argparse
import datetime
import json
import logging as log
import os
import re
from utils.ansible import ansible_add_to_inventory_executor
from utils.ansible import ansible_log_writer_analyzer
from utils.ansible import ansible_run_check
from utils.ansible import ansible_ssh_key_exist
from utils.ansible import ansible_ssh_keys_generate
from utils.cloud_init import cloud_init_sha512_crypt
from utils.cloud_init import generate_random_salt
from utils.docker import ensure_container_running
from utils.docker import is_docker_running
from utils.docker import stop_container
from utils.utils import colors
from utils.utils import setup_logging
docker_name = 'ansible_automation'
[docs]
def builder_func(build_options, install_log_name, log_level, use_docker=True):
"""
Execute a series of build steps based on provided options.
Parameters
----------
build_options : list
A list of dictionaries containing build options.
install_log_name : str
The name of the installation log file.
log_level : str
The logging level.
Returns
-------
None
Notes
-----
This function executes a series of build steps based on the provided `build_options`.
For each build option, it performs various tasks such as configuring hypervisors,
installing required packages, executing build scripts, and setting up virtual machines.
The function logs each step and checks for failures using the `ansible_log_writer_analyzer`
and `ansible_run_check` functions. If a failure occurs, the function logs an error message
and returns False. Otherwise, it returns True upon successful completion.
"""
log_check_bool = []
for build in build_options:
# -- Hypervisor
hypervisor_hostname = build.get('hypervisor_hostname')
hypervisor_username = build.get('hypervisor_username')
hypervisor_password = build.get('hypervisor_password')
# -- VM Configuration
hypervisor_vm_image_loc = build.get('hypervisor_vm_image_loc')
hypervisor_dest_directory = build.get('hypervisor_dest_directory')
vm_qcow_name = build.get('vm_qcow_name')
vm_vcpus = build.get('vm_vcpus', 8)
vm_memory = build.get('vm_memory', 16384)
vm_os_variant = build.get('vm_os_variant', 'centos7.0')
vm_boot = build.get('vm_boot', 'hd,cdrom')
vm_cpu = build.get('vm_cpu', 'host')
vm_source = build.get('vm_source')
vm_model = build.get('vm_model')
vm_source_mode = build.get('vm_source_mode', 'bridge')
vm_network_net_a = build.get('vm_network_net_a')
vm_network_net_b = build.get('vm_network_net_b')
vm_network_app_a = build.get('vm_network_app_a')
vm_network_app_b = build.get('vm_network_app_b')
vm_network_mir_a = build.get('vm_network_mir_a')
vm_network_mir_b = build.get('vm_network_mir_b')
vm_hostname = build.get('vm_hostname')
vm_password = build.get('vm_password')
vm_hashed_password = cloud_init_sha512_crypt(
vm_password, salt=generate_random_salt(), rounds=5000,
)
vm_hashed_password = vm_hashed_password.replace('$', '\\$')
vm_old_password = build.get('vm_old_password')
vm_username = build.get('vm_username', 'root')
# ETO Network configuration
vm_static_ip_address = build.get('vm_static_ip_address')
vm_ip_gateway = build.get('vm_ip_gateway')
vm_ip_netmask = build.get('vm_ip_netmask')
vm_dns_server_1 = build.get('vm_dns_server_1')
vm_dns_server_2 = build.get('vm_dns_server_2')
# ETO API Config
vm_api_username = build.get('vm_api_username')
vm_allow_enrollment = build.get('vm_allow_enrollment')
if vm_static_ip_address is None:
log.info('Cannot connect to ETO unless a static IP is provided, please note that the default settings for the API will have to be changed manually')
# Check if it was not successfully added to the inventory
if not ansible_add_to_inventory_executor(
hostname=hypervisor_hostname,
username=hypervisor_username,
password=hypervisor_password,
use_docker=use_docker,
docker_name=docker_name,
install_log_name=install_log_name,
log_level=log_level,
):
raise Exception(
f'Something went wrong when adding {hypervisor_hostname} to the inventory',
)
log.info('Step 1: Add the SSH keys')
# Step 1: Add the SSH keys
log_check_bool.append(
ansible_log_writer_analyzer(
install_log_name, 'echo Adding SSH Keys',
),
)
make_sure_ssh_available = f'ansible-playbook playbooks/ssh_setup_individual.yml --extra-vars "hypervisor_username={hypervisor_username} hypervisor_password={hypervisor_password} hypervisor_hostname={hypervisor_hostname}"'
ssh_docker_command = f'docker exec -t {docker_name} {make_sure_ssh_available}'
log_check_bool.append(
ansible_log_writer_analyzer(
install_log_name, ssh_docker_command, log_level,
),
)
# If failed exit the loop
if ansible_run_check(log_check_bool):
log.error('Failed Step 1')
break
log.info('Step 2: Install required packages and dependencies')
log_check_bool.append(
ansible_log_writer_analyzer(
install_log_name, 'echo Installing requirements',
),
)
install_required_packages = f'ansible-playbook playbooks/setup.yml --extra-vars \
"hypervisor_hostname={hypervisor_hostname}\
vm_network_net_a={vm_network_net_a}\
vm_network_net_b={vm_network_net_b}\
vm_network_app_a={vm_network_app_a}\
vm_network_app_b={vm_network_app_b}\
vm_network_mir_a={vm_network_mir_a}\
vm_network_mir_b={vm_network_mir_b}\
vm_qcow_name={vm_qcow_name}"'
install_required_packages_docker_command = f'docker exec -t {docker_name} {install_required_packages}'
log_check_bool.append(
ansible_log_writer_analyzer(
install_log_name, install_required_packages_docker_command,
),
)
# If failed exit the loop
if ansible_run_check(log_check_bool):
log.error('Failed Step 2')
break
log.info('Step 3: Run KVM Installation')
log_check_bool.append(
ansible_log_writer_analyzer(
install_log_name, 'echo Running Build',
),
)
command_name = f'ansible-playbook playbooks/install_kvm.yml --extra-vars \
"hypervisor_hostname={hypervisor_hostname}\
hypervisor_vm_image_loc={hypervisor_vm_image_loc}\
hypervisor_dest_directory={hypervisor_dest_directory}\
vm_qcow_name={vm_qcow_name}\
vm_vcpus={vm_vcpus}\
vm_memory={vm_memory}\
vm_os_variant={vm_os_variant}\
vm_boot={vm_boot}\
vm_cpu={vm_cpu}\
vm_source={vm_source}\
vm_model={vm_model}\
vm_source_mode={vm_source_mode}\
vm_network_net_a={vm_network_net_a}\
vm_network_net_b={vm_network_net_b}\
vm_network_app_a={vm_network_app_a}\
vm_network_app_b={vm_network_app_b}\
vm_network_mir_a={vm_network_mir_a}\
vm_network_mir_b={vm_network_mir_b}\
vm_username={vm_username}\
vm_hashed_password={vm_hashed_password}\
vm_hostname={vm_hostname}\
vm_static_ip_address={vm_static_ip_address}\
vm_ip_gateway={vm_ip_gateway}\
vm_ip_netmask={vm_ip_netmask}\
vm_dns_server_1={vm_dns_server_1}\
vm_dns_server_2={vm_dns_server_2}"'
docker_command = f'docker exec -t {docker_name} {command_name}'
log_check_bool.append(
ansible_log_writer_analyzer(
install_log_name, docker_command,
),
)
# If failed break out of the loop
if ansible_run_check(log_check_bool):
log.error('Failed Step 3')
break
if vm_static_ip_address:
# Setup the VM Instance
log.info('Step 4: Setup the VM via API')
log_check_bool.append(
ansible_log_writer_analyzer(
install_log_name, 'echo Setting up the ETO',
),
)
command_name = f'python3 /deploy/setup_eto.py --ip={vm_static_ip_address} --username={vm_api_username} --old_password={vm_old_password} --password={vm_password} --allow-enrollment={vm_allow_enrollment}'
docker_command = f'docker exec -t {docker_name} {command_name}'
log_check_bool.append(
ansible_log_writer_analyzer(
install_log_name, docker_command,
),
)
else:
log.warning('Cannot connect to ETO unless a static IP is provided')
if ansible_run_check(log_check_bool):
log.error(hypervisor_hostname + ' Installation Failed\n')
return False
else:
log.info(hypervisor_hostname + ' Installation Succeeded\n')
return True
[docs]
def parse_deployment_arguments():
"""
Parse command-line arguments for deployment configuration.
Returns
-------
argparse.Namespace
Parsed command-line arguments.
Example
-------
Example usage:
python deploy.py --config /path/to/config.json --log-dir /path/to/logs --log-level DEBUG --allow-enrollment True
Notes
-----
This function parses command-line arguments for deployment configuration. It returns
an `argparse.Namespace` object containing the parsed arguments.
"""
parser = argparse.ArgumentParser(description='Deployment Config Parser')
parser.add_argument(
'--config', required=True, type=str,
help='Path to deployment configuration file',
)
parser.add_argument(
'--log-dir', type=str, default=None,
help='Path to deployment logs',
)
parser.add_argument(
'--log-level', type=str, default='INFO', choices=[
'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL', 'VERBOSE',
], help='Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL, VERBOSE)',
)
parser.add_argument(
'--allow-enrollment', type=bool, default=False,
)
args = parser.parse_args()
return args
if __name__ == '__main__':
"""This script is designed to handle deployment tasks. When executed directly, it performs the following steps:
1. Parses deployment arguments.
2. Checks if Docker is running; if up, starts the ansible_automation container; if not, prompts the user to start Docker and exits.
3. Sets up logging, creating a log file path based on the current timestamp and specified log directory.
4. Creates the log directory if it doesn't exist.
5. Reads configuration data from a JSON file specified in the arguments.
6. Initiates deployment, displaying progress and storing logs in the specified log file.
7. Outputs messages indicating the start and end of deployment.
"""
args = parse_deployment_arguments()
# Check if the docker container is running
if is_docker_running():
# Check the existence of the ssh-keys directory
if ansible_ssh_key_exist(directory='./ssh-keys', key_name='ansible'):
print(colors.GREEN + 'SSH keys exist' + colors.GREEN)
ensure_container_running(
docker_name, compose_file='docker-compose.yml',
)
else:
print(colors.RED + 'Stopping the docker container' + colors.END)
stop_container(docker_name, compose_file='docker-compose.yml')
print(colors.RED + 'Generating the SSH keys' + colors.END)
ansible_ssh_keys_generate(
directory='./ssh-keys', key_name='ansible',
)
print(
colors.RED + 'Starting the containers to ensure the volume mounts correctly' + colors.END,
)
ensure_container_running(
docker_name, compose_file='docker-compose.yml', build=False,
)
else:
print(colors.RED + 'Docker is not running, please start it.' + colors.END)
exit(1)
# Setup Logging
log_dir = args.log_dir
if log_dir is not None:
log_file_path = os.path.join(log_dir, 'deploy-')
else:
log_file_path = 'logs/deploy-'
log_file_path += str(
datetime.datetime.now().replace(
microsecond=0,
).isoformat(),
)
log_file_path = re.sub(':', '-', log_file_path) + '.log'
# Create the directory if it doesn't exist
if not os.path.exists(os.path.dirname(log_file_path)):
os.makedirs(os.path.dirname(log_file_path))
log_level = str.upper(args.log_level)
setup_logging(log_file_path, log_level=log_level)
# Get the data from the JSON file
config_file_path = args.config
with open(config_file_path) as json_file:
configuration = json.load(json_file)
# Begin Deployment
print(colors.GREEN + 'Deployment has begun' + colors.END)
print(f'Logs are stored in {log_file_path}')
result = builder_func(
configuration, log_file_path,
log_level, use_docker=True,
)
print(
(colors.GREEN if result else colors.RED),
'Deployment has Ended', colors.END,
)