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

#pragma once

#include "common/SmartFd.hpp"
#include "common/utils/utils.hpp"
23
#include "mediachanger/Constants.hpp"
24
#include "mediachanger/io.hpp"
25
#include "mediachanger/MediaChangerProxy.hpp"
26
27
28
29
30
31
32
33
34
35
36
#include "mediachanger/MessageHeader.hpp"
#include "mediachanger/RmcMountMsgBody.hpp"
#include "mediachanger/RmcUnmountMsgBody.hpp"

#include <unistd.h>
#include <sys/types.h>

namespace cta {
namespace mediachanger {

/**
Steven Murray's avatar
Steven Murray committed
37
 * A SCSI media changer proxy.
38
 */
Steven Murray's avatar
Steven Murray committed
39
class RmcProxy: public MediaChangerProxy {
40
41
42
43
44
45
46
47
48
49
50
public:

  /**
   * Constructor.
   *
   * @param rmcPort The TCP/IP port on which the rmcd daemon is listening.
   * @param netTimeout The timeout in seconds to be applied when performing
   * network read and write operations.
   * @parm maxRqstAttempts The maximum number of attempts a retriable RMC
   * request should be issued.
   */
Steven Murray's avatar
Steven Murray committed
51
  RmcProxy(
52
53
    const unsigned short rmcPort = RMC_PORT,
    const int netTimeout = RMC_NET_TIMEOUT,
54
    const unsigned int maxRqstAttempts = RMC_MAX_RQST_ATTEMPTS);
55
56
57
58

  /**
   * Destructor.
   */
59
  ~RmcProxy();
60
61
62
63
64
65
66
67
68
69
70

  /**
   * Requests the media changer to mount the specified tape for read-only
   * access into the drive in the specified library slot.
   *
   * Please note that this method provides a best-effort service because not all
   * media changers support read-only mounts.
   *
   * @param vid The volume identifier of the tape.
   * @param librarySlot The library slot containing the tape drive.
   */
71
  void mountTapeReadOnly(const std::string &vid, const LibrarySlot &librarySlot) override;
72
73
74
75
76
77
78
79

  /**
   * Requests the media changer to mount of the specified tape for read/write
   * access into the drive in the specified library slot.
   *
   * @param vid The volume identifier of the tape.
   * @param librarySlot The library slot containing the tape drive.
   */
80
  void mountTapeReadWrite(const std::string &vid, const LibrarySlot &librarySlot) override;
81
82
83
84
85
86
87
88

  /** 
   * Requests the media changer to dismount of the specified tape from the
   * drive in the specifed library slot.
   *
   * @param vid The volume identifier of the tape.
   * @param librarySlot The library slot containing the tape drive.
   */
89
  void dismountTape(const std::string &vid, const LibrarySlot &librarySlot) override;
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249

protected:

  /**
   * The size of buffer used to marshal or unmarshal RMC messages.
   */
  static const int RMC_MSGBUFSIZ = 256;

  /**
   * The TCP/IP port on which the rmcd daemon is listening.
   */
  const unsigned short m_rmcPort;

  /**
   * The timeout in seconds to be applied when performing network read and
   * write operations.
   */
  const int m_netTimeout;

  /**
   * The maximum number of attempts a retriable RMC request should be issued.
   */
  const unsigned int m_maxRqstAttempts;

  /**
   * Connects to the rmcd daemon.
   *
   * Please note that the rmcd daemon only listens on loopback interface.
   *
   * @return The socket-descriptor of the connection with the rmcd daemon.
   */
  int connectToRmc() const ;

  /**
   * Reads the header of an RMC_MAGIC message from the specified connection.
   *
   * @param fd The file descriptor of the connection.
   * @return The message header.
   */
  MessageHeader readRmcMsgHeader(const int fd) ;

  /**
   * Sends the specified request to the rmcd daemon and receives the reply
   * until success or the specified number of retriable attempts has been
   * reached.
   *
   * @param maxAttempts The maximum number of retriable attempts.
   * @param rqstBody The request to be sent.
   */
  template<typename T> void rmcSendRecvNbAttempts(const int maxAttempts,
    const T &rqstBody) {
    for(int attemptNb = 1; attemptNb <= maxAttempts; attemptNb++) {
      std::ostringstream rmcErrorStream;
      const int rmcRc = rmcSendRecv(rqstBody, rmcErrorStream);
      switch(rmcRc) {
      case 0: // Success
        return;
      case ERMCFASTR: // Fast retry
        // If this was the last attempt
        if(maxAttempts == attemptNb) {
          cta::exception::Exception ex;
          ex.getMessage() <<
            "Received error from rmcd after several fast retries" <<
            ": nbAttempts=" << attemptNb << " rmcErrorStream=" <<
            rmcErrorStream.str();
          throw ex;
        }

        // Pause a moment between attempts
        sleep(1);

        continue;
      default:
        {
          cta::exception::Exception ex;
          ex.getMessage() << "Received error from rmcd: rmcRc=" << rmcRc;
          if(!rmcErrorStream.str().empty()) {
            ex.getMessage() << " rmcErrorStream=" << rmcErrorStream.str();
          }
          throw ex;
        }
      }
    }
  }

  /**
   * Sends the specified request to the rmcd daemon and receives the reply.
   *
   * @param rqstBody The request to be sent.
   * @param rmcErrorStream The error stream constructed from ERR_MSG received
   * within the reply from the rmcd daemon.
   * @param The RMC return code.
   */
  template<typename T> int rmcSendRecv(const T &rqstBody,
    std::ostringstream &rmcErrorStream) {
    // Connect to rmcd and send request
    cta::SmartFd fd(connectToRmc());
    {
      char buf[RMC_MSGBUFSIZ];
      const size_t len = marshal(buf, rqstBody);
      writeBytes(fd.get(), m_netTimeout, len, buf);
    }

    // A single RMC reply is composed of 0 to 10 ERR_MSG replies
    // followed by a terminating RMC_RC reply
    const int maxERR_MSG = 10;
    int nbERR_MSG = 0;
    while(true) {
      const MessageHeader header = readRmcMsgHeader(fd.get());
      switch(header.reqType) { 
      case RMC_RC:
        return header.lenOrStatus;
      case MSG_ERR:
        nbERR_MSG++;

        if(maxERR_MSG < nbERR_MSG) {
          cta::exception::Exception ex;
          ex.getMessage() <<
            "Reply from rmcd contains too many ERR_MSG messages"
            ": maxERR_MSG=" << maxERR_MSG << " rmcErrorStream=" <<
            rmcErrorStream.str();
          throw ex;
        }

        if(nbERR_MSG > 1) {
          rmcErrorStream << " ";
        }

        rmcErrorStream << handleMSG_ERR(header, fd.get());
        break;
      default:
        {
          cta::exception::Exception ex;
          ex.getMessage() <<
            "First part of reply from rmcd has unexpected type"
            ": expected=RMC_RC or MSG_ERR actual=" <<
            rmcReplyTypeToStr(header.reqType);
          throw ex;
        }
      } // switch(header.reqType)
    } // while(true)
  }

  /**
   * Returns a string representation of the specified RMC reply type.
   *
   * @param replyType The reply type.
   * @return The string representation.
   */
  std::string rmcReplyTypeToStr(const int replyType);

  /**
   * Handles a MSG_ERR reply from rmcd.
   *
   * @param header The header of the reply.
   * @param fd The file descriptor of the connection with rmcd daemon.
   * @return The message contained within the MSG_ERR reply.
   */
  std::string handleMSG_ERR(const MessageHeader &header, const int fd);

Steven Murray's avatar
Steven Murray committed
250
}; // class RmcProxy
251
252
253

} // namespace mediachanger
} // namespace cta