]>
Commit | Line | Data |
---|---|---|
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- | |
2 | // vim: ts=8 sw=2 smarttab | |
3 | /* | |
4 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * Copyright (C) 2012 Inktank Storage, Inc. | |
7 | * | |
8 | * This is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public | |
10 | * License version 2.1, as published by the Free Software | |
11 | * Foundation. See file COPYING. | |
12 | * | |
13 | */ | |
14 | ||
15 | #ifndef _WIN32 | |
16 | #include <sys/utsname.h> | |
17 | #endif | |
18 | ||
19 | #include <fstream> | |
20 | #include <boost/algorithm/string.hpp> | |
21 | ||
22 | #include "include/compat.h" | |
23 | #include "include/util.h" | |
24 | #include "common/debug.h" | |
25 | #include "common/errno.h" | |
26 | #include "common/version.h" | |
27 | ||
28 | #ifdef HAVE_SYS_VFS_H | |
29 | #include <sys/vfs.h> | |
30 | #endif | |
31 | ||
32 | #if defined(__APPLE__) || defined(__FreeBSD__) | |
33 | #include <sys/param.h> | |
34 | #include <sys/mount.h> | |
35 | #if defined(__APPLE__) | |
36 | #include <sys/types.h> | |
37 | #include <sys/sysctl.h> | |
38 | #endif | |
39 | #endif | |
40 | ||
41 | #include <string> | |
42 | ||
43 | #include <stdio.h> | |
44 | ||
45 | using std::list; | |
46 | using std::map; | |
47 | using std::string; | |
48 | ||
49 | using ceph::bufferlist; | |
50 | using ceph::Formatter; | |
51 | ||
52 | #ifndef _WIN32 | |
53 | int get_fs_stats(ceph_data_stats_t &stats, const char *path) | |
54 | { | |
55 | if (!path) | |
56 | return -EINVAL; | |
57 | ||
58 | struct statfs stbuf; | |
59 | int err = ::statfs(path, &stbuf); | |
60 | if (err < 0) { | |
61 | return -errno; | |
62 | } | |
63 | ||
64 | stats.byte_total = stbuf.f_blocks * stbuf.f_bsize; | |
65 | stats.byte_used = (stbuf.f_blocks - stbuf.f_bfree) * stbuf.f_bsize; | |
66 | stats.byte_avail = stbuf.f_bavail * stbuf.f_bsize; | |
67 | stats.avail_percent = (((float)stats.byte_avail/stats.byte_total)*100); | |
68 | return 0; | |
69 | } | |
70 | #else | |
71 | int get_fs_stats(ceph_data_stats_t &stats, const char *path) | |
72 | { | |
73 | ULARGE_INTEGER avail_bytes, total_bytes, total_free_bytes; | |
74 | ||
75 | if (!GetDiskFreeSpaceExA(path, &avail_bytes, | |
76 | &total_bytes, &total_free_bytes)) { | |
77 | return -EINVAL; | |
78 | } | |
79 | ||
80 | stats.byte_total = total_bytes.QuadPart; | |
81 | stats.byte_used = total_bytes.QuadPart - total_free_bytes.QuadPart; | |
82 | // may not be equal to total_free_bytes due to quotas | |
83 | stats.byte_avail = avail_bytes.QuadPart; | |
84 | stats.avail_percent = ((float)stats.byte_avail / stats.byte_total) * 100; | |
85 | return 0; | |
86 | } | |
87 | #endif | |
88 | ||
89 | static char* value_sanitize(char *value) | |
90 | { | |
91 | while (isspace(*value) || *value == '"') | |
92 | value++; | |
93 | ||
94 | char* end = value + strlen(value) - 1; | |
95 | while (end > value && (isspace(*end) || *end == '"')) | |
96 | end--; | |
97 | ||
98 | *(end + 1) = '\0'; | |
99 | ||
100 | return value; | |
101 | } | |
102 | ||
103 | static bool value_set(char *buf, const char *prefix, | |
104 | map<string, string> *pm, const char *key) | |
105 | { | |
106 | if (strncmp(buf, prefix, strlen(prefix))) { | |
107 | return false; | |
108 | } | |
109 | ||
110 | (*pm)[key] = value_sanitize(buf + strlen(prefix)); | |
111 | return true; | |
112 | } | |
113 | ||
114 | static void file_values_parse(const map<string, string>& kvm, FILE *fp, map<string, string> *m, CephContext *cct) { | |
115 | char buf[512]; | |
116 | while (fgets(buf, sizeof(buf) - 1, fp) != NULL) { | |
117 | for (auto& kv : kvm) { | |
118 | if (value_set(buf, kv.second.c_str(), m, kv.first.c_str())) | |
119 | continue; | |
120 | } | |
121 | } | |
122 | } | |
123 | ||
124 | static bool os_release_parse(map<string, string> *m, CephContext *cct) | |
125 | { | |
126 | #if defined(__linux__) | |
127 | static const map<string, string> kvm = { | |
128 | { "distro", "ID=" }, | |
129 | { "distro_description", "PRETTY_NAME=" }, | |
130 | { "distro_version", "VERSION_ID=" } | |
131 | }; | |
132 | ||
133 | FILE *fp = fopen("/etc/os-release", "r"); | |
134 | if (!fp) { | |
135 | int ret = -errno; | |
136 | lderr(cct) << "os_release_parse - failed to open /etc/os-release: " << cpp_strerror(ret) << dendl; | |
137 | return false; | |
138 | } | |
139 | ||
140 | file_values_parse(kvm, fp, m, cct); | |
141 | ||
142 | fclose(fp); | |
143 | #elif defined(__FreeBSD__) | |
144 | struct utsname u; | |
145 | int r = uname(&u); | |
146 | if (!r) { | |
147 | m->insert(std::make_pair("distro", u.sysname)); | |
148 | m->insert(std::make_pair("distro_description", u.version)); | |
149 | m->insert(std::make_pair("distro_version", u.release)); | |
150 | } | |
151 | #endif | |
152 | ||
153 | return true; | |
154 | } | |
155 | ||
156 | static void distro_detect(map<string, string> *m, CephContext *cct) | |
157 | { | |
158 | if (!os_release_parse(m, cct)) { | |
159 | lderr(cct) << "distro_detect - /etc/os-release is required" << dendl; | |
160 | } | |
161 | ||
162 | for (const char* rk: {"distro", "distro_description"}) { | |
163 | if (m->find(rk) == m->end()) | |
164 | lderr(cct) << "distro_detect - can't detect " << rk << dendl; | |
165 | } | |
166 | } | |
167 | ||
168 | int get_cgroup_memory_limit(uint64_t *limit) | |
169 | { | |
170 | #if defined(__linux__) | |
171 | // /sys/fs/cgroup/memory/memory.limit_in_bytes | |
172 | ||
173 | // the magic value 9223372036854771712 or 0x7ffffffffffff000 | |
174 | // appears to mean no limit. | |
175 | FILE *f = fopen(PROCPREFIX "/sys/fs/cgroup/memory/memory.limit_in_bytes", "r"); | |
176 | if (!f) { | |
177 | return -errno; | |
178 | } | |
179 | char buf[100]; | |
180 | int ret = 0; | |
181 | long long value; | |
182 | char *line = fgets(buf, sizeof(buf), f); | |
183 | if (!line) { | |
184 | ret = -EINVAL; | |
185 | goto out; | |
186 | } | |
187 | if (sscanf(line, "%lld", &value) != 1) { | |
188 | ret = -EINVAL; | |
189 | } | |
190 | if (value == 0x7ffffffffffff000) { | |
191 | *limit = 0; // no limit | |
192 | } else { | |
193 | *limit = value; | |
194 | } | |
195 | out: | |
196 | fclose(f); | |
197 | return ret; | |
198 | #else | |
199 | return 0; | |
200 | #endif | |
201 | } | |
202 | ||
203 | #ifdef _WIN32 | |
204 | int get_windows_version(POSVERSIONINFOEXW ver) { | |
205 | using get_version_func_t = DWORD (WINAPI *)(OSVERSIONINFOEXW*); | |
206 | ||
207 | // We'll load the library directly to avoid depending on the NTDDK. | |
208 | HMODULE ntdll_lib = LoadLibraryW(L"Ntdll.dll"); | |
209 | if (!ntdll_lib) { | |
210 | return -EINVAL; | |
211 | } | |
212 | ||
213 | // The standard "GetVersion" returned values depend on the application | |
214 | // manifest. We'll get the "real" version by using the Rtl* version. | |
215 | auto get_version_func = ( | |
216 | get_version_func_t)GetProcAddress(ntdll_lib, "RtlGetVersion"); | |
217 | int ret = 0; | |
218 | if (!get_version_func || get_version_func(ver)) { | |
219 | // RtlGetVersion returns non-zero values in case of errors. | |
220 | ret = -EINVAL; | |
221 | } | |
222 | ||
223 | FreeLibrary(ntdll_lib); | |
224 | return ret; | |
225 | } | |
226 | #endif | |
227 | ||
228 | void collect_sys_info(map<string, string> *m, CephContext *cct) | |
229 | { | |
230 | // version | |
231 | (*m)["ceph_version"] = pretty_version_to_str(); | |
232 | (*m)["ceph_version_short"] = ceph_version_to_str(); | |
233 | (*m)["ceph_release"] = ceph_release_to_str(); | |
234 | ||
235 | #ifndef _WIN32 | |
236 | // kernel info | |
237 | struct utsname u; | |
238 | int r = uname(&u); | |
239 | if (r >= 0) { | |
240 | (*m)["os"] = u.sysname; | |
241 | (*m)["kernel_version"] = u.release; | |
242 | (*m)["kernel_description"] = u.version; | |
243 | (*m)["hostname"] = u.nodename; | |
244 | (*m)["arch"] = u.machine; | |
245 | } | |
246 | #else | |
247 | OSVERSIONINFOEXW ver = {0}; | |
248 | ver.dwOSVersionInfoSize = sizeof(ver); | |
249 | get_windows_version(&ver); | |
250 | ||
251 | char version_str[64]; | |
252 | snprintf(version_str, 64, "%lu.%lu (%lu)", | |
253 | ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber); | |
254 | ||
255 | char hostname[64]; | |
256 | DWORD hostname_sz = sizeof(hostname); | |
257 | GetComputerNameA(hostname, &hostname_sz); | |
258 | ||
259 | SYSTEM_INFO sys_info; | |
260 | const char* arch_str; | |
261 | GetNativeSystemInfo(&sys_info); | |
262 | ||
263 | switch (sys_info.wProcessorArchitecture) { | |
264 | case PROCESSOR_ARCHITECTURE_AMD64: | |
265 | arch_str = "x86_64"; | |
266 | break; | |
267 | case PROCESSOR_ARCHITECTURE_INTEL: | |
268 | arch_str = "x86"; | |
269 | break; | |
270 | case PROCESSOR_ARCHITECTURE_ARM: | |
271 | arch_str = "arm"; | |
272 | break; | |
273 | default: | |
274 | arch_str = "unknown"; | |
275 | break; | |
276 | } | |
277 | ||
278 | (*m)["os"] = "Windows"; | |
279 | (*m)["kernel_version"] = version_str; | |
280 | (*m)["kernel_description"] = version_str; | |
281 | (*m)["hostname"] = hostname; | |
282 | (*m)["arch"] = arch_str; | |
283 | #endif | |
284 | ||
285 | // but wait, am i in a container? | |
286 | bool in_container = false; | |
287 | ||
288 | if (const char *pod_name = getenv("POD_NAME")) { | |
289 | (*m)["pod_name"] = pod_name; | |
290 | in_container = true; | |
291 | } | |
292 | if (const char *container_name = getenv("CONTAINER_NAME")) { | |
293 | (*m)["container_name"] = container_name; | |
294 | in_container = true; | |
295 | } | |
296 | if (const char *container_image = getenv("CONTAINER_IMAGE")) { | |
297 | (*m)["container_image"] = container_image; | |
298 | in_container = true; | |
299 | } | |
300 | if (in_container) { | |
301 | if (const char *node_name = getenv("NODE_NAME")) { | |
302 | (*m)["container_hostname"] = (*m)["hostname"]; | |
303 | (*m)["hostname"] = node_name; | |
304 | } | |
305 | if (const char *ns = getenv("POD_NAMESPACE")) { | |
306 | (*m)["pod_namespace"] = ns; | |
307 | } | |
308 | } | |
309 | ||
310 | #ifdef __APPLE__ | |
311 | // memory | |
312 | { | |
313 | uint64_t size; | |
314 | size_t len = sizeof(size); | |
315 | r = sysctlbyname("hw.memsize", &size, &len, NULL, 0); | |
316 | if (r == 0) { | |
317 | (*m)["mem_total_kb"] = std::to_string(size); | |
318 | } | |
319 | } | |
320 | { | |
321 | xsw_usage vmusage; | |
322 | size_t len = sizeof(vmusage); | |
323 | r = sysctlbyname("vm.swapusage", &vmusage, &len, NULL, 0); | |
324 | if (r == 0) { | |
325 | (*m)["mem_swap_kb"] = std::to_string(vmusage.xsu_total); | |
326 | } | |
327 | } | |
328 | // processor | |
329 | { | |
330 | char buf[100]; | |
331 | size_t len = sizeof(buf); | |
332 | r = sysctlbyname("machdep.cpu.brand_string", buf, &len, NULL, 0); | |
333 | if (r == 0) { | |
334 | buf[len - 1] = '\0'; | |
335 | (*m)["cpu"] = buf; | |
336 | } | |
337 | } | |
338 | #elif !defined(_WIN32) | |
339 | // memory | |
340 | if (std::ifstream f{PROCPREFIX "/proc/meminfo"}; !f.fail()) { | |
341 | for (std::string line; std::getline(f, line); ) { | |
342 | std::vector<string> parts; | |
343 | boost::split(parts, line, boost::is_any_of(":\t "), boost::token_compress_on); | |
344 | if (parts.size() != 3) { | |
345 | continue; | |
346 | } | |
347 | if (parts[0] == "MemTotal") { | |
348 | (*m)["mem_total_kb"] = parts[1]; | |
349 | } else if (parts[0] == "SwapTotal") { | |
350 | (*m)["mem_swap_kb"] = parts[1]; | |
351 | } | |
352 | } | |
353 | } | |
354 | uint64_t cgroup_limit; | |
355 | if (get_cgroup_memory_limit(&cgroup_limit) == 0 && | |
356 | cgroup_limit > 0) { | |
357 | (*m)["mem_cgroup_limit"] = std::to_string(cgroup_limit); | |
358 | } | |
359 | ||
360 | // processor | |
361 | if (std::ifstream f{PROCPREFIX "/proc/cpuinfo"}; !f.fail()) { | |
362 | for (std::string line; std::getline(f, line); ) { | |
363 | std::vector<string> parts; | |
364 | boost::split(parts, line, boost::is_any_of(":")); | |
365 | if (parts.size() != 2) { | |
366 | continue; | |
367 | } | |
368 | boost::trim(parts[0]); | |
369 | boost::trim(parts[1]); | |
370 | if (parts[0] == "model name") { | |
371 | (*m)["cpu"] = parts[1]; | |
372 | break; | |
373 | } | |
374 | } | |
375 | } | |
376 | #endif | |
377 | // distro info | |
378 | distro_detect(m, cct); | |
379 | } | |
380 | ||
381 | void dump_services(Formatter* f, const map<string, list<int> >& services, const char* type) | |
382 | { | |
383 | ceph_assert(f); | |
384 | ||
385 | f->open_object_section(type); | |
386 | for (auto host = services.begin(); | |
387 | host != services.end(); ++host) { | |
388 | f->open_array_section(host->first.c_str()); | |
389 | const list<int>& hosted = host->second; | |
390 | for (auto s = hosted.cbegin(); | |
391 | s != hosted.cend(); ++s) { | |
392 | f->dump_int(type, *s); | |
393 | } | |
394 | f->close_section(); | |
395 | } | |
396 | f->close_section(); | |
397 | } | |
398 | ||
399 | void dump_services(Formatter* f, const map<string, list<string> >& services, const char* type) | |
400 | { | |
401 | ceph_assert(f); | |
402 | ||
403 | f->open_object_section(type); | |
404 | for (const auto& host : services) { | |
405 | f->open_array_section(host.first.c_str()); | |
406 | const auto& hosted = host.second; | |
407 | for (const auto& s : hosted) { | |
408 | f->dump_string(type, s); | |
409 | } | |
410 | f->close_section(); | |
411 | } | |
412 | f->close_section(); | |
413 | } | |
414 | ||
415 | // If non-printable characters found then convert bufferlist to | |
416 | // base64 encoded string indicating whether it did. | |
417 | string cleanbin(bufferlist &bl, bool &base64, bool show) | |
418 | { | |
419 | bufferlist::iterator it; | |
420 | for (it = bl.begin(); it != bl.end(); ++it) { | |
421 | if (iscntrl(*it)) | |
422 | break; | |
423 | } | |
424 | if (it == bl.end()) { | |
425 | base64 = false; | |
426 | string result(bl.c_str(), bl.length()); | |
427 | return result; | |
428 | } | |
429 | ||
430 | bufferlist b64; | |
431 | bl.encode_base64(b64); | |
432 | string encoded(b64.c_str(), b64.length()); | |
433 | if (show) | |
434 | encoded = "Base64:" + encoded; | |
435 | base64 = true; | |
436 | return encoded; | |
437 | } | |
438 | ||
439 | // If non-printable characters found then convert to "Base64:" followed by | |
440 | // base64 encoding | |
441 | string cleanbin(string &str) | |
442 | { | |
443 | bool base64; | |
444 | bufferlist bl; | |
445 | bl.append(str); | |
446 | string result = cleanbin(bl, base64, true); | |
447 | return result; | |
448 | } | |
449 | ||
450 | std::string bytes2str(uint64_t count) { | |
451 | static char s[][2] = {"\0", "k", "M", "G", "T", "P", "E", "\0"}; | |
452 | int i = 0; | |
453 | while (count >= 1024 && *s[i+1]) { | |
454 | count >>= 10; | |
455 | i++; | |
456 | } | |
457 | char str[128]; | |
458 | snprintf(str, sizeof str, "%" PRIu64 "%sB", count, s[i]); | |
459 | return std::string(str); | |
460 | } |