mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-04-22 19:59:07 +00:00
Initial commit
This commit is contained in:
commit
aebc1b03fd
4861 changed files with 812621 additions and 0 deletions
352
plugins/modules/cloud/centurylink/clc_aa_policy.py
Normal file
352
plugins/modules/cloud/centurylink/clc_aa_policy.py
Normal file
|
|
@ -0,0 +1,352 @@
|
|||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2015 CenturyLink
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
module: clc_aa_policy
|
||||
short_description: 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:
|
||||
- This option does nothing and will be removed in Ansible 2.14.
|
||||
type: bool
|
||||
requirements:
|
||||
- python = 2.7
|
||||
- requests >= 2.5.0
|
||||
- clc-sdk
|
||||
author: "CLC Runner (@clc-runner)"
|
||||
notes:
|
||||
- To use this module, it is required to set the below environment variables which enables access to the
|
||||
Centurylink Cloud
|
||||
- CLC_V2_API_USERNAME, the account login id for the centurylink cloud
|
||||
- CLC_V2_API_PASSWORD, the account password for the centurylink cloud
|
||||
- Alternatively, the module accepts the API token and account alias. The API token can be generated using the
|
||||
CLC account login and password via the HTTP api call @ https://api.ctl.io/v2/authentication/login
|
||||
- CLC_V2_API_TOKEN, the API token generated from https://api.ctl.io/v2/authentication/login
|
||||
- CLC_ACCT_ALIAS, the account alias associated with the centurylink cloud
|
||||
- Users can set CLC_V2_API_URL to specify an endpoint for pointing to a different CLC environment.
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Note - You must set the CLC_V2_API_USERNAME And CLC_V2_API_PASSWD Environment variables before running these examples
|
||||
|
||||
---
|
||||
- name: Create 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
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
policy:
|
||||
description: The anti affinity policy information
|
||||
returned: success
|
||||
type: dict
|
||||
sample:
|
||||
{
|
||||
"id":"1a28dd0988984d87b9cd61fa8da15424",
|
||||
"name":"test_aa_policy",
|
||||
"location":"UC1",
|
||||
"links":[
|
||||
{
|
||||
"rel":"self",
|
||||
"href":"/v2/antiAffinityPolicies/wfad/1a28dd0988984d87b9cd61fa8da15424",
|
||||
"verbs":[
|
||||
"GET",
|
||||
"DELETE",
|
||||
"PUT"
|
||||
]
|
||||
},
|
||||
{
|
||||
"rel":"location",
|
||||
"href":"/v2/datacenters/wfad/UC1",
|
||||
"id":"uc1",
|
||||
"name":"UC1 - US West (Santa Clara)"
|
||||
}
|
||||
]
|
||||
}
|
||||
'''
|
||||
|
||||
__version__ = '${version}'
|
||||
|
||||
import os
|
||||
import traceback
|
||||
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
REQUESTS_IMP_ERR = None
|
||||
try:
|
||||
import requests
|
||||
except ImportError:
|
||||
REQUESTS_IMP_ERR = traceback.format_exc()
|
||||
REQUESTS_FOUND = False
|
||||
else:
|
||||
REQUESTS_FOUND = True
|
||||
|
||||
#
|
||||
# Requires the clc-python-sdk:
|
||||
# sudo pip install clc-sdk
|
||||
#
|
||||
CLC_IMP_ERR = None
|
||||
try:
|
||||
import clc as clc_sdk
|
||||
from clc import CLCException
|
||||
except ImportError:
|
||||
CLC_IMP_ERR = traceback.format_exc()
|
||||
CLC_FOUND = False
|
||||
clc_sdk = None
|
||||
else:
|
||||
CLC_FOUND = True
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
|
||||
|
||||
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=missing_required_lib('clc-sdk'),
|
||||
exception=CLC_IMP_ERR)
|
||||
if not REQUESTS_FOUND:
|
||||
self.module.fail_json(msg=missing_required_lib('requests'),
|
||||
exception=REQUESTS_IMP_ERR)
|
||||
if requests.__version__ and LooseVersion(requests.__version__) < LooseVersion('2.5.0'):
|
||||
self.module.fail_json(
|
||||
msg='requests library version should be >= 2.5.0')
|
||||
|
||||
self._set_user_agent(self.clc)
|
||||
|
||||
@staticmethod
|
||||
def _define_module_argument_spec():
|
||||
"""
|
||||
Define the argument spec for the ansible module
|
||||
:return: argument spec dictionary
|
||||
"""
|
||||
argument_spec = dict(
|
||||
name=dict(required=True),
|
||||
location=dict(required=True),
|
||||
wait=dict(type='bool', removed_in_version='2.14'),
|
||||
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
|
||||
|
||||
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 Affinity Policy using the CLC API.
|
||||
:param p: datacenter to create policy in
|
||||
:return: response dictionary from the CLC API.
|
||||
"""
|
||||
try:
|
||||
return self.clc.v2.AntiAffinity.Create(
|
||||
name=p['name'],
|
||||
location=p['location'])
|
||||
except CLCException as ex:
|
||||
self.module.fail_json(msg='Failed to create anti affinity policy : {0}. {1}'.format(
|
||||
p['name'], ex.response_text
|
||||
))
|
||||
|
||||
def _delete_policy(self, p):
|
||||
"""
|
||||
Delete an Anti Affinity Policy using the CLC API.
|
||||
:param p: datacenter to delete a policy from
|
||||
:return: none
|
||||
"""
|
||||
try:
|
||||
policy = self.policy_dict[p['name']]
|
||||
policy.Delete()
|
||||
except CLCException as ex:
|
||||
self.module.fail_json(msg='Failed to delete anti affinity policy : {0}. {1}'.format(
|
||||
p['name'], ex.response_text
|
||||
))
|
||||
|
||||
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: dictionary 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()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
524
plugins/modules/cloud/centurylink/clc_alert_policy.py
Normal file
524
plugins/modules/cloud/centurylink/clc_alert_policy.py
Normal file
|
|
@ -0,0 +1,524 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
#
|
||||
# Copyright (c) 2015 CenturyLink
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
module: clc_alert_policy
|
||||
short_description: 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
|
||||
id:
|
||||
description:
|
||||
- The alert policy id. This is mutually exclusive with name
|
||||
alert_recipients:
|
||||
description:
|
||||
- A list of recipient email ids to notify the alert.
|
||||
This is required for state 'present'
|
||||
metric:
|
||||
description:
|
||||
- The metric on which to measure the condition that will trigger the alert.
|
||||
This is required for state 'present'
|
||||
choices: ['cpu','memory','disk']
|
||||
duration:
|
||||
description:
|
||||
- The length of time in minutes that the condition must exceed the threshold.
|
||||
This is required for state 'present'
|
||||
threshold:
|
||||
description:
|
||||
- The threshold that will trigger the alert when the metric equals or exceeds it.
|
||||
This is required for state 'present'
|
||||
This number represents a percentage and must be a value between 5.0 - 95.0 that is a multiple of 5.0
|
||||
state:
|
||||
description:
|
||||
- Whether to create or delete the policy.
|
||||
default: present
|
||||
choices: ['present','absent']
|
||||
requirements:
|
||||
- python = 2.7
|
||||
- requests >= 2.5.0
|
||||
- clc-sdk
|
||||
author: "CLC Runner (@clc-runner)"
|
||||
notes:
|
||||
- To use this module, it is required to set the below environment variables which enables access to the
|
||||
Centurylink Cloud
|
||||
- CLC_V2_API_USERNAME, the account login id for the centurylink cloud
|
||||
- CLC_V2_API_PASSWORD, the account password for the centurylink cloud
|
||||
- Alternatively, the module accepts the API token and account alias. The API token can be generated using the
|
||||
CLC account login and password via the HTTP api call @ https://api.ctl.io/v2/authentication/login
|
||||
- CLC_V2_API_TOKEN, the API token generated from https://api.ctl.io/v2/authentication/login
|
||||
- CLC_ACCT_ALIAS, the account alias associated with the centurylink cloud
|
||||
- Users can set CLC_V2_API_URL to specify an endpoint for pointing to a different CLC environment.
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Note - You must set the CLC_V2_API_USERNAME And CLC_V2_API_PASSWD Environment variables before running these examples
|
||||
|
||||
---
|
||||
- name: Create Alert Policy Example
|
||||
hosts: localhost
|
||||
gather_facts: False
|
||||
connection: local
|
||||
tasks:
|
||||
- name: Create an Alert Policy for disk above 80% for 5 minutes
|
||||
clc_alert_policy:
|
||||
alias: wfad
|
||||
name: 'alert for disk > 80%'
|
||||
alert_recipients:
|
||||
- test1@centurylink.com
|
||||
- test2@centurylink.com
|
||||
metric: 'disk'
|
||||
duration: '00:05:00'
|
||||
threshold: 80
|
||||
state: present
|
||||
register: policy
|
||||
|
||||
- name: debug
|
||||
debug: var=policy
|
||||
|
||||
---
|
||||
- name: Delete Alert Policy Example
|
||||
hosts: localhost
|
||||
gather_facts: False
|
||||
connection: local
|
||||
tasks:
|
||||
- name: Delete an Alert Policy
|
||||
clc_alert_policy:
|
||||
alias: wfad
|
||||
name: 'alert for disk > 80%'
|
||||
state: absent
|
||||
register: policy
|
||||
|
||||
- name: debug
|
||||
debug: var=policy
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
policy:
|
||||
description: The alert policy information
|
||||
returned: success
|
||||
type: dict
|
||||
sample:
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"action": "email",
|
||||
"settings": {
|
||||
"recipients": [
|
||||
"user1@domain.com",
|
||||
"user1@domain.com"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"id": "ba54ac54a60d4a4f1ed6d48c1ce240a7",
|
||||
"links": [
|
||||
{
|
||||
"href": "/v2/alertPolicies/alias/ba54ac54a60d4a4fb1d6d48c1ce240a7",
|
||||
"rel": "self",
|
||||
"verbs": [
|
||||
"GET",
|
||||
"DELETE",
|
||||
"PUT"
|
||||
]
|
||||
}
|
||||
],
|
||||
"name": "test_alert",
|
||||
"triggers": [
|
||||
{
|
||||
"duration": "00:05:00",
|
||||
"metric": "disk",
|
||||
"threshold": 80.0
|
||||
}
|
||||
]
|
||||
}
|
||||
'''
|
||||
|
||||
__version__ = '${version}'
|
||||
|
||||
import json
|
||||
import os
|
||||
import traceback
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
REQUESTS_IMP_ERR = None
|
||||
try:
|
||||
import requests
|
||||
except ImportError:
|
||||
REQUESTS_IMP_ERR = traceback.format_exc()
|
||||
REQUESTS_FOUND = False
|
||||
else:
|
||||
REQUESTS_FOUND = True
|
||||
|
||||
#
|
||||
# Requires the clc-python-sdk.
|
||||
# sudo pip install clc-sdk
|
||||
#
|
||||
CLC_IMP_ERR = None
|
||||
try:
|
||||
import clc as clc_sdk
|
||||
from clc import APIFailedResponse
|
||||
except ImportError:
|
||||
CLC_IMP_ERR = traceback.format_exc()
|
||||
CLC_FOUND = False
|
||||
clc_sdk = None
|
||||
else:
|
||||
CLC_FOUND = True
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
|
||||
|
||||
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=missing_required_lib('clc-sdk'), exception=CLC_IMP_ERR)
|
||||
if not REQUESTS_FOUND:
|
||||
self.module.fail_json(msg=missing_required_lib('requests'), exception=REQUESTS_IMP_ERR)
|
||||
if requests.__version__ and LooseVersion(requests.__version__) < LooseVersion('2.5.0'):
|
||||
self.module.fail_json(
|
||||
msg='requests library version should be >= 2.5.0')
|
||||
|
||||
self._set_user_agent(self.clc)
|
||||
|
||||
@staticmethod
|
||||
def _define_module_argument_spec():
|
||||
"""
|
||||
Define the argument spec for the ansible module
|
||||
:return: argument spec dictionary
|
||||
"""
|
||||
argument_spec = dict(
|
||||
name=dict(default=None),
|
||||
id=dict(default=None),
|
||||
alias=dict(required=True, default=None),
|
||||
alert_recipients=dict(type='list', default=None),
|
||||
metric=dict(
|
||||
choices=[
|
||||
'cpu',
|
||||
'memory',
|
||||
'disk'],
|
||||
default=None),
|
||||
duration=dict(type='str', default=None),
|
||||
threshold=dict(type='int', default=None),
|
||||
state=dict(default='present', choices=['present', 'absent'])
|
||||
)
|
||||
mutually_exclusive = [
|
||||
['name', 'id']
|
||||
]
|
||||
return {'argument_spec': argument_spec,
|
||||
'mutually_exclusive': mutually_exclusive}
|
||||
|
||||
# Module Behavior Goodness
|
||||
def process_request(self):
|
||||
"""
|
||||
Process the request - Main Code Path
|
||||
:return: Returns with either an exit_json or fail_json
|
||||
"""
|
||||
p = self.module.params
|
||||
|
||||
self._set_clc_credentials_from_env()
|
||||
self.policy_dict = self._get_alert_policies(p['alias'])
|
||||
|
||||
if p['state'] == 'present':
|
||||
changed, policy = self._ensure_alert_policy_is_present()
|
||||
else:
|
||||
changed, policy = self._ensure_alert_policy_is_absent()
|
||||
|
||||
self.module.exit_json(changed=changed, policy=policy)
|
||||
|
||||
def _set_clc_credentials_from_env(self):
|
||||
"""
|
||||
Set the CLC Credentials on the sdk by reading environment variables
|
||||
:return: none
|
||||
"""
|
||||
env = os.environ
|
||||
v2_api_token = env.get('CLC_V2_API_TOKEN', False)
|
||||
v2_api_username = env.get('CLC_V2_API_USERNAME', False)
|
||||
v2_api_passwd = env.get('CLC_V2_API_PASSWD', False)
|
||||
clc_alias = env.get('CLC_ACCT_ALIAS', False)
|
||||
api_url = env.get('CLC_V2_API_URL', False)
|
||||
|
||||
if api_url:
|
||||
self.clc.defaults.ENDPOINT_URL_V2 = api_url
|
||||
|
||||
if v2_api_token and clc_alias:
|
||||
self.clc._LOGIN_TOKEN_V2 = v2_api_token
|
||||
self.clc._V2_ENABLED = True
|
||||
self.clc.ALIAS = clc_alias
|
||||
elif v2_api_username and v2_api_passwd:
|
||||
self.clc.v2.SetCredentials(
|
||||
api_username=v2_api_username,
|
||||
api_passwd=v2_api_passwd)
|
||||
else:
|
||||
return self.module.fail_json(
|
||||
msg="You must set the CLC_V2_API_USERNAME and CLC_V2_API_PASSWD "
|
||||
"environment variables")
|
||||
|
||||
def _ensure_alert_policy_is_present(self):
|
||||
"""
|
||||
Ensures that the alert policy is present
|
||||
:return: (changed, policy)
|
||||
changed: A flag representing if anything is modified
|
||||
policy: the created/updated alert policy
|
||||
"""
|
||||
changed = False
|
||||
p = self.module.params
|
||||
policy_name = p.get('name')
|
||||
|
||||
if not policy_name:
|
||||
self.module.fail_json(msg='Policy name is a required')
|
||||
policy = self._alert_policy_exists(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)
|
||||
changed: 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 alert policy is updated if anything is changed in the alert policy configuration
|
||||
:param alert_policy: the target alert policy
|
||||
:return: (changed, policy)
|
||||
changed: A flag representing if anything is modified
|
||||
policy: the updated the alert policy
|
||||
"""
|
||||
changed = False
|
||||
p = self.module.params
|
||||
alert_policy_id = alert_policy.get('id')
|
||||
email_list = p.get('alert_recipients')
|
||||
metric = p.get('metric')
|
||||
duration = p.get('duration')
|
||||
threshold = p.get('threshold')
|
||||
policy = alert_policy
|
||||
if (metric and metric != str(alert_policy.get('triggers')[0].get('metric'))) or \
|
||||
(duration and duration != str(alert_policy.get('triggers')[0].get('duration'))) or \
|
||||
(threshold and float(threshold) != float(alert_policy.get('triggers')[0].get('threshold'))):
|
||||
changed = True
|
||||
elif email_list:
|
||||
t_email_list = list(
|
||||
alert_policy.get('actions')[0].get('settings').get('recipients'))
|
||||
if set(email_list) != set(t_email_list):
|
||||
changed = True
|
||||
if changed and not self.module.check_mode:
|
||||
policy = self._update_alert_policy(alert_policy_id)
|
||||
return changed, policy
|
||||
|
||||
def _get_alert_policies(self, alias):
|
||||
"""
|
||||
Get the alert policies for account alias by calling the CLC API.
|
||||
:param alias: the account alias
|
||||
:return: the alert policies for the account alias
|
||||
"""
|
||||
response = {}
|
||||
|
||||
policies = self.clc.v2.API.Call('GET',
|
||||
'/v2/alertPolicies/%s'
|
||||
% alias)
|
||||
|
||||
for policy in policies.get('items'):
|
||||
response[policy.get('id')] = policy
|
||||
return response
|
||||
|
||||
def _create_alert_policy(self):
|
||||
"""
|
||||
Create an alert Policy using the CLC API.
|
||||
:return: response dictionary from the CLC API.
|
||||
"""
|
||||
p = self.module.params
|
||||
alias = p['alias']
|
||||
email_list = p['alert_recipients']
|
||||
metric = p['metric']
|
||||
duration = p['duration']
|
||||
threshold = p['threshold']
|
||||
policy_name = p['name']
|
||||
arguments = json.dumps(
|
||||
{
|
||||
'name': policy_name,
|
||||
'actions': [{
|
||||
'action': 'email',
|
||||
'settings': {
|
||||
'recipients': email_list
|
||||
}
|
||||
}],
|
||||
'triggers': [{
|
||||
'metric': metric,
|
||||
'duration': duration,
|
||||
'threshold': threshold
|
||||
}]
|
||||
}
|
||||
)
|
||||
try:
|
||||
result = self.clc.v2.API.Call(
|
||||
'POST',
|
||||
'/v2/alertPolicies/%s' % alias,
|
||||
arguments)
|
||||
except APIFailedResponse as e:
|
||||
return self.module.fail_json(
|
||||
msg='Unable to create alert policy "{0}". {1}'.format(
|
||||
policy_name, str(e.response_text)))
|
||||
return result
|
||||
|
||||
def _update_alert_policy(self, alert_policy_id):
|
||||
"""
|
||||
Update alert policy using the CLC API.
|
||||
:param alert_policy_id: The clc alert policy id
|
||||
:return: response dictionary from the CLC API.
|
||||
"""
|
||||
p = self.module.params
|
||||
alias = p['alias']
|
||||
email_list = p['alert_recipients']
|
||||
metric = p['metric']
|
||||
duration = p['duration']
|
||||
threshold = p['threshold']
|
||||
policy_name = p['name']
|
||||
arguments = json.dumps(
|
||||
{
|
||||
'name': policy_name,
|
||||
'actions': [{
|
||||
'action': 'email',
|
||||
'settings': {
|
||||
'recipients': email_list
|
||||
}
|
||||
}],
|
||||
'triggers': [{
|
||||
'metric': metric,
|
||||
'duration': duration,
|
||||
'threshold': threshold
|
||||
}]
|
||||
}
|
||||
)
|
||||
try:
|
||||
result = self.clc.v2.API.Call(
|
||||
'PUT', '/v2/alertPolicies/%s/%s' %
|
||||
(alias, alert_policy_id), arguments)
|
||||
except APIFailedResponse as e:
|
||||
return self.module.fail_json(
|
||||
msg='Unable to update alert policy "{0}". {1}'.format(
|
||||
policy_name, str(e.response_text)))
|
||||
return result
|
||||
|
||||
def _delete_alert_policy(self, alias, policy_id):
|
||||
"""
|
||||
Delete an alert policy using the CLC API.
|
||||
:param alias : the account alias
|
||||
:param policy_id: the alert policy id
|
||||
:return: response dictionary from the CLC API.
|
||||
"""
|
||||
try:
|
||||
result = self.clc.v2.API.Call(
|
||||
'DELETE', '/v2/alertPolicies/%s/%s' %
|
||||
(alias, policy_id), None)
|
||||
except APIFailedResponse as e:
|
||||
return self.module.fail_json(
|
||||
msg='Unable to delete alert policy id "{0}". {1}'.format(
|
||||
policy_id, str(e.response_text)))
|
||||
return result
|
||||
|
||||
def _alert_policy_exists(self, 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 policy_id in self.policy_dict:
|
||||
if self.policy_dict.get(policy_id).get('name') == policy_name:
|
||||
result = self.policy_dict.get(policy_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 policy_id in self.policy_dict:
|
||||
if self.policy_dict.get(policy_id).get('name') == alert_policy_name:
|
||||
if not alert_policy_id:
|
||||
alert_policy_id = policy_id
|
||||
else:
|
||||
return module.fail_json(
|
||||
msg='multiple 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()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
300
plugins/modules/cloud/centurylink/clc_blueprint_package.py
Normal file
300
plugins/modules/cloud/centurylink/clc_blueprint_package.py
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2015 CenturyLink
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
module: clc_blueprint_package
|
||||
short_description: 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.
|
||||
required: True
|
||||
package_id:
|
||||
description:
|
||||
- The package id of the blue print.
|
||||
required: True
|
||||
package_params:
|
||||
description:
|
||||
- The dictionary of arguments required to deploy the blue print.
|
||||
default: {}
|
||||
required: False
|
||||
state:
|
||||
description:
|
||||
- Whether to install or uninstall the package. Currently it supports only "present" for install action.
|
||||
required: False
|
||||
default: present
|
||||
choices: ['present']
|
||||
wait:
|
||||
description:
|
||||
- Whether to wait for the tasks to finish before returning.
|
||||
type: bool
|
||||
default: True
|
||||
required: False
|
||||
requirements:
|
||||
- python = 2.7
|
||||
- requests >= 2.5.0
|
||||
- clc-sdk
|
||||
author: "CLC Runner (@clc-runner)"
|
||||
notes:
|
||||
- To use this module, it is required to set the below environment variables which enables access to the
|
||||
Centurylink Cloud
|
||||
- CLC_V2_API_USERNAME, the account login id for the centurylink cloud
|
||||
- CLC_V2_API_PASSWORD, the account password for the centurylink cloud
|
||||
- Alternatively, the module accepts the API token and account alias. The API token can be generated using the
|
||||
CLC account login and password via the HTTP api call @ https://api.ctl.io/v2/authentication/login
|
||||
- CLC_V2_API_TOKEN, the API token generated from https://api.ctl.io/v2/authentication/login
|
||||
- CLC_ACCT_ALIAS, the account alias associated with the centurylink cloud
|
||||
- Users can set CLC_V2_API_URL to specify an endpoint for pointing to a different CLC environment.
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Note - You must set the CLC_V2_API_USERNAME And CLC_V2_API_PASSWD Environment variables before running these examples
|
||||
|
||||
- name: Deploy package
|
||||
clc_blueprint_package:
|
||||
server_ids:
|
||||
- UC1TEST-SERVER1
|
||||
- UC1TEST-SERVER2
|
||||
package_id: 77abb844-579d-478d-3955-c69ab4a7ba1a
|
||||
package_params: {}
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
server_ids:
|
||||
description: The list of server ids that are changed
|
||||
returned: success
|
||||
type: list
|
||||
sample:
|
||||
[
|
||||
"UC1TEST-SERVER1",
|
||||
"UC1TEST-SERVER2"
|
||||
]
|
||||
'''
|
||||
|
||||
__version__ = '${version}'
|
||||
|
||||
import os
|
||||
import traceback
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
REQUESTS_IMP_ERR = None
|
||||
try:
|
||||
import requests
|
||||
except ImportError:
|
||||
REQUESTS_IMP_ERR = traceback.format_exc()
|
||||
REQUESTS_FOUND = False
|
||||
else:
|
||||
REQUESTS_FOUND = True
|
||||
|
||||
#
|
||||
# Requires the clc-python-sdk.
|
||||
# sudo pip install clc-sdk
|
||||
#
|
||||
CLC_IMP_ERR = None
|
||||
try:
|
||||
import clc as clc_sdk
|
||||
from clc import CLCException
|
||||
except ImportError:
|
||||
CLC_IMP_ERR = traceback.format_exc()
|
||||
CLC_FOUND = False
|
||||
clc_sdk = None
|
||||
else:
|
||||
CLC_FOUND = True
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
|
||||
|
||||
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=missing_required_lib('clc-sdk'), exception=CLC_IMP_ERR)
|
||||
if not REQUESTS_FOUND:
|
||||
self.module.fail_json(msg=missing_required_lib('requests'), exception=REQUESTS_IMP_ERR)
|
||||
if requests.__version__ and LooseVersion(
|
||||
requests.__version__) < LooseVersion('2.5.0'):
|
||||
self.module.fail_json(
|
||||
msg='requests library version should be >= 2.5.0')
|
||||
|
||||
self._set_user_agent(self.clc)
|
||||
|
||||
def process_request(self):
|
||||
"""
|
||||
Process the request - Main Code Path
|
||||
:return: Returns with either an exit_json or fail_json
|
||||
"""
|
||||
p = self.module.params
|
||||
changed = False
|
||||
changed_server_ids = []
|
||||
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, request_list = self.ensure_package_installed(
|
||||
server_ids, package_id, package_params)
|
||||
self._wait_for_requests_to_complete(request_list)
|
||||
self.module.exit_json(changed=changed, server_ids=changed_server_ids)
|
||||
|
||||
@staticmethod
|
||||
def define_argument_spec():
|
||||
"""
|
||||
This function defines 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 blueprint package id
|
||||
:param package_params: the package arguments
|
||||
:return: (changed, server_ids, request_list)
|
||||
changed: A flag indicating if a change was made
|
||||
server_ids: The list of servers modified
|
||||
request_list: The list of request objects from clc-sdk
|
||||
"""
|
||||
changed = False
|
||||
request_list = []
|
||||
servers = self._get_servers_from_clc(
|
||||
server_ids,
|
||||
'Failed to get servers from CLC')
|
||||
for server in servers:
|
||||
if not self.module.check_mode:
|
||||
request = self.clc_install_package(
|
||||
server,
|
||||
package_id,
|
||||
package_params)
|
||||
request_list.append(request)
|
||||
changed = True
|
||||
return changed, server_ids, request_list
|
||||
|
||||
def clc_install_package(self, server, package_id, package_params):
|
||||
"""
|
||||
Install the package to a given clc server
|
||||
:param server: The server object where the package needs to be installed
|
||||
:param package_id: The blue print package id
|
||||
:param package_params: the required argument dict for the package installation
|
||||
:return: The result object from the CLC API call
|
||||
"""
|
||||
result = None
|
||||
try:
|
||||
result = server.ExecutePackage(
|
||||
package_id=package_id,
|
||||
parameters=package_params)
|
||||
except CLCException as ex:
|
||||
self.module.fail_json(msg='Failed to install package : {0} to server {1}. {2}'.format(
|
||||
package_id, server.id, ex.message
|
||||
))
|
||||
return result
|
||||
|
||||
def _wait_for_requests_to_complete(self, request_lst):
|
||||
"""
|
||||
Waits until the CLC requests are complete if the wait argument is True
|
||||
:param request_lst: The list of CLC request objects
|
||||
:return: none
|
||||
"""
|
||||
if not self.module.params['wait']:
|
||||
return
|
||||
for request in request_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 server_list: the list of server ids
|
||||
:param message: the error message to raise if there is any error
|
||||
: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()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
581
plugins/modules/cloud/centurylink/clc_firewall_policy.py
Normal file
581
plugins/modules/cloud/centurylink/clc_firewall_policy.py
Normal file
|
|
@ -0,0 +1,581 @@
|
|||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2015 CenturyLink
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
module: clc_firewall_policy
|
||||
short_description: Create/delete/update firewall policies
|
||||
description:
|
||||
- Create or delete or update firewall policies on Centurylink Cloud
|
||||
options:
|
||||
location:
|
||||
description:
|
||||
- Target datacenter for the firewall policy
|
||||
required: True
|
||||
state:
|
||||
description:
|
||||
- Whether to create or delete the firewall policy
|
||||
default: present
|
||||
choices: ['present', 'absent']
|
||||
source:
|
||||
description:
|
||||
- The list of source addresses for traffic on the originating firewall.
|
||||
This is required when state is 'present'
|
||||
destination:
|
||||
description:
|
||||
- The list of destination addresses for traffic on the terminating firewall.
|
||||
This is required when state is 'present'
|
||||
ports:
|
||||
description:
|
||||
- The list of ports associated with the policy.
|
||||
TCP and UDP can take in single ports or port ranges.
|
||||
choices: ['any', 'icmp', 'TCP/123', 'UDP/123', 'TCP/123-456', 'UDP/123-456']
|
||||
firewall_policy_id:
|
||||
description:
|
||||
- Id of the firewall policy. This is required to update or delete an existing firewall policy
|
||||
source_account_alias:
|
||||
description:
|
||||
- CLC alias for the source account
|
||||
required: True
|
||||
destination_account_alias:
|
||||
description:
|
||||
- CLC alias for the destination account
|
||||
wait:
|
||||
description:
|
||||
- Whether to wait for the provisioning tasks to finish before returning.
|
||||
type: bool
|
||||
default: 'yes'
|
||||
enabled:
|
||||
description:
|
||||
- Whether the firewall policy is enabled or disabled
|
||||
choices: [True, False]
|
||||
default: 'yes'
|
||||
requirements:
|
||||
- python = 2.7
|
||||
- requests >= 2.5.0
|
||||
- clc-sdk
|
||||
author: "CLC Runner (@clc-runner)"
|
||||
notes:
|
||||
- To use this module, it is required to set the below environment variables which enables access to the
|
||||
Centurylink Cloud
|
||||
- CLC_V2_API_USERNAME, the account login id for the centurylink cloud
|
||||
- CLC_V2_API_PASSWORD, the account password for the centurylink cloud
|
||||
- Alternatively, the module accepts the API token and account alias. The API token can be generated using the
|
||||
CLC account login and password via the HTTP api call @ https://api.ctl.io/v2/authentication/login
|
||||
- CLC_V2_API_TOKEN, the API token generated from https://api.ctl.io/v2/authentication/login
|
||||
- CLC_ACCT_ALIAS, the account alias associated with the centurylink cloud
|
||||
- Users can set CLC_V2_API_URL to specify an endpoint for pointing to a different CLC environment.
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
---
|
||||
- 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: absent
|
||||
firewall_policy_id: c62105233d7a4231bd2e91b9c791e43e1
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
firewall_policy_id:
|
||||
description: The fire wall policy id
|
||||
returned: success
|
||||
type: str
|
||||
sample: fc36f1bfd47242e488a9c44346438c05
|
||||
firewall_policy:
|
||||
description: The fire wall policy information
|
||||
returned: success
|
||||
type: dict
|
||||
sample:
|
||||
{
|
||||
"destination":[
|
||||
"10.1.1.0/24",
|
||||
"10.2.2.0/24"
|
||||
],
|
||||
"destinationAccount":"wfad",
|
||||
"enabled":true,
|
||||
"id":"fc36f1bfd47242e488a9c44346438c05",
|
||||
"links":[
|
||||
{
|
||||
"href":"http://api.ctl.io/v2-experimental/firewallPolicies/wfad/uc1/fc36f1bfd47242e488a9c44346438c05",
|
||||
"rel":"self",
|
||||
"verbs":[
|
||||
"GET",
|
||||
"PUT",
|
||||
"DELETE"
|
||||
]
|
||||
}
|
||||
],
|
||||
"ports":[
|
||||
"any"
|
||||
],
|
||||
"source":[
|
||||
"10.1.1.0/24",
|
||||
"10.2.2.0/24"
|
||||
],
|
||||
"status":"active"
|
||||
}
|
||||
'''
|
||||
|
||||
__version__ = '${version}'
|
||||
|
||||
import os
|
||||
import traceback
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlparse
|
||||
from time import sleep
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
REQUESTS_IMP_ERR = None
|
||||
try:
|
||||
import requests
|
||||
except ImportError:
|
||||
REQUESTS_IMP_ERR = traceback.format_exc()
|
||||
REQUESTS_FOUND = False
|
||||
else:
|
||||
REQUESTS_FOUND = True
|
||||
|
||||
CLC_IMP_ERR = None
|
||||
try:
|
||||
import clc as clc_sdk
|
||||
from clc import APIFailedResponse
|
||||
except ImportError:
|
||||
CLC_IMP_ERR = traceback.format_exc()
|
||||
CLC_FOUND = False
|
||||
clc_sdk = None
|
||||
else:
|
||||
CLC_FOUND = True
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
|
||||
|
||||
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=missing_required_lib('clc-sdk'), exception=CLC_IMP_ERR)
|
||||
if not REQUESTS_FOUND:
|
||||
self.module.fail_json(msg=missing_required_lib('requests'), exception=REQUESTS_IMP_ERR)
|
||||
if requests.__version__ and LooseVersion(
|
||||
requests.__version__) < LooseVersion('2.5.0'):
|
||||
self.module.fail_json(
|
||||
msg='requests library version should be >= 2.5.0')
|
||||
|
||||
self._set_user_agent(self.clc)
|
||||
|
||||
@staticmethod
|
||||
def _define_module_argument_spec():
|
||||
"""
|
||||
Define the argument spec for the ansible module
|
||||
:return: argument spec dictionary
|
||||
"""
|
||||
argument_spec = dict(
|
||||
location=dict(required=True),
|
||||
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(default=None, type='list'),
|
||||
destination=dict(default=None, type='list'),
|
||||
wait=dict(default=True),
|
||||
state=dict(default='present', choices=['present', 'absent']),
|
||||
enabled=dict(default=True, choices=[True, False])
|
||||
)
|
||||
return argument_spec
|
||||
|
||||
def process_request(self):
|
||||
"""
|
||||
Execute the main code path, and handle the request
|
||||
:return: none
|
||||
"""
|
||||
changed = False
|
||||
firewall_policy = 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()
|
||||
|
||||
if state == 'absent':
|
||||
changed, firewall_policy_id, firewall_policy = self._ensure_firewall_policy_is_absent(
|
||||
source_account_alias, location, self.firewall_dict)
|
||||
|
||||
elif state == 'present':
|
||||
changed, firewall_policy_id, firewall_policy = self._ensure_firewall_policy_is_present(
|
||||
source_account_alias, location, self.firewall_dict)
|
||||
|
||||
return self.module.exit_json(
|
||||
changed=changed,
|
||||
firewall_policy_id=firewall_policy_id,
|
||||
firewall_policy=firewall_policy)
|
||||
|
||||
@staticmethod
|
||||
def _get_policy_id_from_response(response):
|
||||
"""
|
||||
Method to parse out the policy id from creation response
|
||||
:param response: response from firewall creation API call
|
||||
:return: policy_id: firewall policy id from creation call
|
||||
"""
|
||||
url = response.get('links')[0]['href']
|
||||
path = 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 of request parameters for firewall policy
|
||||
:return: (changed, firewall_policy_id, firewall_policy)
|
||||
changed: flag for if a change occurred
|
||||
firewall_policy_id: the firewall policy id that was created/updated
|
||||
firewall_policy: The firewall_policy object
|
||||
"""
|
||||
firewall_policy = None
|
||||
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)
|
||||
changed = True
|
||||
else:
|
||||
firewall_policy = self._get_firewall_policy(
|
||||
source_account_alias, location, firewall_policy_id)
|
||||
if not firewall_policy:
|
||||
return self.module.fail_json(
|
||||
msg='Unable to find the firewall policy id : {0}'.format(
|
||||
firewall_policy_id))
|
||||
changed = self._compare_get_request_with_dict(
|
||||
firewall_policy,
|
||||
firewall_dict)
|
||||
if not self.module.check_mode and changed:
|
||||
self._update_firewall_policy(
|
||||
source_account_alias,
|
||||
location,
|
||||
firewall_policy_id,
|
||||
firewall_dict)
|
||||
if changed and firewall_policy_id:
|
||||
firewall_policy = self._wait_for_requests_to_complete(
|
||||
source_account_alias,
|
||||
location,
|
||||
firewall_policy_id)
|
||||
return changed, firewall_policy_id, firewall_policy
|
||||
|
||||
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: the firewall policy id that was deleted
|
||||
response: response from CLC API call
|
||||
"""
|
||||
changed = False
|
||||
response = []
|
||||
firewall_policy_id = firewall_dict.get('firewall_policy_id')
|
||||
result = self._get_firewall_policy(
|
||||
source_account_alias, location, firewall_policy_id)
|
||||
if result:
|
||||
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):
|
||||
"""
|
||||
Creates the firewall policy for the given account alias
|
||||
:param source_account_alias: the source account alias for the firewall policy
|
||||
:param location: datacenter of the firewall policy
|
||||
:param firewall_dict: dictionary of request parameters for firewall policy
|
||||
: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 APIFailedResponse as e:
|
||||
return self.module.fail_json(
|
||||
msg="Unable to 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 id 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 APIFailedResponse as e:
|
||||
return self.module.fail_json(
|
||||
msg="Unable to delete the firewall policy id : {0}. {1}".format(
|
||||
firewall_policy_id, 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 id to update
|
||||
:param firewall_dict: dictionary of request parameters for firewall policy
|
||||
: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 APIFailedResponse as e:
|
||||
return self.module.fail_json(
|
||||
msg="Unable to update the firewall policy id : {0}. {1}".format(
|
||||
firewall_policy_id, 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 of request parameters for firewall policy
|
||||
: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 - The response from CLC API call
|
||||
"""
|
||||
response = None
|
||||
try:
|
||||
response = self.clc.v2.API.Call(
|
||||
'GET', '/v2-experimental/firewallPolicies/%s/%s/%s' %
|
||||
(source_account_alias, location, firewall_policy_id))
|
||||
except APIFailedResponse as e:
|
||||
if e.response_status_code != 404:
|
||||
self.module.fail_json(
|
||||
msg="Unable to fetch the firewall policy with id : {0}. {1}".format(
|
||||
firewall_policy_id, str(e.response_text)))
|
||||
return response
|
||||
|
||||
def _wait_for_requests_to_complete(
|
||||
self,
|
||||
source_account_alias,
|
||||
location,
|
||||
firewall_policy_id,
|
||||
wait_limit=50):
|
||||
"""
|
||||
Waits until the CLC requests are complete if the wait argument is True
|
||||
:param source_account_alias: The source account alias for the firewall policy
|
||||
:param location: datacenter of the firewall policy
|
||||
:param firewall_policy_id: The firewall policy id
|
||||
:param wait_limit: The number of times to check the status for completion
|
||||
:return: the firewall_policy object
|
||||
"""
|
||||
wait = self.module.params.get('wait')
|
||||
count = 0
|
||||
firewall_policy = None
|
||||
while wait:
|
||||
count += 1
|
||||
firewall_policy = self._get_firewall_policy(
|
||||
source_account_alias, location, firewall_policy_id)
|
||||
status = firewall_policy.get('status')
|
||||
if status == 'active' or count > wait_limit:
|
||||
wait = False
|
||||
else:
|
||||
# wait for 2 seconds
|
||||
sleep(2)
|
||||
return firewall_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=ClcFirewallPolicy._define_module_argument_spec(),
|
||||
supports_check_mode=True)
|
||||
|
||||
clc_firewall = ClcFirewallPolicy(module)
|
||||
clc_firewall.process_request()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
514
plugins/modules/cloud/centurylink/clc_group.py
Normal file
514
plugins/modules/cloud/centurylink/clc_group.py
Normal file
|
|
@ -0,0 +1,514 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
#
|
||||
# Copyright (c) 2015 CenturyLink
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
module: clc_group
|
||||
short_description: 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
|
||||
required: True
|
||||
description:
|
||||
description:
|
||||
- A description of the Server Group
|
||||
required: False
|
||||
parent:
|
||||
description:
|
||||
- The parent group of the server group. If parent is not provided, it creates the group at top level.
|
||||
required: False
|
||||
location:
|
||||
description:
|
||||
- Datacenter to create the group in. If location is not provided, the group gets created in the default datacenter
|
||||
associated with the account
|
||||
required: False
|
||||
state:
|
||||
description:
|
||||
- Whether to create or delete the group
|
||||
default: present
|
||||
choices: ['present', 'absent']
|
||||
wait:
|
||||
description:
|
||||
- Whether to wait for the tasks to finish before returning.
|
||||
type: bool
|
||||
default: True
|
||||
required: False
|
||||
requirements:
|
||||
- python = 2.7
|
||||
- requests >= 2.5.0
|
||||
- clc-sdk
|
||||
author: "CLC Runner (@clc-runner)"
|
||||
notes:
|
||||
- To use this module, it is required to set the below environment variables which enables access to the
|
||||
Centurylink Cloud
|
||||
- CLC_V2_API_USERNAME, the account login id for the centurylink cloud
|
||||
- CLC_V2_API_PASSWORD, the account password for the centurylink cloud
|
||||
- Alternatively, the module accepts the API token and account alias. The API token can be generated using the
|
||||
CLC account login and password via the HTTP api call @ https://api.ctl.io/v2/authentication/login
|
||||
- CLC_V2_API_TOKEN, the API token generated from https://api.ctl.io/v2/authentication/login
|
||||
- CLC_ACCT_ALIAS, the account alias associated with the centurylink cloud
|
||||
- Users can set CLC_V2_API_URL to specify an endpoint for pointing to a different CLC environment.
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
|
||||
# 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
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
group:
|
||||
description: The group information
|
||||
returned: success
|
||||
type: dict
|
||||
sample:
|
||||
{
|
||||
"changeInfo":{
|
||||
"createdBy":"service.wfad",
|
||||
"createdDate":"2015-07-29T18:52:47Z",
|
||||
"modifiedBy":"service.wfad",
|
||||
"modifiedDate":"2015-07-29T18:52:47Z"
|
||||
},
|
||||
"customFields":[
|
||||
|
||||
],
|
||||
"description":"test group",
|
||||
"groups":[
|
||||
|
||||
],
|
||||
"id":"bb5f12a3c6044ae4ad0a03e73ae12cd1",
|
||||
"links":[
|
||||
{
|
||||
"href":"/v2/groups/wfad",
|
||||
"rel":"createGroup",
|
||||
"verbs":[
|
||||
"POST"
|
||||
]
|
||||
},
|
||||
{
|
||||
"href":"/v2/servers/wfad",
|
||||
"rel":"createServer",
|
||||
"verbs":[
|
||||
"POST"
|
||||
]
|
||||
},
|
||||
{
|
||||
"href":"/v2/groups/wfad/bb5f12a3c6044ae4ad0a03e73ae12cd1",
|
||||
"rel":"self",
|
||||
"verbs":[
|
||||
"GET",
|
||||
"PATCH",
|
||||
"DELETE"
|
||||
]
|
||||
},
|
||||
{
|
||||
"href":"/v2/groups/wfad/086ac1dfe0b6411989e8d1b77c4065f0",
|
||||
"id":"086ac1dfe0b6411989e8d1b77c4065f0",
|
||||
"rel":"parentGroup"
|
||||
},
|
||||
{
|
||||
"href":"/v2/groups/wfad/bb5f12a3c6044ae4ad0a03e73ae12cd1/defaults",
|
||||
"rel":"defaults",
|
||||
"verbs":[
|
||||
"GET",
|
||||
"POST"
|
||||
]
|
||||
},
|
||||
{
|
||||
"href":"/v2/groups/wfad/bb5f12a3c6044ae4ad0a03e73ae12cd1/billing",
|
||||
"rel":"billing"
|
||||
},
|
||||
{
|
||||
"href":"/v2/groups/wfad/bb5f12a3c6044ae4ad0a03e73ae12cd1/archive",
|
||||
"rel":"archiveGroupAction"
|
||||
},
|
||||
{
|
||||
"href":"/v2/groups/wfad/bb5f12a3c6044ae4ad0a03e73ae12cd1/statistics",
|
||||
"rel":"statistics"
|
||||
},
|
||||
{
|
||||
"href":"/v2/groups/wfad/bb5f12a3c6044ae4ad0a03e73ae12cd1/upcomingScheduledActivities",
|
||||
"rel":"upcomingScheduledActivities"
|
||||
},
|
||||
{
|
||||
"href":"/v2/groups/wfad/bb5f12a3c6044ae4ad0a03e73ae12cd1/horizontalAutoscalePolicy",
|
||||
"rel":"horizontalAutoscalePolicyMapping",
|
||||
"verbs":[
|
||||
"GET",
|
||||
"PUT",
|
||||
"DELETE"
|
||||
]
|
||||
},
|
||||
{
|
||||
"href":"/v2/groups/wfad/bb5f12a3c6044ae4ad0a03e73ae12cd1/scheduledActivities",
|
||||
"rel":"scheduledActivities",
|
||||
"verbs":[
|
||||
"GET",
|
||||
"POST"
|
||||
]
|
||||
}
|
||||
],
|
||||
"locationId":"UC1",
|
||||
"name":"test group",
|
||||
"status":"active",
|
||||
"type":"default"
|
||||
}
|
||||
'''
|
||||
|
||||
__version__ = '${version}'
|
||||
|
||||
import os
|
||||
import traceback
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
REQUESTS_IMP_ERR = None
|
||||
try:
|
||||
import requests
|
||||
except ImportError:
|
||||
REQUESTS_IMP_ERR = traceback.format_exc()
|
||||
REQUESTS_FOUND = False
|
||||
else:
|
||||
REQUESTS_FOUND = True
|
||||
|
||||
#
|
||||
# Requires the clc-python-sdk.
|
||||
# sudo pip install clc-sdk
|
||||
#
|
||||
CLC_IMP_ERR = None
|
||||
try:
|
||||
import clc as clc_sdk
|
||||
from clc import CLCException
|
||||
except ImportError:
|
||||
CLC_IMP_ERR = traceback.format_exc()
|
||||
CLC_FOUND = False
|
||||
clc_sdk = None
|
||||
else:
|
||||
CLC_FOUND = True
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
|
||||
|
||||
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=missing_required_lib('clc-sdk'), exception=CLC_IMP_ERR)
|
||||
if not REQUESTS_FOUND:
|
||||
self.module.fail_json(msg=missing_required_lib('requests'), exception=REQUESTS_IMP_ERR)
|
||||
if requests.__version__ and LooseVersion(requests.__version__) < LooseVersion('2.5.0'):
|
||||
self.module.fail_json(
|
||||
msg='requests library version should be >= 2.5.0')
|
||||
|
||||
self._set_user_agent(self.clc)
|
||||
|
||||
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, requests = self._ensure_group_is_absent(
|
||||
group_name=group_name, parent_name=parent_name)
|
||||
if requests:
|
||||
self._wait_for_requests_to_complete(requests)
|
||||
else:
|
||||
changed, group = self._ensure_group_is_present(
|
||||
group_name=group_name, parent_name=parent_name, group_description=group_description)
|
||||
try:
|
||||
group = group.data
|
||||
except AttributeError:
|
||||
group = group_name
|
||||
self.module.exit_json(changed=changed, group=group)
|
||||
|
||||
@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),
|
||||
state=dict(default='present', choices=['present', 'absent']),
|
||||
wait=dict(type='bool', default=True))
|
||||
|
||||
return argument_spec
|
||||
|
||||
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)
|
||||
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
|
||||
"""
|
||||
response = None
|
||||
group, parent = self.group_dict.get(group_name)
|
||||
try:
|
||||
response = group.Delete()
|
||||
except CLCException as ex:
|
||||
self.module.fail_json(msg='Failed to delete group :{0}. {1}'.format(
|
||||
group_name, ex.response_text
|
||||
))
|
||||
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
|
||||
"""
|
||||
if not self.root_group:
|
||||
raise AssertionError("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
|
||||
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:
|
||||
group = self._create_group(
|
||||
group=group,
|
||||
parent=parent,
|
||||
description=description)
|
||||
changed = True
|
||||
else:
|
||||
self.module.fail_json(
|
||||
msg="parent group: " +
|
||||
parent +
|
||||
" does not exist")
|
||||
|
||||
return changed, group
|
||||
|
||||
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
|
||||
"""
|
||||
response = None
|
||||
(parent, grandparent) = self.group_dict[parent]
|
||||
try:
|
||||
response = parent.Create(name=group, description=description)
|
||||
except CLCException as ex:
|
||||
self.module.fail_json(msg='Failed to create group :{0}. {1}'.format(
|
||||
group, ex.response_text))
|
||||
return response
|
||||
|
||||
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):
|
||||
"""
|
||||
Walk the tree of groups for a datacenter
|
||||
:param datacenter: string - the datacenter to walk (ex: 'UC1')
|
||||
: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
|
||||
|
||||
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 group request')
|
||||
|
||||
@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()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
930
plugins/modules/cloud/centurylink/clc_loadbalancer.py
Normal file
930
plugins/modules/cloud/centurylink/clc_loadbalancer.py
Normal file
|
|
@ -0,0 +1,930 @@
|
|||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2015 CenturyLink
|
||||
#
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
module: clc_loadbalancer
|
||||
short_description: Create, Delete shared loadbalancers in CenturyLink Cloud.
|
||||
description:
|
||||
- An Ansible module to Create, Delete shared loadbalancers in CenturyLink Cloud.
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- The name of the loadbalancer
|
||||
required: True
|
||||
description:
|
||||
description:
|
||||
- A description for the loadbalancer
|
||||
alias:
|
||||
description:
|
||||
- The alias of your CLC Account
|
||||
required: True
|
||||
location:
|
||||
description:
|
||||
- The location of the datacenter where the load balancer resides in
|
||||
required: True
|
||||
method:
|
||||
description:
|
||||
-The balancing method for the load balancer pool
|
||||
choices: ['leastConnection', 'roundRobin']
|
||||
persistence:
|
||||
description:
|
||||
- The persistence method for the load balancer
|
||||
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 needs to be added to the load balancer pool
|
||||
default: []
|
||||
status:
|
||||
description:
|
||||
- The status of the 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']
|
||||
requirements:
|
||||
- python = 2.7
|
||||
- requests >= 2.5.0
|
||||
- clc-sdk
|
||||
author: "CLC Runner (@clc-runner)"
|
||||
notes:
|
||||
- To use this module, it is required to set the below environment variables which enables access to the
|
||||
Centurylink Cloud
|
||||
- CLC_V2_API_USERNAME, the account login id for the centurylink cloud
|
||||
- CLC_V2_API_PASSWORD, the account password for the centurylink cloud
|
||||
- Alternatively, the module accepts the API token and account alias. The API token can be generated using the
|
||||
CLC account login and password via the HTTP api call @ https://api.ctl.io/v2/authentication/login
|
||||
- CLC_V2_API_TOKEN, the API token generated from https://api.ctl.io/v2/authentication/login
|
||||
- CLC_ACCT_ALIAS, the account alias associated with the centurylink cloud
|
||||
- Users can set CLC_V2_API_URL to specify an endpoint for pointing to a different CLC environment.
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Note - You must set the CLC_V2_API_USERNAME And CLC_V2_API_PASSWD Environment variables before running these examples
|
||||
- name: Create 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
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
loadbalancer:
|
||||
description: The load balancer result object from CLC
|
||||
returned: success
|
||||
type: dict
|
||||
sample:
|
||||
{
|
||||
"description":"test-lb",
|
||||
"id":"ab5b18cb81e94ab9925b61d1ca043fb5",
|
||||
"ipAddress":"66.150.174.197",
|
||||
"links":[
|
||||
{
|
||||
"href":"/v2/sharedLoadBalancers/wfad/wa1/ab5b18cb81e94ab9925b61d1ca043fb5",
|
||||
"rel":"self",
|
||||
"verbs":[
|
||||
"GET",
|
||||
"PUT",
|
||||
"DELETE"
|
||||
]
|
||||
},
|
||||
{
|
||||
"href":"/v2/sharedLoadBalancers/wfad/wa1/ab5b18cb81e94ab9925b61d1ca043fb5/pools",
|
||||
"rel":"pools",
|
||||
"verbs":[
|
||||
"GET",
|
||||
"POST"
|
||||
]
|
||||
}
|
||||
],
|
||||
"name":"test-lb",
|
||||
"pools":[
|
||||
|
||||
],
|
||||
"status":"enabled"
|
||||
}
|
||||
'''
|
||||
|
||||
__version__ = '${version}'
|
||||
|
||||
import json
|
||||
import os
|
||||
import traceback
|
||||
from time import sleep
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
REQUESTS_IMP_ERR = None
|
||||
try:
|
||||
import requests
|
||||
except ImportError:
|
||||
REQUESTS_IMP_ERR = traceback.format_exc()
|
||||
REQUESTS_FOUND = False
|
||||
else:
|
||||
REQUESTS_FOUND = True
|
||||
|
||||
#
|
||||
# Requires the clc-python-sdk.
|
||||
# sudo pip install clc-sdk
|
||||
#
|
||||
CLC_IMP_ERR = None
|
||||
try:
|
||||
import clc as clc_sdk
|
||||
from clc import APIFailedResponse
|
||||
except ImportError:
|
||||
CLC_IMP_ERR = traceback.format_exc()
|
||||
CLC_FOUND = False
|
||||
clc_sdk = None
|
||||
else:
|
||||
CLC_FOUND = True
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
|
||||
|
||||
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=missing_required_lib('clc-sdk'), exception=CLC_IMP_ERR)
|
||||
if not REQUESTS_FOUND:
|
||||
self.module.fail_json(msg=missing_required_lib('requests'), exception=REQUESTS_IMP_ERR)
|
||||
if requests.__version__ and LooseVersion(
|
||||
requests.__version__) < LooseVersion('2.5.0'):
|
||||
self.module.fail_json(
|
||||
msg='requests library version should be >= 2.5.0')
|
||||
|
||||
self._set_user_agent(self.clc)
|
||||
|
||||
def process_request(self):
|
||||
"""
|
||||
Execute the main code path, and handle the request
|
||||
:return: none
|
||||
"""
|
||||
changed = False
|
||||
result_lb = 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 is 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)
|
||||
|
||||
def ensure_loadbalancer_present(
|
||||
self, name, alias, location, description, status):
|
||||
"""
|
||||
Checks to see if a load balancer exists and creates one if it does not.
|
||||
:param name: Name of loadbalancer
|
||||
:param alias: Alias of account
|
||||
:param location: Datacenter
|
||||
:param description: Description of loadbalancer
|
||||
:param status: Enabled / Disabled
|
||||
:return: (changed, result, lb_id)
|
||||
changed: Boolean whether a change was made
|
||||
result: The result object from the CLC load balancer request
|
||||
lb_id: The load balancer id
|
||||
"""
|
||||
changed = False
|
||||
result = name
|
||||
lb_id = self._loadbalancer_exists(name=name)
|
||||
if not lb_id:
|
||||
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 lb_id: The loadbalancer id
|
||||
: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 load balancer pool
|
||||
"""
|
||||
changed = False
|
||||
result = port
|
||||
if not lb_id:
|
||||
return changed, None, None
|
||||
pool_id = self._loadbalancerpool_exists(
|
||||
alias=alias,
|
||||
location=location,
|
||||
port=port,
|
||||
lb_id=lb_id)
|
||||
if not pool_id:
|
||||
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')
|
||||
changed = True
|
||||
|
||||
return changed, result, pool_id
|
||||
|
||||
def ensure_loadbalancer_absent(self, name, alias, location):
|
||||
"""
|
||||
Checks to see if a load balancer exists and deletes it if it does
|
||||
:param name: Name of the load balancer
|
||||
: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 = name
|
||||
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
|
||||
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 name: the name of the load balancer
|
||||
:param port: the port that the load balancer listens on
|
||||
: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:
|
||||
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:
|
||||
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 those 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, result) -
|
||||
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,
|
||||
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, result) -
|
||||
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 removes them if found any
|
||||
: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, result) -
|
||||
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: result: The result from the CLC API call
|
||||
"""
|
||||
result = None
|
||||
try:
|
||||
result = self.clc.v2.API.Call('POST',
|
||||
'/v2/sharedLoadBalancers/%s/%s' % (alias,
|
||||
location),
|
||||
json.dumps({"name": name,
|
||||
"description": description,
|
||||
"status": status}))
|
||||
sleep(1)
|
||||
except APIFailedResponse as e:
|
||||
self.module.fail_json(
|
||||
msg='Unable to create load balancer "{0}". {1}'.format(
|
||||
name, str(e.response_text)))
|
||||
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 = None
|
||||
try:
|
||||
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
|
||||
}))
|
||||
except APIFailedResponse as e:
|
||||
self.module.fail_json(
|
||||
msg='Unable to create pool for load balancer id "{0}". {1}'.format(
|
||||
lb_id, str(e.response_text)))
|
||||
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: result: The result from the CLC API call
|
||||
"""
|
||||
result = None
|
||||
lb_id = self._get_loadbalancer_id(name=name)
|
||||
try:
|
||||
result = self.clc.v2.API.Call(
|
||||
'DELETE', '/v2/sharedLoadBalancers/%s/%s/%s' %
|
||||
(alias, location, lb_id))
|
||||
except APIFailedResponse as e:
|
||||
self.module.fail_json(
|
||||
msg='Unable to delete load balancer "{0}". {1}'.format(
|
||||
name, str(e.response_text)))
|
||||
return result
|
||||
|
||||
def delete_loadbalancerpool(self, alias, location, lb_id, pool_id):
|
||||
"""
|
||||
Delete the 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 load balancer pool
|
||||
:return: result: The result from the delete API call
|
||||
"""
|
||||
result = None
|
||||
try:
|
||||
result = self.clc.v2.API.Call(
|
||||
'DELETE', '/v2/sharedLoadBalancers/%s/%s/%s/pools/%s' %
|
||||
(alias, location, lb_id, pool_id))
|
||||
except APIFailedResponse as e:
|
||||
self.module.fail_json(
|
||||
msg='Unable to delete pool for load balancer id "{0}". {1}'.format(
|
||||
lb_id, str(e.response_text)))
|
||||
return result
|
||||
|
||||
def _get_loadbalancer_id(self, name):
|
||||
"""
|
||||
Retrieves unique ID of loadbalancer
|
||||
:param name: Name of loadbalancer
|
||||
:return: Unique ID of the loadbalancer
|
||||
"""
|
||||
id = None
|
||||
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
|
||||
"""
|
||||
result = None
|
||||
try:
|
||||
result = self.clc.v2.API.Call(
|
||||
'GET', '/v2/sharedLoadBalancers/%s/%s' % (alias, location))
|
||||
except APIFailedResponse as e:
|
||||
self.module.fail_json(
|
||||
msg='Unable to fetch load balancers for account: {0}. {1}'.format(
|
||||
alias, str(e.response_text)))
|
||||
return result
|
||||
|
||||
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
|
||||
try:
|
||||
pool_list = self.clc.v2.API.Call(
|
||||
'GET', '/v2/sharedLoadBalancers/%s/%s/%s/pools' %
|
||||
(alias, location, lb_id))
|
||||
except APIFailedResponse as e:
|
||||
return self.module.fail_json(
|
||||
msg='Unable to fetch the load balancer pools for for load balancer id: {0}. {1}'.format(
|
||||
lb_id, str(e.response_text)))
|
||||
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, 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 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: True / False indicating if the given nodes exist
|
||||
"""
|
||||
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 CLC API call
|
||||
"""
|
||||
result = None
|
||||
if not lb_id:
|
||||
return result
|
||||
if not self.module.check_mode:
|
||||
try:
|
||||
result = self.clc.v2.API.Call('PUT',
|
||||
'/v2/sharedLoadBalancers/%s/%s/%s/pools/%s/nodes'
|
||||
% (alias, location, lb_id, pool_id), json.dumps(nodes))
|
||||
except APIFailedResponse as e:
|
||||
self.module.fail_json(
|
||||
msg='Unable to set nodes for the load balancer pool id "{0}". {1}'.format(
|
||||
pool_id, str(e.response_text)))
|
||||
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_to_add: a list of dictionaries containing the nodes to add
|
||||
:return: (changed, result) -
|
||||
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 node not in nodes:
|
||||
changed = True
|
||||
nodes.append(node)
|
||||
if changed is 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_to_remove: a list of dictionaries containing the nodes to remove
|
||||
:return: (changed, result) -
|
||||
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 is 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 = None
|
||||
try:
|
||||
result = self.clc.v2.API.Call('GET',
|
||||
'/v2/sharedLoadBalancers/%s/%s/%s/pools/%s/nodes'
|
||||
% (alias, location, lb_id, pool_id))
|
||||
except APIFailedResponse as e:
|
||||
self.module.fail_json(
|
||||
msg='Unable to fetch list of available nodes for load balancer pool id: {0}. {1}'.format(
|
||||
pool_id, str(e.response_text)))
|
||||
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),
|
||||
alias=dict(required=True),
|
||||
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'])
|
||||
)
|
||||
return argument_spec
|
||||
|
||||
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()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
962
plugins/modules/cloud/centurylink/clc_modify_server.py
Normal file
962
plugins/modules/cloud/centurylink/clc_modify_server.py
Normal file
|
|
@ -0,0 +1,962 @@
|
|||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2015 CenturyLink
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
module: clc_modify_server
|
||||
short_description: 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.
|
||||
required: True
|
||||
cpu:
|
||||
description:
|
||||
- How many CPUs to update on the server
|
||||
memory:
|
||||
description:
|
||||
- Memory (in GB) to set to the server.
|
||||
anti_affinity_policy_id:
|
||||
description:
|
||||
- The anti affinity policy id to be set for a hyper scale server.
|
||||
This is mutually exclusive with 'anti_affinity_policy_name'
|
||||
anti_affinity_policy_name:
|
||||
description:
|
||||
- The anti affinity policy name to be set for a hyper scale server.
|
||||
This is mutually exclusive with 'anti_affinity_policy_id'
|
||||
alert_policy_id:
|
||||
description:
|
||||
- The alert policy id to be associated to the server.
|
||||
This is mutually exclusive with 'alert_policy_name'
|
||||
alert_policy_name:
|
||||
description:
|
||||
- The alert policy name to be associated to the server.
|
||||
This is mutually exclusive with 'alert_policy_id'
|
||||
state:
|
||||
description:
|
||||
- The state to insure that the provided resources are in.
|
||||
default: 'present'
|
||||
choices: ['present', 'absent']
|
||||
wait:
|
||||
description:
|
||||
- Whether to wait for the provisioning tasks to finish before returning.
|
||||
type: bool
|
||||
default: 'yes'
|
||||
requirements:
|
||||
- python = 2.7
|
||||
- requests >= 2.5.0
|
||||
- clc-sdk
|
||||
author: "CLC Runner (@clc-runner)"
|
||||
notes:
|
||||
- To use this module, it is required to set the below environment variables which enables access to the
|
||||
Centurylink Cloud
|
||||
- CLC_V2_API_USERNAME, the account login id for the centurylink cloud
|
||||
- CLC_V2_API_PASSWORD, the account password for the centurylink cloud
|
||||
- Alternatively, the module accepts the API token and account alias. The API token can be generated using the
|
||||
CLC account login and password via the HTTP api call @ https://api.ctl.io/v2/authentication/login
|
||||
- CLC_V2_API_TOKEN, the API token generated from https://api.ctl.io/v2/authentication/login
|
||||
- CLC_ACCT_ALIAS, the account alias associated with the centurylink cloud
|
||||
- Users can set CLC_V2_API_URL to specify an endpoint for pointing to a different CLC environment.
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Note - You must set the CLC_V2_API_USERNAME And CLC_V2_API_PASSWD Environment variables before running these examples
|
||||
|
||||
- name: set the cpu count to 4 on a server
|
||||
clc_modify_server:
|
||||
server_ids:
|
||||
- UC1TESTSVR01
|
||||
- UC1TESTSVR02
|
||||
cpu: 4
|
||||
state: present
|
||||
|
||||
- name: set the memory to 8GB on a server
|
||||
clc_modify_server:
|
||||
server_ids:
|
||||
- UC1TESTSVR01
|
||||
- UC1TESTSVR02
|
||||
memory: 8
|
||||
state: present
|
||||
|
||||
- name: set the anti affinity policy on a server
|
||||
clc_modify_server:
|
||||
server_ids:
|
||||
- UC1TESTSVR01
|
||||
- UC1TESTSVR02
|
||||
anti_affinity_policy_name: 'aa_policy'
|
||||
state: present
|
||||
|
||||
- name: remove the anti affinity policy on a server
|
||||
clc_modify_server:
|
||||
server_ids:
|
||||
- UC1TESTSVR01
|
||||
- UC1TESTSVR02
|
||||
anti_affinity_policy_name: 'aa_policy'
|
||||
state: absent
|
||||
|
||||
- name: add the alert policy on a server
|
||||
clc_modify_server:
|
||||
server_ids:
|
||||
- UC1TESTSVR01
|
||||
- UC1TESTSVR02
|
||||
alert_policy_name: 'alert_policy'
|
||||
state: present
|
||||
|
||||
- name: remove the alert policy on a server
|
||||
clc_modify_server:
|
||||
server_ids:
|
||||
- UC1TESTSVR01
|
||||
- UC1TESTSVR02
|
||||
alert_policy_name: 'alert_policy'
|
||||
state: absent
|
||||
|
||||
- name: set the memory to 16GB and cpu to 8 core on a lust if servers
|
||||
clc_modify_server:
|
||||
server_ids:
|
||||
- UC1TESTSVR01
|
||||
- UC1TESTSVR02
|
||||
cpu: 8
|
||||
memory: 16
|
||||
state: present
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
server_ids:
|
||||
description: The list of server ids that are changed
|
||||
returned: success
|
||||
type: list
|
||||
sample:
|
||||
[
|
||||
"UC1TEST-SVR01",
|
||||
"UC1TEST-SVR02"
|
||||
]
|
||||
servers:
|
||||
description: The list of server objects that are changed
|
||||
returned: success
|
||||
type: list
|
||||
sample:
|
||||
[
|
||||
{
|
||||
"changeInfo":{
|
||||
"createdBy":"service.wfad",
|
||||
"createdDate":1438196820,
|
||||
"modifiedBy":"service.wfad",
|
||||
"modifiedDate":1438196820
|
||||
},
|
||||
"description":"test-server",
|
||||
"details":{
|
||||
"alertPolicies":[
|
||||
|
||||
],
|
||||
"cpu":1,
|
||||
"customFields":[
|
||||
|
||||
],
|
||||
"diskCount":3,
|
||||
"disks":[
|
||||
{
|
||||
"id":"0:0",
|
||||
"partitionPaths":[
|
||||
|
||||
],
|
||||
"sizeGB":1
|
||||
},
|
||||
{
|
||||
"id":"0:1",
|
||||
"partitionPaths":[
|
||||
|
||||
],
|
||||
"sizeGB":2
|
||||
},
|
||||
{
|
||||
"id":"0:2",
|
||||
"partitionPaths":[
|
||||
|
||||
],
|
||||
"sizeGB":14
|
||||
}
|
||||
],
|
||||
"hostName":"",
|
||||
"inMaintenanceMode":false,
|
||||
"ipAddresses":[
|
||||
{
|
||||
"internal":"10.1.1.1"
|
||||
}
|
||||
],
|
||||
"memoryGB":1,
|
||||
"memoryMB":1024,
|
||||
"partitions":[
|
||||
|
||||
],
|
||||
"powerState":"started",
|
||||
"snapshots":[
|
||||
|
||||
],
|
||||
"storageGB":17
|
||||
},
|
||||
"groupId":"086ac1dfe0b6411989e8d1b77c4065f0",
|
||||
"id":"test-server",
|
||||
"ipaddress":"10.120.45.23",
|
||||
"isTemplate":false,
|
||||
"links":[
|
||||
{
|
||||
"href":"/v2/servers/wfad/test-server",
|
||||
"id":"test-server",
|
||||
"rel":"self",
|
||||
"verbs":[
|
||||
"GET",
|
||||
"PATCH",
|
||||
"DELETE"
|
||||
]
|
||||
},
|
||||
{
|
||||
"href":"/v2/groups/wfad/086ac1dfe0b6411989e8d1b77c4065f0",
|
||||
"id":"086ac1dfe0b6411989e8d1b77c4065f0",
|
||||
"rel":"group"
|
||||
},
|
||||
{
|
||||
"href":"/v2/accounts/wfad",
|
||||
"id":"wfad",
|
||||
"rel":"account"
|
||||
},
|
||||
{
|
||||
"href":"/v2/billing/wfad/serverPricing/test-server",
|
||||
"rel":"billing"
|
||||
},
|
||||
{
|
||||
"href":"/v2/servers/wfad/test-server/publicIPAddresses",
|
||||
"rel":"publicIPAddresses",
|
||||
"verbs":[
|
||||
"POST"
|
||||
]
|
||||
},
|
||||
{
|
||||
"href":"/v2/servers/wfad/test-server/credentials",
|
||||
"rel":"credentials"
|
||||
},
|
||||
{
|
||||
"href":"/v2/servers/wfad/test-server/statistics",
|
||||
"rel":"statistics"
|
||||
},
|
||||
{
|
||||
"href":"/v2/servers/wfad/510ec21ae82d4dc89d28479753bf736a/upcomingScheduledActivities",
|
||||
"rel":"upcomingScheduledActivities"
|
||||
},
|
||||
{
|
||||
"href":"/v2/servers/wfad/510ec21ae82d4dc89d28479753bf736a/scheduledActivities",
|
||||
"rel":"scheduledActivities",
|
||||
"verbs":[
|
||||
"GET",
|
||||
"POST"
|
||||
]
|
||||
},
|
||||
{
|
||||
"href":"/v2/servers/wfad/test-server/capabilities",
|
||||
"rel":"capabilities"
|
||||
},
|
||||
{
|
||||
"href":"/v2/servers/wfad/test-server/alertPolicies",
|
||||
"rel":"alertPolicyMappings",
|
||||
"verbs":[
|
||||
"POST"
|
||||
]
|
||||
},
|
||||
{
|
||||
"href":"/v2/servers/wfad/test-server/antiAffinityPolicy",
|
||||
"rel":"antiAffinityPolicyMapping",
|
||||
"verbs":[
|
||||
"PUT",
|
||||
"DELETE"
|
||||
]
|
||||
},
|
||||
{
|
||||
"href":"/v2/servers/wfad/test-server/cpuAutoscalePolicy",
|
||||
"rel":"cpuAutoscalePolicyMapping",
|
||||
"verbs":[
|
||||
"PUT",
|
||||
"DELETE"
|
||||
]
|
||||
}
|
||||
],
|
||||
"locationId":"UC1",
|
||||
"name":"test-server",
|
||||
"os":"ubuntu14_64Bit",
|
||||
"osType":"Ubuntu 14 64-bit",
|
||||
"status":"active",
|
||||
"storageType":"standard",
|
||||
"type":"standard"
|
||||
}
|
||||
]
|
||||
'''
|
||||
|
||||
__version__ = '${version}'
|
||||
|
||||
import json
|
||||
import os
|
||||
import traceback
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
REQUESTS_IMP_ERR = None
|
||||
try:
|
||||
import requests
|
||||
except ImportError:
|
||||
REQUESTS_IMP_ERR = traceback.format_exc()
|
||||
REQUESTS_FOUND = False
|
||||
else:
|
||||
REQUESTS_FOUND = True
|
||||
|
||||
#
|
||||
# Requires the clc-python-sdk.
|
||||
# sudo pip install clc-sdk
|
||||
#
|
||||
CLC_IMP_ERR = None
|
||||
try:
|
||||
import clc as clc_sdk
|
||||
from clc import CLCException
|
||||
from clc import APIFailedResponse
|
||||
except ImportError:
|
||||
CLC_IMP_ERR = traceback.format_exc()
|
||||
CLC_FOUND = False
|
||||
clc_sdk = None
|
||||
else:
|
||||
CLC_FOUND = True
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
|
||||
|
||||
class ClcModifyServer:
|
||||
clc = clc_sdk
|
||||
|
||||
def __init__(self, module):
|
||||
"""
|
||||
Construct module
|
||||
"""
|
||||
self.clc = clc_sdk
|
||||
self.module = module
|
||||
|
||||
if not CLC_FOUND:
|
||||
self.module.fail_json(msg=missing_required_lib('clc-sdk'), exception=CLC_IMP_ERR)
|
||||
if not REQUESTS_FOUND:
|
||||
self.module.fail_json(msg=missing_required_lib('requests'), exception=REQUESTS_IMP_ERR)
|
||||
if requests.__version__ and LooseVersion(
|
||||
requests.__version__) < LooseVersion('2.5.0'):
|
||||
self.module.fail_json(
|
||||
msg='requests library version should be >= 2.5.0')
|
||||
|
||||
self._set_user_agent(self.clc)
|
||||
|
||||
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
|
||||
cpu = p.get('cpu')
|
||||
memory = p.get('memory')
|
||||
state = p.get('state')
|
||||
if state == 'absent' and (cpu or memory):
|
||||
return self.module.fail_json(
|
||||
msg='\'absent\' state is not supported for \'cpu\' and \'memory\' arguments')
|
||||
|
||||
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, changed_server_ids) = self._modify_servers(
|
||||
server_ids=server_ids)
|
||||
|
||||
self.module.exit_json(
|
||||
changed=changed,
|
||||
server_ids=changed_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")
|
||||
|
||||
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 server_list: The list of server ids
|
||||
:param message: the error message to throw in case of any error
|
||||
: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.message)
|
||||
|
||||
def _modify_servers(self, server_ids):
|
||||
"""
|
||||
modify the servers configuration on the provided list
|
||||
:param server_ids: list of servers to modify
|
||||
:return: a list of dictionaries with server information about the servers that were modified
|
||||
"""
|
||||
p = self.module.params
|
||||
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 = []
|
||||
request_list = []
|
||||
changed_servers = []
|
||||
|
||||
if not isinstance(server_ids, list) or len(server_ids) < 1:
|
||||
return self.module.fail_json(
|
||||
msg='server_ids should be a list of servers, aborting')
|
||||
|
||||
servers = self._get_servers_from_clc(
|
||||
server_ids,
|
||||
'Failed to obtain server list from the CLC API')
|
||||
for server in servers:
|
||||
if state == 'present':
|
||||
server_changed, server_result = self._ensure_server_config(
|
||||
server, server_params)
|
||||
if server_result:
|
||||
request_list.append(server_result)
|
||||
aa_changed = self._ensure_aa_policy_present(
|
||||
server,
|
||||
server_params)
|
||||
ap_changed = self._ensure_alert_policy_present(
|
||||
server,
|
||||
server_params)
|
||||
elif state == 'absent':
|
||||
aa_changed = self._ensure_aa_policy_absent(
|
||||
server,
|
||||
server_params)
|
||||
ap_changed = self._ensure_alert_policy_absent(
|
||||
server,
|
||||
server_params)
|
||||
if server_changed or aa_changed or ap_changed:
|
||||
changed_servers.append(server)
|
||||
changed = True
|
||||
|
||||
self._wait_for_requests(self.module, request_list)
|
||||
self._refresh_servers(self.module, changed_servers)
|
||||
|
||||
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
|
||||
|
||||
def _ensure_server_config(
|
||||
self, server, server_params):
|
||||
"""
|
||||
ensures the server is updated with the provided cpu and memory
|
||||
: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
|
||||
|
||||
if not cpu:
|
||||
cpu = server.cpu
|
||||
if not memory:
|
||||
memory = server.memory
|
||||
if memory != server.memory or cpu != server.cpu:
|
||||
if not self.module.check_mode:
|
||||
result = self._modify_clc_server(
|
||||
self.clc,
|
||||
self.module,
|
||||
server.id,
|
||||
cpu,
|
||||
memory)
|
||||
changed = True
|
||||
return changed, result
|
||||
|
||||
@staticmethod
|
||||
def _modify_clc_server(clc, module, server_id, cpu, memory):
|
||||
"""
|
||||
Modify the memory or CPU of a clc server.
|
||||
:param clc: the clc-sdk instance to use
|
||||
:param module: the AnsibleModule object
|
||||
: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
|
||||
"""
|
||||
result = None
|
||||
acct_alias = clc.v2.Account.GetAlias()
|
||||
try:
|
||||
# Update the server configuration
|
||||
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)
|
||||
except APIFailedResponse as ex:
|
||||
module.fail_json(
|
||||
msg='Unable to update the server configuration for server : "{0}". {1}'.format(
|
||||
server_id, str(ex.response_text)))
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def _wait_for_requests(module, request_list):
|
||||
"""
|
||||
Block until server provisioning requests are completed.
|
||||
:param module: the AnsibleModule object
|
||||
:param request_list: a list of clc-sdk.Request instances
|
||||
:return: none
|
||||
"""
|
||||
wait = module.params.get('wait')
|
||||
if wait:
|
||||
# Requests.WaitUntilComplete() returns the count of failed requests
|
||||
failed_requests_count = sum(
|
||||
[request.WaitUntilComplete() for request in request_list])
|
||||
|
||||
if failed_requests_count > 0:
|
||||
module.fail_json(
|
||||
msg='Unable to process modify server request')
|
||||
|
||||
@staticmethod
|
||||
def _refresh_servers(module, servers):
|
||||
"""
|
||||
Loop through a list of servers and refresh them.
|
||||
:param module: the AnsibleModule object
|
||||
:param servers: list of clc-sdk.Server instances to refresh
|
||||
:return: none
|
||||
"""
|
||||
for server in servers:
|
||||
try:
|
||||
server.Refresh()
|
||||
except CLCException as ex:
|
||||
module.fail_json(msg='Unable to refresh the server {0}. {1}'.format(
|
||||
server.id, ex.message
|
||||
))
|
||||
|
||||
def _ensure_aa_policy_present(
|
||||
self, server, server_params):
|
||||
"""
|
||||
ensures the server is updated with the provided anti affinity policy
|
||||
: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
|
||||
acct_alias = self.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 = self._get_aa_policy_id_by_name(
|
||||
self.clc,
|
||||
self.module,
|
||||
acct_alias,
|
||||
aa_policy_name)
|
||||
current_aa_policy_id = self._get_aa_policy_id_of_server(
|
||||
self.clc,
|
||||
self.module,
|
||||
acct_alias,
|
||||
server.id)
|
||||
|
||||
if aa_policy_id and aa_policy_id != current_aa_policy_id:
|
||||
self._modify_aa_policy(
|
||||
self.clc,
|
||||
self.module,
|
||||
acct_alias,
|
||||
server.id,
|
||||
aa_policy_id)
|
||||
changed = True
|
||||
return changed
|
||||
|
||||
def _ensure_aa_policy_absent(
|
||||
self, server, server_params):
|
||||
"""
|
||||
ensures the provided anti affinity policy is removed from the server
|
||||
: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
|
||||
acct_alias = self.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 = self._get_aa_policy_id_by_name(
|
||||
self.clc,
|
||||
self.module,
|
||||
acct_alias,
|
||||
aa_policy_name)
|
||||
current_aa_policy_id = self._get_aa_policy_id_of_server(
|
||||
self.clc,
|
||||
self.module,
|
||||
acct_alias,
|
||||
server.id)
|
||||
|
||||
if aa_policy_id and aa_policy_id == current_aa_policy_id:
|
||||
self._delete_aa_policy(
|
||||
self.clc,
|
||||
self.module,
|
||||
acct_alias,
|
||||
server.id)
|
||||
changed = True
|
||||
return changed
|
||||
|
||||
@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:
|
||||
try:
|
||||
result = clc.v2.API.Call('PUT',
|
||||
'servers/%s/%s/antiAffinityPolicy' % (
|
||||
acct_alias,
|
||||
server_id),
|
||||
json.dumps({"id": aa_policy_id}))
|
||||
except APIFailedResponse as ex:
|
||||
module.fail_json(
|
||||
msg='Unable to modify anti affinity policy to server : "{0}". {1}'.format(
|
||||
server_id, str(ex.response_text)))
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def _delete_aa_policy(clc, module, acct_alias, server_id):
|
||||
"""
|
||||
Delete 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
|
||||
: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/antiAffinityPolicy' % (
|
||||
acct_alias,
|
||||
server_id),
|
||||
json.dumps({}))
|
||||
except APIFailedResponse as ex:
|
||||
module.fail_json(
|
||||
msg='Unable to delete anti affinity policy to server : "{0}". {1}'.format(
|
||||
server_id, str(ex.response_text)))
|
||||
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
|
||||
try:
|
||||
aa_policies = clc.v2.API.Call(method='GET',
|
||||
url='antiAffinityPolicies/%s' % alias)
|
||||
except APIFailedResponse as ex:
|
||||
return module.fail_json(
|
||||
msg='Unable to fetch anti affinity policies from account alias : "{0}". {1}'.format(
|
||||
alias, str(ex.response_text)))
|
||||
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='multiple anti affinity policies were found with policy name : %s' % aa_policy_name)
|
||||
if not aa_policy_id:
|
||||
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 ex:
|
||||
if ex.response_status_code != 404:
|
||||
module.fail_json(msg='Unable to fetch anti affinity policy for server "{0}". {1}'.format(
|
||||
server_id, str(ex.response_text)))
|
||||
return aa_policy_id
|
||||
|
||||
def _ensure_alert_policy_present(
|
||||
self, server, server_params):
|
||||
"""
|
||||
ensures the server is updated with the provided alert policy
|
||||
: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
|
||||
acct_alias = self.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 = self._get_alert_policy_id_by_name(
|
||||
self.clc,
|
||||
self.module,
|
||||
acct_alias,
|
||||
alert_policy_name)
|
||||
if alert_policy_id and not self._alert_policy_exists(
|
||||
server, alert_policy_id):
|
||||
self._add_alert_policy_to_server(
|
||||
self.clc,
|
||||
self.module,
|
||||
acct_alias,
|
||||
server.id,
|
||||
alert_policy_id)
|
||||
changed = True
|
||||
return changed
|
||||
|
||||
def _ensure_alert_policy_absent(
|
||||
self, server, server_params):
|
||||
"""
|
||||
ensures the alert policy is removed from the server
|
||||
: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
|
||||
|
||||
acct_alias = self.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 = self._get_alert_policy_id_by_name(
|
||||
self.clc,
|
||||
self.module,
|
||||
acct_alias,
|
||||
alert_policy_name)
|
||||
|
||||
if alert_policy_id and self._alert_policy_exists(
|
||||
server, alert_policy_id):
|
||||
self._remove_alert_policy_to_server(
|
||||
self.clc,
|
||||
self.module,
|
||||
acct_alias,
|
||||
server.id,
|
||||
alert_policy_id)
|
||||
changed = True
|
||||
return changed
|
||||
|
||||
@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 APIFailedResponse as ex:
|
||||
module.fail_json(msg='Unable to set alert policy to the server : "{0}". {1}'.format(
|
||||
server_id, str(ex.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 APIFailedResponse as ex:
|
||||
module.fail_json(msg='Unable to remove alert policy from the server : "{0}". {1}'.format(
|
||||
server_id, str(ex.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
|
||||
try:
|
||||
alert_policies = clc.v2.API.Call(method='GET',
|
||||
url='alertPolicies/%s' % alias)
|
||||
except APIFailedResponse as ex:
|
||||
return module.fail_json(msg='Unable to fetch alert policies for account : "{0}". {1}'.format(
|
||||
alias, str(ex.response_text)))
|
||||
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='multiple 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()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
358
plugins/modules/cloud/centurylink/clc_publicip.py
Normal file
358
plugins/modules/cloud/centurylink/clc_publicip.py
Normal file
|
|
@ -0,0 +1,358 @@
|
|||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2015 CenturyLink
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
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:
|
||||
description:
|
||||
- The protocol that the public IP will listen for.
|
||||
default: TCP
|
||||
choices: ['TCP', 'UDP', 'ICMP']
|
||||
ports:
|
||||
description:
|
||||
- A list of ports to expose. This is required when state is 'present'
|
||||
server_ids:
|
||||
description:
|
||||
- A list of servers to create public ips on.
|
||||
required: True
|
||||
state:
|
||||
description:
|
||||
- Determine whether 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']
|
||||
wait:
|
||||
description:
|
||||
- Whether to wait for the tasks to finish before returning.
|
||||
type: bool
|
||||
default: 'yes'
|
||||
requirements:
|
||||
- python = 2.7
|
||||
- requests >= 2.5.0
|
||||
- clc-sdk
|
||||
author: "CLC Runner (@clc-runner)"
|
||||
notes:
|
||||
- To use this module, it is required to set the below environment variables which enables access to the
|
||||
Centurylink Cloud
|
||||
- CLC_V2_API_USERNAME, the account login id for the centurylink cloud
|
||||
- CLC_V2_API_PASSWORD, the account password for the centurylink cloud
|
||||
- Alternatively, the module accepts the API token and account alias. The API token can be generated using the
|
||||
CLC account login and password via the HTTP api call @ https://api.ctl.io/v2/authentication/login
|
||||
- CLC_V2_API_TOKEN, the API token generated from https://api.ctl.io/v2/authentication/login
|
||||
- CLC_ACCT_ALIAS, the account alias associated with the centurylink cloud
|
||||
- Users can set CLC_V2_API_URL to specify an endpoint for pointing to a different CLC environment.
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Note - You must set the CLC_V2_API_USERNAME And CLC_V2_API_PASSWD Environment variables before running these examples
|
||||
|
||||
- name: 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:
|
||||
- UC1TEST-SVR01
|
||||
- UC1TEST-SVR02
|
||||
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:
|
||||
- UC1TEST-SVR01
|
||||
- UC1TEST-SVR02
|
||||
state: absent
|
||||
register: clc
|
||||
|
||||
- name: debug
|
||||
debug:
|
||||
var: clc
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
server_ids:
|
||||
description: The list of server ids that are changed
|
||||
returned: success
|
||||
type: list
|
||||
sample:
|
||||
[
|
||||
"UC1TEST-SVR01",
|
||||
"UC1TEST-SVR02"
|
||||
]
|
||||
'''
|
||||
|
||||
__version__ = '${version}'
|
||||
|
||||
import os
|
||||
import traceback
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
REQUESTS_IMP_ERR = None
|
||||
try:
|
||||
import requests
|
||||
except ImportError:
|
||||
REQUESTS_IMP_ERR = traceback.format_exc()
|
||||
REQUESTS_FOUND = False
|
||||
else:
|
||||
REQUESTS_FOUND = True
|
||||
|
||||
#
|
||||
# Requires the clc-python-sdk.
|
||||
# sudo pip install clc-sdk
|
||||
#
|
||||
CLC_IMP_ERR = None
|
||||
try:
|
||||
import clc as clc_sdk
|
||||
from clc import CLCException
|
||||
except ImportError:
|
||||
CLC_IMP_ERR = traceback.format_exc()
|
||||
CLC_FOUND = False
|
||||
clc_sdk = None
|
||||
else:
|
||||
CLC_FOUND = True
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
|
||||
|
||||
class ClcPublicIp(object):
|
||||
clc = clc_sdk
|
||||
module = None
|
||||
|
||||
def __init__(self, module):
|
||||
"""
|
||||
Construct module
|
||||
"""
|
||||
self.module = module
|
||||
if not CLC_FOUND:
|
||||
self.module.fail_json(msg=missing_required_lib('clc-sdk'), exception=CLC_IMP_ERR)
|
||||
if not REQUESTS_FOUND:
|
||||
self.module.fail_json(msg=missing_required_lib('requests'), exception=REQUESTS_IMP_ERR)
|
||||
if requests.__version__ and LooseVersion(requests.__version__) < LooseVersion('2.5.0'):
|
||||
self.module.fail_json(
|
||||
msg='requests library version should be >= 2.5.0')
|
||||
|
||||
self._set_user_agent(self.clc)
|
||||
|
||||
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()
|
||||
params = self.module.params
|
||||
server_ids = params['server_ids']
|
||||
ports = params['ports']
|
||||
protocol = params['protocol']
|
||||
state = params['state']
|
||||
|
||||
if state == 'present':
|
||||
changed, changed_server_ids, requests = self.ensure_public_ip_present(
|
||||
server_ids=server_ids, protocol=protocol, ports=ports)
|
||||
elif state == 'absent':
|
||||
changed, changed_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=changed_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', choices=['TCP', 'UDP', 'ICMP']),
|
||||
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 = self._add_publicip_to_server(server, ports_to_expose)
|
||||
results.append(result)
|
||||
changed_server_ids.append(server.id)
|
||||
changed = True
|
||||
return changed, changed_server_ids, results
|
||||
|
||||
def _add_publicip_to_server(self, server, ports_to_expose):
|
||||
result = None
|
||||
try:
|
||||
result = server.PublicIPs().Add(ports_to_expose)
|
||||
except CLCException as ex:
|
||||
self.module.fail_json(msg='Failed to add public ip to the server : {0}. {1}'.format(
|
||||
server.id, ex.response_text
|
||||
))
|
||||
return result
|
||||
|
||||
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]
|
||||
for server in servers_to_change:
|
||||
if not self.module.check_mode:
|
||||
result = self._remove_publicip_from_server(server)
|
||||
results.append(result)
|
||||
changed_server_ids.append(server.id)
|
||||
changed = True
|
||||
return changed, changed_server_ids, results
|
||||
|
||||
def _remove_publicip_from_server(self, server):
|
||||
result = None
|
||||
try:
|
||||
for ip_address in server.PublicIPs().public_ips:
|
||||
result = ip_address.Delete()
|
||||
except CLCException as ex:
|
||||
self.module.fail_json(msg='Failed to remove public ip from the server : {0}. {1}'.format(
|
||||
server.id, ex.response_text
|
||||
))
|
||||
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 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()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
1528
plugins/modules/cloud/centurylink/clc_server.py
Normal file
1528
plugins/modules/cloud/centurylink/clc_server.py
Normal file
File diff suppressed because it is too large
Load diff
411
plugins/modules/cloud/centurylink/clc_server_snapshot.py
Normal file
411
plugins/modules/cloud/centurylink/clc_server_snapshot.py
Normal file
|
|
@ -0,0 +1,411 @@
|
|||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2015 CenturyLink
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
module: clc_server_snapshot
|
||||
short_description: 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:
|
||||
- The list of CLC server Ids.
|
||||
required: True
|
||||
expiration_days:
|
||||
description:
|
||||
- The number of days to keep the server snapshot before it expires.
|
||||
default: 7
|
||||
required: False
|
||||
state:
|
||||
description:
|
||||
- The state to insure that the provided resources are in.
|
||||
default: 'present'
|
||||
required: False
|
||||
choices: ['present', 'absent', 'restore']
|
||||
wait:
|
||||
description:
|
||||
- Whether to wait for the provisioning tasks to finish before returning.
|
||||
default: True
|
||||
required: False
|
||||
type: bool
|
||||
requirements:
|
||||
- python = 2.7
|
||||
- requests >= 2.5.0
|
||||
- clc-sdk
|
||||
author: "CLC Runner (@clc-runner)"
|
||||
notes:
|
||||
- To use this module, it is required to set the below environment variables which enables access to the
|
||||
Centurylink Cloud
|
||||
- CLC_V2_API_USERNAME, the account login id for the centurylink cloud
|
||||
- CLC_V2_API_PASSWORD, the account password for the centurylink cloud
|
||||
- Alternatively, the module accepts the API token and account alias. The API token can be generated using the
|
||||
CLC account login and password via the HTTP api call @ https://api.ctl.io/v2/authentication/login
|
||||
- CLC_V2_API_TOKEN, the API token generated from https://api.ctl.io/v2/authentication/login
|
||||
- CLC_ACCT_ALIAS, the account alias associated with the centurylink cloud
|
||||
- Users can set CLC_V2_API_URL to specify an endpoint for pointing to a different CLC environment.
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Note - You must set the CLC_V2_API_USERNAME And CLC_V2_API_PASSWD Environment variables before running these examples
|
||||
|
||||
- name: Create server snapshot
|
||||
clc_server_snapshot:
|
||||
server_ids:
|
||||
- UC1TEST-SVR01
|
||||
- UC1TEST-SVR02
|
||||
expiration_days: 10
|
||||
wait: True
|
||||
state: present
|
||||
|
||||
- name: Restore server snapshot
|
||||
clc_server_snapshot:
|
||||
server_ids:
|
||||
- UC1TEST-SVR01
|
||||
- UC1TEST-SVR02
|
||||
wait: True
|
||||
state: restore
|
||||
|
||||
- name: Delete server snapshot
|
||||
clc_server_snapshot:
|
||||
server_ids:
|
||||
- UC1TEST-SVR01
|
||||
- UC1TEST-SVR02
|
||||
wait: True
|
||||
state: absent
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
server_ids:
|
||||
description: The list of server ids that are changed
|
||||
returned: success
|
||||
type: list
|
||||
sample:
|
||||
[
|
||||
"UC1TEST-SVR01",
|
||||
"UC1TEST-SVR02"
|
||||
]
|
||||
'''
|
||||
|
||||
__version__ = '${version}'
|
||||
|
||||
import os
|
||||
import traceback
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
REQUESTS_IMP_ERR = None
|
||||
try:
|
||||
import requests
|
||||
except ImportError:
|
||||
REQUESTS_IMP_ERR = traceback.format_exc()
|
||||
REQUESTS_FOUND = False
|
||||
else:
|
||||
REQUESTS_FOUND = True
|
||||
|
||||
#
|
||||
# Requires the clc-python-sdk.
|
||||
# sudo pip install clc-sdk
|
||||
#
|
||||
CLC_IMP_ERR = None
|
||||
try:
|
||||
import clc as clc_sdk
|
||||
from clc import CLCException
|
||||
except ImportError:
|
||||
CLC_IMP_ERR = traceback.format_exc()
|
||||
CLC_FOUND = False
|
||||
clc_sdk = None
|
||||
else:
|
||||
CLC_FOUND = True
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
|
||||
|
||||
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=missing_required_lib('clc-sdk'), exception=CLC_IMP_ERR)
|
||||
if not REQUESTS_FOUND:
|
||||
self.module.fail_json(msg=missing_required_lib('requests'), exception=REQUESTS_IMP_ERR)
|
||||
if requests.__version__ and LooseVersion(
|
||||
requests.__version__) < LooseVersion('2.5.0'):
|
||||
self.module.fail_json(
|
||||
msg='requests library version should be >= 2.5.0')
|
||||
|
||||
self._set_user_agent(self.clc)
|
||||
|
||||
def process_request(self):
|
||||
"""
|
||||
Process the request - Main Code Path
|
||||
:return: Returns with either an exit_json or fail_json
|
||||
"""
|
||||
p = self.module.params
|
||||
server_ids = p['server_ids']
|
||||
expiration_days = p['expiration_days']
|
||||
state = p['state']
|
||||
request_list = []
|
||||
changed = False
|
||||
changed_servers = []
|
||||
|
||||
self._set_clc_credentials_from_env()
|
||||
if state == 'present':
|
||||
changed, request_list, changed_servers = self.ensure_server_snapshot_present(
|
||||
server_ids=server_ids,
|
||||
expiration_days=expiration_days)
|
||||
elif state == 'absent':
|
||||
changed, request_list, changed_servers = self.ensure_server_snapshot_absent(
|
||||
server_ids=server_ids)
|
||||
elif state == 'restore':
|
||||
changed, request_list, changed_servers = self.ensure_server_snapshot_restore(
|
||||
server_ids=server_ids)
|
||||
|
||||
self._wait_for_requests_to_complete(request_list)
|
||||
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, request_list, changed_servers)
|
||||
changed: A flag indicating whether any change was made
|
||||
request_list: the list of clc request objects from CLC API call
|
||||
changed_servers: The list of servers ids that are modified
|
||||
"""
|
||||
request_list = []
|
||||
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:
|
||||
request = self._create_server_snapshot(server, expiration_days)
|
||||
request_list.append(request)
|
||||
changed_servers = [
|
||||
server.id for server in servers_to_change if server.id]
|
||||
return changed, request_list, changed_servers
|
||||
|
||||
def _create_server_snapshot(self, server, expiration_days):
|
||||
"""
|
||||
Create the snapshot for the CLC server
|
||||
:param server: the CLC server object
|
||||
:param expiration_days: The number of days to keep the snapshot
|
||||
:return: the create request object from CLC API Call
|
||||
"""
|
||||
result = None
|
||||
try:
|
||||
result = server.CreateSnapshot(
|
||||
delete_existing=True,
|
||||
expiration_days=expiration_days)
|
||||
except CLCException as ex:
|
||||
self.module.fail_json(msg='Failed to create snapshot for server : {0}. {1}'.format(
|
||||
server.id, ex.response_text
|
||||
))
|
||||
return result
|
||||
|
||||
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, request_list, changed_servers)
|
||||
changed: A flag indicating whether any change was made
|
||||
request_list: the list of clc request objects from CLC API call
|
||||
changed_servers: The list of servers ids that are modified
|
||||
"""
|
||||
request_list = []
|
||||
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:
|
||||
request = self._delete_server_snapshot(server)
|
||||
request_list.append(request)
|
||||
changed_servers = [
|
||||
server.id for server in servers_to_change if server.id]
|
||||
return changed, request_list, changed_servers
|
||||
|
||||
def _delete_server_snapshot(self, server):
|
||||
"""
|
||||
Delete snapshot for the CLC server
|
||||
:param server: the CLC server object
|
||||
:return: the delete snapshot request object from CLC API
|
||||
"""
|
||||
result = None
|
||||
try:
|
||||
result = server.DeleteSnapshot()
|
||||
except CLCException as ex:
|
||||
self.module.fail_json(msg='Failed to delete snapshot for server : {0}. {1}'.format(
|
||||
server.id, ex.response_text
|
||||
))
|
||||
return result
|
||||
|
||||
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, request_list, changed_servers)
|
||||
changed: A flag indicating whether any change was made
|
||||
request_list: the list of clc request objects from CLC API call
|
||||
changed_servers: The list of servers ids that are modified
|
||||
"""
|
||||
request_list = []
|
||||
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:
|
||||
request = self._restore_server_snapshot(server)
|
||||
request_list.append(request)
|
||||
changed_servers = [
|
||||
server.id for server in servers_to_change if server.id]
|
||||
return changed, request_list, changed_servers
|
||||
|
||||
def _restore_server_snapshot(self, server):
|
||||
"""
|
||||
Restore snapshot for the CLC server
|
||||
:param server: the CLC server object
|
||||
:return: the restore snapshot request object from CLC API
|
||||
"""
|
||||
result = None
|
||||
try:
|
||||
result = server.RestoreSnapshot()
|
||||
except CLCException as ex:
|
||||
self.module.fail_json(msg='Failed to restore snapshot for server : {0}. {1}'.format(
|
||||
server.id, ex.response_text
|
||||
))
|
||||
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 server snapshot request')
|
||||
|
||||
@staticmethod
|
||||
def define_argument_spec():
|
||||
"""
|
||||
This function defines 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, type='int'),
|
||||
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 server_list: The list of server ids
|
||||
:param message: The error message to throw in case of any error
|
||||
: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()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue