diff --git a/README.md b/README.md
index fe7a5219ca4304d0b6ebf97a98235441ef61b46b..3dd81c9beff21e75dbe61312fa2475ffefe711a8 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-# Sandbox Creator
+# Cyber Sandbox Creator
 
-Sandbox creator is a tool, which can generate portable input files for building lightweight virtual environments using Vagrant and Ansible from a simple YAML definition of topology. The combination of these three tools makes possible to build virtual machines connected with virtual networks even on a desktop computer.
+Cyber Sandbox Creator is a tool, which can generate portable input files for building lightweight virtual environments using Vagrant and Ansible from a simple YAML definition of topology. The combination of these three tools makes possible to build virtual machines connected with virtual networks even on a desktop computer.
 
 ## Installation
 
@@ -27,15 +27,19 @@ Sandbox creator is a tool, which can generate portable input files for building
 
 ### Linux (Ubuntu/Debian)
 
-After the installation simply run the command `$ python3 create.py sandbox.yml` to generate the files.
+1. After the installation simply run the command `$ python3 create.py topology.yml` to generate files.
+2. Navigate to the newly created directory `sandbox` and run `$ vagrant up` to build the virtual environment.
+3. The built environment can be deleted using the command `$ vagrant destroy -f`.
 
 ### Windows 10
 
-Generate the files using the command `python create.py -l sandbox.yml`.
+1. Generate files using the command `python create.py --ansible_local topology.yml`.
+2. Navigate to the newly created folder `sandbox` and run the command `vagrant up` to build the virtual environment.
+3. The built environment can be deleted using the command `vagrant destroy -f`.
 
 ## Credits
 
-**Cybersecurity laboratory**\
+**Cybersecurity Laboratory**\
 **Faculty of Informatics**\
 **Masaryk University**
 
@@ -48,6 +52,7 @@ Generate the files using the command `python create.py -l sandbox.yml`.
 **Contributors**:
 
 - Valdemar Švábenský
+- Kamil Andoniadis (KYPO cyber range platform)
 - Michal StanĂ­k
 - Zdeněk Vydra
 - Adam Skrášek
diff --git a/big_broker.yml b/big_broker.yml
index 72fbd9515c1966a460944966a13367ec4eb9d39a..11921f92cb21a7615aace4231f7ab89335bbb03d 100644
--- a/big_broker.yml
+++ b/big_broker.yml
@@ -14,7 +14,7 @@ hosts:
     memory: 2048
 
   - name: attacker
-    base_box: kalilinux/rolling-light
+    base_box: mu/kali-2019.4
     memory: 2048
 
   - name: client
@@ -31,7 +31,7 @@ networks:
     cidr: 172.18.1.0/24
 
   - name: internet
-    cidr: 10.1.0.0/16
+    cidr: 10.1.0.0/24
 
 net_mappings:
   - host: web
@@ -48,11 +48,11 @@ net_mappings:
 
   - host: attacker
     network: internet
-    ip: 10.1.135.83
+    ip: 10.1.0.83
 
   - host: client
     network: internet
-    ip: 10.1.17.4
+    ip: 10.1.0.4
 
 router_mappings:
   - router: big-broker-router
diff --git a/conf/border_router.py b/conf/border_router.py
new file mode 100644
index 0000000000000000000000000000000000000000..4e28f249f30ae0eb822eee683f32c70a0518d6b0
--- /dev/null
+++ b/conf/border_router.py
@@ -0,0 +1,5 @@
+BORDER_ROUTER_NAME = "br"
+BORDER_ROUTER_IP = "172.18.0.1"
+BORDER_ROUTER_PUBLIC_IP = "172.18.10.1"
+BORDER_ROUTER_NETWORK_NAME = "BR"
+BORDER_ROUTER_NETWORK_IP = "172.18.0.0/24"
diff --git a/conf/flavors.yml b/conf/flavors.yml
new file mode 100644
index 0000000000000000000000000000000000000000..55a4cebae68f2388eb40203ac99e1fed4c54b4bf
--- /dev/null
+++ b/conf/flavors.yml
@@ -0,0 +1,40 @@
+csirtmu.tiny1x2:
+    cores: 1
+    memory: 2048
+    hd: 20480
+csirtmu.tiny1x4:
+    cores: 1
+    memory: 4096
+    hd: 20480
+csirtmu.small2x4:
+    cores: 2
+    memory: 4096
+    hd: 20480
+csirtmu.small2x8:
+    cores: 2
+    memory: 8192
+    hd: 40960
+csirtmu.medium4x8:
+    cores: 4
+    memory: 8192
+    hd: 40960
+csirtmu.medium4x16:
+    cores: 4
+    memory: 16384
+    hd: 40960
+csirtmu.large8x16:
+    cores: 8
+    memory: 16384
+    hd: 81920
+csirtmu.large8x32:
+    cores: 8
+    memory: 32768
+    hd: 81920
+csirtmu.jumbo16x32:
+    cores: 16
+    memory: 32768
+    hd: 102400
+csirtmu.jumbo16x64:
+    cores: 16
+    memory: 65536
+    hd: 102400
diff --git a/conf/router_attributes.yml b/conf/router_attributes.yml
new file mode 100644
index 0000000000000000000000000000000000000000..18b02a8dc939c4dabab70a166908cf386ff1459f
--- /dev/null
+++ b/conf/router_attributes.yml
@@ -0,0 +1,3 @@
+base_box: generic/debian10
+memory: 256
+cpus: 1
diff --git a/conf/vagrant_mapping.yml b/conf/vagrant_mapping.yml
new file mode 100644
index 0000000000000000000000000000000000000000..3d7abf6e6dea10495338a6020f080532a5ce8a33
--- /dev/null
+++ b/conf/vagrant_mapping.yml
@@ -0,0 +1,75 @@
+string:
+    base_mac: vm.base_mac
+    base_address: vm.base_address
+    base_box: vm.box
+    box_download_checksum: vm.box_download_checksum
+    box_download_checksum_type: vm.box_download_checksum_type
+    box_download_client_cert: vm.box_download_client_cert
+    box_download_ca_cert: vm.box_download_ca_cert
+    box_download_ca_path: vm.box_download_ca_path
+    box_version: vm.box_version
+    communicator: vm.communicator
+    name: vm.hostname
+    post_up_message: vm.post_up_message
+    ssh_config: ssh.config
+    ssh_export_command_template: ssh.export_command_template
+    ssh_host: ssh.host
+    ssh_password: ssh.password
+    ssh_proxy_command: ssh.proxy_command
+    ssh_remote_user: ssh.remote_user
+    ssh_shell: ssh.shell 
+    ssh_sudo_command: ssh.sudo_command
+    ssh_username: ssh.username
+    winrm_username: winrm.username
+    winrm_password: winrm.password
+    winrm_host: winrm.host
+    winrm_codepage: winrm.codepage
+    winssh_proxy_command: winssh.proxy_command
+    winssh_shell: winssh.shell
+    winssh_export_command_template: winssh.export_command_template
+    winssh_sudo_command: winssh.sudo_command
+    winssh_upload_directory: winssh.upload_directory
+integer:
+    boot_timeout: vm.boot_timeout
+    graceful_halt_timeout: vm.graceful_halt_timeout
+    ssh_guest_port: ssh.guest_port
+    ssh_port: ssh.port
+    winrm_port: winrm.port
+    winrm_guest_port: winrm.guest_port
+    winrm_timeout: winrm.timeout 
+    winrm_retry_limit: winrm.retry_limit
+    winrm_retry_delay: winrm.retry_delay
+boolean:
+    box_check_update: vm.box_check_update
+    box_download_insecure: vm.box_download_insecure
+    box_download_location_trusted: vm.box_download_location_trusted
+    ignore_box_vagrantfile: vm.ignore_box_vagrantfile
+    ssh_compression: ssh.compression
+    ssh_dsa_authentication: ssh.dsa_authentication
+    ssh_forward_agent: ssh.forward_agent
+    ssh_forward_x11: ssh.forward_x11
+    ssh_insert_key: ssh.insert_key
+    ssh_keep_alive: ssh.keep_alive
+    ssh_keys_only: ssh.keys_only
+    ssh_paranoid: ssh.paranoid
+    ssh_pty: ssh.pty
+    winrm_basic_auth_only: winrm.basic_auth_only
+    winrm_ssl_peer_verification: winrm.ssl_peer_verification
+    winssh_forward_agent: winssh.forward_agent
+    winssh_keep_alive: winssh.keep_alive
+other:
+    box_url: vm.box_url
+    guest: vm.guest
+    provider: vm.provider
+    provision: vm.provision
+    synced_folder: vm.synced_folder
+    usable_port_range: vm.usable_port_range
+    ssh_extra_args: ssh.extra_args
+    ssh_forward_env: ssh.forward_env
+    ssh_private_key_path: ssh.private_key_path
+    ssh_verify_host_key: ssh.verify_host_key
+    winrm_transport: winrm.transport
+    winssh_forward_env: winssh.forward_env
+    vagrant_host: vagrant.host
+    vagrant_plugins: vagrant.plugins
+    vagrant_sensitive: vagrant.sensitive
diff --git a/conf/virtualbox_mapping.yml b/conf/virtualbox_mapping.yml
new file mode 100644
index 0000000000000000000000000000000000000000..46e9575f636dfeae1763fa2e27a9629aba2e7adf
--- /dev/null
+++ b/conf/virtualbox_mapping.yml
@@ -0,0 +1,3 @@
+integer:
+    memory: memory
+    cpus: cpus
diff --git a/create.py b/create.py
index 5ad7906f65143bf67d7be9a16a1240b1b3ae5591..19ba7678c619be82c27a558e77e40598737ca502 100644
--- a/create.py
+++ b/create.py
@@ -1,32 +1,26 @@
 #!/usr/bin/python3
-""" A script that generates a Vagrantfile from yaml. """
+"""Generates Vagrantfile from YAML definition.
 
-import sys
-import jinja2
+Generate a Vagrantfile and ansible files from a YAML definition of virtual
+machines and network topology. See the documentation for details.
+"""
 
-from modules.file_generator import generate_vagrantfile, generate_ansible_files
-from modules.device_creator import open_file
-from modules.routing import create_border_router
+from modules.ansible_generator import generate_playbooks
+from modules.vagrant_generator import generate_vagrantfile
+from modules.file_manager import open_yaml, prepare_directories
+from modules.input_argument_parser import parse_input_args
+from modules.input_file_validator import validate_device_definitions
+from modules.preprocessing import preprocess
 
-if len(sys.argv) == 3:
-    if str(sys.argv[1]) == "-l":
-        ansible_local = True
-        input_file_name = str(sys.argv[2])
-    elif str(sys.argv[2]) == "-l":
-        ansible_local = True
-        input_file_name = str(sys.argv[1])
-    else:
-        print("Error: Expecting a yml file and optionally a flag -l.")
-        sys.exit()
-elif len(sys.argv) == 2:
-    ansible_local = False
-    input_file_name = str(sys.argv[1])
-else:
-    print("Error: Expecting a yml file and optionally a flag -l.")
-    sys.exit()
 
-device_definitions = open_file(input_file_name)
-create_border_router(device_definitions)
+INPUT_FILE_NAME, FLAGS = parse_input_args()
+device_definitions = open_yaml(INPUT_FILE_NAME)
 
-generate_vagrantfile(device_definitions, ansible_local)
-generate_ansible_files(device_definitions)
+validate_device_definitions(device_definitions)
+preprocess(device_definitions, FLAGS)
+prepare_directories(device_definitions)
+
+generate_vagrantfile(device_definitions, FLAGS)
+generate_playbooks(device_definitions, FLAGS)
+
+print("Sandbox was successfully created.")
diff --git a/modules/ansible_data_generator.py b/modules/ansible_data_generator.py
deleted file mode 100644
index 99e8ee4a09a865f35f17c7eb21135506d539e373..0000000000000000000000000000000000000000
--- a/modules/ansible_data_generator.py
+++ /dev/null
@@ -1,64 +0,0 @@
-from modules.device_creator import open_file
-
-INTERFACE_FILE = "name_mapping/interface.yml"
-
-def create_network_map(definitions):
-    """ Creates a structure with network topology for Jinja2 template. """
-
-    routings = []
-
-    for router_mapping in definitions["router_mappings"]:
-        routing = dict()
-        routing["router_name"] = router_mapping["router"]
-        routing["router_network"] = router_mapping["network"]
-        routing["router_network_ip"] = _find_network_ip(router_mapping["network"], definitions)
-        routing["router_ip"] = router_mapping["ip"]
-        routings.append(routing)
-
-    return routings
-
-
-def _find_router_ip(network_name, router_mappings):
-    for router_mapping in router_mappings:
-        if router_mapping["network"] == network_name:
-            return router_mapping["ip"]
-
-def _find_network_ip(network_name, definitions):
-    for network in definitions["networks"]:
-        if network["name"] == network_name:
-            return network["cidr"]
-
-def _find_interface(host_name, hosts):
-
-    for host in hosts:
-        if host["name"] == host_name:
-            interfaces = open_file(INTERFACE_FILE)
-            if host["base_box"] in interfaces:
-                return interfaces[host["base_box"]]
-
-    return "eth1"
-
-
-def create_host_map(net_mappings, router_mappings, host_list):
-    """ Creates a structure with hosts and their primary routers ip """
-
-    hosts = []
-
-    for net_mapping in net_mappings:
-        host = dict()
-        host["host_name"] = net_mapping["host"]
-        host["host_ip"] = net_mapping["ip"]
-        host["router_ip"] = _find_router_ip(
-            net_mapping["network"], router_mappings)
-        host["interface"] = _find_interface(net_mapping["host"], host_list)
-        hosts.append(host)
-    return hosts
-
-def create_network_ips(networks):
-
-    network_ips = []
-    for network in networks:
-        if network["cidr"] not in network_ips:
-            network_ips.append(network["cidr"])
-
-    return network_ips
diff --git a/modules/ansible_generator.py b/modules/ansible_generator.py
new file mode 100644
index 0000000000000000000000000000000000000000..182456da616c828701647fa0e9273cf08360de76
--- /dev/null
+++ b/modules/ansible_generator.py
@@ -0,0 +1,59 @@
+"""Contain functions for ansible file generation."""
+
+from modules.file_manager import generate_file, copy_template_file
+from modules.ansible_vars_generator import generate_ansible_vars
+from conf.border_router import BORDER_ROUTER_NAME
+
+
+def _create_config_playbooks(input_definitions, flags):
+    """Generate playbooks and roles for basic device configuration."""
+    copy_template_file("device_configuration",
+                       "base_provisioning/device_configuration.yml")
+    if input_definitions["hosts"]:
+        copy_template_file("hosts",
+                           "base_provisioning/roles/hosts/tasks/main.yml")
+
+    if input_definitions["routers"]:
+        copy_template_file("routers",
+                           "base_provisioning/roles/routers/tasks/main.yml")
+
+    for device in input_definitions["hosts"] + input_definitions["routers"]:
+        if "border_router" in flags and flags["border_router"] and\
+           device["name"] == BORDER_ROUTER_NAME:
+            copy_template_file("br", "base_provisioning/roles/" +
+                               device["name"] + "/tasks/main.yml")
+        else:
+            copy_template_file("separate_devices", "base_provisioning/roles/" +
+                               device["name"] + "/tasks/main.yml")
+
+
+def _create_user_playbooks(input_definitions):
+    """Generate template playbooks and roles for users."""
+    copy_template_file("playbook", "provisioning/playbook.yml")
+    if input_definitions["hosts"]:
+        copy_template_file("user_hosts",
+                           "provisioning/roles/hosts/tasks/main.yml")
+    if input_definitions["routers"]:
+        copy_template_file("user_routers",
+                           "provisioning/roles/routers/tasks/main.yml")
+
+    for host in input_definitions["hosts"]:
+        generate_file("user_separate_hosts",
+                      "provisioning/roles/" + host["name"] + "/tasks/main.yml",
+                      host_name=host["name"])
+
+    for router in input_definitions["routers"]:
+        generate_file("user_separate_routers",
+                      "provisioning/roles/" + router["name"] +
+                      "/tasks/main.yml", router_name=router["name"])
+
+
+def generate_playbooks(input_definitions, flags):
+    """Generate ansible vars and playbooks.
+
+    :param input_definitions: device definitions structure
+    :param flags: command line input flags
+    """
+    generate_ansible_vars(input_definitions, flags)
+    _create_config_playbooks(input_definitions, flags)
+    _create_user_playbooks(input_definitions)
diff --git a/modules/ansible_vars_generator.py b/modules/ansible_vars_generator.py
new file mode 100644
index 0000000000000000000000000000000000000000..14957318b207c51ed54376313c8855a12098e845
--- /dev/null
+++ b/modules/ansible_vars_generator.py
@@ -0,0 +1,139 @@
+"""Contains functions for generation of ansible variables and playbooks."""
+
+from conf.border_router import BORDER_ROUTER_NETWORK_NAME
+from modules.file_manager import generate_file, dump_to_yaml
+from modules.routing_generator import add_routes
+
+
+def _find_networks_of_device(name, input_definitions):
+    """Return a list of network names in which the device have an interface."""
+    networks = []
+
+    for net_mapping in input_definitions["net_mappings"]:
+        if net_mapping["host"] == name:
+            networks.append(net_mapping["network"])
+
+    if len(networks) == 1:
+        return networks
+
+    if len(networks) > 1:
+        print("Error: Hosts can have only one interface.")
+        raise AttributeError
+
+    for router_mapping in input_definitions["router_mappings"]:
+        if router_mapping["router"] == name:
+            networks.append(router_mapping["network"])
+
+    return networks
+
+
+def _add_aliases(device_name, input_definitions, flags):
+    """Generate aliases for the given device."""
+    home_networks = _find_networks_of_device(device_name, input_definitions)
+
+    aliases = dict()
+
+    for host_mapping in input_definitions["net_mappings"]:
+        aliases[host_mapping["host"]] = host_mapping["ip"]
+        continue
+
+    for router_mapping in input_definitions["router_mappings"]:
+        if router_mapping["network"] in home_networks:
+            aliases[router_mapping["router"]] = router_mapping["ip"]
+            continue
+
+    for router in input_definitions["routers"]:
+        if router["name"] not in aliases:
+            if "border_router" not in flags:
+                print("Error: " + device_name + " has no connection to " +
+                      router["name"] + " .")
+                raise AttributeError
+            for router_mapping in input_definitions["router_mappings"]:
+                if router_mapping["router"] == router["name"] and \
+                   router_mapping["network"] == BORDER_ROUTER_NETWORK_NAME:
+                    aliases[router_mapping["router"]] = router_mapping["ip"]
+                    continue
+
+    for router in input_definitions["routers"]:
+        if router["name"] not in aliases:
+            print("Error: " + device_name + " has no connection to " +
+                  router["name"] + " .")
+            raise AttributeError
+
+    return aliases
+
+
+def _generate_device_vars(input_definitions, flags):
+    """Generate vars files for all devices separately."""
+    for target_host in input_definitions["hosts"]:
+        variables = dict()
+        variables["aliases"] = _add_aliases(target_host["name"],
+                                            input_definitions, flags)
+        variables["routes"] = add_routes(target_host["name"], "host",
+                                         input_definitions, flags)
+        dump_to_yaml(variables, "base_provisioning/roles/" +
+                     target_host["name"] + "/vars/main.yml")
+
+    for target_router in input_definitions["routers"]:
+        variables = dict()
+        variables["aliases"] = _add_aliases(target_router["name"],
+                                            input_definitions, flags)
+        variables["routes"] = add_routes(target_router["name"], "router",
+                                         input_definitions, flags)
+        dump_to_yaml(variables, "base_provisioning/roles/" +
+                     target_router["name"] + "/vars/main.yml")
+
+
+def _generate_hosts_vars(input_definitions, flags):
+    """Generate vars file for all hosts."""
+    return
+
+
+def _generate_routers_vars(input_definitions, flags):
+    """Generate vars file for all routers."""
+    return
+
+
+def _find_ip(device_name, input_definitions):
+    """Return a dictionary with all network names and ips of a device."""
+    networks = dict()
+
+    for host_mapping in input_definitions["net_mappings"]:
+        if host_mapping["host"] == device_name:
+            networks[host_mapping["network"]] = host_mapping["ip"]
+
+    for router_mapping in input_definitions["router_mappings"]:
+        if router_mapping["router"] == device_name:
+            networks[router_mapping["network"]] = router_mapping["ip"]
+
+    return networks
+
+
+def _generate_config_vars(input_definitions, flags):
+    """Generate vars file for all devices."""
+# TODO check what vars needs to be in config
+# TODO dump to yaml without jinja
+    hosts = []
+    for host in input_definitions["hosts"]:
+        new_host = dict()
+        new_host["name"] = host["name"]
+        new_host["networks"] = _find_ip(host["name"], input_definitions)
+        hosts.append(new_host)
+
+    routers = []
+    for router in input_definitions["routers"]:
+        new_router = dict()
+        new_router["name"] = router["name"]
+        new_router["networks"] = _find_ip(router["name"], input_definitions)
+        routers.append(new_router)
+
+    generate_file("config", "base_provisioning/config.yml", hosts=hosts,
+                  routers=routers)
+
+
+def generate_ansible_vars(input_definitions, flags):
+    """Generate files with variables for ansible."""
+    _generate_config_vars(input_definitions, flags)
+    _generate_hosts_vars(input_definitions, flags)
+    _generate_routers_vars(input_definitions, flags)
+    _generate_device_vars(input_definitions, flags)
diff --git a/modules/attribute_formatter.py b/modules/attribute_formatter.py
deleted file mode 100644
index 4c6b6a128d7c2c0e0b6c55202294cbf470837f0d..0000000000000000000000000000000000000000
--- a/modules/attribute_formatter.py
+++ /dev/null
@@ -1,30 +0,0 @@
-""" This module handles the creation of simple vagrant commands from yaml
-attributes. """
-
-def _format_variable_name(key, variable_type, mappings):
-    """ Formats the variable to the required vagrantfile definition. """
-    return "device." + mappings[variable_type][key] + " = "
-
-
-def _format_and_add(key, value, mappings, device_definition):
-    """ Formats and adds the definition of a simple attribute. """
-
-    if key in mappings["string"]:
-        device_definition.append(
-            _format_variable_name(key, "string", mappings)
-            + '\"' + str(value) + '\"')
-    elif key in mappings["integer"]:
-        device_definition.append(
-            _format_variable_name(key, "integer", mappings)
-            + str(value))
-    elif key in mappings["boolean"]:
-        device_definition.append(
-            _format_variable_name(key, "boolean", mappings)
-            + str(value).lower())
-
-
-def add_simple_commands(device, mappings, device_definition):
-    """ Adds definitions of string, integer and boolean vagrant attributes. """
-
-    for key, value in device.items():
-        _format_and_add(key, value, mappings, device_definition)
diff --git a/modules/border_router.py b/modules/border_router.py
new file mode 100644
index 0000000000000000000000000000000000000000..9794bb293ccf2c955a024040e8ffbeb681a382e7
--- /dev/null
+++ b/modules/border_router.py
@@ -0,0 +1,68 @@
+"""Contains functions for border router creation."""
+
+from conf.border_router import *
+
+
+def _are_br_parameters_free(definitions):
+    """Check if border router parameters are not already taken."""
+    for host in definitions["hosts"]:
+        if host["name"] == BORDER_ROUTER_NAME:
+            return False
+
+    for router in definitions["routers"]:
+        if router["name"] == BORDER_ROUTER_NAME:
+            return False
+
+    for network in definitions["networks"]:
+        if network["name"] == BORDER_ROUTER_NETWORK_NAME or \
+           network["cidr"] == BORDER_ROUTER_NETWORK_IP:
+            return False
+
+    for net_mapping in definitions["net_mappings"]:
+        if net_mapping["ip"] == BORDER_ROUTER_IP:
+            return False
+
+    for router_mapping in definitions["router_mappings"]:
+        if router_mapping["ip"] == BORDER_ROUTER_IP:
+            return False
+
+    return True
+
+
+def _create_mappings_to_border_router(definitions):
+    """Create router_mapping entries from routers to border router."""
+    for router in definitions["routers"]:
+        num = definitions["routers"].index(router) + 5
+        if num > 255:
+            raise IndexError("Too many routers.")
+
+        ip_address = BORDER_ROUTER_IP[:(0-len(str(num)))]
+        ip_address += str(num)
+
+        border_router = {"router": router["name"],
+                         "network": BORDER_ROUTER_NETWORK_NAME,
+                         "ip": ip_address}
+        definitions["router_mappings"].append(border_router)
+
+
+def create_border_router(definitions):
+    """Add the definition of border router to definitions.
+
+    :param definitions: device definition structure
+    """
+    # TODO this should be later moved to input check
+    if not _are_br_parameters_free(definitions):
+        raise ValueError("A device with the same name as border router "
+                         "already exists.")
+
+    _create_mappings_to_border_router(definitions)
+
+    definitions["routers"].insert(0, {"name": BORDER_ROUTER_NAME})
+
+    networks = {"name": BORDER_ROUTER_NETWORK_NAME,
+                "cidr": BORDER_ROUTER_NETWORK_IP}
+    definitions["networks"].insert(0, networks)
+
+    router_mappings = {"router": BORDER_ROUTER_NAME, "ip": BORDER_ROUTER_IP,
+                       "network": BORDER_ROUTER_NETWORK_NAME}
+    definitions["router_mappings"].insert(0, router_mappings)
diff --git a/modules/device_creator.py b/modules/device_creator.py
deleted file mode 100644
index 12f67b6064f2c9a0c621f1854ac0dc04bb40b170..0000000000000000000000000000000000000000
--- a/modules/device_creator.py
+++ /dev/null
@@ -1,84 +0,0 @@
-""" This package handles file import and creation of an input structure for
-Jinja2 """
-
-import yaml
-import sys
-
-from modules.attribute_formatter import add_simple_commands
-from modules.provider import add_prov_attributes, add_router_specification
-from modules.network_parser import add_networks, add_router_ip
-
-MAPPING_FILE = "name_mapping/mapping.yml"
-FLAVORS_FILE = "name_mapping/flavors.yml"
-
-
-def open_file(file_name):
-    """ Opens and returns a file from the argument. """
-    try:
-        input_file = open(str(file_name))
-        return yaml.safe_load(input_file)
-    except IOError:
-        print("Error: Cannot find a required file: " + str(file_name))
-        sys.exit(1)
-  
-def _add_provisioning(hostname, host_definitions):
-    """ Adds provisioning to the device if the file exists. """
-    try:
-        provision_file = open("provision/" + str(hostname) + ".yml")
-        host_definitions[hostname].append("device.vm.provision \"ansible\" do |ansible|")
-        host_definitions[hostname].append("  ansible.playbook = \"provision/" + hostname + ".yml\"")
-        host_definitions[hostname].append("end")
-    except IOError:
-        pass
-
-def _add_rsync(box, host_name, definitions):
-    """ add rsync to debian machines """
-
-    if box == "generic/debian10":
-        definitions[host_name].append("# standard shared folder doesn't work on debian")
-        definitions[host_name].append("device.vm.synced_folder \".\", \"/vagrant\", type: \"rsync\", rsync__exclude: \".git/\"")
-    
-    if box == "kalilinux/rolling-light":
-        definitions[host_name].append("device.ssh.password = \"vagrant\"")
-
-def _create_hosts(yml, mappings, flavors, ansible_local):
-    """ Creates a dictionary with formatted definition of each host. """
-    host_definitions = {}
-
-    for host in yml['hosts']:
-        host_definitions[host['name']] = []
-
-        add_simple_commands(host, mappings, host_definitions[host['name']])
-        add_networks(host["name"], yml, host_definitions)
-        add_prov_attributes(
-            host, flavors, mappings['need_provider'], host_definitions)
-        _add_provisioning(host["name"], host_definitions)
-        if ansible_local:
-            _add_rsync(host["base_box"], host["name"], host_definitions)
-
-    return host_definitions
-
-
-def _create_routers(yml, ansible_local):
-    """ Creates a dictionary with formatted definition of each router. """
-    router_definitions = {}
-
-    for router in yml['routers']:
-        router_definitions[router['name']] = []
-        add_router_ip(router["name"], yml, router_definitions)
-        add_router_specification(router, router_definitions, ansible_local)
-        if ansible_local:
-            _add_rsync("generic/debian10", router["name"], router_definitions)
-
-    return router_definitions
-
-
-def create_devices(definitions, ansible_local):
-    """ Returns a merged dictionary of host and router definitions. """
-
-    mappings = open_file(MAPPING_FILE)
-    flavors = open_file(FLAVORS_FILE)
-
-    return {
-        **_create_hosts(definitions, mappings, flavors, ansible_local),
-        **_create_routers(definitions, ansible_local)}
diff --git a/modules/file_generator.py b/modules/file_generator.py
deleted file mode 100644
index d73861e69095902cdff418c15719fe3962cbd528..0000000000000000000000000000000000000000
--- a/modules/file_generator.py
+++ /dev/null
@@ -1,217 +0,0 @@
-import jinja2
-import os
-
-from modules.device_creator import create_devices
-from modules.ansible_data_generator import create_network_map, create_host_map, create_network_ips
-from modules.routing import BORDER_ROUTER_IP, BORDER_ROUTER_NAME, BORDER_ROUTER_NETWORK_NAME, BORDER_ROUTER_PUBLIC_IP
-
-def _load_template(template_name):
-    """ Returns a loaded jinja2 template. """
-
-    template_loader = jinja2.FileSystemLoader(searchpath="templates")
-    template_env = jinja2.Environment(loader=template_loader, trim_blocks=True)
-    return template_env.get_template(template_name)
-
-
-def _generate_file(filename, output_string):
-    """ Generates a file from output string. """
-
-    try:
-        new_file = open(filename, "w")
-        new_file.write(output_string)
-    except IOError:
-        print("Error: cannot write to this location.")
-
-
-def _create_role_directory(role_name, provisioning_dir):
-    """ Creates directory structure for a role. """
-
-    try:
-        os.mkdir(provisioning_dir)
-    except FileExistsError:
-        pass
-    try:
-        os.mkdir(provisioning_dir + "/roles")
-    except FileExistsError:
-        pass
-    try:
-        os.mkdir(provisioning_dir + "/roles/" + role_name)
-    except FileExistsError:
-        pass
-    try:
-        os.mkdir(provisioning_dir + "/roles/" + role_name +"/tasks")
-    except FileExistsError:
-        pass
-
-
-def _find_user_ansible_files(definitions):
-    """ Finds the user ansible files and returns a list of host names. """
-    host_names = []
-
-    for host in definitions["hosts"]:
-        if os.path.isfile("provisioning/" + host["name"] + ".yml" ):
-            host_names.append(host["name"])
-
-    return host_names
-
-def generate_vagrantfile(definitions, ansible_local):
-    """ Writes the prepared output to a Vagrantfile. """
-   
-    device_definitions = create_devices(definitions, ansible_local)
-    user_ansible_files = _find_user_ansible_files(definitions)
-    template = _load_template("vagrantfile")
-    output = template.render(devices=device_definitions, user_files=user_ansible_files, ansible_local=ansible_local)
-    _generate_file("Vagrantfile", output)
-    
-    print("Info: Vagrantfile successfully created.")
-
-
-def _generate_playbook(definitions):
-    """ Generates the main playbook. """
-
-    host_map = create_host_map(definitions["net_mappings"], definitions["router_mappings"], definitions["hosts"])
-    network = create_network_map(definitions)
-    
-    template = _load_template("playbook")
-    output = template.render(hosts=host_map, routers=network)
-
-    try:
-        os.mkdir("provisioning")
-    except FileExistsError:
-        pass
-
-    _generate_file("./provisioning/playbook.yml", output)
-
-
-def _generate_device_configuration(definitions):
-    """ Generates a playbook with basic device configutarion. """
-
-    host_map = create_host_map(definitions["net_mappings"], definitions["router_mappings"], definitions["hosts"])
-    network = create_network_map(definitions) 
-    network_ips = create_network_ips(definitions["networks"])
-
-    template = _load_template("device_configuration")
-    output = template.render(hosts=host_map, routers=network, network_ips=network_ips, border_router_name = BORDER_ROUTER_NAME)
-
-    try:
-        os.mkdir("base_provisioning")
-    except FileExistsError:
-        pass
-
-    _generate_file("./base_provisioning/device_configuration.yml", output)
-
-
-def _generate_hosts_role(definitions):
-    """ Generates hosts role. """
-
-    host_map = create_host_map(definitions["net_mappings"], definitions["router_mappings"], definitions["hosts"])
-
-    network = create_network_map(definitions)
-
-    template = _load_template("hosts")
-    output = template.render(hosts=host_map, routers=network)
-
-    _create_role_directory("hosts", "base_provisioning")
-    _generate_file("./base_provisioning/roles/hosts/tasks/main.yml", output)
-    
-    user_template = _load_template("user_hosts")
-    user_output = template.render()
-
-    _create_role_directory("hosts", "provisioning")
-    _generate_file("./provisioning/roles/hosts/tasks/main.yml", output)
-
-
-def _generate_separate_hosts_role(definitions):
-    """ Generate roles for separate host devices. """
-    
-    host_map = create_host_map(definitions["net_mappings"], definitions["router_mappings"], definitions["hosts"])
-
-    for host in definitions["hosts"]:
-
-        for host_attributes in host_map:
-            if host_attributes["host_name"] == host["name"]:
-                host_name = host_attributes["host_name"]
-                router_ip = host_attributes["router_ip"]
-                interface = host_attributes["interface"]
-
-        template = _load_template("separate_hosts")
-        output = template.render(host_name=host_name, router_ip=router_ip, interface=interface)
-
-        _create_role_directory(host["name"], "base_provisioning")
-        _generate_file("./base_provisioning/roles/" + host["name"]  + "/tasks/main.yml", output)
-
-
-        template = _load_template("user_separate_hosts")
-        output = template.render(host_name=host_name)
-
-        _create_role_directory(host["name"], "provisioning")
-        _generate_file("./provisioning/roles/" + host["name"]  + "/tasks/main.yml", output)
-
-def _generate_routers_role(definitions):
-    """ Generates routers role. """
-
-    if not definitions['routers'] or not definitions['router_mappings']:
-        print("Info: No router definition was found. Skipping router creation.")
-        return
-
-    host_map = create_host_map(definitions["net_mappings"], definitions["router_mappings"], definitions["hosts"])
-
-    network = create_network_map(definitions) 
-
-    template = _load_template("routers")
-    output = template.render(hosts=host_map, routers=network, border_router_ip=BORDER_ROUTER_IP)
-
-    _create_role_directory("routers", "base_provisioning")
-    _generate_file("./base_provisioning/roles/routers/tasks/main.yml", output)
-
-
-def _find_cidr(network_name, definitions):
-    """ Finds cidr of a network from name. """
-
-    for network in definitions["networks"]:
-        if network["name"] == network_name:
-            return network["cidr"]
-
-def _get_br_routers(definitions):
-    """ Returns a list of router ips that are in the border router network. """
-
-    br_mappings = dict()
-    for router_mapping in definitions["router_mappings"]:
-        if router_mapping["network"] == BORDER_ROUTER_NETWORK_NAME:
-            for router_mapping2 in definitions["router_mappings"]:
-                if router_mapping["router"] == router_mapping2["router"] and router_mapping["network"] != router_mapping2["network"]:
-                    br_mappings[_find_cidr(router_mapping2["network"], definitions)] = router_mapping["ip"]
-
-    return br_mappings
-
-def _generate_br_role(definitions):
-    """ Generates br role. """
-
-    if not definitions['routers'] or not definitions['router_mappings']:
-        print("Info: No router definition was found. Skipping border router creation.")
-        return
-
-    network = create_network_map(definitions) 
-    
-    host_map = create_host_map(definitions["net_mappings"], definitions["router_mappings"], definitions["hosts"])
-
-    routers_in_br_network = _get_br_routers(definitions)
-
-    template = _load_template("br")
-    output = template.render(hosts = host_map, routers=network, br_routes=routers_in_br_network, border_router_name=BORDER_ROUTER_NAME, border_router_public_ip=BORDER_ROUTER_PUBLIC_IP)
-
-    _create_role_directory("br", "base_provisioning")
-    _generate_file("./base_provisioning/roles/br/tasks/main.yml", output)
-
-
-def generate_ansible_files(device_definitions):
-    """ Generates files for ansible. """
-    
-    _generate_playbook(device_definitions)
-    _generate_device_configuration(device_definitions)
-    _generate_hosts_role(device_definitions)
-    _generate_separate_hosts_role(device_definitions)
-    _generate_routers_role(device_definitions)
-    _generate_br_role(device_definitions)
-
-    print("Info: Ansible files successfully created.")
diff --git a/modules/file_manager.py b/modules/file_manager.py
new file mode 100644
index 0000000000000000000000000000000000000000..8b4a8b541ae606b60c98595a947118e8bab56aa9
--- /dev/null
+++ b/modules/file_manager.py
@@ -0,0 +1,158 @@
+"""This module handles file imports and creations in general."""
+
+import os
+import shutil
+import sys
+
+import jinja2
+import yaml
+
+OUTPUT_DIRECTORY = "sandbox"
+
+
+def open_yaml(file_name):
+    """Open and return a file from the argument."""
+    try:
+        with open(str(file_name)) as input_file:
+            return yaml.safe_load(input_file)
+    except yaml.YAMLError:
+        cleanup_and_exit("Could not parse yaml file " + str(file_name) + ".")
+    except IOError:
+        cleanup_and_exit("Could not open yaml file " + str(file_name) + ".")
+
+
+def copy_template_file(template, destination):
+    """Copy playbook templates directly to the destination."""
+    try:
+        shutil.copyfile("templates/" + template, "sandbox/" + destination)
+    except IOError:
+        cleanup_and_exit("Could not copy template file " + str(template) +
+                         ".")
+
+
+def dump_to_yaml(data, filename):
+    """Write a data structure to a YAML document.
+
+    :param data: a dict or list which should be written to file
+    :param filename: name of the target file
+    """
+    file_path = OUTPUT_DIRECTORY + "/" + str(filename)
+
+    try:
+        with open(file_path, 'w') as stream:
+            yaml.dump(data, stream)
+    except IOError:
+        cleanup_and_exit("Could not create yaml file " + file_path + ".")
+
+
+def generate_file(template, filename, **template_args):
+    """Generate a file using a template.
+
+    :param template: name of the template file
+    :param filename: path to the output file inside the output directory
+    :param template_args: arbitrary number of named args for the template
+    """
+    inventory_template = _load_template(template)
+    output = inventory_template.render(**template_args)
+    _write_to_file(filename, output)
+
+
+def _write_to_file(filename, output_string):
+    """Generate a file from output string."""
+    try:
+        new_file = open(OUTPUT_DIRECTORY + "/" + filename, "w")
+        new_file.write(output_string)
+    except IOError:
+        cleanup_and_exit("Could not create file " + str(filename) + ".")
+
+
+def _load_template(template_name):
+    """Return a loaded jinja2 template."""
+    try:
+        template_loader = jinja2.FileSystemLoader(searchpath="templates")
+        template_env = jinja2.Environment(loader=template_loader,
+                                          trim_blocks=True,
+                                          lstrip_blocks=True)
+        return template_env.get_template(template_name)
+    except jinja2.TemplateNotFound:
+        cleanup_and_exit("Could not find template " + str(template_name) + ".")
+
+
+def _copy_directory(source, destination):
+    """Copy directory recursively form templates dir to the destination."""
+    try:
+        shutil.copytree("./templates/" + source, destination)
+    except OSError:
+        cleanup_and_exit("Could not copy directory " + str(source) + ".")
+
+
+def _create_provisioning_directories(directory, device_definitions):
+    """Create subdirectories for privisioning."""
+    try:
+        os.mkdir(OUTPUT_DIRECTORY + "/" + directory)
+        os.mkdir(OUTPUT_DIRECTORY + "/" + directory + "/roles")
+
+        if device_definitions["hosts"]:
+            os.mkdir(OUTPUT_DIRECTORY + "/" + directory + "/roles/hosts")
+            os.mkdir(OUTPUT_DIRECTORY + "/" + directory + "/roles/hosts/tasks")
+            os.mkdir(OUTPUT_DIRECTORY + "/" + directory + "/roles/hosts/vars")
+        for host in device_definitions["hosts"]:
+            os.mkdir(OUTPUT_DIRECTORY + "/" + directory + "/roles/" +
+                     host["name"])
+            os.mkdir(OUTPUT_DIRECTORY + "/" + directory + "/roles/" +
+                     host["name"] + "/tasks")
+            os.mkdir(OUTPUT_DIRECTORY + "/" + directory + "/roles/" +
+                     host["name"] + "/vars")
+
+        if device_definitions["routers"]:
+            os.mkdir(OUTPUT_DIRECTORY + "/" + directory + "/roles/routers")
+            os.mkdir(OUTPUT_DIRECTORY + "/" + directory +
+                     "/roles/routers/tasks")
+            os.mkdir(OUTPUT_DIRECTORY + "/" + directory +
+                     "/roles/routers/vars")
+        for router in device_definitions["routers"]:
+            os.mkdir(OUTPUT_DIRECTORY + "/" + directory + "/roles/" +
+                     router["name"])
+            os.mkdir(OUTPUT_DIRECTORY + "/" + directory + "/roles/" +
+                     router["name"] + "/tasks")
+            os.mkdir(OUTPUT_DIRECTORY + "/" + directory + "/roles/" +
+                     router["name"] + "/vars")
+
+    except FileExistsError:
+        pass
+    except IOError:
+        cleanup_and_exit("Could not create directories for provisioning.")
+
+
+def prepare_directories(device_definitions):
+    """Prepare the necessary directory structure."""
+    _remove_sandbox()
+
+    try:
+        os.mkdir(OUTPUT_DIRECTORY)
+    except IOError:
+        cleanup_and_exit("Could not create directory ./" + OUTPUT_DIRECTORY +
+                         ".")
+
+    _create_provisioning_directories("base_provisioning", device_definitions)
+    _create_provisioning_directories("provisioning", device_definitions)
+
+    _copy_directory("interface", OUTPUT_DIRECTORY +
+                    "/base_provisioning/roles/interface")
+    _copy_directory("common", OUTPUT_DIRECTORY +
+                    "/base_provisioning/roles/common")
+
+
+def _remove_sandbox():
+    """Remove the existing sandbox."""
+    shutil.rmtree(OUTPUT_DIRECTORY, True)
+
+
+def cleanup_and_exit(error):
+    """Cleanup function that is called in case of an error."""
+    _remove_sandbox()
+
+    print("Sandbox creation was NOT successful:")
+    print("Error: ", error)
+
+    sys.exit(1)
diff --git a/modules/input_argument_parser.py b/modules/input_argument_parser.py
new file mode 100644
index 0000000000000000000000000000000000000000..7d5c43fc97921f8aca9fa94b1191afd9d584b426
--- /dev/null
+++ b/modules/input_argument_parser.py
@@ -0,0 +1,34 @@
+"""This module contains functions to parse input arguments."""
+
+import argparse
+
+
+def parse_input_args():
+    """Parse the given input arguments for input file name and flags.
+
+    :returns: name of the input file and flags
+    """
+
+    input_file_name = None
+    flags = dict()
+    parser = argparse.ArgumentParser()
+
+    parser.add_argument("definition_file",
+                        help="path to the input yaml definition")
+    parser.add_argument("--ansible_local",
+                        help="uses ansible_local for provisioning instead "
+                        "of ansible", action="store_true")
+    parser.add_argument("--verbose_ansible",
+                        help="sets verbose output for ansible (-vv)",
+                        action="store_true")
+    parser.add_argument("--border_router",
+                        help="creates a border router with connection to "
+                        "all routers", action="store_true")
+    args = parser.parse_args()
+
+    input_file_name = args.definition_file
+    flags["ansible_local"] = args.ansible_local
+    flags["verbose_ansible"] = args.verbose_ansible
+    flags["border_router"] = args.border_router
+
+    return input_file_name, flags
diff --git a/modules/input_file_validator.py b/modules/input_file_validator.py
new file mode 100644
index 0000000000000000000000000000000000000000..8faf8328fc15ea334f8bafb7ba49615c428a584e
--- /dev/null
+++ b/modules/input_file_validator.py
@@ -0,0 +1,10 @@
+"""This module contains functions for input file validation."""
+
+
+def validate_device_definitions(definitions):
+    """Validate the device definitions structure.
+
+    Throw an error if the syntax is not as expected.
+    :param definitions: a device definition structure (dictionary)
+    """
+    # TODO implement validation
diff --git a/modules/network_parser.py b/modules/network_parser.py
deleted file mode 100644
index a7224025003e77dd926ce0809e41df667cf94817..0000000000000000000000000000000000000000
--- a/modules/network_parser.py
+++ /dev/null
@@ -1,83 +0,0 @@
-""" This module handles network creation. """
-
-import sys
-from modules.routing import BORDER_ROUTER_NAME, BORDER_ROUTER_PUBLIC_IP
-
-
-def _find_networks(hostname, mappings, device_type):
-    """ Matches the device to networks. Returns a list of maiching networks. """
-    network_list = []
-
-    for mapping in mappings:
-        if device_type in ('host', 'router'):
-            if mapping[device_type] and mapping[device_type] == hostname:
-                network_list.append(mapping)
-
-    return network_list
-
-def _add_ip(hostname, mappings, device_type, definitions):
-    """ Adds a formatted ip address and network name to device definition. """
-
-    networks = _find_networks(hostname, mappings, device_type)
-
-    for network in networks:
-        if not network["ip"]:
-            print("Cannot find network mapping.")
-            sys.exit()
-
-        definitions[hostname].append(
-            "device.vm.network :private_network, ip: \""
-            + network["ip"] + '\", virtualbox__intnet: ' + network["network"])
-
-
-
-
-def _add_netmask(hostname, my_network, networks, definitions):
-    """ Adds netmask to the end of a formatted ip definition. """
-
-    for network in networks:
-        if network['name'] == my_network:
-            address, mask = network['cidr'].split('/')
-            definitions[hostname][-1] += (', netmask: \"' + mask + "\"")
-
-
-def _add_interfaces(hostname, mapping, device_type, networks, definitions):
-    """ Adds all network interfaces to a device. """
-
-    if not mapping["ip"]:
-        print("Cannot find network mapping.")
-        sys.exit()
-
-    definitions[hostname].append(
-        "device.vm.network :private_network, ip: \"" + mapping["ip"]
-        + "\", virtualbox__intnet: \"" + mapping["network"] + "\"")
-    
-    if hostname == BORDER_ROUTER_NAME:
-        definitions[BORDER_ROUTER_NAME].append("device.vm.network :public_network, ip: \" " + BORDER_ROUTER_PUBLIC_IP + "\"") 
-
-    _add_netmask(hostname, mapping["network"], networks, definitions)
-
-def add_networks(hostname, yml, definitions):
-    """ Adds ip address and natmask to a host. """
-
-    if not yml['net_mappings']:
-        return
-
-    for mapping in yml['net_mappings']:
-        if mapping['host'] == hostname:
-            _add_interfaces(
-                hostname, mapping,
-                'host', yml['networks'], definitions)
-
-
-def add_router_ip(routername, yml, definitions):
-    """ Adds ip address to a router. """
-
-    if not yml['router_mappings']:
-        return
-
-    for mapping in yml['router_mappings']:
-        if mapping['router'] == routername:
-            _add_interfaces(
-                routername, mapping,
-                'router', yml['networks'], definitions)
diff --git a/modules/preprocessing.py b/modules/preprocessing.py
new file mode 100644
index 0000000000000000000000000000000000000000..a1c8d4764097ca661f43ca0a2059f8a5d2c8acd0
--- /dev/null
+++ b/modules/preprocessing.py
@@ -0,0 +1,101 @@
+"""This module contains functions for preprocessing.
+
+They are supposed to be called after validating the input but before device
+creation.
+"""
+
+import itertools
+
+from modules.border_router import create_border_router
+from modules.file_manager import open_yaml, cleanup_and_exit
+
+FLAVORS = open_yaml("conf/flavors.yml")
+ROUTER_ATTRIBUTES = open_yaml("conf/router_attributes.yml")
+
+
+def _add_missing_tags(definitions):
+    """Add necessary structures to the input if they are missing."""
+    if "routers" not in definitions:
+        definitions["routers"] = []
+    if "router_mappings" not in definitions:
+        definitions["router_mappings"] = []
+    if "hosts" not in definitions:
+        definitions["hosts"] = []
+    if "net_mappings" not in definitions:
+        definitions["net_mappings"] = []
+    if "networks" not in definitions:
+        definitions["networks"] = []
+
+
+def _configure_routers(definitions):
+    """Add predefined parameters to all routers.
+
+    Adds them if they are not defined in the source yaml.
+    """
+    for router in definitions["routers"]:
+        for parameter, value in ROUTER_ATTRIBUTES.items():
+            if parameter not in router:
+                router[parameter] = value
+
+
+def _add_extra_arguments(definitions, flags):
+    """Add extra arguments to definitions if they are not present."""
+    if "border_router" in flags and flags["ansible_local"]:
+        for device in itertools.chain(definitions["hosts"],
+                                      definitions["routers"]):
+            if "synced_folder" not in device:
+                device["synced_folder"] = "\".\", \"/vagrant\", type: \"rsync"\
+                                          "\", rsync__exclude: \".git/\""
+
+
+def _add_flavors(definitions):
+    """Change flavor attribute to cpus and memory."""
+    for host in definitions["hosts"]:
+        if "flavor" in host:
+            if host["flavor"] not in FLAVORS:
+                print("Error: Not supported flavor: " + host["flavor"])
+                raise AttributeError
+            if "memory" not in host:
+                host["memory"] = FLAVORS[host["flavor"]]["memory"]
+            if "cpus" not in host:
+                host["cpus"] = FLAVORS[host["flavor"]]["cores"]
+            host.pop("flavor")
+
+
+def preprocess(definitions, flags):
+    """Run preprocessing.
+
+    Handles perations that need to be done before the actual device creation.
+    :param definitions: device definition structure
+    :param flags: a structure with command line flags
+    """
+    try:
+        _add_missing_tags(definitions)
+    except Exception:
+        cleanup_and_exit("Preprocessing not successful: "
+                         "Could not add missing tags.")
+
+    try:
+        if "border_router" in flags and flags["border_router"]:
+            create_border_router(definitions)
+    except (ValueError, IndexError) as error:
+        cleanup_and_exit("Preprocessing not successful: "
+                         "Could not create border router (" + error + ")")
+
+    try:
+        _configure_routers(definitions)
+    except Exception:
+        cleanup_and_exit("Preprocessing not successful: "
+                         "Could not add router configurations to definitions.")
+
+    try:
+        _add_extra_arguments(definitions, flags)
+    except Exception:
+        cleanup_and_exit("Preprocessing not successful: "
+                         "Could not add extra arguments to definitions.")
+
+    try:
+        _add_flavors(definitions)
+    except Exception:
+        cleanup_and_exit("Preprocessing not successful: "
+                         "Could not add flavors.")
diff --git a/modules/provider.py b/modules/provider.py
deleted file mode 100644
index f3279ce8ef0d39b919ee58b1d5746b2d2e39b0a5..0000000000000000000000000000000000000000
--- a/modules/provider.py
+++ /dev/null
@@ -1,67 +0,0 @@
-""" This module handles VirtualBox attributes. """
-
-
-def _print_flavor(host, flavors, provider_attributes, definitions):
-    """ Formats and add a flavor for a device. """
-
-    if 'memory' not in host:
-        definitions[host['name']].append(
-            '  vb.' + provider_attributes['memory'] + ' = '
-            + str(flavors[host['flavor']]['memory']))
-    if 'cpus' not in host:
-        definitions[host['name']].append(
-            '  vb.' + provider_attributes['cpus'] + ' = '
-            + str(flavors[host['flavor']]['cores']))
-
-
-def _add_params(host, flavors, provider_attributes, definitions):
-    """ Formats and adds simple provision attributes. """
-
-    if 'memory' in host:
-        definitions[host['name']].append(
-            '  vb.' + provider_attributes['memory'] + ' = '
-            + str(host['memory']))
-    if 'cpus' in host:
-        definitions[host['name']].append(
-            '  vb.' + provider_attributes['cpus'] + ' = '
-            + str(host['cpus']))
-    if 'flavor' in host and host['flavor'] in flavors:
-        _print_flavor(host, flavors, provider_attributes, definitions)
-
-
-def _need_provider(host, provider_attributes):
-    """ Checks if provision attributes are present. """
-
-    for attribute in provider_attributes:
-        if attribute in host:
-            return True
-    return False
-
-
-def add_prov_attributes(host, flavors, provider_attributes, definitions):
-    """ Adds provider attributes. """
-
-    if _need_provider(host, provider_attributes):
-        definitions[host['name']].append(
-            "device.vm.provider \"virtualbox\" do |vb|")
-        _add_params(host, flavors, provider_attributes, definitions)
-        definitions[host['name']].append("end")
-
-
-def add_router_specification(router, definitions, ansible_local):
-    """ Adds the default specification for a router. """
-
-    router_box = "generic/debian10"
-    router_memory = 256
-    router_cpus = 1
-
-    definitions[router['name']].append(
-        "device.vm.hostname = \"" + router['name'] + "\"")
-    definitions[router['name']].append(
-        "device.vm.box = \"" + router_box + "\"")
-    definitions[router['name']].append(
-        "device.vm.provider \"virtualbox\" do |vb|")
-    definitions[router['name']].append("  vb.memory = " + str(router_memory))
-    definitions[router['name']].append(
-        "  vb.cpus = " + str(router_cpus))
-    definitions[router['name']].append("end")
diff --git a/modules/routing.py b/modules/routing.py
deleted file mode 100644
index f1b556f0b45f4292075b1c8c56ffe352f2f80ca0..0000000000000000000000000000000000000000
--- a/modules/routing.py
+++ /dev/null
@@ -1,73 +0,0 @@
-import sys
-
-BORDER_ROUTER_NAME = "br"
-BORDER_ROUTER_IP = "172.18.0.1"
-BORDER_ROUTER_PUBLIC_IP = "172.18.10.1"
-BORDER_ROUTER_NETWORK_NAME = "BR"
-BORDER_ROUTER_NETWORK_IP = "172.18.0.0/24"
-
-def _are_br_parameters_free(definitions):
-    """ Checks if border router parameters are not already taken. """
-
-    for host in definitions["hosts"]:
-        if host["name"] == BORDER_ROUTER_NAME:
-            return False
-
-    for router in definitions["routers"]:
-        if router["name"] == BORDER_ROUTER_NAME:
-            return False
-    
-    for network in definitions["networks"]:
-        if network["name"] == BORDER_ROUTER_NETWORK_NAME or network["cidr"] == BORDER_ROUTER_NETWORK_IP:
-            return False
-
-    for net_mapping in definitions["net_mappings"]:
-        if net_mapping["ip"] == BORDER_ROUTER_IP:
-            return False
-
-
-    for router_mapping in definitions["router_mappings"]:
-        if router_mapping["ip"] == BORDER_ROUTER_IP:
-            return False
-
-    return True
-
-
-def _create_mappings_to_border_router(definitions):
-    """ Creates router_mapping entries from routers to border router. """
-
-    for router in definitions["routers"]:
-        num = definitions["routers"].index(router) + 5
-        if num > 255:
-            print("Error: too many routers.")
-            sys.exit(1)
-
-        ip = BORDER_ROUTER_IP[:(0-len(str(num)))]
-        ip += str(num)
-
-        definitions["router_mappings"].append({"router":router["name"]
-                                              ,"network":BORDER_ROUTER_NETWORK_NAME
-                                              ,"ip":ip}) 
-        
-
-def create_border_router(definitions):
-    """ Adds the definition of border router to definitions """
-
-    # TODO this should be later moved to input check
-    if not _are_br_parameters_free:
-        print("Error: Device parameter conflict.")
-
-    """ Last number in the ip of routers in border network. """
-    router_n = 5
-
-    _create_mappings_to_border_router(definitions)
-        
-
-    definitions["routers"].append({"name":BORDER_ROUTER_NAME })
-    definitions["networks"].append({"name":BORDER_ROUTER_NETWORK_NAME
-                                    ,"cidr":BORDER_ROUTER_NETWORK_IP})
-    definitions["router_mappings"].append({"router":BORDER_ROUTER_NAME
-                                          ,"network":BORDER_ROUTER_NETWORK_NAME
-                                          ,"ip":BORDER_ROUTER_IP} )
-
-
diff --git a/modules/routing_generator.py b/modules/routing_generator.py
new file mode 100644
index 0000000000000000000000000000000000000000..58f528ebf8fc50e7df676bf1b8a66880750950b3
--- /dev/null
+++ b/modules/routing_generator.py
@@ -0,0 +1,225 @@
+"""Contains generation of vars for routing of different types of devices."""
+
+from conf.border_router import BORDER_ROUTER_NAME, BORDER_ROUTER_NETWORK_NAME,\
+                               BORDER_ROUTER_IP, BORDER_ROUTER_NETWORK_IP
+
+
+def _find_router_in_network(network_name, input_definitions):
+    """Find a router in a network and return its ip."""
+    for router_mapping in input_definitions["router_mappings"]:
+        if router_mapping["network"] == network_name:
+            return router_mapping["ip"]
+    return None
+
+
+def _find_networks_of_device(device_name, input_definitions):
+    """Return the names of all networks a device is in."""
+    networks = []
+
+    for mapping in input_definitions["router_mappings"]:
+        if mapping["router"] == device_name:
+            networks.append(mapping["network"])
+    if networks:
+        return networks
+
+    for mapping in input_definitions["net_mappings"]:
+        if mapping["host"] == device_name:
+            networks.append(mapping["network"])
+
+    return networks
+
+
+def _find_iface_ip_in_network(device_name, network, input_definitions):
+    """Return the ip of the device interface inside a network."""
+    for router_mapping in input_definitions["router_mappings"]:
+        if router_mapping["router"] == device_name and \
+           router_mapping["network"] == network:
+            return router_mapping["ip"]
+
+    for net_mapping in input_definitions["net_mappings"]:
+        if net_mapping["host"] == device_name and \
+           net_mapping["network"] == network:
+            return net_mapping["ip"]
+
+    print("Error: Could not find an interface for the device \"" +
+          device_name + "\" in the network \"" + network + "\".")
+    raise AttributeError
+
+
+def _find_router_ip_in_br_network(other_network, input_definitions):
+    """Find a router inside of a network and returns its ip in br network."""
+    router = None
+
+    for router_mapping in input_definitions["router_mappings"]:
+        if router_mapping["network"] == other_network:
+            router = router_mapping["router"]
+
+    if not router:
+        return None
+
+    for router_mapping in input_definitions["router_mappings"]:
+        if router_mapping["router"] == router and\
+           router_mapping["network"] == BORDER_ROUTER_NETWORK_NAME:
+            return router_mapping["ip"]
+
+
+def _find_netmask(ip, input_definitions):
+    """Find netmask to an ip."""
+    network_name = None
+    for mapping in [*input_definitions["net_mappings"],\
+                    *input_definitions["router_mappings"]]:
+        if mapping["ip"] == ip:
+            network_name = mapping["network"]
+            break
+
+    if not network_name:
+        raise AttributeError("ip " + ip + " was not found.")
+
+    for network in input_definitions["networks"]:
+        if network["name"] == network_name:
+            net_ip, netmask = network["cidr"].split("/")
+            return netmask
+
+
+def _add_interface_route(route, target_interface_ip, interface_mask,
+                         target_routes_list):
+    """Add route to list of routes with the given interface."""
+    for interface in target_routes_list:
+        if interface["interface_ip"] == target_interface_ip:
+            interface["interface_routes"].append(route)
+            return
+
+    new_interface = dict()
+    new_interface["interface_ip"] = target_interface_ip
+    new_interface["interface_netmask"] = interface_mask
+    new_interface["interface_default_gateway"] = ""
+    new_interface["interface_routes"] = [route]
+    target_routes_list.append(new_interface)
+
+
+def _add_default_route(default_gateway, target_interface_ip, interface_mask,
+                       target_routes_list):
+    """Add default route to the given interface."""
+    for interface in target_routes_list:
+        if interface["interface_ip"] == target_interface_ip:
+            interface["interface_default_gateway"] = default_gateway
+            return
+
+    new_interface = dict()
+    new_interface["interface_ip"] = target_interface_ip
+    new_interface["interface_netmask"] = interface_mask
+    new_interface["interface_default_gateway"] = default_gateway
+    new_interface["interface_routes"] = []
+    target_routes_list.append(new_interface)
+
+
+def _configure_auto_on_ansible_interface(target_routes_list,
+                                         is_border_router=False):
+    """Call interfaces role on the main interface to set auto."""
+    new_interface = dict()
+    new_interface["interface_ip"] = "{{ ansible_default_ipv4.address  | " \
+                                    "default(ansible_all_ipv4_addresses[0]) }}"
+    new_interface["interface_netmask"] = "{{ ansible_default_ipv4.netmask  | " \
+                                         "default('24') }}"
+    if is_border_router:
+        new_interface["interface_default_gateway"] =\
+            "{{ ansible_default_ipv4.gateway }}"
+    target_routes_list.insert(0, new_interface)
+
+
+def _create_host_routing(target_host_name, input_definitions, flags):
+    """Generate list of routes for the given host."""
+    routes = []
+    if "border_router" in flags and flags["border_router"]:
+        _configure_auto_on_ansible_interface(routes)
+    mapping = None
+
+    for host_mapping in input_definitions["net_mappings"]:
+        if host_mapping["host"] == target_host_name:
+            mapping = host_mapping
+            break
+
+    if not mapping:
+        raise ValueError("Host was not found: " + target_host_name)
+
+    if flags["border_router"]:
+        gateway = _find_router_in_network(mapping["network"], input_definitions)
+        if_netmask = _find_netmask(mapping["ip"], input_definitions)
+        _add_default_route(gateway, mapping["ip"], if_netmask, routes)
+    else:
+        for network in input_definitions["networks"]:
+            if network["name"] == mapping["network"]:
+                continue
+            routing_to_other_hosts = dict()
+            gateway = _find_router_in_network(mapping["network"],
+                                              input_definitions)
+            routing_to_other_hosts["gateway"] = gateway
+            net_ip, mask = network["cidr"].split('/')
+            routing_to_other_hosts["network"] = net_ip
+            routing_to_other_hosts["mask"] = mask
+            if_mask = _find_netmask(mapping["ip"], input_definitions)
+            _add_interface_route(routing_to_other_hosts, mapping["ip"], if_mask,
+                                 routes)
+
+    return routes
+
+
+def _create_router_routing(router_name, input_definitions, flags):
+    """Generate list of routes for the given router."""
+    routes = []
+    if "border_router" in flags and flags["border_router"]:
+        _configure_auto_on_ansible_interface(routes)
+
+    if flags["border_router"]:
+        interface_ip = _find_iface_ip_in_network(router_name,
+                                                 BORDER_ROUTER_NETWORK_NAME,
+                                                 input_definitions)
+        net_ip, mask = BORDER_ROUTER_NETWORK_IP.split("/")
+        _add_default_route(BORDER_ROUTER_IP, interface_ip, mask, routes)
+
+    return routes
+
+
+def _create_border_router_routing(input_definitions):
+    """Generate routes for the border router."""
+    routes = []
+    _configure_auto_on_ansible_interface(routes, True)
+
+    for network in input_definitions["networks"]:
+        if network["name"] == BORDER_ROUTER_NETWORK_NAME:
+            continue
+        routing_to_hosts = dict()
+        net_ip, mask = network["cidr"].split('/')
+        routing_to_hosts["network"] = net_ip
+        routing_to_hosts["mask"] = mask
+        gateway = _find_router_ip_in_br_network(network["name"],
+                                                input_definitions)
+        routing_to_hosts["gateway"] = gateway
+        net_ip, if_mask = BORDER_ROUTER_NETWORK_IP.split("/")
+        _add_interface_route(routing_to_hosts, BORDER_ROUTER_IP, if_mask,
+                             routes)
+
+    return routes
+
+
+def add_routes(device_name, device_type, input_definitions, flags):
+    """Generate simple and default routes for the given device.
+
+    Returns a list of dicts with the syntax:
+    - gateway: ip of the target device
+      interface_ip: ip of the device on the given interface
+      network: ip of the network (if default is False)
+      mask: mask of the network (if default is False)
+    """
+    if not input_definitions["routers"]:
+        return []
+
+    if device_type == "host":
+        return _create_host_routing(device_name, input_definitions, flags)
+    elif device_type == "router":
+        if device_name != BORDER_ROUTER_NAME:
+            return _create_router_routing(device_name, input_definitions, flags)
+        else:
+            return _create_border_router_routing(input_definitions)
+    else:
+        raise KeyError("Unsupported device type: " + str(device_type))
diff --git a/modules/vagrant_generator.py b/modules/vagrant_generator.py
new file mode 100644
index 0000000000000000000000000000000000000000..44dc52d5a2505339826d4599616d9e3a1e82b9d7
--- /dev/null
+++ b/modules/vagrant_generator.py
@@ -0,0 +1,224 @@
+"""Contains functions for generating a Vagrantfile from input definitions."""
+
+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):
+    """Create simple vagrant attributes like string, integer or boolean."""
+    attribute = dict()
+    attribute["type"] = attribute_type
+    attribute["command"] = key
+    attribute["value"] = value
+
+    return attribute
+
+
+def _create_complex_attribute(key, value):
+    """Create complex vagrant attributes that are not string, int or bool."""
+    separators = {VAGRANT_MAPPING["other"]["synced_folder"]: ""}
+
+    attribute = dict()
+    attribute["type"] = "other"
+    attribute["command"] = key
+    attribute["separator"] = separators[key]
+    attribute["value"] = value
+
+    return attribute
+
+
+def _create_commands(device_attributes):
+    """Create basic vagrant definition commands for a device."""
+    commands = []
+    vb_commands = []
+
+    for attribute, value in device_attributes.items():
+        if attribute in VAGRANT_MAPPING["string"]:
+            vagrant_attribute = VAGRANT_MAPPING["string"][attribute]
+            commands.append(_create_simple_attribute(vagrant_attribute, value,
+                                                     "string"))
+        elif attribute in VAGRANT_MAPPING["boolean"]:
+            vagrant_attribute = VAGRANT_MAPPING["boolean"][attribute]
+            commands.append(_create_simple_attribute(vagrant_attribute, value,
+                                                     "boolean"))
+        elif attribute in VAGRANT_MAPPING["integer"]:
+            vagrant_attribute = VAGRANT_MAPPING["integer"][attribute]
+            commands.append(_create_simple_attribute(vagrant_attribute, value,
+                                                     "integer"))
+        elif attribute in VAGRANT_MAPPING["other"]:
+            vagrant_attribute = VAGRANT_MAPPING["other"][attribute]
+            commands.append(_create_complex_attribute(vagrant_attribute,
+                                                      value))
+        elif attribute in VIRTUALBOX_MAPPING["integer"]:
+            vagrant_attribute = VIRTUALBOX_MAPPING["integer"][attribute]
+            vb_commands.append(_create_simple_attribute(vagrant_attribute,
+                                                        value, "integer"))
+
+    if vb_commands:
+        virtual_box_command = dict()
+        virtual_box_command["type"] = "provider"
+        virtual_box_command["name"] = "virtualbox"
+        virtual_box_command["commands"] = vb_commands
+        commands.append(virtual_box_command)
+
+    return commands
+
+
+def _create_ansible_commands(playbook_location, input_definitions, flags):
+    """Create 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"] = "string"
+        verbosity["command"] = "verbose"
+        verbosity["value"] = "vv"
+        commands.append(verbosity)
+
+    groups = dict()
+    groups["type"] = "groups"
+    groups["groups"] = dict()
+    host_names = []
+    for host in input_definitions["hosts"]:
+        host_names.append(host["name"])
+    groups["groups"]["hosts"] = host_names
+    router_names = []
+    for router in input_definitions["routers"]:
+        router_names.append(router["name"])
+    groups["groups"]["routers"] = router_names
+    commands.append(groups)
+
+    if "ansible_local" in flags and flags["ansible_local"]:
+        install_mode = dict()
+        install_mode["type"] = "string"
+        install_mode["command"] = "install_mode"
+        install_mode["value"] = "pip"
+        commands.append(install_mode)
+
+        extravars = dict()
+        extravars["type"] = "dictionary"
+        extravars["command"] = "extra_vars"
+        extravars["dictionary"] = dict()
+        extravars["dictionary"]["ansible_python_interpreter"] = \
+            "\"/usr/bin/python3\""
+        commands.append(extravars)
+
+    return commands
+
+
+def _find_netmask(network_name, networks):
+    """Return 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
+    return None
+
+
+def _add_networks_to_device(definition, mappings, input_definitions):
+    """Add 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):
+    """Add 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):
+    """Create 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):
+    """Create a definition structure for vagrant.
+
+    This structure 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)
+        vagrant_definitions.append(device)
+
+    for host in input_definitions["hosts"]:
+        device = dict()
+        device["type"] = "host"
+        device["name"] = host["name"]
+        device["commands"] = _create_commands(host)
+        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):
+    """Generate the Vagrantfile.
+
+    :param input_definitions: device definitions from the input file
+    :param flags: command line flags
+    """
+    vagrant_definitions = _build_vagrant_definitions(input_definitions, flags)
+
+    generate_file("vagrantfile", "Vagrantfile", defs=vagrant_definitions)
diff --git a/templates/br b/templates/br
index b3ce313ce718fbe9498d7b7a5e1d991af9d70067..c78f42c3b933c89aa7342c5775e8c8b3f45667e5 100644
--- a/templates/br
+++ b/templates/br
@@ -1,40 +1,27 @@
 ---
-# Configuration for the border router
-
-- name: Enable IP forwarding
-  copy:
-    dest: "/etc/sysctl.conf"
-    content: "net.ipv4.ip_forward=1"
-
-- name: Restarting procps service
-  command: /etc/init.d/procps restart
-
-{% for host in hosts %}
-- name: Add {{ host.host_name }} alias
-  lineinfile:
-    path: /etc/hosts
-    line: {{ host.host_ip }} {{ host.host_name }}
-
-{% endfor %}
-{% for router in routers %}
-- name: Add {{ router.router_name }} alias
+- name: Adding aliases
+  loop: "{{ aliases | dict2items }}"
   lineinfile:
     path: /etc/hosts
-    line: {{ router.router_ip }} {{ router.router_name }}
-
-{% endfor %}
-
-{% for target_cidr, router_ip in br_routes.items() %} 
-- name: Add routing to network {{ target_cidr }} 
-  command: route add -net {{ target_cidr }} gw {{ router_ip }} eth1
-{% endfor %}
-
-- name: Add postrouting
-  # ssh connection fails without async after execution of iptables commands
-  shell: "sleep 2 && sudo iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source {{ border_router_public_ip }}"
-  async: 1
-  poll: 0
-
-{# name: Save postrouting rule #}
-{# command: su -c 'iptables-save > /etc/iptables.rules' #}
+    line: "{{ item.value }} {{ item.key }}"
+
+- name: Configuring routes
+  include_role:
+    name: interface
+  vars:
+    interface_ip: "{{ interface.interface_ip }}"
+    interface_netmask: "{{ interface.interface_netmask }}"
+    interface_default_gateway: "{{ interface.interface_default_gateway | default('') }}"
+    interface_routes: "{{ interface.interface_routes | default([]) }}"
+  loop: "{{ routes }}"
+  loop_control:
+    loop_var: interface
+
+- name: Set up postrouting
+  iptables:
+    table: nat
+    chain: POSTROUTING
+    out_interface: "{{ ansible_default_ipv4.interface }}"
+    jump: SNAT
+    to_source: "{{ ansible_default_ipv4.address }}"
 ...
diff --git a/templates/common/meta/main.yml b/templates/common/meta/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..edff08cf19051df266730d50704938dbf642e748
--- /dev/null
+++ b/templates/common/meta/main.yml
@@ -0,0 +1,15 @@
+
+galaxy_info:
+    role_name: common
+    author: Kamil Andoniadis
+    description: This role provide common macros, templates or files
+    licence: MIT
+    min_ansible_version: 2.3.3
+    platforms:
+        - name: Debian
+          versions:
+              - all
+        - name: Ubuntu
+          versions:
+              - all
+
diff --git a/templates/common/templates/config.j2 b/templates/common/templates/config.j2
new file mode 100644
index 0000000000000000000000000000000000000000..929abb78e17c01dca63d9b607556121a5283fa02
--- /dev/null
+++ b/templates/common/templates/config.j2
@@ -0,0 +1,3 @@
+{%- macro yaml_config_key_regexp(key) -%}
+    ^(#.*)?{{ key }}:.*
+{%- endmacro -%}
diff --git a/templates/common/templates/network.j2 b/templates/common/templates/network.j2
new file mode 100644
index 0000000000000000000000000000000000000000..f409e1ca0c933d39d1f5db773aa7b5ae52ca68d2
--- /dev/null
+++ b/templates/common/templates/network.j2
@@ -0,0 +1,30 @@
+{%- set common_network = namespace(
+    interfaces=[]
+) -%} 
+{%- for ansible_interface in ansible_interfaces -%} 
+    {%- set common_network.interfaces = common_network.interfaces + [hostvars[inventory_hostname]['ansible_' + ansible_interface]] -%} 
+{%- endfor -%}
+
+{%- macro mac_to_interface(mac) -%}
+    {{  
+        (   
+            common_network.interfaces | selectattr('macaddress', 'defined') |
+            selectattr('macaddress', 'equalto', mac) | map(attribute='device') | list
+        ) [0] | default('')
+    }}
+{%- endmacro -%}
+
+{%- macro ip_to_interface(ip) -%}
+    {{
+        (
+            common_network.interfaces | selectattr('ipv4', 'defined') | selectattr('ipv4.address', 'defined') |
+            selectattr('ipv4.address', 'equalto', ip) | map(attribute='device') | list
+        ) [0] | default('')
+    }}
+{%- endmacro -%}
+
+{%- macro get_inactive_interfaces() -%}
+    {{
+        common_network.interfaces | selectattr("active", "equalto", False) | list
+    }}
+{%- endmacro -%}
diff --git a/templates/config b/templates/config
new file mode 100644
index 0000000000000000000000000000000000000000..9c73ad703a3a50a2c1d5fbd1ed2a8c6e1b0f53a5
--- /dev/null
+++ b/templates/config
@@ -0,0 +1,29 @@
+{% if hosts %}
+hosts:
+  {% for host in hosts %}
+  - name: {{ host.name }}
+  {% if "networks" in host %}
+  - networks:
+  {% endif %}
+  {% for network, ip in host.networks.items() %}
+    {{ network }}: {{ ip }}
+  {% endfor %}
+  {% endfor %}
+{% else %}
+hosts: []
+{% endif %}
+
+{% if routers %}
+routers:
+  {% for router in routers %}
+  - name: {{ router.name }}
+  {% if "networks" in router %}
+  - networks:
+  {% endif %}
+  {% for network, ip in router.networks.items() %}
+    {{ network }}: {{ ip }}
+  {% endfor %}
+  {% endfor %}
+{% else %}
+routers: []
+{% endif %}
diff --git a/templates/device_configuration b/templates/device_configuration
index e8ba8322319433a12718201c9e7823f43b14a95a..056e19806fae62a00131a4ae13a3c87ec4a7cae8 100644
--- a/templates/device_configuration
+++ b/templates/device_configuration
@@ -1,40 +1,35 @@
 ---
 # Basic configuration of all defined devices
 
-- name: Configuring all hosts
-  hosts: {{ hosts|map(attribute='host_name')|unique|join(',') }}
-  become: yes
-  roles:
-    - hosts
+- name: Including variables
+  hosts: all
+  tasks:
+
+  - name: Including common variables
+    include_vars:
+      file: config.yml
+      name: config
 
-{% for host in hosts %}
-- name: Configuring host {{ host.host_name }} separately
-  hosts: {{ host.host_name }}
+- name: Configuring hosts
+  hosts: hosts
   become: yes
   roles:
-    - {{ host.host_name }}
+    - hosts
 
-{% endfor %}
-{% for host in hosts %}
-- name: Configuring host {{ host.host_name }}
-  hosts: {{ host.host_name }}
+- name: Configuring routers
+  hosts: routers
   become: yes
   tasks:
-{% for network_ip in network_ips %}
-  - name: Add gateway for {{ network_ip }}
-    command: route add -net {{ network_ip }} gw {{ host.router_ip }} {{ host.interface }}
-{% endfor %}
+  - name: include role
+    include_role:
+      name: routers
+    when: config.routers
 
-{% endfor %}
-- name: Configuring all routers
-  hosts: {{ routers|map(attribute='router_name')|unique|reject('eq', border_router_name)|join(',') }}
+- name: Configuring devices separately
+  hosts: all
   become: yes
-  roles:
-    - routers
-
-- name: Configuring border router
-  hosts: {{ border_router_name }}
-  become: yes
-  roles:
-    - br
+  tasks:
+  - name: include role
+    include_role:
+      name: "{{ inventory_hostname }}"
 ...
diff --git a/templates/hosts b/templates/hosts
index e9254f11c31650636be65efaf1f4cdc53326191e..34e8296b1bf6323068655bdd749d110801b52869 100644
--- a/templates/hosts
+++ b/templates/hosts
@@ -2,23 +2,6 @@
 # Basic configuration of all host devices
 
 - name: Install net-tools
-  command: apt install net-tools
-
-{% for host in hosts %}
-- name: Add {{ host.host_name }} alias
-  lineinfile:
-    path: /etc/hosts
-    line: {{ host.host_ip }} {{ host.host_name }}
-
-{% endfor %}
-{% for router in routers %}
-- name: Add {{ router.router_name }} alias
-  lineinfile:
-    path: /etc/hosts
-    line: {{ router.router_ip }} {{ router.router_name }}
-
-{% endfor %}
-
-- name: Delete default gateway
-  command: route del default
+  apt:
+    name: "net-tools"
 ...
diff --git a/templates/interface/defaults/main.yml b/templates/interface/defaults/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..0910da5b26381303fbc25deb652f190995339756
--- /dev/null
+++ b/templates/interface/defaults/main.yml
@@ -0,0 +1,40 @@
+
+interface_default_file: /etc/network/interfaces
+interface_directory: '{{ interface_default_file }}.d'
+interface_file_name:
+interface_file: '
+    {%- if interface_file_name is defined and interface_file_name -%}
+        {{ interface_directory }}/{{ interface_file_name }}
+    {%- else -%}
+        {{ interface_default_file }}
+    {%- endif %}'
+
+interface_clean: True
+interface_mtu: 1442
+
+interface_ip:
+interface_mac:
+interface_name:
+interface_default_gateway:
+interface_routes: []
+# - gateway:
+#   network:
+#   mask:
+
+interface_device: '
+    {%- import "roles/common/templates/network.j2" as network with context -%}
+    {%- if interface_ip is defined and interface_ip -%}
+        {{ network.ip_to_interface(interface_ip) | default("") }}
+    {%- endif -%}
+    {%- if interface_mac is defined and interface_mac -%}
+        {{ network.mac_to_interface(interface_mac) | default("") }}
+    {%- endif -%}
+    {%- if interface_name is defined and interface_name -%}
+        {{ interface_name }}
+    {%- endif -%}'
+interface_identifiers:
+    interface_ip: '{{ interface_ip }}'
+    interface_mac: '{{ interface_mac }}'
+    interface_name: '{{ interface_name }}'
+interface_condition_single_interface_identifier: '{{ interface_identifiers | dict2items | map(attribute="value") | select("string") | select("ne", "") | list | length != 1 }}'
+
diff --git a/templates/interface/handlers/main.yml b/templates/interface/handlers/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..34553f66237ce5bfa2b75cff663b452a2402eae4
--- /dev/null
+++ b/templates/interface/handlers/main.yml
@@ -0,0 +1,6 @@
+
+- name: interface_networking_restart
+  service:
+      name: networking
+      state: restarted
+
diff --git a/templates/interface/meta/main.yml b/templates/interface/meta/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..58ecda581c150aac8a05d5d99e63955a1cae7073
--- /dev/null
+++ b/templates/interface/meta/main.yml
@@ -0,0 +1,19 @@
+
+dependencies:
+    - src: git@gitlab.ics.muni.cz:CSIRT-MU-public/ansible-roles/common.git
+      scm: git
+
+galaxy_info:
+    role_name: interface
+    author: Kamil Andoniadis
+    description: Basic network interface configuration
+    licence: MIT
+    min_ansible_version: 2.3.3
+    platforms:
+        - name: Debian
+          versions:
+              - all
+        - name: Ubuntu
+          versions:
+              - all
+
diff --git a/templates/interface/tasks/clean.yml b/templates/interface/tasks/clean.yml
new file mode 100644
index 0000000000000000000000000000000000000000..36a651b9e5b6bff74029196dc471219b519fcc64
--- /dev/null
+++ b/templates/interface/tasks/clean.yml
@@ -0,0 +1,31 @@
+
+- name: find all interfaces configuration files
+  find:
+      paths:
+          - '{{ interface_directory }}'
+  register: interface_extra_files
+
+- set_fact:
+      interface_config_files: '{{ (interface_extra_files.files | map(attribute="path") | list) + [interface_default_file] }}'
+
+- name: remove old iface settings for retrieved interface name
+  replace:
+      path: '{{ item }}'
+      regexp: ^iface[ \t]{{ interface_device }}[ \t].*(\n[ \t]+.*)*
+  notify: interface_networking_restart
+  with_items: '{{ interface_config_files }}'
+
+- name: remove the rest of old settings for retrieved interface name
+  replace:
+      path: '{{ item }}'
+      regexp: '^.*(?<=\s){{ interface_device }}(?=\s).*$'
+  notify: interface_networking_restart
+  with_items: '{{ interface_config_files }}'
+
+- name: remove multiple consecutive new line characters
+  replace:
+      path: '{{ item }}'
+      regexp: '(\n)+'
+      replace: '\n'
+  with_items: '{{ interface_config_files }}'
+
diff --git a/templates/interface/tasks/interface.yml b/templates/interface/tasks/interface.yml
new file mode 100644
index 0000000000000000000000000000000000000000..5f78cca50fef9c49df709de26ec1b26379235549
--- /dev/null
+++ b/templates/interface/tasks/interface.yml
@@ -0,0 +1,25 @@
+
+- name: configure interface
+  blockinfile:
+      path: '{{ interface_file }}'
+      create: yes
+      marker: '# {mark} {{ interface_device }}'
+      block: |
+          allow-hotplug {{ interface_device }}
+          auto {{ interface_device }}
+          iface {{ interface_device }} inet static
+              address {{ interface_ip }}
+              netmask {{ interface_netmask }}
+              mtu {{ interface_mtu }}
+              {% if interface_default_gateway -%}
+              gateway {{ interface_default_gateway }}
+              up route add default gw {{ interface_default_gateway }}
+              {% endif -%}
+              {% if interface_routes -%}
+              {% for route in interface_routes -%}
+              post-up ip route add {{ route['network'] }}/{{ route['mask'] }} via {{ route['gateway'] }}
+              pre-down ip route del {{ route['network'] }}/{{ route['mask'] }} via {{ route['gateway'] }}
+              {% endfor %}
+              {% endif %}
+  notify: interface_networking_restart
+
diff --git a/templates/interface/tasks/main.yml b/templates/interface/tasks/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..7e69f74724ae2ed1bf954c99f929b0a53dceccf5
--- /dev/null
+++ b/templates/interface/tasks/main.yml
@@ -0,0 +1,11 @@
+
+- name: check existence of single interface identifier
+  fail:
+      msg: there must be set exactly one of [interface_ip|interface_mac|interface_name] variables, got {{ interface_identifiers }}
+  when: interface_condition_single_interface_identifier
+
+- include: clean.yml
+  when: interface_clean is defined and interface_clean
+
+- include: interface.yml
+
diff --git a/templates/playbook b/templates/playbook
index 7f3e1c7089a960a7da5b952b974f818a23081acf..2fe3cd9f7613d46390332339c044d12c74b3b36a 100644
--- a/templates/playbook
+++ b/templates/playbook
@@ -1,5 +1,5 @@
 ---
-# Main user ansible playbook
+# Main user ansible playbook.
 # Write your custom configuration here:
 
 - name: Hello world
diff --git a/templates/routers b/templates/routers
index 96d9d5926138897fa1e51f290842f753bd671b60..2e1724cbf267a7eb1af1ecacd07ac08d3087782b 100644
--- a/templates/routers
+++ b/templates/routers
@@ -2,30 +2,9 @@
 # Configuration of all router devices
 
 - name: Enable IP forwarding
-  copy:
-    dest: "/etc/sysctl.conf"
-    content: "net.ipv4.ip_forward=1"
-
-- name: Restarting procps service
-  command: /etc/init.d/procps restart
-
-{% for host in hosts %}
-- name: Add {{ host.host_name }} alias
-  lineinfile:
-    path: /etc/hosts
-    line: {{ host.host_ip }} {{ host.host_name }}
-
-{% endfor %}
-{% for router in routers %}
-- name: Add {{ router.router_name }} alias
-  lineinfile:
-    path: /etc/hosts
-    line: {{ router.router_ip }} {{ router.router_name }}
-
-{% endfor %}
-- name: Delete default gateway
-  command: route del default
-
-- name: Add default path to border router
-  command: route add default gw {{ border_router_ip }} eth3
+  sysctl:
+    name: net.ipv4.ip_forward
+    value: '1'
+    sysctl_set: yes
+    reload: yes
 ...
diff --git a/templates/separate_devices b/templates/separate_devices
new file mode 100644
index 0000000000000000000000000000000000000000..5e398eb9f746d2f24c588bf6848e5d9a2a73aad1
--- /dev/null
+++ b/templates/separate_devices
@@ -0,0 +1,19 @@
+---
+- name: Adding aliases
+  loop: "{{ aliases | dict2items }}"
+  lineinfile:
+    path: /etc/hosts
+    line: "{{ item.value }} {{ item.key }}"
+
+- name: Configuring routes
+  include_role:
+    name: interface
+  vars:
+    interface_ip: "{{ interface.interface_ip }}"
+    interface_netmask: "{{ interface.interface_netmask }}"
+    interface_default_gateway: "{{ interface.interface_default_gateway | default('') }}"
+    interface_routes: "{{ interface.interface_routes | default([])}}"
+  loop: "{{ routes }}"
+  loop_control:
+    loop_var: interface
+...
\ No newline at end of file
diff --git a/templates/user_hosts b/templates/user_hosts
index 0dccf70c06e2ec8580828c2c8c6693e870030b59..c79ff48449939fcf1fe5406771c6d21cbbc846f1 100644
--- a/templates/user_hosts
+++ b/templates/user_hosts
@@ -1,7 +1,6 @@
 ---
 # This is a role for all hosts.
 # You can write your tasks here.
-# These changes will affect all hosts.
 
 
 
diff --git a/templates/user_routers b/templates/user_routers
new file mode 100644
index 0000000000000000000000000000000000000000..00eb86eec6c3124109e067240692ae0085cf78cb
--- /dev/null
+++ b/templates/user_routers
@@ -0,0 +1,7 @@
+---
+# This is a role for all routers.
+# You can write your tasks here.
+
+
+
+...
diff --git a/templates/user_separate_hosts b/templates/user_separate_hosts
index f49a8bafc9297f21682d4a170677d597336f6544..25921c0776e23af7285a5f5fb20574ecc9fb4f37 100644
--- a/templates/user_separate_hosts
+++ b/templates/user_separate_hosts
@@ -1,7 +1,6 @@
 ---
 # This is a role for the host {{ host_name }}.
 # You can write your tasks here.
-# These changes will affect only the host {{ host_name }}.
 
 
 
diff --git a/templates/user_separate_routers b/templates/user_separate_routers
new file mode 100644
index 0000000000000000000000000000000000000000..c92c2bd41008a10d5710bb1127eb092c1111054b
--- /dev/null
+++ b/templates/user_separate_routers
@@ -0,0 +1,7 @@
+---
+# This is a role for the router {{ router_name }}.
+# You can write your tasks here.
+
+
+
+...
diff --git a/templates/vagrantfile b/templates/vagrantfile
index 946446c71fc08127a3026740579c095125186d20..664f569a5b63a022d605442c9e515a138fd51485 100644
--- a/templates/vagrantfile
+++ b/templates/vagrantfile
@@ -1,51 +1,152 @@
-# Generated vagrant file
+# Vagrantfile generated by Sandbox Creator.
 #
 # -*- mode: ruby -*-
 # vi: set ft=ruby :
 
-{# Macro that prints out attributes of a device #}
-{% macro printAttributes(device_name) %}
-{% for command in devices[device_name] %}
-    {{ command }}
-{% endfor %}
-{% endmacro %}
+{# Macro for router items #}
+{% macro router(item, namespace) %}
+  # device (router): {{ item.name }}
+  {{ namespace }}.vm.define "{{ item.name }}" do |device|
+{{ layer2(item.commands, "device") }}
+{% endmacro -%}
 
-{# Device definitions #}
-Vagrant.configure("2") do |config|
-{% for name, attributes in devices.items() %}
+{# Macro for host items #}
+{% macro host(item, namespace) %}
+  # device (host): {{ item.name }}
+  {{ namespace }}.vm.define "{{ item.name }}" do |device|
+{{ layer2(item.commands, "device") }}
+{% endmacro -%}
 
-  # device: {{ name }}
-  config.vm.define "{{ name }}" do |device|
-{{ printAttributes(name) }}  end
-{% endfor %}
+{# Macro for provider items #}
+{% macro provider(item, namespace) %}
+  {{ namespace }}.vm.provider "{{ item.name }}" do |provider|
+{{ layer3(item.commands, "provider") }}
+{% endmacro -%}
 
-  # basic ansible configuration of devices and networks
-  config.vm.provision :ansible{% if ansible_local %}_local{% endif %} do |ansible|
-    ansible.playbook = "base_provisioning/device_configuration.yml"
-    ansible.verbose = true
-    ansible.extra_vars = {
-      ansible_python_interpreter: "/usr/bin/python3",
-    }
-  end
+{# Macro for provision items #}
+{% macro provision(item, namespace) %}
+  # {{ item.note }}
+  {{ namespace }}.vm.provision :{{ item.provisioner }} do |provisioner|
+{{ layer2(item.commands, "provisioner") }}
+{% endmacro -%}
 
+{# Macro for string items #}
+{% macro string(item, namespace) %}
+  {{ namespace }}.{{ item.command }} = "{{ item.value }}"
+{% endmacro -%}
 
-  # user configuration of devices with ansible
-  config.vm.provision :ansible{% if ansible_local %}_local{% endif %} do |ansible|
-    ansible.playbook = "provisioning/playbook.yml"
-    ansible.verbose = true
-    ansible.extra_vars = {
-      ansible_python_interpreter: "/usr/bin/python3",
-    }
-  end
+{# Macro for boolean items #}
+{% macro boolean(item, namespace) %}
+{% if item.value %}
+  {{ namespace }}.{{ item.command }} = true
+{% else %}
+  {{ namespace }}.{{ item.command }} = false
+{% endif %}
+{% endmacro -%}
+
+{# Macro for integer items #}
+{% macro integer(item, namespace) %}
+  {{ namespace }}.{{ item.command }} = {{ item.value }}
+{% endmacro -%}
+
+{# Macro for general items (not str int or bool) #}
+{% macro other(item, namespace) %}
+{% if item.separator %}
+  {{ namespace }}.{{ item.command }} {{ item.separator }} {{ item.value }}
+{% else %}
+  {{ namespace }}.{{ item.command }} {{ item.value }}
+{% endif %}
+{% endmacro -%}
 
-  {% for name in user_files %}
-  config.vm.provision :ansible{% if ansible_local %}_local{% endif %} do |ansible|
-    ansible.playbook = "provisioning/{{ name }}.yml"
-    ansible.verbose = true
-    ansible.extra_vars = {
-      ansible_python_interpreter: "/usr/bin/python3",
+{# Macro for dictionaries #}
+{% macro dictionary(item, namespace) %}
+  {{ namespace }}.{{ item.command }} = {
+  {% for key, value in item.dictionary.items() %}
+    {% if loop.last %}
+      {{ key }}: {{ value }}
+    {% else %}
+      {{ key }}: {{ value }},
+    {% endif %}
+  {% endfor %}
     }
-  end
+{% endmacro -%}
+
+{# Macro for dictionaries #}
+{% macro groups(item, namespace) %}
+  {{ namespace }}.groups = {
+  {% for key, value in item.groups.items() %}
+    {% if loop.last %}
+      "{{ key }}" => {{ value }}
+    {% else %}
+      "{{ key }}" => {{ value }},
+    {% endif %}
   {% endfor %}
+    }
+{% endmacro -%}
+
+{# Macro for network items #}
+{% macro network(item, namespace) %}
+  {{ namespace }}.vm.network :{{ item.network_type }}, ip: "{{ item.ip }}"
+{%- if item.netmask %}
+, netmask: "{{ item.netmask }}"
+{%- endif %}
+{%- if item.network_type == "private_network" %}
+, virtualbox__intnet: "{{ item.name }}"
+{% endif %}
+{% endmacro -%}
 
+{# A macro that generates the first level of indentation. #}
+{% macro layer1(structure, namespace) %}
+{% for item in structure %}
+{% if item.type == "router" %}
+{{ router(item, namespace) }}
+{% elif item.type == "host" %}
+{{ host(item, namespace) }}
+{% elif item.type == "provision" %}
+{{ provision(item, namespace) }}
+{% endif %}
+{% endfor %}
 end
+{%- endmacro -%}
+
+{# A macro that generates the second level of indentation. #}
+{% macro layer2(structure, namespace) %}
+{% for item in structure %}
+{% if item.type == "string" %}
+  {{ string(item, namespace) -}}
+{% elif item.type == "boolean" %}
+  {{ boolean(item, namespace) -}}
+{% elif item.type == "integer" %}
+  {{ integer(item, namespace) -}}
+{% elif item.type == "other" %}
+  {{ other(item, namespace) -}}
+{% elif item.type == "provider" %}
+  {{ provider(item, namespace) -}}
+{% elif item.type == "network" %}
+  {{ network(item, namespace) -}}
+{% elif item.type == "dictionary" %}
+  {{ dictionary(item, namespace) -}}
+{% elif item.type == "groups" %}
+  {{ groups(item, namespace) -}}
+{% endif %}
+{% endfor %}
+  end
+{%- endmacro -%}
+
+{# A macro that generates the third level of indentation. #}
+{% macro layer3(structure, namespace) %}
+{% for item in structure %}
+{% if item.type == "string" %}
+    {{ string(item, namespace) -}}
+{% elif item.type == "boolean" %}
+    {{ boolean(item, namespace) -}}
+{% elif item.type == "integer" %}
+    {{ integer(item, namespace) -}}
+{% endif %}
+{% endfor %}
+    end
+{%- endmacro -%}
+
+Vagrant.configure("2") do |config|
+
+{{ layer1(defs, "config") -}}
diff --git a/topologies/0-routers-1-network-1-host.yml b/topologies/0-routers-1-network-1-host.yml
new file mode 100644
index 0000000000000000000000000000000000000000..7d2afb3a99fd3e4d5248674ebf8c134138bb016e
--- /dev/null
+++ b/topologies/0-routers-1-network-1-host.yml
@@ -0,0 +1,16 @@
+# A simple topology with 1 debian 10 host in a network
+name: 0-routers-1-networks-1-hosts
+hosts:
+  - name: debian10
+    base_box: generic/debian10
+    flavor: csirtmu.tiny1x4
+    cpus: 2
+
+networks:
+  - name: network
+    cidr: 10.10.30.0/24
+
+net_mappings:
+    - host: debian10
+      network: network
+      ip: 10.10.30.5
diff --git a/topologies/0-routers-1-network-5-hosts.yml b/topologies/0-routers-1-network-5-hosts.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b389367ad52edb8da39be84efe86a96c04f7e71d
--- /dev/null
+++ b/topologies/0-routers-1-network-5-hosts.yml
@@ -0,0 +1,43 @@
+# A simple topology with various boxes in 1 network
+name: 1-network-various-boxes
+hosts:
+  - name: debian10
+    base_box: generic/debian10
+    memory: 512
+
+  - name: debian9
+    base_box: generic/debian9
+    memory: 512
+
+  - name: debian-stretch
+    base_box: debian/contrib-stretch64
+    memory: 512
+
+  - name: ubuntu-xenial
+    base_box: ubuntu/xenial64
+    memory: 1024
+
+  - name: mu-kali
+    base_box: mu/kali-2019.4
+    memory: 2048
+
+networks:
+  - name: network
+    cidr: 10.10.30.0/24
+
+net_mappings:
+    - host: debian10
+      network: network
+      ip: 10.10.30.5
+    - host: debian9
+      network: network
+      ip: 10.10.30.6
+    - host: debian-stretch
+      network: network
+      ip: 10.10.30.7
+    - host: ubuntu-xenial
+      network: network
+      ip: 10.10.30.8
+    - host: mu-kali
+      network: network
+      ip: 10.10.30.9
diff --git a/topologies/1-router-1-network-1-host.yml b/topologies/1-router-1-network-1-host.yml
new file mode 100644
index 0000000000000000000000000000000000000000..100178292eab6f42af221f3520f415fa4a78d1d2
--- /dev/null
+++ b/topologies/1-router-1-network-1-host.yml
@@ -0,0 +1,23 @@
+
+# 1 network with a host and a router
+name: 1-router-1-host
+hosts:
+  - name: debian10
+    base_box: generic/debian10
+    memory: 512
+
+routers:
+  - name: router
+
+networks:
+  - name: network
+
+net_mappings:
+    - host: debian10
+      network: network
+      ip: 10.10.30.5
+
+router_mappings:
+    - router: router
+      network: network
+      ip: 10.10.30.1
diff --git a/topologies/1-router-2-networks-2-hosts.yml b/topologies/1-router-2-networks-2-hosts.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4fba3a2015b38cc806e5786e215c5d7c6f10a803
--- /dev/null
+++ b/topologies/1-router-2-networks-2-hosts.yml
@@ -0,0 +1,37 @@
+# Topology with one router connecting 2 separate networks, each with 1 host
+name: 2-networks
+hosts:
+  - name: server
+    base_box: generic/debian10
+    memory: 512
+
+  - name: home
+    base_box: generic/debian10
+    memory: 512
+
+routers:
+  - name: router
+
+networks:
+  - name: server-switch
+    cidr: 10.10.20.0/24
+
+  - name: home-switch
+    cidr: 10.10.30.0/24
+
+net_mappings:
+    - host: server
+      network: server-switch
+      ip: 10.10.20.5
+    - host: home
+      network: home-switch
+      ip: 10.10.30.5
+
+router_mappings:
+    - router: router
+      network: server-switch
+      ip: 10.10.20.1
+    - router: router
+      network: home-switch
+      ip: 10.10.30.1
+
diff --git a/sandbox.yml b/topology.yml
similarity index 79%
rename from sandbox.yml
rename to topology.yml
index c53b54d0e2ac286fe728197f90e4f7f631fb0322..9a686249ec1a37139ff783e9c770d39836233e66 100644
--- a/sandbox.yml
+++ b/topology.yml
@@ -16,24 +16,24 @@ routers:
 
 networks:
   - name: server-switch
-    cidr: 10.10.20.0/24
+    cidr: 192.168.20.0/24
 
   - name: home-switch
-    cidr: 10.10.30.0/24
+    cidr: 192.168.30.0/24
 
 net_mappings:
     - host: server
       network: server-switch
-      ip: 10.10.20.5
+      ip: 192.168.20.5
     - host: home
       network: home-switch
-      ip: 10.10.30.5
+      ip: 192.168.30.5
 
 router_mappings:
     - router: router
       network: server-switch
-      ip: 10.10.20.1
+      ip: 192.168.20.1
     - router: router
       network: home-switch
-      ip: 10.10.30.1
+      ip: 192.168.30.1