blob: 3cd44ea7f514db6254067672c0f64857259770ff [file] [log] [blame]
Matteo Scandolo3ed89872020-07-15 17:01:02 -07001# Copyright 2017-present Open Networking Foundation
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15# This tool collects CPU and Memory informations for each container in the VOLTHA stack
16
17# NOTE
18# Collecting the info for all containers in the same chart can be confusing,
19# we may want to create subcharts for the different groups, eg: infra, ONOS, core, adapters
20
Matteo Scandolo7274b432020-08-27 14:28:43 -070021import csv
22from sys import platform as sys_pf
23
24if sys_pf == 'darwin':
25 import matplotlib
26
27 matplotlib.use("TkAgg")
28
Matteo Scandolo3ed89872020-07-15 17:01:02 -070029import argparse
30import requests
31import matplotlib.pyplot as plt
32import matplotlib.dates as mdates
33from datetime import datetime
34import time
35
36EXCLUDED_POD_NAMES = [
37 "kube", "coredns", "kind", "grafana",
38 "prometheus", "tiller", "control-plane",
Matteo Scandolo7274b432020-08-27 14:28:43 -070039 "calico", "nginx", "registry", "cattle", "canal", "metrics",
Matteo Scandolo3ed89872020-07-15 17:01:02 -070040]
41
42DATE_FORMATTER_FN = mdates.DateFormatter('%Y-%m-%d %H:%M:%S')
43
Matteo Scandolo88d01c12020-11-02 17:11:26 -080044KAFKA_TOPICS = [
45 "openolt",
46 "brcm_openomci_onu",
47 "voltha",
48 "adapters",
49 "rwcore"
50]
Matteo Scandolo3ed89872020-07-15 17:01:02 -070051
52def main(address, out_folder, since):
53 """
54 Query Prometheus and generate .pdf files for CPU and Memory consumption for each POD
55 :param address: string The address of the Prometheus instance to query
56 :param out_folder: string The output folder (where to save the .pdf files)
57 :param since: int When to start collection data (minutes in the past)
58 :return: void
59 """
60 time_delta = int(since) * 60
Matteo Scandolo7274b432020-08-27 14:28:43 -070061
Matteo Scandolo86334f52020-08-28 10:56:25 -070062 container_mem_query = "sum by(pod) (container_memory_working_set_bytes{namespace='default',container!='',container!='POD'})"
63
64 container_cpu_query = "sum by(pod) (rate(container_cpu_usage_seconds_total{namespace='default',container!='',container!='POD'}[%sm])) * 100" % since
Matteo Scandolo3ed89872020-07-15 17:01:02 -070065
66 now = time.time()
67 cpu_params = {
68 "query": container_cpu_query,
69 "start": now - time_delta,
70 "end": now,
71 "step": "30",
72 }
Matteo Scandolo86334f52020-08-28 10:56:25 -070073
Matteo Scandolo3ed89872020-07-15 17:01:02 -070074 r = requests.get("http://%s/api/v1/query_range" % address, cpu_params)
75 print("Downloading CPU info from: %s" % r.url)
76 container_cpu = r.json()["data"]["result"]
Matteo Scandolo7274b432020-08-27 14:28:43 -070077 containers = remove_unwanted_containers(container_cpu)
78 plot_cpu_consumption(containers,
Matteo Scandolo806637d2020-07-30 02:07:06 +000079 output="%s/cpu.pdf" % out_folder)
Matteo Scandolo7274b432020-08-27 14:28:43 -070080 data_to_csv(containers, output="%s/cpu.csv" % out_folder,
Matteo Scandolo86334f52020-08-28 10:56:25 -070081 convert_values=lambda values: ["{:.2f}".format(v) for v in values])
Matteo Scandolo3ed89872020-07-15 17:01:02 -070082
Matteo Scandolo7274b432020-08-27 14:28:43 -070083 mem_params = {
84 "query": container_mem_query,
85 "start": now - time_delta,
86 "end": now,
87 "step": "30",
88 }
89
90 r = requests.get("http://%s/api/v1/query_range" % address, mem_params)
Matteo Scandolo3ed89872020-07-15 17:01:02 -070091 print("Downloading Memory info from: %s" % r.url)
92 container_mem = r.json()["data"]["result"]
Matteo Scandolo7274b432020-08-27 14:28:43 -070093 containers = remove_unwanted_containers(container_mem)
94 plot_memory_consumption(containers, output="%s/memory.pdf" % out_folder)
95 data_to_csv(containers, output="%s/memory.csv" % out_folder,
Matteo Scandolo86334f52020-08-28 10:56:25 -070096 convert_values=lambda values: ["{:.2f}".format(bytesto(v, "m")) for v in values])
Matteo Scandolo7274b432020-08-27 14:28:43 -070097
Matteo Scandolo88d01c12020-11-02 17:11:26 -080098 print("Downloading KAFKA stats")
99 get_kafka_stats(address, out_folder)
100 print("Downloading ETCD stats")
101 get_etcd_stats(address, out_folder)
102
103
Matteo Scandolo7274b432020-08-27 14:28:43 -0700104
105def data_to_csv(containers, output=None, convert_values=None):
106 """
107 Get a list of prometheus metrics and dumps them in a csv
108 :param containers: Prometheus metrics
109 :param output: Destination file
110 :param convert_values: Function to convert the valus, take a list on numbers
111 """
112 csv_file = open(output, "w+")
113 csv_writer = csv.writer(csv_file, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
114
115 # we assume all the containers have the same timestamps
116 dates = [datetime.fromtimestamp(x[0]) for x in containers[0]["values"]]
117 csv_writer.writerow([''] + dates)
118
119 for c in containers:
120 name = c["metric"]["pod"]
121 data = c["values"]
122
123 values = [float(x[1]) for x in data]
124
125 if convert_values:
126 values = convert_values(values)
127 csv_writer.writerow([name] + values)
Matteo Scandolo3ed89872020-07-15 17:01:02 -0700128
129
130def plot_cpu_consumption(containers, output=None):
Matteo Scandolo3ed89872020-07-15 17:01:02 -0700131 plt.figure('cpu')
132 fig, ax = plt.subplots()
133 ax.xaxis.set_major_formatter(DATE_FORMATTER_FN)
134 ax.xaxis_date()
135 fig.autofmt_xdate()
136
137 plt.title("CPU Usage per POD")
138 plt.xlabel("Timestamp")
139 plt.ylabel("% used")
140
141 for c in containers:
Matteo Scandolo7274b432020-08-27 14:28:43 -0700142 name = c["metric"]["pod"]
Matteo Scandolo3ed89872020-07-15 17:01:02 -0700143 data = c["values"]
144
145 dates = [datetime.fromtimestamp(x[0]) for x in data]
146
147 values = [float(x[1]) for x in data]
148
149 plt.plot(dates, values, label=name, lw=2, color=get_line_color(name))
150 # plt.plot(dates[1:], get_diff(values), label=name, lw=2, color=get_line_color(name))
151
Matteo Scandolo7274b432020-08-27 14:28:43 -0700152 plt.legend(loc='upper left', title="CPU Consumption", bbox_to_anchor=(1.05, 1))
Matteo Scandolo3ed89872020-07-15 17:01:02 -0700153
154 fig = plt.gcf()
155 fig.set_size_inches(20, 11)
156
Matteo Scandolo7274b432020-08-27 14:28:43 -0700157 plt.savefig(output, bbox_inches="tight")
Matteo Scandolo3ed89872020-07-15 17:01:02 -0700158
159
160def plot_memory_consumption(containers, output=None):
161 plt.figure("memory")
162 fig, ax = plt.subplots()
163 ax.xaxis.set_major_formatter(DATE_FORMATTER_FN)
164 ax.xaxis_date()
165 fig.autofmt_xdate()
166 plt.title("Memory Usage")
167 plt.xlabel("Timestamp")
168 plt.ylabel("MB")
169
170 for c in containers:
Matteo Scandolo7274b432020-08-27 14:28:43 -0700171 name = c["metric"]["pod"]
Matteo Scandolo3ed89872020-07-15 17:01:02 -0700172 data = c["values"]
173
174 dates = [datetime.fromtimestamp(x[0]) for x in data]
175 values = [bytesto(float(x[1]), "m") for x in data]
176
Matteo Scandolo7274b432020-08-27 14:28:43 -0700177 # plt.plot(dates[1:], get_diff(values), label=name, lw=2, color=get_line_color(name))
178 plt.plot(dates[1:], values[1:], label=name, lw=2, color=get_line_color(name))
Matteo Scandolo3ed89872020-07-15 17:01:02 -0700179
Matteo Scandolo7274b432020-08-27 14:28:43 -0700180 plt.legend(loc='upper left', title="Memory Usage", bbox_to_anchor=(1.05, 1))
Matteo Scandolo3ed89872020-07-15 17:01:02 -0700181
182 fig = plt.gcf()
183 fig.set_size_inches(20, 11)
184
Matteo Scandolo7274b432020-08-27 14:28:43 -0700185 plt.savefig(output, bbox_inches="tight")
Matteo Scandolo3ed89872020-07-15 17:01:02 -0700186
187
188def remove_unwanted_containers(cpus):
189 res = []
190 for c in cpus:
Matteo Scandolo3ed89872020-07-15 17:01:02 -0700191
Matteo Scandolo7274b432020-08-27 14:28:43 -0700192 if "pod" in c["metric"]:
193 pod_name = c["metric"]["pod"]
Matteo Scandolo3ed89872020-07-15 17:01:02 -0700194 if any(x in pod_name for x in EXCLUDED_POD_NAMES):
195 continue
Matteo Scandolo3ed89872020-07-15 17:01:02 -0700196 res.append(c)
Matteo Scandolo806637d2020-07-30 02:07:06 +0000197
Matteo Scandolo3ed89872020-07-15 17:01:02 -0700198 return res
199
200
201def get_line_color(container_name):
202 colors = {
203 "bbsim0": "#884EA0",
204 "bbsim1": "#9B59B6",
205 "bbsim-sadis-server": "#D2B4DE",
206 "onos-atomix-0": "#85C1E9",
207 "onos-atomix-1": "#7FB3D5",
208 "onos-atomix-2": "#3498DB",
209 "onos-onos-classic-0": "#1A5276",
210 "onos-onos-classic-1": "#1B4F72",
211 "onos-onos-classic-2": "#154360",
212 "etcd-0": "#7D6608",
213 "etcd-1": "#9A7D0A",
214 "etcd-2": "#B7950B",
215 "open-olt-voltha-adapter-openolt": "#7E5109",
216 "open-onu-voltha-adapter-openonu-0": "#6E2C00",
217 "open-onu-voltha-adapter-openonu-1": "#873600",
218 "open-onu-voltha-adapter-openonu-2": "#A04000",
219 "open-onu-voltha-adapter-openonu-3": "#BA4A00",
220 "open-onu-voltha-adapter-openonu-4": "#D35400",
221 "open-onu-voltha-adapter-openonu-5": "#D35400",
222 "open-onu-voltha-adapter-openonu-6": "#E59866",
223 "open-onu-voltha-adapter-openonu-7": "#EDBB99",
224 "kafka-0": "#4D5656",
225 "kafka-1": "#5F6A6A",
226 "kafka-2": "#717D7E",
227 "kafka-zookeeper-0": "#839192",
228 "kafka-zookeeper-1": "#95A5A6",
229 "kafka-zookeeper-2": "#717D7E",
230 "radius": "#82E0AA",
231 "voltha-voltha-ofagent": "#641E16",
232 "voltha-voltha-rw-core": "#7B241C",
233 }
234
235 if container_name in colors:
236 return colors[container_name]
237 elif "openolt" in container_name:
238 return colors["open-olt-voltha-adapter-openolt"]
239 elif "ofagent" in container_name:
240 return colors["voltha-voltha-ofagent"]
241 elif "rw-core" in container_name:
242 return colors["voltha-voltha-rw-core"]
243 elif "bbsim0" in container_name:
244 return colors["bbsim0"]
245 elif "bbsim1" in container_name:
246 return colors["bbsim1"]
247 elif "bbsim-sadis-server" in container_name:
248 return colors["bbsim-sadis-server"]
249 elif "radius" in container_name:
250 return colors["radius"]
251 else:
252 return "black"
253
254
255def get_diff(data):
Matteo Scandolo7274b432020-08-27 14:28:43 -0700256 # get the delta between the current data and the previous point
Matteo Scandolo3ed89872020-07-15 17:01:02 -0700257 return [x - data[i - 1] for i, x in enumerate(data)][1:]
258
259
260def bytesto(b, to, bsize=1024):
261 """convert bytes to megabytes, etc.
262 sample code:
263 print('mb= ' + str(bytesto(314575262000000, 'm')))
264 sample output:
265 mb= 300002347.946
266 """
267
268 a = {'k': 1, 'm': 2, 'g': 3, 't': 4, 'p': 5, 'e': 6}
269 r = float(b)
270 for i in range(a[to]):
271 r = r / bsize
272
273 return r
274
275
Matteo Scandolo88d01c12020-11-02 17:11:26 -0800276
277def get_etcd_stats(address, out_folder):
278 """
279 :param address: The prometheus address
280 :param out_folder: The folder in which store the output files
281 """
282
283 etcd_stats = {
284 "size":"etcd_debugging_mvcc_db_total_size_in_bytes",
285 "keys":"etcd_debugging_mvcc_keys_total"
286 }
287
288 etcd = {}
289
290 time_delta = 80
291 for stat,query in etcd_stats.items():
292 now = time.time()
293 etcd_params = {
294 "query": "%s{}" % query,
295 "start": now - time_delta,
296 "end": now,
297 "step": "30",
298 }
299 r = requests.get("http://%s/api/v1/query_range" % address, etcd_params)
300
301 i = r.json()["data"]["result"][0]
302 etcd[stat] = i["values"][-1][1]
303
304 csv_file = open("%s/etcd_stats.csv" % out_folder, "w+")
305 csv_writer = csv.writer(csv_file, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
306
307 for k,v in etcd.items():
308 csv_writer.writerow([k, v])
309
310def get_kafka_stats(address, out_folder):
311 """
312 :param address: The prometheus address
313 :param out_folder: The folder in which store the output files
314 """
315 # get the last information for all topics, we only care about the last value so a short interval is fine
316 now = time.time()
317 time_delta = 80
318 kafka_params = {
319 "query": "kafka_topic_partition_current_offset{}",
320 "start": now - time_delta,
321 "end": now,
322 "step": "30",
323 }
324
325 r = requests.get("http://%s/api/v1/query_range" % address, kafka_params)
326
327 msg_per_topic = {}
328
329 for t in r.json()["data"]["result"]:
330 # we only care about some topics
331 topic_name = t["metric"]["topic"]
332
333 if any(x in topic_name for x in KAFKA_TOPICS):
334 # get only the value at the last timestamp
335 msg_per_topic[t["metric"]["topic"]] = t["values"][-1][1]
336
337 csv_file = open("%s/kafka_msg_per_topic.csv" % out_folder, "w+")
338 csv_writer = csv.writer(csv_file, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
339
340 for k,v in msg_per_topic.items():
341 csv_writer.writerow([k, v])
342
Matteo Scandolo3ed89872020-07-15 17:01:02 -0700343if __name__ == "__main__":
344 parser = argparse.ArgumentParser(prog="sizing")
345 parser.add_argument("-a", "--address", help="The address of the Prometheus instance we're targeting",
346 default="127.0.0.1:31301")
347 parser.add_argument("-o", "--output", help="Where to output the generated files",
348 default="plots")
349 parser.add_argument("-s", "--since", help="When to start sampling the data (in minutes before now)",
350 default=10)
351
352 args = parser.parse_args()
353 main(args.address, args.output, args.since)