diff --git a/monitoring/boundary_meter b/monitoring/boundary_meter index 7606b01deeb..793db3822c5 100644 --- a/monitoring/boundary_meter +++ b/monitoring/boundary_meter @@ -1,5 +1,4 @@ #!/usr/bin/python - # -*- coding: utf-8 -*- """ @@ -7,11 +6,6 @@ Ansible module to add boundary meters. (c) 2013, curtis -* 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 Ansible is free software: you can redistribute it and/or modify @@ -32,6 +26,7 @@ import json import datetime import urllib2 import base64 +import os DOCUMENTATION = ''' @@ -43,7 +38,8 @@ version_added: "1.3" author: curtis@serverascode.com requirements: - 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: name: description: @@ -54,22 +50,15 @@ options: - Whether to create or remove the client from boundary required: false default: true - choices: ["present", "removed"] - aliases: [] + choices: ["present", "absent"] apiid: description: - Organizations boundary API ID required: true - default: null - choices: [] - aliases: [] apikey: description: - Organizations boundary API KEY required: true - default: null - choices: [] - aliases: [] notes: - This module does not yet support tags. @@ -81,7 +70,7 @@ EXAMPLES=''' boundary_meter: apiid=AAAAAA apikey=BBBBBB state=present name={{ inventory_hostname }}" - 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: HAS_URLLIB2 = False -API_HOST = "api.boundary.com" -CONFIG_DIRECTORY = "/etc/bprobe" +api_host = "api.boundary.com" +config_directory = "/etc/bprobe" -# "resource" like thing or APIKEY? -def auth_encode(APIKEY): - auth = base64.standard_b64encode(APIKEY) +# "resource" like thing or apikey? +def auth_encode(apikey): + auth = base64.standard_b64encode(apikey) auth.replace("\n", "") 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": - 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": - 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": - 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": - 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": - 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: - url = build_url(NAME, APIID, action) + if meter_id is None: + url = build_url(name, apiid, action) else: - if CERT_TYPE is None: - url = build_url(NAME, APIID, action, METER_ID) + if cert_type is None: + url = build_url(name, apiid, action, meter_id) 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.add_header("Authorization", "Basic {auth}".format(auth=auth)) request.add_header("Content-Type", "application/json") 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 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: # 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 - body = '{"name":"' + NAME + '"}' + body = '{"name":"' + name + '"}' request.add_data(body) try: @@ -147,54 +136,58 @@ def create_meter(module, NAME, APIID, APIKEY): except urllib2.URLError, e: module.fail_json(msg="Failed to connect to api host to create meter") - # If the config dirctory doesn't exist, create it - if not os.path.exists(CONFIG_DIRECTORY): - os.makedirs(CONFIG_DIRECTORY) + # If the config directory doesn't exist, create it + if not os.path.exists(config_directory): + try: + os.makedirs(config_directory) + except: + module.fail_json("Could not create " + config_directory) + # Download both cert files from the api host types = ['key', 'cert'] for cert_type in types: try: # 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: # 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: 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: result = urllib2.urlopen(request) 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 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 - meters = search_meter(module, NAME, APIID, APIKEY) + meters = search_meter(module, name, apiid, apikey) if len(meters) > 0: return meters[0]['id'] else: 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: return 1, "Meter does not exist, so can't delete it" else: 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 # urllib2 only does GET or POST I believe, but here we need delete request.get_method = lambda: 'DELETE' @@ -202,26 +195,35 @@ def delete_meter(module, NAME, APIID, APIKEY): try: result = urllib2.urlopen(request) 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") + + # 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") - return 0, "Meter " + NAME + " deleted" + return 0, "Meter " + name + " deleted" -def download_request(module, NAME, APIID, APIKEY, CERT_TYPE): +def download_request(module, name, apiid, apikey, cert_type): - meter_id = get_meter_id(module, NAME, APIID, APIKEY) + meter_id = get_meter_id(module, name, apiid, apikey) if meter_id is not None: 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: result = urllib2.urlopen(request) 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: 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() cert_file = open(cert_file_path, 'w') cert_file.write(body) @@ -241,25 +243,23 @@ def main(): module = AnsibleModule( argument_spec=dict( - state=dict(required=True, choices=['present', 'removed']), + state=dict(required=True, choices=['present', 'absent']), name=dict(required=False), apikey=dict(required=True), apiid=dict(required=True), - tags=dict(required=False), ) ) state = module.params['state'] - NAME= module.params['name'] - APIKEY = module.params['apikey'] - APIID = module.params['apiid'] - TAGS = module.params['tags'] + name= module.params['name'] + apikey = module.params['apikey'] + apiid = module.params['apiid'] if state == "present": - (rc, result) = create_meter(module, NAME, APIID, APIKEY) + (rc, result) = create_meter(module, name, apiid, apikey) - if state == "removed": - (rc, result) = delete_meter(module, NAME, APIID, APIKEY) + if state == "absent": + (rc, result) = delete_meter(module, name, apiid, apikey) if rc != 0: module.fail_json(msg=result)