From ccb50975dbc3b118a42d3756dada6e49e06511b6 Mon Sep 17 00:00:00 2001
From: Attila Farkas <x394097@fi.muni.cz>
Date: Tue, 5 May 2020 19:48:33 +0200
Subject: [PATCH] fix codestyle and docstyle

---
 create.py                         |  20 +++---
 modules/ansible_generator.py      |  43 +++++++-----
 modules/ansible_vars_generator.py |  90 +++++++++++++-----------
 modules/border_router.py          |  48 +++++++------
 modules/file_manager.py           |  87 ++++++++++++-----------
 modules/input_argument_parser.py  |  30 ++++----
 modules/input_file_validator.py   |   9 ++-
 modules/preprocessing.py          |  59 +++++++++++-----
 modules/vagrant_generator.py      | 113 ++++++++++++++++--------------
 9 files changed, 280 insertions(+), 219 deletions(-)

diff --git a/create.py b/create.py
index ea506e7..19ba767 100644
--- a/create.py
+++ b/create.py
@@ -1,10 +1,9 @@
 #!/usr/bin/python3
-"""
-This script generates a Vagrantfile and ansible files from a YAML definition
-of virtual machines and network topology.  See the documentation for details.
-"""
+"""Generates Vagrantfile from YAML definition.
 
-import sys
+Generate a Vagrantfile and ansible files from a YAML definition of virtual
+machines and network topology. See the documentation for details.
+"""
 
 from modules.ansible_generator import generate_playbooks
 from modules.vagrant_generator import generate_vagrantfile
@@ -14,15 +13,14 @@ from modules.input_file_validator import validate_device_definitions
 from modules.preprocessing import preprocess
 
 
-flags = dict()
-input_file_name = parse_input_args(sys.argv, flags)
-device_definitions = open_yaml(input_file_name)
+INPUT_FILE_NAME, FLAGS = parse_input_args()
+device_definitions = open_yaml(INPUT_FILE_NAME)
 
 validate_device_definitions(device_definitions)
-preprocess(device_definitions, flags)
+preprocess(device_definitions, FLAGS)
 prepare_directories(device_definitions)
 
-generate_vagrantfile(device_definitions, flags)
-generate_playbooks(device_definitions, flags)
+generate_vagrantfile(device_definitions, FLAGS)
+generate_playbooks(device_definitions, FLAGS)
 
 print("Sandbox was successfully created.")
diff --git a/modules/ansible_generator.py b/modules/ansible_generator.py
index 4409f83..4bf98b7 100644
--- a/modules/ansible_generator.py
+++ b/modules/ansible_generator.py
@@ -1,40 +1,49 @@
+"""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
 
-    
-def _create_config_playbooks(input_definitions, flags):
-    """ Generates playbooks and roles for basic device configuration. """
 
-    copy_template_file("device_configuration", "base_provisioning/device_configuration.yml")
-    copy_template_file("hosts", "base_provisioning/roles/hosts/tasks/main.yml")
+def _create_config_playbooks(input_definitions):
+    """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")
 
     for device in input_definitions["hosts"] + input_definitions["routers"]:
-        copy_template_file("separate_devices", "base_provisioning/roles/" + device["name"] + "/tasks/main.yml")
-
-    # TODO create other playbooks
-    return
+        copy_template_file("separate_devices", "base_provisioning/roles/" +
+                           device["name"] + "/tasks/main.yml")
 
 
 def _create_user_playbooks(input_definitions):
-    """ Generates template playbooks and roles for users. """
-
+    """Generate template playbooks and roles for users."""
     copy_template_file("playbook", "provisioning/playbook.yml")
-    copy_template_file("user_hosts", "provisioning/roles/hosts/tasks/main.yml")
-    copy_template_file("user_routers", "provisioning/roles/routers/tasks/main.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"])
+        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"])
+        generate_file("user_separate_routers",
+                      "provisioning/roles/" + router["name"] +
+                      "/tasks/main.yml", router_name=router["name"])
 
 
 def generate_playbooks(input_definitions, flags):
-    """ Generates ansible vars and playbooks.
+    """Generate ansible vars and playbooks.
 
     :param definitions: device definitions structure
     :param flags: command line input flags
     """
     generate_ansible_vars(input_definitions, flags)
-    _create_config_playbooks(input_definitions, flags)
+    _create_config_playbooks(input_definitions)
     _create_user_playbooks(input_definitions)
diff --git a/modules/ansible_vars_generator.py b/modules/ansible_vars_generator.py
index 403f6ac..b444a83 100644
--- a/modules/ansible_vars_generator.py
+++ b/modules/ansible_vars_generator.py
@@ -1,18 +1,18 @@
-from modules.file_manager import generate_file, dump_to_yaml
+"""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_routings
 
 
 def _find_networks_of_device(name, input_definitions):
-    """ Returns a list of network names in which the device have an
-    interface.
-    """
-
+    """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
 
@@ -25,11 +25,10 @@ def _find_networks_of_device(name, input_definitions):
             networks.append(router_mapping["network"])
 
     return networks
-        
 
-def _add_aliases(device_name, input_definitions, flags):
-    """ Generates aliases for the given device. """
 
+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()
@@ -46,51 +45,59 @@ def _add_aliases(device_name, input_definitions, flags):
     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"] + " .")
+                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:
+                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"] + " .")
+            print("Error: " + device_name + " has no connection to " +
+                  router["name"] + " .")
             raise AttributeError
 
     return aliases
-            
 
-def _generate_device_vars(input_definitions, flags):
-    """ Generates vars files for all devices separately. """
 
+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)
-        dump_to_yaml(variables, "base_provisioning/roles/" + target_host["name"] + "/vars/main.yml")
+        variables["aliases"] = _add_aliases(target_host["name"],
+                                            input_definitions, flags)
+        variables["routings"] = add_routings(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)
-        dump_to_yaml(variables, "base_provisioning/roles/" + target_router["name"] + "/vars/main.yml")
+        variables["aliases"] = _add_aliases(target_router["name"],
+                                            input_definitions, flags)
+        variables["routings"] = add_routings(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):
-    """ Generates vars file for all hosts. """
-
+    """Generate vars file for all hosts."""
     return
 
 
 def _generate_routers_vars(input_definitions, flags):
-    """ Generates vars file for all routers. """
-
+    """Generate vars file for all routers."""
     return
 
-def _find_ip(device_name, input_definitions):
-    """ Returns a dictionary with all network names and ips of a device. """
 
+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"]
@@ -103,22 +110,24 @@ def _find_ip(device_name, input_definitions):
 
 
 def _find_default_route(device_name, input_definitions, flags):
-    """ Returns the ip to which the device should be routed defaultly and
-    None if the change of default routing is not required.
-    """
+    """Return the ip to which the device should be routed defaultly.
 
+    None is returned if the change of default routing is not required.
+    """
     return None
 
 
-def _generate_config_vars(input_definitions, flags): # TODO check what vars needs to be in config
-    """ Generates vars file for all devices. """     # TODO dump to yaml without jinja
-
+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)
-        new_host["route_to"] = _find_default_route(host["name"], input_definitions, flags)
+        new_host["route_to"] = _find_default_route(host["name"],
+                                                   input_definitions, flags)
         hosts.append(new_host)
 
     routers = []
@@ -126,15 +135,16 @@ def _generate_config_vars(input_definitions, flags): # TODO check what vars need
         new_router = dict()
         new_router["name"] = router["name"]
         new_router["networks"] = _find_ip(router["name"], input_definitions)
-        new_router["route_to"] = _find_default_route(router["name"], input_definitions, flags)
+        new_router["route_to"] = _find_default_route(router["name"],
+                                                     input_definitions, flags)
         routers.append(new_router)
-    
-    generate_file("config", "base_provisioning/config.yml", hosts=hosts, routers=routers)
 
+    generate_file("config", "base_provisioning/config.yml", hosts=hosts,
+                  routers=routers)
 
-def generate_ansible_vars(input_definitions, flags):
-    """ Generates files with variables for ansible. """
 
+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)
diff --git a/modules/border_router.py b/modules/border_router.py
index 68cffc6..9794bb2 100644
--- a/modules/border_router.py
+++ b/modules/border_router.py
@@ -1,9 +1,10 @@
+"""Contains functions for border router creation."""
+
 from conf.border_router import *
 
 
 def _are_br_parameters_free(definitions):
-    """ Checks if border router parameters are not already taken. """
-
+    """Check if border router parameters are not already taken."""
     for host in definitions["hosts"]:
         if host["name"] == BORDER_ROUTER_NAME:
             return False
@@ -11,9 +12,10 @@ def _are_br_parameters_free(definitions):
     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:
+        if network["name"] == BORDER_ROUTER_NETWORK_NAME or \
+           network["cidr"] == BORDER_ROUTER_NETWORK_IP:
             return False
 
     for net_mapping in definitions["net_mappings"]:
@@ -28,39 +30,39 @@ def _are_br_parameters_free(definitions):
 
 
 def _create_mappings_to_border_router(definitions):
-    """ Creates router_mapping entries from routers to border router. """
-
+    """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 = BORDER_ROUTER_IP[:(0-len(str(num)))]
-        ip += str(num)
+        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)
 
-        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.
+    """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.")
-    
+        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 })
-    definitions["networks"].insert(0, {"name":BORDER_ROUTER_NETWORK_NAME
-                                    ,"cidr":BORDER_ROUTER_NETWORK_IP})
-    definitions["router_mappings"].insert(0, {"router":BORDER_ROUTER_NAME
-                                          ,"network":BORDER_ROUTER_NETWORK_NAME
-                                          ,"ip":BORDER_ROUTER_IP} )
+    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/file_manager.py b/modules/file_manager.py
index 75524ea..8b4a8b5 100644
--- a/modules/file_manager.py
+++ b/modules/file_manager.py
@@ -1,4 +1,4 @@
-""" This module handles file imports and creations in general. """
+"""This module handles file imports and creations in general."""
 
 import os
 import shutil
@@ -11,8 +11,7 @@ OUTPUT_DIRECTORY = "sandbox"
 
 
 def open_yaml(file_name):
-    """ Opens and returns a file from the argument. """
-
+    """Open and return a file from the argument."""
     try:
         with open(str(file_name)) as input_file:
             return yaml.safe_load(input_file)
@@ -23,23 +22,20 @@ def open_yaml(file_name):
 
 
 def copy_template_file(template, destination):
-    """ Copies playbook templates that does not need jinja or yaml to the
-    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(file_name) + ".")
+        cleanup_and_exit("Could not copy template file " + str(template) +
+                         ".")
 
 
 def dump_to_yaml(data, filename):
-    """ Writes a data structure to a YAML document.
+    """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:
@@ -50,21 +46,19 @@ def dump_to_yaml(data, filename):
 
 
 def generate_file(template, filename, **template_args):
-    """ Generates a file using a template.
-    
+    """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):
-    """ Generates a file from output string. """
-
+    """Generate a file from output string."""
     try:
         new_file = open(OUTPUT_DIRECTORY + "/" + filename, "w")
         new_file.write(output_string)
@@ -73,21 +67,19 @@ def _write_to_file(filename, output_string):
 
 
 def _load_template(template_name):
-    """ Returns a loaded jinja2 template. """
-
+    """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)
+        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):
-    """ Copies directory recursively form templates directory to the
-    destination.
-    """
-
+    """Copy directory recursively form templates dir to the destination."""
     try:
         shutil.copytree("./templates/" + source, destination)
     except OSError:
@@ -95,8 +87,7 @@ def _copy_directory(source, destination):
 
 
 def _create_provisioning_directories(directory, device_definitions):
-    """ Creates all subdirectories for privisioning. """
-
+    """Create subdirectories for privisioning."""
     try:
         os.mkdir(OUTPUT_DIRECTORY + "/" + directory)
         os.mkdir(OUTPUT_DIRECTORY + "/" + directory + "/roles")
@@ -106,18 +97,26 @@ def _create_provisioning_directories(directory, device_definitions):
             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")
+            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")
+            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")
+            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
@@ -126,26 +125,32 @@ def _create_provisioning_directories(directory, device_definitions):
 
 
 def prepare_directories(device_definitions):
-    """ Prepares the necessary directory structure. """
+    """Prepare the necessary directory structure."""
+    _remove_sandbox()
 
-    shutil.rmtree(OUTPUT_DIRECTORY, True)
-    
     try:
         os.mkdir(OUTPUT_DIRECTORY)
     except IOError:
-        cleanup_and_exit("Could not create directory ./" + OUTPUT_DIRECTORY + ".")
+        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")
+    _copy_directory("interface", OUTPUT_DIRECTORY +
+                    "/base_provisioning/roles/interface")
+    _copy_directory("common", OUTPUT_DIRECTORY +
+                    "/base_provisioning/roles/common")
 
 
-def cleanup_and_exit(error):
-    """ A cleanup function that is called in case of an error. """
+def _remove_sandbox():
+    """Remove the existing sandbox."""
+    shutil.rmtree(OUTPUT_DIRECTORY, True)
 
-    # TODO cleanup
+
+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)
diff --git a/modules/input_argument_parser.py b/modules/input_argument_parser.py
index 800fba0..1ccacc4 100644
--- a/modules/input_argument_parser.py
+++ b/modules/input_argument_parser.py
@@ -1,23 +1,29 @@
-""" This module contains functions to parse input arguments. """
+"""This module contains functions to parse input arguments."""
 
 import argparse
 
-def parse_input_args(argv, flags):
-    """ 
-    Parses the given input arguments for input file name and flags.
 
-    :param argv: input arguments (list of strings)
-    :param flags: dictionary to write the parsed flags
-    :returns: name of the input file
+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 (-vvvv)", action="store_true")
-    parser.add_argument("--border_router", help="creates a border router with connection to all routers", action="store_true")
+    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 (-vvvv)",
+                        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
@@ -25,4 +31,4 @@ def parse_input_args(argv, flags):
     flags["verbose_ansible"] = args.verbose_ansible
     flags["border_router"] = args.border_router
 
-    return input_file_name
+    return input_file_name, flags
diff --git a/modules/input_file_validator.py b/modules/input_file_validator.py
index bd38f6f..8faf832 100644
--- a/modules/input_file_validator.py
+++ b/modules/input_file_validator.py
@@ -1,11 +1,10 @@
-""" This module contains functions for input file validation. """
+"""This module contains functions for input file validation."""
+
 
 def validate_device_definitions(definitions):
-    """
-    This function validates the device definitions structure and throws an
-    error if the syntax is not as expected.
+    """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/preprocessing.py b/modules/preprocessing.py
index b28f3ac..a1c8d47 100644
--- a/modules/preprocessing.py
+++ b/modules/preprocessing.py
@@ -1,16 +1,20 @@
-""" This module contains functions for preprocessing. They are supposed to be
-called after validating the input but before device creation.
+"""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
+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):
-    """ Adds necessary structures to the input if they are missing. """
 
+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:
@@ -24,19 +28,28 @@ def _add_missing_tags(definitions):
 
 
 def _configure_routers(definitions):
-    """ Adds predefined parameters to all routers if they are not defined in
-    the source yaml.
-    """
+    """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_flavors(definitions): 
-    """ Changes flavor attribute to cpus and memory. """
+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:
@@ -50,31 +63,39 @@ def _add_flavors(definitions):
 
 
 def preprocess(definitions, flags):
-    """
-    This function handles the preprocessing, operations that need to be done
-    before the actual device creation.
+    """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.")
+        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 e:
-        cleanup_and_exit("Preprocessing not successful: Could not create border router (" + e + ")")
+    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.")
+        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.")
+        cleanup_and_exit("Preprocessing not successful: "
+                         "Could not add flavors.")
diff --git a/modules/vagrant_generator.py b/modules/vagrant_generator.py
index 00801b5..f343b0e 100644
--- a/modules/vagrant_generator.py
+++ b/modules/vagrant_generator.py
@@ -1,6 +1,4 @@
-""" This module generates a Vagrantfile from input device definitions. """
-
-import jinja2
+"""Contains functions for generating a Vagrantfile from input definitions."""
 
 from modules.file_manager import generate_file, open_yaml
 
@@ -11,8 +9,7 @@ USER_PLAYBOOK = "provisioning/playbook.yml"
 
 
 def _create_simple_attribute(key, value, attribute_type):
-    """ Creates simple vagrant attributes like string, integer or boolean. """
-
+    """Create simple vagrant attributes like string, integer or boolean."""
     attribute = dict()
     attribute["type"] = attribute_type
     attribute["command"] = key
@@ -22,52 +19,57 @@ def _create_simple_attribute(key, value, attribute_type):
 
 
 def _create_complex_attribute(key, value):
-    """ Creates complex vagrant attributes that are not string, integer or
-    boolean.
-    """
-
-    SEPARATORS = {VAGRANT_MAPPING["other"]["synced_folder"]: ""}
+    """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["separator"] = separators[key]
     attribute["value"] = value
 
     return attribute
 
 
-def _create_commands(device_attributes, device_type, input_definitions, flags):
-    """ This function creates basic vagrant definition commands for a device. """
-
+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"]:
-            commands.append(_create_simple_attribute(VAGRANT_MAPPING["string"][attribute], value, "string"))
+            vagrant_attribute = VAGRANT_MAPPING["string"][attribute]
+            commands.append(_create_simple_attribute(vagrant_attribute, value,
+                                                     "string"))
         elif attribute in VAGRANT_MAPPING["boolean"]:
-            commands.append(_create_simple_attribute(VAGRANT_MAPPING["boolean"][attribute], value, "boolean"))
+            vagrant_attribute = VAGRANT_MAPPING["boolean"][attribute]
+            commands.append(_create_simple_attribute(vagrant_attribute, value,
+                                                     "boolean"))
         elif attribute in VAGRANT_MAPPING["integer"]:
-            commands.append(_create_simple_attribute(VAGRANT_MAPPING["integer"][attribute], value, "integer"))
+            vagrant_attribute = VAGRANT_MAPPING["integer"][attribute]
+            commands.append(_create_simple_attribute(vagrant_attribute, value,
+                                                     "integer"))
         elif attribute in VAGRANT_MAPPING["other"]:
-            commands.append(_create_complex_attribute(VAGRANT_MAPPING["other"][attribute], value))
+            vagrant_attribute = VAGRANT_MAPPING["other"][attribute]
+            commands.append(_create_complex_attribute(vagrant_attribute,
+                                                      value))
         elif attribute in VIRTUALBOX_MAPPING["integer"]:
-            vb_commands.append(_create_simple_attribute(VIRTUALBOX_MAPPING["integer"][attribute], value, "integer"))
+            vagrant_attribute = VIRTUALBOX_MAPPING["integer"][attribute]
+            vb_commands.append(_create_simple_attribute(vagrant_attribute,
+                                                        value, "integer"))
 
     if vb_commands:
-        vb = dict()
-        vb["type"] = "provider"
-        vb["name"] = "virtualbox"
-        vb["commands"] = vb_commands
-        commands.append(vb)
+        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):
-    """ Creates commands for running a playbook from the Vagrantfile. """
-
+    """Create commands for running a playbook from the Vagrantfile."""
     commands = []
 
     playbook = dict()
@@ -97,28 +99,34 @@ def _create_ansible_commands(playbook_location, input_definitions, flags):
     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\""
+        extravars["dictionary"]["ansible_python_interpreter"] = \
+            "\"/usr/bin/python3\""
         commands.append(extravars)
 
     return commands
 
 
 def _find_netmask(network_name, networks):
-    """ Returns the netmask of a network address from network name. """
-
+    """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):
-    """ Adds networks to the vagrant definition of one device. """
-
+    """Add networks to the vagrant definition of one device."""
     for mapping in mappings:
         if mapping[definition["type"]] == definition["name"]:
             network = dict()
@@ -126,23 +134,26 @@ def _add_networks_to_device(definition, mappings, input_definitions):
             network["network_type"] = "private_network"
             network["name"] = mapping["network"]
             network["ip"] = mapping["ip"]
-            network["netmask"] = _find_netmask(mapping["network"], input_definitions["networks"])
+            network["netmask"] = _find_netmask(mapping["network"],
+                                               input_definitions["networks"])
             definition["commands"].append(network)
 
 
 def _add_all_networks(vagrant_definitions, input_definitions, flags):
-    """ Adds all networks to vagrant definitions. """
-
+    """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)
+            _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)
+            _add_networks_to_device(definition,
+                                    input_definitions["router_mappings"],
+                                    input_definitions)
 
 
 def _call_provisioner(input_definitions, flags):
-    """ Creates entry to vagrant definitions for calling the provisioner. """
-
+    """Create entry to vagrant definitions for calling the provisioner."""
     provisioner_calls = []
 
     config_playbook = dict()
@@ -152,8 +163,9 @@ def _call_provisioner(input_definitions, flags):
     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)
+    config_playbook["commands"] = _create_ansible_commands(BASE_PLAYBOOK,
+                                                           input_definitions,
+                                                           flags)
 
     provisioner_calls.append(config_playbook)
 
@@ -164,8 +176,9 @@ def _call_provisioner(input_definitions, flags):
     else:
         user_playbook["provisioner"] = "ansible"
     user_playbook["note"] = "user configuration of devices"
-    user_playbook["commands"] = _create_ansible_commands(
-            USER_PLAYBOOK, input_definitions, flags)
+    user_playbook["commands"] = _create_ansible_commands(USER_PLAYBOOK,
+                                                         input_definitions,
+                                                         flags)
 
     provisioner_calls.append(user_playbook)
 
@@ -173,24 +186,24 @@ def _call_provisioner(input_definitions, flags):
 
 
 def _build_vagrant_definitions(input_definitions, flags):
-    """
-    Creates a definition structure that is more suitable for Vagrantfile
-    generation than input definitions.
-    """
+    """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, "router", input_definitions, flags)
+        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, "host", input_definitions, flags)
+        device["commands"] = _create_commands(host)
         vagrant_definitions.append(device)
 
     _add_all_networks(vagrant_definitions, input_definitions, flags)
@@ -201,13 +214,11 @@ def _build_vagrant_definitions(input_definitions, flags):
 
 
 def generate_vagrantfile(input_definitions, flags):
-    """
-    This method is responsible for Vagrantfile generation.
+    """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)
-- 
GitLab