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