@Override
 public HdfsBlocksMetadata getHdfsBlocksMetadata(
     String blockPoolId, long[] blockIds, List<Token<BlockTokenIdentifier>> tokens)
     throws IOException {
   List<TokenProto> tokensProtos = new ArrayList<TokenProto>(tokens.size());
   for (Token<BlockTokenIdentifier> t : tokens) {
     tokensProtos.add(PBHelper.convert(t));
   }
   // Build the request
   GetHdfsBlockLocationsRequestProto request =
       GetHdfsBlockLocationsRequestProto.newBuilder()
           .setBlockPoolId(blockPoolId)
           .addAllBlockIds(Longs.asList(blockIds))
           .addAllTokens(tokensProtos)
           .build();
   // Send the RPC
   GetHdfsBlockLocationsResponseProto response;
   try {
     response = rpcProxy.getHdfsBlockLocations(NULL_CONTROLLER, request);
   } catch (ServiceException e) {
     throw ProtobufHelper.getRemoteException(e);
   }
   // List of volumes in the response
   List<ByteString> volumeIdsByteStrings = response.getVolumeIdsList();
   List<byte[]> volumeIds = new ArrayList<byte[]>(volumeIdsByteStrings.size());
   for (ByteString bs : volumeIdsByteStrings) {
     volumeIds.add(bs.toByteArray());
   }
   // Array of indexes into the list of volumes, one per block
   List<Integer> volumeIndexes = response.getVolumeIndexesList();
   // Parsed HdfsVolumeId values, one per block
   return new HdfsBlocksMetadata(blockPoolId, blockIds, volumeIds, volumeIndexes);
 }
 @Override
 public ReconfigurationTaskStatus getReconfigurationStatus() throws IOException {
   GetReconfigurationStatusResponseProto response;
   Map<PropertyChange, Optional<String>> statusMap = null;
   long startTime;
   long endTime = 0;
   try {
     response = rpcProxy.getReconfigurationStatus(NULL_CONTROLLER, VOID_GET_RECONFIG_STATUS);
     startTime = response.getStartTime();
     if (response.hasEndTime()) {
       endTime = response.getEndTime();
     }
     if (response.getChangesCount() > 0) {
       statusMap = Maps.newHashMap();
       for (GetReconfigurationStatusConfigChangeProto change : response.getChangesList()) {
         PropertyChange pc =
             new PropertyChange(change.getName(), change.getNewValue(), change.getOldValue());
         String errorMessage = null;
         if (change.hasErrorMessage()) {
           errorMessage = change.getErrorMessage();
         }
         statusMap.put(pc, Optional.fromNullable(errorMessage));
       }
     }
   } catch (ServiceException e) {
     throw ProtobufHelper.getRemoteException(e);
   }
   return new ReconfigurationTaskStatus(startTime, endTime, statusMap);
 }
 @Override
 public void startReconfiguration() throws IOException {
   try {
     rpcProxy.startReconfiguration(NULL_CONTROLLER, VOID_START_RECONFIG);
   } catch (ServiceException e) {
     throw ProtobufHelper.getRemoteException(e);
   }
 }
 @Override
 public void refreshNamenodes() throws IOException {
   try {
     rpcProxy.refreshNamenodes(NULL_CONTROLLER, VOID_REFRESH_NAMENODES);
   } catch (ServiceException e) {
     throw ProtobufHelper.getRemoteException(e);
   }
 }
 @Override
 public DatanodeLocalInfo getDatanodeInfo() throws IOException {
   GetDatanodeInfoResponseProto response;
   try {
     response = rpcProxy.getDatanodeInfo(NULL_CONTROLLER, VOID_GET_DATANODE_INFO);
     return PBHelper.convert(response.getLocalInfo());
   } catch (ServiceException e) {
     throw ProtobufHelper.getRemoteException(e);
   }
 }
 @Override
 public void shutdownDatanode(boolean forUpgrade) throws IOException {
   ShutdownDatanodeRequestProto request =
       ShutdownDatanodeRequestProto.newBuilder().setForUpgrade(forUpgrade).build();
   try {
     rpcProxy.shutdownDatanode(NULL_CONTROLLER, request);
   } catch (ServiceException e) {
     throw ProtobufHelper.getRemoteException(e);
   }
 }
 @Override
 public void deleteBlockPool(String bpid, boolean force) throws IOException {
   DeleteBlockPoolRequestProto req =
       DeleteBlockPoolRequestProto.newBuilder().setBlockPool(bpid).setForce(force).build();
   try {
     rpcProxy.deleteBlockPool(NULL_CONTROLLER, req);
   } catch (ServiceException e) {
     throw ProtobufHelper.getRemoteException(e);
   }
 }
 @Override
 public long getReplicaVisibleLength(ExtendedBlock b) throws IOException {
   GetReplicaVisibleLengthRequestProto req =
       GetReplicaVisibleLengthRequestProto.newBuilder().setBlock(PBHelper.convert(b)).build();
   try {
     return rpcProxy.getReplicaVisibleLength(NULL_CONTROLLER, req).getLength();
   } catch (ServiceException e) {
     throw ProtobufHelper.getRemoteException(e);
   }
 }
 @Override
 public void triggerBlockReport(BlockReportOptions options) throws IOException {
   try {
     rpcProxy.triggerBlockReport(
         NULL_CONTROLLER,
         TriggerBlockReportRequestProto.newBuilder()
             .setIncremental(options.isIncremental())
             .build());
   } catch (ServiceException e) {
     throw ProtobufHelper.getRemoteException(e);
   }
 }
 @Override
 public BlockLocalPathInfo getBlockLocalPathInfo(
     ExtendedBlock block, Token<BlockTokenIdentifier> token) throws IOException {
   GetBlockLocalPathInfoRequestProto req =
       GetBlockLocalPathInfoRequestProto.newBuilder()
           .setBlock(PBHelper.convert(block))
           .setToken(PBHelper.convert(token))
           .build();
   GetBlockLocalPathInfoResponseProto resp;
   try {
     resp = rpcProxy.getBlockLocalPathInfo(NULL_CONTROLLER, req);
   } catch (ServiceException e) {
     throw ProtobufHelper.getRemoteException(e);
   }
   return new BlockLocalPathInfo(
       PBHelper.convert(resp.getBlock()), resp.getLocalPath(), resp.getLocalMetaPath());
 }