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