mirror of https://github.com/ansible/ansible.git
Removed all of the clc-ansbile-modules and kept only clc_publicip as the first module to go
parent
11c953477c
commit
539fd2357b
@ -1 +1 @@
|
|||||||
__version__ = "${version}"
|
|
||||||
|
@ -1,294 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
|
|
||||||
# CenturyLink Cloud Ansible Modules.
|
|
||||||
#
|
|
||||||
# These Ansible modules enable the CenturyLink Cloud v2 API to be called
|
|
||||||
# from an within Ansible Playbook.
|
|
||||||
#
|
|
||||||
# This file is part of CenturyLink Cloud, and is maintained
|
|
||||||
# by the Workflow as a Service Team
|
|
||||||
#
|
|
||||||
# Copyright 2015 CenturyLink Cloud
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
#
|
|
||||||
# CenturyLink Cloud: http://www.CenturyLinkCloud.com
|
|
||||||
# API Documentation: https://www.centurylinkcloud.com/api-docs/v2/
|
|
||||||
|
|
||||||
DOCUMENTATION = '''
|
|
||||||
module: clc_aa_policy
|
|
||||||
short_descirption: Create or Delete Anti Affinity Policies at CenturyLink Cloud.
|
|
||||||
description:
|
|
||||||
- An Ansible module to Create or Delete Anti Affinity Policies at CenturyLink Cloud.
|
|
||||||
options:
|
|
||||||
name:
|
|
||||||
description:
|
|
||||||
- The name of the Anti Affinity Policy.
|
|
||||||
required: True
|
|
||||||
location:
|
|
||||||
description:
|
|
||||||
- Datacenter in which the policy lives/should live.
|
|
||||||
required: True
|
|
||||||
state:
|
|
||||||
description:
|
|
||||||
- Whether to create or delete the policy.
|
|
||||||
required: False
|
|
||||||
default: present
|
|
||||||
choices: ['present','absent']
|
|
||||||
wait:
|
|
||||||
description:
|
|
||||||
- Whether to wait for the provisioning tasks to finish before returning.
|
|
||||||
default: True
|
|
||||||
required: False
|
|
||||||
choices: [ True, False]
|
|
||||||
aliases: []
|
|
||||||
'''
|
|
||||||
|
|
||||||
EXAMPLES = '''
|
|
||||||
# Note - You must set the CLC_V2_API_USERNAME And CLC_V2_API_PASSWD Environment variables before running these examples
|
|
||||||
|
|
||||||
---
|
|
||||||
- name: Create AA Policy
|
|
||||||
hosts: localhost
|
|
||||||
gather_facts: False
|
|
||||||
connection: local
|
|
||||||
tasks:
|
|
||||||
- name: Create an Anti Affinity Policy
|
|
||||||
clc_aa_policy:
|
|
||||||
name: 'Hammer Time'
|
|
||||||
location: 'UK3'
|
|
||||||
state: present
|
|
||||||
register: policy
|
|
||||||
|
|
||||||
- name: debug
|
|
||||||
debug: var=policy
|
|
||||||
|
|
||||||
---
|
|
||||||
- name: Delete AA Policy
|
|
||||||
hosts: localhost
|
|
||||||
gather_facts: False
|
|
||||||
connection: local
|
|
||||||
tasks:
|
|
||||||
- name: Delete an Anti Affinity Policy
|
|
||||||
clc_aa_policy:
|
|
||||||
name: 'Hammer Time'
|
|
||||||
location: 'UK3'
|
|
||||||
state: absent
|
|
||||||
register: policy
|
|
||||||
|
|
||||||
- name: debug
|
|
||||||
debug: var=policy
|
|
||||||
'''
|
|
||||||
|
|
||||||
__version__ = '${version}'
|
|
||||||
|
|
||||||
import requests
|
|
||||||
|
|
||||||
#
|
|
||||||
# Requires the clc-python-sdk.
|
|
||||||
# sudo pip install clc-sdk
|
|
||||||
#
|
|
||||||
try:
|
|
||||||
import clc as clc_sdk
|
|
||||||
from clc import CLCException
|
|
||||||
except ImportError:
|
|
||||||
clc_found = False
|
|
||||||
clc_sdk = None
|
|
||||||
else:
|
|
||||||
clc_found = True
|
|
||||||
|
|
||||||
|
|
||||||
class ClcAntiAffinityPolicy():
|
|
||||||
|
|
||||||
clc = clc_sdk
|
|
||||||
module = None
|
|
||||||
|
|
||||||
def __init__(self, module):
|
|
||||||
"""
|
|
||||||
Construct module
|
|
||||||
"""
|
|
||||||
self.module = module
|
|
||||||
self.policy_dict = {}
|
|
||||||
|
|
||||||
if not clc_found:
|
|
||||||
self.module.fail_json(
|
|
||||||
msg='clc-python-sdk required for this module')
|
|
||||||
|
|
||||||
self._set_user_agent(self.clc)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _define_module_argument_spec():
|
|
||||||
"""
|
|
||||||
Define the argument spec for the ansible module
|
|
||||||
:return: argument spec dictionary
|
|
||||||
"""
|
|
||||||
argument_spec = dict(
|
|
||||||
name=dict(required=True),
|
|
||||||
location=dict(required=True),
|
|
||||||
alias=dict(default=None),
|
|
||||||
wait=dict(default=True),
|
|
||||||
state=dict(default='present', choices=['present', 'absent']),
|
|
||||||
)
|
|
||||||
return argument_spec
|
|
||||||
|
|
||||||
# Module Behavior Goodness
|
|
||||||
def process_request(self):
|
|
||||||
"""
|
|
||||||
Process the request - Main Code Path
|
|
||||||
:return: Returns with either an exit_json or fail_json
|
|
||||||
"""
|
|
||||||
p = self.module.params
|
|
||||||
|
|
||||||
if not clc_found:
|
|
||||||
self.module.fail_json(
|
|
||||||
msg='clc-python-sdk required for this module')
|
|
||||||
|
|
||||||
self._set_clc_credentials_from_env()
|
|
||||||
self.policy_dict = self._get_policies_for_datacenter(p)
|
|
||||||
|
|
||||||
if p['state'] == "absent":
|
|
||||||
changed, policy = self._ensure_policy_is_absent(p)
|
|
||||||
else:
|
|
||||||
changed, policy = self._ensure_policy_is_present(p)
|
|
||||||
|
|
||||||
if hasattr(policy, 'data'):
|
|
||||||
policy = policy.data
|
|
||||||
elif hasattr(policy, '__dict__'):
|
|
||||||
policy = policy.__dict__
|
|
||||||
|
|
||||||
self.module.exit_json(changed=changed, policy=policy)
|
|
||||||
|
|
||||||
def _set_clc_credentials_from_env(self):
|
|
||||||
"""
|
|
||||||
Set the CLC Credentials on the sdk by reading environment variables
|
|
||||||
:return: none
|
|
||||||
"""
|
|
||||||
env = os.environ
|
|
||||||
v2_api_token = env.get('CLC_V2_API_TOKEN', False)
|
|
||||||
v2_api_username = env.get('CLC_V2_API_USERNAME', False)
|
|
||||||
v2_api_passwd = env.get('CLC_V2_API_PASSWD', False)
|
|
||||||
clc_alias = env.get('CLC_ACCT_ALIAS', False)
|
|
||||||
api_url = env.get('CLC_V2_API_URL', False)
|
|
||||||
|
|
||||||
if api_url:
|
|
||||||
self.clc.defaults.ENDPOINT_URL_V2 = api_url
|
|
||||||
|
|
||||||
if v2_api_token and clc_alias:
|
|
||||||
self.clc._LOGIN_TOKEN_V2 = v2_api_token
|
|
||||||
self.clc._V2_ENABLED = True
|
|
||||||
self.clc.ALIAS = clc_alias
|
|
||||||
elif v2_api_username and v2_api_passwd:
|
|
||||||
self.clc.v2.SetCredentials(
|
|
||||||
api_username=v2_api_username,
|
|
||||||
api_passwd=v2_api_passwd)
|
|
||||||
else:
|
|
||||||
return self.module.fail_json(
|
|
||||||
msg="You must set the CLC_V2_API_USERNAME and CLC_V2_API_PASSWD "
|
|
||||||
"environment variables")
|
|
||||||
|
|
||||||
def _get_policies_for_datacenter(self, p):
|
|
||||||
"""
|
|
||||||
Get the Policies for a datacenter by calling the CLC API.
|
|
||||||
:param p: datacenter to get policies from
|
|
||||||
:return: policies in the datacenter
|
|
||||||
"""
|
|
||||||
response = {}
|
|
||||||
|
|
||||||
policies = self.clc.v2.AntiAffinity.GetAll(location=p['location'])
|
|
||||||
|
|
||||||
for policy in policies:
|
|
||||||
response[policy.name] = policy
|
|
||||||
return response
|
|
||||||
|
|
||||||
def _create_policy(self, p):
|
|
||||||
"""
|
|
||||||
Create an Anti Affinnity Policy using the CLC API.
|
|
||||||
:param p: datacenter to create policy in
|
|
||||||
:return: response dictionary from the CLC API.
|
|
||||||
"""
|
|
||||||
return self.clc.v2.AntiAffinity.Create(
|
|
||||||
name=p['name'],
|
|
||||||
location=p['location'])
|
|
||||||
|
|
||||||
def _delete_policy(self, p):
|
|
||||||
"""
|
|
||||||
Delete an Anti Affinity Policy using the CLC API.
|
|
||||||
:param p: datacenter to delete a policy from
|
|
||||||
:return: none
|
|
||||||
"""
|
|
||||||
policy = self.policy_dict[p['name']]
|
|
||||||
policy.Delete()
|
|
||||||
|
|
||||||
def _policy_exists(self, policy_name):
|
|
||||||
"""
|
|
||||||
Check to see if an Anti Affinity Policy exists
|
|
||||||
:param policy_name: name of the policy
|
|
||||||
:return: boolean of if the policy exists
|
|
||||||
"""
|
|
||||||
if policy_name in self.policy_dict:
|
|
||||||
return self.policy_dict.get(policy_name)
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _ensure_policy_is_absent(self, p):
|
|
||||||
"""
|
|
||||||
Makes sure that a policy is absent
|
|
||||||
:param p: dictionary of policy name
|
|
||||||
:return: tuple of if a deletion occurred and the name of the policy that was deleted
|
|
||||||
"""
|
|
||||||
changed = False
|
|
||||||
if self._policy_exists(policy_name=p['name']):
|
|
||||||
changed = True
|
|
||||||
if not self.module.check_mode:
|
|
||||||
self._delete_policy(p)
|
|
||||||
return changed, None
|
|
||||||
|
|
||||||
def _ensure_policy_is_present(self, p):
|
|
||||||
"""
|
|
||||||
Ensures that a policy is present
|
|
||||||
:param p: dictonary of a policy name
|
|
||||||
:return: tuple of if an addition occurred and the name of the policy that was added
|
|
||||||
"""
|
|
||||||
changed = False
|
|
||||||
policy = self._policy_exists(policy_name=p['name'])
|
|
||||||
if not policy:
|
|
||||||
changed = True
|
|
||||||
policy = None
|
|
||||||
if not self.module.check_mode:
|
|
||||||
policy = self._create_policy(p)
|
|
||||||
return changed, policy
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _set_user_agent(clc):
|
|
||||||
if hasattr(clc, 'SetRequestsSession'):
|
|
||||||
agent_string = "ClcAnsibleModule/" + __version__
|
|
||||||
ses = requests.Session()
|
|
||||||
ses.headers.update({"Api-Client": agent_string})
|
|
||||||
ses.headers['User-Agent'] += " " + agent_string
|
|
||||||
clc.SetRequestsSession(ses)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""
|
|
||||||
The main function. Instantiates the module and calls process_request.
|
|
||||||
:return: none
|
|
||||||
"""
|
|
||||||
module = AnsibleModule(
|
|
||||||
argument_spec=ClcAntiAffinityPolicy._define_module_argument_spec(),
|
|
||||||
supports_check_mode=True)
|
|
||||||
clc_aa_policy = ClcAntiAffinityPolicy(module)
|
|
||||||
clc_aa_policy.process_request()
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import * # pylint: disable=W0614
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,473 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
|
|
||||||
# CenturyLink Cloud Ansible Modules.
|
|
||||||
#
|
|
||||||
# These Ansible modules enable the CenturyLink Cloud v2 API to be called
|
|
||||||
# from an within Ansible Playbook.
|
|
||||||
#
|
|
||||||
# This file is part of CenturyLink Cloud, and is maintained
|
|
||||||
# by the Workflow as a Service Team
|
|
||||||
#
|
|
||||||
# Copyright 2015 CenturyLink Cloud
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
#
|
|
||||||
# CenturyLink Cloud: http://www.CenturyLinkCloud.com
|
|
||||||
# API Documentation: https://www.centurylinkcloud.com/api-docs/v2/
|
|
||||||
|
|
||||||
DOCUMENTATION = '''
|
|
||||||
module: clc_alert_policy
|
|
||||||
short_descirption: Create or Delete Alert Policies at CenturyLink Cloud.
|
|
||||||
description:
|
|
||||||
- An Ansible module to Create or Delete Alert Policies at CenturyLink Cloud.
|
|
||||||
options:
|
|
||||||
alias:
|
|
||||||
description:
|
|
||||||
- The alias of your CLC Account
|
|
||||||
required: True
|
|
||||||
name:
|
|
||||||
description:
|
|
||||||
- The name of the alert policy. This is mutually exclusive with id
|
|
||||||
default: None
|
|
||||||
aliases: []
|
|
||||||
id:
|
|
||||||
description:
|
|
||||||
- The alert policy id. This is mutually exclusive with name
|
|
||||||
default: None
|
|
||||||
aliases: []
|
|
||||||
alert_recipients:
|
|
||||||
description:
|
|
||||||
- A list of recipient email ids to notify the alert.
|
|
||||||
required: True
|
|
||||||
aliases: []
|
|
||||||
metric:
|
|
||||||
description:
|
|
||||||
- The metric on which to measure the condition that will trigger the alert.
|
|
||||||
required: True
|
|
||||||
default: None
|
|
||||||
choices: ['cpu','memory','disk']
|
|
||||||
aliases: []
|
|
||||||
duration:
|
|
||||||
description:
|
|
||||||
- The length of time in minutes that the condition must exceed the threshold.
|
|
||||||
required: True
|
|
||||||
default: None
|
|
||||||
aliases: []
|
|
||||||
threshold:
|
|
||||||
description:
|
|
||||||
- The threshold that will trigger the alert when the metric equals or exceeds it.
|
|
||||||
This number represents a percentage and must be a value between 5.0 - 95.0 that is a multiple of 5.0
|
|
||||||
required: True
|
|
||||||
default: None
|
|
||||||
aliases: []
|
|
||||||
state:
|
|
||||||
description:
|
|
||||||
- Whether to create or delete the policy.
|
|
||||||
required: False
|
|
||||||
default: present
|
|
||||||
choices: ['present','absent']
|
|
||||||
'''
|
|
||||||
|
|
||||||
EXAMPLES = '''
|
|
||||||
# Note - You must set the CLC_V2_API_USERNAME And CLC_V2_API_PASSWD Environment variables before running these examples
|
|
||||||
|
|
||||||
---
|
|
||||||
- name: Create Alert Policy Example
|
|
||||||
hosts: localhost
|
|
||||||
gather_facts: False
|
|
||||||
connection: local
|
|
||||||
tasks:
|
|
||||||
- name: Create an Alert Policy for disk above 80% for 5 minutes
|
|
||||||
clc_alert_policy:
|
|
||||||
alias: wfad
|
|
||||||
name: 'alert for disk > 80%'
|
|
||||||
alert_recipients:
|
|
||||||
- test1@centurylink.com
|
|
||||||
- test2@centurylink.com
|
|
||||||
metric: 'disk'
|
|
||||||
duration: '00:05:00'
|
|
||||||
threshold: 80
|
|
||||||
state: present
|
|
||||||
register: policy
|
|
||||||
|
|
||||||
- name: debug
|
|
||||||
debug: var=policy
|
|
||||||
|
|
||||||
---
|
|
||||||
- name: Delete Alert Policy Example
|
|
||||||
hosts: localhost
|
|
||||||
gather_facts: False
|
|
||||||
connection: local
|
|
||||||
tasks:
|
|
||||||
- name: Delete an Alert Policy
|
|
||||||
clc_alert_policy:
|
|
||||||
alias: wfad
|
|
||||||
name: 'alert for disk > 80%'
|
|
||||||
state: absent
|
|
||||||
register: policy
|
|
||||||
|
|
||||||
- name: debug
|
|
||||||
debug: var=policy
|
|
||||||
'''
|
|
||||||
|
|
||||||
__version__ = '${version}'
|
|
||||||
|
|
||||||
import requests
|
|
||||||
|
|
||||||
#
|
|
||||||
# Requires the clc-python-sdk.
|
|
||||||
# sudo pip install clc-sdk
|
|
||||||
#
|
|
||||||
try:
|
|
||||||
import clc as clc_sdk
|
|
||||||
from clc import CLCException
|
|
||||||
except ImportError:
|
|
||||||
clc_found = False
|
|
||||||
clc_sdk = None
|
|
||||||
else:
|
|
||||||
clc_found = True
|
|
||||||
|
|
||||||
|
|
||||||
class ClcAlertPolicy():
|
|
||||||
|
|
||||||
clc = clc_sdk
|
|
||||||
module = None
|
|
||||||
|
|
||||||
def __init__(self, module):
|
|
||||||
"""
|
|
||||||
Construct module
|
|
||||||
"""
|
|
||||||
self.module = module
|
|
||||||
self.policy_dict = {}
|
|
||||||
|
|
||||||
if not clc_found:
|
|
||||||
self.module.fail_json(
|
|
||||||
msg='clc-python-sdk required for this module')
|
|
||||||
|
|
||||||
self._set_user_agent(self.clc)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _define_module_argument_spec():
|
|
||||||
"""
|
|
||||||
Define the argument spec for the ansible module
|
|
||||||
:return: argument spec dictionary
|
|
||||||
"""
|
|
||||||
argument_spec = dict(
|
|
||||||
name=dict(default=None),
|
|
||||||
id=dict(default=None),
|
|
||||||
alias=dict(required=True, default=None),
|
|
||||||
alert_recipients=dict(type='list', required=False, default=None),
|
|
||||||
metric=dict(required=False, choices=['cpu', 'memory', 'disk'], default=None),
|
|
||||||
duration=dict(required=False, type='str', default=None),
|
|
||||||
threshold=dict(required=False, type='int', default=None),
|
|
||||||
state=dict(default='present', choices=['present', 'absent'])
|
|
||||||
)
|
|
||||||
mutually_exclusive = [
|
|
||||||
['name', 'id']
|
|
||||||
]
|
|
||||||
return {'argument_spec': argument_spec,
|
|
||||||
'mutually_exclusive': mutually_exclusive}
|
|
||||||
|
|
||||||
# Module Behavior Goodness
|
|
||||||
def process_request(self):
|
|
||||||
"""
|
|
||||||
Process the request - Main Code Path
|
|
||||||
:return: Returns with either an exit_json or fail_json
|
|
||||||
"""
|
|
||||||
p = self.module.params
|
|
||||||
|
|
||||||
if not clc_found:
|
|
||||||
self.module.fail_json(
|
|
||||||
msg='clc-python-sdk required for this module')
|
|
||||||
|
|
||||||
self._set_clc_credentials_from_env()
|
|
||||||
self.policy_dict = self._get_alert_policies(p['alias'])
|
|
||||||
|
|
||||||
if p['state'] == 'present':
|
|
||||||
changed, policy = self._ensure_alert_policy_is_present()
|
|
||||||
else:
|
|
||||||
changed, policy = self._ensure_alert_policy_is_absent()
|
|
||||||
|
|
||||||
self.module.exit_json(changed=changed, policy=policy)
|
|
||||||
|
|
||||||
def _set_clc_credentials_from_env(self):
|
|
||||||
"""
|
|
||||||
Set the CLC Credentials on the sdk by reading environment variables
|
|
||||||
:return: none
|
|
||||||
"""
|
|
||||||
env = os.environ
|
|
||||||
v2_api_token = env.get('CLC_V2_API_TOKEN', False)
|
|
||||||
v2_api_username = env.get('CLC_V2_API_USERNAME', False)
|
|
||||||
v2_api_passwd = env.get('CLC_V2_API_PASSWD', False)
|
|
||||||
clc_alias = env.get('CLC_ACCT_ALIAS', False)
|
|
||||||
api_url = env.get('CLC_V2_API_URL', False)
|
|
||||||
|
|
||||||
if api_url:
|
|
||||||
self.clc.defaults.ENDPOINT_URL_V2 = api_url
|
|
||||||
|
|
||||||
if v2_api_token and clc_alias:
|
|
||||||
self.clc._LOGIN_TOKEN_V2 = v2_api_token
|
|
||||||
self.clc._V2_ENABLED = True
|
|
||||||
self.clc.ALIAS = clc_alias
|
|
||||||
elif v2_api_username and v2_api_passwd:
|
|
||||||
self.clc.v2.SetCredentials(
|
|
||||||
api_username=v2_api_username,
|
|
||||||
api_passwd=v2_api_passwd)
|
|
||||||
else:
|
|
||||||
return self.module.fail_json(
|
|
||||||
msg="You must set the CLC_V2_API_USERNAME and CLC_V2_API_PASSWD "
|
|
||||||
"environment variables")
|
|
||||||
|
|
||||||
def _ensure_alert_policy_is_present(self):
|
|
||||||
"""
|
|
||||||
Ensures that the alert policy is present
|
|
||||||
:return: (changed, policy)
|
|
||||||
canged: A flag representing if anything is modified
|
|
||||||
policy: the created/updated alert policy
|
|
||||||
"""
|
|
||||||
changed = False
|
|
||||||
p = self.module.params
|
|
||||||
policy_name = p.get('name')
|
|
||||||
alias = p.get('alias')
|
|
||||||
if not policy_name:
|
|
||||||
self.module.fail_json(msg='Policy name is a required')
|
|
||||||
policy = self._alert_policy_exists(alias, policy_name)
|
|
||||||
if not policy:
|
|
||||||
changed = True
|
|
||||||
policy = None
|
|
||||||
if not self.module.check_mode:
|
|
||||||
policy = self._create_alert_policy()
|
|
||||||
else:
|
|
||||||
changed_u, policy = self._ensure_alert_policy_is_updated(policy)
|
|
||||||
if changed_u:
|
|
||||||
changed = True
|
|
||||||
return changed, policy
|
|
||||||
|
|
||||||
def _ensure_alert_policy_is_absent(self):
|
|
||||||
"""
|
|
||||||
Ensures that the alert policy is absent
|
|
||||||
:return: (changed, None)
|
|
||||||
canged: A flag representing if anything is modified
|
|
||||||
"""
|
|
||||||
changed = False
|
|
||||||
p = self.module.params
|
|
||||||
alert_policy_id = p.get('id')
|
|
||||||
alert_policy_name = p.get('name')
|
|
||||||
alias = p.get('alias')
|
|
||||||
if not alert_policy_id and not alert_policy_name:
|
|
||||||
self.module.fail_json(
|
|
||||||
msg='Either alert policy id or policy name is required')
|
|
||||||
if not alert_policy_id and alert_policy_name:
|
|
||||||
alert_policy_id = self._get_alert_policy_id(
|
|
||||||
self.module,
|
|
||||||
alert_policy_name)
|
|
||||||
if alert_policy_id and alert_policy_id in self.policy_dict:
|
|
||||||
changed = True
|
|
||||||
if not self.module.check_mode:
|
|
||||||
self._delete_alert_policy(alias, alert_policy_id)
|
|
||||||
return changed, None
|
|
||||||
|
|
||||||
def _ensure_alert_policy_is_updated(self, alert_policy):
|
|
||||||
"""
|
|
||||||
Ensures the aliert policy is updated if anything is changed in the alert policy configuration
|
|
||||||
:param alert_policy: the targetalert policy
|
|
||||||
:return: (changed, policy)
|
|
||||||
canged: A flag representing if anything is modified
|
|
||||||
policy: the updated the alert policy
|
|
||||||
"""
|
|
||||||
changed = False
|
|
||||||
p = self.module.params
|
|
||||||
alert_policy_id = alert_policy.get('id')
|
|
||||||
email_list = p.get('alert_recipients')
|
|
||||||
metric = p.get('metric')
|
|
||||||
duration = p.get('duration')
|
|
||||||
threshold = p.get('threshold')
|
|
||||||
policy = alert_policy
|
|
||||||
if (metric and metric != str(alert_policy.get('triggers')[0].get('metric'))) or \
|
|
||||||
(duration and duration != str(alert_policy.get('triggers')[0].get('duration'))) or \
|
|
||||||
(threshold and float(threshold) != float(alert_policy.get('triggers')[0].get('threshold'))):
|
|
||||||
changed = True
|
|
||||||
elif email_list:
|
|
||||||
t_email_list = list(
|
|
||||||
alert_policy.get('actions')[0].get('settings').get('recipients'))
|
|
||||||
if set(email_list) != set(t_email_list):
|
|
||||||
changed = True
|
|
||||||
if changed and not self.module.check_mode:
|
|
||||||
policy = self._update_alert_policy(alert_policy_id)
|
|
||||||
return changed, policy
|
|
||||||
|
|
||||||
def _get_alert_policies(self, alias):
|
|
||||||
"""
|
|
||||||
Get the alert policies for account alias by calling the CLC API.
|
|
||||||
:param alias: the account alias
|
|
||||||
:return: the alert policies for the account alias
|
|
||||||
"""
|
|
||||||
response = {}
|
|
||||||
|
|
||||||
policies = self.clc.v2.API.Call('GET',
|
|
||||||
'/v2/alertPolicies/%s'
|
|
||||||
% (alias))
|
|
||||||
|
|
||||||
for policy in policies.get('items'):
|
|
||||||
response[policy.get('id')] = policy
|
|
||||||
return response
|
|
||||||
|
|
||||||
def _create_alert_policy(self):
|
|
||||||
"""
|
|
||||||
Create an alert Policy using the CLC API.
|
|
||||||
:return: response dictionary from the CLC API.
|
|
||||||
"""
|
|
||||||
p = self.module.params
|
|
||||||
alias = p['alias']
|
|
||||||
email_list = p['alert_recipients']
|
|
||||||
metric = p['metric']
|
|
||||||
duration = p['duration']
|
|
||||||
threshold = p['threshold']
|
|
||||||
name = p['name']
|
|
||||||
arguments = json.dumps(
|
|
||||||
{
|
|
||||||
'name': name,
|
|
||||||
'actions': [{
|
|
||||||
'action': 'email',
|
|
||||||
'settings': {
|
|
||||||
'recipients': email_list
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
'triggers': [{
|
|
||||||
'metric': metric,
|
|
||||||
'duration': duration,
|
|
||||||
'threshold': threshold
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
result = self.clc.v2.API.Call(
|
|
||||||
'POST',
|
|
||||||
'/v2/alertPolicies/%s' %
|
|
||||||
(alias),
|
|
||||||
arguments)
|
|
||||||
except self.clc.APIFailedResponse as e:
|
|
||||||
return self.module.fail_json(
|
|
||||||
msg='Unable to create alert policy. %s' % str(
|
|
||||||
e.response_text))
|
|
||||||
return result
|
|
||||||
|
|
||||||
def _update_alert_policy(self, alert_policy_id):
|
|
||||||
"""
|
|
||||||
Update alert policy using the CLC API.
|
|
||||||
:param alert_policy_id: The clc alert policy id
|
|
||||||
:return: response dictionary from the CLC API.
|
|
||||||
"""
|
|
||||||
p = self.module.params
|
|
||||||
alias = p['alias']
|
|
||||||
email_list = p['alert_recipients']
|
|
||||||
metric = p['metric']
|
|
||||||
duration = p['duration']
|
|
||||||
threshold = p['threshold']
|
|
||||||
name = p['name']
|
|
||||||
arguments = json.dumps(
|
|
||||||
{
|
|
||||||
'name': name,
|
|
||||||
'actions': [{
|
|
||||||
'action': 'email',
|
|
||||||
'settings': {
|
|
||||||
'recipients': email_list
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
'triggers': [{
|
|
||||||
'metric': metric,
|
|
||||||
'duration': duration,
|
|
||||||
'threshold': threshold
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
result = self.clc.v2.API.Call(
|
|
||||||
'PUT', '/v2/alertPolicies/%s/%s' %
|
|
||||||
(alias, alert_policy_id), arguments)
|
|
||||||
except self.clc.APIFailedResponse as e:
|
|
||||||
return self.module.fail_json(
|
|
||||||
msg='Unable to update alert policy. %s' % str(
|
|
||||||
e.response_text))
|
|
||||||
return result
|
|
||||||
|
|
||||||
def _delete_alert_policy(self, alias, policy_id):
|
|
||||||
"""
|
|
||||||
Delete an alert policy using the CLC API.
|
|
||||||
:param alias : the account alias
|
|
||||||
:param policy_id: the alert policy id
|
|
||||||
:return: response dictionary from the CLC API.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
result = self.clc.v2.API.Call(
|
|
||||||
'DELETE', '/v2/alertPolicies/%s/%s' %
|
|
||||||
(alias, policy_id), None)
|
|
||||||
except self.clc.APIFailedResponse as e:
|
|
||||||
return self.module.fail_json(
|
|
||||||
msg='Unable to delete alert policy. %s' % str(
|
|
||||||
e.response_text))
|
|
||||||
return result
|
|
||||||
|
|
||||||
def _alert_policy_exists(self, alias, policy_name):
|
|
||||||
"""
|
|
||||||
Check to see if an alert policy exists
|
|
||||||
:param policy_name: name of the alert policy
|
|
||||||
:return: boolean of if the policy exists
|
|
||||||
"""
|
|
||||||
result = False
|
|
||||||
for id in self.policy_dict:
|
|
||||||
if self.policy_dict.get(id).get('name') == policy_name:
|
|
||||||
result = self.policy_dict.get(id)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def _get_alert_policy_id(self, module, alert_policy_name):
|
|
||||||
"""
|
|
||||||
retrieves the alert policy id of the account based on the name of the policy
|
|
||||||
:param module: the AnsibleModule object
|
|
||||||
:param alert_policy_name: the alert policy name
|
|
||||||
:return: alert_policy_id: The alert policy id
|
|
||||||
"""
|
|
||||||
alert_policy_id = None
|
|
||||||
for id in self.policy_dict:
|
|
||||||
if self.policy_dict.get(id).get('name') == alert_policy_name:
|
|
||||||
if not alert_policy_id:
|
|
||||||
alert_policy_id = id
|
|
||||||
else:
|
|
||||||
return module.fail_json(
|
|
||||||
msg='mutiple alert policies were found with policy name : %s' %
|
|
||||||
(alert_policy_name))
|
|
||||||
return alert_policy_id
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _set_user_agent(clc):
|
|
||||||
if hasattr(clc, 'SetRequestsSession'):
|
|
||||||
agent_string = "ClcAnsibleModule/" + __version__
|
|
||||||
ses = requests.Session()
|
|
||||||
ses.headers.update({"Api-Client": agent_string})
|
|
||||||
ses.headers['User-Agent'] += " " + agent_string
|
|
||||||
clc.SetRequestsSession(ses)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""
|
|
||||||
The main function. Instantiates the module and calls process_request.
|
|
||||||
:return: none
|
|
||||||
"""
|
|
||||||
argument_dict = ClcAlertPolicy._define_module_argument_spec()
|
|
||||||
module = AnsibleModule(supports_check_mode=True, **argument_dict)
|
|
||||||
clc_alert_policy = ClcAlertPolicy(module)
|
|
||||||
clc_alert_policy.process_request()
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import * # pylint: disable=W0614
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,263 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
|
|
||||||
# CenturyLink Cloud Ansible Modules.
|
|
||||||
#
|
|
||||||
# These Ansible modules enable the CenturyLink Cloud v2 API to be called
|
|
||||||
# from an within Ansible Playbook.
|
|
||||||
#
|
|
||||||
# This file is part of CenturyLink Cloud, and is maintained
|
|
||||||
# by the Workflow as a Service Team
|
|
||||||
#
|
|
||||||
# Copyright 2015 CenturyLink Cloud
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
#
|
|
||||||
# CenturyLink Cloud: http://www.CenturyLinkCloud.com
|
|
||||||
# API Documentation: https://www.centurylinkcloud.com/api-docs/v2/
|
|
||||||
#
|
|
||||||
|
|
||||||
DOCUMENTATION = '''
|
|
||||||
module: clc_blueprint_package
|
|
||||||
short_desciption: deploys a blue print package on a set of servers in CenturyLink Cloud.
|
|
||||||
description:
|
|
||||||
- An Ansible module to deploy blue print package on a set of servers in CenturyLink Cloud.
|
|
||||||
options:
|
|
||||||
server_ids:
|
|
||||||
description:
|
|
||||||
- A list of server Ids to deploy the blue print package.
|
|
||||||
default: []
|
|
||||||
required: True
|
|
||||||
aliases: []
|
|
||||||
package_id:
|
|
||||||
description:
|
|
||||||
- The package id of the blue print.
|
|
||||||
default: None
|
|
||||||
required: True
|
|
||||||
aliases: []
|
|
||||||
package_params:
|
|
||||||
description:
|
|
||||||
- The dictionary of arguments required to deploy the blue print.
|
|
||||||
default: {}
|
|
||||||
required: False
|
|
||||||
aliases: []
|
|
||||||
'''
|
|
||||||
|
|
||||||
EXAMPLES = '''
|
|
||||||
# Note - You must set the CLC_V2_API_USERNAME And CLC_V2_API_PASSWD Environment variables before running these examples
|
|
||||||
|
|
||||||
- name: Deploy package
|
|
||||||
clc_blueprint_package:
|
|
||||||
server_ids:
|
|
||||||
- UC1WFSDANS01
|
|
||||||
- UC1WFSDANS02
|
|
||||||
package_id: 77abb844-579d-478d-3955-c69ab4a7ba1a
|
|
||||||
package_params: {}
|
|
||||||
'''
|
|
||||||
|
|
||||||
__version__ = '${version}'
|
|
||||||
|
|
||||||
import requests
|
|
||||||
|
|
||||||
#
|
|
||||||
# Requires the clc-python-sdk.
|
|
||||||
# sudo pip install clc-sdk
|
|
||||||
#
|
|
||||||
try:
|
|
||||||
import clc as clc_sdk
|
|
||||||
from clc import CLCException
|
|
||||||
except ImportError:
|
|
||||||
CLC_FOUND = False
|
|
||||||
clc_sdk = None
|
|
||||||
else:
|
|
||||||
CLC_FOUND = True
|
|
||||||
|
|
||||||
|
|
||||||
class ClcBlueprintPackage():
|
|
||||||
|
|
||||||
clc = clc_sdk
|
|
||||||
module = None
|
|
||||||
|
|
||||||
def __init__(self, module):
|
|
||||||
"""
|
|
||||||
Construct module
|
|
||||||
"""
|
|
||||||
self.module = module
|
|
||||||
if not CLC_FOUND:
|
|
||||||
self.module.fail_json(
|
|
||||||
msg='clc-python-sdk required for this module')
|
|
||||||
|
|
||||||
self._set_user_agent(self.clc)
|
|
||||||
|
|
||||||
def process_request(self):
|
|
||||||
"""
|
|
||||||
Process the request - Main Code Path
|
|
||||||
:return: Returns with either an exit_json or fail_json
|
|
||||||
"""
|
|
||||||
p = self.module.params
|
|
||||||
|
|
||||||
if not CLC_FOUND:
|
|
||||||
self.module.fail_json(
|
|
||||||
msg='clc-python-sdk required for this module')
|
|
||||||
|
|
||||||
self._set_clc_credentials_from_env()
|
|
||||||
|
|
||||||
server_ids = p['server_ids']
|
|
||||||
package_id = p['package_id']
|
|
||||||
package_params = p['package_params']
|
|
||||||
state = p['state']
|
|
||||||
if state == 'present':
|
|
||||||
changed, changed_server_ids, requests = self.ensure_package_installed(
|
|
||||||
server_ids, package_id, package_params)
|
|
||||||
if not self.module.check_mode:
|
|
||||||
self._wait_for_requests_to_complete(requests)
|
|
||||||
self.module.exit_json(changed=changed, server_ids=changed_server_ids)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def define_argument_spec():
|
|
||||||
"""
|
|
||||||
This function defnines the dictionary object required for
|
|
||||||
package module
|
|
||||||
:return: the package dictionary object
|
|
||||||
"""
|
|
||||||
argument_spec = dict(
|
|
||||||
server_ids=dict(type='list', required=True),
|
|
||||||
package_id=dict(required=True),
|
|
||||||
package_params=dict(type='dict', default={}),
|
|
||||||
wait=dict(default=True),
|
|
||||||
state=dict(default='present', choices=['present'])
|
|
||||||
)
|
|
||||||
return argument_spec
|
|
||||||
|
|
||||||
def ensure_package_installed(self, server_ids, package_id, package_params):
|
|
||||||
"""
|
|
||||||
Ensure the package is installed in the given list of servers
|
|
||||||
:param server_ids: the server list where the package needs to be installed
|
|
||||||
:param package_id: the package id
|
|
||||||
:param package_params: the package arguments
|
|
||||||
:return: (changed, server_ids)
|
|
||||||
changed: A flag indicating if a change was made
|
|
||||||
server_ids: The list of servers modfied
|
|
||||||
"""
|
|
||||||
changed = False
|
|
||||||
requests = []
|
|
||||||
servers = self._get_servers_from_clc(
|
|
||||||
server_ids,
|
|
||||||
'Failed to get servers from CLC')
|
|
||||||
try:
|
|
||||||
for server in servers:
|
|
||||||
request = self.clc_install_package(
|
|
||||||
server,
|
|
||||||
package_id,
|
|
||||||
package_params)
|
|
||||||
requests.append(request)
|
|
||||||
changed = True
|
|
||||||
except CLCException as ex:
|
|
||||||
self.module.fail_json(
|
|
||||||
msg='Failed while installing package : %s with Error : %s' %
|
|
||||||
(package_id, ex))
|
|
||||||
return changed, server_ids, requests
|
|
||||||
|
|
||||||
def clc_install_package(self, server, package_id, package_params):
|
|
||||||
"""
|
|
||||||
Read all servers from CLC and executes each package from package_list
|
|
||||||
:param server_list: The target list of servers where the packages needs to be installed
|
|
||||||
:param package_list: The list of packages to be installed
|
|
||||||
:return: (changed, server_ids)
|
|
||||||
changed: A flag indicating if a change was made
|
|
||||||
server_ids: The list of servers modfied
|
|
||||||
"""
|
|
||||||
result = None
|
|
||||||
if not self.module.check_mode:
|
|
||||||
result = server.ExecutePackage(
|
|
||||||
package_id=package_id,
|
|
||||||
parameters=package_params)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def _wait_for_requests_to_complete(self, requests_lst):
|
|
||||||
"""
|
|
||||||
Waits until the CLC requests are complete if the wait argument is True
|
|
||||||
:param requests_lst: The list of CLC request objects
|
|
||||||
:return: none
|
|
||||||
"""
|
|
||||||
if not self.module.params['wait']:
|
|
||||||
return
|
|
||||||
for request in requests_lst:
|
|
||||||
request.WaitUntilComplete()
|
|
||||||
for request_details in request.requests:
|
|
||||||
if request_details.Status() != 'succeeded':
|
|
||||||
self.module.fail_json(
|
|
||||||
msg='Unable to process package install request')
|
|
||||||
|
|
||||||
def _get_servers_from_clc(self, server_list, message):
|
|
||||||
"""
|
|
||||||
Internal function to fetch list of CLC server objects from a list of server ids
|
|
||||||
:param the list server ids
|
|
||||||
:return the list of CLC server objects
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
return self.clc.v2.Servers(server_list).servers
|
|
||||||
except CLCException as ex:
|
|
||||||
self.module.fail_json(msg=message + ': %s' % ex)
|
|
||||||
|
|
||||||
def _set_clc_credentials_from_env(self):
|
|
||||||
"""
|
|
||||||
Set the CLC Credentials on the sdk by reading environment variables
|
|
||||||
:return: none
|
|
||||||
"""
|
|
||||||
env = os.environ
|
|
||||||
v2_api_token = env.get('CLC_V2_API_TOKEN', False)
|
|
||||||
v2_api_username = env.get('CLC_V2_API_USERNAME', False)
|
|
||||||
v2_api_passwd = env.get('CLC_V2_API_PASSWD', False)
|
|
||||||
clc_alias = env.get('CLC_ACCT_ALIAS', False)
|
|
||||||
api_url = env.get('CLC_V2_API_URL', False)
|
|
||||||
|
|
||||||
if api_url:
|
|
||||||
self.clc.defaults.ENDPOINT_URL_V2 = api_url
|
|
||||||
|
|
||||||
if v2_api_token and clc_alias:
|
|
||||||
self.clc._LOGIN_TOKEN_V2 = v2_api_token
|
|
||||||
self.clc._V2_ENABLED = True
|
|
||||||
self.clc.ALIAS = clc_alias
|
|
||||||
elif v2_api_username and v2_api_passwd:
|
|
||||||
self.clc.v2.SetCredentials(
|
|
||||||
api_username=v2_api_username,
|
|
||||||
api_passwd=v2_api_passwd)
|
|
||||||
else:
|
|
||||||
return self.module.fail_json(
|
|
||||||
msg="You must set the CLC_V2_API_USERNAME and CLC_V2_API_PASSWD "
|
|
||||||
"environment variables")
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _set_user_agent(clc):
|
|
||||||
if hasattr(clc, 'SetRequestsSession'):
|
|
||||||
agent_string = "ClcAnsibleModule/" + __version__
|
|
||||||
ses = requests.Session()
|
|
||||||
ses.headers.update({"Api-Client": agent_string})
|
|
||||||
ses.headers['User-Agent'] += " " + agent_string
|
|
||||||
clc.SetRequestsSession(ses)
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""
|
|
||||||
Main function
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
module = AnsibleModule(
|
|
||||||
argument_spec=ClcBlueprintPackage.define_argument_spec(),
|
|
||||||
supports_check_mode=True
|
|
||||||
)
|
|
||||||
clc_blueprint_package = ClcBlueprintPackage(module)
|
|
||||||
clc_blueprint_package.process_request()
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import *
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,542 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
|
|
||||||
# CenturyLink Cloud Ansible Modules.
|
|
||||||
#
|
|
||||||
# These Ansible modules enable the CenturyLink Cloud v2 API to be called
|
|
||||||
# from an within Ansible Playbook.
|
|
||||||
#
|
|
||||||
# This file is part of CenturyLink Cloud, and is maintained
|
|
||||||
# by the Workflow as a Service Team
|
|
||||||
#
|
|
||||||
# Copyright 2015 CenturyLink Cloud
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
#
|
|
||||||
# CenturyLink Cloud: http://www.CenturyLinkCloud.com
|
|
||||||
# API Documentation: https://www.centurylinkcloud.com/api-docs/v2/
|
|
||||||
#
|
|
||||||
|
|
||||||
DOCUMENTATION = '''
|
|
||||||
module: clc_firewall_policy
|
|
||||||
short_desciption: Create/delete/update firewall policies
|
|
||||||
description:
|
|
||||||
- Create or delete or updated firewall polices on Centurylink Centurylink Cloud
|
|
||||||
options:
|
|
||||||
location:
|
|
||||||
description:
|
|
||||||
- Target datacenter for the firewall policy
|
|
||||||
default: None
|
|
||||||
required: True
|
|
||||||
aliases: []
|
|
||||||
state:
|
|
||||||
description:
|
|
||||||
- Whether to create or delete the firewall policy
|
|
||||||
default: present
|
|
||||||
required: True
|
|
||||||
choices: ['present', 'absent']
|
|
||||||
aliases: []
|
|
||||||
source:
|
|
||||||
description:
|
|
||||||
- Source addresses for traffic on the originating firewall
|
|
||||||
default: None
|
|
||||||
required: For Creation
|
|
||||||
aliases: []
|
|
||||||
destination:
|
|
||||||
description:
|
|
||||||
- Destination addresses for traffic on the terminating firewall
|
|
||||||
default: None
|
|
||||||
required: For Creation
|
|
||||||
aliases: []
|
|
||||||
ports:
|
|
||||||
description:
|
|
||||||
- types of ports associated with the policy. TCP & UDP can take in single ports or port ranges.
|
|
||||||
default: None
|
|
||||||
required: False
|
|
||||||
choices: ['any', 'icmp', 'TCP/123', 'UDP/123', 'TCP/123-456', 'UDP/123-456']
|
|
||||||
aliases: []
|
|
||||||
firewall_policy_id:
|
|
||||||
description:
|
|
||||||
- Id of the firewall policy
|
|
||||||
default: None
|
|
||||||
required: False
|
|
||||||
aliases: []
|
|
||||||
source_account_alias:
|
|
||||||
description:
|
|
||||||
- CLC alias for the source account
|
|
||||||
default: None
|
|
||||||
required: True
|
|
||||||
aliases: []
|
|
||||||
destination_account_alias:
|
|
||||||
description:
|
|
||||||
- CLC alias for the destination account
|
|
||||||
default: None
|
|
||||||
required: False
|
|
||||||
aliases: []
|
|
||||||
wait:
|
|
||||||
description:
|
|
||||||
- Whether to wait for the provisioning tasks to finish before returning.
|
|
||||||
default: True
|
|
||||||
required: False
|
|
||||||
choices: [ True, False ]
|
|
||||||
aliases: []
|
|
||||||
enabled:
|
|
||||||
description:
|
|
||||||
- If the firewall policy is enabled or disabled
|
|
||||||
default: true
|
|
||||||
required: False
|
|
||||||
choices: [ true, false ]
|
|
||||||
aliases: []
|
|
||||||
|
|
||||||
'''
|
|
||||||
|
|
||||||
EXAMPLES = '''
|
|
||||||
---
|
|
||||||
- name: Create Firewall Policy
|
|
||||||
hosts: localhost
|
|
||||||
gather_facts: False
|
|
||||||
connection: local
|
|
||||||
tasks:
|
|
||||||
- name: Create / Verify an Firewall Policy at CenturyLink Cloud
|
|
||||||
clc_firewall:
|
|
||||||
source_account_alias: WFAD
|
|
||||||
location: VA1
|
|
||||||
state: present
|
|
||||||
source: 10.128.216.0/24
|
|
||||||
destination: 10.128.216.0/24
|
|
||||||
ports: Any
|
|
||||||
destination_account_alias: WFAD
|
|
||||||
|
|
||||||
---
|
|
||||||
- name: Delete Firewall Policy
|
|
||||||
hosts: localhost
|
|
||||||
gather_facts: False
|
|
||||||
connection: local
|
|
||||||
tasks:
|
|
||||||
- name: Delete an Firewall Policy at CenturyLink Cloud
|
|
||||||
clc_firewall:
|
|
||||||
source_account_alias: WFAD
|
|
||||||
location: VA1
|
|
||||||
state: present
|
|
||||||
firewall_policy_id: c62105233d7a4231bd2e91b9c791eaae
|
|
||||||
'''
|
|
||||||
|
|
||||||
__version__ = '${version}'
|
|
||||||
|
|
||||||
import urlparse
|
|
||||||
from time import sleep
|
|
||||||
import requests
|
|
||||||
|
|
||||||
try:
|
|
||||||
import clc as clc_sdk
|
|
||||||
from clc import CLCException
|
|
||||||
except ImportError:
|
|
||||||
CLC_FOUND = False
|
|
||||||
clc_sdk = None
|
|
||||||
else:
|
|
||||||
CLC_FOUND = True
|
|
||||||
|
|
||||||
|
|
||||||
class ClcFirewallPolicy():
|
|
||||||
|
|
||||||
clc = None
|
|
||||||
|
|
||||||
def __init__(self, module):
|
|
||||||
"""
|
|
||||||
Construct module
|
|
||||||
"""
|
|
||||||
self.clc = clc_sdk
|
|
||||||
self.module = module
|
|
||||||
self.firewall_dict = {}
|
|
||||||
|
|
||||||
if not CLC_FOUND:
|
|
||||||
self.module.fail_json(
|
|
||||||
msg='clc-python-sdk required for this module')
|
|
||||||
|
|
||||||
self._set_user_agent(self.clc)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _define_module_argument_spec():
|
|
||||||
"""
|
|
||||||
Define the argument spec for the ansible module
|
|
||||||
:return: argument spec dictionary
|
|
||||||
"""
|
|
||||||
argument_spec = dict(
|
|
||||||
location=dict(required=True, defualt=None),
|
|
||||||
source_account_alias=dict(required=True, default=None),
|
|
||||||
destination_account_alias=dict(default=None),
|
|
||||||
firewall_policy_id=dict(default=None),
|
|
||||||
ports=dict(default=None, type='list'),
|
|
||||||
source=dict(defualt=None, type='list'),
|
|
||||||
destination=dict(defualt=None, type='list'),
|
|
||||||
wait=dict(default=True),
|
|
||||||
state=dict(default='present', choices=['present', 'absent']),
|
|
||||||
enabled=dict(defualt=None)
|
|
||||||
)
|
|
||||||
return argument_spec
|
|
||||||
|
|
||||||
def process_request(self):
|
|
||||||
"""
|
|
||||||
Execute the main code path, and handle the request
|
|
||||||
:return: none
|
|
||||||
"""
|
|
||||||
location = self.module.params.get('location')
|
|
||||||
source_account_alias = self.module.params.get('source_account_alias')
|
|
||||||
destination_account_alias = self.module.params.get(
|
|
||||||
'destination_account_alias')
|
|
||||||
firewall_policy_id = self.module.params.get('firewall_policy_id')
|
|
||||||
ports = self.module.params.get('ports')
|
|
||||||
source = self.module.params.get('source')
|
|
||||||
destination = self.module.params.get('destination')
|
|
||||||
wait = self.module.params.get('wait')
|
|
||||||
state = self.module.params.get('state')
|
|
||||||
enabled = self.module.params.get('enabled')
|
|
||||||
|
|
||||||
self.firewall_dict = {
|
|
||||||
'location': location,
|
|
||||||
'source_account_alias': source_account_alias,
|
|
||||||
'destination_account_alias': destination_account_alias,
|
|
||||||
'firewall_policy_id': firewall_policy_id,
|
|
||||||
'ports': ports,
|
|
||||||
'source': source,
|
|
||||||
'destination': destination,
|
|
||||||
'wait': wait,
|
|
||||||
'state': state,
|
|
||||||
'enabled': enabled}
|
|
||||||
|
|
||||||
self._set_clc_credentials_from_env()
|
|
||||||
requests = []
|
|
||||||
|
|
||||||
if state == 'absent':
|
|
||||||
changed, firewall_policy_id, response = self._ensure_firewall_policy_is_absent(
|
|
||||||
source_account_alias, location, self.firewall_dict)
|
|
||||||
|
|
||||||
elif state == 'present':
|
|
||||||
changed, firewall_policy_id, response = self._ensure_firewall_policy_is_present(
|
|
||||||
source_account_alias, location, self.firewall_dict)
|
|
||||||
else:
|
|
||||||
return self.module.fail_json(msg="Unknown State: " + state)
|
|
||||||
|
|
||||||
return self.module.exit_json(
|
|
||||||
changed=changed,
|
|
||||||
firewall_policy_id=firewall_policy_id)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _get_policy_id_from_response(response):
|
|
||||||
"""
|
|
||||||
Method to parse out the policy id from creation response
|
|
||||||
:param response: response from firewall creation control
|
|
||||||
:return: policy_id: firewall policy id from creation call
|
|
||||||
"""
|
|
||||||
url = response.get('links')[0]['href']
|
|
||||||
path = urlparse.urlparse(url).path
|
|
||||||
path_list = os.path.split(path)
|
|
||||||
policy_id = path_list[-1]
|
|
||||||
return policy_id
|
|
||||||
|
|
||||||
def _set_clc_credentials_from_env(self):
|
|
||||||
"""
|
|
||||||
Set the CLC Credentials on the sdk by reading environment variables
|
|
||||||
:return: none
|
|
||||||
"""
|
|
||||||
env = os.environ
|
|
||||||
v2_api_token = env.get('CLC_V2_API_TOKEN', False)
|
|
||||||
v2_api_username = env.get('CLC_V2_API_USERNAME', False)
|
|
||||||
v2_api_passwd = env.get('CLC_V2_API_PASSWD', False)
|
|
||||||
clc_alias = env.get('CLC_ACCT_ALIAS', False)
|
|
||||||
api_url = env.get('CLC_V2_API_URL', False)
|
|
||||||
|
|
||||||
if api_url:
|
|
||||||
self.clc.defaults.ENDPOINT_URL_V2 = api_url
|
|
||||||
|
|
||||||
if v2_api_token and clc_alias:
|
|
||||||
self.clc._LOGIN_TOKEN_V2 = v2_api_token
|
|
||||||
self.clc._V2_ENABLED = True
|
|
||||||
self.clc.ALIAS = clc_alias
|
|
||||||
elif v2_api_username and v2_api_passwd:
|
|
||||||
self.clc.v2.SetCredentials(
|
|
||||||
api_username=v2_api_username,
|
|
||||||
api_passwd=v2_api_passwd)
|
|
||||||
else:
|
|
||||||
return self.module.fail_json(
|
|
||||||
msg="You must set the CLC_V2_API_USERNAME and CLC_V2_API_PASSWD "
|
|
||||||
"environment variables")
|
|
||||||
|
|
||||||
def _ensure_firewall_policy_is_present(
|
|
||||||
self,
|
|
||||||
source_account_alias,
|
|
||||||
location,
|
|
||||||
firewall_dict):
|
|
||||||
"""
|
|
||||||
Ensures that a given firewall policy is present
|
|
||||||
:param source_account_alias: the source account alias for the firewall policy
|
|
||||||
:param location: datacenter of the firewall policy
|
|
||||||
:param firewall_dict: dictionary or request parameters for firewall policy creation
|
|
||||||
:return: (changed, firewall_policy, response)
|
|
||||||
changed: flag for if a change occurred
|
|
||||||
firewall_policy: policy that was changed
|
|
||||||
response: response from CLC API call
|
|
||||||
"""
|
|
||||||
changed = False
|
|
||||||
response = {}
|
|
||||||
firewall_policy_id = firewall_dict.get('firewall_policy_id')
|
|
||||||
|
|
||||||
if firewall_policy_id is None:
|
|
||||||
if not self.module.check_mode:
|
|
||||||
response = self._create_firewall_policy(
|
|
||||||
source_account_alias,
|
|
||||||
location,
|
|
||||||
firewall_dict)
|
|
||||||
firewall_policy_id = self._get_policy_id_from_response(
|
|
||||||
response)
|
|
||||||
self._wait_for_requests_to_complete(
|
|
||||||
firewall_dict.get('wait'),
|
|
||||||
source_account_alias,
|
|
||||||
location,
|
|
||||||
firewall_policy_id)
|
|
||||||
changed = True
|
|
||||||
else:
|
|
||||||
get_before_response, success = self._get_firewall_policy(
|
|
||||||
source_account_alias, location, firewall_policy_id)
|
|
||||||
if not success:
|
|
||||||
return self.module.fail_json(
|
|
||||||
msg='Unable to find the firewall policy id : %s' %
|
|
||||||
firewall_policy_id)
|
|
||||||
changed = self._compare_get_request_with_dict(
|
|
||||||
get_before_response,
|
|
||||||
firewall_dict)
|
|
||||||
if not self.module.check_mode and changed:
|
|
||||||
response = self._update_firewall_policy(
|
|
||||||
source_account_alias,
|
|
||||||
location,
|
|
||||||
firewall_policy_id,
|
|
||||||
firewall_dict)
|
|
||||||
self._wait_for_requests_to_complete(
|
|
||||||
firewall_dict.get('wait'),
|
|
||||||
source_account_alias,
|
|
||||||
location,
|
|
||||||
firewall_policy_id)
|
|
||||||
return changed, firewall_policy_id, response
|
|
||||||
|
|
||||||
def _ensure_firewall_policy_is_absent(
|
|
||||||
self,
|
|
||||||
source_account_alias,
|
|
||||||
location,
|
|
||||||
firewall_dict):
|
|
||||||
"""
|
|
||||||
Ensures that a given firewall policy is removed if present
|
|
||||||
:param source_account_alias: the source account alias for the firewall policy
|
|
||||||
:param location: datacenter of the firewall policy
|
|
||||||
:param firewall_dict: firewall policy to delete
|
|
||||||
:return: (changed, firewall_policy_id, response)
|
|
||||||
changed: flag for if a change occurred
|
|
||||||
firewall_policy_id: policy that was changed
|
|
||||||
response: response from CLC API call
|
|
||||||
"""
|
|
||||||
changed = False
|
|
||||||
response = []
|
|
||||||
firewall_policy_id = firewall_dict.get('firewall_policy_id')
|
|
||||||
result, success = self._get_firewall_policy(
|
|
||||||
source_account_alias, location, firewall_policy_id)
|
|
||||||
if success:
|
|
||||||
if not self.module.check_mode:
|
|
||||||
response = self._delete_firewall_policy(
|
|
||||||
source_account_alias,
|
|
||||||
location,
|
|
||||||
firewall_policy_id)
|
|
||||||
changed = True
|
|
||||||
return changed, firewall_policy_id, response
|
|
||||||
|
|
||||||
def _create_firewall_policy(
|
|
||||||
self,
|
|
||||||
source_account_alias,
|
|
||||||
location,
|
|
||||||
firewall_dict):
|
|
||||||
"""
|
|
||||||
Ensures that a given firewall policy is present
|
|
||||||
:param source_account_alias: the source account alias for the firewall policy
|
|
||||||
:param location: datacenter of the firewall policy
|
|
||||||
:param firewall_dict: dictionary or request parameters for firewall policy creation
|
|
||||||
:return: response from CLC API call
|
|
||||||
"""
|
|
||||||
payload = {
|
|
||||||
'destinationAccount': firewall_dict.get('destination_account_alias'),
|
|
||||||
'source': firewall_dict.get('source'),
|
|
||||||
'destination': firewall_dict.get('destination'),
|
|
||||||
'ports': firewall_dict.get('ports')}
|
|
||||||
try:
|
|
||||||
response = self.clc.v2.API.Call(
|
|
||||||
'POST', '/v2-experimental/firewallPolicies/%s/%s' %
|
|
||||||
(source_account_alias, location), payload)
|
|
||||||
except self.clc.APIFailedResponse as e:
|
|
||||||
return self.module.fail_json(
|
|
||||||
msg="Unable to successfully create firewall policy. %s" %
|
|
||||||
str(e.response_text))
|
|
||||||
return response
|
|
||||||
|
|
||||||
def _delete_firewall_policy(
|
|
||||||
self,
|
|
||||||
source_account_alias,
|
|
||||||
location,
|
|
||||||
firewall_policy_id):
|
|
||||||
"""
|
|
||||||
Deletes a given firewall policy for an account alias in a datacenter
|
|
||||||
:param source_account_alias: the source account alias for the firewall policy
|
|
||||||
:param location: datacenter of the firewall policy
|
|
||||||
:param firewall_policy_id: firewall policy to delete
|
|
||||||
:return: response: response from CLC API call
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
response = self.clc.v2.API.Call(
|
|
||||||
'DELETE', '/v2-experimental/firewallPolicies/%s/%s/%s' %
|
|
||||||
(source_account_alias, location, firewall_policy_id))
|
|
||||||
except self.clc.APIFailedResponse as e:
|
|
||||||
return self.module.fail_json(
|
|
||||||
msg="Unable to successfully delete firewall policy. %s" %
|
|
||||||
str(e.response_text))
|
|
||||||
return response
|
|
||||||
|
|
||||||
def _update_firewall_policy(
|
|
||||||
self,
|
|
||||||
source_account_alias,
|
|
||||||
location,
|
|
||||||
firewall_policy_id,
|
|
||||||
firewall_dict):
|
|
||||||
"""
|
|
||||||
Updates a firewall policy for a given datacenter and account alias
|
|
||||||
:param source_account_alias: the source account alias for the firewall policy
|
|
||||||
:param location: datacenter of the firewall policy
|
|
||||||
:param firewall_policy_id: firewall policy to delete
|
|
||||||
:param firewall_dict: dictionary or request parameters for firewall policy creation
|
|
||||||
:return: response: response from CLC API call
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
response = self.clc.v2.API.Call(
|
|
||||||
'PUT',
|
|
||||||
'/v2-experimental/firewallPolicies/%s/%s/%s' %
|
|
||||||
(source_account_alias,
|
|
||||||
location,
|
|
||||||
firewall_policy_id),
|
|
||||||
firewall_dict)
|
|
||||||
except self.clc.APIFailedResponse as e:
|
|
||||||
return self.module.fail_json(
|
|
||||||
msg="Unable to successfully update firewall policy. %s" %
|
|
||||||
str(e.response_text))
|
|
||||||
return response
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _compare_get_request_with_dict(response, firewall_dict):
|
|
||||||
"""
|
|
||||||
Helper method to compare the json response for getting the firewall policy with the request parameters
|
|
||||||
:param response: response from the get method
|
|
||||||
:param firewall_dict: dictionary or request parameters for firewall policy creation
|
|
||||||
:return: changed: Boolean that returns true if there are differences between the response parameters and the playbook parameters
|
|
||||||
"""
|
|
||||||
|
|
||||||
changed = False
|
|
||||||
|
|
||||||
response_dest_account_alias = response.get('destinationAccount')
|
|
||||||
response_enabled = response.get('enabled')
|
|
||||||
response_source = response.get('source')
|
|
||||||
response_dest = response.get('destination')
|
|
||||||
response_ports = response.get('ports')
|
|
||||||
|
|
||||||
request_dest_account_alias = firewall_dict.get(
|
|
||||||
'destination_account_alias')
|
|
||||||
request_enabled = firewall_dict.get('enabled')
|
|
||||||
if request_enabled is None:
|
|
||||||
request_enabled = True
|
|
||||||
request_source = firewall_dict.get('source')
|
|
||||||
request_dest = firewall_dict.get('destination')
|
|
||||||
request_ports = firewall_dict.get('ports')
|
|
||||||
|
|
||||||
if (
|
|
||||||
response_dest_account_alias and str(response_dest_account_alias) != str(request_dest_account_alias)) or (
|
|
||||||
response_enabled != request_enabled) or (
|
|
||||||
response_source and response_source != request_source) or (
|
|
||||||
response_dest and response_dest != request_dest) or (
|
|
||||||
response_ports and response_ports != request_ports):
|
|
||||||
changed = True
|
|
||||||
return changed
|
|
||||||
|
|
||||||
def _get_firewall_policy(
|
|
||||||
self,
|
|
||||||
source_account_alias,
|
|
||||||
location,
|
|
||||||
firewall_policy_id):
|
|
||||||
"""
|
|
||||||
Get back details for a particular firewall policy
|
|
||||||
:param source_account_alias: the source account alias for the firewall policy
|
|
||||||
:param location: datacenter of the firewall policy
|
|
||||||
:param firewall_policy_id: id of the firewall policy to get
|
|
||||||
:return: response from CLC API call
|
|
||||||
"""
|
|
||||||
response = []
|
|
||||||
success = False
|
|
||||||
try:
|
|
||||||
response = self.clc.v2.API.Call(
|
|
||||||
'GET', '/v2-experimental/firewallPolicies/%s/%s/%s' %
|
|
||||||
(source_account_alias, location, firewall_policy_id))
|
|
||||||
success = True
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
return response, success
|
|
||||||
|
|
||||||
def _wait_for_requests_to_complete(
|
|
||||||
self,
|
|
||||||
wait,
|
|
||||||
source_account_alias,
|
|
||||||
location,
|
|
||||||
firewall_policy_id):
|
|
||||||
"""
|
|
||||||
Waits until the CLC requests are complete if the wait argument is True
|
|
||||||
:param requests_lst: The list of CLC request objects
|
|
||||||
:return: none
|
|
||||||
"""
|
|
||||||
if wait:
|
|
||||||
response, success = self._get_firewall_policy(
|
|
||||||
source_account_alias, location, firewall_policy_id)
|
|
||||||
if response.get('status') == 'pending':
|
|
||||||
sleep(2)
|
|
||||||
self._wait_for_requests_to_complete(
|
|
||||||
wait,
|
|
||||||
source_account_alias,
|
|
||||||
location,
|
|
||||||
firewall_policy_id)
|
|
||||||
return None
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _set_user_agent(clc):
|
|
||||||
if hasattr(clc, 'SetRequestsSession'):
|
|
||||||
agent_string = "ClcAnsibleModule/" + __version__
|
|
||||||
ses = requests.Session()
|
|
||||||
ses.headers.update({"Api-Client": agent_string})
|
|
||||||
ses.headers['User-Agent'] += " " + agent_string
|
|
||||||
clc.SetRequestsSession(ses)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""
|
|
||||||
The main function. Instantiates the module and calls process_request.
|
|
||||||
:return: none
|
|
||||||
"""
|
|
||||||
module = AnsibleModule(
|
|
||||||
argument_spec=ClcFirewallPolicy._define_module_argument_spec(),
|
|
||||||
supports_check_mode=True)
|
|
||||||
|
|
||||||
clc_firewall = ClcFirewallPolicy(module)
|
|
||||||
clc_firewall.process_request()
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import * # pylint: disable=W0614
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,370 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
|
|
||||||
# CenturyLink Cloud Ansible Modules.
|
|
||||||
#
|
|
||||||
# These Ansible modules enable the CenturyLink Cloud v2 API to be called
|
|
||||||
# from an within Ansible Playbook.
|
|
||||||
#
|
|
||||||
# This file is part of CenturyLink Cloud, and is maintained
|
|
||||||
# by the Workflow as a Service Team
|
|
||||||
#
|
|
||||||
# Copyright 2015 CenturyLink Cloud
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
#
|
|
||||||
# CenturyLink Cloud: http://www.CenturyLinkCloud.com
|
|
||||||
# API Documentation: https://www.centurylinkcloud.com/api-docs/v2/
|
|
||||||
#
|
|
||||||
|
|
||||||
DOCUMENTATION = '''
|
|
||||||
module: clc_group
|
|
||||||
short_desciption: Create/delete Server Groups at Centurylink Cloud
|
|
||||||
description:
|
|
||||||
- Create or delete Server Groups at Centurylink Centurylink Cloud
|
|
||||||
options:
|
|
||||||
name:
|
|
||||||
description:
|
|
||||||
- The name of the Server Group
|
|
||||||
description:
|
|
||||||
description:
|
|
||||||
- A description of the Server Group
|
|
||||||
parent:
|
|
||||||
description:
|
|
||||||
- The parent group of the server group
|
|
||||||
location:
|
|
||||||
description:
|
|
||||||
- Datacenter to create the group in
|
|
||||||
state:
|
|
||||||
description:
|
|
||||||
- Whether to create or delete the group
|
|
||||||
default: present
|
|
||||||
choices: ['present', 'absent']
|
|
||||||
|
|
||||||
'''
|
|
||||||
|
|
||||||
EXAMPLES = '''
|
|
||||||
|
|
||||||
# Create a Server Group
|
|
||||||
|
|
||||||
---
|
|
||||||
- name: Create Server Group
|
|
||||||
hosts: localhost
|
|
||||||
gather_facts: False
|
|
||||||
connection: local
|
|
||||||
tasks:
|
|
||||||
- name: Create / Verify a Server Group at CenturyLink Cloud
|
|
||||||
clc_group:
|
|
||||||
name: 'My Cool Server Group'
|
|
||||||
parent: 'Default Group'
|
|
||||||
state: present
|
|
||||||
register: clc
|
|
||||||
|
|
||||||
- name: debug
|
|
||||||
debug: var=clc
|
|
||||||
|
|
||||||
# Delete a Server Group
|
|
||||||
|
|
||||||
---
|
|
||||||
- name: Delete Server Group
|
|
||||||
hosts: localhost
|
|
||||||
gather_facts: False
|
|
||||||
connection: local
|
|
||||||
tasks:
|
|
||||||
- name: Delete / Verify Absent a Server Group at CenturyLink Cloud
|
|
||||||
clc_group:
|
|
||||||
name: 'My Cool Server Group'
|
|
||||||
parent: 'Default Group'
|
|
||||||
state: absent
|
|
||||||
register: clc
|
|
||||||
|
|
||||||
- name: debug
|
|
||||||
debug: var=clc
|
|
||||||
|
|
||||||
'''
|
|
||||||
|
|
||||||
__version__ = '${version}'
|
|
||||||
|
|
||||||
import requests
|
|
||||||
|
|
||||||
#
|
|
||||||
# Requires the clc-python-sdk.
|
|
||||||
# sudo pip install clc-sdk
|
|
||||||
#
|
|
||||||
try:
|
|
||||||
import clc as clc_sdk
|
|
||||||
from clc import CLCException
|
|
||||||
except ImportError:
|
|
||||||
CLC_FOUND = False
|
|
||||||
clc_sdk = None
|
|
||||||
else:
|
|
||||||
CLC_FOUND = True
|
|
||||||
|
|
||||||
|
|
||||||
class ClcGroup(object):
|
|
||||||
|
|
||||||
clc = None
|
|
||||||
root_group = None
|
|
||||||
|
|
||||||
def __init__(self, module):
|
|
||||||
"""
|
|
||||||
Construct module
|
|
||||||
"""
|
|
||||||
self.clc = clc_sdk
|
|
||||||
self.module = module
|
|
||||||
self.group_dict = {}
|
|
||||||
|
|
||||||
if not CLC_FOUND:
|
|
||||||
self.module.fail_json(
|
|
||||||
msg='clc-python-sdk required for this module')
|
|
||||||
|
|
||||||
self._set_user_agent(self.clc)
|
|
||||||
|
|
||||||
def process_request(self):
|
|
||||||
"""
|
|
||||||
Execute the main code path, and handle the request
|
|
||||||
:return: none
|
|
||||||
"""
|
|
||||||
location = self.module.params.get('location')
|
|
||||||
group_name = self.module.params.get('name')
|
|
||||||
parent_name = self.module.params.get('parent')
|
|
||||||
group_description = self.module.params.get('description')
|
|
||||||
state = self.module.params.get('state')
|
|
||||||
|
|
||||||
self._set_clc_credentials_from_env()
|
|
||||||
self.group_dict = self._get_group_tree_for_datacenter(
|
|
||||||
datacenter=location)
|
|
||||||
|
|
||||||
if state == "absent":
|
|
||||||
changed, group, response = self._ensure_group_is_absent(
|
|
||||||
group_name=group_name, parent_name=parent_name)
|
|
||||||
|
|
||||||
else:
|
|
||||||
changed, group, response = self._ensure_group_is_present(
|
|
||||||
group_name=group_name, parent_name=parent_name, group_description=group_description)
|
|
||||||
|
|
||||||
|
|
||||||
self.module.exit_json(changed=changed, group=group_name)
|
|
||||||
|
|
||||||
#
|
|
||||||
# Functions to define the Ansible module and its arguments
|
|
||||||
#
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _define_module_argument_spec():
|
|
||||||
"""
|
|
||||||
Define the argument spec for the ansible module
|
|
||||||
:return: argument spec dictionary
|
|
||||||
"""
|
|
||||||
argument_spec = dict(
|
|
||||||
name=dict(required=True),
|
|
||||||
description=dict(default=None),
|
|
||||||
parent=dict(default=None),
|
|
||||||
location=dict(default=None),
|
|
||||||
alias=dict(default=None),
|
|
||||||
custom_fields=dict(type='list', default=[]),
|
|
||||||
server_ids=dict(type='list', default=[]),
|
|
||||||
state=dict(default='present', choices=['present', 'absent']))
|
|
||||||
|
|
||||||
return argument_spec
|
|
||||||
|
|
||||||
#
|
|
||||||
# Module Behavior Functions
|
|
||||||
#
|
|
||||||
|
|
||||||
def _set_clc_credentials_from_env(self):
|
|
||||||
"""
|
|
||||||
Set the CLC Credentials on the sdk by reading environment variables
|
|
||||||
:return: none
|
|
||||||
"""
|
|
||||||
env = os.environ
|
|
||||||
v2_api_token = env.get('CLC_V2_API_TOKEN', False)
|
|
||||||
v2_api_username = env.get('CLC_V2_API_USERNAME', False)
|
|
||||||
v2_api_passwd = env.get('CLC_V2_API_PASSWD', False)
|
|
||||||
clc_alias = env.get('CLC_ACCT_ALIAS', False)
|
|
||||||
api_url = env.get('CLC_V2_API_URL', False)
|
|
||||||
|
|
||||||
if api_url:
|
|
||||||
self.clc.defaults.ENDPOINT_URL_V2 = api_url
|
|
||||||
|
|
||||||
if v2_api_token and clc_alias:
|
|
||||||
self.clc._LOGIN_TOKEN_V2 = v2_api_token
|
|
||||||
self.clc._V2_ENABLED = True
|
|
||||||
self.clc.ALIAS = clc_alias
|
|
||||||
elif v2_api_username and v2_api_passwd:
|
|
||||||
self.clc.v2.SetCredentials(
|
|
||||||
api_username=v2_api_username,
|
|
||||||
api_passwd=v2_api_passwd)
|
|
||||||
else:
|
|
||||||
return self.module.fail_json(
|
|
||||||
msg="You must set the CLC_V2_API_USERNAME and CLC_V2_API_PASSWD "
|
|
||||||
"environment variables")
|
|
||||||
|
|
||||||
def _ensure_group_is_absent(self, group_name, parent_name):
|
|
||||||
"""
|
|
||||||
Ensure that group_name is absent by deleting it if necessary
|
|
||||||
:param group_name: string - the name of the clc server group to delete
|
|
||||||
:param parent_name: string - the name of the parent group for group_name
|
|
||||||
:return: changed, group
|
|
||||||
"""
|
|
||||||
changed = False
|
|
||||||
group = []
|
|
||||||
results = []
|
|
||||||
|
|
||||||
if self._group_exists(group_name=group_name, parent_name=parent_name):
|
|
||||||
if not self.module.check_mode:
|
|
||||||
group.append(group_name)
|
|
||||||
for g in group:
|
|
||||||
result = self._delete_group(group_name)
|
|
||||||
results.append(result)
|
|
||||||
changed = True
|
|
||||||
return changed, group, results
|
|
||||||
|
|
||||||
def _delete_group(self, group_name):
|
|
||||||
"""
|
|
||||||
Delete the provided server group
|
|
||||||
:param group_name: string - the server group to delete
|
|
||||||
:return: none
|
|
||||||
"""
|
|
||||||
group, parent = self.group_dict.get(group_name)
|
|
||||||
response = group.Delete()
|
|
||||||
return response
|
|
||||||
|
|
||||||
def _ensure_group_is_present(
|
|
||||||
self,
|
|
||||||
group_name,
|
|
||||||
parent_name,
|
|
||||||
group_description):
|
|
||||||
"""
|
|
||||||
Checks to see if a server group exists, creates it if it doesn't.
|
|
||||||
:param group_name: the name of the group to validate/create
|
|
||||||
:param parent_name: the name of the parent group for group_name
|
|
||||||
:param group_description: a short description of the server group (used when creating)
|
|
||||||
:return: (changed, group) -
|
|
||||||
changed: Boolean- whether a change was made,
|
|
||||||
group: A clc group object for the group
|
|
||||||
"""
|
|
||||||
assert self.root_group, "Implementation Error: Root Group not set"
|
|
||||||
parent = parent_name if parent_name is not None else self.root_group.name
|
|
||||||
description = group_description
|
|
||||||
changed = False
|
|
||||||
results = []
|
|
||||||
groups = []
|
|
||||||
group = group_name
|
|
||||||
|
|
||||||
parent_exists = self._group_exists(group_name=parent, parent_name=None)
|
|
||||||
child_exists = self._group_exists(group_name=group_name, parent_name=parent)
|
|
||||||
|
|
||||||
if parent_exists and child_exists:
|
|
||||||
group, parent = self.group_dict[group_name]
|
|
||||||
changed = False
|
|
||||||
elif parent_exists and not child_exists:
|
|
||||||
if not self.module.check_mode:
|
|
||||||
groups.append(group_name)
|
|
||||||
for g in groups:
|
|
||||||
group = self._create_group(
|
|
||||||
group=group,
|
|
||||||
parent=parent,
|
|
||||||
description=description)
|
|
||||||
results.append(group)
|
|
||||||
changed = True
|
|
||||||
else:
|
|
||||||
self.module.fail_json(
|
|
||||||
msg="parent group: " +
|
|
||||||
parent +
|
|
||||||
" does not exist")
|
|
||||||
|
|
||||||
return changed, group, results
|
|
||||||
|
|
||||||
def _create_group(self, group, parent, description):
|
|
||||||
"""
|
|
||||||
Create the provided server group
|
|
||||||
:param group: clc_sdk.Group - the group to create
|
|
||||||
:param parent: clc_sdk.Parent - the parent group for {group}
|
|
||||||
:param description: string - a text description of the group
|
|
||||||
:return: clc_sdk.Group - the created group
|
|
||||||
"""
|
|
||||||
|
|
||||||
(parent, grandparent) = self.group_dict[parent]
|
|
||||||
return parent.Create(name=group, description=description)
|
|
||||||
|
|
||||||
#
|
|
||||||
# Utility Functions
|
|
||||||
#
|
|
||||||
|
|
||||||
def _group_exists(self, group_name, parent_name):
|
|
||||||
"""
|
|
||||||
Check to see if a group exists
|
|
||||||
:param group_name: string - the group to check
|
|
||||||
:param parent_name: string - the parent of group_name
|
|
||||||
:return: boolean - whether the group exists
|
|
||||||
"""
|
|
||||||
result = False
|
|
||||||
if group_name in self.group_dict:
|
|
||||||
(group, parent) = self.group_dict[group_name]
|
|
||||||
if parent_name is None or parent_name == parent.name:
|
|
||||||
result = True
|
|
||||||
return result
|
|
||||||
|
|
||||||
def _get_group_tree_for_datacenter(self, datacenter=None, alias=None):
|
|
||||||
"""
|
|
||||||
Walk the tree of groups for a datacenter
|
|
||||||
:param datacenter: string - the datacenter to walk (ex: 'UC1')
|
|
||||||
:param alias: string - the account alias to search. Defaults to the current user's account
|
|
||||||
:return: a dictionary of groups and parents
|
|
||||||
"""
|
|
||||||
self.root_group = self.clc.v2.Datacenter(
|
|
||||||
location=datacenter).RootGroup()
|
|
||||||
return self._walk_groups_recursive(
|
|
||||||
parent_group=None,
|
|
||||||
child_group=self.root_group)
|
|
||||||
|
|
||||||
def _walk_groups_recursive(self, parent_group, child_group):
|
|
||||||
"""
|
|
||||||
Walk a parent-child tree of groups, starting with the provided child group
|
|
||||||
:param parent_group: clc_sdk.Group - the parent group to start the walk
|
|
||||||
:param child_group: clc_sdk.Group - the child group to start the walk
|
|
||||||
:return: a dictionary of groups and parents
|
|
||||||
"""
|
|
||||||
result = {str(child_group): (child_group, parent_group)}
|
|
||||||
groups = child_group.Subgroups().groups
|
|
||||||
if len(groups) > 0:
|
|
||||||
for group in groups:
|
|
||||||
if group.type != 'default':
|
|
||||||
continue
|
|
||||||
|
|
||||||
result.update(self._walk_groups_recursive(child_group, group))
|
|
||||||
return result
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _set_user_agent(clc):
|
|
||||||
if hasattr(clc, 'SetRequestsSession'):
|
|
||||||
agent_string = "ClcAnsibleModule/" + __version__
|
|
||||||
ses = requests.Session()
|
|
||||||
ses.headers.update({"Api-Client": agent_string})
|
|
||||||
ses.headers['User-Agent'] += " " + agent_string
|
|
||||||
clc.SetRequestsSession(ses)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""
|
|
||||||
The main function. Instantiates the module and calls process_request.
|
|
||||||
:return: none
|
|
||||||
"""
|
|
||||||
module = AnsibleModule(argument_spec=ClcGroup._define_module_argument_spec(), supports_check_mode=True)
|
|
||||||
|
|
||||||
clc_group = ClcGroup(module)
|
|
||||||
clc_group.process_request()
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import * # pylint: disable=W0614
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,759 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
|
|
||||||
# CenturyLink Cloud Ansible Modules.
|
|
||||||
#
|
|
||||||
# These Ansible modules enable the CenturyLink Cloud v2 API to be called
|
|
||||||
# from an within Ansible Playbook.
|
|
||||||
#
|
|
||||||
# This file is part of CenturyLink Cloud, and is maintained
|
|
||||||
# by the Workflow as a Service Team
|
|
||||||
#
|
|
||||||
# Copyright 2015 CenturyLink Cloud
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
#
|
|
||||||
# CenturyLink Cloud: http://www.CenturyLinkCloud.com
|
|
||||||
# API Documentation: https://www.centurylinkcloud.com/api-docs/v2/
|
|
||||||
#
|
|
||||||
|
|
||||||
DOCUMENTATION = '''
|
|
||||||
module:
|
|
||||||
short_desciption: Create, Delete shared loadbalancers in CenturyLink Cloud.
|
|
||||||
description:
|
|
||||||
- An Ansible module to Create, Delete shared loadbalancers in CenturyLink Cloud.
|
|
||||||
options:
|
|
||||||
options:
|
|
||||||
name:
|
|
||||||
description:
|
|
||||||
- The name of the loadbalancer
|
|
||||||
required: True
|
|
||||||
description:
|
|
||||||
description:
|
|
||||||
- A description for your loadbalancer
|
|
||||||
alias:
|
|
||||||
description:
|
|
||||||
- The alias of your CLC Account
|
|
||||||
required: True
|
|
||||||
location:
|
|
||||||
description:
|
|
||||||
- The location of the datacenter your load balancer resides in
|
|
||||||
required: True
|
|
||||||
method:
|
|
||||||
description:
|
|
||||||
-The balancing method for this pool
|
|
||||||
default: roundRobin
|
|
||||||
choices: ['sticky', 'roundRobin']
|
|
||||||
persistence:
|
|
||||||
description:
|
|
||||||
- The persistence method for this load balancer
|
|
||||||
default: standard
|
|
||||||
choices: ['standard', 'sticky']
|
|
||||||
port:
|
|
||||||
description:
|
|
||||||
- Port to configure on the public-facing side of the load balancer pool
|
|
||||||
choices: [80, 443]
|
|
||||||
nodes:
|
|
||||||
description:
|
|
||||||
- A list of nodes that you want added to your load balancer pool
|
|
||||||
status:
|
|
||||||
description:
|
|
||||||
- The status of your loadbalancer
|
|
||||||
default: enabled
|
|
||||||
choices: ['enabled', 'disabled']
|
|
||||||
state:
|
|
||||||
description:
|
|
||||||
- Whether to create or delete the load balancer pool
|
|
||||||
default: present
|
|
||||||
choices: ['present', 'absent', 'port_absent', 'nodes_present', 'nodes_absent']
|
|
||||||
'''
|
|
||||||
|
|
||||||
EXAMPLES = '''
|
|
||||||
# Note - You must set the CLC_V2_API_USERNAME And CLC_V2_API_PASSWD Environment variables before running these examples
|
|
||||||
- name: Create Loadbalancer
|
|
||||||
hosts: localhost
|
|
||||||
connection: local
|
|
||||||
tasks:
|
|
||||||
- name: Actually Create things
|
|
||||||
clc_loadbalancer:
|
|
||||||
name: test
|
|
||||||
description: test
|
|
||||||
alias: TEST
|
|
||||||
location: WA1
|
|
||||||
port: 443
|
|
||||||
nodes:
|
|
||||||
- { 'ipAddress': '10.11.22.123', 'privatePort': 80 }
|
|
||||||
state: present
|
|
||||||
|
|
||||||
- name: Add node to an existing loadbalancer pool
|
|
||||||
hosts: localhost
|
|
||||||
connection: local
|
|
||||||
tasks:
|
|
||||||
- name: Actually Create things
|
|
||||||
clc_loadbalancer:
|
|
||||||
name: test
|
|
||||||
description: test
|
|
||||||
alias: TEST
|
|
||||||
location: WA1
|
|
||||||
port: 443
|
|
||||||
nodes:
|
|
||||||
- { 'ipAddress': '10.11.22.234', 'privatePort': 80 }
|
|
||||||
state: nodes_present
|
|
||||||
|
|
||||||
- name: Remove node from an existing loadbalancer pool
|
|
||||||
hosts: localhost
|
|
||||||
connection: local
|
|
||||||
tasks:
|
|
||||||
- name: Actually Create things
|
|
||||||
clc_loadbalancer:
|
|
||||||
name: test
|
|
||||||
description: test
|
|
||||||
alias: TEST
|
|
||||||
location: WA1
|
|
||||||
port: 443
|
|
||||||
nodes:
|
|
||||||
- { 'ipAddress': '10.11.22.234', 'privatePort': 80 }
|
|
||||||
state: nodes_absent
|
|
||||||
|
|
||||||
- name: Delete LoadbalancerPool
|
|
||||||
hosts: localhost
|
|
||||||
connection: local
|
|
||||||
tasks:
|
|
||||||
- name: Actually Delete things
|
|
||||||
clc_loadbalancer:
|
|
||||||
name: test
|
|
||||||
description: test
|
|
||||||
alias: TEST
|
|
||||||
location: WA1
|
|
||||||
port: 443
|
|
||||||
nodes:
|
|
||||||
- { 'ipAddress': '10.11.22.123', 'privatePort': 80 }
|
|
||||||
state: port_absent
|
|
||||||
|
|
||||||
- name: Delete Loadbalancer
|
|
||||||
hosts: localhost
|
|
||||||
connection: local
|
|
||||||
tasks:
|
|
||||||
- name: Actually Delete things
|
|
||||||
clc_loadbalancer:
|
|
||||||
name: test
|
|
||||||
description: test
|
|
||||||
alias: TEST
|
|
||||||
location: WA1
|
|
||||||
port: 443
|
|
||||||
nodes:
|
|
||||||
- { 'ipAddress': '10.11.22.123', 'privatePort': 80 }
|
|
||||||
state: absent
|
|
||||||
|
|
||||||
'''
|
|
||||||
|
|
||||||
__version__ = '${version}'
|
|
||||||
|
|
||||||
import requests
|
|
||||||
from time import sleep
|
|
||||||
|
|
||||||
#
|
|
||||||
# Requires the clc-python-sdk.
|
|
||||||
# sudo pip install clc-sdk
|
|
||||||
#
|
|
||||||
try:
|
|
||||||
import clc as clc_sdk
|
|
||||||
from clc import CLCException
|
|
||||||
except ImportError:
|
|
||||||
CLC_FOUND = False
|
|
||||||
clc_sdk = None
|
|
||||||
else:
|
|
||||||
CLC_FOUND = True
|
|
||||||
|
|
||||||
class ClcLoadBalancer():
|
|
||||||
|
|
||||||
clc = None
|
|
||||||
|
|
||||||
def __init__(self, module):
|
|
||||||
"""
|
|
||||||
Construct module
|
|
||||||
"""
|
|
||||||
self.clc = clc_sdk
|
|
||||||
self.module = module
|
|
||||||
self.lb_dict = {}
|
|
||||||
|
|
||||||
if not CLC_FOUND:
|
|
||||||
self.module.fail_json(
|
|
||||||
msg='clc-python-sdk required for this module')
|
|
||||||
|
|
||||||
self._set_user_agent(self.clc)
|
|
||||||
|
|
||||||
def process_request(self):
|
|
||||||
"""
|
|
||||||
Execute the main code path, and handle the request
|
|
||||||
:return: none
|
|
||||||
"""
|
|
||||||
|
|
||||||
loadbalancer_name=self.module.params.get('name')
|
|
||||||
loadbalancer_alias=self.module.params.get('alias')
|
|
||||||
loadbalancer_location=self.module.params.get('location')
|
|
||||||
loadbalancer_description=self.module.params.get('description')
|
|
||||||
loadbalancer_port=self.module.params.get('port')
|
|
||||||
loadbalancer_method=self.module.params.get('method')
|
|
||||||
loadbalancer_persistence=self.module.params.get('persistence')
|
|
||||||
loadbalancer_nodes=self.module.params.get('nodes')
|
|
||||||
loadbalancer_status=self.module.params.get('status')
|
|
||||||
state=self.module.params.get('state')
|
|
||||||
|
|
||||||
if loadbalancer_description == None:
|
|
||||||
loadbalancer_description = loadbalancer_name
|
|
||||||
|
|
||||||
self._set_clc_credentials_from_env()
|
|
||||||
|
|
||||||
self.lb_dict = self._get_loadbalancer_list(alias=loadbalancer_alias, location=loadbalancer_location)
|
|
||||||
|
|
||||||
if state == 'present':
|
|
||||||
changed, result_lb, lb_id = self.ensure_loadbalancer_present(name=loadbalancer_name,
|
|
||||||
alias=loadbalancer_alias,
|
|
||||||
location=loadbalancer_location,
|
|
||||||
description=loadbalancer_description,
|
|
||||||
status=loadbalancer_status)
|
|
||||||
if loadbalancer_port:
|
|
||||||
changed, result_pool, pool_id = self.ensure_loadbalancerpool_present(lb_id=lb_id,
|
|
||||||
alias=loadbalancer_alias,
|
|
||||||
location=loadbalancer_location,
|
|
||||||
method=loadbalancer_method,
|
|
||||||
persistence=loadbalancer_persistence,
|
|
||||||
port=loadbalancer_port)
|
|
||||||
|
|
||||||
if loadbalancer_nodes:
|
|
||||||
changed, result_nodes = self.ensure_lbpool_nodes_set(alias=loadbalancer_alias,
|
|
||||||
location=loadbalancer_location,
|
|
||||||
name=loadbalancer_name,
|
|
||||||
port=loadbalancer_port,
|
|
||||||
nodes=loadbalancer_nodes
|
|
||||||
)
|
|
||||||
elif state == 'absent':
|
|
||||||
changed, result_lb = self.ensure_loadbalancer_absent(name=loadbalancer_name,
|
|
||||||
alias=loadbalancer_alias,
|
|
||||||
location=loadbalancer_location)
|
|
||||||
|
|
||||||
elif state == 'port_absent':
|
|
||||||
changed, result_lb = self.ensure_loadbalancerpool_absent(alias=loadbalancer_alias,
|
|
||||||
location=loadbalancer_location,
|
|
||||||
name=loadbalancer_name,
|
|
||||||
port=loadbalancer_port)
|
|
||||||
|
|
||||||
elif state == 'nodes_present':
|
|
||||||
changed, result_lb = self.ensure_lbpool_nodes_present(alias=loadbalancer_alias,
|
|
||||||
location=loadbalancer_location,
|
|
||||||
name=loadbalancer_name,
|
|
||||||
port=loadbalancer_port,
|
|
||||||
nodes=loadbalancer_nodes)
|
|
||||||
|
|
||||||
elif state == 'nodes_absent':
|
|
||||||
changed, result_lb = self.ensure_lbpool_nodes_absent(alias=loadbalancer_alias,
|
|
||||||
location=loadbalancer_location,
|
|
||||||
name=loadbalancer_name,
|
|
||||||
port=loadbalancer_port,
|
|
||||||
nodes=loadbalancer_nodes)
|
|
||||||
|
|
||||||
self.module.exit_json(changed=changed, loadbalancer=result_lb)
|
|
||||||
#
|
|
||||||
# Functions to define the Ansible module and its arguments
|
|
||||||
#
|
|
||||||
def ensure_loadbalancer_present(self,name,alias,location,description,status):
|
|
||||||
"""
|
|
||||||
Check for loadbalancer presence (available)
|
|
||||||
:param name: Name of loadbalancer
|
|
||||||
:param alias: Alias of account
|
|
||||||
:param location: Datacenter
|
|
||||||
:param description: Description of loadbalancer
|
|
||||||
:param status: Enabled / Disabled
|
|
||||||
:return: True / False
|
|
||||||
"""
|
|
||||||
changed = False
|
|
||||||
result = None
|
|
||||||
lb_id = self._loadbalancer_exists(name=name)
|
|
||||||
if lb_id:
|
|
||||||
result = name
|
|
||||||
changed = False
|
|
||||||
else:
|
|
||||||
if not self.module.check_mode:
|
|
||||||
result = self.create_loadbalancer(name=name,
|
|
||||||
alias=alias,
|
|
||||||
location=location,
|
|
||||||
description=description,
|
|
||||||
status=status)
|
|
||||||
lb_id = result.get('id')
|
|
||||||
changed = True
|
|
||||||
|
|
||||||
return changed, result, lb_id
|
|
||||||
|
|
||||||
def ensure_loadbalancerpool_present(self, lb_id, alias, location, method, persistence, port):
|
|
||||||
"""
|
|
||||||
Checks to see if a load balancer pool exists and creates one if it does not.
|
|
||||||
:param name: The loadbalancer name
|
|
||||||
:param alias: The account alias
|
|
||||||
:param location: the datacenter the load balancer resides in
|
|
||||||
:param method: the load balancing method
|
|
||||||
:param persistence: the load balancing persistence type
|
|
||||||
:param port: the port that the load balancer will listen on
|
|
||||||
:return: (changed, group, pool_id) -
|
|
||||||
changed: Boolean whether a change was made
|
|
||||||
result: The result from the CLC API call
|
|
||||||
pool_id: The string id of the pool
|
|
||||||
"""
|
|
||||||
changed = False
|
|
||||||
result = None
|
|
||||||
if not lb_id:
|
|
||||||
return False, None, None
|
|
||||||
pool_id = self._loadbalancerpool_exists(alias=alias, location=location, port=port, lb_id=lb_id)
|
|
||||||
if not pool_id:
|
|
||||||
changed = True
|
|
||||||
if not self.module.check_mode:
|
|
||||||
result = self.create_loadbalancerpool(alias=alias, location=location, lb_id=lb_id, method=method, persistence=persistence, port=port)
|
|
||||||
pool_id = result.get('id')
|
|
||||||
|
|
||||||
else:
|
|
||||||
changed = False
|
|
||||||
result = port
|
|
||||||
|
|
||||||
return changed, result, pool_id
|
|
||||||
|
|
||||||
def ensure_loadbalancer_absent(self,name,alias,location):
|
|
||||||
"""
|
|
||||||
Check for loadbalancer presence (not available)
|
|
||||||
:param name: Name of loadbalancer
|
|
||||||
:param alias: Alias of account
|
|
||||||
:param location: Datacenter
|
|
||||||
:return: (changed, result)
|
|
||||||
changed: Boolean whether a change was made
|
|
||||||
result: The result from the CLC API Call
|
|
||||||
"""
|
|
||||||
changed = False
|
|
||||||
result = None
|
|
||||||
lb_exists = self._loadbalancer_exists(name=name)
|
|
||||||
if lb_exists:
|
|
||||||
if not self.module.check_mode:
|
|
||||||
result = self.delete_loadbalancer(alias=alias,
|
|
||||||
location=location,
|
|
||||||
name=name)
|
|
||||||
changed = True
|
|
||||||
else:
|
|
||||||
result = name
|
|
||||||
changed = False
|
|
||||||
return changed, result
|
|
||||||
|
|
||||||
def ensure_loadbalancerpool_absent(self, alias, location, name, port):
|
|
||||||
"""
|
|
||||||
Checks to see if a load balancer pool exists and deletes it if it does
|
|
||||||
:param alias: The account alias
|
|
||||||
:param location: the datacenter the load balancer resides in
|
|
||||||
:param loadbalancer: the name of the load balancer
|
|
||||||
:param port: the port that the load balancer will listen on
|
|
||||||
:return: (changed, group) -
|
|
||||||
changed: Boolean whether a change was made
|
|
||||||
result: The result from the CLC API call
|
|
||||||
"""
|
|
||||||
changed = False
|
|
||||||
result = None
|
|
||||||
lb_exists = self._loadbalancer_exists(name=name)
|
|
||||||
if lb_exists:
|
|
||||||
lb_id = self._get_loadbalancer_id(name=name)
|
|
||||||
pool_id = self._loadbalancerpool_exists(alias=alias, location=location, port=port, lb_id=lb_id)
|
|
||||||
if pool_id:
|
|
||||||
changed = True
|
|
||||||
if not self.module.check_mode:
|
|
||||||
result = self.delete_loadbalancerpool(alias=alias, location=location, lb_id=lb_id, pool_id=pool_id)
|
|
||||||
else:
|
|
||||||
changed = False
|
|
||||||
result = "Pool doesn't exist"
|
|
||||||
else:
|
|
||||||
result = "LB Doesn't Exist"
|
|
||||||
return changed, result
|
|
||||||
|
|
||||||
def ensure_lbpool_nodes_set(self, alias, location, name, port, nodes):
|
|
||||||
"""
|
|
||||||
Checks to see if the provided list of nodes exist for the pool and set the nodes if any in the list doesn't exist
|
|
||||||
:param alias: The account alias
|
|
||||||
:param location: the datacenter the load balancer resides in
|
|
||||||
:param name: the name of the load balancer
|
|
||||||
:param port: the port that the load balancer will listen on
|
|
||||||
:param nodes: The list of nodes to be updated to the pool
|
|
||||||
:return: (changed, group) -
|
|
||||||
changed: Boolean whether a change was made
|
|
||||||
result: The result from the CLC API call
|
|
||||||
"""
|
|
||||||
result = {}
|
|
||||||
changed = False
|
|
||||||
lb_exists = self._loadbalancer_exists(name=name)
|
|
||||||
if lb_exists:
|
|
||||||
lb_id = self._get_loadbalancer_id(name=name)
|
|
||||||
pool_id = self._loadbalancerpool_exists(alias=alias, location=location, port=port, lb_id=lb_id)
|
|
||||||
if pool_id:
|
|
||||||
nodes_exist = self._loadbalancerpool_nodes_exists(alias=alias,
|
|
||||||
location=location,
|
|
||||||
port=port,
|
|
||||||
lb_id=lb_id,
|
|
||||||
pool_id=pool_id,
|
|
||||||
nodes_to_check=nodes)
|
|
||||||
if not nodes_exist:
|
|
||||||
changed = True
|
|
||||||
result = self.set_loadbalancernodes(alias=alias,
|
|
||||||
location=location,
|
|
||||||
lb_id=lb_id,
|
|
||||||
pool_id=pool_id,
|
|
||||||
nodes=nodes)
|
|
||||||
else:
|
|
||||||
result = "Pool doesn't exist"
|
|
||||||
else:
|
|
||||||
result = "Load balancer doesn't Exist"
|
|
||||||
return changed, result
|
|
||||||
|
|
||||||
def ensure_lbpool_nodes_present(self, alias, location, name, port, nodes):
|
|
||||||
"""
|
|
||||||
Checks to see if the provided list of nodes exist for the pool and add the missing nodes to the pool
|
|
||||||
:param alias: The account alias
|
|
||||||
:param location: the datacenter the load balancer resides in
|
|
||||||
:param name: the name of the load balancer
|
|
||||||
:param port: the port that the load balancer will listen on
|
|
||||||
:param nodes: the list of nodes to be added
|
|
||||||
:return: (changed, group) -
|
|
||||||
changed: Boolean whether a change was made
|
|
||||||
result: The result from the CLC API call
|
|
||||||
"""
|
|
||||||
changed = False
|
|
||||||
lb_exists = self._loadbalancer_exists(name=name)
|
|
||||||
if lb_exists:
|
|
||||||
lb_id = self._get_loadbalancer_id(name=name)
|
|
||||||
pool_id = self._loadbalancerpool_exists(alias=alias, location=location, port=port, lb_id=lb_id)
|
|
||||||
if pool_id:
|
|
||||||
changed, result = self.add_lbpool_nodes(alias=alias,
|
|
||||||
location=location,
|
|
||||||
lb_id=lb_id,
|
|
||||||
pool_id=pool_id,
|
|
||||||
nodes_to_add=nodes)
|
|
||||||
else:
|
|
||||||
result = "Pool doesn't exist"
|
|
||||||
else:
|
|
||||||
result = "Load balancer doesn't Exist"
|
|
||||||
return changed, result
|
|
||||||
|
|
||||||
def ensure_lbpool_nodes_absent(self, alias, location, name, port, nodes):
|
|
||||||
"""
|
|
||||||
Checks to see if the provided list of nodes exist for the pool and add the missing nodes to the pool
|
|
||||||
:param alias: The account alias
|
|
||||||
:param location: the datacenter the load balancer resides in
|
|
||||||
:param name: the name of the load balancer
|
|
||||||
:param port: the port that the load balancer will listen on
|
|
||||||
:param nodes: the list of nodes to be removed
|
|
||||||
:return: (changed, group) -
|
|
||||||
changed: Boolean whether a change was made
|
|
||||||
result: The result from the CLC API call
|
|
||||||
"""
|
|
||||||
changed = False
|
|
||||||
lb_exists = self._loadbalancer_exists(name=name)
|
|
||||||
if lb_exists:
|
|
||||||
lb_id = self._get_loadbalancer_id(name=name)
|
|
||||||
pool_id = self._loadbalancerpool_exists(alias=alias, location=location, port=port, lb_id=lb_id)
|
|
||||||
if pool_id:
|
|
||||||
changed, result = self.remove_lbpool_nodes(alias=alias,
|
|
||||||
location=location,
|
|
||||||
lb_id=lb_id,
|
|
||||||
pool_id=pool_id,
|
|
||||||
nodes_to_remove=nodes)
|
|
||||||
else:
|
|
||||||
result = "Pool doesn't exist"
|
|
||||||
else:
|
|
||||||
result = "Load balancer doesn't Exist"
|
|
||||||
return changed, result
|
|
||||||
|
|
||||||
def create_loadbalancer(self,name,alias,location,description,status):
|
|
||||||
"""
|
|
||||||
Create a loadbalancer w/ params
|
|
||||||
:param name: Name of loadbalancer
|
|
||||||
:param alias: Alias of account
|
|
||||||
:param location: Datacenter
|
|
||||||
:param description: Description for loadbalancer to be created
|
|
||||||
:param status: Enabled / Disabled
|
|
||||||
:return: Success / Failure
|
|
||||||
"""
|
|
||||||
result = self.clc.v2.API.Call('POST', '/v2/sharedLoadBalancers/%s/%s' % (alias, location), json.dumps({"name":name,"description":description,"status":status}))
|
|
||||||
sleep(1)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def create_loadbalancerpool(self, alias, location, lb_id, method, persistence, port):
|
|
||||||
"""
|
|
||||||
Creates a pool on the provided load balancer
|
|
||||||
:param alias: the account alias
|
|
||||||
:param location: the datacenter the load balancer resides in
|
|
||||||
:param lb_id: the id string of the load balancer
|
|
||||||
:param method: the load balancing method
|
|
||||||
:param persistence: the load balancing persistence type
|
|
||||||
:param port: the port that the load balancer will listen on
|
|
||||||
:return: result: The result from the create API call
|
|
||||||
"""
|
|
||||||
result = self.clc.v2.API.Call('POST', '/v2/sharedLoadBalancers/%s/%s/%s/pools' % (alias, location, lb_id), json.dumps({"port":port, "method":method, "persistence":persistence}))
|
|
||||||
return result
|
|
||||||
|
|
||||||
def delete_loadbalancer(self,alias,location,name):
|
|
||||||
"""
|
|
||||||
Delete CLC loadbalancer
|
|
||||||
:param alias: Alias for account
|
|
||||||
:param location: Datacenter
|
|
||||||
:param name: Name of the loadbalancer to delete
|
|
||||||
:return: 204 if successful else failure
|
|
||||||
"""
|
|
||||||
lb_id = self._get_loadbalancer_id(name=name)
|
|
||||||
result = self.clc.v2.API.Call('DELETE', '/v2/sharedLoadBalancers/%s/%s/%s' % (alias, location, lb_id))
|
|
||||||
return result
|
|
||||||
|
|
||||||
def delete_loadbalancerpool(self, alias, location, lb_id, pool_id):
|
|
||||||
"""
|
|
||||||
Delete a pool on the provided load balancer
|
|
||||||
:param alias: The account alias
|
|
||||||
:param location: the datacenter the load balancer resides in
|
|
||||||
:param lb_id: the id string of the load balancer
|
|
||||||
:param pool_id: the id string of the pool
|
|
||||||
:return: result: The result from the delete API call
|
|
||||||
"""
|
|
||||||
result = self.clc.v2.API.Call('DELETE', '/v2/sharedLoadBalancers/%s/%s/%s/pools/%s' % (alias, location, lb_id, pool_id))
|
|
||||||
return result
|
|
||||||
|
|
||||||
def _get_loadbalancer_id(self, name):
|
|
||||||
"""
|
|
||||||
Retrieve unique ID of loadbalancer
|
|
||||||
:param name: Name of loadbalancer
|
|
||||||
:return: Unique ID of loadbalancer
|
|
||||||
"""
|
|
||||||
for lb in self.lb_dict:
|
|
||||||
if lb.get('name') == name:
|
|
||||||
id = lb.get('id')
|
|
||||||
return id
|
|
||||||
|
|
||||||
def _get_loadbalancer_list(self, alias, location):
|
|
||||||
"""
|
|
||||||
Retrieve a list of loadbalancers
|
|
||||||
:param alias: Alias for account
|
|
||||||
:param location: Datacenter
|
|
||||||
:return: JSON data for all loadbalancers at datacenter
|
|
||||||
"""
|
|
||||||
return self.clc.v2.API.Call('GET', '/v2/sharedLoadBalancers/%s/%s' % (alias, location))
|
|
||||||
|
|
||||||
def _loadbalancer_exists(self, name):
|
|
||||||
"""
|
|
||||||
Verify a loadbalancer exists
|
|
||||||
:param name: Name of loadbalancer
|
|
||||||
:return: False or the ID of the existing loadbalancer
|
|
||||||
"""
|
|
||||||
result = False
|
|
||||||
|
|
||||||
for lb in self.lb_dict:
|
|
||||||
if lb.get('name') == name:
|
|
||||||
result = lb.get('id')
|
|
||||||
return result
|
|
||||||
|
|
||||||
def _loadbalancerpool_exists(self, alias, location, port, lb_id):
|
|
||||||
"""
|
|
||||||
Checks to see if a pool exists on the specified port on the provided load balancer
|
|
||||||
:param alias: the account alias
|
|
||||||
:param location: the datacenter the load balancer resides in
|
|
||||||
:param port: the port to check and see if it exists
|
|
||||||
:param lb_id: the id string of the provided load balancer
|
|
||||||
:return: result: The id string of the pool or False
|
|
||||||
"""
|
|
||||||
result = False
|
|
||||||
pool_list = self.clc.v2.API.Call('GET', '/v2/sharedLoadBalancers/%s/%s/%s/pools' % (alias, location, lb_id))
|
|
||||||
for pool in pool_list:
|
|
||||||
if int(pool.get('port')) == int(port):
|
|
||||||
result = pool.get('id')
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
def _loadbalancerpool_nodes_exists(self, alias, location, port, lb_id, pool_id, nodes_to_check):
|
|
||||||
"""
|
|
||||||
Checks to see if a set of nodes exists on the specified port on the provided load balancer
|
|
||||||
:param alias: the account alias
|
|
||||||
:param location: the datacenter the load balancer resides in
|
|
||||||
:param port: the port to check and see if it exists
|
|
||||||
:param lb_id: the id string of the provided load balancer
|
|
||||||
:param pool_id: the id string of the load balancer pool
|
|
||||||
:param nodes_to_check: the list of nodes to check for
|
|
||||||
:return: result: The id string of the pool or False
|
|
||||||
"""
|
|
||||||
result = False
|
|
||||||
nodes = self._get_lbpool_nodes(alias, location, lb_id, pool_id)
|
|
||||||
for node in nodes_to_check:
|
|
||||||
if not node.get('status'):
|
|
||||||
node['status'] = 'enabled'
|
|
||||||
if node in nodes:
|
|
||||||
result = True
|
|
||||||
else:
|
|
||||||
result = False
|
|
||||||
return result
|
|
||||||
|
|
||||||
def set_loadbalancernodes(self, alias, location, lb_id, pool_id, nodes):
|
|
||||||
"""
|
|
||||||
Updates nodes to the provided pool
|
|
||||||
:param alias: the account alias
|
|
||||||
:param location: the datacenter the load balancer resides in
|
|
||||||
:param lb_id: the id string of the load balancer
|
|
||||||
:param pool_id: the id string of the pool
|
|
||||||
:param nodes: a list of dictionaries containing the nodes to set
|
|
||||||
:return: result: The result from the API call
|
|
||||||
"""
|
|
||||||
result = None
|
|
||||||
if not lb_id:
|
|
||||||
return result
|
|
||||||
if not self.module.check_mode:
|
|
||||||
result = self.clc.v2.API.Call('PUT',
|
|
||||||
'/v2/sharedLoadBalancers/%s/%s/%s/pools/%s/nodes'
|
|
||||||
% (alias, location, lb_id, pool_id), json.dumps(nodes))
|
|
||||||
return result
|
|
||||||
|
|
||||||
def add_lbpool_nodes(self, alias, location, lb_id, pool_id, nodes_to_add):
|
|
||||||
"""
|
|
||||||
Add nodes to the provided pool
|
|
||||||
:param alias: the account alias
|
|
||||||
:param location: the datacenter the load balancer resides in
|
|
||||||
:param lb_id: the id string of the load balancer
|
|
||||||
:param pool_id: the id string of the pool
|
|
||||||
:param nodes: a list of dictionaries containing the nodes to add
|
|
||||||
:return: (changed, group) -
|
|
||||||
changed: Boolean whether a change was made
|
|
||||||
result: The result from the CLC API call
|
|
||||||
"""
|
|
||||||
changed = False
|
|
||||||
result = {}
|
|
||||||
nodes = self._get_lbpool_nodes(alias, location, lb_id, pool_id)
|
|
||||||
for node in nodes_to_add:
|
|
||||||
if not node.get('status'):
|
|
||||||
node['status'] = 'enabled'
|
|
||||||
if not node in nodes:
|
|
||||||
changed = True
|
|
||||||
nodes.append(node)
|
|
||||||
if changed == True and not self.module.check_mode:
|
|
||||||
result = self.set_loadbalancernodes(alias, location, lb_id, pool_id, nodes)
|
|
||||||
return changed, result
|
|
||||||
|
|
||||||
def remove_lbpool_nodes(self, alias, location, lb_id, pool_id, nodes_to_remove):
|
|
||||||
"""
|
|
||||||
Removes nodes from the provided pool
|
|
||||||
:param alias: the account alias
|
|
||||||
:param location: the datacenter the load balancer resides in
|
|
||||||
:param lb_id: the id string of the load balancer
|
|
||||||
:param pool_id: the id string of the pool
|
|
||||||
:param nodes: a list of dictionaries containing the nodes to remove
|
|
||||||
:return: (changed, group) -
|
|
||||||
changed: Boolean whether a change was made
|
|
||||||
result: The result from the CLC API call
|
|
||||||
"""
|
|
||||||
changed = False
|
|
||||||
result = {}
|
|
||||||
nodes = self._get_lbpool_nodes(alias, location, lb_id, pool_id)
|
|
||||||
for node in nodes_to_remove:
|
|
||||||
if not node.get('status'):
|
|
||||||
node['status'] = 'enabled'
|
|
||||||
if node in nodes:
|
|
||||||
changed = True
|
|
||||||
nodes.remove(node)
|
|
||||||
if changed == True and not self.module.check_mode:
|
|
||||||
result = self.set_loadbalancernodes(alias, location, lb_id, pool_id, nodes)
|
|
||||||
return changed, result
|
|
||||||
|
|
||||||
def _get_lbpool_nodes(self, alias, location, lb_id, pool_id):
|
|
||||||
"""
|
|
||||||
Return the list of nodes available to the provided load balancer pool
|
|
||||||
:param alias: the account alias
|
|
||||||
:param location: the datacenter the load balancer resides in
|
|
||||||
:param lb_id: the id string of the load balancer
|
|
||||||
:param pool_id: the id string of the pool
|
|
||||||
:return: result: The list of nodes
|
|
||||||
"""
|
|
||||||
result = self.clc.v2.API.Call('GET',
|
|
||||||
'/v2/sharedLoadBalancers/%s/%s/%s/pools/%s/nodes'
|
|
||||||
% (alias, location, lb_id, pool_id))
|
|
||||||
return result
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def define_argument_spec():
|
|
||||||
"""
|
|
||||||
Define the argument spec for the ansible module
|
|
||||||
:return: argument spec dictionary
|
|
||||||
"""
|
|
||||||
argument_spec = dict(
|
|
||||||
name=dict(required=True),
|
|
||||||
description=dict(default=None),
|
|
||||||
location=dict(required=True, default=None),
|
|
||||||
alias=dict(required=True, default=None),
|
|
||||||
port=dict(choices=[80, 443]),
|
|
||||||
method=dict(choices=['leastConnection', 'roundRobin']),
|
|
||||||
persistence=dict(choices=['standard', 'sticky']),
|
|
||||||
nodes=dict(type='list', default=[]),
|
|
||||||
status=dict(default='enabled', choices=['enabled', 'disabled']),
|
|
||||||
state=dict(default='present', choices=['present', 'absent', 'port_absent', 'nodes_present', 'nodes_absent']),
|
|
||||||
wait=dict(type='bool', default=True)
|
|
||||||
)
|
|
||||||
|
|
||||||
return argument_spec
|
|
||||||
|
|
||||||
#
|
|
||||||
# Module Behavior Functions
|
|
||||||
#
|
|
||||||
|
|
||||||
def _set_clc_credentials_from_env(self):
|
|
||||||
"""
|
|
||||||
Set the CLC Credentials on the sdk by reading environment variables
|
|
||||||
:return: none
|
|
||||||
"""
|
|
||||||
env = os.environ
|
|
||||||
v2_api_token = env.get('CLC_V2_API_TOKEN', False)
|
|
||||||
v2_api_username = env.get('CLC_V2_API_USERNAME', False)
|
|
||||||
v2_api_passwd = env.get('CLC_V2_API_PASSWD', False)
|
|
||||||
clc_alias = env.get('CLC_ACCT_ALIAS', False)
|
|
||||||
api_url = env.get('CLC_V2_API_URL', False)
|
|
||||||
|
|
||||||
if api_url:
|
|
||||||
self.clc.defaults.ENDPOINT_URL_V2 = api_url
|
|
||||||
|
|
||||||
if v2_api_token and clc_alias:
|
|
||||||
self.clc._LOGIN_TOKEN_V2 = v2_api_token
|
|
||||||
self.clc._V2_ENABLED = True
|
|
||||||
self.clc.ALIAS = clc_alias
|
|
||||||
elif v2_api_username and v2_api_passwd:
|
|
||||||
self.clc.v2.SetCredentials(
|
|
||||||
api_username=v2_api_username,
|
|
||||||
api_passwd=v2_api_passwd)
|
|
||||||
else:
|
|
||||||
return self.module.fail_json(
|
|
||||||
msg="You must set the CLC_V2_API_USERNAME and CLC_V2_API_PASSWD "
|
|
||||||
"environment variables")
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _set_user_agent(clc):
|
|
||||||
if hasattr(clc, 'SetRequestsSession'):
|
|
||||||
agent_string = "ClcAnsibleModule/" + __version__
|
|
||||||
ses = requests.Session()
|
|
||||||
ses.headers.update({"Api-Client": agent_string})
|
|
||||||
ses.headers['User-Agent'] += " " + agent_string
|
|
||||||
clc.SetRequestsSession(ses)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""
|
|
||||||
The main function. Instantiates the module and calls process_request.
|
|
||||||
:return: none
|
|
||||||
"""
|
|
||||||
module = AnsibleModule(argument_spec=ClcLoadBalancer.define_argument_spec(),
|
|
||||||
supports_check_mode=True)
|
|
||||||
clc_loadbalancer = ClcLoadBalancer(module)
|
|
||||||
clc_loadbalancer.process_request()
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import * # pylint: disable=W0614
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,710 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
|
|
||||||
# CenturyLink Cloud Ansible Modules.
|
|
||||||
#
|
|
||||||
# These Ansible modules enable the CenturyLink Cloud v2 API to be called
|
|
||||||
# from an within Ansible Playbook.
|
|
||||||
#
|
|
||||||
# This file is part of CenturyLink Cloud, and is maintained
|
|
||||||
# by the Workflow as a Service Team
|
|
||||||
#
|
|
||||||
# Copyright 2015 CenturyLink Cloud
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
#
|
|
||||||
# CenturyLink Cloud: http://www.CenturyLinkCloud.com
|
|
||||||
# API Documentation: https://www.centurylinkcloud.com/api-docs/v2/
|
|
||||||
#
|
|
||||||
|
|
||||||
DOCUMENTATION = '''
|
|
||||||
module: clc_modify_server
|
|
||||||
short_desciption: modify servers in CenturyLink Cloud.
|
|
||||||
description:
|
|
||||||
- An Ansible module to modify servers in CenturyLink Cloud.
|
|
||||||
options:
|
|
||||||
server_ids:
|
|
||||||
description:
|
|
||||||
- A list of server Ids to modify.
|
|
||||||
default: []
|
|
||||||
required: True
|
|
||||||
aliases: []
|
|
||||||
cpu:
|
|
||||||
description:
|
|
||||||
- How many CPUs to update on the server
|
|
||||||
default: None
|
|
||||||
required: False
|
|
||||||
aliases: []
|
|
||||||
memory:
|
|
||||||
description:
|
|
||||||
- Memory in GB.
|
|
||||||
default: None
|
|
||||||
required: False
|
|
||||||
aliases: []
|
|
||||||
anti_affinity_policy_id:
|
|
||||||
description:
|
|
||||||
- The anti affinity policy id to be set for a heperscale server.
|
|
||||||
This is mutually exclusive with 'anti_affinity_policy_name'
|
|
||||||
default: None
|
|
||||||
required: False
|
|
||||||
aliases: []
|
|
||||||
anti_affinity_policy_name:
|
|
||||||
description:
|
|
||||||
- The anti affinity policy name to be set for a heperscale server.
|
|
||||||
This is mutually exclusive with 'anti_affinity_policy_id'
|
|
||||||
default: None
|
|
||||||
required: False
|
|
||||||
aliases: []
|
|
||||||
alert_policy_id:
|
|
||||||
description:
|
|
||||||
- The alert policy id to be associated.
|
|
||||||
This is mutually exclusive with 'alert_policy_name'
|
|
||||||
default: None
|
|
||||||
required: False
|
|
||||||
aliases: []
|
|
||||||
alert_policy_name:
|
|
||||||
description:
|
|
||||||
- The alert policy name to be associated.
|
|
||||||
This is mutually exclusive with 'alert_policy_id'
|
|
||||||
default: None
|
|
||||||
required: False
|
|
||||||
aliases: []
|
|
||||||
state:
|
|
||||||
description:
|
|
||||||
- The state to insure that the provided resources are in.
|
|
||||||
default: 'present'
|
|
||||||
required: False
|
|
||||||
choices: ['present', 'absent']
|
|
||||||
aliases: []
|
|
||||||
wait:
|
|
||||||
description:
|
|
||||||
- Whether to wait for the provisioning tasks to finish before returning.
|
|
||||||
default: True
|
|
||||||
required: False
|
|
||||||
choices: [ True, False]
|
|
||||||
aliases: []
|
|
||||||
'''
|
|
||||||
|
|
||||||
EXAMPLES = '''
|
|
||||||
# Note - You must set the CLC_V2_API_USERNAME And CLC_V2_API_PASSWD Environment variables before running these examples
|
|
||||||
|
|
||||||
- name: set the cpu count to 4 on a server
|
|
||||||
clc_server:
|
|
||||||
server_ids: ['UC1ACCTTEST01']
|
|
||||||
cpu: 4
|
|
||||||
state: present
|
|
||||||
|
|
||||||
- name: set the memory to 8GB on a server
|
|
||||||
clc_server:
|
|
||||||
server_ids: ['UC1ACCTTEST01']
|
|
||||||
memory: 8
|
|
||||||
state: present
|
|
||||||
|
|
||||||
- name: set the anti affinity policy on a server
|
|
||||||
clc_server:
|
|
||||||
server_ids: ['UC1ACCTTEST01']
|
|
||||||
anti_affinity_policy_name: 'aa_policy'
|
|
||||||
state: present
|
|
||||||
|
|
||||||
- name: set the alert policy on a server
|
|
||||||
clc_server:
|
|
||||||
server_ids: ['UC1ACCTTEST01']
|
|
||||||
alert_policy_name: 'alert_policy'
|
|
||||||
state: present
|
|
||||||
|
|
||||||
- name: set the memory to 16GB and cpu to 8 core on a lust if servers
|
|
||||||
clc_server:
|
|
||||||
server_ids: ['UC1ACCTTEST01','UC1ACCTTEST02']
|
|
||||||
cpu: 8
|
|
||||||
memory: 16
|
|
||||||
state: present
|
|
||||||
'''
|
|
||||||
|
|
||||||
__version__ = '${version}'
|
|
||||||
|
|
||||||
import requests
|
|
||||||
|
|
||||||
#
|
|
||||||
# Requires the clc-python-sdk.
|
|
||||||
# sudo pip install clc-sdk
|
|
||||||
#
|
|
||||||
try:
|
|
||||||
import clc as clc_sdk
|
|
||||||
from clc import CLCException
|
|
||||||
from clc import APIFailedResponse
|
|
||||||
except ImportError:
|
|
||||||
CLC_FOUND = False
|
|
||||||
clc_sdk = None
|
|
||||||
else:
|
|
||||||
CLC_FOUND = True
|
|
||||||
|
|
||||||
|
|
||||||
class ClcModifyServer():
|
|
||||||
clc = clc_sdk
|
|
||||||
|
|
||||||
def __init__(self, module):
|
|
||||||
"""
|
|
||||||
Construct module
|
|
||||||
"""
|
|
||||||
self.clc = clc_sdk
|
|
||||||
self.module = module
|
|
||||||
self.group_dict = {}
|
|
||||||
|
|
||||||
if not CLC_FOUND:
|
|
||||||
self.module.fail_json(
|
|
||||||
msg='clc-python-sdk required for this module')
|
|
||||||
|
|
||||||
self._set_user_agent(self.clc)
|
|
||||||
|
|
||||||
def process_request(self):
|
|
||||||
"""
|
|
||||||
Process the request - Main Code Path
|
|
||||||
:return: Returns with either an exit_json or fail_json
|
|
||||||
"""
|
|
||||||
self._set_clc_credentials_from_env()
|
|
||||||
|
|
||||||
p = self.module.params
|
|
||||||
|
|
||||||
server_ids = p['server_ids']
|
|
||||||
if not isinstance(server_ids, list):
|
|
||||||
return self.module.fail_json(
|
|
||||||
msg='server_ids needs to be a list of instances to modify: %s' %
|
|
||||||
server_ids)
|
|
||||||
|
|
||||||
(changed, server_dict_array, new_server_ids) = ClcModifyServer._modify_servers(
|
|
||||||
module=self.module, clc=self.clc, server_ids=server_ids)
|
|
||||||
|
|
||||||
self.module.exit_json(
|
|
||||||
changed=changed,
|
|
||||||
server_ids=new_server_ids,
|
|
||||||
servers=server_dict_array)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _define_module_argument_spec():
|
|
||||||
"""
|
|
||||||
Define the argument spec for the ansible module
|
|
||||||
:return: argument spec dictionary
|
|
||||||
"""
|
|
||||||
argument_spec = dict(
|
|
||||||
server_ids=dict(type='list', required=True),
|
|
||||||
state=dict(default='present', choices=['present', 'absent']),
|
|
||||||
cpu=dict(),
|
|
||||||
memory=dict(),
|
|
||||||
anti_affinity_policy_id=dict(),
|
|
||||||
anti_affinity_policy_name=dict(),
|
|
||||||
alert_policy_id=dict(),
|
|
||||||
alert_policy_name=dict(),
|
|
||||||
wait=dict(type='bool', default=True)
|
|
||||||
)
|
|
||||||
mutually_exclusive = [
|
|
||||||
['anti_affinity_policy_id', 'anti_affinity_policy_name'],
|
|
||||||
['alert_policy_id', 'alert_policy_name']
|
|
||||||
]
|
|
||||||
return {"argument_spec": argument_spec,
|
|
||||||
"mutually_exclusive": mutually_exclusive}
|
|
||||||
|
|
||||||
def _set_clc_credentials_from_env(self):
|
|
||||||
"""
|
|
||||||
Set the CLC Credentials on the sdk by reading environment variables
|
|
||||||
:return: none
|
|
||||||
"""
|
|
||||||
env = os.environ
|
|
||||||
v2_api_token = env.get('CLC_V2_API_TOKEN', False)
|
|
||||||
v2_api_username = env.get('CLC_V2_API_USERNAME', False)
|
|
||||||
v2_api_passwd = env.get('CLC_V2_API_PASSWD', False)
|
|
||||||
clc_alias = env.get('CLC_ACCT_ALIAS', False)
|
|
||||||
api_url = env.get('CLC_V2_API_URL', False)
|
|
||||||
|
|
||||||
if api_url:
|
|
||||||
self.clc.defaults.ENDPOINT_URL_V2 = api_url
|
|
||||||
|
|
||||||
if v2_api_token and clc_alias:
|
|
||||||
self.clc._LOGIN_TOKEN_V2 = v2_api_token
|
|
||||||
self.clc._V2_ENABLED = True
|
|
||||||
self.clc.ALIAS = clc_alias
|
|
||||||
elif v2_api_username and v2_api_passwd:
|
|
||||||
self.clc.v2.SetCredentials(
|
|
||||||
api_username=v2_api_username,
|
|
||||||
api_passwd=v2_api_passwd)
|
|
||||||
else:
|
|
||||||
return self.module.fail_json(
|
|
||||||
msg="You must set the CLC_V2_API_USERNAME and CLC_V2_API_PASSWD "
|
|
||||||
"environment variables")
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _wait_for_requests(clc, requests, servers, wait):
|
|
||||||
"""
|
|
||||||
Block until server provisioning requests are completed.
|
|
||||||
:param clc: the clc-sdk instance to use
|
|
||||||
:param requests: a list of clc-sdk.Request instances
|
|
||||||
:param servers: a list of servers to refresh
|
|
||||||
:param wait: a boolean on whether to block or not. This function is skipped if True
|
|
||||||
:return: none
|
|
||||||
"""
|
|
||||||
if wait:
|
|
||||||
# Requests.WaitUntilComplete() returns the count of failed requests
|
|
||||||
failed_requests_count = sum(
|
|
||||||
[request.WaitUntilComplete() for request in requests])
|
|
||||||
|
|
||||||
if failed_requests_count > 0:
|
|
||||||
raise clc
|
|
||||||
else:
|
|
||||||
ClcModifyServer._refresh_servers(servers)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _refresh_servers(servers):
|
|
||||||
"""
|
|
||||||
Loop through a list of servers and refresh them
|
|
||||||
:param servers: list of clc-sdk.Server instances to refresh
|
|
||||||
:return: none
|
|
||||||
"""
|
|
||||||
for server in servers:
|
|
||||||
server.Refresh()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _modify_servers(module, clc, server_ids):
|
|
||||||
"""
|
|
||||||
modify the servers configuration on the provided list
|
|
||||||
:param module: the AnsibleModule object
|
|
||||||
:param clc: the clc-sdk instance to use
|
|
||||||
:param server_ids: list of servers to modify
|
|
||||||
:return: a list of dictionaries with server information about the servers that were modified
|
|
||||||
"""
|
|
||||||
p = module.params
|
|
||||||
wait = p.get('wait')
|
|
||||||
state = p.get('state')
|
|
||||||
server_params = {
|
|
||||||
'cpu': p.get('cpu'),
|
|
||||||
'memory': p.get('memory'),
|
|
||||||
'anti_affinity_policy_id': p.get('anti_affinity_policy_id'),
|
|
||||||
'anti_affinity_policy_name': p.get('anti_affinity_policy_name'),
|
|
||||||
'alert_policy_id': p.get('alert_policy_id'),
|
|
||||||
'alert_policy_name': p.get('alert_policy_name'),
|
|
||||||
}
|
|
||||||
changed = False
|
|
||||||
server_changed = False
|
|
||||||
aa_changed = False
|
|
||||||
ap_changed = False
|
|
||||||
server_dict_array = []
|
|
||||||
result_server_ids = []
|
|
||||||
requests = []
|
|
||||||
|
|
||||||
if not isinstance(server_ids, list) or len(server_ids) < 1:
|
|
||||||
return module.fail_json(
|
|
||||||
msg='server_ids should be a list of servers, aborting')
|
|
||||||
|
|
||||||
servers = clc.v2.Servers(server_ids).Servers()
|
|
||||||
if state == 'present':
|
|
||||||
for server in servers:
|
|
||||||
server_changed, server_result, changed_servers = ClcModifyServer._ensure_server_config(
|
|
||||||
clc, module, None, server, server_params)
|
|
||||||
if server_result:
|
|
||||||
requests.append(server_result)
|
|
||||||
aa_changed, changed_servers = ClcModifyServer._ensure_aa_policy(
|
|
||||||
clc, module, None, server, server_params)
|
|
||||||
ap_changed, changed_servers = ClcModifyServer._ensure_alert_policy_present(
|
|
||||||
clc, module, None, server, server_params)
|
|
||||||
elif state == 'absent':
|
|
||||||
for server in servers:
|
|
||||||
ap_changed, changed_servers = ClcModifyServer._ensure_alert_policy_absent(
|
|
||||||
clc, module, None, server, server_params)
|
|
||||||
if server_changed or aa_changed or ap_changed:
|
|
||||||
changed = True
|
|
||||||
|
|
||||||
if wait:
|
|
||||||
for r in requests:
|
|
||||||
r.WaitUntilComplete()
|
|
||||||
for server in changed_servers:
|
|
||||||
server.Refresh()
|
|
||||||
|
|
||||||
for server in changed_servers:
|
|
||||||
server_dict_array.append(server.data)
|
|
||||||
result_server_ids.append(server.id)
|
|
||||||
|
|
||||||
return changed, server_dict_array, result_server_ids
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _ensure_server_config(
|
|
||||||
clc, module, alias, server, server_params):
|
|
||||||
"""
|
|
||||||
ensures the server is updated with the provided cpu and memory
|
|
||||||
:param clc: the clc-sdk instance to use
|
|
||||||
:param module: the AnsibleModule object
|
|
||||||
:param alias: the CLC account alias
|
|
||||||
:param server: the CLC server object
|
|
||||||
:param server_params: the dictionary of server parameters
|
|
||||||
:return: (changed, group) -
|
|
||||||
changed: Boolean whether a change was made
|
|
||||||
result: The result from the CLC API call
|
|
||||||
"""
|
|
||||||
cpu = server_params.get('cpu')
|
|
||||||
memory = server_params.get('memory')
|
|
||||||
changed = False
|
|
||||||
result = None
|
|
||||||
changed_servers = []
|
|
||||||
|
|
||||||
if not cpu:
|
|
||||||
cpu = server.cpu
|
|
||||||
if not memory:
|
|
||||||
memory = server.memory
|
|
||||||
if memory != server.memory or cpu != server.cpu:
|
|
||||||
changed_servers.append(server)
|
|
||||||
result = ClcModifyServer._modify_clc_server(
|
|
||||||
clc,
|
|
||||||
module,
|
|
||||||
None,
|
|
||||||
server.id,
|
|
||||||
cpu,
|
|
||||||
memory)
|
|
||||||
changed = True
|
|
||||||
return changed, result, changed_servers
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _modify_clc_server(clc, module, acct_alias, server_id, cpu, memory):
|
|
||||||
"""
|
|
||||||
Modify the memory or CPU on a clc server. This function is not yet implemented.
|
|
||||||
:param clc: the clc-sdk instance to use
|
|
||||||
:param module: the AnsibleModule object
|
|
||||||
:param acct_alias: the clc account alias to look up the server
|
|
||||||
:param server_id: id of the server to modify
|
|
||||||
:param cpu: the new cpu value
|
|
||||||
:param memory: the new memory value
|
|
||||||
:return: the result of CLC API call
|
|
||||||
"""
|
|
||||||
if not acct_alias:
|
|
||||||
acct_alias = clc.v2.Account.GetAlias()
|
|
||||||
if not server_id:
|
|
||||||
return module.fail_json(
|
|
||||||
msg='server_id must be provided to modify the server')
|
|
||||||
|
|
||||||
result = None
|
|
||||||
|
|
||||||
if not module.check_mode:
|
|
||||||
|
|
||||||
# Update the server configuation
|
|
||||||
job_obj = clc.v2.API.Call('PATCH',
|
|
||||||
'servers/%s/%s' % (acct_alias,
|
|
||||||
server_id),
|
|
||||||
json.dumps([{"op": "set",
|
|
||||||
"member": "memory",
|
|
||||||
"value": memory},
|
|
||||||
{"op": "set",
|
|
||||||
"member": "cpu",
|
|
||||||
"value": cpu}]))
|
|
||||||
result = clc.v2.Requests(job_obj)
|
|
||||||
return result
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _ensure_aa_policy(
|
|
||||||
clc, module, acct_alias, server, server_params):
|
|
||||||
"""
|
|
||||||
ensures the server is updated with the provided anti affinity policy
|
|
||||||
:param clc: the clc-sdk instance to use
|
|
||||||
:param module: the AnsibleModule object
|
|
||||||
:param acct_alias: the CLC account alias
|
|
||||||
:param server: the CLC server object
|
|
||||||
:param server_params: the dictionary of server parameters
|
|
||||||
:return: (changed, group) -
|
|
||||||
changed: Boolean whether a change was made
|
|
||||||
result: The result from the CLC API call
|
|
||||||
"""
|
|
||||||
changed = False
|
|
||||||
changed_servers = []
|
|
||||||
|
|
||||||
if not acct_alias:
|
|
||||||
acct_alias = clc.v2.Account.GetAlias()
|
|
||||||
|
|
||||||
aa_policy_id = server_params.get('anti_affinity_policy_id')
|
|
||||||
aa_policy_name = server_params.get('anti_affinity_policy_name')
|
|
||||||
if not aa_policy_id and aa_policy_name:
|
|
||||||
aa_policy_id = ClcModifyServer._get_aa_policy_id_by_name(
|
|
||||||
clc,
|
|
||||||
module,
|
|
||||||
acct_alias,
|
|
||||||
aa_policy_name)
|
|
||||||
current_aa_policy_id = ClcModifyServer._get_aa_policy_id_of_server(
|
|
||||||
clc,
|
|
||||||
module,
|
|
||||||
acct_alias,
|
|
||||||
server.id)
|
|
||||||
|
|
||||||
if aa_policy_id and aa_policy_id != current_aa_policy_id:
|
|
||||||
if server not in changed_servers:
|
|
||||||
changed_servers.append(server)
|
|
||||||
ClcModifyServer._modify_aa_policy(
|
|
||||||
clc,
|
|
||||||
module,
|
|
||||||
acct_alias,
|
|
||||||
server.id,
|
|
||||||
aa_policy_id)
|
|
||||||
changed = True
|
|
||||||
return changed, changed_servers
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _modify_aa_policy(clc, module, acct_alias, server_id, aa_policy_id):
|
|
||||||
"""
|
|
||||||
modifies the anti affinity policy of the CLC server
|
|
||||||
:param clc: the clc-sdk instance to use
|
|
||||||
:param module: the AnsibleModule object
|
|
||||||
:param acct_alias: the CLC account alias
|
|
||||||
:param server_id: the CLC server id
|
|
||||||
:param aa_policy_id: the anti affinity policy id
|
|
||||||
:return: result: The result from the CLC API call
|
|
||||||
"""
|
|
||||||
result = None
|
|
||||||
if not module.check_mode:
|
|
||||||
result = clc.v2.API.Call('PUT',
|
|
||||||
'servers/%s/%s/antiAffinityPolicy' % (
|
|
||||||
acct_alias,
|
|
||||||
server_id),
|
|
||||||
json.dumps({"id": aa_policy_id}))
|
|
||||||
return result
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _get_aa_policy_id_by_name(clc, module, alias, aa_policy_name):
|
|
||||||
"""
|
|
||||||
retrieves the anti affinity policy id of the server based on the name of the policy
|
|
||||||
:param clc: the clc-sdk instance to use
|
|
||||||
:param module: the AnsibleModule object
|
|
||||||
:param alias: the CLC account alias
|
|
||||||
:param aa_policy_name: the anti affinity policy name
|
|
||||||
:return: aa_policy_id: The anti affinity policy id
|
|
||||||
"""
|
|
||||||
aa_policy_id = None
|
|
||||||
aa_policies = clc.v2.API.Call(method='GET',
|
|
||||||
url='antiAffinityPolicies/%s' % (alias))
|
|
||||||
for aa_policy in aa_policies.get('items'):
|
|
||||||
if aa_policy.get('name') == aa_policy_name:
|
|
||||||
if not aa_policy_id:
|
|
||||||
aa_policy_id = aa_policy.get('id')
|
|
||||||
else:
|
|
||||||
return module.fail_json(
|
|
||||||
msg='mutiple anti affinity policies were found with policy name : %s' %
|
|
||||||
(aa_policy_name))
|
|
||||||
if not aa_policy_id:
|
|
||||||
return module.fail_json(
|
|
||||||
msg='No anti affinity policy was found with policy name : %s' %
|
|
||||||
(aa_policy_name))
|
|
||||||
return aa_policy_id
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _get_aa_policy_id_of_server(clc, module, alias, server_id):
|
|
||||||
"""
|
|
||||||
retrieves the anti affinity policy id of the server based on the CLC server id
|
|
||||||
:param clc: the clc-sdk instance to use
|
|
||||||
:param module: the AnsibleModule object
|
|
||||||
:param alias: the CLC account alias
|
|
||||||
:param server_id: the CLC server id
|
|
||||||
:return: aa_policy_id: The anti affinity policy id
|
|
||||||
"""
|
|
||||||
aa_policy_id = None
|
|
||||||
try:
|
|
||||||
result = clc.v2.API.Call(
|
|
||||||
method='GET', url='servers/%s/%s/antiAffinityPolicy' %
|
|
||||||
(alias, server_id))
|
|
||||||
aa_policy_id = result.get('id')
|
|
||||||
except APIFailedResponse as e:
|
|
||||||
if e.response_status_code != 404:
|
|
||||||
raise e
|
|
||||||
return aa_policy_id
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _ensure_alert_policy_present(
|
|
||||||
clc, module, acct_alias, server, server_params):
|
|
||||||
"""
|
|
||||||
ensures the server is updated with the provided alert policy
|
|
||||||
:param clc: the clc-sdk instance to use
|
|
||||||
:param module: the AnsibleModule object
|
|
||||||
:param acct_alias: the CLC account alias
|
|
||||||
:param server: the CLC server object
|
|
||||||
:param server_params: the dictionary of server parameters
|
|
||||||
:return: (changed, group) -
|
|
||||||
changed: Boolean whether a change was made
|
|
||||||
result: The result from the CLC API call
|
|
||||||
"""
|
|
||||||
changed = False
|
|
||||||
changed_servers = []
|
|
||||||
|
|
||||||
if not acct_alias:
|
|
||||||
acct_alias = clc.v2.Account.GetAlias()
|
|
||||||
|
|
||||||
alert_policy_id = server_params.get('alert_policy_id')
|
|
||||||
alert_policy_name = server_params.get('alert_policy_name')
|
|
||||||
if not alert_policy_id and alert_policy_name:
|
|
||||||
alert_policy_id = ClcModifyServer._get_alert_policy_id_by_name(
|
|
||||||
clc,
|
|
||||||
module,
|
|
||||||
acct_alias,
|
|
||||||
alert_policy_name)
|
|
||||||
if alert_policy_id and not ClcModifyServer._alert_policy_exists(server, alert_policy_id):
|
|
||||||
if server not in changed_servers:
|
|
||||||
changed_servers.append(server)
|
|
||||||
ClcModifyServer._add_alert_policy_to_server(
|
|
||||||
clc,
|
|
||||||
module,
|
|
||||||
acct_alias,
|
|
||||||
server.id,
|
|
||||||
alert_policy_id)
|
|
||||||
changed = True
|
|
||||||
return changed, changed_servers
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _ensure_alert_policy_absent(
|
|
||||||
clc, module, acct_alias, server, server_params):
|
|
||||||
"""
|
|
||||||
ensures the alert policy is removed from the server
|
|
||||||
:param clc: the clc-sdk instance to use
|
|
||||||
:param module: the AnsibleModule object
|
|
||||||
:param acct_alias: the CLC account alias
|
|
||||||
:param server: the CLC server object
|
|
||||||
:param server_params: the dictionary of server parameters
|
|
||||||
:return: (changed, group) -
|
|
||||||
changed: Boolean whether a change was made
|
|
||||||
result: The result from the CLC API call
|
|
||||||
"""
|
|
||||||
changed = False
|
|
||||||
result = None
|
|
||||||
changed_servers = []
|
|
||||||
|
|
||||||
if not acct_alias:
|
|
||||||
acct_alias = clc.v2.Account.GetAlias()
|
|
||||||
|
|
||||||
alert_policy_id = server_params.get('alert_policy_id')
|
|
||||||
alert_policy_name = server_params.get('alert_policy_name')
|
|
||||||
if not alert_policy_id and alert_policy_name:
|
|
||||||
alert_policy_id = ClcModifyServer._get_alert_policy_id_by_name(
|
|
||||||
clc,
|
|
||||||
module,
|
|
||||||
acct_alias,
|
|
||||||
alert_policy_name)
|
|
||||||
|
|
||||||
if alert_policy_id and ClcModifyServer._alert_policy_exists(server, alert_policy_id):
|
|
||||||
if server not in changed_servers:
|
|
||||||
changed_servers.append(server)
|
|
||||||
ClcModifyServer._remove_alert_policy_to_server(
|
|
||||||
clc,
|
|
||||||
module,
|
|
||||||
acct_alias,
|
|
||||||
server.id,
|
|
||||||
alert_policy_id)
|
|
||||||
changed = True
|
|
||||||
return changed, changed_servers
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _add_alert_policy_to_server(clc, module, acct_alias, server_id, alert_policy_id):
|
|
||||||
"""
|
|
||||||
add the alert policy to CLC server
|
|
||||||
:param clc: the clc-sdk instance to use
|
|
||||||
:param module: the AnsibleModule object
|
|
||||||
:param acct_alias: the CLC account alias
|
|
||||||
:param server_id: the CLC server id
|
|
||||||
:param alert_policy_id: the alert policy id
|
|
||||||
:return: result: The result from the CLC API call
|
|
||||||
"""
|
|
||||||
result = None
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
result = clc.v2.API.Call('POST',
|
|
||||||
'servers/%s/%s/alertPolicies' % (
|
|
||||||
acct_alias,
|
|
||||||
server_id),
|
|
||||||
json.dumps({"id": alert_policy_id}))
|
|
||||||
except clc.APIFailedResponse as e:
|
|
||||||
return module.fail_json(
|
|
||||||
msg='Unable to set alert policy to the server : %s. %s' % (server_id, str(e.response_text)))
|
|
||||||
return result
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _remove_alert_policy_to_server(clc, module, acct_alias, server_id, alert_policy_id):
|
|
||||||
"""
|
|
||||||
remove the alert policy to the CLC server
|
|
||||||
:param clc: the clc-sdk instance to use
|
|
||||||
:param module: the AnsibleModule object
|
|
||||||
:param acct_alias: the CLC account alias
|
|
||||||
:param server_id: the CLC server id
|
|
||||||
:param alert_policy_id: the alert policy id
|
|
||||||
:return: result: The result from the CLC API call
|
|
||||||
"""
|
|
||||||
result = None
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
result = clc.v2.API.Call('DELETE',
|
|
||||||
'servers/%s/%s/alertPolicies/%s'
|
|
||||||
% (acct_alias, server_id, alert_policy_id))
|
|
||||||
except clc.APIFailedResponse as e:
|
|
||||||
return module.fail_json(
|
|
||||||
msg='Unable to remove alert policy to the server : %s. %s' % (server_id, str(e.response_text)))
|
|
||||||
return result
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _get_alert_policy_id_by_name(clc, module, alias, alert_policy_name):
|
|
||||||
"""
|
|
||||||
retrieves the alert policy id of the server based on the name of the policy
|
|
||||||
:param clc: the clc-sdk instance to use
|
|
||||||
:param module: the AnsibleModule object
|
|
||||||
:param alias: the CLC account alias
|
|
||||||
:param alert_policy_name: the alert policy name
|
|
||||||
:return: alert_policy_id: The alert policy id
|
|
||||||
"""
|
|
||||||
alert_policy_id = None
|
|
||||||
alert_policies = clc.v2.API.Call(method='GET',
|
|
||||||
url='alertPolicies/%s' % (alias))
|
|
||||||
for alert_policy in alert_policies.get('items'):
|
|
||||||
if alert_policy.get('name') == alert_policy_name:
|
|
||||||
if not alert_policy_id:
|
|
||||||
alert_policy_id = alert_policy.get('id')
|
|
||||||
else:
|
|
||||||
return module.fail_json(
|
|
||||||
msg='mutiple alert policies were found with policy name : %s' %
|
|
||||||
(alert_policy_name))
|
|
||||||
return alert_policy_id
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _alert_policy_exists(server, alert_policy_id):
|
|
||||||
"""
|
|
||||||
Checks if the alert policy exists for the server
|
|
||||||
:param server: the clc server object
|
|
||||||
:param alert_policy_id: the alert policy
|
|
||||||
:return: True: if the given alert policy id associated to the server, False otherwise
|
|
||||||
"""
|
|
||||||
result = False
|
|
||||||
alert_policies = server.alertPolicies
|
|
||||||
if alert_policies:
|
|
||||||
for alert_policy in alert_policies:
|
|
||||||
if alert_policy.get('id') == alert_policy_id:
|
|
||||||
result = True
|
|
||||||
return result
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _set_user_agent(clc):
|
|
||||||
if hasattr(clc, 'SetRequestsSession'):
|
|
||||||
agent_string = "ClcAnsibleModule/" + __version__
|
|
||||||
ses = requests.Session()
|
|
||||||
ses.headers.update({"Api-Client": agent_string})
|
|
||||||
ses.headers['User-Agent'] += " " + agent_string
|
|
||||||
clc.SetRequestsSession(ses)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""
|
|
||||||
The main function. Instantiates the module and calls process_request.
|
|
||||||
:return: none
|
|
||||||
"""
|
|
||||||
|
|
||||||
argument_dict = ClcModifyServer._define_module_argument_spec()
|
|
||||||
module = AnsibleModule(supports_check_mode=True, **argument_dict)
|
|
||||||
clc_modify_server = ClcModifyServer(module)
|
|
||||||
clc_modify_server.process_request()
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import * # pylint: disable=W0614
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
File diff suppressed because it is too large
Load Diff
@ -1,341 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
|
|
||||||
# CenturyLink Cloud Ansible Modules.
|
|
||||||
#
|
|
||||||
# These Ansible modules enable the CenturyLink Cloud v2 API to be called
|
|
||||||
# from an within Ansible Playbook.
|
|
||||||
#
|
|
||||||
# This file is part of CenturyLink Cloud, and is maintained
|
|
||||||
# by the Workflow as a Service Team
|
|
||||||
#
|
|
||||||
# Copyright 2015 CenturyLink Cloud
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
#
|
|
||||||
# CenturyLink Cloud: http://www.CenturyLinkCloud.com
|
|
||||||
# API Documentation: https://www.centurylinkcloud.com/api-docs/v2/
|
|
||||||
#
|
|
||||||
|
|
||||||
DOCUMENTATION = '''
|
|
||||||
module: clc_server
|
|
||||||
short_desciption: Create, Delete and Restore server snapshots in CenturyLink Cloud.
|
|
||||||
description:
|
|
||||||
- An Ansible module to Create, Delete and Restore server snapshots in CenturyLink Cloud.
|
|
||||||
options:
|
|
||||||
server_ids:
|
|
||||||
description:
|
|
||||||
- A list of server Ids to snapshot.
|
|
||||||
default: []
|
|
||||||
required: True
|
|
||||||
aliases: []
|
|
||||||
expiration_days:
|
|
||||||
description:
|
|
||||||
- The number of days to keep the server snapshot before it expires.
|
|
||||||
default: 7
|
|
||||||
required: False
|
|
||||||
aliases: []
|
|
||||||
state:
|
|
||||||
description:
|
|
||||||
- The state to insure that the provided resources are in.
|
|
||||||
default: 'present'
|
|
||||||
required: False
|
|
||||||
choices: ['present', 'absent', 'restore']
|
|
||||||
aliases: []
|
|
||||||
wait:
|
|
||||||
description:
|
|
||||||
- Whether to wait for the provisioning tasks to finish before returning.
|
|
||||||
default: True
|
|
||||||
required: False
|
|
||||||
choices: [ True, False]
|
|
||||||
aliases: []
|
|
||||||
'''
|
|
||||||
|
|
||||||
EXAMPLES = '''
|
|
||||||
# Note - You must set the CLC_V2_API_USERNAME And CLC_V2_API_PASSWD Environment variables before running these examples
|
|
||||||
|
|
||||||
- name: Create server snapshot
|
|
||||||
clc_server_snapshot:
|
|
||||||
server_ids:
|
|
||||||
- UC1WFSDTEST01
|
|
||||||
- UC1WFSDTEST02
|
|
||||||
expiration_days: 10
|
|
||||||
wait: True
|
|
||||||
state: present
|
|
||||||
|
|
||||||
- name: Restore server snapshot
|
|
||||||
clc_server_snapshot:
|
|
||||||
server_ids:
|
|
||||||
- UC1WFSDTEST01
|
|
||||||
- UC1WFSDTEST02
|
|
||||||
wait: True
|
|
||||||
state: restore
|
|
||||||
|
|
||||||
- name: Delete server snapshot
|
|
||||||
clc_server_snapshot:
|
|
||||||
server_ids:
|
|
||||||
- UC1WFSDTEST01
|
|
||||||
- UC1WFSDTEST02
|
|
||||||
wait: True
|
|
||||||
state: absent
|
|
||||||
'''
|
|
||||||
|
|
||||||
__version__ = '${version}'
|
|
||||||
|
|
||||||
import requests
|
|
||||||
|
|
||||||
#
|
|
||||||
# Requires the clc-python-sdk.
|
|
||||||
# sudo pip install clc-sdk
|
|
||||||
#
|
|
||||||
try:
|
|
||||||
import clc as clc_sdk
|
|
||||||
from clc import CLCException
|
|
||||||
except ImportError:
|
|
||||||
clc_found = False
|
|
||||||
clc_sdk = None
|
|
||||||
else:
|
|
||||||
CLC_FOUND = True
|
|
||||||
|
|
||||||
|
|
||||||
class ClcSnapshot():
|
|
||||||
|
|
||||||
clc = clc_sdk
|
|
||||||
module = None
|
|
||||||
|
|
||||||
def __init__(self, module):
|
|
||||||
"""
|
|
||||||
Construct module
|
|
||||||
"""
|
|
||||||
self.module = module
|
|
||||||
if not CLC_FOUND:
|
|
||||||
self.module.fail_json(
|
|
||||||
msg='clc-python-sdk required for this module')
|
|
||||||
|
|
||||||
self._set_user_agent(self.clc)
|
|
||||||
|
|
||||||
def process_request(self):
|
|
||||||
"""
|
|
||||||
Process the request - Main Code Path
|
|
||||||
:return: Returns with either an exit_json or fail_json
|
|
||||||
"""
|
|
||||||
p = self.module.params
|
|
||||||
|
|
||||||
if not CLC_FOUND:
|
|
||||||
self.module.fail_json(
|
|
||||||
msg='clc-python-sdk required for this module')
|
|
||||||
|
|
||||||
server_ids = p['server_ids']
|
|
||||||
expiration_days = p['expiration_days']
|
|
||||||
state = p['state']
|
|
||||||
|
|
||||||
if not server_ids:
|
|
||||||
return self.module.fail_json(msg='List of Server ids are required')
|
|
||||||
|
|
||||||
self._set_clc_credentials_from_env()
|
|
||||||
if state == 'present':
|
|
||||||
changed, requests, changed_servers = self.ensure_server_snapshot_present(server_ids=server_ids,
|
|
||||||
expiration_days=expiration_days)
|
|
||||||
elif state == 'absent':
|
|
||||||
changed, requests, changed_servers = self.ensure_server_snapshot_absent(
|
|
||||||
server_ids=server_ids)
|
|
||||||
elif state == 'restore':
|
|
||||||
changed, requests, changed_servers = self.ensure_server_snapshot_restore(
|
|
||||||
server_ids=server_ids)
|
|
||||||
else:
|
|
||||||
return self.module.fail_json(msg="Unknown State: " + state)
|
|
||||||
|
|
||||||
self._wait_for_requests_to_complete(requests)
|
|
||||||
return self.module.exit_json(
|
|
||||||
changed=changed,
|
|
||||||
server_ids=changed_servers)
|
|
||||||
|
|
||||||
def ensure_server_snapshot_present(self, server_ids, expiration_days):
|
|
||||||
"""
|
|
||||||
Ensures the given set of server_ids have the snapshots created
|
|
||||||
:param server_ids: The list of server_ids to create the snapshot
|
|
||||||
:param expiration_days: The number of days to keep the snapshot
|
|
||||||
:return: (changed, result, changed_servers)
|
|
||||||
changed: A flag indicating whether any change was made
|
|
||||||
result: the list of clc request objects from CLC API call
|
|
||||||
changed_servers: The list of servers ids that are modified
|
|
||||||
"""
|
|
||||||
result = []
|
|
||||||
changed = False
|
|
||||||
servers = self._get_servers_from_clc(
|
|
||||||
server_ids,
|
|
||||||
'Failed to obtain server list from the CLC API')
|
|
||||||
servers_to_change = [
|
|
||||||
server for server in servers if len(
|
|
||||||
server.GetSnapshots()) == 0]
|
|
||||||
for server in servers_to_change:
|
|
||||||
changed = True
|
|
||||||
if not self.module.check_mode:
|
|
||||||
res = server.CreateSnapshot(
|
|
||||||
delete_existing=True,
|
|
||||||
expiration_days=expiration_days)
|
|
||||||
result.append(res)
|
|
||||||
changed_servers = [
|
|
||||||
server.id for server in servers_to_change if server.id]
|
|
||||||
return changed, result, changed_servers
|
|
||||||
|
|
||||||
def ensure_server_snapshot_absent(self, server_ids):
|
|
||||||
"""
|
|
||||||
Ensures the given set of server_ids have the snapshots removed
|
|
||||||
:param server_ids: The list of server_ids to delete the snapshot
|
|
||||||
:return: (changed, result, changed_servers)
|
|
||||||
changed: A flag indicating whether any change was made
|
|
||||||
result: the list of clc request objects from CLC API call
|
|
||||||
changed_servers: The list of servers ids that are modified
|
|
||||||
"""
|
|
||||||
result = []
|
|
||||||
changed = False
|
|
||||||
servers = self._get_servers_from_clc(
|
|
||||||
server_ids,
|
|
||||||
'Failed to obtain server list from the CLC API')
|
|
||||||
servers_to_change = [
|
|
||||||
server for server in servers if len(
|
|
||||||
server.GetSnapshots()) > 0]
|
|
||||||
for server in servers_to_change:
|
|
||||||
changed = True
|
|
||||||
if not self.module.check_mode:
|
|
||||||
res = server.DeleteSnapshot()
|
|
||||||
result.append(res)
|
|
||||||
changed_servers = [
|
|
||||||
server.id for server in servers_to_change if server.id]
|
|
||||||
return changed, result, changed_servers
|
|
||||||
|
|
||||||
def ensure_server_snapshot_restore(self, server_ids):
|
|
||||||
"""
|
|
||||||
Ensures the given set of server_ids have the snapshots restored
|
|
||||||
:param server_ids: The list of server_ids to delete the snapshot
|
|
||||||
:return: (changed, result, changed_servers)
|
|
||||||
changed: A flag indicating whether any change was made
|
|
||||||
result: the list of clc request objects from CLC API call
|
|
||||||
changed_servers: The list of servers ids that are modified
|
|
||||||
"""
|
|
||||||
result = []
|
|
||||||
changed = False
|
|
||||||
servers = self._get_servers_from_clc(
|
|
||||||
server_ids,
|
|
||||||
'Failed to obtain server list from the CLC API')
|
|
||||||
servers_to_change = [
|
|
||||||
server for server in servers if len(
|
|
||||||
server.GetSnapshots()) > 0]
|
|
||||||
for server in servers_to_change:
|
|
||||||
changed = True
|
|
||||||
if not self.module.check_mode:
|
|
||||||
res = server.RestoreSnapshot()
|
|
||||||
result.append(res)
|
|
||||||
changed_servers = [
|
|
||||||
server.id for server in servers_to_change if server.id]
|
|
||||||
return changed, result, changed_servers
|
|
||||||
|
|
||||||
def _wait_for_requests_to_complete(self, requests_lst):
|
|
||||||
"""
|
|
||||||
Waits until the CLC requests are complete if the wait argument is True
|
|
||||||
:param requests_lst: The list of CLC request objects
|
|
||||||
:return: none
|
|
||||||
"""
|
|
||||||
if not self.module.params['wait']:
|
|
||||||
return
|
|
||||||
for request in requests_lst:
|
|
||||||
request.WaitUntilComplete()
|
|
||||||
for request_details in request.requests:
|
|
||||||
if request_details.Status() != 'succeeded':
|
|
||||||
self.module.fail_json(
|
|
||||||
msg='Unable to process server snapshot request')
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def define_argument_spec():
|
|
||||||
"""
|
|
||||||
This function defnines the dictionary object required for
|
|
||||||
package module
|
|
||||||
:return: the package dictionary object
|
|
||||||
"""
|
|
||||||
argument_spec = dict(
|
|
||||||
server_ids=dict(type='list', required=True),
|
|
||||||
expiration_days=dict(default=7),
|
|
||||||
wait=dict(default=True),
|
|
||||||
state=dict(
|
|
||||||
default='present',
|
|
||||||
choices=[
|
|
||||||
'present',
|
|
||||||
'absent',
|
|
||||||
'restore']),
|
|
||||||
)
|
|
||||||
return argument_spec
|
|
||||||
|
|
||||||
def _get_servers_from_clc(self, server_list, message):
|
|
||||||
"""
|
|
||||||
Internal function to fetch list of CLC server objects from a list of server ids
|
|
||||||
:param the list server ids
|
|
||||||
:return the list of CLC server objects
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
return self.clc.v2.Servers(server_list).servers
|
|
||||||
except CLCException as ex:
|
|
||||||
return self.module.fail_json(msg=message + ': %s' % ex)
|
|
||||||
|
|
||||||
def _set_clc_credentials_from_env(self):
|
|
||||||
"""
|
|
||||||
Set the CLC Credentials on the sdk by reading environment variables
|
|
||||||
:return: none
|
|
||||||
"""
|
|
||||||
env = os.environ
|
|
||||||
v2_api_token = env.get('CLC_V2_API_TOKEN', False)
|
|
||||||
v2_api_username = env.get('CLC_V2_API_USERNAME', False)
|
|
||||||
v2_api_passwd = env.get('CLC_V2_API_PASSWD', False)
|
|
||||||
clc_alias = env.get('CLC_ACCT_ALIAS', False)
|
|
||||||
api_url = env.get('CLC_V2_API_URL', False)
|
|
||||||
|
|
||||||
if api_url:
|
|
||||||
self.clc.defaults.ENDPOINT_URL_V2 = api_url
|
|
||||||
|
|
||||||
if v2_api_token and clc_alias:
|
|
||||||
self.clc._LOGIN_TOKEN_V2 = v2_api_token
|
|
||||||
self.clc._V2_ENABLED = True
|
|
||||||
self.clc.ALIAS = clc_alias
|
|
||||||
elif v2_api_username and v2_api_passwd:
|
|
||||||
self.clc.v2.SetCredentials(
|
|
||||||
api_username=v2_api_username,
|
|
||||||
api_passwd=v2_api_passwd)
|
|
||||||
else:
|
|
||||||
return self.module.fail_json(
|
|
||||||
msg="You must set the CLC_V2_API_USERNAME and CLC_V2_API_PASSWD "
|
|
||||||
"environment variables")
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _set_user_agent(clc):
|
|
||||||
if hasattr(clc, 'SetRequestsSession'):
|
|
||||||
agent_string = "ClcAnsibleModule/" + __version__
|
|
||||||
ses = requests.Session()
|
|
||||||
ses.headers.update({"Api-Client": agent_string})
|
|
||||||
ses.headers['User-Agent'] += " " + agent_string
|
|
||||||
clc.SetRequestsSession(ses)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""
|
|
||||||
Main function
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
module = AnsibleModule(
|
|
||||||
argument_spec=ClcSnapshot.define_argument_spec(),
|
|
||||||
supports_check_mode=True
|
|
||||||
)
|
|
||||||
clc_snapshot = ClcSnapshot(module)
|
|
||||||
clc_snapshot.process_request()
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import *
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
Loading…
Reference in New Issue