]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 | ||
7c673cae FG |
15 | #include <sys/utsname.h> |
16 | #include <boost/lexical_cast.hpp> | |
17 | ||
18 | #include "include/compat.h" | |
19 | #include "include/util.h" | |
20 | #include "common/debug.h" | |
21 | #include "common/errno.h" | |
7c673cae FG |
22 | #include "common/version.h" |
23 | ||
24 | #ifdef HAVE_SYS_VFS_H | |
25 | #include <sys/vfs.h> | |
26 | #endif | |
27 | ||
28 | #if defined(DARWIN) || defined(__FreeBSD__) | |
29 | #include <sys/param.h> | |
30 | #include <sys/mount.h> | |
31 | #endif | |
32 | ||
33 | int64_t unit_to_bytesize(string val, ostream *pss) | |
34 | { | |
35 | if (val.empty()) { | |
36 | if (pss) | |
37 | *pss << "value is empty!"; | |
38 | return -EINVAL; | |
39 | } | |
40 | ||
41 | char c = val[val.length()-1]; | |
42 | int modifier = 0; | |
43 | if (!::isdigit(c)) { | |
44 | if (val.length() < 2) { | |
45 | if (pss) | |
46 | *pss << "invalid value: " << val; | |
47 | return -EINVAL; | |
48 | } | |
49 | val = val.substr(0,val.length()-1); | |
50 | switch (c) { | |
51 | case 'B': | |
52 | break; | |
53 | case 'k': | |
54 | case 'K': | |
55 | modifier = 10; | |
56 | break; | |
57 | case 'M': | |
58 | modifier = 20; | |
59 | break; | |
60 | case 'G': | |
61 | modifier = 30; | |
62 | break; | |
63 | case 'T': | |
64 | modifier = 40; | |
65 | break; | |
66 | case 'P': | |
67 | modifier = 50; | |
68 | break; | |
69 | case 'E': | |
70 | modifier = 60; | |
71 | break; | |
72 | default: | |
73 | if (pss) | |
74 | *pss << "unrecognized modifier '" << c << "'" << std::endl; | |
75 | return -EINVAL; | |
76 | } | |
77 | } | |
78 | ||
79 | if (val[0] == '+' || val[0] == '-') { | |
80 | if (pss) | |
81 | *pss << "expected numerical value, got: " << val; | |
82 | return -EINVAL; | |
83 | } | |
84 | ||
85 | string err; | |
86 | int64_t r = strict_strtoll(val.c_str(), 10, &err); | |
87 | if ((r == 0) && !err.empty()) { | |
88 | if (pss) | |
89 | *pss << err; | |
90 | return -1; | |
91 | } | |
92 | if (r < 0) { | |
93 | if (pss) | |
94 | *pss << "unable to parse positive integer '" << val << "'"; | |
95 | return -1; | |
96 | } | |
97 | return (r * (1LL << modifier)); | |
98 | } | |
99 | ||
100 | int get_fs_stats(ceph_data_stats_t &stats, const char *path) | |
101 | { | |
102 | if (!path) | |
103 | return -EINVAL; | |
104 | ||
105 | struct statfs stbuf; | |
106 | int err = ::statfs(path, &stbuf); | |
107 | if (err < 0) { | |
108 | return -errno; | |
109 | } | |
110 | ||
111 | stats.byte_total = stbuf.f_blocks * stbuf.f_bsize; | |
112 | stats.byte_used = (stbuf.f_blocks - stbuf.f_bfree) * stbuf.f_bsize; | |
113 | stats.byte_avail = stbuf.f_bavail * stbuf.f_bsize; | |
114 | stats.avail_percent = (((float)stats.byte_avail/stats.byte_total)*100); | |
115 | return 0; | |
116 | } | |
117 | ||
118 | static char* value_sanitize(char *value) | |
119 | { | |
120 | while (isspace(*value) || *value == '"') | |
121 | value++; | |
122 | ||
123 | char* end = value + strlen(value) - 1; | |
124 | while (end > value && (isspace(*end) || *end == '"')) | |
125 | end--; | |
126 | ||
127 | *(end + 1) = '\0'; | |
128 | ||
129 | return value; | |
130 | } | |
131 | ||
132 | static bool value_set(char *buf, const char *prefix, | |
133 | map<string, string> *pm, const char *key) | |
134 | { | |
135 | if (strncmp(buf, prefix, strlen(prefix))) { | |
136 | return false; | |
137 | } | |
138 | ||
139 | (*pm)[key] = value_sanitize(buf + strlen(prefix)); | |
140 | return true; | |
141 | } | |
142 | ||
143 | static void file_values_parse(const map<string, string>& kvm, FILE *fp, map<string, string> *m, CephContext *cct) { | |
144 | char buf[512]; | |
145 | while (fgets(buf, sizeof(buf) - 1, fp) != NULL) { | |
146 | for (auto& kv : kvm) { | |
147 | if (value_set(buf, kv.second.c_str(), m, kv.first.c_str())) | |
148 | continue; | |
149 | } | |
150 | } | |
151 | } | |
152 | ||
153 | static bool os_release_parse(map<string, string> *m, CephContext *cct) | |
154 | { | |
155 | static const map<string, string> kvm = { | |
156 | { "distro", "ID=" }, | |
157 | { "distro_description", "PRETTY_NAME=" }, | |
158 | { "distro_version", "VERSION_ID=" } | |
159 | }; | |
160 | ||
161 | FILE *fp = fopen("/etc/os-release", "r"); | |
162 | if (!fp) { | |
163 | int ret = -errno; | |
164 | lderr(cct) << "os_release_parse - failed to open /etc/os-release: " << cpp_strerror(ret) << dendl; | |
165 | return false; | |
166 | } | |
167 | ||
168 | file_values_parse(kvm, fp, m, cct); | |
169 | ||
170 | fclose(fp); | |
171 | ||
172 | return true; | |
173 | } | |
174 | ||
175 | static void distro_detect(map<string, string> *m, CephContext *cct) | |
176 | { | |
177 | if (!os_release_parse(m, cct)) { | |
178 | lderr(cct) << "distro_detect - /etc/os-release is required" << dendl; | |
179 | } | |
180 | ||
181 | for (const char* rk: {"distro", "distro_version"}) { | |
182 | if (m->find(rk) == m->end()) | |
183 | lderr(cct) << "distro_detect - can't detect " << rk << dendl; | |
184 | } | |
185 | } | |
186 | ||
187 | void collect_sys_info(map<string, string> *m, CephContext *cct) | |
188 | { | |
189 | // version | |
190 | (*m)["ceph_version"] = pretty_version_to_str(); | |
191 | ||
192 | // kernel info | |
193 | struct utsname u; | |
194 | int r = uname(&u); | |
195 | if (r >= 0) { | |
196 | (*m)["os"] = u.sysname; | |
197 | (*m)["kernel_version"] = u.release; | |
198 | (*m)["kernel_description"] = u.version; | |
199 | (*m)["hostname"] = u.nodename; | |
200 | (*m)["arch"] = u.machine; | |
201 | } | |
202 | ||
203 | // memory | |
204 | FILE *f = fopen(PROCPREFIX "/proc/meminfo", "r"); | |
205 | if (f) { | |
206 | char buf[100]; | |
207 | while (!feof(f)) { | |
208 | char *line = fgets(buf, sizeof(buf), f); | |
209 | if (!line) | |
210 | break; | |
211 | char key[40]; | |
212 | long long value; | |
213 | int r = sscanf(line, "%s %lld", key, &value); | |
214 | if (r == 2) { | |
215 | if (strcmp(key, "MemTotal:") == 0) | |
216 | (*m)["mem_total_kb"] = boost::lexical_cast<string>(value); | |
217 | else if (strcmp(key, "SwapTotal:") == 0) | |
218 | (*m)["mem_swap_kb"] = boost::lexical_cast<string>(value); | |
219 | } | |
220 | } | |
221 | fclose(f); | |
222 | } | |
223 | ||
224 | // processor | |
225 | f = fopen(PROCPREFIX "/proc/cpuinfo", "r"); | |
226 | if (f) { | |
227 | char buf[100]; | |
228 | while (!feof(f)) { | |
229 | char *line = fgets(buf, sizeof(buf), f); | |
230 | if (!line) | |
231 | break; | |
232 | if (strncmp(line, "model name", 10) == 0) { | |
233 | char *c = strchr(buf, ':'); | |
234 | c++; | |
235 | while (*c == ' ') | |
236 | ++c; | |
237 | char *nl = c; | |
238 | while (*nl != '\n') | |
239 | ++nl; | |
240 | *nl = '\0'; | |
241 | (*m)["cpu"] = c; | |
242 | break; | |
243 | } | |
244 | } | |
245 | fclose(f); | |
246 | } | |
247 | ||
248 | // distro info | |
249 | distro_detect(m, cct); | |
250 | } | |
251 | ||
252 | void dump_services(Formatter* f, const map<string, list<int> >& services, const char* type) | |
253 | { | |
254 | assert(f); | |
255 | ||
256 | f->open_object_section(type); | |
257 | for (map<string, list<int> >::const_iterator host = services.begin(); | |
258 | host != services.end(); ++host) { | |
259 | f->open_array_section(host->first.c_str()); | |
260 | const list<int>& hosted = host->second; | |
261 | for (list<int>::const_iterator s = hosted.begin(); | |
262 | s != hosted.end(); ++s) { | |
263 | f->dump_int(type, *s); | |
264 | } | |
265 | f->close_section(); | |
266 | } | |
267 | f->close_section(); | |
268 | } | |
269 | ||
270 | ||
271 | // If non-printable characters found then convert bufferlist to | |
272 | // base64 encoded string indicating whether it did. | |
273 | string cleanbin(bufferlist &bl, bool &base64) | |
274 | { | |
275 | bufferlist::iterator it; | |
276 | for (it = bl.begin(); it != bl.end(); ++it) { | |
277 | if (iscntrl(*it)) | |
278 | break; | |
279 | } | |
280 | if (it == bl.end()) { | |
281 | base64 = false; | |
282 | string result(bl.c_str(), bl.length()); | |
283 | return result; | |
284 | } | |
285 | ||
286 | bufferlist b64; | |
287 | bl.encode_base64(b64); | |
288 | string encoded(b64.c_str(), b64.length()); | |
289 | base64 = true; | |
290 | return encoded; | |
291 | } | |
292 | ||
293 | // If non-printable characters found then convert to "Base64:" followed by | |
294 | // base64 encoding | |
295 | string cleanbin(string &str) | |
296 | { | |
297 | bool base64; | |
298 | bufferlist bl; | |
299 | bl.append(str); | |
300 | string result = cleanbin(bl, base64); | |
301 | if (base64) | |
302 | result = "Base64:" + result; | |
303 | return result; | |
304 | } |