summaryrefslogtreecommitdiff
path: root/src/tools/ceph.cc
blob: b36bf1d6edfa4bc7cd9626e6eb1a1e7b75d1f04c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2004-2010 Sage Weil <sage@newdream.net>
 * Copyright (C) 2010 Dreamhost
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation.  See file COPYING.
 *
 */


#include <limits.h>
#include <errno.h>
#include <fcntl.h>
#include <iostream>
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <vector>
#include <sys/socket.h>
#include <linux/un.h>
#include <unistd.h>
#include <string.h>

#include "common/ceph_argparse.h"
#include "global/global_init.h"
#include "global/debug.h"
#include "common/errno.h"
#include "common/safe_io.h"
#include "common/config.h"
#include "tools/common.h"

#include "include/compat.h"
#include "include/assert.h"

using std::vector;

void do_status(CephToolCtx *ctx, bool shutdown = false);

static void usage()
{
  cout << "usage:\n";
  cout << " ceph [options] [command]\n";
  cout << " ceph -s     cluster status summary\n";
  cout << " ceph -w     running cluster summary and events\n";
  cout << "\n";
  cout << "If no commands are specified, enter interactive mode.\n";
  cout << "\n";
  cout << "CLUSTER COMMANDS\n";
  cout << "  ceph health [detail]\n";
  cout << "  ceph quorum_status\n";
  cout << "  ceph -m <mon-ip-or-host> mon_status\n";
  cout << "\n";
  cout << "AUTHENTICATION (AUTH) COMMANDS\n";
  cout << "  ceph auth get-or-create[-key] <name> [capsys1 capval1 [...]]\n";
  cout << "  ceph auth del <name>\n";
  cout << "  ceph auth list\n";
  cout << "\n";
  cout << "METADATA SERVER (MDS) COMMANDS\n";
  cout << "  ceph mds stat\n";
  cout << "  ceph mds tell <mds-id or *> injectargs '--<switch> <value> [--<switch> <value>...]'\n";
  cout << "\n";
  cout << "MONITOR (MON) COMMANDS\n";
  cout << "  ceph mon add <name> <ip>[:<port>]\n";
  cout << "  ceph mon delete <name>\n";
  cout << "  ceph mon stat\n";
  cout << "  ceph mon tell <mon-id or *> injectargs '--<switch> <value> [--<switch> <value>...]'\n";
  cout << "\n";
  cout << "OBJECT STORAGE DEVICE (OSD) COMMANDS\n";
  cout << "  ceph osd dump [--format=json]\n";
  cout << "  ceph osd ls [--format=json]\n";
  cout << "  ceph osd tree\n";
  cout << "  ceph osd map <pool-name> <object-name>\n";
  cout << "  ceph osd down <osd-id>\n";
  cout << "  ceph osd in <osd-id>\n";
  cout << "  ceph osd out <osd-id>\n";
  cout << "  ceph osd set <noout|noin|nodown|noup>\n";
  cout << "  ceph osd unset <noout|noin|nodown|noup>\n";
  cout << "  ceph osd pause\n";
  cout << "  ceph osd unpause\n";
  cout << "  ceph osd tell <osd-id or *> injectargs '--<switch> <value> [--<switch> <value>...]'\n";
  cout << "  ceph osd getcrushmap -o <file>\n";
  cout << "  ceph osd getmap -o <file>\n";
  cout << "  ceph osd crush set <osd-id> <weight> <loc1> [<loc2> ...]\n";
  cout << "  ceph osd crush move <bucketname> <loc1> [<loc2> ...]\n";
  cout << "  ceph osd crush create-or-move <osd-id> <initial-weight> <loc1> [<loc2> ...]\n";
  cout << "  ceph osd crush reweight <name> <weight>\n";
  cout << "  ceph osd create [<uuid>]\n";
  cout << "  ceph osd rm <osd-id> [<osd-id>...]\n";
  cout << "  ceph osd lost [--yes-i-really-mean-it]\n";
  cout << "  ceph osd reweight <osd-id> <weight>\n";
  cout << "  ceph osd blacklist add <address>[:source_port] [time]\n";
  cout << "  ceph osd blacklist rm <address>[:source_port]\n";
  cout << "  ceph osd pool mksnap <pool> <snapname>\n";
  cout << "  ceph osd pool rmsnap <pool> <snapname>\n";
  cout << "  ceph osd pool create <pool> <pg_num> [<pgp_num>]\n";
  cout << "  ceph osd pool delete <pool>\n";
  cout << "  ceph osd pool rename <pool> <new pool name>\n";
  cout << "  ceph osd pool set <pool> <field> <value>\n";
  cout << "  ceph osd scrub <osd-id>\n";
  cout << "  ceph osd deep-scrub <osd-id>\n";
  cout << "  ceph osd repair <osd-id>\n";
  cout << "  ceph osd tell <osd-id or *> bench [bytes per write] [total bytes]\n";
  cout << "\n";
  cout << "PLACEMENT GROUP (PG) COMMANDS\n";
  cout << "  ceph pg dump\n";
  cout << "  ceph pg <pg-id> query\n";
  cout << "  ceph pg scrub <pg-id>\n";
  cout << "  ceph pg map <pg-id>\n";
  cout << "\n";
  cout << "OPTIONS\n";
  cout << "  -o <file>        Write out to <file>\n";
  cout << "  -i <file>        Read input from <file> (for some commands)\n";
  generic_client_usage(); // Will exit()
}

static void parse_cmd_args(vector<const char*> &args,
		std::string *in_file, std::string *out_file,
			   ceph_tool_mode_t *mode, bool *concise,
			   string *admin_socket, string *admin_socket_cmd,
			   string *watch_level)
{
  std::vector<const char*>::iterator i;
  std::string val;
  for (i = args.begin(); i != args.end(); ) {
    if (ceph_argparse_double_dash(args, i)) {
      break;
    } else if (ceph_argparse_witharg(args, i, &val, "-i", "--in-file", (char*)NULL)) {
      *in_file = val;
    } else if (ceph_argparse_witharg(args, i, &val, "-o", "--out-file", (char*)NULL)) {
      *out_file = val;
    } else if (ceph_argparse_witharg(args, i, &val, "--admin-daemon", (char*)NULL)) {
      *admin_socket = val;
      if (i == args.end())
	usage();
      *admin_socket_cmd = *i++;
    } else if (ceph_argparse_flag(args, i, "-s", "--status", (char*)NULL)) {
      *mode = CEPH_TOOL_MODE_STATUS;
    } else if (ceph_argparse_flag(args, i, "-w", "--watch", (char*)NULL)) {
      *mode = CEPH_TOOL_MODE_WATCH;
    } else if (ceph_argparse_flag(args, i, "--watch-debug", (char*) NULL)) {
      *watch_level = "log-debug";
    } else if (ceph_argparse_flag(args, i, "--watch-info", (char*) NULL)) {
      *watch_level = "log-info";
    } else if (ceph_argparse_flag(args, i, "--watch-sec", (char*) NULL)) {
      *watch_level = "log-sec";
    } else if (ceph_argparse_flag(args, i, "--watch-warn", (char*) NULL)) {
      *watch_level = "log-warn";
    } else if (ceph_argparse_flag(args, i, "--watch-error", (char*) NULL)) {
      *watch_level = "log-error";
    } else if (ceph_argparse_flag(args, i, "--concise", (char*)NULL)) {
      *concise = true;
    } else if (ceph_argparse_flag(args, i, "--verbose", (char*)NULL)) {
      *concise = false;
    } else if (ceph_argparse_flag(args, i, "-h", "--help", (char*)NULL)) {
      usage();
    } else {
      if (admin_socket_cmd && admin_socket_cmd->length()) {
	*admin_socket_cmd += " " + string(*i);
      }
      ++i;
    }
  }
}

static int get_indata(const char *in_file, bufferlist &indata)
{
  int fd = TEMP_FAILURE_RETRY(::open(in_file, O_RDONLY));
  if (fd < 0) {
    int err = errno;
    derr << "error opening in_file '" << in_file << "': "
	 << cpp_strerror(err) << dendl;
    return 1;
  }
  struct stat st;
  if (::fstat(fd, &st)) {
    int err = errno;
    derr << "error getting size of in_file '" << in_file << "': "
	 << cpp_strerror(err) << dendl;
    return 1;
  }

  indata.push_back(buffer::create(st.st_size));
  indata.zero();
  int ret = safe_read_exact(fd, indata.c_str(), st.st_size);
  if (ret) {
    derr << "error reading in_file '" << in_file << "': "
	 << cpp_strerror(ret) << dendl;
    TEMP_FAILURE_RETRY(::close(fd));
    return 1;
  }

  TEMP_FAILURE_RETRY(::close(fd));
  derr << "read " << st.st_size << " bytes from " << in_file << dendl;
  return 0;
}

int do_admin_socket(string path, string cmd)
{
  struct sockaddr_un address;
  int fd;
  int r;
  
  fd = socket(PF_UNIX, SOCK_STREAM, 0);
  if(fd < 0) {
    cerr << "socket failed with " << cpp_strerror(errno) << std::endl;
    return -1;
  }

  memset(&address, 0, sizeof(struct sockaddr_un));
  address.sun_family = AF_UNIX;
  snprintf(address.sun_path, UNIX_PATH_MAX, "%s", path.c_str());

  if (connect(fd, (struct sockaddr *) &address, 
	      sizeof(struct sockaddr_un)) != 0) {
    cerr << "connect to " << path << " failed with " << cpp_strerror(errno) << std::endl;
    return -1;
  }
  
  char *buf;
  uint32_t len;
  r = safe_write(fd, cmd.c_str(), cmd.length() + 1);
  if (r < 0) {
    cerr << "write to " << path << " failed with " << cpp_strerror(errno) << std::endl;
    goto out;
  }
  
  r = safe_read(fd, &len, sizeof(len));
  if (r < 0) {
    cerr << "read " << len << " length from " << path << " failed with " << cpp_strerror(errno) << std::endl;
    goto out;
  }
  if (r < 4) {
    cerr << "read only got " << r << " bytes of 4 expected for response length; invalid command?" << std::endl;
    goto out;
  }
  len = ntohl(len);

  buf = new char[len+1];
  r = safe_read(fd, buf, len);
  if (r < 0) {
    cerr << "read " << len << " bytes from " << path << " failed with " << cpp_strerror(errno) << std::endl;
    goto out;
  }
  buf[len] = '\0';

  cout << buf << std::endl;
  r = 0;

 out:
  ::close(fd);
  return r;
}


int main(int argc, const char **argv)
{
  std::string in_file, out_file;
  enum ceph_tool_mode_t mode = CEPH_TOOL_MODE_CLI_INPUT;
  vector<const char*> args;
  argv_to_vec(argc, argv, args);
  env_to_vec(args);

  // initialize globals
  global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT, CODE_ENVIRONMENT_UTILITY, 0);
  common_init_finish(g_ceph_context);

  // parse user input
  bool concise = true;
  string admin_socket;
  string admin_socket_cmd;
  string watch_level = "log-info";
  parse_cmd_args(args, &in_file, &out_file, &mode, &concise, 
		 &admin_socket, &admin_socket_cmd, &watch_level);

  // daemon admin socket?
  if (admin_socket.length()) {
    return do_admin_socket(admin_socket, admin_socket_cmd);
  }

  // input
  bufferlist indata;
  if (!in_file.empty()) {
    if (get_indata(in_file.c_str(), indata)) {
      derr << "failed to get data from '" << in_file << "'" << dendl;
      return 1;
    }
  }

  CephToolCtx *ctx = ceph_tool_common_init(mode, concise);
  if (!ctx) {
    derr << "ceph_tool_common_init failed." << dendl;
    return 1;
  }
  signal(SIGINT, SIG_DFL);
  signal(SIGTERM, SIG_DFL);

  bufferlist outbl;
  int ret = 0;
  switch (mode) {
    case CEPH_TOOL_MODE_STATUS:
      do_status(ctx, true);
      break;
    case CEPH_TOOL_MODE_WATCH: {
      do_status(ctx);
      ctx->lock.Lock();
      ctx->dispatcher->subs.name = watch_level;
      ctx->dispatcher->subs.last_known_version = 0;
      ctx->mc.sub_want(watch_level, 0, 0);
      ctx->mc.renew_subs();
      ctx->lock.Unlock();
      break;
    }

    case CEPH_TOOL_MODE_CLI_INPUT: {
      if (args.empty()) {
	if (ceph_tool_do_cli(ctx))
	  ret = 1;
      } else {
	while (!args.empty()) {
	  vector<string> cmd;
	  for (vector<const char*>::iterator n = args.begin();
	       n != args.end(); ) {
	    std::string np(*n);
	    n = args.erase(n);
	    if (np == ";")
	      break;
	    cmd.push_back(np);
	  }

	  bufferlist obl;
	  if (do_command(ctx, cmd, indata, obl) < 0) {
	    ret = 1;
	    break;
	  }
	  outbl.claim(obl);
	}
      }
      if (ceph_tool_messenger_shutdown())
	ret = 1;
      break;
    }

    default: {
      derr << "logic error: illegal ceph command mode " << mode << dendl;
      ret = 1;
      break;
    }
  }
 
  if (ret == 0 && outbl.length()) {
    // output
    int err;
    if (out_file.empty() || out_file == "-") {
      err = outbl.write_fd(STDOUT_FILENO);
    } else {
      int out_fd = TEMP_FAILURE_RETRY(::open(out_file.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0644));
      if (out_fd < 0) {
	int ret = errno;
	derr << " failed to create file '" << out_file << "': "
	     << cpp_strerror(ret) << dendl;
	return 1;
      }
      err = outbl.write_fd(out_fd);
      ::close(out_fd);
    }
    if (err) {
      derr << " failed to write " << outbl.length() << " bytes to " << out_file << ": "
	   << cpp_strerror(err) << dendl;
      ret = 1;
    } else if (!concise && !out_file.empty())
      cerr << " wrote " << outbl.length() << " byte payload to " << out_file << std::endl;
  }

  if (ceph_tool_common_shutdown(ctx))
    ret = 1;
  return ret;
}