diff --git a/cloud/centurylink/__init__.py b/cloud/centurylink/__init__.py index 71f0abcff9d..8b137891791 100644 --- a/cloud/centurylink/__init__.py +++ b/cloud/centurylink/__init__.py @@ -1 +1 @@ -__version__ = "${version}" + diff --git a/cloud/centurylink/clc_aa_policy.py b/cloud/centurylink/clc_aa_policy.py deleted file mode 100644 index 644f3817c4f..00000000000 --- a/cloud/centurylink/clc_aa_policy.py +++ /dev/null @@ -1,294 +0,0 @@ -#!/usr/bin/python - -# CenturyLink Cloud Ansible Modules. -# -# These Ansible modules enable the CenturyLink Cloud v2 API to be called -# from an within Ansible Playbook. -# -# This file is part of CenturyLink Cloud, and is maintained -# by the Workflow as a Service Team -# -# Copyright 2015 CenturyLink Cloud -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# CenturyLink Cloud: http://www.CenturyLinkCloud.com -# API Documentation: https://www.centurylinkcloud.com/api-docs/v2/ - -DOCUMENTATION = ''' -module: clc_aa_policy -short_descirption: 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. - required: True - location: - description: - - Datacenter in which the policy lives/should live. - required: True - state: - description: - - Whether to create or delete the policy. - required: False - default: present - choices: ['present','absent'] - wait: - description: - - Whether to wait for the provisioning tasks to finish before returning. - default: True - required: False - choices: [ True, False] - aliases: [] -''' - -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 - clc_aa_policy: - name: 'Hammer Time' - location: 'UK3' - state: present - register: policy - - - name: debug - debug: var=policy - ---- -- name: Delete AA Policy - hosts: localhost - gather_facts: False - connection: local - tasks: - - name: Delete an Anti Affinity Policy - clc_aa_policy: - name: 'Hammer Time' - location: 'UK3' - state: absent - register: policy - - - name: debug - debug: var=policy -''' - -__version__ = '${version}' - -import requests - -# -# Requires the clc-python-sdk. -# sudo pip install clc-sdk -# -try: - import clc as clc_sdk - from clc import CLCException -except ImportError: - clc_found = False - clc_sdk = None -else: - clc_found = True - - -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='clc-python-sdk required for this module') - - 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), - alias=dict(default=None), - wait=dict(default=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 - - if not clc_found: - self.module.fail_json( - msg='clc-python-sdk required for this module') - - 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 Affinnity Policy using the CLC API. - :param p: datacenter to create policy in - :return: response dictionary from the CLC API. - """ - return self.clc.v2.AntiAffinity.Create( - name=p['name'], - location=p['location']) - - def _delete_policy(self, p): - """ - Delete an Anti Affinity Policy using the CLC API. - :param p: datacenter to delete a policy from - :return: none - """ - policy = self.policy_dict[p['name']] - policy.Delete() - - 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: dictonary 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() - -from ansible.module_utils.basic import * # pylint: disable=W0614 -if __name__ == '__main__': - main() diff --git a/cloud/centurylink/clc_alert_policy.py b/cloud/centurylink/clc_alert_policy.py deleted file mode 100644 index 75467967a85..00000000000 --- a/cloud/centurylink/clc_alert_policy.py +++ /dev/null @@ -1,473 +0,0 @@ -#!/usr/bin/python - -# CenturyLink Cloud Ansible Modules. -# -# These Ansible modules enable the CenturyLink Cloud v2 API to be called -# from an within Ansible Playbook. -# -# This file is part of CenturyLink Cloud, and is maintained -# by the Workflow as a Service Team -# -# Copyright 2015 CenturyLink Cloud -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# CenturyLink Cloud: http://www.CenturyLinkCloud.com -# API Documentation: https://www.centurylinkcloud.com/api-docs/v2/ - -DOCUMENTATION = ''' -module: clc_alert_policy -short_descirption: Create or Delete Alert Policies at CenturyLink Cloud. -description: - - An Ansible module to Create or Delete Alert Policies at CenturyLink Cloud. -options: - alias: - description: - - The alias of your CLC Account - required: True - name: - description: - - The name of the alert policy. This is mutually exclusive with id - default: None - aliases: [] - id: - description: - - The alert policy id. This is mutually exclusive with name - default: None - aliases: [] - alert_recipients: - description: - - A list of recipient email ids to notify the alert. - required: True - aliases: [] - metric: - description: - - The metric on which to measure the condition that will trigger the alert. - required: True - default: None - choices: ['cpu','memory','disk'] - aliases: [] - duration: - description: - - The length of time in minutes that the condition must exceed the threshold. - required: True - default: None - aliases: [] - threshold: - description: - - The threshold that will trigger the alert when the metric equals or exceeds it. - This number represents a percentage and must be a value between 5.0 - 95.0 that is a multiple of 5.0 - required: True - default: None - aliases: [] - state: - description: - - Whether to create or delete the policy. - required: False - default: present - choices: ['present','absent'] -''' - -EXAMPLES = ''' -# Note - You must set the CLC_V2_API_USERNAME And CLC_V2_API_PASSWD Environment variables before running these examples - ---- -- name: Create Alert Policy Example - hosts: localhost - gather_facts: False - connection: local - tasks: - - name: Create an Alert Policy for disk above 80% for 5 minutes - clc_alert_policy: - alias: wfad - name: 'alert for disk > 80%' - alert_recipients: - - test1@centurylink.com - - test2@centurylink.com - metric: 'disk' - duration: '00:05:00' - threshold: 80 - state: present - register: policy - - - name: debug - debug: var=policy - ---- -- name: Delete Alert Policy Example - hosts: localhost - gather_facts: False - connection: local - tasks: - - name: Delete an Alert Policy - clc_alert_policy: - alias: wfad - name: 'alert for disk > 80%' - state: absent - register: policy - - - name: debug - debug: var=policy -''' - -__version__ = '${version}' - -import requests - -# -# Requires the clc-python-sdk. -# sudo pip install clc-sdk -# -try: - import clc as clc_sdk - from clc import CLCException -except ImportError: - clc_found = False - clc_sdk = None -else: - clc_found = True - - -class ClcAlertPolicy(): - - 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='clc-python-sdk required for this module') - - 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(default=None), - id=dict(default=None), - alias=dict(required=True, default=None), - alert_recipients=dict(type='list', required=False, default=None), - metric=dict(required=False, choices=['cpu', 'memory', 'disk'], default=None), - duration=dict(required=False, type='str', default=None), - threshold=dict(required=False, type='int', default=None), - state=dict(default='present', choices=['present', 'absent']) - ) - mutually_exclusive = [ - ['name', 'id'] - ] - return {'argument_spec': argument_spec, - 'mutually_exclusive': mutually_exclusive} - - # 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 - - if not clc_found: - self.module.fail_json( - msg='clc-python-sdk required for this module') - - self._set_clc_credentials_from_env() - self.policy_dict = self._get_alert_policies(p['alias']) - - if p['state'] == 'present': - changed, policy = self._ensure_alert_policy_is_present() - else: - changed, policy = self._ensure_alert_policy_is_absent() - - 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 _ensure_alert_policy_is_present(self): - """ - Ensures that the alert policy is present - :return: (changed, policy) - canged: A flag representing if anything is modified - policy: the created/updated alert policy - """ - changed = False - p = self.module.params - policy_name = p.get('name') - alias = p.get('alias') - if not policy_name: - self.module.fail_json(msg='Policy name is a required') - policy = self._alert_policy_exists(alias, policy_name) - if not policy: - changed = True - policy = None - if not self.module.check_mode: - policy = self._create_alert_policy() - else: - changed_u, policy = self._ensure_alert_policy_is_updated(policy) - if changed_u: - changed = True - return changed, policy - - def _ensure_alert_policy_is_absent(self): - """ - Ensures that the alert policy is absent - :return: (changed, None) - canged: A flag representing if anything is modified - """ - changed = False - p = self.module.params - alert_policy_id = p.get('id') - alert_policy_name = p.get('name') - alias = p.get('alias') - if not alert_policy_id and not alert_policy_name: - self.module.fail_json( - msg='Either alert policy id or policy name is required') - if not alert_policy_id and alert_policy_name: - alert_policy_id = self._get_alert_policy_id( - self.module, - alert_policy_name) - if alert_policy_id and alert_policy_id in self.policy_dict: - changed = True - if not self.module.check_mode: - self._delete_alert_policy(alias, alert_policy_id) - return changed, None - - def _ensure_alert_policy_is_updated(self, alert_policy): - """ - Ensures the aliert policy is updated if anything is changed in the alert policy configuration - :param alert_policy: the targetalert policy - :return: (changed, policy) - canged: A flag representing if anything is modified - policy: the updated the alert policy - """ - changed = False - p = self.module.params - alert_policy_id = alert_policy.get('id') - email_list = p.get('alert_recipients') - metric = p.get('metric') - duration = p.get('duration') - threshold = p.get('threshold') - policy = alert_policy - if (metric and metric != str(alert_policy.get('triggers')[0].get('metric'))) or \ - (duration and duration != str(alert_policy.get('triggers')[0].get('duration'))) or \ - (threshold and float(threshold) != float(alert_policy.get('triggers')[0].get('threshold'))): - changed = True - elif email_list: - t_email_list = list( - alert_policy.get('actions')[0].get('settings').get('recipients')) - if set(email_list) != set(t_email_list): - changed = True - if changed and not self.module.check_mode: - policy = self._update_alert_policy(alert_policy_id) - return changed, policy - - def _get_alert_policies(self, alias): - """ - Get the alert policies for account alias by calling the CLC API. - :param alias: the account alias - :return: the alert policies for the account alias - """ - response = {} - - policies = self.clc.v2.API.Call('GET', - '/v2/alertPolicies/%s' - % (alias)) - - for policy in policies.get('items'): - response[policy.get('id')] = policy - return response - - def _create_alert_policy(self): - """ - Create an alert Policy using the CLC API. - :return: response dictionary from the CLC API. - """ - p = self.module.params - alias = p['alias'] - email_list = p['alert_recipients'] - metric = p['metric'] - duration = p['duration'] - threshold = p['threshold'] - name = p['name'] - arguments = json.dumps( - { - 'name': name, - 'actions': [{ - 'action': 'email', - 'settings': { - 'recipients': email_list - } - }], - 'triggers': [{ - 'metric': metric, - 'duration': duration, - 'threshold': threshold - }] - } - ) - try: - result = self.clc.v2.API.Call( - 'POST', - '/v2/alertPolicies/%s' % - (alias), - arguments) - except self.clc.APIFailedResponse as e: - return self.module.fail_json( - msg='Unable to create alert policy. %s' % str( - e.response_text)) - return result - - def _update_alert_policy(self, alert_policy_id): - """ - Update alert policy using the CLC API. - :param alert_policy_id: The clc alert policy id - :return: response dictionary from the CLC API. - """ - p = self.module.params - alias = p['alias'] - email_list = p['alert_recipients'] - metric = p['metric'] - duration = p['duration'] - threshold = p['threshold'] - name = p['name'] - arguments = json.dumps( - { - 'name': name, - 'actions': [{ - 'action': 'email', - 'settings': { - 'recipients': email_list - } - }], - 'triggers': [{ - 'metric': metric, - 'duration': duration, - 'threshold': threshold - }] - } - ) - try: - result = self.clc.v2.API.Call( - 'PUT', '/v2/alertPolicies/%s/%s' % - (alias, alert_policy_id), arguments) - except self.clc.APIFailedResponse as e: - return self.module.fail_json( - msg='Unable to update alert policy. %s' % str( - e.response_text)) - return result - - def _delete_alert_policy(self, alias, policy_id): - """ - Delete an alert policy using the CLC API. - :param alias : the account alias - :param policy_id: the alert policy id - :return: response dictionary from the CLC API. - """ - try: - result = self.clc.v2.API.Call( - 'DELETE', '/v2/alertPolicies/%s/%s' % - (alias, policy_id), None) - except self.clc.APIFailedResponse as e: - return self.module.fail_json( - msg='Unable to delete alert policy. %s' % str( - e.response_text)) - return result - - def _alert_policy_exists(self, alias, policy_name): - """ - Check to see if an alert policy exists - :param policy_name: name of the alert policy - :return: boolean of if the policy exists - """ - result = False - for id in self.policy_dict: - if self.policy_dict.get(id).get('name') == policy_name: - result = self.policy_dict.get(id) - return result - - def _get_alert_policy_id(self, module, alert_policy_name): - """ - retrieves the alert policy id of the account based on the name of the policy - :param module: the AnsibleModule object - :param alert_policy_name: the alert policy name - :return: alert_policy_id: The alert policy id - """ - alert_policy_id = None - for id in self.policy_dict: - if self.policy_dict.get(id).get('name') == alert_policy_name: - if not alert_policy_id: - alert_policy_id = id - else: - return module.fail_json( - msg='mutiple alert policies were found with policy name : %s' % - (alert_policy_name)) - return alert_policy_id - - @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 - """ - argument_dict = ClcAlertPolicy._define_module_argument_spec() - module = AnsibleModule(supports_check_mode=True, **argument_dict) - clc_alert_policy = ClcAlertPolicy(module) - clc_alert_policy.process_request() - -from ansible.module_utils.basic import * # pylint: disable=W0614 -if __name__ == '__main__': - main() diff --git a/cloud/centurylink/clc_blueprint_package.py b/cloud/centurylink/clc_blueprint_package.py deleted file mode 100644 index 80cc18a24ca..00000000000 --- a/cloud/centurylink/clc_blueprint_package.py +++ /dev/null @@ -1,263 +0,0 @@ -#!/usr/bin/python - -# CenturyLink Cloud Ansible Modules. -# -# These Ansible modules enable the CenturyLink Cloud v2 API to be called -# from an within Ansible Playbook. -# -# This file is part of CenturyLink Cloud, and is maintained -# by the Workflow as a Service Team -# -# Copyright 2015 CenturyLink Cloud -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# CenturyLink Cloud: http://www.CenturyLinkCloud.com -# API Documentation: https://www.centurylinkcloud.com/api-docs/v2/ -# - -DOCUMENTATION = ''' -module: clc_blueprint_package -short_desciption: deploys a blue print package on a set of servers in CenturyLink Cloud. -description: - - An Ansible module to deploy blue print package on a set of servers in CenturyLink Cloud. -options: - server_ids: - description: - - A list of server Ids to deploy the blue print package. - default: [] - required: True - aliases: [] - package_id: - description: - - The package id of the blue print. - default: None - required: True - aliases: [] - package_params: - description: - - The dictionary of arguments required to deploy the blue print. - default: {} - required: False - aliases: [] -''' - -EXAMPLES = ''' -# Note - You must set the CLC_V2_API_USERNAME And CLC_V2_API_PASSWD Environment variables before running these examples - -- name: Deploy package - clc_blueprint_package: - server_ids: - - UC1WFSDANS01 - - UC1WFSDANS02 - package_id: 77abb844-579d-478d-3955-c69ab4a7ba1a - package_params: {} -''' - -__version__ = '${version}' - -import requests - -# -# Requires the clc-python-sdk. -# sudo pip install clc-sdk -# -try: - import clc as clc_sdk - from clc import CLCException -except ImportError: - CLC_FOUND = False - clc_sdk = None -else: - CLC_FOUND = True - - -class ClcBlueprintPackage(): - - clc = clc_sdk - module = None - - def __init__(self, module): - """ - Construct module - """ - self.module = module - if not CLC_FOUND: - self.module.fail_json( - msg='clc-python-sdk required for this module') - - self._set_user_agent(self.clc) - - def process_request(self): - """ - Process the request - Main Code Path - :return: Returns with either an exit_json or fail_json - """ - p = self.module.params - - if not CLC_FOUND: - self.module.fail_json( - msg='clc-python-sdk required for this module') - - self._set_clc_credentials_from_env() - - server_ids = p['server_ids'] - package_id = p['package_id'] - package_params = p['package_params'] - state = p['state'] - if state == 'present': - changed, changed_server_ids, requests = self.ensure_package_installed( - server_ids, package_id, package_params) - if not self.module.check_mode: - self._wait_for_requests_to_complete(requests) - self.module.exit_json(changed=changed, server_ids=changed_server_ids) - - @staticmethod - def define_argument_spec(): - """ - This function defnines the dictionary object required for - package module - :return: the package dictionary object - """ - argument_spec = dict( - server_ids=dict(type='list', required=True), - package_id=dict(required=True), - package_params=dict(type='dict', default={}), - wait=dict(default=True), - state=dict(default='present', choices=['present']) - ) - return argument_spec - - def ensure_package_installed(self, server_ids, package_id, package_params): - """ - Ensure the package is installed in the given list of servers - :param server_ids: the server list where the package needs to be installed - :param package_id: the package id - :param package_params: the package arguments - :return: (changed, server_ids) - changed: A flag indicating if a change was made - server_ids: The list of servers modfied - """ - changed = False - requests = [] - servers = self._get_servers_from_clc( - server_ids, - 'Failed to get servers from CLC') - try: - for server in servers: - request = self.clc_install_package( - server, - package_id, - package_params) - requests.append(request) - changed = True - except CLCException as ex: - self.module.fail_json( - msg='Failed while installing package : %s with Error : %s' % - (package_id, ex)) - return changed, server_ids, requests - - def clc_install_package(self, server, package_id, package_params): - """ - Read all servers from CLC and executes each package from package_list - :param server_list: The target list of servers where the packages needs to be installed - :param package_list: The list of packages to be installed - :return: (changed, server_ids) - changed: A flag indicating if a change was made - server_ids: The list of servers modfied - """ - result = None - if not self.module.check_mode: - result = server.ExecutePackage( - package_id=package_id, - parameters=package_params) - return result - - def _wait_for_requests_to_complete(self, requests_lst): - """ - Waits until the CLC requests are complete if the wait argument is True - :param requests_lst: The list of CLC request objects - :return: none - """ - if not self.module.params['wait']: - return - for request in requests_lst: - request.WaitUntilComplete() - for request_details in request.requests: - if request_details.Status() != 'succeeded': - self.module.fail_json( - msg='Unable to process package install request') - - def _get_servers_from_clc(self, server_list, message): - """ - Internal function to fetch list of CLC server objects from a list of server ids - :param the list server ids - :return the list of CLC server objects - """ - try: - return self.clc.v2.Servers(server_list).servers - except CLCException as ex: - self.module.fail_json(msg=message + ': %s' % ex) - - 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") - - @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(): - """ - Main function - :return: None - """ - module = AnsibleModule( - argument_spec=ClcBlueprintPackage.define_argument_spec(), - supports_check_mode=True - ) - clc_blueprint_package = ClcBlueprintPackage(module) - clc_blueprint_package.process_request() - -from ansible.module_utils.basic import * -if __name__ == '__main__': - main() diff --git a/cloud/centurylink/clc_firewall_policy.py b/cloud/centurylink/clc_firewall_policy.py deleted file mode 100644 index 260c82bc885..00000000000 --- a/cloud/centurylink/clc_firewall_policy.py +++ /dev/null @@ -1,542 +0,0 @@ -#!/usr/bin/python - -# CenturyLink Cloud Ansible Modules. -# -# These Ansible modules enable the CenturyLink Cloud v2 API to be called -# from an within Ansible Playbook. -# -# This file is part of CenturyLink Cloud, and is maintained -# by the Workflow as a Service Team -# -# Copyright 2015 CenturyLink Cloud -# -# Licensed under the Apache License, Version 2.0 (the "License"); - -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# CenturyLink Cloud: http://www.CenturyLinkCloud.com -# API Documentation: https://www.centurylinkcloud.com/api-docs/v2/ -# - -DOCUMENTATION = ''' -module: clc_firewall_policy -short_desciption: Create/delete/update firewall policies -description: - - Create or delete or updated firewall polices on Centurylink Centurylink Cloud -options: - location: - description: - - Target datacenter for the firewall policy - default: None - required: True - aliases: [] - state: - description: - - Whether to create or delete the firewall policy - default: present - required: True - choices: ['present', 'absent'] - aliases: [] - source: - description: - - Source addresses for traffic on the originating firewall - default: None - required: For Creation - aliases: [] - destination: - description: - - Destination addresses for traffic on the terminating firewall - default: None - required: For Creation - aliases: [] - ports: - description: - - types of ports associated with the policy. TCP & UDP can take in single ports or port ranges. - default: None - required: False - choices: ['any', 'icmp', 'TCP/123', 'UDP/123', 'TCP/123-456', 'UDP/123-456'] - aliases: [] - firewall_policy_id: - description: - - Id of the firewall policy - default: None - required: False - aliases: [] - source_account_alias: - description: - - CLC alias for the source account - default: None - required: True - aliases: [] - destination_account_alias: - description: - - CLC alias for the destination account - default: None - required: False - aliases: [] - wait: - description: - - Whether to wait for the provisioning tasks to finish before returning. - default: True - required: False - choices: [ True, False ] - aliases: [] - enabled: - description: - - If the firewall policy is enabled or disabled - default: true - required: False - choices: [ true, false ] - aliases: [] - -''' - -EXAMPLES = ''' ---- -- name: Create Firewall Policy - hosts: localhost - gather_facts: False - connection: local - tasks: - - name: Create / Verify an Firewall Policy at CenturyLink Cloud - clc_firewall: - source_account_alias: WFAD - location: VA1 - state: present - source: 10.128.216.0/24 - destination: 10.128.216.0/24 - ports: Any - destination_account_alias: WFAD - ---- -- name: Delete Firewall Policy - hosts: localhost - gather_facts: False - connection: local - tasks: - - name: Delete an Firewall Policy at CenturyLink Cloud - clc_firewall: - source_account_alias: WFAD - location: VA1 - state: present - firewall_policy_id: c62105233d7a4231bd2e91b9c791eaae -''' - -__version__ = '${version}' - -import urlparse -from time import sleep -import requests - -try: - import clc as clc_sdk - from clc import CLCException -except ImportError: - CLC_FOUND = False - clc_sdk = None -else: - CLC_FOUND = True - - -class ClcFirewallPolicy(): - - clc = None - - def __init__(self, module): - """ - Construct module - """ - self.clc = clc_sdk - self.module = module - self.firewall_dict = {} - - if not CLC_FOUND: - self.module.fail_json( - msg='clc-python-sdk required for this module') - - 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( - location=dict(required=True, defualt=None), - source_account_alias=dict(required=True, default=None), - destination_account_alias=dict(default=None), - firewall_policy_id=dict(default=None), - ports=dict(default=None, type='list'), - source=dict(defualt=None, type='list'), - destination=dict(defualt=None, type='list'), - wait=dict(default=True), - state=dict(default='present', choices=['present', 'absent']), - enabled=dict(defualt=None) - ) - return argument_spec - - def process_request(self): - """ - Execute the main code path, and handle the request - :return: none - """ - location = self.module.params.get('location') - source_account_alias = self.module.params.get('source_account_alias') - destination_account_alias = self.module.params.get( - 'destination_account_alias') - firewall_policy_id = self.module.params.get('firewall_policy_id') - ports = self.module.params.get('ports') - source = self.module.params.get('source') - destination = self.module.params.get('destination') - wait = self.module.params.get('wait') - state = self.module.params.get('state') - enabled = self.module.params.get('enabled') - - self.firewall_dict = { - 'location': location, - 'source_account_alias': source_account_alias, - 'destination_account_alias': destination_account_alias, - 'firewall_policy_id': firewall_policy_id, - 'ports': ports, - 'source': source, - 'destination': destination, - 'wait': wait, - 'state': state, - 'enabled': enabled} - - self._set_clc_credentials_from_env() - requests = [] - - if state == 'absent': - changed, firewall_policy_id, response = self._ensure_firewall_policy_is_absent( - source_account_alias, location, self.firewall_dict) - - elif state == 'present': - changed, firewall_policy_id, response = self._ensure_firewall_policy_is_present( - source_account_alias, location, self.firewall_dict) - else: - return self.module.fail_json(msg="Unknown State: " + state) - - return self.module.exit_json( - changed=changed, - firewall_policy_id=firewall_policy_id) - - @staticmethod - def _get_policy_id_from_response(response): - """ - Method to parse out the policy id from creation response - :param response: response from firewall creation control - :return: policy_id: firewall policy id from creation call - """ - url = response.get('links')[0]['href'] - path = urlparse.urlparse(url).path - path_list = os.path.split(path) - policy_id = path_list[-1] - return policy_id - - 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 _ensure_firewall_policy_is_present( - self, - source_account_alias, - location, - firewall_dict): - """ - Ensures that a given firewall policy is present - :param source_account_alias: the source account alias for the firewall policy - :param location: datacenter of the firewall policy - :param firewall_dict: dictionary or request parameters for firewall policy creation - :return: (changed, firewall_policy, response) - changed: flag for if a change occurred - firewall_policy: policy that was changed - response: response from CLC API call - """ - changed = False - response = {} - firewall_policy_id = firewall_dict.get('firewall_policy_id') - - if firewall_policy_id is None: - if not self.module.check_mode: - response = self._create_firewall_policy( - source_account_alias, - location, - firewall_dict) - firewall_policy_id = self._get_policy_id_from_response( - response) - self._wait_for_requests_to_complete( - firewall_dict.get('wait'), - source_account_alias, - location, - firewall_policy_id) - changed = True - else: - get_before_response, success = self._get_firewall_policy( - source_account_alias, location, firewall_policy_id) - if not success: - return self.module.fail_json( - msg='Unable to find the firewall policy id : %s' % - firewall_policy_id) - changed = self._compare_get_request_with_dict( - get_before_response, - firewall_dict) - if not self.module.check_mode and changed: - response = self._update_firewall_policy( - source_account_alias, - location, - firewall_policy_id, - firewall_dict) - self._wait_for_requests_to_complete( - firewall_dict.get('wait'), - source_account_alias, - location, - firewall_policy_id) - return changed, firewall_policy_id, response - - def _ensure_firewall_policy_is_absent( - self, - source_account_alias, - location, - firewall_dict): - """ - Ensures that a given firewall policy is removed if present - :param source_account_alias: the source account alias for the firewall policy - :param location: datacenter of the firewall policy - :param firewall_dict: firewall policy to delete - :return: (changed, firewall_policy_id, response) - changed: flag for if a change occurred - firewall_policy_id: policy that was changed - response: response from CLC API call - """ - changed = False - response = [] - firewall_policy_id = firewall_dict.get('firewall_policy_id') - result, success = self._get_firewall_policy( - source_account_alias, location, firewall_policy_id) - if success: - if not self.module.check_mode: - response = self._delete_firewall_policy( - source_account_alias, - location, - firewall_policy_id) - changed = True - return changed, firewall_policy_id, response - - def _create_firewall_policy( - self, - source_account_alias, - location, - firewall_dict): - """ - Ensures that a given firewall policy is present - :param source_account_alias: the source account alias for the firewall policy - :param location: datacenter of the firewall policy - :param firewall_dict: dictionary or request parameters for firewall policy creation - :return: response from CLC API call - """ - payload = { - 'destinationAccount': firewall_dict.get('destination_account_alias'), - 'source': firewall_dict.get('source'), - 'destination': firewall_dict.get('destination'), - 'ports': firewall_dict.get('ports')} - try: - response = self.clc.v2.API.Call( - 'POST', '/v2-experimental/firewallPolicies/%s/%s' % - (source_account_alias, location), payload) - except self.clc.APIFailedResponse as e: - return self.module.fail_json( - msg="Unable to successfully create firewall policy. %s" % - str(e.response_text)) - return response - - def _delete_firewall_policy( - self, - source_account_alias, - location, - firewall_policy_id): - """ - Deletes a given firewall policy for an account alias in a datacenter - :param source_account_alias: the source account alias for the firewall policy - :param location: datacenter of the firewall policy - :param firewall_policy_id: firewall policy to delete - :return: response: response from CLC API call - """ - try: - response = self.clc.v2.API.Call( - 'DELETE', '/v2-experimental/firewallPolicies/%s/%s/%s' % - (source_account_alias, location, firewall_policy_id)) - except self.clc.APIFailedResponse as e: - return self.module.fail_json( - msg="Unable to successfully delete firewall policy. %s" % - str(e.response_text)) - return response - - def _update_firewall_policy( - self, - source_account_alias, - location, - firewall_policy_id, - firewall_dict): - """ - Updates a firewall policy for a given datacenter and account alias - :param source_account_alias: the source account alias for the firewall policy - :param location: datacenter of the firewall policy - :param firewall_policy_id: firewall policy to delete - :param firewall_dict: dictionary or request parameters for firewall policy creation - :return: response: response from CLC API call - """ - try: - response = self.clc.v2.API.Call( - 'PUT', - '/v2-experimental/firewallPolicies/%s/%s/%s' % - (source_account_alias, - location, - firewall_policy_id), - firewall_dict) - except self.clc.APIFailedResponse as e: - return self.module.fail_json( - msg="Unable to successfully update firewall policy. %s" % - str(e.response_text)) - return response - - @staticmethod - def _compare_get_request_with_dict(response, firewall_dict): - """ - Helper method to compare the json response for getting the firewall policy with the request parameters - :param response: response from the get method - :param firewall_dict: dictionary or request parameters for firewall policy creation - :return: changed: Boolean that returns true if there are differences between the response parameters and the playbook parameters - """ - - changed = False - - response_dest_account_alias = response.get('destinationAccount') - response_enabled = response.get('enabled') - response_source = response.get('source') - response_dest = response.get('destination') - response_ports = response.get('ports') - - request_dest_account_alias = firewall_dict.get( - 'destination_account_alias') - request_enabled = firewall_dict.get('enabled') - if request_enabled is None: - request_enabled = True - request_source = firewall_dict.get('source') - request_dest = firewall_dict.get('destination') - request_ports = firewall_dict.get('ports') - - if ( - response_dest_account_alias and str(response_dest_account_alias) != str(request_dest_account_alias)) or ( - response_enabled != request_enabled) or ( - response_source and response_source != request_source) or ( - response_dest and response_dest != request_dest) or ( - response_ports and response_ports != request_ports): - changed = True - return changed - - def _get_firewall_policy( - self, - source_account_alias, - location, - firewall_policy_id): - """ - Get back details for a particular firewall policy - :param source_account_alias: the source account alias for the firewall policy - :param location: datacenter of the firewall policy - :param firewall_policy_id: id of the firewall policy to get - :return: response from CLC API call - """ - response = [] - success = False - try: - response = self.clc.v2.API.Call( - 'GET', '/v2-experimental/firewallPolicies/%s/%s/%s' % - (source_account_alias, location, firewall_policy_id)) - success = True - except: - pass - return response, success - - def _wait_for_requests_to_complete( - self, - wait, - source_account_alias, - location, - firewall_policy_id): - """ - Waits until the CLC requests are complete if the wait argument is True - :param requests_lst: The list of CLC request objects - :return: none - """ - if wait: - response, success = self._get_firewall_policy( - source_account_alias, location, firewall_policy_id) - if response.get('status') == 'pending': - sleep(2) - self._wait_for_requests_to_complete( - wait, - source_account_alias, - location, - firewall_policy_id) - return None - - @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=ClcFirewallPolicy._define_module_argument_spec(), - supports_check_mode=True) - - clc_firewall = ClcFirewallPolicy(module) - clc_firewall.process_request() - -from ansible.module_utils.basic import * # pylint: disable=W0614 -if __name__ == '__main__': - main() diff --git a/cloud/centurylink/clc_group.py b/cloud/centurylink/clc_group.py deleted file mode 100644 index a4fd976d429..00000000000 --- a/cloud/centurylink/clc_group.py +++ /dev/null @@ -1,370 +0,0 @@ -#!/usr/bin/python - -# CenturyLink Cloud Ansible Modules. -# -# These Ansible modules enable the CenturyLink Cloud v2 API to be called -# from an within Ansible Playbook. -# -# This file is part of CenturyLink Cloud, and is maintained -# by the Workflow as a Service Team -# -# Copyright 2015 CenturyLink Cloud -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# CenturyLink Cloud: http://www.CenturyLinkCloud.com -# API Documentation: https://www.centurylinkcloud.com/api-docs/v2/ -# - -DOCUMENTATION = ''' -module: clc_group -short_desciption: Create/delete Server Groups at Centurylink Cloud -description: - - Create or delete Server Groups at Centurylink Centurylink Cloud -options: - name: - description: - - The name of the Server Group - description: - description: - - A description of the Server Group - parent: - description: - - The parent group of the server group - location: - description: - - Datacenter to create the group in - state: - description: - - Whether to create or delete the group - default: present - choices: ['present', 'absent'] - -''' - -EXAMPLES = ''' - -# Create a Server Group - ---- -- name: Create Server Group - hosts: localhost - gather_facts: False - connection: local - tasks: - - name: Create / Verify a Server Group at CenturyLink Cloud - clc_group: - name: 'My Cool Server Group' - parent: 'Default Group' - state: present - register: clc - - - name: debug - debug: var=clc - -# Delete a Server Group - ---- -- name: Delete Server Group - hosts: localhost - gather_facts: False - connection: local - tasks: - - name: Delete / Verify Absent a Server Group at CenturyLink Cloud - clc_group: - name: 'My Cool Server Group' - parent: 'Default Group' - state: absent - register: clc - - - name: debug - debug: var=clc - -''' - -__version__ = '${version}' - -import requests - -# -# Requires the clc-python-sdk. -# sudo pip install clc-sdk -# -try: - import clc as clc_sdk - from clc import CLCException -except ImportError: - CLC_FOUND = False - clc_sdk = None -else: - CLC_FOUND = True - - -class ClcGroup(object): - - clc = None - root_group = None - - def __init__(self, module): - """ - Construct module - """ - self.clc = clc_sdk - self.module = module - self.group_dict = {} - - if not CLC_FOUND: - self.module.fail_json( - msg='clc-python-sdk required for this module') - - self._set_user_agent(self.clc) - - def process_request(self): - """ - Execute the main code path, and handle the request - :return: none - """ - location = self.module.params.get('location') - group_name = self.module.params.get('name') - parent_name = self.module.params.get('parent') - group_description = self.module.params.get('description') - state = self.module.params.get('state') - - self._set_clc_credentials_from_env() - self.group_dict = self._get_group_tree_for_datacenter( - datacenter=location) - - if state == "absent": - changed, group, response = self._ensure_group_is_absent( - group_name=group_name, parent_name=parent_name) - - else: - changed, group, response = self._ensure_group_is_present( - group_name=group_name, parent_name=parent_name, group_description=group_description) - - - self.module.exit_json(changed=changed, group=group_name) - - # - # Functions to define the Ansible module and its arguments - # - - @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), - description=dict(default=None), - parent=dict(default=None), - location=dict(default=None), - alias=dict(default=None), - custom_fields=dict(type='list', default=[]), - server_ids=dict(type='list', default=[]), - state=dict(default='present', choices=['present', 'absent'])) - - return argument_spec - - # - # Module Behavior Functions - # - - 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 _ensure_group_is_absent(self, group_name, parent_name): - """ - Ensure that group_name is absent by deleting it if necessary - :param group_name: string - the name of the clc server group to delete - :param parent_name: string - the name of the parent group for group_name - :return: changed, group - """ - changed = False - group = [] - results = [] - - if self._group_exists(group_name=group_name, parent_name=parent_name): - if not self.module.check_mode: - group.append(group_name) - for g in group: - result = self._delete_group(group_name) - results.append(result) - changed = True - return changed, group, results - - def _delete_group(self, group_name): - """ - Delete the provided server group - :param group_name: string - the server group to delete - :return: none - """ - group, parent = self.group_dict.get(group_name) - response = group.Delete() - return response - - def _ensure_group_is_present( - self, - group_name, - parent_name, - group_description): - """ - Checks to see if a server group exists, creates it if it doesn't. - :param group_name: the name of the group to validate/create - :param parent_name: the name of the parent group for group_name - :param group_description: a short description of the server group (used when creating) - :return: (changed, group) - - changed: Boolean- whether a change was made, - group: A clc group object for the group - """ - assert self.root_group, "Implementation Error: Root Group not set" - parent = parent_name if parent_name is not None else self.root_group.name - description = group_description - changed = False - results = [] - groups = [] - group = group_name - - parent_exists = self._group_exists(group_name=parent, parent_name=None) - child_exists = self._group_exists(group_name=group_name, parent_name=parent) - - if parent_exists and child_exists: - group, parent = self.group_dict[group_name] - changed = False - elif parent_exists and not child_exists: - if not self.module.check_mode: - groups.append(group_name) - for g in groups: - group = self._create_group( - group=group, - parent=parent, - description=description) - results.append(group) - changed = True - else: - self.module.fail_json( - msg="parent group: " + - parent + - " does not exist") - - return changed, group, results - - def _create_group(self, group, parent, description): - """ - Create the provided server group - :param group: clc_sdk.Group - the group to create - :param parent: clc_sdk.Parent - the parent group for {group} - :param description: string - a text description of the group - :return: clc_sdk.Group - the created group - """ - - (parent, grandparent) = self.group_dict[parent] - return parent.Create(name=group, description=description) - - # - # Utility Functions - # - - def _group_exists(self, group_name, parent_name): - """ - Check to see if a group exists - :param group_name: string - the group to check - :param parent_name: string - the parent of group_name - :return: boolean - whether the group exists - """ - result = False - if group_name in self.group_dict: - (group, parent) = self.group_dict[group_name] - if parent_name is None or parent_name == parent.name: - result = True - return result - - def _get_group_tree_for_datacenter(self, datacenter=None, alias=None): - """ - Walk the tree of groups for a datacenter - :param datacenter: string - the datacenter to walk (ex: 'UC1') - :param alias: string - the account alias to search. Defaults to the current user's account - :return: a dictionary of groups and parents - """ - self.root_group = self.clc.v2.Datacenter( - location=datacenter).RootGroup() - return self._walk_groups_recursive( - parent_group=None, - child_group=self.root_group) - - def _walk_groups_recursive(self, parent_group, child_group): - """ - Walk a parent-child tree of groups, starting with the provided child group - :param parent_group: clc_sdk.Group - the parent group to start the walk - :param child_group: clc_sdk.Group - the child group to start the walk - :return: a dictionary of groups and parents - """ - result = {str(child_group): (child_group, parent_group)} - groups = child_group.Subgroups().groups - if len(groups) > 0: - for group in groups: - if group.type != 'default': - continue - - result.update(self._walk_groups_recursive(child_group, group)) - return result - - @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=ClcGroup._define_module_argument_spec(), supports_check_mode=True) - - clc_group = ClcGroup(module) - clc_group.process_request() - -from ansible.module_utils.basic import * # pylint: disable=W0614 -if __name__ == '__main__': - main() diff --git a/cloud/centurylink/clc_loadbalancer.py b/cloud/centurylink/clc_loadbalancer.py deleted file mode 100644 index 058954c687b..00000000000 --- a/cloud/centurylink/clc_loadbalancer.py +++ /dev/null @@ -1,759 +0,0 @@ -#!/usr/bin/python - -# CenturyLink Cloud Ansible Modules. -# -# These Ansible modules enable the CenturyLink Cloud v2 API to be called -# from an within Ansible Playbook. -# -# This file is part of CenturyLink Cloud, and is maintained -# by the Workflow as a Service Team -# -# Copyright 2015 CenturyLink Cloud -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# CenturyLink Cloud: http://www.CenturyLinkCloud.com -# API Documentation: https://www.centurylinkcloud.com/api-docs/v2/ -# - -DOCUMENTATION = ''' -module: -short_desciption: Create, Delete shared loadbalancers in CenturyLink Cloud. -description: - - An Ansible module to Create, Delete shared loadbalancers in CenturyLink Cloud. -options: -options: - name: - description: - - The name of the loadbalancer - required: True - description: - description: - - A description for your loadbalancer - alias: - description: - - The alias of your CLC Account - required: True - location: - description: - - The location of the datacenter your load balancer resides in - required: True - method: - description: - -The balancing method for this pool - default: roundRobin - choices: ['sticky', 'roundRobin'] - persistence: - description: - - The persistence method for this load balancer - default: standard - choices: ['standard', 'sticky'] - port: - description: - - Port to configure on the public-facing side of the load balancer pool - choices: [80, 443] - nodes: - description: - - A list of nodes that you want added to your load balancer pool - status: - description: - - The status of your loadbalancer - default: enabled - choices: ['enabled', 'disabled'] - state: - description: - - Whether to create or delete the load balancer pool - default: present - choices: ['present', 'absent', 'port_absent', 'nodes_present', 'nodes_absent'] -''' - -EXAMPLES = ''' -# Note - You must set the CLC_V2_API_USERNAME And CLC_V2_API_PASSWD Environment variables before running these examples -- name: Create Loadbalancer - hosts: localhost - connection: local - tasks: - - name: Actually Create things - clc_loadbalancer: - name: test - description: test - alias: TEST - location: WA1 - port: 443 - nodes: - - { 'ipAddress': '10.11.22.123', 'privatePort': 80 } - state: present - -- name: Add node to an existing loadbalancer pool - hosts: localhost - connection: local - tasks: - - name: Actually Create things - clc_loadbalancer: - name: test - description: test - alias: TEST - location: WA1 - port: 443 - nodes: - - { 'ipAddress': '10.11.22.234', 'privatePort': 80 } - state: nodes_present - -- name: Remove node from an existing loadbalancer pool - hosts: localhost - connection: local - tasks: - - name: Actually Create things - clc_loadbalancer: - name: test - description: test - alias: TEST - location: WA1 - port: 443 - nodes: - - { 'ipAddress': '10.11.22.234', 'privatePort': 80 } - state: nodes_absent - -- name: Delete LoadbalancerPool - hosts: localhost - connection: local - tasks: - - name: Actually Delete things - clc_loadbalancer: - name: test - description: test - alias: TEST - location: WA1 - port: 443 - nodes: - - { 'ipAddress': '10.11.22.123', 'privatePort': 80 } - state: port_absent - -- name: Delete Loadbalancer - hosts: localhost - connection: local - tasks: - - name: Actually Delete things - clc_loadbalancer: - name: test - description: test - alias: TEST - location: WA1 - port: 443 - nodes: - - { 'ipAddress': '10.11.22.123', 'privatePort': 80 } - state: absent - -''' - -__version__ = '${version}' - -import requests -from time import sleep - -# -# Requires the clc-python-sdk. -# sudo pip install clc-sdk -# -try: - import clc as clc_sdk - from clc import CLCException -except ImportError: - CLC_FOUND = False - clc_sdk = None -else: - CLC_FOUND = True - -class ClcLoadBalancer(): - - clc = None - - def __init__(self, module): - """ - Construct module - """ - self.clc = clc_sdk - self.module = module - self.lb_dict = {} - - if not CLC_FOUND: - self.module.fail_json( - msg='clc-python-sdk required for this module') - - self._set_user_agent(self.clc) - - def process_request(self): - """ - Execute the main code path, and handle the request - :return: none - """ - - loadbalancer_name=self.module.params.get('name') - loadbalancer_alias=self.module.params.get('alias') - loadbalancer_location=self.module.params.get('location') - loadbalancer_description=self.module.params.get('description') - loadbalancer_port=self.module.params.get('port') - loadbalancer_method=self.module.params.get('method') - loadbalancer_persistence=self.module.params.get('persistence') - loadbalancer_nodes=self.module.params.get('nodes') - loadbalancer_status=self.module.params.get('status') - state=self.module.params.get('state') - - if loadbalancer_description == None: - loadbalancer_description = loadbalancer_name - - self._set_clc_credentials_from_env() - - self.lb_dict = self._get_loadbalancer_list(alias=loadbalancer_alias, location=loadbalancer_location) - - if state == 'present': - changed, result_lb, lb_id = self.ensure_loadbalancer_present(name=loadbalancer_name, - alias=loadbalancer_alias, - location=loadbalancer_location, - description=loadbalancer_description, - status=loadbalancer_status) - if loadbalancer_port: - changed, result_pool, pool_id = self.ensure_loadbalancerpool_present(lb_id=lb_id, - alias=loadbalancer_alias, - location=loadbalancer_location, - method=loadbalancer_method, - persistence=loadbalancer_persistence, - port=loadbalancer_port) - - if loadbalancer_nodes: - changed, result_nodes = self.ensure_lbpool_nodes_set(alias=loadbalancer_alias, - location=loadbalancer_location, - name=loadbalancer_name, - port=loadbalancer_port, - nodes=loadbalancer_nodes - ) - elif state == 'absent': - changed, result_lb = self.ensure_loadbalancer_absent(name=loadbalancer_name, - alias=loadbalancer_alias, - location=loadbalancer_location) - - elif state == 'port_absent': - changed, result_lb = self.ensure_loadbalancerpool_absent(alias=loadbalancer_alias, - location=loadbalancer_location, - name=loadbalancer_name, - port=loadbalancer_port) - - elif state == 'nodes_present': - changed, result_lb = self.ensure_lbpool_nodes_present(alias=loadbalancer_alias, - location=loadbalancer_location, - name=loadbalancer_name, - port=loadbalancer_port, - nodes=loadbalancer_nodes) - - elif state == 'nodes_absent': - changed, result_lb = self.ensure_lbpool_nodes_absent(alias=loadbalancer_alias, - location=loadbalancer_location, - name=loadbalancer_name, - port=loadbalancer_port, - nodes=loadbalancer_nodes) - - self.module.exit_json(changed=changed, loadbalancer=result_lb) - # - # Functions to define the Ansible module and its arguments - # - def ensure_loadbalancer_present(self,name,alias,location,description,status): - """ - Check for loadbalancer presence (available) - :param name: Name of loadbalancer - :param alias: Alias of account - :param location: Datacenter - :param description: Description of loadbalancer - :param status: Enabled / Disabled - :return: True / False - """ - changed = False - result = None - lb_id = self._loadbalancer_exists(name=name) - if lb_id: - result = name - changed = False - else: - if not self.module.check_mode: - result = self.create_loadbalancer(name=name, - alias=alias, - location=location, - description=description, - status=status) - lb_id = result.get('id') - changed = True - - return changed, result, lb_id - - def ensure_loadbalancerpool_present(self, lb_id, alias, location, method, persistence, port): - """ - Checks to see if a load balancer pool exists and creates one if it does not. - :param name: The loadbalancer name - :param alias: The account alias - :param location: the datacenter the load balancer resides in - :param method: the load balancing method - :param persistence: the load balancing persistence type - :param port: the port that the load balancer will listen on - :return: (changed, group, pool_id) - - changed: Boolean whether a change was made - result: The result from the CLC API call - pool_id: The string id of the pool - """ - changed = False - result = None - if not lb_id: - return False, None, None - pool_id = self._loadbalancerpool_exists(alias=alias, location=location, port=port, lb_id=lb_id) - if not pool_id: - changed = True - if not self.module.check_mode: - result = self.create_loadbalancerpool(alias=alias, location=location, lb_id=lb_id, method=method, persistence=persistence, port=port) - pool_id = result.get('id') - - else: - changed = False - result = port - - return changed, result, pool_id - - def ensure_loadbalancer_absent(self,name,alias,location): - """ - Check for loadbalancer presence (not available) - :param name: Name of loadbalancer - :param alias: Alias of account - :param location: Datacenter - :return: (changed, result) - changed: Boolean whether a change was made - result: The result from the CLC API Call - """ - changed = False - result = None - lb_exists = self._loadbalancer_exists(name=name) - if lb_exists: - if not self.module.check_mode: - result = self.delete_loadbalancer(alias=alias, - location=location, - name=name) - changed = True - else: - result = name - changed = False - return changed, result - - def ensure_loadbalancerpool_absent(self, alias, location, name, port): - """ - Checks to see if a load balancer pool exists and deletes it if it does - :param alias: The account alias - :param location: the datacenter the load balancer resides in - :param loadbalancer: the name of the load balancer - :param port: the port that the load balancer will listen on - :return: (changed, group) - - changed: Boolean whether a change was made - result: The result from the CLC API call - """ - changed = False - result = None - lb_exists = self._loadbalancer_exists(name=name) - if lb_exists: - lb_id = self._get_loadbalancer_id(name=name) - pool_id = self._loadbalancerpool_exists(alias=alias, location=location, port=port, lb_id=lb_id) - if pool_id: - changed = True - if not self.module.check_mode: - result = self.delete_loadbalancerpool(alias=alias, location=location, lb_id=lb_id, pool_id=pool_id) - else: - changed = False - result = "Pool doesn't exist" - else: - result = "LB Doesn't Exist" - return changed, result - - def ensure_lbpool_nodes_set(self, alias, location, name, port, nodes): - """ - Checks to see if the provided list of nodes exist for the pool and set the nodes if any in the list doesn't exist - :param alias: The account alias - :param location: the datacenter the load balancer resides in - :param name: the name of the load balancer - :param port: the port that the load balancer will listen on - :param nodes: The list of nodes to be updated to the pool - :return: (changed, group) - - changed: Boolean whether a change was made - result: The result from the CLC API call - """ - result = {} - changed = False - lb_exists = self._loadbalancer_exists(name=name) - if lb_exists: - lb_id = self._get_loadbalancer_id(name=name) - pool_id = self._loadbalancerpool_exists(alias=alias, location=location, port=port, lb_id=lb_id) - if pool_id: - nodes_exist = self._loadbalancerpool_nodes_exists(alias=alias, - location=location, - port=port, - lb_id=lb_id, - pool_id=pool_id, - nodes_to_check=nodes) - if not nodes_exist: - changed = True - result = self.set_loadbalancernodes(alias=alias, - location=location, - lb_id=lb_id, - pool_id=pool_id, - nodes=nodes) - else: - result = "Pool doesn't exist" - else: - result = "Load balancer doesn't Exist" - return changed, result - - def ensure_lbpool_nodes_present(self, alias, location, name, port, nodes): - """ - Checks to see if the provided list of nodes exist for the pool and add the missing nodes to the pool - :param alias: The account alias - :param location: the datacenter the load balancer resides in - :param name: the name of the load balancer - :param port: the port that the load balancer will listen on - :param nodes: the list of nodes to be added - :return: (changed, group) - - changed: Boolean whether a change was made - result: The result from the CLC API call - """ - changed = False - lb_exists = self._loadbalancer_exists(name=name) - if lb_exists: - lb_id = self._get_loadbalancer_id(name=name) - pool_id = self._loadbalancerpool_exists(alias=alias, location=location, port=port, lb_id=lb_id) - if pool_id: - changed, result = self.add_lbpool_nodes(alias=alias, - location=location, - lb_id=lb_id, - pool_id=pool_id, - nodes_to_add=nodes) - else: - result = "Pool doesn't exist" - else: - result = "Load balancer doesn't Exist" - return changed, result - - def ensure_lbpool_nodes_absent(self, alias, location, name, port, nodes): - """ - Checks to see if the provided list of nodes exist for the pool and add the missing nodes to the pool - :param alias: The account alias - :param location: the datacenter the load balancer resides in - :param name: the name of the load balancer - :param port: the port that the load balancer will listen on - :param nodes: the list of nodes to be removed - :return: (changed, group) - - changed: Boolean whether a change was made - result: The result from the CLC API call - """ - changed = False - lb_exists = self._loadbalancer_exists(name=name) - if lb_exists: - lb_id = self._get_loadbalancer_id(name=name) - pool_id = self._loadbalancerpool_exists(alias=alias, location=location, port=port, lb_id=lb_id) - if pool_id: - changed, result = self.remove_lbpool_nodes(alias=alias, - location=location, - lb_id=lb_id, - pool_id=pool_id, - nodes_to_remove=nodes) - else: - result = "Pool doesn't exist" - else: - result = "Load balancer doesn't Exist" - return changed, result - - def create_loadbalancer(self,name,alias,location,description,status): - """ - Create a loadbalancer w/ params - :param name: Name of loadbalancer - :param alias: Alias of account - :param location: Datacenter - :param description: Description for loadbalancer to be created - :param status: Enabled / Disabled - :return: Success / Failure - """ - result = self.clc.v2.API.Call('POST', '/v2/sharedLoadBalancers/%s/%s' % (alias, location), json.dumps({"name":name,"description":description,"status":status})) - sleep(1) - return result - - def create_loadbalancerpool(self, alias, location, lb_id, method, persistence, port): - """ - Creates a pool on the provided load balancer - :param alias: the account alias - :param location: the datacenter the load balancer resides in - :param lb_id: the id string of the load balancer - :param method: the load balancing method - :param persistence: the load balancing persistence type - :param port: the port that the load balancer will listen on - :return: result: The result from the create API call - """ - result = self.clc.v2.API.Call('POST', '/v2/sharedLoadBalancers/%s/%s/%s/pools' % (alias, location, lb_id), json.dumps({"port":port, "method":method, "persistence":persistence})) - return result - - def delete_loadbalancer(self,alias,location,name): - """ - Delete CLC loadbalancer - :param alias: Alias for account - :param location: Datacenter - :param name: Name of the loadbalancer to delete - :return: 204 if successful else failure - """ - lb_id = self._get_loadbalancer_id(name=name) - result = self.clc.v2.API.Call('DELETE', '/v2/sharedLoadBalancers/%s/%s/%s' % (alias, location, lb_id)) - return result - - def delete_loadbalancerpool(self, alias, location, lb_id, pool_id): - """ - Delete a pool on the provided load balancer - :param alias: The account alias - :param location: the datacenter the load balancer resides in - :param lb_id: the id string of the load balancer - :param pool_id: the id string of the pool - :return: result: The result from the delete API call - """ - result = self.clc.v2.API.Call('DELETE', '/v2/sharedLoadBalancers/%s/%s/%s/pools/%s' % (alias, location, lb_id, pool_id)) - return result - - def _get_loadbalancer_id(self, name): - """ - Retrieve unique ID of loadbalancer - :param name: Name of loadbalancer - :return: Unique ID of loadbalancer - """ - for lb in self.lb_dict: - if lb.get('name') == name: - id = lb.get('id') - return id - - def _get_loadbalancer_list(self, alias, location): - """ - Retrieve a list of loadbalancers - :param alias: Alias for account - :param location: Datacenter - :return: JSON data for all loadbalancers at datacenter - """ - return self.clc.v2.API.Call('GET', '/v2/sharedLoadBalancers/%s/%s' % (alias, location)) - - def _loadbalancer_exists(self, name): - """ - Verify a loadbalancer exists - :param name: Name of loadbalancer - :return: False or the ID of the existing loadbalancer - """ - result = False - - for lb in self.lb_dict: - if lb.get('name') == name: - result = lb.get('id') - return result - - def _loadbalancerpool_exists(self, alias, location, port, lb_id): - """ - Checks to see if a pool exists on the specified port on the provided load balancer - :param alias: the account alias - :param location: the datacenter the load balancer resides in - :param port: the port to check and see if it exists - :param lb_id: the id string of the provided load balancer - :return: result: The id string of the pool or False - """ - result = False - pool_list = self.clc.v2.API.Call('GET', '/v2/sharedLoadBalancers/%s/%s/%s/pools' % (alias, location, lb_id)) - for pool in pool_list: - if int(pool.get('port')) == int(port): - result = pool.get('id') - - return result - - def _loadbalancerpool_nodes_exists(self, alias, location, port, lb_id, pool_id, nodes_to_check): - """ - Checks to see if a set of nodes exists on the specified port on the provided load balancer - :param alias: the account alias - :param location: the datacenter the load balancer resides in - :param port: the port to check and see if it exists - :param lb_id: the id string of the provided load balancer - :param pool_id: the id string of the load balancer pool - :param nodes_to_check: the list of nodes to check for - :return: result: The id string of the pool or False - """ - result = False - nodes = self._get_lbpool_nodes(alias, location, lb_id, pool_id) - for node in nodes_to_check: - if not node.get('status'): - node['status'] = 'enabled' - if node in nodes: - result = True - else: - result = False - return result - - def set_loadbalancernodes(self, alias, location, lb_id, pool_id, nodes): - """ - Updates nodes to the provided pool - :param alias: the account alias - :param location: the datacenter the load balancer resides in - :param lb_id: the id string of the load balancer - :param pool_id: the id string of the pool - :param nodes: a list of dictionaries containing the nodes to set - :return: result: The result from the API call - """ - result = None - if not lb_id: - return result - if not self.module.check_mode: - result = self.clc.v2.API.Call('PUT', - '/v2/sharedLoadBalancers/%s/%s/%s/pools/%s/nodes' - % (alias, location, lb_id, pool_id), json.dumps(nodes)) - return result - - def add_lbpool_nodes(self, alias, location, lb_id, pool_id, nodes_to_add): - """ - Add nodes to the provided pool - :param alias: the account alias - :param location: the datacenter the load balancer resides in - :param lb_id: the id string of the load balancer - :param pool_id: the id string of the pool - :param nodes: a list of dictionaries containing the nodes to add - :return: (changed, group) - - changed: Boolean whether a change was made - result: The result from the CLC API call - """ - changed = False - result = {} - nodes = self._get_lbpool_nodes(alias, location, lb_id, pool_id) - for node in nodes_to_add: - if not node.get('status'): - node['status'] = 'enabled' - if not node in nodes: - changed = True - nodes.append(node) - if changed == True and not self.module.check_mode: - result = self.set_loadbalancernodes(alias, location, lb_id, pool_id, nodes) - return changed, result - - def remove_lbpool_nodes(self, alias, location, lb_id, pool_id, nodes_to_remove): - """ - Removes nodes from the provided pool - :param alias: the account alias - :param location: the datacenter the load balancer resides in - :param lb_id: the id string of the load balancer - :param pool_id: the id string of the pool - :param nodes: a list of dictionaries containing the nodes to remove - :return: (changed, group) - - changed: Boolean whether a change was made - result: The result from the CLC API call - """ - changed = False - result = {} - nodes = self._get_lbpool_nodes(alias, location, lb_id, pool_id) - for node in nodes_to_remove: - if not node.get('status'): - node['status'] = 'enabled' - if node in nodes: - changed = True - nodes.remove(node) - if changed == True and not self.module.check_mode: - result = self.set_loadbalancernodes(alias, location, lb_id, pool_id, nodes) - return changed, result - - def _get_lbpool_nodes(self, alias, location, lb_id, pool_id): - """ - Return the list of nodes available to the provided load balancer pool - :param alias: the account alias - :param location: the datacenter the load balancer resides in - :param lb_id: the id string of the load balancer - :param pool_id: the id string of the pool - :return: result: The list of nodes - """ - result = self.clc.v2.API.Call('GET', - '/v2/sharedLoadBalancers/%s/%s/%s/pools/%s/nodes' - % (alias, location, lb_id, pool_id)) - return result - - @staticmethod - def define_argument_spec(): - """ - Define the argument spec for the ansible module - :return: argument spec dictionary - """ - argument_spec = dict( - name=dict(required=True), - description=dict(default=None), - location=dict(required=True, default=None), - alias=dict(required=True, default=None), - port=dict(choices=[80, 443]), - method=dict(choices=['leastConnection', 'roundRobin']), - persistence=dict(choices=['standard', 'sticky']), - nodes=dict(type='list', default=[]), - status=dict(default='enabled', choices=['enabled', 'disabled']), - state=dict(default='present', choices=['present', 'absent', 'port_absent', 'nodes_present', 'nodes_absent']), - wait=dict(type='bool', default=True) - ) - - return argument_spec - - # - # Module Behavior Functions - # - - 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") - - @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=ClcLoadBalancer.define_argument_spec(), - supports_check_mode=True) - clc_loadbalancer = ClcLoadBalancer(module) - clc_loadbalancer.process_request() - -from ansible.module_utils.basic import * # pylint: disable=W0614 -if __name__ == '__main__': - main() diff --git a/cloud/centurylink/clc_modify_server.py b/cloud/centurylink/clc_modify_server.py deleted file mode 100644 index 1a1e4d5b858..00000000000 --- a/cloud/centurylink/clc_modify_server.py +++ /dev/null @@ -1,710 +0,0 @@ -#!/usr/bin/python - -# CenturyLink Cloud Ansible Modules. -# -# These Ansible modules enable the CenturyLink Cloud v2 API to be called -# from an within Ansible Playbook. -# -# This file is part of CenturyLink Cloud, and is maintained -# by the Workflow as a Service Team -# -# Copyright 2015 CenturyLink Cloud -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# CenturyLink Cloud: http://www.CenturyLinkCloud.com -# API Documentation: https://www.centurylinkcloud.com/api-docs/v2/ -# - -DOCUMENTATION = ''' -module: clc_modify_server -short_desciption: modify servers in CenturyLink Cloud. -description: - - An Ansible module to modify servers in CenturyLink Cloud. -options: - server_ids: - description: - - A list of server Ids to modify. - default: [] - required: True - aliases: [] - cpu: - description: - - How many CPUs to update on the server - default: None - required: False - aliases: [] - memory: - description: - - Memory in GB. - default: None - required: False - aliases: [] - anti_affinity_policy_id: - description: - - The anti affinity policy id to be set for a heperscale server. - This is mutually exclusive with 'anti_affinity_policy_name' - default: None - required: False - aliases: [] - anti_affinity_policy_name: - description: - - The anti affinity policy name to be set for a heperscale server. - This is mutually exclusive with 'anti_affinity_policy_id' - default: None - required: False - aliases: [] - alert_policy_id: - description: - - The alert policy id to be associated. - This is mutually exclusive with 'alert_policy_name' - default: None - required: False - aliases: [] - alert_policy_name: - description: - - The alert policy name to be associated. - This is mutually exclusive with 'alert_policy_id' - default: None - required: False - aliases: [] - state: - description: - - The state to insure that the provided resources are in. - default: 'present' - required: False - choices: ['present', 'absent'] - aliases: [] - wait: - description: - - Whether to wait for the provisioning tasks to finish before returning. - default: True - required: False - choices: [ True, False] - aliases: [] -''' - -EXAMPLES = ''' -# Note - You must set the CLC_V2_API_USERNAME And CLC_V2_API_PASSWD Environment variables before running these examples - -- name: set the cpu count to 4 on a server - clc_server: - server_ids: ['UC1ACCTTEST01'] - cpu: 4 - state: present - -- name: set the memory to 8GB on a server - clc_server: - server_ids: ['UC1ACCTTEST01'] - memory: 8 - state: present - -- name: set the anti affinity policy on a server - clc_server: - server_ids: ['UC1ACCTTEST01'] - anti_affinity_policy_name: 'aa_policy' - state: present - -- name: set the alert policy on a server - clc_server: - server_ids: ['UC1ACCTTEST01'] - alert_policy_name: 'alert_policy' - state: present - -- name: set the memory to 16GB and cpu to 8 core on a lust if servers - clc_server: - server_ids: ['UC1ACCTTEST01','UC1ACCTTEST02'] - cpu: 8 - memory: 16 - state: present -''' - -__version__ = '${version}' - -import requests - -# -# Requires the clc-python-sdk. -# sudo pip install clc-sdk -# -try: - import clc as clc_sdk - from clc import CLCException - from clc import APIFailedResponse -except ImportError: - CLC_FOUND = False - clc_sdk = None -else: - CLC_FOUND = True - - -class ClcModifyServer(): - clc = clc_sdk - - def __init__(self, module): - """ - Construct module - """ - self.clc = clc_sdk - self.module = module - self.group_dict = {} - - if not CLC_FOUND: - self.module.fail_json( - msg='clc-python-sdk required for this module') - - self._set_user_agent(self.clc) - - def process_request(self): - """ - Process the request - Main Code Path - :return: Returns with either an exit_json or fail_json - """ - self._set_clc_credentials_from_env() - - p = self.module.params - - server_ids = p['server_ids'] - if not isinstance(server_ids, list): - return self.module.fail_json( - msg='server_ids needs to be a list of instances to modify: %s' % - server_ids) - - (changed, server_dict_array, new_server_ids) = ClcModifyServer._modify_servers( - module=self.module, clc=self.clc, server_ids=server_ids) - - self.module.exit_json( - changed=changed, - server_ids=new_server_ids, - servers=server_dict_array) - - @staticmethod - def _define_module_argument_spec(): - """ - Define the argument spec for the ansible module - :return: argument spec dictionary - """ - argument_spec = dict( - server_ids=dict(type='list', required=True), - state=dict(default='present', choices=['present', 'absent']), - cpu=dict(), - memory=dict(), - anti_affinity_policy_id=dict(), - anti_affinity_policy_name=dict(), - alert_policy_id=dict(), - alert_policy_name=dict(), - wait=dict(type='bool', default=True) - ) - mutually_exclusive = [ - ['anti_affinity_policy_id', 'anti_affinity_policy_name'], - ['alert_policy_id', 'alert_policy_name'] - ] - return {"argument_spec": argument_spec, - "mutually_exclusive": mutually_exclusive} - - 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") - - @staticmethod - def _wait_for_requests(clc, requests, servers, wait): - """ - Block until server provisioning requests are completed. - :param clc: the clc-sdk instance to use - :param requests: a list of clc-sdk.Request instances - :param servers: a list of servers to refresh - :param wait: a boolean on whether to block or not. This function is skipped if True - :return: none - """ - if wait: - # Requests.WaitUntilComplete() returns the count of failed requests - failed_requests_count = sum( - [request.WaitUntilComplete() for request in requests]) - - if failed_requests_count > 0: - raise clc - else: - ClcModifyServer._refresh_servers(servers) - - @staticmethod - def _refresh_servers(servers): - """ - Loop through a list of servers and refresh them - :param servers: list of clc-sdk.Server instances to refresh - :return: none - """ - for server in servers: - server.Refresh() - - @staticmethod - def _modify_servers(module, clc, server_ids): - """ - modify the servers configuration on the provided list - :param module: the AnsibleModule object - :param clc: the clc-sdk instance to use - :param server_ids: list of servers to modify - :return: a list of dictionaries with server information about the servers that were modified - """ - p = module.params - wait = p.get('wait') - state = p.get('state') - server_params = { - 'cpu': p.get('cpu'), - 'memory': p.get('memory'), - 'anti_affinity_policy_id': p.get('anti_affinity_policy_id'), - 'anti_affinity_policy_name': p.get('anti_affinity_policy_name'), - 'alert_policy_id': p.get('alert_policy_id'), - 'alert_policy_name': p.get('alert_policy_name'), - } - changed = False - server_changed = False - aa_changed = False - ap_changed = False - server_dict_array = [] - result_server_ids = [] - requests = [] - - if not isinstance(server_ids, list) or len(server_ids) < 1: - return module.fail_json( - msg='server_ids should be a list of servers, aborting') - - servers = clc.v2.Servers(server_ids).Servers() - if state == 'present': - for server in servers: - server_changed, server_result, changed_servers = ClcModifyServer._ensure_server_config( - clc, module, None, server, server_params) - if server_result: - requests.append(server_result) - aa_changed, changed_servers = ClcModifyServer._ensure_aa_policy( - clc, module, None, server, server_params) - ap_changed, changed_servers = ClcModifyServer._ensure_alert_policy_present( - clc, module, None, server, server_params) - elif state == 'absent': - for server in servers: - ap_changed, changed_servers = ClcModifyServer._ensure_alert_policy_absent( - clc, module, None, server, server_params) - if server_changed or aa_changed or ap_changed: - changed = True - - if wait: - for r in requests: - r.WaitUntilComplete() - for server in changed_servers: - server.Refresh() - - for server in changed_servers: - server_dict_array.append(server.data) - result_server_ids.append(server.id) - - return changed, server_dict_array, result_server_ids - - @staticmethod - def _ensure_server_config( - clc, module, alias, server, server_params): - """ - ensures the server is updated with the provided cpu and memory - :param clc: the clc-sdk instance to use - :param module: the AnsibleModule object - :param alias: the CLC account alias - :param server: the CLC server object - :param server_params: the dictionary of server parameters - :return: (changed, group) - - changed: Boolean whether a change was made - result: The result from the CLC API call - """ - cpu = server_params.get('cpu') - memory = server_params.get('memory') - changed = False - result = None - changed_servers = [] - - if not cpu: - cpu = server.cpu - if not memory: - memory = server.memory - if memory != server.memory or cpu != server.cpu: - changed_servers.append(server) - result = ClcModifyServer._modify_clc_server( - clc, - module, - None, - server.id, - cpu, - memory) - changed = True - return changed, result, changed_servers - - @staticmethod - def _modify_clc_server(clc, module, acct_alias, server_id, cpu, memory): - """ - Modify the memory or CPU on a clc server. This function is not yet implemented. - :param clc: the clc-sdk instance to use - :param module: the AnsibleModule object - :param acct_alias: the clc account alias to look up the server - :param server_id: id of the server to modify - :param cpu: the new cpu value - :param memory: the new memory value - :return: the result of CLC API call - """ - if not acct_alias: - acct_alias = clc.v2.Account.GetAlias() - if not server_id: - return module.fail_json( - msg='server_id must be provided to modify the server') - - result = None - - if not module.check_mode: - - # Update the server configuation - job_obj = clc.v2.API.Call('PATCH', - 'servers/%s/%s' % (acct_alias, - server_id), - json.dumps([{"op": "set", - "member": "memory", - "value": memory}, - {"op": "set", - "member": "cpu", - "value": cpu}])) - result = clc.v2.Requests(job_obj) - return result - - @staticmethod - def _ensure_aa_policy( - clc, module, acct_alias, server, server_params): - """ - ensures the server is updated with the provided anti affinity policy - :param clc: the clc-sdk instance to use - :param module: the AnsibleModule object - :param acct_alias: the CLC account alias - :param server: the CLC server object - :param server_params: the dictionary of server parameters - :return: (changed, group) - - changed: Boolean whether a change was made - result: The result from the CLC API call - """ - changed = False - changed_servers = [] - - if not acct_alias: - acct_alias = clc.v2.Account.GetAlias() - - aa_policy_id = server_params.get('anti_affinity_policy_id') - aa_policy_name = server_params.get('anti_affinity_policy_name') - if not aa_policy_id and aa_policy_name: - aa_policy_id = ClcModifyServer._get_aa_policy_id_by_name( - clc, - module, - acct_alias, - aa_policy_name) - current_aa_policy_id = ClcModifyServer._get_aa_policy_id_of_server( - clc, - module, - acct_alias, - server.id) - - if aa_policy_id and aa_policy_id != current_aa_policy_id: - if server not in changed_servers: - changed_servers.append(server) - ClcModifyServer._modify_aa_policy( - clc, - module, - acct_alias, - server.id, - aa_policy_id) - changed = True - return changed, changed_servers - - @staticmethod - def _modify_aa_policy(clc, module, acct_alias, server_id, aa_policy_id): - """ - modifies the anti affinity policy of the CLC server - :param clc: the clc-sdk instance to use - :param module: the AnsibleModule object - :param acct_alias: the CLC account alias - :param server_id: the CLC server id - :param aa_policy_id: the anti affinity policy id - :return: result: The result from the CLC API call - """ - result = None - if not module.check_mode: - result = clc.v2.API.Call('PUT', - 'servers/%s/%s/antiAffinityPolicy' % ( - acct_alias, - server_id), - json.dumps({"id": aa_policy_id})) - return result - - @staticmethod - def _get_aa_policy_id_by_name(clc, module, alias, aa_policy_name): - """ - retrieves the anti affinity policy id of the server based on the name of the policy - :param clc: the clc-sdk instance to use - :param module: the AnsibleModule object - :param alias: the CLC account alias - :param aa_policy_name: the anti affinity policy name - :return: aa_policy_id: The anti affinity policy id - """ - aa_policy_id = None - aa_policies = clc.v2.API.Call(method='GET', - url='antiAffinityPolicies/%s' % (alias)) - for aa_policy in aa_policies.get('items'): - if aa_policy.get('name') == aa_policy_name: - if not aa_policy_id: - aa_policy_id = aa_policy.get('id') - else: - return module.fail_json( - msg='mutiple anti affinity policies were found with policy name : %s' % - (aa_policy_name)) - if not aa_policy_id: - return module.fail_json( - msg='No anti affinity policy was found with policy name : %s' % - (aa_policy_name)) - return aa_policy_id - - @staticmethod - def _get_aa_policy_id_of_server(clc, module, alias, server_id): - """ - retrieves the anti affinity policy id of the server based on the CLC server id - :param clc: the clc-sdk instance to use - :param module: the AnsibleModule object - :param alias: the CLC account alias - :param server_id: the CLC server id - :return: aa_policy_id: The anti affinity policy id - """ - aa_policy_id = None - try: - result = clc.v2.API.Call( - method='GET', url='servers/%s/%s/antiAffinityPolicy' % - (alias, server_id)) - aa_policy_id = result.get('id') - except APIFailedResponse as e: - if e.response_status_code != 404: - raise e - return aa_policy_id - - @staticmethod - def _ensure_alert_policy_present( - clc, module, acct_alias, server, server_params): - """ - ensures the server is updated with the provided alert policy - :param clc: the clc-sdk instance to use - :param module: the AnsibleModule object - :param acct_alias: the CLC account alias - :param server: the CLC server object - :param server_params: the dictionary of server parameters - :return: (changed, group) - - changed: Boolean whether a change was made - result: The result from the CLC API call - """ - changed = False - changed_servers = [] - - if not acct_alias: - acct_alias = clc.v2.Account.GetAlias() - - alert_policy_id = server_params.get('alert_policy_id') - alert_policy_name = server_params.get('alert_policy_name') - if not alert_policy_id and alert_policy_name: - alert_policy_id = ClcModifyServer._get_alert_policy_id_by_name( - clc, - module, - acct_alias, - alert_policy_name) - if alert_policy_id and not ClcModifyServer._alert_policy_exists(server, alert_policy_id): - if server not in changed_servers: - changed_servers.append(server) - ClcModifyServer._add_alert_policy_to_server( - clc, - module, - acct_alias, - server.id, - alert_policy_id) - changed = True - return changed, changed_servers - - @staticmethod - def _ensure_alert_policy_absent( - clc, module, acct_alias, server, server_params): - """ - ensures the alert policy is removed from the server - :param clc: the clc-sdk instance to use - :param module: the AnsibleModule object - :param acct_alias: the CLC account alias - :param server: the CLC server object - :param server_params: the dictionary of server parameters - :return: (changed, group) - - changed: Boolean whether a change was made - result: The result from the CLC API call - """ - changed = False - result = None - changed_servers = [] - - if not acct_alias: - acct_alias = clc.v2.Account.GetAlias() - - alert_policy_id = server_params.get('alert_policy_id') - alert_policy_name = server_params.get('alert_policy_name') - if not alert_policy_id and alert_policy_name: - alert_policy_id = ClcModifyServer._get_alert_policy_id_by_name( - clc, - module, - acct_alias, - alert_policy_name) - - if alert_policy_id and ClcModifyServer._alert_policy_exists(server, alert_policy_id): - if server not in changed_servers: - changed_servers.append(server) - ClcModifyServer._remove_alert_policy_to_server( - clc, - module, - acct_alias, - server.id, - alert_policy_id) - changed = True - return changed, changed_servers - - @staticmethod - def _add_alert_policy_to_server(clc, module, acct_alias, server_id, alert_policy_id): - """ - add the alert policy to CLC server - :param clc: the clc-sdk instance to use - :param module: the AnsibleModule object - :param acct_alias: the CLC account alias - :param server_id: the CLC server id - :param alert_policy_id: the alert policy id - :return: result: The result from the CLC API call - """ - result = None - if not module.check_mode: - try: - result = clc.v2.API.Call('POST', - 'servers/%s/%s/alertPolicies' % ( - acct_alias, - server_id), - json.dumps({"id": alert_policy_id})) - except clc.APIFailedResponse as e: - return module.fail_json( - msg='Unable to set alert policy to the server : %s. %s' % (server_id, str(e.response_text))) - return result - - @staticmethod - def _remove_alert_policy_to_server(clc, module, acct_alias, server_id, alert_policy_id): - """ - remove the alert policy to the CLC server - :param clc: the clc-sdk instance to use - :param module: the AnsibleModule object - :param acct_alias: the CLC account alias - :param server_id: the CLC server id - :param alert_policy_id: the alert policy id - :return: result: The result from the CLC API call - """ - result = None - if not module.check_mode: - try: - result = clc.v2.API.Call('DELETE', - 'servers/%s/%s/alertPolicies/%s' - % (acct_alias, server_id, alert_policy_id)) - except clc.APIFailedResponse as e: - return module.fail_json( - msg='Unable to remove alert policy to the server : %s. %s' % (server_id, str(e.response_text))) - return result - - @staticmethod - def _get_alert_policy_id_by_name(clc, module, alias, alert_policy_name): - """ - retrieves the alert policy id of the server based on the name of the policy - :param clc: the clc-sdk instance to use - :param module: the AnsibleModule object - :param alias: the CLC account alias - :param alert_policy_name: the alert policy name - :return: alert_policy_id: The alert policy id - """ - alert_policy_id = None - alert_policies = clc.v2.API.Call(method='GET', - url='alertPolicies/%s' % (alias)) - for alert_policy in alert_policies.get('items'): - if alert_policy.get('name') == alert_policy_name: - if not alert_policy_id: - alert_policy_id = alert_policy.get('id') - else: - return module.fail_json( - msg='mutiple alert policies were found with policy name : %s' % - (alert_policy_name)) - return alert_policy_id - - @staticmethod - def _alert_policy_exists(server, alert_policy_id): - """ - Checks if the alert policy exists for the server - :param server: the clc server object - :param alert_policy_id: the alert policy - :return: True: if the given alert policy id associated to the server, False otherwise - """ - result = False - alert_policies = server.alertPolicies - if alert_policies: - for alert_policy in alert_policies: - if alert_policy.get('id') == alert_policy_id: - result = True - return result - - @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 - """ - - argument_dict = ClcModifyServer._define_module_argument_spec() - module = AnsibleModule(supports_check_mode=True, **argument_dict) - clc_modify_server = ClcModifyServer(module) - clc_modify_server.process_request() - -from ansible.module_utils.basic import * # pylint: disable=W0614 -if __name__ == '__main__': - main() diff --git a/cloud/centurylink/clc_server.py b/cloud/centurylink/clc_server.py deleted file mode 100644 index e102cd21f47..00000000000 --- a/cloud/centurylink/clc_server.py +++ /dev/null @@ -1,1323 +0,0 @@ -#!/usr/bin/python - -# CenturyLink Cloud Ansible Modules. -# -# These Ansible modules enable the CenturyLink Cloud v2 API to be called -# from an within Ansible Playbook. -# -# This file is part of CenturyLink Cloud, and is maintained -# by the Workflow as a Service Team -# -# Copyright 2015 CenturyLink Cloud -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# CenturyLink Cloud: http://www.CenturyLinkCloud.com -# API Documentation: https://www.centurylinkcloud.com/api-docs/v2/ -# - -DOCUMENTATION = ''' -module: clc_server -short_desciption: Create, Delete, Start and Stop servers in CenturyLink Cloud. -description: - - An Ansible module to Create, Delete, Start and Stop servers in CenturyLink Cloud. -options: - additional_disks: - description: - - Specify additional disks for the server - required: False - default: None - aliases: [] - add_public_ip: - description: - - Whether to add a public ip to the server - required: False - default: False - choices: [False, True] - aliases: [] - alias: - description: - - The account alias to provision the servers under. - default: - - The default alias for the API credentials - required: False - default: None - aliases: [] - anti_affinity_policy_id: - description: - - The anti-affinity policy to assign to the server. This is mutually exclusive with 'anti_affinity_policy_name'. - required: False - default: None - aliases: [] - anti_affinity_policy_name: - description: - - The anti-affinity policy to assign to the server. This is mutually exclusive with 'anti_affinity_policy_id'. - required: False - default: None - aliases: [] - alert_policy_id: - description: - - The alert policy to assign to the server. This is mutually exclusive with 'alert_policy_name'. - required: False - default: None - aliases: [] - alert_policy_name: - description: - - The alert policy to assign to the server. This is mutually exclusive with 'alert_policy_id'. - required: False - default: None - aliases: [] - - count: - description: - - The number of servers to build (mutually exclusive with exact_count) - default: None - aliases: [] - count_group: - description: - - Required when exact_count is specified. The Server Group use to determine how many severs to deploy. - default: 1 - required: False - aliases: [] - cpu: - description: - - How many CPUs to provision on the server - default: None - required: False - aliases: [] - cpu_autoscale_policy_id: - description: - - The autoscale policy to assign to the server. - default: None - required: False - aliases: [] - custom_fields: - description: - - A dictionary of custom fields to set on the server. - default: [] - required: False - aliases: [] - description: - description: - - The description to set for the server. - default: None - required: False - aliases: [] - exact_count: - description: - - Run in idempotent mode. Will insure that this exact number of servers are running in the provided group, creating and deleting them to reach that count. Requires count_group to be set. - default: None - required: False - aliases: [] - group: - description: - - The Server Group to create servers under. - default: 'Default Group' - required: False - aliases: [] - ip_address: - description: - - The IP Address for the server. One is assigned if not provided. - default: None - required: False - aliases: [] - location: - description: - - The Datacenter to create servers in. - default: None - required: False - aliases: [] - managed_os: - description: - - Whether to create the server as 'Managed' or not. - default: False - required: False - choices: [True, False] - aliases: [] - memory: - description: - - Memory in GB. - default: 1 - required: False - aliases: [] - name: - description: - - A 1 to 6 character identifier to use for the server. - default: None - required: False - aliases: [] - network_id: - description: - - The network UUID on which to create servers. - default: None - required: False - aliases: [] - packages: - description: - - Blueprints to run on the server after its created. - default: [] - required: False - aliases: [] - password: - description: - - Password for the administrator user - default: None - required: False - aliases: [] - primary_dns: - description: - - Primary DNS used by the server. - default: None - required: False - aliases: [] - public_ip_protocol: - description: - - The protocol to use for the public ip if add_public_ip is set to True. - default: 'TCP' - required: False - aliases: [] - public_ip_ports: - description: - - A list of ports to allow on the firewall to thes servers public ip, if add_public_ip is set to True. - default: [] - required: False - aliases: [] - secondary_dns: - description: - - Secondary DNS used by the server. - default: None - required: False - aliases: [] - server_ids: - description: - - Required for started, stopped, and absent states. A list of server Ids to insure are started, stopped, or absent. - default: [] - required: False - aliases: [] - source_server_password: - description: - - The password for the source server if a clone is specified. - default: None - required: False - aliases: [] - state: - description: - - The state to insure that the provided resources are in. - default: 'present' - required: False - choices: ['present', 'absent', 'started', 'stopped'] - aliases: [] - storage_type: - description: - - The type of storage to attach to the server. - default: 'standard' - required: False - choices: ['standard', 'hyperscale'] - aliases: [] - template: - description: - - The template to use for server creation. Will search for a template if a partial string is provided. - default: None - required: false - aliases: [] - ttl: - description: - - The time to live for the server in seconds. The server will be deleted when this time expires. - default: None - required: False - aliases: [] - type: - description: - - The type of server to create. - default: 'standard' - required: False - choices: ['standard', 'hyperscale'] - aliases: [] - wait: - description: - - Whether to wait for the provisioning tasks to finish before returning. - default: True - required: False - choices: [ True, False] - aliases: [] -''' - -EXAMPLES = ''' -# Note - You must set the CLC_V2_API_USERNAME And CLC_V2_API_PASSWD Environment variables before running these examples - -- name: Provision a single Ubuntu Server - clc_server: - name: test - template: ubuntu-14-64 - count: 1 - group: 'Default Group' - state: present - -- name: Ensure 'Default Group' has exactly 5 servers - clc_server: - name: test - template: ubuntu-14-64 - exact_count: 5 - count_group: 'Default Group' - group: 'Default Group' - -- name: Stop a Server - clc_server: - server_ids: ['UC1ACCTTEST01'] - state: stopped - -- name: Start a Server - clc_server: - server_ids: ['UC1ACCTTEST01'] - state: started - -- name: Delete a Server - clc_server: - server_ids: ['UC1ACCTTEST01'] - state: absent -''' - -__version__ = '${version}' - -import requests -from time import sleep - -# -# Requires the clc-python-sdk. -# sudo pip install clc-sdk -# -try: - import clc as clc_sdk - from clc import CLCException - from clc import APIFailedResponse -except ImportError: - CLC_FOUND = False - clc_sdk = None -else: - CLC_FOUND = True - - -class ClcServer(): - clc = clc_sdk - - def __init__(self, module): - """ - Construct module - """ - self.clc = clc_sdk - self.module = module - self.group_dict = {} - - if not CLC_FOUND: - self.module.fail_json( - msg='clc-python-sdk required for this module') - - self._set_user_agent(self.clc) - - def process_request(self): - """ - Process the request - Main Code Path - :return: Returns with either an exit_json or fail_json - """ - self._set_clc_credentials_from_env() - - self.module.params = ClcServer._validate_module_params(self.clc, - self.module) - p = self.module.params - state = p.get('state') - - # - # Handle each state - # - - if state == 'absent': - server_ids = p['server_ids'] - if not isinstance(server_ids, list): - self.module.fail_json( - msg='server_ids needs to be a list of instances to delete: %s' % - server_ids) - - (changed, - server_dict_array, - new_server_ids) = ClcServer._delete_servers(module=self.module, - clc=self.clc, - server_ids=server_ids) - - elif state in ('started', 'stopped'): - server_ids = p.get('server_ids') - if not isinstance(server_ids, list): - self.module.fail_json( - msg='server_ids needs to be a list of servers to run: %s' % - server_ids) - - (changed, - server_dict_array, - new_server_ids) = ClcServer._startstop_servers(self.module, - self.clc, - server_ids) - - elif state == 'present': - # Changed is always set to true when provisioning new instances - if not p.get('template'): - self.module.fail_json( - msg='template parameter is required for new instance') - - if p.get('exact_count') is None: - (server_dict_array, - new_server_ids, - changed) = ClcServer._create_servers(self.module, - self.clc) - else: - (server_dict_array, - new_server_ids, - changed) = ClcServer._enforce_count(self.module, - self.clc) - - self.module.exit_json( - changed=changed, - server_ids=new_server_ids, - servers=server_dict_array) - - @staticmethod - def _define_module_argument_spec(): - """ - Define the argument spec for the ansible module - :return: argument spec dictionary - """ - argument_spec = dict(name=dict(), - template=dict(), - group=dict(default='Default Group'), - network_id=dict(), - location=dict(default=None), - cpu=dict(default=1), - memory=dict(default='1'), - alias=dict(default=None), - password=dict(default=None), - ip_address=dict(default=None), - storage_type=dict(default='standard'), - type=dict( - default='standard', - choices=[ - 'standard', - 'hyperscale']), - primary_dns=dict(default=None), - secondary_dns=dict(default=None), - additional_disks=dict(type='list', default=[]), - custom_fields=dict(type='list', default=[]), - ttl=dict(default=None), - managed_os=dict(type='bool', default=False), - description=dict(default=None), - source_server_password=dict(default=None), - cpu_autoscale_policy_id=dict(default=None), - anti_affinity_policy_id=dict(default=None), - anti_affinity_policy_name=dict(default=None), - alert_policy_id=dict(default=None), - alert_policy_name=dict(default=None), - packages=dict(type='list', default=[]), - state=dict( - default='present', - choices=[ - 'present', - 'absent', - 'started', - 'stopped']), - count=dict(type='int', default='1'), - exact_count=dict(type='int', default=None), - count_group=dict(), - server_ids=dict(type='list'), - add_public_ip=dict(type='bool', default=False), - public_ip_protocol=dict(default='TCP'), - public_ip_ports=dict(type='list'), - wait=dict(type='bool', default=True)) - - mutually_exclusive = [ - ['exact_count', 'count'], - ['exact_count', 'state'], - ['anti_affinity_policy_id', 'anti_affinity_policy_name'], - ['alert_policy_id', 'alert_policy_name'], - ] - return {"argument_spec": argument_spec, - "mutually_exclusive": mutually_exclusive} - - 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") - - @staticmethod - def _validate_module_params(clc, module): - """ - Validate the module params, and lookup default values. - :param clc: clc-sdk instance to use - :param module: module to validate - :return: dictionary of validated params - """ - params = module.params - datacenter = ClcServer._find_datacenter(clc, module) - - ClcServer._validate_types(module) - ClcServer._validate_name(module) - - params['alias'] = ClcServer._find_alias(clc, module) - params['cpu'] = ClcServer._find_cpu(clc, module) - params['memory'] = ClcServer._find_memory(clc, module) - params['description'] = ClcServer._find_description(module) - params['ttl'] = ClcServer._find_ttl(clc, module) - params['template'] = ClcServer._find_template_id(module, datacenter) - params['group'] = ClcServer._find_group(module, datacenter).id - params['network_id'] = ClcServer._find_network_id(module, datacenter) - - return params - - @staticmethod - def _find_datacenter(clc, module): - """ - Find the datacenter by calling the CLC API. - :param clc: clc-sdk instance to use - :param module: module to validate - :return: clc-sdk.Datacenter instance - """ - location = module.params.get('location') - try: - datacenter = clc.v2.Datacenter(location) - return datacenter - except CLCException: - module.fail_json(msg=str("Unable to find location: " + location)) - - @staticmethod - def _find_alias(clc, module): - """ - Find or Validate the Account Alias by calling the CLC API - :param clc: clc-sdk instance to use - :param module: module to validate - :return: clc-sdk.Account instance - """ - alias = module.params.get('alias') - if not alias: - alias = clc.v2.Account.GetAlias() - return alias - - @staticmethod - def _find_cpu(clc, module): - """ - Find or validate the CPU value by calling the CLC API - :param clc: clc-sdk instance to use - :param module: module to validate - :return: Int value for CPU - """ - cpu = module.params.get('cpu') - group_id = module.params.get('group_id') - alias = module.params.get('alias') - state = module.params.get('state') - - if not cpu and state == 'present': - group = clc.v2.Group(id=group_id, - alias=alias) - if group.Defaults("cpu"): - cpu = group.Defaults("cpu") - else: - module.fail_json( - msg=str("Cannot determine a default cpu value. Please provide a value for cpu.")) - return cpu - - @staticmethod - def _find_memory(clc, module): - """ - Find or validate the Memory value by calling the CLC API - :param clc: clc-sdk instance to use - :param module: module to validate - :return: Int value for Memory - """ - memory = module.params.get('memory') - group_id = module.params.get('group_id') - alias = module.params.get('alias') - state = module.params.get('state') - - if not memory and state == 'present': - group = clc.v2.Group(id=group_id, - alias=alias) - if group.Defaults("memory"): - memory = group.Defaults("memory") - else: - module.fail_json(msg=str( - "Cannot determine a default memory value. Please provide a value for memory.")) - return memory - - @staticmethod - def _find_description(module): - """ - Set the description module param to name if description is blank - :param module: the module to validate - :return: string description - """ - description = module.params.get('description') - if not description: - description = module.params.get('name') - return description - - @staticmethod - def _validate_types(module): - """ - Validate that type and storage_type are set appropriately, and fail if not - :param module: the module to validate - :return: none - """ - state = module.params.get('state') - type = module.params.get( - 'type').lower() if module.params.get('type') else None - storage_type = module.params.get( - 'storage_type').lower() if module.params.get('storage_type') else None - - if state == "present": - if type == "standard" and storage_type not in ( - "standard", "premium"): - module.fail_json( - msg=str("Standard VMs must have storage_type = 'standard' or 'premium'")) - - if type == "hyperscale" and storage_type != "hyperscale": - module.fail_json( - msg=str("Hyperscale VMs must have storage_type = 'hyperscale'")) - - @staticmethod - def _find_ttl(clc, module): - """ - Validate that TTL is > 3600 if set, and fail if not - :param clc: clc-sdk instance to use - :param module: module to validate - :return: validated ttl - """ - ttl = module.params.get('ttl') - - if ttl: - if ttl <= 3600: - module.fail_json(msg=str("Ttl cannot be <= 3600")) - else: - ttl = clc.v2.time_utils.SecondsToZuluTS(int(time.time()) + ttl) - return ttl - - @staticmethod - def _find_template_id(module, datacenter): - """ - Find the template id by calling the CLC API. - :param module: the module to validate - :param datacenter: the datacenter to search for the template - :return: a valid clc template id - """ - lookup_template = module.params.get('template') - state = module.params.get('state') - result = None - - if state == 'present': - try: - result = datacenter.Templates().Search(lookup_template)[0].id - except CLCException: - module.fail_json( - msg=str( - "Unable to find a template: " + - lookup_template + - " in location: " + - datacenter.id)) - return result - - @staticmethod - def _find_network_id(module, datacenter): - """ - Validate the provided network id or return a default. - :param module: the module to validate - :param datacenter: the datacenter to search for a network id - :return: a valid network id - """ - network_id = module.params.get('network_id') - - if not network_id: - try: - network_id = datacenter.Networks().networks[0].id - except CLCException: - module.fail_json( - msg=str( - "Unable to find a network in location: " + - datacenter.id)) - - return network_id - - @staticmethod - def _create_servers(module, clc, override_count=None): - """ - Create New Servers - :param module: the AnsibleModule object - :param clc: the clc-sdk instance to use - :return: a list of dictionaries with server information about the servers that were created - """ - p = module.params - requests = [] - servers = [] - server_dict_array = [] - created_server_ids = [] - - add_public_ip = p.get('add_public_ip') - public_ip_protocol = p.get('public_ip_protocol') - public_ip_ports = p.get('public_ip_ports') - wait = p.get('wait') - - params = { - 'name': p.get('name'), - 'template': p.get('template'), - 'group_id': p.get('group'), - 'network_id': p.get('network_id'), - 'cpu': p.get('cpu'), - 'memory': p.get('memory'), - 'alias': p.get('alias'), - 'password': p.get('password'), - 'ip_address': p.get('ip_address'), - 'storage_type': p.get('storage_type'), - 'type': p.get('type'), - 'primary_dns': p.get('primary_dns'), - 'secondary_dns': p.get('secondary_dns'), - 'additional_disks': p.get('additional_disks'), - 'custom_fields': p.get('custom_fields'), - 'ttl': p.get('ttl'), - 'managed_os': p.get('managed_os'), - 'description': p.get('description'), - 'source_server_password': p.get('source_server_password'), - 'cpu_autoscale_policy_id': p.get('cpu_autoscale_policy_id'), - 'anti_affinity_policy_id': p.get('anti_affinity_policy_id'), - 'anti_affinity_policy_name': p.get('anti_affinity_policy_name'), - 'packages': p.get('packages') - } - - count = override_count if override_count else p.get('count') - - changed = False if count == 0 else True - - if changed: - for i in range(0, count): - if not module.check_mode: - req = ClcServer._create_clc_server(clc=clc, - module=module, - server_params=params) - server = req.requests[0].Server() - requests.append(req) - servers.append(server) - - ClcServer._wait_for_requests(clc, requests, servers, wait) - - ClcServer._add_public_ip_to_servers( - should_add_public_ip=add_public_ip, - servers=servers, - public_ip_protocol=public_ip_protocol, - public_ip_ports=public_ip_ports, - wait=wait) - ClcServer._add_alert_policy_to_servers(clc=clc, - module=module, - servers=servers) - - for server in servers: - # reload server details - server = clc.v2.Server(server.id) - - server.data['ipaddress'] = server.details[ - 'ipAddresses'][0]['internal'] - - if add_public_ip and len(server.PublicIPs().public_ips) > 0: - server.data['publicip'] = str( - server.PublicIPs().public_ips[0]) - - server_dict_array.append(server.data) - created_server_ids.append(server.id) - - return server_dict_array, created_server_ids, changed - - @staticmethod - def _validate_name(module): - """ - Validate that name is the correct length if provided, fail if it's not - :param module: the module to validate - :return: none - """ - name = module.params.get('name') - state = module.params.get('state') - - if state == 'present' and (len(name) < 1 or len(name) > 6): - module.fail_json(msg=str( - "When state = 'present', name must be a string with a minimum length of 1 and a maximum length of 6")) - -# -# Functions to execute the module's behaviors -# (called from main()) -# - - @staticmethod - def _enforce_count(module, clc): - """ - Enforce that there is the right number of servers in the provided group. - Starts or stops servers as necessary. - :param module: the AnsibleModule object - :param clc: the clc-sdk instance to use - :return: a list of dictionaries with server information about the servers that were created or deleted - """ - p = module.params - changed_server_ids = None - changed = False - count_group = p.get('count_group') - datacenter = ClcServer._find_datacenter(clc, module) - exact_count = p.get('exact_count') - server_dict_array = [] - - # fail here if the exact count was specified without filtering - # on a group, as this may lead to a undesired removal of instances - if exact_count and count_group is None: - module.fail_json( - msg="you must use the 'count_group' option with exact_count") - - servers, running_servers = ClcServer._find_running_servers_by_group( - module, datacenter, count_group) - - if len(running_servers) == exact_count: - changed = False - - elif len(running_servers) < exact_count: - changed = True - to_create = exact_count - len(running_servers) - server_dict_array, changed_server_ids, changed \ - = ClcServer._create_servers(module, clc, override_count=to_create) - - for server in server_dict_array: - running_servers.append(server) - - elif len(running_servers) > exact_count: - changed = True - to_remove = len(running_servers) - exact_count - all_server_ids = sorted([x.id for x in running_servers]) - remove_ids = all_server_ids[0:to_remove] - - (changed, server_dict_array, changed_server_ids) \ - = ClcServer._delete_servers(module, clc, remove_ids) - - return server_dict_array, changed_server_ids, changed - - @staticmethod - def _wait_for_requests(clc, requests, servers, wait): - """ - Block until server provisioning requests are completed. - :param clc: the clc-sdk instance to use - :param requests: a list of clc-sdk.Request instances - :param servers: a list of servers to refresh - :param wait: a boolean on whether to block or not. This function is skipped if True - :return: none - """ - if wait: - # Requests.WaitUntilComplete() returns the count of failed requests - failed_requests_count = sum( - [request.WaitUntilComplete() for request in requests]) - - if failed_requests_count > 0: - raise clc - else: - ClcServer._refresh_servers(servers) - - @staticmethod - def _refresh_servers(servers): - """ - Loop through a list of servers and refresh them - :param servers: list of clc-sdk.Server instances to refresh - :return: none - """ - for server in servers: - server.Refresh() - - @staticmethod - def _add_public_ip_to_servers( - should_add_public_ip, - servers, - public_ip_protocol, - public_ip_ports, - wait): - """ - Create a public IP for servers - :param should_add_public_ip: boolean - whether or not to provision a public ip for servers. Skipped if False - :param servers: List of servers to add public ips to - :param public_ip_protocol: a protocol to allow for the public ips - :param public_ip_ports: list of ports to allow for the public ips - :param wait: boolean - whether to block until the provisioning requests complete - :return: none - """ - - if should_add_public_ip: - ports_lst = [] - requests = [] - - for port in public_ip_ports: - ports_lst.append( - {'protocol': public_ip_protocol, 'port': port}) - - for server in servers: - requests.append(server.PublicIPs().Add(ports_lst)) - - if wait: - for r in requests: - r.WaitUntilComplete() - - @staticmethod - def _add_alert_policy_to_servers(clc, module, servers): - """ - Associate an alert policy to servers - :param clc: the clc-sdk instance to use - :param module: the AnsibleModule object - :param servers: List of servers to add alert policy to - :return: none - """ - p = module.params - alert_policy_id = p.get('alert_policy_id') - alert_policy_name = p.get('alert_policy_name') - alias = p.get('alias') - if not alert_policy_id and alert_policy_name: - alert_policy_id = ClcServer._get_alert_policy_id_by_name( - clc=clc, - module=module, - alias=alias, - alert_policy_name=alert_policy_name - ) - if not alert_policy_id: - module.fail_json( - msg='No alert policy exist with name : %s' - % (alert_policy_name)) - for server in servers: - ClcServer._add_alert_policy_to_server( - clc=clc, - module=module, - alias=alias, - server_id=server.id, - alert_policy_id=alert_policy_id) - - @staticmethod - def _add_alert_policy_to_server(clc, module, alias, server_id, alert_policy_id): - """ - Associate an alert policy to a clc server - :param clc: the clc-sdk instance to use - :param module: the AnsibleModule object - :param alias: the clc account alias - :param serverid: The clc server id - :param alert_policy_id: the alert policy id to be associated to the server - :return: none - """ - try: - clc.v2.API.Call( - method='POST', - url='servers/%s/%s/alertPolicies' % (alias, server_id), - payload=json.dumps( - { - 'id': alert_policy_id - })) - except clc.APIFailedResponse as e: - return module.fail_json( - msg='Failed to associate alert policy to the server : %s with Error %s' - % (server_id, str(e.response_text))) - - @staticmethod - def _get_alert_policy_id_by_name(clc, module, alias, alert_policy_name): - """ - Returns the alert policy id for the given alert policy name - :param clc: the clc-sdk instance to use - :param module: the AnsibleModule object - :param alias: the clc account alias - :param alert_policy_name: the name of the alert policy - :return: the alert policy id - """ - alert_policy_id = None - policies = clc.v2.API.Call('GET', '/v2/alertPolicies/%s' % (alias)) - if not policies: - return alert_policy_id - for policy in policies.get('items'): - if policy.get('name') == alert_policy_name: - if not alert_policy_id: - alert_policy_id = policy.get('id') - else: - return module.fail_json( - msg='mutiple alert policies were found with policy name : %s' % - (alert_policy_name)) - return alert_policy_id - - - @staticmethod - def _delete_servers(module, clc, server_ids): - """ - Delete the servers on the provided list - :param module: the AnsibleModule object - :param clc: the clc-sdk instance to use - :param server_ids: list of servers to delete - :return: a list of dictionaries with server information about the servers that were deleted - """ - # Whether to wait for termination to complete before returning - p = module.params - wait = p.get('wait') - terminated_server_ids = [] - server_dict_array = [] - requests = [] - - changed = False - if not isinstance(server_ids, list) or len(server_ids) < 1: - module.fail_json( - msg='server_ids should be a list of servers, aborting') - - servers = clc.v2.Servers(server_ids).Servers() - changed = True - - for server in servers: - if not module.check_mode: - requests.append(server.Delete()) - - if wait: - for r in requests: - r.WaitUntilComplete() - - for server in servers: - terminated_server_ids.append(server.id) - - return changed, server_dict_array, terminated_server_ids - - @staticmethod - def _startstop_servers(module, clc, server_ids): - """ - Start or Stop the servers on the provided list - :param module: the AnsibleModule object - :param clc: the clc-sdk instance to use - :param server_ids: list of servers to start or stop - :return: a list of dictionaries with server information about the servers that were started or stopped - """ - p = module.params - wait = p.get('wait') - state = p.get('state') - changed = False - changed_servers = [] - server_dict_array = [] - result_server_ids = [] - requests = [] - - if not isinstance(server_ids, list) or len(server_ids) < 1: - module.fail_json( - msg='server_ids should be a list of servers, aborting') - - servers = clc.v2.Servers(server_ids).Servers() - for server in servers: - if server.powerState != state: - changed_servers.append(server) - if not module.check_mode: - requests.append( - ClcServer._change_server_power_state( - module, - server, - state)) - changed = True - - if wait: - for r in requests: - r.WaitUntilComplete() - for server in changed_servers: - server.Refresh() - - for server in changed_servers: - server_dict_array.append(server.data) - result_server_ids.append(server.id) - - return changed, server_dict_array, result_server_ids - - @staticmethod - def _change_server_power_state(module, server, state): - """ - Change the server powerState - :param module: the module to check for intended state - :param server: the server to start or stop - :param state: the intended powerState for the server - :return: the request object from clc-sdk call - """ - result = None - try: - if state == 'started': - result = server.PowerOn() - else: - result = server.PowerOff() - except: - module.fail_json( - msg='Unable to change state for server {0}'.format( - server.id)) - return result - return result - - @staticmethod - def _find_running_servers_by_group(module, datacenter, count_group): - """ - Find a list of running servers in the provided group - :param module: the AnsibleModule object - :param datacenter: the clc-sdk.Datacenter instance to use to lookup the group - :param count_group: the group to count the servers - :return: list of servers, and list of running servers - """ - group = ClcServer._find_group( - module=module, - datacenter=datacenter, - lookup_group=count_group) - - servers = group.Servers().Servers() - running_servers = [] - - for server in servers: - if server.status == 'active' and server.powerState == 'started': - running_servers.append(server) - - return servers, running_servers - - @staticmethod - def _find_group(module, datacenter, lookup_group=None): - """ - Find a server group in a datacenter by calling the CLC API - :param module: the AnsibleModule instance - :param datacenter: clc-sdk.Datacenter instance to search for the group - :param lookup_group: string name of the group to search for - :return: clc-sdk.Group instance - """ - result = None - if not lookup_group: - lookup_group = module.params.get('group') - try: - return datacenter.Groups().Get(lookup_group) - except: - pass - - # The search above only acts on the main - result = ClcServer._find_group_recursive( - module, - datacenter.Groups(), - lookup_group) - - if result is None: - module.fail_json( - msg=str( - "Unable to find group: " + - lookup_group + - " in location: " + - datacenter.id)) - - return result - - @staticmethod - def _find_group_recursive(module, group_list, lookup_group): - """ - Find a server group by recursively walking the tree - :param module: the AnsibleModule instance to use - :param group_list: a list of groups to search - :param lookup_group: the group to look for - :return: list of groups - """ - result = None - for group in group_list.groups: - subgroups = group.Subgroups() - try: - return subgroups.Get(lookup_group) - except: - result = ClcServer._find_group_recursive( - module, - subgroups, - lookup_group) - - if result is not None: - break - - return result - - @staticmethod - def _create_clc_server( - clc, - module, - server_params): - """ - Call the CLC Rest API to Create a Server - :param clc: the clc-python-sdk instance to use - :param server_params: a dictionary of params to use to create the servers - :return: clc-sdk.Request object linked to the queued server request - """ - - aa_policy_id = server_params.get('anti_affinity_policy_id') - aa_policy_name = server_params.get('anti_affinity_policy_name') - if not aa_policy_id and aa_policy_name: - aa_policy_id = ClcServer._get_anti_affinity_policy_id( - clc, - module, - server_params.get('alias'), - aa_policy_name) - - res = clc.v2.API.Call( - method='POST', - url='servers/%s' % - (server_params.get('alias')), - payload=json.dumps( - { - 'name': server_params.get('name'), - 'description': server_params.get('description'), - 'groupId': server_params.get('group_id'), - 'sourceServerId': server_params.get('template'), - 'isManagedOS': server_params.get('managed_os'), - 'primaryDNS': server_params.get('primary_dns'), - 'secondaryDNS': server_params.get('secondary_dns'), - 'networkId': server_params.get('network_id'), - 'ipAddress': server_params.get('ip_address'), - 'password': server_params.get('password'), - 'sourceServerPassword': server_params.get('source_server_password'), - 'cpu': server_params.get('cpu'), - 'cpuAutoscalePolicyId': server_params.get('cpu_autoscale_policy_id'), - 'memoryGB': server_params.get('memory'), - 'type': server_params.get('type'), - 'storageType': server_params.get('storage_type'), - 'antiAffinityPolicyId': aa_policy_id, - 'customFields': server_params.get('custom_fields'), - 'additionalDisks': server_params.get('additional_disks'), - 'ttl': server_params.get('ttl'), - 'packages': server_params.get('packages')})) - - result = clc.v2.Requests(res) - - # - # Patch the Request object so that it returns a valid server - - # Find the server's UUID from the API response - server_uuid = [obj['id'] - for obj in res['links'] if obj['rel'] == 'self'][0] - - # Change the request server method to a _find_server_by_uuid closure so - # that it will work - result.requests[0].Server = lambda: ClcServer._find_server_by_uuid_w_retry( - clc, - module, - server_uuid, - server_params.get('alias')) - - return result - - @staticmethod - def _get_anti_affinity_policy_id(clc, module, alias, aa_policy_name): - """ - retrieves the anti affinity policy id of the server based on the name of the policy - :param clc: the clc-sdk instance to use - :param module: the AnsibleModule object - :param alias: the CLC account alias - :param aa_policy_name: the anti affinity policy name - :return: aa_policy_id: The anti affinity policy id - """ - aa_policy_id = None - aa_policies = clc.v2.API.Call(method='GET', - url='antiAffinityPolicies/%s' % (alias)) - for aa_policy in aa_policies.get('items'): - if aa_policy.get('name') == aa_policy_name: - if not aa_policy_id: - aa_policy_id = aa_policy.get('id') - else: - return module.fail_json( - msg='mutiple anti affinity policies were found with policy name : %s' % - (aa_policy_name)) - if not aa_policy_id: - return module.fail_json( - msg='No anti affinity policy was found with policy name : %s' % - (aa_policy_name)) - return aa_policy_id - - # - # This is the function that gets patched to the Request.server object using a lamda closure - # - - @staticmethod - def _find_server_by_uuid_w_retry( - clc, module, svr_uuid, alias=None, retries=5, backout=2): - """ - Find the clc server by the UUID returned from the provisioning request. Retry the request if a 404 is returned. - :param clc: the clc-sdk instance to use - :param svr_uuid: UUID of the server - :param alias: the Account Alias to search - :return: a clc-sdk.Server instance - """ - if not alias: - alias = clc.v2.Account.GetAlias() - - # Wait and retry if the api returns a 404 - while True: - retries -= 1 - try: - server_obj = clc.v2.API.Call( - method='GET', url='servers/%s/%s?uuid=true' % - (alias, svr_uuid)) - server_id = server_obj['id'] - server = clc.v2.Server( - id=server_id, - alias=alias, - server_obj=server_obj) - return server - - except APIFailedResponse as e: - if e.response_status_code != 404: - module.fail_json( - msg='A failure response was received from CLC API when ' - 'attempting to get details for a server: UUID=%s, Code=%i, Message=%s' % - (svr_uuid, e.response_status_code, e.message)) - return - if retries == 0: - module.fail_json( - msg='Unable to reach the CLC API after 5 attempts') - return - - sleep(backout) - backout = backout * 2 - - @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 - """ - argument_dict = ClcServer._define_module_argument_spec() - module = AnsibleModule(supports_check_mode=True, **argument_dict) - clc_server = ClcServer(module) - clc_server.process_request() - -from ansible.module_utils.basic import * # pylint: disable=W0614 -if __name__ == '__main__': - main() diff --git a/cloud/centurylink/clc_server_snapshot.py b/cloud/centurylink/clc_server_snapshot.py deleted file mode 100644 index 9ca1474f248..00000000000 --- a/cloud/centurylink/clc_server_snapshot.py +++ /dev/null @@ -1,341 +0,0 @@ -#!/usr/bin/python - -# CenturyLink Cloud Ansible Modules. -# -# These Ansible modules enable the CenturyLink Cloud v2 API to be called -# from an within Ansible Playbook. -# -# This file is part of CenturyLink Cloud, and is maintained -# by the Workflow as a Service Team -# -# Copyright 2015 CenturyLink Cloud -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# CenturyLink Cloud: http://www.CenturyLinkCloud.com -# API Documentation: https://www.centurylinkcloud.com/api-docs/v2/ -# - -DOCUMENTATION = ''' -module: clc_server -short_desciption: Create, Delete and Restore server snapshots in CenturyLink Cloud. -description: - - An Ansible module to Create, Delete and Restore server snapshots in CenturyLink Cloud. -options: - server_ids: - description: - - A list of server Ids to snapshot. - default: [] - required: True - aliases: [] - expiration_days: - description: - - The number of days to keep the server snapshot before it expires. - default: 7 - required: False - aliases: [] - state: - description: - - The state to insure that the provided resources are in. - default: 'present' - required: False - choices: ['present', 'absent', 'restore'] - aliases: [] - wait: - description: - - Whether to wait for the provisioning tasks to finish before returning. - default: True - required: False - choices: [ True, False] - aliases: [] -''' - -EXAMPLES = ''' -# Note - You must set the CLC_V2_API_USERNAME And CLC_V2_API_PASSWD Environment variables before running these examples - -- name: Create server snapshot - clc_server_snapshot: - server_ids: - - UC1WFSDTEST01 - - UC1WFSDTEST02 - expiration_days: 10 - wait: True - state: present - -- name: Restore server snapshot - clc_server_snapshot: - server_ids: - - UC1WFSDTEST01 - - UC1WFSDTEST02 - wait: True - state: restore - -- name: Delete server snapshot - clc_server_snapshot: - server_ids: - - UC1WFSDTEST01 - - UC1WFSDTEST02 - wait: True - state: absent -''' - -__version__ = '${version}' - -import requests - -# -# Requires the clc-python-sdk. -# sudo pip install clc-sdk -# -try: - import clc as clc_sdk - from clc import CLCException -except ImportError: - clc_found = False - clc_sdk = None -else: - CLC_FOUND = True - - -class ClcSnapshot(): - - clc = clc_sdk - module = None - - def __init__(self, module): - """ - Construct module - """ - self.module = module - if not CLC_FOUND: - self.module.fail_json( - msg='clc-python-sdk required for this module') - - self._set_user_agent(self.clc) - - def process_request(self): - """ - Process the request - Main Code Path - :return: Returns with either an exit_json or fail_json - """ - p = self.module.params - - if not CLC_FOUND: - self.module.fail_json( - msg='clc-python-sdk required for this module') - - server_ids = p['server_ids'] - expiration_days = p['expiration_days'] - state = p['state'] - - if not server_ids: - return self.module.fail_json(msg='List of Server ids are required') - - self._set_clc_credentials_from_env() - if state == 'present': - changed, requests, changed_servers = self.ensure_server_snapshot_present(server_ids=server_ids, - expiration_days=expiration_days) - elif state == 'absent': - changed, requests, changed_servers = self.ensure_server_snapshot_absent( - server_ids=server_ids) - elif state == 'restore': - changed, requests, changed_servers = self.ensure_server_snapshot_restore( - server_ids=server_ids) - else: - return self.module.fail_json(msg="Unknown State: " + state) - - self._wait_for_requests_to_complete(requests) - return self.module.exit_json( - changed=changed, - server_ids=changed_servers) - - def ensure_server_snapshot_present(self, server_ids, expiration_days): - """ - Ensures the given set of server_ids have the snapshots created - :param server_ids: The list of server_ids to create the snapshot - :param expiration_days: The number of days to keep the snapshot - :return: (changed, result, changed_servers) - changed: A flag indicating whether any change was made - result: the list of clc request objects from CLC API call - changed_servers: The list of servers ids that are modified - """ - result = [] - changed = False - servers = self._get_servers_from_clc( - server_ids, - 'Failed to obtain server list from the CLC API') - servers_to_change = [ - server for server in servers if len( - server.GetSnapshots()) == 0] - for server in servers_to_change: - changed = True - if not self.module.check_mode: - res = server.CreateSnapshot( - delete_existing=True, - expiration_days=expiration_days) - result.append(res) - changed_servers = [ - server.id for server in servers_to_change if server.id] - return changed, result, changed_servers - - def ensure_server_snapshot_absent(self, server_ids): - """ - Ensures the given set of server_ids have the snapshots removed - :param server_ids: The list of server_ids to delete the snapshot - :return: (changed, result, changed_servers) - changed: A flag indicating whether any change was made - result: the list of clc request objects from CLC API call - changed_servers: The list of servers ids that are modified - """ - result = [] - changed = False - servers = self._get_servers_from_clc( - server_ids, - 'Failed to obtain server list from the CLC API') - servers_to_change = [ - server for server in servers if len( - server.GetSnapshots()) > 0] - for server in servers_to_change: - changed = True - if not self.module.check_mode: - res = server.DeleteSnapshot() - result.append(res) - changed_servers = [ - server.id for server in servers_to_change if server.id] - return changed, result, changed_servers - - def ensure_server_snapshot_restore(self, server_ids): - """ - Ensures the given set of server_ids have the snapshots restored - :param server_ids: The list of server_ids to delete the snapshot - :return: (changed, result, changed_servers) - changed: A flag indicating whether any change was made - result: the list of clc request objects from CLC API call - changed_servers: The list of servers ids that are modified - """ - result = [] - changed = False - servers = self._get_servers_from_clc( - server_ids, - 'Failed to obtain server list from the CLC API') - servers_to_change = [ - server for server in servers if len( - server.GetSnapshots()) > 0] - for server in servers_to_change: - changed = True - if not self.module.check_mode: - res = server.RestoreSnapshot() - result.append(res) - changed_servers = [ - server.id for server in servers_to_change if server.id] - return changed, result, changed_servers - - def _wait_for_requests_to_complete(self, requests_lst): - """ - Waits until the CLC requests are complete if the wait argument is True - :param requests_lst: The list of CLC request objects - :return: none - """ - if not self.module.params['wait']: - return - for request in requests_lst: - request.WaitUntilComplete() - for request_details in request.requests: - if request_details.Status() != 'succeeded': - self.module.fail_json( - msg='Unable to process server snapshot request') - - @staticmethod - def define_argument_spec(): - """ - This function defnines the dictionary object required for - package module - :return: the package dictionary object - """ - argument_spec = dict( - server_ids=dict(type='list', required=True), - expiration_days=dict(default=7), - wait=dict(default=True), - state=dict( - default='present', - choices=[ - 'present', - 'absent', - 'restore']), - ) - return argument_spec - - def _get_servers_from_clc(self, server_list, message): - """ - Internal function to fetch list of CLC server objects from a list of server ids - :param the list server ids - :return the list of CLC server objects - """ - try: - return self.clc.v2.Servers(server_list).servers - except CLCException as ex: - return self.module.fail_json(msg=message + ': %s' % ex) - - 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") - - @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(): - """ - Main function - :return: None - """ - module = AnsibleModule( - argument_spec=ClcSnapshot.define_argument_spec(), - supports_check_mode=True - ) - clc_snapshot = ClcSnapshot(module) - clc_snapshot.process_request() - -from ansible.module_utils.basic import * -if __name__ == '__main__': - main()