HDFS-8680. OzoneHandler : Add Local StorageHandler support for volumes. (Contributed by Anu Engineer)
This commit is contained in:
parent
835f872a0a
commit
07213f4eb5
|
@ -21,3 +21,6 @@
|
||||||
Arpit Agarwal)
|
Arpit Agarwal)
|
||||||
|
|
||||||
HDFS-8654. OzoneHandler : Add ACL support. (Anu Engineer via Arpit Agarwal)
|
HDFS-8654. OzoneHandler : Add ACL support. (Anu Engineer via Arpit Agarwal)
|
||||||
|
|
||||||
|
HDFS-8680. OzoneHandler : Add Local StorageHandler support for volumes.
|
||||||
|
(Anu Engineer via Arpit Agarwal)
|
||||||
|
|
|
@ -19,12 +19,11 @@
|
||||||
package org.apache.hadoop.ozone.web.localstorage;
|
package org.apache.hadoop.ozone.web.localstorage;
|
||||||
|
|
||||||
import org.apache.hadoop.classification.InterfaceAudience;
|
import org.apache.hadoop.classification.InterfaceAudience;
|
||||||
import org.apache.hadoop.ozone.OzoneConfigKeys;
|
|
||||||
import org.apache.hadoop.ozone.StorageContainerConfiguration;
|
|
||||||
import org.apache.hadoop.ozone.web.exceptions.OzoneException;
|
import org.apache.hadoop.ozone.web.exceptions.OzoneException;
|
||||||
import org.apache.hadoop.ozone.web.handlers.UserArgs;
|
import org.apache.hadoop.ozone.web.handlers.UserArgs;
|
||||||
import org.apache.hadoop.ozone.web.handlers.VolumeArgs;
|
import org.apache.hadoop.ozone.web.handlers.VolumeArgs;
|
||||||
import org.apache.hadoop.ozone.web.interfaces.StorageHandler;
|
import org.apache.hadoop.ozone.web.interfaces.StorageHandler;
|
||||||
|
import org.apache.hadoop.ozone.web.request.OzoneQuota;
|
||||||
import org.apache.hadoop.ozone.web.response.ListVolumes;
|
import org.apache.hadoop.ozone.web.response.ListVolumes;
|
||||||
import org.apache.hadoop.ozone.web.response.VolumeInfo;
|
import org.apache.hadoop.ozone.web.response.VolumeInfo;
|
||||||
|
|
||||||
|
@ -38,16 +37,10 @@ import java.io.IOException;
|
||||||
*/
|
*/
|
||||||
@InterfaceAudience.Private
|
@InterfaceAudience.Private
|
||||||
public class LocalStorageHandler implements StorageHandler {
|
public class LocalStorageHandler implements StorageHandler {
|
||||||
private String storageRoot = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs LocalStorageHandler.
|
* Constructs LocalStorageHandler.
|
||||||
*/
|
*/
|
||||||
public LocalStorageHandler() {
|
public LocalStorageHandler() {
|
||||||
StorageContainerConfiguration conf = new StorageContainerConfiguration();
|
|
||||||
storageRoot = conf.getTrimmed(
|
|
||||||
OzoneConfigKeys.DFS_STORAGE_LOCAL_ROOT,
|
|
||||||
OzoneConfigKeys.DFS_STORAGE_LOCAL_ROOT_DEFAULT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -59,6 +52,9 @@ public class LocalStorageHandler implements StorageHandler {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void createVolume(VolumeArgs args) throws IOException, OzoneException {
|
public void createVolume(VolumeArgs args) throws IOException, OzoneException {
|
||||||
|
OzoneMetadataManager oz = OzoneMetadataManager.getOzoneMetadataManager();
|
||||||
|
oz.createVolume(args);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -71,6 +67,8 @@ public class LocalStorageHandler implements StorageHandler {
|
||||||
@Override
|
@Override
|
||||||
public void setVolumeOwner(VolumeArgs args)
|
public void setVolumeOwner(VolumeArgs args)
|
||||||
throws IOException, OzoneException {
|
throws IOException, OzoneException {
|
||||||
|
OzoneMetadataManager oz = OzoneMetadataManager.getOzoneMetadataManager();
|
||||||
|
oz.setVolumeProperty(args, OzoneMetadataManager.VolumeProperty.OWNER);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -84,6 +82,13 @@ public class LocalStorageHandler implements StorageHandler {
|
||||||
@Override
|
@Override
|
||||||
public void setVolumeQuota(VolumeArgs args, boolean remove)
|
public void setVolumeQuota(VolumeArgs args, boolean remove)
|
||||||
throws IOException, OzoneException {
|
throws IOException, OzoneException {
|
||||||
|
OzoneMetadataManager oz = OzoneMetadataManager.getOzoneMetadataManager();
|
||||||
|
|
||||||
|
if(remove) {
|
||||||
|
OzoneQuota quota = new OzoneQuota();
|
||||||
|
args.setQuota(quota);
|
||||||
|
}
|
||||||
|
oz.setVolumeProperty(args, OzoneMetadataManager.VolumeProperty.QUOTA);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -96,12 +101,13 @@ public class LocalStorageHandler implements StorageHandler {
|
||||||
* @return - Boolean - True if the user can modify the volume.
|
* @return - Boolean - True if the user can modify the volume.
|
||||||
* This is possible for owners of the volume and admin users
|
* This is possible for owners of the volume and admin users
|
||||||
*
|
*
|
||||||
* @throws FileSystemException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean checkVolumeAccess(VolumeArgs args)
|
public boolean checkVolumeAccess(VolumeArgs args)
|
||||||
throws IOException, OzoneException {
|
throws IOException, OzoneException {
|
||||||
return true;
|
OzoneMetadataManager oz = OzoneMetadataManager.getOzoneMetadataManager();
|
||||||
|
return oz.checkVolumeAccess(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -117,7 +123,8 @@ public class LocalStorageHandler implements StorageHandler {
|
||||||
@Override
|
@Override
|
||||||
public VolumeInfo getVolumeInfo(VolumeArgs args)
|
public VolumeInfo getVolumeInfo(VolumeArgs args)
|
||||||
throws IOException, OzoneException {
|
throws IOException, OzoneException {
|
||||||
return null;
|
OzoneMetadataManager oz = OzoneMetadataManager.getOzoneMetadataManager();
|
||||||
|
return oz.getVolumeInfo(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -130,6 +137,9 @@ public class LocalStorageHandler implements StorageHandler {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void deleteVolume(VolumeArgs args) throws IOException, OzoneException {
|
public void deleteVolume(VolumeArgs args) throws IOException, OzoneException {
|
||||||
|
OzoneMetadataManager oz = OzoneMetadataManager.getOzoneMetadataManager();
|
||||||
|
oz.deleteVolume(args);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -144,7 +154,8 @@ public class LocalStorageHandler implements StorageHandler {
|
||||||
@Override
|
@Override
|
||||||
public ListVolumes listVolumes(UserArgs args)
|
public ListVolumes listVolumes(UserArgs args)
|
||||||
throws IOException, OzoneException {
|
throws IOException, OzoneException {
|
||||||
return null;
|
OzoneMetadataManager oz = OzoneMetadataManager.getOzoneMetadataManager();
|
||||||
|
return oz.listVolumes(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
* 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.ozone.web.localstorage;
|
||||||
|
|
||||||
|
import org.fusesource.leveldbjni.JniDBFactory;
|
||||||
|
import org.iq80.leveldb.DB;
|
||||||
|
import org.iq80.leveldb.Options;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OzoneLevelDBStore is used by the local
|
||||||
|
* OzoneStore which is used in testing.
|
||||||
|
*/
|
||||||
|
class OzoneLevelDBStore {
|
||||||
|
private DB db;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a DB file.
|
||||||
|
*
|
||||||
|
* @param dbPath - DB File path
|
||||||
|
* @param createIfMissing - Create if missing
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
OzoneLevelDBStore(File dbPath, boolean createIfMissing) throws IOException {
|
||||||
|
Options options = new Options();
|
||||||
|
options.createIfMissing(createIfMissing);
|
||||||
|
db = JniDBFactory.factory.open(dbPath, options);
|
||||||
|
if (db == null) {
|
||||||
|
throw new IOException("Db is null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Puts a Key into file.
|
||||||
|
*
|
||||||
|
* @param key - key
|
||||||
|
* @param value - value
|
||||||
|
*/
|
||||||
|
public void put(byte[] key, byte[] value) {
|
||||||
|
db.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Key.
|
||||||
|
*
|
||||||
|
* @param key key
|
||||||
|
*
|
||||||
|
* @return value
|
||||||
|
*/
|
||||||
|
public byte[] get(byte[] key) {
|
||||||
|
return db.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete Key.
|
||||||
|
*
|
||||||
|
* @param key - Key
|
||||||
|
*/
|
||||||
|
public void delete(byte[] key) {
|
||||||
|
db.delete(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the DB.
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void close() throws IOException {
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,432 @@
|
||||||
|
/*
|
||||||
|
* 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.ozone.web.localstorage;
|
||||||
|
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.apache.hadoop.ozone.OzoneConfigKeys;
|
||||||
|
import org.apache.hadoop.ozone.StorageContainerConfiguration;
|
||||||
|
import org.apache.hadoop.ozone.web.exceptions.ErrorTable;
|
||||||
|
import org.apache.hadoop.ozone.web.exceptions.OzoneException;
|
||||||
|
import org.apache.hadoop.ozone.web.handlers.UserArgs;
|
||||||
|
import org.apache.hadoop.ozone.web.handlers.VolumeArgs;
|
||||||
|
import org.apache.hadoop.ozone.web.response.ListVolumes;
|
||||||
|
import org.apache.hadoop.ozone.web.response.VolumeInfo;
|
||||||
|
import org.apache.hadoop.ozone.web.response.VolumeOwner;
|
||||||
|
import org.apache.hadoop.ozone.web.utils.OzoneConsts;
|
||||||
|
import org.iq80.leveldb.DBException;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
import java.util.concurrent.locks.ReadWriteLock;
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A stand alone Ozone implementation that allows us to run
|
||||||
|
* Ozone tests in local mode. This acts as the
|
||||||
|
* ozone backend when using MiniDFSCluster for testing.
|
||||||
|
*/
|
||||||
|
public final class OzoneMetadataManager {
|
||||||
|
static final Log LOG = LogFactory.getLog(OzoneMetadataManager.class);
|
||||||
|
private static OzoneMetadataManager bm = null;
|
||||||
|
|
||||||
|
/*
|
||||||
|
OzoneMetadataManager manages volume/bucket/object metadata and
|
||||||
|
data.
|
||||||
|
|
||||||
|
Metadata is maintained in 2 level DB files, UserDB and MetadataDB.
|
||||||
|
|
||||||
|
UserDB contains a Name and a List. For example volumes owned by the user
|
||||||
|
bilbo, would be maintained in UserDB as {bilbo}->{shire, rings}
|
||||||
|
|
||||||
|
This list part of mapping is context sensitive. That is, if you use {user
|
||||||
|
name} as the key, the list you get is a list of volumes. if you use
|
||||||
|
{user/volume} as the key the list you get is list of buckets. if you use
|
||||||
|
{user/volume/bucket} as key the list you get is the list of objects.
|
||||||
|
|
||||||
|
All keys in the UserDB starts with the UserName.
|
||||||
|
|
||||||
|
We also need to maintain a flat namespace for volumes. This is
|
||||||
|
maintained by the MetadataDB. MetadataDB contains the name of an
|
||||||
|
object(volume, bucket or key) and its associated metadata.
|
||||||
|
The keys in the Metadata DB are {volume}, {volume/bucket} or
|
||||||
|
{volume/bucket/key}. User name is absent, so we have a common root name
|
||||||
|
space for the volume.
|
||||||
|
|
||||||
|
The value of part of metadataDB points to corresponding *Info structures.
|
||||||
|
{volume] -> volumeInfo
|
||||||
|
{volume/bucket} -> bucketInfo
|
||||||
|
{volume/bucket/key} -> keyInfo
|
||||||
|
|
||||||
|
|
||||||
|
Here are various work flows :
|
||||||
|
|
||||||
|
CreateVolume -> Check if Volume exists in metadataDB, if not update UserDB
|
||||||
|
with a list of volumes and update metadataDB with VolumeInfo.
|
||||||
|
|
||||||
|
DeleteVolume -> Check the Volume, and check the VolumeInfo->bucketCount.
|
||||||
|
if bucketCount == 0, delete volume from userDB->{List of volumes} and
|
||||||
|
metadataDB.
|
||||||
|
|
||||||
|
Very similar work flows exist for CreateBucket and DeleteBucket.
|
||||||
|
|
||||||
|
// Please note : These database operations are *not* transactional,
|
||||||
|
// which means that failure can lead to inconsistencies.
|
||||||
|
// Only way to recover is to reset to a clean state, or
|
||||||
|
// use rm -rf /tmp/ozone :)
|
||||||
|
|
||||||
|
We have very simple locking policy. We have a ReaderWriter lock that is
|
||||||
|
taken for each action, this lock is aptly named "lock".
|
||||||
|
|
||||||
|
All actions *must* be performed with a lock held, either a read
|
||||||
|
lock or a write lock. Violation of these locking policies can be harmful.
|
||||||
|
|
||||||
|
|
||||||
|
// // IMPORTANT :
|
||||||
|
// // This is a simulation layer, this is NOT how the real
|
||||||
|
// // OZONE functions. This is written to so that we can write
|
||||||
|
// // stand-alone tests for the protocol and client code.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
private OzoneLevelDBStore userDB;
|
||||||
|
private OzoneLevelDBStore metadataDB;
|
||||||
|
|
||||||
|
private static final String USER_DB = "/user.db";
|
||||||
|
private static final String META_DB = "/metadata.db";
|
||||||
|
|
||||||
|
private ReadWriteLock lock;
|
||||||
|
private Charset encoding = Charset.forName("UTF-8");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs OzoneMetadataManager.
|
||||||
|
*/
|
||||||
|
private OzoneMetadataManager() {
|
||||||
|
|
||||||
|
lock = new ReentrantReadWriteLock();
|
||||||
|
StorageContainerConfiguration conf = new StorageContainerConfiguration();
|
||||||
|
|
||||||
|
String storageRoot =
|
||||||
|
conf.getTrimmed(OzoneConfigKeys.DFS_STORAGE_LOCAL_ROOT,
|
||||||
|
OzoneConfigKeys.DFS_STORAGE_LOCAL_ROOT_DEFAULT);
|
||||||
|
|
||||||
|
File file = new File(storageRoot);
|
||||||
|
|
||||||
|
if (!file.exists() && !file.mkdirs()) {
|
||||||
|
LOG.fatal("Creation of Ozone root failed. " + file.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
userDB = new OzoneLevelDBStore(new File(storageRoot + USER_DB), true);
|
||||||
|
metadataDB = new OzoneLevelDBStore(new File(storageRoot + META_DB), true);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
LOG.fatal("Cannot open db :" + ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets Ozone Manager.
|
||||||
|
* @return OzoneMetadataManager
|
||||||
|
*/
|
||||||
|
public static synchronized OzoneMetadataManager getOzoneMetadataManager() {
|
||||||
|
if (bm == null) {
|
||||||
|
bm = new OzoneMetadataManager();
|
||||||
|
}
|
||||||
|
return bm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a volume.
|
||||||
|
*
|
||||||
|
* @param args - VolumeArgs
|
||||||
|
*
|
||||||
|
* @throws OzoneException
|
||||||
|
*/
|
||||||
|
public void createVolume(VolumeArgs args) throws OzoneException {
|
||||||
|
try {
|
||||||
|
SimpleDateFormat format =
|
||||||
|
new SimpleDateFormat(OzoneConsts.OZONE_DATE_FORMAT, Locale.US);
|
||||||
|
format.setTimeZone(TimeZone.getTimeZone(OzoneConsts.OZONE_TIME_ZONE));
|
||||||
|
|
||||||
|
lock.writeLock().lock();
|
||||||
|
byte[] volumeName =
|
||||||
|
metadataDB.get(args.getVolumeName().getBytes(encoding));
|
||||||
|
|
||||||
|
if (volumeName != null) {
|
||||||
|
LOG.debug("Volume already exists.");
|
||||||
|
throw ErrorTable.newError(ErrorTable.VOLUME_ALREADY_EXISTS, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
VolumeInfo newVInfo = new VolumeInfo(args.getVolumeName(), format
|
||||||
|
.format(new Date(System.currentTimeMillis())), args.getAdminName());
|
||||||
|
|
||||||
|
newVInfo.setQuota(args.getQuota());
|
||||||
|
VolumeOwner owner = new VolumeOwner(args.getUserName());
|
||||||
|
newVInfo.setOwner(owner);
|
||||||
|
|
||||||
|
ListVolumes volumeList;
|
||||||
|
byte[] userVolumes = userDB.get(args.getUserName().getBytes(encoding));
|
||||||
|
if (userVolumes == null) {
|
||||||
|
volumeList = new ListVolumes();
|
||||||
|
} else {
|
||||||
|
volumeList = ListVolumes.parse(new String(userVolumes, encoding));
|
||||||
|
}
|
||||||
|
|
||||||
|
volumeList.addVolume(newVInfo);
|
||||||
|
volumeList.sort();
|
||||||
|
|
||||||
|
// Please note : These database operations are *not* transactional,
|
||||||
|
// which means that failure can lead to inconsistencies.
|
||||||
|
// Only way to recover is to reset to a clean state, or
|
||||||
|
// use rm -rf /tmp/ozone :)
|
||||||
|
|
||||||
|
|
||||||
|
userDB.put(args.getUserName().getBytes(encoding),
|
||||||
|
volumeList.toDBString().getBytes(encoding));
|
||||||
|
|
||||||
|
metadataDB.put(args.getVolumeName().getBytes(encoding),
|
||||||
|
newVInfo.toDBString().getBytes(encoding));
|
||||||
|
|
||||||
|
} catch (IOException | DBException ex) {
|
||||||
|
throw ErrorTable.newError(ErrorTable.SERVER_ERROR, args, ex);
|
||||||
|
} finally {
|
||||||
|
lock.writeLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the Volume properties like Owner Name and Quota.
|
||||||
|
*
|
||||||
|
* @param args - Volume Args
|
||||||
|
* @param property - Flag which tells us what property to upgrade
|
||||||
|
*
|
||||||
|
* @throws OzoneException
|
||||||
|
*/
|
||||||
|
public void setVolumeProperty(VolumeArgs args, VolumeProperty property)
|
||||||
|
throws OzoneException {
|
||||||
|
VolumeInfo info;
|
||||||
|
try {
|
||||||
|
lock.writeLock().lock();
|
||||||
|
byte[] volumeInfo =
|
||||||
|
metadataDB.get(args.getVolumeName().getBytes(encoding));
|
||||||
|
if (volumeInfo == null) {
|
||||||
|
throw ErrorTable.newError(ErrorTable.VOLUME_NOT_FOUND, args);
|
||||||
|
}
|
||||||
|
info = VolumeInfo.parse(new String(volumeInfo, encoding));
|
||||||
|
|
||||||
|
byte[] userBytes = userDB.get(args.getResourceName().getBytes(encoding));
|
||||||
|
ListVolumes volumeList;
|
||||||
|
if (userBytes == null) {
|
||||||
|
volumeList = new ListVolumes();
|
||||||
|
} else {
|
||||||
|
volumeList = ListVolumes.parse(new String(userBytes, encoding));
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (property) {
|
||||||
|
case OWNER:
|
||||||
|
// needs new owner, we delete the volume object from the
|
||||||
|
// old user's volume list
|
||||||
|
removeOldOwner(info);
|
||||||
|
VolumeOwner owner = new VolumeOwner(args.getUserName());
|
||||||
|
// set the new owner
|
||||||
|
info.setOwner(owner);
|
||||||
|
break;
|
||||||
|
case QUOTA:
|
||||||
|
// if this is quota update we just remove the old object from the
|
||||||
|
// current users list and update the same object later.
|
||||||
|
volumeList.getVolumes().remove(info);
|
||||||
|
info.setQuota(args.getQuota());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OzoneException ozEx =
|
||||||
|
ErrorTable.newError(ErrorTable.SERVER_ERROR, args);
|
||||||
|
ozEx.setMessage("Volume property is not recognized");
|
||||||
|
throw ozEx;
|
||||||
|
}
|
||||||
|
|
||||||
|
volumeList.addVolume(info);
|
||||||
|
|
||||||
|
metadataDB.put(args.getVolumeName().getBytes(encoding),
|
||||||
|
info.toDBString().getBytes(encoding));
|
||||||
|
|
||||||
|
// if this is an owner change this put will create a new owner or update
|
||||||
|
// the owner's volume list.
|
||||||
|
userDB.put(args.getResourceName().getBytes(encoding),
|
||||||
|
volumeList.toDBString().getBytes(encoding));
|
||||||
|
|
||||||
|
} catch (IOException | DBException ex) {
|
||||||
|
throw ErrorTable.newError(ErrorTable.SERVER_ERROR, args, ex);
|
||||||
|
} finally {
|
||||||
|
lock.writeLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the old owner from the volume.
|
||||||
|
*
|
||||||
|
* @param info - VolumeInfo
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
private void removeOldOwner(VolumeInfo info) throws IOException {
|
||||||
|
// We need to look the owner that we know is the current owner
|
||||||
|
byte[] volumeBytes =
|
||||||
|
userDB.get(info.getOwner().getName().getBytes(encoding));
|
||||||
|
ListVolumes volumeList =
|
||||||
|
ListVolumes.parse(new String(volumeBytes, encoding));
|
||||||
|
volumeList.getVolumes().remove(info);
|
||||||
|
|
||||||
|
// Write the new list info to the old user data
|
||||||
|
userDB.put(info.getOwner().getName().getBytes(encoding),
|
||||||
|
volumeList.toDBString().getBytes(encoding));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if you are the owner of a specific volume.
|
||||||
|
*
|
||||||
|
* @param args - VolumeArgs
|
||||||
|
*
|
||||||
|
* @return - True if you are the owner, false otherwise
|
||||||
|
*
|
||||||
|
* @throws OzoneException
|
||||||
|
*/
|
||||||
|
public boolean checkVolumeAccess(VolumeArgs args) throws OzoneException {
|
||||||
|
VolumeInfo info;
|
||||||
|
try {
|
||||||
|
lock.readLock().lock();
|
||||||
|
byte[] volumeInfo =
|
||||||
|
metadataDB.get(args.getVolumeName().getBytes(encoding));
|
||||||
|
if (volumeInfo == null) {
|
||||||
|
throw ErrorTable.newError(ErrorTable.VOLUME_NOT_FOUND, args);
|
||||||
|
}
|
||||||
|
info = VolumeInfo.parse(new String(volumeInfo, encoding));
|
||||||
|
return info.getOwner().getName().equals(args.getUserName());
|
||||||
|
} catch (IOException | DBException ex) {
|
||||||
|
throw ErrorTable.newError(ErrorTable.SERVER_ERROR, args, ex);
|
||||||
|
} finally {
|
||||||
|
lock.readLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getVolumeInfo returns the Volume Info of a specific volume.
|
||||||
|
*
|
||||||
|
* @param args - Volume args
|
||||||
|
*
|
||||||
|
* @return VolumeInfo
|
||||||
|
*
|
||||||
|
* @throws OzoneException
|
||||||
|
*/
|
||||||
|
public VolumeInfo getVolumeInfo(VolumeArgs args) throws OzoneException {
|
||||||
|
try {
|
||||||
|
lock.readLock().lock();
|
||||||
|
byte[] volumeInfo =
|
||||||
|
metadataDB.get(args.getVolumeName().getBytes(encoding));
|
||||||
|
if (volumeInfo == null) {
|
||||||
|
throw ErrorTable.newError(ErrorTable.VOLUME_NOT_FOUND, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
return VolumeInfo.parse(new String(volumeInfo, encoding));
|
||||||
|
} catch (IOException | DBException ex) {
|
||||||
|
throw ErrorTable.newError(ErrorTable.SERVER_ERROR, args, ex);
|
||||||
|
} finally {
|
||||||
|
lock.readLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all the volumes owned by a specific user.
|
||||||
|
*
|
||||||
|
* @param args - User Args
|
||||||
|
*
|
||||||
|
* @return - ListVolumes
|
||||||
|
*
|
||||||
|
* @throws OzoneException
|
||||||
|
*/
|
||||||
|
public ListVolumes listVolumes(UserArgs args) throws OzoneException {
|
||||||
|
try {
|
||||||
|
lock.readLock().lock();
|
||||||
|
byte[] volumeList = userDB.get(args.getUserName().getBytes(encoding));
|
||||||
|
if (volumeList == null) {
|
||||||
|
throw ErrorTable.newError(ErrorTable.USER_NOT_FOUND, args);
|
||||||
|
}
|
||||||
|
return ListVolumes.parse(new String(volumeList, encoding));
|
||||||
|
} catch (IOException | DBException ex) {
|
||||||
|
throw ErrorTable.newError(ErrorTable.SERVER_ERROR, args, ex);
|
||||||
|
} finally {
|
||||||
|
lock.readLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a volume if it exists and is empty.
|
||||||
|
*
|
||||||
|
* @param args - volume args
|
||||||
|
*
|
||||||
|
* @throws OzoneException
|
||||||
|
*/
|
||||||
|
public void deleteVolume(VolumeArgs args) throws OzoneException {
|
||||||
|
try {
|
||||||
|
lock.writeLock().lock();
|
||||||
|
byte[] volumeName =
|
||||||
|
metadataDB.get(args.getVolumeName().getBytes(encoding));
|
||||||
|
if (volumeName == null) {
|
||||||
|
throw ErrorTable.newError(ErrorTable.VOLUME_NOT_FOUND, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
VolumeInfo vInfo = VolumeInfo.parse(new String(volumeName, encoding));
|
||||||
|
|
||||||
|
// Only remove volumes if they are empty.
|
||||||
|
if (vInfo.getBucketCount() > 0) {
|
||||||
|
throw ErrorTable.newError(ErrorTable.VOLUME_NOT_EMPTY, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
ListVolumes volumeList;
|
||||||
|
String user = vInfo.getOwner().getName();
|
||||||
|
byte[] userVolumes = userDB.get(user.getBytes(encoding));
|
||||||
|
if (userVolumes == null) {
|
||||||
|
throw ErrorTable.newError(ErrorTable.VOLUME_NOT_FOUND, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
volumeList = ListVolumes.parse(new String(userVolumes, encoding));
|
||||||
|
volumeList.getVolumes().remove(vInfo);
|
||||||
|
|
||||||
|
metadataDB.delete(args.getVolumeName().getBytes(encoding));
|
||||||
|
userDB.put(user.getBytes(encoding),
|
||||||
|
volumeList.toDBString().getBytes(encoding));
|
||||||
|
} catch (IOException | DBException ex) {
|
||||||
|
throw ErrorTable.newError(ErrorTable.SERVER_ERROR, args, ex);
|
||||||
|
} finally {
|
||||||
|
lock.writeLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is used in updates to volume metadata.
|
||||||
|
*/
|
||||||
|
public enum VolumeProperty {
|
||||||
|
OWNER, QUOTA
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,6 +43,10 @@ public final class OzoneConsts {
|
||||||
public static final String OZONE_ACL_READ_WRITE = "rw";
|
public static final String OZONE_ACL_READ_WRITE = "rw";
|
||||||
public static final String OZONE_ACL_WRITE_READ = "wr";
|
public static final String OZONE_ACL_WRITE_READ = "wr";
|
||||||
|
|
||||||
|
public static final String OZONE_DATE_FORMAT =
|
||||||
|
"EEE, dd MMM yyyy HH:mm:ss zzz";
|
||||||
|
public static final String OZONE_TIME_ZONE = "GMT";
|
||||||
|
|
||||||
private OzoneConsts() {
|
private OzoneConsts() {
|
||||||
// Never Constructed
|
// Never Constructed
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,6 @@ import org.apache.hadoop.ozone.web.exceptions.ErrorTable;
|
||||||
import org.apache.hadoop.ozone.web.exceptions.OzoneException;
|
import org.apache.hadoop.ozone.web.exceptions.OzoneException;
|
||||||
import org.apache.hadoop.ozone.web.handlers.UserArgs;
|
import org.apache.hadoop.ozone.web.handlers.UserArgs;
|
||||||
import org.apache.hadoop.ozone.web.headers.Header;
|
import org.apache.hadoop.ozone.web.headers.Header;
|
||||||
import org.apache.hadoop.util.Time;
|
|
||||||
|
|
||||||
import javax.ws.rs.core.HttpHeaders;
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
import javax.ws.rs.core.Request;
|
import javax.ws.rs.core.Request;
|
||||||
|
@ -45,6 +44,10 @@ import java.util.UUID;
|
||||||
@InterfaceAudience.Private
|
@InterfaceAudience.Private
|
||||||
public final class OzoneUtils {
|
public final class OzoneUtils {
|
||||||
|
|
||||||
|
private OzoneUtils() {
|
||||||
|
// Never constructed
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* verifies that bucket name / volume name is a valid DNS name.
|
* verifies that bucket name / volume name is a valid DNS name.
|
||||||
*
|
*
|
||||||
|
@ -128,7 +131,7 @@ public final class OzoneUtils {
|
||||||
/**
|
/**
|
||||||
* Returns a random Request ID.
|
* Returns a random Request ID.
|
||||||
*
|
*
|
||||||
* Request ID is returned to the client as well as flows thru the system
|
* Request ID is returned to the client as well as flows through the system
|
||||||
* facilitating debugging on why a certain request failed.
|
* facilitating debugging on why a certain request failed.
|
||||||
*
|
*
|
||||||
* @return String random request ID
|
* @return String random request ID
|
||||||
|
@ -206,8 +209,8 @@ public final class OzoneUtils {
|
||||||
String resource, String hostname)
|
String resource, String hostname)
|
||||||
throws OzoneException {
|
throws OzoneException {
|
||||||
SimpleDateFormat format =
|
SimpleDateFormat format =
|
||||||
new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
|
new SimpleDateFormat(OzoneConsts.OZONE_DATE_FORMAT, Locale.US);
|
||||||
format.setTimeZone(TimeZone.getTimeZone("GMT"));
|
format.setTimeZone(TimeZone.getTimeZone(OzoneConsts.OZONE_TIME_ZONE));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return format.parse(dateString);
|
return format.parse(dateString);
|
||||||
|
@ -231,9 +234,9 @@ public final class OzoneUtils {
|
||||||
public static Response getResponse(UserArgs args, int statusCode,
|
public static Response getResponse(UserArgs args, int statusCode,
|
||||||
String payload) {
|
String payload) {
|
||||||
SimpleDateFormat format =
|
SimpleDateFormat format =
|
||||||
new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
|
new SimpleDateFormat(OzoneConsts.OZONE_DATE_FORMAT, Locale.US);
|
||||||
format.setTimeZone(TimeZone.getTimeZone("GMT"));
|
format.setTimeZone(TimeZone.getTimeZone(OzoneConsts.OZONE_TIME_ZONE));
|
||||||
String date = format.format(new Date(Time.monotonicNow()));
|
String date = format.format(new Date(System.currentTimeMillis()));
|
||||||
return Response.ok(payload)
|
return Response.ok(payload)
|
||||||
.header(Header.OZONE_SERVER_NAME, args.getHostName())
|
.header(Header.OZONE_SERVER_NAME, args.getHostName())
|
||||||
.header(Header.OZONE_REQUEST_ID, args.getRequestID())
|
.header(Header.OZONE_REQUEST_ID, args.getRequestID())
|
||||||
|
@ -252,17 +255,13 @@ public final class OzoneUtils {
|
||||||
public static Response getResponse(UserArgs args, int statusCode,
|
public static Response getResponse(UserArgs args, int statusCode,
|
||||||
InputStream stream) {
|
InputStream stream) {
|
||||||
SimpleDateFormat format =
|
SimpleDateFormat format =
|
||||||
new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
|
new SimpleDateFormat(OzoneConsts.OZONE_DATE_FORMAT, Locale.US);
|
||||||
format.setTimeZone(TimeZone.getTimeZone("GMT"));
|
format.setTimeZone(TimeZone.getTimeZone(OzoneConsts.OZONE_TIME_ZONE));
|
||||||
String date = format.format(new Date(Time.monotonicNow()));
|
String date = format.format(new Date(System.currentTimeMillis()));
|
||||||
return Response.ok(stream)
|
return Response.ok(stream)
|
||||||
.header(Header.OZONE_SERVER_NAME, args.getHostName())
|
.header(Header.OZONE_SERVER_NAME, args.getHostName())
|
||||||
.header(Header.OZONE_REQUEST_ID, args.getRequestID())
|
.header(Header.OZONE_REQUEST_ID, args.getRequestID())
|
||||||
.header(HttpHeaders.DATE, date).status(statusCode)
|
.header(HttpHeaders.DATE, date).status(statusCode)
|
||||||
.header(HttpHeaders.CONTENT_TYPE, "application/octet-stream").build();
|
.header(HttpHeaders.CONTENT_TYPE, "application/octet-stream").build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private OzoneUtils() {
|
|
||||||
// Never constructed
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* 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.ozone.web;
|
||||||
|
|
||||||
|
import org.apache.hadoop.ozone.web.response.ListVolumes;
|
||||||
|
import org.apache.hadoop.ozone.web.response.VolumeInfo;
|
||||||
|
import org.apache.hadoop.ozone.web.response.VolumeOwner;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class TestVolumeStructs {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVolumeInfoParse() throws IOException {
|
||||||
|
VolumeInfo volInfo = new VolumeInfo("testvol",
|
||||||
|
"Thu, Apr 9, 2015 10:23:45 GMT", "gandalf");
|
||||||
|
VolumeOwner owner = new VolumeOwner("bilbo");
|
||||||
|
volInfo.setOwner(owner);
|
||||||
|
String jString = volInfo.toJsonString();
|
||||||
|
VolumeInfo newVollInfo = VolumeInfo.parse(jString);
|
||||||
|
String one = volInfo.toJsonString();
|
||||||
|
String two = newVollInfo.toJsonString();
|
||||||
|
|
||||||
|
assertEquals(volInfo.toJsonString(), newVollInfo.toJsonString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVolumeInfoValue() throws IOException {
|
||||||
|
String createdOn = "Thu, Apr 9, 2015 10:23:45 GMT";
|
||||||
|
String createdBy = "gandalf";
|
||||||
|
VolumeInfo volInfo = new VolumeInfo("testvol",
|
||||||
|
createdOn, createdBy);
|
||||||
|
assertEquals(volInfo.getCreatedBy(), createdBy);
|
||||||
|
assertEquals(volInfo.getCreatedOn(), createdOn);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVolumeListParse() throws IOException {
|
||||||
|
|
||||||
|
ListVolumes list = new ListVolumes();
|
||||||
|
for (int x = 0; x < 100; x++) {
|
||||||
|
VolumeInfo volInfo = new VolumeInfo("testvol" + Integer.toString(x),
|
||||||
|
"Thu, Apr 9, 2015 10:23:45 GMT", "gandalf");
|
||||||
|
list.addVolume(volInfo);
|
||||||
|
}
|
||||||
|
list.sort();
|
||||||
|
String listString = list.toJsonString();
|
||||||
|
ListVolumes newList = ListVolumes.parse(listString);
|
||||||
|
assertEquals(list.toJsonString(), newList.toJsonString());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue