2 # Copyright (C) The Arvados Authors. All rights reserved.
4 # SPDX-License-Identifier: AGPL-3.0
15 from prometheus_api_client.utils import parse_datetime
16 from datetime import timedelta
20 from prometheus_api_client import PrometheusConnect, MetricsList, Metric
22 def parse_arguments(arguments):
23 arg_parser = argparse.ArgumentParser()
24 arg_parser.add_argument('--start', help='Start date for the report in YYYY-MM-DD format (UTC)')
25 arg_parser.add_argument('--end', help='End date for the report in YYYY-MM-DD format (UTC), default "now"')
26 arg_parser.add_argument('--days', type=int, help='Number of days before "end" to start the report')
27 arg_parser.add_argument('--cluster', type=str, help='Cluster to query')
28 args = arg_parser.parse_args(arguments)
30 if args.days and args.start:
31 arg_parser.print_help()
32 print("Error: either specify --days or both --start and --end")
35 if not args.days and not args.start:
36 arg_parser.print_help()
37 print("\nError: either specify --days or both --start and --end")
40 if (args.start and not args.end):
41 arg_parser.print_help()
42 print("\nError: no start or end date found, either specify --days or both --start and --end")
47 to = datetime.datetime.strptime(args.end,"%Y-%m-%d")
49 arg_parser.print_help()
50 print("\nError: end date must be in YYYY-MM-DD format")
53 to = datetime.datetime.utcnow()
56 since = to - datetime.timedelta(days=args.days)
60 since = datetime.datetime.strptime(args.start,"%Y-%m-%d")
62 arg_parser.print_help()
63 print("\nError: start date must be in YYYY-MM-DD format")
67 return args, since, to
69 def data_usage(prom, timestamp, cluster, label):
70 metric_data = prom.get_current_metric_value(metric_name='arvados_keep_total_bytes',
71 label_config={"cluster": cluster},
72 params={"time": timestamp.timestamp()})
74 metric_object_list = MetricsList(metric_data)
76 if len(metric_data) == 0:
79 my_metric_object = metric_object_list[0] # one of the metrics from the list
80 value = my_metric_object.metric_values.iloc[0]["y"]
83 metric_data = prom.get_current_metric_value(metric_name='arvados_keep_dedup_byte_ratio',
84 label_config={"cluster": cluster},
85 params={"time": timestamp.timestamp()})
87 if len(metric_data) == 0:
90 my_metric_object = MetricsList(metric_data)[0]
91 dedup_ratio = my_metric_object.metric_values.iloc[0]["y"]
93 value_gb = value / (1024*1024*1024)
94 first_50tb = min(1024*50, value_gb)
95 next_450tb = max(min(1024*450, value_gb-1024*50), 0)
96 over_500tb = max(value_gb-1024*500, 0)
98 monthly_cost = (first_50tb * 0.023) + (next_450tb * 0.022) + (over_500tb * 0.021)
100 for scale in ["KiB", "MiB", "GiB", "TiB", "PiB"]:
101 summary_value = summary_value / 1024
102 if summary_value < 1024:
104 "%.3f %s apparent," % (summary_value*dedup_ratio, scale),
105 "%.3f %s actually stored," % (summary_value, scale),
106 "$%.2f monthly S3 storage cost" % monthly_cost)
112 def container_usage(prom, start_time, end_time, metric, label, fn=None):
114 chunk_size = timedelta(days=1)
117 while start < end_time:
118 if start + chunk_size > end_time:
119 chunk_size = end_time - start
121 metric_data = prom.custom_query_range(metric,
123 end_time=(start + chunk_size),
127 if len(metric_data) == 0:
130 if "__name__" not in metric_data[0]["metric"]:
131 metric_data[0]["metric"]["__name__"] = metric
133 metric_object_list = MetricsList(metric_data)
134 my_metric_object = metric_object_list[0] # one of the metrics from the list
136 series = my_metric_object.metric_values.set_index(pandas.DatetimeIndex(my_metric_object.metric_values['ds']))
138 # Resample to 1 minute increments, fill in missing values
139 rs = series.resample("min").mean(1).ffill()
141 # Calculate the sum of values
142 #print(rs.sum()["y"])
143 cumulative += rs.sum()["y"]
148 cumulative = fn(cumulative)
150 print(label % cumulative)
153 def main(arguments=None):
154 if arguments is None:
155 arguments = sys.argv[1:]
157 args, since, to = parse_arguments(arguments)
161 prom_host = os.environ["PROMETHEUS_HOST"]
162 prom_token = os.environ.get("PROMETHEUS_APIKEY")
163 prom_user = os.environ.get("PROMETHEUS_USER")
164 prom_pw = os.environ.get("PROMETHEUS_PASSWORD")
168 headers["Authorization"] = "Bearer "+prom_token
171 headers["Authorization"] = "Basic "+(base64.b64encode("%s:%s" % (prom_user, prom_pw)))
173 prom = PrometheusConnect(url=prom_host, headers=header)
175 cluster = args.cluster
177 print(cluster, "between", since, "and", to, "timespan", (to-since))
179 data_usage(prom, since, cluster, "at start:")
180 data_usage(prom, to - timedelta(minutes=240), cluster, "current :")
182 container_usage(prom, since, to, "arvados_dispatchcloud_containers_running{cluster='%s'}" % cluster, '%.1f container hours', lambda x: x/60)
183 container_usage(prom, since, to, "sum(arvados_dispatchcloud_instances_price{cluster='%s'})" % cluster, '$%.2f spent on compute', lambda x: x/60)
186 if __name__ == "__main__":