Skip to content

Commit 8c1d749

Browse files
authored
[VMware] Enable unmanaging guest VMs (#4103)
* Enable unmanaging guest VMs * Minor fixes * Fix stop usage event only if VM is not stopped when unmanaging * Rename unmanaged VMs manager * Generate netofferingremove usage event if VM is not stopped * Generate usage event VM snapshot primary off when unmanaging
1 parent 3ede1ea commit 8c1d749

File tree

39 files changed

+1174
-40
lines changed

39 files changed

+1174
-40
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ public class EventTypes {
102102
public static final String EVENT_VM_RESTORE = "VM.RESTORE";
103103
public static final String EVENT_VM_EXPUNGE = "VM.EXPUNGE";
104104
public static final String EVENT_VM_IMPORT = "VM.IMPORT";
105+
public static final String EVENT_VM_UNMANAGE = "VM.UNMANAGE";
105106

106107
// Domain Router
107108
public static final String EVENT_ROUTER_CREATE = "ROUTER.CREATE";
@@ -624,6 +625,7 @@ public class EventTypes {
624625
entityEventDetails.put(EVENT_VM_RESTORE, VirtualMachine.class);
625626
entityEventDetails.put(EVENT_VM_EXPUNGE, VirtualMachine.class);
626627
entityEventDetails.put(EVENT_VM_IMPORT, VirtualMachine.class);
628+
entityEventDetails.put(EVENT_VM_UNMANAGE, VirtualMachine.class);
627629

628630
entityEventDetails.put(EVENT_ROUTER_CREATE, VirtualRouter.class);
629631
entityEventDetails.put(EVENT_ROUTER_DESTROY, VirtualRouter.class);

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,4 +517,9 @@ UserVm importVM(final DataCenter zone, final Host host, final VirtualMachineTemp
517517
final long accountId, final long userId, final ServiceOffering serviceOffering, final String sshPublicKey,
518518
final String hostName, final HypervisorType hypervisorType, final Map<String, String> customParameters, final VirtualMachine.PowerState powerState) throws InsufficientCapacityException;
519519

520+
/**
521+
* Unmanage a guest VM from CloudStack
522+
* @return true if the VM is successfully unmanaged, false if not.
523+
*/
524+
boolean unmanageUserVM(Long vmId);
520525
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ public static class Param {
6464
public static final Param BootMode = new Param("BootMode");
6565
public static final Param BootType = new Param("BootType");
6666
public static final Param BootIntoSetup = new Param("enterHardwareSetup");
67+
public static final Param PreserveNics = new Param("PreserveNics");
6768

6869
private String name;
6970

api/src/main/java/com/cloud/vm/snapshot/VMSnapshotService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,5 @@ UserVm revertToSnapshot(Long vmSnapshotId) throws InsufficientServerCapacityExce
5252
* the vm gets deleted on hypervisor (no need to delete each vm snapshot before deleting vm, just mark them as deleted on DB)
5353
* @param id vm id
5454
*/
55-
boolean deleteVMSnapshotsFromDB(Long vmId);
55+
boolean deleteVMSnapshotsFromDB(Long vmId, boolean unmanage);
5656
}

api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ImportUnmanagedInstanceCmd.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.apache.cloudstack.context.CallContext;
4040
import org.apache.cloudstack.vm.VmImportService;
4141
import org.apache.commons.collections.MapUtils;
42+
import org.apache.commons.lang.BooleanUtils;
4243
import org.apache.log4j.Logger;
4344

4445
import com.cloud.event.EventTypes;
@@ -152,6 +153,11 @@ public class ImportUnmanagedInstanceCmd extends BaseAsyncCmd {
152153
description = "vm and its volumes are allowed to migrate to different host/pool when offerings passed are incompatible with current host/pool")
153154
private Boolean migrateAllowed;
154155

156+
@Parameter(name = ApiConstants.FORCED,
157+
type = CommandType.BOOLEAN,
158+
description = "VM is imported despite some of its NIC's MAC addresses are already present")
159+
private Boolean forced;
160+
155161
/////////////////////////////////////////////////////
156162
/////////////////// Accessors ///////////////////////
157163
/////////////////////////////////////////////////////
@@ -268,6 +274,10 @@ public String getEventDescription() {
268274
return "Importing unmanaged VM";
269275
}
270276

277+
public boolean isForced() {
278+
return BooleanUtils.isTrue(forced);
279+
}
280+
271281
/////////////////////////////////////////////////////
272282
/////////////// API Implementation///////////////////
273283
/////////////////////////////////////////////////////
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
//
2+
// Licensed to the Apache Software Foundation (ASF) under one
3+
// or more contributor license agreements. See the NOTICE file
4+
// distributed with this work for additional information
5+
// regarding copyright ownership. The ASF licenses this file
6+
// to you under the Apache License, Version 2.0 (the
7+
// "License"); you may not use this file except in compliance
8+
// with the License. You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing,
13+
// software distributed under the License is distributed on an
14+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
// KIND, either express or implied. See the License for the
16+
// specific language governing permissions and limitations
17+
// under the License.
18+
//
19+
20+
package org.apache.cloudstack.api.command.admin.vm;
21+
22+
import com.cloud.event.EventTypes;
23+
import com.cloud.exception.ConcurrentOperationException;
24+
import com.cloud.exception.InsufficientCapacityException;
25+
import com.cloud.exception.NetworkRuleConflictException;
26+
import com.cloud.exception.ResourceAllocationException;
27+
import com.cloud.exception.ResourceUnavailableException;
28+
import com.cloud.user.Account;
29+
import com.cloud.uservm.UserVm;
30+
import com.cloud.vm.VirtualMachine;
31+
import org.apache.cloudstack.acl.RoleType;
32+
import org.apache.cloudstack.api.APICommand;
33+
import org.apache.cloudstack.api.ApiCommandJobType;
34+
import org.apache.cloudstack.api.ApiConstants;
35+
import org.apache.cloudstack.api.ApiErrorCode;
36+
import org.apache.cloudstack.api.BaseAsyncCmd;
37+
import org.apache.cloudstack.api.Parameter;
38+
import org.apache.cloudstack.api.ServerApiException;
39+
import org.apache.cloudstack.api.response.UnmanageVMInstanceResponse;
40+
import org.apache.cloudstack.api.response.UserVmResponse;
41+
import org.apache.cloudstack.context.CallContext;
42+
import org.apache.cloudstack.vm.UnmanagedVMsManager;
43+
import org.apache.log4j.Logger;
44+
45+
import javax.inject.Inject;
46+
47+
@APICommand(name = UnmanageVMInstanceCmd.API_NAME,
48+
description = "Unmanage a guest virtual machine.",
49+
entityType = {VirtualMachine.class},
50+
responseObject = UnmanageVMInstanceResponse.class,
51+
requestHasSensitiveInfo = false,
52+
authorized = {RoleType.Admin},
53+
since = "4.15.0")
54+
public class UnmanageVMInstanceCmd extends BaseAsyncCmd {
55+
56+
public static final Logger LOGGER = Logger.getLogger(UnmanageVMInstanceCmd.class);
57+
public static final String API_NAME = "unmanageVirtualMachine";
58+
59+
@Inject
60+
private UnmanagedVMsManager unmanagedVMsManager;
61+
62+
/////////////////////////////////////////////////////
63+
//////////////// API parameters /////////////////////
64+
/////////////////////////////////////////////////////
65+
66+
@Parameter(name = ApiConstants.ID, type = CommandType.UUID,
67+
entityType = UserVmResponse.class, required = true,
68+
description = "The ID of the virtual machine to unmanage")
69+
private Long vmId;
70+
71+
/////////////////////////////////////////////////////
72+
/////////////////// Accessors ///////////////////////
73+
/////////////////////////////////////////////////////
74+
75+
public Long getVmId() {
76+
return vmId;
77+
}
78+
79+
@Override
80+
public String getEventType() {
81+
return EventTypes.EVENT_VM_UNMANAGE;
82+
}
83+
84+
@Override
85+
public String getEventDescription() {
86+
return "unmanaging VM. VM ID = " + vmId;
87+
}
88+
89+
/////////////////////////////////////////////////////
90+
/////////////// API Implementation///////////////////
91+
/////////////////////////////////////////////////////
92+
93+
@Override
94+
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException,
95+
ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
96+
UnmanageVMInstanceResponse response = new UnmanageVMInstanceResponse();
97+
try {
98+
CallContext.current().setEventDetails("VM ID = " + vmId);
99+
boolean result = unmanagedVMsManager.unmanageVMInstance(vmId);
100+
response.setSuccess(result);
101+
if (result) {
102+
response.setDetails("VM unmanaged successfully");
103+
}
104+
} catch (Exception e) {
105+
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
106+
}
107+
response.setResponseName(getCommandName());
108+
response.setObjectName(getCommandName());
109+
this.setResponseObject(response);
110+
}
111+
112+
@Override
113+
public String getCommandName() {
114+
return API_NAME.toLowerCase() + BaseAsyncCmd.RESPONSE_SUFFIX;
115+
}
116+
117+
@Override
118+
public long getEntityOwnerId() {
119+
UserVm vm = _responseGenerator.findUserVmById(vmId);
120+
if (vm != null) {
121+
return vm.getAccountId();
122+
}
123+
return Account.ACCOUNT_ID_SYSTEM;
124+
}
125+
126+
@Override
127+
public ApiCommandJobType getInstanceType() {
128+
return ApiCommandJobType.VirtualMachine;
129+
}
130+
131+
@Override
132+
public Long getInstanceId() {
133+
return vmId;
134+
}
135+
136+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.apache.cloudstack.api.response;
19+
20+
import com.cloud.serializer.Param;
21+
import com.google.gson.annotations.SerializedName;
22+
import org.apache.cloudstack.api.ApiConstants;
23+
import org.apache.cloudstack.api.BaseResponse;
24+
25+
public class UnmanageVMInstanceResponse extends BaseResponse {
26+
27+
@SerializedName(ApiConstants.RESULT)
28+
@Param(description = "result of the unmanage VM operation")
29+
private boolean success;
30+
31+
@SerializedName(ApiConstants.DETAILS)
32+
@Param(description = "details of the unmanage VM operation")
33+
private String details;
34+
35+
public UnmanageVMInstanceResponse() {
36+
}
37+
38+
public UnmanageVMInstanceResponse(boolean success, String details) {
39+
this.success = success;
40+
this.details = details;
41+
}
42+
43+
public boolean isSuccess() {
44+
return success;
45+
}
46+
47+
public void setSuccess(boolean success) {
48+
this.success = success;
49+
}
50+
51+
public String getDetails() {
52+
return details;
53+
}
54+
55+
public void setDetails(String details) {
56+
this.details = details;
57+
}
58+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.apache.cloudstack.vm;
19+
20+
public interface UnmanageVMService {
21+
22+
/**
23+
* Unmanage a guest VM from CloudStack
24+
* @return true if the VM is successfully unmanaged, false if not.
25+
*/
26+
boolean unmanageVMInstance(long vmId);
27+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.apache.cloudstack.vm;
19+
20+
import com.cloud.utils.component.PluggableService;
21+
import org.apache.cloudstack.framework.config.ConfigKey;
22+
import org.apache.cloudstack.framework.config.Configurable;
23+
24+
public interface UnmanagedVMsManager extends VmImportService, UnmanageVMService, PluggableService, Configurable {
25+
26+
ConfigKey<Boolean> UnmanageVMPreserveNic = new ConfigKey<>("Advanced", Boolean.class, "unmanage.vm.preserve.nics", "false",
27+
"If set to true, do not remove VM nics (and its MAC addresses) when unmanaging a VM, leaving them allocated but not reserved. " +
28+
"If set to false, nics are removed and MAC addresses can be reassigned", true, ConfigKey.Scope.Zone);
29+
}

api/src/main/java/org/apache/cloudstack/vm/VmImportService.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,7 @@
2323
import org.apache.cloudstack.api.response.UnmanagedInstanceResponse;
2424
import org.apache.cloudstack.api.response.UserVmResponse;
2525

26-
import com.cloud.utils.component.PluggableService;
27-
28-
public interface VmImportService extends PluggableService {
26+
public interface VmImportService {
2927
ListResponse<UnmanagedInstanceResponse> listUnmanagedInstances(ListUnmanagedInstancesCmd cmd);
3028
UserVmResponse importUnmanagedInstance(ImportUnmanagedInstanceCmd cmd);
3129
}

0 commit comments

Comments
 (0)