mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-02-04 07:51:50 +00:00
433 lines
12 KiB
Python
433 lines
12 KiB
Python
#!/usr/bin/python
|
|
#
|
|
# Copyright (c) 2024, Alexander Bakanovskii <skottttt228@gmail.com>
|
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
# Make coding more python3-ish
|
|
from __future__ import annotations
|
|
|
|
DOCUMENTATION = r"""
|
|
module: one_vnet
|
|
short_description: Manages OpenNebula virtual networks
|
|
version_added: 9.4.0
|
|
author: "Alexander Bakanovskii (@abakanovskii)"
|
|
requirements:
|
|
- pyone
|
|
description:
|
|
- Manages virtual networks in OpenNebula.
|
|
attributes:
|
|
check_mode:
|
|
support: partial
|
|
details:
|
|
- Note that check mode always returns RV(ignore:changed=true) for existing networks, even if the network would not actually
|
|
change.
|
|
diff_mode:
|
|
support: none
|
|
options:
|
|
id:
|
|
description:
|
|
- A O(id) of the network you would like to manage.
|
|
- If not set then a new network is created with the given O(name).
|
|
type: int
|
|
name:
|
|
description:
|
|
- A O(name) of the network you would like to manage. If a network with the given name does not exist it, then is created,
|
|
otherwise it is managed by this module.
|
|
type: str
|
|
template:
|
|
description:
|
|
- A string containing the network template contents.
|
|
type: str
|
|
state:
|
|
description:
|
|
- V(present) - state that is used to manage the network.
|
|
- V(absent) - delete the network.
|
|
choices: ["present", "absent"]
|
|
default: present
|
|
type: str
|
|
|
|
extends_documentation_fragment:
|
|
- community.general.opennebula
|
|
- community.general.attributes
|
|
"""
|
|
|
|
EXAMPLES = r"""
|
|
- name: Make sure the network is present by ID
|
|
community.general.one_vnet:
|
|
id: 0
|
|
state: present
|
|
register: result
|
|
|
|
- name: Make sure the network is present by name
|
|
community.general.one_vnet:
|
|
name: opennebula-bridge
|
|
state: present
|
|
register: result
|
|
|
|
- name: Create a new or update an existing network
|
|
community.general.one_vnet:
|
|
name: bridge-network
|
|
template: |
|
|
VN_MAD = "bridge"
|
|
BRIDGE = "br0"
|
|
BRIDGE_TYPE = "linux"
|
|
AR=[
|
|
TYPE = "IP4",
|
|
IP = 192.0.2.50,
|
|
SIZE = "20"
|
|
]
|
|
DNS = 192.0.2.1
|
|
GATEWAY = 192.0.2.1
|
|
|
|
- name: Delete the network by ID
|
|
community.general.one_vnet:
|
|
id: 0
|
|
state: absent
|
|
"""
|
|
|
|
RETURN = r"""
|
|
id:
|
|
description: The network ID.
|
|
type: int
|
|
returned: when O(state=present)
|
|
sample: 153
|
|
name:
|
|
description: The network name.
|
|
type: str
|
|
returned: when O(state=present)
|
|
sample: app1
|
|
template:
|
|
description: The parsed network template.
|
|
type: dict
|
|
returned: when O(state=present)
|
|
sample:
|
|
BRIDGE: onebr.1000
|
|
BRIDGE_TYPE: linux
|
|
DESCRIPTION: sampletext
|
|
PHYDEV: eth0
|
|
SECURITY_GROUPS: 0
|
|
VLAN_ID: 1000
|
|
VN_MAD: 802.1Q
|
|
user_id:
|
|
description: The network's user name.
|
|
type: int
|
|
returned: when O(state=present)
|
|
sample: 1
|
|
user_name:
|
|
description: The network's user ID.
|
|
type: str
|
|
returned: when O(state=present)
|
|
sample: oneadmin
|
|
group_id:
|
|
description: The network's group ID.
|
|
type: int
|
|
returned: when O(state=present)
|
|
sample: 1
|
|
group_name:
|
|
description: The network's group name.
|
|
type: str
|
|
returned: when O(state=present)
|
|
sample: one-users
|
|
owner_id:
|
|
description: The network's owner ID.
|
|
type: int
|
|
returned: when O(state=present)
|
|
sample: 143
|
|
owner_name:
|
|
description: The network's owner name.
|
|
type: str
|
|
returned: when O(state=present)
|
|
sample: ansible-test
|
|
permissions:
|
|
description: The network's permissions.
|
|
type: dict
|
|
returned: when O(state=present)
|
|
contains:
|
|
owner_u:
|
|
description: The network's owner USAGE permissions.
|
|
type: str
|
|
sample: 1
|
|
owner_m:
|
|
description: The network's owner MANAGE permissions.
|
|
type: str
|
|
sample: 0
|
|
owner_a:
|
|
description: The network's owner ADMIN permissions.
|
|
type: str
|
|
sample: 0
|
|
group_u:
|
|
description: The network's group USAGE permissions.
|
|
type: str
|
|
sample: 0
|
|
group_m:
|
|
description: The network's group MANAGE permissions.
|
|
type: str
|
|
sample: 0
|
|
group_a:
|
|
description: The network's group ADMIN permissions.
|
|
type: str
|
|
sample: 0
|
|
other_u:
|
|
description: The network's other users USAGE permissions.
|
|
type: str
|
|
sample: 0
|
|
other_m:
|
|
description: The network's other users MANAGE permissions.
|
|
type: str
|
|
sample: 0
|
|
other_a:
|
|
description: The network's other users ADMIN permissions.
|
|
type: str
|
|
sample: 0
|
|
sample:
|
|
owner_u: 1
|
|
owner_m: 0
|
|
owner_a: 0
|
|
group_u: 0
|
|
group_m: 0
|
|
group_a: 0
|
|
other_u: 0
|
|
other_m: 0
|
|
other_a: 0
|
|
clusters:
|
|
description: The network's clusters.
|
|
type: list
|
|
returned: when O(state=present)
|
|
sample: [0, 100]
|
|
bridge:
|
|
description: The network's bridge interface.
|
|
type: str
|
|
returned: when O(state=present)
|
|
sample: br0
|
|
bridge_type:
|
|
description: The network's bridge type.
|
|
type: str
|
|
returned: when O(state=present)
|
|
sample: linux
|
|
parent_network_id:
|
|
description: The network's parent network ID.
|
|
type: int
|
|
returned: when O(state=present)
|
|
sample: 1
|
|
vn_mad:
|
|
description: The network's VN_MAD.
|
|
type: str
|
|
returned: when O(state=present)
|
|
sample: bridge
|
|
phydev:
|
|
description: The network's physical device (NIC).
|
|
type: str
|
|
returned: when O(state=present)
|
|
sample: eth0
|
|
vlan_id:
|
|
description: The network's VLAN tag.
|
|
type: int
|
|
returned: when O(state=present)
|
|
sample: 1000
|
|
outer_vlan_id:
|
|
description: The network's outer VLAN tag.
|
|
type: int
|
|
returned: when O(state=present)
|
|
sample: 1000
|
|
vrouters:
|
|
description: The network's list of virtual routers IDs.
|
|
type: list
|
|
returned: when O(state=present)
|
|
sample: [0, 1]
|
|
ar_pool:
|
|
description: The network's list of ar_pool.
|
|
type: list
|
|
returned: when O(state=present)
|
|
sample:
|
|
- ar_id: 0
|
|
ip: 192.0.2.1
|
|
mac: 6c:1e:46:01:cd:d1
|
|
size: 20
|
|
type: IP4
|
|
- ar_id: 1
|
|
allocated: 0
|
|
ip: 198.51.100.1
|
|
mac: 5d:9b:c0:9e:f6:e5
|
|
size: 20
|
|
type: IP4
|
|
"""
|
|
|
|
|
|
from ansible_collections.community.general.plugins.module_utils.opennebula import OpenNebulaModule
|
|
|
|
|
|
class NetworksModule(OpenNebulaModule):
|
|
def __init__(self):
|
|
argument_spec = dict(
|
|
id=dict(type="int"),
|
|
name=dict(type="str"),
|
|
state=dict(type="str", choices=["present", "absent"], default="present"),
|
|
template=dict(type="str"),
|
|
)
|
|
|
|
mutually_exclusive = [["id", "name"]]
|
|
|
|
required_one_of = [("id", "name")]
|
|
|
|
required_if = [["state", "present", ["template"]]]
|
|
|
|
OpenNebulaModule.__init__(
|
|
self,
|
|
argument_spec,
|
|
supports_check_mode=True,
|
|
mutually_exclusive=mutually_exclusive,
|
|
required_one_of=required_one_of,
|
|
required_if=required_if,
|
|
)
|
|
|
|
def run(self, one, module, result):
|
|
params = module.params
|
|
id = params.get("id")
|
|
name = params.get("name")
|
|
desired_state = params.get("state")
|
|
template_data = params.get("template")
|
|
|
|
self.result = {}
|
|
|
|
template = self.get_template_instance(id, name)
|
|
needs_creation = False
|
|
if not template and desired_state != "absent":
|
|
if id:
|
|
module.fail_json(msg=f"There is no template with id={id}")
|
|
else:
|
|
needs_creation = True
|
|
|
|
if desired_state == "absent":
|
|
self.result = self.delete_template(template)
|
|
else:
|
|
if needs_creation:
|
|
self.result = self.create_template(name, template_data)
|
|
else:
|
|
self.result = self.update_template(template, template_data)
|
|
|
|
self.exit()
|
|
|
|
def get_template(self, predicate):
|
|
# -2 means "Resources belonging to all users"
|
|
# the other two parameters are used for pagination, -1 for both essentially means "return all"
|
|
pool = self.one.vnpool.info(-2, -1, -1)
|
|
|
|
for template in pool.VNET:
|
|
if predicate(template):
|
|
return template
|
|
|
|
return None
|
|
|
|
def get_template_by_id(self, template_id):
|
|
return self.get_template(lambda template: (template_id == template.ID))
|
|
|
|
def get_template_by_name(self, name):
|
|
return self.get_template(lambda template: (name == template.NAME))
|
|
|
|
def get_template_instance(self, requested_id, requested_name):
|
|
if requested_id:
|
|
return self.get_template_by_id(requested_id)
|
|
else:
|
|
return self.get_template_by_name(requested_name)
|
|
|
|
def get_networks_ar_pool(self, template):
|
|
ar_pool = []
|
|
template_pool = template.AR_POOL.AR
|
|
for ar in range(len(template_pool)):
|
|
template_param = template_pool[ar]
|
|
ar_pool.append(
|
|
{
|
|
# These params will always be present
|
|
"ar_id": template_param.AR_ID,
|
|
"mac": template_param.MAC,
|
|
"size": template_param.SIZE,
|
|
"type": template_param.TYPE,
|
|
# These are optional so firstly check for presence
|
|
# and if not present set value to Null
|
|
"allocated": getattr(template_param, "ALLOCATED", "Null"),
|
|
"ip": getattr(template_param, "IP", "Null"),
|
|
"global_prefix": getattr(template_param, "GLOBAL_PREFIX", "Null"),
|
|
"parent_network_ar_id": getattr(template_param, "PARENT_NETWORK_AR_ID", "Null"),
|
|
"ula_prefix": getattr(template_param, "ULA_PREFIX", "Null"),
|
|
"vn_mad": getattr(template_param, "VN_MAD", "Null"),
|
|
}
|
|
)
|
|
return ar_pool
|
|
|
|
def get_template_info(self, template):
|
|
info = {
|
|
"id": template.ID,
|
|
"name": template.NAME,
|
|
"template": template.TEMPLATE,
|
|
"user_name": template.UNAME,
|
|
"user_id": template.UID,
|
|
"group_name": template.GNAME,
|
|
"group_id": template.GID,
|
|
"permissions": {
|
|
"owner_u": template.PERMISSIONS.OWNER_U,
|
|
"owner_m": template.PERMISSIONS.OWNER_M,
|
|
"owner_a": template.PERMISSIONS.OWNER_A,
|
|
"group_u": template.PERMISSIONS.GROUP_U,
|
|
"group_m": template.PERMISSIONS.GROUP_M,
|
|
"group_a": template.PERMISSIONS.GROUP_A,
|
|
"other_u": template.PERMISSIONS.OTHER_U,
|
|
"other_m": template.PERMISSIONS.OTHER_M,
|
|
"other_a": template.PERMISSIONS.OTHER_A,
|
|
},
|
|
"clusters": template.CLUSTERS.ID,
|
|
"bridge": template.BRIDGE,
|
|
"bride_type": template.BRIDGE_TYPE,
|
|
"parent_network_id": template.PARENT_NETWORK_ID,
|
|
"vn_mad": template.VN_MAD,
|
|
"phydev": template.PHYDEV,
|
|
"vlan_id": template.VLAN_ID,
|
|
"outer_vlan_id": template.OUTER_VLAN_ID,
|
|
"used_leases": template.USED_LEASES,
|
|
"vrouters": template.VROUTERS.ID,
|
|
"ar_pool": self.get_networks_ar_pool(template),
|
|
}
|
|
|
|
return info
|
|
|
|
def create_template(self, name, template_data):
|
|
if not self.module.check_mode:
|
|
# -1 means that network won't be added to any cluster which happens by default
|
|
self.one.vn.allocate(f'NAME = "{name}"\n{template_data}', -1)
|
|
|
|
result = self.get_template_info(self.get_template_by_name(name))
|
|
result["changed"] = True
|
|
|
|
return result
|
|
|
|
def update_template(self, template, template_data):
|
|
if not self.module.check_mode:
|
|
# 0 = replace the whole template
|
|
self.one.vn.update(template.ID, template_data, 0)
|
|
|
|
result = self.get_template_info(self.get_template_by_id(template.ID))
|
|
if self.module.check_mode:
|
|
# Unfortunately it is not easy to detect if the template would have changed, therefore always report a change here.
|
|
result["changed"] = True
|
|
else:
|
|
# if the previous parsed template data is not equal to the updated one, this has changed
|
|
result["changed"] = result["template"] != template.TEMPLATE
|
|
|
|
return result
|
|
|
|
def delete_template(self, template):
|
|
if not template:
|
|
return {"changed": False}
|
|
|
|
if not self.module.check_mode:
|
|
self.one.vn.delete(template.ID)
|
|
|
|
return {"changed": True}
|
|
|
|
|
|
def main():
|
|
NetworksModule().run_module()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|