From ecf30095bca767e77780fb7c14c1c4104e2d6ce1 Mon Sep 17 00:00:00 2001
From: Siva Popuri <popuri.siva@gmail.com>
Date: Mon, 20 Jul 2015 11:52:24 -0500
Subject: [PATCH] ansible module for managing alert policy in centurylink cloud
 has been added

---
 cloud/centurylink/clc_alert_policy.py | 501 ++++++++++++++++++++++++++
 1 file changed, 501 insertions(+)
 create mode 100644 cloud/centurylink/clc_alert_policy.py

diff --git a/cloud/centurylink/clc_alert_policy.py b/cloud/centurylink/clc_alert_policy.py
new file mode 100644
index 00000000000..71cfe70169f
--- /dev/null
+++ b/cloud/centurylink/clc_alert_policy.py
@@ -0,0 +1,501 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2015 CenturyLink
+#
+# This file is part of Ansible.
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible.  If not, see <http://www.gnu.org/licenses/>
+#
+
+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.
+version_added: "2.0"
+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
+    required: False
+    default: None
+    aliases: []
+  id:
+    description:
+      - The alert policy id. This is mutually exclusive with name
+    required: False
+    default: None
+    aliases: []
+  alert_recipients:
+    description:
+      - A list of recipient email ids to notify the alert.
+        This is required for state 'present'
+    required: False
+    aliases: []
+  metric:
+    description:
+      - The metric on which to measure the condition that will trigger the alert.
+        This is required for state 'present'
+    required: False
+    default: None
+    choices: ['cpu','memory','disk']
+    aliases: []
+  duration:
+    description:
+      - The length of time in minutes that the condition must exceed the threshold.
+        This is required for state 'present'
+    required: False
+    default: None
+    aliases: []
+  threshold:
+    description:
+      - The threshold that will trigger the alert when the metric equals or exceeds it.
+        This is required for state 'present'
+        This number represents a percentage and must be a value between 5.0 - 95.0 that is a multiple of 5.0
+    required: False
+    default: None
+    aliases: []
+  state:
+    description:
+      - Whether to create or delete the policy.
+    required: False
+    default: present
+    choices: ['present','absent']
+requirements:
+    - python = 2.7
+    - requests >= 2.5.0
+    - clc-sdk
+notes:
+    - To use this module, it is required to set the below environment variables which enables access to the
+      Centurylink Cloud
+          - CLC_V2_API_USERNAME, the account login id for the centurylink cloud
+          - CLC_V2_API_PASSWORD, the account passwod for the centurylink cloud
+    - Alternatively, the module accepts the API token and account alias. The API token can be generated using the
+      CLC account login and password via the HTTP api call @ https://api.ctl.io/v2/authentication/login
+          - CLC_V2_API_TOKEN, the API token generated from https://api.ctl.io/v2/authentication/login
+          - CLC_ACCT_ALIAS, the account alias associated with the centurylink cloud
+    - Users can set CLC_V2_API_URL to specify an endpoint for pointing to a different CLC environment.
+'''
+
+EXAMPLES = '''
+# Note - You must set the CLC_V2_API_USERNAME And CLC_V2_API_PASSWD Environment variables before running these examples
+
+---
+- name: Create 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}'
+
+from distutils.version import LooseVersion
+
+try:
+    import requests
+except ImportError:
+    REQUESTS_FOUND = False
+else:
+    REQUESTS_FOUND = True
+
+#
+#  Requires the clc-python-sdk.
+#  sudo pip install clc-sdk
+#
+try:
+    import clc as clc_sdk
+    from clc import APIFailedResponse
+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')
+        if not REQUESTS_FOUND:
+            self.module.fail_json(
+                msg='requests library is required for this module')
+        if requests.__version__ and LooseVersion(requests.__version__) < LooseVersion('2.5.0'):
+            self.module.fail_json(
+                msg='requests library  version should be >= 2.5.0')
+
+        self._set_user_agent(self.clc)
+
+    @staticmethod
+    def _define_module_argument_spec():
+        """
+        Define the argument spec for the ansible module
+        :return: argument spec dictionary
+        """
+        argument_spec = dict(
+            name=dict(default=None),
+            id=dict(default=None),
+            alias=dict(required=True, default=None),
+            alert_recipients=dict(type='list', default=None),
+            metric=dict(
+                choices=[
+                    'cpu',
+                    'memory',
+                    'disk'],
+                default=None),
+            duration=dict(type='str', default=None),
+            threshold=dict(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
+
+        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']
+        policy_name = p['name']
+        arguments = json.dumps(
+            {
+                'name': policy_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 APIFailedResponse as e:
+            return self.module.fail_json(
+                msg='Unable to create alert policy "{0}". {1}'.format(
+                    policy_name, 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']
+        policy_name = p['name']
+        arguments = json.dumps(
+            {
+                'name': policy_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 APIFailedResponse as e:
+            return self.module.fail_json(
+                msg='Unable to update alert policy "{0}". {1}'.format(
+                    policy_name, 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 APIFailedResponse as e:
+            return self.module.fail_json(
+                msg='Unable to delete alert policy id "{0}". {1}'.format(
+                    policy_id, 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()