From cfd621e9d000393b53f19724d7ce8f788f04663e Mon Sep 17 00:00:00 2001 From: Eric Cano <Eric.Cano@cern.ch> Date: Tue, 10 Sep 2019 16:07:45 +0200 Subject: [PATCH] #252: Created and packaged a C++ based utility as an atlernative to the python based helper. --- CMakeLists.txt | 2 + .../ctafrontend/cc7/opt/run/bin/client.sh | 2 +- .../orchestration/tests/CMakeLists.txt | 26 +++ .../tests/client-ar-abortPrepare.cpp | 161 ++++++++++++++++++ .../orchestration/tests/client_ar.sh | 10 +- cta.spec.in | 10 ++ 6 files changed, 206 insertions(+), 5 deletions(-) create mode 100644 continuousintegration/orchestration/tests/CMakeLists.txt create mode 100644 continuousintegration/orchestration/tests/client-ar-abortPrepare.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 77f6db411a..92b3c3be1a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -152,6 +152,8 @@ ELSE(DEFINED PackageOnly) add_subdirectory(tapeserver) add_subdirectory(XRootdSSiRmcd) + add_subdirectory(continuousintegration/orchestration/tests) + #Generate version information configure_file(${PROJECT_SOURCE_DIR}/version.hpp.in ${CMAKE_BINARY_DIR}/version.h) diff --git a/continuousintegration/docker/ctafrontend/cc7/opt/run/bin/client.sh b/continuousintegration/docker/ctafrontend/cc7/opt/run/bin/client.sh index 21ad0cf692..627afc14bd 100755 --- a/continuousintegration/docker/ctafrontend/cc7/opt/run/bin/client.sh +++ b/continuousintegration/docker/ctafrontend/cc7/opt/run/bin/client.sh @@ -7,7 +7,7 @@ if [ ! -e /etc/buildtreeRunner ]; then yum-config-manager --enable ceph # Install missing RPMs - yum -y install cta-cli cta-debuginfo xrootd-client eos-client jq python36 + yum -y install cta-cli cta-systemtest-helpers cta-debuginfo xrootd-client eos-client jq python36 ## Keep this temporary fix that may be needed if going to protobuf3-3.5.1 for CTA # Install eos-protobuf3 separately as eos is OK with protobuf3 but cannot use it.. diff --git a/continuousintegration/orchestration/tests/CMakeLists.txt b/continuousintegration/orchestration/tests/CMakeLists.txt new file mode 100644 index 0000000000..53e8a9fa51 --- /dev/null +++ b/continuousintegration/orchestration/tests/CMakeLists.txt @@ -0,0 +1,26 @@ +# 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/>. + +cmake_minimum_required (VERSION 2.6) + +find_package (xrootd REQUIRED) +find_package (xrootdclient REQUIRED) + +include_directories (${XROOTD_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}) + +add_executable(cta-client-ar-abortPrepare client-ar-abortPrepare.cpp) +target_link_libraries(cta-client-ar-abortPrepare XrdCl ctacommon) +install(TARGETS cta-client-ar-abortPrepare DESTINATION usr/bin) diff --git a/continuousintegration/orchestration/tests/client-ar-abortPrepare.cpp b/continuousintegration/orchestration/tests/client-ar-abortPrepare.cpp new file mode 100644 index 0000000000..6e242cacfb --- /dev/null +++ b/continuousintegration/orchestration/tests/client-ar-abortPrepare.cpp @@ -0,0 +1,161 @@ +/* + * 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 <getopt.h> +#include <string> +#include <iostream> +#include <memory> +#include <XrdCl/XrdClFileSystem.hh> +#include "common/utils/Regex.hpp" +#include "common/exception/XrootCl.hpp" + + +// No short options. +const char short_options[] = ""; +// We require a handful of long options. + +enum class OptionIds: int { + eos_instance = 1, + eos_poweruser = 2, + eos_dir = 3, + subdir = 4, + file = 5, + error_dir = 6 +}; + +const struct ::option long_options[] = { + { "eos-instance", required_argument, nullptr, (int)OptionIds::eos_instance }, + { "eos-poweruser", required_argument, nullptr, (int)OptionIds::eos_poweruser }, + { "eos-dir", required_argument, nullptr, (int)OptionIds::eos_dir }, + { "subdir", required_argument, nullptr, (int)OptionIds::subdir }, + { "file", required_argument, nullptr, (int)OptionIds::file }, + { "error-dir", required_argument, nullptr, (int)OptionIds::error_dir }, + { nullptr, 0, nullptr, 0 } +}; + +void help() { + std::cerr << "Expected parameters are: "; + const struct ::option * pOpt = long_options; + while (pOpt->name) { + std::cerr << "--" << pOpt->name << " "; + ++pOpt; + } + std::cerr << std::endl; +} + +// We make these variables global as they will be part of the process's environment. +char envXRD_LOGLEVEL[] = "XRD_LOGLEVEL=Dump"; +std::unique_ptr<char[]> envKRB5CCNAME; +char envXrdSecPROTOCOL[] = "XrdSecPROTOCOL=krb5"; + + +int main(int argc, char **argv) { + + struct { + std::string eos_instance; + std::string eos_poweruser; + std::string eos_dir; + std::string subdir; + std::string file; + std::string error_dir; + } options; + + int opt_ret; + while (-1 != (opt_ret = getopt_long(argc, argv, short_options, long_options, nullptr))) { + switch (opt_ret) { + case (int)OptionIds::eos_instance: + options.eos_instance = optarg; + break; + case (int)OptionIds::eos_poweruser: + options.eos_poweruser = optarg; + break; + case (int)OptionIds::eos_dir: + options.eos_dir = optarg; + break; + case (int)OptionIds::subdir: + options.subdir = optarg; + break; + case (int)OptionIds::file: + options.file = optarg; + break; + case (int)OptionIds::error_dir: + options.error_dir = optarg; + break; + case '?': + default: + std::cerr << "Unexpected option or missing argument." << std::endl; + exit(EXIT_FAILURE); + break; + } + } + + if (options.eos_instance.empty() || options.eos_poweruser.empty() || options.error_dir.empty() || options.subdir.empty() || + options.file.empty() || options.error_dir.empty()) { + std::cerr << "At least one option missing." << std::endl; + help(); + exit (EXIT_FAILURE); + } + + std::cout << "To run again: " << argv[0] + << " --eos-instance=" << options.eos_instance + << " --eos-poweruser=" << options.eos_poweruser + << " --eos-dir=" << options.eos_dir + << " --subdir=" << options.subdir + << " --file=" << options.file + << " --error_dir=" << options.error_dir << std::endl; + + // Get the extended attribute for the retrieve request id + try { + // Prepare environment. + putenv(envXRD_LOGLEVEL); + std::string envKRB5CCNAMEvalue = std::string("KRB5CCNAME=/tmp/") + options.eos_poweruser + "/krb5cc_0"; + // We need to copy to an array because of putenv's lack of const correctness. + envKRB5CCNAME.reset(new char[envKRB5CCNAMEvalue.size() + 1]); + strncpy(envKRB5CCNAME.get(), envKRB5CCNAMEvalue.c_str(), envKRB5CCNAMEvalue.size() + 1); + putenv(envKRB5CCNAME.get()); + putenv(envXrdSecPROTOCOL); + + XrdCl::FileSystem xrdfs(options.eos_instance); + std::string fileName = options.eos_dir + "/" + options.subdir + "/" + options.file; + std::string query = fileName + "?mgm.pcmd=xattr&mgm.subcmd=get&mgm.xattrname=sys.retrieve.req_id"; + auto qcOpaque = XrdCl::QueryCode::OpaqueFile; + XrdCl::Buffer xrdArg; + xrdArg.FromString(query); + XrdCl::Buffer *respPtr = nullptr; + auto status = xrdfs.Query(qcOpaque, xrdArg, respPtr, (uint16_t)0 /*timeout=default*/); + // Ensure proper memory management for the response buffer (it is our responsilibity to free it, we delegate to the unique_ptr). + std::unique_ptr<XrdCl::Buffer> respUP(respPtr); + respPtr = nullptr; + cta::exception::XrootCl::throwOnError(status, "Error during XrdCl::Query"); + cta::utils::Regex re("value=(.*)"); + std::string respStr(respUP->GetBuffer(), respUP->GetSize()); + auto reResult = re.exec(respStr); + if (reResult.size() != 2) { + // We did not receive the expected structure + throw cta::exception::Exception(std::string("Unexpected result from xattr query: ") + respStr); + } + std::string retrieveRequestId = reResult[1]; + std::vector<std::string> files = { retrieveRequestId, fileName }; + XrdCl::PrepareFlags::Flags flags = XrdCl::PrepareFlags::Cancel; + auto abortStatus = xrdfs.Prepare(files, flags, 0, respPtr, 0 /* timeout */); + cta::exception::XrootCl::throwOnError(abortStatus, "Error during XrdCl::Prepare"); + } catch (cta::exception::Exception & ex) { + std::cerr << "Received exception: " << ex.what() << std::endl; + } + return 0; +} diff --git a/continuousintegration/orchestration/tests/client_ar.sh b/continuousintegration/orchestration/tests/client_ar.sh index f97a7d3be1..a404e1f781 100644 --- a/continuousintegration/orchestration/tests/client_ar.sh +++ b/continuousintegration/orchestration/tests/client_ar.sh @@ -359,6 +359,8 @@ for ((subdir=0; subdir < ${NB_DIRS}; subdir++)); do done # Put all tape drives down +echo "Sleeping 3 seconds to let previous sessions finish." +sleep 3 admin_kdestroy &>/dev/null admin_kinit &>/dev/null INITIAL_DRIVES_STATE=`admin_cta --json dr ls` @@ -403,7 +405,7 @@ fi for ((subdir=0; subdir < ${NB_DIRS}; subdir++)); do echo -n "Cancelling prepare for files in ${EOS_DIR}/${subdir} using ${NB_PROCS} processes (prepare_abort)..." cat ${STATUS_FILE} | grep ^${subdir}/ | cut -d/ -f2 \ - | xargs --max-procs=${NB_PROCS} -iTEST_FILE_NAME /root/client_ar_abortPrepare.py --eos-instance ${EOSINSTANCE} \ + | xargs --max-procs=${NB_PROCS} -iTEST_FILE_NAME cta-client-ar-abortPrepare --eos-instance ${EOSINSTANCE} \ --eos-poweruser ${EOSPOWER_USER} --eos-dir ${EOS_DIR} --subdir ${subdir} --file TEST_FILE_NAME --error-dir ${ERROR_DIR} \ | tee ${LOGDIR}/prepare_abort_sys.retrieve.req_id_${subdir}.log # | grep ^ERROR echo Done. @@ -440,11 +442,12 @@ while test ${REMAINING_REQUESTS} -gt 0; do done # Check that the files were not retrieved -echo "Checking restaged files." +echo -n "Checking restaged files. Found: " RESTAGEDFILES=0 for ((subdir=0; subdir < ${NB_DIRS}; subdir++)); do - RESTAGEDFILES=$(( ${RESTAGEDFILES} + $(eos root://${EOSINSTANCE} ls -y ${EOS_DIR}/${subdir} | egrep '^d[1-9][0-9]*::t1' | wc -l) )) + (( RESTAGEDFILES += $(eos root://${EOSINSTANCE} ls -y ${EOS_DIR}/${subdir} | egrep '^d[1-9][0-9]*::t1' | wc -l) )) done +echo ${RESTAGEDFILES} if [ "0" != "$(ls ${ERROR_DIR} 2> /dev/null | wc -l)" ]; then # there were some prepare errors @@ -453,7 +456,6 @@ if [ "0" != "$(ls ${ERROR_DIR} 2> /dev/null | wc -l)" ]; then mv ${ERROR_DIR}/* ${LOGDIR}/xrd_errors/ fi - # We can now delete the files DELETED=0 if [[ $REMOVE == 1 ]]; then diff --git a/cta.spec.in b/cta.spec.in index f091c76fe4..bfd09d3d1c 100644 --- a/cta.spec.in +++ b/cta.spec.in @@ -410,3 +410,13 @@ collects EOS disk copies that have been safely stored to tape. %postun -n cta-fst-gcd %systemd_postun cta-fst-gcd.service %systemdDaemonReload + +%package -n cta-systemtest-helpers +Summary: Collection of utilities deployed in system test client containers. +Group: Application/CTA +Requires: cta-lib = %{version}-%{release} +%description -n cta-systemtest-helpers +Collection of utilities deployed in system test client containers. +Currently contains a helper for the client-ar script, which should be installed alongside it. +%files -n cta-systemtest-helpers +%attr(0755,root,root) /usr/bin/cta-client-ar-abortPrepare -- GitLab