made changes as requested by mpdehaan and added code to remove key and cert pem files on removal of meter
This commit is contained in:
parent
7983139383
commit
b3df566399
1 changed files with 69 additions and 69 deletions
|
@ -1,5 +1,4 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -7,11 +6,6 @@ Ansible module to add boundary meters.
|
||||||
|
|
||||||
(c) 2013, curtis <curtis@serverascode.com>
|
(c) 2013, curtis <curtis@serverascode.com>
|
||||||
|
|
||||||
* Module sponsored by Cybera - a non-profit organization providing
|
|
||||||
high-capacity networking and computing solutions to the province
|
|
||||||
of Alberta.
|
|
||||||
* Please note much of this converted from the boundary puppet module!
|
|
||||||
|
|
||||||
This file is part of Ansible
|
This file is part of Ansible
|
||||||
|
|
||||||
Ansible is free software: you can redistribute it and/or modify
|
Ansible is free software: you can redistribute it and/or modify
|
||||||
|
@ -32,6 +26,7 @@ import json
|
||||||
import datetime
|
import datetime
|
||||||
import urllib2
|
import urllib2
|
||||||
import base64
|
import base64
|
||||||
|
import os
|
||||||
|
|
||||||
DOCUMENTATION = '''
|
DOCUMENTATION = '''
|
||||||
|
|
||||||
|
@ -43,7 +38,8 @@ version_added: "1.3"
|
||||||
author: curtis@serverascode.com
|
author: curtis@serverascode.com
|
||||||
requirements:
|
requirements:
|
||||||
- Boundary API access
|
- Boundary API access
|
||||||
- Boundary client is needed to send data, but not to register meter
|
- bprobe is required to send data, but not to register a meter
|
||||||
|
- Python urllib2
|
||||||
options:
|
options:
|
||||||
name:
|
name:
|
||||||
description:
|
description:
|
||||||
|
@ -54,22 +50,15 @@ options:
|
||||||
- Whether to create or remove the client from boundary
|
- Whether to create or remove the client from boundary
|
||||||
required: false
|
required: false
|
||||||
default: true
|
default: true
|
||||||
choices: ["present", "removed"]
|
choices: ["present", "absent"]
|
||||||
aliases: []
|
|
||||||
apiid:
|
apiid:
|
||||||
description:
|
description:
|
||||||
- Organizations boundary API ID
|
- Organizations boundary API ID
|
||||||
required: true
|
required: true
|
||||||
default: null
|
|
||||||
choices: []
|
|
||||||
aliases: []
|
|
||||||
apikey:
|
apikey:
|
||||||
description:
|
description:
|
||||||
- Organizations boundary API KEY
|
- Organizations boundary API KEY
|
||||||
required: true
|
required: true
|
||||||
default: null
|
|
||||||
choices: []
|
|
||||||
aliases: []
|
|
||||||
|
|
||||||
notes:
|
notes:
|
||||||
- This module does not yet support tags.
|
- This module does not yet support tags.
|
||||||
|
@ -81,7 +70,7 @@ EXAMPLES='''
|
||||||
boundary_meter: apiid=AAAAAA apikey=BBBBBB state=present name={{ inventory_hostname }}"
|
boundary_meter: apiid=AAAAAA apikey=BBBBBB state=present name={{ inventory_hostname }}"
|
||||||
|
|
||||||
- name: Delete meter
|
- name: Delete meter
|
||||||
boundary_meter: apiid=AAAAAA apikey=BBBBBB state=removed name={{ inventory_hostname }}"
|
boundary_meter: apiid=AAAAAA apikey=BBBBBB state=absent name={{ inventory_hostname }}"
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
@ -91,55 +80,55 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
HAS_URLLIB2 = False
|
HAS_URLLIB2 = False
|
||||||
|
|
||||||
API_HOST = "api.boundary.com"
|
api_host = "api.boundary.com"
|
||||||
CONFIG_DIRECTORY = "/etc/bprobe"
|
config_directory = "/etc/bprobe"
|
||||||
|
|
||||||
# "resource" like thing or APIKEY?
|
# "resource" like thing or apikey?
|
||||||
def auth_encode(APIKEY):
|
def auth_encode(apikey):
|
||||||
auth = base64.standard_b64encode(APIKEY)
|
auth = base64.standard_b64encode(apikey)
|
||||||
auth.replace("\n", "")
|
auth.replace("\n", "")
|
||||||
return auth
|
return auth
|
||||||
|
|
||||||
def build_url(NAME, APIID, action, METER_ID=None, CERT_TYPE=None):
|
def build_url(name, apiid, action, meter_id=None, cert_type=None):
|
||||||
if action == "create":
|
if action == "create":
|
||||||
return 'https://{API_HOST}/{APIID}/meters'.format(API_HOST=API_HOST, APIID=APIID)
|
return 'https://{api_host}/{apiid}/meters'.format(api_host=api_host, apiid=apiid)
|
||||||
elif action == "search":
|
elif action == "search":
|
||||||
return "https://{API_HOST}/{APIID}/meters?name={NAME}".format(API_HOST=API_HOST, APIID=APIID, NAME=NAME)
|
return "https://{api_host}/{apiid}/meters?name={name}".format(api_host=api_host, apiid=apiid, name=name)
|
||||||
elif action == "certificates":
|
elif action == "certificates":
|
||||||
return "https://{API_HOST}/{APIID}/meters/{METER_ID}/{CERT_TYPE}.pem".format(API_HOST=API_HOST, APIID=APIID, METER_ID=METER_ID, CERT_TYPE=CERT_TYPE)
|
return "https://{api_host}/{apiid}/meters/{meter_id}/{cert_type}.pem".format(api_host=api_host, apiid=apiid, meter_id=meter_id, cert_type=cert_type)
|
||||||
elif action == "tags":
|
elif action == "tags":
|
||||||
return "https://{API_HOST}/{APIID}/meters/{METER_ID}/tags".format(API_HOST=API_HOST, APIID=APIID, METER_ID=METER_ID)
|
return "https://{api_host}/{apiid}/meters/{meter_id}/tags".format(api_host=api_host, apiid=apiid, meter_id=meter_id)
|
||||||
elif action == "delete":
|
elif action == "delete":
|
||||||
return "https://{API_HOST}/{APIID}/meters/{METER_ID}".format(API_HOST=API_HOST, APIID=APIID, METER_ID=METER_ID)
|
return "https://{api_host}/{apiid}/meters/{meter_id}".format(api_host=api_host, apiid=apiid, meter_id=meter_id)
|
||||||
|
|
||||||
def http_request(NAME, APIID, APIKEY, action, METER_ID=None, CERT_TYPE=None):
|
def http_request(name, apiid, apikey, action, meter_id=None, cert_type=None):
|
||||||
|
|
||||||
if METER_ID is None:
|
if meter_id is None:
|
||||||
url = build_url(NAME, APIID, action)
|
url = build_url(name, apiid, action)
|
||||||
else:
|
else:
|
||||||
if CERT_TYPE is None:
|
if cert_type is None:
|
||||||
url = build_url(NAME, APIID, action, METER_ID)
|
url = build_url(name, apiid, action, meter_id)
|
||||||
else:
|
else:
|
||||||
url = build_url(NAME, APIID, action, METER_ID, CERT_TYPE)
|
url = build_url(name, apiid, action, meter_id, cert_type)
|
||||||
|
|
||||||
auth = auth_encode(APIKEY)
|
auth = auth_encode(apikey)
|
||||||
request = urllib2.Request(url)
|
request = urllib2.Request(url)
|
||||||
request.add_header("Authorization", "Basic {auth}".format(auth=auth))
|
request.add_header("Authorization", "Basic {auth}".format(auth=auth))
|
||||||
request.add_header("Content-Type", "application/json")
|
request.add_header("Content-Type", "application/json")
|
||||||
return request
|
return request
|
||||||
|
|
||||||
def create_meter(module, NAME, APIID, APIKEY):
|
def create_meter(module, name, apiid, apikey):
|
||||||
|
|
||||||
meters = search_meter(module, NAME, APIID, APIKEY)
|
meters = search_meter(module, name, apiid, apikey)
|
||||||
|
|
||||||
if len(meters) > 0:
|
if len(meters) > 0:
|
||||||
# If the meter already exists, do nothing
|
# If the meter already exists, do nothing
|
||||||
module.exit_json(status="Meter " + NAME + " already exists",changed=False)
|
module.exit_json(status="Meter " + name + " already exists",changed=False)
|
||||||
else:
|
else:
|
||||||
# If it doesn't exist, create it
|
# If it doesn't exist, create it
|
||||||
request = http_request(NAME, APIID, APIKEY, action="create")
|
request = http_request(name, apiid, apikey, action="create")
|
||||||
# A create request seems to need a json body with the name of the meter in it
|
# A create request seems to need a json body with the name of the meter in it
|
||||||
body = '{"name":"' + NAME + '"}'
|
body = '{"name":"' + name + '"}'
|
||||||
request.add_data(body)
|
request.add_data(body)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -147,54 +136,58 @@ def create_meter(module, NAME, APIID, APIKEY):
|
||||||
except urllib2.URLError, e:
|
except urllib2.URLError, e:
|
||||||
module.fail_json(msg="Failed to connect to api host to create meter")
|
module.fail_json(msg="Failed to connect to api host to create meter")
|
||||||
|
|
||||||
# If the config dirctory doesn't exist, create it
|
# If the config directory doesn't exist, create it
|
||||||
if not os.path.exists(CONFIG_DIRECTORY):
|
if not os.path.exists(config_directory):
|
||||||
os.makedirs(CONFIG_DIRECTORY)
|
try:
|
||||||
|
os.makedirs(config_directory)
|
||||||
|
except:
|
||||||
|
module.fail_json("Could not create " + config_directory)
|
||||||
|
|
||||||
|
|
||||||
# Download both cert files from the api host
|
# Download both cert files from the api host
|
||||||
types = ['key', 'cert']
|
types = ['key', 'cert']
|
||||||
for cert_type in types:
|
for cert_type in types:
|
||||||
try:
|
try:
|
||||||
# If we can't open the file it's not there, so we should download it
|
# If we can't open the file it's not there, so we should download it
|
||||||
cert_file = open('/etc/bprobe/{CERT_TYPE}.pem'.format(CERT_TYPE=cert_type))
|
cert_file = open('/etc/bprobe/{cert_type}.pem'.format(cert_type=cert_type))
|
||||||
except IOError:
|
except IOError:
|
||||||
# Now download the file...
|
# Now download the file...
|
||||||
rc = download_request(module, NAME, APIID, APIKEY, cert_type)
|
rc = download_request(module, name, apiid, apikey, cert_type)
|
||||||
if rc == False:
|
if rc == False:
|
||||||
module.fail_json("Download request for " + cert_type + ".pem failed")
|
module.fail_json("Download request for " + cert_type + ".pem failed")
|
||||||
|
|
||||||
return 0, "Meter " + NAME + " created"
|
return 0, "Meter " + name + " created"
|
||||||
|
|
||||||
def search_meter(module, NAME, APIID, APIKEY):
|
def search_meter(module, name, apiid, apikey):
|
||||||
|
|
||||||
request = http_request(NAME, APIID, APIKEY, action="search")
|
request = http_request(name, apiid, apikey, action="search")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = urllib2.urlopen(request)
|
result = urllib2.urlopen(request)
|
||||||
except urllib2.URLError, e:
|
except urllib2.URLError, e:
|
||||||
module.fail_json("Failed to connect to api host for searching")
|
module.fail_json("Failed to connect to api host to search for meter")
|
||||||
|
|
||||||
# Return meters
|
# Return meters
|
||||||
return json.loads(result.read())
|
return json.loads(result.read())
|
||||||
|
|
||||||
def get_meter_id(module, NAME, APIID, APIKEY):
|
def get_meter_id(module, name, apiid, apikey):
|
||||||
# In order to delete the meter we need its id
|
# In order to delete the meter we need its id
|
||||||
meters = search_meter(module, NAME, APIID, APIKEY)
|
meters = search_meter(module, name, apiid, apikey)
|
||||||
|
|
||||||
if len(meters) > 0:
|
if len(meters) > 0:
|
||||||
return meters[0]['id']
|
return meters[0]['id']
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def delete_meter(module, NAME, APIID, APIKEY):
|
def delete_meter(module, name, apiid, apikey):
|
||||||
|
|
||||||
meter_id = get_meter_id(module, NAME, APIID, APIKEY)
|
meter_id = get_meter_id(module, name, apiid, apikey)
|
||||||
|
|
||||||
if meter_id is None:
|
if meter_id is None:
|
||||||
return 1, "Meter does not exist, so can't delete it"
|
return 1, "Meter does not exist, so can't delete it"
|
||||||
else:
|
else:
|
||||||
action = "delete"
|
action = "delete"
|
||||||
request = http_request(NAME, APIID, APIKEY, action, meter_id)
|
request = http_request(name, apiid, apikey, action, meter_id)
|
||||||
# See http://stackoverflow.com/questions/4511598/how-to-make-http-delete-method-using-urllib2
|
# See http://stackoverflow.com/questions/4511598/how-to-make-http-delete-method-using-urllib2
|
||||||
# urllib2 only does GET or POST I believe, but here we need delete
|
# urllib2 only does GET or POST I believe, but here we need delete
|
||||||
request.get_method = lambda: 'DELETE'
|
request.get_method = lambda: 'DELETE'
|
||||||
|
@ -202,26 +195,35 @@ def delete_meter(module, NAME, APIID, APIKEY):
|
||||||
try:
|
try:
|
||||||
result = urllib2.urlopen(request)
|
result = urllib2.urlopen(request)
|
||||||
except urllib2.URLError, e:
|
except urllib2.URLError, e:
|
||||||
module.fail_json("Failed to connect to api host for deleting")
|
module.fail_json("Failed to connect to api host to delete meter")
|
||||||
|
|
||||||
return 0, "Meter " + NAME + " deleted"
|
# Each new meter gets a new key.pem and ca.pem file, so they should be deleted
|
||||||
|
types = ['cert', 'key']
|
||||||
|
for cert_type in types:
|
||||||
|
try:
|
||||||
|
cert_file = '/{config_directory}/{cert_type}.pem'.format(config_directory=config_directory,cert_type=cert_type)
|
||||||
|
os.remove(cert_file)
|
||||||
|
except OSError, e: ## if failed, report it back to the user ##
|
||||||
|
module.fail_json("Failed to remove " + cert_type + ".pem file")
|
||||||
|
|
||||||
def download_request(module, NAME, APIID, APIKEY, CERT_TYPE):
|
return 0, "Meter " + name + " deleted"
|
||||||
|
|
||||||
meter_id = get_meter_id(module, NAME, APIID, APIKEY)
|
def download_request(module, name, apiid, apikey, cert_type):
|
||||||
|
|
||||||
|
meter_id = get_meter_id(module, name, apiid, apikey)
|
||||||
|
|
||||||
if meter_id is not None:
|
if meter_id is not None:
|
||||||
action = "certificates"
|
action = "certificates"
|
||||||
request = http_request(NAME, APIID, APIKEY, action, meter_id, CERT_TYPE)
|
request = http_request(name, apiid, apikey, action, meter_id, cert_type)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = urllib2.urlopen(request)
|
result = urllib2.urlopen(request)
|
||||||
except urllib2.URLError, e:
|
except urllib2.URLError, e:
|
||||||
module.fail_json("Failed to connect to api host for certificate download")
|
module.fail_json("Failed to connect to api host to download certificate")
|
||||||
|
|
||||||
if result:
|
if result:
|
||||||
try:
|
try:
|
||||||
cert_file_path = '/{CONFIG_DIR}/{CERT_TYPE}.pem'.format(CONFIG_DIR=CONFIG_DIRECTORY,CERT_TYPE=CERT_TYPE)
|
cert_file_path = '/{config_directory}/{cert_type}.pem'.format(config_directory=config_directory,cert_type=cert_type)
|
||||||
body = result.read()
|
body = result.read()
|
||||||
cert_file = open(cert_file_path, 'w')
|
cert_file = open(cert_file_path, 'w')
|
||||||
cert_file.write(body)
|
cert_file.write(body)
|
||||||
|
@ -241,25 +243,23 @@ def main():
|
||||||
|
|
||||||
module = AnsibleModule(
|
module = AnsibleModule(
|
||||||
argument_spec=dict(
|
argument_spec=dict(
|
||||||
state=dict(required=True, choices=['present', 'removed']),
|
state=dict(required=True, choices=['present', 'absent']),
|
||||||
name=dict(required=False),
|
name=dict(required=False),
|
||||||
apikey=dict(required=True),
|
apikey=dict(required=True),
|
||||||
apiid=dict(required=True),
|
apiid=dict(required=True),
|
||||||
tags=dict(required=False),
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
state = module.params['state']
|
state = module.params['state']
|
||||||
NAME= module.params['name']
|
name= module.params['name']
|
||||||
APIKEY = module.params['apikey']
|
apikey = module.params['apikey']
|
||||||
APIID = module.params['apiid']
|
apiid = module.params['apiid']
|
||||||
TAGS = module.params['tags']
|
|
||||||
|
|
||||||
if state == "present":
|
if state == "present":
|
||||||
(rc, result) = create_meter(module, NAME, APIID, APIKEY)
|
(rc, result) = create_meter(module, name, apiid, apikey)
|
||||||
|
|
||||||
if state == "removed":
|
if state == "absent":
|
||||||
(rc, result) = delete_meter(module, NAME, APIID, APIKEY)
|
(rc, result) = delete_meter(module, name, apiid, apikey)
|
||||||
|
|
||||||
if rc != 0:
|
if rc != 0:
|
||||||
module.fail_json(msg=result)
|
module.fail_json(msg=result)
|
||||||
|
|
Loading…
Reference in a new issue