blob: 389bb1c35f9d0b84b04ff44d9acec8b2e236891c [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
21import argparse
22import requests
23import matplotlib.pyplot as plt
24import matplotlib.dates as mdates
25from datetime import datetime
26import time
27
28EXCLUDED_POD_NAMES = [
29 "kube", "coredns", "kind", "grafana",
30 "prometheus", "tiller", "control-plane",
31 "calico", "nginx", "registry"
32]
33
34DATE_FORMATTER_FN = mdates.DateFormatter('%Y-%m-%d %H:%M:%S')
35
36
37def main(address, out_folder, since):
38 """
39 Query Prometheus and generate .pdf files for CPU and Memory consumption for each POD
40 :param address: string The address of the Prometheus instance to query
41 :param out_folder: string The output folder (where to save the .pdf files)
42 :param since: int When to start collection data (minutes in the past)
43 :return: void
44 """
45 time_delta = int(since) * 60
46 container_mem_query = "container_memory_usage_bytes{image!=''}[%sm]" % since
47 container_cpu_query = "rate(container_cpu_user_seconds_total{image!=''}[%sm]) * 100" % since
48
49 now = time.time()
50 cpu_params = {
51 "query": container_cpu_query,
52 "start": now - time_delta,
53 "end": now,
54 "step": "30",
55 }
56 r = requests.get("http://%s/api/v1/query_range" % address, cpu_params)
57 print("Downloading CPU info from: %s" % r.url)
58 container_cpu = r.json()["data"]["result"]
59 plot_cpu_consumption(remove_unwanted_containers(container_cpu),
60 output="%s/cpu.pdf" % out_folder)
61
62 r = requests.get("http://%s/api/v1/query" % address, {"query": container_mem_query})
63 print("Downloading Memory info from: %s" % r.url)
64 container_mem = r.json()["data"]["result"]
65 plot_memory_consumption(remove_unwanted_containers(container_mem),
66 output="%s/memory.pdf" % out_folder)
67
68
69def plot_cpu_consumption(containers, output=None):
70
71 plt.figure('cpu')
72 fig, ax = plt.subplots()
73 ax.xaxis.set_major_formatter(DATE_FORMATTER_FN)
74 ax.xaxis_date()
75 fig.autofmt_xdate()
76
77 plt.title("CPU Usage per POD")
78 plt.xlabel("Timestamp")
79 plt.ylabel("% used")
80
81 for c in containers:
82 name = c["metric"]["pod_name"]
83 data = c["values"]
84
85 dates = [datetime.fromtimestamp(x[0]) for x in data]
86
87 values = [float(x[1]) for x in data]
88
89 plt.plot(dates, values, label=name, lw=2, color=get_line_color(name))
90 # plt.plot(dates[1:], get_diff(values), label=name, lw=2, color=get_line_color(name))
91
92 plt.legend(loc='upper left')
93
94 fig = plt.gcf()
95 fig.set_size_inches(20, 11)
96
97 plt.savefig(output)
98
99
100def plot_memory_consumption(containers, output=None):
101 plt.figure("memory")
102 fig, ax = plt.subplots()
103 ax.xaxis.set_major_formatter(DATE_FORMATTER_FN)
104 ax.xaxis_date()
105 fig.autofmt_xdate()
106 plt.title("Memory Usage")
107 plt.xlabel("Timestamp")
108 plt.ylabel("MB")
109
110 for c in containers:
111 name = c["metric"]["pod_name"]
112 data = c["values"]
113
114 dates = [datetime.fromtimestamp(x[0]) for x in data]
115 values = [bytesto(float(x[1]), "m") for x in data]
116
117 plt.plot(dates[1:], get_diff(values), label=name, lw=2, color=get_line_color(name))
118
119 plt.legend(loc='upper left')
120
121 fig = plt.gcf()
122 fig.set_size_inches(20, 11)
123
124 plt.savefig(output)
125
126
127def remove_unwanted_containers(cpus):
128 res = []
129 for c in cpus:
130 if "pod_name" in c["metric"]:
131
132 pod_name = c["metric"]["pod_name"]
133 container_name = c["metric"]["name"]
134
135 if any(x in pod_name for x in EXCLUDED_POD_NAMES):
136 continue
137
138 if "k8s_POD" in container_name:
139 # this is the kubernetes POD controller, we don't care about it
140 continue
141
142 # if "_0" not in container_name:
143 # # this is something with the ONOS chart that is weird (each POD is reported 3 times)
144 # continue
145
146 res.append(c)
147 else:
148 continue
149
150 return res
151
152
153def get_line_color(container_name):
154 colors = {
155 "bbsim0": "#884EA0",
156 "bbsim1": "#9B59B6",
157 "bbsim-sadis-server": "#D2B4DE",
158 "onos-atomix-0": "#85C1E9",
159 "onos-atomix-1": "#7FB3D5",
160 "onos-atomix-2": "#3498DB",
161 "onos-onos-classic-0": "#1A5276",
162 "onos-onos-classic-1": "#1B4F72",
163 "onos-onos-classic-2": "#154360",
164 "etcd-0": "#7D6608",
165 "etcd-1": "#9A7D0A",
166 "etcd-2": "#B7950B",
167 "open-olt-voltha-adapter-openolt": "#7E5109",
168 "open-onu-voltha-adapter-openonu-0": "#6E2C00",
169 "open-onu-voltha-adapter-openonu-1": "#873600",
170 "open-onu-voltha-adapter-openonu-2": "#A04000",
171 "open-onu-voltha-adapter-openonu-3": "#BA4A00",
172 "open-onu-voltha-adapter-openonu-4": "#D35400",
173 "open-onu-voltha-adapter-openonu-5": "#D35400",
174 "open-onu-voltha-adapter-openonu-6": "#E59866",
175 "open-onu-voltha-adapter-openonu-7": "#EDBB99",
176 "kafka-0": "#4D5656",
177 "kafka-1": "#5F6A6A",
178 "kafka-2": "#717D7E",
179 "kafka-zookeeper-0": "#839192",
180 "kafka-zookeeper-1": "#95A5A6",
181 "kafka-zookeeper-2": "#717D7E",
182 "radius": "#82E0AA",
183 "voltha-voltha-ofagent": "#641E16",
184 "voltha-voltha-rw-core": "#7B241C",
185 }
186
187 if container_name in colors:
188 return colors[container_name]
189 elif "openolt" in container_name:
190 return colors["open-olt-voltha-adapter-openolt"]
191 elif "ofagent" in container_name:
192 return colors["voltha-voltha-ofagent"]
193 elif "rw-core" in container_name:
194 return colors["voltha-voltha-rw-core"]
195 elif "bbsim0" in container_name:
196 return colors["bbsim0"]
197 elif "bbsim1" in container_name:
198 return colors["bbsim1"]
199 elif "bbsim-sadis-server" in container_name:
200 return colors["bbsim-sadis-server"]
201 elif "radius" in container_name:
202 return colors["radius"]
203 else:
204 return "black"
205
206
207def get_diff(data):
208 return [x - data[i - 1] for i, x in enumerate(data)][1:]
209
210
211def bytesto(b, to, bsize=1024):
212 """convert bytes to megabytes, etc.
213 sample code:
214 print('mb= ' + str(bytesto(314575262000000, 'm')))
215 sample output:
216 mb= 300002347.946
217 """
218
219 a = {'k': 1, 'm': 2, 'g': 3, 't': 4, 'p': 5, 'e': 6}
220 r = float(b)
221 for i in range(a[to]):
222 r = r / bsize
223
224 return r
225
226
227if __name__ == "__main__":
228 parser = argparse.ArgumentParser(prog="sizing")
229 parser.add_argument("-a", "--address", help="The address of the Prometheus instance we're targeting",
230 default="127.0.0.1:31301")
231 parser.add_argument("-o", "--output", help="Where to output the generated files",
232 default="plots")
233 parser.add_argument("-s", "--since", help="When to start sampling the data (in minutes before now)",
234 default=10)
235
236 args = parser.parse_args()
237 main(args.address, args.output, args.since)