#!/usr/bin/python # Copyright (c) Ansible Project # 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: oneandone_private_network short_description: Configure 1&1 private networking description: - Create, remove, reconfigure, update a private network. This module has a dependency on 1and1 >= 1.0. deprecated: removed_in: 13.0.0 why: DNS fails to resolve the API endpoint used by the module. alternative: There is none. extends_documentation_fragment: - community.general.attributes attributes: check_mode: support: full diff_mode: support: none options: state: description: - Define a network's state to create, remove, or update. type: str default: 'present' choices: ["present", "absent", "update"] auth_token: description: - Authenticating API token provided by 1&1. type: str private_network: description: - The identifier (id or name) of the network used with update state. type: str api_url: description: - Custom API URL. Overrides the E(ONEANDONE_API_URL) environment variable. type: str name: description: - Private network name used with present state. Used as identifier (id or name) when used with absent state. type: str description: description: - Set a description for the network. type: str datacenter: description: - The identifier of the datacenter where the private network is created. type: str choices: [US, ES, DE, GB] network_address: description: - Set a private network space, for example V(192.168.1.0). type: str subnet_mask: description: - Set the netmask for the private network, for example V(255.255.255.0). type: str add_members: description: - List of server identifiers (name or id) to be added to the private network. type: list elements: str default: [] remove_members: description: - List of server identifiers (name or id) to be removed from the private network. type: list elements: str default: [] wait: description: - Wait for the instance to be in state 'running' before returning. default: true type: bool wait_timeout: description: - How long before wait gives up, in seconds. type: int default: 600 wait_interval: description: - Defines the number of seconds to wait when using the _wait_for methods. type: int default: 5 requirements: - "1and1" author: - Amel Ajdinovic (@aajdinov) - Ethan Devenport (@edevenport) """ EXAMPLES = r""" - name: Create a private network community.general.oneandone_private_network: auth_token: oneandone_private_api_key name: backup_network description: Testing creation of a private network with ansible network_address: 70.35.193.100 subnet_mask: 255.0.0.0 datacenter: US - name: Destroy a private network community.general.oneandone_private_network: auth_token: oneandone_private_api_key state: absent name: backup_network - name: Modify the private network community.general.oneandone_private_network: auth_token: oneandone_private_api_key state: update private_network: backup_network network_address: 192.168.2.0 subnet_mask: 255.255.255.0 - name: Add members to the private network community.general.oneandone_private_network: auth_token: oneandone_private_api_key state: update private_network: backup_network add_members: - server identifier (id or name) - name: Remove members from the private network community.general.oneandone_private_network: auth_token: oneandone_private_api_key state: update private_network: backup_network remove_members: - server identifier (id or name) """ RETURN = r""" private_network: description: Information about the private network. type: dict sample: {"name": "backup_network", "id": "55726DEDA20C99CF6F2AF8F18CAC9963"} returned: always """ import os from ansible.module_utils.basic import AnsibleModule from ansible_collections.community.general.plugins.module_utils.oneandone import ( OneAndOneResources, get_datacenter, get_private_network, get_server, wait_for_resource_creation_completion, wait_for_resource_deletion_completion, ) HAS_ONEANDONE_SDK = True try: import oneandone.client except ImportError: HAS_ONEANDONE_SDK = False DATACENTERS = ["US", "ES", "DE", "GB"] def _check_mode(module, result): if module.check_mode: module.exit_json(changed=result) def _add_servers(module, oneandone_conn, name, members): try: private_network_id = get_private_network(oneandone_conn, name) if module.check_mode: return bool(private_network_id and members) network = oneandone_conn.attach_private_network_servers( private_network_id=private_network_id, server_ids=members ) return network except Exception as e: module.fail_json(msg=str(e)) def _remove_member(module, oneandone_conn, name, member_id): try: private_network_id = get_private_network(oneandone_conn, name) if module.check_mode: if private_network_id: network_member = oneandone_conn.get_private_network_server( private_network_id=private_network_id, server_id=member_id ) if network_member: return True return False network = oneandone_conn.remove_private_network_server(private_network_id=name, server_id=member_id) return network except Exception as ex: module.fail_json(msg=str(ex)) def create_network(module, oneandone_conn): """ Create new private network module : AnsibleModule object oneandone_conn: authenticated oneandone object Returns a dictionary containing a 'changed' attribute indicating whether any network was added. """ name = module.params.get("name") description = module.params.get("description") network_address = module.params.get("network_address") subnet_mask = module.params.get("subnet_mask") datacenter = module.params.get("datacenter") wait = module.params.get("wait") wait_timeout = module.params.get("wait_timeout") wait_interval = module.params.get("wait_interval") if datacenter is not None: datacenter_id = get_datacenter(oneandone_conn, datacenter) if datacenter_id is None: module.fail_json(msg=f"datacenter {datacenter} not found.") try: _check_mode(module, True) network = oneandone_conn.create_private_network( private_network=oneandone.client.PrivateNetwork( name=name, description=description, network_address=network_address, subnet_mask=subnet_mask, datacenter_id=datacenter_id, ) ) if wait: wait_for_resource_creation_completion( oneandone_conn, OneAndOneResources.private_network, network["id"], wait_timeout, wait_interval ) network = get_private_network(oneandone_conn, network["id"], True) changed = True if network else False _check_mode(module, False) return (changed, network) except Exception as e: module.fail_json(msg=str(e)) def update_network(module, oneandone_conn): """ Modifies a private network. module : AnsibleModule object oneandone_conn: authenticated oneandone object """ try: _private_network_id = module.params.get("private_network") _name = module.params.get("name") _description = module.params.get("description") _network_address = module.params.get("network_address") _subnet_mask = module.params.get("subnet_mask") _add_members = module.params.get("add_members") _remove_members = module.params.get("remove_members") changed = False private_network = get_private_network(oneandone_conn, _private_network_id, True) if private_network is None: _check_mode(module, False) if _name or _description or _network_address or _subnet_mask: _check_mode(module, True) private_network = oneandone_conn.modify_private_network( private_network_id=private_network["id"], name=_name, description=_description, network_address=_network_address, subnet_mask=_subnet_mask, ) changed = True if _add_members: instances = [] for member in _add_members: instance_id = get_server(oneandone_conn, member) instance_obj = oneandone.client.AttachServer(server_id=instance_id) instances.extend([instance_obj]) private_network = _add_servers(module, oneandone_conn, private_network["id"], instances) _check_mode(module, private_network) changed = True if _remove_members: chk_changed = False for member in _remove_members: instance = get_server(oneandone_conn, member, True) if module.check_mode: chk_changed |= _remove_member(module, oneandone_conn, private_network["id"], instance["id"]) _check_mode(module, instance and chk_changed) _remove_member(module, oneandone_conn, private_network["id"], instance["id"]) private_network = get_private_network(oneandone_conn, private_network["id"], True) changed = True return (changed, private_network) except Exception as ex: module.fail_json(msg=str(ex)) def remove_network(module, oneandone_conn): """ Removes a private network. module : AnsibleModule object oneandone_conn: authenticated oneandone object. """ try: pn_id = module.params.get("name") wait_timeout = module.params.get("wait_timeout") wait_interval = module.params.get("wait_interval") private_network_id = get_private_network(oneandone_conn, pn_id) if module.check_mode: if private_network_id is None: _check_mode(module, False) _check_mode(module, True) private_network = oneandone_conn.delete_private_network(private_network_id) wait_for_resource_deletion_completion( oneandone_conn, OneAndOneResources.private_network, private_network["id"], wait_timeout, wait_interval ) changed = True if private_network else False return (changed, {"id": private_network["id"], "name": private_network["name"]}) except Exception as e: module.fail_json(msg=str(e)) def main(): module = AnsibleModule( argument_spec=dict( auth_token=dict(type="str", no_log=True, default=os.environ.get("ONEANDONE_AUTH_TOKEN")), api_url=dict(type="str", default=os.environ.get("ONEANDONE_API_URL")), private_network=dict(type="str"), name=dict(type="str"), description=dict(type="str"), network_address=dict(type="str"), subnet_mask=dict(type="str"), add_members=dict(type="list", elements="str", default=[]), remove_members=dict(type="list", elements="str", default=[]), datacenter=dict(choices=DATACENTERS), wait=dict(type="bool", default=True), wait_timeout=dict(type="int", default=600), wait_interval=dict(type="int", default=5), state=dict(type="str", default="present", choices=["present", "absent", "update"]), ), supports_check_mode=True, ) if not HAS_ONEANDONE_SDK: module.fail_json(msg="1and1 required for this module") if not module.params.get("auth_token"): module.fail_json(msg="auth_token parameter is required.") if not module.params.get("api_url"): oneandone_conn = oneandone.client.OneAndOneService(api_token=module.params.get("auth_token")) else: oneandone_conn = oneandone.client.OneAndOneService( api_token=module.params.get("auth_token"), api_url=module.params.get("api_url") ) state = module.params.get("state") if state == "absent": if not module.params.get("name"): module.fail_json(msg="'name' parameter is required for deleting a network.") try: (changed, private_network) = remove_network(module, oneandone_conn) except Exception as e: module.fail_json(msg=str(e)) elif state == "update": if not module.params.get("private_network"): module.fail_json(msg="'private_network' parameter is required for updating a network.") try: (changed, private_network) = update_network(module, oneandone_conn) except Exception as e: module.fail_json(msg=str(e)) elif state == "present": if not module.params.get("name"): module.fail_json(msg="'name' parameter is required for new networks.") try: (changed, private_network) = create_network(module, oneandone_conn) except Exception as e: module.fail_json(msg=str(e)) module.exit_json(changed=changed, private_network=private_network) if __name__ == "__main__": main()