XRootD
Loading...
Searching...
No Matches
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 <algorithm>
42#include <cerrno>
43#include <getopt.h>
44#include <cstdlib>
45#include <cstdio>
46#include <cstring>
47#include <strings.h>
48#include <unistd.h>
49#include <sys/param.h>
50#include <sys/types.h>
51#include <vector>
52
54#include "XrdCl/XrdClEnv.hh"
57#include "XrdNet/XrdNetAddr.hh"
58#include "XrdOuc/XrdOucHash.hh"
60
61/******************************************************************************/
62/* L o c a l D e f i n i t i o n s */
63/******************************************************************************/
64
65#define EMSG(x) std::cerr <<"xrdmapc: " <<x <<std::endl
66
67// Bypass stupid issue with stupid solaris for missdefining 'struct opt'.
68//
69#ifdef __solaris__
70#define OPT_TYPE (char *)
71#else
72#define OPT_TYPE
73#endif
74
75namespace
76{
77struct clMap
78{ clMap *nextMan;
79 clMap *nextSrv;
80 clMap *nextLvl;
81 const char *state;
82 char *key;
83 char name[284];
84 char hasfile;
85 char verfile;
86 char valid;
87 char isMan;
88
89 clMap(const char *addr) : nextMan(0), nextSrv(0), nextLvl(0),
90 state(""), hasfile(' '), verfile(' '),
91 valid(1), isMan(0)
92 {if (addr)
93 {XrdNetAddr epAddr;
94 epAddr.Set(addr); epAddr.Name();
95 epAddr.Format(name, sizeof(name));
96 key = strdup(addr);
97 } else {
98 *name = 0;
99 key = strdup("");
100 }
101 }
102 ~clMap() {}
103};
104};
105
106/******************************************************************************/
107/* G l o b a l O b j e c t s */
108/******************************************************************************/
109
110extern int optind, optopt;
111
112namespace
113{
114bool listMan = true, listSrv = true, doVerify = false, doHush = false, doJson = false;
115
116clMap *clLost = 0;
117
118char *Path = 0;
119
120uint16_t theTO = 30;
121
122XrdOucHash<clMap> clHash;
123};
124
125/******************************************************************************/
126/* M a k e U R L */
127/******************************************************************************/
128
129namespace
130{
131const char *MakeURL(const char *name, char *buff, int blen)
132{
133 snprintf(buff, blen, "xroot://%s//", name);
134 return buff;
135}
136};
137
138/******************************************************************************/
139/* M a p C o d e */
140/******************************************************************************/
141
142namespace
143{
144void MapCode(XrdCl::XRootDStatus &Status, clMap *node, bool nspl=false)
145{
146 char buff[128];
147
148 node->verfile = '?';
149
150 if (Status.code == XrdCl::errErrorResponse)
151 {switch(Status.errNo)
152 {case kXR_FSError: node->state = " [fs error]"; break;
153 case kXR_IOError: node->state = " [io error]"; break;
154 case kXR_NoMemory: node->state = " [no memory]"; break;
155 case kXR_NotAuthorized: node->state = " [not authorized]";break;
156 case kXR_NotFound: if (nspl)
157 node->state = " [no subscribers]";
158 else {node->state = "";
159 node->verfile = '-';
160 }
161 break;
162 case kXR_NotFile: node->state = " [not a file]"; break;
163 default: sprintf(buff, " [xrootd error %d]", Status.errNo);
164 node->state = strdup(buff);
165 break;
166 }
167 return;
168 }
169
170 switch(Status.code)
171 {case XrdCl::errInvalidAddr: node->state = " [invalid addr]";
172 break;
173 case XrdCl::errSocketError: node->state = " [socket error]";
174 break;
175 case XrdCl::errSocketTimeout: node->state = " [timeout]";
176 break;
177 case XrdCl::errSocketDisconnected: node->state = " [disconnect]";
178 break;
179 case XrdCl::errStreamDisconnect: node->state = " [disconnect]";
180 break;
181 case XrdCl::errConnectionError: node->state = " [connect error]";
182 break;
183 case XrdCl::errHandShakeFailed: node->state = " [handshake failed]";
184 break;
185 case XrdCl::errLoginFailed: node->state = " [login failed]";
186 break;
187 case XrdCl::errAuthFailed: node->state = " [auth failed]";
188 break;
189 case XrdCl::errOperationExpired: node->state = " [op expired]";
190 break;
191 case XrdCl::errRedirectLimit: node->state = " [redirect loop]";
192 break;
193 default: if (!(*Status.ToStr().c_str()))
194 sprintf(buff, " [client error %d]", Status.code);
195 else snprintf(buff, sizeof(buff), " [%s]",
196 Status.ToStr().c_str());
197 node->state = strdup(buff);
198 break;
199 }
200}
201};
202
203/******************************************************************************/
204/* M a p C l u s t e r */
205/******************************************************************************/
206
207namespace
208{
209void MapCluster(clMap *node, clMap *origin)
210{
212 char buff[2048];
213 XrdCl::URL theURL((const std::string)MakeURL(node->name,buff,sizeof(buff)));
214 XrdCl::FileSystem xrdFS(theURL);
215 XrdCl::XRootDStatus Status;
216 XrdCl::LocationInfo *info = 0;
219 clMap *clmP, *branch;
220
221// Issue a locate
222//
223 Status = xrdFS.Locate((const std::string)"*", flags, info, theTO);
224
225// Make sure all went well
226//
227 if (!Status.IsOK())
228 {if (Status.errNo != kXR_NotFound && !doHush)
229 EMSG("Unable to get " <<node->name <<" subscribers; "
230 <<Status.ToStr().c_str());
231 MapCode(Status, origin, true);
232 node->valid = 0;
233 return;
234 }
235
236// Grab all of the information
237//
238 for( it = info->Begin(); it != info->End(); ++it )
239 {clmP = new clMap(it->GetAddress().c_str());
240 locType = it->GetType();
243 {clmP->nextSrv = node->nextSrv;
244 node->nextSrv = clmP;
245 } else {
246 clmP->nextMan = node->nextMan;
247 node->nextMan = clmP;
248 clmP->isMan = 1;
249 }
250 clHash.Add(clmP->key, clmP, 0, Hash_keep);
251 }
252
253// Now map all managers
254//
255 clmP = node->nextMan;
256 while(clmP)
257 {branch = new clMap(clmP->name);
258 MapCluster(branch, clmP);
259// if (branch->nextSrv || branch->nextMan || node->valid)
260 clmP->nextLvl = branch;
261// else delete branch;
262 clmP = clmP->nextMan;
263 }
264
265// All done
266//
267 delete info;
268}
269};
270
271/******************************************************************************/
272/* M a p P a t h */
273/******************************************************************************/
274
275namespace
276{
277void MapPath(clMap *node, const char *Path, bool doRefresh=false)
278{
280 char buff[2048];
281 XrdCl::URL theURL((const std::string)MakeURL(node->name,buff,sizeof(buff)));
282 XrdCl::FileSystem xrdFS(theURL);
283 XrdCl::XRootDStatus Status;
284 XrdCl::LocationInfo *info = 0;
286 clMap *clmP;
287
288// Insert refresh is so wanted
289//
290 if (doRefresh) flags = XrdCl::OpenFlags::Refresh;
291
292// Issue a locate
293//
294 Status = xrdFS.Locate((const std::string)Path, flags, info, theTO);
295
296// Make sure all went well
297//
298 if (!Status.IsOK())
299 {if (Status.errNo != kXR_NotFound && !doHush)
300 EMSG("Unable to query " <<node->name <<" about path; "
301 <<Status.ToStr().c_str());
302 MapCode(Status, node);
303 return;
304 }
305
306// Recursively mark each node as having the file
307//
308 for( it = info->Begin(); it != info->End(); ++it )
309 {const char *clAddr = it->GetAddress().c_str();
310 if ((clmP = clHash.Find(clAddr)))
311 {clmP->hasfile = '>';
312 if (clmP->isMan) MapPath(clmP, Path);
313 } else {
314 clmP = new clMap(clAddr);
315 clmP->nextSrv = clLost;
316 clLost = clmP;
317 }
318 }
319
320// All done here
321//
322 delete info;
323}
324};
325
326/******************************************************************************/
327/* O p N a m e */
328/******************************************************************************/
329
330namespace
331{
332const char *OpName(char *Argv[])
333{
334 static char oName[4] = {'-', 0, 0, 0};
335
336 if (!optopt || optopt == '-' || *(Argv[optind-1]+1) == '-')
337 return Argv[optind-1];
338 oName[1] = optopt;
339 return oName;
340}
341};
342
343/******************************************************************************/
344/* P a t h C h k */
345/******************************************************************************/
346
347namespace
348{
349void PathChk(clMap *node)
350{
351 char buff[2048];
352 XrdCl::URL theURL((const std::string)MakeURL(node->name,buff,sizeof(buff)));
353 XrdCl::FileSystem xrdFS(theURL);
354 XrdCl::XRootDStatus Status;
355 XrdCl::StatInfo *info = 0;
356
357// Issue a stat for the file
358//
359 Status = xrdFS.Stat((const std::string)Path, info);
360
361// Make sure all went well
362//
363 if (!Status.IsOK()) MapCode(Status, node);
364 else node->verfile = '+';
365
366// All done here
367//
368 delete info;
369}
370};
371
372/******************************************************************************/
373/* P r i n t M a p */
374/******************************************************************************/
375
376namespace
377{
378void PrintMap(clMap *clmP, int lvl)
379{
380 clMap *clnow;
381 const char *pfx = "";
382 char *pfxbuff = 0;
383 int n;
384
385// Compute index spacing
386//
387 if ((n = lvl*5))
388 {pfxbuff = (char *)malloc(n+1);
389 memset(pfxbuff, ' ', n); pfxbuff[n] = 0;
390 pfx = pfxbuff;
391 }
392
393// Print all of the servers first
394//
395 if (listSrv)
396 {clnow = clmP->nextSrv;
397 while(clnow)
398 {if (doVerify) PathChk(clnow);
399 if (lvl)
400 {pfxbuff[1] = clnow->hasfile;
401 pfxbuff[2] = clnow->verfile;
402 }
403 std::cout <<' ' <<pfx <<"Srv " <<clnow->name <<clnow->state <<std::endl;
404 clnow = clnow->nextSrv;
405 }
406 }
407
408// Now recursively print the managers
409//
410 if (listMan)
411 {clnow = clmP->nextMan;
412 if (lvl) pfxbuff[2] = ' ';
413 while(clnow)
414 {if (lvl) pfxbuff[1] = clnow->hasfile;
415 std::cout <<lvl <<pfx <<"Man " <<clnow->name <<clnow->state <<std::endl;
416 if (clnow->valid && clnow->nextLvl) PrintMap(clnow->nextLvl,lvl+1);
417 clnow = clnow->nextMan;
418 }
419 }
420
421// All done
422//
423 if (lvl) free(pfxbuff);
424}
425};
426
427/******************************************************************************/
428/* P r i n t J s o n */
429/******************************************************************************/
430
431namespace
432{
433void PrintJsonSrv(clMap *clnow)
434{
435 const char *st;
436
437 printf("{\"name\":\"%s\"", clnow->name);
438 if (*clnow->state)
439 {st = clnow->state; while (*st == ' ') st++;
440 printf(",\"state\":\"%s\"", st);
441 }
442 if (clnow->hasfile != ' ')
443 printf(",\"hasFile\":%s", clnow->hasfile == '>' ? "true" : "false");
444 if (clnow->verfile != ' ' && clnow->verfile != '?')
445 printf(",\"verified\":%s", clnow->verfile == '+' ? "true" : "false");
446 putchar('}');
447}
448
449void PrintJson(clMap *clmP)
450{
451 clMap *clnow;
452 const char *st;
453 std::vector<clMap*> nodes;
454 size_t i;
455
456 printf("{\"name\":\"%s\",\"type\":\"manager\"", clmP->name);
457
458 if (*clmP->state)
459 {st = clmP->state; while (*st == ' ') st++;
460 printf(",\"state\":\"%s\"", st);
461 }
462 if (clmP->hasfile != ' ')
463 printf(",\"hasFile\":%s", clmP->hasfile == '>' ? "true" : "false");
464 if (clmP->verfile != ' ' && clmP->verfile != '?')
465 printf(",\"verified\":%s", clmP->verfile == '+' ? "true" : "false");
466
467 // Emit children sorted by name so topology JSON is deterministic.
468 // Locate response order is otherwise not stable.
469 //
470 if (clmP->nextSrv && listSrv)
471 {nodes.clear();
472 for (clnow = clmP->nextSrv; clnow; clnow = clnow->nextSrv)
473 nodes.push_back(clnow);
474 std::sort(nodes.begin(), nodes.end(),
475 [](const clMap *a, const clMap *b)
476 { return strcmp(a->name, b->name) < 0; });
477 printf(",\"servers\":[");
478 for (i = 0; i < nodes.size(); ++i)
479 {if (i) putchar(',');
480 PrintJsonSrv(nodes[i]);
481 }
482 putchar(']');
483 }
484
485 if (clmP->nextMan && listMan)
486 {nodes.clear();
487 for (clnow = clmP->nextMan; clnow; clnow = clnow->nextMan)
488 nodes.push_back(clnow);
489 std::sort(nodes.begin(), nodes.end(),
490 [](const clMap *a, const clMap *b)
491 { return strcmp(a->name, b->name) < 0; });
492 printf(",\"managers\":[");
493 for (i = 0; i < nodes.size(); ++i)
494 {if (i) putchar(',');
495 clnow = nodes[i];
496 if (clnow->nextLvl)
497 {if (*clnow->state) clnow->nextLvl->state = clnow->state;
498 PrintJson(clnow->nextLvl);
499 }
500 else
501 {printf("{\"name\":\"%s\",\"type\":\"manager\"", clnow->name);
502 if (*clnow->state)
503 {st = clnow->state; while (*st == ' ') st++;
504 printf(",\"state\":\"%s\"", st);
505 }
506 putchar('}');
507 }
508 }
509 putchar(']');
510 }
511
512 putchar('}');
513}
514};
515
516/******************************************************************************/
517/* S e t E n v */
518/******************************************************************************/
519
520namespace
521{
522int cwValue = 10;
523int crValue = 0;
524int trValue = 5;
525
526void SetEnv()
527{
529
530 env->PutInt("ConnectionWindow", cwValue);
531 env->PutInt("ConnectionRetry", crValue);
532 env->PutInt("TimeoutResolution",trValue);
533}
534};
535
536/******************************************************************************/
537/* U s a g e */
538/******************************************************************************/
539
540namespace
541{
542void Usage(const char *emsg)
543{
544 if (emsg) EMSG(emsg);
545 std::cerr <<"Usage: xrdmapc [<opt>] <host>:<port> [<path>]\n"
546 <<"<opt>: [--help] [--format {text|json}] [--list {all|m|s}] [--quiet] [--refresh] [--verify]" <<std::endl;
547 if (!emsg)
548 {std::cerr <<
549"--format | -f 'text' prints human-readable output (default), 'json' prints\n"
550" a JSON representation of the cluster topology (managers and\n"
551" servers arrays are sorted by name).\n"
552"--list | -l 'all' lists managers and servers (default), 'm' lists only\n"
553" managers and 's' lists only servers.\n"
554"--quiet | -q does not print error messages to std::cerr; errors appear inline.\n"
555"--refresh | -r does not use cached information but will refresh the cache.\n"
556"--verify | -v verifies <path> existence status at each server.\n"
557"<path> when specified, uses <host>:<port> to determine the locations\n"
558" of path and does optional verification."
559 <<std::endl;
560 }
561 exit((emsg ? 1 : 0));
562}
563};
564
565/******************************************************************************/
566/* m a i n */
567/******************************************************************************/
568
569int main(int argc, char *argv[])
570{
571 const char *opLetters = ":f:hl:rv";
572 struct option opVec[] = // For getopt_long()
573 {
574 {OPT_TYPE "format", 1, 0, (int)'f'},
575 {OPT_TYPE "help", 0, 0, (int)'h'},
576 {OPT_TYPE "list", 1, 0, (int)'l'},
577 {OPT_TYPE "quiet", 0, 0, (int)'q'},
578 {OPT_TYPE "refresh", 0, 0, (int)'r'},
579 {OPT_TYPE "verify", 0, 0, (int)'v'},
580 {0, 0, 0, 0}
581 };
582 extern int optind, opterr;
583 extern char *optarg;
584 XrdNetAddr sPoint;
585 clMap *baseNode, *clNow;
586 const char *eMsg;
587 char opC;
588 int i;
589 bool doRefresh = false;
590
591// Process options
592//
593 opterr = 0;
594 optind = 1;
595 while((opC = getopt_long(argc, argv, opLetters, opVec, &i)) != (char)-1)
596 switch(opC)
597 // We implicity silence the errors for the json output since we store
598 // the error state for the site in the state field of the json output
599 {case 'f': if (!strcmp("json",optarg))
600 {doJson = true; doHush = true;}
601 else if (!strcmp("text",optarg))
602 doJson = false;
603 else Usage("Invalid format argument.");
604 break;
605 case 'h': Usage(0);
606 break;
607 case 'l': if (!strcmp("all",optarg))
608 {listMan = true; listSrv = true;}
609 else if (!strcmp("m", optarg))
610 {listMan = true; listSrv = false;}
611 else if (!strcmp("s", optarg))
612 {listMan = false; listSrv = true;}
613 else Usage("Invalid list argument.");
614 break;
615 case 'q': doHush = true;
616 break;
617 case 'r': doRefresh = true;
618 break;
619 case 'v': doVerify = true;
620 break;
621 case ':': EMSG("'" <<OpName(argv) <<"' argument missing.");
622 exit(2); break;
623 case '?': EMSG("Invalid option, '" <<OpName(argv) <<"'.");
624 exit(2); break;
625 default: EMSG("Internal error processing '" <<OpName(argv) <<"'.");
626 exit(2); break;
627 }
628
629// Make sure we have a starting point
630//
631 if (optind >= argc) Usage("Initial node not specified.");
632
633// Establish starting point
634//
635 if ((eMsg = sPoint.Set(argv[optind])))
636 {EMSG("Unable to validate initial node; " <<eMsg);
637 exit(2);
638 }
639
640// Make sure it's resolvable
641//
642 if (!sPoint.Name(0, &eMsg))
643 {EMSG("Unable to resolve " <<argv[optind] <<"; " <<eMsg);
644 exit(2);
645 }
646
647// Establish the base node
648//
649 baseNode = new clMap(argv[optind]);
650
651// Check if we will be checking a path
652//
653 if (optind+1 < argc) Path = argv[optind+1];
654 else doVerify = false;
655
656// Set default client values
657//
658 SetEnv();
659
660// Map the cluster
661//
662 MapCluster(baseNode, baseNode);
663
664// Check if we need to do a locate on a file and possibly verify results
665//
666 if (Path)
667 {MapPath(baseNode, Path, doRefresh);
668 eMsg = (doVerify ? "0*rv* " : "0*r** ");
669 } else eMsg = "0**** ";
670
671// Print the output in the selected format
672//
673 if (doJson)
674 {PrintJson(baseNode);
675 putchar('\n');
676 }
677 else
678 {std::cout <<eMsg <<baseNode->name <<baseNode->state <<std::endl;
679 PrintMap(baseNode, 1);
680 }
681
682// Check if we have any phantom nodes
683//
684 if (Path && clLost)
685 {std::cerr <<"Warning! " <<baseNode->name
686 <<" referred to the following unconnected node:" <<std::endl;
687 clNow = clLost;
688 while(clNow)
689 {std::cerr <<"????? " <<clNow->name <<std::endl;
690 clNow = clNow->nextSrv;
691 }
692 }
693
694// All done
695//
696 exit(0);
697}
@ kXR_NotAuthorized
@ kXR_NotFound
@ kXR_NotFile
@ kXR_IOError
@ kXR_FSError
@ kXR_NoMemory
void Usage(const char *msg)
#define OPT_TYPE
#define EMSG(x)
int main(int argc, char *argv[])
Definition XrdMain.cc:161
int optopt
int optind
@ 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:136
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)
const uint16_t errInvalidAddr
const uint16_t errStreamDisconnect
const uint16_t errRedirectLimit
const uint16_t errErrorResponse
const uint16_t errOperationExpired
const uint16_t errLoginFailed
const uint16_t errSocketTimeout
const uint16_t errHandShakeFailed
const uint16_t errConnectionError
const uint16_t errSocketError
const uint16_t errSocketDisconnected
const uint16_t errAuthFailed
Flags
Open flags, may be or'd when appropriate.
uint16_t code
Error type, or additional hints on what to do.
bool IsOK() const
We're fine.
uint32_t errNo
Errno, if any.