CleanerSession.cpp 16.8 KB
Newer Older
1
2
/*
 * @project        The CERN Tape Archive (CTA)
3
 * @copyright      Copyright(C) 2003-2021 CERN
4
5
6
7
 * @license        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.
8
 *
9
10
11
12
 *                 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.
13
 *
14
15
16
 *                 You should have received a copy of the GNU General Public License
 *                 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
17
18

#include "castor/tape/tapeserver/daemon/CleanerSession.hpp"
19
20
#include "catalogue/Catalogue.hpp"
#include <exception>
21
22
23
24
25

//------------------------------------------------------------------------------
// constructor
//------------------------------------------------------------------------------
castor::tape::tapeserver::daemon::CleanerSession::CleanerSession(
26
  cta::server::ProcessCap &capUtils,
27
  cta::mediachanger::MediaChangerFacade &mc,
Victor Kotlyar's avatar
Victor Kotlyar committed
28
  cta::log::Logger &log,
29
  const cta::tape::daemon::TpconfigLine &driveConfig,
30
  System::virtualWrapper &sysWrapper,
31
  const std::string &vid,
32
  const bool waitMediaInDrive,
33
  const uint32_t waitMediaInDriveTimeout,
34
  const std::string & externalEncryptionKeyScript,
35
36
  cta::catalogue::Catalogue & catalogue,
  cta::Scheduler & scheduler):
37
  m_capUtils(capUtils),
38
  m_mc(mc),
39
40
  m_log(log),
  m_driveConfig(driveConfig),
41
  m_sysWrapper(sysWrapper),
42
  m_vid(vid),
43
  m_waitMediaInDrive(waitMediaInDrive),
44
  m_tapeLoadTimeout(waitMediaInDriveTimeout),
45
  m_encryptionControl(externalEncryptionKeyScript),
46
47
  m_catalogue(catalogue),
  m_scheduler(scheduler)
48
  {}
49
50

//------------------------------------------------------------------------------
51
// execute
52
//------------------------------------------------------------------------------
53
castor::tape::tapeserver::daemon::Session::EndOfSessionAction
54
  castor::tape::tapeserver::daemon::CleanerSession::execute() throw() {
55
56
57
58
  std::string errorMessage;

  try {
    return exceptionThrowingExecute();
59
  } catch(cta::exception::Exception &ex) {
60
61
62
63
64
65
66
    errorMessage = ex.getMessage().str();
  } catch(std::exception &se) {
    errorMessage = se.what();
  } catch(...) {
    errorMessage = "Caught an unknown exception";
  }

67
  //Reaching this point means the cleaner failed and an exception was thrown
Victor Kotlyar's avatar
Victor Kotlyar committed
68
  std::list<cta::log::Param> params = {
69
70
    cta::log::Param("tapeVid", m_vid),
    cta::log::Param("tapeDrive", m_driveConfig.unitName),
Victor Kotlyar's avatar
Victor Kotlyar committed
71
    cta::log::Param("message", errorMessage)};
72
73
74
75
76
77
78
79
80
81
82
83
84
  m_log(cta::log::ERR, "Cleaner failed. Putting the drive down.", params);
  
  //Putting the drive down  
  try {
    setDriveDownAfterCleanerFailed(std::string("Cleaner failed. ") + errorMessage);
  } catch(const cta::exception::Exception &ex) {
    std::list<cta::log::Param> params = {
    cta::log::Param("tapeVid", m_vid),
    cta::log::Param("tapeDrive", m_driveConfig.unitName),
    cta::log::Param("message", ex.getMessageValue())};
    m_log(cta::log::ERR, "Cleaner failed. Failed to put the drive down", params);
  }
  
85
86
87
  return MARK_DRIVE_AS_DOWN;
}

88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
void castor::tape::tapeserver::daemon::CleanerSession::setDriveDownAfterCleanerFailed(const std::string & errorMsg) {
  
  std::string logicalLibrary =  m_driveConfig.logicalLibrary;
  std::string hostname=cta::utils::getShortHostname();
  std::string driveName = m_driveConfig.unitName;
  
  cta::common::dataStructures::DriveInfo driveInfo;
  driveInfo.driveName = driveName;
  driveInfo.logicalLibrary = logicalLibrary;
  driveInfo.host = hostname;
  
  cta::log::LogContext lc(m_log);
  
  m_scheduler.reportDriveStatus(driveInfo, cta::common::dataStructures::MountType::NoMount, cta::common::dataStructures::DriveStatus::Down, lc);
  cta::common::dataStructures::SecurityIdentity cliId;
  cta::common::dataStructures::DesiredDriveState driveState;
  driveState.up = false;
  driveState.forceDown = false;
  driveState.setReasonFromLogMsg(cta::log::ERR,errorMsg);
  m_scheduler.setDesiredDriveState(cliId, m_driveConfig.unitName, driveState, lc);
}

110
111
112
113
114
115
116
117
//------------------------------------------------------------------------------
// exceptionThrowingExecute
//------------------------------------------------------------------------------
castor::tape::tapeserver::daemon::Session::EndOfSessionAction
  castor::tape::tapeserver::daemon::CleanerSession::exceptionThrowingExecute() {

  setProcessCapabilities("cap_sys_rawio+ep");

118
  std::unique_ptr<drive::DriveInterface> drivePtr = createDrive();
119
  drive::DriveInterface &drive = *drivePtr.get();
120

121
122
123
124
  try {
    cleanDrive(drive);
  } catch(...) {
    logAndClearTapeAlerts(drive);
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
    //As we failed to clean the drive (unmount the tape or rewinding impossible), 
    //we set the tape as disabled so that it will not be mounted for future retrieves
    //otherwise, we will go in an infinite loop of mounting with errors.
    //Gitlab ticket reference : https://gitlab.cern.ch/cta/CTA/issues/224
    if(!m_vid.empty()){
      
      //Get the message exception to log it into the comment section of the tape table
      auto currException = std::current_exception();
      std::string currentExceptionMsg = "";
      try {
        std::rethrow_exception(currException);
      } catch(cta::exception::Exception &ex){
        currentExceptionMsg = ex.getMessageValue();
      } catch(std::exception & ex){
        currentExceptionMsg = ex.what();
      } catch (...) {
        currentExceptionMsg = "Unknown exception";
      }
      
      std::string hostname = cta::utils::getShortHostname();
      std::string tapeDrive = m_driveConfig.unitName;
      std::list<cta::log::Param> params = {
        cta::log::Param("tapeVid", m_vid),
        cta::log::Param("tapeDrive", tapeDrive),
        cta::log::Param("logicalLibrary", m_driveConfig.logicalLibrary),
        cta::log::Param("host",hostname),
        cta::log::Param("exceptionMsg", currentExceptionMsg)};
      m_log(cta::log::ERR,"In CleanerSession::exceptionThrowingExecute(), failed to clean the Drive with a tape mounted. Disabling the tape.",params);
      cta::common::dataStructures::SecurityIdentity admin;
154
155
      admin.username = c_defaultUserNameUpdate + " " + tapeDrive;
      admin.host = hostname;
156
157
      
      try{
158
159
        std::string disabledReason = cta::utils::getCurrentLocalTime("%F %T") + ":" + currentExceptionMsg; 
        m_catalogue.setTapeDisabled(admin, m_vid, disabledReason);
160
161
162
163
164
165
      } catch(cta::exception::Exception &ex) {
        cta::log::Param param("exceptionMsg",ex.getMessageValue());
        params.push_back(param);
        m_log(cta::log::ERR,"In CleanerSession::exceptionThrowingExecute(), failed to disable the tape.",params);
      }
    }
166
167
168
169
170
171
172
173
174
175
176
    throw;
  }
  
  logAndClearTapeAlerts(drive);
  return MARK_DRIVE_AS_UP;
}

//------------------------------------------------------------------------------
// cleanDrive
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::CleanerSession::cleanDrive(drive::DriveInterface &drive) {
177
178
179
  if(m_waitMediaInDrive) {
    waitUntilMediaIsReady(drive);
  }
180

181
  if(!drive.hasTapeInPlace()) {
Victor Kotlyar's avatar
Victor Kotlyar committed
182
    std::list<cta::log::Param> params;
183
184
    params.push_back(cta::log::Param("tapeVid", m_vid));
    params.push_back(cta::log::Param("tapeDrive", m_driveConfig.unitName));
Victor Kotlyar's avatar
Victor Kotlyar committed
185
    m_log(cta::log::INFO, "Cleaner found tape drive empty", params);
186
    return; //return immediately if there is no tape
187
188
  }

189
190
  m_encryptionControl.disable(drive);

191
  rewindDrive(drive);
192
  drive.disableLogicalBlockProtection();
193

194
  checkTapeContainsData(drive);
195

196
  const std::string volumeLabelVSN = checkVolumeLabel(drive);
197

198
  unloadTape(volumeLabelVSN, drive);
199
200

  dismountTape(volumeLabelVSN);
201
}
202

203
204
205
206
207
208
209
210
211
212
213
214
215
//------------------------------------------------------------------------------
// logAndClearTapeAlerts
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::CleanerSession::logAndClearTapeAlerts(drive::DriveInterface &drive) throw() {
  std::string errorMessage;
  try{
    std::vector<uint16_t> tapeAlertCodes = drive.getTapeAlertCodes();
    if (!tapeAlertCodes.empty()) {
      size_t alertNumber = 0;
      // Log tape alerts in the logs.
      std::vector<std::string> tapeAlerts = drive.getTapeAlerts(tapeAlertCodes);
      for (std::vector<std::string>::iterator ta=tapeAlerts.begin(); ta!=tapeAlerts.end();ta++)
      {
Victor Kotlyar's avatar
Victor Kotlyar committed
216
217
218
219
220
        std::list<cta::log::Param> params = {
          cta::log::Param("tapeAlert",*ta),
          cta::log::Param("tapeAlertNumber", alertNumber++),
          cta::log::Param("tapeAlertCount", tapeAlerts.size())};
        m_log(cta::log::WARNING, "Tape alert detected",params);
221
222
223
      }
    }
    return;
224
  } catch(cta::exception::Exception &ex) {
225
226
227
228
229
230
231
232
    errorMessage = ex.getMessage().str();
  } catch(std::exception &se) {
    errorMessage = se.what();
  } catch(...) {
    errorMessage = "Caught an unknown exception";
  }

  // Reaching this point means it failed and an exception was thrown (because of the "return" above)
Victor Kotlyar's avatar
Victor Kotlyar committed
233
  std::list<cta::log::Param> params = {
234
235
    cta::log::Param("tapeVid", m_vid),
    cta::log::Param("tapeDrive", m_driveConfig.unitName),
Victor Kotlyar's avatar
Victor Kotlyar committed
236
237
    cta::log::Param("message", errorMessage)};
  m_log(cta::log::ERR, "Cleaner failed getting tape alerts from the drive", params);  
238
239
240
241
242
243
244
245
}

//------------------------------------------------------------------------------
// setProcessCapabilities
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::CleanerSession::setProcessCapabilities(
  const std::string &capabilities) {
  m_capUtils.setProcText(capabilities);
246
  {
Victor Kotlyar's avatar
Victor Kotlyar committed
247
248
249
    std::list<cta::log::Param> params = {
      cta::log::Param("capabilities", m_capUtils.getProcText())};
    m_log(cta::log::INFO, "Cleaner set process capabilities for using tape",
250
251
      params);
  }
252
}
253

254
255
256
//------------------------------------------------------------------------------
// createDrive
//------------------------------------------------------------------------------
257
std::unique_ptr<castor::tape::tapeserver::drive::DriveInterface>
258
  castor::tape::tapeserver::daemon::CleanerSession::createDrive() {
259
  SCSI::DeviceVector dv(m_sysWrapper);
260
  SCSI::DeviceInfo driveInfo = dv.findBySymlink(m_driveConfig.devFilename);
261
262
  
  // Instantiate the drive object
263
  std::unique_ptr<castor::tape::tapeserver::drive::DriveInterface>
264
    drive(drive::createDrive(driveInfo, m_sysWrapper));
265

266
  if(NULL == drive.get()) {
267
    cta::exception::Exception ex;
268
    ex.getMessage() << "Failed to instantiate drive object";
269
    throw ex;
270
271
  } 
    
272
273
274
275
276
277
  return drive;
}

//------------------------------------------------------------------------------
// waitUntilDriveIsReady
//------------------------------------------------------------------------------
278
279
void castor::tape::tapeserver::daemon::CleanerSession::waitUntilMediaIsReady(
  drive::DriveInterface &drive) {  
Victor Kotlyar's avatar
Victor Kotlyar committed
280
  std::list<cta::log::Param> params;
281
282
  params.push_back(cta::log::Param("tapeVid", m_vid));
  params.push_back(cta::log::Param("tapeDrive", m_driveConfig.unitName));
Victor Kotlyar's avatar
Victor Kotlyar committed
283
  params.push_back(cta::log::Param("waitMediaInDriveTimeout",
284
    m_tapeLoadTimeout));
285
286

  try {
Victor Kotlyar's avatar
Victor Kotlyar committed
287
    m_log(cta::log::INFO, "Cleaner waiting for drive to be ready", params);
288
    drive.waitUntilReady(m_tapeLoadTimeout);
Victor Kotlyar's avatar
Victor Kotlyar committed
289
    m_log(cta::log::INFO, "Cleaner detected drive is ready", params);
290
  } catch (cta::exception::Exception &ex) {
Victor Kotlyar's avatar
Victor Kotlyar committed
291
292
    params.push_back(cta::log::Param("message", ex.getMessage().str()));
    m_log(cta::log::INFO, "Cleaner caught non-fatal exception whilst waiting for"
293
      " drive to become ready", params);
294
  }
295
296
297
298
299
300
}

//------------------------------------------------------------------------------
// rewindDrive
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::CleanerSession::rewindDrive(
301
  drive::DriveInterface &drive) {
Victor Kotlyar's avatar
Victor Kotlyar committed
302
  std::list<cta::log::Param> params;
303
304
  params.push_back(cta::log::Param("tapeVid", m_vid));
  params.push_back(cta::log::Param("tapeDrive", m_driveConfig.unitName));
305

Victor Kotlyar's avatar
Victor Kotlyar committed
306
  m_log(cta::log::INFO, "Cleaner rewinding tape", params);
307
  drive.rewind();
Victor Kotlyar's avatar
Victor Kotlyar committed
308
  m_log(cta::log::INFO, "Cleaner successfully rewound tape", params);
309
310
311
312
313
314
}

//------------------------------------------------------------------------------
// checkTapeContainsData
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::CleanerSession::checkTapeContainsData(
315
  drive::DriveInterface &drive) {
Victor Kotlyar's avatar
Victor Kotlyar committed
316
  std::list<cta::log::Param> params;
317
318
  params.push_back(cta::log::Param("tapeVid", m_vid));
  params.push_back(cta::log::Param("tapeDrive", m_driveConfig.unitName));
319

Victor Kotlyar's avatar
Victor Kotlyar committed
320
  m_log(cta::log::INFO, "Cleaner checking tape contains data", params);
321
  if(drive.isTapeBlank()) {
322
    cta::exception::Exception ex;
323
324
325
    ex.getMessage() << "Tape is completely blank when it should be labeled";
    throw ex;
  }
Victor Kotlyar's avatar
Victor Kotlyar committed
326
  m_log(cta::log::INFO, "Cleaner successfully detected tape contains data", params);
327
328
329
330
331
332
}

//------------------------------------------------------------------------------
// checkVolumeLabel
//------------------------------------------------------------------------------
std::string castor::tape::tapeserver::daemon::CleanerSession::checkVolumeLabel(
333
  drive::DriveInterface &drive) {
334
  tapeFile::VOL1 vol1;
Victor Kotlyar's avatar
Victor Kotlyar committed
335
  std::list<cta::log::Param> params;
336
337
  params.push_back(cta::log::Param("tapeVid", m_vid));
  params.push_back(cta::log::Param("tapeDrive", m_driveConfig.unitName));
338
  
339
  try {
340
    drive.readExactBlock((void * )&vol1, sizeof(vol1),
341
342
343
344
      "[CleanerSession::clean()] - Reading header VOL1");
    vol1.verify();

    const std::string &volumeLabelVSN = vol1.getVSN();
Victor Kotlyar's avatar
Victor Kotlyar committed
345
    params.push_back(cta::log::Param("volumeLabelVSN", volumeLabelVSN));
346

Victor Kotlyar's avatar
Victor Kotlyar committed
347
    m_log(cta::log::INFO, "Cleaner read VSN from volume label", params);
348
349
350
351

    // If the cleaner was given a VID
    if(!m_vid.empty()) {
      if(m_vid == volumeLabelVSN) {
Victor Kotlyar's avatar
Victor Kotlyar committed
352
        m_log(cta::log::INFO, "Cleaner detected volume label contains expected VSN",
353
          params);
354
      } else {
Victor Kotlyar's avatar
Victor Kotlyar committed
355
        m_log(cta::log::WARNING,
356
          "Cleaner detected volume label does not contain expected VSN", params);
357
      }
358
    }
359
360

    return volumeLabelVSN;
361
362
  } catch(cta::exception::Exception &ne) {
    cta::exception::Exception ex;
363
364
365
366
367
368
369
370
371
    ex.getMessage() << "Failed to check volume label: " << ne.getMessage().str();
    throw ex;
  }
}

//------------------------------------------------------------------------------
// unloadTape
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::CleanerSession::unloadTape(
372
  const std::string &vid, drive::DriveInterface &drive) {
373
  const cta::mediachanger::LibrarySlot &librarySlot = m_driveConfig.librarySlot();
Victor Kotlyar's avatar
Victor Kotlyar committed
374
  std::list<cta::log::Param> params;
375
376
  params.push_back(cta::log::Param("tapeVid", vid));
  params.push_back(cta::log::Param("tapeDrive", m_driveConfig.unitName));
Victor Kotlyar's avatar
Victor Kotlyar committed
377
  params.push_back(cta::log::Param("librarySlot", librarySlot.str()));
378
379
380

  // We implement the same policy as with the tape sessions: 
  // if the librarySlot parameter is "manual", do nothing.
381
  if(cta::mediachanger::TAPE_LIBRARY_TYPE_MANUAL == librarySlot.getLibraryType()) {
Victor Kotlyar's avatar
Victor Kotlyar committed
382
    m_log(cta::log::INFO, "Cleaner not unloading tape because media changer is"
383
384
385
386
387
      " manual", params);
    return;
  }

  try {
Victor Kotlyar's avatar
Victor Kotlyar committed
388
    m_log(cta::log::INFO, "Cleaner unloading tape", params);
389
    drive.unloadTape();
Victor Kotlyar's avatar
Victor Kotlyar committed
390
    m_log(cta::log::INFO, "Cleaner unloaded tape", params);
391
392
  } catch (cta::exception::Exception &ne) {
    cta::exception::Exception ex;
393
394
395
396
397
398
399
400
401
402
403
    ex.getMessage() << "Cleaner failed to unload tape: " <<
      ne.getMessage().str();
    throw ex;
  }
}

//------------------------------------------------------------------------------
// dismountTape
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::CleanerSession::dismountTape(
  const std::string &vid) {
404
  const cta::mediachanger::LibrarySlot &librarySlot = m_driveConfig.librarySlot();
Victor Kotlyar's avatar
Victor Kotlyar committed
405
  std::list<cta::log::Param> params;
406
407
  params.push_back(cta::log::Param("tapeVid", vid));
  params.push_back(cta::log::Param("tapeDrive", m_driveConfig.unitName));
Victor Kotlyar's avatar
Victor Kotlyar committed
408
  params.push_back(cta::log::Param("librarySlot", librarySlot.str()));
409
410

  try {
411
    m_mc.dismountTape(vid, librarySlot);
412
    const bool dismountWasManual = cta::mediachanger::TAPE_LIBRARY_TYPE_MANUAL ==
413
      librarySlot.getLibraryType();
414
    if(dismountWasManual) {
Victor Kotlyar's avatar
Victor Kotlyar committed
415
      m_log(cta::log::INFO, "Cleaner did not dismount tape because media changer is"
416
417
        " manual", params);
    } else {
Victor Kotlyar's avatar
Victor Kotlyar committed
418
      m_log(cta::log::INFO, "Cleaner dismounted tape", params);
419
    }
420
421
  } catch(cta::exception::Exception &ne) {
    cta::exception::Exception ex;
422
423
424
    ex.getMessage() << "Cleaner failed to dismount tape: " <<
      ne.getMessage().str();
    throw ex;
425
426
  }
}