diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/cblock/CBlockConfigKeys.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/cblock/CBlockConfigKeys.java index a9de0ec2316..cafc5a971c1 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/cblock/CBlockConfigKeys.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/cblock/CBlockConfigKeys.java @@ -25,13 +25,19 @@ public final class CBlockConfigKeys { "dfs.cblock.enabled"; public static final String DFS_CBLOCK_SERVICERPC_ADDRESS_KEY = "dfs.cblock.servicerpc-address"; - public static final int DFS_CBLOCK_RPCSERVICE_PORT_DEFAULT = + public static final String DFS_CBLOCK_SERVICERPC_PORT_KEY = + "dfs.cblock.servicerpc.port"; + public static final int DFS_CBLOCK_SERVICERPC_PORT_DEFAULT = 9810; + public static final String DFS_CBLOCK_SERVICERPC_HOSTNAME_KEY = + "dfs.cblock.servicerpc.hostname"; + public static final String DFS_CBLOCK_SERVICERPC_HOSTNAME_DEFAULT = + "0.0.0.0"; public static final String DFS_CBLOCK_RPCSERVICE_IP_DEFAULT = "0.0.0.0"; public static final String DFS_CBLOCK_SERVICERPC_ADDRESS_DEFAULT = - DFS_CBLOCK_RPCSERVICE_IP_DEFAULT - + ":" + DFS_CBLOCK_RPCSERVICE_PORT_DEFAULT; + DFS_CBLOCK_SERVICERPC_HOSTNAME_DEFAULT + + ":" + DFS_CBLOCK_SERVICERPC_PORT_DEFAULT; public static final String DFS_CBLOCK_JSCSIRPC_ADDRESS_KEY = "dfs.cblock.jscsi-address"; diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/cblock/cli/CBlockCli.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/cblock/cli/CBlockCli.java new file mode 100644 index 00000000000..a8ef700213a --- /dev/null +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/cblock/cli/CBlockCli.java @@ -0,0 +1,284 @@ +/* + * 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.cblock.cli; + +import org.apache.commons.cli.BasicParser; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.OptionBuilder; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.apache.hadoop.cblock.client.CBlockVolumeClient; +import org.apache.hadoop.cblock.meta.VolumeInfo; +import org.apache.hadoop.cblock.protocolPB.CBlockServiceProtocolPB; +import org.apache.hadoop.conf.Configured; +import org.apache.hadoop.ipc.ProtobufRpcEngine; +import org.apache.hadoop.ipc.RPC; +import org.apache.hadoop.ozone.OzoneConfiguration; +import org.apache.hadoop.util.Tool; +import org.apache.hadoop.util.ToolRunner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.PrintStream; +import java.net.InetSocketAddress; +import java.util.Arrays; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * The command line tool class. + */ +public class CBlockCli extends Configured implements Tool { + + private static final String CREATE_VOLUME = "createVolume"; + + private static final String DELETE_VOLUME = "deleteVolume"; + + private static final String INFO_VOLUME = "infoVolume"; + + private static final String LIST_VOLUME = "listVolume"; + + private static final String SERVER_ADDR = "serverAddr"; + + private static final String HELP = "help"; + + private static final Logger LOG = + LoggerFactory.getLogger(CBlockCli.class); + private OzoneConfiguration conf; + + private PrintStream printStream; + + private Options options; + + private BasicParser parser; + + private CBlockVolumeClient localProxy; + + public CBlockCli(OzoneConfiguration conf, PrintStream printStream) + throws IOException { + this.printStream = printStream; + this.conf = conf; + this.options = getOptions(); + this.parser = new BasicParser(); + } + + public CBlockCli(OzoneConfiguration conf) throws IOException{ + this(conf, System.out); + } + + private CommandLine parseArgs(String[] argv) + throws ParseException { + return parser.parse(options, argv); + } + + private static Options getOptions() { + Options options = new Options(); + Option serverAddress = OptionBuilder + .withArgName("serverAddress>: 2) { + force = Boolean.parseBoolean(deleteArgs[2]); + } + localProxy.deleteVolume(userName, volumeName, force); + } + + private void infoVolume(String[] infoArgs) throws IOException { + String userName = infoArgs[0]; + String volumeName = infoArgs[1]; + VolumeInfo volumeInfo = localProxy.infoVolume(userName, volumeName); + printStream.println(volumeInfo.toString()); + } + + private void listVolume(String[] listArgs) throws IOException { + StringBuilder stringBuilder = new StringBuilder(); + List volumeResponse; + if (listArgs == null) { + volumeResponse = localProxy.listVolume(null); + } else { + volumeResponse = localProxy.listVolume(listArgs[0]); + } + for (int i = 0; i listVolume(String userName) throws IOException { + CBlockServiceProtocolProtos.ListVolumeRequestProto.Builder req = + CBlockServiceProtocolProtos.ListVolumeRequestProto.newBuilder(); + if (userName != null) { + req.setUserName(userName); + } + try { + CBlockServiceProtocolProtos.ListVolumeResponseProto resp = + rpcProxy.listVolume(null, req.build()); + List respList = new ArrayList<>(); + for (CBlockServiceProtocolProtos.VolumeInfoProto entry : + resp.getVolumeEntryList()) { + VolumeInfo volumeInfo = new VolumeInfo( + entry.getUserName(), entry.getVolumeName(), entry.getVolumeSize(), + entry.getBlockSize()); + respList.add(volumeInfo); + } + return respList; + } catch (ServiceException e) { + throw ProtobufHelper.getRemoteException(e); + } catch (Exception e) { + throw new IOException("got" + e.getCause() + " " + e.getMessage()); + } + } +} diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/cblock/client/CBlockVolumeClient.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/cblock/client/CBlockVolumeClient.java new file mode 100644 index 00000000000..c783c67e609 --- /dev/null +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/cblock/client/CBlockVolumeClient.java @@ -0,0 +1,96 @@ +/* + * 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.cblock.client; + +import org.apache.hadoop.cblock.meta.VolumeInfo; +import org.apache.hadoop.cblock.protocolPB.CBlockServiceProtocolPB; +import org.apache.hadoop.io.retry.RetryPolicies; +import org.apache.hadoop.ipc.RPC; +import org.apache.hadoop.net.NetUtils; +import org.apache.hadoop.ozone.OzoneConfiguration; +import org.apache.hadoop.security.UserGroupInformation; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static org.apache.hadoop.cblock.CBlockConfigKeys.DFS_CBLOCK_SERVICERPC_HOSTNAME_DEFAULT; +import static org.apache.hadoop.cblock.CBlockConfigKeys.DFS_CBLOCK_SERVICERPC_HOSTNAME_KEY; +import static org.apache.hadoop.cblock.CBlockConfigKeys.DFS_CBLOCK_SERVICERPC_PORT_DEFAULT; +import static org.apache.hadoop.cblock.CBlockConfigKeys.DFS_CBLOCK_SERVICERPC_PORT_KEY; + +/** + * Implementation of client used by CBlock command line tool. + */ +public class CBlockVolumeClient { + private final CBlockServiceProtocolClientSideTranslatorPB cblockClient; + private final OzoneConfiguration conf; + + public CBlockVolumeClient(OzoneConfiguration conf) throws IOException { + this.conf = conf; + long version = RPC.getProtocolVersion(CBlockServiceProtocolPB.class); + String serverAddress = conf.get(DFS_CBLOCK_SERVICERPC_HOSTNAME_KEY, + DFS_CBLOCK_SERVICERPC_HOSTNAME_DEFAULT); + int serverPort = conf.getInt(DFS_CBLOCK_SERVICERPC_PORT_KEY, + DFS_CBLOCK_SERVICERPC_PORT_DEFAULT); + InetSocketAddress address = new InetSocketAddress( + serverAddress, serverPort); + // currently the largest supported volume is about 8TB, which might take + // > 20 seconds to finish creating containers. thus set timeout to 30 sec. + cblockClient = new CBlockServiceProtocolClientSideTranslatorPB( + RPC.getProtocolProxy(CBlockServiceProtocolPB.class, version, + address, UserGroupInformation.getCurrentUser(), conf, + NetUtils.getDefaultSocketFactory(conf), 30000, RetryPolicies + .retryUpToMaximumCountWithFixedSleep(300, 1, TimeUnit + .SECONDS)).getProxy()); + } + + public CBlockVolumeClient(OzoneConfiguration conf, + InetSocketAddress serverAddress) throws IOException { + this.conf = conf; + long version = RPC.getProtocolVersion(CBlockServiceProtocolPB.class); + cblockClient = new CBlockServiceProtocolClientSideTranslatorPB( + RPC.getProtocolProxy(CBlockServiceProtocolPB.class, version, + serverAddress, UserGroupInformation.getCurrentUser(), conf, + NetUtils.getDefaultSocketFactory(conf), 30000, RetryPolicies + .retryUpToMaximumCountWithFixedSleep(300, 1, TimeUnit + .SECONDS)).getProxy()); + } + + public void createVolume(String userName, String volumeName, + long volumeSize, int blockSize) throws IOException { + cblockClient.createVolume(userName, volumeName, + volumeSize, blockSize); + } + + public void deleteVolume(String userName, String volumeName, boolean force) + throws IOException { + cblockClient.deleteVolume(userName, volumeName, force); + } + + public VolumeInfo infoVolume(String userName, String volumeName) + throws IOException { + return cblockClient.infoVolume(userName, volumeName); + } + + public List listVolume(String userName) + throws IOException { + return cblockClient.listVolume(userName); + } +} diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/cblock/client/package-info.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/cblock/client/package-info.java new file mode 100644 index 00000000000..761b71eba59 --- /dev/null +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/cblock/client/package-info.java @@ -0,0 +1,18 @@ +/** + * 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.cblock.client; diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/cblock/TestCBlockCLI.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/cblock/TestCBlockCLI.java new file mode 100644 index 00000000000..b8697595334 --- /dev/null +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/cblock/TestCBlockCLI.java @@ -0,0 +1,224 @@ +/* + * 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.cblock; + +import org.apache.hadoop.cblock.cli.CBlockCli; +import org.apache.hadoop.cblock.meta.VolumeDescriptor; +import org.apache.hadoop.cblock.util.MockStorageClient; +import org.apache.hadoop.ozone.OzoneConfiguration; +import org.apache.hadoop.scm.client.ScmClient; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.util.List; + +import static org.apache.hadoop.cblock.CBlockConfigKeys.DFS_CBLOCK_SERVICE_LEVELDB_PATH_KEY; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * A testing class for cblock command line tool. + */ +public class TestCBlockCLI { + private static final long GB = 1 * 1024 * 1024 * 1024L; + private static final int KB = 1024; + private static CBlockCli cmd; + private static OzoneConfiguration conf; + private static CBlockManager cBlockManager; + private static ByteArrayOutputStream outContent; + private static PrintStream testPrintOut; + + @BeforeClass + public static void setup() throws IOException { + outContent = new ByteArrayOutputStream(); + ScmClient storageClient = new MockStorageClient(); + conf = new OzoneConfiguration(); + conf.set(DFS_CBLOCK_SERVICE_LEVELDB_PATH_KEY, "/tmp/testCblockCli.dat"); + cBlockManager = new CBlockManager(conf, storageClient); + cBlockManager.start(); + testPrintOut = new PrintStream(outContent); + cmd = new CBlockCli(conf, testPrintOut); + } + + @AfterClass + public static void clean() { + cBlockManager.stop(); + cBlockManager.join(); + cBlockManager.clean(); + } + + @After + public void reset() { + outContent.reset(); + } + + /** + * Test the help command. + * @throws Exception + */ + @Test + public void testCliHelp() throws Exception { + PrintStream initialStdOut = System.out; + System.setOut(testPrintOut); + String[] args = {"-h"}; + cmd.run(args); + String helpPrints = + "usage: cblock\n" + + " -c,--createVolume " + + " create a fresh new volume\n" + + " -d,--deleteVolume " + + " delete a volume\n" + + " -h,--help " + + " help\n" + + " -i,--infoVolume " + + " info a volume\n" + + " -l,--listVolume " + + " list all volumes\n" + + " -s,--serverAddr : " + + " specify server address:port\n"; + assertEquals(helpPrints, outContent.toString()); + outContent.reset(); + System.setOut(initialStdOut); + } + + /** + * Test volume listing command. + * @throws Exception + */ + @Test + public void testCliList() throws Exception { + String userName0 = "userTestCliList0"; + String userName1 = "userTestCliList1"; + String userTestNotExist = "userTestNotExist"; + String volumeName0 = "volumeTest0"; + String volumeName1 = "volumeTest1"; + String volumeSize0 = "30GB"; + String volumeSize1 = "40GB"; + String blockSize = Integer.toString(4); + String[] argsCreate0 = + {"-c", userName0, volumeName0, volumeSize0, blockSize}; + cmd.run(argsCreate0); + String[] argsCreate1 = + {"-c", userName0, volumeName1, volumeSize1, blockSize}; + cmd.run(argsCreate1); + String[] argsCreate2 = + {"-c", userName1, volumeName0, volumeSize0, blockSize}; + cmd.run(argsCreate2); + String[] argsList0 = {"-l"}; + cmd.run(argsList0); + String[] outExpected1 = { + "userTestCliList1:volumeTest0\t32212254720\t4096\n", + "userTestCliList0:volumeTest0\t32212254720\t4096\n", + "userTestCliList0:volumeTest1\t42949672960\t4096\n"}; + int length = 0; + for (String str : outExpected1) { + assertTrue(outContent.toString().contains(str)); + length += str.length(); + } + assertEquals(length, outContent.toString().length()); + outContent.reset(); + + String[] argsList1 = {"-l", userName1}; + cmd.run(argsList1); + String outExpected2 = "userTestCliList1:volumeTest0\t32212254720\t4096\n"; + assertEquals(outExpected2, outContent.toString()); + outContent.reset(); + + String[] argsList2 = {"-l", userTestNotExist}; + cmd.run(argsList2); + String outExpected3 = "\n"; + assertEquals(outExpected3, outContent.toString()); + } + + /** + * Test create volume command. + * @throws Exception + */ + @Test + public void testCliCreate() throws Exception { + String userName = "userTestCliCreate"; + String volumeName = "volumeTest"; + String volumeSize = "30GB"; + String blockSize = "4"; + String[] argsCreate = {"-c", userName, volumeName, volumeSize, blockSize}; + cmd.run(argsCreate); + List allVolumes = cBlockManager.getAllVolumes(userName); + assertEquals(1, allVolumes.size()); + VolumeDescriptor volume = allVolumes.get(0); + assertEquals(userName, volume.getUserName()); + assertEquals(volumeName, volume.getVolumeName()); + long volumeSizeB = volume.getVolumeSize(); + assertEquals(30, (int)(volumeSizeB/ GB)); + assertEquals(4, volume.getBlockSize()/ KB); + } + + /** + * Test delete volume command. + * @throws Exception + */ + @Test + public void testCliDelete() throws Exception { + String userName = "userTestCliDelete"; + String volumeName = "volumeTest"; + String volumeSize = "30GB"; + String blockSize = "4"; + String[] argsCreate = {"-c", userName, volumeName, volumeSize, blockSize}; + cmd.run(argsCreate); + List allVolumes = cBlockManager.getAllVolumes(userName); + assertEquals(1, allVolumes.size()); + VolumeDescriptor volume = allVolumes.get(0); + assertEquals(userName, volume.getUserName()); + assertEquals(volumeName, volume.getVolumeName()); + long volumeSizeB = volume.getVolumeSize(); + assertEquals(30, (int)(volumeSizeB/ GB)); + assertEquals(4, volume.getBlockSize()/ KB); + + String[] argsDelete = {"-d", userName, volumeName}; + cmd.run(argsDelete); + allVolumes = cBlockManager.getAllVolumes(userName); + assertEquals(0, allVolumes.size()); + } + + /** + * Test info volume command. + * @throws Exception + */ + @Test + public void testCliInfoVolume() throws Exception { + String userName0 = "userTestCliInfo"; + String volumeName0 = "volumeTest0"; + String volumeSize = "8000GB"; + String blockSize = "4"; + String[] argsCreate0 = { + "-c", userName0, volumeName0, volumeSize, blockSize}; + cmd.run(argsCreate0); + String[] argsInfo = {"-i", userName0, volumeName0}; + cmd.run(argsInfo); + // TODO : the usage field is not implemented yet, always 0 now. + String outExpected = " userName:userTestCliInfo " + + "volumeName:volumeTest0 " + + "volumeSize:8589934592000 " + + "blockSize:4096 (sizeInBlocks:2097152000) usageInBlocks:0\n"; + assertEquals(outExpected, outContent.toString()); + } +}