Skip to content

Instantly share code, notes, and snippets.

@jriguera
Created October 14, 2016 09:31
Show Gist options
  • Save jriguera/4c52c57fe43ed04704d3a4e34d6d5ec8 to your computer and use it in GitHub Desktop.
Save jriguera/4c52c57fe43ed04704d3a4e34d6d5ec8 to your computer and use it in GitHub Desktop.
Ansibe cf organizations
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Program to create an ansible inventory from all the deployments, jobs and
instances managed by a BOSH Director.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
# Python 2 and 3 compatibility
from __future__ import unicode_literals, print_function
import sys
import json
import base64
import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
from ansible.module_utils.basic import AnsibleModule
__program__ = "cf_org"
__version__ = "0.1.0"
__author__ = "Jose Riguera"
__year__ = "2016"
__email__ = "<[email protected]>"
__license__ = "MIT"
DOCUMENTATION = '''
---
module: cf_org
short_description: Manage Cloud Foundry Orgs
description:
- Manage Cloud Foundry Orgs
author: "Jose Riguera, [email protected]"
options:
state:
description:
- Desired state of the org
required: false
default: present
choices: [present, absent]
name:
description:
- Name of the org
required: true
default: null
aliases: [id]
admin_user:
description:
- Administrator username/email
required: true
default: null
admin_password:
description:
- Administrator password
required: true
default: null
api_url:
description:
- URL of api end point
required: true
quota:
description:
- Name of quota to associate with the org
required: false
default: default
validate_certs:
description:
- Validate SSL certs. Validation will fail with self-signed certificates.
required: false
default: false
force:
description:
- Force deletion of system org and recursive entities in an org
required: false
default: false
'''
EXAMPLES = '''
# Create org with default quota
- cf_org: state=present name=test admin_user=admin admin_password=abc123 api_url=http://cf.example.com
# Create org specifying quota/change quota of existing org
- cf_org: state=present name=test admin_user=admin admin_password=abc123 quota=runaway api_url=http://cf.example.com
# Delete org
- cf_org: state=absent name=test admin_user=admin admin_password=abc123 api_url=http://cf.example.com
'''
RETURN = '''
dest:
description: destination file/path
returned: success
type: string
sample: "/path/to/file.txt"
src:
description: source file used for the copy on the target machine
returned: changed
type: string
sample: "/home/httpd/.ansible/tmp/ansible-tmp-1423796390.97-147729857856000/source"
md5sum:
description: md5 checksum of the file after running copy
returned: when supported
type: string
sample: "2a5aeecc61dc98c4d780b14b330e3282"
...
'''
class CF(object):
user_agent = "ansible-cloudfoundry"
info_url = '/v2/info'
auth_token_url = '/oauth/token'
quotas_url = '/v2/quota_definitions'
organizations_url = '/v2/organizations'
organization_space_url = '/v2/organizations/%s/spaces'
organization_summary_url = '/v2/organizations/%s/summary'
organization_services_url = '/v2/organizations/%s/services'
organization_space_quota_definitions_url = '/v2/organizations/%s/space_quota_definitions'
apps_space_url = '/v2/spaces/%s/apps'
spaces_summary_url = '/v2/spaces/%s/summary'
app_service_bindings = '/v2/apps/%s/service_bindings'
def __init__(self, api_url, username='', password='', ca_cert=None):
self.session = requests.Session()
self.session.headers.update({
"Accept": "application/json",
"Content-Type": "application/json",
"User-Agent": self.user_agent}
)
self.session.verify = True if ca_cert else False
self.session.cert = ca_cert if ca_cert else None
self.api_url = api_url
self.username = str(username)
self.password = str(password)
def info(self):
url = self.api_url + self.info_url
r = self.session.get(url)
return r.json()
def login(self, username='', password=''):
auth = None
if username:
self.username = str(username)
if password:
self.password = str(password)
if self.username:
url = self.info()['token_endpoint'] + self.auth_token_url
headers = {
'Authorization': "Basic %s" % base64.b64encode("%s:%s" % ('cf', '')),
'Content-Type': "application/x-www-form-urlencoded"
}
params = {
'username': self.username,
'password': self.password,
'client_id': 'cf',
'grant_type': 'password',
'response_type': 'token'
}
resp = self.session.post(url, params=params, headers=headers)
if resp.status_code == requests.codes.ok:
auth = resp.json()
self.session.headers.update({
'Authorization': ("%s %s" % (auth['token_type'], auth['access_token']))
})
return auth
def request(self, method, url, params=None, http_headers=None, data=None):
if http_headers:
headers = dict(self.session.headers)
headers.update(http_headers)
else:
headers = self.session.headers
req = requests.Request(method, url, data=data, headers=headers, params=params).prepare()
res = self.session.send(req)
# len(res.content):
try:
response = res.json()
if response:
if ('error_code' in response and (
response['error_code'] == 'CF-InvalidAuthToken' or
response['error_code'] == 'CF-NotAuthenticated')):
if self.login():
res = self.session.send(req)
response = res.json()
except:
response = {}
return response, res.status_code
def get_org(self, name):
url = self.api_url + self.organizations_url
params = {'q': "name:%s" % str(name)}
response, rcode = self.request('GET', url, params)
num = int(response['total_results'])
if num == 0 or rcode != 200:
return None
else:
return response['resources'][0]
def delete_org(self, guid, async=False, recursive=False):
url = self.api_url + self.organizations_url + '/' + guid
params = {
'async': str(async).lower(),
'recursive': str(recursive).lower()
}
response, rcode = self.request('DELETE', url, params)
if rcode != 204:
return False
return True
def create_org(self, name, quota_guid=None):
url = self.api_url + self.organizations_url
data = {'name': name }
if quota_guid:
data['quota_definition_guid'] = str(quota_guid)
response, rcode = self.request('POST', url, None, None, json.dumps(data))
if rcode != 201:
return None
return response
def update_org(self, guid, name, quota_guid=None):
url = self.api_url + self.organizations_url + '/' + guid
data = {'name': name }
if quota_guid:
data['quota_definition_guid'] = str(quota_guid)
response, rcode = self.request('PUT', url, None, None, json.dumps(data))
if rcode != 201:
return None
return response
def get_quota(self, name):
url = self.api_url + self.quotas_url
params = {'q': "name:%s" % str(name)}
response, rcode = self.request('GET', url, params)
num = int(response['total_results'])
if num == 0 or rcode != 200:
return None
else:
return response['resources'][0]
def delete_quota(self, guid, async=False):
url = self.api_url + self.quotas_url + '/' + guid
params = {
'async': str(async).lower()
}
response, rcode = self.request('DELETE', url, params)
if rcode != 204:
return False
return True
def create_quota(self, name, non_basic_services_allowed, total_services,
total_routes, memory_limit, instance_memory_limit,
total_service_keys=-1, total_reserved_route_ports=-1,
total_private_domains=-1, app_instance_limit=-1):
url = self.api_url + self.quotas_url
data = {
'name': str(name),
"non_basic_services_allowed": str(non_basic_services_allowed).lower(),
"total_services": str(total_services),
"total_routes": str(total_routes),
"memory_limit": str(memory_limit),
"instance_memory_limit": str(instance_memory_limit),
"total_service_keys": str(total_service_keys),
"total_reserved_route_ports": str(total_reserved_route_ports),
"total_private_domains": str(total_private_domains),
"app_instance_limit": str(app_instance_limit)
}
response, rcode = self.request('POST', url, None, None, json.dumps(data))
if rcode != 201:
return None
return response
def update_quota(self, guid, name, non_basic_services_allowed, total_services,
total_routes, memory_limit, instance_memory_limit,
total_service_keys=-1, total_reserved_route_ports=-1,
total_private_domains=-1, app_instance_limit=-1):
url = self.api_url + self.quotas_url + '/' + guid
data = {
'name': str(name),
"non_basic_services_allowed": str(non_basic_services_allowed).lower(),
"total_services": str(total_services),
"total_routes": str(total_routes),
"memory_limit": str(memory_limit),
"instance_memory_limit": str(instance_memory_limit),
"total_service_keys": str(total_service_keys),
"total_reserved_route_ports": str(total_reserved_route_ports),
"total_private_domains": str(total_private_domains),
"app_instance_limit": str(app_instance_limit)
}
response, rcode = self.request('PUT', url, None, None, json.dumps(data))
if rcode != 201:
return None
return response
class CF_Org():
def __init__(self, module):
self.module = module
admin_user = self.module.params['admin_user']
admin_password = self.module.params['admin_password']
api_url = self.module.params['api_url']
self.cf = CF(api_url, admin_user, admin_password)
self.name = self.module.params['name']
self.cf.login()
def run(self):
state = self.module.params['state']
if state == 'present':
result = self.present()
elif state == 'absent':
result = self.absent()
else:
self.module.fail_json(msg='Invalid state: %s' % state)
self.module.exit_json(**result)
def absent(self, async=False, forbidden=['pivotal']):
force = self.module.params['force']
if self.name in forbidden and not force:
self.module.fail_json(msg="Cannot delete a system org")
changed = False
org = self.cf.get_org(self.name)
if org:
org_guid = org['metadata']['guid']
changed = True
if not self.module.check_mode:
if not self.cf.delete_org(org_guid, async, force):
self.module.fail_json(msg='Cannot delete org')
result = {
'changed': changed,
'msg': "CF org %s deleted" % self.name
}
return result
def present(self):
changed = False
quota_name = self.module.params['quota']
quota = self.cf.get_quota(quota_name)
if not quota:
self.module.fail_json(msg="Quota %s not found" % quota_name)
quota_guid = quota['metadata']['guid']
org = self.cf.get_org(self.name)
if not org:
changed = True
if not self.module.check_mode:
org = self.cf.create_org(self.name, quota_guid)
if not org:
self.module.fail_json(msg='Cannot create org')
msg = "CF org %s created" % self.name
else:
guid = org['metadata']['guid']
if org['entity']['quota_definition_guid'] != quota_guid:
changed = True
if not self.module.check_mode:
org = self.cf.update_org(guid, self.name, quota_guid)
if not org:
self.module.fail_json(msg='Cannot update org')
msg = "CF org %s updated" % self.name
else:
msg = "CF org %s not updated" % self.name
result = {
'changed': changed,
'msg': msg,
'data': org
}
return result
def main():
module = AnsibleModule(
argument_spec = dict(
state = dict(default='present', type='str', choices=['present', 'absent']),
name = dict(required=True, type='str', aliases=['id']),
admin_user = dict(required=True, type='str'),
admin_password = dict(required=True, type='str', no_log=True),
api_url = dict(required=True, type='str'),
quota = dict(default='default', type='str'),
validate_certs = dict(default=False, type='bool'),
force = dict(default=False, type='bool'),
),
supports_check_mode = True,
)
cf = CF_Org(module)
cf.run()
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment