blob: 83fa1a07827d1713f5fda25d8d7121363973066b [file] [log] [blame]
Matteo Scandoloe5ed3a62020-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 Scandolode60d422020-08-31 09:38:59 -070021import csv
22from sys import platform as sys_pf
23
24if sys_pf == 'darwin':
25 import matplotlib
26
27 matplotlib.use("TkAgg")
28
Matteo Scandoloe5ed3a62020-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 Scandolode60d422020-08-31 09:38:59 -070039 "calico", "nginx", "registry", "cattle", "canal", "metrics",
Matteo Scandoloe5ed3a62020-07-15 17:01:02 -070040]
41
42DATE_FORMATTER_FN = mdates.DateFormatter('%Y-%m-%d %H:%M:%S')
43
44
45def main(address, out_folder, since):
46 """
47 Query Prometheus and generate .pdf files for CPU and Memory consumption for each POD
48 :param address: string The address of the Prometheus instance to query
49 :param out_folder: string The output folder (where to save the .pdf files)
50 :param since: int When to start collection data (minutes in the past)
51 :return: void
52 """
53 time_delta = int(since) * 60
Matteo Scandolode60d422020-08-31 09:38:59 -070054
55 container_mem_query = "sum by(pod) (container_memory_working_set_bytes{namespace='default',container!='',container!='POD'})"
56
57 container_cpu_query = "sum by(pod) (rate(container_cpu_usage_seconds_total{namespace='default',container!='',container!='POD'}[%sm])) * 100" % since
Matteo Scandoloe5ed3a62020-07-15 17:01:02 -070058
59 now = time.time()
60 cpu_params = {
61 "query": container_cpu_query,
62 "start": now - time_delta,
63 "end": now,
64 "step": "30",
65 }
Matteo Scandolode60d422020-08-31 09:38:59 -070066
Matteo Scandoloe5ed3a62020-07-15 17:01:02 -070067 r = requests.get("http://%s/api/v1/query_range" % address, cpu_params)
68 print("Downloading CPU info from: %s" % r.url)
69 container_cpu = r.json()["data"]["result"]
Matteo Scandolode60d422020-08-31 09:38:59 -070070 containers = remove_unwanted_containers(container_cpu)
71 plot_cpu_consumption(containers,
Matteo Scandoloe5ed3a62020-07-15 17:01:02 -070072 output="%s/cpu.pdf" % out_folder)
Matteo Scandolode60d422020-08-31 09:38:59 -070073 data_to_csv(containers, output="%s/cpu.csv" % out_folder,
74 convert_values=lambda values: ["{:.2f}".format(v) for v in values])
Matteo Scandoloe5ed3a62020-07-15 17:01:02 -070075
Matteo Scandolode60d422020-08-31 09:38:59 -070076 mem_params = {
77 "query": container_mem_query,
78 "start": now - time_delta,
79 "end": now,
80 "step": "30",
81 }
82
83 r = requests.get("http://%s/api/v1/query_range" % address, mem_params)
Matteo Scandoloe5ed3a62020-07-15 17:01:02 -070084 print("Downloading Memory info from: %s" % r.url)
85 container_mem = r.json()["data"]["result"]
Matteo Scandolode60d422020-08-31 09:38:59 -070086 containers = remove_unwanted_containers(container_mem)
87 plot_memory_consumption(containers, output="%s/memory.pdf" % out_folder)
88 data_to_csv(containers, output="%s/memory.csv" % out_folder,
89 convert_values=lambda values: ["{:.2f}".format(bytesto(v, "m")) for v in values])
90
91
92def data_to_csv(containers, output=None, convert_values=None):
93 """
94 Get a list of prometheus metrics and dumps them in a csv
95 :param containers: Prometheus metrics
96 :param output: Destination file
97 :param convert_values: Function to convert the valus, take a list on numbers
98 """
99 csv_file = open(output, "w+")
100 csv_writer = csv.writer(csv_file, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
101
102 # we assume all the containers have the same timestamps
103 dates = [datetime.fromtimestamp(x[0]) for x in containers[0]["values"]]
104 csv_writer.writerow([''] + dates)
105
106 for c in containers:
107 name = c["metric"]["pod"]
108 data = c["values"]
109
110 values = [float(x[1]) for x in data]
111
112 if convert_values:
113 values = convert_values(values)
114 csv_writer.writerow([name] + values)
Matteo Scandoloe5ed3a62020-07-15 17:01:02 -0700115
116
117def plot_cpu_consumption(containers, output=None):
Matteo Scandoloe5ed3a62020-07-15 17:01:02 -0700118 plt.figure('cpu')
119 fig, ax = plt.subplots()
120 ax.xaxis.set_major_formatter(DATE_FORMATTER_FN)
121 ax.xaxis_date()
122 fig.autofmt_xdate()
123
124 plt.title("CPU Usage per POD")
125 plt.xlabel("Timestamp")
126 plt.ylabel("% used")
127
128 for c in containers:
Matteo Scandolode60d422020-08-31 09:38:59 -0700129 name = c["metric"]["pod"]
Matteo Scandoloe5ed3a62020-07-15 17:01:02 -0700130 data = c["values"]
131
132 dates = [datetime.fromtimestamp(x[0]) for x in data]
133
134 values = [float(x[1]) for x in data]
135
136 plt.plot(dates, values, label=name, lw=2, color=get_line_color(name))
137 # plt.plot(dates[1:], get_diff(values), label=name, lw=2, color=get_line_color(name))
138
Matteo Scandolode60d422020-08-31 09:38:59 -0700139 plt.legend(loc='upper left', title="CPU Consumption", bbox_to_anchor=(1.05, 1))
Matteo Scandoloe5ed3a62020-07-15 17:01:02 -0700140
141 fig = plt.gcf()
142 fig.set_size_inches(20, 11)
143
Matteo Scandolode60d422020-08-31 09:38:59 -0700144 plt.savefig(output, bbox_inches="tight")
Matteo Scandoloe5ed3a62020-07-15 17:01:02 -0700145
146
147def plot_memory_consumption(containers, output=None):
148 plt.figure("memory")
149 fig, ax = plt.subplots()
150 ax.xaxis.set_major_formatter(DATE_FORMATTER_FN)
151 ax.xaxis_date()
152 fig.autofmt_xdate()
153 plt.title("Memory Usage")
154 plt.xlabel("Timestamp")
155 plt.ylabel("MB")
156
157 for c in containers:
Matteo Scandolode60d422020-08-31 09:38:59 -0700158 name = c["metric"]["pod"]
Matteo Scandoloe5ed3a62020-07-15 17:01:02 -0700159 data = c["values"]
160
161 dates = [datetime.fromtimestamp(x[0]) for x in data]
162 values = [bytesto(float(x[1]), "m") for x in data]
163
Matteo Scandolode60d422020-08-31 09:38:59 -0700164 # plt.plot(dates[1:], get_diff(values), label=name, lw=2, color=get_line_color(name))
165 plt.plot(dates[1:], values[1:], label=name, lw=2, color=get_line_color(name))
Matteo Scandoloe5ed3a62020-07-15 17:01:02 -0700166
Matteo Scandolode60d422020-08-31 09:38:59 -0700167 plt.legend(loc='upper left', title="Memory Usage", bbox_to_anchor=(1.05, 1))
Matteo Scandoloe5ed3a62020-07-15 17:01:02 -0700168
169 fig = plt.gcf()
170 fig.set_size_inches(20, 11)
171
Matteo Scandolode60d422020-08-31 09:38:59 -0700172 plt.savefig(output, bbox_inches="tight")
Matteo Scandoloe5ed3a62020-07-15 17:01:02 -0700173
174
175def remove_unwanted_containers(cpus):
176 res = []
177 for c in cpus:
Matteo Scandoloe5ed3a62020-07-15 17:01:02 -0700178
Matteo Scandolode60d422020-08-31 09:38:59 -0700179 if "pod" in c["metric"]:
180 pod_name = c["metric"]["pod"]
Matteo Scandoloe5ed3a62020-07-15 17:01:02 -0700181 if any(x in pod_name for x in EXCLUDED_POD_NAMES):
182 continue
Matteo Scandoloe5ed3a62020-07-15 17:01:02 -0700183 res.append(c)
Matteo Scandoloe5ed3a62020-07-15 17:01:02 -0700184
185 return res
186
187
188def get_line_color(container_name):
189 colors = {
190 "bbsim0": "#884EA0",
191 "bbsim1": "#9B59B6",
192 "bbsim-sadis-server": "#D2B4DE",
193 "onos-atomix-0": "#85C1E9",
194 "onos-atomix-1": "#7FB3D5",
195 "onos-atomix-2": "#3498DB",
196 "onos-onos-classic-0": "#1A5276",
197 "onos-onos-classic-1": "#1B4F72",
198 "onos-onos-classic-2": "#154360",
199 "etcd-0": "#7D6608",
200 "etcd-1": "#9A7D0A",
201 "etcd-2": "#B7950B",
202 "open-olt-voltha-adapter-openolt": "#7E5109",
203 "open-onu-voltha-adapter-openonu-0": "#6E2C00",
204 "open-onu-voltha-adapter-openonu-1": "#873600",
205 "open-onu-voltha-adapter-openonu-2": "#A04000",
206 "open-onu-voltha-adapter-openonu-3": "#BA4A00",
207 "open-onu-voltha-adapter-openonu-4": "#D35400",
208 "open-onu-voltha-adapter-openonu-5": "#D35400",
209 "open-onu-voltha-adapter-openonu-6": "#E59866",
210 "open-onu-voltha-adapter-openonu-7": "#EDBB99",
211 "kafka-0": "#4D5656",
212 "kafka-1": "#5F6A6A",
213 "kafka-2": "#717D7E",
214 "kafka-zookeeper-0": "#839192",
215 "kafka-zookeeper-1": "#95A5A6",
216 "kafka-zookeeper-2": "#717D7E",
217 "radius": "#82E0AA",
218 "voltha-voltha-ofagent": "#641E16",
219 "voltha-voltha-rw-core": "#7B241C",
220 }
221
222 if container_name in colors:
223 return colors[container_name]
224 elif "openolt" in container_name:
225 return colors["open-olt-voltha-adapter-openolt"]
226 elif "ofagent" in container_name:
227 return colors["voltha-voltha-ofagent"]
228 elif "rw-core" in container_name:
229 return colors["voltha-voltha-rw-core"]
230 elif "bbsim0" in container_name:
231 return colors["bbsim0"]
232 elif "bbsim1" in container_name:
233 return colors["bbsim1"]
234 elif "bbsim-sadis-server" in container_name:
235 return colors["bbsim-sadis-server"]
236 elif "radius" in container_name:
237 return colors["radius"]
238 else:
239 return "black"
240
241
242def get_diff(data):
Matteo Scandolode60d422020-08-31 09:38:59 -0700243 # get the delta between the current data and the previous point
Matteo Scandoloe5ed3a62020-07-15 17:01:02 -0700244 return [x - data[i - 1] for i, x in enumerate(data)][1:]
245
246
247def bytesto(b, to, bsize=1024):
248 """convert bytes to megabytes, etc.
249 sample code:
250 print('mb= ' + str(bytesto(314575262000000, 'm')))
251 sample output:
252 mb= 300002347.946
253 """
254
255 a = {'k': 1, 'm': 2, 'g': 3, 't': 4, 'p': 5, 'e': 6}
256 r = float(b)
257 for i in range(a[to]):
258 r = r / bsize
259
260 return r
261
262
263if __name__ == "__main__":
264 parser = argparse.ArgumentParser(prog="sizing")
265 parser.add_argument("-a", "--address", help="The address of the Prometheus instance we're targeting",
266 default="127.0.0.1:31301")
267 parser.add_argument("-o", "--output", help="Where to output the generated files",
268 default="plots")
269 parser.add_argument("-s", "--since", help="When to start sampling the data (in minutes before now)",
270 default=10)
271
272 args = parser.parse_args()
273 main(args.address, args.output, args.since)