Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// 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.api.command.user.bucket;

import com.cloud.exception.ConcurrentOperationException;
import com.cloud.user.Account;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.api.ACL;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiCommandResourceType;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ResponseObject.ResponseView;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.command.user.UserCmd;
import org.apache.cloudstack.api.response.BucketResponse;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.storage.object.Bucket;

@APICommand(name = "syncBucketUsage", description = "Synchronizes Bucket usage.", responseObject = BucketResponse.class,
responseView = ResponseView.Restricted, entityType = {Bucket.class},
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.19.0",
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
public class SyncBucketUsageCmd extends BaseCmd implements UserCmd {

private static final String s_name = "syncbucketusageresponse";

@ACL(accessType = AccessType.OperateEntry)
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = BucketResponse.class,
required = true, description = "The ID of the Bucket")
private Long id;

public Long getId() {
return id;
}

@Override
public String getCommandName() {
return s_name;
}

public static String getResultObjectName() {
return "bucket";
}

@Override
public long getEntityOwnerId() {
Bucket bucket = _entityMgr.findById(Bucket.class, getId());
if (bucket != null) {
return bucket.getAccountId();
}

return Account.ACCOUNT_ID_SYSTEM;
}

@Override
public Long getApiResourceId() {
return id;
}

@Override
public ApiCommandResourceType getApiResourceType() {
return ApiCommandResourceType.Bucket;
}

@Override
public void execute() throws ConcurrentOperationException {
CallContext.current().setEventDetails("Bucket Id: " + this._uuidMgr.getUuid(Bucket.class, getId()));
Bucket bucket;
try {
bucket = _bucketService.syncBucketUsage(this, CallContext.current().getCallingAccount());
} catch (Exception e) {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Error while synchronizing bucket usage. " + e.getMessage());
}
if (bucket != null) {
BucketResponse response = _responseGenerator.createBucketResponse(bucket);
response.setResponseName(getCommandName());
setResponseObject(response);
} else {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to synchronize bucket usage");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.cloud.exception.ResourceAllocationException;
import com.cloud.user.Account;
import org.apache.cloudstack.api.command.user.bucket.CreateBucketCmd;
import org.apache.cloudstack.api.command.user.bucket.SyncBucketUsageCmd;
import org.apache.cloudstack.api.command.user.bucket.UpdateBucketCmd;
import org.apache.cloudstack.framework.config.ConfigKey;

Expand Down Expand Up @@ -107,5 +108,7 @@ public interface BucketApiService {

boolean updateBucket(UpdateBucketCmd cmd, Account caller) throws ResourceAllocationException;

Bucket syncBucketUsage(SyncBucketUsageCmd cmd, Account caller);

void getBucketUsage();
}
13 changes: 7 additions & 6 deletions server/src/main/java/com/cloud/server/ManagementServerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@
import org.apache.cloudstack.api.command.user.bucket.CreateBucketCmd;
import org.apache.cloudstack.api.command.user.bucket.DeleteBucketCmd;
import org.apache.cloudstack.api.command.user.bucket.ListBucketsCmd;
import org.apache.cloudstack.api.command.user.bucket.SyncBucketUsageCmd;
import org.apache.cloudstack.api.command.user.bucket.UpdateBucketCmd;
import org.apache.cloudstack.api.command.user.config.ListCapabilitiesCmd;
import org.apache.cloudstack.api.command.user.consoleproxy.CreateConsoleEndpointCmd;
Expand Down Expand Up @@ -947,11 +948,10 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
static final String FOR_SYSTEMVMS = "forsystemvms";
static final ConfigKey<Integer> vmPasswordLength = new ConfigKey<>("Advanced", Integer.class, "vm.password.length", "6", "Specifies the length of a randomly generated password", false);
static final ConfigKey<Integer> sshKeyLength = new ConfigKey<>("Advanced", Integer.class, "ssh.key.length", "2048", "Specifies custom SSH key length (bit)", true, ConfigKey.Scope.Global);
static final ConfigKey<Integer> LicenseCheckInterval = new ConfigKey<>("Advanced", Integer.class,
"license.check.interval", "1",
"License check interval (days)", true);
static final ConfigKey<Integer> licenseCheckInterval = new ConfigKey<>("Advanced", Integer.class, "license.check.interval", "1", "License check interval (days)", true);
static final ConfigKey<Boolean> humanReadableSizes = new ConfigKey<>("Advanced", Boolean.class, "display.human.readable.sizes", "true", "Enables outputting human readable byte sizes to logs and usage records.", false, ConfigKey.Scope.Global);
public static final ConfigKey<String> customCsIdentifier = new ConfigKey<>("Advanced", String.class, "custom.cs.identifier", UUID.randomUUID().toString().split("-")[0].substring(4), "Custom identifier for the cloudstack installation", true, ConfigKey.Scope.Global);
public static final ConfigKey<Double> dbAactiveRestartThresholdPercent = new ConfigKey<>("Advanced", Double.class, "mold.db.pool.restart.threshold.percent", "0.98", "Restart the mold management service when active Cloud DB pool usage reaches this ratio of the maximum pool size. Values between 0 and 1 are recommended.", true, ConfigKey.Scope.Global);
private static final VirtualMachine.Type []systemVmTypes = { VirtualMachine.Type.SecondaryStorageVm, VirtualMachine.Type.ConsoleProxy};
private static final List<HypervisorType> LIVE_MIGRATION_SUPPORTING_HYPERVISORS = List.of(HypervisorType.Hyperv, HypervisorType.KVM,
HypervisorType.LXC, HypervisorType.Ovm, HypervisorType.Ovm3, HypervisorType.Simulator, HypervisorType.VMware, HypervisorType.XenServer);
Expand Down Expand Up @@ -1240,8 +1240,8 @@ public boolean configure(final String name, final Map<String, Object> params) th
_alertExecutor.scheduleAtFixedRate(new AlertPurgeTask(), alertPurgeInterval, alertPurgeInterval, TimeUnit.SECONDS);
}

if(LicenseCheckInterval.value() > 0) {
_licenseExecutor.scheduleAtFixedRate(new LicenseCheckTask(), 0, LicenseCheckInterval.value(), TimeUnit.DAYS);
if(licenseCheckInterval.value() > 0) {
_licenseExecutor.scheduleAtFixedRate(new LicenseCheckTask(), 0, licenseCheckInterval.value(), TimeUnit.DAYS);
}

final String[] availableIds = TimeZone.getAvailableIDs();
Expand Down Expand Up @@ -6832,6 +6832,7 @@ public List<Class<?>> getCommands() {
cmdList.add(UpdateBucketCmd.class);
cmdList.add(DeleteBucketCmd.class);
cmdList.add(ListBucketsCmd.class);
cmdList.add(SyncBucketUsageCmd.class);
cmdList.add(LicenseCheckCmd.class);
cmdList.add(ListHostRedfishDataCmd.class);

Expand All @@ -6845,7 +6846,7 @@ public String getConfigComponentName() {

@Override
public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] {vmPasswordLength, sshKeyLength, humanReadableSizes, customCsIdentifier};
return new ConfigKey<?>[] {vmPasswordLength, sshKeyLength, humanReadableSizes, customCsIdentifier, dbAactiveRestartThresholdPercent};
}

protected class EventPurgeTask extends ManagedContextRunnable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
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.SyncBucketUsageCmd;
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;
Expand Down Expand Up @@ -86,7 +87,6 @@ public boolean configure(String name, Map<String, Object> params) throws Configu
@Override
public boolean start() {
_executor.scheduleWithFixedDelay(new BucketUsageTask(), 60L, 3600L, TimeUnit.SECONDS);
_executor.scheduleWithFixedDelay(new BucketUsageCustomTask(), 60L, 60L, TimeUnit.SECONDS);
return true;
}

Expand Down Expand Up @@ -295,32 +295,39 @@ public void getBucketUsage() {
}
}

private class BucketUsageCustomTask extends ManagedContextRunnable {
public BucketUsageCustomTask() {
@Override
public Bucket syncBucketUsage(SyncBucketUsageCmd 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());
if (objectStoreVO == null) {
throw new InvalidParameterValueException("Unable to find object store with ID: " + bucket.getObjectStoreId());
}
ObjectStoreEntity objectStore = (ObjectStoreEntity)_dataStoreMgr.getDataStore(objectStoreVO.getId(), DataStoreRole.Object);
Map<String, Long> bucketSizes = objectStore.getAllBucketsUsage();
updateBucketUsage(bucket, bucketSizes.getOrDefault(bucket.getName(), 0L), true);
return _bucketDao.findById(bucket.getId());
}

@Override
protected void runInContext() {
try {
List<ObjectStoreVO> objectStores = _objectStoreDao.listObjectStores();
for(ObjectStoreVO objectStoreVO: objectStores) {
List<BucketVO> buckets = _bucketDao.listByObjectStoreId(objectStoreVO.getId());
if (buckets.isEmpty()) {
continue;
}
ObjectStoreEntity objectStore = (ObjectStoreEntity)_dataStoreMgr.getDataStore(objectStoreVO.getId(), DataStoreRole.Object);
Map<String, Long> bucketSizes = objectStore.getAllBucketsUsage();
for(BucketVO bucket : buckets) {
Long size = bucketSizes.get(bucket.getName());
if( size != null){
bucket.setSize(size);
_bucketDao.update(bucket.getId(), bucket);
}
}
}
} catch (Exception e) {
logger.error("Error while fetching bucket usage", e);
}
private void updateBucketUsage(BucketVO bucket, long size, boolean updateStats) {
bucket.setSize(size);
_bucketDao.update(bucket.getId(), bucket);

if (!updateStats) {
return;
}

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);
}
}

Expand All @@ -342,19 +349,7 @@ protected void runInContext() {
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);
}
updateBucketUsage(bucket, size, true);
}
}
}
Expand Down
1 change: 1 addition & 0 deletions ui/public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -3699,6 +3699,7 @@
"message.success.release.dedicated.ipv4.subnet": "Successfully released dedicated IPv4 subnet",
"message.success.remove.egress.rule": "Successfully removed egress rule",
"message.success.remove.objectstore.objects": "Successfully removed selected object(s)",
"message.success.remove.objectstore.objects.count": "Removed {count} object(s).",
"message.success.remove.objectstore.directory": "Successfully removed selected directory",
"message.success.remove.firewall.rule": "Successfully removed firewall rule",
"message.success.remove.instance.rule": "Successfully removed Instance from rule",
Expand Down
1 change: 1 addition & 0 deletions ui/public/locales/ko_KR.json
Original file line number Diff line number Diff line change
Expand Up @@ -3698,6 +3698,7 @@
"message.success.release.dedicated.ipv4.subnet": "\uc804\uc6a9 IPv4 \uc11c\ube0c\ub137\uc744 \uc131\uacf5\uc801\uc73c\ub85c \ub9b4\ub9ac\uc2a4\ud588\uc2b5\ub2c8\ub2e4",
"message.success.remove.egress.rule": "\uc1a1\uc2e0 \uaddc\uce59\uc744 \uc81c\uac70\ud588\uc2b5\ub2c8\ub2e4.",
"message.success.remove.objectstore.objects": "\uc120\ud0dd\ud55c \uac1c\uccb4\ub97c \uc131\uacf5\uc801\uc73c\ub85c \uc81c\uac70\ud588\uc2b5\ub2c8\ub2e4.",
"message.success.remove.objectstore.objects.count": "\ucd1d {count}\uac1c \uac1d\uccb4\ub97c \uc0ad\uc81c\ud588\uc2b5\ub2c8\ub2e4.",
"message.success.remove.objectstore.directory": "\uc120\ud0dd\ud55c \ub514\ub809\ud130\ub9ac\ub97c \uc131\uacf5\uc801\uc73c\ub85c \uc81c\uac70\ud588\uc2b5\ub2c8\ub2e4.",
"message.success.remove.firewall.rule": "\ubc29\ud654\ubcbd \uaddc\uce59\uc744 \uc81c\uac70\ud588\uc2b5\ub2c8\ub2e4.",
"message.success.remove.instance.rule": "\uaddc\uce59\uc5d0\uc11c \uac00\uc0c1\uba38\uc2e0\uc744 \uc81c\uac70\ud588\uc2b5\ub2c8\ub2e4.",
Expand Down
Loading
Loading