mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-02-04 07:51:50 +00:00
339 lines
10 KiB
Python
339 lines
10 KiB
Python
#!/usr/bin/python
|
|
|
|
# Copyright (c) 2018, Dag Wieers (dagwieers) <dag@wieers.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
|
|
|
|
from __future__ import annotations
|
|
|
|
DOCUMENTATION = r"""
|
|
module: cobbler_system
|
|
short_description: Manage system objects in Cobbler
|
|
description:
|
|
- Add, modify or remove systems in Cobbler.
|
|
extends_documentation_fragment:
|
|
- community.general.attributes
|
|
attributes:
|
|
check_mode:
|
|
support: full
|
|
diff_mode:
|
|
support: full
|
|
options:
|
|
host:
|
|
description:
|
|
- The name or IP address of the Cobbler system.
|
|
default: 127.0.0.1
|
|
type: str
|
|
port:
|
|
description:
|
|
- Port number to be used for REST connection.
|
|
- The default value depends on parameter O(use_ssl).
|
|
type: int
|
|
username:
|
|
description:
|
|
- The username to log in to Cobbler.
|
|
default: cobbler
|
|
type: str
|
|
password:
|
|
description:
|
|
- The password to log in to Cobbler.
|
|
type: str
|
|
use_ssl:
|
|
description:
|
|
- If V(false), an HTTP connection is used instead of the default HTTPS connection.
|
|
type: bool
|
|
default: true
|
|
validate_certs:
|
|
description:
|
|
- If V(false), SSL certificates are not validated.
|
|
- This should only set to V(false) when used on personally controlled sites using self-signed certificates.
|
|
type: bool
|
|
default: true
|
|
name:
|
|
description:
|
|
- The system name to manage.
|
|
type: str
|
|
properties:
|
|
description:
|
|
- A dictionary with system properties.
|
|
type: dict
|
|
interfaces:
|
|
description:
|
|
- A list of dictionaries containing interface options.
|
|
type: dict
|
|
sync:
|
|
description:
|
|
- Sync on changes.
|
|
- Concurrently syncing Cobbler is bound to fail.
|
|
type: bool
|
|
default: false
|
|
state:
|
|
description:
|
|
- Whether the system should be present, absent or a query is made.
|
|
choices: [absent, present, query]
|
|
default: present
|
|
type: str
|
|
author:
|
|
- Dag Wieers (@dagwieers)
|
|
notes:
|
|
- Concurrently syncing Cobbler is bound to fail with weird errors.
|
|
"""
|
|
|
|
EXAMPLES = r"""
|
|
- name: Ensure the system exists in Cobbler
|
|
community.general.cobbler_system:
|
|
host: cobbler01
|
|
username: cobbler
|
|
password: MySuperSecureP4sswOrd
|
|
name: myhost
|
|
properties:
|
|
profile: CentOS6-x86_64
|
|
name_servers: [2.3.4.5, 3.4.5.6]
|
|
name_servers_search: foo.com, bar.com
|
|
interfaces:
|
|
eth0:
|
|
macaddress: 00:01:02:03:04:05
|
|
ipaddress: 1.2.3.4
|
|
delegate_to: localhost
|
|
|
|
- name: Enable network boot in Cobbler
|
|
community.general.cobbler_system:
|
|
host: bdsol-aci-cobbler-01
|
|
username: cobbler
|
|
password: ins3965!
|
|
name: bdsol-aci51-apic1.cisco.com
|
|
properties:
|
|
netboot_enabled: true
|
|
state: present
|
|
delegate_to: localhost
|
|
|
|
- name: Query all systems in Cobbler
|
|
community.general.cobbler_system:
|
|
host: cobbler01
|
|
username: cobbler
|
|
password: MySuperSecureP4sswOrd
|
|
state: query
|
|
register: cobbler_systems
|
|
delegate_to: localhost
|
|
|
|
- name: Query a specific system in Cobbler
|
|
community.general.cobbler_system:
|
|
host: cobbler01
|
|
username: cobbler
|
|
password: MySuperSecureP4sswOrd
|
|
name: '{{ inventory_hostname }}'
|
|
state: query
|
|
register: cobbler_properties
|
|
delegate_to: localhost
|
|
|
|
- name: Ensure the system does not exist in Cobbler
|
|
community.general.cobbler_system:
|
|
host: cobbler01
|
|
username: cobbler
|
|
password: MySuperSecureP4sswOrd
|
|
name: myhost
|
|
state: absent
|
|
delegate_to: localhost
|
|
"""
|
|
|
|
RETURN = r"""
|
|
systems:
|
|
description: List of systems.
|
|
returned: O(state=query) and O(name) is not provided
|
|
type: list
|
|
system:
|
|
description: (Resulting) information about the system we are working with.
|
|
returned: when O(name) is provided
|
|
type: dict
|
|
"""
|
|
|
|
import ssl
|
|
import xmlrpc.client as xmlrpc_client
|
|
|
|
from ansible.module_utils.basic import AnsibleModule
|
|
|
|
from ansible_collections.community.general.plugins.module_utils.datetime import (
|
|
now,
|
|
)
|
|
|
|
IFPROPS_MAPPING = dict(
|
|
bondingopts="bonding_opts",
|
|
bridgeopts="bridge_opts",
|
|
connected_mode="connected_mode",
|
|
cnames="cnames",
|
|
dhcptag="dhcp_tag",
|
|
dnsname="dns_name",
|
|
ifgateway="if_gateway",
|
|
interfacetype="interface_type",
|
|
interfacemaster="interface_master",
|
|
ipaddress="ip_address",
|
|
ipv6address="ipv6_address",
|
|
ipv6defaultgateway="ipv6_default_gateway",
|
|
ipv6mtu="ipv6_mtu",
|
|
ipv6prefix="ipv6_prefix",
|
|
ipv6secondaries="ipv6_secondariesu",
|
|
ipv6staticroutes="ipv6_static_routes",
|
|
macaddress="mac_address",
|
|
management="management",
|
|
mtu="mtu",
|
|
netmask="netmask",
|
|
static="static",
|
|
staticroutes="static_routes",
|
|
virtbridge="virt_bridge",
|
|
)
|
|
|
|
|
|
def getsystem(conn, name, token):
|
|
system = dict()
|
|
if name:
|
|
# system = conn.get_system(name, token)
|
|
systems = conn.find_system(dict(name=name), token)
|
|
if systems:
|
|
system = systems[0]
|
|
return system
|
|
|
|
|
|
def main():
|
|
module = AnsibleModule(
|
|
argument_spec=dict(
|
|
host=dict(type="str", default="127.0.0.1"),
|
|
port=dict(type="int"),
|
|
username=dict(type="str", default="cobbler"),
|
|
password=dict(type="str", no_log=True),
|
|
use_ssl=dict(type="bool", default=True),
|
|
validate_certs=dict(type="bool", default=True),
|
|
name=dict(type="str"),
|
|
interfaces=dict(type="dict"),
|
|
properties=dict(type="dict"),
|
|
sync=dict(type="bool", default=False),
|
|
state=dict(type="str", default="present", choices=["absent", "present", "query"]),
|
|
),
|
|
supports_check_mode=True,
|
|
)
|
|
|
|
username = module.params["username"]
|
|
password = module.params["password"]
|
|
port = module.params["port"]
|
|
use_ssl = module.params["use_ssl"]
|
|
validate_certs = module.params["validate_certs"]
|
|
|
|
name = module.params["name"]
|
|
state = module.params["state"]
|
|
|
|
module.params["proto"] = "https" if use_ssl else "http"
|
|
if not port:
|
|
module.params["port"] = "443" if use_ssl else "80"
|
|
|
|
result = dict(
|
|
changed=False,
|
|
)
|
|
|
|
start = now()
|
|
|
|
ssl_context = None if validate_certs or not use_ssl else ssl._create_unverified_context()
|
|
|
|
url = "{proto}://{host}:{port}/cobbler_api".format(**module.params)
|
|
conn = xmlrpc_client.ServerProxy(url, context=ssl_context)
|
|
|
|
try:
|
|
token = conn.login(username, password)
|
|
except xmlrpc_client.Fault as e:
|
|
module.fail_json(
|
|
msg="Failed to log in to Cobbler '{url}' as '{username}'. {error}".format(
|
|
url=url, error=f"{e}", **module.params
|
|
)
|
|
)
|
|
except Exception as e:
|
|
module.fail_json(msg=f"Connection to '{url}' failed. {e}")
|
|
|
|
system = getsystem(conn, name, token)
|
|
# result['system'] = system
|
|
|
|
if state == "query":
|
|
if name:
|
|
result["system"] = system
|
|
else:
|
|
# Turn it into a dictionary of dictionaries
|
|
# all_systems = conn.get_systems()
|
|
# result['systems'] = { system['name']: system for system in all_systems }
|
|
|
|
# Return a list of dictionaries
|
|
result["systems"] = conn.get_systems()
|
|
|
|
elif state == "present":
|
|
if system:
|
|
# Update existing entry
|
|
system_id = ""
|
|
# https://github.com/cobbler/cobbler/blame/v3.3.7/cobbler/api.py#L277
|
|
if float(conn.version()) >= 3.4:
|
|
system_id = conn.get_system_handle(name)
|
|
else:
|
|
system_id = conn.get_system_handle(name, token)
|
|
|
|
for key, value in module.params["properties"].items():
|
|
if key not in system:
|
|
module.warn(f"Property '{key}' is not a valid system property.")
|
|
if system[key] != value:
|
|
try:
|
|
conn.modify_system(system_id, key, value, token)
|
|
result["changed"] = True
|
|
except Exception as e:
|
|
module.fail_json(msg=f"Unable to change '{key}' to '{value}'. {e}")
|
|
|
|
else:
|
|
# Create a new entry
|
|
system_id = conn.new_system(token)
|
|
conn.modify_system(system_id, "name", name, token)
|
|
result["changed"] = True
|
|
|
|
if module.params["properties"]:
|
|
for key, value in module.params["properties"].items():
|
|
try:
|
|
conn.modify_system(system_id, key, value, token)
|
|
except Exception as e:
|
|
module.fail_json(msg=f"Unable to change '{key}' to '{value}'. {e}")
|
|
|
|
# Add interface properties
|
|
interface_properties = dict()
|
|
if module.params["interfaces"]:
|
|
for device, values in module.params["interfaces"].items():
|
|
for key, value in values.items():
|
|
if key == "name":
|
|
continue
|
|
if key not in IFPROPS_MAPPING:
|
|
module.warn(f"Property '{key}' is not a valid system property.")
|
|
if not system or system["interfaces"][device][IFPROPS_MAPPING[key]] != value:
|
|
result["changed"] = True
|
|
interface_properties[f"{key}-{device}"] = value
|
|
|
|
if result["changed"] is True:
|
|
conn.modify_system(system_id, "modify_interface", interface_properties, token)
|
|
|
|
# Only save when the entry was changed
|
|
if not module.check_mode and result["changed"]:
|
|
conn.save_system(system_id, token)
|
|
|
|
elif state == "absent":
|
|
if system:
|
|
if not module.check_mode:
|
|
conn.remove_system(name, token)
|
|
result["changed"] = True
|
|
|
|
if not module.check_mode and module.params["sync"] and result["changed"]:
|
|
try:
|
|
conn.sync(token)
|
|
except Exception as e:
|
|
module.fail_json(msg=f"Failed to sync Cobbler. {e}")
|
|
|
|
if state in ("absent", "present"):
|
|
result["system"] = getsystem(conn, name, token)
|
|
|
|
if module._diff:
|
|
result["diff"] = dict(before=system, after=result["system"])
|
|
|
|
elapsed = now() - start
|
|
module.exit_json(elapsed=elapsed.seconds, **result)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|