blob: 904b5c000592256bb863855cbd7563af5aeaaa6e [file] [log] [blame]
/**
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.ozone.om.request.key;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.hadoop.hdds.client.ECReplicationConfig;
import org.apache.hadoop.hdds.client.ReplicationConfig;
import org.apache.hadoop.hdds.utils.db.cache.CacheKey;
import org.apache.hadoop.hdds.utils.db.cache.CacheValue;
import org.apache.hadoop.ozone.OmUtils;
import org.apache.hadoop.ozone.OzoneAcl;
import org.apache.hadoop.ozone.OzoneConsts;
import org.apache.hadoop.ozone.om.PrefixManager;
import org.apache.hadoop.ozone.om.ResolvedBucket;
import org.apache.hadoop.ozone.om.helpers.BucketEncryptionKeyInfo;
import org.apache.hadoop.ozone.om.helpers.BucketLayout;
import org.apache.hadoop.ozone.om.helpers.KeyValueUtil;
import org.apache.hadoop.ozone.om.helpers.OmBucketInfo;
import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;
import org.apache.hadoop.ozone.om.helpers.OmKeyLocationInfo;
import org.apache.hadoop.ozone.om.helpers.OmKeyLocationInfoGroup;
import org.apache.hadoop.ozone.om.helpers.OmPrefixInfo;
import org.apache.hadoop.ozone.om.helpers.OmVolumeArgs;
import org.apache.hadoop.ozone.om.helpers.OzoneAclUtil;
import org.apache.hadoop.ozone.om.helpers.QuotaUtil;
import org.apache.hadoop.ozone.om.helpers.RepeatedOmKeyInfo;
import org.apache.hadoop.ozone.om.lock.OzoneLockStrategy;
import org.apache.hadoop.ozone.om.request.OMClientRequestUtils;
import org.apache.hadoop.ozone.om.request.file.OMFileRequest;
import org.apache.hadoop.ozone.protocolPB.OMPBHelper;
import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer;
import org.apache.hadoop.ozone.security.acl.OzoneObj;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension
.EncryptedKeyVersion;
import org.apache.hadoop.fs.FileEncryptionInfo;
import org.apache.hadoop.hdds.client.BlockID;
import org.apache.hadoop.hdds.scm.container.common.helpers.AllocatedBlock;
import org.apache.hadoop.hdds.scm.container.common.helpers.ExcludeList;
import org.apache.hadoop.hdds.scm.exceptions.SCMException;
import org.apache.hadoop.ipc.Server;
import org.apache.hadoop.ozone.om.OMMetadataManager;
import org.apache.hadoop.ozone.om.OzoneManager;
import org.apache.hadoop.ozone.om.ScmClient;
import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.ozone.om.request.OMClientRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos
.KeyArgs;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos
.OMRequest;
import org.apache.hadoop.hdds.security.token.OzoneBlockTokenSecretManager;
import org.apache.hadoop.security.SecurityUtil;
import org.apache.hadoop.security.UserGroupInformation;
import static org.apache.hadoop.hdds.protocol.proto.HddsProtos.BlockTokenSecretProto.AccessModeProto.READ;
import static org.apache.hadoop.hdds.protocol.proto.HddsProtos.BlockTokenSecretProto.AccessModeProto.WRITE;
import static org.apache.hadoop.ozone.OzoneConsts.OZONE_URI_DELIMITER;
import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes
.BUCKET_NOT_FOUND;
import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes
.VOLUME_NOT_FOUND;
import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.BUCKET_LOCK;
import static org.apache.hadoop.util.Time.monotonicNow;
/**
* Interface for key write requests.
*/
public abstract class OMKeyRequest extends OMClientRequest {
private static final Logger LOG = LoggerFactory.getLogger(OMKeyRequest.class);
private BucketLayout bucketLayout = BucketLayout.DEFAULT;
public OMKeyRequest(OMRequest omRequest) {
super(omRequest);
}
public OMKeyRequest(OMRequest omRequest, BucketLayout bucketLayoutArg) {
super(omRequest);
this.bucketLayout = bucketLayoutArg;
}
public BucketLayout getBucketLayout() {
return bucketLayout;
}
protected KeyArgs resolveBucketLink(
OzoneManager ozoneManager, KeyArgs keyArgs,
Map<String, String> auditMap) throws IOException {
ResolvedBucket bucket = ozoneManager.resolveBucketLink(keyArgs, this);
keyArgs = bucket.update(keyArgs);
bucket.audit(auditMap);
return keyArgs;
}
/**
* This methods avoids multiple rpc calls to SCM by allocating multiple blocks
* in one rpc call.
* @throws IOException
*/
@SuppressWarnings("parameternumber")
protected List< OmKeyLocationInfo > allocateBlock(ScmClient scmClient,
OzoneBlockTokenSecretManager secretManager,
ReplicationConfig replicationConfig, ExcludeList excludeList,
long requestedSize, long scmBlockSize, int preallocateBlocksMax,
boolean grpcBlockTokenEnabled, String omID) throws IOException {
int dataGroupSize = replicationConfig instanceof ECReplicationConfig
? ((ECReplicationConfig) replicationConfig).getData() : 1;
int numBlocks = (int) Math.min(preallocateBlocksMax,
(requestedSize - 1) / (scmBlockSize * dataGroupSize) + 1);
List<OmKeyLocationInfo> locationInfos = new ArrayList<>(numBlocks);
String remoteUser = getRemoteUser().getShortUserName();
List<AllocatedBlock> allocatedBlocks;
try {
allocatedBlocks = scmClient.getBlockClient()
.allocateBlock(scmBlockSize, numBlocks, replicationConfig, omID,
excludeList);
} catch (SCMException ex) {
if (ex.getResult()
.equals(SCMException.ResultCodes.SAFE_MODE_EXCEPTION)) {
throw new OMException(ex.getMessage(),
OMException.ResultCodes.SCM_IN_SAFE_MODE);
}
throw ex;
}
for (AllocatedBlock allocatedBlock : allocatedBlocks) {
BlockID blockID = new BlockID(allocatedBlock.getBlockID());
OmKeyLocationInfo.Builder builder = new OmKeyLocationInfo.Builder()
.setBlockID(blockID)
.setLength(scmBlockSize)
.setOffset(0)
.setPipeline(allocatedBlock.getPipeline());
if (grpcBlockTokenEnabled) {
builder.setToken(secretManager.generateToken(remoteUser, blockID,
EnumSet.of(READ, WRITE), scmBlockSize));
}
locationInfos.add(builder.build());
}
return locationInfos;
}
/* Optimize ugi lookup for RPC operations to avoid a trip through
* UGI.getCurrentUser which is synch'ed.
*/
private UserGroupInformation getRemoteUser() throws IOException {
UserGroupInformation ugi = Server.getRemoteUser();
return (ugi != null) ? ugi : UserGroupInformation.getCurrentUser();
}
/**
* Validate bucket and volume exists or not.
* @param omMetadataManager
* @param volumeName
* @param bucketName
* @throws IOException
*/
public void validateBucketAndVolume(OMMetadataManager omMetadataManager,
String volumeName, String bucketName)
throws IOException {
String bucketKey = omMetadataManager.getBucketKey(volumeName, bucketName);
// Check if bucket exists
if (!omMetadataManager.getBucketTable().isExist(bucketKey)) {
String volumeKey = omMetadataManager.getVolumeKey(volumeName);
// If the volume also does not exist, we should throw volume not found
// exception
if (!omMetadataManager.getVolumeTable().isExist(volumeKey)) {
throw new OMException("Volume not found " + volumeName,
VOLUME_NOT_FOUND);
}
// if the volume exists but bucket does not exist, throw bucket not found
// exception
throw new OMException("Bucket not found " + bucketName, BUCKET_NOT_FOUND);
}
// Make sure associated bucket's layout matches the one associated with
// the request.
OMClientRequestUtils.checkClientRequestPrecondition(
omMetadataManager.getBucketTable().get(bucketKey).getBucketLayout(),
getBucketLayout());
}
// For keys batch delete and rename only
protected String getVolumeOwner(OMMetadataManager omMetadataManager,
String volumeName) throws IOException {
String dbVolumeKey = omMetadataManager.getVolumeKey(volumeName);
OmVolumeArgs volumeArgs =
omMetadataManager.getVolumeTable().get(dbVolumeKey);
if (volumeArgs == null) {
throw new OMException("Volume not found " + volumeName,
VOLUME_NOT_FOUND);
}
return volumeArgs.getOwnerName();
}
protected static Optional<FileEncryptionInfo> getFileEncryptionInfo(
OzoneManager ozoneManager, OmBucketInfo bucketInfo) throws IOException {
Optional<FileEncryptionInfo> encInfo = Optional.absent();
BucketEncryptionKeyInfo ezInfo = bucketInfo.getEncryptionKeyInfo();
if (ezInfo != null) {
final String ezKeyName = ezInfo.getKeyName();
EncryptedKeyVersion edek = generateEDEK(ozoneManager, ezKeyName);
encInfo = Optional.of(new FileEncryptionInfo(ezInfo.getSuite(),
ezInfo.getVersion(),
edek.getEncryptedKeyVersion().getMaterial(),
edek.getEncryptedKeyIv(), ezKeyName,
edek.getEncryptionKeyVersionName()));
}
return encInfo;
}
private static EncryptedKeyVersion generateEDEK(OzoneManager ozoneManager,
String ezKeyName) throws IOException {
if (ezKeyName == null) {
return null;
}
long generateEDEKStartTime = monotonicNow();
EncryptedKeyVersion edek = SecurityUtil.doAsLoginUser(
new PrivilegedExceptionAction<EncryptedKeyVersion >() {
@Override
public EncryptedKeyVersion run() throws IOException {
try {
return ozoneManager.getKmsProvider()
.generateEncryptedKey(ezKeyName);
} catch (GeneralSecurityException e) {
throw new IOException(e);
}
}
});
long generateEDEKTime = monotonicNow() - generateEDEKStartTime;
LOG.debug("generateEDEK takes {} ms", generateEDEKTime);
Preconditions.checkNotNull(edek);
return edek;
}
protected List< OzoneAcl > getAclsForKey(KeyArgs keyArgs,
OmBucketInfo bucketInfo, PrefixManager prefixManager) {
List<OzoneAcl> acls = new ArrayList<>();
if (keyArgs.getAclsList() != null) {
acls.addAll(OzoneAclUtil.fromProtobuf(keyArgs.getAclsList()));
}
// Inherit DEFAULT acls from prefix.
if (prefixManager != null) {
List< OmPrefixInfo > prefixList = prefixManager.getLongestPrefixPath(
OZONE_URI_DELIMITER +
keyArgs.getVolumeName() + OZONE_URI_DELIMITER +
keyArgs.getBucketName() + OZONE_URI_DELIMITER +
keyArgs.getKeyName());
if (prefixList.size() > 0) {
// Add all acls from direct parent to key.
OmPrefixInfo prefixInfo = prefixList.get(prefixList.size() - 1);
if (prefixInfo != null) {
if (OzoneAclUtil.inheritDefaultAcls(acls, prefixInfo.getAcls())) {
return acls;
}
}
}
}
// Inherit DEFAULT acls from bucket only if DEFAULT acls for
// prefix are not set.
if (bucketInfo != null) {
if (OzoneAclUtil.inheritDefaultAcls(acls, bucketInfo.getAcls())) {
return acls;
}
}
return acls;
}
/**
* Check Acls for the ozone bucket.
* @param ozoneManager
* @param volume
* @param bucket
* @param key
* @throws IOException
*/
protected void checkBucketAcls(OzoneManager ozoneManager, String volume,
String bucket, String key, IAccessAuthorizer.ACLType aclType)
throws IOException {
if (ozoneManager.getAclsEnabled()) {
checkAcls(ozoneManager, OzoneObj.ResourceType.BUCKET,
OzoneObj.StoreType.OZONE, aclType,
volume, bucket, key);
}
}
/**
* Check Acls for the ozone key.
* @param ozoneManager
* @param volume
* @param bucket
* @param key
* @param aclType
* @param resourceType
* @throws IOException
*/
protected void checkKeyAcls(OzoneManager ozoneManager, String volume,
String bucket, String key, IAccessAuthorizer.ACLType aclType,
OzoneObj.ResourceType resourceType)
throws IOException {
if (ozoneManager.getAclsEnabled()) {
checkAcls(ozoneManager, resourceType, OzoneObj.StoreType.OZONE, aclType,
volume, bucket, key);
}
}
/**
* Check Acls for the ozone key with volumeOwner.
* @param ozoneManager
* @param volume
* @param bucket
* @param key
* @param aclType
* @param resourceType
* @throws IOException
*/
@SuppressWarnings("parameternumber")
protected void checkKeyAcls(OzoneManager ozoneManager, String volume,
String bucket, String key, IAccessAuthorizer.ACLType aclType,
OzoneObj.ResourceType resourceType, String volumeOwner)
throws IOException {
if (ozoneManager.getAclsEnabled()) {
checkAcls(ozoneManager, resourceType, OzoneObj.StoreType.OZONE,
aclType, volume, bucket, key, volumeOwner,
ozoneManager.getBucketOwner(volume, bucket, aclType, resourceType));
}
}
/**
* Check ACLs for Ozone Key in OpenKey table
* if ozone native authorizer is enabled.
* @param ozoneManager
* @param volume
* @param bucket
* @param key
* @param aclType
* @param clientId
* @throws IOException
*/
protected void checkKeyAclsInOpenKeyTable(OzoneManager ozoneManager,
String volume, String bucket, String key,
IAccessAuthorizer.ACLType aclType, long clientId) throws IOException {
String keyNameForAclCheck = key;
// Native authorizer requires client id as part of key name to check
// write ACL on key. Add client id to key name if ozone native
// authorizer is configured.
if (ozoneManager.isNativeAuthorizerEnabled()) {
keyNameForAclCheck = key + "/" + clientId;
}
checkKeyAcls(ozoneManager, volume, bucket, keyNameForAclCheck,
aclType, OzoneObj.ResourceType.KEY);
}
/**
* Generate EncryptionInfo and set in to newKeyArgs.
* @param keyArgs
* @param newKeyArgs
* @param ozoneManager
*/
protected void generateRequiredEncryptionInfo(KeyArgs keyArgs,
KeyArgs.Builder newKeyArgs, OzoneManager ozoneManager)
throws IOException {
String volumeName = keyArgs.getVolumeName();
String bucketName = keyArgs.getBucketName();
boolean acquireLock = false;
OMMetadataManager omMetadataManager = ozoneManager.getMetadataManager();
// When TDE is enabled, we are doing a DB read in pre-execute. As for
// most of the operations we don't read from DB because of our isLeader
// semantics. This issue will be solved with implementation of leader
// leases which provider strong leader semantics in the system.
// If KMS is not enabled, follow the normal approach of execution of not
// reading DB in pre-execute.
OmBucketInfo bucketInfo = null;
if (ozoneManager.getKmsProvider() != null) {
try {
acquireLock = omMetadataManager.getLock().acquireReadLock(
BUCKET_LOCK, volumeName, bucketName);
bucketInfo = omMetadataManager.getBucketTable().get(
omMetadataManager.getBucketKey(volumeName,
bucketName));
// If bucket is symlink, resolveBucketLink to figure out real
// volume/bucket.
if (bucketInfo.isLink()) {
ResolvedBucket resolvedBucket = ozoneManager.resolveBucketLink(
Pair.of(keyArgs.getVolumeName(), keyArgs.getBucketName()));
bucketInfo = omMetadataManager.getBucketTable().get(
omMetadataManager.getBucketKey(resolvedBucket.realVolume(),
resolvedBucket.realBucket()));
}
} finally {
if (acquireLock) {
omMetadataManager.getLock().releaseReadLock(
BUCKET_LOCK, volumeName, bucketName);
}
}
// Don't throw exception of bucket not found when bucketinfo is
// null. If bucketinfo is null, later when request
// is submitted and if bucket does not really exist it will fail in
// applyTransaction step. Why we are doing this is if OM thinks it is
// the leader, but it is not, we don't want to fail request in this
// case. As anyway when it submits request to ratis it will fail with
// not leader exception, and client will retry on correct leader and
// request will be executed.
if (bucketInfo != null) {
Optional<FileEncryptionInfo> encryptionInfo =
getFileEncryptionInfo(ozoneManager, bucketInfo);
if (encryptionInfo.isPresent()) {
newKeyArgs.setFileEncryptionInfo(
OMPBHelper.convert(encryptionInfo.get()));
}
}
}
}
protected void getFileEncryptionInfoForMpuKey(KeyArgs keyArgs,
KeyArgs.Builder newKeyArgs, OzoneManager ozoneManager)
throws IOException {
String volumeName = keyArgs.getVolumeName();
String bucketName = keyArgs.getBucketName();
boolean acquireLock = false;
OMMetadataManager omMetadataManager = ozoneManager.getMetadataManager();
if (ozoneManager.getKmsProvider() != null) {
acquireLock = omMetadataManager.getLock().acquireReadLock(
BUCKET_LOCK, volumeName, bucketName);
try {
ResolvedBucket resolvedBucket = ozoneManager.resolveBucketLink(
Pair.of(keyArgs.getVolumeName(), keyArgs.getBucketName()));
// Get the DB key name for looking up keyInfo in OpenKeyTable with
// resolved volume/bucket.
String dbMultipartOpenKey =
getDBMultipartOpenKey(resolvedBucket.realVolume(),
resolvedBucket.realBucket(), keyArgs.getKeyName(),
keyArgs.getMultipartUploadID(), omMetadataManager);
OmKeyInfo omKeyInfo =
omMetadataManager.getOpenKeyTable(getBucketLayout())
.get(dbMultipartOpenKey);
if (omKeyInfo != null && omKeyInfo.getFileEncryptionInfo() != null) {
newKeyArgs.setFileEncryptionInfo(
OMPBHelper.convert(omKeyInfo.getFileEncryptionInfo()));
}
} finally {
if (acquireLock) {
omMetadataManager.getLock()
.releaseReadLock(BUCKET_LOCK, volumeName, bucketName);
}
}
}
}
/**
* Get FileEncryptionInfoProto from KeyArgs.
* @param keyArgs
* @return
*/
protected FileEncryptionInfo getFileEncryptionInfo(KeyArgs keyArgs) {
FileEncryptionInfo encryptionInfo = null;
if (keyArgs.hasFileEncryptionInfo()) {
encryptionInfo = OMPBHelper.convert(keyArgs.getFileEncryptionInfo());
}
return encryptionInfo;
}
/**
* Check bucket quota in bytes.
* @param omBucketInfo
* @param allocateSize
* @throws IOException
*/
protected void checkBucketQuotaInBytes(OmBucketInfo omBucketInfo,
long allocateSize) throws IOException {
if (omBucketInfo.getQuotaInBytes() > OzoneConsts.QUOTA_RESET) {
long usedBytes = omBucketInfo.getUsedBytes();
long quotaInBytes = omBucketInfo.getQuotaInBytes();
if (quotaInBytes - usedBytes < allocateSize) {
throw new OMException("The DiskSpace quota of bucket:"
+ omBucketInfo.getBucketName() + "exceeded: quotaInBytes: "
+ quotaInBytes + " Bytes but diskspace consumed: " + (usedBytes
+ allocateSize) + " Bytes.",
OMException.ResultCodes.QUOTA_EXCEEDED);
}
}
}
/**
* Check namespace quota.
*/
protected void checkBucketQuotaInNamespace(OmBucketInfo omBucketInfo,
long allocatedNamespace) throws IOException {
if (omBucketInfo.getQuotaInNamespace() > OzoneConsts.QUOTA_RESET) {
long usedNamespace = omBucketInfo.getUsedNamespace();
long quotaInNamespace = omBucketInfo.getQuotaInNamespace();
long toUseNamespaceInTotal = usedNamespace + allocatedNamespace;
if (quotaInNamespace < toUseNamespaceInTotal) {
throw new OMException("The namespace quota of Bucket:"
+ omBucketInfo.getBucketName() + " exceeded: quotaInNamespace: "
+ quotaInNamespace + " but namespace consumed: "
+ toUseNamespaceInTotal + ".",
OMException.ResultCodes.QUOTA_EXCEEDED);
}
}
}
/**
* Check directory exists. If exists return true, else false.
* @param volumeName
* @param bucketName
* @param keyName
* @param omMetadataManager
* @throws IOException
*/
protected boolean checkDirectoryAlreadyExists(String volumeName,
String bucketName, String keyName, OMMetadataManager omMetadataManager)
throws IOException {
if (omMetadataManager.getKeyTable(getBucketLayout()).isExist(
omMetadataManager.getOzoneDirKey(volumeName, bucketName,
keyName))) {
return true;
}
return false;
}
/**
* @return the number of bytes used by blocks pointed to by {@code omKeyInfo}.
*/
protected static long sumBlockLengths(OmKeyInfo omKeyInfo) {
long bytesUsed = 0;
for (OmKeyLocationInfoGroup group: omKeyInfo.getKeyLocationVersions()) {
for (OmKeyLocationInfo locationInfo : group.getLocationList()) {
bytesUsed += QuotaUtil.getReplicatedSize(
locationInfo.getLength(), omKeyInfo.getReplicationConfig());
}
}
return bytesUsed;
}
/**
* Return bucket info for the specified bucket.
*/
@Nullable
protected OmBucketInfo getBucketInfo(OMMetadataManager omMetadataManager,
String volume, String bucket) {
String bucketKey = omMetadataManager.getBucketKey(volume, bucket);
CacheValue<OmBucketInfo> value = omMetadataManager.getBucketTable()
.getCacheValue(new CacheKey<>(bucketKey));
return value != null ? value.getCacheValue() : null;
}
/**
* Prepare OmKeyInfo which will be persisted to openKeyTable.
* @return OmKeyInfo
* @throws IOException
*/
@SuppressWarnings("parameternumber")
protected OmKeyInfo prepareKeyInfo(
@Nonnull OMMetadataManager omMetadataManager,
@Nonnull KeyArgs keyArgs, OmKeyInfo dbKeyInfo, long size,
@Nonnull List<OmKeyLocationInfo> locations,
@Nullable FileEncryptionInfo encInfo,
@Nonnull PrefixManager prefixManager,
@Nullable OmBucketInfo omBucketInfo,
long transactionLogIndex, long objectID, boolean isRatisEnabled,
ReplicationConfig replicationConfig)
throws IOException {
return prepareFileInfo(omMetadataManager, keyArgs, dbKeyInfo, size,
locations, encInfo, prefixManager, omBucketInfo, null,
transactionLogIndex, objectID, isRatisEnabled, replicationConfig);
}
/**
* Prepare OmKeyInfo which will be persisted to openKeyTable.
* @return OmKeyInfo
* @throws IOException
*/
@SuppressWarnings("parameternumber")
protected OmKeyInfo prepareFileInfo(
@Nonnull OMMetadataManager omMetadataManager,
@Nonnull KeyArgs keyArgs, OmKeyInfo dbKeyInfo, long size,
@Nonnull List<OmKeyLocationInfo> locations,
@Nullable FileEncryptionInfo encInfo,
@Nonnull PrefixManager prefixManager,
@Nullable OmBucketInfo omBucketInfo,
OMFileRequest.OMPathInfoWithFSO omPathInfo,
long transactionLogIndex, long objectID,
boolean isRatisEnabled, ReplicationConfig replicationConfig)
throws IOException {
if (keyArgs.getIsMultipartKey()) {
return prepareMultipartFileInfo(omMetadataManager, keyArgs,
size, locations, encInfo, prefixManager, omBucketInfo,
omPathInfo, transactionLogIndex, objectID);
//TODO args.getMetadata
}
if (dbKeyInfo != null) {
// The key already exist, the new blocks will replace old ones
// as new versions unless the bucket does not have versioning
// turned on.
dbKeyInfo.addNewVersion(locations, false,
omBucketInfo.getIsVersionEnabled());
long newSize = size;
if (omBucketInfo.getIsVersionEnabled()) {
newSize += dbKeyInfo.getDataSize();
}
dbKeyInfo.setDataSize(newSize);
// The modification time is set in preExecute. Use the same
// modification time.
dbKeyInfo.setModificationTime(keyArgs.getModificationTime());
dbKeyInfo.setUpdateID(transactionLogIndex, isRatisEnabled);
dbKeyInfo.setReplicationConfig(replicationConfig);
return dbKeyInfo;
}
// the key does not exist, create a new object.
// Blocks will be appended as version 0.
return createFileInfo(keyArgs, locations, replicationConfig,
keyArgs.getDataSize(), encInfo, prefixManager,
omBucketInfo, omPathInfo, transactionLogIndex, objectID);
}
/**
* Create OmKeyInfo object.
* @return OmKeyInfo
*/
@SuppressWarnings("parameterNumber")
protected OmKeyInfo createFileInfo(@Nonnull KeyArgs keyArgs,
@Nonnull List<OmKeyLocationInfo> locations,
@Nonnull ReplicationConfig replicationConfig,
long size,
@Nullable FileEncryptionInfo encInfo,
@Nonnull PrefixManager prefixManager,
@Nullable OmBucketInfo omBucketInfo,
OMFileRequest.OMPathInfoWithFSO omPathInfo,
long transactionLogIndex, long objectID) {
OmKeyInfo.Builder builder = new OmKeyInfo.Builder();
builder.setVolumeName(keyArgs.getVolumeName())
.setBucketName(keyArgs.getBucketName())
.setKeyName(keyArgs.getKeyName())
.setOmKeyLocationInfos(Collections.singletonList(
new OmKeyLocationInfoGroup(0, locations)))
.setCreationTime(keyArgs.getModificationTime())
.setModificationTime(keyArgs.getModificationTime())
.setDataSize(size)
.setReplicationConfig(replicationConfig)
.setFileEncryptionInfo(encInfo)
.setAcls(getAclsForKey(keyArgs, omBucketInfo, prefixManager))
.addAllMetadata(KeyValueUtil.getFromProtobuf(
keyArgs.getMetadataList()))
.setUpdateID(transactionLogIndex);
if (omPathInfo != null) {
// FileTable metadata format
objectID = omPathInfo.getLeafNodeObjectId();
builder.setParentObjectID(omPathInfo.getLastKnownParentId());
builder.setFileName(omPathInfo.getLeafNodeName());
}
builder.setObjectID(objectID);
return builder.build();
}
/**
* Prepare OmKeyInfo for multi-part upload part key which will be persisted
* to openKeyTable.
* @return OmKeyInfo
* @throws IOException
*/
@SuppressWarnings("parameternumber")
private OmKeyInfo prepareMultipartFileInfo(
@Nonnull OMMetadataManager omMetadataManager,
@Nonnull KeyArgs args, long size,
@Nonnull List<OmKeyLocationInfo> locations,
FileEncryptionInfo encInfo, @Nonnull PrefixManager prefixManager,
@Nullable OmBucketInfo omBucketInfo,
OMFileRequest.OMPathInfoWithFSO omPathInfo,
@Nonnull long transactionLogIndex, long objectID)
throws IOException {
Preconditions.checkArgument(args.getMultipartNumber() > 0,
"PartNumber Should be greater than zero");
// When key is multipart upload part key, we should take replication
// type and replication factor from original key which has done
// initiate multipart upload. If we have not found any such, we throw
// error no such multipart upload.
String uploadID = args.getMultipartUploadID();
Preconditions.checkNotNull(uploadID);
String multipartKey = "";
if (omPathInfo != null) {
final long volumeId = omMetadataManager.getVolumeId(
args.getVolumeName());
final long bucketId = omMetadataManager.getBucketId(
args.getVolumeName(), args.getBucketName());
// FileTable metadata format
multipartKey = omMetadataManager.getMultipartKey(volumeId, bucketId,
omPathInfo.getLastKnownParentId(),
omPathInfo.getLeafNodeName(), uploadID);
} else {
multipartKey = omMetadataManager
.getMultipartKey(args.getVolumeName(), args.getBucketName(),
args.getKeyName(), uploadID);
}
OmKeyInfo partKeyInfo =
omMetadataManager.getOpenKeyTable(getBucketLayout()).get(multipartKey);
if (partKeyInfo == null) {
throw new OMException("No such Multipart upload is with specified " +
"uploadId " + uploadID,
OMException.ResultCodes.NO_SUCH_MULTIPART_UPLOAD_ERROR);
}
// For this upload part we don't need to check in KeyTable. As this
// is not an actual key, it is a part of the key.
return createFileInfo(args, locations, partKeyInfo.getReplicationConfig(),
size, encInfo, prefixManager, omBucketInfo, omPathInfo,
transactionLogIndex, objectID);
}
/**
* Returns the DB key name of a multipart open key in OM metadata store.
*
* @param volumeName - volume name.
* @param bucketName - bucket name.
* @param keyName - key name.
* @param uploadID - Multi part upload ID for this key.
* @param omMetadataManager
* @return
* @throws IOException
*/
protected String getDBMultipartOpenKey(String volumeName, String bucketName,
String keyName, String uploadID,
OMMetadataManager omMetadataManager)
throws IOException {
return omMetadataManager
.getMultipartKey(volumeName, bucketName, keyName, uploadID);
}
/**
* Prepare key for deletion service on overwrite.
*
* @param dbOzoneKey key to point to an object in RocksDB
* @param keyToDelete OmKeyInfo of a key to be in deleteTable
* @param omMetadataManager
* @param trxnLogIndex
* @param isRatisEnabled
* @return Old keys eligible for deletion.
* @throws IOException
*/
protected RepeatedOmKeyInfo getOldVersionsToCleanUp(
@Nonnull String dbOzoneKey, @Nonnull OmKeyInfo keyToDelete,
OMMetadataManager omMetadataManager, long trxnLogIndex,
boolean isRatisEnabled) throws IOException {
// Past keys that was deleted but still in deleted table,
// waiting for deletion service.
RepeatedOmKeyInfo keysToDelete =
omMetadataManager.getDeletedTable().get(dbOzoneKey);
return OmUtils.prepareKeyForDelete(keyToDelete, keysToDelete,
trxnLogIndex, isRatisEnabled);
}
protected OzoneLockStrategy getOzoneLockStrategy(OzoneManager ozoneManager) {
return ozoneManager.getOzoneLockProvider()
.createLockStrategy(getBucketLayout());
}
}