package com.bytedance.hadoop.hdfs.server.proxy; import com.bytedance.hadoop.hdfs.server.NNProxy; import com.bytedance.hadoop.hdfs.server.upstream.UpstreamManager; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.crypto.CryptoProtocolVersion; import org.apache.hadoop.fs.*; import org.apache.hadoop.fs.permission.AclEntry; import org.apache.hadoop.fs.permission.AclStatus; import org.apache.hadoop.fs.permission.FsAction; import org.apache.hadoop.fs.permission.FsPermission; import org.apache.hadoop.hdfs.inotify.EventBatchList; import org.apache.hadoop.hdfs.protocol.*; import org.apache.hadoop.hdfs.security.token.block.DataEncryptionKey; import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier; import org.apache.hadoop.hdfs.server.namenode.NotReplicatedYetException; import org.apache.hadoop.hdfs.server.namenode.SafeModeException; import org.apache.hadoop.hdfs.server.protocol.DatanodeStorageReport; import org.apache.hadoop.io.EnumSetWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.security.AccessControlException; import org.apache.hadoop.security.token.Token; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.FileNotFoundException; import java.io.IOException; import java.util.*; /** */ @InterfaceAudience.Private @InterfaceStability.Evolving public class ProxyClientProtocolHandler implements ClientProtocol { private static final Logger LOG = LoggerFactory.getLogger(ProxyClientProtocolHandler.class); final NNProxy nnProxy; final Configuration conf; final UpstreamManager upstreamManager; final Router router; public ProxyClientProtocolHandler(NNProxy nnProxy, Configuration conf, UpstreamManager upstreamManager) { this.nnProxy = nnProxy; this.conf = conf; this.upstreamManager = upstreamManager; this.router = new Router(nnProxy, conf, upstreamManager); } void ensureCanRename(String path) throws IOException { if (nnProxy.getMounts().isMountPoint(path)) { throw new IOException("Cannot rename a mount point (" + path + ")"); } if (!nnProxy.getMounts().isUnified(path)) { throw new IOException("Cannot rename a non-unified directory " + path + " (contains mount point)"); } } /* begin protocol handlers */ @Override public LocatedBlocks getBlockLocations(String src, long offset, long length) throws AccessControlException, FileNotFoundException, UnresolvedLinkException, IOException { RouteInfo routeInfo = router.route(src); return routeInfo.upstream.getBlockLocations(routeInfo.realPath, offset, length); } @Override public FsServerDefaults getServerDefaults() throws IOException { return router.getRoot().upstream.getServerDefaults(); } @Override public HdfsFileStatus create(String src, FsPermission masked, String clientName, EnumSetWritable<CreateFlag> flag, boolean createParent, short replication, long blockSize, CryptoProtocolVersion[] supportedVersions) throws AccessControlException, AlreadyBeingCreatedException, DSQuotaExceededException, FileAlreadyExistsException, FileNotFoundException, NSQuotaExceededException, ParentNotDirectoryException, SafeModeException, UnresolvedLinkException, SnapshotAccessControlException, IOException { RouteInfo routeInfo = router.route(src); return routeInfo.upstream.create(routeInfo.realPath, masked, clientName, flag, createParent, replication, blockSize, supportedVersions); } @Override public LocatedBlock append(String src, String clientName) throws AccessControlException, DSQuotaExceededException, FileNotFoundException, SafeModeException, UnresolvedLinkException, SnapshotAccessControlException, IOException { RouteInfo routeInfo = router.route(src); return routeInfo.upstream.append(routeInfo.realPath, clientName); } @Override public boolean setReplication(String src, short replication) throws AccessControlException, DSQuotaExceededException, FileNotFoundException, SafeModeException, UnresolvedLinkException, SnapshotAccessControlException, IOException { RouteInfo routeInfo = router.route(src); return routeInfo.upstream.setReplication(routeInfo.realPath, replication); } @Override public BlockStoragePolicy[] getStoragePolicies() throws IOException { return router.getRoot().upstream.getStoragePolicies(); } @Override public void setStoragePolicy(String src, String policyName) throws SnapshotAccessControlException, UnresolvedLinkException, FileNotFoundException, QuotaExceededException, IOException { RouteInfo routeInfo = router.route(src); routeInfo.upstream.setStoragePolicy(routeInfo.realPath, policyName); } @Override public void setPermission(String src, FsPermission permission) throws AccessControlException, FileNotFoundException, SafeModeException, UnresolvedLinkException, SnapshotAccessControlException, IOException { RouteInfo routeInfo = router.route(src); routeInfo.upstream.setPermission(routeInfo.realPath, permission); } @Override public void setOwner(String src, String username, String groupname) throws AccessControlException, FileNotFoundException, SafeModeException, UnresolvedLinkException, SnapshotAccessControlException, IOException { RouteInfo routeInfo = router.route(src); routeInfo.upstream.setOwner(routeInfo.realPath, username, groupname); } @Override public void abandonBlock(ExtendedBlock b, long fileId, String src, String holder) throws AccessControlException, FileNotFoundException, UnresolvedLinkException, IOException { RouteInfo routeInfo = router.route(src); routeInfo.upstream.abandonBlock(b, fileId, routeInfo.realPath, holder); } @Override public LocatedBlock addBlock(String src, String clientName, ExtendedBlock previous, DatanodeInfo[] excludeNodes, long fileId, String[] favoredNodes) throws AccessControlException, FileNotFoundException, NotReplicatedYetException, SafeModeException, UnresolvedLinkException, IOException { RouteInfo routeInfo = router.route(src); return routeInfo.upstream.addBlock(routeInfo.realPath, clientName, previous, excludeNodes, fileId, favoredNodes); } @Override public LocatedBlock getAdditionalDatanode(String src, long fileId, ExtendedBlock blk, DatanodeInfo[] existings, String[] existingStorageIDs, DatanodeInfo[] excludes, int numAdditionalNodes, String clientName) throws AccessControlException, FileNotFoundException, SafeModeException, UnresolvedLinkException, IOException { RouteInfo routeInfo = router.route(src); return routeInfo.upstream.getAdditionalDatanode(routeInfo.realPath, fileId, blk, existings, existingStorageIDs, excludes, numAdditionalNodes, clientName); } @Override public boolean complete(String src, String clientName, ExtendedBlock last, long fileId) throws AccessControlException, FileNotFoundException, SafeModeException, UnresolvedLinkException, IOException { RouteInfo routeInfo = router.route(src); return routeInfo.upstream.complete(routeInfo.realPath, clientName, last, fileId); } @Override public void reportBadBlocks(LocatedBlock[] blocks) throws IOException { Map<String, List<LocatedBlock>> fsBlocks = new HashMap<>(); for (LocatedBlock blk : blocks) { String bpId = blk.getBlock().getBlockPoolId(); String fs = nnProxy.getBlockPoolRegistry().getFs(bpId); if (fs == null) { throw new IOException("Unknown block pool: " + bpId); } if (!fsBlocks.containsKey(fs)) { fsBlocks.put(fs, new ArrayList<LocatedBlock>()); } fsBlocks.get(fs).add(blk); } for (Map.Entry<String, List<LocatedBlock>> entry : fsBlocks.entrySet()) { String fs = entry.getKey(); router.getProtocol(fs).reportBadBlocks(entry.getValue().toArray(new LocatedBlock[0])); } } @Override public boolean rename(String src, String dst) throws UnresolvedLinkException, SnapshotAccessControlException, IOException { ensureCanRename(src); ensureCanRename(dst); RouteInfo srcRouteInfo = router.route(src); RouteInfo dstRouteInfo = router.route(dst); if (!srcRouteInfo.fs.equals(dstRouteInfo.fs)) { throw new IOException("Cannot rename across namespaces"); } return srcRouteInfo.upstream.rename(srcRouteInfo.realPath, dstRouteInfo.realPath); } @Override public void concat(String trg, String[] srcs) throws IOException, UnresolvedLinkException, SnapshotAccessControlException { RouteInfo trgRouteInfo = router.route(trg); RouteInfo[] routeInfos = new RouteInfo[srcs.length]; for (int i = 0; i < srcs.length; i++) { routeInfos[i] = router.route(srcs[i]); } String fs = null; String[] newSrcs = new String[srcs.length]; for (int i = 0; i < routeInfos.length; i++) { if (fs != null && !fs.equals(routeInfos[i].fs)) { throw new IOException("Cannot concat across namespaces"); } fs = routeInfos[i].fs; newSrcs[i] = routeInfos[i].realPath; } if (fs != null && !fs.equals(trgRouteInfo.fs)) { throw new IOException("Cannot concat across namespaces"); } trgRouteInfo.upstream.concat(trgRouteInfo.realPath, newSrcs); } @Override public void rename2(String src, String dst, Options.Rename... options) throws AccessControlException, DSQuotaExceededException, FileAlreadyExistsException, FileNotFoundException, NSQuotaExceededException, ParentNotDirectoryException, SafeModeException, UnresolvedLinkException, SnapshotAccessControlException, IOException { ensureCanRename(src); ensureCanRename(dst); RouteInfo srcRouteInfo = router.route(src); RouteInfo dstRouteInfo = router.route(dst); if (!srcRouteInfo.fs.equals(dstRouteInfo.fs)) { throw new IOException("Cannot rename across namespaces"); } srcRouteInfo.upstream.rename2(srcRouteInfo.realPath, dstRouteInfo.realPath, options); } @Override public boolean delete(String src, boolean recursive) throws AccessControlException, FileNotFoundException, SafeModeException, UnresolvedLinkException, SnapshotAccessControlException, IOException { RouteInfo routeInfo = router.route(src); return routeInfo.upstream.delete(routeInfo.realPath, recursive); } @Override public boolean mkdirs(String src, FsPermission masked, boolean createParent) throws AccessControlException, FileAlreadyExistsException, FileNotFoundException, NSQuotaExceededException, ParentNotDirectoryException, SafeModeException, UnresolvedLinkException, SnapshotAccessControlException, IOException { RouteInfo routeInfo = router.route(src); return routeInfo.upstream.mkdirs(routeInfo.realPath, masked, createParent); } @Override public DirectoryListing getListing(String src, byte[] startAfter, boolean needLocation) throws AccessControlException, FileNotFoundException, UnresolvedLinkException, IOException { RouteInfo routeInfo = router.route(src); return routeInfo.upstream.getListing(routeInfo.realPath, startAfter, needLocation); } @Override public SnapshottableDirectoryStatus[] getSnapshottableDirListing() throws IOException { return new SnapshottableDirectoryStatus[0]; } @Override public void renewLease(String clientName) throws AccessControlException, IOException { // currently, just renew lease on all namenodes for (String fs : nnProxy.getMounts().getAllFs()) { router.getProtocol(fs).renewLease(clientName); } } @Override public boolean recoverLease(String src, String clientName) throws IOException { RouteInfo routeInfo = router.route(src); return routeInfo.upstream.recoverLease(routeInfo.realPath, clientName); } @Override public long[] getStats() throws IOException { return router.getRoot().upstream.getStats(); } @Override public DatanodeInfo[] getDatanodeReport(HdfsConstants.DatanodeReportType type) throws IOException { throw new IOException("Invalid operation, do not use proxy"); } @Override public DatanodeStorageReport[] getDatanodeStorageReport(HdfsConstants.DatanodeReportType type) throws IOException { throw new IOException("Invalid operation, do not use proxy"); } @Override public long getPreferredBlockSize(String filename) throws IOException, UnresolvedLinkException { RouteInfo routeInfo = router.route(filename); return routeInfo.upstream.getPreferredBlockSize(routeInfo.realPath); } @Override public boolean setSafeMode(HdfsConstants.SafeModeAction action, boolean isChecked) throws IOException { if (action.equals(HdfsConstants.SafeModeAction.SAFEMODE_GET)) { // FIXME: properly handle return false; } throw new IOException("Invalid operation, do not use proxy"); } @Override public void saveNamespace() throws AccessControlException, IOException { throw new IOException("Invalid operation, do not use proxy"); } @Override public long rollEdits() throws AccessControlException, IOException { throw new IOException("Invalid operation, do not use proxy"); } @Override public boolean restoreFailedStorage(String arg) throws AccessControlException, IOException { throw new IOException("Invalid operation, do not use proxy"); } @Override public void refreshNodes() throws IOException { throw new IOException("Invalid operation, do not use proxy"); } @Override public void finalizeUpgrade() throws IOException { throw new IOException("Invalid operation, do not use proxy"); } @Override public RollingUpgradeInfo rollingUpgrade(HdfsConstants.RollingUpgradeAction action) throws IOException { throw new IOException("Invalid operation, do not use proxy"); } @Override public CorruptFileBlocks listCorruptFileBlocks(String path, String cookie) throws IOException { RouteInfo routeInfo = router.route(path); return routeInfo.upstream.listCorruptFileBlocks(routeInfo.realPath, cookie); } @Override public void metaSave(String filename) throws IOException { throw new IOException("Invalid operation, do not use proxy"); } @Override public void setBalancerBandwidth(long bandwidth) throws IOException { throw new IOException("Invalid operation, do not use proxy"); } @Override public HdfsFileStatus getFileInfo(String src) throws AccessControlException, FileNotFoundException, UnresolvedLinkException, IOException { RouteInfo routeInfo = router.route(src); return routeInfo.upstream.getFileInfo(routeInfo.realPath); } @Override public boolean isFileClosed(String src) throws AccessControlException, FileNotFoundException, UnresolvedLinkException, IOException { RouteInfo routeInfo = router.route(src); return routeInfo.upstream.isFileClosed(routeInfo.realPath); } @Override public HdfsFileStatus getFileLinkInfo(String src) throws AccessControlException, UnresolvedLinkException, IOException { RouteInfo routeInfo = router.route(src); return routeInfo.upstream.getFileInfo(routeInfo.realPath); } @Override public ContentSummary getContentSummary(String path) throws AccessControlException, FileNotFoundException, UnresolvedLinkException, IOException { RouteInfo routeInfo = router.route(path); return routeInfo.upstream.getContentSummary(routeInfo.realPath); } @Override public void setQuota(String path, long namespaceQuota, long diskspaceQuota) throws AccessControlException, FileNotFoundException, UnresolvedLinkException, SnapshotAccessControlException, IOException { RouteInfo routeInfo = router.route(path); routeInfo.upstream.setQuota(routeInfo.realPath, namespaceQuota, diskspaceQuota); } @Override public void fsync(String src, long inodeId, String client, long lastBlockLength) throws AccessControlException, FileNotFoundException, UnresolvedLinkException, IOException { RouteInfo routeInfo = router.route(src); routeInfo.upstream.fsync(routeInfo.realPath, inodeId, client, lastBlockLength); } @Override public void setTimes(String src, long mtime, long atime) throws AccessControlException, FileNotFoundException, UnresolvedLinkException, SnapshotAccessControlException, IOException { RouteInfo routeInfo = router.route(src); routeInfo.upstream.setTimes(routeInfo.realPath, mtime, atime); } @Override public void createSymlink(String target, String link, FsPermission dirPerm, boolean createParent) throws AccessControlException, FileAlreadyExistsException, FileNotFoundException, ParentNotDirectoryException, SafeModeException, UnresolvedLinkException, SnapshotAccessControlException, IOException { RouteInfo routeInfo = router.route(target); routeInfo.upstream.getFileInfo(routeInfo.realPath); } @Override public String getLinkTarget(String path) throws AccessControlException, FileNotFoundException, IOException { RouteInfo routeInfo = router.route(path); return routeInfo.upstream.getLinkTarget(routeInfo.realPath); } @Override public LocatedBlock updateBlockForPipeline(ExtendedBlock block, String clientName) throws IOException { return router.getUpstreamForBlockPool(block.getBlockPoolId()).updateBlockForPipeline(block, clientName); } @Override public void updatePipeline(String clientName, ExtendedBlock oldBlock, ExtendedBlock newBlock, DatanodeID[] newNodes, String[] newStorageIDs) throws IOException { if (!newBlock.getBlockPoolId().equals(oldBlock.getBlockPoolId())) { throw new IOException("Cannot update pipeline across block pools"); } router.getUpstreamForBlockPool(newBlock.getBlockPoolId()).updatePipeline(clientName, oldBlock, newBlock, newNodes, newStorageIDs); } @Override public Token<DelegationTokenIdentifier> getDelegationToken(Text renewer) throws IOException { throw new IOException("Invalid operation, do not use proxy"); } @Override public long renewDelegationToken(Token<DelegationTokenIdentifier> token) throws IOException { throw new IOException("Invalid operation, do not use proxy"); } @Override public void cancelDelegationToken(Token<DelegationTokenIdentifier> token) throws IOException { throw new IOException("Invalid operation, do not use proxy"); } @Override public DataEncryptionKey getDataEncryptionKey() throws IOException { return router.getRoot().upstream.getDataEncryptionKey(); } @Override public String createSnapshot(String snapshotRoot, String snapshotName) throws IOException { throw new IOException("Invalid operation, do not use proxy"); } @Override public void deleteSnapshot(String snapshotRoot, String snapshotName) throws IOException { throw new IOException("Invalid operation, do not use proxy"); } @Override public void renameSnapshot(String snapshotRoot, String snapshotOldName, String snapshotNewName) throws IOException { throw new IOException("Invalid operation, do not use proxy"); } @Override public void allowSnapshot(String snapshotRoot) throws IOException { throw new IOException("Invalid operation, do not use proxy"); } @Override public void disallowSnapshot(String snapshotRoot) throws IOException { throw new IOException("Invalid operation, do not use proxy"); } @Override public SnapshotDiffReport getSnapshotDiffReport(String snapshotRoot, String fromSnapshot, String toSnapshot) throws IOException { throw new IOException("Invalid operation, do not use proxy"); } @Override public long addCacheDirective(CacheDirectiveInfo directive, EnumSet<CacheFlag> flags) throws IOException { return nnProxy.getCacheRegistry().addCacheDirective(directive, flags); } @Override public void modifyCacheDirective(CacheDirectiveInfo directive, EnumSet<CacheFlag> flags) throws IOException { nnProxy.getCacheRegistry().modifyCacheDirective(directive, flags); } @Override public void removeCacheDirective(long id) throws IOException { nnProxy.getCacheRegistry().removeCacheDirective(id); } @Override public BatchedRemoteIterator.BatchedEntries<CacheDirectiveEntry> listCacheDirectives(long prevId, CacheDirectiveInfo filter) throws IOException { return nnProxy.getCacheRegistry().listCacheDirectives(prevId, filter); } @Override public void addCachePool(CachePoolInfo info) throws IOException { throw new IOException("Invalid operation, do not use proxy"); } @Override public void modifyCachePool(CachePoolInfo req) throws IOException { throw new IOException("Invalid operation, do not use proxy"); } @Override public void removeCachePool(String pool) throws IOException { throw new IOException("Invalid operation, do not use proxy"); } @Override public BatchedRemoteIterator.BatchedEntries<CachePoolEntry> listCachePools(String prevPool) throws IOException { return nnProxy.getCacheRegistry().listCachePools(prevPool); } @Override public void modifyAclEntries(String src, List<AclEntry> aclSpec) throws IOException { RouteInfo routeInfo = router.route(src); routeInfo.upstream.modifyAclEntries(routeInfo.realPath, aclSpec); } @Override public void removeAclEntries(String src, List<AclEntry> aclSpec) throws IOException { RouteInfo routeInfo = router.route(src); routeInfo.upstream.removeAclEntries(routeInfo.realPath, aclSpec); } @Override public void removeDefaultAcl(String src) throws IOException { RouteInfo routeInfo = router.route(src); routeInfo.upstream.removeDefaultAcl(routeInfo.realPath); } @Override public void removeAcl(String src) throws IOException { RouteInfo routeInfo = router.route(src); routeInfo.upstream.removeAcl(routeInfo.realPath); } @Override public void setAcl(String src, List<AclEntry> aclSpec) throws IOException { RouteInfo routeInfo = router.route(src); routeInfo.upstream.setAcl(routeInfo.realPath, aclSpec); } @Override public AclStatus getAclStatus(String src) throws IOException { RouteInfo routeInfo = router.route(src); return routeInfo.upstream.getAclStatus(routeInfo.realPath); } @Override public void createEncryptionZone(String src, String keyName) throws IOException { RouteInfo routeInfo = router.route(src); routeInfo.upstream.createEncryptionZone(routeInfo.realPath, keyName); } @Override public EncryptionZone getEZForPath(String src) throws IOException { RouteInfo routeInfo = router.route(src); return routeInfo.upstream.getEZForPath(routeInfo.realPath); } @Override public BatchedRemoteIterator.BatchedEntries<EncryptionZone> listEncryptionZones(long prevId) throws IOException { throw new IOException("Invalid operation, do not use proxy"); } @Override public void setXAttr(String src, XAttr xAttr, EnumSet<XAttrSetFlag> flag) throws IOException { RouteInfo routeInfo = router.route(src); routeInfo.upstream.setXAttr(routeInfo.realPath, xAttr, flag); } @Override public List<XAttr> getXAttrs(String src, List<XAttr> xAttrs) throws IOException { RouteInfo routeInfo = router.route(src); return routeInfo.upstream.getXAttrs(routeInfo.realPath, xAttrs); } @Override public List<XAttr> listXAttrs(String src) throws IOException { RouteInfo routeInfo = router.route(src); return routeInfo.upstream.listXAttrs(routeInfo.realPath); } @Override public void removeXAttr(String src, XAttr xAttr) throws IOException { RouteInfo routeInfo = router.route(src); routeInfo.upstream.removeXAttr(routeInfo.realPath, xAttr); } @Override public void checkAccess(String path, FsAction mode) throws IOException { RouteInfo routeInfo = router.route(path); routeInfo.upstream.checkAccess(routeInfo.realPath, mode); } @Override public long getCurrentEditLogTxid() throws IOException { throw new IOException("Invalid operation, do not use proxy"); } @Override public EventBatchList getEditsFromTxid(long txid) throws IOException { throw new IOException("Invalid operation, do not use proxy"); } }