HDFS-7349. Support DFS command for the EC encoding (Contributed by Vinayakumar B)
This commit is contained in:
parent
2c277802c1
commit
ceb3d1c170
|
@ -111,6 +111,10 @@ public class FsShell extends Configured implements Tool {
|
||||||
return getTrash().getCurrentTrashDir();
|
return getTrash().getCurrentTrashDir();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected String getUsagePrefix() {
|
||||||
|
return usagePrefix;
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: Usage/Help are inner classes to allow access to outer methods
|
// NOTE: Usage/Help are inner classes to allow access to outer methods
|
||||||
// that access commandFactory
|
// that access commandFactory
|
||||||
|
|
||||||
|
@ -194,7 +198,7 @@ public class FsShell extends Configured implements Tool {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// display help or usage for all commands
|
// display help or usage for all commands
|
||||||
out.println(usagePrefix);
|
out.println(getUsagePrefix());
|
||||||
|
|
||||||
// display list of short usages
|
// display list of short usages
|
||||||
ArrayList<Command> instances = new ArrayList<Command>();
|
ArrayList<Command> instances = new ArrayList<Command>();
|
||||||
|
@ -218,7 +222,7 @@ public class FsShell extends Configured implements Tool {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void printInstanceUsage(PrintStream out, Command instance) {
|
private void printInstanceUsage(PrintStream out, Command instance) {
|
||||||
out.println(usagePrefix + " " + instance.getUsage());
|
out.println(getUsagePrefix() + " " + instance.getUsage());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void printInstanceHelp(PrintStream out, Command instance) {
|
private void printInstanceHelp(PrintStream out, Command instance) {
|
||||||
|
|
|
@ -80,3 +80,5 @@
|
||||||
|
|
||||||
HDFS-8123. Erasure Coding: Better to move EC related proto messages to a
|
HDFS-8123. Erasure Coding: Better to move EC related proto messages to a
|
||||||
separate erasurecoding proto file (Rakesh R via vinayakumarb)
|
separate erasurecoding proto file (Rakesh R via vinayakumarb)
|
||||||
|
|
||||||
|
HDFS-7349. Support DFS command for the EC encoding (vinayakumarb)
|
|
@ -134,6 +134,11 @@ case ${COMMAND} in
|
||||||
hadoop_debug "Appending HADOOP_CLIENT_OPTS onto HADOOP_OPTS"
|
hadoop_debug "Appending HADOOP_CLIENT_OPTS onto HADOOP_OPTS"
|
||||||
HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_CLIENT_OPTS}"
|
HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_CLIENT_OPTS}"
|
||||||
;;
|
;;
|
||||||
|
erasurecode)
|
||||||
|
CLASS=org.apache.hadoop.hdfs.tools.erasurecode.ECCli
|
||||||
|
hadoop_debug "Appending HADOOP_CLIENT_OPTS onto HADOOP_OPTS"
|
||||||
|
HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_CLIENT_OPTS}"
|
||||||
|
;;
|
||||||
fetchdt)
|
fetchdt)
|
||||||
CLASS=org.apache.hadoop.hdfs.tools.DelegationTokenFetcher
|
CLASS=org.apache.hadoop.hdfs.tools.DelegationTokenFetcher
|
||||||
;;
|
;;
|
||||||
|
|
|
@ -120,6 +120,7 @@ import org.apache.hadoop.hdfs.protocol.DatanodeID;
|
||||||
import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
|
import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
|
||||||
import org.apache.hadoop.hdfs.protocol.DirectoryListing;
|
import org.apache.hadoop.hdfs.protocol.DirectoryListing;
|
||||||
import org.apache.hadoop.hdfs.protocol.ECInfo;
|
import org.apache.hadoop.hdfs.protocol.ECInfo;
|
||||||
|
import org.apache.hadoop.hdfs.protocol.ECZoneInfo;
|
||||||
import org.apache.hadoop.hdfs.protocol.EncryptionZone;
|
import org.apache.hadoop.hdfs.protocol.EncryptionZone;
|
||||||
import org.apache.hadoop.hdfs.protocol.EncryptionZoneIterator;
|
import org.apache.hadoop.hdfs.protocol.EncryptionZoneIterator;
|
||||||
import org.apache.hadoop.hdfs.protocol.ExtendedBlock;
|
import org.apache.hadoop.hdfs.protocol.ExtendedBlock;
|
||||||
|
@ -3351,4 +3352,21 @@ public class DFSClient implements java.io.Closeable, RemotePeerFactory,
|
||||||
}
|
}
|
||||||
return scope;
|
return scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the erasure coding zone information for the specified path
|
||||||
|
*
|
||||||
|
* @param src path to get the information for
|
||||||
|
* @return Returns the zone information if path is in EC Zone, null otherwise
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public ECZoneInfo getErasureCodingZoneInfo(String src) throws IOException {
|
||||||
|
checkOpen();
|
||||||
|
try {
|
||||||
|
return namenode.getErasureCodingZoneInfo(src);
|
||||||
|
} catch (RemoteException re) {
|
||||||
|
throw re.unwrapRemoteException(FileNotFoundException.class,
|
||||||
|
AccessControlException.class, UnresolvedPathException.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,7 @@ import org.apache.hadoop.hdfs.protocol.CachePoolInfo;
|
||||||
import org.apache.hadoop.hdfs.protocol.ClientProtocol;
|
import org.apache.hadoop.hdfs.protocol.ClientProtocol;
|
||||||
import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
|
import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
|
||||||
import org.apache.hadoop.hdfs.protocol.DirectoryListing;
|
import org.apache.hadoop.hdfs.protocol.DirectoryListing;
|
||||||
|
import org.apache.hadoop.hdfs.protocol.ECZoneInfo;
|
||||||
import org.apache.hadoop.hdfs.protocol.EncryptionZone;
|
import org.apache.hadoop.hdfs.protocol.EncryptionZone;
|
||||||
import org.apache.hadoop.hdfs.protocol.HdfsConstants;
|
import org.apache.hadoop.hdfs.protocol.HdfsConstants;
|
||||||
import org.apache.hadoop.hdfs.protocol.HdfsConstants.DatanodeReportType;
|
import org.apache.hadoop.hdfs.protocol.HdfsConstants.DatanodeReportType;
|
||||||
|
@ -2306,4 +2307,35 @@ public class DistributedFileSystem extends FileSystem {
|
||||||
}
|
}
|
||||||
}.resolve(this, absF);
|
}.resolve(this, absF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get ErasureCoding zone information for the specified path
|
||||||
|
*
|
||||||
|
* @param path
|
||||||
|
* @return Returns the zone information if path is in EC zone, null otherwise
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public ECZoneInfo getErasureCodingZoneInfo(final Path path)
|
||||||
|
throws IOException {
|
||||||
|
Path absF = fixRelativePart(path);
|
||||||
|
return new FileSystemLinkResolver<ECZoneInfo>() {
|
||||||
|
@Override
|
||||||
|
public ECZoneInfo doCall(final Path p) throws IOException,
|
||||||
|
UnresolvedLinkException {
|
||||||
|
return dfs.getErasureCodingZoneInfo(getPathName(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ECZoneInfo next(final FileSystem fs, final Path p)
|
||||||
|
throws IOException {
|
||||||
|
if (fs instanceof DistributedFileSystem) {
|
||||||
|
DistributedFileSystem myDfs = (DistributedFileSystem) fs;
|
||||||
|
return myDfs.getErasureCodingZoneInfo(p);
|
||||||
|
}
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Cannot getErasureCodingZoneInfo through a symlink to a "
|
||||||
|
+ "non-DistributedFileSystem: " + path + " -> " + p);
|
||||||
|
}
|
||||||
|
}.resolve(this, absF);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1484,4 +1484,13 @@ public interface ClientProtocol {
|
||||||
*/
|
*/
|
||||||
@Idempotent
|
@Idempotent
|
||||||
public ECSchema[] getECSchemas() throws IOException;
|
public ECSchema[] getECSchemas() throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the information about the EC zone for the path
|
||||||
|
*
|
||||||
|
* @param src path to get the info for
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
@Idempotent
|
||||||
|
public ECZoneInfo getErasureCodingZoneInfo(String src) throws IOException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with this
|
||||||
|
* work for additional information regarding copyright ownership. The ASF
|
||||||
|
* licenses this file to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
package org.apache.hadoop.hdfs.protocol;
|
||||||
|
|
||||||
|
import org.apache.hadoop.io.erasurecode.ECSchema;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information about the EC Zone at the specified path.
|
||||||
|
*/
|
||||||
|
public class ECZoneInfo {
|
||||||
|
|
||||||
|
private String dir;
|
||||||
|
private ECSchema schema;
|
||||||
|
|
||||||
|
public ECZoneInfo(String dir, ECSchema schema) {
|
||||||
|
this.dir = dir;
|
||||||
|
this.schema = schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get directory of the EC zone.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getDir() {
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the schema for the EC Zone
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public ECSchema getSchema() {
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Dir: " + getDir() + ", Schema: " + schema;
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,6 +36,7 @@ import org.apache.hadoop.hdfs.protocol.ClientProtocol;
|
||||||
import org.apache.hadoop.hdfs.protocol.CorruptFileBlocks;
|
import org.apache.hadoop.hdfs.protocol.CorruptFileBlocks;
|
||||||
import org.apache.hadoop.hdfs.protocol.DirectoryListing;
|
import org.apache.hadoop.hdfs.protocol.DirectoryListing;
|
||||||
import org.apache.hadoop.hdfs.protocol.ECInfo;
|
import org.apache.hadoop.hdfs.protocol.ECInfo;
|
||||||
|
import org.apache.hadoop.hdfs.protocol.ECZoneInfo;
|
||||||
import org.apache.hadoop.hdfs.protocol.EncryptionZone;
|
import org.apache.hadoop.hdfs.protocol.EncryptionZone;
|
||||||
import org.apache.hadoop.hdfs.protocol.HdfsConstants;
|
import org.apache.hadoop.hdfs.protocol.HdfsConstants;
|
||||||
import org.apache.hadoop.hdfs.protocol.HdfsFileStatus;
|
import org.apache.hadoop.hdfs.protocol.HdfsFileStatus;
|
||||||
|
@ -201,6 +202,8 @@ import org.apache.hadoop.hdfs.protocol.proto.EncryptionZonesProtos.ListEncryptio
|
||||||
import org.apache.hadoop.hdfs.protocol.proto.EncryptionZonesProtos.ListEncryptionZonesRequestProto;
|
import org.apache.hadoop.hdfs.protocol.proto.EncryptionZonesProtos.ListEncryptionZonesRequestProto;
|
||||||
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.GetECSchemasRequestProto;
|
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.GetECSchemasRequestProto;
|
||||||
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.GetECSchemasResponseProto;
|
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.GetECSchemasResponseProto;
|
||||||
|
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.GetECZoneInfoRequestProto;
|
||||||
|
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.GetECZoneInfoResponseProto;
|
||||||
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.GetErasureCodingInfoRequestProto;
|
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.GetErasureCodingInfoRequestProto;
|
||||||
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.GetErasureCodingInfoResponseProto;
|
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.GetErasureCodingInfoResponseProto;
|
||||||
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.CreateErasureCodingZoneRequestProto;
|
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.CreateErasureCodingZoneRequestProto;
|
||||||
|
@ -1551,4 +1554,19 @@ public class ClientNamenodeProtocolServerSideTranslatorPB implements
|
||||||
throw new ServiceException(e);
|
throw new ServiceException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GetECZoneInfoResponseProto getErasureCodingZoneInfo(RpcController controller,
|
||||||
|
GetECZoneInfoRequestProto request) throws ServiceException {
|
||||||
|
try {
|
||||||
|
ECZoneInfo ecZoneInfo = server.getErasureCodingZoneInfo(request.getSrc());
|
||||||
|
GetECZoneInfoResponseProto.Builder builder = GetECZoneInfoResponseProto.newBuilder();
|
||||||
|
if (ecZoneInfo != null) {
|
||||||
|
builder.setECZoneInfo(PBHelper.convertECZoneInfo(ecZoneInfo));
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ServiceException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,7 @@ import org.apache.hadoop.hdfs.protocol.DatanodeID;
|
||||||
import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
|
import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
|
||||||
import org.apache.hadoop.hdfs.protocol.DirectoryListing;
|
import org.apache.hadoop.hdfs.protocol.DirectoryListing;
|
||||||
import org.apache.hadoop.hdfs.protocol.ECInfo;
|
import org.apache.hadoop.hdfs.protocol.ECInfo;
|
||||||
|
import org.apache.hadoop.hdfs.protocol.ECZoneInfo;
|
||||||
import org.apache.hadoop.hdfs.protocol.EncryptionZone;
|
import org.apache.hadoop.hdfs.protocol.EncryptionZone;
|
||||||
import org.apache.hadoop.hdfs.protocol.ExtendedBlock;
|
import org.apache.hadoop.hdfs.protocol.ExtendedBlock;
|
||||||
import org.apache.hadoop.hdfs.protocol.HdfsConstants.DatanodeReportType;
|
import org.apache.hadoop.hdfs.protocol.HdfsConstants.DatanodeReportType;
|
||||||
|
@ -167,6 +168,8 @@ import org.apache.hadoop.hdfs.protocol.proto.EncryptionZonesProtos.GetEZForPathR
|
||||||
import org.apache.hadoop.hdfs.protocol.proto.EncryptionZonesProtos.ListEncryptionZonesRequestProto;
|
import org.apache.hadoop.hdfs.protocol.proto.EncryptionZonesProtos.ListEncryptionZonesRequestProto;
|
||||||
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.GetECSchemasRequestProto;
|
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.GetECSchemasRequestProto;
|
||||||
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.GetECSchemasResponseProto;
|
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.GetECSchemasResponseProto;
|
||||||
|
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.GetECZoneInfoRequestProto;
|
||||||
|
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.GetECZoneInfoResponseProto;
|
||||||
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.GetErasureCodingInfoRequestProto;
|
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.GetErasureCodingInfoRequestProto;
|
||||||
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.GetErasureCodingInfoResponseProto;
|
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.GetErasureCodingInfoResponseProto;
|
||||||
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.CreateErasureCodingZoneRequestProto;
|
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.CreateErasureCodingZoneRequestProto;
|
||||||
|
@ -1576,4 +1579,20 @@ public class ClientNamenodeProtocolTranslatorPB implements
|
||||||
throw ProtobufHelper.getRemoteException(e);
|
throw ProtobufHelper.getRemoteException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ECZoneInfo getErasureCodingZoneInfo(String src) throws IOException {
|
||||||
|
GetECZoneInfoRequestProto req = GetECZoneInfoRequestProto.newBuilder()
|
||||||
|
.setSrc(src).build();
|
||||||
|
try {
|
||||||
|
GetECZoneInfoResponseProto response = rpcProxy.getErasureCodingZoneInfo(
|
||||||
|
null, req);
|
||||||
|
if (response.hasECZoneInfo()) {
|
||||||
|
return PBHelper.convertECZoneInfo(response.getECZoneInfo());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (ServiceException e) {
|
||||||
|
throw ProtobufHelper.getRemoteException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,6 +76,7 @@ import org.apache.hadoop.hdfs.protocol.DirectoryListing;
|
||||||
import org.apache.hadoop.hdfs.protocol.EncryptionZone;
|
import org.apache.hadoop.hdfs.protocol.EncryptionZone;
|
||||||
import org.apache.hadoop.hdfs.protocol.ExtendedBlock;
|
import org.apache.hadoop.hdfs.protocol.ExtendedBlock;
|
||||||
import org.apache.hadoop.fs.FileEncryptionInfo;
|
import org.apache.hadoop.fs.FileEncryptionInfo;
|
||||||
|
import org.apache.hadoop.hdfs.protocol.ECZoneInfo;
|
||||||
import org.apache.hadoop.hdfs.protocol.FsPermissionExtension;
|
import org.apache.hadoop.hdfs.protocol.FsPermissionExtension;
|
||||||
import org.apache.hadoop.hdfs.protocol.HdfsConstants;
|
import org.apache.hadoop.hdfs.protocol.HdfsConstants;
|
||||||
import org.apache.hadoop.hdfs.protocol.HdfsConstants.DatanodeReportType;
|
import org.apache.hadoop.hdfs.protocol.HdfsConstants.DatanodeReportType;
|
||||||
|
@ -134,6 +135,7 @@ import org.apache.hadoop.hdfs.protocol.proto.DatanodeProtocolProtos.BlockReportC
|
||||||
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.ECInfoProto;
|
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.ECInfoProto;
|
||||||
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.ECSchemaOptionEntryProto;
|
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.ECSchemaOptionEntryProto;
|
||||||
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.ECSchemaProto;
|
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.ECSchemaProto;
|
||||||
|
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.ECZoneInfoProto;
|
||||||
import org.apache.hadoop.hdfs.protocol.proto.HdfsProtos;
|
import org.apache.hadoop.hdfs.protocol.proto.HdfsProtos;
|
||||||
import org.apache.hadoop.hdfs.protocol.proto.HdfsProtos.BlockKeyProto;
|
import org.apache.hadoop.hdfs.protocol.proto.HdfsProtos.BlockKeyProto;
|
||||||
import org.apache.hadoop.hdfs.protocol.proto.HdfsProtos.BlockProto;
|
import org.apache.hadoop.hdfs.protocol.proto.HdfsProtos.BlockProto;
|
||||||
|
@ -3138,4 +3140,14 @@ public class PBHelper {
|
||||||
}
|
}
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ECZoneInfoProto convertECZoneInfo(ECZoneInfo ecZoneInfo) {
|
||||||
|
return ECZoneInfoProto.newBuilder().setDir(ecZoneInfo.getDir())
|
||||||
|
.setSchema(convertECSchema(ecZoneInfo.getSchema())).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ECZoneInfo convertECZoneInfo(ECZoneInfoProto ecZoneInfoProto) {
|
||||||
|
return new ECZoneInfo(ecZoneInfoProto.getDir(),
|
||||||
|
convertECSchema(ecZoneInfoProto.getSchema()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import com.google.common.collect.Lists;
|
||||||
import org.apache.hadoop.fs.XAttr;
|
import org.apache.hadoop.fs.XAttr;
|
||||||
import org.apache.hadoop.fs.XAttrSetFlag;
|
import org.apache.hadoop.fs.XAttrSetFlag;
|
||||||
import org.apache.hadoop.hdfs.XAttrHelper;
|
import org.apache.hadoop.hdfs.XAttrHelper;
|
||||||
|
import org.apache.hadoop.hdfs.protocol.ECZoneInfo;
|
||||||
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.ECSchemaProto;
|
import org.apache.hadoop.hdfs.protocol.proto.ErasureCodingProtos.ECSchemaProto;
|
||||||
import org.apache.hadoop.hdfs.protocolPB.PBHelper;
|
import org.apache.hadoop.hdfs.protocolPB.PBHelper;
|
||||||
import org.apache.hadoop.io.erasurecode.ECSchema;
|
import org.apache.hadoop.io.erasurecode.ECSchema;
|
||||||
|
@ -58,6 +59,11 @@ public class ErasureCodingZoneManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
ECSchema getECSchema(INodesInPath iip) throws IOException {
|
ECSchema getECSchema(INodesInPath iip) throws IOException {
|
||||||
|
ECZoneInfo ecZoneInfo = getECZoneInfo(iip);
|
||||||
|
return ecZoneInfo == null ? null : ecZoneInfo.getSchema();
|
||||||
|
}
|
||||||
|
|
||||||
|
ECZoneInfo getECZoneInfo(INodesInPath iip) throws IOException {
|
||||||
assert dir.hasReadLock();
|
assert dir.hasReadLock();
|
||||||
Preconditions.checkNotNull(iip);
|
Preconditions.checkNotNull(iip);
|
||||||
List<INode> inodes = iip.getReadOnlyINodes();
|
List<INode> inodes = iip.getReadOnlyINodes();
|
||||||
|
@ -80,7 +86,8 @@ public class ErasureCodingZoneManager {
|
||||||
if (XATTR_ERASURECODING_ZONE.equals(XAttrHelper.getPrefixName(xAttr))) {
|
if (XATTR_ERASURECODING_ZONE.equals(XAttrHelper.getPrefixName(xAttr))) {
|
||||||
ECSchemaProto ecSchemaProto;
|
ECSchemaProto ecSchemaProto;
|
||||||
ecSchemaProto = ECSchemaProto.parseFrom(xAttr.getValue());
|
ecSchemaProto = ECSchemaProto.parseFrom(xAttr.getValue());
|
||||||
return PBHelper.convertECSchema(ecSchemaProto);
|
ECSchema schema = PBHelper.convertECSchema(ecSchemaProto);
|
||||||
|
return new ECZoneInfo(inode.getFullPathName(), schema);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ import org.apache.hadoop.hdfs.DFSUtil;
|
||||||
import org.apache.hadoop.hdfs.XAttrHelper;
|
import org.apache.hadoop.hdfs.XAttrHelper;
|
||||||
import org.apache.hadoop.hdfs.protocol.Block;
|
import org.apache.hadoop.hdfs.protocol.Block;
|
||||||
import org.apache.hadoop.hdfs.protocol.BlockStoragePolicy;
|
import org.apache.hadoop.hdfs.protocol.BlockStoragePolicy;
|
||||||
|
import org.apache.hadoop.hdfs.protocol.ECZoneInfo;
|
||||||
import org.apache.hadoop.hdfs.protocol.EncryptionZone;
|
import org.apache.hadoop.hdfs.protocol.EncryptionZone;
|
||||||
import org.apache.hadoop.hdfs.protocol.FSLimitException.MaxDirectoryItemsExceededException;
|
import org.apache.hadoop.hdfs.protocol.FSLimitException.MaxDirectoryItemsExceededException;
|
||||||
import org.apache.hadoop.hdfs.protocol.FSLimitException.PathComponentTooLongException;
|
import org.apache.hadoop.hdfs.protocol.FSLimitException.PathComponentTooLongException;
|
||||||
|
@ -1249,6 +1250,15 @@ public class FSDirectory implements Closeable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ECZoneInfo getECZoneInfo(INodesInPath iip) throws IOException {
|
||||||
|
readLock();
|
||||||
|
try {
|
||||||
|
return ecZoneManager.getECZoneInfo(iip);
|
||||||
|
} finally {
|
||||||
|
readUnlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static INode resolveLastINode(INodesInPath iip) throws FileNotFoundException {
|
static INode resolveLastINode(INodesInPath iip) throws FileNotFoundException {
|
||||||
INode inode = iip.getLastINode();
|
INode inode = iip.getLastINode();
|
||||||
if (inode == null) {
|
if (inode == null) {
|
||||||
|
|
|
@ -181,6 +181,7 @@ import org.apache.hadoop.hdfs.protocol.DatanodeID;
|
||||||
import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
|
import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
|
||||||
import org.apache.hadoop.hdfs.protocol.DirectoryListing;
|
import org.apache.hadoop.hdfs.protocol.DirectoryListing;
|
||||||
import org.apache.hadoop.hdfs.protocol.ECInfo;
|
import org.apache.hadoop.hdfs.protocol.ECInfo;
|
||||||
|
import org.apache.hadoop.hdfs.protocol.ECZoneInfo;
|
||||||
import org.apache.hadoop.hdfs.protocol.EncryptionZone;
|
import org.apache.hadoop.hdfs.protocol.EncryptionZone;
|
||||||
import org.apache.hadoop.hdfs.protocol.ExtendedBlock;
|
import org.apache.hadoop.hdfs.protocol.ExtendedBlock;
|
||||||
import org.apache.hadoop.hdfs.protocol.HdfsConstants;
|
import org.apache.hadoop.hdfs.protocol.HdfsConstants;
|
||||||
|
@ -7593,6 +7594,29 @@ public class FSNamesystem implements Namesystem, FSNamesystemMBean,
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the erasure coding zone information for specified path
|
||||||
|
*/
|
||||||
|
ECZoneInfo getErasureCodingZoneInfo(String src) throws AccessControlException,
|
||||||
|
UnresolvedLinkException, IOException {
|
||||||
|
checkOperation(OperationCategory.READ);
|
||||||
|
final byte[][] pathComponents = FSDirectory
|
||||||
|
.getPathComponentsForReservedPath(src);
|
||||||
|
final FSPermissionChecker pc = getPermissionChecker();
|
||||||
|
readLock();
|
||||||
|
try {
|
||||||
|
checkOperation(OperationCategory.READ);
|
||||||
|
src = dir.resolvePath(pc, src, pathComponents);
|
||||||
|
final INodesInPath iip = dir.getINodesInPath(src, true);
|
||||||
|
if (isPermissionEnabled) {
|
||||||
|
dir.checkPathAccess(pc, iip, FsAction.READ);
|
||||||
|
}
|
||||||
|
return dir.getECZoneInfo(iip);
|
||||||
|
} finally {
|
||||||
|
readUnlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get available ECSchemas
|
* Get available ECSchemas
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -85,6 +85,7 @@ import org.apache.hadoop.hdfs.protocol.DatanodeID;
|
||||||
import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
|
import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
|
||||||
import org.apache.hadoop.hdfs.protocol.DirectoryListing;
|
import org.apache.hadoop.hdfs.protocol.DirectoryListing;
|
||||||
import org.apache.hadoop.hdfs.protocol.ECInfo;
|
import org.apache.hadoop.hdfs.protocol.ECInfo;
|
||||||
|
import org.apache.hadoop.hdfs.protocol.ECZoneInfo;
|
||||||
import org.apache.hadoop.hdfs.protocol.EncryptionZone;
|
import org.apache.hadoop.hdfs.protocol.EncryptionZone;
|
||||||
import org.apache.hadoop.hdfs.protocol.ExtendedBlock;
|
import org.apache.hadoop.hdfs.protocol.ExtendedBlock;
|
||||||
import org.apache.hadoop.hdfs.protocol.FSLimitException;
|
import org.apache.hadoop.hdfs.protocol.FSLimitException;
|
||||||
|
@ -2043,4 +2044,10 @@ class NameNodeRpcServer implements NamenodeProtocols {
|
||||||
checkNNStartup();
|
checkNNStartup();
|
||||||
return namesystem.getECSchemas();
|
return namesystem.getECSchemas();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override // ClientProtocol
|
||||||
|
public ECZoneInfo getErasureCodingZoneInfo(String src) throws IOException {
|
||||||
|
checkNNStartup();
|
||||||
|
return namesystem.getErasureCodingZoneInfo(src);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with this
|
||||||
|
* work for additional information regarding copyright ownership. The ASF
|
||||||
|
* licenses this file to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
package org.apache.hadoop.hdfs.tools.erasurecode;
|
||||||
|
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
import org.apache.hadoop.fs.FsShell;
|
||||||
|
import org.apache.hadoop.fs.shell.CommandFactory;
|
||||||
|
import org.apache.hadoop.hdfs.HdfsConfiguration;
|
||||||
|
import org.apache.hadoop.util.ToolRunner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CLI for the erasure code encoding operations.
|
||||||
|
*/
|
||||||
|
public class ECCli extends FsShell {
|
||||||
|
|
||||||
|
private final static String usagePrefix =
|
||||||
|
"Usage: hdfs erasurecode [generic options]";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getUsagePrefix() {
|
||||||
|
return usagePrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void registerCommands(CommandFactory factory) {
|
||||||
|
factory.registerCommands(ECCommand.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
Configuration conf = new HdfsConfiguration();
|
||||||
|
int res = ToolRunner.run(conf, new ECCli(), args);
|
||||||
|
System.exit(res);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,209 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with this
|
||||||
|
* work for additional information regarding copyright ownership. The ASF
|
||||||
|
* licenses this file to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
package org.apache.hadoop.hdfs.tools.erasurecode;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
import org.apache.hadoop.HadoopIllegalArgumentException;
|
||||||
|
import org.apache.hadoop.classification.InterfaceAudience;
|
||||||
|
import org.apache.hadoop.classification.InterfaceStability;
|
||||||
|
import org.apache.hadoop.fs.FileSystem;
|
||||||
|
import org.apache.hadoop.fs.Path;
|
||||||
|
import org.apache.hadoop.fs.shell.Command;
|
||||||
|
import org.apache.hadoop.fs.shell.CommandFactory;
|
||||||
|
import org.apache.hadoop.fs.shell.PathData;
|
||||||
|
import org.apache.hadoop.hdfs.DistributedFileSystem;
|
||||||
|
import org.apache.hadoop.hdfs.protocol.ECZoneInfo;
|
||||||
|
import org.apache.hadoop.hdfs.server.namenode.UnsupportedActionException;
|
||||||
|
import org.apache.hadoop.io.erasurecode.ECSchema;
|
||||||
|
import org.apache.hadoop.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erasure Coding CLI commands
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
@InterfaceStability.Evolving
|
||||||
|
public abstract class ECCommand extends Command {
|
||||||
|
|
||||||
|
public static void registerCommands(CommandFactory factory) {
|
||||||
|
// Register all commands of Erasure CLI, with a '-' at the beginning in name
|
||||||
|
// of the command.
|
||||||
|
factory.addClass(CreateECZoneCommand.class, "-" + CreateECZoneCommand.NAME);
|
||||||
|
factory.addClass(GetECZoneInfoCommand.class, "-"
|
||||||
|
+ GetECZoneInfoCommand.NAME);
|
||||||
|
factory.addClass(ListECSchemas.class, "-" + ListECSchemas.NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandName() {
|
||||||
|
return getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void run(Path path) throws IOException {
|
||||||
|
throw new RuntimeException("Not suppose to get here");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
@Override
|
||||||
|
public int runAll() {
|
||||||
|
return run(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void processPath(PathData item) throws IOException {
|
||||||
|
if (!(item.fs instanceof DistributedFileSystem)) {
|
||||||
|
throw new UnsupportedActionException(
|
||||||
|
"Erasure commands are only supported for the HDFS paths");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create EC encoding zone command. Zones are created to use specific EC
|
||||||
|
* encoding schema, other than default while encoding the files under some
|
||||||
|
* specific directory.
|
||||||
|
*/
|
||||||
|
static class CreateECZoneCommand extends ECCommand {
|
||||||
|
public static final String NAME = "createZone";
|
||||||
|
public static final String USAGE = "[-s <schemaName>] <path>";
|
||||||
|
public static final String DESCRIPTION =
|
||||||
|
"Create a zone to encode files using a specified schema\n"
|
||||||
|
+ "Options :\n"
|
||||||
|
+ " -s <schemaName> : EC schema name to encode files. "
|
||||||
|
+ "If not passed default schema will be used\n"
|
||||||
|
+ " <path> : Path to an empty directory. Under this directory "
|
||||||
|
+ "files will be encoded using specified schema";
|
||||||
|
private String schemaName;
|
||||||
|
private ECSchema schema = null;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void processOptions(LinkedList<String> args) throws IOException {
|
||||||
|
schemaName = StringUtils.popOptionWithArgument("-s", args);
|
||||||
|
if (args.isEmpty()) {
|
||||||
|
throw new HadoopIllegalArgumentException("<path> is missing");
|
||||||
|
}
|
||||||
|
if (args.size() > 1) {
|
||||||
|
throw new HadoopIllegalArgumentException("Too many arguments");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void processPath(PathData item) throws IOException {
|
||||||
|
super.processPath(item);
|
||||||
|
DistributedFileSystem dfs = (DistributedFileSystem) item.fs;
|
||||||
|
try {
|
||||||
|
if (schemaName != null) {
|
||||||
|
ECSchema[] ecSchemas = dfs.getClient().getECSchemas();
|
||||||
|
for (ECSchema ecSchema : ecSchemas) {
|
||||||
|
if (schemaName.equals(ecSchema.getSchemaName())) {
|
||||||
|
schema = ecSchema;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (schema == null) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("Schema '");
|
||||||
|
sb.append(schemaName);
|
||||||
|
sb.append("' does not match any of the supported schemas.");
|
||||||
|
sb.append("Please select any one of [");
|
||||||
|
for (ECSchema ecSchema : ecSchemas) {
|
||||||
|
sb.append(ecSchema.getSchemaName());
|
||||||
|
sb.append(", ");
|
||||||
|
}
|
||||||
|
throw new HadoopIllegalArgumentException(sb.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dfs.createErasureCodingZone(item.path, schema);
|
||||||
|
out.println("EC Zone created successfully at " + item.path);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IOException("Unable to create EC zone for the path "
|
||||||
|
+ item.path, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the information about the zone
|
||||||
|
*/
|
||||||
|
static class GetECZoneInfoCommand extends ECCommand {
|
||||||
|
public static final String NAME = "getZoneInfo";
|
||||||
|
public static final String USAGE = "<path>";
|
||||||
|
public static final String DESCRIPTION =
|
||||||
|
"Get information about the EC zone at specified path\n";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void processOptions(LinkedList<String> args) throws IOException {
|
||||||
|
if (args.isEmpty()) {
|
||||||
|
throw new HadoopIllegalArgumentException("<path> is missing");
|
||||||
|
}
|
||||||
|
if (args.size() > 1) {
|
||||||
|
throw new HadoopIllegalArgumentException("Too many arguments");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void processPath(PathData item) throws IOException {
|
||||||
|
super.processPath(item);
|
||||||
|
DistributedFileSystem dfs = (DistributedFileSystem) item.fs;
|
||||||
|
try {
|
||||||
|
ECZoneInfo ecZoneInfo = dfs.getErasureCodingZoneInfo(item.path);
|
||||||
|
out.println(ecZoneInfo.toString());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IOException("Unable to create EC zone for the path "
|
||||||
|
+ item.path, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List all supported EC Schemas
|
||||||
|
*/
|
||||||
|
static class ListECSchemas extends ECCommand {
|
||||||
|
public static final String NAME = "listSchemas";
|
||||||
|
public static final String USAGE = "";
|
||||||
|
public static final String DESCRIPTION =
|
||||||
|
"Get the list of ECSchemas supported\n";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void processOptions(LinkedList<String> args) throws IOException {
|
||||||
|
if (!args.isEmpty()) {
|
||||||
|
throw new HadoopIllegalArgumentException("Too many parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSystem fs = FileSystem.get(getConf());
|
||||||
|
if (fs instanceof DistributedFileSystem == false) {
|
||||||
|
throw new UnsupportedActionException(
|
||||||
|
"Erasure commands are only supported for the HDFS");
|
||||||
|
}
|
||||||
|
DistributedFileSystem dfs = (DistributedFileSystem) fs;
|
||||||
|
|
||||||
|
ECSchema[] ecSchemas = dfs.getClient().getECSchemas();
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
int i = 0;
|
||||||
|
while (i < ecSchemas.length) {
|
||||||
|
ECSchema ecSchema = ecSchemas[i];
|
||||||
|
sb.append(ecSchema.getSchemaName());
|
||||||
|
i++;
|
||||||
|
if (i < ecSchemas.length) {
|
||||||
|
sb.append(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.println(sb.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -867,4 +867,6 @@ service ClientNamenodeProtocol {
|
||||||
returns(GetErasureCodingInfoResponseProto);
|
returns(GetErasureCodingInfoResponseProto);
|
||||||
rpc getECSchemas(GetECSchemasRequestProto)
|
rpc getECSchemas(GetECSchemasRequestProto)
|
||||||
returns(GetECSchemasResponseProto);
|
returns(GetECSchemasResponseProto);
|
||||||
|
rpc getErasureCodingZoneInfo(GetECZoneInfoRequestProto)
|
||||||
|
returns(GetECZoneInfoResponseProto);
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,13 @@ message ECInfoProto {
|
||||||
required ECSchemaProto schema = 2;
|
required ECSchemaProto schema = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ECZoneInfo
|
||||||
|
*/
|
||||||
|
message ECZoneInfoProto {
|
||||||
|
required string dir = 1;
|
||||||
|
required ECSchemaProto schema = 2;
|
||||||
|
}
|
||||||
|
|
||||||
message CreateErasureCodingZoneRequestProto {
|
message CreateErasureCodingZoneRequestProto {
|
||||||
required string src = 1;
|
required string src = 1;
|
||||||
|
@ -72,3 +79,11 @@ message GetECSchemasRequestProto { // void request
|
||||||
message GetECSchemasResponseProto {
|
message GetECSchemasResponseProto {
|
||||||
repeated ECSchemaProto schemas = 1;
|
repeated ECSchemaProto schemas = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message GetECZoneInfoRequestProto {
|
||||||
|
required string src = 1; // path to get the zone info
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetECZoneInfoResponseProto {
|
||||||
|
optional ECZoneInfoProto ECZoneInfo = 1;
|
||||||
|
}
|
Loading…
Reference in New Issue