Add GNS3 support and fix typo in site.yml

This commit is contained in:
2020-04-09 14:57:54 -04:00
parent 0989800e14
commit af4b3cf4f3
31 changed files with 2694 additions and 3 deletions

1
TODO.txt Normal file
View File

@@ -0,0 +1 @@
- Replace alvaroaleman.freeipa-client with https://galaxy.ansible.com/freeipa/ansible_freeipa

View File

@@ -0,0 +1,93 @@
# Compiled source #
###################
*.com
*.class
*.dll
*.exe
*.o
*.so
__pycache__/
*.py[cod]
*$py.class
# Packages #
############
# it's better to unpack these files and commit the raw source
# git has its own built in compression methods
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# Logs and databases #
######################
*.log
*.sql
*.sqlite
# OS generated files #
######################
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
*.retry
.vscode
.mypy_cache
.pytest_cache
__pycache__
prof
.envFile
.env
.eapi.conf
.idea
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Sphinx documentation
docs/_build/
# MkDocs
site/
# mypy
.mypy_cache/
.dmypy.json
dmypy.json

View File

@@ -0,0 +1,69 @@
# Releases
## 1.5.0
**Enhancements:**
- Added `gns3_facts` to retrieve the compute information like: console ports, server version, available emulators, available images, etc..
- Added `gns3_snapshot` to manipulate the snapshot creation/deletion and restoration of a project.
**Fixes:**
- Added the needed `user` and `password` arguments to all the modules, when interacting with a GNS3 server with authentication.
- Refactored the modules a little to be more standard with each other.
## 1.4.0
**Enhacements:**
- Added `gns3_node` to interact with the node inside a project. Provides the following:
- `start/stop/suspend/reload`: Actions to be applied on the node. These are idempotent with the exception of `reload`.
- Special flags like `retry` for an action to be applied a second time just in case... And a `force_project_open` to interact with a device if the project is closed
- Refactored `gns3_project` to be more pythonic
## 1.3.0
**Enhancements:**
- Added `gns3_node_file` and `gns3_project_file` modules.
- Improved the Makefile
- Added alpine node to the tests
## 1.2.2.
**Fixes:**
- Upgrading to `gns3fy ^0.4.0`
## 1.2.1
**Enhancements:**
- No more `node_type` needed when creating nodes in a project.
## 1.2.0
**Enhancements:**
- Modules:
- `gns3_nodes_inventory`: Returns inventory-style dictionary of the nodes.
**Fixes:**
- Modules:
- Error when using the `gns3_version` module when `gns3fy` is not installed
- Tests:
- Added check for `gns3_version`
## 1.1.0
**Enhancements:**
- Roles:
- `create_lab`: Create a GNS3 Lab by creating a project and starting up the nodes
- `delete_lab`: Deletes a GNS3 Lab by stopping nodes and deleting the project
## 1.0.1
Initial push of the ansible collections. Released focused on the `gns3` module

View File

@@ -0,0 +1,299 @@
{
"files": [
{
"name": ".",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": "plugins",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": "roles",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": ".DS_Store",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "07d2bba37d01224569c017d4c7a1279b25034e29466d550ed965f82ed0aaa357",
"format": 1
},
{
"name": "LICENSE",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "ecec1ee81897773f92976da30c1fbb4414107ef182511644e86945d74e1a986f",
"format": 1
},
{
"name": "requirements.txt",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "59e53d1f4cae821cf5701a9d35babccae916321df53376fa3aa9aaced4134071",
"format": 1
},
{
"name": "CHANGELOG.md",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "8274d3baedce15723f01977a4bbf351b5b3d0773fdecb31e67fd33f4a219d866",
"format": 1
},
{
"name": "dev-requirements.txt",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "c1f58a2e2e6ad1c44048e835ebc329fb1d8b4e3e65da997defeeac119c22f44c",
"format": 1
},
{
"name": "Makefile",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "293c346df21e6637e8bc604cdd5dc7757acdace716eec4a8917be922d821e4aa",
"format": 1
},
{
"name": "README.md",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "b2b255969f71c789635f478015538bad0da15af127521fb5d98580617ae5f368",
"format": 1
},
{
"name": ".gitignore",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "2282d3966f03871ec05c50da914155bf8d715beebb8027b559a8870116fc5651",
"format": 1
},
{
"name": "galaxy.yml",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "11fdd35c3e074f92599a16e94a4425db14b0c2a741020c41cac4b15538a717f5",
"format": 1
},
{
"name": "test/playbooks",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": "test/playbooks/group_vars",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": "test/playbooks/create_files.yml",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "633a5a2b3c11c27c7264a5444da7249843490349bc9704e73828e58c75822bc4",
"format": 1
},
{
"name": "test/playbooks/node_interaction.yml",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "4d30f6988337e9be5777106bd65357b7064dcfe2345e9672fb184b909c831dff",
"format": 1
},
{
"name": "test/playbooks/delete_files.yml",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "2cc8b222450116e3ac24c959a7e9c36f4ad36073c3f9213bf81dbca769080da5",
"format": 1
},
{
"name": "test/playbooks/snapshots.yml",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "4d659f0381a8ae4130081b62ce11f25518a3881cf1f164dd7c078dc70a284d00",
"format": 1
},
{
"name": "test/playbooks/ansible.cfg",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "27762b4172718c13f2abb4b8180678875717a465e497eeae3f546e2f24556421",
"format": 1
},
{
"name": "test/playbooks/facts.yml",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "781170433cc129bb49ec0b215f280f73f35634afac0d397dda3da38bcd03da4c",
"format": 1
},
{
"name": "test/playbooks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "e66b08210468e91df3c15ef46169a2064bc26b18bf28465f1f42d464321b05a1",
"format": 1
},
{
"name": "test/playbooks/group_vars/all.yml",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "fa9871fb705af3de74be241428233020ff7d02322f5b58c0b32d05df17990e6a",
"format": 1
},
{
"name": "plugins/modules",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": "plugins/modules/gns3_facts.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "b47b546f9bcbd38128dbb7d4d24a4f1ec4a329a52cd0b2cb6acd37797bbb944a",
"format": 1
},
{
"name": "plugins/modules/gns3_project.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "857080b1b5f6b9c1e04aa6e51501f2edae3a9d4fd63a1eecbffb214cc57e12c4",
"format": 1
},
{
"name": "plugins/modules/gns3_node_file.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "83eb69764b268fee6d67decdb1fc01f915f47882b4dfcb885599d6d475ce5e47",
"format": 1
},
{
"name": "plugins/modules/gns3_node.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "03d49fac0ef8191c620929deb82d1ef2b2dcb0fe28e78d5ab1ff868b516d4e44",
"format": 1
},
{
"name": "plugins/modules/gns3_snapshot.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "f25e7a9bfdd07abea77971dbf6126238c8d870036cb9beb8668aa74698accdb4",
"format": 1
},
{
"name": "plugins/modules/gns3_version.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "470c19c7e60aed0fc7b318fa8b4433e87c0b66f5f5c6ddda0050e27e85b4abb9",
"format": 1
},
{
"name": "plugins/modules/gns3_nodes_inventory.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "7b021e9ef803a03dfe4b975a47d27bfb558b910feee4e31249bf4a8f702846ba",
"format": 1
},
{
"name": "plugins/modules/gns3_project_file.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "e85dac29bd330c67214038eb1a9098e67ce3e3e16ae62ffb447dfdaea14c7ef3",
"format": 1
},
{
"name": "roles/delete_lab",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": "roles/create_lab",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": "roles/delete_lab/tasks",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": "roles/delete_lab/meta",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": "roles/delete_lab/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "95ac2ba013e97795a2e86623b6cbce9d5e432d9a6b58216be475d1d92ebe56b4",
"format": 1
},
{
"name": "roles/delete_lab/meta/main.yml",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "9d92ea65373c0af69fd88e95f63fec9f4001e04128277f340a75932e0500eade",
"format": 1
},
{
"name": "roles/create_lab/tasks",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": "roles/create_lab/meta",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": "roles/create_lab/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "ed0bd1a5303442348d549b2aa5cc046abdc675be5d4dc8f4fa1d87f3fe44b621",
"format": 1
},
{
"name": "roles/create_lab/meta/main.yml",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "14579b38e8cee8b07fe7b2cd337ee0611e949864ff84b1d8a1bc61ffb54e06c4",
"format": 1
},
{
"name": ".vscode/settings.json",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "4d5e35cc829fff24f9d120ef957b2ce72fbcc854577ae11ae7534f79bc21ad10",
"format": 1
}
],
"format": 1
}

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 David Flores
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,37 @@
{
"collection_info": {
"namespace": "davidban77",
"name": "gns3",
"version": "1.5.0",
"license": [
"MIT"
],
"description": "Module to interact with GNS3 server REST API based on gns3fy",
"repository": "https://github.com/davidban77/ansible-collection-gns3",
"documentation": "https://github.com/davidban77/ansible-collection-gns3/blob/master/README.md",
"homepage": "https://github.com/davidban77/ansible-collection-gns3",
"issues": "https://github.com/davidban77/ansible-collection-gns3/issues",
"authors": [
"David Flores <davidflores7_8@hotmail.com>"
],
"tags": [
"gns3",
"gns3fy",
"networking",
"rest",
"system",
"netops"
],
"license_file": null,
"readme": "README.md",
"dependencies": {}
},
"file_manifest_file": {
"name": "FILES.json",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "f4608d42f56cf93d10743fa414e9a358810137998c8a7129f0aa9a378344aa50",
"format": 1
},
"format": 1
}

View File

@@ -0,0 +1,30 @@
VERSION=1.5.0
build:
rm -rf releases/
mazer build
publish: build
mazer publish releases/davidban77-gns3-${VERSION}.tar.gz
test-create-lab:
cd test/playbooks; ansible-playbook main.yml -e execute=create
test-node-interaction:
cd test/playbooks; ansible-playbook node_interaction.yml
test-create-files:
cd test/playbooks; ansible-playbook create_files.yml
test-snapshots:
cd test/playbooks; ansible-playbook snapshots.yml
test-delete-files:
cd test/playbooks; ansible-playbook delete_files.yml
test-delete-lab:
cd test/playbooks; ansible-playbook main.yml -e execute=delete
test-create-all: test-create-lab test-create-files test-node-interaction test-snapshots
test-delete-all: test-delete-files test-delete-lab

View File

@@ -0,0 +1,207 @@
# Ansible Module for GNS3
[Ansible-Galaxy collections](https://galaxy.ansible.com/davidban77/gns3) repository for GNS3 Server REST API using [gns3fy - see the docs](https://davidban77.github.io/gns3fy/).
## Installation
For the module to be used you need to have installed [gns3fy](https://github.com/davidban77/gns3fy)
```
pip install gns3fy
```
This collections is packaged under ansible-galaxy, so to install it you need [mazer from Ansible Projects](https://galaxy.ansible.com/docs/mazer/index.html):
```
mazer install davidban77.gns3
```
## Features
- Open/closes projects.
- Starts/stops all nodes inside a project, or it can be done sequentially with a delay factor.
- Creates/Updates projects with nodes and links specified as variables in a playbook.
- Deletes projects safely by stopping nodes, if there are any, then closing the project and finally deleting it.
- Creates/Deletes/Restores snapshots of projects.
- Retrieves information about available emulators on the GNS3 server compute, as well as available images, console ports, version, etc..
- Idempotency is present in all actions. An example could be reflected in a playbook that creates a project with nodes and links, these settings will not be executed again on a rerun (and by settings I mean projects settings, nodes and links)/
## Modules
These are the modules provided with this collection:
- `gns3_version`: Retrieves GNS3 server version. (**TO BE DEPRECATED** with the `gns3_facts` module)
- `gns3_facts`: Retrieves the compute(s) information of a GNS3 server
- `gns3_project`: Module to interact with GNS3 server projects
- It opens/closes projects and performs basic turnup/teradown operations on nodes.
- It creates/updates or deletes projects, with the respective nodes and links specified
- `gns3_project_file`: Updates/creates a file on a project directory.
- `gns3_snapshot`: Module that interacts with snapshots of a project on GNS3 server.
- `gns3_node`: Module to operate a node in a GNS3 server project.
- `gns3_node_file`: Updates/creates a file on a node directory.
- `gns3_nodes_inventory`: Retrieves GNS3 a project nodes console information.
## Examples: using the module
Here are some examples of how to use the module.
#### Get server version
```yaml
---
- host: localhost
# Call the collections to use the respective modules
collections:
- davidban77.gns3
vars:
gns3_url: http://localhost
tasks:
- name: Get the server facts
gns3_facts:
url: "{{ gns3_url }}"
port: 3080
get_images: all
get_compute_ports: yes
register: result
- debug: var=result
```
#### Get nodes_inventory information from a project
```yaml
---
- host: localhost
# Call the collections to use the respective modules
collections:
- davidban77.gns3
vars:
gns3_url: http://localhost
tasks:
- name: Get the server version
gns3_nodes_inventory:
url: "{{ gns3_url }}"
project_name: lab_example
register: result
- debug: var=result
```
#### Manipulate GNS3 projects
```yaml
---
# Open a GNS3 project
- name: Start lab
gns3_project:
url: "{{ gns3_url }}"
state: opened
project_name: lab_example
# Stop all nodes inside an open project
- name: Stop nodes
gns3_project:
url: "{{ gns3_url }}"
state: opened
project_name: lab_example
nodes_state: stopped
nodes_strategy: all
poll_wait_time: 5
# Open a GNS3 project and start nodes one by one with a delay of 10sec between them
- name: Start nodes one by one
gns3_project:
url: "{{ gns3_url }}"
state: opened
project_name: lab_example
nodes_state: started
nodes_strategy: one_by_one
nodes_delay: 10
# Close a GNS3 project
- name: Stop lab
gns3_project:
url: "{{ gns3_url }}"
state: closed
project_id: "UUID-SOMETHING-1234567"
```
#### Create and delete projects
```yaml
---
# Create a GNS3 project given nodes and links specifications
- name: Create a project
gns3_project:
url: "{{ gns3_url }}"
state: present
project_name: new_lab
nodes_spec:
- name: alpine-1
template: alpine
- name: alpine-2
template: alpine
links_spec:
- ['alpine-1', 'eth0', 'alpine-2', 'eth1']
# Delete a GNS3 project
- name: Delete project
gns3_project:
url: "{{ gns3_url }}"
state: absent
project_name: new_lab
```
## Examples: using the roles
There are also some convinient roles that you can use to manage your labs. Here is an example playbook:
`main.yml`
```yaml
- hosts: localhost
tasks:
- import_role:
name: create_lab
when: execute == "create"
- import_role:
name: delete_lab
when: execute == "delete"
```
This way you can call and switch the behaviour of the playbook:
**Create the lab**
```
ansible-playbook main.yml -e execute=create
```
Or **delete the lab**
```
ansible-playbook main.yml -e execute=delete
```
Here is the example variable file which specifies the naming convention used. You can see that the variable names come from the module itself with only `gns3_`
```yaml
---
gns3_url: "http://dev_gns3server"
gns3_project_name: test_ansible
gns3_nodes_spec:
- name: veos-1
template: "vEOS-4.21.5F"
- name: veos-2
template: "vEOS-4.21.5F"
- name: ios-1
template: "IOU-15.4"
- name: ios-2
template: "IOU-15.4"
gns3_nodes_strategy: one_by_one
gns3_links_spec:
- ["veos-1", "Ethernet1", "veos-2", "Ethernet1"]
- ["veos-1", "Ethernet2", "ios-1", "Ethernet1/0"]
- ["veos-2", "Ethernet2", "ios-2", "Ethernet1/0"]
- ["ios-1", "Ethernet1/2", "ios-2", "Ethernet1/2"]
```
### More examples
For more examples like create an `/etc/network/interfaces` file for an `alpine` docker node to configure its network interfaces, or restore a project to an specific snapshot, you can go to the `test/playbooks` directory.

View File

@@ -0,0 +1,4 @@
-r requirements.txt
flake8
black

View File

@@ -0,0 +1,21 @@
namespace: "davidban77"
name: "gns3"
version: "1.5.0"
readme: "README.md"
description: "Module to interact with GNS3 server REST API based on gns3fy"
authors:
- "David Flores <davidflores7_8@hotmail.com>"
dependencies:
license:
- "MIT"
tags:
- gns3
- gns3fy
- networking
- rest
- system
- netops
repository: "https://github.com/davidban77/ansible-collection-gns3"
documentation: "https://github.com/davidban77/ansible-collection-gns3/blob/master/README.md"
homepage: "https://github.com/davidban77/ansible-collection-gns3"
issues: "https://github.com/davidban77/ansible-collection-gns3/issues"

View File

@@ -0,0 +1,187 @@
#!/usr/bin/env python
ANSIBLE_METADATA = {
"metadata_version": "1.1",
"status": ["preview"],
"supported_by": "community",
}
DOCUMENTATION = """
---
module: gns3_facts
short_description: Module that retrieves the compute(s) information of a GNS3 server
version_added: '2.8'
description:
- Module that retrieves the compute(s) information of a GNS3 server
requirements: [ gns3fy ]
author:
- David Flores (@davidban77)
options:
url:
description:
- URL target of the GNS3 server
required: true
type: str
port:
description:
- TCP port to connect to server REST API
type: int
default: 3080
user:
description:
- User to connect to GNS3 server
type: str
password:
description:
- Password to connect to GNS3 server
type: str
get_images:
description:
- If set it will also retrieve the images of the specified emulator unless
- is set to 'all', in which case will retrieve from all emulators. For a
- list of available emulators, visit the GNS3 API information
type: str
get_compute_ports:
description:
- If set it will retrieve the console_ports and udp_ports of the compute
type: bool
"""
EXAMPLES = """
# Retrieves all the information from the computes of GNS3 server
- name: Retrieve all the facts of a GNS3 server computes
gns3_facts:
url: http://localhost
get_images: all
get_compute_ports: yes
register: computes_info
- debug: var=computes_info
# Retrieves only basic facts data of the GNS3 server computes
- gns3_facts:
url: http://localhost
register: computes_info
- debug: var=computes_info
"""
RETURN = """
compute_id:
description: Server identifier
type: str
name:
description: Server name
type: str
host:
description: Server host
type: str
capabilities:
description: Object that describes what the server supports
type: dict
connected:
description: Whether the controller is connected to the compute or not
type: bool
cpu_usage_percent:
description: CPU usage of the compute
type: float
memory_usage_percent:
description: RAM usage of the compute
type: int
port:
description: Server port
type: int
protocol:
description: Protocol used (http, https)
type: str
user:
description: User for authentication
type: str
last_error:
description: Last error on the compute
type: str
images:
description: Images configured on the compute depending on the emulator (optional)
type: dict
compute_ports:
description: Ports used by the compute (console and udp ports) (optional)
type: dict
"""
import traceback
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
GNS3FY_IMP_ERR = None
try:
from gns3fy import Gns3Connector
HAS_GNS3FY = True
except Exception:
HAS_GNS3FY = False
GNS3FY_IMP_ERR = traceback.format_exc()
def main():
module = AnsibleModule(
argument_spec=dict(
url=dict(type="str", required=True),
port=dict(type="int", default=3080),
user=dict(type="str", default=None),
password=dict(type="str", default=None, no_log=True),
get_images=dict(type="str", default=None),
get_compute_ports=dict(type="bool", default=False),
)
)
result = dict(changed=False)
if not HAS_GNS3FY:
module.fail_json(msg=missing_required_lib("gns3fy"), exception=GNS3FY_IMP_ERR)
server_url = module.params["url"]
server_port = module.params["port"]
server_user = module.params["user"]
server_password = module.params["password"]
get_images = module.params["get_images"]
get_compute_ports = module.params["get_compute_ports"]
try:
# Create server session
server = Gns3Connector(
url=f"{server_url}:{server_port}", user=server_user, cred=server_password
)
computes = server.get_computes()
for compute in computes:
# Images
if get_images:
compute["images"] = dict()
if get_images == "all":
for emulator in compute["capabilities"]["node_types"]:
try:
compute["images"][emulator] = server.get_compute_images(
emulator=emulator, compute_id=compute["compute_id"]
)
except Exception as err:
if "404" in str(err):
# Contine if no image dir is set for that emulator
continue
else:
compute["images"][get_images] = server.get_compute_images(
emulator=get_images, compute_id=compute["compute_id"]
)
# Compute ports
if get_compute_ports:
compute["compute_ports"] = server.get_compute_ports(
compute_id=compute["compute_id"]
)
result["facts"] = computes
module.exit_json(**result)
except Exception as err:
module.fail_json(msg=str(err), **result)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,272 @@
#!/usr/bin/env python
ANSIBLE_METADATA = {
"metadata_version": "1.2",
"status": ["preview"],
"supported_by": "community",
}
DOCUMENTATION = """
---
module: gns3_node
short_description: Module to operate a node in a GNS3 server project
version_added: '2.8'
description:
- Module to operate a node in a GNS3 server project.
- It starts/stops/suspend/reloads a node.
requirements: [ gns3fy ]
author:
- David Flores (@davidban77)
options:
url:
description:
- URL target of the GNS3 server
required: true
type: str
port:
description:
- TCP port to connect to server REST API
type: int
default: 3080
user:
description:
- User to connect to GNS3 server
type: str
password:
description:
- Password to connect to GNS3 server
type: str
project_name:
description:
- Project name
type: str
project_id:
description:
- Project ID
type: str
node_name:
description:
- Node name
type: str
node_id:
description:
- Node ID
type: str
state:
description:
- State of the node, it can be:
- '- C(started): Starts a node'
- '- C(stopped): Stops a node'
- '- C(suspended): Suspends a node'
- '- C(reload): Special non-idempotent action that reloads a node'
type: str
choices: ['started', 'stopped', 'suspended', 'reload']
retry:
description:
- Retries an action based on the state, if true and state is set to reload
- it will reload the device and try to start it again if the status was not
- changed
type: bool
default: false
poll_wait_time:
description:
- Delay in seconds to wait to poll nodes when they are started/stopped.
- Used when I(nodes_state) is C(started)/C(stopped)
type: int
default: 5
force_project_open:
description:
- It will open the project (if closed) to interact with the device.
- Otherwise it will throw out an error
type: bool
default: false
"""
EXAMPLES = """
# Open a GNS3 project and start router01 node
- name: Start node
gns3_node:
url: http://localhost
project_name: lab_example
node_name: router01
node_state: started
force_project_open: true
# Stop a node and wait 10 seconds to poll for status
- name: Stop node
gns3_node:
url: http://localhost
project_name: lab_example
node_name: router01
state: stopped
# Suspend a node based on UUID
- name: Suspend node
gns3_node:
url: http://localhost
project_name: lab_example
node_id: 'ROUTER-UUID-SOMETHING-1234567'
state: suspended
# Reload a node and apply a retry to start if needed
- name: Stop lab
gns3_node:
url: http://localhost
project_id: 'PROJECT-UUID-SOMETHING-1234567'
node_name: router01
state: reload
retry: true
poll_wait_time: 30
"""
RETURN = """
name:
description: Project name
type: str
project_id:
description: Project UUID
type: str
node_id:
description: Node UUID
type: str
status:
description: Project status. Possible values: opened, closed
type: str
node_directory:
description: Path of the node on the server (works only with compute=local)
type: str
node_type:
description: Network node type
type: str
"""
import time
import traceback
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
GNS3FY_IMP_ERR = None
try:
from gns3fy import Gns3Connector, Project
HAS_GNS3FY = True
except Exception:
HAS_GNS3FY = False
GNS3FY_IMP_ERR = traceback.format_exc()
def return_node_data(node):
"Returns the node main attributes"
return dict(
name=node.name,
project_id=node.project_id,
node_id=node.node_id,
status=node.status,
node_directory=node.node_directory,
node_type=node.node_type,
)
def state_verification(expected_state, node, retry=False, poll_wait_time=5):
"Verifies node state and returns a changed attribute"
if expected_state == "started" and node.status != "started":
node.start()
if node.status != "started" and retry:
node.start()
return True
elif expected_state == "stopped" and node.status != "stopped":
node.stop()
if node.status != "stopped" and retry:
node.stop()
return True
elif expected_state == "suspended" and node.status != "suspended":
node.suspend()
if node.status != "suspended" and retry:
node.suspend()
return True
elif expected_state == "reload":
node.reload()
time.sleep(poll_wait_time)
node.get()
if node.status != "started" and retry:
node.start()
return True
return False
def main():
module = AnsibleModule(
argument_spec=dict(
url=dict(type="str", required=True),
port=dict(type="int", default=3080),
user=dict(type="str", default=None),
password=dict(type="str", default=None, no_log=True),
state=dict(
type="str",
required=True,
choices=["started", "stopped", "suspended", "reload"],
),
project_name=dict(type="str", default=None),
project_id=dict(type="str", default=None),
node_name=dict(type="str", default=None),
node_id=dict(type="str", default=None),
retry=dict(type="bool", default=False),
poll_wait_time=dict(type="int", default=5),
force_project_open=dict(type="bool", default=False),
),
required_one_of=[["project_name", "project_id"], ["node_name", "node_id"]],
)
result = dict(changed=False)
if not HAS_GNS3FY:
module.fail_json(msg=missing_required_lib("gns3fy"), exception=GNS3FY_IMP_ERR)
server_url = module.params["url"]
server_port = module.params["port"]
server_user = module.params["user"]
server_password = module.params["password"]
state = module.params["state"]
project_name = module.params["project_name"]
project_id = module.params["project_id"]
node_name = module.params["node_name"]
node_id = module.params["node_id"]
retry = module.params["retry"]
poll_wait_time = module.params["poll_wait_time"]
force_project_open = module.params["force_project_open"]
try:
# Create server session
server = Gns3Connector(
url=f"{server_url}:{server_port}", user=server_user, cred=server_password
)
# Define the project
if project_name is not None:
project = Project(name=project_name, connector=server)
elif project_id is not None:
project = Project(project_id=project_id, connector=server)
if project is None:
module.fail_json(msg="Could not retrieve project. Check name", **result)
project.get()
if project.status != "opened" and force_project_open:
project.open()
# Retrieve node
if node_name is not None:
node = project.get_node(name=node_name)
elif node_id is not None:
node = project.get_node(node_id=node_id)
if node is None:
module.fail_json(msg="Could not retrieve node. Check name", **result)
except Exception as err:
module.fail_json(msg=str(err), **result)
# Apply state change
result["changed"] = state_verification(
expected_state=state, node=node, retry=retry, poll_wait_time=poll_wait_time
)
# Return the node data
result["node"] = return_node_data(node)
module.exit_json(**result)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,192 @@
#!/usr/bin/env python
ANSIBLE_METADATA = {
"metadata_version": "1.2",
"status": ["preview"],
"supported_by": "community",
}
DOCUMENTATION = """
---
module: gns3_node_file
short_description: Updates/creates a file on a node directory
version_added: '2.8'
description:
- "Updates/creates a file on a node directory of a GNS3 project"
requirements: [ gns3fy ]
author:
- David Flores (@davidban77)
options:
url:
description:
- URL target of the GNS3 server
required: true
type: str
port:
description:
- TCP port to connect to server REST API
type: int
default: 3080
user:
description:
- User to connect to GNS3 server
type: str
password:
description:
- Password to connect to GNS3 server
type: str
project_name:
description:
- Project name
type: str
project_id:
description:
- Project ID
type: str
node_name:
description:
- Node name
type: str
node_id:
description:
- Node ID
type: str
data:
description:
- The text to insert.
type: str
dest:
description:
- Node destination path. Like 'etc/network/interfaces'
type: str
required: true
state:
description:
- If the file should present or absent
type: str
choices: ['present', 'absent']
default: present
"""
EXAMPLES = """
# Retrieve the GNS3 server version
- name: Get the server version
gns3_node_file:
url: http://localhost
port: 3080
project_name: test_lab
node_name: alpine-1
data: |
auto eth0
iface eth0 inet dhcp
dest: /etc/network/interfaces
"""
import traceback
GNS3FY_IMP_ERR = None
try:
from gns3fy import Gns3Connector, Project
HAS_GNS3FY = True
except ImportError:
GNS3FY_IMP_ERR = traceback.format_exc()
HAS_GNS3FY = False
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
def node_write_file(module, node, path, data):
"Writes text data into specified path of the node"
try:
node.write_file(path=path, data=data)
except Exception as err:
module.fail_json(msg=str(err), exception=traceback.format_exc())
def main():
module = AnsibleModule(
argument_spec=dict(
url=dict(type="str", required=True),
port=dict(type="int", default=3080),
user=dict(type="str", default=None),
password=dict(type="str", default=None, no_log=True),
project_name=dict(type="str", default=None),
project_id=dict(type="str", default=None),
node_name=dict(type="str", default=None),
node_id=dict(type="str", default=None),
data=dict(type="str", default=None),
dest=dict(type="str", required=True),
state=dict(type="str", choices=["present", "absent"], default="present"),
),
required_one_of=[["project_name", "project_id"], ["node_name", "node_id"]],
)
if not HAS_GNS3FY:
module.fail_json(msg=missing_required_lib("gns3fy"), exception=GNS3FY_IMP_ERR)
result = dict(changed=False)
server_url = module.params["url"]
server_port = module.params["port"]
server_user = module.params["user"]
server_password = module.params["password"]
project_name = module.params["project_name"]
project_id = module.params["project_id"]
node_name = module.params["node_name"]
node_id = module.params["node_id"]
data = module.params["data"]
dest = module.params["dest"]
state = module.params["state"]
if state == "present" and data is None:
module.fail_json(msg="Parameter needs to be passed: data", **result)
# Create server session
server = Gns3Connector(
url=f"{server_url}:{server_port}", user=server_user, cred=server_password
)
# Define the project
if project_name is not None:
project = Project(name=project_name, connector=server)
elif project_id is not None:
project = Project(project_id=project_id, connector=server)
if project is None:
module.fail_json(msg="Could not retrieve project. Check name", **result)
# Retrieve project info
project.get()
# Define the Node
if node_name is not None:
node = project.get_node(name=node_name)
elif node_id is not None:
node = project.get_node(node_id=node_id)
if node is None:
module.fail_json(msg="Could not retrieve node. Check name", **result)
# Try to get file data
try:
file_data = node.get_file(path=dest)
except Exception as err:
if "not found" in str(err):
file_data = None
else:
module.fail_json(msg=str(err), exception=traceback.format_exc())
if state == "absent":
if file_data is None or file_data == "":
result.update(changed=False)
else:
# Delete node file data
# As of now (GNS3 v2.2.rc5) the DELETE method is not allowed
node_write_file(module, node, dest, "")
result.update(changed=True)
elif state == "present":
if file_data == data:
result.update(changed=False)
else:
node_write_file(module, node, dest, data)
result.update(changed=True)
module.exit_json(**result)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,128 @@
#!/usr/bin/env python
ANSIBLE_METADATA = {
"metadata_version": "1.2",
"status": ["preview"],
"supported_by": "community",
}
DOCUMENTATION = """
---
module: gns3_nodes_inventory
short_description: Retrieves GNS3 a project nodes console information
version_added: '2.8'
description:
- "Retrieves nodes inventory information from a GNS3 project"
requirements: [ gns3fy ]
author:
- David Flores (@davidban77)
options:
url:
description:
- URL target of the GNS3 server
required: true
type: str
port:
description:
- TCP port to connect to server REST API
type: int
default: 3080
user:
description:
- User to connect to GNS3 server
type: str
password:
description:
- Password to connect to GNS3 server
type: str
project_name:
description:
- Project name
type: str
project_id:
description:
- Project ID
type: str
"""
EXAMPLES = """
# Retrieve the GNS3 server version
- name: Get the server version
gns3_nodes_inventory:
url: http://localhost
port: 3080
project_name: test_lab
register: nodes_inventory
- debug: var=nodes_inventory
"""
RETURN = """
nodes_inventory:
description: Dictionary that contain: name, server, console_port, console_type,
type and template of each node
type: dict
total_nodes:
description: Total number of nodes
type: int
"""
import traceback
GNS3FY_IMP_ERR = None
try:
from gns3fy import Gns3Connector, Project
HAS_GNS3FY = True
except ImportError:
GNS3FY_IMP_ERR = traceback.format_exc()
HAS_GNS3FY = False
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
def main():
module = AnsibleModule(
argument_spec=dict(
url=dict(type="str", required=True),
port=dict(type="int", default=3080),
user=dict(type="str", default=None),
password=dict(type="str", default=None, no_log=True),
project_name=dict(type="str", default=None),
project_id=dict(type="str", default=None),
),
required_one_of=[["project_name", "project_id"]],
)
if not HAS_GNS3FY:
module.fail_json(msg=missing_required_lib("gns3fy"), exception=GNS3FY_IMP_ERR)
result = dict(changed=False, nodes_inventory=None, total_nodes=None)
server_url = module.params["url"]
server_port = module.params["port"]
server_user = module.params["user"]
server_password = module.params["password"]
project_name = module.params["project_name"]
project_id = module.params["project_id"]
# Create server session
server = Gns3Connector(
url=f"{server_url}:{server_port}", user=server_user, cred=server_password
)
# Define the project
if project_name is not None:
project = Project(name=project_name, connector=server)
elif project_id is not None:
project = Project(project_id=project_id, connector=server)
# Retrieve project info
project.get()
nodes_inventory = project.nodes_inventory()
result.update(
nodes_inventory=nodes_inventory, total_nodes=len(nodes_inventory.keys())
)
module.exit_json(**result)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,434 @@
#!/usr/bin/env python
ANSIBLE_METADATA = {
"metadata_version": "1.4",
"status": ["preview"],
"supported_by": "community",
}
DOCUMENTATION = """
---
module: gns3_project
short_description: Module to interact with GNS3 server projects
version_added: '2.8'
description:
- 'Module to interact with GNS3 server projects.
- It is using the L(gns3fy library,https://davidban77.github.io/gns3fy/)'
- It opens/closes projects and performs basic turnup/teradown operations on nodes.
- It creates/updates or deletes projects.
requirements: [ gns3fy ]
author:
- David Flores (@davidban77)
options:
url:
description:
- URL target of the GNS3 server
required: true
type: str
port:
description:
- TCP port to connect to server REST API
type: int
default: 3080
user:
description:
- User to connect to GNS3 server
type: str
password:
description:
- Password to connect to GNS3 server
type: str
state:
description:
- State of the project to be on the GNS3 server
- '- C(opened): Opens a project and turns up nodes'
- '- C(closed): Closes a project and turns down nodes'
- '- C(present): Creates/update a project on the server'
- '- C(absent): Deletes a project on the server'
type: str
choices: ['opened', 'closed', 'present', 'absent']
project_name:
description:
- Project name
type: str
project_id:
description:
- Project ID
type: str
nodes_state:
description:
- Starts/stops nodes on the project.
- Used when I(state) is C(opened)/C(closed)
type: str
choices: ['started', 'stopped']
nodes_strategy:
description:
- Start/stop strategy of the devices defined on the project.
- '- C(all): It starts/stops all nodes at once'
- '- C(one_by_one): It starts/stops nodes serialy using I(nodes_delay) time
between each action'
- Used when I(state) is C(opened)/C(closed)
type: str
choices: ['all', 'one_by_one']
default: 'all'
nodes_delay:
description:
- Delay time in seconds to wait between nodes start/stop
- Used when I(nodes_strategy) is C(one_by_one)
type: int
default: 10
poll_wait_time:
description:
- Delay in seconds to wait to poll nodes when they are started/stopped.
- Used when I(nodes_state) is C(started)/C(stopped)
type: int
default: 5
nodes_spec:
description:
- List of dictionaries specifying the nodes properties.
- '- Mandatory attributes: C(name), C(node_type) and C(template).'
- '- Optional attributes: C(compute_id). It defaults to C(local)'
type: list
links_spec:
description:
- 'List of lists specifying the links endpoints. Example: C(- ["alpine-1",
"eth0", "alpine-2", "eth0"])'
- 'Mandatory attributes: C(node_a), C(port_a), C(node_b) and C(port_b)'
type: list
"""
EXAMPLES = """
# Open a GNS3 project
- name: Start lab
gns3_project:
url: http://localhost
state: opened
project_name: lab_example
# Stop all nodes inside an open project
- name: Stop nodes
gns3_project:
url: http://localhost
state: opened
project_name: lab_example
nodes_state: stopped
nodes_strategy: all
poll_wait_time: 5
# Open a GNS3 project and start nodes one by one with a delay of 10sec between them
- name: Start nodes one by one
gns3_project:
url: http://localhost
state: opened
project_name: lab_example
nodes_state: started
nodes_strategy: one_by_one
nodes_delay: 10
# Close a GNS3 project
- name: Stop lab
gns3_project:
url: http://localhost
state: closed
project_id: 'UUID-SOMETHING-1234567'
# Create a GNS3 project
- name: Create a project given nodes and links specs
gns3_project:
url: http://localhost
state: present
project_name: new_lab
nodes_spec:
- name: alpine-1
node_type: docker
template: alpine
- name: alpine-2
node_type: docker
template: alpine
links_spec:
- ('alpine-1', 'eth0', 'alpine-2', 'eth1')
# Delete a GNS3 project
- name: Delete project
gns3_project:
url: http://localhost
state: absent
project_name: new_lab
"""
RETURN = """
name:
description: Project name
type: str
project_id:
description: Project UUID
type: str
status:
description: Project status. Possible values: opened, closed
type: str
path:
description: Path of the project on the server (works only with compute=local)
type: str
auto_close:
description: Project auto close when client cut off the notifications feed
type: bool
auto_open:
description: Project open when GNS3 start
type: bool
auto_start:
description: Project start when opened
type: bool
filename:
description: Project filename
type: str
"""
import time
import traceback
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
GNS3FY_IMP_ERR = None
try:
from gns3fy import Gns3Connector, Project
HAS_GNS3FY = True
except Exception:
HAS_GNS3FY = False
GNS3FY_IMP_ERR = traceback.format_exc()
def return_project_data(project):
"Returns the project main attributes"
return dict(
name=project.name,
project_id=project.project_id,
status=project.status,
path=project.path,
auto_close=project.auto_close,
auto_open=project.auto_open,
auto_start=project.auto_start,
filename=project.filename,
)
# def nodes_state_verification(module, project, result):
def nodes_state_verification(
expected_nodes_state, nodes_strategy, nodes_delay, poll_wait_time, project
):
"Verifies each node state and returns a changed attribute"
nodes_statuses = [node.status for node in project.nodes]
# Verify if nodes do not match expected state
if expected_nodes_state == "started" and any(
status == "stopped" for status in nodes_statuses
):
# Turnup the nodes based on strategy
if nodes_strategy == "all":
project.start_nodes(poll_wait_time=poll_wait_time)
elif nodes_strategy == "one_by_one":
for node in project.nodes:
if node.status != "started":
node.start()
time.sleep(nodes_delay)
return True
elif expected_nodes_state == "stopped" and any(
status == "started" for status in nodes_statuses
):
# Shutdown nodes based on strategy
if nodes_strategy == "all":
project.stop_nodes(poll_wait_time=poll_wait_time)
elif nodes_strategy == "one_by_one":
for node in project.nodes:
if node.status != "stopped":
node.stop()
time.sleep(nodes_delay)
return True
return False
def create_node(node_spec, project, module):
"Creates the node specified in nodes_spec"
# If exceptions occur then print them out in ansible format
try:
project.create_node(**node_spec)
except Exception as err:
module.fail_json(msg=str(err), exception=traceback.format_exc())
def create_link(link_spec, project, module):
"Creates the node specified in nodes_spec"
# If exceptions occur then print them out in ansible format
try:
project.create_link(*link_spec)
except ValueError as err:
if "At least one port is used" in str(err):
return False
else:
module.fail_json(msg=str(err), exception=traceback.format_exc())
except Exception as err:
module.fail_json(msg=str(err), exception=traceback.format_exc())
return True
def main():
module = AnsibleModule(
argument_spec=dict(
url=dict(type="str", required=True),
port=dict(type="int", default=3080),
user=dict(type="str", default=None),
password=dict(type="str", default=None, no_log=True),
state=dict(
type="str",
required=True,
choices=["opened", "closed", "present", "absent"],
),
project_name=dict(type="str", default=None),
project_id=dict(type="str", default=None),
nodes_state=dict(type="str", choices=["started", "stopped"]),
nodes_strategy=dict(
type="str", choices=["all", "one_by_one"], default="all"
),
nodes_delay=dict(type="int", default=10),
poll_wait_time=dict(type="int", default=5),
nodes_spec=dict(type="list"),
links_spec=dict(type="list"),
),
supports_check_mode=True,
required_one_of=[["project_name", "project_id"]],
required_if=[["nodes_strategy", "one_by_one", ["nodes_delay"]]],
)
result = dict(changed=False)
if not HAS_GNS3FY:
module.fail_json(msg=missing_required_lib("gns3fy"), exception=GNS3FY_IMP_ERR)
if module.check_mode:
module.exit_json(**result)
server_url = module.params["url"]
server_port = module.params["port"]
server_user = module.params["user"]
server_password = module.params["password"]
state = module.params["state"]
project_name = module.params["project_name"]
project_id = module.params["project_id"]
nodes_state = module.params["nodes_state"]
nodes_strategy = module.params["nodes_strategy"]
nodes_delay = module.params["nodes_delay"]
poll_wait_time = module.params["poll_wait_time"]
nodes_spec = module.params["nodes_spec"]
links_spec = module.params["links_spec"]
try:
# Create server session
server = Gns3Connector(
url=f"{server_url}:{server_port}", user=server_user, cred=server_password
)
# Define the project
if project_name is not None:
project = Project(name=project_name, connector=server)
elif project_id is not None:
project = Project(project_id=project_id, connector=server)
except Exception as err:
module.fail_json(msg=str(err), **result)
#  Retrieve project information
try:
project.get()
pr_exists = True
except Exception as err:
pr_exists = False
reason = str(err)
if state == "opened":
if pr_exists:
if project.status != "opened":
# Open project
project.open()
# Now verify nodes
if nodes_state is not None:
# Change flag based on the nodes state
result["changed"] = nodes_state_verification(
expected_nodes_state=nodes_state,
nodes_strategy=nodes_strategy,
nodes_delay=nodes_delay,
poll_wait_time=poll_wait_time,
project=project,
)
else:
# Means that nodes are not taken into account for idempotency
result["changed"] = True
# Even if the project is open if nodes_state has been set, check it
else:
if nodes_state is not None:
result["changed"] = nodes_state_verification(
expected_nodes_state=nodes_state,
nodes_strategy=nodes_strategy,
nodes_delay=nodes_delay,
poll_wait_time=poll_wait_time,
project=project,
)
else:
module.fail_json(msg=reason, **result)
elif state == "closed":
if pr_exists:
if project.status != "closed":
# Close project
project.close()
result["changed"] = True
else:
module.fail_json(msg=reason, **result)
elif state == "present":
if pr_exists:
if nodes_spec is not None:
# Need to verify if nodes exist
_nodes_already_created = [node.name for node in project.nodes]
for node_spec in nodes_spec:
if node_spec["name"] not in _nodes_already_created:
# Open the project in case it was closed
project.open()
create_node(node_spec, project, module)
result["changed"] = True
if links_spec is not None:
for link_spec in links_spec:
project.open()
# Trigger another get to refresh nodes attributes
project.get()
# Link verification is already built in the library
created = create_link(link_spec, project, module)
if created:
result["changed"] = True
else:
# Create project
project.create()
# Nodes section
if nodes_spec is not None:
for node_spec in nodes_spec:
create_node(node_spec, project, module)
# Links section
if links_spec is not None:
for link_spec in links_spec:
create_link(link_spec, project, module)
result["changed"] = True
elif state == "absent":
if pr_exists:
# Stop nodes and close project to perform delete gracefully
if project.status != "opened":
# Project needs to be opened in order to be deleted...
project.open()
project.stop_nodes(poll_wait_time=0)
project.delete()
result["changed"] = True
else:
module.exit_json(**result)
# Return the project data
result["project"] = return_project_data(project)
module.exit_json(**result)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,170 @@
#!/usr/bin/env python
ANSIBLE_METADATA = {
"metadata_version": "1.2",
"status": ["preview"],
"supported_by": "community",
}
DOCUMENTATION = """
---
module: gns3_project_file
short_description: Updates/creates a file on a project directory
version_added: '2.8'
description:
- "Updates/creates a file on a project directory on the GNS3 server"
requirements: [ gns3fy ]
author:
- David Flores (@davidban77)
options:
url:
description:
- URL target of the GNS3 server
required: true
type: str
port:
description:
- TCP port to connect to server REST API
type: int
default: 3080
user:
description:
- User to connect to GNS3 server
type: str
password:
description:
- Password to connect to GNS3 server
type: str
project_name:
description:
- Project name
type: str
project_id:
description:
- Project ID
type: str
data:
description:
- The text to insert.
type: str
dest:
description:
- Node destination path. Like 'etc/network/interfaces'
type: str
required: true
state:
description:
- If the file should present or absent
type: str
choices: ['present', 'absent']
default: present
"""
EXAMPLES = """
# Retrieve the GNS3 server version
- name: Get the server version
gns3_project_file:
url: http://localhost
port: 3080
project_name: test_lab
data: |
Hello this is a README!
dest: README.txt
"""
import traceback
GNS3FY_IMP_ERR = None
try:
from gns3fy import Gns3Connector, Project
HAS_GNS3FY = True
except ImportError:
GNS3FY_IMP_ERR = traceback.format_exc()
HAS_GNS3FY = False
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
def project_write_file(module, project, path, data):
"Writes text data into specified path of the project"
try:
project.write_file(path=path, data=data)
except Exception as err:
module.fail_json(msg=str(err), exception=traceback.format_exc())
def main():
module = AnsibleModule(
argument_spec=dict(
url=dict(type="str", required=True),
port=dict(type="int", default=3080),
user=dict(type="str", default=None),
password=dict(type="str", default=None, no_log=True),
project_name=dict(type="str", default=None),
project_id=dict(type="str", default=None),
data=dict(type="str", default=None),
dest=dict(type="str", required=True),
state=dict(type="str", choices=["present", "absent"], default="present"),
),
required_one_of=[["project_name", "project_id"]],
)
if not HAS_GNS3FY:
module.fail_json(msg=missing_required_lib("gns3fy"), exception=GNS3FY_IMP_ERR)
result = dict(changed=False)
server_url = module.params["url"]
server_port = module.params["port"]
server_user = module.params["user"]
server_password = module.params["password"]
project_name = module.params["project_name"]
project_id = module.params["project_id"]
data = module.params["data"]
dest = module.params["dest"]
state = module.params["state"]
if state == "present" and data is None:
module.fail_json(msg="Parameter needs to be passed: data", **result)
# Create server session
server = Gns3Connector(
url=f"{server_url}:{server_port}", user=server_user, cred=server_password
)
# Define the project
if project_name is not None:
project = Project(name=project_name, connector=server)
elif project_id is not None:
project = Project(project_id=project_id, connector=server)
if project is None:
module.fail_json(msg="Could not retrieve project. Check name", **result)
# Retrieve project info
project.get()
# Try to get file data
try:
file_data = project.get_file(path=dest)
except Exception as err:
if "Not Found" in str(err):
file_data = None
else:
module.fail_json(msg=str(err), exception=traceback.format_exc())
if state == "absent":
if file_data is None or file_data == "":
result.update(changed=False)
else:
# Delete project file data
# As of now (GNS3 v2.2.rc5) the DELETE method is not allowed
project_write_file(module, project, dest, "")
result.update(changed=True)
elif state == "present":
if file_data == data:
result.update(changed=False)
else:
project_write_file(module, project, dest, data)
result.update(changed=True)
module.exit_json(**result)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,203 @@
#!/usr/bin/env python
ANSIBLE_METADATA = {
"metadata_version": "1.1",
"status": ["preview"],
"supported_by": "community",
}
DOCUMENTATION = """
---
module: gns3_snapshot
short_description: Module that interacts with snapshots of a project on GNS3 server
version_added: '2.8'
description:
- Module that interacts with snapshots of a project on GNS3 server
requirements: [ gns3fy ]
author:
- David Flores (@davidban77)
options:
url:
description:
- URL target of the GNS3 server
required: true
type: str
port:
description:
- TCP port to connect to server REST API
type: int
default: 3080
user:
description:
- User to connect to GNS3 server
type: str
password:
description:
- Password to connect to GNS3 server
type: str
project_name:
description:
- Project name
type: str
project_id:
description:
- Project ID
type: str
snapshot_name:
description:
- Snapshot name
type: str
snapshot_id:
description:
- Snapshot ID
type: str
state:
description:
- Creates/deletes/restores a project snapshot.
- NOTE: The restore is not an idempotent task
type: str
choices: ['present', 'absent', 'restore']
required: true
"""
EXAMPLES = """
# Retrieves all the information from the computes of GNS3 server
- name: Retrieve all the facts of a GNS3 server computes
gns3_snapshot:
url: http://localhost
project_name: demo_lab
snapshot_name: snap1
state: present
register: result
- debug: var=result
# Retrieves only basic facts data of the GNS3 server computes
- gns3_snapshot:
url: http://localhost
project_name: demo_lab
snapshot_id: "SOME_UUID"
state: absent
# Restores the project from a specific snapshot. This is NOT idempotent
- gns3_snapshot:
url: http://localhost
project_name: demo_lab
snapshot_name: snap1
state: restore
"""
RETURN = """
created_at:
description: Unix format datetime
type: int
name:
description: Snapshot name
type: str
project_id:
description: Project UUID
type: str
snapshot_id:
description: Snapshot UUID
type: dict
"""
import traceback
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
GNS3FY_IMP_ERR = None
try:
from gns3fy import Gns3Connector, Project
HAS_GNS3FY = True
except Exception:
HAS_GNS3FY = False
GNS3FY_IMP_ERR = traceback.format_exc()
def main():
module = AnsibleModule(
argument_spec=dict(
url=dict(type="str", required=True),
user=dict(type="str", default=None),
password=dict(type="str", default=None, no_log=True),
port=dict(type="int", default=3080),
state=dict(
type="str", required=True, choices=["present", "absent", "restore"]
),
project_name=dict(type="str", default=None),
project_id=dict(type="str", default=None),
snapshot_name=dict(type="str", default=None),
snapshot_id=dict(type="str", default=None),
),
required_one_of=[
["project_name", "project_id"],
["snapshot_name", "snapshot_id"],
],
)
result = dict(changed=False)
if not HAS_GNS3FY:
module.fail_json(msg=missing_required_lib("gns3fy"), exception=GNS3FY_IMP_ERR)
server_url = module.params["url"]
server_port = module.params["port"]
server_user = module.params["user"]
server_password = module.params["password"]
project_name = module.params["project_name"]
project_id = module.params["project_id"]
snapshot_name = module.params["snapshot_name"]
snapshot_id = module.params["snapshot_id"]
state = module.params["state"]
try:
# Create server session
server = Gns3Connector(
url=f"{server_url}:{server_port}", user=server_user, cred=server_password
)
# Define the project
if project_name is not None:
project = Project(name=project_name, connector=server)
elif project_id is not None:
project = Project(project_id=project_id, connector=server)
# Collect project and snapshots data
project.get()
snapshot = project.get_snapshot(name=snapshot_name, snapshot_id=snapshot_id)
if state == "present":
if snapshot is None:
# Create the snapshot
if not snapshot_name:
module.fail_json(
msg="Need to specify snapshot name for creation", **result
)
project.create_snapshot(name=snapshot_name)
result["changed"] = True
result["snapshot"] = project.get_snapshot(
name=snapshot_name, snapshot_id=snapshot_id
)
else:
result["snapshot"] = snapshot
elif state == "absent":
if snapshot:
#  Delete snapshot
project.delete_snapshot(name=snapshot_name, snapshot_id=snapshot_id)
result["changed"] = True
elif state == "restore":
if not snapshot:
module.fail_json(msg="Snapshot not found", **result)
# Restore snapshot
project.restore_snapshot(name=snapshot_name, snapshot_id=snapshot_id)
result["changed"] = True
result["snapshot"] = snapshot
module.exit_json(**result)
except Exception as err:
module.fail_json(msg=str(err), **result)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,100 @@
#!/usr/bin/env python
ANSIBLE_METADATA = {
"metadata_version": "1.3",
"status": ["preview"],
"supported_by": "community",
}
DOCUMENTATION = """
---
module: gns3_version
short_description: Retrieves GNS3 server version
version_added: '2.8'
description:
- 'Retrieves GNS3 server version using gns3fy'
requirements: [ gns3fy ]
author:
- David Flores (@davidban77)
options:
url:
description:
- URL target of the GNS3 server
required: true
type: str
port:
description:
- TCP port to connect to server REST API
type: int
default: 3080
user:
description:
- User to connect to GNS3 server
type: str
password:
description:
- Password to connect to GNS3 server
type: str
"""
EXAMPLES = """
# Retrieve the GNS3 server version
- name: Get the server version
gns3_version:
url: http://localhost
port: 3080
register: result
- debug: var=result
"""
RETURN = """
local_compute:
description: Whether this is a local server or not
type: bool
returned: always
version:
description: Version number of the server
type: str
returned: always
"""
import traceback
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
GNS3FY_IMP_ERR = None
try:
from gns3fy import Gns3Connector
HAS_GNS3FY = True
except Exception:
HAS_GNS3FY = False
GNS3FY_IMP_ERR = traceback.format_exc()
def main():
module = AnsibleModule(
argument_spec=dict(
url=dict(type="str", required=True),
port=dict(type="int", default=3080),
user=dict(type="str", default=None),
password=dict(type="str", default=None, no_log=True),
)
)
if not HAS_GNS3FY:
module.fail_json(msg=missing_required_lib("gns3fy"), exception=GNS3FY_IMP_ERR)
result = dict(changed=False, local_compute=None, version=None)
server_url = module.params["url"]
server_port = module.params["port"]
server_user = module.params["user"]
server_password = module.params["password"]
server = Gns3Connector(
url=f"{server_url}:{server_port}", user=server_user, cred=server_password
)
_version = server.get_version()
result.update(local_compute=_version["local"], version=_version["version"])
module.exit_json(**result)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,2 @@
ansible>=2.8.0
gns3fy>=0.5.2

View File

@@ -0,0 +1,6 @@
[defaults]
library = ../../plugins/modules
roles_path = ../../roles
nocows = 1
retry_files_enabled = False
display_skipped_hosts = no

View File

@@ -0,0 +1,22 @@
- hosts: localhost
tasks:
- name: Play with a dummy file to node
gns3_node_file:
url: "{{ gns3_url }}"
project_name: "{{ gns3_project_name }}"
node_name: alpine-1
state: present
dest: /etc/network/dummy_file
data: |
# Some data to insert on the file
auto eth0
iface eth0 inect dhcp
- name: Play with a dummy file on project
gns3_project_file:
url: "{{ gns3_url }}"
project_name: "{{ gns3_project_name }}"
state: present
dest: README.txt
data: |
Hello! this is a README

View File

@@ -0,0 +1,16 @@
- hosts: localhost
tasks:
- name: Play with a dummy file to node
gns3_node_file:
url: "{{ gns3_url }}"
project_name: "{{ gns3_project_name }}"
node_name: alpine-1
state: absent
dest: /etc/network/dummy_file
- name: Play with a dummy file on project
gns3_project_file:
url: "{{ gns3_url }}"
project_name: "{{ gns3_project_name }}"
state: absent
dest: README.txt

View File

@@ -0,0 +1,18 @@
- hosts: localhost
gather_facts: no
tasks:
- name: Retrieve computes info all
gns3_facts:
url: "{{ gns3_url }}"
get_images: iou
get_compute_ports: no
register: resultado
- debug: var=resultado
# - name: Retrieves basic facts
# gns3_facts:
# url: "{{ gns3_url }}"
# register: resultado
# - debug: var=resultado

View File

@@ -0,0 +1,21 @@
---
gns3_url: "http://dev_gns3server"
gns3_project_name: test_ansible
gns3_nodes_spec:
- name: veos-1
template: "vEOS-4.21.5F"
- name: veos-2
template: "vEOS-4.21.5F"
- name: ios-1
template: "IOU-L2-15.1"
- name: ios-2
template: "IOU-L3-15.4"
- name: alpine-1
template: "alpine"
gns3_nodes_strategy: one_by_one
gns3_links_spec:
- ["veos-1", "Ethernet1", "veos-2", "Ethernet1"]
- ["veos-1", "Ethernet2", "ios-1", "Ethernet1/0"]
- ["veos-2", "Ethernet2", "ios-2", "Ethernet1/0"]
- ["ios-1", "Ethernet1/2", "ios-2", "Ethernet1/2"]
- ["ios-1", "Ethernet1/3", "alpine-1", "eth0"]

View File

@@ -0,0 +1,28 @@
- hosts: localhost
tasks:
- name: Get server version
gns3_facts:
url: "{{ gns3_url }}"
get_images: all
get_compute_ports: yes
register: resultado
- debug: var=resultado
- name: Creation/Active section
when: execute == "create"
block:
- import_role:
name: create_lab
- name: Get nodes inventory
gns3_nodes_inventory:
url: "{{ gns3_url }}"
project_name: "{{ gns3_project_name }}"
register: nodes_inventory
- debug: var=nodes_inventory
- import_role:
name: delete_lab
when: execute == "delete"

View File

@@ -0,0 +1,28 @@
- hosts: localhost
gather_facts: no
tasks:
- name: Reload a node alpine node which requires a special reload
gns3_node:
url: "{{ gns3_url }}"
project_name: "{{ gns3_project_name }}"
node_name: alpine-1
state: reload
retry: yes
poll_wait_time: 30
- name: Stop ios-1
gns3_node:
url: "{{ gns3_url }}"
project_name: "{{ gns3_project_name }}"
node_name: ios-1
state: stopped
- name: Start ios-1
gns3_node:
url: "{{ gns3_url }}"
project_name: "{{ gns3_project_name }}"
node_name: ios-1
state: started
register: node
- debug: var=node

View File

@@ -0,0 +1,39 @@
- hosts: localhost
gather_facts: no
tasks:
- name: Stop nodes on the project
gns3_project:
url: "{{ gns3_url }}"
project_name: "{{ gns3_project_name }}"
state: opened
nodes_state: stopped
- name: Create snapshot
gns3_snapshot:
url: "{{ gns3_url }}"
project_name: "{{ gns3_project_name }}"
snapshot_name: snap
state: present
register: resultado
- debug: var=resultado
- pause:
minutes: 1
- name: Restore snapshot
gns3_snapshot:
url: "{{ gns3_url }}"
project_name: "{{ gns3_project_name }}"
snapshot_name: snap
state: restore
- pause:
minutes: 1
- name: Delete snapshot
gns3_snapshot:
url: "{{ gns3_url }}"
project_name: "{{ gns3_project_name }}"
snapshot_name: snap
state: absent

View File

@@ -0,0 +1,4 @@
---
collections:
- name: davidban77.gns3
source: https://galaxy.ansible.com

39
gitlab.yml Normal file
View File

@@ -0,0 +1,39 @@
- name: Ensure GitLab is installed
hosts: gitlab.lab.toal.ca
become: true
vars:
gitlab_external_url: "http://gitlab.lab.toal.ca/"
gitlab_git_data_dir: "/var/opt/gitlab/git-data"
# gitlab_edition: "gitlab-ce"
# gitlab_redirect_http_to_https: "false"
# # LDAP Configuration.
# gitlab_ldap_enabled: "true"
# gitlab_ldap_host: "idm1.mgmt.toal.ca"
# gitlab_ldap_port: "389"
# gitlab_ldap_uid: "ldapauth"
# gitlab_ldap_method: "start_tls"
# gitlab_ldap_bind_dn: "uid=ldapauth,cn=sysaccounts,cn=etc,dc=idm,dc=toal,dc=ca"
# gitlab_ldap_password: "growwaternapkin"
# gitlab_ldap_base: "cn=users,cn=accounts,dc=idm,dc=toal,dc=ca"
# # Email configuration.
# gitlab_email_enabled: "true"
# gitlab_email_from: "gitlab@takeflight.ca"
# gitlab_email_display_name: "Gitlab"
# gitlab_email_reply_to: "ptoal@takeflight.ca"
# # SMTP Configuration
# gitlab_smtp_enable: "true"
# gitlab_smtp_address: "smtp.gmail.com"
# gitlab_smtp_port: "587"
# gitlab_smtp_user_name: "server"
# gitlab_smtp_password: "ReJ3n_Dj9EB-j3b"
# gitlab_smtp_domain: "takeflight.ca"
# gitlab_smtp_authentication: "login"
# gitlab_smtp_enable_starttls_auto: "true"
# # gitlab_smtp_tls: "false"
# gitlab_smtp_openssl_verify_mode: "none"
# # gitlab_smtp_ca_path: "/etc/ssl/certs"
# # gitlab_smtp_ca_file: "/etc/ssl/certs/ca-certificates.crt"
# gitlab_nginx_listen_https: "false"
roles:
- { role: geerlingguy.gitlab }

View File

@@ -23,7 +23,7 @@
- toallab.infrastructure
- name: Include Minecraft tasks
include_tasks: minecraft.yml
include: minecraft.yml
- name: Include Gitea tasks
include_tasks: gitea.yml
include: gitea.yml

View File

@@ -1,4 +1,4 @@
- name: create a ovirt rhel template
- name: create an ovirt rhel template
hosts: localhost
gather_facts: False
connection: local