diff --git a/objectstore/CMakeLists.txt b/objectstore/CMakeLists.txt index 7e7fb2ec98c44316a60634785b4af26e1abf7aef..9e5a4dc6e38d5c624623393d6b7956c3684d3191 100644 --- a/objectstore/CMakeLists.txt +++ b/objectstore/CMakeLists.txt @@ -18,44 +18,19 @@ add_library (CTAObjectStore SHARED AgentRegister.cpp AgentWatchdog.cpp TapePool.cpp + Tape.cpp DriveRegister.cpp - #AdminUsersList.cpp BackendVFS.cpp BackendRados.cpp ObjectOps.cpp ProtocolBuffersAlgorithms.cpp - #FIFO.cpp GenericObject.cpp GarbageCollector.cpp) -# add_executable(tapeResourceManagerTest tapeResourceManagerTest.cpp) -# target_link_libraries(tapeResourceManagerTest -# protobuf rados CTAObjectStore) -# -# add_executable(dumpStructure dumpStructure.cpp) -# target_link_libraries(dumpStructure -# protobuf rados CTAObjectStore) -# -# add_executable(jobPoster jobPoster.cpp) -# target_link_libraries(jobPoster -# protobuf rados CTAObjectStore) -# -# add_executable(recaller recaller.cpp) -# target_link_libraries(recaller -# protobuf rados CTAObjectStore) -# -# add_executable(garbageCollector garbageCollector.cpp) -# target_link_libraries(garbageCollector -# protobuf rados CTAObjectStore) -# -# add_executable(createEnvironment createEnvironment.cpp) -# target_link_libraries(createEnvironment -# protobuf rados CTAObjectStore) - set(ObjectStoreUnitTests BackendTest.cpp RootEntryTest.cpp - #FIFOTest.cpp + TapeTest.cpp GarbageCollectorTest.cpp ) diff --git a/objectstore/CreationLog.hpp b/objectstore/CreationLog.hpp index 1045c43382eeea4d4d988734942d23bbf83957b6..49a1fce33f68f50fd24cb45e5212afd6e5677b09 100644 --- a/objectstore/CreationLog.hpp +++ b/objectstore/CreationLog.hpp @@ -16,6 +16,8 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#pragma once + #include "common/UserIdentity.hpp" #include "objectstore/cta.pb.h" #include "scheduler/CreationLog.hpp" diff --git a/objectstore/ObjectOps.cpp b/objectstore/ObjectOps.cpp index 8aaba7ff562b37e56fde88b8ded176359a33bc6f..16032a44c3c8fc2d0716053e80bfca78063442c1 100644 --- a/objectstore/ObjectOps.cpp +++ b/objectstore/ObjectOps.cpp @@ -23,19 +23,14 @@ namespace cta { namespace objectstore { #define MAKE_CTA_OBJECTSTORE_OBJECTOPS_TYPEID(A) \ template <> const serializers::ObjectType ObjectOps<serializers::A>::typeId = serializers::A##_t + MAKE_CTA_OBJECTSTORE_OBJECTOPS_TYPEID(GenericObject); MAKE_CTA_OBJECTSTORE_OBJECTOPS_TYPEID(RootEntry); MAKE_CTA_OBJECTSTORE_OBJECTOPS_TYPEID(AgentRegister); MAKE_CTA_OBJECTSTORE_OBJECTOPS_TYPEID(Agent); MAKE_CTA_OBJECTSTORE_OBJECTOPS_TYPEID(TapePool); MAKE_CTA_OBJECTSTORE_OBJECTOPS_TYPEID(DriveRegister); -// MAKE_CTA_OBJECTSTORE_OBJECTOPS_TYPEID(JobPool); -// MAKE_CTA_OBJECTSTORE_OBJECTOPS_TYPEID(RecallFIFO); -// MAKE_CTA_OBJECTSTORE_OBJECTOPS_TYPEID(MigrationFIFO); -// MAKE_CTA_OBJECTSTORE_OBJECTOPS_TYPEID(RecallJob); -// MAKE_CTA_OBJECTSTORE_OBJECTOPS_TYPEID(Counter); -// MAKE_CTA_OBJECTSTORE_OBJECTOPS_TYPEID(FIFO); -// MAKE_CTA_OBJECTSTORE_OBJECTOPS_TYPEID(AdminUsersList); + MAKE_CTA_OBJECTSTORE_OBJECTOPS_TYPEID(Tape); #undef MAKE_CTA_OBJECTSTORE_OBJECTOPS_TYPEID }} \ No newline at end of file diff --git a/objectstore/RootEntry.cpp b/objectstore/RootEntry.cpp index 1894993331762c1a4b5c697e76bfa10d9a9b8afd..60524149f130f3918266108718298a015b141839 100644 --- a/objectstore/RootEntry.cpp +++ b/objectstore/RootEntry.cpp @@ -456,6 +456,7 @@ std::string cta::objectstore::RootEntry::addOrGetTapePoolAndCommit(const std::st // Insert the tape pool, then its pointer, with agent intent log update // First generate the intent. We expect the agent to be passed locked. std::string tapePoolAddress = agent.nextId("tapePool"); + // TODO Do we expect the agent to be passed locked or not: to be clarified. ScopedExclusiveLock agl(agent); agent.fetch(); agent.addToOwnership(tapePoolAddress); @@ -762,6 +763,6 @@ std::string cta::objectstore::RootEntry::dump () { // if (m_payload.has_jobpool()) ret << "jobPool=" << m_payload.jobpool() << std::endl; /* if (m_payload.has_driveregister()) ret << "driveRegister=" << m_payload.driveregister() << std::endl; if (m_payload.has_taperegister()) ret << "tapeRegister=" << m_payload.taperegister() << std::endl;*/ - ret << ">>>> Root entry dump start" << std::endl; + ret << ">>>> Root entry dump end" << std::endl; return ret.str(); } diff --git a/objectstore/Tape.cpp b/objectstore/Tape.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dc3b27e9f82bec66084ff12c8efaa91e903726ad --- /dev/null +++ b/objectstore/Tape.cpp @@ -0,0 +1,97 @@ +/* + * 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/>. + */ + +#include "Tape.hpp" +#include "GenericObject.hpp" + +cta::objectstore::Tape::Tape(const std::string& address, Backend& os): + ObjectOps<serializers::Tape>(os, address) { } + +cta::objectstore::Tape::Tape(GenericObject& go): + ObjectOps<serializers::Tape>(go.objectStore()){ + // Here we transplant the generic object into the new object + go.transplantHeader(*this); + // And interpret the header. + getPayloadFromHeader(); +} + +void cta::objectstore::Tape::initialize(const std::string &name) { + ObjectOps<serializers::Tape>::initialize(); + // Set the reguired fields + m_payload.set_vid(name); + m_payload.set_bytesstored(0); + m_payload.set_lastfseq(0); + m_payloadInterpreted = true; +} + + +bool cta::objectstore::Tape::isEmpty() { + checkPayloadReadable(); + return !m_payload.retrievaljobs_size(); +} + +void cta::objectstore::Tape::removeIfEmpty() { + checkPayloadWritable(); + if (!isEmpty()) { + throw NotEmpty("In Tape::removeIfEmpty: trying to remove an tape with retrievals queued"); + } + remove(); +} + +void cta::objectstore::Tape::addStoredData(uint64_t bytes) { + checkPayloadWritable(); + m_payload.set_bytesstored(m_payload.bytesstored()+bytes); +} + +uint64_t cta::objectstore::Tape::getStoredData() { + checkPayloadReadable(); + return m_payload.bytesstored(); +} + +std::string cta::objectstore::Tape::getVid() { + checkPayloadReadable(); + return m_payload.vid(); +} + +std::string cta::objectstore::Tape::dump() { + checkPayloadReadable(); + std::stringstream ret; + ret << "<<<< Tape dump start: vid=" << m_payload.vid() << std::endl; + ret << " lastFseq=" << m_payload.lastfseq() + << " bytesStored=" << m_payload.bytesstored() << std::endl; + ret << " Retrieval jobs queued: " << m_payload.retrievaljobs_size() << std::endl; + if (m_payload.readmounts_size()) { + auto lrm = m_payload.readmounts(0); + ret << " Latest read for mount: " << lrm.host() << " " << lrm.time() << " " + << lrm.drivevendor() << " " << lrm.drivemodel() << " " + << lrm.driveserial() << std::endl; + } + if (m_payload.writemounts_size()) { + auto lwm = m_payload.writemounts(0); + ret << " Latest write for mount: " << lwm.host() << " " << lwm.time() << " " + << lwm.drivevendor() << " " << lwm.drivemodel() << " " + << lwm.driveserial() << std::endl; + } + ret << ">>>> Tape dump end" << std::endl; + return ret.str(); +} + + + + + diff --git a/objectstore/Tape.hpp b/objectstore/Tape.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ca188a48c8e76b66c6f1253bd5680922df72263f --- /dev/null +++ b/objectstore/Tape.hpp @@ -0,0 +1,48 @@ +/* + * 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/>. + */ + +#pragma once + +#include "ObjectOps.hpp" +#include "objectstore/cta.pb.h" + +namespace cta { namespace objectstore { + +class Backend; +class Agent; +class GenericObject; + +class Tape: public ObjectOps<serializers::Tape> { +public: + Tape(const std::string & address, Backend & os); + Tape(GenericObject & go); + void initialize(const std::string & vid); + void garbageCollect(); + bool isEmpty(); + CTA_GENERATE_EXCEPTION_CLASS(NotEmpty); + void removeIfEmpty(); + std::string dump(); + + // -- Stored data counting --------------------------------------------------- + uint64_t getStoredData(); + std::string getVid(); + void setStoredData(uint64_t bytes); + void addStoredData(uint64_t bytes); +}; + +}} \ No newline at end of file diff --git a/objectstore/TapePool.cpp b/objectstore/TapePool.cpp index 56f17bee5f8ac9a2f2d4e1c094edf8de900fee18..53db02bf75f4ac915860c3b779a80468ac3b89e2 100644 --- a/objectstore/TapePool.cpp +++ b/objectstore/TapePool.cpp @@ -18,6 +18,9 @@ #include "TapePool.hpp" #include "GenericObject.hpp" +#include "ProtcolBuffersAlgorithms.hpp" +#include "CreationLog.hpp" +#include "Tape.hpp" cta::objectstore::TapePool::TapePool(const std::string& address, Backend& os): ObjectOps<serializers::TapePool>(os, address) { } @@ -42,6 +45,107 @@ void cta::objectstore::TapePool::initialize(const std::string& name) { m_payloadInterpreted = true; } +namespace { + bool operator == (const std::string & vid, + const cta::objectstore::serializers::TapePointer &t) { + return vid==t.vid(); + } +} + +std::string cta::objectstore::TapePool::addOrGetTapeAndCommit(const std::string& vid, + const std::string& logicalLibraryName, const uint64_t capacityInBytes, + Agent& agent, const cta::CreationLog& creationLog) { + checkPayloadWritable(); + // Check the tape already exists + try { + return serializers::findElement(m_payload.tapes(), vid).address(); + } catch (serializers::NotFound &) {} + // Insert the tape, then its pointer, with agent intent log update + // first generate the intent. We expect the agent to be passed locked. + std::string tapeAddress = agent.nextId(std::string("tape_") + vid + "_"); + // TODO Do we expect the agent to be passed locked or not: to be clarified. + ScopedExclusiveLock agl(agent); + agent.fetch(); + agent.addToOwnership(tapeAddress); + agent.commit(); + // The create the tape object + Tape t(tapeAddress, ObjectOps<serializers::TapePool>::m_objectStore); + t.initialize(vid); + t.setOwner(agent.getAddressIfSet()); + t.setBackupOwner(getAddressIfSet()); + t.insert(); + ScopedExclusiveLock tl(t); + // Now reference the tape in the pool + auto * pt = m_payload.mutable_tapes()->Add(); + pt->set_address(tapeAddress); + pt->set_capacity(capacityInBytes); + pt->set_library(logicalLibraryName); + pt->set_vid(vid); + objectstore::CreationLog oslog(creationLog); + oslog.serialize(*pt->mutable_log()); + commit(); + // Switch the tape ownership + t.setOwner(getAddressIfSet()); + t.commit(); + // Clean up the agent. We're done. + agent.removeFromOwnership(tapeAddress); + agent.commit(); + return tapeAddress; +} + +void cta::objectstore::TapePool::removeTapeAndCommit(const std::string& vid) { + checkPayloadWritable(); + try { + // Find the tape + auto tp = serializers::findElement(m_payload.tapes(), vid); + // Open the tape object + Tape t(tp.address(), m_objectStore); + ScopedExclusiveLock tl(t); + t.fetch(); + // Verify this is the tape we're looking for. + if (t.getVid() != vid) { + std::stringstream err; + err << "Unexpected tape VID found in object pointed to for tape: " + << vid << " found: " << t.getVid(); + throw WrongTape(err.str()); + } + // We can now delete the tape + t.remove(); + // And remove it from our entry + serializers::removeOccurences(m_payload.mutable_tapes(), vid); + // We commit for safety and symmetry with the add operation + commit(); + } catch (serializers::NotFound &) { + // No such tape. Nothing to to. + throw NoSuchTape("In TapePool::removeTapeAndCommit: trying to remove non-existing tape"); + } +} + +auto cta::objectstore::TapePool::dumpTapes() -> std::list<TapeDump>{ + checkPayloadReadable(); + std::list<TapeDump> ret; + auto & tl = m_payload.tapes(); + for (auto t=tl.begin(); t!=tl.end(); t++) { + ret.push_back(TapeDump()); + ret.back().address = t->address(); + ret.back().vid = t->vid(); + ret.back().capacityInBytes = t->capacity(); + ret.back().logicalLibraryName = t->library(); + ret.back().log.deserialize(t->log()); + } + return ret; +} + +std::string cta::objectstore::TapePool::getTapeAddress(const std::string& vid) { + checkPayloadReadable(); + return serializers::findElement(m_payload.tapes(), vid).address(); +} + + + + + + bool cta::objectstore::TapePool::isEmpty() { checkPayloadReadable(); // Check we have no tapes in pool @@ -57,7 +161,7 @@ bool cta::objectstore::TapePool::isEmpty() { void cta::objectstore::TapePool::garbageCollect() { checkPayloadWritable(); if (!isEmpty()) { - throw (NotEmpty("Trying to garbage collect a non-empty AgentRegister: internal error")); + throw (NotEmpty("Trying to garbage collect a non-empty TapePool: internal error")); } remove(); } diff --git a/objectstore/TapePool.hpp b/objectstore/TapePool.hpp index be5a4a9f6edde0fa2b7114fdd20b03be330430d7..e35fb6bdf873e73e803bfb41864858e51779da5b 100644 --- a/objectstore/TapePool.hpp +++ b/objectstore/TapePool.hpp @@ -22,6 +22,9 @@ #include "ObjectOps.hpp" #include <string> #include "objectstore/cta.pb.h" +#include "scheduler/CreationLog.hpp" +#include "CreationLog.hpp" +#include "Agent.hpp" namespace cta { namespace objectstore { @@ -42,6 +45,26 @@ public: void setName(const std::string & name); std::string getName(); + // Tapes management ========================================================== + std::string addOrGetTapeAndCommit(const std::string &vid, + const std::string &logicalLibraryName, const uint64_t capacityInBytes, + Agent & agent, const cta::CreationLog & CreationLog); + CTA_GENERATE_EXCEPTION_CLASS(NoSuchTape); + CTA_GENERATE_EXCEPTION_CLASS(WrongTape); + void removeTapeAndCommit(const std::string &vid); + std::string getTapeAddress(const std::string &vid); + class TapeDump { + public: + std::string vid; + std::string address; + std::string logicalLibraryName; + uint64_t capacityInBytes; + objectstore::CreationLog log; + }; + std::list<TapeDump> dumpTapes(); + + // Archival jobs management ================================================== + // Check that the tape pool is empty (of both tapes and jobs) bool isEmpty(); diff --git a/objectstore/TapeTest.cpp b/objectstore/TapeTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1161a89087e4d0743fb3bd56b4a90698244d4b52 --- /dev/null +++ b/objectstore/TapeTest.cpp @@ -0,0 +1,52 @@ +/* + * 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/>. + */ + +#include <gtest/gtest.h> +#include "Tape.hpp" +#include "BackendVFS.hpp" +#include "Agent.hpp" + +namespace unitTests { + +TEST(Tape, BasicAccess) { + cta::objectstore::BackendVFS be; + cta::objectstore::Agent agent(be); + agent.generateName("unitTest"); + std::string tapeAddress = agent.nextId("Tape"); + { + // Try to create the tape entry + cta::objectstore::Tape t(tapeAddress, be); + t.initialize("V12345"); + t.insert(); + } + { + // Try to read back and dump the tape + cta::objectstore::Tape t(tapeAddress, be); + ASSERT_THROW(t.fetch(), cta::exception::Exception); + cta::objectstore::ScopedSharedLock lock(t); + ASSERT_NO_THROW(t.fetch()); + t.dump(); + } + // Delete the root entry + cta::objectstore::Tape t(tapeAddress, be); + cta::objectstore::ScopedExclusiveLock lock(t); + t.fetch(); + t.removeIfEmpty(); + ASSERT_EQ(false, t.exists()); +} +} \ No newline at end of file diff --git a/objectstore/cta.proto b/objectstore/cta.proto index 1ff1efc5c16e8af423aa7ff39c0ac97a3766bf9a..1974b0c7d5968e6be3b5c165c372a8cc030beb4a 100644 --- a/objectstore/cta.proto +++ b/objectstore/cta.proto @@ -8,14 +8,7 @@ enum ObjectType { Agent_t = 2; TapePool_t = 3; DriveRegister_t = 4; -// JobPool_t = 3; -// RecallFIFO_t = 4; -// MigrationFIFO_t = 5; -// RecallJob_t = 6; -// Counter_t = 7; -// FIFO_t = 8; -// AdminUsersList_t = 9; -// StorageClassList_t = 10; + Tape_t = 5; GenericObject_t = 1000; } @@ -149,13 +142,6 @@ message Agent { repeated string ownedobjects = 2003; } -////// The registers (simple name arrays) -////message Register { -//// repeated string elements = 150; -////} -//// -// The agent register holds 2 lists: -// a full list, and a list of agents not yet watched message AgentRegister { repeated string agents = 2100; repeated string untrackedagents = 2101; @@ -168,12 +154,19 @@ message ArchivalJobPointer { required string address = 3002; } +message RetrievalJobPointer { + required uint64 size = 3001; + required string address = 3002; +} + // ------------- Tape pools --------------------------------------------------- message TapePointer { - required string name = 4000; + required string vid = 4000; required string address = 4001; - required CreationLog log = 4002; + required string library = 4002; + required uint64 capacity = 4003; + required CreationLog log = 4004; } message TapePool { @@ -183,131 +176,30 @@ message TapePool { required uint64 ArchivalJobsTotalSize = 4103; } +// ------------- Tape ---------------------------------------------------------- + +message MountInfo { + required string host = 4200; + required string drivevendor = 4201; + required string drivemodel = 4202; + required string driveserial = 4203; + required uint64 time = 4204; +} + +message Tape { + required string vid = 4300; + required uint64 lastfseq = 4301; + required uint64 bytesstored = 4302; + repeated MountInfo readmounts = 4303; + repeated MountInfo writemounts = 4304; + repeated RetrievalJobPointer retrievaljobs = 4305; +} + // ------------- Drives handling ---------------------------------------------- message DriveRegister { repeated string drivenames = 5000; } -//// -////// A basic FIFO -////// poping is done by increasing the read pointer, and from time to time -////// collapsing the name array. -////// There is no write pointer because we always append at the end of the name -////// array. -////message FIFO { -//// required uint64 readPointer = 200; -//// repeated string name = 201; -////} -//// -////// A basic shared counter -////message Counter { -//// required uint64 count = 300; -////} -//// -////// The agents's elements: -////message ObjectCreationIntent { -//// required ObjectType type = 1101; -//// required string name = 1102; -//// required string container = 1103; -////} -//// -////message ObjectOwnershipIntent { -//// required ObjectType type = 1111; -//// required string name = 1112; -////} -//// -////// The tape record -////message Tape { -//// required string type = 2001; -//// required string format = 2002; -//// required string vid = 2003; -//// required uint64 maxFseq = 2004; -//// required string status = 2005 ; -////} -//// -////// The drive record -////message Drive { -//// required string name = 3001; -//// required string status = 3002; -////} -//// -////// The jobs -////message MigrationJob { -//// required string owner = 4001; -//// required string status = 4002; -//// required string source = 4003; -//// required string destination = 4004; -////} -//// -////message RecallJob { -//// required string owner = 5001; -//// required string status = 5002; -//// required string source = 5003; -//// required string destination = 5004; -////} -//// -////// The job pool -////message JobPool { -//// required string migration = 7001; -//// required string recall = 7002; -//// required string recallcounter = 7003; -////} -//// -////message RecallFIFO {} -//// -////message MigrationFIFO {} -//// -////message UserIdentity { -//// required uint32 uid = 8001; -//// required uint32 gid = 8002; -////} -//// -////message AdminUser { -//// required UserIdentity user = 8010; -//// required UserIdentity creator = 8011; -//// required uint64 creationTime = 8012; -//// required string comment = 8013; -////} -//// -////// AdminHosts are just strings -//// -////message ArchivalRoute { -//// required uint32 copynb = 8100; -//// required string tapepool = 8101; -////} -//// -////message StorageClass { -//// required string name = 8200; -//// required uint32 nbcopies = 8201; -//// repeated ArchivalRoute routes = 8202; -////} -//// -////message Tape { -//// required uint64 capacity = 8300; -//// required uint64 dataontape = 8301; -//// required string logicallibrary = 8302; -////} -//// -////message TapePool { -//// -////} -//// -//// -//// -////message ConfigurationItem { -//// required UserIdentity creator = 9002; -//// optional string comment = 9003; -////} -//// -////message StorageClass { -//// required string name = 9000; -//// required uint32 nbCopies = 9001; -//// -////} -//// -////message StorageClassList { -//// repeated StorageClass element = 9050; -////} diff --git a/scheduler/MockSchedulerDatabase.cpp b/scheduler/MockSchedulerDatabase.cpp index 1e95dbf3225b80405b34b964dc3f388a92f862ff..cf9bf81ed3c91d266d657eeab2fe73768b22195c 100644 --- a/scheduler/MockSchedulerDatabase.cpp +++ b/scheduler/MockSchedulerDatabase.cpp @@ -578,7 +578,7 @@ cta::Tape cta::MockSchedulerDatabase::getTape(const std::string &vid) const { std::ostringstream query; cta::Tape tape; query << "SELECT VID, LOGICALLIBRARY, TAPEPOOL, CAPACITY_BYTES," - " DATAONTAPE_BYTES, UID, GID, CREATIONTIME, COMMENT FROM TAPE WHERE VID='" + " DATAONTAPE_BYTES, UID, GID, HOST, CREATIONTIME, COMMENT FROM TAPE WHERE VID='" << vid << "';"; sqlite3_stmt *s = NULL; const int rc = sqlite3_prepare(m_dbHandle, query.str().c_str(), -1, &s, 0 ); @@ -598,10 +598,11 @@ cta::Tape cta::MockSchedulerDatabase::getTape(const std::string &vid) const { (char *)sqlite3_column_text(statement.get(),idx("TAPEPOOL")), (uint64_t)sqlite3_column_int64(statement.get(),idx("CAPACITY_BYTES")), (uint64_t)sqlite3_column_int64(statement.get(),idx("DATAONTAPE_BYTES")), - UserIdentity(sqlite3_column_int(statement.get(),idx("UID")), - sqlite3_column_int(statement.get(),idx("GID"))), - (char *)sqlite3_column_text(statement.get(),idx("COMMENT")), - time_t(sqlite3_column_int(statement.get(),idx("CREATIONTIME"))) + CreationLog(UserIdentity(sqlite3_column_int(statement.get(),idx("UID")), + sqlite3_column_int(statement.get(),idx("GID"))), + (char *)sqlite3_column_text(statement.get(),idx("COMMENT")), + time_t(sqlite3_column_int(statement.get(),idx("CREATIONTIME"))), + (char *)sqlite3_column_text(statement.get(),idx("COMMENT"))) ); } break; @@ -1374,20 +1375,20 @@ std::list<cta::LogicalLibrary> cta::MockSchedulerDatabase::getLogicalLibraries() // createTape //------------------------------------------------------------------------------ void cta::MockSchedulerDatabase::createTape( - const SecurityIdentity &requester, const std::string &vid, const std::string &logicalLibraryName, const std::string &tapePoolName, const uint64_t capacityInBytes, - const std::string &comment) { + const CreationLog &creationLog) { char *zErrMsg = 0; std::ostringstream query; query << "INSERT INTO TAPE(VID, LOGICALLIBRARY, TAPEPOOL," - " CAPACITY_BYTES, DATAONTAPE_BYTES, UID, GID, CREATIONTIME, COMMENT)" + " CAPACITY_BYTES, DATAONTAPE_BYTES, UID, GID, HOST, CREATIONTIME, COMMENT)" " VALUES('" << vid << "','" << logicalLibraryName << "','" << tapePoolName << "',"<< (long unsigned int)capacityInBytes << ",0," << - requester.getUser().uid << "," << requester.getUser().gid << "," - << (int)time(NULL) << ",'" << comment << "');"; + creationLog.user.uid << "," << creationLog.user.gid << "," + << "'" << creationLog.host << "', " << creationLog.time << ",'" + << creationLog.comment << "');"; if(SQLITE_OK != sqlite3_exec(m_dbHandle, query.str().c_str(), 0, 0, &zErrMsg)) { std::ostringstream msg; @@ -1429,7 +1430,7 @@ std::list<cta::Tape> cta::MockSchedulerDatabase::getTapes() const { std::ostringstream query; std::list<cta::Tape> tapes; query << "SELECT VID, LOGICALLIBRARY, TAPEPOOL, CAPACITY_BYTES," - " DATAONTAPE_BYTES, UID, GID, CREATIONTIME, COMMENT" + " DATAONTAPE_BYTES, UID, GID, HOST, CREATIONTIME, COMMENT" " FROM TAPE ORDER BY VID;"; sqlite3_stmt *s = NULL; const int rc = sqlite3_prepare(m_dbHandle, query.str().c_str(), -1, &s, 0 ); @@ -1447,10 +1448,11 @@ std::list<cta::Tape> cta::MockSchedulerDatabase::getTapes() const { (char *)sqlite3_column_text(statement.get(),idx("TAPEPOOL")), (uint64_t)sqlite3_column_int64(statement.get(),idx("CAPACITY_BYTES")), (uint64_t)sqlite3_column_int64(statement.get(),idx("DATAONTAPE_BYTES")), - UserIdentity(sqlite3_column_int(statement.get(),idx("UID")), - sqlite3_column_int(statement.get(),idx("GID"))), - (char *)sqlite3_column_text(statement.get(),idx("COMMENT")), - time_t(sqlite3_column_int(statement.get(),idx("CREATIONTIME"))) + CreationLog(UserIdentity(sqlite3_column_int(statement.get(),idx("UID")), + sqlite3_column_int(statement.get(),idx("GID"))), + (char *)sqlite3_column_text(statement.get(),idx("COMMENT")), + time_t(sqlite3_column_int(statement.get(),idx("CREATIONTIME"))), + (char *)sqlite3_column_text(statement.get(),idx("COMMENT"))) )); } return tapes; diff --git a/scheduler/MockSchedulerDatabase.hpp b/scheduler/MockSchedulerDatabase.hpp index 3774ed163c6830d7573708282cdd4ec2eadf34b1..0a1d781e1c78554283731dee3a997130f5c78afd 100644 --- a/scheduler/MockSchedulerDatabase.hpp +++ b/scheduler/MockSchedulerDatabase.hpp @@ -383,21 +383,19 @@ public: /** * Creates a tape. * - * @param requester The identity of the requester. * @param vid The volume identifier of the tape. * @param logicalLibraryName The name of the logical library to which the tape * belongs. * @param tapePoolName The name of the tape pool to which the tape belongs. * @param capacityInBytes The capacity of the tape. - * @param comment The comment describing the logical library. + * @param creationLog The who, where, when an why of this modification. */ - void createTape( - const SecurityIdentity &requester, + virtual void createTape( const std::string &vid, const std::string &logicalLibraryName, const std::string &tapePoolName, const uint64_t capacityInBytes, - const std::string &comment); + const CreationLog &creationLog); /** * Deletes the tape with the specified volume identifier. diff --git a/scheduler/OStoreDB/OStoreDB.cpp b/scheduler/OStoreDB/OStoreDB.cpp index 6a3b00f9a751be26d4e4ea1074423e5f67584fb6..d7f3025644ccdeab963f1c4a8e1bf607ba718972 100644 --- a/scheduler/OStoreDB/OStoreDB.cpp +++ b/scheduler/OStoreDB/OStoreDB.cpp @@ -19,6 +19,8 @@ #include "OStoreDB.hpp" #include "scheduler/SecurityIdentity.hpp" #include "objectstore/RootEntry.hpp" +#include "objectstore/TapePool.hpp" +#include "objectstore/Tape.hpp" #include "common/exception/Exception.hpp" #include "scheduler/AdminHost.hpp" #include "scheduler/AdminUser.hpp" @@ -26,6 +28,7 @@ #include "scheduler/LogicalLibrary.hpp" #include "scheduler/StorageClass.hpp" #include "scheduler/TapePool.hpp" +#include "scheduler/Tape.hpp" #include <algorithm> namespace cta { @@ -259,13 +262,13 @@ void OStoreDB::createTapePool(const std::string& name, re.commit(); } -std::list<TapePool> OStoreDB::getTapePools() const { +std::list<cta::TapePool> OStoreDB::getTapePools() const { RootEntry re(m_objectStore); ScopedSharedLock rel(re); re.fetch(); auto tpd = re.dumpTapePools(); rel.release(); - std::list<TapePool> ret; + std::list<cta::TapePool> ret; for (auto tp=tpd.begin(); tp!=tpd.end(); tp++) { ret.push_back(cta::TapePool(tp->tapePool, tp->nbPartialTapes, tp->log)); } @@ -281,15 +284,62 @@ void OStoreDB::deleteTapePool(const SecurityIdentity& requester, re.commit(); } -void OStoreDB::createTape(const SecurityIdentity& requester, - const std::string& vid, const std::string& logicalLibraryName, +void OStoreDB::createTape(const std::string& vid, + const std::string& logicalLibraryName, const std::string& tapePoolName, const uint64_t capacityInBytes, - const std::string& comment) { - throw exception::Exception("Not Implemented"); -} - -std::list<Tape> OStoreDB::getTapes() const { - throw exception::Exception("Not Implemented"); + const cta::CreationLog& creationLog) { + // To create a tape, we have to + // - Find the storage class and lock for write. + // - Create the tape object. + // - Connect the tape object to the tape pool. + RootEntry re(m_objectStore); + ScopedSharedLock rel(re); + re.fetch(); + // Check the library exists + auto libs = re.dumpLibraries(); + for (auto l=libs.begin(); l!=libs.end(); l++) { + if (l->library == logicalLibraryName) + goto found; + } + throw NoSuchLibrary("In OStoreDB::createTape: trying to create a tape in a non-existing library"); + found: + std::string tpAddress = re.getTapePoolAddress(tapePoolName); + // Take hold of the tape pool + objectstore::TapePool tp(tpAddress, m_objectStore); + ScopedExclusiveLock tpl(tp); + tp.fetch(); + // Check that the tape exists and throw an exception if it does. + try { + tp.getTapeAddress(vid); + throw TapeAlreadyExists("In OStoreDB::createTape: trying to create an existing tape."); + } catch (cta::exception::Exception &) {} + // Create the tape. The tape pool method takes care of the gory details for us. + tp.addOrGetTapeAndCommit(vid, logicalLibraryName, capacityInBytes, + *m_agent, creationLog); + tp.commit(); +} + +std::list<cta::Tape> OStoreDB::getTapes() const { + std::list<cta::Tape> ret; + // Got through all tape pools. Get the list of them + RootEntry re(m_objectStore); + ScopedSharedLock rel(re); + re.fetch(); + auto tpl = re.dumpTapePools(); + for (auto tpi=tpl.begin(); tpi!=tpl.end(); tpi++) { + objectstore::TapePool tp(tpi->address, m_objectStore); + ScopedSharedLock tpl(tp); + tp.fetch(); + auto tl=tp.dumpTapes(); + for (auto ti=tl.begin(); ti!=tl.end(); ti++) { + objectstore::Tape t(ti->address, m_objectStore); + ScopedSharedLock tl(t); + t.fetch(); + ret.push_back(cta::Tape(ti->vid, ti->logicalLibraryName, tpi->tapePool, + ti->capacityInBytes, t.getStoredData(), ti->log)); + } + } + return ret; } void OStoreDB::deleteTape(const SecurityIdentity& requester, @@ -323,6 +373,18 @@ void OStoreDB::deleteLogicalLibrary(const SecurityIdentity& requester, const std::string& name) { RootEntry re(m_objectStore); ScopedExclusiveLock rel(re); + // Check we are not deleting a non-empty library + auto tpl = re.dumpTapePools(); + for (auto tpp=tpl.begin(); tpp!=tpl.end(); tpp++) { + objectstore::TapePool tp(tpp->address, m_objectStore); + ScopedSharedLock tplock(tp); + tp.fetch(); + auto tl=tp.dumpTapes(); + for (auto t=tl.begin(); t!=tl.end(); t++) { + if (t->logicalLibraryName == name) + throw LibraryInUse("In OStoreDB::deleteLogicalLibrary: trying to delete a library used by a tape."); + } + } re.fetch(); re.removeLibrary(name); re.commit(); @@ -346,7 +408,7 @@ void OStoreDB::markArchiveRequestForDeletion(const SecurityIdentity& requester, throw exception::Exception("Not Implemented"); } -std::map<TapePool, std::list<ArchiveToTapeCopyRequest> > +std::map<cta::TapePool, std::list<ArchiveToTapeCopyRequest> > OStoreDB::getArchiveRequests() const { throw exception::Exception("Not Implemented"); } @@ -368,7 +430,7 @@ std::list<RetrieveFromTapeCopyRequest> OStoreDB::getRetrieveRequests(const std:: throw exception::Exception("Not Implemented"); } -std::map<Tape, std::list<RetrieveFromTapeCopyRequest> > OStoreDB::getRetrieveRequests() const { +std::map<cta::Tape, std::list<RetrieveFromTapeCopyRequest> > OStoreDB::getRetrieveRequests() const { throw exception::Exception("Not Implemented"); } diff --git a/scheduler/OStoreDB/OStoreDB.hpp b/scheduler/OStoreDB/OStoreDB.hpp index fc5b0fd947e701c06a7ebad63b2ae721658475af..e3d247c6e126a13dcc100c938528d5bc62351b99 100644 --- a/scheduler/OStoreDB/OStoreDB.hpp +++ b/scheduler/OStoreDB/OStoreDB.hpp @@ -101,10 +101,11 @@ public: virtual void deleteTapePool(const SecurityIdentity& requester, const std::string& name); /* === Tapes handling ==================================================== */ - virtual void createTape(const SecurityIdentity& requester, - const std::string& vid, const std::string& logicalLibraryName, + CTA_GENERATE_EXCEPTION_CLASS(TapeAlreadyExists); + CTA_GENERATE_EXCEPTION_CLASS(NoSuchLibrary); + virtual void createTape(const std::string& vid, const std::string& logicalLibraryName, const std::string& tapePoolName, const uint64_t capacityInBytes, - const std::string& comment); + const cta::CreationLog& creationLog); virtual std::list<Tape> getTapes() const; @@ -116,6 +117,7 @@ public: virtual std::list<LogicalLibrary> getLogicalLibraries() const; + CTA_GENERATE_EXCEPTION_CLASS(LibraryInUse); virtual void deleteLogicalLibrary(const SecurityIdentity& requester, const std::string& name); /* === Archival requests handling ======================================== */ diff --git a/scheduler/OStoreDB/OStoreDBFactory.hpp b/scheduler/OStoreDB/OStoreDBFactory.hpp index 380e3aebfa2820fb678c2ba88ed98de19b948ab0..0abd0a4d36136175d7528457671107b56838673c 100644 --- a/scheduler/OStoreDB/OStoreDBFactory.hpp +++ b/scheduler/OStoreDB/OStoreDBFactory.hpp @@ -98,8 +98,8 @@ public: m_OStoreDB.createStorageClass(name, nbCopies, creationLog); } - virtual void createTape(const SecurityIdentity& requester, const std::string& vid, const std::string& logicalLibraryName, const std::string& tapePoolName, const uint64_t capacityInBytes, const std::string& comment) { - m_OStoreDB.createTape(requester, vid, logicalLibraryName, tapePoolName, capacityInBytes, comment); + virtual void createTape(const std::string& vid, const std::string& logicalLibraryName, const std::string& tapePoolName, const uint64_t capacityInBytes, const cta::CreationLog & creationLog) { + m_OStoreDB.createTape(vid, logicalLibraryName, tapePoolName, capacityInBytes, creationLog); } virtual void createTapePool(const std::string& name, const uint32_t nbPartialTapes, const CreationLog& creationLog) { diff --git a/scheduler/Scheduler.cpp b/scheduler/Scheduler.cpp index cf0afe480c34b116b5170ed6455e977cf114b77d..89037124c15cadb713717d122894cfac5a5ceb54 100644 --- a/scheduler/Scheduler.cpp +++ b/scheduler/Scheduler.cpp @@ -328,10 +328,10 @@ void cta::Scheduler::createTape( const std::string &logicalLibraryName, const std::string &tapePoolName, const uint64_t capacityInBytes, - const std::string &comment) { + const CreationLog &creationLog) { m_db.assertIsAdminOnAdminHost(requester); - m_db.createTape(requester, vid, logicalLibraryName, tapePoolName, - capacityInBytes, comment); + m_db.createTape(vid, logicalLibraryName, tapePoolName, + capacityInBytes, creationLog); } //------------------------------------------------------------------------------ diff --git a/scheduler/Scheduler.hpp b/scheduler/Scheduler.hpp index a59b8d733a5c09e7592c97950f5a8adf78eaf18e..71ac6e9e5062566b235f6c504ed2e0d550c310ba 100644 --- a/scheduler/Scheduler.hpp +++ b/scheduler/Scheduler.hpp @@ -382,14 +382,13 @@ public: /** * Creates a tape. * - * @param requester The identity of the user requesting the creation of the - * tape. + * @param requester The identity of the requester. * @param vid The volume identifier of the tape. * @param logicalLibraryName The name of the logical library to which the tape * belongs. * @param tapePoolName The name of the tape pool to which the tape belongs. * @param capacityInBytes The capacity of the tape. - * @param comment The comment describing the logical library. + * @param creationLog The who, where, when an why of this modification. */ void createTape( const SecurityIdentity &requester, @@ -397,7 +396,7 @@ public: const std::string &logicalLibraryName, const std::string &tapePoolName, const uint64_t capacityInBytes, - const std::string &comment); + const CreationLog &creationLog); /** * Deletes the tape with the specified volume identifier. diff --git a/scheduler/SchedulerDatabase.hpp b/scheduler/SchedulerDatabase.hpp index 4a6e13fd2be0126eab94d70a649d1d779660194e..d34ac6c5475acbaa7e63f4033c5583930b938d3a 100644 --- a/scheduler/SchedulerDatabase.hpp +++ b/scheduler/SchedulerDatabase.hpp @@ -397,21 +397,19 @@ public: /** * Creates a tape. * - * @param requester The identity of the requester. * @param vid The volume identifier of the tape. * @param logicalLibraryName The name of the logical library to which the tape * belongs. * @param tapePoolName The name of the tape pool to which the tape belongs. * @param capacityInBytes The capacity of the tape. - * @param comment The comment describing the logical library. + * @param creationLog The who, where, when an why of this modification. */ virtual void createTape( - const SecurityIdentity &requester, const std::string &vid, const std::string &logicalLibraryName, const std::string &tapePoolName, const uint64_t capacityInBytes, - const std::string &comment) = 0; + const CreationLog &creationLog) = 0; /** * Deletes the tape with the specified volume identifier. diff --git a/scheduler/SchedulerTest.cpp b/scheduler/SchedulerTest.cpp index 1ea6f1c45ffefbe1bb9c6c73a287d6de54da832a..c3ec14680f86ad8a151f3ca4452a6dbaf6e9ff9f 100644 --- a/scheduler/SchedulerTest.cpp +++ b/scheduler/SchedulerTest.cpp @@ -826,8 +826,10 @@ TEST_P(SchedulerTest, admin_createTape_new) { const std::string vid = "TestVid"; const uint64_t capacityInBytes = 12345678; const std::string tapeComment = "Tape comment"; + CreationLog log(s_adminOnAdminHost.getUser(), s_adminOnAdminHost.getHost(), + time(NULL), tapeComment); ASSERT_NO_THROW(scheduler.createTape(s_adminOnAdminHost, vid, libraryName, tapePoolName, - capacityInBytes, tapeComment)); + capacityInBytes, log)); { std::list<Tape> tapes; ASSERT_NO_THROW(tapes = scheduler.getTapes(s_adminOnAdminHost)); @@ -840,7 +842,7 @@ TEST_P(SchedulerTest, admin_createTape_new) { ASSERT_EQ(tapePoolName, tape.getTapePoolName()); ASSERT_EQ(capacityInBytes, tape.getCapacityInBytes()); ASSERT_EQ(0, tape.getDataOnTapeInBytes()); - ASSERT_EQ(tapeComment, tape.getComment()); + ASSERT_EQ(tapeComment, tape.getCreationLog().comment); } } @@ -891,8 +893,10 @@ TEST_P(SchedulerTest, const std::string vid = "TestVid"; const uint64_t capacityInBytes = 12345678; const std::string tapeComment = "Tape comment"; + CreationLog log(s_adminOnAdminHost.getUser(), s_adminOnAdminHost.getHost(), + time(NULL), tapeComment); ASSERT_THROW(scheduler.createTape(s_adminOnAdminHost, vid, libraryName, tapePoolName, - capacityInBytes, tapeComment), std::exception); + capacityInBytes, log), std::exception); } TEST_P(SchedulerTest, admin_createTape_new_non_existing_pool) { @@ -939,8 +943,10 @@ TEST_P(SchedulerTest, admin_createTape_new_non_existing_pool) { const std::string vid = "TestVid"; const uint64_t capacityInBytes = 12345678; const std::string tapeComment = "Tape comment"; + CreationLog log(s_adminOnAdminHost.getUser(), s_adminOnAdminHost.getHost(), + time(NULL), tapeComment); ASSERT_THROW(scheduler.createTape(s_adminOnAdminHost, vid, libraryName, tapePoolName, - capacityInBytes, tapeComment), std::exception); + capacityInBytes, log), std::exception); } TEST_P(SchedulerTest, getDirContents_root_dir_is_empty) { @@ -2276,8 +2282,10 @@ TEST_P(SchedulerTest, archive_and_retrieve_new_file) { const std::string vid = "TestVid"; const uint64_t capacityInBytes = 12345678; const std::string tapeComment = "Tape comment"; - ASSERT_NO_THROW(scheduler.createTape(s_adminOnAdminHost, vid, libraryName, tapePoolName, - capacityInBytes, tapeComment)); + CreationLog log(s_adminOnAdminHost.getUser(), s_adminOnAdminHost.getHost(), + time(NULL), tapeComment); + ASSERT_NO_THROW(scheduler.createTape(s_adminOnAdminHost, vid, libraryName, + tapePoolName, capacityInBytes, log)); const uint16_t copyNb = 1; const std::string archivalRouteComment = "Archival-route comment"; diff --git a/scheduler/Tape.cpp b/scheduler/Tape.cpp index 305a3914f4428c389b8e33ea2c497c24bd154373..784cecaa62bfec456b5e8c2da93df96f2790cbc6 100644 --- a/scheduler/Tape.cpp +++ b/scheduler/Tape.cpp @@ -41,15 +41,13 @@ cta::Tape::Tape( const std::string &tapePoolName, const uint64_t capacityInBytes, const uint64_t dataOnTapeInBytes, - const UserIdentity &creator, - const std::string &comment, - const time_t creationTime): - ConfigurationItem(creator, comment, creationTime), + const CreationLog & creationLog): m_vid(vid), m_logicalLibraryName(logicalLibraryName), m_tapePoolName(tapePoolName), m_capacityInBytes(capacityInBytes), - m_dataOnTapeInBytes(dataOnTapeInBytes) { + m_dataOnTapeInBytes(dataOnTapeInBytes), + m_creationLog(creationLog){ } //------------------------------------------------------------------------------ @@ -93,3 +91,10 @@ uint64_t cta::Tape::getCapacityInBytes() const throw() { uint64_t cta::Tape::getDataOnTapeInBytes() const throw() { return m_dataOnTapeInBytes; } + +//------------------------------------------------------------------------------ +// getCreationLog +//------------------------------------------------------------------------------ +auto cta::Tape::getCreationLog() const throw() -> const CreationLog & { + return m_creationLog; +} \ No newline at end of file diff --git a/scheduler/Tape.hpp b/scheduler/Tape.hpp index d3a64af08f4643bc080f80bc6f33a2f17652a341..82656855359f9624001303a5fecca3918e46bf03 100644 --- a/scheduler/Tape.hpp +++ b/scheduler/Tape.hpp @@ -19,7 +19,7 @@ #pragma once #include "common/UserIdentity.hpp" -#include "scheduler/ConfigurationItem.hpp" +#include "scheduler/CreationLog.hpp" #include <stdint.h> #include <string> @@ -29,7 +29,7 @@ namespace cta { /** * Class representing a tape. */ -class Tape: public ConfigurationItem { +class Tape { public: /** @@ -52,12 +52,7 @@ public: * @param capacityInBytes The capacity of the tape. * @param dataOnTapeInBytes The amount of data currently stored on the tape in * bytes. - * @param creator The identity of the user that created this configuration - * item. - * @param comment The comment describing this configuration item. - * @param creationTime Optionally the absolute time at which this - * configuration item was created. If no value is given then the current - * time is used. + * @param creationLog The who, where, when an why of this modification. */ Tape( const std::string &vid, @@ -65,9 +60,7 @@ public: const std::string &tapePoolName, const uint64_t capacityInBytes, const uint64_t dataOnTapeInBytes, - const UserIdentity &creator, - const std::string &comment, - const time_t creationTime = time(NULL)); + const CreationLog &creationLog); /** * Less than operator. @@ -111,6 +104,13 @@ public: */ uint64_t getDataOnTapeInBytes() const throw(); + + /** + * Get the creation log + * @return Reference to the creation log + */ + const CreationLog & getCreationLog() const throw(); + private: /** @@ -138,6 +138,10 @@ private: */ uint64_t m_dataOnTapeInBytes; + /** + * The record of the entry's creation + */ + CreationLog m_creationLog; }; // class Tape } // namespace cta diff --git a/xroot_plugins/XrdProFile.cpp b/xroot_plugins/XrdProFile.cpp index a679169967b1ae44fdc67aa2b246b6ab0c41bca9..16faa3983c3b70b9e6286879b51fe0949180a24f 100644 --- a/xroot_plugins/XrdProFile.cpp +++ b/xroot_plugins/XrdProFile.cpp @@ -796,7 +796,8 @@ void XrdProFile::xCom_tape(const std::vector<std::string> &tokens, const cta::Se std::istringstream capacity_ss(capacity_s); uint64_t capacity = 0; capacity_ss >> capacity; - m_scheduler->createTape(requester, vid, logicalLibrary, tapePool, capacity, comment); + cta::CreationLog log(requester.getUser(), requester.getHost(), time(NULL), comment); + m_scheduler->createTape(requester, vid, logicalLibrary, tapePool, capacity, log); } else if("ch" == tokens[2]) { std::string vid = getOptionValue(tokens, "-v", "--vid"); @@ -830,10 +831,11 @@ void XrdProFile::xCom_tape(const std::vector<std::string> &tokens, const cta::Se << " " << it->getCapacityInBytes() << " " << it->getTapePoolName() << " " << it->getDataOnTapeInBytes() - << " " << it->getCreator().uid - << " " << it->getCreator().gid - << " " << it->getCreationTime() - << " " << it->getComment() << std::endl; + << " " << it->getCreationLog().user.uid + << " " << it->getCreationLog().user.gid + << " " << it->getCreationLog().host + << " " << it->getCreationLog().time + << " " << it->getCreationLog().comment << std::endl; } m_data = responseSS.str(); }