273 lines
7.8 KiB
Python
273 lines
7.8 KiB
Python
#!/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()
|