diff --git a/deps/wazuh_testing/wazuh_testing/qa_ctl/deployment/DockerWrapper.py b/deps/wazuh_testing/wazuh_testing/qa_ctl/deployment/DockerWrapper.py new file mode 100644 index 0000000000..534ffbf8c5 --- /dev/null +++ b/deps/wazuh_testing/wazuh_testing/qa_ctl/deployment/DockerWrapper.py @@ -0,0 +1,116 @@ +import docker +from Instance import Instance +from json import dumps + + +class DockerWrapper(Instance): + """Class to handle docker operations. This class uses the docker python SDK to read a dockerfile and create + the image and container. + + Attributes: + dockerfile_path (str): Path where the Dockerfile is stored. + name (str): Container's name. + remove (bool): Remove the container after it has finished. + detach (bool): Run container in background. + ports (dict): Ports to bind inside the container. + The keys of the dictionary are the ports to bind inside the container and the values of the + dictionary are the corresponding ports to open on the host. + stdout (bool): Return stdout logs when detach is False. + stderr (bool): Return stderr logs when detach is False. + + Args: + dockerfile_path (str): Value to set dockerfile_path attribute. + name (str): Value to set name attribute. + remove (bool): Value to set remove attribute. + detach (bool): Value to set detach attribute. + ports (dict): Value to set ports attribute. + stdout (bool): Value to set stdout attribute. + stderr (bool): Value to set stderr attribute. + """ + def __init__(self, dockerfile_path, name, remove, ports=None, detach=True, stdout=False, stderr=False): + self.docker_client = docker.from_env() + self.dockerfile_path = dockerfile_path + self.name = name + self.remove = remove + self.detach = detach + self.ports = ports + + if self.detach: + self.stdout = stdout + self.stderr = stderr + else: + self.stdout = True + self.stderr = True + + self.image = self.docker_client.images.build(path=self.dockerfile_path)[0] + + def get_container(self): + """Function to get the container using the name attribute: + + Returns: + Container: Container object with the container info. + + Raises: + docker.errors.NotFound: If the container does not exist. + docker.errors.APIError: If the server returns an error. + """ + return self.docker_client.containers.get(self.name) + + def run(self): + self.docker_client.containers.run(image=self.image, name=self.name, ports=self.ports, + remove=self.remove, detach=self.detach, stdout=self.stdout, + stderr=self.stderr) + + def restart(self): + """Restart the container. + Raises: + docker.errors.APIError: If the server returns an error. + """ + self.get_container().restart() + + def halt(self): + """Stops the container. + Raises: + docker.errors.APIError: If the server returns an error. + """ + self.get_container().stop() + + def destroy(self, remove_image=False): + """Removes the container + Args: + remove_image(bool): Remove the docker image too. Defaults to False. + Raises: + docker.errors.APIError: If the server returns an error. + """ + self.get_container().remove() + if remove_image: + self.docker_client.images.remove(image=self.image.id, force=True) + + def get_instance_info(self): + """Get the parameters information. + Returns + str: String in JSON format with the parameters of the class. + """ + return dumps({'name': self.name, 'parameters': { + 'dockerfile_path': self.dockerfile_path, 'remove': self.remove, + 'detach': self.detach, 'ports': self.ports, 'stderr': self.stderr, + 'stdout': self.stdout} + }) + + def get_name(self): + """Get the name of the container. + Returns + str: String with the name of the container. + """ + return self.name + + def status(self): + """Get the status of the container. + Returns: + str: String with the status of the container (running, exited, not created, etc). + """ + try: + status = self.get_container().status + except docker.errors.NotFound: + status = 'not_created' + return status diff --git a/deps/wazuh_testing/wazuh_testing/qa_ctl/deployment/Instance.py b/deps/wazuh_testing/wazuh_testing/qa_ctl/deployment/Instance.py new file mode 100644 index 0000000000..b2ab1e0154 --- /dev/null +++ b/deps/wazuh_testing/wazuh_testing/qa_ctl/deployment/Instance.py @@ -0,0 +1,41 @@ +# Copyright (C) 2015-2021, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is free software; you can redistribute it and/or modify it under the terms of GPLv2 +from abc import ABC, abstractmethod + + +class Instance(ABC): + """Abstract class to hold common methods for instance handling""" + @abstractmethod + def run(self): + """Method to start the instance.""" + pass + + @abstractmethod + def restart(self): + """Method to restart the instance.""" + pass + + @abstractmethod + def halt(self): + """Method to stop the instance.""" + pass + + @abstractmethod + def destroy(self): + """Method to destroy the instance.""" + pass + + @abstractmethod + def get_instance_info(self): + """Method to get the instance information.""" + pass + + @abstractmethod + def get_name(self): + """Method to get the instance name.""" + pass + + @abstractmethod + def status(self): + """Method to get the instance status.""" diff --git a/deps/wazuh_testing/wazuh_testing/qa_ctl/deployment/QAInfraestructure.py b/deps/wazuh_testing/wazuh_testing/qa_ctl/deployment/QAInfraestructure.py new file mode 100644 index 0000000000..39fbd0fdb8 --- /dev/null +++ b/deps/wazuh_testing/wazuh_testing/qa_ctl/deployment/QAInfraestructure.py @@ -0,0 +1,82 @@ +# Copyright (C) 2015-2021, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is free software; you can redistribute it and/or modify it under the terms of GPLv2 +from DockerWrapper import DockerWrapper +from VagrantWrapper import VagrantWrapper + + +class QAInfraestructure: + """Class to handle multiples instances objects. + Attributes: + instances (list): List with the instances to handle. + Args: + vm_list (dict): Dictionary with the information of the instances. Must follow the format of the yaml template. + """ + instances = [] + + def __init__(self, vm_list): + for host in vm_list: + for provider in vm_list[host]['provider']: + data = vm_list[host]['provider'][provider] + if not data['enabled']: + continue + + if provider == 'vagrant': + quiet_out = True if 'quiet_out' not in data else data['quiet_out'] + vagrant_instance = VagrantWrapper(data['vagrantfile_path'], data['vagrant_box'], data['label'], + data['vm_name'], data['vm_cpu'], data['vm_memory'], + data['vm_system'], data['vm_ip'], quiet_out) + self.instances.append(vagrant_instance) + + elif provider == 'docker': + _ports = None if 'ports' not in data else data['ports'] + _detach = True if 'detach' not in data else data['detach'] + _stdout = False if 'stdout' not in data else data['stdout'] + _stderr = False if 'stderr' not in data else data['stderr'] + + docker_instance = DockerWrapper(data['dockerfile_path'], data['name'], data['remove'], + _ports, _detach, _stdout, _stderr) + self.instances.append(docker_instance) + + def run(self): + """Executes the run method on every configured instance.""" + for instance in self.instances: + instance.run() + + def halt(self): + """Executes the 'halt' method on every configured instance.""" + for instance in self.instances: + instance.halt() + + def restart(self): + """Executes the 'restart' method on every configured instance.""" + for instance in self.instances: + instance.restart() + + def destroy(self): + """Executes the 'destroy' method on every configured instance.""" + for instance in self.instances: + instance.destroy() + + def status(self): + """Executes the 'status' method on every configured instance. + + Returns: + Dictionary: Contains the status for each configured instance. + """ + status = {} + for instance in self.instances: + status[instance.get_name()] = instance.status() + + return status + + def get_instances_info(self): + """Get information about the information for all the configured instances. + Returns: + Dictionary: Dictionary with the information for each configured instance. + """ + info = {} + for instance in self.instances: + info[instance.get_name()] = instance.get_instance_info() + + return info diff --git a/deps/wazuh_testing/wazuh_testing/qa_ctl/deployment/VagrantWrapper.py b/deps/wazuh_testing/wazuh_testing/qa_ctl/deployment/VagrantWrapper.py new file mode 100644 index 0000000000..9a92dcbbab --- /dev/null +++ b/deps/wazuh_testing/wazuh_testing/qa_ctl/deployment/VagrantWrapper.py @@ -0,0 +1,102 @@ +# Copyright (C) 2015-2021, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is free software; you can redistribute it and/or modify it under the terms of GPLv2 +import Vagrantfile as vfile +import vagrant +import os +from Instance import Instance + + +class VagrantWrapper(Instance): + """Class to handle Vagrant operations. The class will use the Vagrantfile class to create a vagrantfile in + runtime. The vagrantfile will be dumped to disk only if the up method is executed. + + Attributes: + vagrantfile (Vagrantfile): Vagrantfile object containing the vagrantfile information. + vagrant (Vagrant): Vagrant object to handle vagrant operations + Args: + vagrant_root_folder (str): Root folder where the vagrant environment will be created. + vm_box (str): Name or link to the Vagrant box + vm_name (str): Name that will be assigned to the VM + vm_label (str): Label used in the vagrantfile. + vm_cpus (int): Number of CPUs assigned to the VM. + vm_memory (int): Number of RAM bytes assigned to the VM. + vm_system (str): System of the VM (Linux, Windows, Solaris, etc) + vm_ip (str): IP assigned to the VM. + quiet_out (Boolean): Flag to ignore the vagrant output. Defaults to True. + """ + + def __init__(self, vagrant_root_folder, vm_box, vm_label, vm_name, vm_cpus, vm_memory, vm_system, vm_ip, + quiet_out=True): + + box_folder = os.path.join(vagrant_root_folder, vm_name) + os.makedirs(box_folder, exist_ok=True) + + self.vagrantfile = vfile.Vagrantfile(box_folder, vm_box, vm_label, vm_name, vm_cpus, vm_memory, + vm_system, vm_ip) + + self.vagrant = vagrant.Vagrant(root=box_folder, quiet_stdout=quiet_out, quiet_stderr=False) + self.vagrantfile.write_vagrantfile() + + def run(self): + """Writes the vagrantfile and starts the VM specified in the vagrantfile.""" + self.vagrant.up() + + def halt(self): + """Stops the VM specified in the vagrantfile.""" + self.vagrant.halt() + + def restart(self): + """Restarts the VM specified in the vagrantfile.""" + self.vagrant.restart() + + def destroy(self): + """Destroys the VM specified in the vagrantfile and remove the vagrantfile.""" + self.vagrant.destroy() + self.vagrantfile.remove_vagrantfile() + + def suspend(self): + """Suspends the VM specified in the vagrantfile.""" + self.vagrant.suspend() + + def resume(self): + """Resumes the VM specified in the vagrantfile.""" + self.vagrant.resume() + + def get_vagrant_version(self): + """Gets the vagrant version of the host. + Returns: + String: Vagrant version + """ + return self.vagrant.version() + + def status(self): + """Gets the status of the VM specified in the vagrantfile. + The vagrant module returns a list of namedtuples like the following + `[Status(name='ubuntu', state='not_created', provider='virtualbox')]` + but we are only interested in the `state` field. + Returns: + Dictionary: Status of the VM. + """ + return self.vagrant.status()[0].state + + def get_ssh_config(self): + """Gets the config of the VM specified in the vagrantfile. + Returns: + Dictionary: Dictionary with the configuration of the VM. + """ + return self.vagrant.conf() + + def get_instance_info(self): + """Gets the instance info. + Returns: + Dictionary: Dictionary with the parameters of the VM. + """ + return str(self.vagrantfile) + + def get_name(self): + """Gets the name of the VM. + Returns: + String: Name of the VM. + """ + return self.vagrantfile.vm_name diff --git a/deps/wazuh_testing/wazuh_testing/qa_ctl/deployment/Vagrantfile.py b/deps/wazuh_testing/wazuh_testing/qa_ctl/deployment/Vagrantfile.py new file mode 100644 index 0000000000..ed240476ae --- /dev/null +++ b/deps/wazuh_testing/wazuh_testing/qa_ctl/deployment/Vagrantfile.py @@ -0,0 +1,85 @@ +# Copyright (C) 2015-2021, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is free software; you can redistribute it and/or modify it under the terms of GPLv2 +from pathlib import Path +import os +import json + + +class Vagrantfile(): + """Class to handle Vagrantfile creation in runtime. This class will use a template (specified in TEMPLATE_FILE + constant) to fill the `json_box` variable. This variable will have all the needed vagrant parameters in a JSON + format. + Attributes: + TEMPLATE_FILE (str): Path where the vagrantfile_template is stored. + REPLACE_PATTERN(str): Pattern to replace inside the vagrantfile_template. + file_path (str): Path where the vagrantfile will be stored. + vm_name (str): Name of the VM. + file_path (str): Path where the vagrantfile will be stored. + box_image (str): URL for the box image or Vagrant Box. + vm_label (str): Label for the VM + vm_name (str): Name of the VM. + cpus (int): Number of CPU cores for the VM. + memory (int): Memory assigned to the VM (in MB). + system (str): Type of system (/Linux, /Windows, /Solaris....). + It MUST start with '/' to assign properly the group in VirtualBox. + Args: + file_path (str): Path where the vagrantfile will be stored. + box_image (str): URL for the box image or Vagrant Box. + vm_label (str): Label for the VM + vm_name (str): Name of the VM. + cpus (int): Number of CPU cores for the VM. + memory (int): Memory assigned to the VM (in MB). + system (str): Type of system (/Linux, /Windows, /Solaris....). + It MUST start with '/' to assign properly the group in VirtualBox. + private_ip (str): IP of the VM. + """ + TEMPLATE_FILE = os.path.join( + Path(__file__).parent, 'vagrantfile_template.txt') + REPLACE_PATTERN = 'json_box = {}\n' + + def __init__(self, file_path, box_image, vm_label, vm_name, cpus, memory, system, private_ip): + self.file_path = os.path.join(file_path, 'vagrantfile') + self.vm_name = vm_name + self.box_image = box_image + self.vm_label = vm_label + self.cpus = cpus + self.memory = memory + self.system = f'/{system}' + self.private_ip = private_ip + + def __str__(self): + """To str method. It will print the dictionary in JSON format.""" + parameters = { + 'box_image': self.box_image, + 'vm_label': self.vm_label, + 'cpus': self.cpus, + 'memory': self.memory, + 'system': self.system, + 'private_ip': self.private_ip + } + return json.dumps({self.vm_name: parameters}) + + def read_vagrantfile_template(self): + """Function that will read the vagrantfile template located in self.TEMPLATEFILE constant + + Returns: + List: List with the content of the template vagrant template.""" + with open(self.TEMPLATE_FILE, 'r') as template_fd: + return template_fd.readlines() + + def write_vagrantfile(self): + """Replace the self.REPLACE_PATTERN line with a string with the parameters in JSON format and write the new + contents in self.file_path file.""" + read_lines = self.read_vagrantfile_template() + replace_line = read_lines.index(self.REPLACE_PATTERN) + read_lines[replace_line] = self.REPLACE_PATTERN.format( + f"'{self.__str__()}'") + + with open(self.file_path, 'w') as vagrantfile_fd: + vagrantfile_fd.writelines(read_lines) + + def remove_vagrantfile(self): + """Removes the file self.file_path if it exists.""" + if os.path.exists(self.file_path): + os.remove(self.file_path) diff --git a/deps/wazuh_testing/wazuh_testing/qa_ctl/deployment/__init__.py b/deps/wazuh_testing/wazuh_testing/qa_ctl/deployment/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/deps/wazuh_testing/wazuh_testing/qa_ctl/deployment/dockerfiles/ALDockerfile b/deps/wazuh_testing/wazuh_testing/qa_ctl/deployment/dockerfiles/ALDockerfile new file mode 100644 index 0000000000..9caf145359 --- /dev/null +++ b/deps/wazuh_testing/wazuh_testing/qa_ctl/deployment/dockerfiles/ALDockerfile @@ -0,0 +1,17 @@ +FROM amazonlinux:2.0.20200602.0 + +RUN yum update -y && \ + yum install -y \ + openssh-server \ + nano \ + openssl \ + sudo \ + gcc \ + git \ + python3 \ + python3-devel + +ADD entrypoint.sh /usr/bin/entrypoint.sh +RUN chmod +x /usr/bin/entrypoint.sh + +ENTRYPOINT ["/usr/bin/entrypoint.sh"] diff --git a/deps/wazuh_testing/wazuh_testing/qa_ctl/deployment/dockerfiles/UBDockerfile b/deps/wazuh_testing/wazuh_testing/qa_ctl/deployment/dockerfiles/UBDockerfile new file mode 100644 index 0000000000..e1a6a2b340 --- /dev/null +++ b/deps/wazuh_testing/wazuh_testing/qa_ctl/deployment/dockerfiles/UBDockerfile @@ -0,0 +1,27 @@ +FROM ubuntu:18.04 + +RUN apt-get update && \ + apt-get install -y \ + software-properties-common \ + openssl \ + curl \ + apt-transport-https \ + lsb-release \ + gnupg2 \ + make \ + libc6-dev \ + gcc \ + g++ \ + python \ + policycoreutils \ + automake \ + autoconf \ + libtool \ + libssl-dev \ + git \ + wget + +ADD entrypoint.sh /usr/bin/entrypoint.sh +RUN chmod +x /usr/bin/entrypoint.sh + +ENTRYPOINT ["/usr/bin/entrypoint.sh"] diff --git a/deps/wazuh_testing/wazuh_testing/qa_ctl/deployment/dockerfiles/entrypoint.sh b/deps/wazuh_testing/wazuh_testing/qa_ctl/deployment/dockerfiles/entrypoint.sh new file mode 100755 index 0000000000..1fb176615f --- /dev/null +++ b/deps/wazuh_testing/wazuh_testing/qa_ctl/deployment/dockerfiles/entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +tail -f /dev/null diff --git a/deps/wazuh_testing/wazuh_testing/qa_ctl/deployment/vagrantfile_template.txt b/deps/wazuh_testing/wazuh_testing/qa_ctl/deployment/vagrantfile_template.txt new file mode 100644 index 0000000000..da32146e5f --- /dev/null +++ b/deps/wazuh_testing/wazuh_testing/qa_ctl/deployment/vagrantfile_template.txt @@ -0,0 +1,24 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +require 'json' + +json_box = {} +box = JSON.load(json_box) + +Vagrant.configure("2") do |config| + box.each do |vm_name, vm_parameters| + config.vm.define "#{vm_name}" do |node| + node.vm.box = "#{vm_parameters['box_image']}" + node.vm.network :private_network, ip: "#{vm_parameters['private_ip']}" + node.vm.hostname = "#{vm_name}" + node.vm.provider "virtualbox" do |vb| + vb.memory = "#{vm_parameters['memory']}" + vb.cpus = "#{vm_parameters['cpus']}" + vb.name = "#{vm_name}" + vb.customize ["setextradata", :id, "VBoxInternal/Devices/VMMDev/0/Config/GetHostTimeDisabled", 1] + vb.customize ["modifyvm", "#{vm_name}", "--groups", "#{vm_parameters['system']}"] + end + end + end +end