mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
[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
This commit is contained in:
parent
3ede1eaa49
commit
8c1d749360
@ -102,6 +102,7 @@ public class EventTypes {
|
|||||||
public static final String EVENT_VM_RESTORE = "VM.RESTORE";
|
public static final String EVENT_VM_RESTORE = "VM.RESTORE";
|
||||||
public static final String EVENT_VM_EXPUNGE = "VM.EXPUNGE";
|
public static final String EVENT_VM_EXPUNGE = "VM.EXPUNGE";
|
||||||
public static final String EVENT_VM_IMPORT = "VM.IMPORT";
|
public static final String EVENT_VM_IMPORT = "VM.IMPORT";
|
||||||
|
public static final String EVENT_VM_UNMANAGE = "VM.UNMANAGE";
|
||||||
|
|
||||||
// Domain Router
|
// Domain Router
|
||||||
public static final String EVENT_ROUTER_CREATE = "ROUTER.CREATE";
|
public static final String EVENT_ROUTER_CREATE = "ROUTER.CREATE";
|
||||||
@ -624,6 +625,7 @@ public class EventTypes {
|
|||||||
entityEventDetails.put(EVENT_VM_RESTORE, VirtualMachine.class);
|
entityEventDetails.put(EVENT_VM_RESTORE, VirtualMachine.class);
|
||||||
entityEventDetails.put(EVENT_VM_EXPUNGE, VirtualMachine.class);
|
entityEventDetails.put(EVENT_VM_EXPUNGE, VirtualMachine.class);
|
||||||
entityEventDetails.put(EVENT_VM_IMPORT, VirtualMachine.class);
|
entityEventDetails.put(EVENT_VM_IMPORT, VirtualMachine.class);
|
||||||
|
entityEventDetails.put(EVENT_VM_UNMANAGE, VirtualMachine.class);
|
||||||
|
|
||||||
entityEventDetails.put(EVENT_ROUTER_CREATE, VirtualRouter.class);
|
entityEventDetails.put(EVENT_ROUTER_CREATE, VirtualRouter.class);
|
||||||
entityEventDetails.put(EVENT_ROUTER_DESTROY, VirtualRouter.class);
|
entityEventDetails.put(EVENT_ROUTER_DESTROY, VirtualRouter.class);
|
||||||
|
|||||||
@ -517,4 +517,9 @@ public interface UserVmService {
|
|||||||
final long accountId, final long userId, final ServiceOffering serviceOffering, final String sshPublicKey,
|
final long accountId, final long userId, final ServiceOffering serviceOffering, final String sshPublicKey,
|
||||||
final String hostName, final HypervisorType hypervisorType, final Map<String, String> customParameters, final VirtualMachine.PowerState powerState) throws InsufficientCapacityException;
|
final String hostName, final HypervisorType hypervisorType, final Map<String, String> customParameters, final VirtualMachine.PowerState powerState) throws InsufficientCapacityException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unmanage a guest VM from CloudStack
|
||||||
|
* @return true if the VM is successfully unmanaged, false if not.
|
||||||
|
*/
|
||||||
|
boolean unmanageUserVM(Long vmId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -64,6 +64,7 @@ public interface VirtualMachineProfile {
|
|||||||
public static final Param BootMode = new Param("BootMode");
|
public static final Param BootMode = new Param("BootMode");
|
||||||
public static final Param BootType = new Param("BootType");
|
public static final Param BootType = new Param("BootType");
|
||||||
public static final Param BootIntoSetup = new Param("enterHardwareSetup");
|
public static final Param BootIntoSetup = new Param("enterHardwareSetup");
|
||||||
|
public static final Param PreserveNics = new Param("PreserveNics");
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
|
|||||||
@ -52,5 +52,5 @@ public interface VMSnapshotService {
|
|||||||
* the vm gets deleted on hypervisor (no need to delete each vm snapshot before deleting vm, just mark them as deleted on DB)
|
* the vm gets deleted on hypervisor (no need to delete each vm snapshot before deleting vm, just mark them as deleted on DB)
|
||||||
* @param id vm id
|
* @param id vm id
|
||||||
*/
|
*/
|
||||||
boolean deleteVMSnapshotsFromDB(Long vmId);
|
boolean deleteVMSnapshotsFromDB(Long vmId, boolean unmanage);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,6 +39,7 @@ import org.apache.cloudstack.api.response.UserVmResponse;
|
|||||||
import org.apache.cloudstack.context.CallContext;
|
import org.apache.cloudstack.context.CallContext;
|
||||||
import org.apache.cloudstack.vm.VmImportService;
|
import org.apache.cloudstack.vm.VmImportService;
|
||||||
import org.apache.commons.collections.MapUtils;
|
import org.apache.commons.collections.MapUtils;
|
||||||
|
import org.apache.commons.lang.BooleanUtils;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
import com.cloud.event.EventTypes;
|
import com.cloud.event.EventTypes;
|
||||||
@ -152,6 +153,11 @@ public class ImportUnmanagedInstanceCmd extends BaseAsyncCmd {
|
|||||||
description = "vm and its volumes are allowed to migrate to different host/pool when offerings passed are incompatible with current host/pool")
|
description = "vm and its volumes are allowed to migrate to different host/pool when offerings passed are incompatible with current host/pool")
|
||||||
private Boolean migrateAllowed;
|
private Boolean migrateAllowed;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.FORCED,
|
||||||
|
type = CommandType.BOOLEAN,
|
||||||
|
description = "VM is imported despite some of its NIC's MAC addresses are already present")
|
||||||
|
private Boolean forced;
|
||||||
|
|
||||||
/////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////
|
||||||
/////////////////// Accessors ///////////////////////
|
/////////////////// Accessors ///////////////////////
|
||||||
/////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////
|
||||||
@ -268,6 +274,10 @@ public class ImportUnmanagedInstanceCmd extends BaseAsyncCmd {
|
|||||||
return "Importing unmanaged VM";
|
return "Importing unmanaged VM";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isForced() {
|
||||||
|
return BooleanUtils.isTrue(forced);
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////
|
||||||
/////////////// API Implementation///////////////////
|
/////////////// API Implementation///////////////////
|
||||||
/////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////
|
||||||
|
|||||||
@ -0,0 +1,136 @@
|
|||||||
|
//
|
||||||
|
// 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.admin.vm;
|
||||||
|
|
||||||
|
import com.cloud.event.EventTypes;
|
||||||
|
import com.cloud.exception.ConcurrentOperationException;
|
||||||
|
import com.cloud.exception.InsufficientCapacityException;
|
||||||
|
import com.cloud.exception.NetworkRuleConflictException;
|
||||||
|
import com.cloud.exception.ResourceAllocationException;
|
||||||
|
import com.cloud.exception.ResourceUnavailableException;
|
||||||
|
import com.cloud.user.Account;
|
||||||
|
import com.cloud.uservm.UserVm;
|
||||||
|
import com.cloud.vm.VirtualMachine;
|
||||||
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
|
import org.apache.cloudstack.api.APICommand;
|
||||||
|
import org.apache.cloudstack.api.ApiCommandJobType;
|
||||||
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
|
import org.apache.cloudstack.api.ApiErrorCode;
|
||||||
|
import org.apache.cloudstack.api.BaseAsyncCmd;
|
||||||
|
import org.apache.cloudstack.api.Parameter;
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.api.response.UnmanageVMInstanceResponse;
|
||||||
|
import org.apache.cloudstack.api.response.UserVmResponse;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.vm.UnmanagedVMsManager;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
@APICommand(name = UnmanageVMInstanceCmd.API_NAME,
|
||||||
|
description = "Unmanage a guest virtual machine.",
|
||||||
|
entityType = {VirtualMachine.class},
|
||||||
|
responseObject = UnmanageVMInstanceResponse.class,
|
||||||
|
requestHasSensitiveInfo = false,
|
||||||
|
authorized = {RoleType.Admin},
|
||||||
|
since = "4.15.0")
|
||||||
|
public class UnmanageVMInstanceCmd extends BaseAsyncCmd {
|
||||||
|
|
||||||
|
public static final Logger LOGGER = Logger.getLogger(UnmanageVMInstanceCmd.class);
|
||||||
|
public static final String API_NAME = "unmanageVirtualMachine";
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private UnmanagedVMsManager unmanagedVMsManager;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
//////////////// API parameters /////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.ID, type = CommandType.UUID,
|
||||||
|
entityType = UserVmResponse.class, required = true,
|
||||||
|
description = "The ID of the virtual machine to unmanage")
|
||||||
|
private Long vmId;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////////// Accessors ///////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public Long getVmId() {
|
||||||
|
return vmId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEventType() {
|
||||||
|
return EventTypes.EVENT_VM_UNMANAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEventDescription() {
|
||||||
|
return "unmanaging VM. VM ID = " + vmId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////// API Implementation///////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException,
|
||||||
|
ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
|
||||||
|
UnmanageVMInstanceResponse response = new UnmanageVMInstanceResponse();
|
||||||
|
try {
|
||||||
|
CallContext.current().setEventDetails("VM ID = " + vmId);
|
||||||
|
boolean result = unmanagedVMsManager.unmanageVMInstance(vmId);
|
||||||
|
response.setSuccess(result);
|
||||||
|
if (result) {
|
||||||
|
response.setDetails("VM unmanaged successfully");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
|
||||||
|
}
|
||||||
|
response.setResponseName(getCommandName());
|
||||||
|
response.setObjectName(getCommandName());
|
||||||
|
this.setResponseObject(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandName() {
|
||||||
|
return API_NAME.toLowerCase() + BaseAsyncCmd.RESPONSE_SUFFIX;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getEntityOwnerId() {
|
||||||
|
UserVm vm = _responseGenerator.findUserVmById(vmId);
|
||||||
|
if (vm != null) {
|
||||||
|
return vm.getAccountId();
|
||||||
|
}
|
||||||
|
return Account.ACCOUNT_ID_SYSTEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ApiCommandJobType getInstanceType() {
|
||||||
|
return ApiCommandJobType.VirtualMachine;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getInstanceId() {
|
||||||
|
return vmId;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,58 @@
|
|||||||
|
// 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.response;
|
||||||
|
|
||||||
|
import com.cloud.serializer.Param;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
|
import org.apache.cloudstack.api.BaseResponse;
|
||||||
|
|
||||||
|
public class UnmanageVMInstanceResponse extends BaseResponse {
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.RESULT)
|
||||||
|
@Param(description = "result of the unmanage VM operation")
|
||||||
|
private boolean success;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.DETAILS)
|
||||||
|
@Param(description = "details of the unmanage VM operation")
|
||||||
|
private String details;
|
||||||
|
|
||||||
|
public UnmanageVMInstanceResponse() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public UnmanageVMInstanceResponse(boolean success, String details) {
|
||||||
|
this.success = success;
|
||||||
|
this.details = details;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSuccess() {
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSuccess(boolean success) {
|
||||||
|
this.success = success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDetails() {
|
||||||
|
return details;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDetails(String details) {
|
||||||
|
this.details = details;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
// 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.vm;
|
||||||
|
|
||||||
|
public interface UnmanageVMService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unmanage a guest VM from CloudStack
|
||||||
|
* @return true if the VM is successfully unmanaged, false if not.
|
||||||
|
*/
|
||||||
|
boolean unmanageVMInstance(long vmId);
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
// 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.vm;
|
||||||
|
|
||||||
|
import com.cloud.utils.component.PluggableService;
|
||||||
|
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||||
|
import org.apache.cloudstack.framework.config.Configurable;
|
||||||
|
|
||||||
|
public interface UnmanagedVMsManager extends VmImportService, UnmanageVMService, PluggableService, Configurable {
|
||||||
|
|
||||||
|
ConfigKey<Boolean> UnmanageVMPreserveNic = new ConfigKey<>("Advanced", Boolean.class, "unmanage.vm.preserve.nics", "false",
|
||||||
|
"If set to true, do not remove VM nics (and its MAC addresses) when unmanaging a VM, leaving them allocated but not reserved. " +
|
||||||
|
"If set to false, nics are removed and MAC addresses can be reassigned", true, ConfigKey.Scope.Zone);
|
||||||
|
}
|
||||||
@ -23,9 +23,7 @@ import org.apache.cloudstack.api.response.ListResponse;
|
|||||||
import org.apache.cloudstack.api.response.UnmanagedInstanceResponse;
|
import org.apache.cloudstack.api.response.UnmanagedInstanceResponse;
|
||||||
import org.apache.cloudstack.api.response.UserVmResponse;
|
import org.apache.cloudstack.api.response.UserVmResponse;
|
||||||
|
|
||||||
import com.cloud.utils.component.PluggableService;
|
public interface VmImportService {
|
||||||
|
|
||||||
public interface VmImportService extends PluggableService {
|
|
||||||
ListResponse<UnmanagedInstanceResponse> listUnmanagedInstances(ListUnmanagedInstancesCmd cmd);
|
ListResponse<UnmanagedInstanceResponse> listUnmanagedInstances(ListUnmanagedInstancesCmd cmd);
|
||||||
UserVmResponse importUnmanagedInstance(ImportUnmanagedInstanceCmd cmd);
|
UserVmResponse importUnmanagedInstance(ImportUnmanagedInstanceCmd cmd);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,27 @@
|
|||||||
|
// 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 com.cloud.agent.api;
|
||||||
|
|
||||||
|
public class PrepareUnmanageVMInstanceAnswer extends Answer {
|
||||||
|
|
||||||
|
public PrepareUnmanageVMInstanceAnswer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrepareUnmanageVMInstanceAnswer(PrepareUnmanageVMInstanceCommand cmd, boolean result, String details) {
|
||||||
|
super(cmd, result, details);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
// 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 com.cloud.agent.api;
|
||||||
|
|
||||||
|
public class PrepareUnmanageVMInstanceCommand extends Command {
|
||||||
|
|
||||||
|
private String instanceName;
|
||||||
|
|
||||||
|
public PrepareUnmanageVMInstanceCommand() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getInstanceName() {
|
||||||
|
return instanceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInstanceName(String instanceName) {
|
||||||
|
this.instanceName = instanceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean executeInSequence() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -58,4 +58,6 @@ public interface VirtualMachineGuru {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
void prepareStop(VirtualMachineProfile profile);
|
void prepareStop(VirtualMachineProfile profile);
|
||||||
|
|
||||||
|
void finalizeUnmanage(VirtualMachine vm);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -213,4 +213,11 @@ public interface VirtualMachineManager extends Manager {
|
|||||||
void migrateForScale(String vmUuid, long srcHostId, DeployDestination dest, Long newSvcOfferingId) throws ResourceUnavailableException, ConcurrentOperationException;
|
void migrateForScale(String vmUuid, long srcHostId, DeployDestination dest, Long newSvcOfferingId) throws ResourceUnavailableException, ConcurrentOperationException;
|
||||||
|
|
||||||
boolean getExecuteInSequence(HypervisorType hypervisorType);
|
boolean getExecuteInSequence(HypervisorType hypervisorType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unmanage a VM from CloudStack:
|
||||||
|
* - Remove the references of the VM and its volumes, nics, IPs from database
|
||||||
|
* - Keep the VM as it is on the hypervisor
|
||||||
|
*/
|
||||||
|
boolean unmanage(String vmUuid);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -317,5 +317,7 @@ public interface NetworkOrchestrationService {
|
|||||||
*/
|
*/
|
||||||
void cleanupNicDhcpDnsEntry(Network network, VirtualMachineProfile vmProfile, NicProfile nicProfile);
|
void cleanupNicDhcpDnsEntry(Network network, VirtualMachineProfile vmProfile, NicProfile nicProfile);
|
||||||
|
|
||||||
Pair<NicProfile, Integer> importNic(final String macAddress, int deviceId, final Network network, final Boolean isDefaultNic, final VirtualMachine vm, final Network.IpAddresses ipAddresses) throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException;
|
Pair<NicProfile, Integer> importNic(final String macAddress, int deviceId, final Network network, final Boolean isDefaultNic, final VirtualMachine vm, final Network.IpAddresses ipAddresses, boolean forced) throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException;
|
||||||
|
|
||||||
|
void unmanageNics(VirtualMachineProfile vm);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -149,4 +149,9 @@ public interface VolumeOrchestrationService {
|
|||||||
*/
|
*/
|
||||||
DiskProfile importVolume(Type type, String name, DiskOffering offering, Long size, Long minIops, Long maxIops, VirtualMachine vm, VirtualMachineTemplate template,
|
DiskProfile importVolume(Type type, String name, DiskOffering offering, Long size, Long minIops, Long maxIops, VirtualMachine vm, VirtualMachineTemplate template,
|
||||||
Account owner, Long deviceId, Long poolId, String path, String chainInfo);
|
Account owner, Long deviceId, Long poolId, String path, String chainInfo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unmanage VM volumes
|
||||||
|
*/
|
||||||
|
void unmanageVolumes(long vmId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,5 +35,5 @@ public interface VMSnapshotStrategy {
|
|||||||
* @param vmSnapshot vm snapshot to be marked as deleted.
|
* @param vmSnapshot vm snapshot to be marked as deleted.
|
||||||
* @return true if vm snapshot removed from DB, false if not.
|
* @return true if vm snapshot removed from DB, false if not.
|
||||||
*/
|
*/
|
||||||
boolean deleteVMSnapshotFromDB(VMSnapshot vmSnapshot);
|
boolean deleteVMSnapshotFromDB(VMSnapshot vmSnapshot, boolean unmanage);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -93,4 +93,6 @@ public interface VolumeService {
|
|||||||
SnapshotInfo takeSnapshot(VolumeInfo volume);
|
SnapshotInfo takeSnapshot(VolumeInfo volume);
|
||||||
|
|
||||||
VolumeInfo updateHypervisorSnapshotReserveForVolume(DiskOffering diskOffering, long volumeId, HypervisorType hyperType);
|
VolumeInfo updateHypervisorSnapshotReserveForVolume(DiskOffering diskOffering, long volumeId, HypervisorType hyperType);
|
||||||
|
|
||||||
|
void unmanageVolume(long volumeId);
|
||||||
}
|
}
|
||||||
@ -74,8 +74,10 @@ import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
|||||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||||
import org.apache.cloudstack.storage.to.VolumeObjectTO;
|
import org.apache.cloudstack.storage.to.VolumeObjectTO;
|
||||||
import org.apache.cloudstack.utils.identity.ManagementServerNode;
|
import org.apache.cloudstack.utils.identity.ManagementServerNode;
|
||||||
|
import org.apache.cloudstack.vm.UnmanagedVMsManager;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.commons.collections.MapUtils;
|
import org.apache.commons.collections.MapUtils;
|
||||||
|
import org.apache.commons.lang.BooleanUtils;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
import com.cloud.agent.AgentManager;
|
import com.cloud.agent.AgentManager;
|
||||||
@ -212,6 +214,7 @@ import com.cloud.utils.db.DB;
|
|||||||
import com.cloud.utils.db.EntityManager;
|
import com.cloud.utils.db.EntityManager;
|
||||||
import com.cloud.utils.db.GlobalLock;
|
import com.cloud.utils.db.GlobalLock;
|
||||||
import com.cloud.utils.db.Transaction;
|
import com.cloud.utils.db.Transaction;
|
||||||
|
import com.cloud.utils.db.TransactionCallback;
|
||||||
import com.cloud.utils.db.TransactionCallbackWithException;
|
import com.cloud.utils.db.TransactionCallbackWithException;
|
||||||
import com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn;
|
import com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn;
|
||||||
import com.cloud.utils.db.TransactionLegacy;
|
import com.cloud.utils.db.TransactionLegacy;
|
||||||
@ -1488,6 +1491,88 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean unmanage(String vmUuid) {
|
||||||
|
VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
|
||||||
|
if (vm == null || vm.getRemoved() != null) {
|
||||||
|
throw new CloudRuntimeException("Could not find VM with id = " + vmUuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<VmWorkJobVO> pendingWorkJobs = _workJobDao.listPendingWorkJobs(VirtualMachine.Type.Instance, vm.getId());
|
||||||
|
if (CollectionUtils.isNotEmpty(pendingWorkJobs) || _haMgr.hasPendingHaWork(vm.getId())) {
|
||||||
|
String msg = "There are pending jobs or HA tasks working on the VM with id: " + vm.getId() + ", can't unmanage the VM.";
|
||||||
|
s_logger.info(msg);
|
||||||
|
throw new ConcurrentOperationException(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
Boolean result = Transaction.execute(new TransactionCallback<Boolean>() {
|
||||||
|
@Override
|
||||||
|
public Boolean doInTransaction(TransactionStatus status) {
|
||||||
|
|
||||||
|
if (s_logger.isDebugEnabled()) {
|
||||||
|
s_logger.debug("Unmanaging vm " + vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
final VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm);
|
||||||
|
final HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vm.getHypervisorType());
|
||||||
|
final VirtualMachineGuru guru = getVmGuru(vm);
|
||||||
|
|
||||||
|
try {
|
||||||
|
unmanageVMSnapshots(vm);
|
||||||
|
unmanageVMNics(profile, vm);
|
||||||
|
unmanageVMVolumes(vm);
|
||||||
|
|
||||||
|
guru.finalizeUnmanage(vm);
|
||||||
|
} catch (Exception e) {
|
||||||
|
s_logger.error("Error while unmanaging VM " + vm, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return BooleanUtils.isTrue(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean up VM snapshots (if any) from DB
|
||||||
|
*/
|
||||||
|
private void unmanageVMSnapshots(VMInstanceVO vm) {
|
||||||
|
_vmSnapshotMgr.deleteVMSnapshotsFromDB(vm.getId(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean up volumes for a VM to be unmanaged from CloudStack
|
||||||
|
*/
|
||||||
|
private void unmanageVMVolumes(VMInstanceVO vm) {
|
||||||
|
final Long hostId = vm.getHostId() != null ? vm.getHostId() : vm.getLastHostId();
|
||||||
|
if (hostId != null) {
|
||||||
|
volumeMgr.revokeAccess(vm.getId(), hostId);
|
||||||
|
}
|
||||||
|
volumeMgr.unmanageVolumes(vm.getId());
|
||||||
|
|
||||||
|
List<Map<String, String>> targets = getTargets(hostId, vm.getId());
|
||||||
|
if (hostId != null && CollectionUtils.isNotEmpty(targets)) {
|
||||||
|
removeDynamicTargets(hostId, targets);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean up NICs for a VM to be unmanaged from CloudStack:
|
||||||
|
* - If 'unmanage.vm.preserve.nics' = true: then the NICs are not removed but still Allocated, to preserve MAC addresses
|
||||||
|
* - If 'unmanage.vm.preserve.nics' = false: then the NICs are removed while unmanaging
|
||||||
|
*/
|
||||||
|
private void unmanageVMNics(VirtualMachineProfile profile, VMInstanceVO vm) {
|
||||||
|
s_logger.debug("Cleaning up NICs");
|
||||||
|
Boolean preserveNics = UnmanagedVMsManager.UnmanageVMPreserveNic.valueIn(vm.getDataCenterId());
|
||||||
|
if (BooleanUtils.isTrue(preserveNics)) {
|
||||||
|
s_logger.debug("Preserve NICs configuration enabled");
|
||||||
|
profile.setParameter(VirtualMachineProfile.Param.PreserveNics, true);
|
||||||
|
}
|
||||||
|
_networkMgr.unmanageNics(profile);
|
||||||
|
}
|
||||||
|
|
||||||
private List<Map<String, String>> getVolumesToDisconnect(VirtualMachine vm) {
|
private List<Map<String, String>> getVolumesToDisconnect(VirtualMachine vm) {
|
||||||
List<Map<String, String>> volumesToDisconnect = new ArrayList<>();
|
List<Map<String, String>> volumesToDisconnect = new ArrayList<>();
|
||||||
|
|
||||||
@ -1978,7 +2063,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (expunge) {
|
if (expunge) {
|
||||||
_vmSnapshotMgr.deleteVMSnapshotsFromDB(vm.getId());
|
_vmSnapshotMgr.deleteVMSnapshotsFromDB(vm.getId(), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
@ -38,6 +39,8 @@ import java.util.stream.Collectors;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.naming.ConfigurationException;
|
import javax.naming.ConfigurationException;
|
||||||
|
|
||||||
|
import com.cloud.event.EventTypes;
|
||||||
|
import com.cloud.event.UsageEventUtils;
|
||||||
import com.cloud.network.dao.NetworkDetailVO;
|
import com.cloud.network.dao.NetworkDetailVO;
|
||||||
import com.cloud.network.dao.NetworkDetailsDao;
|
import com.cloud.network.dao.NetworkDetailsDao;
|
||||||
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
|
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
|
||||||
@ -53,6 +56,7 @@ import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
|||||||
import org.apache.cloudstack.framework.messagebus.MessageBus;
|
import org.apache.cloudstack.framework.messagebus.MessageBus;
|
||||||
import org.apache.cloudstack.framework.messagebus.PublishScope;
|
import org.apache.cloudstack.framework.messagebus.PublishScope;
|
||||||
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
|
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
|
||||||
|
import org.apache.commons.lang.BooleanUtils;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
import com.cloud.agent.AgentManager;
|
import com.cloud.agent.AgentManager;
|
||||||
@ -2057,8 +2061,12 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nic.setState(Nic.State.Deallocating);
|
Boolean preserveNics = (Boolean) vm.getParameter(VirtualMachineProfile.Param.PreserveNics);
|
||||||
_nicDao.update(nic.getId(), nic);
|
if (BooleanUtils.isNotTrue(preserveNics)) {
|
||||||
|
nic.setState(Nic.State.Deallocating);
|
||||||
|
_nicDao.update(nic.getId(), nic);
|
||||||
|
}
|
||||||
|
|
||||||
final NicProfile profile = new NicProfile(nic, network, null, null, null, _networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(
|
final NicProfile profile = new NicProfile(nic, network, null, null, null, _networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(
|
||||||
vm.getHypervisorType(), network));
|
vm.getHypervisorType(), network));
|
||||||
|
|
||||||
@ -2113,7 +2121,9 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||||||
|
|
||||||
final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, network.getGuruName());
|
final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, network.getGuruName());
|
||||||
guru.deallocate(network, profile, vm);
|
guru.deallocate(network, profile, vm);
|
||||||
_nicDao.remove(nic.getId());
|
if (BooleanUtils.isNotTrue(preserveNics)) {
|
||||||
|
_nicDao.remove(nic.getId());
|
||||||
|
}
|
||||||
|
|
||||||
s_logger.debug("Removed nic id=" + nic.getId());
|
s_logger.debug("Removed nic id=" + nic.getId());
|
||||||
//remove the secondary ip addresses corresponding to to this nic
|
//remove the secondary ip addresses corresponding to to this nic
|
||||||
@ -4002,7 +4012,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||||||
|
|
||||||
@DB
|
@DB
|
||||||
@Override
|
@Override
|
||||||
public Pair<NicProfile, Integer> importNic(final String macAddress, int deviceId, final Network network, final Boolean isDefaultNic, final VirtualMachine vm, final Network.IpAddresses ipAddresses)
|
public Pair<NicProfile, Integer> importNic(final String macAddress, int deviceId, final Network network, final Boolean isDefaultNic, final VirtualMachine vm, final Network.IpAddresses ipAddresses, final boolean forced)
|
||||||
throws ConcurrentOperationException, InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException {
|
throws ConcurrentOperationException, InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException {
|
||||||
s_logger.debug("Allocating nic for vm " + vm.getUuid() + " in network " + network + " during import");
|
s_logger.debug("Allocating nic for vm " + vm.getUuid() + " in network " + network + " during import");
|
||||||
String guestIp = null;
|
String guestIp = null;
|
||||||
@ -4024,6 +4034,17 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||||||
final NicVO vo = Transaction.execute(new TransactionCallback<NicVO>() {
|
final NicVO vo = Transaction.execute(new TransactionCallback<NicVO>() {
|
||||||
@Override
|
@Override
|
||||||
public NicVO doInTransaction(TransactionStatus status) {
|
public NicVO doInTransaction(TransactionStatus status) {
|
||||||
|
NicVO existingNic = _nicDao.findByNetworkIdAndMacAddress(network.getId(), macAddress);
|
||||||
|
if (existingNic != null) {
|
||||||
|
if (!forced) {
|
||||||
|
throw new CloudRuntimeException("NIC with MAC address = " + macAddress + " exists on network with ID = " + network.getId() +
|
||||||
|
" and forced flag is disabled");
|
||||||
|
}
|
||||||
|
s_logger.debug("Removing existing NIC with MAC address = " + macAddress + " on network with ID = " + network.getId());
|
||||||
|
existingNic.setState(Nic.State.Deallocating);
|
||||||
|
existingNic.setRemoved(new Date());
|
||||||
|
_nicDao.update(existingNic.getId(), existingNic);
|
||||||
|
}
|
||||||
NicVO vo = new NicVO(network.getGuruName(), vm.getId(), network.getId(), vm.getType());
|
NicVO vo = new NicVO(network.getGuruName(), vm.getId(), network.getId(), vm.getType());
|
||||||
vo.setMacAddress(macAddress);
|
vo.setMacAddress(macAddress);
|
||||||
vo.setAddressFormat(Networks.AddressFormat.Ip4);
|
vo.setAddressFormat(Networks.AddressFormat.Ip4);
|
||||||
@ -4065,6 +4086,24 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||||||
return new Pair<NicProfile, Integer>(vmNic, Integer.valueOf(deviceId));
|
return new Pair<NicProfile, Integer>(vmNic, Integer.valueOf(deviceId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unmanageNics(VirtualMachineProfile vm) {
|
||||||
|
if (s_logger.isDebugEnabled()) {
|
||||||
|
s_logger.debug("Unmanaging NICs for VM: " + vm.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualMachine virtualMachine = vm.getVirtualMachine();
|
||||||
|
final List<NicVO> nics = _nicDao.listByVmId(vm.getId());
|
||||||
|
for (final NicVO nic : nics) {
|
||||||
|
removeNic(vm, nic);
|
||||||
|
NetworkVO network = _networksDao.findById(nic.getNetworkId());
|
||||||
|
if (virtualMachine.getState() != VirtualMachine.State.Stopped) {
|
||||||
|
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_REMOVE, virtualMachine.getAccountId(), virtualMachine.getDataCenterId(), virtualMachine.getId(),
|
||||||
|
Long.toString(nic.getId()), network.getNetworkOfferingId(), null, 0L, virtualMachine.getClass().getName(), virtualMachine.getUuid(), virtualMachine.isDisplay());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getConfigComponentName() {
|
public String getConfigComponentName() {
|
||||||
return NetworkOrchestrationService.class.getSimpleName();
|
return NetworkOrchestrationService.class.getSimpleName();
|
||||||
|
|||||||
@ -1680,4 +1680,27 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
|
|||||||
vol = _volsDao.persist(vol);
|
vol = _volsDao.persist(vol);
|
||||||
return toDiskProfile(vol, offering);
|
return toDiskProfile(vol, offering);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unmanageVolumes(long vmId) {
|
||||||
|
if (s_logger.isDebugEnabled()) {
|
||||||
|
s_logger.debug("Unmanaging storage for vm: " + vmId);
|
||||||
|
}
|
||||||
|
final List<VolumeVO> volumesForVm = _volsDao.findByInstance(vmId);
|
||||||
|
|
||||||
|
Transaction.execute(new TransactionCallbackNoReturn() {
|
||||||
|
@Override
|
||||||
|
public void doInTransactionWithoutResult(TransactionStatus status) {
|
||||||
|
for (VolumeVO vol : volumesForVm) {
|
||||||
|
boolean volumeAlreadyDestroyed = (vol.getState() == Volume.State.Destroy || vol.getState() == Volume.State.Expunged
|
||||||
|
|| vol.getState() == Volume.State.Expunging);
|
||||||
|
if (volumeAlreadyDestroyed) {
|
||||||
|
s_logger.debug("Skipping destroy for the volume " + vol + " as its in state " + vol.getState().toString());
|
||||||
|
} else {
|
||||||
|
volService.unmanageVolume(vol.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -463,4 +463,24 @@ public class NetworkOrchestratorTest extends TestCase {
|
|||||||
testOrchastrator.validateLockedRequestedIp(ipVoSpy, lockedIp);
|
testOrchastrator.validateLockedRequestedIp(ipVoSpy, lockedIp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDontReleaseNicWhenPreserveNicsSettingEnabled() {
|
||||||
|
VirtualMachineProfile vm = mock(VirtualMachineProfile.class);
|
||||||
|
NicVO nic = mock(NicVO.class);
|
||||||
|
NetworkVO network = mock(NetworkVO.class);
|
||||||
|
|
||||||
|
when(vm.getType()).thenReturn(Type.User);
|
||||||
|
when(network.getGuruName()).thenReturn(guruName);
|
||||||
|
when(testOrchastrator._networksDao.findById(nic.getNetworkId())).thenReturn(network);
|
||||||
|
|
||||||
|
Long nicId = 1L;
|
||||||
|
when(nic.getId()).thenReturn(nicId);
|
||||||
|
when(vm.getParameter(VirtualMachineProfile.Param.PreserveNics)).thenReturn(true);
|
||||||
|
|
||||||
|
testOrchastrator.removeNic(vm, nic);
|
||||||
|
|
||||||
|
verify(nic, never()).setState(Nic.State.Deallocating);
|
||||||
|
verify(testOrchastrator._nicDao, never()).remove(nicId);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -426,7 +426,7 @@ public class DefaultVMSnapshotStrategy extends ManagerBase implements VMSnapshot
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean deleteVMSnapshotFromDB(VMSnapshot vmSnapshot) {
|
public boolean deleteVMSnapshotFromDB(VMSnapshot vmSnapshot, boolean unmanage) {
|
||||||
try {
|
try {
|
||||||
vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.ExpungeRequested);
|
vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.ExpungeRequested);
|
||||||
} catch (NoTransitionException e) {
|
} catch (NoTransitionException e) {
|
||||||
@ -435,9 +435,14 @@ public class DefaultVMSnapshotStrategy extends ManagerBase implements VMSnapshot
|
|||||||
}
|
}
|
||||||
UserVm userVm = userVmDao.findById(vmSnapshot.getVmId());
|
UserVm userVm = userVmDao.findById(vmSnapshot.getVmId());
|
||||||
List<VolumeObjectTO> volumeTOs = vmSnapshotHelper.getVolumeTOList(userVm.getId());
|
List<VolumeObjectTO> volumeTOs = vmSnapshotHelper.getVolumeTOList(userVm.getId());
|
||||||
|
long full_chain_size = 0;
|
||||||
for (VolumeObjectTO volumeTo: volumeTOs) {
|
for (VolumeObjectTO volumeTo: volumeTOs) {
|
||||||
volumeTo.setSize(0);
|
volumeTo.setSize(0);
|
||||||
publishUsageEvent(EventTypes.EVENT_VM_SNAPSHOT_DELETE, vmSnapshot, userVm, volumeTo);
|
publishUsageEvent(EventTypes.EVENT_VM_SNAPSHOT_DELETE, vmSnapshot, userVm, volumeTo);
|
||||||
|
full_chain_size += volumeTo.getSize();
|
||||||
|
}
|
||||||
|
if (unmanage) {
|
||||||
|
publishUsageEvent(EventTypes.EVENT_VM_SNAPSHOT_OFF_PRIMARY, vmSnapshot, userVm, full_chain_size, 0L);
|
||||||
}
|
}
|
||||||
return vmSnapshotDao.remove(vmSnapshot.getId());
|
return vmSnapshotDao.remove(vmSnapshot.getId());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2108,4 +2108,21 @@ public class VolumeServiceImpl implements VolumeService {
|
|||||||
|
|
||||||
return volFactory.getVolume(volumeId);
|
return volFactory.getVolume(volumeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DB
|
||||||
|
@Override
|
||||||
|
/**
|
||||||
|
* The volume must be marked as expunged on DB to exclude it from the storage cleanup task
|
||||||
|
*/
|
||||||
|
public void unmanageVolume(long volumeId) {
|
||||||
|
VolumeInfo vol = volFactory.getVolume(volumeId);
|
||||||
|
if (vol != null) {
|
||||||
|
vol.stateTransit(Volume.Event.DestroyRequested);
|
||||||
|
snapshotMgr.deletePoliciesForVolume(volumeId);
|
||||||
|
vol.stateTransit(Volume.Event.OperationSucceeded);
|
||||||
|
vol.stateTransit(Volume.Event.ExpungingRequested);
|
||||||
|
vol.stateTransit(Volume.Event.OperationSucceeded);
|
||||||
|
volDao.remove(vol.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -129,6 +129,8 @@ import com.cloud.agent.api.PlugNicAnswer;
|
|||||||
import com.cloud.agent.api.PlugNicCommand;
|
import com.cloud.agent.api.PlugNicCommand;
|
||||||
import com.cloud.agent.api.PrepareForMigrationAnswer;
|
import com.cloud.agent.api.PrepareForMigrationAnswer;
|
||||||
import com.cloud.agent.api.PrepareForMigrationCommand;
|
import com.cloud.agent.api.PrepareForMigrationCommand;
|
||||||
|
import com.cloud.agent.api.PrepareUnmanageVMInstanceAnswer;
|
||||||
|
import com.cloud.agent.api.PrepareUnmanageVMInstanceCommand;
|
||||||
import com.cloud.agent.api.PvlanSetupCommand;
|
import com.cloud.agent.api.PvlanSetupCommand;
|
||||||
import com.cloud.agent.api.ReadyAnswer;
|
import com.cloud.agent.api.ReadyAnswer;
|
||||||
import com.cloud.agent.api.ReadyCommand;
|
import com.cloud.agent.api.ReadyCommand;
|
||||||
@ -561,6 +563,8 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||||||
answer = execute((UnregisterNicCommand) cmd);
|
answer = execute((UnregisterNicCommand) cmd);
|
||||||
} else if (clz == GetUnmanagedInstancesCommand.class) {
|
} else if (clz == GetUnmanagedInstancesCommand.class) {
|
||||||
answer = execute((GetUnmanagedInstancesCommand) cmd);
|
answer = execute((GetUnmanagedInstancesCommand) cmd);
|
||||||
|
} else if (clz == PrepareUnmanageVMInstanceCommand.class) {
|
||||||
|
answer = execute((PrepareUnmanageVMInstanceCommand) cmd);
|
||||||
} else {
|
} else {
|
||||||
answer = Answer.createUnsupportedCommandAnswer(cmd);
|
answer = Answer.createUnsupportedCommandAnswer(cmd);
|
||||||
}
|
}
|
||||||
@ -7148,4 +7152,26 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||||||
}
|
}
|
||||||
return new GetUnmanagedInstancesAnswer(cmd, "", unmanagedInstances);
|
return new GetUnmanagedInstancesAnswer(cmd, "", unmanagedInstances);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Answer execute(PrepareUnmanageVMInstanceCommand cmd) {
|
||||||
|
s_logger.debug("Verify VMware instance: " + cmd.getInstanceName() + " is available before unmanaging VM");
|
||||||
|
VmwareContext context = getServiceContext();
|
||||||
|
VmwareHypervisorHost hyperHost = getHyperHost(context);
|
||||||
|
String instanceName = cmd.getInstanceName();
|
||||||
|
|
||||||
|
try {
|
||||||
|
ManagedObjectReference dcMor = hyperHost.getHyperHostDatacenter();
|
||||||
|
DatacenterMO dataCenterMo = new DatacenterMO(getServiceContext(), dcMor);
|
||||||
|
VirtualMachineMO vm = dataCenterMo.findVm(instanceName);
|
||||||
|
if (vm == null) {
|
||||||
|
return new PrepareUnmanageVMInstanceAnswer(cmd, false, "Cannot find VM with name " + instanceName +
|
||||||
|
" in datacenter " + dataCenterMo.getName());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
s_logger.error("Error trying to verify if VM to unmanage exists", e);
|
||||||
|
return new PrepareUnmanageVMInstanceAnswer(cmd, false, "Error: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PrepareUnmanageVMInstanceAnswer(cmd, true, "OK");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -596,4 +596,8 @@ public class ElasticLoadBalancerManagerImpl extends ManagerBase implements Elast
|
|||||||
public void prepareStop(VirtualMachineProfile profile) {
|
public void prepareStop(VirtualMachineProfile profile) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finalizeUnmanage(VirtualMachine vm) {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -356,6 +356,10 @@ public class InternalLoadBalancerVMManagerImpl extends ManagerBase implements In
|
|||||||
public void prepareStop(final VirtualMachineProfile profile) {
|
public void prepareStop(final VirtualMachineProfile profile) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finalizeUnmanage(VirtualMachine vm) {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException {
|
public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException {
|
||||||
final Map<String, String> configs = _configDao.getConfiguration("AgentManager", params);
|
final Map<String, String> configs = _configDao.getConfiguration("AgentManager", params);
|
||||||
|
|||||||
@ -189,6 +189,10 @@ public class NetScalerVMManagerImpl extends ManagerBase implements NetScalerVMMa
|
|||||||
public void prepareStop(VirtualMachineProfile profile) {
|
public void prepareStop(VirtualMachineProfile profile) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finalizeUnmanage(VirtualMachine vm) {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
|
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
|
||||||
_itMgr.registerGuru(VirtualMachine.Type.NetScalerVm, this);
|
_itMgr.registerGuru(VirtualMachine.Type.NetScalerVm, this);
|
||||||
|
|||||||
@ -1735,6 +1735,10 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
|
|||||||
public void prepareStop(VirtualMachineProfile profile) {
|
public void prepareStop(VirtualMachineProfile profile) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finalizeUnmanage(VirtualMachine vm) {
|
||||||
|
}
|
||||||
|
|
||||||
public List<ConsoleProxyAllocator> getConsoleProxyAllocators() {
|
public List<ConsoleProxyAllocator> getConsoleProxyAllocators() {
|
||||||
return _consoleProxyAllocators;
|
return _consoleProxyAllocators;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3148,6 +3148,10 @@ Configurable, StateListener<VirtualMachine.State, VirtualMachine.Event, VirtualM
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finalizeUnmanage(VirtualMachine vm) {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VirtualRouter findRouter(final long routerId) {
|
public VirtualRouter findRouter(final long routerId) {
|
||||||
return _routerDao.findById(routerId);
|
return _routerDao.findById(routerId);
|
||||||
|
|||||||
@ -47,9 +47,12 @@ import javax.xml.parsers.DocumentBuilder;
|
|||||||
import javax.xml.parsers.DocumentBuilderFactory;
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
|
||||||
|
import com.cloud.exception.UnsupportedServiceException;
|
||||||
|
import com.cloud.hypervisor.Hypervisor;
|
||||||
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
|
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
|
||||||
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
||||||
import org.apache.cloudstack.affinity.AffinityGroupService;
|
import org.apache.cloudstack.affinity.AffinityGroupService;
|
||||||
|
import org.apache.cloudstack.affinity.AffinityGroupVMMapVO;
|
||||||
import org.apache.cloudstack.affinity.AffinityGroupVO;
|
import org.apache.cloudstack.affinity.AffinityGroupVO;
|
||||||
import org.apache.cloudstack.affinity.dao.AffinityGroupDao;
|
import org.apache.cloudstack.affinity.dao.AffinityGroupDao;
|
||||||
import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
|
import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
|
||||||
@ -5038,7 +5041,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||||||
throw new PermissionDeniedException("Expunging a vm can only be done by an Admin. Or when the allow.user.expunge.recover.vm key is set.");
|
throw new PermissionDeniedException("Expunging a vm can only be done by an Admin. Or when the allow.user.expunge.recover.vm key is set.");
|
||||||
}
|
}
|
||||||
|
|
||||||
_vmSnapshotMgr.deleteVMSnapshotsFromDB(vmId);
|
_vmSnapshotMgr.deleteVMSnapshotsFromDB(vmId, false);
|
||||||
|
|
||||||
boolean status;
|
boolean status;
|
||||||
|
|
||||||
@ -6951,6 +6954,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finalizeUnmanage(VirtualMachine vm) {
|
||||||
|
}
|
||||||
|
|
||||||
private void encryptAndStorePassword(UserVmVO vm, String password) {
|
private void encryptAndStorePassword(UserVmVO vm, String password) {
|
||||||
String sshPublicKey = vm.getDetail(VmDetailConstants.SSH_PUBLIC_KEY);
|
String sshPublicKey = vm.getDetail(VmDetailConstants.SSH_PUBLIC_KEY);
|
||||||
if (sshPublicKey != null && !sshPublicKey.equals("") && password != null && !password.equals("saved_password")) {
|
if (sshPublicKey != null && !sshPublicKey.equals("") && password != null && !password.equals("saved_password")) {
|
||||||
@ -7112,4 +7119,139 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||||||
id, instanceName, uuidName, hypervisorType, customParameters,
|
id, instanceName, uuidName, hypervisorType, customParameters,
|
||||||
null, null, null, powerState);
|
null, null, null, powerState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean unmanageUserVM(Long vmId) {
|
||||||
|
UserVmVO vm = _vmDao.findById(vmId);
|
||||||
|
if (vm == null || vm.getRemoved() != null) {
|
||||||
|
throw new InvalidParameterValueException("Unable to find a VM with ID = " + vmId);
|
||||||
|
}
|
||||||
|
|
||||||
|
vm = _vmDao.acquireInLockTable(vm.getId());
|
||||||
|
boolean result;
|
||||||
|
try {
|
||||||
|
if (vm.getState() != State.Running && vm.getState() != State.Stopped) {
|
||||||
|
s_logger.debug("VM ID = " + vmId + " is not running or stopped, cannot be unmanaged");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vm.getHypervisorType() != Hypervisor.HypervisorType.VMware) {
|
||||||
|
throw new UnsupportedServiceException("Unmanaging a VM is currently allowed for VMware VMs only");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<VolumeVO> volumes = _volsDao.findByInstance(vm.getId());
|
||||||
|
checkUnmanagingVMOngoingVolumeSnapshots(vm);
|
||||||
|
checkUnmanagingVMVolumes(vm, volumes);
|
||||||
|
|
||||||
|
result = _itMgr.unmanage(vm.getUuid());
|
||||||
|
if (result) {
|
||||||
|
cleanupUnmanageVMResources(vm.getId());
|
||||||
|
unmanageVMFromDB(vm.getId());
|
||||||
|
publishUnmanageVMUsageEvents(vm, volumes);
|
||||||
|
} else {
|
||||||
|
throw new CloudRuntimeException("Error while unmanaging VM: " + vm.getUuid());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
s_logger.error("Could not unmanage VM " + vm.getUuid(), e);
|
||||||
|
throw new CloudRuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
_vmDao.releaseFromLockTable(vm.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Generate usage events related to unmanaging a VM
|
||||||
|
*/
|
||||||
|
private void publishUnmanageVMUsageEvents(UserVmVO vm, List<VolumeVO> volumes) {
|
||||||
|
postProcessingUnmanageVMVolumes(volumes, vm);
|
||||||
|
postProcessingUnmanageVM(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Cleanup the VM from resources and groups
|
||||||
|
*/
|
||||||
|
private void cleanupUnmanageVMResources(long vmId) {
|
||||||
|
cleanupVmResources(vmId);
|
||||||
|
removeVMFromAffinityGroups(vmId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void unmanageVMFromDB(long vmId) {
|
||||||
|
VMInstanceVO vm = _vmInstanceDao.findById(vmId);
|
||||||
|
userVmDetailsDao.removeDetails(vmId);
|
||||||
|
vm.setState(State.Expunging);
|
||||||
|
vm.setRemoved(new Date());
|
||||||
|
_vmInstanceDao.update(vm.getId(), vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Remove VM from affinity groups after unmanaging
|
||||||
|
*/
|
||||||
|
private void removeVMFromAffinityGroups(long vmId) {
|
||||||
|
List<AffinityGroupVMMapVO> affinityGroups = _affinityGroupVMMapDao.listByInstanceId(vmId);
|
||||||
|
if (affinityGroups.size() > 0) {
|
||||||
|
s_logger.debug("Cleaning up VM from affinity groups after unmanaging");
|
||||||
|
for (AffinityGroupVMMapVO map : affinityGroups) {
|
||||||
|
_affinityGroupVMMapDao.expunge(map.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Decrement VM resources and generate usage events after unmanaging VM
|
||||||
|
*/
|
||||||
|
private void postProcessingUnmanageVM(UserVmVO vm) {
|
||||||
|
ServiceOfferingVO offering = _serviceOfferingDao.findById(vm.getServiceOfferingId());
|
||||||
|
|
||||||
|
// First generate a VM stop event if the VM was not stopped already
|
||||||
|
if (vm.getState() != State.Stopped) {
|
||||||
|
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_STOP, vm.getAccountId(), vm.getDataCenterId(),
|
||||||
|
vm.getId(), vm.getHostName(), vm.getServiceOfferingId(), vm.getTemplateId(),
|
||||||
|
vm.getHypervisorType().toString(), VirtualMachine.class.getName(), vm.getUuid(), vm.isDisplayVm());
|
||||||
|
resourceCountDecrement(vm.getAccountId(), vm.isDisplayVm(), new Long(offering.getCpu()), new Long(offering.getRamSize()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// VM destroy usage event
|
||||||
|
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_DESTROY, vm.getAccountId(), vm.getDataCenterId(),
|
||||||
|
vm.getId(), vm.getHostName(), vm.getServiceOfferingId(), vm.getTemplateId(),
|
||||||
|
vm.getHypervisorType().toString(), VirtualMachine.class.getName(), vm.getUuid(), vm.isDisplayVm());
|
||||||
|
resourceCountDecrement(vm.getAccountId(), vm.isDisplayVm(), new Long(offering.getCpu()), new Long(offering.getRamSize()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Decrement resources for volumes and generate usage event for ROOT volume after unmanaging VM.
|
||||||
|
Usage events for DATA disks are published by the transition listener: @see VolumeStateListener#postStateTransitionEvent
|
||||||
|
*/
|
||||||
|
private void postProcessingUnmanageVMVolumes(List<VolumeVO> volumes, UserVmVO vm) {
|
||||||
|
for (VolumeVO volume : volumes) {
|
||||||
|
if (volume.getVolumeType() == Volume.Type.ROOT) {
|
||||||
|
//
|
||||||
|
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_DELETE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(),
|
||||||
|
Volume.class.getName(), volume.getUuid(), volume.isDisplayVolume());
|
||||||
|
}
|
||||||
|
_resourceLimitMgr.decrementResourceCount(vm.getAccountId(), ResourceType.volume);
|
||||||
|
_resourceLimitMgr.decrementResourceCount(vm.getAccountId(), ResourceType.primary_storage, new Long(volume.getSize()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkUnmanagingVMOngoingVolumeSnapshots(UserVmVO vm) {
|
||||||
|
s_logger.debug("Checking if there are any ongoing snapshots on the ROOT volumes associated with VM with ID " + vm.getId());
|
||||||
|
if (checkStatusOfVolumeSnapshots(vm.getId(), Volume.Type.ROOT)) {
|
||||||
|
throw new CloudRuntimeException("There is/are unbacked up snapshot(s) on ROOT volume, vm unmanage is not permitted, please try again later.");
|
||||||
|
}
|
||||||
|
s_logger.debug("Found no ongoing snapshots on volume of type ROOT, for the vm with id " + vm.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkUnmanagingVMVolumes(UserVmVO vm, List<VolumeVO> volumes) {
|
||||||
|
for (VolumeVO volume : volumes) {
|
||||||
|
if (volume.getInstanceId() == null || !volume.getInstanceId().equals(vm.getId())) {
|
||||||
|
throw new CloudRuntimeException("Invalid state for volume with ID " + volume.getId() + " of VM " +
|
||||||
|
vm.getId() +": it is not attached to VM");
|
||||||
|
} else if (volume.getVolumeType() != Volume.Type.ROOT && volume.getVolumeType() != Volume.Type.DATADISK) {
|
||||||
|
throw new CloudRuntimeException("Invalid type for volume with ID " + volume.getId() +
|
||||||
|
": ROOT or DATADISK expected but got " + volume.getVolumeType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -1282,7 +1282,7 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean deleteVMSnapshotsFromDB(Long vmId) {
|
public boolean deleteVMSnapshotsFromDB(Long vmId, boolean unmanage) {
|
||||||
List<VMSnapshotVO> listVmSnapshots = _vmSnapshotDao.findByVm(vmId);
|
List<VMSnapshotVO> listVmSnapshots = _vmSnapshotDao.findByVm(vmId);
|
||||||
if (listVmSnapshots == null || listVmSnapshots.isEmpty()) {
|
if (listVmSnapshots == null || listVmSnapshots.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
@ -1290,7 +1290,7 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme
|
|||||||
for (VMSnapshotVO snapshot : listVmSnapshots) {
|
for (VMSnapshotVO snapshot : listVmSnapshots) {
|
||||||
try {
|
try {
|
||||||
VMSnapshotStrategy strategy = findVMSnapshotStrategy(snapshot);
|
VMSnapshotStrategy strategy = findVMSnapshotStrategy(snapshot);
|
||||||
if (! strategy.deleteVMSnapshotFromDB(snapshot)) {
|
if (! strategy.deleteVMSnapshotFromDB(snapshot, unmanage)) {
|
||||||
s_logger.error("Couldn't delete vm snapshot with id " + snapshot.getId());
|
s_logger.error("Couldn't delete vm snapshot with id " + snapshot.getId());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,6 +25,17 @@ import java.util.Set;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import com.cloud.agent.api.PrepareUnmanageVMInstanceAnswer;
|
||||||
|
import com.cloud.agent.api.PrepareUnmanageVMInstanceCommand;
|
||||||
|
import com.cloud.event.ActionEvent;
|
||||||
|
import com.cloud.exception.UnsupportedServiceException;
|
||||||
|
import com.cloud.storage.Snapshot;
|
||||||
|
import com.cloud.storage.SnapshotVO;
|
||||||
|
import com.cloud.storage.dao.SnapshotDao;
|
||||||
|
import com.cloud.vm.NicVO;
|
||||||
|
import com.cloud.vm.UserVmVO;
|
||||||
|
import com.cloud.vm.dao.UserVmDao;
|
||||||
|
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
|
||||||
import org.apache.cloudstack.api.ApiConstants;
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
import org.apache.cloudstack.api.ApiErrorCode;
|
import org.apache.cloudstack.api.ApiErrorCode;
|
||||||
import org.apache.cloudstack.api.ResponseGenerator;
|
import org.apache.cloudstack.api.ResponseGenerator;
|
||||||
@ -32,6 +43,7 @@ import org.apache.cloudstack.api.ResponseObject;
|
|||||||
import org.apache.cloudstack.api.ServerApiException;
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
import org.apache.cloudstack.api.command.admin.vm.ImportUnmanagedInstanceCmd;
|
import org.apache.cloudstack.api.command.admin.vm.ImportUnmanagedInstanceCmd;
|
||||||
import org.apache.cloudstack.api.command.admin.vm.ListUnmanagedInstancesCmd;
|
import org.apache.cloudstack.api.command.admin.vm.ListUnmanagedInstancesCmd;
|
||||||
|
import org.apache.cloudstack.api.command.admin.vm.UnmanageVMInstanceCmd;
|
||||||
import org.apache.cloudstack.api.response.ListResponse;
|
import org.apache.cloudstack.api.response.ListResponse;
|
||||||
import org.apache.cloudstack.api.response.NicResponse;
|
import org.apache.cloudstack.api.response.NicResponse;
|
||||||
import org.apache.cloudstack.api.response.UnmanagedInstanceDiskResponse;
|
import org.apache.cloudstack.api.response.UnmanagedInstanceDiskResponse;
|
||||||
@ -40,6 +52,7 @@ import org.apache.cloudstack.api.response.UserVmResponse;
|
|||||||
import org.apache.cloudstack.context.CallContext;
|
import org.apache.cloudstack.context.CallContext;
|
||||||
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
||||||
import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
|
import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
|
||||||
|
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||||
@ -127,9 +140,9 @@ import com.cloud.vm.dao.VMInstanceDao;
|
|||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
|
||||||
public class VmImportManagerImpl implements VmImportService {
|
public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
||||||
public static final String VM_IMPORT_DEFAULT_TEMPLATE_NAME = "system-default-vm-import-dummy-template.iso";
|
public static final String VM_IMPORT_DEFAULT_TEMPLATE_NAME = "system-default-vm-import-dummy-template.iso";
|
||||||
private static final Logger LOGGER = Logger.getLogger(VmImportManagerImpl.class);
|
private static final Logger LOGGER = Logger.getLogger(UnmanagedVMsManagerImpl.class);
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private AgentManager agentManager;
|
private AgentManager agentManager;
|
||||||
@ -191,10 +204,16 @@ public class VmImportManagerImpl implements VmImportService {
|
|||||||
private GuestOSDao guestOSDao;
|
private GuestOSDao guestOSDao;
|
||||||
@Inject
|
@Inject
|
||||||
private GuestOSHypervisorDao guestOSHypervisorDao;
|
private GuestOSHypervisorDao guestOSHypervisorDao;
|
||||||
|
@Inject
|
||||||
|
private VMSnapshotDao vmSnapshotDao;
|
||||||
|
@Inject
|
||||||
|
private SnapshotDao snapshotDao;
|
||||||
|
@Inject
|
||||||
|
private UserVmDao userVmDao;
|
||||||
|
|
||||||
protected Gson gson;
|
protected Gson gson;
|
||||||
|
|
||||||
public VmImportManagerImpl() {
|
public UnmanagedVMsManagerImpl() {
|
||||||
gson = GsonHelper.getGsonLogger();
|
gson = GsonHelper.getGsonLogger();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -680,8 +699,8 @@ public class VmImportManagerImpl implements VmImportService {
|
|||||||
return new Pair<DiskProfile, StoragePool>(profile, storagePool);
|
return new Pair<DiskProfile, StoragePool>(profile, storagePool);
|
||||||
}
|
}
|
||||||
|
|
||||||
private NicProfile importNic(UnmanagedInstanceTO.Nic nic, VirtualMachine vm, Network network, Network.IpAddresses ipAddresses, boolean isDefaultNic) throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException {
|
private NicProfile importNic(UnmanagedInstanceTO.Nic nic, VirtualMachine vm, Network network, Network.IpAddresses ipAddresses, boolean isDefaultNic, boolean forced) throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException {
|
||||||
Pair<NicProfile, Integer> result = networkOrchestrationService.importNic(nic.getMacAddress(), 0, network, isDefaultNic, vm, ipAddresses);
|
Pair<NicProfile, Integer> result = networkOrchestrationService.importNic(nic.getMacAddress(), 0, network, isDefaultNic, vm, ipAddresses, forced);
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("NIC ID: %s import failed", nic.getNicId()));
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("NIC ID: %s import failed", nic.getNicId()));
|
||||||
}
|
}
|
||||||
@ -850,12 +869,16 @@ public class VmImportManagerImpl implements VmImportService {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (!serviceOfferingVO.isDynamic()) {
|
if (!serviceOfferingVO.isDynamic()) {
|
||||||
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_IMPORT, userVm.getAccountId(), userVm.getDataCenterId(), userVm.getId(), userVm.getHostName(), serviceOfferingVO.getId(), userVm.getTemplateId(),
|
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_CREATE, userVm.getAccountId(), userVm.getDataCenterId(), userVm.getId(), userVm.getHostName(), serviceOfferingVO.getId(), userVm.getTemplateId(),
|
||||||
userVm.getHypervisorType().toString(), VirtualMachine.class.getName(), userVm.getUuid(), userVm.isDisplayVm());
|
userVm.getHypervisorType().toString(), VirtualMachine.class.getName(), userVm.getUuid(), userVm.isDisplayVm());
|
||||||
} else {
|
} else {
|
||||||
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_IMPORT, userVm.getAccountId(), userVm.getAccountId(), userVm.getDataCenterId(), userVm.getHostName(), serviceOfferingVO.getId(), userVm.getTemplateId(),
|
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_CREATE, userVm.getAccountId(), userVm.getAccountId(), userVm.getDataCenterId(), userVm.getHostName(), serviceOfferingVO.getId(), userVm.getTemplateId(),
|
||||||
userVm.getHypervisorType().toString(), VirtualMachine.class.getName(), userVm.getUuid(), userVm.getDetails(), userVm.isDisplayVm());
|
userVm.getHypervisorType().toString(), VirtualMachine.class.getName(), userVm.getUuid(), userVm.getDetails(), userVm.isDisplayVm());
|
||||||
}
|
}
|
||||||
|
if (userVm.getState() == VirtualMachine.State.Running) {
|
||||||
|
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_START, userVm.getAccountId(), userVm.getDataCenterId(), userVm.getId(), userVm.getHostName(), serviceOfferingVO.getId(), userVm.getTemplateId(),
|
||||||
|
userVm.getHypervisorType().toString(), VirtualMachine.class.getName(), userVm.getUuid(), userVm.isDisplayVm());
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOGGER.error(String.format("Failed to publish usage records during VM import for unmanaged vm %s", userVm.getInstanceName()), e);
|
LOGGER.error(String.format("Failed to publish usage records during VM import for unmanaged vm %s", userVm.getInstanceName()), e);
|
||||||
cleanupFailedImportVM(userVm);
|
cleanupFailedImportVM(userVm);
|
||||||
@ -876,13 +899,24 @@ public class VmImportManagerImpl implements VmImportService {
|
|||||||
resourceLimitService.incrementResourceCount(userVm.getAccountId(), Resource.ResourceType.volume, volume.isDisplayVolume());
|
resourceLimitService.incrementResourceCount(userVm.getAccountId(), Resource.ResourceType.volume, volume.isDisplayVolume());
|
||||||
resourceLimitService.incrementResourceCount(userVm.getAccountId(), Resource.ResourceType.primary_storage, volume.isDisplayVolume(), volume.getSize());
|
resourceLimitService.incrementResourceCount(userVm.getAccountId(), Resource.ResourceType.primary_storage, volume.isDisplayVolume(), volume.getSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<NicVO> nics = nicDao.listByVmId(userVm.getId());
|
||||||
|
for (NicVO nic : nics) {
|
||||||
|
try {
|
||||||
|
NetworkVO network = networkDao.findById(nic.getNetworkId());
|
||||||
|
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_ASSIGN, userVm.getAccountId(), userVm.getDataCenterId(), userVm.getId(),
|
||||||
|
Long.toString(nic.getId()), network.getNetworkOfferingId(), null, 1L, VirtualMachine.class.getName(), userVm.getUuid(), userVm.isDisplay());
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOGGER.error(String.format("Failed to publish network usage records during VM import. %s", Strings.nullToEmpty(e.getMessage())));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private UserVm importVirtualMachineInternal(final UnmanagedInstanceTO unmanagedInstance, final String instanceName, final DataCenter zone, final Cluster cluster, final HostVO host,
|
private UserVm importVirtualMachineInternal(final UnmanagedInstanceTO unmanagedInstance, final String instanceName, final DataCenter zone, final Cluster cluster, final HostVO host,
|
||||||
final VirtualMachineTemplate template, final String displayName, final String hostName, final Account caller, final Account owner, final Long userId,
|
final VirtualMachineTemplate template, final String displayName, final String hostName, final Account caller, final Account owner, final Long userId,
|
||||||
final ServiceOfferingVO serviceOffering, final Map<String, Long> dataDiskOfferingMap,
|
final ServiceOfferingVO serviceOffering, final Map<String, Long> dataDiskOfferingMap,
|
||||||
final Map<String, Long> nicNetworkMap, final Map<String, Network.IpAddresses> callerNicIpAddressMap,
|
final Map<String, Long> nicNetworkMap, final Map<String, Network.IpAddresses> callerNicIpAddressMap,
|
||||||
final Map<String, String> details, final boolean migrateAllowed) {
|
final Map<String, String> details, final boolean migrateAllowed, final boolean forced) {
|
||||||
UserVm userVm = null;
|
UserVm userVm = null;
|
||||||
|
|
||||||
ServiceOfferingVO validatedServiceOffering = null;
|
ServiceOfferingVO validatedServiceOffering = null;
|
||||||
@ -986,7 +1020,7 @@ public class VmImportManagerImpl implements VmImportService {
|
|||||||
for (UnmanagedInstanceTO.Nic nic : unmanagedInstance.getNics()) {
|
for (UnmanagedInstanceTO.Nic nic : unmanagedInstance.getNics()) {
|
||||||
Network network = networkDao.findById(allNicNetworkMap.get(nic.getNicId()));
|
Network network = networkDao.findById(allNicNetworkMap.get(nic.getNicId()));
|
||||||
Network.IpAddresses ipAddresses = nicIpAddressMap.get(nic.getNicId());
|
Network.IpAddresses ipAddresses = nicIpAddressMap.get(nic.getNicId());
|
||||||
importNic(nic, userVm, network, ipAddresses, firstNic);
|
importNic(nic, userVm, network, ipAddresses, firstNic, forced);
|
||||||
firstNic = false;
|
firstNic = false;
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -1139,6 +1173,7 @@ public class VmImportManagerImpl implements VmImportService {
|
|||||||
final Map<String, Network.IpAddresses> nicIpAddressMap = cmd.getNicIpAddressList();
|
final Map<String, Network.IpAddresses> nicIpAddressMap = cmd.getNicIpAddressList();
|
||||||
final Map<String, Long> dataDiskOfferingMap = cmd.getDataDiskToDiskOfferingList();
|
final Map<String, Long> dataDiskOfferingMap = cmd.getDataDiskToDiskOfferingList();
|
||||||
final Map<String, String> details = cmd.getDetails();
|
final Map<String, String> details = cmd.getDetails();
|
||||||
|
final boolean forced = cmd.isForced();
|
||||||
List<HostVO> hosts = resourceManager.listHostsInClusterByStatus(clusterId, Status.Up);
|
List<HostVO> hosts = resourceManager.listHostsInClusterByStatus(clusterId, Status.Up);
|
||||||
UserVm userVm = null;
|
UserVm userVm = null;
|
||||||
List<String> additionalNameFilters = getAdditionalNameFilters(cluster);
|
List<String> additionalNameFilters = getAdditionalNameFilters(cluster);
|
||||||
@ -1192,7 +1227,7 @@ public class VmImportManagerImpl implements VmImportService {
|
|||||||
template, displayName, hostName, caller, owner, userId,
|
template, displayName, hostName, caller, owner, userId,
|
||||||
serviceOffering, dataDiskOfferingMap,
|
serviceOffering, dataDiskOfferingMap,
|
||||||
nicNetworkMap, nicIpAddressMap,
|
nicNetworkMap, nicIpAddressMap,
|
||||||
details, cmd.getMigrateAllowed());
|
details, cmd.getMigrateAllowed(), forced);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1211,6 +1246,124 @@ public class VmImportManagerImpl implements VmImportService {
|
|||||||
final List<Class<?>> cmdList = new ArrayList<Class<?>>();
|
final List<Class<?>> cmdList = new ArrayList<Class<?>>();
|
||||||
cmdList.add(ListUnmanagedInstancesCmd.class);
|
cmdList.add(ListUnmanagedInstancesCmd.class);
|
||||||
cmdList.add(ImportUnmanagedInstanceCmd.class);
|
cmdList.add(ImportUnmanagedInstanceCmd.class);
|
||||||
|
cmdList.add(UnmanageVMInstanceCmd.class);
|
||||||
return cmdList;
|
return cmdList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform validations before attempting to unmanage a VM from CloudStack:
|
||||||
|
* - VM must not have any associated volume snapshot
|
||||||
|
* - VM must not have an attached ISO
|
||||||
|
*/
|
||||||
|
private void performUnmanageVMInstancePrechecks(VMInstanceVO vmVO) {
|
||||||
|
if (hasVolumeSnapshotsPriorToUnmanageVM(vmVO)) {
|
||||||
|
throw new UnsupportedServiceException("Cannot unmanage VM with id = " + vmVO.getUuid() +
|
||||||
|
" as there are volume snapshots for its volume(s). Please remove snapshots before unmanaging.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasISOAttached(vmVO)) {
|
||||||
|
throw new UnsupportedServiceException("Cannot unmanage VM with id = " + vmVO.getUuid() +
|
||||||
|
" as there is an ISO attached. Please detach ISO before unmanaging.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasVolumeSnapshotsPriorToUnmanageVM(VMInstanceVO vmVO) {
|
||||||
|
List<VolumeVO> volumes = volumeDao.findByInstance(vmVO.getId());
|
||||||
|
for (VolumeVO volume : volumes) {
|
||||||
|
List<SnapshotVO> snaps = snapshotDao.listByVolumeId(volume.getId());
|
||||||
|
if (CollectionUtils.isNotEmpty(snaps)) {
|
||||||
|
for (SnapshotVO snap : snaps) {
|
||||||
|
if (snap.getState() != Snapshot.State.Destroyed && snap.getRemoved() == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasISOAttached(VMInstanceVO vmVO) {
|
||||||
|
UserVmVO userVM = userVmDao.findById(vmVO.getId());
|
||||||
|
if (userVM == null) {
|
||||||
|
throw new InvalidParameterValueException("Could not find user VM with ID = " + vmVO.getUuid());
|
||||||
|
}
|
||||||
|
return userVM.getIsoId() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a suitable host within the scope of the VM to unmanage to verify the VM exists
|
||||||
|
*/
|
||||||
|
private Long findSuitableHostId(VMInstanceVO vmVO) {
|
||||||
|
Long hostId = vmVO.getHostId();
|
||||||
|
if (hostId == null) {
|
||||||
|
long zoneId = vmVO.getDataCenterId();
|
||||||
|
List<HostVO> hosts = hostDao.listAllHostsUpByZoneAndHypervisor(zoneId, vmVO.getHypervisorType());
|
||||||
|
for (HostVO host : hosts) {
|
||||||
|
if (host.isInMaintenanceStates() || host.getState() != Status.Up || host.getStatus() != Status.Up) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
hostId = host.getId();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hostId == null) {
|
||||||
|
throw new CloudRuntimeException("Cannot find a host to verify if the VM to unmanage " +
|
||||||
|
"with id = " + vmVO.getUuid() + " exists.");
|
||||||
|
}
|
||||||
|
return hostId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@ActionEvent(eventType = EventTypes.EVENT_VM_UNMANAGE, eventDescription = "unmanaging VM", async = true)
|
||||||
|
public boolean unmanageVMInstance(long vmId) {
|
||||||
|
VMInstanceVO vmVO = vmDao.findById(vmId);
|
||||||
|
if (vmVO == null || vmVO.getRemoved() != null) {
|
||||||
|
throw new InvalidParameterValueException("Could not find VM to unmanage, it is either removed or not existing VM");
|
||||||
|
} else if (vmVO.getState() != VirtualMachine.State.Running && vmVO.getState() != VirtualMachine.State.Stopped) {
|
||||||
|
throw new InvalidParameterValueException("VM with id = " + vmVO.getUuid() + " must be running or stopped to be unmanaged");
|
||||||
|
} else if (vmVO.getHypervisorType() != Hypervisor.HypervisorType.VMware) {
|
||||||
|
throw new UnsupportedServiceException("Unmanage VM is currently allowed for VMware VMs only");
|
||||||
|
} else if (vmVO.getType() != VirtualMachine.Type.User) {
|
||||||
|
throw new UnsupportedServiceException("Unmanage VM is currently allowed for guest VMs only");
|
||||||
|
}
|
||||||
|
|
||||||
|
performUnmanageVMInstancePrechecks(vmVO);
|
||||||
|
|
||||||
|
Long hostId = findSuitableHostId(vmVO);
|
||||||
|
String instanceName = vmVO.getInstanceName();
|
||||||
|
|
||||||
|
if (!existsVMToUnmanage(instanceName, hostId)) {
|
||||||
|
throw new CloudRuntimeException("VM with id = " + vmVO.getUuid() + " is not found in the hypervisor");
|
||||||
|
}
|
||||||
|
|
||||||
|
return userVmManager.unmanageUserVM(vmId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify the VM to unmanage exists on the hypervisor
|
||||||
|
*/
|
||||||
|
private boolean existsVMToUnmanage(String instanceName, Long hostId) {
|
||||||
|
PrepareUnmanageVMInstanceCommand command = new PrepareUnmanageVMInstanceCommand();
|
||||||
|
command.setInstanceName(instanceName);
|
||||||
|
Answer ans = agentManager.easySend(hostId, command);
|
||||||
|
if (!(ans instanceof PrepareUnmanageVMInstanceAnswer)) {
|
||||||
|
throw new CloudRuntimeException("Error communicating with host " + hostId);
|
||||||
|
}
|
||||||
|
PrepareUnmanageVMInstanceAnswer answer = (PrepareUnmanageVMInstanceAnswer) ans;
|
||||||
|
if (!answer.getResult()) {
|
||||||
|
LOGGER.error("Error verifying VM " + instanceName + " exists on host with ID = " + hostId + ": " + answer.getDetails());
|
||||||
|
}
|
||||||
|
return answer.getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getConfigComponentName() {
|
||||||
|
return UnmanagedVMsManagerImpl.class.getSimpleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigKey<?>[] getConfigKeys() {
|
||||||
|
return new ConfigKey<?>[] { UnmanageVMPreserveNic };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -35,6 +35,6 @@
|
|||||||
<property name="name" value="LXCGuru" />
|
<property name="name" value="LXCGuru" />
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="vmImportService" class="org.apache.cloudstack.vm.VmImportManagerImpl" />
|
<bean id="vmImportService" class="org.apache.cloudstack.vm.UnmanagedVMsManagerImpl" />
|
||||||
|
|
||||||
</beans>
|
</beans>
|
||||||
|
|||||||
@ -985,7 +985,11 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Pair<NicProfile, Integer> importNic(String macAddress, int deviceId, Network network, Boolean isDefaultNic, VirtualMachine vm, IpAddresses ipAddresses) {
|
public Pair<NicProfile, Integer> importNic(String macAddress, int deviceId, Network network, Boolean isDefaultNic, VirtualMachine vm, IpAddresses ipAddresses, boolean forced) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unmanageNics(VirtualMachineProfile vm) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,17 +19,29 @@ package org.apache.cloudstack.vm;
|
|||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.nullable;
|
import static org.mockito.ArgumentMatchers.nullable;
|
||||||
import static org.mockito.Matchers.any;
|
import static org.mockito.Matchers.any;
|
||||||
|
import static org.mockito.Matchers.anyBoolean;
|
||||||
import static org.mockito.Matchers.anyLong;
|
import static org.mockito.Matchers.anyLong;
|
||||||
import static org.mockito.Mockito.doNothing;
|
import static org.mockito.Mockito.doNothing;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import com.cloud.exception.UnsupportedServiceException;
|
||||||
|
import com.cloud.storage.SnapshotVO;
|
||||||
|
import com.cloud.storage.dao.SnapshotDao;
|
||||||
|
import com.cloud.vm.NicVO;
|
||||||
|
import com.cloud.vm.dao.NicDao;
|
||||||
|
import com.cloud.vm.dao.UserVmDao;
|
||||||
|
import com.cloud.vm.snapshot.VMSnapshotVO;
|
||||||
|
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
|
||||||
import org.apache.cloudstack.api.ResponseGenerator;
|
import org.apache.cloudstack.api.ResponseGenerator;
|
||||||
import org.apache.cloudstack.api.ResponseObject;
|
import org.apache.cloudstack.api.ResponseObject;
|
||||||
import org.apache.cloudstack.api.ServerApiException;
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
@ -111,10 +123,10 @@ import com.cloud.vm.dao.VMInstanceDao;
|
|||||||
|
|
||||||
@RunWith(PowerMockRunner.class)
|
@RunWith(PowerMockRunner.class)
|
||||||
@PrepareForTest(UsageEventUtils.class)
|
@PrepareForTest(UsageEventUtils.class)
|
||||||
public class VmImportManagerImplTest {
|
public class UnmanagedVMsManagerImplTest {
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
private VmImportService vmIngestionService = new VmImportManagerImpl();
|
private UnmanagedVMsManager unmanagedVMsManager = new UnmanagedVMsManagerImpl();
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private UserVmManager userVmManager;
|
private UserVmManager userVmManager;
|
||||||
@ -160,6 +172,21 @@ public class VmImportManagerImplTest {
|
|||||||
private NetworkModel networkModel;
|
private NetworkModel networkModel;
|
||||||
@Mock
|
@Mock
|
||||||
private ConfigurationDao configurationDao;
|
private ConfigurationDao configurationDao;
|
||||||
|
@Mock
|
||||||
|
private VMSnapshotDao vmSnapshotDao;
|
||||||
|
@Mock
|
||||||
|
private SnapshotDao snapshotDao;
|
||||||
|
@Mock
|
||||||
|
private UserVmDao userVmDao;
|
||||||
|
@Mock
|
||||||
|
private NicDao nicDao;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private VMInstanceVO virtualMachine;
|
||||||
|
@Mock
|
||||||
|
private NicVO nicVO;
|
||||||
|
|
||||||
|
private static final long virtualMachineId = 1L;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
@ -281,7 +308,7 @@ public class VmImportManagerImplTest {
|
|||||||
NicProfile profile = Mockito.mock(NicProfile.class);
|
NicProfile profile = Mockito.mock(NicProfile.class);
|
||||||
Integer deviceId = 100;
|
Integer deviceId = 100;
|
||||||
Pair<NicProfile, Integer> pair = new Pair<NicProfile, Integer>(profile, deviceId);
|
Pair<NicProfile, Integer> pair = new Pair<NicProfile, Integer>(profile, deviceId);
|
||||||
when(networkOrchestrationService.importNic(nullable(String.class), nullable(Integer.class), nullable(Network.class), nullable(Boolean.class), nullable(VirtualMachine.class), nullable(Network.IpAddresses.class))).thenReturn(pair);
|
when(networkOrchestrationService.importNic(nullable(String.class), nullable(Integer.class), nullable(Network.class), nullable(Boolean.class), nullable(VirtualMachine.class), nullable(Network.IpAddresses.class), anyBoolean())).thenReturn(pair);
|
||||||
when(volumeManager.importVolume(Mockito.any(Volume.Type.class), Mockito.anyString(), Mockito.any(DiskOffering.class), Mockito.anyLong(),
|
when(volumeManager.importVolume(Mockito.any(Volume.Type.class), Mockito.anyString(), Mockito.any(DiskOffering.class), Mockito.anyLong(),
|
||||||
Mockito.anyLong(), Mockito.anyLong(), Mockito.any(VirtualMachine.class), Mockito.any(VirtualMachineTemplate.class),
|
Mockito.anyLong(), Mockito.anyLong(), Mockito.any(VirtualMachine.class), Mockito.any(VirtualMachineTemplate.class),
|
||||||
Mockito.any(Account.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyString())).thenReturn(Mockito.mock(DiskProfile.class));
|
Mockito.any(Account.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyString())).thenReturn(Mockito.mock(DiskProfile.class));
|
||||||
@ -291,6 +318,18 @@ public class VmImportManagerImplTest {
|
|||||||
userVmResponse.setInstanceName(instance.getName());
|
userVmResponse.setInstanceName(instance.getName());
|
||||||
userVmResponses.add(userVmResponse);
|
userVmResponses.add(userVmResponse);
|
||||||
when(responseGenerator.createUserVmResponse(Mockito.any(ResponseObject.ResponseView.class), Mockito.anyString(), Mockito.any(UserVm.class))).thenReturn(userVmResponses);
|
when(responseGenerator.createUserVmResponse(Mockito.any(ResponseObject.ResponseView.class), Mockito.anyString(), Mockito.any(UserVm.class))).thenReturn(userVmResponses);
|
||||||
|
|
||||||
|
when(vmDao.findById(virtualMachineId)).thenReturn(virtualMachine);
|
||||||
|
when(virtualMachine.getState()).thenReturn(VirtualMachine.State.Running);
|
||||||
|
when(virtualMachine.getInstanceName()).thenReturn("i-2-7-VM");
|
||||||
|
when(virtualMachine.getId()).thenReturn(virtualMachineId);
|
||||||
|
VolumeVO volumeVO = mock(VolumeVO.class);
|
||||||
|
when(volumeDao.findByInstance(virtualMachineId)).thenReturn(Collections.singletonList(volumeVO));
|
||||||
|
when(volumeVO.getInstanceId()).thenReturn(virtualMachineId);
|
||||||
|
when(volumeVO.getId()).thenReturn(virtualMachineId);
|
||||||
|
when(nicDao.listByVmId(virtualMachineId)).thenReturn(Collections.singletonList(nicVO));
|
||||||
|
when(nicVO.getNetworkId()).thenReturn(1L);
|
||||||
|
when(networkDao.findById(1L)).thenReturn(networkVO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@ -301,7 +340,7 @@ public class VmImportManagerImplTest {
|
|||||||
@Test
|
@Test
|
||||||
public void listUnmanagedInstancesTest() {
|
public void listUnmanagedInstancesTest() {
|
||||||
ListUnmanagedInstancesCmd cmd = Mockito.mock(ListUnmanagedInstancesCmd.class);
|
ListUnmanagedInstancesCmd cmd = Mockito.mock(ListUnmanagedInstancesCmd.class);
|
||||||
vmIngestionService.listUnmanagedInstances(cmd);
|
unmanagedVMsManager.listUnmanagedInstances(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = InvalidParameterValueException.class)
|
@Test(expected = InvalidParameterValueException.class)
|
||||||
@ -310,7 +349,7 @@ public class VmImportManagerImplTest {
|
|||||||
ClusterVO cluster = new ClusterVO(1, 1, "Cluster");
|
ClusterVO cluster = new ClusterVO(1, 1, "Cluster");
|
||||||
cluster.setHypervisorType(Hypervisor.HypervisorType.KVM.toString());
|
cluster.setHypervisorType(Hypervisor.HypervisorType.KVM.toString());
|
||||||
when(clusterDao.findById(Mockito.anyLong())).thenReturn(cluster);
|
when(clusterDao.findById(Mockito.anyLong())).thenReturn(cluster);
|
||||||
vmIngestionService.listUnmanagedInstances(cmd);
|
unmanagedVMsManager.listUnmanagedInstances(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = PermissionDeniedException.class)
|
@Test(expected = PermissionDeniedException.class)
|
||||||
@ -320,7 +359,7 @@ public class VmImportManagerImplTest {
|
|||||||
UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString(), User.Source.UNKNOWN);
|
UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString(), User.Source.UNKNOWN);
|
||||||
CallContext.register(user, account);
|
CallContext.register(user, account);
|
||||||
ListUnmanagedInstancesCmd cmd = Mockito.mock(ListUnmanagedInstancesCmd.class);
|
ListUnmanagedInstancesCmd cmd = Mockito.mock(ListUnmanagedInstancesCmd.class);
|
||||||
vmIngestionService.listUnmanagedInstances(cmd);
|
unmanagedVMsManager.listUnmanagedInstances(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -330,7 +369,7 @@ public class VmImportManagerImplTest {
|
|||||||
when(importUnmanageInstanceCmd.getAccountName()).thenReturn(null);
|
when(importUnmanageInstanceCmd.getAccountName()).thenReturn(null);
|
||||||
when(importUnmanageInstanceCmd.getDomainId()).thenReturn(null);
|
when(importUnmanageInstanceCmd.getDomainId()).thenReturn(null);
|
||||||
PowerMockito.mockStatic(UsageEventUtils.class);
|
PowerMockito.mockStatic(UsageEventUtils.class);
|
||||||
vmIngestionService.importUnmanagedInstance(importUnmanageInstanceCmd);
|
unmanagedVMsManager.importUnmanagedInstance(importUnmanageInstanceCmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = InvalidParameterValueException.class)
|
@Test(expected = InvalidParameterValueException.class)
|
||||||
@ -339,7 +378,7 @@ public class VmImportManagerImplTest {
|
|||||||
when(importUnmanageInstanceCmd.getName()).thenReturn("TestInstance");
|
when(importUnmanageInstanceCmd.getName()).thenReturn("TestInstance");
|
||||||
when(importUnmanageInstanceCmd.getName()).thenReturn("some name");
|
when(importUnmanageInstanceCmd.getName()).thenReturn("some name");
|
||||||
when(importUnmanageInstanceCmd.getMigrateAllowed()).thenReturn(false);
|
when(importUnmanageInstanceCmd.getMigrateAllowed()).thenReturn(false);
|
||||||
vmIngestionService.importUnmanagedInstance(importUnmanageInstanceCmd);
|
unmanagedVMsManager.importUnmanagedInstance(importUnmanageInstanceCmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = ServerApiException.class)
|
@Test(expected = ServerApiException.class)
|
||||||
@ -348,6 +387,44 @@ public class VmImportManagerImplTest {
|
|||||||
when(importUnmanageInstanceCmd.getName()).thenReturn("SomeInstance");
|
when(importUnmanageInstanceCmd.getName()).thenReturn("SomeInstance");
|
||||||
when(importUnmanageInstanceCmd.getAccountName()).thenReturn(null);
|
when(importUnmanageInstanceCmd.getAccountName()).thenReturn(null);
|
||||||
when(importUnmanageInstanceCmd.getDomainId()).thenReturn(null);
|
when(importUnmanageInstanceCmd.getDomainId()).thenReturn(null);
|
||||||
vmIngestionService.importUnmanagedInstance(importUnmanageInstanceCmd);
|
unmanagedVMsManager.importUnmanagedInstance(importUnmanageInstanceCmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = InvalidParameterValueException.class)
|
||||||
|
public void unmanageVMInstanceMissingInstanceTest() {
|
||||||
|
long notExistingId = 10L;
|
||||||
|
unmanagedVMsManager.unmanageVMInstance(notExistingId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = InvalidParameterValueException.class)
|
||||||
|
public void unmanageVMInstanceDestroyedInstanceTest() {
|
||||||
|
when(virtualMachine.getState()).thenReturn(VirtualMachine.State.Destroyed);
|
||||||
|
unmanagedVMsManager.unmanageVMInstance(virtualMachineId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = InvalidParameterValueException.class)
|
||||||
|
public void unmanageVMInstanceExpungedInstanceTest() {
|
||||||
|
when(virtualMachine.getState()).thenReturn(VirtualMachine.State.Expunging);
|
||||||
|
unmanagedVMsManager.unmanageVMInstance(virtualMachineId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = UnsupportedServiceException.class)
|
||||||
|
public void unmanageVMInstanceExistingVMSnapshotsTest() {
|
||||||
|
when(vmSnapshotDao.findByVm(virtualMachineId)).thenReturn(Arrays.asList(new VMSnapshotVO(), new VMSnapshotVO()));
|
||||||
|
unmanagedVMsManager.unmanageVMInstance(virtualMachineId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = UnsupportedServiceException.class)
|
||||||
|
public void unmanageVMInstanceExistingVolumeSnapshotsTest() {
|
||||||
|
when(snapshotDao.listByVolumeId(virtualMachineId)).thenReturn(Arrays.asList(new SnapshotVO(), new SnapshotVO()));
|
||||||
|
unmanagedVMsManager.unmanageVMInstance(virtualMachineId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = UnsupportedServiceException.class)
|
||||||
|
public void unmanageVMInstanceExistingISOAttachedTest() {
|
||||||
|
UserVmVO userVmVO = mock(UserVmVO.class);
|
||||||
|
when(userVmDao.findById(virtualMachineId)).thenReturn(userVmVO);
|
||||||
|
when(userVmVO.getIsoId()).thenReturn(3L);
|
||||||
|
unmanagedVMsManager.unmanageVMInstance(virtualMachineId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1496,6 +1496,10 @@ public class SecondaryStorageManagerImpl extends ManagerBase implements Secondar
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finalizeUnmanage(VirtualMachine vm) {
|
||||||
|
}
|
||||||
|
|
||||||
public List<SecondaryStorageVmAllocator> getSecondaryStorageVmAllocators() {
|
public List<SecondaryStorageVmAllocator> getSecondaryStorageVmAllocators() {
|
||||||
return _ssVmAllocators;
|
return _ssVmAllocators;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,7 +25,9 @@ from marvin.cloudstackAPI import (recoverVirtualMachine,
|
|||||||
provisionCertificate,
|
provisionCertificate,
|
||||||
updateConfiguration,
|
updateConfiguration,
|
||||||
migrateVirtualMachine,
|
migrateVirtualMachine,
|
||||||
migrateVirtualMachineWithVolume)
|
migrateVirtualMachineWithVolume,
|
||||||
|
unmanageVirtualMachine,
|
||||||
|
listUnmanagedInstances)
|
||||||
from marvin.lib.utils import *
|
from marvin.lib.utils import *
|
||||||
|
|
||||||
from marvin.lib.base import (Account,
|
from marvin.lib.base import (Account,
|
||||||
@ -37,13 +39,17 @@ from marvin.lib.base import (Account,
|
|||||||
Configurations,
|
Configurations,
|
||||||
StoragePool,
|
StoragePool,
|
||||||
Volume,
|
Volume,
|
||||||
DiskOffering)
|
DiskOffering,
|
||||||
|
NetworkOffering,
|
||||||
|
Network)
|
||||||
from marvin.lib.common import (get_domain,
|
from marvin.lib.common import (get_domain,
|
||||||
get_zone,
|
get_zone,
|
||||||
get_template,
|
get_template,
|
||||||
list_hosts)
|
list_hosts,
|
||||||
|
list_virtual_machines)
|
||||||
from marvin.codes import FAILED, PASS
|
from marvin.codes import FAILED, PASS
|
||||||
from nose.plugins.attrib import attr
|
from nose.plugins.attrib import attr
|
||||||
|
from marvin.lib.decoratorGenerators import skipTestIf
|
||||||
# Import System modules
|
# Import System modules
|
||||||
import time
|
import time
|
||||||
|
|
||||||
@ -1512,3 +1518,152 @@ class TestKVMLiveMigration(cloudstackTestCase):
|
|||||||
target_host.id,
|
target_host.id,
|
||||||
"HostID not as expected")
|
"HostID not as expected")
|
||||||
|
|
||||||
|
|
||||||
|
class TestUnmanageVM(cloudstackTestCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
testClient = super(TestUnmanageVM, cls).getClsTestClient()
|
||||||
|
cls.apiclient = testClient.getApiClient()
|
||||||
|
cls.services = testClient.getParsedTestDataConfig()
|
||||||
|
cls.hypervisor = testClient.getHypervisorInfo()
|
||||||
|
cls._cleanup = []
|
||||||
|
|
||||||
|
# Get Zone, Domain and templates
|
||||||
|
cls.domain = get_domain(cls.apiclient)
|
||||||
|
cls.zone = get_zone(cls.apiclient, cls.testClient.getZoneForTests())
|
||||||
|
cls.services['mode'] = cls.zone.networktype
|
||||||
|
|
||||||
|
cls.template = get_template(
|
||||||
|
cls.apiclient,
|
||||||
|
cls.zone.id,
|
||||||
|
cls.services["ostype"],
|
||||||
|
hypervisor=cls.hypervisor.lower()
|
||||||
|
)
|
||||||
|
if cls.template == FAILED:
|
||||||
|
assert False, "get_template() failed to return template with description %s" % cls.services["ostype"]
|
||||||
|
|
||||||
|
cls.hypervisorNotSupported = cls.hypervisor.lower() != "vmware"
|
||||||
|
|
||||||
|
cls.services["small"]["zoneid"] = cls.zone.id
|
||||||
|
cls.services["small"]["template"] = cls.template.id
|
||||||
|
|
||||||
|
cls.account = Account.create(
|
||||||
|
cls.apiclient,
|
||||||
|
cls.services["account"],
|
||||||
|
domainid=cls.domain.id
|
||||||
|
)
|
||||||
|
|
||||||
|
cls.small_offering = ServiceOffering.create(
|
||||||
|
cls.apiclient,
|
||||||
|
cls.services["service_offerings"]["small"]
|
||||||
|
)
|
||||||
|
|
||||||
|
cls.network_offering = NetworkOffering.create(
|
||||||
|
cls.apiclient,
|
||||||
|
cls.services["l2-network_offering"],
|
||||||
|
)
|
||||||
|
cls.network_offering.update(cls.apiclient, state='Enabled')
|
||||||
|
|
||||||
|
cls._cleanup = [
|
||||||
|
cls.small_offering,
|
||||||
|
cls.network_offering,
|
||||||
|
cls.account
|
||||||
|
]
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.apiclient = self.testClient.getApiClient()
|
||||||
|
self.services["network"]["networkoffering"] = self.network_offering.id
|
||||||
|
|
||||||
|
self.network = Network.create(
|
||||||
|
self.apiclient,
|
||||||
|
self.services["l2-network"],
|
||||||
|
zoneid=self.zone.id,
|
||||||
|
networkofferingid=self.network_offering.id
|
||||||
|
)
|
||||||
|
|
||||||
|
self.cleanup = [
|
||||||
|
self.network
|
||||||
|
]
|
||||||
|
|
||||||
|
@attr(tags=["advanced", "advancedns", "smoke", "sg"], required_hardware="false")
|
||||||
|
@skipTestIf("hypervisorNotSupported")
|
||||||
|
def test_01_unmanage_vm_cycle(self):
|
||||||
|
"""
|
||||||
|
Test the following:
|
||||||
|
1. Deploy VM
|
||||||
|
2. Unmanage VM
|
||||||
|
3. Verify VM is not listed in CloudStack
|
||||||
|
4. Verify VM is listed as part of the unmanaged instances
|
||||||
|
5. Import VM
|
||||||
|
6. Destroy VM
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 1 - Deploy VM
|
||||||
|
self.virtual_machine = VirtualMachine.create(
|
||||||
|
self.apiclient,
|
||||||
|
self.services["virtual_machine"],
|
||||||
|
templateid=self.template.id,
|
||||||
|
serviceofferingid=self.small_offering.id,
|
||||||
|
networkids=self.network.id,
|
||||||
|
zoneid=self.zone.id
|
||||||
|
)
|
||||||
|
vm_id = self.virtual_machine.id
|
||||||
|
vm_instance_name = self.virtual_machine.instancename
|
||||||
|
hostid = self.virtual_machine.hostid
|
||||||
|
hosts = Host.list(
|
||||||
|
self.apiclient,
|
||||||
|
id=hostid
|
||||||
|
)
|
||||||
|
host = hosts[0]
|
||||||
|
clusterid = host.clusterid
|
||||||
|
|
||||||
|
list_vm = list_virtual_machines(
|
||||||
|
self.apiclient,
|
||||||
|
id=vm_id
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
isinstance(list_vm, list),
|
||||||
|
True,
|
||||||
|
"Check if virtual machine is present"
|
||||||
|
)
|
||||||
|
vm_response = list_vm[0]
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
vm_response.state,
|
||||||
|
"Running",
|
||||||
|
"VM state should be running after deployment"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 2 - Unmanage VM from CloudStack
|
||||||
|
self.virtual_machine.unmanage(self.apiclient)
|
||||||
|
|
||||||
|
list_vm = list_virtual_machines(
|
||||||
|
self.apiclient,
|
||||||
|
id=vm_id
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
list_vm,
|
||||||
|
None,
|
||||||
|
"VM should not be listed"
|
||||||
|
)
|
||||||
|
|
||||||
|
unmanaged_vms = VirtualMachine.listUnmanagedInstances(
|
||||||
|
self.apiclient,
|
||||||
|
clusterid=clusterid,
|
||||||
|
name=vm_instance_name
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
len(unmanaged_vms),
|
||||||
|
1,
|
||||||
|
"Unmanaged VMs matching instance name list size is 1"
|
||||||
|
)
|
||||||
|
|
||||||
|
unmanaged_vm = unmanaged_vms[0]
|
||||||
|
self.assertEqual(
|
||||||
|
unmanaged_vm.powerstate,
|
||||||
|
"PowerOn",
|
||||||
|
"Unmanaged VM is still running"
|
||||||
|
)
|
||||||
@ -966,6 +966,20 @@ class VirtualMachine:
|
|||||||
cmd.details[0]["memory"] = custommemory
|
cmd.details[0]["memory"] = custommemory
|
||||||
return apiclient.scaleVirtualMachine(cmd)
|
return apiclient.scaleVirtualMachine(cmd)
|
||||||
|
|
||||||
|
def unmanage(self, apiclient):
|
||||||
|
"""Unmanage a VM from CloudStack (currently VMware only)"""
|
||||||
|
cmd = unmanageVirtualMachine.unmanageVirtualMachineCmd()
|
||||||
|
cmd.id = self.id
|
||||||
|
return apiclient.unmanageVirtualMachine(cmd)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def listUnmanagedInstances(cls, apiclient, clusterid, name = None):
|
||||||
|
"""List unmanaged VMs (currently VMware only)"""
|
||||||
|
cmd = listUnmanagedInstances.listUnmanagedInstancesCmd()
|
||||||
|
cmd.clusterid = clusterid
|
||||||
|
cmd.name = name
|
||||||
|
return apiclient.listUnmanagedInstances(cmd)
|
||||||
|
|
||||||
|
|
||||||
class Volume:
|
class Volume:
|
||||||
"""Manage Volume Life cycle
|
"""Manage Volume Life cycle
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user