From 11c953477c012435e078d62204b0cf1db2796d2f Mon Sep 17 00:00:00 2001
From: Siva Popuri <popuri.siva@gmail.com>
Date: Wed, 8 Jul 2015 11:22:38 -0500
Subject: [PATCH] ansible modules for centurylink cloud are added

---
 cloud/centurylink/__init__.py              |    1 +
 cloud/centurylink/clc_aa_policy.py         |  294 +++++
 cloud/centurylink/clc_alert_policy.py      |  473 +++++++
 cloud/centurylink/clc_blueprint_package.py |  263 ++++
 cloud/centurylink/clc_firewall_policy.py   |  542 ++++++++
 cloud/centurylink/clc_group.py             |  370 ++++++
 cloud/centurylink/clc_loadbalancer.py      |  759 +++++++++++
 cloud/centurylink/clc_modify_server.py     |  710 +++++++++++
 cloud/centurylink/clc_publicip.py          |  316 +++++
 cloud/centurylink/clc_server.py            | 1323 ++++++++++++++++++++
 cloud/centurylink/clc_server_snapshot.py   |  341 +++++
 11 files changed, 5392 insertions(+)
 create mode 100644 cloud/centurylink/__init__.py
 create mode 100644 cloud/centurylink/clc_aa_policy.py
 create mode 100644 cloud/centurylink/clc_alert_policy.py
 create mode 100644 cloud/centurylink/clc_blueprint_package.py
 create mode 100644 cloud/centurylink/clc_firewall_policy.py
 create mode 100644 cloud/centurylink/clc_group.py
 create mode 100644 cloud/centurylink/clc_loadbalancer.py
 create mode 100644 cloud/centurylink/clc_modify_server.py
 create mode 100644 cloud/centurylink/clc_publicip.py
 create mode 100644 cloud/centurylink/clc_server.py
 create mode 100644 cloud/centurylink/clc_server_snapshot.py

diff --git a/cloud/centurylink/__init__.py b/cloud/centurylink/__init__.py
new file mode 100644
index 00000000000..71f0abcff9d
--- /dev/null
+++ b/cloud/centurylink/__init__.py
@@ -0,0 +1 @@
+__version__ = "${version}"
diff --git a/cloud/centurylink/clc_aa_policy.py b/cloud/centurylink/clc_aa_policy.py
new file mode 100644
index 00000000000..644f3817c4f
--- /dev/null
+++ b/cloud/centurylink/clc_aa_policy.py
@@ -0,0 +1,294 @@
+#!/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
new file mode 100644
index 00000000000..75467967a85
--- /dev/null
+++ b/cloud/centurylink/clc_alert_policy.py
@@ -0,0 +1,473 @@
+#!/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
new file mode 100644
index 00000000000..80cc18a24ca
--- /dev/null
+++ b/cloud/centurylink/clc_blueprint_package.py
@@ -0,0 +1,263 @@
+#!/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
new file mode 100644
index 00000000000..260c82bc885
--- /dev/null
+++ b/cloud/centurylink/clc_firewall_policy.py
@@ -0,0 +1,542 @@
+#!/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
new file mode 100644
index 00000000000..a4fd976d429
--- /dev/null
+++ b/cloud/centurylink/clc_group.py
@@ -0,0 +1,370 @@
+#!/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
new file mode 100644
index 00000000000..058954c687b
--- /dev/null
+++ b/cloud/centurylink/clc_loadbalancer.py
@@ -0,0 +1,759 @@
+#!/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
new file mode 100644
index 00000000000..1a1e4d5b858
--- /dev/null
+++ b/cloud/centurylink/clc_modify_server.py
@@ -0,0 +1,710 @@
+#!/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_publicip.py b/cloud/centurylink/clc_publicip.py
new file mode 100644
index 00000000000..2e525a51455
--- /dev/null
+++ b/cloud/centurylink/clc_publicip.py
@@ -0,0 +1,316 @@
+#!/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_publicip
+short_description: Add and Delete public ips on servers in CenturyLink Cloud.
+description:
+  - An Ansible module to add or delete public ip addresses on an existing server or servers in CenturyLink Cloud.
+options:
+  protocol:
+    descirption:
+      - The protocol that the public IP will listen for.
+    default: TCP
+    required: False
+  ports:
+    description:
+      - A list of ports to expose.
+    required: True
+  server_ids:
+    description:
+      - A list of servers to create public ips on.
+    required: True
+  state:
+    description:
+      - Determine wheteher to create or delete public IPs. If present module will not create a second public ip if one
+        already exists.
+    default: present
+    choices: ['present', 'absent']
+    required: False
+  wait:
+    description:
+      - Whether to wait for the tasks to finish before returning.
+    choices: [ True, False ]
+    default: True
+    required: False
+'''
+
+EXAMPLES = '''
+# Note - You must set the CLC_V2_API_USERNAME And CLC_V2_API_PASSWD Environment variables before running these examples
+
+- name: Add Public IP to Server
+  hosts: localhost
+  gather_facts: False
+  connection: local
+  tasks:
+    - name: Create Public IP For Servers
+      clc_publicip:
+        protocol: 'TCP'
+        ports:
+            - 80
+        server_ids:
+            - UC1ACCTSRVR01
+            - UC1ACCTSRVR02
+        state: present
+      register: clc
+
+    - name: debug
+      debug: var=clc
+
+- name: Delete Public IP from Server
+  hosts: localhost
+  gather_facts: False
+  connection: local
+  tasks:
+    - name: Create Public IP For Servers
+      clc_publicip:
+        server_ids:
+            - UC1ACCTSRVR01
+            - UC1ACCTSRVR02
+        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 ClcPublicIp(object):
+    clc = clc_sdk
+    module = None
+    group_dict = {}
+
+    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
+        :param params: dictionary of module parameters
+        :return: Returns with either an exit_json or fail_json
+        """
+        self._set_clc_credentials_from_env()
+        params = self.module.params
+        server_ids = params['server_ids']
+        ports = params['ports']
+        protocol = params['protocol']
+        state = params['state']
+        requests = []
+        chagned_server_ids = []
+        changed = False
+
+        if state == 'present':
+            changed, chagned_server_ids, requests = self.ensure_public_ip_present(
+                server_ids=server_ids, protocol=protocol, ports=ports)
+        elif state == 'absent':
+            changed, chagned_server_ids, requests = self.ensure_public_ip_absent(
+                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=chagned_server_ids)
+
+    @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),
+            protocol=dict(default='TCP'),
+            ports=dict(type='list'),
+            wait=dict(type='bool', default=True),
+            state=dict(default='present', choices=['present', 'absent']),
+        )
+        return argument_spec
+
+    def ensure_public_ip_present(self, server_ids, protocol, ports):
+        """
+        Ensures the given server ids having the public ip available
+        :param server_ids: the list of server ids
+        :param protocol: the ip protocol
+        :param ports: the list of ports to expose
+        :return: (changed, changed_server_ids, results)
+                  changed: A flag indicating if there is any change
+                  changed_server_ids : the list of server ids that are changed
+                  results: The result list from clc public ip call
+        """
+        changed = False
+        results = []
+        changed_server_ids = []
+        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.PublicIPs().public_ips) == 0]
+        ports_to_expose = [{'protocol': protocol, 'port': port}
+                           for port in ports]
+        for server in servers_to_change:
+            if not self.module.check_mode:
+                result = server.PublicIPs().Add(ports_to_expose)
+                results.append(result)
+            changed_server_ids.append(server.id)
+            changed = True
+        return changed, changed_server_ids, results
+
+    def ensure_public_ip_absent(self, server_ids):
+        """
+        Ensures the given server ids having the public ip removed if there is any
+        :param server_ids: the list of server ids
+        :return: (changed, changed_server_ids, results)
+                  changed: A flag indicating if there is any change
+                  changed_server_ids : the list of server ids that are changed
+                  results: The result list from clc public ip call
+        """
+        changed = False
+        results = []
+        changed_server_ids = []
+        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.PublicIPs().public_ips) > 0]
+        ips_to_delete = []
+        for server in servers_to_change:
+            for ip_address in server.PublicIPs().public_ips:
+                ips_to_delete.append(ip_address)
+        for server in servers_to_change:
+            if not self.module.check_mode:
+                for ip in ips_to_delete:
+                    result = ip.Delete()
+                    results.append(result)
+            changed_server_ids.append(server.id)
+            changed = True
+        return changed, changed_server_ids, results
+
+    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 public ip request')
+
+    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_servers_from_clc(self, server_ids, message):
+        """
+        Gets list of servers form CLC api
+        """
+        try:
+            return self.clc.v2.Servers(server_ids).servers
+        except CLCException as exception:
+            self.module.fail_json(msg=message + ': %s' % exception)
+
+    @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=ClcPublicIp._define_module_argument_spec(),
+        supports_check_mode=True
+    )
+    clc_public_ip = ClcPublicIp(module)
+    clc_public_ip.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
new file mode 100644
index 00000000000..e102cd21f47
--- /dev/null
+++ b/cloud/centurylink/clc_server.py
@@ -0,0 +1,1323 @@
+#!/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
new file mode 100644
index 00000000000..9ca1474f248
--- /dev/null
+++ b/cloud/centurylink/clc_server_snapshot.py
@@ -0,0 +1,341 @@
+#!/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()