HDFS-11138. Block Storage: add block storage server. Contributed by Chen Liang
This commit is contained in:
parent
48db56adea
commit
9997d36eb6
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* This class contains constants for configuration keys used in CBlock.
|
||||
*/
|
||||
public final class CBlockConfigKeys {
|
||||
public static final String DFS_CBLOCK_ENABLED_KEY =
|
||||
"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 =
|
||||
9810;
|
||||
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;
|
||||
|
||||
public static final String DFS_CBLOCK_JSCSIRPC_ADDRESS_KEY =
|
||||
"dfs.cblock.jscsi-address";
|
||||
|
||||
//The port on CBlockManager node for jSCSI to ask
|
||||
public static final int DFS_CBLOCK_JSCSI_PORT_DEFAULT =
|
||||
9811;
|
||||
public static final String DFS_CBLOCK_JSCSIRPC_ADDRESS_DEFAULT =
|
||||
DFS_CBLOCK_RPCSERVICE_IP_DEFAULT
|
||||
+ ":" + DFS_CBLOCK_JSCSI_PORT_DEFAULT;
|
||||
|
||||
|
||||
public static final String DFS_CBLOCK_SERVICERPC_BIND_HOST_KEY =
|
||||
"dfs.cblock.service.rpc-bind-host";
|
||||
public static final String DFS_CBLOCK_JSCSIRPC_BIND_HOST_KEY =
|
||||
"dfs.cblock.jscsi.rpc-bind-host";
|
||||
|
||||
// default block size is 4KB
|
||||
public static final int DFS_CBLOCK_SERVICE_BLOCK_SIZE_DEFAULT =
|
||||
4096;
|
||||
|
||||
public static final String DFS_CBLOCK_SERVICERPC_HANDLER_COUNT_KEY =
|
||||
"dfs.storage.service.handler.count";
|
||||
public static final int DFS_CBLOCK_SERVICERPC_HANDLER_COUNT_DEFAULT = 10;
|
||||
|
||||
private CBlockConfigKeys() {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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.conf.Configuration;
|
||||
|
||||
/**
|
||||
* This class specifies the CBlock configuration resources.
|
||||
*/
|
||||
public class CBlockConfiguration extends Configuration {
|
||||
static {
|
||||
// adds the default resources
|
||||
Configuration.addDefaultResource("hdfs-default.xml");
|
||||
Configuration.addDefaultResource("hdfs-site.xml");
|
||||
Configuration.addDefaultResource("cblock-default.xml");
|
||||
Configuration.addDefaultResource("cblock-site.xml");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
* 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 com.google.protobuf.BlockingService;
|
||||
import org.apache.hadoop.cblock.meta.VolumeDescriptor;
|
||||
import org.apache.hadoop.cblock.meta.VolumeInfo;
|
||||
import org.apache.hadoop.cblock.proto.CBlockClientServerProtocol;
|
||||
import org.apache.hadoop.cblock.proto.CBlockServiceProtocol;
|
||||
import org.apache.hadoop.cblock.proto.MountVolumeResponse;
|
||||
import org.apache.hadoop.cblock.protocol.proto.CBlockClientServerProtocolProtos;
|
||||
import org.apache.hadoop.cblock.protocol.proto.CBlockServiceProtocolProtos;
|
||||
import org.apache.hadoop.cblock.protocolPB.CBlockClientServerProtocolPB;
|
||||
import org.apache.hadoop.cblock.protocolPB.CBlockClientServerProtocolServerSideTranslatorPB;
|
||||
import org.apache.hadoop.cblock.protocolPB.CBlockServiceProtocolPB;
|
||||
import org.apache.hadoop.cblock.protocolPB.CBlockServiceProtocolServerSideTranslatorPB;
|
||||
import org.apache.hadoop.cblock.storage.IStorageClient;
|
||||
import org.apache.hadoop.cblock.storage.StorageManager;
|
||||
import org.apache.hadoop.ipc.ProtobufRpcEngine;
|
||||
import org.apache.hadoop.ipc.RPC;
|
||||
import org.apache.hadoop.net.NetUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.apache.hadoop.cblock.CBlockConfigKeys.DFS_CBLOCK_JSCSIRPC_ADDRESS_DEFAULT;
|
||||
import static org.apache.hadoop.cblock.CBlockConfigKeys.DFS_CBLOCK_JSCSIRPC_ADDRESS_KEY;
|
||||
import static org.apache.hadoop.cblock.CBlockConfigKeys.DFS_CBLOCK_JSCSIRPC_BIND_HOST_KEY;
|
||||
import static org.apache.hadoop.cblock.CBlockConfigKeys.DFS_CBLOCK_SERVICERPC_ADDRESS_DEFAULT;
|
||||
import static org.apache.hadoop.cblock.CBlockConfigKeys.DFS_CBLOCK_SERVICERPC_ADDRESS_KEY;
|
||||
import static org.apache.hadoop.cblock.CBlockConfigKeys.DFS_CBLOCK_SERVICERPC_BIND_HOST_KEY;
|
||||
import static org.apache.hadoop.cblock.CBlockConfigKeys.DFS_CBLOCK_SERVICERPC_HANDLER_COUNT_DEFAULT;
|
||||
import static org.apache.hadoop.cblock.CBlockConfigKeys.DFS_CBLOCK_SERVICERPC_HANDLER_COUNT_KEY;
|
||||
|
||||
/**
|
||||
* The main entry point of CBlock operations, ALL the CBlock operations
|
||||
* will go through this class. But NOTE that:
|
||||
*
|
||||
* volume operations (create/
|
||||
* delete/info) are:
|
||||
* client -> CBlockManager -> StorageManager -> CBlock client
|
||||
*
|
||||
* IO operations (put/get block) are;
|
||||
* client -> CBlock client -> container
|
||||
*
|
||||
*/
|
||||
public class CBlockManager implements CBlockServiceProtocol,
|
||||
CBlockClientServerProtocol {
|
||||
|
||||
private static final Logger LOG =
|
||||
LoggerFactory.getLogger(CBlockManager.class);
|
||||
|
||||
private final RPC.Server cblockService;
|
||||
private final RPC.Server cblockServer;
|
||||
|
||||
private final StorageManager storageManager;
|
||||
|
||||
public CBlockManager(CBlockConfiguration conf, IStorageClient storageClient
|
||||
) throws IOException {
|
||||
storageManager = new StorageManager(storageClient);
|
||||
|
||||
RPC.setProtocolEngine(conf, CBlockServiceProtocolPB.class,
|
||||
ProtobufRpcEngine.class);
|
||||
RPC.setProtocolEngine(conf, CBlockClientServerProtocolPB.class,
|
||||
ProtobufRpcEngine.class);
|
||||
// start service for client command-to-cblock server service
|
||||
InetSocketAddress serviceRpcAddr = NetUtils.createSocketAddr(
|
||||
conf.getTrimmed(DFS_CBLOCK_SERVICERPC_ADDRESS_KEY,
|
||||
DFS_CBLOCK_SERVICERPC_ADDRESS_DEFAULT), -1,
|
||||
DFS_CBLOCK_SERVICERPC_ADDRESS_KEY);
|
||||
BlockingService cblockProto =
|
||||
CBlockServiceProtocolProtos
|
||||
.CBlockServiceProtocolService
|
||||
.newReflectiveBlockingService(
|
||||
new CBlockServiceProtocolServerSideTranslatorPB(this)
|
||||
);
|
||||
cblockService = startRpcServer(conf, CBlockServiceProtocolPB.class,
|
||||
cblockProto, serviceRpcAddr,
|
||||
DFS_CBLOCK_SERVICERPC_BIND_HOST_KEY,
|
||||
DFS_CBLOCK_SERVICERPC_HANDLER_COUNT_KEY,
|
||||
DFS_CBLOCK_SERVICERPC_HANDLER_COUNT_DEFAULT);
|
||||
|
||||
LOG.info("CBlock manager listening for client commands on: {}",
|
||||
serviceRpcAddr);
|
||||
// now start service for cblock client-to-cblock server communication
|
||||
InetSocketAddress serverRpcAddr = NetUtils.createSocketAddr(
|
||||
conf.get(DFS_CBLOCK_JSCSIRPC_ADDRESS_KEY,
|
||||
DFS_CBLOCK_JSCSIRPC_ADDRESS_DEFAULT), -1,
|
||||
DFS_CBLOCK_JSCSIRPC_ADDRESS_KEY);
|
||||
BlockingService serverProto =
|
||||
CBlockClientServerProtocolProtos
|
||||
.CBlockClientServerProtocolService
|
||||
.newReflectiveBlockingService(
|
||||
new CBlockClientServerProtocolServerSideTranslatorPB(this)
|
||||
);
|
||||
cblockServer = startRpcServer(
|
||||
conf, CBlockClientServerProtocolPB.class,
|
||||
serverProto, serverRpcAddr,
|
||||
DFS_CBLOCK_JSCSIRPC_BIND_HOST_KEY,
|
||||
DFS_CBLOCK_SERVICERPC_HANDLER_COUNT_KEY,
|
||||
DFS_CBLOCK_SERVICERPC_HANDLER_COUNT_DEFAULT);
|
||||
LOG.info("CBlock server listening for client commands on: {}",
|
||||
serverRpcAddr);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
cblockService.start();
|
||||
cblockServer.start();
|
||||
LOG.info("CBlock manager started!");
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
cblockService.stop();
|
||||
cblockServer.stop();
|
||||
}
|
||||
|
||||
public void join() {
|
||||
try {
|
||||
cblockService.join();
|
||||
cblockServer.join();
|
||||
} catch (InterruptedException e) {
|
||||
LOG.error("Interrupted during join");
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an RPC server, if configured.
|
||||
*
|
||||
* @param conf configuration
|
||||
* @param protocol RPC protocol provided by RPC server
|
||||
* @param instance RPC protocol implementation instance
|
||||
* @param addr configured address of RPC server
|
||||
* @param bindHostKey configuration key for setting explicit bind host. If
|
||||
* the property is not configured, then the bind host is taken from addr.
|
||||
* @param handlerCountKey configuration key for RPC server handler count
|
||||
* @param handlerCountDefault default RPC server handler count if unconfigured
|
||||
* @return RPC server, or null if addr is null
|
||||
* @throws IOException if there is an I/O error while creating RPC server
|
||||
*/
|
||||
private static RPC.Server startRpcServer(CBlockConfiguration conf,
|
||||
Class<?> protocol, BlockingService instance,
|
||||
InetSocketAddress addr, String bindHostKey,
|
||||
String handlerCountKey, int handlerCountDefault) throws IOException {
|
||||
if (addr == null) {
|
||||
return null;
|
||||
}
|
||||
String bindHost = conf.getTrimmed(bindHostKey);
|
||||
if (bindHost == null || bindHost.isEmpty()) {
|
||||
bindHost = addr.getHostName();
|
||||
}
|
||||
int numHandlers = conf.getInt(handlerCountKey, handlerCountDefault);
|
||||
RPC.Server rpcServer = new RPC.Builder(conf)
|
||||
.setProtocol(protocol)
|
||||
.setInstance(instance)
|
||||
.setBindAddress(bindHost)
|
||||
.setPort(addr.getPort())
|
||||
.setNumHandlers(numHandlers)
|
||||
.setVerbose(false)
|
||||
.setSecretManager(null)
|
||||
.build();
|
||||
return rpcServer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MountVolumeResponse mountVolume(
|
||||
String userName, String volumeName) throws IOException {
|
||||
return storageManager.isVolumeValid(userName, volumeName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createVolume(String userName, String volumeName,
|
||||
long volumeSize, int blockSize) throws IOException {
|
||||
LOG.info("Create volume received: userName: {} volumeName: {} " +
|
||||
"volumeSize: {} blockSize: {}", userName, volumeName,
|
||||
volumeSize, blockSize);
|
||||
// It is important to create in-memory representation of the
|
||||
// volume first, then writes to persistent storage (levelDB)
|
||||
// such that it is guaranteed that when there is an entry in
|
||||
// levelDB, the volume is allocated. (more like a UNDO log fashion)
|
||||
// TODO: what if creation failed? we allocated containers but lost
|
||||
// the reference to the volume and all it's containers. How to release
|
||||
// the containers?
|
||||
storageManager.createVolume(userName, volumeName, volumeSize, blockSize);
|
||||
VolumeDescriptor volume = storageManager.getVolume(userName, volumeName);
|
||||
if (volume == null) {
|
||||
throw new IOException("Volume creation failed!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteVolume(String userName,
|
||||
String volumeName, boolean force) throws IOException {
|
||||
LOG.info("Delete volume received: volume:" + volumeName
|
||||
+ " force?:" + force);
|
||||
storageManager.deleteVolume(userName, volumeName, force);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VolumeInfo infoVolume(String userName, String volumeName
|
||||
) throws IOException {
|
||||
LOG.info("Info volume received: volume: {}", volumeName);
|
||||
return storageManager.infoVolume(userName, volumeName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<VolumeInfo> listVolume(String userName) throws IOException {
|
||||
ArrayList<VolumeInfo> response = new ArrayList<>();
|
||||
List<VolumeDescriptor> allVolumes =
|
||||
storageManager.getAllVolume(userName);
|
||||
for (VolumeDescriptor volume : allVolumes) {
|
||||
VolumeInfo info =
|
||||
new VolumeInfo(volume.getUserName(), volume.getVolumeName(),
|
||||
volume.getVolumeSize(), volume.getBlockSize());
|
||||
response.add(info);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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.exception;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* The exception class used in CBlock.
|
||||
*/
|
||||
public class CBlockException extends IOException {
|
||||
public CBlockException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -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.exception;
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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.meta;
|
||||
|
||||
/**
|
||||
*
|
||||
* The internal representation of a container maintained by CBlock server.
|
||||
* Include enough information to exactly identify a container for read/write
|
||||
* operation.
|
||||
*
|
||||
* NOTE that this class is work-in-progress. Depends on HDFS-7240 container
|
||||
* implementation. Currently only to allow testing.
|
||||
*/
|
||||
public class ContainerDescriptor {
|
||||
private final String containerID;
|
||||
// the index of this container with in a volume
|
||||
// on creation, there is no way to know the index of the container
|
||||
// as it is a volume specific information
|
||||
private int containerIndex;
|
||||
|
||||
public ContainerDescriptor(String containerID) {
|
||||
this.containerID = containerID;
|
||||
}
|
||||
|
||||
public void setContainerIndex(int idx) {
|
||||
this.containerIndex = idx;
|
||||
}
|
||||
|
||||
public String getContainerID() {
|
||||
return containerID;
|
||||
}
|
||||
|
||||
public int getContainerIndex() {
|
||||
return containerIndex;
|
||||
}
|
||||
|
||||
public long getUtilization() {
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* 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.meta;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The internal representation maintained by CBlock server as the info for
|
||||
* a volume. Contains the list of containers belonging to this volume.
|
||||
*
|
||||
* Many methods of this class is made such that the volume information (
|
||||
* including container list) can be easily transformed into a Json string
|
||||
* that can be stored/parsed from a persistent store for cblock server
|
||||
* persistence.
|
||||
*
|
||||
* This class is still work-in-progress.
|
||||
*/
|
||||
public class VolumeDescriptor {
|
||||
// The main data structure is the container location map
|
||||
// other thing are mainly just information
|
||||
|
||||
// since only one operation at a time is allowed, no
|
||||
// need to consider concurrency control here
|
||||
|
||||
// key is container id
|
||||
|
||||
private static final Logger LOG =
|
||||
LoggerFactory.getLogger(VolumeDescriptor.class);
|
||||
|
||||
private HashMap<String, ContainerDescriptor> containerMap;
|
||||
private String userName;
|
||||
private int blockSize;
|
||||
private long volumeSize;
|
||||
private String volumeName;
|
||||
// this is essentially the ordered keys of containerMap
|
||||
// which is kind of redundant information. But since we
|
||||
// are likely to access it frequently based on ordering.
|
||||
// keeping this copy to avoid having to sort the key every
|
||||
// time
|
||||
private List<String> containerIdOrdered;
|
||||
|
||||
/**
|
||||
* This is not being called explicitly, but this is necessary as
|
||||
* it will be called by the parse method implicitly when
|
||||
* reconstructing the object from json string. The get*() methods
|
||||
* and set*() methods are for the same purpose also.
|
||||
*/
|
||||
public VolumeDescriptor() {
|
||||
containerMap = new HashMap<>();
|
||||
containerIdOrdered = new ArrayList<>();
|
||||
}
|
||||
|
||||
public VolumeDescriptor(String userName, String volumeName, long volumeSize,
|
||||
int blockSize) {
|
||||
this.containerMap = new HashMap<>();
|
||||
this.userName = userName;
|
||||
this.volumeName = volumeName;
|
||||
this.blockSize = blockSize;
|
||||
this.volumeSize = volumeSize;
|
||||
this.containerIdOrdered = new LinkedList<>();
|
||||
}
|
||||
|
||||
public String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
public void setUserName(String userName) {
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
public String getVolumeName() {
|
||||
return volumeName;
|
||||
}
|
||||
|
||||
public void setVolumeName(String volumeName) {
|
||||
this.volumeName = volumeName;
|
||||
}
|
||||
|
||||
public long getVolumeSize() {
|
||||
return volumeSize;
|
||||
}
|
||||
|
||||
public void setVolumeSize(long volumeSize) {
|
||||
this.volumeSize = volumeSize;
|
||||
}
|
||||
|
||||
public int getBlockSize() {
|
||||
return blockSize;
|
||||
}
|
||||
|
||||
public void setBlockSize(int blockSize) {
|
||||
this.blockSize = blockSize;
|
||||
}
|
||||
|
||||
public void setContainerIDs(ArrayList<String> containerIDs) {
|
||||
containerIdOrdered.addAll(containerIDs);
|
||||
}
|
||||
|
||||
public void addContainer(ContainerDescriptor containerDescriptor) {
|
||||
containerMap.put(containerDescriptor.getContainerID(),
|
||||
containerDescriptor);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
VolumeInfo info = getInfo();
|
||||
return info.getUsage() == 0;
|
||||
}
|
||||
|
||||
public VolumeInfo getInfo() {
|
||||
// TODO : need to actually go through all containers of this volume and
|
||||
// ask for their utilization.
|
||||
long utilization = 0;
|
||||
for (Map.Entry<String, ContainerDescriptor> entry :
|
||||
containerMap.entrySet()) {
|
||||
utilization += entry.getValue().getUtilization();
|
||||
}
|
||||
return new VolumeInfo(this.userName, this.volumeName,
|
||||
this.volumeSize, this.blockSize,
|
||||
utilization * blockSize);
|
||||
}
|
||||
|
||||
public String[] getContainerIDs() {
|
||||
//ArrayList<Long> ids = new ArrayList(containerMap.keySet());
|
||||
//return ids.toArray(new Long[ids.size()]);
|
||||
return containerIdOrdered.toArray(new String[containerIdOrdered.size()]);
|
||||
}
|
||||
|
||||
public List<String> getContainerIDsList() {
|
||||
return new ArrayList<>(containerIdOrdered);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String string = "";
|
||||
string += "Username:" + userName + "\n";
|
||||
string += "VolumeName:" + volumeName + "\n";
|
||||
string += "VolumeSize:" + volumeSize + "\n";
|
||||
string += "blockSize:" + blockSize + "\n";
|
||||
string += "containerIds:" + containerIdOrdered + "\n";
|
||||
string += "containerIdsWithObject:" + containerMap.keySet();
|
||||
return string;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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.meta;
|
||||
|
||||
/**
|
||||
* A wrapper class that represents the information about a volume. Used in
|
||||
* communication between CBlock client and CBlock server only.
|
||||
*/
|
||||
public class VolumeInfo {
|
||||
private final String userName;
|
||||
private final String volumeName;
|
||||
private final long volumeSize;
|
||||
private final long blockSize;
|
||||
private final long usage;
|
||||
|
||||
public VolumeInfo(String userName, String volumeName, long volumeSize,
|
||||
long blockSize, long usage) {
|
||||
this.userName = userName;
|
||||
this.volumeName = volumeName;
|
||||
this.volumeSize = volumeSize;
|
||||
this.blockSize = blockSize;
|
||||
this.usage = usage;
|
||||
}
|
||||
|
||||
// When listing volume, the usage will not be set.
|
||||
public VolumeInfo(String userName, String volumeName, long volumeSize,
|
||||
long blockSize) {
|
||||
this.userName = userName;
|
||||
this.volumeName = volumeName;
|
||||
this.volumeSize = volumeSize;
|
||||
this.blockSize = blockSize;
|
||||
this.usage = -1;
|
||||
}
|
||||
|
||||
public long getVolumeSize() {
|
||||
return volumeSize;
|
||||
}
|
||||
|
||||
public long getBlockSize() {
|
||||
return blockSize;
|
||||
}
|
||||
|
||||
public long getUsage() {
|
||||
return usage;
|
||||
}
|
||||
|
||||
public String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
public String getVolumeName() {
|
||||
return volumeName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return " userName:" + userName +
|
||||
" volumeName:" + volumeName +
|
||||
" volumeSize:" + volumeSize +
|
||||
" blockSize:" + blockSize +
|
||||
" (sizeInBlocks:" + volumeSize/blockSize + ")" +
|
||||
" usageInBlocks:" + usage;
|
||||
}
|
||||
}
|
|
@ -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.meta;
|
|
@ -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;
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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.proto;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* The protocol that CBlock client side uses to talk to server side. CBlock
|
||||
* client is the point where a volume is mounted. All the actual volume IO
|
||||
* operations will go through CBlock client after the volume is mounted.
|
||||
*
|
||||
* When users mount a volume on CBlock client, CBlock client side uses this
|
||||
* protocol to send mount request to CBlock server.
|
||||
*/
|
||||
public interface CBlockClientServerProtocol {
|
||||
MountVolumeResponse mountVolume(String userName, String volumeName)
|
||||
throws IOException;
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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.proto;
|
||||
|
||||
import org.apache.hadoop.cblock.meta.VolumeInfo;
|
||||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CBlock uses a separate command line tool to send volume management
|
||||
* operations to CBlock server, including create/delete/info/list volumes. This
|
||||
* is the protocol used by the command line tool to send these requests and get
|
||||
* responses from CBlock server.
|
||||
*/
|
||||
@InterfaceAudience.Private
|
||||
public interface CBlockServiceProtocol {
|
||||
|
||||
void createVolume(String userName, String volumeName,
|
||||
long volumeSize, int blockSize) throws IOException;
|
||||
|
||||
void deleteVolume(String userName, String volumeName,
|
||||
boolean force) throws IOException;
|
||||
|
||||
VolumeInfo infoVolume(String userName,
|
||||
String volumeName) throws IOException;
|
||||
|
||||
List<VolumeInfo> listVolume(String userName) throws IOException;
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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.proto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The response message of mounting a volume. Including enough information
|
||||
* for the client to communicate (perform IO) with the volume containers
|
||||
* directly.
|
||||
*/
|
||||
public class MountVolumeResponse {
|
||||
private final boolean isValid;
|
||||
private final String userName;
|
||||
private final String volumeName;
|
||||
private final long volumeSize;
|
||||
private final int blockSize;
|
||||
private List<String> containerList;
|
||||
|
||||
public MountVolumeResponse(boolean isValid, String userName,
|
||||
String volumeName, long volumeSize, int blockSize,
|
||||
List<String> containerList) {
|
||||
this.isValid = isValid;
|
||||
this.userName = userName;
|
||||
this.volumeName = volumeName;
|
||||
this.volumeSize = volumeSize;
|
||||
this.blockSize = blockSize;
|
||||
this.containerList = containerList;
|
||||
}
|
||||
|
||||
public boolean getIsValid() {
|
||||
return isValid;
|
||||
}
|
||||
|
||||
public String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
public String getVolumeName() {
|
||||
return volumeName;
|
||||
}
|
||||
|
||||
public long getVolumeSize() {
|
||||
return volumeSize;
|
||||
}
|
||||
|
||||
public int getBlockSize() {
|
||||
return blockSize;
|
||||
}
|
||||
|
||||
public List<String> getContainerList() {
|
||||
return containerList;
|
||||
}
|
||||
}
|
|
@ -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.proto;
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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.protocolPB;
|
||||
|
||||
import org.apache.hadoop.cblock.protocol.proto.CBlockClientServerProtocolProtos;
|
||||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
import org.apache.hadoop.ipc.ProtocolInfo;
|
||||
|
||||
/**
|
||||
* This is the protocol CBlock client uses to talk to CBlock server.
|
||||
* CBlock client is the mounting point of a volume. When a user mounts a
|
||||
* volume, the cBlock client running on the local node will use this protocol
|
||||
* to talk to CBlock server to mount the volume.
|
||||
*/
|
||||
@ProtocolInfo(protocolName =
|
||||
"org.apache.hadoop.cblock.protocolPB.CBlockClientServerProtocol",
|
||||
protocolVersion = 1)
|
||||
@InterfaceAudience.Private
|
||||
public interface CBlockClientServerProtocolPB extends
|
||||
CBlockClientServerProtocolProtos
|
||||
.CBlockClientServerProtocolService.BlockingInterface {
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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.protocolPB;
|
||||
|
||||
import com.google.protobuf.RpcController;
|
||||
import com.google.protobuf.ServiceException;
|
||||
import org.apache.hadoop.cblock.proto.CBlockClientServerProtocol;
|
||||
import org.apache.hadoop.cblock.proto.MountVolumeResponse;
|
||||
import org.apache.hadoop.cblock.protocol.proto.CBlockClientServerProtocolProtos;
|
||||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The server side implementation of cblock client to server protocol.
|
||||
*/
|
||||
@InterfaceAudience.Private
|
||||
public class CBlockClientServerProtocolServerSideTranslatorPB implements
|
||||
CBlockClientServerProtocolPB {
|
||||
|
||||
private final CBlockClientServerProtocol impl;
|
||||
|
||||
public CBlockClientServerProtocolServerSideTranslatorPB(
|
||||
CBlockClientServerProtocol impl) {
|
||||
this.impl = impl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CBlockClientServerProtocolProtos.MountVolumeResponseProto mountVolume(
|
||||
RpcController controller,
|
||||
CBlockClientServerProtocolProtos.MountVolumeRequestProto request)
|
||||
throws ServiceException {
|
||||
String userName = request.getUserName();
|
||||
String volumeName = request.getVolumeName();
|
||||
CBlockClientServerProtocolProtos.MountVolumeResponseProto.Builder
|
||||
resp =
|
||||
CBlockClientServerProtocolProtos
|
||||
.MountVolumeResponseProto.newBuilder();
|
||||
try {
|
||||
MountVolumeResponse result = impl.mountVolume(userName, volumeName);
|
||||
boolean isValid = result.getIsValid();
|
||||
resp.setIsValid(isValid);
|
||||
if (isValid) {
|
||||
resp.setUserName(result.getUserName());
|
||||
resp.setVolumeName(result.getVolumeName());
|
||||
resp.setVolumeSize(result.getVolumeSize());
|
||||
resp.setBlockSize(result.getBlockSize());
|
||||
List<String> containers = result.getContainerList();
|
||||
for (int i=0; i<containers.size(); i++) {
|
||||
CBlockClientServerProtocolProtos.ContainerIDProto.Builder id =
|
||||
CBlockClientServerProtocolProtos.ContainerIDProto.newBuilder();
|
||||
id.setContainerID(containers.get(i));
|
||||
id.setIndex(i);
|
||||
resp.addAllContainerIDs(id.build());
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new ServiceException(e);
|
||||
}
|
||||
return resp.build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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.protocolPB;
|
||||
|
||||
import org.apache.hadoop.cblock.protocol.proto.CBlockServiceProtocolProtos;
|
||||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
import org.apache.hadoop.ipc.ProtocolInfo;
|
||||
|
||||
/**
|
||||
* Users use a independent command line tool to talk to CBlock server for
|
||||
* volume operations (create/delete/info/list). This is the protocol used by
|
||||
* the the command line tool to send these requests to CBlock server.
|
||||
*/
|
||||
@ProtocolInfo(protocolName =
|
||||
"org.apache.hadoop.cblock.protocolPB.CBlockServiceProtocol",
|
||||
protocolVersion = 1)
|
||||
@InterfaceAudience.Private
|
||||
public interface CBlockServiceProtocolPB extends
|
||||
CBlockServiceProtocolProtos.CBlockServiceProtocolService.BlockingInterface {
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* 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.protocolPB;
|
||||
|
||||
import com.google.protobuf.RpcController;
|
||||
import com.google.protobuf.ServiceException;
|
||||
import org.apache.hadoop.cblock.meta.VolumeInfo;
|
||||
import org.apache.hadoop.cblock.proto.CBlockServiceProtocol;
|
||||
import org.apache.hadoop.cblock.protocol.proto.CBlockServiceProtocolProtos;
|
||||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static org.apache.hadoop.cblock.CBlockConfigKeys.DFS_CBLOCK_SERVICE_BLOCK_SIZE_DEFAULT;
|
||||
|
||||
/**
|
||||
* Server side implementation of the protobuf service.
|
||||
*/
|
||||
@InterfaceAudience.Private
|
||||
public class CBlockServiceProtocolServerSideTranslatorPB
|
||||
implements CBlockServiceProtocolPB {
|
||||
|
||||
private final CBlockServiceProtocol impl;
|
||||
private static final Logger LOG =
|
||||
LoggerFactory.getLogger(
|
||||
CBlockServiceProtocolServerSideTranslatorPB.class);
|
||||
|
||||
@Override
|
||||
public CBlockServiceProtocolProtos.CreateVolumeResponseProto createVolume(
|
||||
RpcController controller,
|
||||
CBlockServiceProtocolProtos.CreateVolumeRequestProto request)
|
||||
throws ServiceException {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("createVolume called! volume size: " + request.getVolumeSize()
|
||||
+ " block size: " + request.getBlockSize());
|
||||
}
|
||||
try {
|
||||
if (request.hasBlockSize()) {
|
||||
impl.createVolume(request.getUserName(), request.getVolumeName(),
|
||||
request.getVolumeSize(), request.getBlockSize());
|
||||
} else{
|
||||
impl.createVolume(request.getUserName(), request.getVolumeName(),
|
||||
request.getVolumeSize(), DFS_CBLOCK_SERVICE_BLOCK_SIZE_DEFAULT);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new ServiceException(e);
|
||||
}
|
||||
return CBlockServiceProtocolProtos.CreateVolumeResponseProto
|
||||
.newBuilder().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CBlockServiceProtocolProtos.DeleteVolumeResponseProto deleteVolume(
|
||||
RpcController controller,
|
||||
CBlockServiceProtocolProtos.DeleteVolumeRequestProto request)
|
||||
throws ServiceException {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("deleteVolume called! volume name: " + request.getVolumeName()
|
||||
+ " force:" + request.getForce());
|
||||
}
|
||||
try {
|
||||
if (request.hasForce()) {
|
||||
impl.deleteVolume(request.getUserName(), request.getVolumeName(),
|
||||
request.getForce());
|
||||
} else {
|
||||
impl.deleteVolume(request.getUserName(), request.getVolumeName(),
|
||||
false);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new ServiceException(e);
|
||||
}
|
||||
return CBlockServiceProtocolProtos.DeleteVolumeResponseProto
|
||||
.newBuilder().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CBlockServiceProtocolProtos.InfoVolumeResponseProto infoVolume(
|
||||
RpcController controller,
|
||||
CBlockServiceProtocolProtos.InfoVolumeRequestProto request
|
||||
) throws ServiceException {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("infoVolume called! volume name: " + request.getVolumeName());
|
||||
}
|
||||
CBlockServiceProtocolProtos.InfoVolumeResponseProto.Builder resp =
|
||||
CBlockServiceProtocolProtos.InfoVolumeResponseProto.newBuilder();
|
||||
CBlockServiceProtocolProtos.VolumeInfoProto.Builder volumeInfoProto =
|
||||
CBlockServiceProtocolProtos.VolumeInfoProto.newBuilder();
|
||||
VolumeInfo volumeInfo;
|
||||
try {
|
||||
volumeInfo = impl.infoVolume(request.getUserName(),
|
||||
request.getVolumeName());
|
||||
} catch (IOException e) {
|
||||
throw new ServiceException(e);
|
||||
}
|
||||
|
||||
volumeInfoProto.setVolumeSize(volumeInfo.getVolumeSize());
|
||||
volumeInfoProto.setBlockSize(volumeInfo.getBlockSize());
|
||||
volumeInfoProto.setUsage(volumeInfo.getUsage());
|
||||
volumeInfoProto.setUserName(volumeInfo.getUserName());
|
||||
volumeInfoProto.setVolumeName(volumeInfo.getVolumeName());
|
||||
resp.setVolumeInfo(volumeInfoProto);
|
||||
return resp.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CBlockServiceProtocolProtos.ListVolumeResponseProto listVolume(
|
||||
RpcController controller,
|
||||
CBlockServiceProtocolProtos.ListVolumeRequestProto request
|
||||
) throws ServiceException {
|
||||
CBlockServiceProtocolProtos.ListVolumeResponseProto.Builder resp =
|
||||
CBlockServiceProtocolProtos.ListVolumeResponseProto.newBuilder();
|
||||
String userName = null;
|
||||
if (request.hasUserName()) {
|
||||
userName = request.getUserName();
|
||||
}
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("list volume received for :" + userName);
|
||||
}
|
||||
List<VolumeInfo> volumes;
|
||||
try {
|
||||
volumes = impl.listVolume(userName);
|
||||
} catch (IOException e) {
|
||||
throw new ServiceException(e);
|
||||
}
|
||||
for (VolumeInfo volume : volumes) {
|
||||
CBlockServiceProtocolProtos.VolumeInfoProto.Builder volumeEntryProto
|
||||
= CBlockServiceProtocolProtos.VolumeInfoProto.newBuilder();
|
||||
volumeEntryProto.setUserName(volume.getUserName());
|
||||
volumeEntryProto.setVolumeName(volume.getVolumeName());
|
||||
volumeEntryProto.setVolumeSize(volume.getVolumeSize());
|
||||
volumeEntryProto.setBlockSize(volume.getBlockSize());
|
||||
resp.addVolumeEntry(volumeEntryProto.build());
|
||||
}
|
||||
return resp.build();
|
||||
}
|
||||
|
||||
public CBlockServiceProtocolServerSideTranslatorPB(
|
||||
CBlockServiceProtocol impl) {
|
||||
this.impl = impl;
|
||||
}
|
||||
}
|
|
@ -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.protocolPB;
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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.storage;
|
||||
|
||||
import org.apache.hadoop.cblock.meta.ContainerDescriptor;
|
||||
import org.apache.hadoop.classification.InterfaceStability;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* The interface to call into underlying container layer.
|
||||
*
|
||||
* Written as interface to allow easy testing: implement a mock container layer
|
||||
* for standalone testing of CBlock API without actually calling into remote
|
||||
* containers. Actual container layer can simply re-implement this.
|
||||
*
|
||||
* NOTE this is temporarily needed class. When SCM containers are full-fledged,
|
||||
* this interface will likely be removed.
|
||||
*/
|
||||
@InterfaceStability.Unstable
|
||||
public interface IStorageClient {
|
||||
ContainerDescriptor createContainer() throws IOException;
|
||||
|
||||
void deleteContainer(String containerId) throws IOException;
|
||||
|
||||
ContainerDescriptor getContainer(String containerId) throws IOException;
|
||||
|
||||
long getContainerSize() throws IOException;
|
||||
}
|
|
@ -0,0 +1,306 @@
|
|||
/*
|
||||
* 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.storage;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import org.apache.hadoop.cblock.exception.CBlockException;
|
||||
import org.apache.hadoop.cblock.meta.ContainerDescriptor;
|
||||
import org.apache.hadoop.cblock.meta.VolumeDescriptor;
|
||||
import org.apache.hadoop.cblock.meta.VolumeInfo;
|
||||
import org.apache.hadoop.cblock.proto.MountVolumeResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* This class maintains the key space of CBlock, more specifically, the
|
||||
* volume to container mapping. The core data structure
|
||||
* is a map from users to their volumes info, where volume info is a handler
|
||||
* to a volume, containing information for IO on that volume.
|
||||
*
|
||||
* and a storage client responsible for talking to the SCM
|
||||
*
|
||||
* TODO : all the volume operations are fully serialized, which can potentially
|
||||
* be optimized.
|
||||
*
|
||||
* TODO : if the certain operations (e.g. create) failed, the failure-handling
|
||||
* logic may not be properly implemented currently.
|
||||
*/
|
||||
public class StorageManager {
|
||||
private static final Logger LOGGER =
|
||||
LoggerFactory.getLogger(StorageManager.class);
|
||||
private final IStorageClient storageClient;
|
||||
/**
|
||||
* We will NOT have the situation where same kv pair getting
|
||||
* processed, but it is possible to have multiple kv pair being
|
||||
* processed at same time.
|
||||
*
|
||||
* So using just ConcurrentHashMap should be sufficient
|
||||
*
|
||||
* Again since currently same user accessing from multiple places
|
||||
* is not allowed, no need to consider concurrency of volume map
|
||||
* within one user
|
||||
*/
|
||||
private ConcurrentHashMap<String, HashMap<String, VolumeDescriptor>>
|
||||
user2VolumeMap;
|
||||
// size of an underlying container.
|
||||
// TODO : assuming all containers are of the same size
|
||||
private long containerSizeB;
|
||||
|
||||
public StorageManager(IStorageClient storageClient) throws IOException {
|
||||
this.storageClient = storageClient;
|
||||
this.user2VolumeMap = new ConcurrentHashMap<>();
|
||||
this.containerSizeB = storageClient.getContainerSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* This call will put the volume into in-memory map.
|
||||
*
|
||||
* more specifically, make the volume discoverable on jSCSI server
|
||||
* and keep it's reference in-memory for look up.
|
||||
* @param userName the user name of the volume.
|
||||
* @param volumeName the name of the volume,
|
||||
* @param volume a {@link VolumeDescriptor} object encapsulating the
|
||||
* information about the volume.
|
||||
*/
|
||||
private void makeVolumeReady(String userName, String volumeName,
|
||||
VolumeDescriptor volume) {
|
||||
HashMap<String, VolumeDescriptor> userVolumes;
|
||||
if (user2VolumeMap.containsKey(userName)) {
|
||||
userVolumes = user2VolumeMap.get(userName);
|
||||
} else {
|
||||
userVolumes = new HashMap<>();
|
||||
user2VolumeMap.put(userName, userVolumes);
|
||||
}
|
||||
userVolumes.put(volumeName, volume);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by CBlockManager to add volumes read from persistent store into
|
||||
* memory, need to contact SCM to setup the reference to the containers given
|
||||
* their id.
|
||||
*
|
||||
* Only for failover process where container meta info is read from
|
||||
* persistent store, and containers themselves are alive.
|
||||
*
|
||||
* TODO : Currently, this method is not being called as failover process
|
||||
* is not implemented yet.
|
||||
*
|
||||
* @param volumeDescriptor a {@link VolumeDescriptor} object encapsulating
|
||||
* the information about a volume.
|
||||
* @throws IOException when adding the volume failed. e.g. volume already
|
||||
* exist, or no more container available.
|
||||
*/
|
||||
public synchronized void addVolume(VolumeDescriptor volumeDescriptor)
|
||||
throws IOException{
|
||||
String userName = volumeDescriptor.getUserName();
|
||||
String volumeName = volumeDescriptor.getVolumeName();
|
||||
LOGGER.info("addVolume:" + userName + ":" + volumeName);
|
||||
if (user2VolumeMap.containsKey(userName)
|
||||
&& user2VolumeMap.get(userName).containsKey(volumeName)) {
|
||||
throw new CBlockException("Volume already exist for "
|
||||
+ userName + ":" + volumeName);
|
||||
}
|
||||
// the container ids are read from levelDB, setting up the
|
||||
// container handlers here.
|
||||
String[] containerIds = volumeDescriptor.getContainerIDs();
|
||||
|
||||
for (String containerId : containerIds) {
|
||||
try {
|
||||
ContainerDescriptor containerDescriptor =
|
||||
storageClient.getContainer(containerId);
|
||||
volumeDescriptor.addContainer(containerDescriptor);
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Getting container failed! Container:{} error:{}",
|
||||
containerId, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
// now ready to put into in-memory map.
|
||||
makeVolumeReady(userName, volumeName, volumeDescriptor);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called by CBlock server when creating a fresh volume. The core
|
||||
* logic is adding needed information into in-memory meta data.
|
||||
*
|
||||
* @param userName the user name of the volume.
|
||||
* @param volumeName the name of the volume.
|
||||
* @param volumeSize the size of the volume.
|
||||
* @param blockSize the block size of the volume.
|
||||
* @throws CBlockException when the volume can not be created.
|
||||
*/
|
||||
public synchronized void createVolume(String userName, String volumeName,
|
||||
long volumeSize, int blockSize) throws CBlockException {
|
||||
LOGGER.debug("createVolume:" + userName + ":" + volumeName);
|
||||
if (user2VolumeMap.containsKey(userName)
|
||||
&& user2VolumeMap.get(userName).containsKey(volumeName)) {
|
||||
throw new CBlockException("Volume already exist for "
|
||||
+ userName + ":" + volumeName);
|
||||
}
|
||||
if (volumeSize < blockSize) {
|
||||
throw new CBlockException("Volume size smaller than block size? " +
|
||||
"volume size:" + volumeSize + " block size:" + blockSize);
|
||||
}
|
||||
VolumeDescriptor volume;
|
||||
int containerIdx = 0;
|
||||
try {
|
||||
volume = new VolumeDescriptor(userName, volumeName,
|
||||
volumeSize, blockSize);
|
||||
long allocatedSize = 0;
|
||||
ArrayList<String> containerIds = new ArrayList<>();
|
||||
while (allocatedSize < volumeSize) {
|
||||
ContainerDescriptor container = storageClient.createContainer();
|
||||
container.setContainerIndex(containerIdx);
|
||||
volume.addContainer(container);
|
||||
containerIds.add(container.getContainerID());
|
||||
allocatedSize += containerSizeB;
|
||||
containerIdx += 1;
|
||||
}
|
||||
volume.setContainerIDs(containerIds);
|
||||
} catch (IOException e) {
|
||||
throw new CBlockException("Error when creating volume:" + e.getMessage());
|
||||
// TODO : delete already created containers? or re-try policy
|
||||
}
|
||||
makeVolumeReady(userName, volumeName, volume);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by CBlock server to delete a specific volume. Mainly
|
||||
* to check whether it can be deleted, and remove it from in-memory meta
|
||||
* data.
|
||||
*
|
||||
* @param userName the user name of the volume.
|
||||
* @param volumeName the name of the volume.
|
||||
* @param force if set to false, only delete volume it is empty, otherwise
|
||||
* throw exception. if set to true, delete regardless.
|
||||
* @throws CBlockException when the volume can not be deleted.
|
||||
*/
|
||||
public synchronized void deleteVolume(String userName, String volumeName,
|
||||
boolean force) throws CBlockException {
|
||||
if (!user2VolumeMap.containsKey(userName)
|
||||
|| !user2VolumeMap.get(userName).containsKey(volumeName)) {
|
||||
throw new CBlockException("Deleting non-exist volume "
|
||||
+ userName + ":" + volumeName);
|
||||
}
|
||||
if (!force && !user2VolumeMap.get(userName).get(volumeName).isEmpty()) {
|
||||
throw new CBlockException("Deleting a non-empty volume without force!");
|
||||
}
|
||||
VolumeDescriptor volume = user2VolumeMap.get(userName).remove(volumeName);
|
||||
for (String containerID : volume.getContainerIDsList()) {
|
||||
try {
|
||||
storageClient.deleteContainer(containerID);
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Error deleting container Container:{} error:{}",
|
||||
containerID, e);
|
||||
throw new CBlockException(e.getMessage());
|
||||
}
|
||||
}
|
||||
if (user2VolumeMap.get(userName).size() == 0) {
|
||||
user2VolumeMap.remove(userName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by CBlock server to get information of a specific volume.
|
||||
*
|
||||
* @param userName the user name of the volume.
|
||||
* @param volumeName the name of the volume.
|
||||
* @return a {@link VolumeInfo} object encapsulating the information of the
|
||||
* volume.
|
||||
* @throws CBlockException when the information can not be retrieved.
|
||||
*/
|
||||
public synchronized VolumeInfo infoVolume(String userName, String volumeName)
|
||||
throws CBlockException {
|
||||
if (!user2VolumeMap.containsKey(userName)
|
||||
|| !user2VolumeMap.get(userName).containsKey(volumeName)) {
|
||||
throw new CBlockException("Getting info for non-exist volume "
|
||||
+ userName + ":" + volumeName);
|
||||
}
|
||||
return user2VolumeMap.get(userName).get(volumeName).getInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by CBlock server to check whether the given volume can be
|
||||
* mounted, i.e. whether it can be found in the meta data.
|
||||
*
|
||||
* return a {@link MountVolumeResponse} with isValid flag to indicate
|
||||
* whether the volume can be mounted or not.
|
||||
*
|
||||
* @param userName the user name of the volume.
|
||||
* @param volumeName the name of the volume
|
||||
* @return a {@link MountVolumeResponse} object encapsulating whether the
|
||||
* volume is valid, and if yes, the requried information for client to
|
||||
* read/write the volume.
|
||||
*/
|
||||
public synchronized MountVolumeResponse isVolumeValid(
|
||||
String userName, String volumeName) {
|
||||
if (!user2VolumeMap.containsKey(userName)
|
||||
|| !user2VolumeMap.get(userName).containsKey(volumeName)) {
|
||||
// in the case of invalid volume, no need to set any value other than
|
||||
// isValid flag.
|
||||
return new MountVolumeResponse(false, null, null, 0, 0, null);
|
||||
}
|
||||
VolumeDescriptor volume = user2VolumeMap.get(userName).get(volumeName);
|
||||
return new MountVolumeResponse(true, userName,
|
||||
volumeName, volume.getVolumeSize(), volume.getBlockSize(),
|
||||
volume.getContainerIDsList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by CBlock manager to list all volumes.
|
||||
*
|
||||
* @param userName the userName whose volume to be listed, if set to null,
|
||||
* all volumes will be listed.
|
||||
* @return a list of {@link VolumeDescriptor} representing all volumes
|
||||
* requested.
|
||||
*/
|
||||
public synchronized List<VolumeDescriptor> getAllVolume(String userName) {
|
||||
ArrayList<VolumeDescriptor> allVolumes = new ArrayList<>();
|
||||
if (userName == null) {
|
||||
for (Map.Entry<String, HashMap<String, VolumeDescriptor>> entry
|
||||
: user2VolumeMap.entrySet()) {
|
||||
allVolumes.addAll(entry.getValue().values());
|
||||
}
|
||||
} else {
|
||||
if (user2VolumeMap.containsKey(userName)) {
|
||||
allVolumes.addAll(user2VolumeMap.get(userName).values());
|
||||
}
|
||||
}
|
||||
return allVolumes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only for testing the behavior of create/delete volumes.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public VolumeDescriptor getVolume(String userName, String volumeName) {
|
||||
if (!user2VolumeMap.containsKey(userName)
|
||||
|| !user2VolumeMap.get(userName).containsKey(volumeName)) {
|
||||
return null;
|
||||
}
|
||||
return user2VolumeMap.get(userName).get(volumeName);
|
||||
}
|
||||
}
|
|
@ -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.storage;
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* 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.meta.VolumeInfo;
|
||||
import org.apache.hadoop.cblock.storage.IStorageClient;
|
||||
import org.apache.hadoop.cblock.util.MockStorageClient;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* This class tests the basics of CBlock server. Mainly about the four
|
||||
* operations on volumes: create, delete, info and list.
|
||||
*/
|
||||
public class TestCBlockServer {
|
||||
private static CBlockManager cBlockManager;
|
||||
private static CBlockConfiguration conf;
|
||||
|
||||
@BeforeClass
|
||||
public static void setup() throws Exception {
|
||||
IStorageClient storageClient = new MockStorageClient();
|
||||
conf = new CBlockConfiguration();
|
||||
cBlockManager = new CBlockManager(conf, storageClient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test create volume for different users.
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testCreateVolume() throws Exception {
|
||||
String userName1 = "testCreateUser1";
|
||||
String userName2 = "testCreateUser2";
|
||||
String volumeName1 = "testVolume1";
|
||||
String volumeName2 = "testVolume2";
|
||||
long volumeSize = 1L*1024*1024;
|
||||
int blockSize = 4096;
|
||||
cBlockManager.createVolume(userName1, volumeName1, volumeSize, blockSize);
|
||||
List<VolumeInfo> volumes = cBlockManager.listVolume(userName1);
|
||||
assertEquals(1, volumes.size());
|
||||
VolumeInfo existingVolume = volumes.get(0);
|
||||
assertEquals(userName1, existingVolume.getUserName());
|
||||
assertEquals(volumeName1, existingVolume.getVolumeName());
|
||||
assertEquals(volumeSize, existingVolume.getVolumeSize());
|
||||
assertEquals(blockSize, existingVolume.getBlockSize());
|
||||
|
||||
cBlockManager.createVolume(userName1, volumeName2, volumeSize, blockSize);
|
||||
cBlockManager.createVolume(userName2, volumeName1, volumeSize, blockSize);
|
||||
volumes = cBlockManager.listVolume(userName1);
|
||||
assertEquals(2, volumes.size());
|
||||
volumes = cBlockManager.listVolume(userName2);
|
||||
assertEquals(1, volumes.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test delete volume.
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testDeleteVolume() throws Exception {
|
||||
String userName = "testDeleteUser";
|
||||
String volumeName1 = "testVolume1";
|
||||
String volumeName2 = "testVolume2";
|
||||
long volumeSize = 1L*1024*1024;
|
||||
int blockSize = 4096;
|
||||
cBlockManager.createVolume(userName, volumeName1, volumeSize, blockSize);
|
||||
cBlockManager.createVolume(userName, volumeName2, volumeSize, blockSize);
|
||||
cBlockManager.deleteVolume(userName, volumeName1, true);
|
||||
List<VolumeInfo> volumes = cBlockManager.listVolume(userName);
|
||||
assertEquals(1, volumes.size());
|
||||
|
||||
VolumeInfo existingVolume = volumes.get(0);
|
||||
assertEquals(userName, existingVolume.getUserName());
|
||||
assertEquals(volumeName2, existingVolume.getVolumeName());
|
||||
assertEquals(volumeSize, existingVolume.getVolumeSize());
|
||||
assertEquals(blockSize, existingVolume.getBlockSize());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test info volume.
|
||||
*
|
||||
* TODO : usage field is not being tested (as it is not implemented yet)
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testInfoVolume() throws Exception {
|
||||
String userName = "testInfoUser";
|
||||
String volumeName = "testVolume";
|
||||
long volumeSize = 1L*1024*1024;
|
||||
int blockSize = 4096;
|
||||
cBlockManager.createVolume(userName, volumeName, volumeSize, blockSize);
|
||||
VolumeInfo info = cBlockManager.infoVolume(userName, volumeName);
|
||||
assertEquals(userName, info.getUserName());
|
||||
assertEquals(volumeName, info.getVolumeName());
|
||||
assertEquals(volumeSize, info.getVolumeSize());
|
||||
assertEquals(blockSize, info.getBlockSize());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test listing a number of volumes.
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testListVolume() throws Exception {
|
||||
String userName = "testListUser";
|
||||
String volumeName = "testVolume";
|
||||
long volumeSize = 1L*1024*1024;
|
||||
int blockSize = 4096;
|
||||
int volumeNum = 100;
|
||||
for (int i = 0; i<volumeNum; i++) {
|
||||
cBlockManager.createVolume(userName, volumeName + i,
|
||||
volumeSize, blockSize);
|
||||
}
|
||||
List<VolumeInfo> volumes = cBlockManager.listVolume(userName);
|
||||
assertEquals(volumeNum, volumes.size());
|
||||
HashSet<String> volumeIds = new HashSet<>();
|
||||
for (int i = 0; i<volumeNum; i++) {
|
||||
VolumeInfo volumeInfo = volumes.get(i);
|
||||
assertEquals(userName, volumeInfo.getUserName());
|
||||
assertFalse(volumeIds.contains(volumeName + i));
|
||||
volumeIds.add(volumeName + i);
|
||||
assertEquals(volumeSize, volumeInfo.getVolumeSize());
|
||||
assertEquals(blockSize, volumeInfo.getBlockSize());
|
||||
}
|
||||
for (int i = 0; i<volumeNum; i++) {
|
||||
assertTrue(volumeIds.contains(volumeName + i));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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.util;
|
||||
|
||||
import org.apache.hadoop.cblock.meta.ContainerDescriptor;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* NOTE : This class is only for testing purpose.
|
||||
*
|
||||
* Mock an underlying container storage layer, expose to CBlock to perform
|
||||
* IO. While in this mock implementation, a container is nothing more than
|
||||
* a in memory hashmap.
|
||||
*
|
||||
* This is to allow volume creation call and perform standalone tests.
|
||||
*/
|
||||
public final class ContainerLookUpService {
|
||||
private static HashMap<String, ContainerDescriptor>
|
||||
containers = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Return an *existing* container with given Id.
|
||||
*
|
||||
* TODO : for testing purpose, return a new container if the given Id
|
||||
* is not found
|
||||
*
|
||||
* found
|
||||
* @param containerID
|
||||
* @return
|
||||
*/
|
||||
public static ContainerDescriptor lookUp(String containerID) {
|
||||
if (!containers.containsKey(containerID)) {
|
||||
System.err.println("A container id never seen, return a new one " +
|
||||
"for testing purpose:" + containerID);
|
||||
containers.put(containerID, new ContainerDescriptor(containerID));
|
||||
}
|
||||
return containers.get(containerID);
|
||||
}
|
||||
|
||||
public static void addContainer(String containerID) {
|
||||
containers.put(containerID, new ContainerDescriptor(containerID));
|
||||
}
|
||||
|
||||
private ContainerLookUpService() {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* 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.util;
|
||||
|
||||
import org.apache.hadoop.cblock.meta.ContainerDescriptor;
|
||||
import org.apache.hadoop.cblock.storage.IStorageClient;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This class is the one that directly talks to SCM server.
|
||||
*
|
||||
* NOTE : this is only a mock class, only to allow testing volume
|
||||
* creation without actually creating containers. In real world, need to be
|
||||
* replaced with actual container look up calls.
|
||||
*
|
||||
*/
|
||||
public class MockStorageClient implements IStorageClient {
|
||||
private static long currentContainerId = -1;
|
||||
|
||||
/**
|
||||
* Ask SCM to get a exclusive container.
|
||||
*
|
||||
* @return A container descriptor object to locate this container
|
||||
* @throws Exception
|
||||
*/
|
||||
public ContainerDescriptor createContainer() throws IOException {
|
||||
currentContainerId += 1;
|
||||
ContainerLookUpService.addContainer(Long.toString(currentContainerId));
|
||||
return ContainerLookUpService.lookUp(Long.toString(currentContainerId));
|
||||
}
|
||||
|
||||
/**
|
||||
* As this is only a testing class, with all "container" maintained in
|
||||
* memory, no need to really delete anything for now.
|
||||
* @param containerId
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public void deleteContainer(String containerId) throws IOException {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return reference to an *existing* container with given ID.
|
||||
*
|
||||
* @param containerId
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public ContainerDescriptor getContainer(String containerId)
|
||||
throws IOException {
|
||||
return ContainerLookUpService.lookUp(containerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getContainerSize() throws IOException {
|
||||
// just return a constant value for now
|
||||
return 5L*1024*1024*1024; // 5GB
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue