Commit ac22ba37 authored by Michael Davis's avatar Michael Davis
Browse files

[CTA-EOS] Merge from master

parent d2d17725
......@@ -37,10 +37,10 @@ BuildRequires: xrootd-client-devel = 1:4.2.3
BuildRequires: xrootd-devel = 1:4.2.3
BuildRequires: cryptopp-devel >= 5.6.2
%else
BuildRequires: xrootd-client-devel >= 20170421
BuildRequires: xrootd-devel >= 20170421
BuildRequires: xrootd-server-devel >= 20170421
BuildRequires: xrootd-private-devel >= 20170421
BuildRequires: xrootd-client-devel = 1:20170801.af5dd76
BuildRequires: xrootd-devel = 1:20170801.af5dd76
BuildRequires: xrootd-server-devel = 1:20170801.af5dd76
BuildRequires: xrootd-private-devel = 1:20170801.af5dd76
BuildRequires: librados-devel >= 11.0, libradosstriper-devel >= 11.0,
BuildRequires: protobuf3-compiler >= 3.3.1 protobuf3-devel >= 3.3.1
BuildRequires: gmock-devel >= 1.5.0 gtest-devel >= 1.5.0
......
......@@ -130,16 +130,6 @@ void cta::objectstore::ArchiveRequest::setAllJobsFailed() {
}
}
void cta::objectstore::ArchiveRequest::setAllJobsPendingNSdeletion() {
checkPayloadWritable();
auto * jl=m_payload.mutable_jobs();
for (auto j=jl->begin(); j!=jl->end(); j++) {
j->set_status(serializers::AJS_PendingNsDeletion);
}
}
void ArchiveRequest::setArchiveFile(const cta::common::dataStructures::ArchiveFile& archiveFile) {
checkPayloadWritable();
// TODO: factor out the archivefile structure from the flat ArchiveRequest.
......@@ -295,6 +285,7 @@ void ArchiveRequest::garbageCollect(const std::string &presumedOwner, AgentRefer
// of a selected job. The Request could also still being connected to tape
// pools. In this case we will finish the connection to tape pools unconditionally.
auto * jl = m_payload.mutable_jobs();
bool anythingGarbageCollected=false;
for (auto j=jl->begin(); j!=jl->end(); j++) {
auto owner=j->owner();
auto status=j->status();
......@@ -307,12 +298,15 @@ void ArchiveRequest::garbageCollect(const std::string &presumedOwner, AgentRefer
// (Re)connect the job to the tape pool and make it pending.
// If we fail to reconnect, we have to fail the job and potentially
// finish the request.
std::string queueObject="Not defined yet";
anythingGarbageCollected=true;
try {
// Get the queue where we should requeue the job. The queue might need to be
// recreated (this will be done by helper).
ArchiveQueue aq(m_objectStore);
ScopedExclusiveLock tpl;
Helpers::getLockedAndFetchedQueue<ArchiveQueue>(aq, tpl, agentReference, j->tapepool(), lc);
queueObject=aq.getAddressIfSet();
ArchiveRequest::JobDump jd;
jd.copyNb = j->copynb();
jd.tapePool = j->tapepool();
......@@ -323,66 +317,53 @@ void ArchiveRequest::garbageCollect(const std::string &presumedOwner, AgentRefer
j->set_owner(aq.getAddressIfSet());
j->set_status(serializers::AJS_PendingMount);
commit();
log::ScopedParamContainer params(lc);
params.add("jobObject", getAddressIfSet())
.add("queueObject", queueObject)
.add("presumedOwner", presumedOwner)
.add("copyNb", j->copynb());
lc.log(log::INFO, "In ArchiveRequest::garbageCollect(): requeued job.");
} catch (...) {
// We could not requeue the job: fail it.
j->set_status(serializers::AJS_Failed);
log::ScopedParamContainer params(lc);
params.add("jobObject", getAddressIfSet())
.add("queueObject", queueObject)
.add("presumedOwner", presumedOwner)
.add("copyNb", j->copynb());
// Log differently depending on the exception type.
std::string backtrace = "";
try {
std::rethrow_exception(std::current_exception());
} catch (cta::exception::Exception &ex) {
params.add("exceptionMessage", ex.getMessageValue());
backtrace = ex.backtrace();
} catch (std::exception & ex) {
params.add("exceptionWhat", ex.what());
} catch (...) {
params.add("exceptionType", "unknown");
}
// This could be the end of the request, with various consequences.
// This is handled here:
if (finishIfNecessary())
return;
}
} else if (status==serializers::AJS_PendingNsCreation) {
// If the job is pending NsCreation, we have to queue it in the tape pool's
// queue for files orphaned pending ns creation. Some user process will have
// to pick them up actively (recovery involves schedulerDB + NameServerDB)
try {
ArchiveQueue aq(j->owner(), m_objectStore);
ScopedExclusiveLock tpl(aq);
aq.fetch();
ArchiveRequest::JobDump jd;
jd.copyNb = j->copynb();
jd.tapePool = j->tapepool();
jd.owner = j->owner();
if (aq.addOrphanedJobPendingNsCreation(jd, getAddressIfSet(),
m_payload.archivefileid(), m_payload.filesize(), getMountPolicy()))
aq.commit();
j->set_owner(aq.getAddressIfSet());
commit();
} catch (...) {
j->set_status(serializers::AJS_Failed);
// This could be the end of the request, with various consequences.
// This is handled here:
if (finishIfNecessary())
return;
}
} else if (status==serializers::AJS_PendingNsDeletion) {
// If the job is pending NsDeletion, we have to queue it in the tape pool's
// queue for files orphaned pending ns deletion. Some user process will have
// to pick them up actively (recovery involves schedulerDB + NameServerDB)
try {
ArchiveQueue aq(j->owner(), m_objectStore);
ScopedExclusiveLock tpl(aq);
aq.fetch();
ArchiveRequest::JobDump jd;
jd.copyNb = j->copynb();
jd.tapePool = j->tapepool();
jd.owner = j->owner();
if (aq.addOrphanedJobPendingNsCreation(jd, getAddressIfSet(),
m_payload.archivefileid(), m_payload.filesize(), getMountPolicy()))
aq.commit();
j->set_owner(aq.getAddressIfSet());
j->set_status(serializers::AJS_PendingMount);
commit();
} catch (...) {
j->set_status(serializers::AJS_Failed);
// This could be the end of the request, with various consequences.
// This is handled here:
if (finishIfNecessary())
if (finishIfNecessary()) {
std::string message="In ArchiveRequest::garbageCollect(): failed to requeue the job. Failed it and removed the request as a consequence.";
if (backtrace.size()) message += " Backtrace follows.";
lc.log(log::ERR, message);
if (backtrace.size()) lc.logBacktrace(log::ERR, backtrace);
return;
} else {
commit();
lc.log(log::ERR, "In ArchiveRequest::garbageCollect(): failed to requeue the job and failed it.");
}
}
} else {
return;
}
}
if (!anythingGarbageCollected) {
log::ScopedParamContainer params(lc);
params.add("jobObject", getAddressIfSet())
.add("presumedOwner", presumedOwner);
lc.log(log::INFO, "In ArchiveRequest::garbageCollect(): nothing to garbage collect.");
}
}
void ArchiveRequest::setJobOwner(
......
......@@ -55,8 +55,6 @@ public:
void setAllJobsLinkingToArchiveQueue();
// Mark all the jobs as being deleted, in case of a cancellation
void setAllJobsFailed();
// Mark all the jobs as pending deletion from NS.
void setAllJobsPendingNSdeletion();
CTA_GENERATE_EXCEPTION_CLASS(NoSuchJob);
// Set a job ownership
void setJobOwner(uint16_t copyNumber, const std::string & owner);
......
......@@ -114,15 +114,18 @@ void Helpers::getLockedAndFetchedQueue<ArchiveQueue>(ArchiveQueue& archiveQueue,
.add("queueLockTime", queueLockTime)
.add("queueFetchTime", queueFetchTime);
lc.log(log::ERR, "In Helpers::getLockedAndFetchedQueue<ArchiveQueue>(): failed to fetch an existing queue. Retrying.");
archiveQueue.resetAddress();
continue;
} catch (...) {
// Also release the lock if needed here.
if (archiveQueueLock.isLocked()) archiveQueueLock.release();
archiveQueue.resetAddress();
throw;
}
}
// Also release the lock if needed here.
if (archiveQueueLock.isLocked()) archiveQueueLock.release();
archiveQueue.resetAddress();
throw cta::exception::Exception(std::string(
"In OStoreDB::getLockedAndFetchedArchiveQueue(): failed to find or create and lock archive queue after 5 retries for tapepool: ")
+ tapePool);
......
......@@ -52,6 +52,7 @@ public:
CTA_GENERATE_EXCEPTION_CLASS(AddressAlreadySet);
CTA_GENERATE_EXCEPTION_CLASS(InvalidAddress);
CTA_GENERATE_EXCEPTION_CLASS(FailedToSerialize);
CTA_GENERATE_EXCEPTION_CLASS(StillLocked);
protected:
void checkHeaderWritable() {
if (!m_headerInterpreted)
......@@ -91,13 +92,24 @@ public:
void setAddress(const std::string & name) {
if (m_nameSet)
throw AddressAlreadySet("In ObjectOps::setName: trying to overwrite an already set name");
throw AddressAlreadySet("In ObjectOps::setAddress(): trying to overwrite an already set name");
if (name.empty())
throw InvalidAddress("In ObjectOps::setName: empty name");
throw InvalidAddress("In ObjectOps::setAddress(): empty name");
m_name = name;
m_nameSet = true;
}
void resetAddress() {
if (m_locksCount || m_locksForWriteCount) {
throw StillLocked("In ObjectOps::resetAddress: reset the address of a locked object");
}
m_nameSet = false;
m_name = "";
m_headerInterpreted = false;
m_payloadInterpreted = false;
m_existingObject = false;
}
std::string & getAddressIfSet() {
if (!m_nameSet) {
throw AddressNotSet("In ObjectOpsBase::getNameIfSet: name not set yet");
......
......@@ -206,10 +206,8 @@ message ArchiveFile {
// This life cycle represented by the following enum
enum ArchiveJobStatus {
AJS_PendingNsCreation = 0;
AJS_LinkingToArchiveQueue = 1;
AJS_PendingMount = 2;
AJS_PendingNsDeletion = 99;
AJS_Selected = 3;
AJS_Complete = 4;
AJS_Failed = 999;
......
......@@ -61,11 +61,23 @@ private:
template <class Queue, class Request>
SharedQueueLock<Queue, Request>::~SharedQueueLock() {
double waitTime = m_timer.secs(utils::Timer::resetCounter);
m_lock->release();
bool skipQueuesTrim=false;
if (m_lock.get() && m_lock->isLocked()) {
m_lock->release();
} else {
m_logContext.log(log::ERR, "In SharedQueueLock::~SharedQueueLock(): the lock was not present or not locked. Skipping unlock.");
skipQueuesTrim=true;
}
double queueUnlockTime = m_timer.secs(utils::Timer::resetCounter);
// The next update of the queue can now proceed
ANNOTATE_HAPPENS_BEFORE(m_promiseForSuccessor.get());
m_promiseForSuccessor->set_value();
if (m_promiseForSuccessor.get()) {
ANNOTATE_HAPPENS_BEFORE(m_promiseForSuccessor.get());
m_promiseForSuccessor->set_value();
} else {
m_logContext.log(log::ERR, "In SharedQueueLock::~SharedQueueLock(): the promise was not present. Skipping value setting.");
skipQueuesTrim=true;
}
if (skipQueuesTrim) return;
double successorUnlockTime = m_timer.secs(utils::Timer::resetCounter);
// We can now cleanup the promise/future couple if they were not picked up to trim the maps.
// A next thread finding them unlocked or absent will be equivalent.
......
......@@ -570,65 +570,6 @@ void OStoreDB::deleteArchiveRequest(const std::string &diskInstanceName,
throw NoSuchArchiveRequest("In OStoreDB::deleteArchiveRequest: ArchiveToFileRequest not found");
}
//------------------------------------------------------------------------------
// OStoreDB::markArchiveRequestForDeletion()
//------------------------------------------------------------------------------
std::unique_ptr<SchedulerDatabase::ArchiveToFileRequestCancelation>
OStoreDB::markArchiveRequestForDeletion(const common::dataStructures::SecurityIdentity& requester,
uint64_t fileId) {
assertAgentAddressSet();
// Construct the return value immediately
std::unique_ptr<cta::OStoreDB::ArchiveToFileRequestCancelation>
internalRet(new cta::OStoreDB::ArchiveToFileRequestCancelation(*m_agentReference, m_objectStore, m_catalogue, m_logger));
cta::objectstore::ArchiveRequest & ar = internalRet->m_request;
cta::objectstore::ScopedExclusiveLock & atfrl = internalRet->m_lock;
// Attempt to find the request
objectstore::RootEntry re(m_objectStore);
ScopedSharedLock rel(re);
re.fetch();
auto tpl=re.dumpArchiveQueues();
rel.release();
for (auto tpp=tpl.begin(); tpp!=tpl.end(); tpp++) {
try {
objectstore::ArchiveQueue aq(tpp->address, m_objectStore);
ScopedSharedLock tpl(aq);
aq.fetch();
auto arl = aq.dumpJobs();
tpl.release();
for (auto arp=arl.begin(); arp!=arl.end(); arp++) {
objectstore::ArchiveRequest tar(arp->address, m_objectStore);
objectstore::ScopedSharedLock tatfrl(tar);
tar.fetch();
if (tar.getArchiveFile().archiveFileID == fileId) {
// Point the agent to the request
m_agentReference->addToOwnership(arp->address, m_objectStore);
// Mark all jobs are being pending NS deletion (for being deleted them selves)
tatfrl.release();
ar.setAddress(arp->address);
atfrl.lock(ar);
ar.fetch();
ar.setAllJobsPendingNSdeletion();
ar.commit();
// Unlink the jobs from the tape pools (it is safely referenced in the agent)
auto arJobs=ar.dumpJobs();
for (auto atpp=arJobs.begin(); atpp!=arJobs.end(); atpp++) {
objectstore::ArchiveQueue aqp(atpp->owner, m_objectStore);
objectstore::ScopedExclusiveLock atpl(aqp);
aqp.fetch();
aqp.removeJob(arp->address);
aqp.commit();
}
// Return the object to the caller, so complete() can be called later.
std::unique_ptr<cta::SchedulerDatabase::ArchiveToFileRequestCancelation> ret;
ret.reset(internalRet.release());
return ret;
}
}
} catch (...) {}
}
throw NoSuchArchiveRequest("In OStoreDB::markArchiveRequestForDeletion: ArchiveToFileRequest no found");
}
//------------------------------------------------------------------------------
// OStoreDB::ArchiveToFileRequestCancelation::complete()
//------------------------------------------------------------------------------
......@@ -2267,7 +2208,11 @@ OStoreDB::ArchiveJob::~ArchiveJob() {
m_archiveRequest.garbageCollect(m_agentReference.getAgentAddress(), m_agentReference, lc, m_catalogue);
atfrl.release();
// Remove ownership from agent
log::ScopedParamContainer params(lc);
params.add("agentObject", m_agentReference.getAgentAddress())
.add("jobObject", m_archiveRequest.getAddressIfSet());
m_agentReference.removeFromOwnership(m_archiveRequest.getAddressIfSet(), m_objectStore);
lc.log(log::INFO, "In OStoreDB::ArchiveJob::~ArchiveJob(): removed job from ownership after garbage collection.");
}
}
......
......@@ -225,7 +225,6 @@ public:
bool m_closed;
friend class OStoreDB;
};
std::unique_ptr<SchedulerDatabase::ArchiveToFileRequestCancelation> markArchiveRequestForDeletion(const common::dataStructures::SecurityIdentity &cliIdentity, uint64_t fileId) override;
std::map<std::string, std::list<common::dataStructures::ArchiveJob> > getArchiveJobs() const override;
......
......@@ -89,10 +89,6 @@ public:
m_OStoreDB.deleteArchiveRequest(diskInstanceName, archiveFileId);
}
std::unique_ptr<cta::SchedulerDatabase::ArchiveToFileRequestCancelation> markArchiveRequestForDeletion(const common::dataStructures::SecurityIdentity &cliIdentity, uint64_t fileId) override {
return m_OStoreDB.markArchiveRequestForDeletion(cliIdentity, fileId);
}
void deleteRetrieveRequest(const common::dataStructures::SecurityIdentity& cliIdentity, const std::string& remoteFile) override {
m_OStoreDB.deleteRetrieveRequest(cliIdentity, remoteFile);
}
......
......@@ -150,17 +150,6 @@ public:
virtual void complete() = 0;
virtual ~ArchiveToFileRequestCancelation() {};
};
/**
* Marks the specified archive request for deletion. The request can only be
* fully deleted once the corresponding entry has been deleted from the
* archive namespace.
*
* @param requester The identity of the requester.
* @param fileId Id of the destination file within the archive catalogue.
*/
virtual std::unique_ptr<ArchiveToFileRequestCancelation> markArchiveRequestForDeletion(
const common::dataStructures::SecurityIdentity &cliIdentity,
uint64_t fileId) = 0;
/*============ Archive management: tape server side =======================*/
/**
......
......@@ -40,22 +40,3 @@ INSTALL (FILES xrootd-cta.cfg DESTINATION /etc/xrootd/)
INSTALL (FILES cta-frontend.conf DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/cta)
install (FILES cta-frontend.logrotate DESTINATION /etc/logrotate.d RENAME cta-frontend)
set (OPAQUE_QUERY_CMD_SRC_FILES
CmdLineTool.cpp
OpaqueQueryCmd.cpp
OpaqueQueryCmdLineArgs.cpp
OpaqueQueryCmdMain.cpp)
add_executable (cta-xrootd_plugins-opaque-query ${OPAQUE_QUERY_CMD_SRC_FILES})
add_dependencies (cta-xrootd_plugins-opaque-query generate_notification.pb.h)
target_link_libraries (cta-xrootd_plugins-opaque-query ctacommon)
set (WRITE_NOTIFICATION_MSG_CMD_SRC_FILES
CmdLineTool.cpp
WriteNotificationMsgCmd.cpp
WriteNotificationMsgCmdLineArgs.cpp
WriteNotificationMsgCmdMain.cpp)
#add_executable (cta-xrootd_plugins-write-notification-msg ${WRITE_NOTIFICATION_MSG_CMD_SRC_FILES})
#add_dependencies (cta-xrootd_plugins-write-notification-msg generate_notification.pb.h)
#target_link_libraries (cta-xrootd_plugins-write-notification-msg ctacommon ctaeosmessages)
/*
* 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 "common/make_unique.hpp"
#include "eos/messages/eos_messages.pb.h"
#include "xroot_plugins/OpaqueQueryCmd.hpp"
#include "xroot_plugins/OpaqueQueryCmdLineArgs.hpp"
#include <fstream>
#include <iostream>
#include <stdint.h>
#include <string>
#include <XrdCl/XrdClFileSystem.hh>
namespace cta {
namespace xroot_plugins {
//------------------------------------------------------------------------------
// constructor
//------------------------------------------------------------------------------
OpaqueQueryCmd::OpaqueQueryCmd(std::istream &inStream, std::ostream &outStream, std::ostream &errStream):
CmdLineTool(inStream, outStream, errStream) {
}
//------------------------------------------------------------------------------
// destructor
//------------------------------------------------------------------------------
OpaqueQueryCmd::~OpaqueQueryCmd() noexcept {
}
//------------------------------------------------------------------------------
// exceptionThrowingMain
//------------------------------------------------------------------------------
int OpaqueQueryCmd::exceptionThrowingMain(const int argc, char *const *const argv) {
const OpaqueQueryCmdLineArgs cmdLineArgs(argc, argv);
if(cmdLineArgs.help) {
printUsage(m_out);
return 0;
}
std::ifstream queryFileStream(cmdLineArgs.queryFilename, std::ios_base::binary);
if(!queryFileStream) {
m_err << "Failed to open " << cmdLineArgs.queryFilename << std::endl;
return 1;
}
std::vector<char> queryFileContents(
(std::istreambuf_iterator<char>(queryFileStream)),
(std::istreambuf_iterator<char>()));
const std::string protocol = "xroot";
const std::string fsUrl = protocol + ":" + "//" + cmdLineArgs.ctaHost + ":" + std::to_string(cmdLineArgs.ctaPort);
XrdCl::FileSystem fs(fsUrl, false);
XrdCl::Buffer arg;
arg.Append(&queryFileContents[0], queryFileContents.size(), 0);
XrdCl::Buffer *response = nullptr;
const XrdCl::XRootDStatus status = fs.Query(XrdCl::QueryCode::Opaque, arg, response);
std::unique_ptr<XrdCl::Buffer> smartResponse(response);
std::cout << "status.IsError()=" << (status.IsError() ? "true" : "false") << std::endl;
std::cout << "status.IsFatal()=" << (status.IsFatal() ? "true" : "false") << std::endl;
std::cout << "status.IsOK()=" << (status.IsOK() ? "true" : "false") << std::endl;
if(nullptr != smartResponse.get()) {
std::cout << "response->GetSize()=" << response->GetSize() << std::endl;
std::ofstream responseFileStream(cmdLineArgs.responseFilename, std::ios_base::binary);
if(!responseFileStream) {
std::cerr << "Failed to open " << cmdLineArgs.responseFilename << std::endl;
}
const char *buf = smartResponse->GetBuffer();
const uint32_t bufSize = smartResponse->GetSize();
responseFileStream.write(buf, bufSize);
}
return 0;
}
//------------------------------------------------------------------------------
// printUsage
//------------------------------------------------------------------------------
void OpaqueQueryCmd::printUsage(std::ostream &os) {
OpaqueQueryCmdLineArgs::printUsage(os);
}
} // namespace xroot_plugins
} // namespace cta
/*
* 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 "xroot_plugins/CmdLineTool.hpp"
#include "rdbms/Conn.hpp"
namespace cta {
namespace xroot_plugins {
/**
* Command-line tool to emulate EOS sending CTA a message.
*/
class OpaqueQueryCmd: public CmdLineTool {
public:
/**
* Constructor.
*
* @param inStream Standard input stream.
* @param outStream Standard output stream.
* @param errStream Standard error stream.
*/
OpaqueQueryCmd(std::istream &inStream, std::ostream &outStream, std::ostream &errStream);
/**
* Destructor.
*/
~OpaqueQueryCmd() noexcept;
private:
/**
* An exception throwing version of main().
*
* @param argc The number of command-line arguments including the program name.
* @param argv The command-line arguments.
* @return The exit value of the program.
*/
int exceptionThrowingMain(const int argc, char *const *const argv) override;
/**
* Prints the usage message of the command-line tool.
*
* @param os The output stream to which the usage message is to be printed.
*/
void printUsage(std::ostream &os) override;
}; // class OpaqueQueryCmd
} // namespace xroot_plugins
} // namespace cta
/*
* 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 "common/exception/CommandLineNotParsed.hpp"
#include "common/utils/utils.hpp"
#include "xroot_plugins/OpaqueQueryCmdLineArgs.hpp"
#include <getopt.h>