Skip to content

Commit 7632814

Browse files
sudo87rohityadavcloudvishesh92
authored
Instance lease: Allow deployment of instances with lease duration and leaseexpiry action (#10560)
* FR-248: Instance lease, WIP commit * insert lease expiry into db and use that to filter exiring vms, add asyncjobmanager * Add leaseDuration and leaseExpiryAction in Service offering create flow * Update listVM cmd to allow listing only leased instances * Add methods to fetch instances for which lease is expiring in next days * Changes included: config key setup and configured for alert email lease options in create and update vm screen handle delete protection, edit vm, create vm validated stop and detroy, delete protection * Update UI screens for leased properties coming from config and service offering * use global lock before running scheduler * Unit tests * Flow changes done in UI based on discussion * Include view changes in schema upgrade files and use feature in various UI elements * Added integration test for vm deployment, UI enhancements for user persona, bug fixes * validate integration tests, minor ui changes and log messages * fix build: moving configkey from setup to test itself * Disable testAlert to unblock build and trim whitespaces in integration tests * Address review comments * Minor changes in EditVM screen * Use ExecutorService instead of Timer and TimerTask * Additional review comments * Incorporate following changes: 1. Execute lease action once on the instance 2. Cancel lease on instance when feature is disabled 3. Relevant events when lease gets disabled, cancelled, executed 4. Disable associating lease after deployment 5. UI elements and flow changes 6. Changes based on feedback from demo * Handle pr review comments * address review comments * move instance.lease.enabled config to VMLeaseManager interface * bug fix in edit instance flow and reject api request for invalid values * max allowed lease is for 100 years * log instance ids for expired instance * Fix config validation for value range and code coverage improvement * fix lease expiry request failures in async * dont use forced: true for StopVmCmd * Update server/src/main/java/org/apache/cloudstack/vm/lease/VMLeaseManager.java Co-authored-by: Vishesh <[email protected]> * handle review comments --------- Co-authored-by: Rohit Yadav <[email protected]> Co-authored-by: Vishesh <[email protected]>
1 parent 650b5ec commit 7632814

File tree

44 files changed

+2490
-148
lines changed

Some content is hidden

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

44 files changed

+2490
-148
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,8 @@ jobs:
164164
component/test_cpu_limits
165165
component/test_cpu_max_limits
166166
component/test_cpu_project_limits
167-
component/test_deploy_vm_userdata_multi_nic",
167+
component/test_deploy_vm_userdata_multi_nic
168+
component/test_deploy_vm_lease",
168169
"component/test_egress_fw_rules
169170
component/test_invalid_gw_nm
170171
component/test_ip_reservation",

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -796,6 +796,11 @@ public class EventTypes {
796796
// Resource Limit
797797
public static final String EVENT_RESOURCE_LIMIT_UPDATE = "RESOURCE.LIMIT.UPDATE";
798798

799+
public static final String VM_LEASE_EXPIRED = "VM.LEASE.EXPIRED";
800+
public static final String VM_LEASE_DISABLED = "VM.LEASE.DISABLED";
801+
public static final String VM_LEASE_CANCELLED = "VM.LEASE.CANCELLED";
802+
public static final String VM_LEASE_EXPIRING = "VM.LEASE.EXPIRING";
803+
799804
static {
800805

801806
// TODO: need a way to force author adding event types to declare the entity details as well, with out braking
@@ -1290,6 +1295,12 @@ public class EventTypes {
12901295
entityEventDetails.put(EVENT_SHAREDFS_DESTROY, SharedFS.class);
12911296
entityEventDetails.put(EVENT_SHAREDFS_EXPUNGE, SharedFS.class);
12921297
entityEventDetails.put(EVENT_SHAREDFS_RECOVER, SharedFS.class);
1298+
1299+
// VM Lease
1300+
entityEventDetails.put(VM_LEASE_EXPIRED, VirtualMachine.class);
1301+
entityEventDetails.put(VM_LEASE_EXPIRING, VirtualMachine.class);
1302+
entityEventDetails.put(VM_LEASE_DISABLED, VirtualMachine.class);
1303+
entityEventDetails.put(VM_LEASE_CANCELLED, VirtualMachine.class);
12931304
}
12941305

12951306
public static boolean isNetworkEvent(String eventType) {

api/src/main/java/com/cloud/vm/VmDetailConstants.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,4 +110,8 @@ public interface VmDetailConstants {
110110
// CPU mode and model, ADMIN only
111111
String GUEST_CPU_MODE = "guest.cpu.mode";
112112
String GUEST_CPU_MODEL = "guest.cpu.model";
113+
114+
String INSTANCE_LEASE_EXPIRY_DATE = "leaseexpirydate";
115+
String INSTANCE_LEASE_EXPIRY_ACTION = "leaseexpiryaction";
116+
String INSTANCE_LEASE_EXECUTION = "leaseactionexecution";
113117
}

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,10 @@ public class ApiConstants {
269269
public static final String INTERNAL_DNS2 = "internaldns2";
270270
public static final String INTERNET_PROTOCOL = "internetprotocol";
271271
public static final String INTERVAL_TYPE = "intervaltype";
272-
public static final String LOCATION_TYPE = "locationtype";
272+
public static final String INSTANCE_LEASE_DURATION = "leaseduration";
273+
public static final String INSTANCE_LEASE_ENABLED = "instanceleaseenabled";
274+
public static final String INSTANCE_LEASE_EXPIRY_ACTION = "leaseexpiryaction";
275+
public static final String INSTANCE_LEASE_EXPIRY_DATE= "leaseexpirydate";
273276
public static final String IOPS_READ_RATE = "iopsreadrate";
274277
public static final String IOPS_READ_RATE_MAX = "iopsreadratemax";
275278
public static final String IOPS_READ_RATE_MAX_LENGTH = "iopsreadratemaxlength";
@@ -317,11 +320,13 @@ public class ApiConstants {
317320
public static final String LAST_BOOT = "lastboottime";
318321
public static final String LAST_SERVER_START = "lastserverstart";
319322
public static final String LAST_SERVER_STOP = "lastserverstop";
323+
public static final String LEASED = "leased";
320324
public static final String LEVEL = "level";
321325
public static final String LENGTH = "length";
322326
public static final String LIMIT = "limit";
323327
public static final String LIMIT_CPU_USE = "limitcpuuse";
324328
public static final String LIST_HOSTS = "listhosts";
329+
public static final String LOCATION_TYPE = "locationtype";
325330
public static final String LOCK = "lock";
326331
public static final String LUN = "lun";
327332
public static final String LBID = "lbruleid";

api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@
3434
import org.apache.cloudstack.api.response.VsphereStoragePoliciesResponse;
3535
import org.apache.cloudstack.api.response.ZoneResponse;
3636
import org.apache.cloudstack.api.response.DiskOfferingResponse;
37+
import org.apache.cloudstack.vm.lease.VMLeaseManager;
3738
import org.apache.commons.collections.MapUtils;
39+
import org.apache.commons.lang3.EnumUtils;
3840
import org.apache.commons.lang3.StringUtils;
3941
import org.apache.commons.collections.CollectionUtils;
4042

@@ -251,7 +253,15 @@ public class CreateServiceOfferingCmd extends BaseCmd {
251253
since="4.20")
252254
private Boolean purgeResources;
253255

256+
@Parameter(name = ApiConstants.INSTANCE_LEASE_DURATION,
257+
type = CommandType.INTEGER,
258+
description = "Number of days instance is leased for.",
259+
since = "4.21.0")
260+
private Integer leaseDuration;
254261

262+
@Parameter(name = ApiConstants.INSTANCE_LEASE_EXPIRY_ACTION, type = CommandType.STRING, since = "4.21.0",
263+
description = "Lease expiry action, valid values are STOP and DESTROY")
264+
private String leaseExpiryAction;
255265

256266
/////////////////////////////////////////////////////
257267
/////////////////// Accessors ///////////////////////
@@ -487,6 +497,22 @@ public boolean getEncryptRoot() {
487497
return false;
488498
}
489499

500+
public VMLeaseManager.ExpiryAction getLeaseExpiryAction() {
501+
if (StringUtils.isBlank(leaseExpiryAction)) {
502+
return null;
503+
}
504+
VMLeaseManager.ExpiryAction action = EnumUtils.getEnumIgnoreCase(VMLeaseManager.ExpiryAction.class, leaseExpiryAction);
505+
if (action == null) {
506+
throw new InvalidParameterValueException("Invalid value configured for leaseexpiryaction, valid values are: " +
507+
com.cloud.utils.EnumUtils.listValues(VMLeaseManager.ExpiryAction.values()));
508+
}
509+
return action;
510+
}
511+
512+
public Integer getLeaseDuration() {
513+
return leaseDuration;
514+
}
515+
490516
public boolean isPurgeResources() {
491517
return Boolean.TRUE.equals(purgeResources);
492518
}

api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ public void execute() {
7272
response.setInstancesDisksStatsRetentionTime((Integer) capabilities.get(ApiConstants.INSTANCES_DISKS_STATS_RETENTION_TIME));
7373
response.setSharedFsVmMinCpuCount((Integer)capabilities.get(ApiConstants.SHAREDFSVM_MIN_CPU_COUNT));
7474
response.setSharedFsVmMinRamSize((Integer)capabilities.get(ApiConstants.SHAREDFSVM_MIN_RAM_SIZE));
75+
response.setInstanceLeaseEnabled((Boolean) capabilities.get(ApiConstants.INSTANCE_LEASE_ENABLED));
7576
response.setObjectName("capability");
7677
response.setResponseName(getCommandName());
7778
this.setResponseObject(response);

api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java

Lines changed: 53 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,24 @@
1616
// under the License.
1717
package org.apache.cloudstack.api.command.user.vm;
1818

19-
import java.util.ArrayList;
20-
import java.util.Arrays;
21-
import java.util.Collection;
22-
import java.util.HashMap;
23-
import java.util.Iterator;
24-
import java.util.LinkedHashMap;
25-
import java.util.List;
26-
import java.util.Map;
27-
28-
import javax.annotation.Nonnull;
29-
19+
import com.cloud.agent.api.LogLevel;
20+
import com.cloud.event.EventTypes;
21+
import com.cloud.exception.ConcurrentOperationException;
22+
import com.cloud.exception.InsufficientCapacityException;
23+
import com.cloud.exception.InsufficientServerCapacityException;
24+
import com.cloud.exception.InvalidParameterValueException;
25+
import com.cloud.exception.ResourceAllocationException;
26+
import com.cloud.exception.ResourceUnavailableException;
27+
import com.cloud.hypervisor.Hypervisor.HypervisorType;
28+
import com.cloud.network.Network;
29+
import com.cloud.network.Network.IpAddresses;
30+
import com.cloud.offering.DiskOffering;
31+
import com.cloud.template.VirtualMachineTemplate;
32+
import com.cloud.uservm.UserVm;
33+
import com.cloud.utils.net.Dhcp;
34+
import com.cloud.utils.net.NetUtils;
35+
import com.cloud.vm.VirtualMachine;
36+
import com.cloud.vm.VmDetailConstants;
3037
import org.apache.cloudstack.acl.RoleType;
3138
import org.apache.cloudstack.affinity.AffinityGroupResponse;
3239
import org.apache.cloudstack.api.ACL;
@@ -53,29 +60,22 @@
5360
import org.apache.cloudstack.api.response.UserVmResponse;
5461
import org.apache.cloudstack.api.response.ZoneResponse;
5562
import org.apache.cloudstack.context.CallContext;
63+
import org.apache.cloudstack.vm.lease.VMLeaseManager;
5664
import org.apache.commons.collections.CollectionUtils;
5765
import org.apache.commons.collections.MapUtils;
5866
import org.apache.commons.lang3.BooleanUtils;
67+
import org.apache.commons.lang3.EnumUtils;
5968
import org.apache.commons.lang3.StringUtils;
6069

61-
import com.cloud.agent.api.LogLevel;
62-
import com.cloud.event.EventTypes;
63-
import com.cloud.exception.ConcurrentOperationException;
64-
import com.cloud.exception.InsufficientCapacityException;
65-
import com.cloud.exception.InsufficientServerCapacityException;
66-
import com.cloud.exception.InvalidParameterValueException;
67-
import com.cloud.exception.ResourceAllocationException;
68-
import com.cloud.exception.ResourceUnavailableException;
69-
import com.cloud.hypervisor.Hypervisor.HypervisorType;
70-
import com.cloud.network.Network;
71-
import com.cloud.network.Network.IpAddresses;
72-
import com.cloud.offering.DiskOffering;
73-
import com.cloud.template.VirtualMachineTemplate;
74-
import com.cloud.uservm.UserVm;
75-
import com.cloud.utils.net.Dhcp;
76-
import com.cloud.utils.net.NetUtils;
77-
import com.cloud.vm.VirtualMachine;
78-
import com.cloud.vm.VmDetailConstants;
70+
import javax.annotation.Nonnull;
71+
import java.util.ArrayList;
72+
import java.util.Arrays;
73+
import java.util.Collection;
74+
import java.util.HashMap;
75+
import java.util.Iterator;
76+
import java.util.LinkedHashMap;
77+
import java.util.List;
78+
import java.util.Map;
7979

8080
@APICommand(name = "deployVirtualMachine", description = "Creates and automatically starts a virtual machine based on a service offering, disk offering, and template.", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class},
8181
requestHasSensitiveInfo = false, responseHasSensitiveInfo = true)
@@ -278,6 +278,14 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityG
278278
description = "Enable packed virtqueues or not.")
279279
private Boolean nicPackedVirtQueues;
280280

281+
@Parameter(name = ApiConstants.INSTANCE_LEASE_DURATION, type = CommandType.INTEGER, since = "4.21.0",
282+
description = "Number of days instance is leased for.")
283+
private Integer leaseDuration;
284+
285+
@Parameter(name = ApiConstants.INSTANCE_LEASE_EXPIRY_ACTION, type = CommandType.STRING, since = "4.21.0",
286+
description = "Lease expiry action, valid values are STOP and DESTROY")
287+
private String leaseExpiryAction;
288+
281289
/////////////////////////////////////////////////////
282290
/////////////////// Accessors ///////////////////////
283291
/////////////////////////////////////////////////////
@@ -475,6 +483,22 @@ public String getPassword() {
475483
return password;
476484
}
477485

486+
public Integer getLeaseDuration() {
487+
return leaseDuration;
488+
}
489+
490+
public VMLeaseManager.ExpiryAction getLeaseExpiryAction() {
491+
if (StringUtils.isBlank(leaseExpiryAction)) {
492+
return null;
493+
}
494+
VMLeaseManager.ExpiryAction action = EnumUtils.getEnumIgnoreCase(VMLeaseManager.ExpiryAction.class, leaseExpiryAction);
495+
if (action == null) {
496+
throw new InvalidParameterValueException("Invalid value configured for leaseexpiryaction, valid values are: " +
497+
com.cloud.utils.EnumUtils.listValues(VMLeaseManager.ExpiryAction.values()));
498+
}
499+
return action;
500+
}
501+
478502
public List<Long> getNetworkIds() {
479503
if (MapUtils.isNotEmpty(vAppNetworks)) {
480504
if (CollectionUtils.isNotEmpty(networkIds) || ipAddress != null || getIp6Address() != null || MapUtils.isNotEmpty(ipToNetworkList)) {

api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,11 @@ public class ListVMsCmd extends BaseListRetrieveOnlyResourceCountCmd implements
160160
since = "4.20.1")
161161
private String arch;
162162

163+
@Parameter(name = ApiConstants.LEASED, type = CommandType.BOOLEAN,
164+
description = "Whether to return only leased instances",
165+
since = "4.21.0")
166+
private Boolean onlyLeasedInstances = false;
167+
163168
/////////////////////////////////////////////////////
164169
/////////////////// Accessors ///////////////////////
165170
/////////////////////////////////////////////////////
@@ -303,6 +308,10 @@ public CPU.CPUArch getArch() {
303308
return StringUtils.isBlank(arch) ? null : CPU.CPUArch.fromType(arch);
304309
}
305310

311+
public boolean getOnlyLeasedInstances() {
312+
return BooleanUtils.toBoolean(onlyLeasedInstances);
313+
}
314+
306315
/////////////////////////////////////////////////////
307316
/////////////// API Implementation///////////////////
308317
/////////////////////////////////////////////////////

api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,19 @@
1616
// under the License.
1717
package org.apache.cloudstack.api.command.user.vm;
1818

19-
import java.util.Collection;
20-
import java.util.HashMap;
21-
import java.util.List;
22-
import java.util.Map;
23-
19+
import com.cloud.exception.InsufficientCapacityException;
20+
import com.cloud.exception.InvalidParameterValueException;
21+
import com.cloud.exception.ResourceUnavailableException;
22+
import com.cloud.user.Account;
23+
import com.cloud.uservm.UserVm;
2424
import com.cloud.utils.exception.CloudRuntimeException;
25-
26-
import org.apache.cloudstack.api.ApiArgValidator;
27-
import org.apache.cloudstack.api.response.UserDataResponse;
28-
25+
import com.cloud.utils.net.Dhcp;
26+
import com.cloud.vm.VirtualMachine;
2927
import org.apache.cloudstack.acl.RoleType;
3028
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
3129
import org.apache.cloudstack.api.ACL;
3230
import org.apache.cloudstack.api.APICommand;
31+
import org.apache.cloudstack.api.ApiArgValidator;
3332
import org.apache.cloudstack.api.ApiCommandResourceType;
3433
import org.apache.cloudstack.api.ApiConstants;
3534
import org.apache.cloudstack.api.ApiErrorCode;
@@ -40,15 +39,17 @@
4039
import org.apache.cloudstack.api.command.user.UserCmd;
4140
import org.apache.cloudstack.api.response.GuestOSResponse;
4241
import org.apache.cloudstack.api.response.SecurityGroupResponse;
42+
import org.apache.cloudstack.api.response.UserDataResponse;
4343
import org.apache.cloudstack.api.response.UserVmResponse;
4444
import org.apache.cloudstack.context.CallContext;
45+
import org.apache.cloudstack.vm.lease.VMLeaseManager;
46+
import org.apache.commons.lang3.EnumUtils;
47+
import org.apache.commons.lang3.StringUtils;
4548

46-
import com.cloud.exception.InsufficientCapacityException;
47-
import com.cloud.exception.ResourceUnavailableException;
48-
import com.cloud.user.Account;
49-
import com.cloud.uservm.UserVm;
50-
import com.cloud.utils.net.Dhcp;
51-
import com.cloud.vm.VirtualMachine;
49+
import java.util.Collection;
50+
import java.util.HashMap;
51+
import java.util.List;
52+
import java.util.Map;
5253

5354
@APICommand(name = "updateVirtualMachine", description="Updates properties of a virtual machine. The VM has to be stopped and restarted for the " +
5455
"new properties to take effect. UpdateVirtualMachine does not first check whether the VM is stopped. " +
@@ -154,6 +155,14 @@ public class UpdateVMCmd extends BaseCustomIdCmd implements SecurityGroupAction,
154155
" autoscaling groups or CKS, delete protection will be ignored.")
155156
private Boolean deleteProtection;
156157

158+
@Parameter(name = ApiConstants.INSTANCE_LEASE_DURATION, type = CommandType.INTEGER, since = "4.21.0",
159+
description = "Number of days to lease the instance from now onward. Use -1 to remove the existing lease")
160+
private Integer leaseDuration;
161+
162+
@Parameter(name = ApiConstants.INSTANCE_LEASE_EXPIRY_ACTION, type = CommandType.STRING, since = "4.21.0",
163+
description = "Lease expiry action, valid values are STOP and DESTROY")
164+
private String leaseExpiryAction;
165+
157166
/////////////////////////////////////////////////////
158167
/////////////////// Accessors ///////////////////////
159168
/////////////////////////////////////////////////////
@@ -324,4 +333,21 @@ public Long getApiResourceId() {
324333
public ApiCommandResourceType getApiResourceType() {
325334
return ApiCommandResourceType.VirtualMachine;
326335
}
336+
337+
public Integer getLeaseDuration() {
338+
return leaseDuration;
339+
}
340+
341+
public VMLeaseManager.ExpiryAction getLeaseExpiryAction() {
342+
if (StringUtils.isBlank(leaseExpiryAction)) {
343+
return null;
344+
}
345+
VMLeaseManager.ExpiryAction action = EnumUtils.getEnumIgnoreCase(VMLeaseManager.ExpiryAction.class, leaseExpiryAction);
346+
if (action == null) {
347+
throw new InvalidParameterValueException("Invalid value configured for leaseexpiryaction, valid values are: " +
348+
com.cloud.utils.EnumUtils.listValues(VMLeaseManager.ExpiryAction.values()));
349+
}
350+
return action;
351+
}
352+
327353
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ public class CapabilitiesResponse extends BaseResponse {
136136
@Param(description = "the min Ram size for the service offering used by the shared filesystem instance", since = "4.20.0")
137137
private Integer sharedFsVmMinRamSize;
138138

139+
@SerializedName(ApiConstants.INSTANCE_LEASE_ENABLED)
140+
@Param(description = "true if instance lease feature is enabled", since = "4.21.0")
141+
private Boolean instanceLeaseEnabled;
142+
139143
public void setSecurityGroupsEnabled(boolean securityGroupsEnabled) {
140144
this.securityGroupsEnabled = securityGroupsEnabled;
141145
}
@@ -247,4 +251,8 @@ public void setSharedFsVmMinCpuCount(Integer sharedFsVmMinCpuCount) {
247251
public void setSharedFsVmMinRamSize(Integer sharedFsVmMinRamSize) {
248252
this.sharedFsVmMinRamSize = sharedFsVmMinRamSize;
249253
}
254+
255+
public void setInstanceLeaseEnabled(Boolean instanceLeaseEnabled) {
256+
this.instanceLeaseEnabled = instanceLeaseEnabled;
257+
}
250258
}

0 commit comments

Comments
 (0)