XRootD
XrdMapCluster.cc
Go to the documentation of this file.
1 /******************************************************************************/
2 /* */
3 /* X r d M a p C l u s t e r . c c */
4 /* */
5 /* (c) 2013 by the Board of Trustees of the Leland Stanford, Jr., University */
6 /* Produced by Andrew Hanushevsky for Stanford University under contract */
7 /* DE-AC02-76-SFO0515 with the Department of Energy */
8 /* */
9 /* This file is part of the XRootD software suite. */
10 /* */
11 /* XRootD is free software: you can redistribute it and/or modify it under */
12 /* the terms of the GNU Lesser General Public License as published by the */
13 /* Free Software Foundation, either version 3 of the License, or (at your */
14 /* option) any later version. */
15 /* */
16 /* XRootD is distributed in the hope that it will be useful, but WITHOUT */
17 /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
18 /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
19 /* License for more details. */
20 /* */
21 /* You should have received a copy of the GNU Lesser General Public License */
22 /* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
23 /* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
24 /* */
25 /* The copyright holder's institutional names and contributor's names may not */
26 /* be used to endorse or promote products derived from this software without */
27 /* specific prior written permission of the institution or contributor. */
28 /******************************************************************************/
29 
30 /* This utility maps the connections in a cluster starting at some node. It
31  can also, optionally, check for file existence at each point. Syntax:
32 
33  xrdmapc <host>:<port> [<path>]
34 
35 */
36 
37 /******************************************************************************/
38 /* i n c l u d e f i l e s */
39 /******************************************************************************/
40 
41 #include <cerrno>
42 #include <getopt.h>
43 #include <cstdlib>
44 #include <cstdio>
45 #include <cstring>
46 #include <strings.h>
47 #include <unistd.h>
48 #include <sys/param.h>
49 #include <sys/types.h>
50 
51 #include "XProtocol/XProtocol.hh"
52 #include "XrdCl/XrdClEnv.hh"
53 #include "XrdCl/XrdClFileSystem.hh"
54 #include "XrdCl/XrdClDefaultEnv.hh"
55 #include "XrdNet/XrdNetAddr.hh"
56 #include "XrdOuc/XrdOucHash.hh"
57 #include "XrdSys/XrdSysHeaders.hh"
58 
59 /******************************************************************************/
60 /* L o c a l D e f i n i t i o n s */
61 /******************************************************************************/
62 
63 #define EMSG(x) std::cerr <<"xrdmapc: " <<x <<std::endl
64 
65 // Bypass stupid issue with stupid solaris for missdefining 'struct opt'.
66 //
67 #ifdef __solaris__
68 #define OPT_TYPE (char *)
69 #else
70 #define OPT_TYPE
71 #endif
72 
73 namespace
74 {
75 struct clMap
76 { clMap *nextMan;
77  clMap *nextSrv;
78  clMap *nextLvl;
79  const char *state;
80  char *key;
81  char name[284];
82  char hasfile;
83  char verfile;
84  char valid;
85  char isMan;
86 
87  clMap(const char *addr) : nextMan(0), nextSrv(0), nextLvl(0),
88  state(""), hasfile(' '), verfile(' '),
89  valid(1), isMan(0)
90  {if (addr)
91  {XrdNetAddr epAddr;
92  epAddr.Set(addr); epAddr.Name();
93  epAddr.Format(name, sizeof(name));
94  key = strdup(addr);
95  } else {
96  *name = 0;
97  key = strdup("");
98  }
99  }
100  ~clMap() {}
101 };
102 };
103 
104 /******************************************************************************/
105 /* G l o b a l O b j e c t s */
106 /******************************************************************************/
107 
108 extern int optind, optopt;
109 
110 namespace
111 {
112 bool listMan = true, listSrv = true, doVerify = false, doHush = false;
113 
114 clMap *clLost = 0;
115 
116 char *Path = 0;
117 
118 uint16_t theTO = 30;
119 
120 XrdOucHash<clMap> clHash;
121 };
122 
123 /******************************************************************************/
124 /* M a k e U R L */
125 /******************************************************************************/
126 
127 namespace
128 {
129 const char *MakeURL(const char *name, char *buff, int blen)
130 {
131  snprintf(buff, blen, "xroot://%s//", name);
132  return buff;
133 }
134 };
135 
136 /******************************************************************************/
137 /* M a p C o d e */
138 /******************************************************************************/
139 
140 namespace
141 {
142 void MapCode(XrdCl::XRootDStatus &Status, clMap *node, bool nspl=false)
143 {
144  char buff[128];
145 
146  node->verfile = '?';
147 
148  if (Status.code == XrdCl::errErrorResponse)
149  {switch(Status.errNo)
150  {case kXR_FSError: node->state = " [fs error]"; break;
151  case kXR_IOError: node->state = " [io error]"; break;
152  case kXR_NoMemory: node->state = " [no memory]"; break;
153  case kXR_NotAuthorized: node->state = " [not authorized]";break;
154  case kXR_NotFound: if (nspl)
155  node->state = " [no subscribers]";
156  else {node->state = "";
157  node->verfile = '-';
158  }
159  break;
160  case kXR_NotFile: node->state = " [not a file]"; break;
161  default: sprintf(buff, " [xrootd error %d]", Status.errNo);
162  node->state = strdup(buff);
163  break;
164  }
165  return;
166  }
167 
168  switch(Status.code)
169  {case XrdCl::errInvalidAddr: node->state = " [invalid addr]";
170  break;
171  case XrdCl::errSocketError: node->state = " [socket error]";
172  break;
173  case XrdCl::errSocketTimeout: node->state = " [timeout]";
174  break;
175  case XrdCl::errSocketDisconnected: node->state = " [disconnect]";
176  break;
177  case XrdCl::errStreamDisconnect: node->state = " [disconnect]";
178  break;
179  case XrdCl::errConnectionError: node->state = " [connect error]";
180  break;
181  case XrdCl::errHandShakeFailed: node->state = " [handshake failed]";
182  break;
183  case XrdCl::errLoginFailed: node->state = " [login failed]";
184  break;
185  case XrdCl::errAuthFailed: node->state = " [auth failed]";
186  break;
187  case XrdCl::errOperationExpired: node->state = " [op expired]";
188  break;
189  case XrdCl::errRedirectLimit: node->state = " [redirect loop]";
190  break;
191  default: if (!(*Status.ToStr().c_str()))
192  sprintf(buff, " [client error %d]", Status.code);
193  else snprintf(buff, sizeof(buff), " [%s]",
194  Status.ToStr().c_str());
195  node->state = strdup(buff);
196  break;
197  }
198 }
199 };
200 
201 /******************************************************************************/
202 /* M a p C l u s t e r */
203 /******************************************************************************/
204 
205 namespace
206 {
207 void MapCluster(clMap *node, clMap *origin)
208 {
210  char buff[2048];
211  XrdCl::URL theURL((const std::string)MakeURL(node->name,buff,sizeof(buff)));
212  XrdCl::FileSystem xrdFS(theURL);
213  XrdCl::XRootDStatus Status;
214  XrdCl::LocationInfo *info = 0;
217  clMap *clmP, *branch;
218 
219 // Issue a locate
220 //
221  Status = xrdFS.Locate((const std::string)"*", flags, info, theTO);
222 
223 // Make sure all went well
224 //
225  if (!Status.IsOK())
226  {if (Status.errNo != kXR_NotFound && !doHush)
227  EMSG("Unable to get " <<node->name <<" subscribers; "
228  <<Status.ToStr().c_str());
229  MapCode(Status, origin, true);
230  node->valid = 0;
231  return;
232  }
233 
234 // Grab all of the information
235 //
236  for( it = info->Begin(); it != info->End(); ++it )
237  {clmP = new clMap(it->GetAddress().c_str());
238  locType = it->GetType();
239  if (locType == XrdCl::LocationInfo::ServerOnline
241  {clmP->nextSrv = node->nextSrv;
242  node->nextSrv = clmP;
243  } else {
244  clmP->nextMan = node->nextMan;
245  node->nextMan = clmP;
246  clmP->isMan = 1;
247  }
248  clHash.Add(clmP->key, clmP, 0, Hash_keep);
249  }
250 
251 // Now map all managers
252 //
253  clmP = node->nextMan;
254  while(clmP)
255  {branch = new clMap(clmP->name);
256  MapCluster(branch, clmP);
257 // if (branch->nextSrv || branch->nextMan || node->valid)
258  clmP->nextLvl = branch;
259 // else delete branch;
260  clmP = clmP->nextMan;
261  }
262 
263 // All done
264 //
265  delete info;
266 }
267 };
268 
269 /******************************************************************************/
270 /* M a p P a t h */
271 /******************************************************************************/
272 
273 namespace
274 {
275 void MapPath(clMap *node, const char *Path, bool doRefresh=false)
276 {
278  char buff[2048];
279  XrdCl::URL theURL((const std::string)MakeURL(node->name,buff,sizeof(buff)));
280  XrdCl::FileSystem xrdFS(theURL);
281  XrdCl::XRootDStatus Status;
282  XrdCl::LocationInfo *info = 0;
284  clMap *clmP;
285 
286 // Insert refresh is so wanted
287 //
288  if (doRefresh) flags = XrdCl::OpenFlags::Refresh;
289 
290 // Issue a locate
291 //
292  Status = xrdFS.Locate((const std::string)Path, flags, info, theTO);
293 
294 // Make sure all went well
295 //
296  if (!Status.IsOK())
297  {if (Status.errNo != kXR_NotFound && !doHush)
298  EMSG("Unable to query " <<node->name <<" about path; "
299  <<Status.ToStr().c_str());
300  MapCode(Status, node);
301  return;
302  }
303 
304 // Recursively mark each node as having the file
305 //
306  for( it = info->Begin(); it != info->End(); ++it )
307  {const char *clAddr = it->GetAddress().c_str();
308  if ((clmP = clHash.Find(clAddr)))
309  {clmP->hasfile = '>';
310  if (clmP->isMan) MapPath(clmP, Path);
311  } else {
312  clmP = new clMap(clAddr);
313  clmP->nextSrv = clLost;
314  clLost = clmP;
315  }
316  }
317 
318 // All done here
319 //
320  delete info;
321 }
322 };
323 
324 /******************************************************************************/
325 /* O p N a m e */
326 /******************************************************************************/
327 
328 namespace
329 {
330 const char *OpName(char *Argv[])
331 {
332  static char oName[4] = {'-', 0, 0, 0};
333 
334  if (!optopt || optopt == '-' || *(Argv[optind-1]+1) == '-')
335  return Argv[optind-1];
336  oName[1] = optopt;
337  return oName;
338 }
339 };
340 
341 /******************************************************************************/
342 /* P a t h C h k */
343 /******************************************************************************/
344 
345 namespace
346 {
347 void PathChk(clMap *node)
348 {
349  char buff[2048];
350  XrdCl::URL theURL((const std::string)MakeURL(node->name,buff,sizeof(buff)));
351  XrdCl::FileSystem xrdFS(theURL);
352  XrdCl::XRootDStatus Status;
353  XrdCl::StatInfo *info = 0;
354 
355 // Issue a stat for the file
356 //
357  Status = xrdFS.Stat((const std::string)Path, info);
358 
359 // Make sure all went well
360 //
361  if (!Status.IsOK()) MapCode(Status, node);
362  else node->verfile = '+';
363 
364 // All done here
365 //
366  delete info;
367 }
368 };
369 
370 /******************************************************************************/
371 /* P r i n t M a p */
372 /******************************************************************************/
373 
374 namespace
375 {
376 void PrintMap(clMap *clmP, int lvl)
377 {
378  clMap *clnow;
379  const char *pfx = "";
380  char *pfxbuff = 0;
381  int n;
382 
383 // Compute index spacing
384 //
385  if ((n = lvl*5))
386  {pfxbuff = (char *)malloc(n+1);
387  memset(pfxbuff, ' ', n); pfxbuff[n] = 0;
388  pfx = pfxbuff;
389  }
390 
391 // Print all of the servers first
392 //
393  if (listSrv)
394  {clnow = clmP->nextSrv;
395  while(clnow)
396  {if (doVerify) PathChk(clnow);
397  if (lvl)
398  {pfxbuff[1] = clnow->hasfile;
399  pfxbuff[2] = clnow->verfile;
400  }
401  std::cout <<' ' <<pfx <<"Srv " <<clnow->name <<clnow->state <<std::endl;
402  clnow = clnow->nextSrv;
403  }
404  }
405 
406 // Now recursively print the managers
407 //
408  if (listMan)
409  {clnow = clmP->nextMan;
410  if (lvl) pfxbuff[2] = ' ';
411  while(clnow)
412  {if (lvl) pfxbuff[1] = clnow->hasfile;
413  std::cout <<lvl <<pfx <<"Man " <<clnow->name <<clnow->state <<std::endl;
414  if (clnow->valid && clnow->nextLvl) PrintMap(clnow->nextLvl,lvl+1);
415  clnow = clnow->nextMan;
416  }
417  }
418 
419 // All done
420 //
421  if (lvl) free(pfxbuff);
422 }
423 };
424 
425 /******************************************************************************/
426 /* S e t E n v */
427 /******************************************************************************/
428 
429 namespace
430 {
431 int cwValue = 10;
432 int crValue = 0;
433 int trValue = 5;
434 
435 void SetEnv()
436 {
438 
439  env->PutInt("ConnectionWindow", cwValue);
440  env->PutInt("ConnectionRetry", crValue);
441  env->PutInt("TimeoutResolution",trValue);
442 }
443 };
444 
445 /******************************************************************************/
446 /* U s a g e */
447 /******************************************************************************/
448 
449 namespace
450 {
451 void Usage(const char *emsg)
452 {
453  if (emsg) EMSG(emsg);
454  std::cerr <<"Usage: xrdmapc [<opt>] <host>:<port> [<path>]\n"
455  <<"<opt>: [--help] [--list {all|m|s}] [--quiet] [--refresh] [--verify]" <<std::endl;
456  if (!emsg)
457  {std::cerr <<
458 "--list | -l 'all' lists managers and servers (default), 'm' lists only\n"
459 " managers and 's' lists only servers.\n"
460 "--quiet | -q does not print error messages to std::cerr; errors appear inline.\n"
461 "--refresh | -r does not use cached information but will refresh the cache.\n"
462 "--verify | -v verifies <path> existence status at each server.\n"
463 "<path> when specified, uses <host>:<port> to determine the locations\n"
464 " of path and does optional verification."
465  <<std::endl;
466  }
467  exit((emsg ? 1 : 0));
468 }
469 };
470 
471 /******************************************************************************/
472 /* m a i n */
473 /******************************************************************************/
474 
475 int main(int argc, char *argv[])
476 {
477  const char *opLetters = ":hl:rv";
478  struct option opVec[] = // For getopt_long()
479  {
480  {OPT_TYPE "help", 0, 0, (int)'h'},
481  {OPT_TYPE "list", 1, 0, (int)'l'},
482  {OPT_TYPE "quiet", 0, 0, (int)'q'},
483  {OPT_TYPE "refresh", 0, 0, (int)'r'},
484  {OPT_TYPE "verify", 0, 0, (int)'v'},
485  {0, 0, 0, 0}
486  };
487  extern int optind, opterr;
488  extern char *optarg;
489  XrdNetAddr sPoint;
490  clMap *baseNode, *clNow;
491  const char *eMsg;
492  char opC;
493  int i;
494  bool doRefresh = false;
495 
496 // Process options
497 //
498  opterr = 0;
499  optind = 1;
500  while((opC = getopt_long(argc, argv, opLetters, opVec, &i)) != (char)-1)
501  switch(opC)
502  {case 'h': Usage(0);
503  break;
504  case 'l': if (!strcmp("all",optarg))
505  {listMan = true; listSrv = true;}
506  else if (!strcmp("m", optarg))
507  {listMan = true; listSrv = false;}
508  else if (!strcmp("s", optarg))
509  {listMan = false; listSrv = true;}
510  else Usage("Invalid list argument.");
511  break;
512  case 'q': doHush = true;
513  break;
514  case 'r': doRefresh = true;
515  break;
516  case 'v': doVerify = true;
517  break;
518  case ':': EMSG("'" <<OpName(argv) <<"' argument missing.");
519  exit(2); break;
520  case '?': EMSG("Invalid option, '" <<OpName(argv) <<"'.");
521  exit(2); break;
522  default: EMSG("Internal error processing '" <<OpName(argv) <<"'.");
523  exit(2); break;
524  }
525 
526 // Make sure we have a starting point
527 //
528  if (optind >= argc) Usage("Initial node not specified.");
529 
530 // Establish starting point
531 //
532  if ((eMsg = sPoint.Set(argv[optind])))
533  {EMSG("Unable to validate initial node; " <<eMsg);
534  exit(2);
535  }
536 
537 // Make sure it's resolvable
538 //
539  if (!sPoint.Name(0, &eMsg))
540  {EMSG("Unable to resolve " <<argv[optind] <<"; " <<eMsg);
541  exit(2);
542  }
543 
544 // Establish the base node
545 //
546  baseNode = new clMap(argv[optind]);
547 
548 // Check if we will be checking a path
549 //
550  if (optind+1 < argc) Path = argv[optind+1];
551  else doVerify = false;
552 
553 // Set default client values
554 //
555  SetEnv();
556 
557 // Map the cluster
558 //
559  MapCluster(baseNode, baseNode);
560 
561 // Check if we need to do a locate on a file and possibly verify results
562 //
563  if (Path)
564  {MapPath(baseNode, Path, doRefresh);
565  eMsg = (doVerify ? "0*rv* " : "0*r** ");
566  } else eMsg = "0**** ";
567 
568 // Print the first line
569 //
570  std::cout <<eMsg <<baseNode->name <<baseNode->state <<std::endl;
571  PrintMap(baseNode, 1);
572 
573 // Check if we have any phantom nodes
574 //
575  if (Path && clLost)
576  {std::cerr <<"Warning! " <<baseNode->name
577  <<" referred to the following unconnected node:" <<std::endl;
578  clNow = clLost;
579  while(clNow)
580  {std::cerr <<"????? " <<clNow->name <<std::endl;
581  clNow = clNow->nextSrv;
582  }
583  }
584 
585 // All done
586 //
587  exit(0);
588 }
@ kXR_NotAuthorized
Definition: XProtocol.hh:1000
@ kXR_NotFound
Definition: XProtocol.hh:1001
@ kXR_NotFile
Definition: XProtocol.hh:1005
@ kXR_IOError
Definition: XProtocol.hh:997
@ kXR_FSError
Definition: XProtocol.hh:995
@ kXR_NoMemory
Definition: XProtocol.hh:998
void Usage(const char *msg)
Definition: XrdAccTest.cc:105
int main(int argc, char *argv[])
int optopt
#define OPT_TYPE
int optind
#define EMSG(x)
@ Hash_keep
Definition: XrdOucHash.hh:55
XrdOucString Path
#define eMsg(x)
int emsg(int rc, char *msg)
static Env * GetEnv()
Get default client environment.
bool PutInt(const std::string &key, int value)
Definition: XrdClEnv.cc:110
Send file/filesystem queries to an XRootD cluster.
Path location info.
Iterator Begin()
Get the location begin iterator.
LocationType
Describes the node type and file status for a given location.
@ ServerPending
server node where the file is pending to be online
@ ServerOnline
server node where the file is online
LocationList::iterator Iterator
Iterator over locations.
Iterator End()
Get the location end iterator.
Object stat info.
URL representation.
Definition: XrdClURL.hh:31
std::string ToStr() const
Convert to string.
int Format(char *bAddr, int bLen, fmtUse fmtType=fmtAuto, int fmtOpts=0)
const char * Name(const char *eName=0, const char **eText=0)
const char * Set(const char *hSpec, int pNum=PortInSpec)
Definition: XrdNetAddr.cc:216
const uint16_t errInvalidAddr
Definition: XrdClStatus.hh:71
const uint16_t errStreamDisconnect
Definition: XrdClStatus.hh:77
const uint16_t errRedirectLimit
Definition: XrdClStatus.hh:102
const uint16_t errErrorResponse
Definition: XrdClStatus.hh:105
const uint16_t errOperationExpired
Definition: XrdClStatus.hh:90
const uint16_t errLoginFailed
Definition: XrdClStatus.hh:87
const uint16_t errSocketTimeout
Definition: XrdClStatus.hh:73
const uint16_t errHandShakeFailed
Definition: XrdClStatus.hh:86
const uint16_t errConnectionError
Definition: XrdClStatus.hh:78
const uint16_t errSocketError
Definition: XrdClStatus.hh:72
const uint16_t errSocketDisconnected
Definition: XrdClStatus.hh:74
const uint16_t errAuthFailed
Definition: XrdClStatus.hh:88
Flags
Open flags, may be or'd when appropriate.
uint16_t code
Error type, or additional hints on what to do.
Definition: XrdClStatus.hh:147
bool IsOK() const
We're fine.
Definition: XrdClStatus.hh:124
uint32_t errNo
Errno, if any.
Definition: XrdClStatus.hh:148