Skip to content
Snippets Groups Projects
vagrant_generator.py 6.68 KiB
Newer Older
""" This module generates a Vagrantfile from input device definitions. """

import jinja2
from modules.file_manager import generate_file, open_yaml

VAGRANT_MAPPING = open_yaml("conf/vagrant_mapping.yml")
VIRTUALBOX_MAPPING = open_yaml("conf/virtualbox_mapping.yml")
BASE_PLAYBOOK = "base_provisioning/device_configuration.yml"
USER_PLAYBOOK = "provisioning/playbook.yml"


def _create_simple_attribute(key, value, attribute_type):
    """ Creates simple vagrant attributes like string, integer or boolean. """

    attribute = dict()
    attribute["type"] = attribute_type
    attribute["command"] = key
    attribute["value"] = value

    return attribute


def _create_commands(device_attributes, device_type, input_definitions, flags):
    """ This function creates basic vagrant definition commands for a device. """

    commands = []
    vb_commands = []

    for attribute, value in device_attributes.items():
        if attribute in VAGRANT_MAPPING["string"]:
            commands.append(_create_simple_attribute(VAGRANT_MAPPING["string"][attribute], value, "string"))
        elif attribute in VAGRANT_MAPPING["boolean"]:
            commands.append(_create_simple_attribute(VAGRANT_MAPPING["boolean"][attribute], value, "boolean"))
        elif attribute in VAGRANT_MAPPING["integer"]:
            commands.append(_create_simple_attribute(VAGRANT_MAPPING["integer"][attribute], value, "integer"))
        elif attribute in VIRTUALBOX_MAPPING["integer"]:
            vb_commands.append(_create_simple_attribute(VIRTUALBOX_MAPPING["integer"][attribute], value, "integer"))

    if vb_commands:
        vb = dict()
        vb["type"] = "provider"
        vb["name"] = "virtualbox"
        vb["commands"] = vb_commands
        commands.append(vb)
    return commands


def _create_ansible_commands(playbook_location, input_definitions, flags):
    """ Creates commands for running a playbook from the Vagrantfile. """

    commands = []
    playbook = dict()
    playbook["type"] = "string"
    playbook["command"] = "playbook"
    playbook["value"] = playbook_location
    commands.append(playbook)

    if "verbose_ansible" in flags and flags["verbose_ansible"]:
        verbosity = dict()
        verbosity["type"] = "boolean"
        verbosity["command"] = "verbose"
        verbosity["value"] = True
        commands.append(verbosity)

    groups = dict()
    groups["type"] = "dictionary"
    groups["command"] = "groups"
    groups["dictionary"] = dict()
    host_names = []
    for host in input_definitions["hosts"]:
        host_names.append(host["name"])
    groups["dictionary"]["hosts"] = host_names
    router_names = []
    for router in input_definitions["routers"]:
        router_names.append(router["name"])
    groups["dictionary"]["routers"] = router_names
    commands.append(groups)

    return commands


def _find_netmask(network_name, networks):
    """ Returns the netmask of a network address from network name. """

    for network in networks:
        if network['name'] == network_name:
            address, netmask = network['cidr'].split('/')
            return netmask


def _add_networks_to_device(definition, mappings, input_definitions):
    """ Adds networks to the vagrant definition of one device. """

    for mapping in mappings:
        if mapping[definition["type"]] == definition["name"]:
            network = dict()
            network["type"] = "network"
            network["network_type"] = "private_network"
            network["name"] = mapping["network"]
            network["ip"] = mapping["ip"]
            network["netmask"] = _find_netmask(mapping["network"], input_definitions["networks"])
            definition["commands"].append(network)


def _add_all_networks(vagrant_definitions, input_definitions, flags):
    """ Adds all networks to vagrant definitions. """

    for definition in vagrant_definitions:
        if definition["type"] == "host":
            _add_networks_to_device(definition, input_definitions["net_mappings"], input_definitions)
        elif definition["type"] == "router":
            _add_networks_to_device(definition, input_definitions["router_mappings"], input_definitions)


def _call_provisioner(input_definitions, flags):
    """ Creates entry to vagrant definitions for calling the provisioner. """
    provisioner_calls = []

    config_playbook = dict()
    config_playbook["type"] = "provision"
    if "ansible_local" in flags and flags["ansible_local"]:
        config_playbook["provisioner"] = "ansible_local"
    else:
        config_playbook["provisioner"] = "ansible"
    config_playbook["note"] = "basic configuration of devices and networks"
    config_playbook["commands"] = _create_ansible_commands(
            BASE_PLAYBOOK, input_definitions, flags)

    provisioner_calls.append(config_playbook)

    user_playbook = dict()
    user_playbook["type"] = "provision"
    if "ansible_local" in flags and flags["ansible_local"]:
        user_playbook["provisioner"] = "ansible_local"
    else:
        user_playbook["provisioner"] = "ansible"
    user_playbook["note"] = "user configuration of devices"
    user_playbook["commands"] = _create_ansible_commands(
            USER_PLAYBOOK, input_definitions, flags)
    provisioner_calls.append(user_playbook)

    return provisioner_calls


def _build_vagrant_definitions(input_definitions, flags):
    """
    Creates a definition structure that is more suitable for Vagrantfile
    generation than input definitions.
    """

    vagrant_definitions = []
    for router in input_definitions["routers"]:
        device = dict()
        device["type"] = "router"
        device["name"] = router["name"]
        device["commands"] = _create_commands(router, "router", input_definitions, flags)
        vagrant_definitions.append(device)

    for host in input_definitions["hosts"]:
        device["type"] = "host"
        device["name"] = host["name"]
        device["commands"] = _create_commands(host, "host", input_definitions, flags)
        vagrant_definitions.append(device)

    _add_all_networks(vagrant_definitions, input_definitions, flags)
    vagrant_definitions.extend(_call_provisioner(input_definitions, flags))
    return vagrant_definitions


def generate_vagrantfile(input_definitions, flags):
    """
    This method is responsible for Vagrantfile generation.

    :param input_definitions: device definitions from the input file
    :param flags: command line flags
    """

    try:
        vagrant_definitions = _build_vagrant_definitions(input_definitions, flags)
    except Exception:
       print("Could not create definitions for Vagrantfile.")
       raise

       generate_file("vagrantfile", "Vagrantfile", defs=vagrant_definitions)
    except Exception:
       print("Could not generate Vagrantfile.")
       raise