#!/usr/bin/python # -*- coding: utf-8 -*- # # Copyright (c) 2015 CenturyLink # 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 absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = ''' module: clc_aa_policy short_description: Create or Delete Anti Affinity Policies at CenturyLink Cloud. description: - An Ansible module to Create or Delete Anti Affinity Policies at CenturyLink Cloud. options: name: description: - The name of the Anti Affinity Policy. type: str required: True location: description: - Datacenter in which the policy lives/should live. type: str required: True state: description: - Whether to create or delete the policy. type: str required: False default: present choices: ['present','absent'] requirements: - python = 2.7 - requests >= 2.5.0 - clc-sdk author: "CLC Runner (@clc-runner)" notes: - To use this module, it is required to set the below environment variables which enables access to the Centurylink Cloud - CLC_V2_API_USERNAME, the account login id for the centurylink cloud - CLC_V2_API_PASSWORD, the account password for the centurylink cloud - Alternatively, the module accepts the API token and account alias. The API token can be generated using the CLC account login and password via the HTTP api call @ https://api.ctl.io/v2/authentication/login - CLC_V2_API_TOKEN, the API token generated from https://api.ctl.io/v2/authentication/login - CLC_ACCT_ALIAS, the account alias associated with the centurylink cloud - Users can set CLC_V2_API_URL to specify an endpoint for pointing to a different CLC environment. ''' EXAMPLES = ''' # Note - You must set the CLC_V2_API_USERNAME And CLC_V2_API_PASSWD Environment variables before running these examples --- - name: Create AA Policy hosts: localhost gather_facts: False connection: local tasks: - name: Create an Anti Affinity Policy community.general.clc_aa_policy: name: Hammer Time location: UK3 state: present register: policy - name: Debug ansible.builtin.debug: var: policy - name: Delete AA Policy hosts: localhost gather_facts: False connection: local tasks: - name: Delete an Anti Affinity Policy community.general.clc_aa_policy: name: Hammer Time location: UK3 state: absent register: policy - name: Debug ansible.builtin.debug: var: policy ''' RETURN = ''' policy: description: The anti affinity policy information returned: success type: dict sample: { "id":"1a28dd0988984d87b9cd61fa8da15424", "name":"test_aa_policy", "location":"UC1", "links":[ { "rel":"self", "href":"/v2/antiAffinityPolicies/wfad/1a28dd0988984d87b9cd61fa8da15424", "verbs":[ "GET", "DELETE", "PUT" ] }, { "rel":"location", "href":"/v2/datacenters/wfad/UC1", "id":"uc1", "name":"UC1 - US West (Santa Clara)" } ] } ''' __version__ = '${version}' import os import traceback from ansible_collections.community.general.plugins.module_utils.version import LooseVersion REQUESTS_IMP_ERR = None try: import requests except ImportError: REQUESTS_IMP_ERR = traceback.format_exc() REQUESTS_FOUND = False else: REQUESTS_FOUND = True # # Requires the clc-python-sdk: # sudo pip install clc-sdk # CLC_IMP_ERR = None try: import clc as clc_sdk from clc import CLCException except ImportError: CLC_IMP_ERR = traceback.format_exc() CLC_FOUND = False clc_sdk = None else: CLC_FOUND = True from ansible.module_utils.basic import AnsibleModule, missing_required_lib class ClcAntiAffinityPolicy: clc = clc_sdk module = None def __init__(self, module): """ Construct module """ self.module = module self.policy_dict = {} if not CLC_FOUND: self.module.fail_json(msg=missing_required_lib('clc-sdk'), exception=CLC_IMP_ERR) if not REQUESTS_FOUND: self.module.fail_json(msg=missing_required_lib('requests'), exception=REQUESTS_IMP_ERR) if requests.__version__ and LooseVersion(requests.__version__) < LooseVersion('2.5.0'): self.module.fail_json( msg='requests library version should be >= 2.5.0') self._set_user_agent(self.clc) @staticmethod def _define_module_argument_spec(): """ Define the argument spec for the ansible module :return: argument spec dictionary """ argument_spec = dict( name=dict(required=True), location=dict(required=True), state=dict(default='present', choices=['present', 'absent']), ) return argument_spec # Module Behavior Goodness def process_request(self): """ Process the request - Main Code Path :return: Returns with either an exit_json or fail_json """ p = self.module.params self._set_clc_credentials_from_env() self.policy_dict = self._get_policies_for_datacenter(p) if p['state'] == "absent": changed, policy = self._ensure_policy_is_absent(p) else: changed, policy = self._ensure_policy_is_present(p) if hasattr(policy, 'data'): policy = policy.data elif hasattr(policy, '__dict__'): policy = policy.__dict__ self.module.exit_json(changed=changed, policy=policy) def _set_clc_credentials_from_env(self): """ Set the CLC Credentials on the sdk by reading environment variables :return: none """ env = os.environ v2_api_token = env.get('CLC_V2_API_TOKEN', False) v2_api_username = env.get('CLC_V2_API_USERNAME', False) v2_api_passwd = env.get('CLC_V2_API_PASSWD', False) clc_alias = env.get('CLC_ACCT_ALIAS', False) api_url = env.get('CLC_V2_API_URL', False) if api_url: self.clc.defaults.ENDPOINT_URL_V2 = api_url if v2_api_token and clc_alias: self.clc._LOGIN_TOKEN_V2 = v2_api_token self.clc._V2_ENABLED = True self.clc.ALIAS = clc_alias elif v2_api_username and v2_api_passwd: self.clc.v2.SetCredentials( api_username=v2_api_username, api_passwd=v2_api_passwd) else: return self.module.fail_json( msg="You must set the CLC_V2_API_USERNAME and CLC_V2_API_PASSWD " "environment variables") def _get_policies_for_datacenter(self, p): """ Get the Policies for a datacenter by calling the CLC API. :param p: datacenter to get policies from :return: policies in the datacenter """ response = {} policies = self.clc.v2.AntiAffinity.GetAll(location=p['location']) for policy in policies: response[policy.name] = policy return response def _create_policy(self, p): """ Create an Anti Affinity Policy using the CLC API. :param p: datacenter to create policy in :return: response dictionary from the CLC API. """ try: return self.clc.v2.AntiAffinity.Create( name=p['name'], location=p['location']) except CLCException as ex: self.module.fail_json(msg='Failed to create anti affinity policy : {0}. {1}'.format( p['name'], ex.response_text )) def _delete_policy(self, p): """ Delete an Anti Affinity Policy using the CLC API. :param p: datacenter to delete a policy from :return: none """ try: policy = self.policy_dict[p['name']] policy.Delete() except CLCException as ex: self.module.fail_json(msg='Failed to delete anti affinity policy : {0}. {1}'.format( p['name'], ex.response_text )) def _policy_exists(self, policy_name): """ Check to see if an Anti Affinity Policy exists :param policy_name: name of the policy :return: boolean of if the policy exists """ if policy_name in self.policy_dict: return self.policy_dict.get(policy_name) return False def _ensure_policy_is_absent(self, p): """ Makes sure that a policy is absent :param p: dictionary of policy name :return: tuple of if a deletion occurred and the name of the policy that was deleted """ changed = False if self._policy_exists(policy_name=p['name']): changed = True if not self.module.check_mode: self._delete_policy(p) return changed, None def _ensure_policy_is_present(self, p): """ Ensures that a policy is present :param p: dictionary of a policy name :return: tuple of if an addition occurred and the name of the policy that was added """ changed = False policy = self._policy_exists(policy_name=p['name']) if not policy: changed = True policy = None if not self.module.check_mode: policy = self._create_policy(p) return changed, policy @staticmethod def _set_user_agent(clc): if hasattr(clc, 'SetRequestsSession'): agent_string = "ClcAnsibleModule/" + __version__ ses = requests.Session() ses.headers.update({"Api-Client": agent_string}) ses.headers['User-Agent'] += " " + agent_string clc.SetRequestsSession(ses) def main(): """ The main function. Instantiates the module and calls process_request. :return: none """ module = AnsibleModule( argument_spec=ClcAntiAffinityPolicy._define_module_argument_spec(), supports_check_mode=True) clc_aa_policy = ClcAntiAffinityPolicy(module) clc_aa_policy.process_request() if __name__ == '__main__': main()