From 87822b72296e4b1c7e2dc28dce76659791374dd0 Mon Sep 17 00:00:00 2001 From: Jorge Camarero Vera <jorge.camarero@cern.ch> Date: Tue, 4 Oct 2022 15:59:21 +0200 Subject: [PATCH] Resolve "Problem with handling of the non-native formats by the CleanerSession" --- .gitlab/ci/kube-tests.gitlab-ci.yml | 44 +- ReleaseNotes.md | 1 + catalogue/Catalogue.hpp | 2 + catalogue/CatalogueRetryWrapper.hpp | 4 + catalogue/CatalogueTest.cpp | 24 + catalogue/DummyCatalogue.hpp | 2 + catalogue/RdbmsCatalogue.cpp | 27 ++ catalogue/RdbmsCatalogue.hpp | 2 + .../cc7/opt/run/bin/externaltapetests.sh | 33 ++ .../orchestration/create_instance.sh | 28 +- .../orchestration/pod-externaltapetests.yaml | 81 ++++ .../orchestration/run_systemtest.sh | 6 +- .../tests/external_tapes_test.sh | 111 +++++ cta.spec.in | 1 + .../tape/tapeserver/daemon/CleanerSession.cpp | 152 +++---- .../tape/tapeserver/daemon/CleanerSession.hpp | 426 +++++++++--------- .../tape/tapeserver/file/CMakeLists.txt | 16 + .../tape/tapeserver/file/HeaderChecker.cpp | 61 +++ .../tape/tapeserver/file/HeaderChecker.hpp | 20 + .../tape/tapeserver/file/OsmFileReader.cpp | 32 +- .../tape/tapeserver/file/OsmFileStructure.hpp | 28 +- .../tape/tapeserver/file/OsmReadSession.cpp | 44 +- .../tape/tapeserver/file/OsmReaderTest.cpp | 196 ++++++++ 23 files changed, 969 insertions(+), 372 deletions(-) create mode 100755 continuousintegration/docker/ctafrontend/cc7/opt/run/bin/externaltapetests.sh create mode 100644 continuousintegration/orchestration/pod-externaltapetests.yaml create mode 100755 continuousintegration/orchestration/tests/external_tapes_test.sh create mode 100644 tapeserver/castor/tape/tapeserver/file/OsmReaderTest.cpp diff --git a/.gitlab/ci/kube-tests.gitlab-ci.yml b/.gitlab/ci/kube-tests.gitlab-ci.yml index cd4220c281..c92e990986 100644 --- a/.gitlab/ci/kube-tests.gitlab-ci.yml +++ b/.gitlab/ci/kube-tests.gitlab-ci.yml @@ -1,8 +1,19 @@ +.manual_rules: + rules: + - if: $CI_PIPELINE_SOURCE == "web" + when: manual + allow_failure: true + - if: $CI_PIPELINE_SOURCE == "push" + when: manual + allow_failure: true + - if: $CI_PIPELINE_SOURCE == "schedule" + when: on_success + allow_failure: false + .kubernetes_test: script: - export NAMESPACE="${JOB_NAME}-${CTA_BUILD_ID}-$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 4 | head -n 1)" - cd continuousintegration/orchestration/ - - echo "${EXTENDED_OPTIONS}" - ./run_systemtest.sh -n ${NAMESPACE} -p ${CI_PIPELINE_ID} -s ${TEST_SCRIPT} ${EXTENDED_OPTIONS} variables: TEST_SCRIPT: @@ -21,15 +32,7 @@ extends: - .kubernetes_test rules: - - if: $CI_PIPELINE_SOURCE == "web" - when: manual - allow_failure: true - - if: $CI_PIPELINE_SOURCE == "push" - when: manual - allow_failure: true - - if: $CI_PIPELINE_SOURCE == "schedule" - when: on_success - allow_failure: false + - !reference [.manual_rules, rules] variables: EXTENDED_OPTIONS: "-O -D -t 2400 -C -e eos5-config-quarkdb.yaml" @@ -85,19 +88,22 @@ liquibase-update: extends: - .kubernetes_test rules: - - if: $CI_PIPELINE_SOURCE == "web" - when: manual - allow_failure: true - - if: $CI_PIPELINE_SOURCE == "push" - when: manual - allow_failure: true - - if: $CI_PIPELINE_SOURCE == "schedule" - when: on_success - allow_failure: false + - !reference [.manual_rules, rules] variables: TEST_SCRIPT: "tests/update_db_test.sh" EXTENDED_OPTIONS: "-O -D -t 2400 -C -u" +external-tape-formats: + stage: test + extends: + - .kubernetes_test + rules: + - !reference [.manual_rules, rules] + variables: + TEST_SCRIPT: "/usr/bin/true" + EXTENDED_OPTIONS: "-O -D -t 2400 -C -T" + JOB_NAME: "external-tape" + nightly1: stage: test extends: diff --git a/ReleaseNotes.md b/ReleaseNotes.md index a128f03747..f5cb36a8d9 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -2,6 +2,7 @@ ## Summary ### Bug fixes +- cta/CTA#122 - Problem with handling of the non-native formats by the CleanerSession - cta/CTA#160 - Improve DB access to get all the Tape Drive States - cta/CTA#164 - Cppcheck job is not failing when there are errors - cta/CTA#165 - Fix oracle dbunittests diff --git a/catalogue/Catalogue.hpp b/catalogue/Catalogue.hpp index e7c6178677..be6c5e83fe 100644 --- a/catalogue/Catalogue.hpp +++ b/catalogue/Catalogue.hpp @@ -224,6 +224,8 @@ public: */ virtual std::list<TapeForWriting> getTapesForWriting(const std::string &logicalLibraryName) const = 0; + virtual common::dataStructures::Label::Format getTapeLabelFormat(const std::string& vid) const = 0; + /** * Notifies the catalogue that the specified files have been written to tape. * diff --git a/catalogue/CatalogueRetryWrapper.hpp b/catalogue/CatalogueRetryWrapper.hpp index 8bd0e67608..33547358a8 100644 --- a/catalogue/CatalogueRetryWrapper.hpp +++ b/catalogue/CatalogueRetryWrapper.hpp @@ -79,6 +79,10 @@ public: return retryOnLostConnection(m_log, [&]{return m_catalogue->getTapesForWriting(logicalLibraryName);}, m_maxTriesToConnect); } + common::dataStructures::Label::Format getTapeLabelFormat(const std::string& vid) const override { + return retryOnLostConnection(m_log, [&]{return m_catalogue->getTapeLabelFormat(vid);}, m_maxTriesToConnect); + } + void filesWrittenToTape(const std::set<TapeItemWrittenPointer> &event) override { return retryOnLostConnection(m_log, [&]{return m_catalogue->filesWrittenToTape(event);}, m_maxTriesToConnect); } diff --git a/catalogue/CatalogueTest.cpp b/catalogue/CatalogueTest.cpp index 482c880343..77ff7875ce 100644 --- a/catalogue/CatalogueTest.cpp +++ b/catalogue/CatalogueTest.cpp @@ -7290,6 +7290,30 @@ TEST_P(cta_catalogue_CatalogueTest, getTapesForWriting) { ASSERT_EQ(0, tape.dataOnTapeInBytes); } +TEST_P(cta_catalogue_CatalogueTest, getTapeLabelFormat) { + const bool logicalLibraryIsDisabled= false; + const uint64_t nbPartialTapes = 2; + const bool isEncrypted = true; + const std::optional<std::string> supply("value for the supply pool mechanism"); + + m_catalogue->createMediaType(m_admin, m_mediaType); + m_catalogue->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library"); + m_catalogue->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment); + m_catalogue->createVirtualOrganization(m_admin, m_vo); + m_catalogue->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool"); + m_catalogue->createTape(m_admin, m_tape1); + + // Get Tape + const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->getTapes(); + ASSERT_EQ(1, tapes.size()); + const cta::common::dataStructures::Tape tape = tapes.front(); + ASSERT_EQ(m_tape1.vid, tape.vid); + + // Get label format and compare + const auto labelFormat = m_catalogue->getTapeLabelFormat(m_tape1.vid); + ASSERT_EQ(tape.labelFormat, labelFormat); +} + TEST_P(cta_catalogue_CatalogueTest, getTapesForWritingOrderedByDataInBytesDesc) { using namespace cta; diff --git a/catalogue/DummyCatalogue.hpp b/catalogue/DummyCatalogue.hpp index daa1559a29..570623fec8 100644 --- a/catalogue/DummyCatalogue.hpp +++ b/catalogue/DummyCatalogue.hpp @@ -21,6 +21,7 @@ #include <list> #include "Catalogue.hpp" +#include "common/threading/MutexLocker.hpp" namespace cta { @@ -120,6 +121,7 @@ public: std::list<common::dataStructures::Tape> getTapes(const TapeSearchCriteria& searchCriteria) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); } // getTapesByVid is implemented below (and works). std::map<std::string, std::string> getVidToLogicalLibrary(const std::set<std::string> &vids) const override { throw exception::Exception(std::string("In ") + __PRETTY_FUNCTION__ + ": not implemented"); } + common::dataStructures::Label::Format getTapeLabelFormat(const std::string& vid) const override { throw exception::Exception(std::string("In ") + __PRETTY_FUNCTION__ + ": not implemented"); } std::list<TapeForWriting> getTapesForWriting(const std::string& logicalLibraryName) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); } bool isAdmin(const common::dataStructures::SecurityIdentity& admin) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); } void modifyAdminUserComment(const common::dataStructures::SecurityIdentity& admin, const std::string& username, const std::string& comment) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); } diff --git a/catalogue/RdbmsCatalogue.cpp b/catalogue/RdbmsCatalogue.cpp index 2f38670cd9..f134c9fd15 100644 --- a/catalogue/RdbmsCatalogue.cpp +++ b/catalogue/RdbmsCatalogue.cpp @@ -9439,6 +9439,33 @@ std::list<TapeForWriting> RdbmsCatalogue::getTapesForWriting(const std::string & } } +common::dataStructures::Label::Format RdbmsCatalogue::getTapeLabelFormat(const std::string& vid) const { + try { + const char *const sql = + "SELECT " + "TAPE.LABEL_FORMAT AS LABEL_FORMAT " + "FROM " + "TAPE " + "WHERE " + "VID = :VID"; + + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql); + stmt.bindString(":VID", vid); + auto rset = stmt.executeQuery(); + if(rset.next()) { + return common::dataStructures::Label::validateFormat(rset.columnOptionalUint8("LABEL_FORMAT"), "[RdbmsCatalogue::getTapeLabelFormat()]"); + } else { + throw exception::Exception(std::string("No such tape with vid=") + vid); + } + } catch(exception::UserError &) { + throw; + } catch(exception::Exception &ex) { + ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str()); + throw; + } +} + //------------------------------------------------------------------------------ // insertTapeFile //------------------------------------------------------------------------------ diff --git a/catalogue/RdbmsCatalogue.hpp b/catalogue/RdbmsCatalogue.hpp index f167f04980..ea76e97d35 100644 --- a/catalogue/RdbmsCatalogue.hpp +++ b/catalogue/RdbmsCatalogue.hpp @@ -156,6 +156,8 @@ public: */ std::list<TapeForWriting> getTapesForWriting(const std::string &logicalLibraryName) const override; + common::dataStructures::Label::Format getTapeLabelFormat(const std::string& vid) const override; + /** * Notifies the CTA catalogue that the specified tape has been mounted in * order to archive files. diff --git a/continuousintegration/docker/ctafrontend/cc7/opt/run/bin/externaltapetests.sh b/continuousintegration/docker/ctafrontend/cc7/opt/run/bin/externaltapetests.sh new file mode 100755 index 0000000000..60411e8e24 --- /dev/null +++ b/continuousintegration/docker/ctafrontend/cc7/opt/run/bin/externaltapetests.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# @project The CERN Tape Archive (CTA) +# @copyright Copyright © 2022 CERN +# @license This program is free software, distributed under the terms of the GNU General Public +# Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can +# redistribute it and/or modify it under the terms of the GPL Version 3, 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. +# +# In applying this licence, CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization or +# submit itself to any jurisdiction. + +. /opt/run/bin/init_pod.sh + +set -e + +if [ ! -e /etc/buildtreeRunner ]; then + yum-config-manager --enable cta-artifacts + yum-config-manager --enable ceph + yum-config-manager --enable castor + + # Install missing RPMs + yum -y install mt-st lsscsi sg3_utils cta-taped cta-tape-label cta-debuginfo ceph-common +fi + +yum -y install cta-systemtests + +2>&1 cta-osmReaderTest ${DEVICE_NAME} ${DRIVE_DEVICE} diff --git a/continuousintegration/orchestration/create_instance.sh b/continuousintegration/orchestration/create_instance.sh index 87e22b5e23..757db5abf7 100755 --- a/continuousintegration/orchestration/create_instance.sh +++ b/continuousintegration/orchestration/create_instance.sh @@ -46,6 +46,9 @@ runoracleunittests=0 # By default doesn't prepare the images with the previous schema version updatedatabasetest=0 +# By default doesn't run the tests for external tape formats +runexternaltapetests=0 + usage() { cat <<EOF 1>&2 Usage: $0 -n <namespace> [-o <objectstore_configmap>] [-d <database_configmap>] \ [-e <eos_configmap>] [-a <additional_k8_resources>]\ @@ -63,13 +66,14 @@ Options: -a additional kubernetes resources added to the kubernetes namespace -U Run database unit test only -u Prepare the pods to run the liquibase test + -T Execute tests for external tape formats EOF exit 1 } die() { echo "$@" 1>&2 ; exit 1; } -while getopts "n:o:d:e:a:p:b:B:E:SDOUum" o; do +while getopts "n:o:d:e:a:p:b:B:E:SDOUumT" o; do case "${o}" in o) config_objectstore=${OPTARG} @@ -94,7 +98,7 @@ while getopts "n:o:d:e:a:p:b:B:E:SDOUum" o; do n) instance=${OPTARG} ;; - p) + p) pipelineid=${OPTARG} ;; b) @@ -118,6 +122,9 @@ while getopts "n:o:d:e:a:p:b:B:E:SDOUum" o; do U) runoracleunittests=1 ;; + T) + runexternaltapetests=1 + ;; u) updatedatabasetest=1 ;; @@ -463,4 +470,21 @@ fi echo "Instance ${instance} successfully created:" kubectl get pods -a --namespace=${instance} +if [ $runexternaltapetests == 1 ] ; then + echo "Running database unit-tests" + ./tests/external_tapes_test.sh -n ${instance} -P ${poddir} + + kubectl --namespace=${instance} logs externaltapetests + + # database unit-tests went wrong => exit now with error + if $(kubectl get pod externaltapetests -a --namespace=${instance} | grep -q Error); then + echo "init pod in Error status here are its last log lines:" + kubectl --namespace=${instance} logs externaltapetests --tail 10 + die "ERROR: externaltapetests pod in Error state. Initialization failed." + fi + + # database unit-tests were successful => exit now with success + exit 0 +fi + exit 0 diff --git a/continuousintegration/orchestration/pod-externaltapetests.yaml b/continuousintegration/orchestration/pod-externaltapetests.yaml new file mode 100644 index 0000000000..0de217d569 --- /dev/null +++ b/continuousintegration/orchestration/pod-externaltapetests.yaml @@ -0,0 +1,81 @@ +apiVersion: v1 +kind: Pod +metadata: + name: externaltapetests +spec: + restartPolicy: Never + containers: + - name: externaltapetests + image: gitlab-registry.cern.ch/cta/ctageneric:78673git921a9300 + stdin: true + env: + - name: MY_CONTAINER + value: "externaltapetests" + - name: MY_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: MY_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: INSTANCE_NAME + value: "$(MY_NAMESPACE)" + - name: DEVICE_NAME + value: "DEVICE_NAME_VALUE" + - name: DRIVE_DEVICE + value: "DEVICE_PATH" + - name: BUILDTREE_BASE + valueFrom: + configMapKeyRef: + name: buildtree + key: base + - name: CTA_BUILDTREE_SUBDIR + valueFrom: + configMapKeyRef: + name: buildtree + key: cta_subdir + - name: EOS_BUILDTREE_SUBDIR + valueFrom: + configMapKeyRef: + name: buildtree + key: eos_subdir + - name: TERM + value: "xterm" + - name: driveslot + value: "0" + command: ['/opt/run/bin/externaltapetests.sh'] + args: ["none"] + volumeMounts: + - mountPath: /shared + name: shared + - mountPath: /etc/config/objectstore + name: myobjectstore + - mountPath: /etc/config/database + name: mydatabase + - mountPath: /etc/config/library + name: mylibrary + - mountPath: /mnt/logs + name: logstorage + securityContext: + privileged: true + + volumes: + - name: shared + hostPath: + path: /opt/cta + - name: myobjectstore + configMap: + name: objectstore-config + - name: mydatabase + configMap: + name: database-config + - name: mylibrary + configMap: + name: library-config + - name: logstorage + persistentVolumeClaim: + claimName: claimlogs + + imagePullSecrets: + - name: ctaregsecret diff --git a/continuousintegration/orchestration/run_systemtest.sh b/continuousintegration/orchestration/run_systemtest.sh index 427a5bdf71..bd031a85d3 100755 --- a/continuousintegration/orchestration/run_systemtest.sh +++ b/continuousintegration/orchestration/run_systemtest.sh @@ -61,6 +61,7 @@ Options: -U Run database unit test only -C Cleanup leftover kubernetes namespaces -u Prepare the pods to run the liquibase test + -T Execute tests for external tape formats Create a kubernetes instance and launch the system test script specified. Makes sure the created instance is cleaned up at the end and return the status of the system test. @@ -73,7 +74,7 @@ exit 1 # always delete DB and OBJECTSTORE for tests CREATE_OPTS="-D -O" -while getopts "n:d:s:p:b:e:a:B:t:ukDOSUC" o; do +while getopts "n:d:s:p:b:e:a:B:t:ukDOSUCT" o; do case "${o}" in s) systemtest_script=${OPTARG} @@ -126,6 +127,9 @@ while getopts "n:d:s:p:b:e:a:B:t:ukDOSUC" o; do u) CREATE_OPTS="${CREATE_OPTS} -u" ;; + T) + CREATE_OPTS="${CREATE_OPTS} -T" + ;; *) usage ;; diff --git a/continuousintegration/orchestration/tests/external_tapes_test.sh b/continuousintegration/orchestration/tests/external_tapes_test.sh new file mode 100755 index 0000000000..34b7b99bb3 --- /dev/null +++ b/continuousintegration/orchestration/tests/external_tapes_test.sh @@ -0,0 +1,111 @@ +#!/bin/bash + +# @project The CERN Tape Archive (CTA) +# @copyright Copyright © 2022 CERN +# @license This program is free software, distributed under the terms of the GNU General Public +# Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can +# redistribute it and/or modify it under the terms of the GPL Version 3, 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. +# +# In applying this licence, CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization or +# submit itself to any jurisdiction. + +usage() { cat <<EOF 1>&2 +Usage: $0 -n <namespace> +EOF +exit 1 +} + +NAMESPACE="" +tempdir=$(mktemp -d) +poddir="" +READ_OSM_SCRIPT="${tempdir}/read_osm_tape.sh" + +while getopts "n:P:" o; do + case "${o}" in + n) + NAMESPACE=${OPTARG} + ;; + P) + poddir=${OPTARG} + ;; + *) + usage + ;; + esac +done +shift $((OPTIND-1)) + +if [ -z "${NAMESPACE}" ]; then + usage +fi + +echo " +device=\"DEVICE\" +echo \"Device is \$device\" + +# Load tape in a tapedrive +mtx -f /dev/smc status +mtx -f /dev/smc load 1 0 +mtx -f /dev/smc status + +# Get the device status where the tape is loaded and rewind it. +mt -f \$device status +mt -f \$device rewind + +# The header files have 4 more bytes in the git file +truncate -s -4 /osm-mhvtl/L08033/L1 +touch /osm-tape.img +dd if=/osm-mhvtl/L08033/L1 of=/osm-tape.img bs=32768 +dd if=/osm-mhvtl/L08033/L2 of=/osm-tape.img bs=32768 seek=1 +dd if=/osm-tape.img of=\$device bs=32768 count=2 +dd if=/osm-mhvtl/L08033/file1 of=\$device bs=262144 count=202 +dd if=/osm-mhvtl/L08033/file2 of=\$device bs=262144 count=202 + +mt -f \$device rewind +" &> ${READ_OSM_SCRIPT} + +# Download OSM Tape from git +echo "${tempdir}/osm-mhvtl" +if [ ! -d "${tempdir}/osm-mhvtl" ] ; then + yum install -y git git-lfs + git lfs install || git lfs update --force + git lfs clone https://gitlab.desy.de/mwai.karimi/osm-mhvtl.git ${tempdir}/osm-mhvtl +fi + +# Get the device to be used. +device_name=$(kubectl -n ${NAMESPACE} exec tpsrv01 -c taped -- cat /etc/cta/TPCONFIG | grep smc0 | awk '{ print $1 }') +device=$(kubectl -n ${NAMESPACE} exec tpsrv01 -c taped -- cat /etc/cta/TPCONFIG | grep smc0 | awk '{ print $3 }') +sed -i "s#DEVICE#${device}#g" ${READ_OSM_SCRIPT} +sed -i "s#DEVICE_NAME_VALUE#${device_name}#g" ${poddir}/pod-externaltapetests.yaml +sed -i "s#DEVICE_PATH#${device}#g" ${poddir}/pod-externaltapetests.yaml + +# Copy the above script to the pod rmcd to be run +kubectl -n ${NAMESPACE} cp ${tempdir}/osm-mhvtl tpsrv01:/osm-mhvtl -c rmcd +kubectl -n ${NAMESPACE} cp ${READ_OSM_SCRIPT} tpsrv01:/root/read_osm_tape.sh -c rmcd +kubectl -n ${NAMESPACE} exec tpsrv01 -c rmcd -- /bin/bash /root/read_osm_tape.sh + +# Creation of the pod to run the tests +kubectl create -f "${poddir}/pod-externaltapetests.yaml" --namespace=${NAMESPACE} + +echo -n "Waiting for externaltapetests" +for ((i=0; i<400; i++)); do + echo -n "." + kubectl get pod externaltapetests -a --namespace=${NAMESPACE} | egrep -q 'Completed|Error' && break + sleep 1 +done +echo "\n" + +# Copy the logs outside +kubectl -n ${NAMESPACE} logs externaltapetests &> "../../pod_logs/${NAMESPACE}/externaltapetests.log" + +# Cleanning +rm -rf ${tempdir}/osm-mhvtl +rm ${READ_OSM_SCRIPT} + +exit 0 diff --git a/cta.spec.in b/cta.spec.in index edf6b8fb71..a93c6c3d04 100644 --- a/cta.spec.in +++ b/cta.spec.in @@ -329,6 +329,7 @@ Unit tests and system tests with virtual tape drives %{_bindir}/cta-unitTests-multiProcess %{_bindir}/cta-valgrindUnitTests.sh %{_bindir}/cta-unitPlusSystemTests.sh +%{_bindir}/cta-osmReaderTest %{_libdir}/libctacataloguecmdlineunittests.so* %{_libdir}/libctacommonunittests.so* %{_libdir}/libctadbconfigcatalogueunittests.so* diff --git a/tapeserver/castor/tape/tapeserver/daemon/CleanerSession.cpp b/tapeserver/castor/tape/tapeserver/daemon/CleanerSession.cpp index dd3efded2e..fbc5171c05 100644 --- a/tapeserver/castor/tape/tapeserver/daemon/CleanerSession.cpp +++ b/tapeserver/castor/tape/tapeserver/daemon/CleanerSession.cpp @@ -15,9 +15,13 @@ * submit itself to any jurisdiction. */ +#include <exception> +#include <list> +#include <vector> + #include "castor/tape/tapeserver/daemon/CleanerSession.hpp" +#include "castor/tape/tapeserver/file/HeaderChecker.hpp" #include "catalogue/Catalogue.hpp" -#include <exception> //------------------------------------------------------------------------------ // constructor @@ -64,14 +68,14 @@ castor::tape::tapeserver::daemon::Session::EndOfSessionAction errorMessage = "Caught an unknown exception"; } - //Reaching this point means the cleaner failed and an exception was thrown + // Reaching this point means the cleaner failed and an exception was thrown std::list<cta::log::Param> params = { cta::log::Param("tapeVid", m_vid), cta::log::Param("tapeDrive", m_driveConfig.unitName), cta::log::Param("message", errorMessage)}; m_log(cta::log::ERR, "Cleaner failed. Putting the drive down.", params); - - //Putting the drive down + + // Putting the drive down try { setDriveDownAfterCleanerFailed(std::string("Cleaner failed. ") + errorMessage); } catch(const cta::exception::Exception &ex) { @@ -81,29 +85,30 @@ castor::tape::tapeserver::daemon::Session::EndOfSessionAction cta::log::Param("message", ex.getMessageValue())}; m_log(cta::log::ERR, "Cleaner failed. Failed to put the drive down", params); } - + return MARK_DRIVE_AS_DOWN; } 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 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); + + 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); + driveState.setReasonFromLogMsg(cta::log::ERR, errorMsg); m_scheduler.setDesiredDriveState(cliId, m_driveConfig.unitName, driveState, lc); } @@ -112,7 +117,6 @@ void castor::tape::tapeserver::daemon::CleanerSession::setDriveDownAfterCleanerF //------------------------------------------------------------------------------ castor::tape::tapeserver::daemon::Session::EndOfSessionAction castor::tape::tapeserver::daemon::CleanerSession::exceptionThrowingExecute() { - setProcessCapabilities("cap_sys_rawio+ep"); std::unique_ptr<drive::DriveInterface> drivePtr = createDrive(); @@ -122,50 +126,49 @@ castor::tape::tapeserver::daemon::Session::EndOfSessionAction cleanDrive(drive); } catch(...) { logAndClearTapeAlerts(drive); - //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 + // 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){ + } catch (cta::exception::Exception &ex) { currentExceptionMsg = ex.getMessageValue(); - } catch(std::exception & ex){ + } 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("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); + 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; admin.username = c_defaultUserNameUpdate + " " + tapeDrive; admin.host = hostname; - - try{ - std::string disabledReason = cta::utils::getCurrentLocalTime("%F %T") + ":" + currentExceptionMsg; + + try { + std::string disabledReason = cta::utils::getCurrentLocalTime("%F %T") + ":" + currentExceptionMsg; m_catalogue.setTapeDisabled(admin, m_vid, disabledReason); } catch(cta::exception::Exception &ex) { - cta::log::Param param("exceptionMsg",ex.getMessageValue()); + 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); + m_log(cta::log::ERR, "In CleanerSession::exceptionThrowingExecute(), failed to disable the tape.", params); } } throw; } - + logAndClearTapeAlerts(drive); return MARK_DRIVE_AS_UP; } @@ -174,16 +177,16 @@ castor::tape::tapeserver::daemon::Session::EndOfSessionAction // cleanDrive //------------------------------------------------------------------------------ void castor::tape::tapeserver::daemon::CleanerSession::cleanDrive(drive::DriveInterface &drive) { - if(m_waitMediaInDrive) { + if (m_waitMediaInDrive) { waitUntilMediaIsReady(drive); } - if(!drive.hasTapeInPlace()) { + if (!drive.hasTapeInPlace()) { std::list<cta::log::Param> params; params.push_back(cta::log::Param("tapeVid", m_vid)); params.push_back(cta::log::Param("tapeDrive", m_driveConfig.unitName)); m_log(cta::log::INFO, "Cleaner found tape drive empty", params); - return; //return immediately if there is no tape + return; // return immediately if there is no tape } m_encryptionControl.disable(drive); @@ -205,19 +208,18 @@ void castor::tape::tapeserver::daemon::CleanerSession::cleanDrive(drive::DriveIn //------------------------------------------------------------------------------ void castor::tape::tapeserver::daemon::CleanerSession::logAndClearTapeAlerts(drive::DriveInterface &drive) throw() { std::string errorMessage; - try{ + 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) - { + for (std::vector<std::string>::iterator ta = tapeAlerts.begin(); ta != tapeAlerts.end(); ++ta) { std::list<cta::log::Param> params = { - cta::log::Param("tapeAlert",*ta), + 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); + m_log(cta::log::WARNING, "Tape alert detected", params); } } return; @@ -234,7 +236,7 @@ void castor::tape::tapeserver::daemon::CleanerSession::logAndClearTapeAlerts(dri cta::log::Param("tapeVid", m_vid), cta::log::Param("tapeDrive", m_driveConfig.unitName), cta::log::Param("message", errorMessage)}; - m_log(cta::log::ERR, "Cleaner failed getting tape alerts from the drive", params); + m_log(cta::log::ERR, "Cleaner failed getting tape alerts from the drive", params); } //------------------------------------------------------------------------------ @@ -258,25 +260,24 @@ std::unique_ptr<castor::tape::tapeserver::drive::DriveInterface> castor::tape::tapeserver::daemon::CleanerSession::createDrive() { SCSI::DeviceVector dv(m_sysWrapper); SCSI::DeviceInfo driveInfo = dv.findBySymlink(m_driveConfig.devFilename); - + // Instantiate the drive object std::unique_ptr<castor::tape::tapeserver::drive::DriveInterface> drive(drive::createDrive(driveInfo, m_sysWrapper)); - if(nullptr == drive.get()) { + if (nullptr == drive.get()) { cta::exception::Exception ex; ex.getMessage() << "Failed to instantiate drive object"; throw ex; - } - + } + return drive; } //------------------------------------------------------------------------------ // waitUntilDriveIsReady //------------------------------------------------------------------------------ -void castor::tape::tapeserver::daemon::CleanerSession::waitUntilMediaIsReady( - drive::DriveInterface &drive) { +void castor::tape::tapeserver::daemon::CleanerSession::waitUntilMediaIsReady(drive::DriveInterface &drive) { std::list<cta::log::Param> params; params.push_back(cta::log::Param("tapeVid", m_vid)); params.push_back(cta::log::Param("tapeDrive", m_driveConfig.unitName)); @@ -297,8 +298,7 @@ void castor::tape::tapeserver::daemon::CleanerSession::waitUntilMediaIsReady( //------------------------------------------------------------------------------ // rewindDrive //------------------------------------------------------------------------------ -void castor::tape::tapeserver::daemon::CleanerSession::rewindDrive( - drive::DriveInterface &drive) { +void castor::tape::tapeserver::daemon::CleanerSession::rewindDrive(drive::DriveInterface &drive) { std::list<cta::log::Param> params; params.push_back(cta::log::Param("tapeVid", m_vid)); params.push_back(cta::log::Param("tapeDrive", m_driveConfig.unitName)); @@ -311,14 +311,13 @@ void castor::tape::tapeserver::daemon::CleanerSession::rewindDrive( //------------------------------------------------------------------------------ // checkTapeContainsData //------------------------------------------------------------------------------ -void castor::tape::tapeserver::daemon::CleanerSession::checkTapeContainsData( - drive::DriveInterface &drive) { +void castor::tape::tapeserver::daemon::CleanerSession::checkTapeContainsData(drive::DriveInterface &drive) { std::list<cta::log::Param> params; params.push_back(cta::log::Param("tapeVid", m_vid)); params.push_back(cta::log::Param("tapeDrive", m_driveConfig.unitName)); m_log(cta::log::INFO, "Cleaner checking tape contains data", params); - if(drive.isTapeBlank()) { + if (drive.isTapeBlank()) { cta::exception::Exception ex; ex.getMessage() << "Tape is completely blank when it should be labeled"; throw ex; @@ -331,45 +330,35 @@ void castor::tape::tapeserver::daemon::CleanerSession::checkTapeContainsData( //------------------------------------------------------------------------------ std::string castor::tape::tapeserver::daemon::CleanerSession::checkVolumeLabel( drive::DriveInterface &drive) { - tapeFile::VOL1 vol1; std::list<cta::log::Param> params; params.push_back(cta::log::Param("tapeVid", m_vid)); params.push_back(cta::log::Param("tapeDrive", m_driveConfig.unitName)); - - try { - drive.readExactBlock((void * )&vol1, sizeof(vol1), - "[CleanerSession::clean()] - Reading header VOL1"); - vol1.verify(); - - const std::string &volumeLabelVSN = vol1.getVSN(); - params.push_back(cta::log::Param("volumeLabelVSN", volumeLabelVSN)); - - m_log(cta::log::INFO, "Cleaner read VSN from volume label", params); - - // If the cleaner was given a VID - if(!m_vid.empty()) { - if(m_vid == volumeLabelVSN) { - m_log(cta::log::INFO, "Cleaner detected volume label contains expected VSN", - params); - } else { - m_log(cta::log::WARNING, - "Cleaner detected volume label does not contain expected VSN", params); - } - } - return volumeLabelVSN; - } catch(cta::exception::Exception &ne) { - cta::exception::Exception ex; - ex.getMessage() << "Failed to check volume label: " << ne.getMessage().str(); - throw ex; + using LabelFormat = cta::common::dataStructures::Label::Format; + const LabelFormat labelFormat = m_catalogue.getTapeLabelFormat(m_vid); + + const std::string volumeLabelVSN = tapeFile::HeaderChecker::checkVolumeLabel(drive, labelFormat); + + params.push_back(cta::log::Param("volumeLabelVSN", volumeLabelVSN)); + + m_log(cta::log::INFO, "Cleaner read VSN from volume label", params); + + // If the cleaner was given a VID + if (!m_vid.empty()) { + if (m_vid == volumeLabelVSN) { + m_log(cta::log::INFO, "Cleaner detected volume label contains expected VSN", params); + } else { + m_log(cta::log::WARNING, "Cleaner detected volume label does not contain expected VSN", params); + } } + return volumeLabelVSN; } //------------------------------------------------------------------------------ // unloadTape //------------------------------------------------------------------------------ -void castor::tape::tapeserver::daemon::CleanerSession::unloadTape( - const std::string &vid, drive::DriveInterface &drive) { +void castor::tape::tapeserver::daemon::CleanerSession::unloadTape(const std::string &vid, + drive::DriveInterface &drive) { const cta::mediachanger::LibrarySlot &librarySlot = m_driveConfig.librarySlot(); std::list<cta::log::Param> params; params.push_back(cta::log::Param("tapeVid", vid)); @@ -391,8 +380,7 @@ void castor::tape::tapeserver::daemon::CleanerSession::unloadTape( //------------------------------------------------------------------------------ // dismountTape //------------------------------------------------------------------------------ -void castor::tape::tapeserver::daemon::CleanerSession::dismountTape( - const std::string &vid) { +void castor::tape::tapeserver::daemon::CleanerSession::dismountTape(const std::string &vid) { const cta::mediachanger::LibrarySlot &librarySlot = m_driveConfig.librarySlot(); std::list<cta::log::Param> params; params.push_back(cta::log::Param("tapeVid", vid)); diff --git a/tapeserver/castor/tape/tapeserver/daemon/CleanerSession.hpp b/tapeserver/castor/tape/tapeserver/daemon/CleanerSession.hpp index c609e71a1b..7cb05873a1 100644 --- a/tapeserver/castor/tape/tapeserver/daemon/CleanerSession.hpp +++ b/tapeserver/castor/tape/tapeserver/daemon/CleanerSession.hpp @@ -17,230 +17,228 @@ #pragma once +#include <memory> +#include <string> + +#include "catalogue/Catalogue.hpp" #include "common/log/LogContext.hpp" #include "common/log/Logger.hpp" #include "common/processCap/ProcessCap.hpp" #include "mediachanger/MediaChangerFacade.hpp" -#include "Session.hpp" -#include "tapeserver/daemon/TpconfigLine.hpp" +#include "scheduler/Scheduler.hpp" +#include "tapeserver/castor/tape/tapeserver/daemon/EncryptionControl.hpp" +#include "tapeserver/castor/tape/tapeserver/daemon/Session.hpp" #include "tapeserver/castor/tape/tapeserver/drive/DriveInterface.hpp" #include "tapeserver/castor/tape/tapeserver/file/Structures.hpp" #include "tapeserver/castor/tape/tapeserver/SCSI/Device.hpp" -#include "tapeserver/castor/tape/tapeserver/daemon/EncryptionControl.hpp" -#include "catalogue/Catalogue.hpp" -#include "scheduler/Scheduler.hpp" - -#include <memory> +#include "tapeserver/daemon/TpconfigLine.hpp" namespace castor { namespace tape { namespace tapeserver { namespace daemon { +/** + * Class responsible for cleaning up a tape drive left in a (possibly) dirty state. + */ +class CleanerSession : public Session { +public: + /** + * Constructor + * + * @param capUtils Object providing support for UNIX capabilities. + * @param mc Object representing the media changer. + * @param log Object representing the API to the CASTOR logging system. + * @param driveConfig Configuration of the tape drive to be cleaned. + * @param sysWrapper Object representing the operating system. + * @param vid The volume identifier of the mounted tape if known, + * else the empty string. + * @param waitMediaInDrive true if we want to check the presence of the media in the drive before cleaning, + * false otherwise. + * @param waitMediaInDriveTimeout The maximum number of seconds to wait for + * the media to be ready for operations inside the drive. + * @param externalEncryptionKeyScript path to the operator provided script + * for encryption control. + * @param catalogue the CTA catalogue + */ + CleanerSession( + cta::server::ProcessCap &capUtils, + cta::mediachanger::MediaChangerFacade &mc, + cta::log::Logger &log, + const cta::tape::daemon::TpconfigLine &driveConfig, + System::virtualWrapper &sysWrapper, + const std::string &vid, + const bool waitMediaInDrive, + const uint32_t waitMediaInDriveTimeout, + const std::string & externalEncryptionKeyScript, + cta::catalogue::Catalogue & catalogue, + cta::Scheduler & scheduler); + + /** + * Execute the session and return the type of action to be performed + * immediately after the session has completed. + * + * @return Returns the type of action to be performed after the session has + * completed. + */ + EndOfSessionAction execute() throw(); + +private: + /** + * Object providing support for UNIX capabilities. + */ + cta::server::ProcessCap &m_capUtils; + + /** + * The object representing the media changer. + */ + cta::mediachanger::MediaChangerFacade &m_mc; + + /** + * The logging object + */ + cta::log::Logger & m_log; + + /** + * The configuration of the tape drive to be cleaned. + */ + const cta::tape::daemon::TpconfigLine m_driveConfig; + + /** + * The system wrapper used to find the device and instantiate the drive object + */ + System::virtualWrapper & m_sysWrapper; + + /** + * The volume identifier of the mounted tape if known, else the empty + * string. + */ + const std::string m_vid; + + /** + * true if we want to check the presence of the media in the drive before cleaning, + * false otherwise. + */ + const bool m_waitMediaInDrive; + + /** + * The maximum number of seconds to wait for + * the media to be ready for operations inside the drive. + */ + const uint32_t m_tapeLoadTimeout; + + /** + * Encryption helper object + */ + EncryptionControl m_encryptionControl; + + /** + * CTA catalogue + */ + cta::catalogue::Catalogue & m_catalogue; + + /** + * CTA scheduler + */ + cta::Scheduler & m_scheduler; + + /** + * Variable used to log UPDATE_USER_NAME in the DB + */ + const std::string c_defaultUserNameUpdate = "cta-taped"; + + /** + * Execute the session and return the type of action to be performed + * immediately after the session has completed. + * + * @return Returns the type of action to be performed after the session has + * completed. + */ + EndOfSessionAction exceptionThrowingExecute(); + + /** + * Logs and clears (just by reading them...) any outstanding tape alerts + * + * @param drive The tape drive. + */ + void logAndClearTapeAlerts(drive::DriveInterface &drive) throw(); + + /** + * Does the actual steps to clean the drive + * + * @param drive The tape drive. + */ + void cleanDrive(drive::DriveInterface &drive); + + /** + * Sets the capabilities of the process and logs the result. + * + * @param capabilities The string representation of the capabilities. + */ + void setProcessCapabilities(const std::string &capabilities); + + /** + * Creates and returns the object that represents the tape drive to be + * cleaned. + * + * @return The tape drive. + */ + std::unique_ptr<drive::DriveInterface> createDrive(); + + /** + * Waits for the specified drive to be ready. + * + * @param drive The tape drive. + */ + void waitUntilMediaIsReady(drive::DriveInterface &drive); + + /** + * Rewinds the specified tape drive. + * + * @param drive The tape drive. + */ + void rewindDrive(drive::DriveInterface &drive); + + /** + * Checks the tape in the specified tape drive contains some data where no + * data means the tape does not even contain a volume label. + * + * @param drive The tape drive. + */ + void checkTapeContainsData(drive::DriveInterface &drive); + + /** + * Checks that the tape in the specified drive contains a valid volume + * label. + * + * @param drive The tape drive for which it is assumed the tape to be + * tested is present and rewound to the beginning. + * @return The VSN stored within the colue label. + */ + std::string checkVolumeLabel(drive::DriveInterface &drive); + + /** + * Unloads the specified tape from the specified tape drive. + * + * @param vid The volume identifier of the tape to be unloaded. Please note + * that the value of this field is only used for logging purposes. + * @param drive The tape drive. + */ + void unloadTape(const std::string &vid, drive::DriveInterface &drive); + + /** + * Dismounts the specified tape. + * + * @param vid The volume identifier of the tape to be dismounted. + */ + void dismountTape(const std::string &vid); + /** - * Class responsible for cleaning up a tape drive left in a (possibly) dirty state. - */ - class CleanerSession : public Session { - - public: - /** - * Constructor - * - * @param capUtils Object providing support for UNIX capabilities. - * @param mc Object representing the media changer. - * @param log Object representing the API to the CASTOR logging system. - * @param driveConfig Configuration of the tape drive to be cleaned. - * @param sysWrapper Object representing the operating system. - * @param vid The volume identifier of the mounted tape if known, - * else the empty string. - * @param waitMediaInDrive true if we want to check the presence of the media in the drive before cleaning, - * false otherwise. - * @param waitMediaInDriveTimeout The maximum number of seconds to wait for - * the media to be ready for operations inside the drive. - * @param externalEncryptionKeyScript path to the operator provided script - * for encryption control. - * @param catalogue the CTA catalogue - */ - CleanerSession( - cta::server::ProcessCap &capUtils, - cta::mediachanger::MediaChangerFacade &mc, - cta::log::Logger &log, - const cta::tape::daemon::TpconfigLine &driveConfig, - System::virtualWrapper &sysWrapper, - const std::string &vid, - const bool waitMediaInDrive, - const uint32_t waitMediaInDriveTimeout, - const std::string & externalEncryptionKeyScript, - cta::catalogue::Catalogue & catalogue, - cta::Scheduler & scheduler); - - /** - * Execute the session and return the type of action to be performed - * immediately after the session has completed. - * - * @return Returns the type of action to be performed after the session has - * completed. - */ - EndOfSessionAction execute() throw(); - - private: - - /** - * Object providing support for UNIX capabilities. - */ - cta::server::ProcessCap &m_capUtils; - - /** - * The object representing the media changer. - */ - cta::mediachanger::MediaChangerFacade &m_mc; - - /** - * The logging object - */ - cta::log::Logger & m_log; - - /** - * The configuration of the tape drive to be cleaned. - */ - const cta::tape::daemon::TpconfigLine m_driveConfig; - - /** - * The system wrapper used to find the device and instantiate the drive object - */ - System::virtualWrapper & m_sysWrapper; - - /** - * The volume identifier of the mounted tape if known, else the empty - * string. - */ - const std::string m_vid; - - /** - * true if we want to check the presence of the media in the drive before cleaning, - * false otherwise. - */ - const bool m_waitMediaInDrive; - - /** - * The maximum number of seconds to wait for - * the media to be ready for operations inside the drive. - */ - const uint32_t m_tapeLoadTimeout; - - /** - * Encryption helper object - */ - EncryptionControl m_encryptionControl; - - /** - * CTA catalogue - */ - cta::catalogue::Catalogue & m_catalogue; - - /** - * CTA scheduler - */ - cta::Scheduler & m_scheduler; - - /** - * Variable used to log UPDATE_USER_NAME in the DB - */ - const std::string c_defaultUserNameUpdate = "cta-taped"; - - /** - * Execute the session and return the type of action to be performed - * immediately after the session has completed. - * - * @return Returns the type of action to be performed after the session has - * completed. - */ - EndOfSessionAction exceptionThrowingExecute(); - - /** - * Logs and clears (just by reading them...) any outstanding tape alerts - * - * @param drive The tape drive. - */ - void logAndClearTapeAlerts(drive::DriveInterface &drive) throw(); - - /** - * Does the actual steps to clean the drive - * - * @param drive The tape drive. - */ - void cleanDrive(drive::DriveInterface &drive); - - /** - * Sets the capabilities of the process and logs the result. - * - * @param capabilities The string representation of the capabilities. - */ - void setProcessCapabilities(const std::string &capabilities); - - /** - * Creates and returns the object that represents the tape drive to be - * cleaned. - * - * @return The tape drive. - */ - std::unique_ptr<drive::DriveInterface> createDrive(); - - /** - * Waits for the specified drive to be ready. - * - * @param drive The tape drive. - */ - void waitUntilMediaIsReady(drive::DriveInterface &drive); - - /** - * Rewinds the specified tape drive. - * - * @param drive The tape drive. - */ - void rewindDrive(drive::DriveInterface &drive); - - /** - * Checks the tape in the specified tape drive contains some data where no - * data means the tape does not even contain a volume label. - * - * @param drive The tape drive. - */ - void checkTapeContainsData(drive::DriveInterface &drive); - - /** - * Checks that the tape in the specified drive contains a valid volume - * label. - * - * @param drive The tape drive for which it is assumed the tape to be - * tested is present and rewound to the beginning. - * @return The VSN stored within the colue label. - */ - std::string checkVolumeLabel(drive::DriveInterface &drive); - - /** - * Unloads the specified tape from the specified tape drive. - * - * @param vid The volume identifier of the tape to be unloaded. Please note - * that the value of this field is only used for logging purposes. - * @param drive The tape drive. - */ - void unloadTape(const std::string &vid, drive::DriveInterface &drive); - - /** - * Dismounts the specified tape. - * - * @param vid The volume identifier of the tape to be dismounted. - */ - void dismountTape(const std::string &vid); - - /** - * Put the drive down in case the Cleaner has failed - */ - void setDriveDownAfterCleanerFailed(const std::string & errorMsg); - - }; // class CleanerSession - -} // namespace daemon -} // namespace tapeserver -} // namespace tape -} // namespace castor + * Put the drive down in case the Cleaner has failed + */ + void setDriveDownAfterCleanerFailed(const std::string & errorMsg); +}; // class CleanerSession + +} // namespace daemon +} // namespace tapeserver +} // namespace tape +} // namespace castor diff --git a/tapeserver/castor/tape/tapeserver/file/CMakeLists.txt b/tapeserver/castor/tape/tapeserver/file/CMakeLists.txt index 3e42441adf..103df19e08 100644 --- a/tapeserver/castor/tape/tapeserver/file/CMakeLists.txt +++ b/tapeserver/castor/tape/tapeserver/file/CMakeLists.txt @@ -20,6 +20,8 @@ include_directories(/usr/include/shift) include_directories(${CMAKE_SOURCE_DIR}/tapeserver) include_directories (${XROOTD_INCLUDE_DIR}) +find_package(Protobuf3 REQUIRED) + set(TAPESERVER_FILE_LIBRARY_SRCS CtaFileReader.cpp CtaReadSession.cpp @@ -69,3 +71,17 @@ set_property(TARGET ctatapeserverfileunittests PROPERTY SOVERSION "${CTA_SOVERSI set_property(TARGET ctatapeserverfileunittests PROPERTY VERSION "${CTA_LIBVERSION}") install(TARGETS ctatapeserverfileunittests DESTINATION usr/${CMAKE_INSTALL_LIBDIR}) + +include_directories(${PROTOBUF3_INCLUDE_DIRS}) +add_executable(cta-osmReaderTest + OsmReaderTest.cpp) + +target_link_libraries(cta-osmReaderTest + ctacommon + ctaTapeServerDaemon + File + gtest +) +set_property(TARGET cta-osmReaderTest APPEND PROPERTY INSTALL_RPATH ${PROTOBUF3_RPATH}) + +install(TARGETS cta-osmReaderTest DESTINATION usr/bin) \ No newline at end of file diff --git a/tapeserver/castor/tape/tapeserver/file/HeaderChecker.cpp b/tapeserver/castor/tape/tapeserver/file/HeaderChecker.cpp index 0fc6a30f08..95f710c3eb 100644 --- a/tapeserver/castor/tape/tapeserver/file/HeaderChecker.cpp +++ b/tapeserver/castor/tape/tapeserver/file/HeaderChecker.cpp @@ -19,8 +19,10 @@ #include <string> #include "castor/tape/tapeserver/daemon/VolumeInfo.hpp" +#include "castor/tape/tapeserver/drive/DriveInterface.hpp" #include "castor/tape/tapeserver/file/Exceptions.hpp" #include "castor/tape/tapeserver/file/HeaderChecker.hpp" +#include "castor/tape/tapeserver/file/OsmFileStructure.hpp" #include "castor/tape/tapeserver/file/Structures.hpp" #include "scheduler/RetrieveJob.hpp" @@ -37,6 +39,15 @@ void HeaderChecker::checkVOL1(const VOL1 &vol1, const std::string &volId) { } } +void HeaderChecker::checkOSM(const osm::LABEL &osmLabel, const std::string &volId) { + if (osmLabel.name().compare(volId) != 0) { + std::stringstream ex_str; + ex_str << "[OsmReadSession::OsmReadSession()] - VSN of tape (" << osmLabel.name() + << ") is not the one requested (" << volId << ")"; + throw TapeFormatError(ex_str.str()); + } +} + bool HeaderChecker::checkHeaderNumericalField(const std::string &headerField, const uint64_t value, const HeaderBase& base) { uint64_t res = 0; @@ -103,6 +114,56 @@ void HeaderChecker::checkUTL1(const UTL1 &utl1, const uint32_t fSeq) { } } +std::string HeaderChecker::checkVolumeLabel(tapeserver::drive::DriveInterface &drive, LabelFormat labelFormat) { + std::string volumeLabelVSN; + auto vol1Label = [](tapeserver::drive::DriveInterface &drive, const std::string &expectedLblStandard) { + tapeFile::VOL1 vol1; + drive.readExactBlock(reinterpret_cast<void *>(&vol1), sizeof(vol1), + "[HeaderChecker::checkVolumeLabel()] - Reading header VOL1"); + vol1.verify(expectedLblStandard.c_str()); + return vol1.getVSN(); + }; + + auto osmLabel = [](tapeserver::drive::DriveInterface &drive) { + tapeFile::osm::LABEL osmLabel; + drive.readExactBlock(reinterpret_cast<void*>(osmLabel.rawLabel()), + tapeFile::osm::LIMITS::MAXMRECSIZE, + "[HeaderChecker::checkVolumeLabel()] - Reading OSM label - part 1"); + drive.readExactBlock(reinterpret_cast<void*>(osmLabel.rawLabel() + tapeFile::osm::LIMITS::MAXMRECSIZE), + tapeFile::osm::LIMITS::MAXMRECSIZE, + "[HeaderChecker::checkVolumeLabel()] - Reading OSM label - part 2"); + osmLabel.decode(); + return osmLabel.name(); + }; + + try { + switch (labelFormat) { + case LabelFormat::CTA: + volumeLabelVSN = vol1Label(drive, "3"); + break; + case LabelFormat::OSM: + volumeLabelVSN = osmLabel(drive); + break; + case LabelFormat::Enstore: + volumeLabelVSN = vol1Label(drive, "0"); + break; + default: { + cta::exception::Exception ex; + ex.getMessage() << "In HeaderChecker::checkVolumeLabel(): unknown label format: "; + ex.getMessage() << std::showbase << std::internal << std::setfill('0') << std::hex << std::setw(4) + << static_cast<unsigned int>(labelFormat); + throw ex; + } + } + } catch(cta::exception::Exception &ne) { + std::ostringstream ex_str; + ex_str << "Failed to check volume label: " << ne.getMessageValue(); + throw TapeFormatError(ex_str.str()); + } + + return volumeLabelVSN; +} + } // namespace tapeFile } // namespace tape } // namespace castor diff --git a/tapeserver/castor/tape/tapeserver/file/HeaderChecker.hpp b/tapeserver/castor/tape/tapeserver/file/HeaderChecker.hpp index 894e594616..81ae2d4b05 100644 --- a/tapeserver/castor/tape/tapeserver/file/HeaderChecker.hpp +++ b/tapeserver/castor/tape/tapeserver/file/HeaderChecker.hpp @@ -19,6 +19,8 @@ #include <string> +#include "common/dataStructures/LabelFormat.hpp" + namespace cta { class RetrieveJob; } @@ -30,6 +32,10 @@ namespace tapeserver { namespace daemon { class VolumeInfo; } + +namespace drive { +class DriveInterface; +} } namespace tapeFile { @@ -39,6 +45,10 @@ class UHL1; class UTL1; class VOL1; +namespace osm { +class LABEL; +} + class HeaderChecker { public: /** @@ -72,6 +82,16 @@ public: */ static void checkVOL1(const VOL1 &vol1, const std::string &volId); + /** + * Checks the osm::LABEL + * @param osm: the osm header of the current file + * @param volId: the volume id of the tape in the drive + */ + static void checkOSM(const osm::LABEL &osmLabel, const std::string &volId); + + using LabelFormat = cta::common::dataStructures::Label::Format; + static std::string checkVolumeLabel(tapeserver::drive::DriveInterface &drive, LabelFormat labelFormat); + private: enum class HeaderBase { octal, diff --git a/tapeserver/castor/tape/tapeserver/file/OsmFileReader.cpp b/tapeserver/castor/tape/tapeserver/file/OsmFileReader.cpp index 54675a14f5..9c5cefa790 100644 --- a/tapeserver/castor/tape/tapeserver/file/OsmFileReader.cpp +++ b/tapeserver/castor/tape/tapeserver/file/OsmFileReader.cpp @@ -21,7 +21,6 @@ #include <string> #include "castor/tape/tapeserver/drive/DriveInterface.hpp" -#include "castor/tape/tapeserver/file/HeaderChecker.hpp" #include "castor/tape/tapeserver/file/OsmFileReader.hpp" #include "castor/tape/tapeserver/file/OsmReadSession.hpp" #include "castor/tape/tapeserver/file/Structures.hpp" @@ -80,9 +79,9 @@ void OsmFileReader::positionByBlockID(const cta::RetrieveJob &fileToRecall) { } void OsmFileReader::moveToFirstFile() { - // special case: we can rewind the tape to be faster - //(TODO: in the future we could also think of a threshold above - //which we rewind the tape anyway and then space forward) + // special case: we can rewind the tape to be faster + // (TODO: in the future we could also think of a threshold above + // which we rewind the tape anyway and then space forward) m_session->m_drive.rewind(); osm::LABEL osmLabel; m_session->m_drive.readExactBlock(reinterpret_cast<void*>(osmLabel.rawLabel()), osm::LIMITS::MAXMRECSIZE, "[FileReader::position] - Reading OSM label - part 1"); @@ -145,22 +144,24 @@ size_t OsmFileReader::readNextDataBlock(void *data, const size_t size) { * Cpio filter for OSM file * - caclulate the file size and position of the trailer */ - if(size < CPIO::MAXHEADERSIZE) { + if (size < CPIO::MAXHEADERSIZE) { std::ostringstream ex_str; - ex_str << "Invalid block size: " << size << " - " << "the block size is smaller then max size of a CPIO header: " << CPIO::MAXHEADERSIZE; + ex_str << "Invalid block size: " << size << " - " + << "the block size is smaller then max size of a CPIO header: " + << CPIO::MAXHEADERSIZE; throw TapeFormatError(ex_str.str()); } - if(!m_cpioHeader.valid()) { + if (!m_cpioHeader.valid()) { size_t uiHeaderSize = 0; size_t uiResiduesSize = 0; uint8_t* pucTmpData = new uint8_t[size]; - + bytes_read = m_session->m_drive.readBlock(pucTmpData, size); uiHeaderSize = m_cpioHeader.decode(pucTmpData, size); uiResiduesSize = bytes_read - uiHeaderSize; - + // Copy the rest of data to the buffer - if(uiResiduesSize >= m_cpioHeader.m_ui64FileSize) { + if (uiResiduesSize >= m_cpioHeader.m_ui64FileSize) { bytes_read = m_cpioHeader.m_ui64FileSize; m_ui64CPIODataSize = bytes_read; memcpy(data, pucTmpData + uiHeaderSize, bytes_read); @@ -170,26 +171,25 @@ size_t OsmFileReader::readNextDataBlock(void *data, const size_t size) { m_ui64CPIODataSize = bytes_read >= m_cpioHeader.m_ui64FileSize ? m_cpioHeader.m_ui64FileSize : bytes_read; } delete[] pucTmpData; - } else { bytes_read = m_session->m_drive.readBlock(data, size); m_ui64CPIODataSize += bytes_read; - if(m_ui64CPIODataSize > m_cpioHeader.m_ui64FileSize && bytes_read > 0) { + if (m_ui64CPIODataSize > m_cpioHeader.m_ui64FileSize && bytes_read > 0) { // File is ready // size_t uiOldSize = bytes_read; bytes_read = bytes_read - (m_ui64CPIODataSize - m_cpioHeader.m_ui64FileSize); // m_ui64CPIODataSize += bytes_read - uiOldSize; } } - + // end of file reached! keep reading until the header of the next file - if(!bytes_read) { - m_session->setCurrentFseq(m_session->getCurrentFseq() + 1); // moving on to the header of the next file + if (!bytes_read) { + m_session->setCurrentFseq(m_session->getCurrentFseq() + 1); // moving on to the header of the next file m_session->setCurrentFilePart(PartOfFile::Header); // the following is a normal day exception: end of files exceptions are thrown at the end of each file being read throw EndOfFile(); } - + return bytes_read; } diff --git a/tapeserver/castor/tape/tapeserver/file/OsmFileStructure.hpp b/tapeserver/castor/tape/tapeserver/file/OsmFileStructure.hpp index cde57f3bbe..3d4bda8157 100644 --- a/tapeserver/castor/tape/tapeserver/file/OsmFileStructure.hpp +++ b/tapeserver/castor/tape/tapeserver/file/OsmFileStructure.hpp @@ -16,12 +16,13 @@ */ #pragma once - + +#include <string> + #include "tapeserver/castor/tape/tapeserver/SCSI/Structures.hpp" #include "common/Constants.hpp" -#include <string> - + namespace castor { namespace tape { /** @@ -38,10 +39,10 @@ namespace osm { struct LIMITS { public: static const uint64_t MVOLMAGIC = 0x070460; - static const size_t LABELVERSIONLEN = 9; // label version string - static const size_t VOLNAMELEN = 16; // volume name - static const size_t CIDLEN = 33; // client id length - static const size_t MAXMRECSIZE = 32768; // half of label block + static const size_t LABELVERSIONLEN = 9; // label version string + static const size_t VOLNAMELEN = 16; // volume name + static const size_t CIDLEN = 33; // client id length + static const size_t MAXMRECSIZE = 32768; // half of label block static const size_t MMAXCHK = 2048; // prevent the generation of default public constructor and destructor @@ -63,8 +64,8 @@ public: inline char* rawLabel() { return m_tcRawLabel; } - - void decode(); // can throw + + void decode(); // can throw inline std::string owner() { return std::string(m_tcOwner); @@ -72,8 +73,8 @@ public: inline std::string version() { return std::string(m_tcVersion); } - inline std::string name() { - // Returns truncated name of the label + inline std::string name() const { + // Returns truncated name of the label return std::string(m_tcName, cta::CA_MAXVIDLEN); } inline uint64_t createTime() { @@ -96,7 +97,6 @@ public: // SCSI::logicBlockProtectionMethod::DoNotUse // SCSI::logicBlockProtectionMethod::ReedSolomon // SCSI::logicBlockProtectionMethod::CRC32C - return SCSI::logicBlockProtectionMethod::CRC32C; } @@ -106,11 +106,11 @@ protected: char m_tcName[LIMITS::VOLNAMELEN]; char m_tcRawLabel[2 * LIMITS::MAXMRECSIZE]; uint64_t m_ulCreateTime; - uint64_t m_ulExpireTime; + uint64_t m_ulExpireTime; uint64_t m_ulRecSize; uint64_t m_ulVolId; }; - + } /* namespace osm */ } /* namespace tapeFile */ } /* namespace tape */ diff --git a/tapeserver/castor/tape/tapeserver/file/OsmReadSession.cpp b/tapeserver/castor/tape/tapeserver/file/OsmReadSession.cpp index 237a3e3aab..fb9f8cd86c 100644 --- a/tapeserver/castor/tape/tapeserver/file/OsmReadSession.cpp +++ b/tapeserver/castor/tape/tapeserver/file/OsmReadSession.cpp @@ -19,9 +19,10 @@ #include <string> #include "castor/tape/tapeserver/file/Exceptions.hpp" +#include "castor/tape/tapeserver/file/HeaderChecker.hpp" +#include "castor/tape/tapeserver/file/OsmFileStructure.hpp" #include "castor/tape/tapeserver/file/OsmReadSession.hpp" #include "castor/tape/tapeserver/file/Structures.hpp" -#include "castor/tape/tapeserver/file/OsmFileStructure.hpp" namespace castor { namespace tape { @@ -30,24 +31,18 @@ namespace tapeFile { OsmReadSession::OsmReadSession(tapeserver::drive::DriveInterface &drive, const tapeserver::daemon::VolumeInfo &volInfo, const bool useLbp) : ReadSession(drive, volInfo, useLbp) { - - if (!m_vid.compare("")) { - throw cta::exception::InvalidArgument(); - } - if (m_drive.isTapeBlank()) { - cta::exception::Exception ex; - ex.getMessage() << "[OsmReadSession::OsmReadSession()] - Tape is blank, cannot proceed with constructing the ReadSession"; - throw ex; - } - m_drive.rewind(); m_drive.disableLogicalBlockProtection(); uint8_t uiLLBPMethod = SCSI::logicBlockProtectionMethod::DoNotUse; osm::LABEL osmLabel; - m_drive.readExactBlock(reinterpret_cast<void*>(osmLabel.rawLabel()), osm::LIMITS::MAXMRECSIZE, "[OsmReadSession::OsmReadSession] - Reading OSM label - part 1"); - m_drive.readExactBlock(reinterpret_cast<void*>(osmLabel.rawLabel() + osm::LIMITS::MAXMRECSIZE), osm::LIMITS::MAXMRECSIZE, "[OsmReadSession::OsmReadSession] - Reading OSM label - part 2"); + m_drive.readExactBlock(reinterpret_cast<void*>(osmLabel.rawLabel()), + osm::LIMITS::MAXMRECSIZE, + "[OsmReadSession::OsmReadSession] - Reading OSM label - part 1"); + m_drive.readExactBlock(reinterpret_cast<void*>(osmLabel.rawLabel() + osm::LIMITS::MAXMRECSIZE), + osm::LIMITS::MAXMRECSIZE, + "[OsmReadSession::OsmReadSession] - Reading OSM label - part 2"); try { osmLabel.decode(); @@ -77,18 +72,19 @@ OsmReadSession::OsmReadSession(tapeserver::drive::DriveInterface &drive, } // from this point the right LBP mode should be set or not set m_drive.rewind(); - m_drive.readExactBlock(reinterpret_cast<void*>(osmLabel.rawLabel()), osm::LIMITS::MAXMRECSIZE, "[OsmReadSession::OsmReadSession] - Reading OSM label - part 1"); - m_drive.readExactBlock(reinterpret_cast<void*>(osmLabel.rawLabel() + osm::LIMITS::MAXMRECSIZE), osm::LIMITS::MAXMRECSIZE, "[OsmReadSession::OsmReadSession] - Reading OSM label - part 2"); - - try { - osmLabel.decode(); - if (osmLabel.name().compare(m_vid) != 0) { - std::stringstream ex_str; - ex_str<<"[OsmReadSession::OsmReadSession()] - VSN of tape ("<<osmLabel.name()<<") is not the one requested ("<<m_vid<<")"; - throw TapeFormatError(ex_str.str()); + { + m_drive.readExactBlock(reinterpret_cast<void*>(osmLabel.rawLabel()), + osm::LIMITS::MAXMRECSIZE, + "[OsmReadSession::OsmReadSession] - Reading OSM label - part 1"); + m_drive.readExactBlock(reinterpret_cast<void*>(osmLabel.rawLabel() + osm::LIMITS::MAXMRECSIZE), + osm::LIMITS::MAXMRECSIZE, + "[OsmReadSession::OsmReadSession] - Reading OSM label - part 2"); + try { + osmLabel.decode(); + } catch (const std::exception& e) { + throw TapeFormatError(e.what()); } - } catch (const std::exception& osmExc) { - throw TapeFormatError(osmExc.what()); + HeaderChecker::checkOSM(osmLabel, volInfo.vid); } } diff --git a/tapeserver/castor/tape/tapeserver/file/OsmReaderTest.cpp b/tapeserver/castor/tape/tapeserver/file/OsmReaderTest.cpp new file mode 100644 index 0000000000..6b456b9c2b --- /dev/null +++ b/tapeserver/castor/tape/tapeserver/file/OsmReaderTest.cpp @@ -0,0 +1,196 @@ +/* + * @project The CERN Tape Archive (CTA) + * @copyright Copyright © 2021-2022 CERN + * @license This program is free software, distributed under the terms of the GNU General Public + * Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can + * redistribute it and/or modify it under the terms of the GPL Version 3, 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. + * + * In applying this licence, CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization or + * submit itself to any jurisdiction. + */ + +#include <gtest/gtest.h> + +#include <string> + +#include "castor/tape/tapeserver/daemon/CleanerSession.hpp" +#include "castor/tape/tapeserver/daemon/VolumeInfo.hpp" +#include "castor/tape/tapeserver/drive/DriveInterface.hpp" +#include "castor/tape/tapeserver/file/Exceptions.hpp" +#include "castor/tape/tapeserver/file/FileReader.hpp" +#include "castor/tape/tapeserver/file/FileReaderFactory.hpp" +#include "castor/tape/tapeserver/file/ReadSession.hpp" +#include "castor/tape/tapeserver/file/ReadSessionFactory.hpp" +#include "castor/tape/tapeserver/SCSI/Device.hpp" +#include "castor/tape/tapeserver/system/Wrapper.hpp" +#include "scheduler/RetrieveJob.hpp" +#include "catalogue/DummyCatalogue.hpp" +#include "common/processCap/ProcessCapDummy.hpp" +#include "common/log/StdoutLogger.hpp" +#include "common/log/StringLogger.hpp" +#include "scheduler/OStoreDB/OStoreDBFactory.hpp" + +namespace { +std::string g_device_name; +std::string g_device_path; +} + +class OSMCatalogue: public cta::catalogue::DummyCatalogue { +public: + using LabelFormat = cta::common::dataStructures::Label::Format; + OSMCatalogue() = default; + + LabelFormat getTapeLabelFormat(const std::string& vid) const override { + return LabelFormat::OSM; + } +}; + +class BasicRetrieveJob: public cta::RetrieveJob { +public: + BasicRetrieveJob() : cta::RetrieveJob(nullptr, + cta::common::dataStructures::RetrieveRequest(), + cta::common::dataStructures::ArchiveFile(), 1, + cta::PositioningMethod::ByBlock) {} +}; + +class OsmReaderTest : public ::testing::Test { +protected: + OsmReaderTest() : m_sWrapper(), m_dev(), m_catalogue(std::make_unique<OSMCatalogue>()) { + cta::OStoreDBFactory<cta::objectstore::BackendVFS> factory; + } + + void SetUp() override { + m_vid = "L08033"; + m_nstDev = g_device_path; + m_devName = g_device_name; + } + + void TearDown() override { + m_db.reset(); + m_catalogue.reset(); + } + + void createDrive() { + // Create drive object and open tape device + m_dev.product = "MHVTL"; + m_dev.nst_dev = m_nstDev; + m_drive.reset(castor::tape::tapeserver::drive::createDrive(m_dev, m_sWrapper)); + + try { + /** + * Gets generic device info for the drive object. + */ + castor::tape::tapeserver::drive::deviceInfo devInfo; + devInfo = m_drive->getDeviceInfo(); + auto removeWhiteSpaces = [](std::string *str) -> std::string { + str->erase(std::remove_if(str->begin(), str->end(), [](uint8_t x){return std::isspace(x);}), str->end()); + return *str; + }; + ASSERT_EQ("STK", removeWhiteSpaces(&devInfo.vendor)); + ASSERT_EQ("MHVTL", removeWhiteSpaces(&devInfo.product)); + ASSERT_EQ("0105", devInfo.productRevisionLevel); + ASSERT_EQ(m_devName, removeWhiteSpaces(&devInfo.serialNumber)); + } catch (cta::exception::Exception & ex) { + FAIL() << "The drive couldn't be created. " << ex.getMessageValue(); + } + + try { + /** + * Checks if the drive ready to use the tape installed loaded into it. + */ + m_drive->waitUntilReady(5); + } catch(cta::exception::Exception &ex) { + FAIL() << "The drive is not ready to use. " << ex.getMessageValue(); + } + + m_drive->rewind(); + } + + castor::tape::System::realWrapper m_sWrapper; + castor::tape::SCSI::DeviceInfo m_dev; + std::unique_ptr<castor::tape::tapeserver::drive::DriveInterface> m_drive; + std::unique_ptr<cta::SchedulerDatabase> m_db; + std::unique_ptr<cta::catalogue::Catalogue> m_catalogue; + std::string m_vid; + std::string m_nstDev; + std::string m_devName; +}; + +TEST_F(OsmReaderTest, ReadOsmTape) { + createDrive(); + try { + castor::tape::tapeserver::daemon::VolumeInfo m_volInfo; + m_volInfo.vid = m_vid; + m_volInfo.nbFiles = 1; + m_volInfo.labelFormat = m_catalogue->getTapeLabelFormat(m_volInfo.vid); + m_volInfo.mountType = cta::common::dataStructures::MountType::Retrieve; + + // Now read a random file + // Create Read Session OSM + auto readSession = castor::tape::tapeFile::ReadSessionFactory::create(*m_drive, m_volInfo, false); + BasicRetrieveJob fileToRecall; + fileToRecall.selectedCopyNb = 0; + fileToRecall.archiveFile.tapeFiles.push_back(cta::common::dataStructures::TapeFile()); + fileToRecall.selectedTapeFile().blockId = 1; // here should be the block ID of HDR1 + fileToRecall.selectedTapeFile().fSeq = 1; + fileToRecall.retrieveRequest.archiveFileID = 1; + fileToRecall.positioningMethod = cta::PositioningMethod::ByBlock; + + // Create Read File OSM + auto reader = castor::tape::tapeFile::FileReaderFactory::create(readSession, fileToRecall); + size_t bs = reader->getBlockSize(); + char *data = new char[bs+1]; + size_t j = 0; + while (j < 100) { + reader->readNextDataBlock(data, bs); + j++; + } + } catch(cta::exception::Exception &ex) { + FAIL() << "Problem to read the OSM Tape. " << ex.getMessageValue(); + } +} + +TEST_F(OsmReaderTest, CleanDrive) { + cta::log::DummyLogger dummylogger("dummy", "unitTest"); + cta::log::StringLogger strlogger("string", "unitTest", cta::log::DEBUG); + cta::log::StdoutLogger stdoutlogger("stdout", "unitTest"); + cta::server::ProcessCapDummy capUtils; + cta::mediachanger::MediaChangerFacade mc(dummylogger); + cta::tape::daemon::TpconfigLine driveConfig(m_devName, "TestLogicalLibrary", m_nstDev, "dummy"); + + auto scheduler = std::make_unique<cta::Scheduler>(*m_catalogue, *m_db, 5, 2 * 1000 * 1000); + + castor::tape::tapeserver::daemon::CleanerSession cleanerSession( + capUtils, + mc, + strlogger, + driveConfig, + m_sWrapper, + m_vid, + false, + 0, + "", + *m_catalogue, + *scheduler); + + cleanerSession.execute(); + + const auto logToCheck = strlogger.getLog(); + ASSERT_NE(std::string::npos, logToCheck.find("Cleaner successfully detected tape contains data")); + ASSERT_NE(std::string::npos, logToCheck.find("Cleaner detected volume label contains expected VSN")); + ASSERT_NE(std::string::npos, logToCheck.find("Cleaner unloaded tape")); + ASSERT_NE(std::string::npos, logToCheck.find("Cleaner dismounted tape")); +} + +int main(int argc, char **argv) { + g_device_name = argv[1]; + g_device_path = argv[2]; + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file -- GitLab