Search in sources :

Example 1 with RedirectResponse

use of org.dcache.xrootd.protocol.messages.RedirectResponse in project dcache by dCache.

the class XrootdRedirectHandler method doOnOpen.

/**
 * For client-server read and write, the open, if successful, will always result in a redirect
 * response to the proper pool; hence no subsequent requests like sync, read, write or close are
 * expected at the door.
 * <p>
 * For third-party copy where dCache is the source, the interactions are as follows:
 * <p>
 * 1.  The client opens the file to check availability (the 'placement' stage).  An OK response
 * is followed by the client closing the file. 2.  The client opens the file again with
 * rendezvous metadata.  The client will close the file only when notified by the destination
 * server that the transfer has completed. 3.  The destination server will open the file for the
 * actual read.
 * <p>
 * The order of 2, 3 is not deterministic; hence the response here must provide for the
 * possibility that the destination server attempts an open before the client specifies a
 * time-to-live on the rendezvous point.
 * <p>
 * The strategy adopted is therefore as follows:  response to (1) is simply to check file
 * permissions.  No metadata is generated and a "dummy" file handle is returned.  For 2 and 3,
 * whichever occurs first will cause a metadata object to be stored.  If the destination server
 * open occurs first, a wait response will tell the server to try again in a maximum of 3
 * seconds; otherwise, if the request matches and occurs within the ttl, the mover will be
 * started and the destination redirected to the pool. Response to the client will carry a file
 * handle but will not actually open a mover.  The close from the client is handled at the door
 * by removing the rendezvous information.
 * <p>
 * Third-party copy where dCache is the destination should proceed with the usual upload
 * transfer creation, but when the client is redirected to the pool and calls kXR_open there, a
 * third-party client will be started which does read requests from the source and then writes
 * the data to the mover channel.
 * <p>
 * NOTE:  with the changed TPC Lite protocol, the client is not required to open the source
 * again during the copy phase (2) if delegation is being used.
 */
@Override
protected XrootdResponse<OpenRequest> doOnOpen(ChannelHandlerContext ctx, OpenRequest req) {
    /*
         * TODO
         *
         * We ought to process this asynchronously to not block the calling thread during
         * staging or queuing. We should also switch to an asynchronous reply model if
         * the request is nearline or is queued on a pool. The naive approach to always
         * use an asynchronous reply model doesn't work because the xrootd 3.x client
         * introduces an artificial 1 second delay when processing such a response.
         */
    InetSocketAddress localAddress = getDestinationAddress();
    InetSocketAddress remoteAddress = getSourceAddress();
    LoginSessionInfo loginSessionInfo = sessionInfo();
    Map<String, String> opaque;
    try {
        opaque = OpaqueStringParser.getOpaqueMap(req.getOpaque());
        if (opaque.isEmpty()) {
            /*
                 * create a new HashMap as empty opaque map is immutable
                 */
            opaque = new HashMap<>();
        }
    } catch (ParseException e) {
        _log.warn("Ignoring malformed open opaque {}: {}", req.getOpaque(), e.getMessage());
        opaque = new HashMap<>();
    }
    try {
        FsPath path = createFullPath(req.getPath());
        XrootdResponse response = conditionallyHandleThirdPartyRequest(req, loginSessionInfo, opaque, path, remoteAddress.getHostName());
        if (response != null) {
            return response;
        }
        FilePerm neededPerm = req.getRequiredPermission();
        _log.info("Opening {} for {}", req.getPath(), neededPerm.xmlText());
        if (_log.isDebugEnabled()) {
            logDebugOnOpen(req);
        }
        String ioQueue = appSpecificQueue(req);
        Long size = null;
        try {
            String value = opaque.get("oss.asize");
            if (value != null) {
                size = Long.valueOf(value);
            }
        } catch (NumberFormatException exception) {
            _log.warn("Ignoring malformed oss.asize: {}", exception.getMessage());
        }
        _log.info("OPAQUE : {}", opaque);
        Set<String> triedHosts = extractTriedHosts(opaque);
        UUID uuid = UUID.randomUUID();
        opaque.put(UUID_PREFIX, uuid.toString());
        /*
             *  In case this is a third-party open as destination,
             *  pass the client information to the pool.
             */
        opaque.put("org.dcache.xrootd.client", getTpcClientId(req.getSession()));
        String opaqueString = OpaqueStringParser.buildOpaqueString(opaque);
        /*
             * Interact with core dCache to open the requested file.
             */
        XrootdTransfer transfer;
        if (neededPerm == FilePerm.WRITE) {
            /**
             *  boolean createDir = req.isMkPath() has
             *  been changed to default to true
             *  so as to conform to the general expectations that this
             *  behavior should not depend on the client.
             */
            boolean overwrite = req.isDelete() && !req.isNew();
            boolean persistOnSuccessfulClose = (req.getOptions() & XrootdProtocol.kXR_posc) == XrootdProtocol.kXR_posc;
            // TODO: replace with req.isPersistOnSuccessfulClose() with the latest xrootd4j
            transfer = _door.write(remoteAddress, path, triedHosts, ioQueue, uuid, true, overwrite, size, loginSessionInfo.getMaximumUploadSize(), localAddress, loginSessionInfo.getSubject(), loginSessionInfo.getRestriction(), persistOnSuccessfulClose, ((loginSessionInfo.isLoggedIn()) ? loginSessionInfo.getUserRootPath() : _rootPath), req.getSession().getDelegatedCredential(), opaque);
        } else {
            /*
                 * If this is a tpc transfer, then dCache is source here.
                 *
                 * Since we accept (from the destination server) any
                 * valid form of authentication, but without requiring
                 * the associated user to be mapped, we can override
                 * file permission restrictions (since we possess the
                 * 'token' rendezvous key, and the client file permissions
                 * have been checked during its open request).
                 */
            Subject subject;
            if (opaque.get("tpc.key") == null) {
                subject = loginSessionInfo.getSubject();
            } else {
                subject = Subjects.ROOT;
            }
            transfer = _door.read(remoteAddress, path, triedHosts, ioQueue, uuid, localAddress, subject, loginSessionInfo.getRestriction(), opaque);
            /*
                 * Again, if this is a tpc transfer, then dCache is source here.
                 * The transfer is initiated by the destination server
                 * (= current session).  However, we wish the doorinfo
                 * client in billing to reflect the original user connection,
                 * so we overwrite the transfer client address, which
                 * is unused by the mover.
                 */
            String client = opaque.get("tpc.org");
            if (client != null) {
                int index = client.indexOf("@");
                if (index != -1 && index < client.length() - 1) {
                    client = client.substring(index + 1);
                    transfer.setClientAddress(new InetSocketAddress(client, 0));
                }
            }
        }
        /*
             * ok, open was successful
             */
        InetSocketAddress address = transfer.getRedirect();
        /*
             *  Do not use the IP address as host name, as this will block
             *  TLS from working.
             *
             *  According to https://tools.ietf.org/html/rfc5280#section-4.2.1.6
             *  an IP is required to be in the list of Subject Alternative Names
             *  in the host certificate, but these are rarely added in practice.
             *  TLS enforces the RFC and this is a workaround.
             */
        String host = address.getHostName();
        if (InetAddresses.isInetAddress(host)) {
            _log.warn("Unable to resolve IP address {} " + "to a canonical name", host);
        }
        _log.info("Redirecting to {}, {}", host, address);
        return new RedirectResponse<>(req, host, address.getPort(), opaqueString, "");
    } catch (ParseException e) {
        return withError(req, kXR_ArgInvalid, "Path arguments do not parse");
    } catch (FileNotFoundCacheException e) {
        return withError(req, xrootdErrorCode(e.getRc()), "No such file");
    } catch (FileExistsCacheException e) {
        return withError(req, kXR_NotAuthorized, "File already exists");
    } catch (TimeoutCacheException e) {
        return withError(req, xrootdErrorCode(e.getRc()), "Internal timeout");
    } catch (PermissionDeniedCacheException e) {
        return withError(req, xrootdErrorCode(e.getRc()), e.getMessage());
    } catch (FileIsNewCacheException e) {
        return withError(req, xrootdErrorCode(e.getRc()), "File is locked by upload");
    } catch (NotFileCacheException e) {
        return withError(req, xrootdErrorCode(e.getRc()), "Not a file");
    } catch (CacheException e) {
        return withError(req, xrootdErrorCode(e.getRc()), String.format("Failed to open file (%s [%d])", e.getMessage(), e.getRc()));
    } catch (InterruptedException e) {
        /* Interrupt may be caused by cell shutdown or client
             * disconnect.  If the client disconnected, then the error
             * message will never reach the client, so saying that the
             * server shut down is okay.
             */
        return withError(req, kXR_ServerError, "Server shutdown");
    } catch (XrootdException e) {
        return withError(req, e.getError(), e.getMessage());
    }
}
Also used : HashMap(java.util.HashMap) FileIsNewCacheException(diskCacheV111.util.FileIsNewCacheException) FileExistsCacheException(diskCacheV111.util.FileExistsCacheException) FileNotFoundCacheException(diskCacheV111.util.FileNotFoundCacheException) NotFileCacheException(diskCacheV111.util.NotFileCacheException) TimeoutCacheException(diskCacheV111.util.TimeoutCacheException) CacheException(diskCacheV111.util.CacheException) PermissionDeniedCacheException(diskCacheV111.util.PermissionDeniedCacheException) InetSocketAddress(java.net.InetSocketAddress) XrootdResponse(org.dcache.xrootd.protocol.messages.XrootdResponse) FileIsNewCacheException(diskCacheV111.util.FileIsNewCacheException) UUID(java.util.UUID) XrootdException(org.dcache.xrootd.core.XrootdException) FsPath(diskCacheV111.util.FsPath) RedirectResponse(org.dcache.xrootd.protocol.messages.RedirectResponse) FilePerm(org.dcache.xrootd.protocol.XrootdProtocol.FilePerm) Subject(javax.security.auth.Subject) NotFileCacheException(diskCacheV111.util.NotFileCacheException) PermissionDeniedCacheException(diskCacheV111.util.PermissionDeniedCacheException) OptionalLong(java.util.OptionalLong) FileNotFoundCacheException(diskCacheV111.util.FileNotFoundCacheException) ParseException(org.dcache.xrootd.util.ParseException) FileExistsCacheException(diskCacheV111.util.FileExistsCacheException) TimeoutCacheException(diskCacheV111.util.TimeoutCacheException)

Example 2 with RedirectResponse

use of org.dcache.xrootd.protocol.messages.RedirectResponse in project dcache by dCache.

the class AccessLogHandler method write.

@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
    if (msg instanceof XrootdResponse<?> && logger.isErrorEnabled()) {
        XrootdResponse<?> response = (XrootdResponse<?>) msg;
        XrootdRequest request = response.getRequest();
        NetLoggerBuilder.Level level;
        if (response instanceof ErrorResponse) {
            level = ERROR;
        } else if (request instanceof WriteRequest || request instanceof ReadRequest || request instanceof ReadVRequest) {
            level = DEBUG;
        } else {
            level = INFO;
        }
        if (level == ERROR || level == INFO && logger.isInfoEnabled() || level == DEBUG && logger.isDebugEnabled()) {
            NetLoggerBuilder log = new NetLoggerBuilder(level, "org.dcache.xrootd.request").omitNullValues();
            log.add("session", CDC.getSession());
            log.add("request", getRequestId(request));
            if (request instanceof PathRequest) {
                log.add("path", (Strings.emptyToNull(((PathRequest) request).getPath())));
                log.add("opaque", (Strings.emptyToNull(((PathRequest) request).getOpaque())));
                if (request instanceof OpenRequest) {
                    if (!((OpenRequest) request).isReadOnly()) {
                        int mode = ((OpenRequest) request).getUMask();
                        if (mode == 0) {
                            log.add("mode", "0");
                        } else {
                            log.add("mode", "0" + Integer.toOctalString(mode));
                        }
                    }
                    log.add("options", "0x" + Integer.toHexString(((OpenRequest) request).getOptions()));
                } else if (request instanceof LocateRequest) {
                    log.add("options", "0x" + Integer.toHexString(((LocateRequest) request).getOptions()));
                } else if (request instanceof MkDirRequest) {
                    log.add("options", "0x" + Integer.toHexString(((MkDirRequest) request).getOptions()));
                } else if (request instanceof StatRequest) {
                    if (((StatRequest) request).getTarget() == Target.FHANDLE) {
                        log.add("handle", ((StatRequest) request).getFhandle());
                    }
                    log.add("vfs", ((StatRequest) request).isVfsSet());
                }
            } else if (request instanceof CloseRequest) {
                log.add("handle", ((CloseRequest) request).getFileHandle());
            } else if (request instanceof LoginRequest) {
                log.add("username", ((LoginRequest) request).getUserName());
                log.add("capver", ((LoginRequest) request).getClientProtocolVersion());
                log.add("pid", ((LoginRequest) request).getPID());
                log.add("token", emptyToNull(((LoginRequest) request).getToken()));
            } else if (request instanceof MvRequest) {
                log.add("source", ((MvRequest) request).getSourcePath());
                log.add("target", ((MvRequest) request).getTargetPath());
            } else if (request instanceof PrepareRequest) {
                log.add("options", "0x" + Integer.toHexString(((PrepareRequest) request).getOptions()));
                if (((PrepareRequest) request).getPathList().length == 1) {
                    log.add("path", ((PrepareRequest) request).getPathList()[0]);
                } else {
                    log.add("files", ((PrepareRequest) request).getPathList().length);
                }
            } else if (request instanceof QueryRequest) {
                log.add("reqcode", getQueryReqCode(request));
                int fhandle = ((QueryRequest) request).getFhandle();
                if (fhandle != 0) {
                    log.add("fhandle", fhandle);
                }
                log.add("args", Strings.emptyToNull(((QueryRequest) request).getArgs()));
            } else if (request instanceof StatxRequest) {
                if (((StatxRequest) request).getPaths().length == 1) {
                    log.add("path", ((StatxRequest) request).getPaths()[0]);
                } else {
                    log.add("files", ((StatxRequest) request).getPaths().length);
                }
            } else if (request instanceof SetRequest) {
                final String APPID_PREFIX = "appid ";
                final int APPID_PREFIX_LENGTH = APPID_PREFIX.length();
                final int APPID_MSG_LENGTH = 80;
                String data = ((SetRequest) request).getData();
                if (data.startsWith(APPID_PREFIX)) {
                    log.add("appid", data.substring(APPID_PREFIX_LENGTH, Math.min(APPID_PREFIX_LENGTH + APPID_MSG_LENGTH, data.length())));
                }
            } else if (request instanceof EndSessionRequest) {
                log.add("sessionId", ((EndSessionRequest) request).getSessionId());
            } else if (request instanceof SyncRequest) {
                log.add("handle", ((SyncRequest) request).getFileHandle());
            }
            log.add("response", getStatusCode(response));
            if (response instanceof ErrorResponse) {
                log.add("error.code", getErrorCode((ErrorResponse) response));
                log.add("error.msg", ((ErrorResponse) response).getErrorMessage());
            } else if (response instanceof RedirectResponse) {
                log.add("host", ((RedirectResponse) response).getHost());
                log.add("port", ((RedirectResponse) response).getPort());
                log.add("token", emptyToNull(((RedirectResponse) response).getToken()));
            } else if (response instanceof StatResponse) {
                log.add("flags", ((StatResponse) response).getFlags());
                log.add("modtime", Instant.ofEpochSecond(((StatResponse) response).getModificationTime()));
                log.add("size", ((StatResponse) response).getSize());
            } else if (response instanceof LoginResponse) {
                log.add("sessionId", ((LoginResponse) response).getSessionId());
                log.add("sec", emptyToNull(((LoginResponse) response).getSec()));
            } else if (response instanceof OpenResponse) {
                log.add("handle", ((OpenResponse) response).getFileHandle());
                FileStatus fs = ((OpenResponse) response).getFileStatus();
                if (fs != null) {
                    log.add("flags", fs.getFlags());
                    log.add("modtime", Instant.ofEpochSecond(fs.getModificationTime()));
                    log.add("size", fs.getSize());
                }
            }
            log.toLogger(logger);
        }
    }
    ctx.write(msg, promise);
}
Also used : FileStatus(org.dcache.xrootd.util.FileStatus) PathRequest(org.dcache.xrootd.protocol.messages.PathRequest) MkDirRequest(org.dcache.xrootd.protocol.messages.MkDirRequest) LoginRequest(org.dcache.xrootd.protocol.messages.LoginRequest) StatxRequest(org.dcache.xrootd.protocol.messages.StatxRequest) StatRequest(org.dcache.xrootd.protocol.messages.StatRequest) XrootdResponse(org.dcache.xrootd.protocol.messages.XrootdResponse) ReadVRequest(org.dcache.xrootd.protocol.messages.ReadVRequest) EndSessionRequest(org.dcache.xrootd.protocol.messages.EndSessionRequest) OpenResponse(org.dcache.xrootd.protocol.messages.OpenResponse) ReadRequest(org.dcache.xrootd.protocol.messages.ReadRequest) PrepareRequest(org.dcache.xrootd.protocol.messages.PrepareRequest) MvRequest(org.dcache.xrootd.protocol.messages.MvRequest) XrootdRequest(org.dcache.xrootd.protocol.messages.XrootdRequest) LoginResponse(org.dcache.xrootd.protocol.messages.LoginResponse) QueryRequest(org.dcache.xrootd.protocol.messages.QueryRequest) WriteRequest(org.dcache.xrootd.protocol.messages.WriteRequest) RedirectResponse(org.dcache.xrootd.protocol.messages.RedirectResponse) XrootdProtocol.kXR_chkpoint(org.dcache.xrootd.protocol.XrootdProtocol.kXR_chkpoint) NetLoggerBuilder(org.dcache.util.NetLoggerBuilder) ErrorResponse(org.dcache.xrootd.protocol.messages.ErrorResponse) LocateRequest(org.dcache.xrootd.protocol.messages.LocateRequest) SetRequest(org.dcache.xrootd.protocol.messages.SetRequest) SyncRequest(org.dcache.xrootd.protocol.messages.SyncRequest) StatResponse(org.dcache.xrootd.protocol.messages.StatResponse) OpenRequest(org.dcache.xrootd.protocol.messages.OpenRequest) CloseRequest(org.dcache.xrootd.protocol.messages.CloseRequest)

Aggregations

RedirectResponse (org.dcache.xrootd.protocol.messages.RedirectResponse)2 XrootdResponse (org.dcache.xrootd.protocol.messages.XrootdResponse)2 CacheException (diskCacheV111.util.CacheException)1 FileExistsCacheException (diskCacheV111.util.FileExistsCacheException)1 FileIsNewCacheException (diskCacheV111.util.FileIsNewCacheException)1 FileNotFoundCacheException (diskCacheV111.util.FileNotFoundCacheException)1 FsPath (diskCacheV111.util.FsPath)1 NotFileCacheException (diskCacheV111.util.NotFileCacheException)1 PermissionDeniedCacheException (diskCacheV111.util.PermissionDeniedCacheException)1 TimeoutCacheException (diskCacheV111.util.TimeoutCacheException)1 InetSocketAddress (java.net.InetSocketAddress)1 HashMap (java.util.HashMap)1 OptionalLong (java.util.OptionalLong)1 UUID (java.util.UUID)1 Subject (javax.security.auth.Subject)1 NetLoggerBuilder (org.dcache.util.NetLoggerBuilder)1 XrootdException (org.dcache.xrootd.core.XrootdException)1 FilePerm (org.dcache.xrootd.protocol.XrootdProtocol.FilePerm)1 XrootdProtocol.kXR_chkpoint (org.dcache.xrootd.protocol.XrootdProtocol.kXR_chkpoint)1 CloseRequest (org.dcache.xrootd.protocol.messages.CloseRequest)1