diff --git a/.gitignore b/.gitignore
index 8230afb21b522e60708e4f3985e205e170f7ab46..10ad04846cf7da272a86ddfd5db14ed8054ff278 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,5 @@ __pycache__/
 .DS_Store
 .vscode
 geant/gap_ansible/.ansible-lint
+.venv
+geant-gap_ansible-*.tar.gz
diff --git a/geant/gap_ansible/plugins/modules/junos_config.py b/geant/gap_ansible/plugins/modules/junos_config.py
new file mode 100644
index 0000000000000000000000000000000000000000..c2581ce6dc7554a5203a0f3c68f65cab90e97c79
--- /dev/null
+++ b/geant/gap_ansible/plugins/modules/junos_config.py
@@ -0,0 +1,592 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# (c) 2017, Ansible by Red Hat, inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+from contextlib import contextmanager
+
+DOCUMENTATION = """
+module: junos_config
+author: Peter Sprygada (@privateip)
+short_description: Manage configuration on devices running Juniper JUNOS
+description:
+- This module provides an implementation for working with the active configuration
+  running on Juniper JUNOS devices.  It provides a set of arguments for loading configuration,
+  performing rollback operations and zeroing the active configuration on the device.
+version_added: 1.0.0
+extends_documentation_fragment:
+- junipernetworks.junos.junos
+options:
+  lines:
+    description:
+    - This argument takes a list of C(set) or C(delete) configuration lines to push
+      into the remote device.  Each line must start with either C(set) or C(delete).  This
+      argument is mutually exclusive with the I(src) argument.
+    type: list
+    aliases:
+    - commands
+    elements: str
+  src:
+    description:
+    - The I(src) argument provides a path to the configuration file to load into the
+      remote system. The path can either be a full system path to the configuration
+      file if the value starts with / or relative to the root of the implemented role
+      or playbook. This argument is mutually exclusive with the I(lines) argument.
+    type: path
+  src_format:
+    description:
+    - The I(src_format) argument specifies the format of the configuration found int
+      I(src).  If the I(src_format) argument is not provided, the module will attempt
+      to determine the format of the configuration file specified in I(src).
+    type: str
+    choices:
+    - xml
+    - set
+    - text
+    - json
+  rollback:
+    description:
+    - The C(rollback) argument instructs the module to rollback the current configuration
+      to the identifier specified in the argument.  If the specified rollback identifier
+      does not exist on the remote device, the module will fail.  To rollback to the
+      most recent commit, set the C(rollback) argument to 0.
+    type: int
+  zeroize:
+    description:
+    - The C(zeroize) argument is used to completely sanitize the remote device configuration
+      back to initial defaults.  This argument will effectively remove all current
+      configuration statements on the remote device.
+    type: bool
+    default: false
+  confirm:
+    description:
+    - The C(confirm) argument will configure a time out value in minutes for the commit
+      to be confirmed before it is automatically rolled back.  If the C(confirm) argument
+      is set to False, this argument is silently ignored.  If the value for this argument
+      is set to 0, the commit is confirmed immediately.
+    type: int
+    default: 0
+  comment:
+    description:
+    - The C(comment) argument specifies a text string to be used when committing the
+      configuration.  If the C(confirm) argument is set to False, this argument is
+      silently ignored.
+    default: configured by junos_config
+    type: str
+  replace:
+    description:
+    - The C(replace) argument will instruct the remote device to replace the current
+      configuration hierarchy with the one specified in the corresponding hierarchy
+      of the source configuration loaded from this module.
+    - Note this argument should be considered deprecated.  To achieve the equivalent,
+      set the I(update) argument to C(replace). This argument will be removed in a
+      future release. The C(replace) and C(update) argument is mutually exclusive.
+    type: bool
+  backup:
+    description:
+    - This argument will cause the module to create a full backup of the current C(running-config)
+      from the remote device before any changes are made. If the C(backup_options)
+      value is not given, the backup file is written to the C(backup) folder in the
+      playbook root directory or role root directory, if playbook is part of an ansible
+      role. If the directory does not exist, it is created.
+    type: bool
+    default: false
+  update:
+    description:
+    - This argument will decide how to load the configuration data particularly when
+      the candidate configuration and loaded configuration contain conflicting statements.
+      Following are accepted values. C(merge) combines the data in the loaded configuration
+      with the candidate configuration. If statements in the loaded configuration
+      conflict with statements in the candidate configuration, the loaded statements
+      replace the candidate ones. C(override) discards the entire candidate configuration
+      and replaces it with the loaded configuration. C(replace) substitutes each hierarchy
+      level in the loaded configuration for the corresponding level. C(update) is
+      similar to the override option. The new configuration completely replaces the
+      existing configuration. The difference comes when the configuration is later
+      committed. This option performs a 'diff' between the new candidate configuration
+      and the existing committed configuration. It then only notifies system processes
+      responsible for the changed portions of the configuration, and only marks the
+      actual configuration changes as 'changed'.
+    type: str
+    default: merge
+    choices:
+    - merge
+    - override
+    - replace
+    - update
+  confirm_commit:
+    description:
+    - This argument will execute commit operation on remote device. It can be used
+      to confirm a previous commit.
+    type: bool
+    default: false
+  check_commit:
+    description:
+    - This argument will check correctness of syntax; do not apply changes.
+    - Note that this argument can be used to confirm verified configuration done via
+      commit confirmed operation
+    type: bool
+    default: false
+  backup_options:
+    description:
+    - This is a dict object containing configurable options related to backup file
+      path. The value of this option is read only when C(backup) is set to I(true),
+      if C(backup) is set to I(false) this option will be silently ignored.
+    suboptions:
+      filename:
+        description:
+        - The filename to be used to store the backup configuration. If the filename
+          is not given it will be generated based on the hostname, current time and
+          date in format defined by <hostname>_config.<current-date>@<current-time>
+        type: str
+      dir_path:
+        description:
+        - This option provides the path ending with directory name in which the backup
+          configuration file will be stored. If the directory does not exist it will
+          be first created and the filename is either the value of C(filename) or
+          default filename as described in C(filename) options description. If the
+          path value is not given in that case a I(backup) directory will be created
+          in the current working directory and backup configuration will be copied
+          in C(filename) within I(backup) directory.
+        type: path
+      backup_format:
+        description:
+        - This argument specifies the format of the configuration the backup file will
+          be stored as.  If the argument is not specified, the module will use the 'set'
+          format.
+        type: str
+        default: set
+        choices:
+        - xml
+        - set
+        - text
+        - json
+    type: dict
+config_mode:
+  description:
+    - If set to “private”, open a private candidate datastore instead of the global one.
+  type: str
+  choices: [ private ]
+  required: false
+  default: null
+requirements:
+- ncclient (>=v0.5.2)
+notes:
+- This module requires the netconf system service be enabled on the remote device
+  being managed.
+- Abbreviated commands are NOT idempotent, see L(Network FAQ,../network/user_guide/faq.html)
+- Loading JSON-formatted configuration I(json) is supported starting in Junos OS Release
+  16.1 onwards.
+- Update C(override) not currently compatible with C(set) notation.
+- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4.
+- Recommended connection is C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html).
+- This module also works with C(local) connections for legacy playbooks.
+"""
+
+EXAMPLES = """
+- name: load configure file into device
+  junipernetworks.junos.junos_config:
+    src: srx.cfg
+    comment: update config
+
+- name: load configure lines into device
+  junipernetworks.junos.junos_config:
+    lines:
+    - set interfaces ge-0/0/1 unit 0 description "Test interface"
+    - set vlans vlan01 description "Test vlan"
+    comment: update config
+
+- name: Set routed VLAN interface (RVI) IPv4 address
+  junipernetworks.junos.junos_config:
+    lines:
+    - set vlans vlan01 vlan-id 1
+    - set interfaces irb unit 10 family inet address 10.0.0.1/24
+    - set vlans vlan01 l3-interface irb.10
+
+- name: Check correctness of commit configuration
+  junipernetworks.junos.junos_config:
+    check_commit: true
+
+- name: rollback the configuration to id 10
+  junipernetworks.junos.junos_config:
+    rollback: 10
+
+- name: zero out the current configuration
+  junipernetworks.junos.junos_config:
+    zeroize: true
+
+- name: Set VLAN access and trunking
+  junipernetworks.junos.junos_config:
+    lines:
+    - set vlans vlan02 vlan-id 6
+    - set interfaces ge-0/0/6.0 family ethernet-switching interface-mode access vlan
+      members vlan02
+    - set interfaces ge-0/0/6.0 family ethernet-switching interface-mode trunk vlan
+      members vlan02
+
+- name: confirm a previous commit
+  junipernetworks.junos.junos_config:
+    confirm_commit: true
+
+- name: for idempotency, use full-form commands
+  junipernetworks.junos.junos_config:
+    lines:
+      # - set int ge-0/0/1 unit 0 desc "Test interface"
+    - set interfaces ge-0/0/1 unit 0 description "Test interface"
+
+- name: configurable backup path
+  junipernetworks.junos.junos_config:
+    src: srx.cfg
+    backup: true
+    backup_options:
+      filename: backup.cfg
+      dir_path: /home/user
+"""
+
+RETURN = """
+backup_path:
+  description: The full path to the backup file
+  returned: when backup is true
+  type: str
+  sample: /playbooks/ansible/backup/config.2016-07-16@22:28:34
+filename:
+  description: The name of the backup file
+  returned: when backup is true and filename is not specified in backup options
+  type: str
+  sample: junos01_config.2016-07-16@22:28:34
+shortname:
+  description: The full path to the backup file excluding the timestamp
+  returned: when backup is true and filename is not specified in backup options
+  type: str
+  sample: /playbooks/ansible/backup/junos01_config
+date:
+  description: The date extracted from the backup file name
+  returned: when backup is true
+  type: str
+  sample: "2016-07-16"
+time:
+  description: The time extracted from the backup file name
+  returned: when backup is true
+  type: str
+  sample: "22:28:34"
+"""
+import json
+import re
+
+from ansible.module_utils._text import to_native, to_text
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.six import string_types
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.netconf import (
+    exec_rpc,
+)
+from ansible.module_utils.connection import ConnectionError
+from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import (
+    commit_configuration,
+    discard_changes,
+    get_configuration,
+    get_diff,
+    load_config,
+    load_configuration,
+    tostring,
+    get_connection,
+    lock_configuration,
+    unlock_configuration,
+)
+
+try:
+    from lxml.etree import Element, fromstring
+except ImportError:
+    from xml.etree.ElementTree import Element, fromstring
+
+try:
+    from lxml.etree import ParseError
+except ImportError:
+    try:
+        from xml.etree.ElementTree import ParseError
+    except ImportError:
+        # for Python < 2.7
+        from xml.parsers.expat import ExpatError
+
+        ParseError = ExpatError
+
+USE_PERSISTENT_CONNECTION = True
+DEFAULT_COMMENT = "configured by junos_config"
+
+
+def check_args(module, warnings):
+    if module.params["replace"] is not None:
+        module.fail_json(msg="argument replace is deprecated, use update")
+
+
+def zeroize(module):
+    return exec_rpc(
+        module,
+        tostring(Element("request-system-zeroize")),
+        ignore_warning=False,
+    )
+
+
+def rollback(ele, id="0"):
+    return get_diff(ele, id)
+
+
+def guess_format(config):
+    try:
+        json.loads(config)
+        return "json"
+    except ValueError:
+        pass
+
+    try:
+        fromstring(config)
+        return "xml"
+    except ParseError:
+        pass
+
+    if config.startswith("set") or config.startswith("delete"):
+        return "set"
+
+    return "text"
+
+
+def filter_delete_statements(module, candidate):
+    reply = get_configuration(module, format="set")
+    match = reply.find(".//configuration-set")
+    if match is None:
+        # Could not find configuration-set in reply, perhaps device does not support it?
+        return candidate
+    config = to_native(match.text, encoding="latin-1")
+
+    modified_candidate = candidate[:]
+    for index, line in reversed(list(enumerate(candidate))):
+        if line.startswith("delete"):
+            newline = re.sub("^delete", "set", line)
+            if newline not in config:
+                del modified_candidate[index]
+
+    return modified_candidate
+
+
+def configure_device(module, warnings, candidate):
+    kwargs = {}
+    config_format = None
+
+    if module.params["src"]:
+        config_format = module.params["src_format"] or guess_format(
+            str(candidate),
+        )
+        if config_format == "set":
+            kwargs.update({"format": "text", "action": "set"})
+        else:
+            kwargs.update(
+                {"format": config_format, "action": module.params["update"]},
+            )
+
+    if isinstance(candidate, string_types):
+        candidate = candidate.split("\n")
+
+    # this is done to filter out `delete ...` statements which map to
+    # nothing in the config as that will cause an exception to be raised
+    if any((module.params["lines"], config_format == "set")):
+        candidate = filter_delete_statements(module, candidate)
+        kwargs["format"] = "text"
+        kwargs["action"] = "set"
+
+    return load_config(module, candidate, warnings, **kwargs)
+
+
+@contextmanager
+def safe_locked_config(module):
+    """Lock the candidate—either shared or private—depending on config_mode."""
+    conn = get_connection(module)
+    private = module.params.get("config_mode") == "private"
+
+    try:
+        if private:
+            # Try to clean up any stale private first
+            try:
+                conn.execute_rpc(
+                    tostring(
+                        Element("close-configuration",
+                                {"xmlns": "http://xml.juniper.net/xnm/1.1/xnm"}
+                                )
+                    )
+                )
+            except ConnectionError:
+                pass
+
+            # Open your own private sandbox
+            conn.execute_rpc(
+                tostring(
+                    Element("open-configuration",
+                            {"xmlns": "http://xml.juniper.net/xnm/1.1/xnm",
+                             "private": "true"}
+                            )
+                )
+            )
+
+        else:
+            # shared candidate: standard lock
+            lock_configuration(module)
+    except ConnectionError as exc:
+        module.fail_json(msg=f"Could not acquire {'private' if private else 'shared'} lock: {to_text(exc)}")
+
+    # Hand control back to the caller
+    try:
+        yield
+    finally:
+        # Teardown / unlock
+        try:
+            if private:
+                conn.execute_rpc(
+                    tostring(Element("close-configuration",
+                                     {"xmlns": "http://xml.juniper.net/xnm/1.1/xnm"}
+                                     ))
+                )
+            else:
+                unlock_configuration(module)
+        except ConnectionError as exc:
+            module.warn(f"Ignoring failure closing {'private' if private else 'shared'} lock: {to_text(exc)}")
+
+
+def main():
+    """main entry point for module execution"""
+    backup_spec = dict(
+        filename=dict(),
+        dir_path=dict(type="path"),
+        backup_format=dict(
+            default="set",
+            choices=["xml", "text", "set", "json"],
+        ),
+    )
+    argument_spec = dict(
+        lines=dict(aliases=["commands"], type="list", elements="str"),
+        src=dict(type="path"),
+        src_format=dict(choices=["xml", "text", "set", "json"]),
+        # update operations
+        update=dict(
+            default="merge",
+            choices=["merge", "override", "replace", "update"],
+        ),
+        # deprecated replace in Ansible 2.3
+        replace=dict(type="bool"),
+        confirm=dict(default=0, type="int"),
+        comment=dict(default=DEFAULT_COMMENT),
+        confirm_commit=dict(type="bool", default=False),
+        check_commit=dict(type="bool", default=False),
+        # config operations
+        backup=dict(type="bool", default=False),
+        backup_options=dict(type="dict", options=backup_spec),
+        rollback=dict(type="int"),
+        zeroize=dict(default=False, type="bool"),
+        config_mode=dict(type='str', choices=['private'], required=False, default=None),
+    )
+
+    mutually_exclusive = [("lines", "src", "rollback", "zeroize")]
+
+    module = AnsibleModule(
+        argument_spec=argument_spec,
+        mutually_exclusive=mutually_exclusive,
+        supports_check_mode=True,
+    )
+
+    warnings = list()
+    check_args(module, warnings)
+
+    candidate = module.params["lines"] or module.params["src"]
+    commit = not module.check_mode
+
+    result = {"changed": False, "warnings": warnings}
+
+    if module.params["backup"]:
+        if module.params["backup_options"] is not None:
+            conf_format = module.params["backup_options"]["backup_format"]
+        else:
+            conf_format = "set"
+        reply = get_configuration(module, format=conf_format)
+        if reply is None:
+            module.fail_json(msg="unable to retrieve device configuration")
+        else:
+            if conf_format in ["set", "text"]:
+                reply = reply.find(
+                    ".//configuration-%s" % conf_format,
+                ).text.strip()
+            elif conf_format in "xml":
+                reply = str(
+                    tostring(reply.find(".//configuration"), pretty_print=True),
+                ).strip()
+            elif conf_format in "json":
+                reply = str(reply.xpath("//rpc-reply/text()")[0]).strip()
+            if not isinstance(reply, str):
+                module.fail_json(
+                    msg="unable to format retrieved device configuration",
+                )
+            result["__backup__"] = reply
+
+    rollback_id = module.params["rollback"]
+    if isinstance(rollback_id, int) and rollback_id >= 0:
+        diff = rollback(module, rollback_id)
+        if commit:
+            kwargs = {"comment": module.params["comment"]}
+            with safe_locked_config(module):
+                load_configuration(module, rollback=rollback_id)
+                commit_configuration(module, **kwargs)
+            if module._diff:
+                result["diff"] = {"prepared": diff}
+        result["changed"] = True
+
+    elif module.params["zeroize"]:
+        if commit:
+            zeroize(module)
+        result["changed"] = True
+
+    else:
+        if candidate:
+            with safe_locked_config(module):
+                diff = configure_device(module, warnings, candidate)
+                if diff:
+                    if commit:
+                        kwargs = {
+                            "comment": module.params["comment"],
+                            "check": module.params["check_commit"],
+                        }
+
+                        confirm = module.params["confirm"]
+                        if confirm > 0:
+                            kwargs.update(
+                                {
+                                    "confirm": True,
+                                    "confirm_timeout": to_text(
+                                        confirm,
+                                        errors="surrogate_then_replace",
+                                    ),
+                                },
+                            )
+                        commit_configuration(module, **kwargs)
+                    else:
+                        discard_changes(module)
+                    result["changed"] = True
+
+                    if module._diff:
+                        result["diff"] = {"prepared": diff}
+
+        elif module.params["check_commit"]:
+            commit_configuration(module, check=True)
+
+        elif module.params["confirm_commit"]:
+            with safe_locked_config(module):
+                # confirm a previous commit
+                commit_configuration(module)
+
+            result["changed"] = True
+
+    module.exit_json(**result)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/geant/gap_ansible/plugins/modules/nokia_netconf_config.py b/geant/gap_ansible/plugins/modules/nokia_netconf_config.py
index ca63f799765ee5e9bfbe54fda1b3174ff23b237e..20736e57fdc60e73bbae34d0095c6c5780d4f710 100644
--- a/geant/gap_ansible/plugins/modules/nokia_netconf_config.py
+++ b/geant/gap_ansible/plugins/modules/nokia_netconf_config.py
@@ -396,7 +396,6 @@ EXAMPLES = """
 #      "interfaces.interface.config.enabled": "true",
 #      "interfaces.interface.config.mtu": "0",
 #      "interfaces.interface.config.name": "Ethernet2",
-
 """
 
 RETURN = """