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

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()