Skip to content

Commit a7beaaf

Browse files
abh1sarDaanHooglandlucas-a-martinsLucas MartinsPearl1594
authored
Add Resource Limits to Backups and Object Storage (#10017)
Doc PR : apache/cloudstack-documentation#461 This PR fixes #8638 == Description Four new Resource Types have been added. Admin can configure corresponding resource limits for the tenants at different levels (domain, account, project) User dashboard's Storage section will show the new resources, their limits and current usage. 1. backup - No. of backups used by the account 2. backup_storage - Backup storage allocated for the account 3. bucket - No. of buckets used by the accounts 4. object_storage - Object storage allocated for the account. Some other related changes done to BnR framework: 1. Maximum number of Backups to retain can be specified while creating Backup schedules, similar to Scheduled snapshots. 2. Oldest Scheduled backup of the same interval type will be deleted once the number reaches the configured max Backups value. 3. Code refactor: Moved syncBackups method from BackupProvider to the framework BackupManagerImpl, as it is a common functionality and all providers were using duplicated code. Changes done to the Object Storage Framework 1. Quota parameter is made mandatory while creating a bucket. Bucket quota is considered to be the allocated space and will be used to enforce Resource limits. == Schema Changes: 1. New Column `max_backups` added to `backup_schedule` table 4. New Column `backup_interval_type` added to `backups` table == Api Changes: 1. createBackup: new Parameter `scheduleid`. It should be specified whenever a scheduled backup is created. This will translate to the `backup_interval_type` in the `backups` table. 3. createBackupScheduke: new Parameter `max_backups`. To specify maximum number of backups to retain for the given schedule. == Configurations: |Setting |Scope |Default Value |Description| |-------|--------|--------------|-----------| |backup.max.hourly |Global |8 |Maximum recurring hourly backups to be retained for an instance| |backup.max.daily |Global |8 |Maximum recurring daily backups to be retained for an instance| |backup.max.weekly |Global |8 |Maximum recurring weekly backups to be retained for an instance| |backup.max.monthly |Global |8 |Maximum recurring monthly backups to be retained for an instance| |max.account.backups| Global| 20 | The default maximum number of backups that can be created for an account| |max.account.backup.storage| Global| 400 | The default maximum backup storage space (in GiB) that can be used for an account| |max.domain.backups| Global| 40 | The default maximum number of backups that can be created for an domain| |max.domain.backup.storage| Global| 800 | The default maximum backup storage space (in GiB) that can be used for an domain| |max.project.backups| Global| 20 | The default maximum number of backups that can be created for an project| |max.project.backup.storage| Global| 400 | The default maximum backup storage space (in GiB) that can be used for an project| |Setting |Scope |Default Value |Description| |-------|--------|--------------|-----------| |max.account.buckets| Global| 20 | The default maximum number of buckets that can be created for an account| |max.account.object.storage| Global| 400 | The default maximum object storage space (in GiB) that can be used for an account| |max.domain.buckets| Global| 40 | The default maximum number of buckets that can be created for an domain| |max.domain.object.storage| Global| 800 | The default maximum object storage space (in GiB) that can be used for an domain| |max.project.buckets| Global| 20 | The default maximum number of buckets that can be created for an project| |max.project.object.storage| Global| 400 | The default maximum object storage space (in GiB) that can be used for an project| Co-authored-by: Daan Hoogland <daan@onecht.net> Co-authored-by: Lucas Martins <56271185+lucas-a-martins@users.noreply.github.com> Co-authored-by: Lucas Martins <lucas.martins@scclouds.com.br> Co-authored-by: Pearl Dsilva <pearl1594@gmail.com> Co-authored-by: Rohit Yadav <rohit.yadav@shapeblue.com>
1 parent 648170c commit a7beaaf

File tree

58 files changed

+2182
-270
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+2182
-270
lines changed

api/src/main/java/com/cloud/configuration/Resource.java

+10-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public interface Resource {
2121
short RESOURCE_UNLIMITED = -1;
2222
String UNLIMITED = "Unlimited";
2323

24-
enum ResourceType { // Primary and Secondary storage are allocated_storage and not the physical storage.
24+
enum ResourceType { // All storage type resources are allocated_storage and not the physical storage.
2525
user_vm("user_vm", 0),
2626
public_ip("public_ip", 1),
2727
volume("volume", 2),
@@ -33,7 +33,11 @@ enum ResourceType { // Primary and Secondary storage are allocated_storage and n
3333
cpu("cpu", 8),
3434
memory("memory", 9),
3535
primary_storage("primary_storage", 10),
36-
secondary_storage("secondary_storage", 11);
36+
secondary_storage("secondary_storage", 11),
37+
backup("backup", 12),
38+
backup_storage("backup_storage", 13),
39+
bucket("bucket", 14),
40+
object_storage("object_storage", 15);
3741

3842
private String name;
3943
private int ordinal;
@@ -62,6 +66,10 @@ public static ResourceType fromOrdinal(int ordinal) {
6266
}
6367
return null;
6468
}
69+
70+
public static Boolean isStorageType(ResourceType type) {
71+
return (type == primary_storage || type == secondary_storage || type == backup_storage || type == object_storage);
72+
}
6573
}
6674

6775
public static class ResourceOwnerType {

api/src/main/java/com/cloud/event/EventTypes.java

+3
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,9 @@ public class EventTypes {
785785
public static final String EVENT_SHAREDFS_EXPUNGE = "SHAREDFS.EXPUNGE";
786786
public static final String EVENT_SHAREDFS_RECOVER = "SHAREDFS.RECOVER";
787787

788+
// Resource Limit
789+
public static final String EVENT_RESOURCE_LIMIT_UPDATE = "RESOURCE.LIMIT.UPDATE";
790+
788791
static {
789792

790793
// TODO: need a way to force author adding event types to declare the entity details as well, with out braking

api/src/main/java/com/cloud/storage/VolumeApiService.java

+2
Original file line numberDiff line numberDiff line change
@@ -190,4 +190,6 @@ Volume updateVolume(long volumeId, String path, String state, Long storageId,
190190
boolean stateTransitTo(Volume vol, Volume.Event event) throws NoTransitionException;
191191

192192
Pair<String, String> checkAndRepairVolume(CheckAndRepairVolumeCmd cmd) throws ResourceAllocationException;
193+
194+
Long getVolumePhysicalSize(Storage.ImageFormat format, String path, String chainInfo);
193195
}

api/src/main/java/org/apache/cloudstack/api/ApiConstants.java

+18-3
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,15 @@ public class ApiConstants {
5151
public static final String AVAILABLE = "available";
5252
public static final String AVAILABLE_SUBNETS = "availablesubnets";
5353
public static final String AVAILABLE_VIRTUAL_MACHINE_COUNT = "availablevirtualmachinecount";
54+
public static final String BACKUP_AVAILABLE = "backupavailable";
5455
public static final String BACKUP_ID = "backupid";
56+
public static final String BACKUP_LIMIT = "backuplimit";
5557
public static final String BACKUP_OFFERING_NAME = "backupofferingname";
5658
public static final String BACKUP_OFFERING_ID = "backupofferingid";
59+
public static final String BACKUP_STORAGE_AVAILABLE = "backupstorageavailable";
60+
public static final String BACKUP_STORAGE_LIMIT = "backupstoragelimit";
61+
public static final String BACKUP_STORAGE_TOTAL = "backupstoragetotal";
62+
public static final String BACKUP_TOTAL = "backuptotal";
5763
public static final String BASE64_IMAGE = "base64image";
5864
public static final String BGP_PEERS = "bgppeers";
5965
public static final String BGP_PEER_IDS = "bgppeerids";
@@ -322,6 +328,7 @@ public class ApiConstants {
322328
public static final String MAC_ADDRESS = "macaddress";
323329
public static final String MAX = "max";
324330
public static final String MAX_SNAPS = "maxsnaps";
331+
public static final String MAX_BACKUPS = "maxbackups";
325332
public static final String MAX_CPU_NUMBER = "maxcpunumber";
326333
public static final String MAX_MEMORY = "maxmemory";
327334
public static final String MIN_CPU_NUMBER = "mincpunumber";
@@ -436,6 +443,7 @@ public class ApiConstants {
436443
public static final String QUALIFIERS = "qualifiers";
437444
public static final String QUERY_FILTER = "queryfilter";
438445
public static final String SCHEDULE = "schedule";
446+
public static final String SCHEDULE_ID = "scheduleid";
439447
public static final String SCOPE = "scope";
440448
public static final String SEARCH_BASE = "searchbase";
441449
public static final String SECONDARY_IP = "secondaryip";
@@ -1148,7 +1156,6 @@ public class ApiConstants {
11481156
public static final String MTU = "mtu";
11491157
public static final String AUTO_ENABLE_KVM_HOST = "autoenablekvmhost";
11501158
public static final String LIST_APIS = "listApis";
1151-
public static final String OBJECT_STORAGE_ID = "objectstorageid";
11521159
public static final String VERSIONING = "versioning";
11531160
public static final String OBJECT_LOCKING = "objectlocking";
11541161
public static final String ENCRYPTION = "encryption";
@@ -1162,7 +1169,6 @@ public class ApiConstants {
11621169
public static final String DISK_PATH = "diskpath";
11631170
public static final String IMPORT_SOURCE = "importsource";
11641171
public static final String TEMP_PATH = "temppath";
1165-
public static final String OBJECT_STORAGE = "objectstore";
11661172
public static final String HEURISTIC_RULE = "heuristicrule";
11671173
public static final String HEURISTIC_TYPE_VALID_OPTIONS = "Valid options are: ISO, SNAPSHOT, TEMPLATE and VOLUME.";
11681174
public static final String MANAGEMENT = "management";
@@ -1190,11 +1196,20 @@ public class ApiConstants {
11901196
public static final String SHAREDFSVM_MIN_CPU_COUNT = "sharedfsvmmincpucount";
11911197
public static final String SHAREDFSVM_MIN_RAM_SIZE = "sharedfsvmminramsize";
11921198

1199+
// Object Storage related
1200+
public static final String BUCKET_AVAILABLE = "bucketavailable";
1201+
public static final String BUCKET_LIMIT = "bucketlimit";
1202+
public static final String BUCKET_TOTAL = "buckettotal";
1203+
public static final String OBJECT_STORAGE_ID = "objectstorageid";
1204+
public static final String OBJECT_STORAGE = "objectstore";
1205+
public static final String OBJECT_STORAGE_AVAILABLE = "objectstorageavailable";
1206+
public static final String OBJECT_STORAGE_LIMIT = "objectstoragelimit";
1207+
public static final String OBJECT_STORAGE_TOTAL = "objectstoragetotal";
1208+
11931209
public static final String PARAMETER_DESCRIPTION_ACTIVATION_RULE = "Quota tariff's activation rule. It can receive a JS script that results in either " +
11941210
"a boolean or a numeric value: if it results in a boolean value, the tariff value will be applied according to the result; if it results in a numeric value, the " +
11951211
"numeric value will be applied; if the result is neither a boolean nor a numeric value, the tariff will not be applied. If the rule is not informed, the tariff " +
11961212
"value will be applied.";
1197-
11981213
public static final String PARAMETER_DESCRIPTION_START_DATE_POSSIBLE_FORMATS = "The recommended format is \"yyyy-MM-dd'T'HH:mm:ssZ\" (e.g.: \"2023-01-01T12:00:00+0100\"); " +
11991214
"however, the following formats are also accepted: \"yyyy-MM-dd HH:mm:ss\" (e.g.: \"2023-01-01 12:00:00\") and \"yyyy-MM-dd\" (e.g.: \"2023-01-01\" - if the time is not " +
12001215
"added, it will be interpreted as \"00:00:00\"). If the recommended format is not used, the date will be considered in the server timezone.";

api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateBackupCmd.java

+18-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import javax.inject.Inject;
2121

22+
import com.cloud.storage.Snapshot;
2223
import org.apache.cloudstack.acl.RoleType;
2324
import org.apache.cloudstack.api.APICommand;
2425
import org.apache.cloudstack.api.ApiCommandResourceType;
@@ -27,6 +28,7 @@
2728
import org.apache.cloudstack.api.BaseAsyncCreateCmd;
2829
import org.apache.cloudstack.api.Parameter;
2930
import org.apache.cloudstack.api.ServerApiException;
31+
import org.apache.cloudstack.api.response.BackupScheduleResponse;
3032
import org.apache.cloudstack.api.response.SuccessResponse;
3133
import org.apache.cloudstack.api.response.UserVmResponse;
3234
import org.apache.cloudstack.backup.BackupManager;
@@ -60,6 +62,13 @@ public class CreateBackupCmd extends BaseAsyncCreateCmd {
6062
description = "ID of the VM")
6163
private Long vmId;
6264

65+
@Parameter(name = ApiConstants.SCHEDULE_ID,
66+
type = CommandType.LONG,
67+
entityType = BackupScheduleResponse.class,
68+
description = "backup schedule ID of the VM, if this is null, it indicates that it is a manual backup.",
69+
since = "4.21.0")
70+
private Long scheduleId;
71+
6372
/////////////////////////////////////////////////////
6473
/////////////////// Accessors ///////////////////////
6574
/////////////////////////////////////////////////////
@@ -68,14 +77,22 @@ public Long getVmId() {
6877
return vmId;
6978
}
7079

80+
public Long getScheduleId() {
81+
if (scheduleId != null) {
82+
return scheduleId;
83+
} else {
84+
return Snapshot.MANUAL_POLICY_ID;
85+
}
86+
}
87+
7188
/////////////////////////////////////////////////////
7289
/////////////// API Implementation///////////////////
7390
/////////////////////////////////////////////////////
7491

7592
@Override
7693
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
7794
try {
78-
boolean result = backupManager.createBackup(getVmId());
95+
boolean result = backupManager.createBackup(getVmId(), getScheduleId());
7996
if (result) {
8097
SuccessResponse response = new SuccessResponse(getCommandName());
8198
response.setResponseName(getCommandName());

api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateBackupScheduleCmd.java

+10
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ public class CreateBackupScheduleCmd extends BaseCmd {
7575
description = "Specifies a timezone for this command. For more information on the timezone parameter, see TimeZone Format.")
7676
private String timezone;
7777

78+
@Parameter(name = ApiConstants.MAX_BACKUPS,
79+
type = CommandType.INTEGER,
80+
description = "maximum number of backups to retain",
81+
since = "4.21.0")
82+
private Integer maxBackups;
83+
7884
/////////////////////////////////////////////////////
7985
/////////////////// Accessors ///////////////////////
8086
/////////////////////////////////////////////////////
@@ -95,6 +101,10 @@ public String getTimezone() {
95101
return timezone;
96102
}
97103

104+
public Integer getMaxBackups() {
105+
return maxBackups;
106+
}
107+
98108
/////////////////////////////////////////////////////
99109
/////////////// API Implementation///////////////////
100110
/////////////////////////////////////////////////////

api/src/main/java/org/apache/cloudstack/api/command/user/bucket/CreateBucketCmd.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public class CreateBucketCmd extends BaseAsyncCreateCmd implements UserCmd {
7272
description = "Id of the Object Storage Pool where bucket is created")
7373
private long objectStoragePoolId;
7474

75-
@Parameter(name = ApiConstants.QUOTA, type = CommandType.INTEGER,description = "Bucket Quota in GB")
75+
@Parameter(name = ApiConstants.QUOTA, type = CommandType.INTEGER, required = true, description = "Bucket Quota in GiB")
7676
private Integer quota;
7777

7878
@Parameter(name = ApiConstants.ENCRYPTION, type = CommandType.BOOLEAN, description = "Enable bucket encryption")

api/src/main/java/org/apache/cloudstack/api/command/user/bucket/UpdateBucketCmd.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public class UpdateBucketCmd extends BaseCmd {
5656
@Parameter(name = ApiConstants.POLICY, type = CommandType.STRING, description = "Bucket Access Policy")
5757
private String policy;
5858

59-
@Parameter(name = ApiConstants.QUOTA, type = CommandType.INTEGER,description = "Bucket Quota in GB")
59+
@Parameter(name = ApiConstants.QUOTA, type = CommandType.INTEGER, description = "Bucket Quota in GiB")
6060
private Integer quota;
6161

6262
/////////////////////////////////////////////////////

api/src/main/java/org/apache/cloudstack/api/response/AccountResponse.java

+108
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,30 @@ public class AccountResponse extends BaseResponse implements ResourceLimitAndCou
127127
@Param(description = "the total number of snapshots available for this account")
128128
private String snapshotAvailable;
129129

130+
@SerializedName(ApiConstants.BACKUP_LIMIT)
131+
@Param(description = "the total number of backups which can be stored by this account", since = "4.21.0")
132+
private String backupLimit;
133+
134+
@SerializedName(ApiConstants.BACKUP_TOTAL)
135+
@Param(description = "the total number of backups stored by this account", since = "4.21.0")
136+
private Long backupTotal;
137+
138+
@SerializedName(ApiConstants.BACKUP_AVAILABLE)
139+
@Param(description = "the total number of backups available to this account", since = "4.21.0")
140+
private String backupAvailable;
141+
142+
@SerializedName(ApiConstants.BACKUP_STORAGE_LIMIT)
143+
@Param(description = "the total backup storage space (in GiB) the account can own", since = "4.21.0")
144+
private String backupStorageLimit;
145+
146+
@SerializedName(ApiConstants.BACKUP_STORAGE_TOTAL)
147+
@Param(description = "the total backup storage space (in GiB) owned by the account", since = "4.21.0")
148+
private Long backupStorageTotal;
149+
150+
@SerializedName(ApiConstants.BACKUP_STORAGE_AVAILABLE)
151+
@Param(description = "the total backup storage space (in GiB) available to the account", since = "4.21.0")
152+
private String backupStorageAvailable;
153+
130154
@SerializedName("templatelimit")
131155
@Param(description = "the total number of templates which can be created by this account")
132156
private String templateLimit;
@@ -231,6 +255,30 @@ public class AccountResponse extends BaseResponse implements ResourceLimitAndCou
231255
@Param(description = "the total secondary storage space (in GiB) available to be used for this account", since = "4.2.0")
232256
private String secondaryStorageAvailable;
233257

258+
@SerializedName(ApiConstants.BUCKET_LIMIT)
259+
@Param(description = "the total number of buckets which can be stored by this account", since = "4.21.0")
260+
private String bucketLimit;
261+
262+
@SerializedName(ApiConstants.BUCKET_TOTAL)
263+
@Param(description = "the total number of buckets stored by this account", since = "4.21.0")
264+
private Long bucketTotal;
265+
266+
@SerializedName(ApiConstants.BUCKET_AVAILABLE)
267+
@Param(description = "the total number of buckets available to this account", since = "4.21.0")
268+
private String bucketAvailable;
269+
270+
@SerializedName(ApiConstants.OBJECT_STORAGE_LIMIT)
271+
@Param(description = "the total object storage space (in GiB) the account can own", since = "4.21.0")
272+
private String objectStorageLimit;
273+
274+
@SerializedName(ApiConstants.OBJECT_STORAGE_TOTAL)
275+
@Param(description = "the total object storage space (in GiB) owned by the account", since = "4.21.0")
276+
private Long objectStorageTotal;
277+
278+
@SerializedName(ApiConstants.OBJECT_STORAGE_AVAILABLE)
279+
@Param(description = "the total object storage space (in GiB) available to the account", since = "4.21.0")
280+
private String objectStorageAvailable;
281+
234282
@SerializedName(ApiConstants.STATE)
235283
@Param(description = "the state of the account")
236284
private String state;
@@ -386,6 +434,36 @@ public void setSnapshotAvailable(String snapshotAvailable) {
386434
this.snapshotAvailable = snapshotAvailable;
387435
}
388436

437+
@Override
438+
public void setBackupLimit(String backupLimit) {
439+
this.backupLimit = backupLimit;
440+
}
441+
442+
@Override
443+
public void setBackupTotal(Long backupTotal) {
444+
this.backupTotal = backupTotal;
445+
}
446+
447+
@Override
448+
public void setBackupAvailable(String backupAvailable) {
449+
this.backupAvailable = backupAvailable;
450+
}
451+
452+
@Override
453+
public void setBackupStorageLimit(String backupStorageLimit) {
454+
this.backupStorageLimit = backupStorageLimit;
455+
}
456+
457+
@Override
458+
public void setBackupStorageTotal(Long backupStorageTotal) {
459+
this.backupStorageTotal = backupStorageTotal;
460+
}
461+
462+
@Override
463+
public void setBackupStorageAvailable(String backupStorageAvailable) {
464+
this.backupStorageAvailable = backupStorageAvailable;
465+
}
466+
389467
@Override
390468
public void setTemplateLimit(String templateLimit) {
391469
this.templateLimit = templateLimit;
@@ -537,6 +615,36 @@ public void setSecondaryStorageAvailable(String secondaryStorageAvailable) {
537615
this.secondaryStorageAvailable = secondaryStorageAvailable;
538616
}
539617

618+
@Override
619+
public void setBucketLimit(String bucketLimit) {
620+
this.bucketLimit = bucketLimit;
621+
}
622+
623+
@Override
624+
public void setBucketTotal(Long bucketTotal) {
625+
this.bucketTotal = bucketTotal;
626+
}
627+
628+
@Override
629+
public void setBucketAvailable(String bucketAvailable) {
630+
this.bucketAvailable = bucketAvailable;
631+
}
632+
633+
@Override
634+
public void setObjectStorageLimit(String objectStorageLimit) {
635+
this.objectStorageLimit = objectStorageLimit;
636+
}
637+
638+
@Override
639+
public void setObjectStorageTotal(Long objectStorageTotal) {
640+
this.objectStorageTotal = objectStorageTotal;
641+
}
642+
643+
@Override
644+
public void setObjectStorageAvailable(String objectStorageAvailable) {
645+
this.objectStorageAvailable = objectStorageAvailable;
646+
}
647+
540648
public void setDefaultZone(String defaultZoneId) {
541649
this.defaultZoneId = defaultZoneId;
542650
}

api/src/main/java/org/apache/cloudstack/api/response/BackupScheduleResponse.java

+11-3
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,22 @@ public class BackupScheduleResponse extends BaseResponse {
3737
@Param(description = "ID of the VM")
3838
private String vmId;
3939

40-
@SerializedName("schedule")
40+
@SerializedName(ApiConstants.SCHEDULE)
4141
@Param(description = "time the backup is scheduled to be taken.")
4242
private String schedule;
4343

44-
@SerializedName("intervaltype")
44+
@SerializedName(ApiConstants.INTERVAL_TYPE)
4545
@Param(description = "the interval type of the backup schedule")
4646
private DateUtil.IntervalType intervalType;
4747

48-
@SerializedName("timezone")
48+
@SerializedName(ApiConstants.TIMEZONE)
4949
@Param(description = "the time zone of the backup schedule")
5050
private String timezone;
5151

52+
@SerializedName(ApiConstants.MAX_BACKUPS)
53+
@Param(description = "maximum number of backups retained")
54+
private Integer maxBakups;
55+
5256
public String getVmName() {
5357
return vmName;
5458
}
@@ -88,4 +92,8 @@ public String getTimezone() {
8892
public void setTimezone(String timezone) {
8993
this.timezone = timezone;
9094
}
95+
96+
public void setMaxBakups(Integer maxBakups) {
97+
this.maxBakups = maxBakups;
98+
}
9199
}

api/src/main/java/org/apache/cloudstack/api/response/BucketResponse.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public class BucketResponse extends BaseResponseWithTagInformation implements Co
7575
private String state;
7676

7777
@SerializedName(ApiConstants.QUOTA)
78-
@Param(description = "Bucket Quota in GB")
78+
@Param(description = "Bucket Quota in GiB")
7979
private Integer quota;
8080

8181
@SerializedName(ApiConstants.ENCRYPTION)

0 commit comments

Comments
 (0)