From 77377cf2e33a6e61e83cc6db40f17d7c208200c0 Mon Sep 17 00:00:00 2001 From: Ed McLain Date: Thu, 11 Jun 2015 17:07:43 -0500 Subject: [PATCH 1/5] Added support for looping internally (e.g. run as a service) and for only pulling mass metrics for the active node in a cluster --- graphitecollectors/f5.py | 143 ++++++++++++++++++++++++--------------- 1 file changed, 88 insertions(+), 55 deletions(-) diff --git a/graphitecollectors/f5.py b/graphitecollectors/f5.py index e9c62aa..5c02927 100755 --- a/graphitecollectors/f5.py +++ b/graphitecollectors/f5.py @@ -95,7 +95,7 @@ def get_parser(): help='Logging output filename', action='store', dest='logfile') icontrol_group = parser.add_argument_group('icontrol') - icontrol_group.add_argument('--f5-username', '--f5-user', + icontrol_group.add_argument('--f5-username', '--f5-user', help='Username for F5 iControl authentication', dest='f5_username', required=True) icontrol_group.add_argument('--f5-password', '--f5-pass', @@ -103,6 +103,11 @@ def get_parser(): dest='f5_password', required=True) icontrol_group.add_argument('--f5-host', help="F5 host", dest="f5_host", required=True) + icontrol_group.add_argument('--loop', help="Gather stats every X seconds in a loop", + dest="loopInterval", type=int, default=0) + icontrol_group.add_argument('--active_only', help="Only gather in depth stats for " + + "active cluster node", action="store_true", dest="active_only", + default=False) carbon_group = parser.add_argument_group('carbon') carbon_group.add_argument('--carbon-host', help="Carbon host", dest="carbon_host") @@ -204,6 +209,13 @@ def convert_to_epoch(year, month, day, hour, minute, second, tz): return(epoch) +def get_cluster_state(ltm_host, user, password): + logging.info("Connecting to BIG-IP to check cluster status") + b = bigsuds.BIGIP(hostname=ltm_host, username=user, password=password) + cstate = b.System.Failover.get_failover_state() + return(cstate) + + def gather_f5_metrics(ltm_host, user, password, prefix, remote_ts, interface_limit, no_ip, no_ipv6, no_icmp, no_icmpv6, no_tcp, no_tmm, no_client_ssl, @@ -753,60 +765,82 @@ def main(): prefix = "bigip.%s" % scrubbed_f5_host logging.debug("prefix = %s" % prefix) - start_timestamp = timestamp_local() - logging.debug("start_timestamp = %s" % start_timestamp) - - metric_list = gather_f5_metrics(args.f5_host, args.f5_username, - args.f5_password, prefix, remote_ts, - args.interfaces, args.no_ip, - args.no_ipv6, args.no_icmp, - args.no_icmpv6, args.no_tcp, args.no_tmm, - args.no_client_ssl, args.no_interface, - args.no_trunk, args.no_cpu, args.no_host, - args.no_snat_pool, - args.no_snat_translation, - args.no_virtual_server, args.no_pool) - - if not args.skip_upload and args.carbon_host: - upload_attempts = 0 - upload_success = False - max_attempts = args.carbon_retries + 1 - while not upload_success and (upload_attempts < max_attempts): - upload_attempts += 1 - logging.info("Uploading metrics (try #%d/%d)..." % - (upload_attempts, max_attempts)) - try: - send_metrics(args.carbon_host, args.carbon_port, metric_list, - args.chunk_size) - except Exception, detail: - logging.error("Unable to upload metrics.") - logging.debug(Exception) - logging.debug(detail) - upload_success = False - if upload_attempts < max_attempts: # don't sleep on last run - logging.info("Sleeping %d seconds before retry..." % - args.carbon_interval) - time.sleep(args.carbon_interval) + running = True + while running: + start_timestamp = timestamp_local() + logging.debug("start_timestamp = %s" % start_timestamp) + if args.active_only: + cstate = get_cluster_state(args.f5_host, args.f5_username, args.f5_password) + + if args.active_only and cstate != 'FAILOVER_STATE_ACTIVE': + metric_list = gather_f5_metrics(args.f5_host, args.f5_username, + args.f5_password, prefix, remote_ts, + args.interfaces, args.no_ip, + args.no_ipv6, args.no_icmp, + args.no_icmpv6, args.no_tcp, True, + True, args.no_interface, + True, args.no_cpu, args.no_host, + True,True,True,True) + else: + metric_list = gather_f5_metrics(args.f5_host, args.f5_username, + args.f5_password, prefix, remote_ts, + args.interfaces, args.no_ip, + args.no_ipv6, args.no_icmp, + args.no_icmpv6, args.no_tcp, args.no_tmm, + args.no_client_ssl, args.no_interface, + args.no_trunk, args.no_cpu, args.no_host, + args.no_snat_pool, + args.no_snat_translation, + args.no_virtual_server, args.no_pool) + + if not args.skip_upload and args.carbon_host: + upload_attempts = 0 + upload_success = False + max_attempts = args.carbon_retries + 1 + while not upload_success and (upload_attempts < max_attempts): + upload_attempts += 1 + logging.info("Uploading metrics (try #%d/%d)..." % + (upload_attempts, max_attempts)) + try: + send_metrics(args.carbon_host, args.carbon_port, metric_list, + args.chunk_size) + except Exception, detail: + logging.error("Unable to upload metrics.") + logging.debug(Exception) + logging.debug(detail) + upload_success = False + if upload_attempts < max_attempts: # don't sleep on last run + logging.info("Sleeping %d seconds before retry..." % + args.carbon_interval) + time.sleep(args.carbon_interval) + else: + upload_success = True + if not upload_success: + logging.error("Unable to upload metrics after %d attempts." % + upload_attempts) + logging.info("Saving collected data to local disk for later " + + "replay...") + date_str = datetime.now().strftime("%Y%m%dT%H%M%S") + logging.debug("date_str = %s" % date_str) + write_json_metrics(metric_list, "%s_%s_fail.json" % + (prefix, date_str)) + else: + logging.info("Dry-run or no carbon host provided -- skipping " + + "upload step.") + + finish_timestamp = timestamp_local() + logging.debug("finish_timestamp = %s" % finish_timestamp) + runtime = finish_timestamp - start_timestamp + logging.info("Elapsed time in seconds is %d." % runtime) + if args.loopInterval > 0: + sleepDuration = args.loopInterval - runtime + if sleepDuration > 0: + logging.info("Sleeping for %d seconds" % sleepDuration) + time.sleep(sleepDuration) else: - upload_success = True - if not upload_success: - logging.error("Unable to upload metrics after %d attempts." % - upload_attempts) - logging.info("Saving collected data to local disk for later " + - "replay...") - date_str = datetime.now().strftime("%Y%m%dT%H%M%S") - logging.debug("date_str = %s" % date_str) - write_json_metrics(metric_list, "%s_%s_fail.json" % - (prefix, date_str)) - else: - logging.info("Dry-run or no carbon host provided -- skipping " + - "upload step.") - - finish_timestamp = timestamp_local() - logging.debug("finish_timestamp = %s" % finish_timestamp) - runtime = finish_timestamp - start_timestamp - logging.info("Elapsed time in seconds is %d." % runtime) - + logging.info("Sleep < 0 for loop of %d seconds - Not Sleeping", % args.loopInterval) + else: + running = False if __name__ == '__main__': main() @@ -817,4 +851,3 @@ def main(): # - detect connection failures, ie. unable to connect to server # - put each metric collection in a try expect and return partial # - reload capabilities should be moved into separate utility - From 3889caf8d08a9e5367ba92b0c5669380fe51009d Mon Sep 17 00:00:00 2001 From: Ed McLain Date: Thu, 11 Jun 2015 17:12:01 -0500 Subject: [PATCH 2/5] Fixed typo --- graphitecollectors/f5.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphitecollectors/f5.py b/graphitecollectors/f5.py index 5c02927..0001784 100755 --- a/graphitecollectors/f5.py +++ b/graphitecollectors/f5.py @@ -838,7 +838,7 @@ def main(): logging.info("Sleeping for %d seconds" % sleepDuration) time.sleep(sleepDuration) else: - logging.info("Sleep < 0 for loop of %d seconds - Not Sleeping", % args.loopInterval) + logging.info("Sleep < 0 for loop of %d seconds - Not Sleeping" % args.loopInterval) else: running = False From 951319bf63f9471309ad172c8ac307b2a6099d7c Mon Sep 17 00:00:00 2001 From: Ed McLain Date: Fri, 31 Jul 2015 12:12:05 -0500 Subject: [PATCH 3/5] Moved stat processing ot function, added ability to pull pool_member stats, added ability to disable ssl validation on new systems --- graphitecollectors/f5.py | 263 +++++++++++++++++---------------------- 1 file changed, 111 insertions(+), 152 deletions(-) diff --git a/graphitecollectors/f5.py b/graphitecollectors/f5.py index 0001784..2a85a1c 100755 --- a/graphitecollectors/f5.py +++ b/graphitecollectors/f5.py @@ -13,7 +13,7 @@ from datetime import tzinfo, timedelta, datetime from pprint import pformat -__VERSION__ = "1.5" +__VERSION__ = "1.5.1" # list of pool statistics to monitor @@ -26,6 +26,21 @@ 'server_side_total_connections', 'total_requests'] +# list of pool_member statistics to monitor +POOL_MEMBER_STATISTICS = [ 'server_side_bytes_in', + 'server_side_bytes_out', + 'server_side_packets_in', + 'server_side_packets_out', + 'server_side_current_connections', + 'server_side_maximum_connections', + 'total_requests', + 'current_sessions' + 'connqueue_connections', + 'connqueue_age_oldest_entry', + 'connqueue_age_moving_avg', + 'connqueue_age_exponential_decay_max', + 'connqueue_serviced'] + # list of virtual server statistics to monitor # syn cookie statistics for virtual servers available # in 11.4+ @@ -73,7 +88,8 @@ # Host -HOST_STATISTICS = ['memory_total_bytes', +HOST_STATISTICS = [] +HOST_MEMORY_STATISTICS = ['memory_total_bytes', 'memory_used_bytes'] def get_parser(): @@ -108,6 +124,8 @@ def get_parser(): icontrol_group.add_argument('--active_only', help="Only gather in depth stats for " + "active cluster node", action="store_true", dest="active_only", default=False) + icontrol_group.add_argument('--no-ssl-verify', help="Do not validate SSL Cert", + dest="sslverify", default=True, action="store_false") carbon_group = parser.add_argument_group('carbon') carbon_group.add_argument('--carbon-host', help="Carbon host", dest="carbon_host") @@ -165,6 +183,8 @@ def get_parser(): metric_group.add_argument('--no-virtual-server', action="store_true", dest="no_virtual_server") metric_group.add_argument('--no-pool', action="store_true", dest="no_pool") + metric_group.add_argument('--no-pool-members', action="store_true", + dest="no_pool_members") return parser @@ -215,12 +235,32 @@ def get_cluster_state(ltm_host, user, password): cstate = b.System.Failover.get_failover_state() return(cstate) +# Itterates a list of F5 statistics [f5Input] and compares that with a stats list [statsList] to see +# what should be stored. If statList is null then all stats are saved. +# It then creates a metric entry using statPath for the pre-path value and inTime for the timestamp +def itterate_statistics(f5Input, statPath, inTime, statsList = None): + metrics = [] + for y in f5Input: + stat_name = y['type'].split("STATISTIC_")[-1].lower() + if statsList is None or stat_name in statsList: + high = y['value']['high'] + low = y['value']['low'] + stat_val = convert_to_64_bit(high, low) + stat_path = "%s.%s" % (statPath, stat_name) + metric = (stat_path, (inTime, stat_val)) + logging.debug("is_metric = %s" % str(metric)) + metrics.append(metric) + return metrics + +# Makes sure the path is correct for Graphite +def cleanStatPath(inPath): + return inPath.replace('/','.').replace('..','.') def gather_f5_metrics(ltm_host, user, password, prefix, remote_ts, interface_limit, no_ip, no_ipv6, no_icmp, no_icmpv6, no_tcp, no_tmm, no_client_ssl, no_interface, no_trunk, no_cpu, no_host, no_snat_pool, - no_snat_translation, no_virtual_server, no_pool): + no_snat_translation, no_virtual_server, no_pool, no_pool_members): """ Connects to an F5 via iControl and pulls statistics. """ metric_list = [] @@ -258,15 +298,9 @@ def gather_f5_metrics(ltm_host, user, password, prefix, remote_ts, else: now = timestamp_local() logging.debug("Local timestamp is %s." % now) - for y in statistics: - stat_name = y['type'].split("STATISTIC_")[-1].lower() - high = y['value']['high'] - low = y['value']['low'] - stat_val = convert_to_64_bit(high, low) - stat_path = "%s.protocol.ip.%s" % (prefix, stat_name) - metric = (stat_path, (now, stat_val)) - logging.debug("metric = %s" % str(metric)) - metric_list.append(metric) + metric_path = cleanStatPath("%s.protocol.ip" % (prefix)) + metrics = itterate_statistics(statistics, metric_path, now) + metric_list.extend(metrics) else: logging.debug("Skipping IP...") @@ -286,15 +320,9 @@ def gather_f5_metrics(ltm_host, user, password, prefix, remote_ts, else: now = timestamp_local() logging.debug("Local timestamp is %s." % now) - for y in statistics: - stat_name = y['type'].split("STATISTIC_")[-1].lower() - high = y['value']['high'] - low = y['value']['low'] - stat_val = convert_to_64_bit(high, low) - stat_path = "%s.protocol.ipv6.%s" % (prefix, stat_name) - metric = (stat_path, (now, stat_val)) - logging.debug("metric = %s" % str(metric)) - metric_list.append(metric) + metric_path = cleanStatPath("%s.protocol.ipv6" % (prefix)) + metrics = itterate_statistics(statistics, metric_path, now) + metric_list.extend(metrics) else: logging.debug("Skipping IPv6...") @@ -314,15 +342,9 @@ def gather_f5_metrics(ltm_host, user, password, prefix, remote_ts, else: now = timestamp_local() logging.debug("Local timestamp is %s." % now) - for y in statistics: - stat_name = y['type'].split("STATISTIC_")[-1].lower() - high = y['value']['high'] - low = y['value']['low'] - stat_val = convert_to_64_bit(high, low) - stat_path = "%s.protocol.icmp.%s" % (prefix, stat_name) - metric = (stat_path, (now, stat_val)) - logging.debug("metric = %s" % str(metric)) - metric_list.append(metric) + metric_path = cleanStatPath("%s.protocol.icmp" % (prefix)) + metrics = itterate_statistics(statistics, metric_path, now) + metric_list.extend(metrics) else: logging.debug("Skipping ICMP...") @@ -342,15 +364,9 @@ def gather_f5_metrics(ltm_host, user, password, prefix, remote_ts, else: now = timestamp_local() logging.debug("Local timestamp is %s." % now) - for y in statistics: - stat_name = y['type'].split("STATISTIC_")[-1].lower() - high = y['value']['high'] - low = y['value']['low'] - stat_val = convert_to_64_bit(high, low) - stat_path = "%s.protocol.icmpv6.%s" % (prefix, stat_name) - metric = (stat_path, (now, stat_val)) - logging.debug("metric = %s" % str(metric)) - metric_list.append(metric) + metric_path = cleanStatPath("%s.protocol.icmpv6" % (prefix)) + metrics = itterate_statistics(statistics, metric_path, now) + metric_list.extend(metrics) else: logging.debug("Skipping ICMPv6...") @@ -370,15 +386,9 @@ def gather_f5_metrics(ltm_host, user, password, prefix, remote_ts, else: now = timestamp_local() logging.debug("Local timestamp is %s." % now) - for y in statistics: - stat_name = y['type'].split("STATISTIC_")[-1].lower() - high = y['value']['high'] - low = y['value']['low'] - stat_val = convert_to_64_bit(high, low) - stat_path = "%s.protocol.tcp.%s" % (prefix, stat_name) - metric = (stat_path, (now, stat_val)) - logging.debug("metric = %s" % str(metric)) - metric_list.append(metric) + metric_path = "%s.protocol.tcp" % (prefix) + metrics = itterate_statistics(statistics, metric_path, now) + metric_list.extend(metrics) else: logging.debug("Skipping TCP...") @@ -398,15 +408,9 @@ def gather_f5_metrics(ltm_host, user, password, prefix, remote_ts, else: now = timestamp_local() logging.debug("Local timestamp is %s." % now) - for y in statistics: - stat_name = y['type'].split("STATISTIC_")[-1].lower() - high = y['value']['high'] - low = y['value']['low'] - stat_val = convert_to_64_bit(high, low) - stat_path = "%s.tmm.global.%s" % (prefix, stat_name) - metric = (stat_path, (now, stat_val)) - logging.debug("metric = %s" % str(metric)) - metric_list.append(metric) + metric_path = cleanStatPath("%s.tmm.global" % (prefix)) + metrics = itterate_statistics(statistics, metric_path, now) + metric_list.extend(metrics) else: logging.debug("Skipping TMM...") @@ -426,16 +430,9 @@ def gather_f5_metrics(ltm_host, user, password, prefix, remote_ts, else: now = timestamp_local() logging.debug("Local timestamp is %s." % now) - for y in statistics: - stat_name = y['type'].split("STATISTIC_")[-1].lower() - if stat_name in CLIENT_SSL_STATISTICS: - high = y['value']['high'] - low = y['value']['low'] - stat_val = convert_to_64_bit(high, low) - stat_path = "%s.client_ssl.%s" % (prefix, stat_name) - metric = (stat_path, (now, stat_val)) - logging.debug("metric = %s" % str(metric)) - metric_list.append(metric) + metric_path = cleanStatPath("%s.client_ssl" % (prefix)) + metrics = itterate_statistics(statistics, metric_path, now, CLIENT_SSL_STATISTICS) + metric_list.extend(metrics) else: logging.debug("Skipping client SSL...") @@ -465,15 +462,9 @@ def gather_f5_metrics(ltm_host, user, password, prefix, remote_ts, logging.debug("Local timestamp is %s." % now) for x in statistics: int_name = x['interface_name'].replace('.', '-') - for y in x['statistics']: - stat_name = y['type'].split("STATISTIC_")[-1].lower() - high = y['value']['high'] - low = y['value']['low'] - stat_val = convert_to_64_bit(high, low) - stat_path = "%s.interface.%s.%s" % (prefix, int_name, stat_name) - metric = (stat_path, (now, stat_val)) - logging.debug("metric = %s" % str(metric)) - metric_list.append(metric) + metric_path = cleanStatPath("%s.interface.%s" % (prefix, int_name)) + metrics = itterate_statistics(x['statistics'], metric_path, now) + metric_list.extend(metrics) else: logging.debug("Skipping interfaces...") @@ -499,15 +490,9 @@ def gather_f5_metrics(ltm_host, user, password, prefix, remote_ts, logging.debug("Local timestamp is %s." % now) for x in statistics: trunk_name = x['trunk_name'].replace('.', '-') - for y in x['statistics']: - stat_name = y['type'].split("STATISTIC_")[-1].lower() - high = y['value']['high'] - low = y['value']['low'] - stat_val = convert_to_64_bit(high, low) - stat_path = "%s.trunk.%s.%s" % (prefix, trunk_name, stat_name) - metric = (stat_path, (now, stat_val)) - logging.debug("metric = %s" % str(metric)) - metric_list.append(metric) + metric_path = cleanStatPath("%s.trunk.%s" % (prefix, trunk_name)) + metrics = itterate_statistics(x['statistics'], metric_path, now) + metric_list.extend(metrics) else: logging.debug("Skipping trunks...") @@ -530,15 +515,9 @@ def gather_f5_metrics(ltm_host, user, password, prefix, remote_ts, for x in statistics: host_id = x['host_id'].replace('.', '-') for cpu_num, cpu_stat in enumerate(x['statistics']): - for y in cpu_stat: - stat_name = y['type'].split("STATISTIC_")[-1].lower() - high = y['value']['high'] - low = y['value']['low'] - stat_val = convert_to_64_bit(high, low) - stat_path = "%s.cpu.%s.cpu%s.%s" % (prefix, host_id, cpu_num, stat_name) - metric = (stat_path, (now, stat_val)) - logging.debug("metric = %s" % str(metric)) - metric_list.append(metric) + metric_path = cleanStatPath("%s.cpu.%s.cpu%s" % (prefix, host_id, cpu_num)) + metrics = itterate_statistics(cpu_stat, metric_path, now) + metric_list.extend(metrics) else: logging.debug("Skipping CPU...") @@ -560,21 +539,14 @@ def gather_f5_metrics(ltm_host, user, password, prefix, remote_ts, logging.debug("Local timestamp is %s." % now) for x in statistics: host_id = x['host_id'].replace('.', '-') - for y in x['statistics']: - stat_name = y['type'].split("STATISTIC_")[-1].lower() - if stat_name in HOST_STATISTICS: - high = y['value']['high'] - low = y['value']['low'] - stat_val = convert_to_64_bit(high, low) - if stat_name.startswith("memory_"): - # throw memory stats into dedicated memory section - stat_path = "%s.memory.%s.%s" % (prefix, host_id, stat_name) - else: - # catch-all - stat_path = "%s.system.host.%s.%s" % (prefix, host_id, stat_name) - metric = (stat_path, (now, stat_val)) - logging.debug("metric = %s" % str(metric)) - metric_list.append(metric) + if len(HOST_MEMORY_STATISTICS) > 0: + metric_path = cleanStatPath("%s.memory.%s" % (prefix,host_id)) + metrics = itterate_statistics(x['statistics'], metric_path, now, HOST_MEMORY_STATISTICS) + metric_list.extend(metrics) + if len(HOST_STATISTICS) > 0: + metric_path = cleanStatPath("%s.system.host.%s" % (prefix,host_id)) + metrics = itterate_statistics(x['statistics'], metric_path, now, HOST_STATISTICS) + metric_list.extend(metrics) else: logging.debug("Skipping host statistics...") @@ -596,15 +568,9 @@ def gather_f5_metrics(ltm_host, user, password, prefix, remote_ts, logging.debug("Local timestamp is %s." % now) for x in statistics: snat_pool = x['snat_pool'].replace(".", '-') - for y in x['statistics']: - stat_name = y['type'].split("STATISTIC_")[-1].lower() - high = y['value']['high'] - low = y['value']['low'] - stat_val = convert_to_64_bit(high, low) - stat_path = "%s.snat_pool.%s.%s" % (prefix, snat_pool, stat_name) - metric = (stat_path, (now, stat_val)) - logging.debug("metric = %s" % str(metric)) - metric_list.append(metric) + metric_path = cleanStatPath("%s.snat_pool.%s" % (prefix, snat_pool)) + metrics = itterate_statistics(x['statistics'], metric_path, now) + metric_list.extend(metrics) else: logging.debug("Skipping SNAT pools...") @@ -626,15 +592,9 @@ def gather_f5_metrics(ltm_host, user, password, prefix, remote_ts, logging.debug("Local timestamp is %s." % now) for x in statistics: trans_addr = x['translation_address'].replace(".", '-') - for y in x['statistics']: - stat_name = y['type'].split("STATISTIC_")[-1].lower() - high = y['value']['high'] - low = y['value']['low'] - stat_val = convert_to_64_bit(high, low) - stat_path = "%s.snat_translation.%s.%s" % (prefix, trans_addr, stat_name) - metric = (stat_path, (now, stat_val)) - logging.debug("metric = %s" % str(metric)) - metric_list.append(metric) + metric_path = cleanStatPath("%s.snat_translation.%s" % (prefix, trans_addr)) + metrics = itterate_statistics(x['statistics'], metric_path, now) + metric_list.extend(metrics) else: logging.debug("Skipping SNAT translations...") @@ -656,16 +616,9 @@ def gather_f5_metrics(ltm_host, user, password, prefix, remote_ts, logging.debug("Local timestamp is %s." % now) for x in statistics: vs_name = x['virtual_server']['name'].replace('.', '-') - for y in x['statistics']: - stat_name = y['type'].split("STATISTIC_")[-1].lower() - if stat_name in VS_STATISTICS: - high = y['value']['high'] - low = y['value']['low'] - stat_val = convert_to_64_bit(high, low) - stat_path = "%s.vs.%s.%s" % (prefix, vs_name, stat_name) - metric = (stat_path, (now, stat_val)) - logging.debug("metric = %s" % str(metric)) - metric_list.append(metric) + metric_path = cleanStatPath("%s.vs.%s" % (prefix, vs_name)) + metrics = itterate_statistics(x['statistics'], metric_path, now, VS_STATISTICS) + metric_list.extend(metrics) else: logging.debug("Skipping virtual servers...") @@ -687,16 +640,10 @@ def gather_f5_metrics(ltm_host, user, password, prefix, remote_ts, logging.debug("Local timestamp is %s." % now) for x in statistics: pool_name = x['pool_name'].replace('.', '-') - for y in x['statistics']: - stat_name = y['type'].split("STATISTIC_")[-1].lower() - if stat_name in POOL_STATISTICS: - high = y['value']['high'] - low = y['value']['low'] - stat_val = convert_to_64_bit(high, low) - stat_path = "%s.pool.%s.%s" % (prefix, pool_name, stat_name) - metric = (stat_path, (now, stat_val)) - logging.debug("metric = %s" % str(metric)) - metric_list.append(metric) + metric_path = cleanStatPath("%s.pool.%s" % (prefix, pool_name)) + metrics = itterate_statistics(x['statistics'], metric_path, now, POOL_STATISTICS) + metric_list.extend(metrics) + # Reuse previous timestamp (a.k.a. fake it!) logging.info("Retrieving pool list...") pool_list = b.LocalLB.Pool.get_list() @@ -705,18 +652,25 @@ def gather_f5_metrics(ltm_host, user, password, prefix, remote_ts, logging.info("Retrieving active member count for all pools...") active_member_count = b.LocalLB.Pool.get_active_member_count(pool_names=pool_list) for pool_name, stat_val in zip(pool_list, active_member_count): - stat_path = "%s.pool.%s.active_member_count" % (prefix, pool_name) + stat_path = cleanStatPath("%s.pool.%s.active_member_count" % (prefix, pool_name)) metric = (stat_path, (now, stat_val)) logging.debug("metric = %s" % str(metric)) metric_list.append(metric) logging.info("Retrieving member count for all pools...") - pool_members = b.LocalLB.Pool.get_member_v2(pool_names=pool_list) + pool_members = b.LocalLB.Pool.get_all_member_statistics(pool_names=[pool_name]) pool_member_count = [len(x) for x in pool_members] for pool_name, stat_val in zip(pool_list, pool_member_count): - stat_path = "%s.pool.%s.member_count" % (prefix, pool_name) + stat_path = cleanStatPath("%s.pool.%s.member_count" % (prefix, pool_name)) metric = (stat_path, (now, stat_val)) logging.debug("metric = %s" % str(metric)) metric_list.append(metric) + if not no_pool_members: + for memindex, members in enumerate(pool_members): + for cur_member in members['statistics']: + member_name = "%s-%s" % (cur_member['member']['address'].split('/')[-1].lower(), cur_member['member']['port']) + metric_path = cleanStatPath("%s.pool.%s.members.%s.%s" % (prefix, pool_name, memindex, member_name)) + metrics = itterate_statistics(cur_member['statistics'], metric_path, now, POOL_MEMBER_STATISTICS) + metric_list.extend(metrics) else: logging.info("Pool list is empty, skipping member count retrieval.") else: @@ -765,6 +719,10 @@ def main(): prefix = "bigip.%s" % scrubbed_f5_host logging.debug("prefix = %s" % prefix) + if args.sslverify == False: + import ssl + ssl._create_default_https_context = ssl._create_unverified_context + running = True while running: start_timestamp = timestamp_local() @@ -780,7 +738,7 @@ def main(): args.no_icmpv6, args.no_tcp, True, True, args.no_interface, True, args.no_cpu, args.no_host, - True,True,True,True) + True,True,True,True, True) else: metric_list = gather_f5_metrics(args.f5_host, args.f5_username, args.f5_password, prefix, remote_ts, @@ -791,7 +749,8 @@ def main(): args.no_trunk, args.no_cpu, args.no_host, args.no_snat_pool, args.no_snat_translation, - args.no_virtual_server, args.no_pool) + args.no_virtual_server, args.no_pool, + args.no_pool_members) if not args.skip_upload and args.carbon_host: upload_attempts = 0 From 45923845cb5476184c2d673b25d77d3c77026a31 Mon Sep 17 00:00:00 2001 From: Ed McLain Date: Fri, 31 Jul 2015 12:36:58 -0500 Subject: [PATCH 4/5] Now properly handles pool member names with periods. --- graphitecollectors/f5.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphitecollectors/f5.py b/graphitecollectors/f5.py index 2a85a1c..6d54b77 100755 --- a/graphitecollectors/f5.py +++ b/graphitecollectors/f5.py @@ -667,7 +667,7 @@ def gather_f5_metrics(ltm_host, user, password, prefix, remote_ts, if not no_pool_members: for memindex, members in enumerate(pool_members): for cur_member in members['statistics']: - member_name = "%s-%s" % (cur_member['member']['address'].split('/')[-1].lower(), cur_member['member']['port']) + member_name = "%s-%s" % (cur_member['member']['address'].split('/')[-1].lower().replace('.', '-'), cur_member['member']['port']) metric_path = cleanStatPath("%s.pool.%s.members.%s.%s" % (prefix, pool_name, memindex, member_name)) metrics = itterate_statistics(cur_member['statistics'], metric_path, now, POOL_MEMBER_STATISTICS) metric_list.extend(metrics) From 50b8476e0df073094cc8715ec3dc140f663525af Mon Sep 17 00:00:00 2001 From: Ed McLain Date: Fri, 31 Jul 2015 16:43:39 -0500 Subject: [PATCH 5/5] Fixed multiple pools with pool members --- graphitecollectors/f5.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/graphitecollectors/f5.py b/graphitecollectors/f5.py index 6d54b77..6b1b3fc 100755 --- a/graphitecollectors/f5.py +++ b/graphitecollectors/f5.py @@ -657,7 +657,7 @@ def gather_f5_metrics(ltm_host, user, password, prefix, remote_ts, logging.debug("metric = %s" % str(metric)) metric_list.append(metric) logging.info("Retrieving member count for all pools...") - pool_members = b.LocalLB.Pool.get_all_member_statistics(pool_names=[pool_name]) + pool_members = b.LocalLB.Pool.get_all_member_statistics(pool_names=[pool_list]) pool_member_count = [len(x) for x in pool_members] for pool_name, stat_val in zip(pool_list, pool_member_count): stat_path = cleanStatPath("%s.pool.%s.member_count" % (prefix, pool_name)) @@ -665,10 +665,10 @@ def gather_f5_metrics(ltm_host, user, password, prefix, remote_ts, logging.debug("metric = %s" % str(metric)) metric_list.append(metric) if not no_pool_members: - for memindex, members in enumerate(pool_members): + for pool_name, members in zip(pool_list, pool_members): for cur_member in members['statistics']: member_name = "%s-%s" % (cur_member['member']['address'].split('/')[-1].lower().replace('.', '-'), cur_member['member']['port']) - metric_path = cleanStatPath("%s.pool.%s.members.%s.%s" % (prefix, pool_name, memindex, member_name)) + metric_path = cleanStatPath("%s.pool.%s.members.%s" % (prefix, pool_name, member_name)) metrics = itterate_statistics(cur_member['statistics'], metric_path, now, POOL_MEMBER_STATISTICS) metric_list.extend(metrics) else: