XrdCtaFile.cpp 120 KB
Newer Older
1
/*
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 * The CERN Tape Archive (CTA) project
 * Copyright (C) 2015  CERN
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

19
#include "scheduler/SchedulerDatabase.hpp"
20
#include "xroot_plugins/XrdCtaFile.hpp"
Daniele Kruse's avatar
Daniele Kruse committed
21

22
#include "XrdSec/XrdSecEntity.hh"
23

24
#include "catalogue/TapeFileSearchCriteria.hpp"
25
#include "common/Configuration.hpp"
26
#include "common/utils/utils.hpp"
27
#include "common/Timer.hpp"
28
#include "common/utils/GetOptThreadSafe.hpp"
29
30
31
#include "common/exception/UserError.hpp"
#include "common/exception/NonRetryableError.hpp"
#include "common/exception/RetryableError.hpp"
32

33
34
#include <cryptopp/base64.h>
#include <cryptopp/osrng.h>
35
#include <iomanip>
36
#include <iostream>
37
#include <memory>
38
#include <pwd.h>
39
40
#include <sstream>
#include <string>
41
#include <time.h>
42

43
44
namespace cta { namespace xrootPlugins {

45
46
47
//------------------------------------------------------------------------------
// checkClient
//------------------------------------------------------------------------------
48
void XrdCtaFile::checkClient(const XrdSecEntity *client) {
49
50
  if(client==nullptr || client->name==nullptr || client->host==nullptr || client->prot==nullptr) {
    throw cta::exception::Exception(std::string(__FUNCTION__)+": [ERROR] XrdSecEntity from xroot contains invalid information (nullptr pointer detected!)");
51
  }
52
  std::cerr << "Client request-> Username: " << client->name << " Host: " << client->host << " Prot: " << client->prot << std::endl;
53
54
55
56
57
58
59
60
61
62
63
64
65
  /**
   * The username here has two meanings depending on the protocol used:
   * 
   * 1) Kerberos 5: in this case it is the actual username of the administrator issuing the command
   * 2) Simple Shared Secret (SSS): in this case it is the name of the instance
   * 
   * It is important to remember that we are using kerberos authentication only for admin commands, while for user (EOS)
   * commands we use SSS. Each EOS instance will receive a separate SSS key which will be named (this means that a
   * username will be associated to it), for example all the cms EOS instances (1 or more) will receive the same SSS key
   * with the username "cms" associated to it. Having named keys is important to identify instances and limit crosstalk
   * as much as possible: a client having a "cms" SSS key will only be authorized to archive/retrieve/update/delete
   * cms files, or list cms storageclasses.
   */
66
67
  m_cliIdentity.username=client->name;
  m_cliIdentity.host=client->host;
68
  m_protocol=client->prot;
69
70
}

71
//------------------------------------------------------------------------------
72
// checkOptions
73
//------------------------------------------------------------------------------
74
75
76
77
void XrdCtaFile::checkOptions(const std::string &helpString) {
  if(!m_missingRequiredOptions.empty()||(!m_missingOptionalOptions.empty() && m_optionalOptions.empty())) {
    throw cta::exception::UserError(helpString);
  }
78
79
}

80
81
82
//------------------------------------------------------------------------------
// logRequestAndSetCmdlineResult
//------------------------------------------------------------------------------
83
void XrdCtaFile::logRequestAndSetCmdlineResult(const cta::common::dataStructures::FrontendReturnCode rc, const std::string &returnString) {
84
85
86
87
88
89
  // The return code of teh executed command is the first character of the
  // result sent back to the cta command-line tool
  //
  // Please note return codes can only be between "0" and "9".
  m_cmdlineOutput = std::to_string(rc);

90
91
92
93
94
95
96
97
  if(!m_missingRequiredOptions.empty()) {
    m_cmdlineOutput += "The following required options are missing:\n";
    for(auto it=m_missingRequiredOptions.cbegin(); it!=m_missingRequiredOptions.cend(); it++) {
      m_cmdlineOutput += "Missing option: ";
      m_cmdlineOutput += *it;
      m_cmdlineOutput += "\n";
    }
  }
98
  if(!m_missingOptionalOptions.empty() && m_optionalOptions.empty() && !m_suppressOptionalOptionsWarning) {
99
100
101
102
103
104
105
106
    m_cmdlineOutput += "At least one of the following options is required:\n";
    for(auto it=m_missingOptionalOptions.cbegin(); it!=m_missingOptionalOptions.cend(); it++) {
      m_cmdlineOutput += "Missing option: ";
      m_cmdlineOutput += *it;
      m_cmdlineOutput += "\n";
    }
  }
  m_cmdlineOutput += returnString;
107

Victor Kotlyar's avatar
Victor Kotlyar committed
108
  std::list<cta::log::Param> params;
109
110
  params.push_back({"Username", m_cliIdentity.username});
  params.push_back({"Host", m_cliIdentity.host});
111
  params.push_back({"ReturnCode", toString(rc)});
112
  params.push_back({"CommandOutput", m_cmdlineOutput});
113
  std::stringstream originalRequest;
114
  for(auto it=m_requestTokens.begin(); it!=m_requestTokens.end(); it++) {
115
116
    originalRequest << *it << " ";
  }
Victor Kotlyar's avatar
Victor Kotlyar committed
117
  params.push_back(cta::log::Param("REQUEST", originalRequest.str()));
118
  
119
  switch(rc) {
120
121
122
123
    case cta::common::dataStructures::FrontendReturnCode::ok:
      m_log(log::INFO, "Successful Request", params);
      break;
    case cta::common::dataStructures::FrontendReturnCode::userErrorNoRetry:
124
      m_log(log::USERERR, "Syntax error or missing argument(s) in request", params);
125
126
      break;
    default:
Victor Kotlyar's avatar
Victor Kotlyar committed
127
      params.push_back(cta::log::Param("ERROR", returnString));
128
129
130
      m_log(log::ERR, "Unsuccessful Request", params);
      break;
  }
131
132
}

133
134
135
136
//------------------------------------------------------------------------------
// authorizeAdmin
//------------------------------------------------------------------------------
void XrdCtaFile::authorizeAdmin(){
137
138
139
  if(m_protocol!="krb5") {
    throw cta::exception::Exception(std::string("[ERROR] Admin commands are possible only through Kerberos 5 protocol authentication. Protocol used for this connection: ")+m_protocol);
  }
140
141
142
  m_scheduler->authorizeAdmin(m_cliIdentity);
}

143
144
145
//------------------------------------------------------------------------------
// commandDispatcher
//------------------------------------------------------------------------------
146
std::string XrdCtaFile::dispatchCommand() {
147
  std::string command(m_requestTokens.at(1));
148
  
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
  if     ("ad"   == command || "admin"                  == command) {authorizeAdmin(); return xCom_admin();}
  else if("ah"   == command || "adminhost"              == command) {authorizeAdmin(); return xCom_adminhost();}
  else if("tp"   == command || "tapepool"               == command) {authorizeAdmin(); return xCom_tapepool();}
  else if("ar"   == command || "archiveroute"           == command) {authorizeAdmin(); return xCom_archiveroute();}
  else if("ll"   == command || "logicallibrary"         == command) {authorizeAdmin(); return xCom_logicallibrary();}
  else if("ta"   == command || "tape"                   == command) {authorizeAdmin(); return xCom_tape();}
  else if("sc"   == command || "storageclass"           == command) {authorizeAdmin(); return xCom_storageclass();}
  else if("rmr"  == command || "requestermountrule"     == command) {authorizeAdmin(); return xCom_requestermountrule();}
  else if("gmr"  == command || "groupmountrule"         == command) {authorizeAdmin(); return xCom_groupmountrule();}
  else if("mp"   == command || "mountpolicy"            == command) {authorizeAdmin(); return xCom_mountpolicy();}
  else if("re"   == command || "repack"                 == command) {authorizeAdmin(); return xCom_repack();}
  else if("sh"   == command || "shrink"                 == command) {authorizeAdmin(); return xCom_shrink();}
  else if("ve"   == command || "verify"                 == command) {authorizeAdmin(); return xCom_verify();}
  else if("af"   == command || "archivefile"            == command) {authorizeAdmin(); return xCom_archivefile();}
  else if("te"   == command || "test"                   == command) {authorizeAdmin(); return xCom_test();}
  else if("dr"   == command || "drive"                  == command) {authorizeAdmin(); return xCom_drive();}
  else if("lpa"  == command || "listpendingarchives"    == command) {authorizeAdmin(); return xCom_listpendingarchives();}
  else if("lpr"  == command || "listpendingretrieves"   == command) {authorizeAdmin(); return xCom_listpendingretrieves();}
167
  else if("sq"   == command || "showqueues"             == command) {authorizeAdmin(); return xCom_showqueues();}
Daniele Kruse's avatar
Daniele Kruse committed
168
  
169
170
171
172
173
174
175
  else if("a"    == command || "archive"                == command) {return xCom_archive();}
  else if("r"    == command || "retrieve"               == command) {return xCom_retrieve();}
  else if("da"   == command || "deletearchive"          == command) {return xCom_deletearchive();}
  else if("cr"   == command || "cancelretrieve"         == command) {return xCom_cancelretrieve();}
  else if("ufi"  == command || "updatefileinfo"         == command) {return xCom_updatefileinfo();}
  else if("ufsc" == command || "updatefilestorageclass" == command) {return xCom_updatefilestorageclass();}
  else if("lsc"  == command || "liststorageclass"       == command) {return xCom_liststorageclass();}
176
  
177
  else {
178
    throw cta::exception::UserError(getGenericHelp(m_requestTokens.at(0)));
179
  }
180
}
Daniele Kruse's avatar
Daniele Kruse committed
181

182
183
184
//------------------------------------------------------------------------------
// decode
//------------------------------------------------------------------------------
185
std::string XrdCtaFile::decode(const std::string msg) const {
186
187
188
189
190
  std::string ret;
  CryptoPP::StringSource ss1(msg, true, new CryptoPP::Base64Decoder(new CryptoPP::StringSink(ret)));
  return ret;
}

Daniele Kruse's avatar
Daniele Kruse committed
191
192
193
//------------------------------------------------------------------------------
// open
//------------------------------------------------------------------------------
194
int XrdCtaFile::open(const char *fileName, XrdSfsFileOpenMode openMode, mode_t createMode, const XrdSecEntity *client, const char *opaque) {
195
  try {
196
    checkClient(client);
197
    if(!strlen(fileName)) { //this should never happen
198
      throw cta::exception::UserError(getGenericHelp(""));
199
    }
200
    
201
    std::stringstream ss(fileName+1); //let's skip the first slash which is always prepended since we are asking for an absolute path
202
203
204
205
206
207
208
209
    std::string item;
    while (std::getline(ss, item, '&')) {
      replaceAll(item, "_", "/"); 
      //need to add this because xroot removes consecutive slashes, and the 
      //cryptopp base64 algorithm may produce consecutive slashes. This is solved 
      //in cryptopp-5.6.3 (using Base64URLEncoder instead of Base64Encoder) but we 
      //currently have cryptopp-5.6.2. To be changed in the future...
      item = decode(item);
210
      m_requestTokens.push_back(item);
211
212
    }

213
    if(m_requestTokens.size() == 0) { //this should never happen
214
      throw cta::exception::UserError(getGenericHelp(""));
215
    }
216
    if(m_requestTokens.size() < 2) {
217
      throw cta::exception::UserError(getGenericHelp(m_requestTokens.at(0)));
218
    }    
219
220
    const std::string cmdlineOutput = dispatchCommand();
    logRequestAndSetCmdlineResult(cta::common::dataStructures::FrontendReturnCode::ok, cmdlineOutput);
221
  } catch (cta::exception::UserError &ex) {
Daniele Kruse's avatar
Daniele Kruse committed
222
    logRequestAndSetCmdlineResult(cta::common::dataStructures::FrontendReturnCode::userErrorNoRetry, ex.getMessageValue()+"\n");
223
  } catch (cta::exception::NonRetryableError &ex) {
Daniele Kruse's avatar
Daniele Kruse committed
224
    logRequestAndSetCmdlineResult(cta::common::dataStructures::FrontendReturnCode::ctaErrorNoRetry, ex.getMessageValue()+"\n");
225
  } catch (cta::exception::RetryableError &ex) {
Daniele Kruse's avatar
Daniele Kruse committed
226
    logRequestAndSetCmdlineResult(cta::common::dataStructures::FrontendReturnCode::ctaErrorRetry, ex.getMessageValue()+"\n");
227
  } catch (cta::exception::Exception &ex) {
Daniele Kruse's avatar
Daniele Kruse committed
228
    logRequestAndSetCmdlineResult(cta::common::dataStructures::FrontendReturnCode::ctaErrorNoRetry, ex.getMessageValue()+"\n");
229
  } catch (std::exception &ex) {
Daniele Kruse's avatar
Daniele Kruse committed
230
    logRequestAndSetCmdlineResult(cta::common::dataStructures::FrontendReturnCode::ctaErrorNoRetry, std::string(ex.what())+"\n");
231
  } catch (...) {
Daniele Kruse's avatar
Daniele Kruse committed
232
    logRequestAndSetCmdlineResult(cta::common::dataStructures::FrontendReturnCode::ctaErrorNoRetry, "Unknown exception caught!\n");
233
  }
Daniele Kruse's avatar
Daniele Kruse committed
234
  return SFS_OK;
Daniele Kruse's avatar
Daniele Kruse committed
235
236
237
238
239
}

//------------------------------------------------------------------------------
// close
//------------------------------------------------------------------------------
240
int XrdCtaFile::close() {
Daniele Kruse's avatar
Daniele Kruse committed
241
  return SFS_OK;
Daniele Kruse's avatar
Daniele Kruse committed
242
243
244
245
246
}

//------------------------------------------------------------------------------
// fctl
//------------------------------------------------------------------------------
247
int XrdCtaFile::fctl(const int cmd, const char *args, XrdOucErrInfo &eInfo) {  
Daniele Kruse's avatar
Daniele Kruse committed
248
249
250
251
252
253
254
  error.setErrInfo(ENOTSUP, "Not supported.");
  return SFS_ERROR;
}

//------------------------------------------------------------------------------
// FName
//------------------------------------------------------------------------------
255
const char* XrdCtaFile::FName() {
Daniele Kruse's avatar
Daniele Kruse committed
256
  error.setErrInfo(ENOTSUP, "Not supported.");
257
  return nullptr;
Daniele Kruse's avatar
Daniele Kruse committed
258
259
260
261
262
}

//------------------------------------------------------------------------------
// getMmap
//------------------------------------------------------------------------------
263
int XrdCtaFile::getMmap(void **Addr, off_t &Size) {
264
265
  error.setErrInfo(ENOTSUP, "Not supported.");
  return SFS_ERROR;
Daniele Kruse's avatar
Daniele Kruse committed
266
267
268
269
270
}

//------------------------------------------------------------------------------
// read
//------------------------------------------------------------------------------
271
XrdSfsXferSize XrdCtaFile::read(XrdSfsFileOffset offset, char *buffer, XrdSfsXferSize size) {
272
  if (nullptr != m_listArchiveFilesCmd.get()) {
273
    // Temporarily treat the "cta archive ls" command the same as all the others
274
    return m_listArchiveFilesCmd->read(offset, buffer, size);
275
276
277
278
279
280
281
282
283
  } else {
    return readFromCmdlineOutput(offset, buffer, size);
  }
}

//------------------------------------------------------------------------------
// readFromCmdlineOutput
//------------------------------------------------------------------------------
XrdSfsXferSize XrdCtaFile::readFromCmdlineOutput(XrdSfsFileOffset offset, char *buffer, XrdSfsXferSize size) {
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
  if(0 > offset) {
    error.setErrInfo(EINVAL, "The value of offset is negative");
    return SFS_ERROR;
  }

  if(offset >= (XrdSfsFileOffset)m_cmdlineOutput.length()) {
    return SFS_OK;
  }

  const XrdSfsXferSize remainingNbBytesToRead = m_cmdlineOutput.length() - offset;
  const XrdSfsXferSize actualNbBytesToRead = remainingNbBytesToRead >= size ? size : remainingNbBytesToRead;

  if(0 < actualNbBytesToRead) {
    strncpy(buffer, m_cmdlineOutput.c_str() + offset, actualNbBytesToRead);
    return actualNbBytesToRead;
  } else {
    return SFS_OK;
  }
Daniele Kruse's avatar
Daniele Kruse committed
302
303
}

304
305
306
//------------------------------------------------------------------------------
// read
//------------------------------------------------------------------------------
307
XrdSfsXferSize XrdCtaFile::read(XrdSfsFileOffset offset, XrdSfsXferSize size) {
308
  error.setErrInfo(ENOTSUP, "Not supported.");
309
  return SFS_ERROR;
310
311
}

Daniele Kruse's avatar
Daniele Kruse committed
312
313
314
//------------------------------------------------------------------------------
// read
//------------------------------------------------------------------------------
315
XrdSfsXferSize XrdCtaFile::read(XrdSfsAio *aioparm) {
Daniele Kruse's avatar
Daniele Kruse committed
316
  error.setErrInfo(ENOTSUP, "Not supported.");
317
  return SFS_ERROR;
Daniele Kruse's avatar
Daniele Kruse committed
318
319
320
321
322
}

//------------------------------------------------------------------------------
// write
//------------------------------------------------------------------------------
323
XrdSfsXferSize XrdCtaFile::write(XrdSfsFileOffset offset, const char *buffer, XrdSfsXferSize size) {
Daniele Kruse's avatar
Daniele Kruse committed
324
  error.setErrInfo(ENOTSUP, "Not supported.");
325
  return SFS_ERROR;
Daniele Kruse's avatar
Daniele Kruse committed
326
327
328
329
330
}

//------------------------------------------------------------------------------
// write
//------------------------------------------------------------------------------
331
int XrdCtaFile::write(XrdSfsAio *aioparm) {
Daniele Kruse's avatar
Daniele Kruse committed
332
333
334
335
336
337
338
  error.setErrInfo(ENOTSUP, "Not supported.");
  return SFS_ERROR;
}

//------------------------------------------------------------------------------
// stat
//------------------------------------------------------------------------------
339
int XrdCtaFile::stat(struct stat *buf) {
340
  buf->st_size=m_cmdlineOutput.length();
Daniele Kruse's avatar
Daniele Kruse committed
341
  return SFS_OK;
Daniele Kruse's avatar
Daniele Kruse committed
342
343
344
345
346
}

//------------------------------------------------------------------------------
// sync
//------------------------------------------------------------------------------
347
int XrdCtaFile::sync() {
Daniele Kruse's avatar
Daniele Kruse committed
348
349
350
351
352
353
354
  error.setErrInfo(ENOTSUP, "Not supported.");
  return SFS_ERROR;
}

//------------------------------------------------------------------------------
// sync
//------------------------------------------------------------------------------
355
int XrdCtaFile::sync(XrdSfsAio *aiop) {
Daniele Kruse's avatar
Daniele Kruse committed
356
357
358
359
360
361
362
  error.setErrInfo(ENOTSUP, "Not supported.");
  return SFS_ERROR;
}

//------------------------------------------------------------------------------
// truncate
//------------------------------------------------------------------------------
363
int XrdCtaFile::truncate(XrdSfsFileOffset fsize) {
Daniele Kruse's avatar
Daniele Kruse committed
364
365
366
367
368
369
370
  error.setErrInfo(ENOTSUP, "Not supported.");
  return SFS_ERROR;
}

//------------------------------------------------------------------------------
// getCXinfo
//------------------------------------------------------------------------------
371
int XrdCtaFile::getCXinfo(char cxtype[4], int &cxrsz) {
Daniele Kruse's avatar
Daniele Kruse committed
372
373
374
375
376
377
378
  error.setErrInfo(ENOTSUP, "Not supported.");
  return SFS_ERROR;
}

//------------------------------------------------------------------------------
// Constructor
//------------------------------------------------------------------------------
379
XrdCtaFile::XrdCtaFile(
380
381
  cta::catalogue::Catalogue *catalogue,
  cta::Scheduler *scheduler,
382
  cta::log::Logger *log,
383
384
  const char *user,
  int MonID):
385
  XrdSfsFile(user, MonID),
386
387
  m_catalogue(catalogue),
  m_scheduler(scheduler),
388
  m_log(*log),
389
  m_cmdlineOutput(""),
390
  m_suppressOptionalOptionsWarning(false){
Daniele Kruse's avatar
Daniele Kruse committed
391
392
393
394
395
}

//------------------------------------------------------------------------------
// Destructor
//------------------------------------------------------------------------------
396
XrdCtaFile::~XrdCtaFile() {  
397
398
399
400
401
}

//------------------------------------------------------------------------------
// replaceAll
//------------------------------------------------------------------------------
402
void XrdCtaFile::replaceAll(std::string& str, const std::string& from, const std::string& to) const {
403
  if(from.empty() || str.empty())
404
    return;
405
406
  size_t start_pos = 0;
  while((start_pos = str.find(from, start_pos)) != std::string::npos) {
407
408
    str.replace(start_pos, from.length(), to);
    start_pos += to.length();
409
  }
410
411
}

412
//------------------------------------------------------------------------------
413
// getOption
414
//------------------------------------------------------------------------------
415
416
std::string XrdCtaFile::getOption(const std::string& optionShortName, const std::string& optionLongName) {
  bool encoded = false;
417
  for(auto it=m_requestTokens.cbegin(); it!=m_requestTokens.cend(); it++) {
418
419
420
421
    if(optionShortName == *it || optionLongName == *it || optionLongName+":base64" == *it) {
      if(optionLongName+":base64" == *it) {
        encoded = true;
      }
422
      auto it_next=it+1;
423
      if(it_next!=m_requestTokens.cend()) {
424
425
426
        std::string value = *it_next;
        if(!encoded) return value;
        else return decode(value.erase(0,7)); //erasing the first 7 characters "base64:" and decoding the rest
427
428
429
430
      }
      else {
        return "";
      }
431
432
433
434
435
    }
  }
  return "";
}

436
437
438
//------------------------------------------------------------------------------
// getOptionStringValue
//------------------------------------------------------------------------------
Daniele Kruse's avatar
Daniele Kruse committed
439
optional<std::string> XrdCtaFile::getOptionStringValue(const std::string& optionShortName, 
440
        const std::string& optionLongName,
Daniele Kruse's avatar
Daniele Kruse committed
441
442
443
        const bool required, 
        const bool useDefaultIfMissing, 
        const std::string& defaultValue) {
444
  std::string option = getOption(optionShortName, optionLongName);
Daniele Kruse's avatar
Daniele Kruse committed
445
446
447
  if(option.empty()&&useDefaultIfMissing) {
    option = defaultValue;
  }
448
  if(option.empty()) {
449
450
451
452
453
454
    if(required) {
      m_missingRequiredOptions.push_back(optionLongName);
    }
    else {
      m_missingOptionalOptions.push_back(optionLongName);
    }
455
456
    return optional<std::string>();
  }
457
458
459
  if(!required) {
    m_optionalOptions.push_back(optionLongName);
  }
460
461
462
463
464
465
  return optional<std::string>(option);
}

//------------------------------------------------------------------------------
// getOptionUint64Value
//------------------------------------------------------------------------------
Daniele Kruse's avatar
Daniele Kruse committed
466
optional<uint64_t> XrdCtaFile::getOptionUint64Value(const std::string& optionShortName, 
467
        const std::string& optionLongName,
Daniele Kruse's avatar
Daniele Kruse committed
468
469
470
        const bool required, 
        const bool useDefaultIfMissing, 
        const std::string& defaultValue) {
471
  std::string option = getOption(optionShortName, optionLongName);
Daniele Kruse's avatar
Daniele Kruse committed
472
473
474
  if(option.empty()&&useDefaultIfMissing) {
    option = defaultValue;
  }
475
  if(option.empty()) {
476
477
478
479
480
481
    if(required) {
      m_missingRequiredOptions.push_back(optionLongName);
    }
    else {
      m_missingOptionalOptions.push_back(optionLongName);
    }
482
483
    return optional<uint64_t>();
  }
484
485
486
  if(!required) {
    m_optionalOptions.push_back(optionLongName);
  }
487
488
489
490
491
492
  return optional<uint64_t>(stringParameterToUint64(optionLongName, option));
}

//------------------------------------------------------------------------------
// getOptionBoolValue
//------------------------------------------------------------------------------
Daniele Kruse's avatar
Daniele Kruse committed
493
optional<bool> XrdCtaFile::getOptionBoolValue(const std::string& optionShortName, 
494
        const std::string& optionLongName,
Daniele Kruse's avatar
Daniele Kruse committed
495
496
497
        const bool required, 
        const bool useDefaultIfMissing, 
        const std::string& defaultValue) {
498
  std::string option = getOption(optionShortName, optionLongName);
Daniele Kruse's avatar
Daniele Kruse committed
499
500
501
  if(option.empty()&&useDefaultIfMissing) {
    option = defaultValue;
  }
502
  if(option.empty()) {
503
504
505
506
507
508
    if(required) {
      m_missingRequiredOptions.push_back(optionLongName);
    }
    else {
      m_missingOptionalOptions.push_back(optionLongName);
    }
509
510
    return optional<bool>();
  }
511
512
513
  if(!required) {
    m_optionalOptions.push_back(optionLongName);
  }
514
515
516
517
518
519
  return optional<bool>(stringParameterToBool(optionLongName, option));
}

//------------------------------------------------------------------------------
// getOptionTimeValue
//------------------------------------------------------------------------------
Daniele Kruse's avatar
Daniele Kruse committed
520
optional<time_t> XrdCtaFile::getOptionTimeValue(const std::string& optionShortName, 
521
        const std::string& optionLongName,
Daniele Kruse's avatar
Daniele Kruse committed
522
523
524
        const bool required, 
        const bool useDefaultIfMissing, 
        const std::string& defaultValue) {
525
  std::string option = getOption(optionShortName, optionLongName);
Daniele Kruse's avatar
Daniele Kruse committed
526
527
528
  if(option.empty()&&useDefaultIfMissing) {
    option = defaultValue;
  }
529
  if(option.empty()) {
530
531
532
533
534
535
    if(required) {
      m_missingRequiredOptions.push_back(optionLongName);
    }
    else {
      m_missingOptionalOptions.push_back(optionLongName);
    }
536
537
    return optional<time_t>();
  }
538
539
540
  if(!required) {
    m_optionalOptions.push_back(optionLongName);
  }
541
542
543
  return optional<time_t>(stringParameterToTime(optionLongName, option));
}

544
545
546
//------------------------------------------------------------------------------
// hasOption
//------------------------------------------------------------------------------
547
bool XrdCtaFile::hasOption(const std::string& optionShortName, const std::string& optionLongName) {
548
  for(auto it=m_requestTokens.cbegin(); it!=m_requestTokens.cend(); it++) {
549
550
551
552
553
554
555
    if(optionShortName == *it || optionLongName == *it) {
      return true;
    }
  }
  return false;
}

556
557
558
//------------------------------------------------------------------------------
// timeToString
//------------------------------------------------------------------------------
559
std::string XrdCtaFile::timeToString(const time_t &time) {
560
561
562
563
564
  std::string timeString(ctime(&time));
  timeString=timeString.substr(0,timeString.size()-1); //remove newline
  return timeString;
}

565
//------------------------------------------------------------------------------
566
// formatResponse
567
//------------------------------------------------------------------------------
568
std::string XrdCtaFile::formatResponse(const std::vector<std::vector<std::string>> &responseTable, const bool withHeader) {
569
570
  if(responseTable.empty()||responseTable.at(0).empty()) {
    return "";
571
  }
572
  std::vector<int> columnSizes;
573
  for(uint j=0; j<responseTable.at(0).size(); j++) { //for each column j
574
    uint columnSize=0;
575
    for(uint i=0; i<responseTable.size(); i++) { //for each row i
576
577
578
      if(responseTable.at(i).at(j).size()>columnSize) {
        columnSize=responseTable.at(i).at(j).size();
      }
579
    }
580
    columnSizes.push_back(columnSize);//loops here
581
  }
582
583
  std::stringstream responseSS;
  for(auto row=responseTable.cbegin(); row!=responseTable.cend(); row++) {
584
585
    if(withHeader && row==responseTable.cbegin()) responseSS << "\x1b[31;1m";
    for(uint i=0; i<row->size(); i++) {
586
      responseSS << std::string(i?"  ":"")<< std::setw(columnSizes.at(i)) << row->at(i);
587
    }
588
589
    if(withHeader && row==responseTable.cbegin()) responseSS << "\x1b[0m" << std::endl;
    else responseSS << std::endl;
590
  }
591
592
593
594
595
596
  return responseSS.str();
}

//------------------------------------------------------------------------------
// addLogInfoToResponseRow
//------------------------------------------------------------------------------
597
void XrdCtaFile::addLogInfoToResponseRow(std::vector<std::string> &responseRow, const cta::common::dataStructures::EntryLog &creationLog, const cta::common::dataStructures::EntryLog &lastModificationLog) {
598
  responseRow.push_back(creationLog.username);
599
600
  responseRow.push_back(creationLog.host);
  responseRow.push_back(timeToString(creationLog.time));
601
  responseRow.push_back(lastModificationLog.username);
602
603
  responseRow.push_back(lastModificationLog.host);
  responseRow.push_back(timeToString(lastModificationLog.time));
604
605
}

606
607
608
609
610
//------------------------------------------------------------------------------
// stringParameterToUint64
//------------------------------------------------------------------------------
uint64_t XrdCtaFile::stringParameterToUint64(const std::string &parameterName, const std::string &parameterValue) const {
  try {
611
612
613
614
    return cta::utils::toUint64(parameterValue);
  } catch(cta::exception::Exception &ex) {
    throw cta::exception::Exception(std::string(__FUNCTION__)+" - Parameter: "+parameterName+" ("+parameterValue+
            ") could not be converted to uint64_t because: " + ex.getMessageValue());
615
616
617
618
619
620
621
622
623
624
625
626
  }
}

//------------------------------------------------------------------------------
// stringParameterToBool
//------------------------------------------------------------------------------
bool XrdCtaFile::stringParameterToBool(const std::string &parameterName, const std::string &parameterValue) const {
  if(parameterValue == "true") {
    return true;
  } 
  else if(parameterValue == "false") {
    return false;
627
  } 
628
629
630
631
632
633
634
635
636
637
638
  else {
    throw cta::exception::Exception(std::string(__FUNCTION__)+" - Parameter: "+parameterName+" ("+parameterValue+
            ") could not be converted to bool: the only values allowed are \"true\" and \"false\"");
  }
}

//------------------------------------------------------------------------------
// stringParameterToTime
//------------------------------------------------------------------------------
time_t XrdCtaFile::stringParameterToTime(const std::string &parameterName, const std::string &parameterValue) const {
  struct tm time;
639
  if(nullptr==strptime(parameterValue.c_str(), "%d/%m/%Y", &time)) {
640
641
642
643
    throw cta::exception::Exception(std::string(__FUNCTION__)+" - Parameter: "+parameterName+" ("+parameterValue+
            ") could not be interpreted as a time, the allowed format is: <DD/MM/YYYY>");
  }
  return mktime(&time);
644
645
}

646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
//------------------------------------------------------------------------------
// EOS2CTAChecksumType
//------------------------------------------------------------------------------
optional<std::string> XrdCtaFile::EOS2CTAChecksumType(const optional<std::string> &EOSChecksumType) {
  if(EOSChecksumType && EOSChecksumType.value()=="adler") return optional<std::string>("ADLER32");
  return nullopt;
}

//------------------------------------------------------------------------------
// EOS2CTAChecksumValue
//------------------------------------------------------------------------------
optional<std::string> XrdCtaFile::EOS2CTAChecksumValue(const optional<std::string> &EOSChecksumValue) {
  if(EOSChecksumValue) {
    std::string CTAChecksumValue("0X");  
    CTAChecksumValue += EOSChecksumValue.value();
    cta::utils::toUpper(CTAChecksumValue);
    return optional<std::string>(CTAChecksumValue);
  }
  return nullopt;
}

667
668
669
//------------------------------------------------------------------------------
// xCom_admin
//------------------------------------------------------------------------------
670
std::string XrdCtaFile::xCom_admin() {
671
  std::stringstream cmdlineOutput;
672
  std::stringstream help;
673
  help << m_requestTokens.at(0) << " ad/admin add/ch/rm/ls:" << std::endl
674
675
676
       << "\tadd --username/-u <user_name> --comment/-m <\"comment\">" << std::endl
       << "\tch  --username/-u <user_name> --comment/-m <\"comment\">" << std::endl
       << "\trm  --username/-u <user_name>" << std::endl
677
       << "\tls  [--header/-h]" << std::endl;
678
  if(m_requestTokens.size() < 3) {
679
    throw cta::exception::UserError(help.str());
680
  }
681
  if("add" == m_requestTokens.at(2) || "ch" == m_requestTokens.at(2) || "rm" == m_requestTokens.at(2)) {
682
    optional<std::string> username = getOptionStringValue("-u", "--username", true, false);
683
    if("add" == m_requestTokens.at(2) || "ch" == m_requestTokens.at(2)) {
684
      optional<std::string> comment = getOptionStringValue("-m", "--comment", true, false);
685
      if("add" == m_requestTokens.at(2)) { //add
686
        checkOptions(help.str());
687
        m_catalogue->createAdminUser(m_cliIdentity, username.value(), comment.value());
688
689
      }
      else { //ch
690
        checkOptions(help.str());
691
        m_catalogue->modifyAdminUserComment(m_cliIdentity, username.value(), comment.value());
692
693
694
      }
    }
    else { //rm
695
      checkOptions(help.str());
696
      m_catalogue->deleteAdminUser(username.value());
697
    }
698
  }
699
  else if("ls" == m_requestTokens.at(2)) { //ls
700
    std::list<cta::common::dataStructures::AdminUser> list= m_catalogue->getAdminUsers();
701
    if(list.size()>0) {
702
      std::vector<std::vector<std::string>> responseTable;
703
      std::vector<std::string> header = {"user","c.user","c.host","c.time","m.user","m.host","m.time","comment"};
704
      if(hasOption("-h", "--header")) responseTable.push_back(header);    
705
      for(auto it = list.cbegin(); it != list.cend(); it++) {
706
        std::vector<std::string> currentRow;
707
        currentRow.push_back(it->name);
708
709
        addLogInfoToResponseRow(currentRow, it->creationLog, it->lastModificationLog);
        currentRow.push_back(it->comment);
710
711
        responseTable.push_back(currentRow);
      }
712
      cmdlineOutput << formatResponse(responseTable, hasOption("-h", "--header"));
713
714
715
    }
  }
  else {
716
    throw cta::exception::UserError(help.str());
717
  }
718
  return cmdlineOutput.str();
719
}
720

721
//------------------------------------------------------------------------------
722
// xCom_adminhost
723
//------------------------------------------------------------------------------
724
std::string XrdCtaFile::xCom_adminhost() {
725
  std::stringstream cmdlineOutput;
726
  std::stringstream help;
727
  help << m_requestTokens.at(0) << " ah/adminhost add/ch/rm/ls:" << std::endl
728
       << "\tadd --name/-n <host_name> --comment/-m <\"comment\">" << std::endl
Daniele Kruse's avatar
Daniele Kruse committed
729
       << "\tch  --name/-n <host_name> --comment/-m <\"comment\">" << std::endl
730
       << "\trm  --name/-n <host_name>" << std::endl
731
       << "\tls  [--header/-h]" << std::endl;  
732
  if(m_requestTokens.size() < 3) {
733
    throw cta::exception::UserError(help.str());
734
  }
735
  if("add" == m_requestTokens.at(2) || "ch" == m_requestTokens.at(2) || "rm" == m_requestTokens.at(2)) {
736
    optional<std::string> hostname = getOptionStringValue("-n", "--name", true, false);
737
    if("add" == m_requestTokens.at(2) || "ch" == m_requestTokens.at(2)) {
738
      optional<std::string> comment = getOptionStringValue("-m", "--comment", true, false);
739
      if("add" == m_requestTokens.at(2)) { //add
740
        checkOptions(help.str());
741
        m_catalogue->createAdminHost(m_cliIdentity, hostname.value(), comment.value());
Daniele Kruse's avatar
Daniele Kruse committed
742
743
      }
      else { //ch
744
        checkOptions(help.str());
745
        m_catalogue->modifyAdminHostComment(m_cliIdentity, hostname.value(), comment.value());
Daniele Kruse's avatar
Daniele Kruse committed
746
747
748
      }
    }
    else { //rm
749
      checkOptions(help.str());
750
      m_catalogue->deleteAdminHost(hostname.value());
Daniele Kruse's avatar
Daniele Kruse committed
751
752
    }
  }
753
  else if("ls" == m_requestTokens.at(2)) { //ls
754
    std::list<cta::common::dataStructures::AdminHost> list= m_catalogue->getAdminHosts();
Daniele Kruse's avatar
Daniele Kruse committed
755
756
    if(list.size()>0) {
      std::vector<std::vector<std::string>> responseTable;
757
      std::vector<std::string> header = {"hostname","c.user","c.host","c.time","m.user","m.host","m.time","comment"};
758
      if(hasOption("-h", "--header")) responseTable.push_back(header);
759
      for(auto it = list.cbegin(); it != list.cend(); it++) {
Daniele Kruse's avatar
Daniele Kruse committed
760
        std::vector<std::string> currentRow;
761
762
763
        currentRow.push_back(it->name);
        addLogInfoToResponseRow(currentRow, it->creationLog, it->lastModificationLog);
        currentRow.push_back(it->comment);
Daniele Kruse's avatar
Daniele Kruse committed
764
765
        responseTable.push_back(currentRow);
      }
766
      cmdlineOutput << formatResponse(responseTable, hasOption("-h", "--header"));
Daniele Kruse's avatar
Daniele Kruse committed
767
768
769
    }
  }
  else {
770
    throw cta::exception::UserError(help.str());
Daniele Kruse's avatar
Daniele Kruse committed
771
  }
772
  return cmdlineOutput.str();
773
}
774

775
776
777
//------------------------------------------------------------------------------
// xCom_tapepool
//------------------------------------------------------------------------------
778
std::string XrdCtaFile::xCom_tapepool() {
779
  std::stringstream cmdlineOutput;
780
  std::stringstream help;
781
  help << m_requestTokens.at(0) << " tp/tapepool add/ch/rm/ls:" << std::endl
Daniele Kruse's avatar
Daniele Kruse committed
782
783
       << "\tadd --name/-n <tapepool_name> --partialtapesnumber/-p <number_of_partial_tapes> --encrypted/-e <\"true\" or \"false\"> --comment/-m <\"comment\">" << std::endl
       << "\tch  --name/-n <tapepool_name> [--partialtapesnumber/-p <number_of_partial_tapes>] [--encrypted/-e <\"true\" or \"false\">] [--comment/-m <\"comment\">]" << std::endl
784
       << "\trm  --name/-n <tapepool_name>" << std::endl
785
       << "\tls  [--header/-h]" << std::endl;  
786
  if(m_requestTokens.size() < 3) {
787
    throw cta::exception::UserError(help.str());
788
  }
789
  if("add" == m_requestTokens.at(2) || "ch" == m_requestTokens.at(2) || "rm" == m_requestTokens.at(2)) {
790
    optional<std::string> name = getOptionStringValue("-n", "--name", true, false);
791
    if("add" == m_requestTokens.at(2)) { //add
792
793
794
      optional<uint64_t> ptn = getOptionUint64Value("-p", "--partialtapesnumber", true, false);
      optional<std::string> comment = getOptionStringValue("-m", "--comment", true, false);
      optional<bool> encrypted = getOptionBoolValue("-e", "--encrypted", true, false);
795
      checkOptions(help.str());
Daniele Kruse's avatar
Daniele Kruse committed
796
      m_catalogue->createTapePool(m_cliIdentity, name.value(), ptn.value(), encrypted.value(), comment.value());
Daniele Kruse's avatar
Daniele Kruse committed
797
    }
798
    else if("ch" == m_requestTokens.at(2)) { //ch
799
800
801
      optional<uint64_t> ptn = getOptionUint64Value("-p", "--partialtapesnumber", false, false);
      optional<std::string> comment = getOptionStringValue("-m", "--comment", false, false);
      optional<bool> encrypted = getOptionBoolValue("-e", "--encrypted", false, false);
802
      checkOptions(help.str());
803
      if(comment) {
804
        m_catalogue->modifyTapePoolComment(m_cliIdentity, name.value(), comment.value());
Daniele Kruse's avatar
Daniele Kruse committed
805
      }
806
      if(ptn) {
807
        m_catalogue->modifyTapePoolNbPartialTapes(m_cliIdentity, name.value(), ptn.value());
Daniele Kruse's avatar
Daniele Kruse committed
808
      }
Daniele Kruse's avatar
Daniele Kruse committed
809
810
      if(encrypted) {
        m_catalogue->setTapePoolEncryption(m_cliIdentity, name.value(), encrypted.value());
811
      }
Daniele Kruse's avatar
Daniele Kruse committed
812
813
    }
    else { //rm
814
      checkOptions(help.str());
815
      m_catalogue->deleteTapePool(name.value());
Daniele Kruse's avatar
Daniele Kruse committed
816
817
    }
  }
818
  else if("ls" == m_requestTokens.at(2)) { //ls
819
    std::list<cta::common::dataStructures::TapePool> list= m_catalogue->getTapePools();
Daniele Kruse's avatar
Daniele Kruse committed
820
821
    if(list.size()>0) {
      std::vector<std::vector<std::string>> responseTable;
822
      std::vector<std::string> header = {"name","# partial tapes","encrypt","c.user","c.host","c.time","m.user","m.host","m.time","comment"};
823
      if(hasOption("-h", "--header")) responseTable.push_back(header);    
824
      for(auto it = list.cbegin(); it != list.cend(); it++) {
Daniele Kruse's avatar
Daniele Kruse committed
825
        std::vector<std::string> currentRow;
826
827
        currentRow.push_back(it->name);
        currentRow.push_back(std::to_string((unsigned long long)it->nbPartialTapes));
828
        if(it->encryption) currentRow.push_back("true"); else currentRow.push_back("false");
829
830
        addLogInfoToResponseRow(currentRow, it->creationLog, it->lastModificationLog);
        currentRow.push_back(it->comment);
Daniele Kruse's avatar
Daniele Kruse committed
831
832
        responseTable.push_back(currentRow);
      }
833
      cmdlineOutput << formatResponse(responseTable, hasOption("-h", "--header"));
Daniele Kruse's avatar
Daniele Kruse committed
834
835
836
    }
  }
  else {
837
    throw cta::exception::UserError(help.str());
Daniele Kruse's avatar
Daniele Kruse committed
838
  }
839
  return cmdlineOutput.str();
840
}
841

842
843
844
//------------------------------------------------------------------------------
// xCom_archiveroute
//------------------------------------------------------------------------------
845
std::string XrdCtaFile::xCom_archiveroute() {
846
  std::stringstream cmdlineOutput;
847
  std::stringstream help;
848
  help << m_requestTokens.at(0) << " ar/archiveroute add/ch/rm/ls:" << std::endl
849
850
851
       << "\tadd --instance/-i <instance_name> --storageclass/-s <storage_class_name> --copynb/-c <copy_number> --tapepool/-t <tapepool_name> --comment/-m <\"comment\">" << std::endl
       << "\tch  --instance/-i <instance_name> --storageclass/-s <storage_class_name> --copynb/-c <copy_number> [--tapepool/-t <tapepool_name>] [--comment/-m <\"comment\">]" << std::endl
       << "\trm  --instance/-i <instance_name> --storageclass/-s <storage_class_name> --copynb/-c <copy_number>" << std::endl
852
       << "\tls  [--header/-h]" << std::endl;  
853
  if(m_requestTokens.size() < 3) {
854
    throw cta::exception::UserError(help.str());
855
  }
856
  if("add" == m_requestTokens.at(2) || "ch" == m_requestTokens.at(2) || "rm" == m_requestTokens.at(2)) {
857
858
859
    optional<std::string> scn = getOptionStringValue("-s", "--storageclass", true, false);
    optional<uint64_t> cn = getOptionUint64Value("-c", "--copynb", true, false);
    optional<std::string> in = getOptionStringValue("-i", "--instance", true, false);
860
    if("add" == m_requestTokens.at(2)) { //add