#!/usr/bin/python # # Copyright (c) 2016 Dimension Data # Authors: # - Aimon Bustardo # - Bert Diwa # - Adam Friedman # # 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: dimensiondata_network short_description: Create, update, and delete MCP 1.0 & 2.0 networks extends_documentation_fragment: - community.general.dimensiondata - community.general.dimensiondata_wait - community.general.attributes description: - Create, update, and delete MCP 1.0 & 2.0 networks. deprecated: removed_in: 13.0.0 why: Service and its endpoints are no longer available. alternative: There is none. author: 'Aimon Bustardo (@aimonb)' attributes: check_mode: support: none diff_mode: support: none options: name: description: - The name of the network domain to create. required: true type: str description: description: - Additional description of the network domain. type: str service_plan: description: - The service plan, either "ESSENTIALS" or "ADVANCED". - MCP 2.0 Only. choices: [ESSENTIALS, ADVANCED] default: ESSENTIALS type: str state: description: - Should the resource be present or absent. choices: [present, absent] default: present type: str """ EXAMPLES = r""" - name: Create an MCP 1.0 network community.general.dimensiondata_network: region: na location: NA5 name: mynet - name: Create an MCP 2.0 network community.general.dimensiondata_network: region: na mcp_user: my_user mcp_password: my_password location: NA9 name: mynet service_plan: ADVANCED - name: Delete a network community.general.dimensiondata_network: region: na location: NA1 name: mynet state: absent """ RETURN = r""" network: description: Dictionary describing the network. returned: On success when O(state=present). type: complex contains: id: description: Network ID. type: str sample: "8c787000-a000-4050-a215-280893411a7d" name: description: Network name. type: str sample: "My network" description: description: Network description. type: str sample: "My network description" location: description: Datacenter location. type: str sample: NA3 status: description: Network status. (MCP 2.0 only). type: str sample: NORMAL private_net: description: Private network subnet. (MCP 1.0 only). type: str sample: "10.2.3.0" multicast: description: Multicast enabled? (MCP 1.0 only). type: bool sample: false """ import traceback from ansible.module_utils.basic import AnsibleModule from ansible_collections.community.general.plugins.module_utils.dimensiondata import HAS_LIBCLOUD, DimensionDataModule if HAS_LIBCLOUD: from libcloud.compute.base import NodeLocation from libcloud.common.dimensiondata import DimensionDataAPIException class DimensionDataNetworkModule(DimensionDataModule): """ The dimensiondata_network module for Ansible. """ def __init__(self): """ Create a new Dimension Data network module. """ super().__init__( module=AnsibleModule( argument_spec=DimensionDataModule.argument_spec_with_wait( name=dict(type="str", required=True), description=dict(type="str"), service_plan=dict(default="ESSENTIALS", choices=["ADVANCED", "ESSENTIALS"]), state=dict(default="present", choices=["present", "absent"]), ), required_together=DimensionDataModule.required_together(), ) ) self.name = self.module.params["name"] self.description = self.module.params["description"] self.service_plan = self.module.params["service_plan"] self.state = self.module.params["state"] def state_present(self): network = self._get_network() if network: self.module.exit_json(changed=False, msg="Network already exists", network=self._network_to_dict(network)) network = self._create_network() self.module.exit_json( changed=True, msg=f'Created network "{self.name}" in datacenter "{self.location}".', network=self._network_to_dict(network), ) def state_absent(self): network = self._get_network() if not network: self.module.exit_json( changed=False, msg=f'Network "{self.name}" does not exist', network=self._network_to_dict(network) ) self._delete_network(network) def _get_network(self): if self.mcp_version == "1.0": networks = self.driver.list_networks(location=self.location) else: networks = self.driver.ex_list_network_domains(location=self.location) matched_network = [network for network in networks if network.name == self.name] if matched_network: return matched_network[0] return None def _network_to_dict(self, network): network_dict = dict(id=network.id, name=network.name, description=network.description) if isinstance(network.location, NodeLocation): network_dict["location"] = network.location.id else: network_dict["location"] = network.location if self.mcp_version == "1.0": network_dict["private_net"] = network.private_net network_dict["multicast"] = network.multicast network_dict["status"] = None else: network_dict["private_net"] = None network_dict["multicast"] = None network_dict["status"] = network.status return network_dict def _create_network(self): # Make sure service_plan argument is defined if self.mcp_version == "2.0" and "service_plan" not in self.module.params: self.module.fail_json(msg="service_plan required when creating network and location is MCP 2.0") # Create network try: if self.mcp_version == "1.0": network = self.driver.ex_create_network(self.location, self.name, description=self.description) else: network = self.driver.ex_create_network_domain( self.location, self.name, self.module.params["service_plan"], description=self.description ) except DimensionDataAPIException as e: self.module.fail_json(msg=f"Failed to create new network: {e}", exception=traceback.format_exc()) if self.module.params["wait"] is True: network = self._wait_for_network_state(network.id, "NORMAL") return network def _delete_network(self, network): try: if self.mcp_version == "1.0": deleted = self.driver.ex_delete_network(network) else: deleted = self.driver.ex_delete_network_domain(network) if deleted: self.module.exit_json(changed=True, msg=f"Deleted network with id {network.id}") self.module.fail_json(f"Unexpected failure deleting network with id {network.id}") except DimensionDataAPIException as e: self.module.fail_json(msg=f"Failed to delete network: {e}", exception=traceback.format_exc()) def _wait_for_network_state(self, net_id, state_to_wait_for): try: return self.driver.connection.wait_for_state( state_to_wait_for, self.driver.ex_get_network_domain, self.module.params["wait_poll_interval"], self.module.params["wait_time"], net_id, ) except DimensionDataAPIException as e: self.module.fail_json( msg=f"Network did not reach {state_to_wait_for} state in time: {e}", exception=traceback.format_exc(), ) def main(): module = DimensionDataNetworkModule() if module.state == "present": module.state_present() elif module.state == "absent": module.state_absent() if __name__ == "__main__": main()