Last active
May 30, 2020 17:30
-
-
Save clutchski/7ccca3166e0b8e54450ecb2f2f71e8a8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import argparse | |
from collections import defaultdict | |
import os | |
import sys | |
import time | |
import requests | |
import json | |
# the API sends back at most 100 results per page | |
RESULTS_PER_PAGE = 100 | |
MAX_RETRIES = 2 | |
def make_request(url, headers, params, retries=0): | |
""" | |
Make request, retry known errors | |
""" | |
if retries >= MAX_RETRIES: | |
raise Exception("Max retries of {} reached".format(MAX_RETRIES)) | |
try: | |
r = requests.get(url, headers=headers, params=params) | |
r.raise_for_status() | |
except requests.exceptions.ConnectionError as e: | |
print("Connection Error: {}".format(e)) | |
print("Retrying in 5 seconds...") | |
time.sleep(5) | |
return make_request(url, headers=headers, params=params, retries=retries+1) | |
except requests.exceptions.Timeout as e: | |
print("Timeout: {}".format(e)) | |
print("Retrying in 5 seconds...") | |
time.sleep(5) | |
return make_request(url, headers=headers, params=params, retries=retries+1) | |
except requests.exceptions.HTTPError as e: | |
if r.status_code // 100 == 5: | |
print("Server error: {}".format(e)) | |
print("Retrying in 5 seconds...") | |
time.sleep(5) | |
return make_request(url, headers=headers, params=params, retries=retries+1) | |
raise | |
return r | |
def is_affected_version(version): | |
if not version: | |
return False | |
parts = version.split(".") | |
try: | |
major, minor, patch = [int(x) for x in parts] | |
except: | |
return False | |
if major >= 6 or (major >= 5 and minor >= 32 and patch >= 7): | |
return False | |
else: | |
return True | |
return False | |
def write_to_file_as_json(filepath, data, out_console): | |
""" | |
Dump data as json in file | |
""" | |
with open (filepath, 'w') as outfile: | |
json.dump(data, outfile, indent=4) | |
if out_console: | |
print(json.dumps(data, indent=4)) | |
def find_agents(api_key, application_key, filepath, site, out_console): | |
base_url = "" | |
base_site = "" | |
if site == "us": | |
base_url = "https://api.datadoghq.com/api" | |
base_site = "https://app.datadoghq.com" | |
print("Checking hosts at app.datadoghq.com") | |
elif site == "eu": | |
base_url = "https://api.datadoghq.eu/api" | |
base_site = "https://app.datadoghq.eu" | |
print("Checking hosts at app.datadoghq.eu") | |
else : | |
raise Exception("Unknown site {}".format(site)) | |
url = base_url + "/v1/hosts" | |
headers = {'DD-API-KEY': api_key, 'DD-APPLICATION-KEY': application_key} | |
base_params = {'sort_field': 'name'} | |
page_start = 0 | |
total_n_hosts = 0 | |
agents_by_version = defaultdict(list) | |
while True: | |
params = base_params.copy() | |
params.update({'start': page_start, 'count': RESULTS_PER_PAGE}) | |
print("Querying data of {} hosts, starting at offset: {}".format(RESULTS_PER_PAGE, page_start)) | |
try: | |
r = make_request(url, headers=headers, params=params) | |
except requests.exceptions.HTTPError as e: | |
if e.response.status_code == 403: | |
print("Client error: {}".format(e)) | |
print("Your API and/or Application key appear to be invalid, please double check them against {}/account/settings#api".format(base_site)) | |
return | |
raise | |
data = r.json() | |
for host in data["host_list"]: | |
host_meta = host["meta"] | |
version = host_meta.get("agent_version", "") | |
if is_affected_version(version): | |
agents_by_version[version].append(host["host_name"]) | |
if data["total_returned"] < RESULTS_PER_PAGE: | |
# if we're getting fewer results than we asked for, we've reached the last page | |
total_n_hosts = data["total_matching"] | |
print("Finished querying all {} hosts".format(total_n_hosts)) | |
break | |
page_start += RESULTS_PER_PAGE | |
print("--- Results") | |
print("Found {} Agents running affected versions (out of {} hosts).".format(sum([len(agents_by_version[agent]) for agent in agents_by_version]), total_n_hosts)) | |
write_to_file_as_json(filepath, agents_by_version, out_console) | |
print("Results written in JSON format to {}".format(filepath)) | |
def main(): | |
parser = argparse.ArgumentParser(description='Finds hosts that run Datadog Agent with version less than 5.32.7 and report to Datadog, and writes their hostnames to a JSON-formatted file ordered by version.') | |
parser.add_argument('--api-key', type=str, help='(required) your API key (can also be specified with the DD_API_KEY environment variable)', default=os.environ.get('DD_API_KEY')) | |
parser.add_argument('--application-key', type=str, help='(required) your Application key (can also be specified with the DD_APPLICATION_KEY environment variable)', default=os.environ.get('DD_APPLICATION_KEY')) | |
parser.add_argument('--site', type=str, help='Datadog site to query. Valid options are "US" and "EU". If you log in to datadoghq.com, use US; if you log into datadoghq.eu, use EU. Default: "US"', default=os.environ.get('DD_SITE', "US")) | |
parser.add_argument('--output-file-path', type=str, help='path of the file where the script results are written, in JSON format. Default: "./hosts_agents.json"', default="./hosts_agents.json", dest="filepath") | |
parser.add_argument('--out-console', help='output results to the console, in addition to writing to file"', action='store_true') | |
args = parser.parse_args() | |
error_message = "" | |
if args.api_key is None: | |
error_message += "\nAPI key required but not specified. Specify it with the DD_API_KEY env var or the --api-key parameter." | |
if args.application_key is None: | |
error_message += "\nApplication key required but not specified. Specify it with the DD_APPLICATION_KEY env var or the --application-key parameter" | |
if args.site.lower() != "eu" and args.site.lower() != "us": | |
error_message += "\nInvalid value for 'site': '{}'. Allowed values for 'site' are 'US' and 'EU'.".format(args.site) | |
if error_message: | |
parser.print_usage() | |
parser.exit(status=2, message=error_message+"\n") | |
find_agents(args.api_key, args.application_key, args.filepath, args.site.lower(), args.out_console) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Need a try .. catch around
In case filesystem permission issues no?