blob: e6acd180f16fa40cc6509ea3415d5133e2c57713 [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
//
// 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.cloudstack.storage.object;
import com.amazonaws.services.s3.internal.BucketNameUtils;
import com.amazonaws.services.s3.model.IllegalBucketNameException;
import com.cloud.event.ActionEvent;
import com.cloud.event.EventTypes;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.storage.BucketVO;
import com.cloud.storage.DataStoreRole;
import com.cloud.storage.dao.BucketDao;
import com.cloud.usage.BucketStatisticsVO;
import com.cloud.usage.dao.BucketStatisticsDao;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.concurrency.NamedThreadFactory;
import com.cloud.utils.db.GlobalLock;
import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.cloudstack.api.command.user.bucket.CreateBucketCmd;
import org.apache.cloudstack.api.command.user.bucket.UpdateBucketCmd;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.Configurable;
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
import org.apache.cloudstack.storage.datastore.db.ObjectStoreDao;
import org.apache.cloudstack.storage.datastore.db.ObjectStoreVO;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class BucketApiServiceImpl extends ManagerBase implements BucketApiService, Configurable {
@Inject
private ObjectStoreDao _objectStoreDao;
@Inject
DataStoreManager _dataStoreMgr;
@Inject
private BucketDao _bucketDao;
@Inject
private AccountManager _accountMgr;
@Inject
private BucketStatisticsDao _bucketStatisticsDao;
private ScheduledExecutorService _executor = null;
private static final int ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION = 3;
protected BucketApiServiceImpl() {
}
@Override
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
_executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("Bucket-Usage"));
return true;
}
@Override
public boolean start() {
_executor.scheduleWithFixedDelay(new BucketUsageTask(), 60L, 3600L, TimeUnit.SECONDS);
return true;
}
@Override
public boolean stop() {
_executor.shutdown();
return true;
}
@Override
public String getConfigComponentName() {
return BucketApiService.class.getSimpleName();
}
@Override
public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] {
};
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_BUCKET_CREATE, eventDescription = "creating bucket", create = true)
public Bucket allocBucket(CreateBucketCmd cmd) {
try {
BucketNameUtils.validateBucketName(cmd.getBucketName());
} catch (IllegalBucketNameException e) {
logger.error("Invalid Bucket Name: " +cmd.getBucketName(), e);
throw new InvalidParameterValueException("Invalid Bucket Name: "+e.getMessage());
}
//ToDo check bucket exists
long ownerId = cmd.getEntityOwnerId();
Account owner = _accountMgr.getActiveAccountById(ownerId);
ObjectStoreVO objectStoreVO = _objectStoreDao.findById(cmd.getObjectStoragePoolId());
ObjectStoreEntity objectStore = (ObjectStoreEntity)_dataStoreMgr.getDataStore(objectStoreVO.getId(), DataStoreRole.Object);
try {
if(!objectStore.createUser(ownerId)) {
logger.error("Failed to create user in objectstore "+ objectStore.getName());
return null;
}
} catch (CloudRuntimeException e) {
logger.error("Error while checking object store user.", e);
return null;
}
BucketVO bucket = new BucketVO(ownerId, owner.getDomainId(), cmd.getObjectStoragePoolId(), cmd.getBucketName(), cmd.getQuota(),
cmd.isVersioning(), cmd.isEncryption(), cmd.isObjectLocking(), cmd.getPolicy());
_bucketDao.persist(bucket);
return bucket;
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_BUCKET_CREATE, eventDescription = "creating bucket", async = true)
public Bucket createBucket(CreateBucketCmd cmd) {
ObjectStoreVO objectStoreVO = _objectStoreDao.findById(cmd.getObjectStoragePoolId());
ObjectStoreEntity objectStore = (ObjectStoreEntity)_dataStoreMgr.getDataStore(objectStoreVO.getId(), DataStoreRole.Object);
BucketVO bucket = _bucketDao.findById(cmd.getEntityId());
boolean objectLock = false;
boolean bucketCreated = false;
if(cmd.isObjectLocking()) {
objectLock = true;
}
try {
objectStore.createBucket(bucket, objectLock);
bucketCreated = true;
if (cmd.isVersioning()) {
objectStore.setBucketVersioning(bucket.getName());
}
if (cmd.isEncryption()) {
objectStore.setBucketEncryption(bucket.getName());
}
if (cmd.getQuota() != null) {
objectStore.setQuota(bucket.getName(), cmd.getQuota());
}
if (cmd.getPolicy() != null) {
objectStore.setBucketPolicy(bucket.getName(), cmd.getPolicy());
}
bucket.setState(Bucket.State.Created);
_bucketDao.update(bucket.getId(), bucket);
} catch (Exception e) {
logger.debug("Failed to create bucket with name: "+bucket.getName(), e);
if(bucketCreated) {
objectStore.deleteBucket(bucket.getName());
}
_bucketDao.remove(bucket.getId());
throw new CloudRuntimeException("Failed to create bucket with name: "+bucket.getName()+". "+e.getMessage());
}
return bucket;
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_BUCKET_DELETE, eventDescription = "deleting bucket")
public boolean deleteBucket(long bucketId, Account caller) {
Bucket bucket = _bucketDao.findById(bucketId);
if (bucket == null) {
throw new InvalidParameterValueException("Unable to find bucket with ID: " + bucketId);
}
_accountMgr.checkAccess(caller, null, true, bucket);
ObjectStoreVO objectStoreVO = _objectStoreDao.findById(bucket.getObjectStoreId());
ObjectStoreEntity objectStore = (ObjectStoreEntity)_dataStoreMgr.getDataStore(objectStoreVO.getId(), DataStoreRole.Object);
if (objectStore.deleteBucket(bucket.getName())) {
return _bucketDao.remove(bucketId);
}
return false;
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_BUCKET_UPDATE, eventDescription = "updating bucket")
public boolean updateBucket(UpdateBucketCmd cmd, Account caller) {
BucketVO bucket = _bucketDao.findById(cmd.getId());
if (bucket == null) {
throw new InvalidParameterValueException("Unable to find bucket with ID: " + cmd.getId());
}
_accountMgr.checkAccess(caller, null, true, bucket);
ObjectStoreVO objectStoreVO = _objectStoreDao.findById(bucket.getObjectStoreId());
ObjectStoreEntity objectStore = (ObjectStoreEntity)_dataStoreMgr.getDataStore(objectStoreVO.getId(), DataStoreRole.Object);
try {
if (cmd.getEncryption() != null) {
if (cmd.getEncryption()) {
objectStore.setBucketEncryption(bucket.getName());
} else {
objectStore.deleteBucketEncryption(bucket.getName());
}
bucket.setEncryption(cmd.getEncryption());
}
if (cmd.getVersioning() != null) {
if (cmd.getVersioning()) {
objectStore.setBucketVersioning(bucket.getName());
} else {
objectStore.deleteBucketVersioning(bucket.getName());
}
bucket.setVersioning(cmd.getVersioning());
}
if (cmd.getPolicy() != null) {
objectStore.setBucketPolicy(bucket.getName(), cmd.getPolicy());
bucket.setPolicy(cmd.getPolicy());
}
if (cmd.getQuota() != null) {
objectStore.setQuota(bucket.getName(), cmd.getQuota());
bucket.setQuota(cmd.getQuota());
}
_bucketDao.update(bucket.getId(), bucket);
} catch (Exception e) {
throw new CloudRuntimeException("Error while updating bucket: " +bucket.getName() +". "+e.getMessage());
}
return true;
}
public void getBucketUsage() {
//ToDo track usage one last time when object store or bucket is removed
List<ObjectStoreVO> objectStores = _objectStoreDao.listObjectStores();
for(ObjectStoreVO objectStoreVO: objectStores) {
ObjectStoreEntity objectStore = (ObjectStoreEntity)_dataStoreMgr.getDataStore(objectStoreVO.getId(), DataStoreRole.Object);
Map<String, Long> bucketSizes = objectStore.getAllBucketsUsage();
List<BucketVO> buckets = _bucketDao.listByObjectStoreId(objectStoreVO.getId());
for(BucketVO bucket : buckets) {
Long size = bucketSizes.get(bucket.getName());
if( size != null){
bucket.setSize(size);
_bucketDao.update(bucket.getId(), bucket);
}
}
}
}
private class BucketUsageTask extends ManagedContextRunnable {
public BucketUsageTask() {
}
@Override
protected void runInContext() {
GlobalLock scanLock = GlobalLock.getInternLock("BucketUsage");
try {
if (scanLock.lock(ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION)) {
try {
List<ObjectStoreVO> objectStores = _objectStoreDao.listObjectStores();
for(ObjectStoreVO objectStoreVO: objectStores) {
ObjectStoreEntity objectStore = (ObjectStoreEntity)_dataStoreMgr.getDataStore(objectStoreVO.getId(), DataStoreRole.Object);
Map<String, Long> bucketSizes = objectStore.getAllBucketsUsage();
List<BucketVO> buckets = _bucketDao.listByObjectStoreId(objectStoreVO.getId());
for(BucketVO bucket : buckets) {
Long size = bucketSizes.get(bucket.getName());
if( size != null){
bucket.setSize(size);
_bucketDao.update(bucket.getId(), bucket);
//Update Bucket Usage stats
BucketStatisticsVO bucketStatisticsVO = _bucketStatisticsDao.findBy(bucket.getAccountId(), bucket.getId());
if(bucketStatisticsVO != null) {
bucketStatisticsVO.setSize(size);
_bucketStatisticsDao.update(bucketStatisticsVO.getId(), bucketStatisticsVO);
} else {
bucketStatisticsVO = new BucketStatisticsVO(bucket.getAccountId(), bucket.getId());
bucketStatisticsVO.setSize(size);
_bucketStatisticsDao.persist(bucketStatisticsVO);
}
}
}
}
logger.debug("Completed updating bucket usage for all object stores");
} catch (Exception e) {
logger.error("Error while fetching bucket usage", e);
} finally {
scanLock.unlock();
}
}
} finally {
scanLock.releaseRef();
}
}
}
}