mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
KVM Ingestion - Import Instance (#7976)
This PR adds new functionality to import KVM instances from an external host or from disk images in local or shared storage. Doc PR: https://github.com/apache/cloudstack-documentation/pull/356
This commit is contained in:
parent
82f7abddb3
commit
ab20b1220f
@ -16,6 +16,7 @@
|
||||
// under the License.
|
||||
package com.cloud.vm;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@ -518,7 +519,8 @@ public interface UserVmService {
|
||||
|
||||
UserVm importVM(final DataCenter zone, final Host host, final VirtualMachineTemplate template, final String instanceName, final String displayName, final Account owner, final String userData, final Account caller, final Boolean isDisplayVm, final String keyboard,
|
||||
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, final LinkedHashMap<String, List<NicProfile>> networkNicMap) throws InsufficientCapacityException;
|
||||
|
||||
/**
|
||||
* Unmanage a guest VM from CloudStack
|
||||
|
||||
@ -39,6 +39,7 @@ public interface VmDetailConstants {
|
||||
// KVM specific (internal)
|
||||
String KVM_VNC_PORT = "kvm.vnc.port";
|
||||
String KVM_VNC_ADDRESS = "kvm.vnc.address";
|
||||
String KVM_VNC_PASSWORD = "kvm.vnc.password";
|
||||
|
||||
// KVM specific, custom virtual GPU hardware
|
||||
String VIDEO_HARDWARE = "video.hardware";
|
||||
|
||||
@ -212,6 +212,7 @@ public class ApiConstants {
|
||||
public static final String HOST_IDS = "hostids";
|
||||
public static final String HOST_IP = "hostip";
|
||||
public static final String HOST_NAME = "hostname";
|
||||
public static final String HOST = "host";
|
||||
public static final String HOST_CONTROL_STATE = "hostcontrolstate";
|
||||
public static final String HOSTS_MAP = "hostsmap";
|
||||
public static final String HYPERVISOR = "hypervisor";
|
||||
@ -1064,7 +1065,9 @@ public class ApiConstants {
|
||||
public static final String SOURCE_NAT_IP = "sourcenatipaddress";
|
||||
public static final String SOURCE_NAT_IP_ID = "sourcenatipaddressid";
|
||||
public static final String HAS_RULES = "hasrules";
|
||||
public static final String DISK_PATH = "diskpath";
|
||||
public static final String IMPORT_SOURCE = "importsource";
|
||||
public static final String TEMP_PATH = "temppath";
|
||||
public static final String OBJECT_STORAGE = "objectstore";
|
||||
|
||||
public static final String HEURISTIC_RULE = "heuristicrule";
|
||||
|
||||
@ -84,7 +84,7 @@ public class ImportUnmanagedInstanceCmd extends BaseAsyncCmd {
|
||||
@Parameter(name = ApiConstants.NAME,
|
||||
type = CommandType.STRING,
|
||||
required = true,
|
||||
description = "the hypervisor name of the instance")
|
||||
description = "the name of the instance as it is known to the hypervisor")
|
||||
private String name;
|
||||
|
||||
@Parameter(name = ApiConstants.DISPLAY_NAME,
|
||||
|
||||
@ -31,13 +31,18 @@ import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.ResponseObject;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.response.HostResponse;
|
||||
import org.apache.cloudstack.api.response.NetworkResponse;
|
||||
import org.apache.cloudstack.api.response.StoragePoolResponse;
|
||||
import org.apache.cloudstack.api.response.UserVmResponse;
|
||||
import org.apache.cloudstack.api.response.VmwareDatacenterResponse;
|
||||
import org.apache.cloudstack.api.response.ZoneResponse;
|
||||
import org.apache.cloudstack.vm.VmImportService;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@APICommand(name = "importVm",
|
||||
description = "Import virtual machine from a unmanaged host into CloudStack",
|
||||
responseObject = UserVmResponse.class,
|
||||
@ -47,21 +52,72 @@ import org.apache.log4j.Logger;
|
||||
authorized = {RoleType.Admin},
|
||||
since = "4.19.0")
|
||||
public class ImportVmCmd extends ImportUnmanagedInstanceCmd {
|
||||
|
||||
public static final Logger LOGGER = Logger.getLogger(ImportVmCmd.class);
|
||||
|
||||
@Inject
|
||||
public VmImportService vmImportService;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
//////////////// API parameters /////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@Parameter(name = ApiConstants.ZONE_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = ZoneResponse.class,
|
||||
required = true,
|
||||
description = "the zone ID")
|
||||
private Long zoneId;
|
||||
|
||||
@Parameter(name = ApiConstants.USERNAME,
|
||||
type = CommandType.STRING,
|
||||
description = "the username for the host")
|
||||
private String username;
|
||||
|
||||
@Parameter(name = ApiConstants.PASSWORD,
|
||||
type = CommandType.STRING,
|
||||
description = "the password for the host")
|
||||
private String password;
|
||||
|
||||
@Parameter(name = ApiConstants.HOST,
|
||||
type = CommandType.STRING,
|
||||
description = "the host name or IP address")
|
||||
private String host;
|
||||
|
||||
@Parameter(name = ApiConstants.HYPERVISOR,
|
||||
type = CommandType.STRING,
|
||||
required = true,
|
||||
description = "hypervisor type of the host")
|
||||
private String hypervisor;
|
||||
|
||||
@Parameter(name = ApiConstants.DISK_PATH,
|
||||
type = CommandType.STRING,
|
||||
description = "path of the disk image")
|
||||
private String diskPath;
|
||||
|
||||
@Parameter(name = ApiConstants.IMPORT_SOURCE,
|
||||
type = CommandType.STRING,
|
||||
required = true,
|
||||
description = "Source location for Import" )
|
||||
private String importSource;
|
||||
|
||||
@Parameter(name = ApiConstants.NETWORK_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = NetworkResponse.class,
|
||||
description = "the network ID")
|
||||
private Long networkId;
|
||||
|
||||
@Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "Host where local disk is located")
|
||||
private Long hostId;
|
||||
|
||||
@Parameter(name = ApiConstants.STORAGE_ID, type = CommandType.UUID, entityType = StoragePoolResponse.class, description = "Shared storage pool where disk is located")
|
||||
private Long storagePoolId;
|
||||
|
||||
@Parameter(name = ApiConstants.TEMP_PATH,
|
||||
type = CommandType.STRING,
|
||||
description = "Temp Path on external host for disk image copy" )
|
||||
private String tmpPath;
|
||||
|
||||
// Import from Vmware to KVM migration parameters
|
||||
|
||||
@Parameter(name = ApiConstants.EXISTING_VCENTER_ID,
|
||||
@ -73,7 +129,7 @@ public class ImportVmCmd extends ImportUnmanagedInstanceCmd {
|
||||
@Parameter(name = ApiConstants.HOST_IP,
|
||||
type = BaseCmd.CommandType.STRING,
|
||||
description = "(only for importing migrated VMs from Vmware to KVM) VMware ESXi host IP/Name.")
|
||||
private String host;
|
||||
private String hostip;
|
||||
|
||||
@Parameter(name = ApiConstants.VCENTER,
|
||||
type = CommandType.STRING,
|
||||
@ -88,14 +144,6 @@ public class ImportVmCmd extends ImportUnmanagedInstanceCmd {
|
||||
description = "(only for importing migrated VMs from Vmware to KVM) Name of VMware cluster.")
|
||||
private String clusterName;
|
||||
|
||||
@Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING,
|
||||
description = "(only for importing migrated VMs from Vmware to KVM) The Username required to connect to resource.")
|
||||
private String username;
|
||||
|
||||
@Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING,
|
||||
description = "(only for importing migrated VMs from Vmware to KVM) The password for the specified username.")
|
||||
private String password;
|
||||
|
||||
@Parameter(name = ApiConstants.CONVERT_INSTANCE_HOST_ID, type = CommandType.UUID, entityType = HostResponse.class,
|
||||
description = "(only for importing migrated VMs from Vmware to KVM) optional - the host to perform the virt-v2v migration from VMware to KVM.")
|
||||
private Long convertInstanceHostId;
|
||||
@ -104,30 +152,20 @@ public class ImportVmCmd extends ImportUnmanagedInstanceCmd {
|
||||
description = "(only for importing migrated VMs from Vmware to KVM) optional - the temporary storage pool to perform the virt-v2v migration from VMware to KVM.")
|
||||
private Long convertStoragePoolId;
|
||||
|
||||
@Override
|
||||
public String getEventType() {
|
||||
return EventTypes.EVENT_VM_IMPORT;
|
||||
}
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public String getEventDescription() {
|
||||
String vmName = getName();
|
||||
if (ObjectUtils.anyNotNull(vcenter, existingVcenterId)) {
|
||||
String msg = StringUtils.isNotBlank(vcenter) ?
|
||||
String.format("external vCenter: %s - datacenter: %s", vcenter, datacenterName) :
|
||||
String.format("existing vCenter Datacenter with ID: %s", existingVcenterId);
|
||||
return String.format("Importing unmanaged VM: %s from %s - VM: %s", getDisplayName(), msg, vmName);
|
||||
public Long getZoneId() {
|
||||
return zoneId;
|
||||
}
|
||||
return String.format("Importing unmanaged VM: %s", vmName);
|
||||
}
|
||||
|
||||
|
||||
public Long getExistingVcenterId() {
|
||||
return existingVcenterId;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
public String getHostIp() {
|
||||
return hostip;
|
||||
}
|
||||
|
||||
public String getVcenter() {
|
||||
@ -150,6 +188,10 @@ public class ImportVmCmd extends ImportUnmanagedInstanceCmd {
|
||||
return password;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public Long getConvertInstanceHostId() {
|
||||
return convertInstanceHostId;
|
||||
}
|
||||
@ -162,10 +204,47 @@ public class ImportVmCmd extends ImportUnmanagedInstanceCmd {
|
||||
return hypervisor;
|
||||
}
|
||||
|
||||
public String getDiskPath() {
|
||||
return diskPath;
|
||||
}
|
||||
|
||||
public String getImportSource() {
|
||||
return importSource;
|
||||
}
|
||||
|
||||
public Long getHostId() {
|
||||
return hostId;
|
||||
}
|
||||
|
||||
public Long getStoragePoolId() {
|
||||
return storagePoolId;
|
||||
}
|
||||
|
||||
public String getTmpPath() {
|
||||
return tmpPath;
|
||||
}
|
||||
|
||||
public Long getNetworkId() {
|
||||
return networkId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventType() {
|
||||
return EventTypes.EVENT_VM_IMPORT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventDescription() {
|
||||
String vmName = getName();
|
||||
if (ObjectUtils.anyNotNull(vcenter, existingVcenterId)) {
|
||||
String msg = StringUtils.isNotBlank(vcenter) ?
|
||||
String.format("external vCenter: %s - datacenter: %s", vcenter, datacenterName) :
|
||||
String.format("existing vCenter Datacenter with ID: %s", existingVcenterId);
|
||||
return String.format("Importing unmanaged VM: %s from %s - VM: %s", getDisplayName(), msg, vmName);
|
||||
}
|
||||
return String.format("Importing unmanaged VM: %s", vmName);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
@ -176,5 +255,4 @@ public class ImportVmCmd extends ImportUnmanagedInstanceCmd {
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,134 @@
|
||||
// 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.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 org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseListCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.ResponseObject;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.UnmanagedInstanceResponse;
|
||||
import org.apache.cloudstack.api.response.ZoneResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
|
||||
import org.apache.cloudstack.vm.VmImportService;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@APICommand(name = "listVmsForImport",
|
||||
description = "Lists virtual machines on a unmanaged host",
|
||||
responseObject = UnmanagedInstanceResponse.class,
|
||||
responseView = ResponseObject.ResponseView.Full,
|
||||
entityType = {UnmanagedInstanceTO.class},
|
||||
requestHasSensitiveInfo = false,
|
||||
responseHasSensitiveInfo = true,
|
||||
authorized = {RoleType.Admin},
|
||||
since = "4.19.0")
|
||||
public class ListVmsForImportCmd extends BaseListCmd {
|
||||
public static final Logger LOGGER = Logger.getLogger(ListVmsForImportCmd.class.getName());
|
||||
|
||||
@Inject
|
||||
public VmImportService vmImportService;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
//////////////// API parameters /////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@Parameter(name = ApiConstants.ZONE_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = ZoneResponse.class,
|
||||
required = true,
|
||||
description = "the zone ID")
|
||||
private Long zoneId;
|
||||
|
||||
@Parameter(name = ApiConstants.USERNAME,
|
||||
type = CommandType.STRING,
|
||||
description = "the username for the host")
|
||||
private String username;
|
||||
|
||||
@Parameter(name = ApiConstants.PASSWORD,
|
||||
type = CommandType.STRING,
|
||||
description = "the password for the host")
|
||||
private String password;
|
||||
|
||||
@Parameter(name = ApiConstants.HOST,
|
||||
type = CommandType.STRING,
|
||||
required = true,
|
||||
description = "the host name or IP address")
|
||||
private String host;
|
||||
|
||||
@Parameter(name = ApiConstants.HYPERVISOR,
|
||||
type = CommandType.STRING,
|
||||
required = true,
|
||||
description = "hypervisor type of the host")
|
||||
private String hypervisor;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
public Long getZoneId() {
|
||||
return zoneId;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public String getHypervisor() {
|
||||
return hypervisor;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
|
||||
ListResponse<UnmanagedInstanceResponse> response = vmImportService.listVmsForImport(this);
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
Account account = CallContext.current().getCallingAccount();
|
||||
if (account != null) {
|
||||
return account.getId();
|
||||
}
|
||||
return Account.ACCOUNT_ID_SYSTEM;
|
||||
}
|
||||
}
|
||||
@ -55,6 +55,8 @@ public class UnmanagedInstanceTO {
|
||||
|
||||
private List<Nic> nics;
|
||||
|
||||
private String vncPassword;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
@ -167,6 +169,14 @@ public class UnmanagedInstanceTO {
|
||||
this.nics = nics;
|
||||
}
|
||||
|
||||
public String getVncPassword() {
|
||||
return vncPassword;
|
||||
}
|
||||
|
||||
public void setVncPassword(String vncPassword) {
|
||||
this.vncPassword = vncPassword;
|
||||
}
|
||||
|
||||
public static class Disk {
|
||||
private String diskId;
|
||||
|
||||
@ -192,6 +202,8 @@ public class UnmanagedInstanceTO {
|
||||
|
||||
private String datastorePath;
|
||||
|
||||
private int datastorePort;
|
||||
|
||||
private String datastoreType;
|
||||
|
||||
public String getDiskId() {
|
||||
@ -297,6 +309,14 @@ public class UnmanagedInstanceTO {
|
||||
public void setDatastoreType(String datastoreType) {
|
||||
this.datastoreType = datastoreType;
|
||||
}
|
||||
|
||||
public void setDatastorePort(int datastorePort) {
|
||||
this.datastorePort = datastorePort;
|
||||
}
|
||||
|
||||
public int getDatastorePort() {
|
||||
return datastorePort;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Nic {
|
||||
|
||||
@ -17,13 +17,20 @@
|
||||
|
||||
package org.apache.cloudstack.vm;
|
||||
|
||||
import com.cloud.hypervisor.Hypervisor;
|
||||
import com.cloud.utils.component.PluggableService;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.config.Configurable;
|
||||
import static com.cloud.hypervisor.Hypervisor.HypervisorType.KVM;
|
||||
import static com.cloud.hypervisor.Hypervisor.HypervisorType.VMware;
|
||||
|
||||
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);
|
||||
|
||||
static boolean isSupported(Hypervisor.HypervisorType hypervisorType) {
|
||||
return hypervisorType == VMware || hypervisorType == KVM;
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,6 +20,7 @@ package org.apache.cloudstack.vm;
|
||||
import org.apache.cloudstack.api.command.admin.vm.ImportUnmanagedInstanceCmd;
|
||||
import org.apache.cloudstack.api.command.admin.vm.ImportVmCmd;
|
||||
import org.apache.cloudstack.api.command.admin.vm.ListUnmanagedInstancesCmd;
|
||||
import org.apache.cloudstack.api.command.admin.vm.ListVmsForImportCmd;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.UnmanagedInstanceResponse;
|
||||
import org.apache.cloudstack.api.response.UserVmResponse;
|
||||
@ -37,5 +38,8 @@ public interface VmImportService {
|
||||
|
||||
ListResponse<UnmanagedInstanceResponse> listUnmanagedInstances(ListUnmanagedInstancesCmd cmd);
|
||||
UserVmResponse importUnmanagedInstance(ImportUnmanagedInstanceCmd cmd);
|
||||
|
||||
UserVmResponse importVm(ImportVmCmd cmd);
|
||||
|
||||
ListResponse<UnmanagedInstanceResponse> listVmsForImport(ListVmsForImportCmd cmd);
|
||||
}
|
||||
|
||||
@ -0,0 +1,40 @@
|
||||
// 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;
|
||||
|
||||
@LogLevel(LogLevel.Log4jLevel.Trace)
|
||||
public class CheckVolumeAnswer extends Answer {
|
||||
|
||||
private long size;
|
||||
|
||||
CheckVolumeAnswer() {
|
||||
}
|
||||
|
||||
public CheckVolumeAnswer(CheckVolumeCommand cmd, String details, long size) {
|
||||
super(cmd, true, details);
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public String getString() {
|
||||
return "CheckVolumeAnswer [size=" + size + "]";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
//
|
||||
// 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;
|
||||
|
||||
import com.cloud.agent.api.to.StorageFilerTO;
|
||||
|
||||
@LogLevel(LogLevel.Log4jLevel.Trace)
|
||||
public class CheckVolumeCommand extends Command {
|
||||
|
||||
String srcFile;
|
||||
|
||||
StorageFilerTO storageFilerTO;
|
||||
|
||||
|
||||
public String getSrcFile() {
|
||||
return srcFile;
|
||||
}
|
||||
|
||||
public void setSrcFile(String srcFile) {
|
||||
this.srcFile = srcFile;
|
||||
}
|
||||
|
||||
public CheckVolumeCommand() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeInSequence() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getString() {
|
||||
return "CheckVolumeCommand [srcFile=" + srcFile + "]";
|
||||
}
|
||||
|
||||
public StorageFilerTO getStorageFilerTO() {
|
||||
return storageFilerTO;
|
||||
}
|
||||
|
||||
public void setStorageFilerTO(StorageFilerTO storageFilerTO) {
|
||||
this.storageFilerTO = storageFilerTO;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
// 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;
|
||||
|
||||
@LogLevel(LogLevel.Log4jLevel.Trace)
|
||||
public class CopyRemoteVolumeAnswer extends Answer {
|
||||
|
||||
private String remoteIp;
|
||||
private String filename;
|
||||
|
||||
private long size;
|
||||
|
||||
CopyRemoteVolumeAnswer() {
|
||||
}
|
||||
|
||||
public CopyRemoteVolumeAnswer(CopyRemoteVolumeCommand cmd, String details, String filename, long size) {
|
||||
super(cmd, true, details);
|
||||
this.remoteIp = cmd.getRemoteIp();
|
||||
this.filename = filename;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public String getRemoteIp() {
|
||||
return remoteIp;
|
||||
}
|
||||
|
||||
public void setRemoteIp(String remoteIp) {
|
||||
this.remoteIp = remoteIp;
|
||||
}
|
||||
|
||||
public void setFilename(String filename) {
|
||||
this.filename = filename;
|
||||
}
|
||||
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public String getString() {
|
||||
return "CopyRemoteVolumeAnswer [remoteIp=" + remoteIp + "]";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,101 @@
|
||||
//
|
||||
// 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;
|
||||
|
||||
import com.cloud.agent.api.to.StorageFilerTO;
|
||||
|
||||
@LogLevel(LogLevel.Log4jLevel.Trace)
|
||||
public class CopyRemoteVolumeCommand extends Command {
|
||||
|
||||
String remoteIp;
|
||||
String username;
|
||||
String password;
|
||||
String srcFile;
|
||||
|
||||
String tmpPath;
|
||||
|
||||
StorageFilerTO storageFilerTO;
|
||||
|
||||
public CopyRemoteVolumeCommand(String remoteIp, String username, String password) {
|
||||
this.remoteIp = remoteIp;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getRemoteIp() {
|
||||
return remoteIp;
|
||||
}
|
||||
|
||||
public void setRemoteIp(String remoteIp) {
|
||||
this.remoteIp = remoteIp;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getSrcFile() {
|
||||
return srcFile;
|
||||
}
|
||||
|
||||
public void setSrcFile(String srcFile) {
|
||||
this.srcFile = srcFile;
|
||||
}
|
||||
|
||||
public CopyRemoteVolumeCommand() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeInSequence() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getString() {
|
||||
return "CopyRemoteVolumeCommand [remoteIp=" + remoteIp + "]";
|
||||
}
|
||||
|
||||
public void setTempPath(String tmpPath) {
|
||||
this.tmpPath = tmpPath;
|
||||
}
|
||||
|
||||
public String getTmpPath() {
|
||||
return tmpPath;
|
||||
}
|
||||
|
||||
public StorageFilerTO getStorageFilerTO() {
|
||||
return storageFilerTO;
|
||||
}
|
||||
|
||||
public void setStorageFilerTO(StorageFilerTO storageFilerTO) {
|
||||
this.storageFilerTO = storageFilerTO;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
// 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;
|
||||
|
||||
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
@LogLevel(LogLevel.Log4jLevel.Trace)
|
||||
public class GetRemoteVmsAnswer extends Answer {
|
||||
|
||||
private String remoteIp;
|
||||
private HashMap<String, UnmanagedInstanceTO> unmanagedInstances;
|
||||
|
||||
List<String> vmNames;
|
||||
|
||||
GetRemoteVmsAnswer() {
|
||||
}
|
||||
|
||||
public GetRemoteVmsAnswer(GetRemoteVmsCommand cmd, String details, HashMap<String, UnmanagedInstanceTO> unmanagedInstances) {
|
||||
super(cmd, true, details);
|
||||
this.remoteIp = cmd.getRemoteIp();
|
||||
this.unmanagedInstances = unmanagedInstances;
|
||||
}
|
||||
|
||||
public GetRemoteVmsAnswer(GetRemoteVmsCommand cmd, String details, List<String> vmNames) {
|
||||
super(cmd, true, details);
|
||||
this.remoteIp = cmd.getRemoteIp();
|
||||
this.vmNames = vmNames;
|
||||
}
|
||||
|
||||
public String getRemoteIp() {
|
||||
return remoteIp;
|
||||
}
|
||||
|
||||
public void setRemoteIp(String remoteIp) {
|
||||
this.remoteIp = remoteIp;
|
||||
}
|
||||
|
||||
public HashMap<String, UnmanagedInstanceTO> getUnmanagedInstances() {
|
||||
return unmanagedInstances;
|
||||
}
|
||||
|
||||
public void setUnmanagedInstances(HashMap<String, UnmanagedInstanceTO> unmanagedInstances) {
|
||||
this.unmanagedInstances = unmanagedInstances;
|
||||
}
|
||||
|
||||
public List<String> getVmNames() {
|
||||
return vmNames;
|
||||
}
|
||||
|
||||
public void setVmNames(List<String> vmNames) {
|
||||
this.vmNames = vmNames;
|
||||
}
|
||||
|
||||
public String getString() {
|
||||
return "GetRemoteVmsAnswer [remoteIp=" + remoteIp + "]";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
//
|
||||
// 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;
|
||||
|
||||
@LogLevel(LogLevel.Log4jLevel.Trace)
|
||||
public class GetRemoteVmsCommand extends Command {
|
||||
|
||||
String remoteIp;
|
||||
String username;
|
||||
String password;
|
||||
|
||||
public GetRemoteVmsCommand(String remoteIp, String username, String password) {
|
||||
this.remoteIp = remoteIp;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getRemoteIp() {
|
||||
return remoteIp;
|
||||
}
|
||||
|
||||
public void setRemoteIp(String remoteIp) {
|
||||
this.remoteIp = remoteIp;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public GetRemoteVmsCommand() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeInSequence() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getString() {
|
||||
return "GetRemoteVmsCommand [remoteIp=" + remoteIp + "]";
|
||||
}
|
||||
}
|
||||
@ -30,6 +30,10 @@ public class GetUnmanagedInstancesAnswer extends Answer {
|
||||
GetUnmanagedInstancesAnswer() {
|
||||
}
|
||||
|
||||
public GetUnmanagedInstancesAnswer(GetUnmanagedInstancesCommand cmd, String details) {
|
||||
super(cmd, false, details);
|
||||
}
|
||||
|
||||
public GetUnmanagedInstancesAnswer(GetUnmanagedInstancesCommand cmd, String details, HashMap<String, UnmanagedInstanceTO> unmanagedInstances) {
|
||||
super(cmd, true, details);
|
||||
this.instanceName = cmd.getInstanceName();
|
||||
|
||||
@ -168,6 +168,9 @@ public interface VolumeOrchestrationService {
|
||||
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);
|
||||
|
||||
DiskProfile updateImportedVolume(Type type, DiskOffering offering, VirtualMachine vm, VirtualMachineTemplate template,
|
||||
Long deviceId, Long poolId, String path, String chainInfo, DiskProfile diskProfile);
|
||||
|
||||
/**
|
||||
* Unmanage VM volumes
|
||||
*/
|
||||
|
||||
@ -2224,6 +2224,51 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
|
||||
return toDiskProfile(vol, offering);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DiskProfile updateImportedVolume(Type type, DiskOffering offering, VirtualMachine vm, VirtualMachineTemplate template,
|
||||
Long deviceId, Long poolId, String path, String chainInfo, DiskProfile diskProfile) {
|
||||
|
||||
VolumeVO vol = _volsDao.findById(diskProfile.getVolumeId());
|
||||
if (vm != null) {
|
||||
vol.setInstanceId(vm.getId());
|
||||
}
|
||||
|
||||
if (deviceId != null) {
|
||||
vol.setDeviceId(deviceId);
|
||||
} else if (type.equals(Type.ROOT)) {
|
||||
vol.setDeviceId(0l);
|
||||
} else {
|
||||
vol.setDeviceId(1l);
|
||||
}
|
||||
|
||||
if (template != null) {
|
||||
if (ImageFormat.ISO.equals(template.getFormat())) {
|
||||
vol.setIsoId(template.getId());
|
||||
} else if (Storage.TemplateType.DATADISK.equals(template.getTemplateType())) {
|
||||
vol.setTemplateId(template.getId());
|
||||
}
|
||||
if (type == Type.ROOT) {
|
||||
vol.setTemplateId(template.getId());
|
||||
}
|
||||
}
|
||||
|
||||
// display flag matters only for the User vms
|
||||
if (VirtualMachine.Type.User.equals(vm.getType())) {
|
||||
UserVmVO userVm = _userVmDao.findById(vm.getId());
|
||||
vol.setDisplayVolume(userVm.isDisplayVm());
|
||||
}
|
||||
|
||||
vol.setFormat(getSupportedImageFormatForCluster(vm.getHypervisorType()));
|
||||
vol.setPoolId(poolId);
|
||||
vol.setPath(path);
|
||||
vol.setChainInfo(chainInfo);
|
||||
vol.setSize(diskProfile.getSize());
|
||||
vol.setState(Volume.State.Ready);
|
||||
vol.setAttached(new Date());
|
||||
_volsDao.update(vol.getId(), vol);
|
||||
return toDiskProfile(vol, offering);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unmanageVolumes(long vmId) {
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
|
||||
@ -152,5 +152,7 @@ public interface VolumeDao extends GenericDao<VolumeVO, Long>, StateDao<Volume.S
|
||||
|
||||
List<VolumeVO> listByPoolIdAndPaths(long id, List<String> pathList);
|
||||
|
||||
VolumeVO findByPoolIdAndPath(long id, String path);
|
||||
|
||||
List<VolumeVO> listByIds(List<Long> ids);
|
||||
}
|
||||
|
||||
@ -71,6 +71,7 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
|
||||
protected GenericSearchBuilder<VolumeVO, SumCount> primaryStorageSearch;
|
||||
protected GenericSearchBuilder<VolumeVO, SumCount> primaryStorageSearch2;
|
||||
protected GenericSearchBuilder<VolumeVO, SumCount> secondaryStorageSearch;
|
||||
private final SearchBuilder<VolumeVO> poolAndPathSearch;
|
||||
@Inject
|
||||
ResourceTagDao _tagsDao;
|
||||
|
||||
@ -487,6 +488,11 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
|
||||
volumeIdSearch.and("idIN", volumeIdSearch.entity().getId(), Op.IN);
|
||||
volumeIdSearch.done();
|
||||
|
||||
poolAndPathSearch = createSearchBuilder();
|
||||
poolAndPathSearch.and("poolId", poolAndPathSearch.entity().getPoolId(), Op.EQ);
|
||||
poolAndPathSearch.and("path", poolAndPathSearch.entity().getPath(), Op.EQ);
|
||||
poolAndPathSearch.done();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -802,6 +808,14 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VolumeVO findByPoolIdAndPath(long id, String path) {
|
||||
SearchCriteria<VolumeVO> sc = poolAndPathSearch.create();
|
||||
sc.setParameters("poolId", id);
|
||||
sc.setParameters("path", path);
|
||||
return findOneBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<VolumeVO> listByIds(List<Long> ids) {
|
||||
if (CollectionUtils.isEmpty(ids)) {
|
||||
|
||||
@ -2295,7 +2295,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
return new Pair<Map<String, Integer>, Integer>(macAddressToNicNum, devNum);
|
||||
}
|
||||
|
||||
protected PowerState convertToPowerState(final DomainState ps) {
|
||||
public PowerState convertToPowerState(final DomainState ps) {
|
||||
final PowerState state = POWER_STATES_TABLE.get(ps);
|
||||
return state == null ? PowerState.PowerUnknown : state;
|
||||
}
|
||||
@ -3777,7 +3777,39 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
}
|
||||
}
|
||||
|
||||
protected List<String> getAllVmNames(final Connect conn) {
|
||||
/**
|
||||
* Given a disk path on KVM host, attempts to find source host and path using mount command
|
||||
* @param diskPath KVM host path for virtual disk
|
||||
* @return Pair with IP of host and path
|
||||
*/
|
||||
public Pair<String, String> getSourceHostPath(String diskPath) {
|
||||
String sourceHostIp = null;
|
||||
String sourcePath = null;
|
||||
try {
|
||||
String mountResult = Script.runSimpleBashScript("mount | grep \"" + diskPath + "\"");
|
||||
s_logger.debug("Got mount result for " + diskPath + "\n\n" + mountResult);
|
||||
if (StringUtils.isNotEmpty(mountResult)) {
|
||||
String[] res = mountResult.strip().split(" ");
|
||||
if (res[0].contains(":")) {
|
||||
res = res[0].split(":");
|
||||
sourceHostIp = res[0].strip();
|
||||
sourcePath = res[1].strip();
|
||||
} else {
|
||||
// Assume local storage
|
||||
sourceHostIp = getPrivateIp();
|
||||
sourcePath = diskPath;
|
||||
}
|
||||
}
|
||||
if (StringUtils.isNotEmpty(sourceHostIp) && StringUtils.isNotEmpty(sourcePath)) {
|
||||
return new Pair<>(sourceHostIp, sourcePath);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
s_logger.warn("Failed to list source host and IP for " + diskPath + ex.toString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<String> getAllVmNames(final Connect conn) {
|
||||
final ArrayList<String> la = new ArrayList<String>();
|
||||
try {
|
||||
final String names[] = conn.listDefinedDomains();
|
||||
@ -5339,4 +5371,25 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Scp volume from remote host to local directory
|
||||
*/
|
||||
public String copyVolume(String srcIp, String username, String password, String localDir, String remoteFile, String tmpPath) {
|
||||
try {
|
||||
String outputFile = UUID.randomUUID().toString();
|
||||
StringBuilder command = new StringBuilder("qemu-img convert -O qcow2 ");
|
||||
command.append(remoteFile);
|
||||
command.append(" "+tmpPath);
|
||||
command.append(outputFile);
|
||||
s_logger.debug("Converting remoteFile: "+remoteFile);
|
||||
SshHelper.sshExecute(srcIp, 22, username, null, password, command.toString());
|
||||
s_logger.debug("Copying remoteFile to: "+localDir);
|
||||
SshHelper.scpFrom(srcIp, 22, username, null, password, localDir, tmpPath+outputFile);
|
||||
s_logger.debug("Successfully copyied remoteFile to: "+localDir+"/"+outputFile);
|
||||
return outputFile;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,8 +57,10 @@ public class LibvirtDomainXMLParser {
|
||||
private final List<ChannelDef> channels = new ArrayList<ChannelDef>();
|
||||
private final List<WatchDogDef> watchDogDefs = new ArrayList<WatchDogDef>();
|
||||
private Integer vncPort;
|
||||
private String vncPasswd;
|
||||
private String desc;
|
||||
|
||||
private LibvirtVMDef.CpuTuneDef cpuTuneDef;
|
||||
private LibvirtVMDef.CpuModeDef cpuModeDef;
|
||||
private String name;
|
||||
|
||||
public boolean parseDomainXML(String domXML) {
|
||||
@ -278,6 +280,14 @@ public class LibvirtDomainXMLParser {
|
||||
String name = getAttrValue("target", "name", channel);
|
||||
String state = getAttrValue("target", "state", channel);
|
||||
|
||||
if (ChannelDef.ChannelType.valueOf(type.toUpperCase()).equals(ChannelDef.ChannelType.SPICEVMC)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (path == null) {
|
||||
path = "";
|
||||
}
|
||||
|
||||
ChannelDef def = null;
|
||||
if (StringUtils.isBlank(state)) {
|
||||
def = new ChannelDef(name, ChannelDef.ChannelType.valueOf(type.toUpperCase()), new File(path));
|
||||
@ -305,6 +315,12 @@ public class LibvirtDomainXMLParser {
|
||||
vncPort = null;
|
||||
}
|
||||
}
|
||||
|
||||
String passwd = graphic.getAttribute("passwd");
|
||||
if (passwd != null) {
|
||||
vncPasswd = passwd;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
NodeList rngs = devices.getElementsByTagName("rng");
|
||||
@ -317,6 +333,26 @@ public class LibvirtDomainXMLParser {
|
||||
String period = getAttrValue("rate", "period", rng);
|
||||
if (StringUtils.isAnyEmpty(bytes, period)) {
|
||||
s_logger.debug(String.format("Bytes and period in the rng section should not be null, please check the VM %s", name));
|
||||
}
|
||||
|
||||
if (bytes == null) {
|
||||
bytes = "0";
|
||||
}
|
||||
|
||||
if (period == null) {
|
||||
period = "0";
|
||||
}
|
||||
|
||||
if (bytes == null) {
|
||||
bytes = "0";
|
||||
}
|
||||
|
||||
if (period == null) {
|
||||
period = "0";
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(backendModel)) {
|
||||
def = new RngDef(path, Integer.parseInt(bytes), Integer.parseInt(period));
|
||||
} else {
|
||||
if (StringUtils.isEmpty(backendModel)) {
|
||||
def = new RngDef(path, Integer.parseInt(bytes), Integer.parseInt(period));
|
||||
@ -350,7 +386,8 @@ public class LibvirtDomainXMLParser {
|
||||
|
||||
watchDogDefs.add(def);
|
||||
}
|
||||
|
||||
extractCpuTuneDef(rootElement);
|
||||
extractCpuModeDef(rootElement);
|
||||
return true;
|
||||
} catch (ParserConfigurationException e) {
|
||||
s_logger.debug(e.toString());
|
||||
@ -411,6 +448,10 @@ public class LibvirtDomainXMLParser {
|
||||
return interfaces;
|
||||
}
|
||||
|
||||
public String getVncPasswd() {
|
||||
return vncPasswd;
|
||||
}
|
||||
|
||||
public MemBalloonDef getMemBalloon() {
|
||||
return memBalloonDef;
|
||||
}
|
||||
@ -438,4 +479,65 @@ public class LibvirtDomainXMLParser {
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public LibvirtVMDef.CpuTuneDef getCpuTuneDef() {
|
||||
return cpuTuneDef;
|
||||
}
|
||||
|
||||
public LibvirtVMDef.CpuModeDef getCpuModeDef() {
|
||||
return cpuModeDef;
|
||||
}
|
||||
|
||||
private void extractCpuTuneDef(final Element rootElement) {
|
||||
NodeList cpuTunesList = rootElement.getElementsByTagName("cputune");
|
||||
if (cpuTunesList.getLength() > 0) {
|
||||
cpuTuneDef = new LibvirtVMDef.CpuTuneDef();
|
||||
final Element cpuTuneDefElement = (Element) cpuTunesList.item(0);
|
||||
final String cpuShares = getTagValue("shares", cpuTuneDefElement);
|
||||
if (StringUtils.isNotBlank(cpuShares)) {
|
||||
cpuTuneDef.setShares((Integer.parseInt(cpuShares)));
|
||||
}
|
||||
|
||||
final String quota = getTagValue("quota", cpuTuneDefElement);
|
||||
if (StringUtils.isNotBlank(quota)) {
|
||||
cpuTuneDef.setQuota((Integer.parseInt(quota)));
|
||||
}
|
||||
|
||||
final String period = getTagValue("period", cpuTuneDefElement);
|
||||
if (StringUtils.isNotBlank(period)) {
|
||||
cpuTuneDef.setPeriod((Integer.parseInt(period)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void extractCpuModeDef(final Element rootElement){
|
||||
NodeList cpuModeList = rootElement.getElementsByTagName("cpu");
|
||||
if (cpuModeList.getLength() > 0){
|
||||
cpuModeDef = new LibvirtVMDef.CpuModeDef();
|
||||
final Element cpuModeDefElement = (Element) cpuModeList.item(0);
|
||||
final String cpuModel = getTagValue("model", cpuModeDefElement);
|
||||
if (StringUtils.isNotBlank(cpuModel)){
|
||||
cpuModeDef.setModel(cpuModel);
|
||||
}
|
||||
NodeList cpuFeatures = cpuModeDefElement.getElementsByTagName("features");
|
||||
if (cpuFeatures.getLength() > 0) {
|
||||
final ArrayList<String> features = new ArrayList<>(cpuFeatures.getLength());
|
||||
for (int i = 0; i < cpuFeatures.getLength(); i++) {
|
||||
final Element feature = (Element)cpuFeatures.item(i);
|
||||
final String policy = feature.getAttribute("policy");
|
||||
String featureName = feature.getAttribute("name");
|
||||
if ("disable".equals(policy)) {
|
||||
featureName = "-" + featureName;
|
||||
}
|
||||
features.add(featureName);
|
||||
}
|
||||
cpuModeDef.setFeatures(features);
|
||||
}
|
||||
final String sockets = getAttrValue("topology", "sockets", cpuModeDefElement);
|
||||
final String cores = getAttrValue("topology", "cores", cpuModeDefElement);
|
||||
if (StringUtils.isNotBlank(sockets) && StringUtils.isNotBlank(cores)) {
|
||||
cpuModeDef.setTopology(Integer.parseInt(cores), Integer.parseInt(sockets));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1072,6 +1072,18 @@ public class LibvirtVMDef {
|
||||
|
||||
public LibvirtDiskEncryptDetails getLibvirtDiskEncryptDetails() { return this.encryptDetails; }
|
||||
|
||||
public String getSourceHost() {
|
||||
return _sourceHost;
|
||||
}
|
||||
|
||||
public int getSourceHostPort() {
|
||||
return _sourcePort;
|
||||
}
|
||||
|
||||
public String getSourcePath() {
|
||||
return _sourcePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder diskBuilder = new StringBuilder();
|
||||
@ -1737,6 +1749,10 @@ public class LibvirtVMDef {
|
||||
modeBuilder.append("</cpu>");
|
||||
return modeBuilder.toString();
|
||||
}
|
||||
|
||||
public int getCoresPerSocket() {
|
||||
return _coresPerSocket;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SerialDef {
|
||||
@ -1793,7 +1809,7 @@ public class LibvirtVMDef {
|
||||
|
||||
public final static class ChannelDef {
|
||||
enum ChannelType {
|
||||
UNIX("unix"), SERIAL("serial");
|
||||
UNIX("unix"), SERIAL("serial"), SPICEVMC("spicevmc");
|
||||
String type;
|
||||
|
||||
ChannelType(String type) {
|
||||
|
||||
@ -0,0 +1,86 @@
|
||||
//
|
||||
// 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.hypervisor.kvm.resource.wrapper;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.CheckVolumeAnswer;
|
||||
import com.cloud.agent.api.CheckVolumeCommand;
|
||||
import com.cloud.agent.api.to.StorageFilerTO;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
|
||||
import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk;
|
||||
import com.cloud.hypervisor.kvm.storage.KVMStoragePool;
|
||||
import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager;
|
||||
import com.cloud.resource.CommandWrapper;
|
||||
import com.cloud.resource.ResourceWrapper;
|
||||
import com.cloud.storage.Storage;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import org.apache.cloudstack.utils.qemu.QemuImg;
|
||||
import org.apache.cloudstack.utils.qemu.QemuImgException;
|
||||
import org.apache.cloudstack.utils.qemu.QemuImgFile;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.libvirt.LibvirtException;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@ResourceWrapper(handles = CheckVolumeCommand.class)
|
||||
public final class LibvirtCheckVolumeCommandWrapper extends CommandWrapper<CheckVolumeCommand, Answer, LibvirtComputingResource> {
|
||||
|
||||
private static final Logger s_logger = Logger.getLogger(LibvirtCheckVolumeCommandWrapper.class);
|
||||
|
||||
@Override
|
||||
public Answer execute(final CheckVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) {
|
||||
String result = null;
|
||||
String srcFile = command.getSrcFile();
|
||||
StorageFilerTO storageFilerTO = command.getStorageFilerTO();
|
||||
KVMStoragePoolManager poolMgr = libvirtComputingResource.getStoragePoolMgr();
|
||||
KVMStoragePool pool = poolMgr.getStoragePool(storageFilerTO.getType(), storageFilerTO.getUuid());
|
||||
|
||||
try {
|
||||
if (storageFilerTO.getType() == Storage.StoragePoolType.Filesystem ||
|
||||
storageFilerTO.getType() == Storage.StoragePoolType.NetworkFilesystem) {
|
||||
final KVMPhysicalDisk vol = pool.getPhysicalDisk(srcFile);
|
||||
final String path = vol.getPath();
|
||||
long size = getVirtualSizeFromFile(path);
|
||||
return new CheckVolumeAnswer(command, "", size);
|
||||
} else {
|
||||
return new Answer(command, false, "Unsupported Storage Pool");
|
||||
}
|
||||
|
||||
} catch (final Exception e) {
|
||||
s_logger.error("Error while locating disk: "+ e.getMessage());
|
||||
return new Answer(command, false, result);
|
||||
}
|
||||
}
|
||||
|
||||
private long getVirtualSizeFromFile(String path) {
|
||||
try {
|
||||
QemuImg qemu = new QemuImg(0);
|
||||
QemuImgFile qemuFile = new QemuImgFile(path);
|
||||
Map<String, String> info = qemu.info(qemuFile);
|
||||
if (info.containsKey(QemuImg.VIRTUAL_SIZE)) {
|
||||
return Long.parseLong(info.get(QemuImg.VIRTUAL_SIZE));
|
||||
} else {
|
||||
throw new CloudRuntimeException("Unable to determine virtual size of volume at path " + path);
|
||||
}
|
||||
} catch (QemuImgException | LibvirtException ex) {
|
||||
throw new CloudRuntimeException("Error when inspecting volume at path " + path, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,93 @@
|
||||
//
|
||||
// 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.hypervisor.kvm.resource.wrapper;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.CopyRemoteVolumeAnswer;
|
||||
import com.cloud.agent.api.CopyRemoteVolumeCommand;
|
||||
import com.cloud.agent.api.to.StorageFilerTO;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
|
||||
import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk;
|
||||
import com.cloud.hypervisor.kvm.storage.KVMStoragePool;
|
||||
import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager;
|
||||
import com.cloud.resource.CommandWrapper;
|
||||
import com.cloud.resource.ResourceWrapper;
|
||||
import com.cloud.storage.Storage;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import org.apache.cloudstack.utils.qemu.QemuImg;
|
||||
import org.apache.cloudstack.utils.qemu.QemuImgException;
|
||||
import org.apache.cloudstack.utils.qemu.QemuImgFile;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.libvirt.LibvirtException;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@ResourceWrapper(handles = CopyRemoteVolumeCommand.class)
|
||||
public final class LibvirtCopyRemoteVolumeCommandWrapper extends CommandWrapper<CopyRemoteVolumeCommand, Answer, LibvirtComputingResource> {
|
||||
|
||||
private static final Logger s_logger = Logger.getLogger(LibvirtCopyRemoteVolumeCommandWrapper.class);
|
||||
|
||||
@Override
|
||||
public Answer execute(final CopyRemoteVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) {
|
||||
String result = null;
|
||||
String srcIp = command.getRemoteIp();
|
||||
String username = command.getUsername();
|
||||
String password = command.getPassword();
|
||||
String srcFile = command.getSrcFile();
|
||||
StorageFilerTO storageFilerTO = command.getStorageFilerTO();
|
||||
String tmpPath = command.getTmpPath();
|
||||
KVMStoragePoolManager poolMgr = libvirtComputingResource.getStoragePoolMgr();
|
||||
KVMStoragePool pool = poolMgr.getStoragePool(storageFilerTO.getType(), storageFilerTO.getUuid());
|
||||
String dstPath = pool.getLocalPath();
|
||||
|
||||
try {
|
||||
if (storageFilerTO.getType() == Storage.StoragePoolType.Filesystem ||
|
||||
storageFilerTO.getType() == Storage.StoragePoolType.NetworkFilesystem) {
|
||||
String filename = libvirtComputingResource.copyVolume(srcIp, username, password, dstPath, srcFile, tmpPath);
|
||||
s_logger.debug("Volume Copy Successful");
|
||||
final KVMPhysicalDisk vol = pool.getPhysicalDisk(filename);
|
||||
final String path = vol.getPath();
|
||||
long size = getVirtualSizeFromFile(path);
|
||||
return new CopyRemoteVolumeAnswer(command, "", filename, size);
|
||||
} else {
|
||||
return new Answer(command, false, "Unsupported Storage Pool");
|
||||
}
|
||||
|
||||
} catch (final Exception e) {
|
||||
s_logger.error("Error while copying file from remote host: "+ e.getMessage());
|
||||
return new Answer(command, false, result);
|
||||
}
|
||||
}
|
||||
|
||||
private long getVirtualSizeFromFile(String path) {
|
||||
try {
|
||||
QemuImg qemu = new QemuImg(0);
|
||||
QemuImgFile qemuFile = new QemuImgFile(path);
|
||||
Map<String, String> info = qemu.info(qemuFile);
|
||||
if (info.containsKey(QemuImg.VIRTUAL_SIZE)) {
|
||||
return Long.parseLong(info.get(QemuImg.VIRTUAL_SIZE));
|
||||
} else {
|
||||
throw new CloudRuntimeException("Unable to determine virtual size of volume at path " + path);
|
||||
}
|
||||
} catch (QemuImgException | LibvirtException ex) {
|
||||
throw new CloudRuntimeException("Error when inspecting volume at path " + path, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,194 @@
|
||||
//
|
||||
// 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.hypervisor.kvm.resource.wrapper;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.GetRemoteVmsAnswer;
|
||||
import com.cloud.agent.api.GetRemoteVmsCommand;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtConnection;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtDomainXMLParser;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef;
|
||||
import com.cloud.resource.CommandWrapper;
|
||||
import com.cloud.resource.ResourceWrapper;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.libvirt.Connect;
|
||||
import org.libvirt.Domain;
|
||||
import org.libvirt.DomainBlockInfo;
|
||||
import org.libvirt.DomainInfo;
|
||||
import org.libvirt.LibvirtException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
@ResourceWrapper(handles = GetRemoteVmsCommand.class)
|
||||
public final class LibvirtGetRemoteVmsCommandWrapper extends CommandWrapper<GetRemoteVmsCommand, Answer, LibvirtComputingResource> {
|
||||
|
||||
private static final Logger s_logger = Logger.getLogger(LibvirtGetRemoteVmsCommandWrapper.class);
|
||||
|
||||
@Override
|
||||
public Answer execute(final GetRemoteVmsCommand command, final LibvirtComputingResource libvirtComputingResource) {
|
||||
String result = null;
|
||||
String hypervisorURI = "qemu+tcp://" + command.getRemoteIp() +
|
||||
"/system";
|
||||
HashMap<String, UnmanagedInstanceTO> unmanagedInstances = new HashMap<>();
|
||||
try {
|
||||
Connect conn = LibvirtConnection.getConnection(hypervisorURI);
|
||||
final List<String> allVmNames = libvirtComputingResource.getAllVmNames(conn);
|
||||
for (String name : allVmNames) {
|
||||
final Domain domain = libvirtComputingResource.getDomain(conn, name);
|
||||
|
||||
final DomainInfo.DomainState ps = domain.getInfo().state;
|
||||
|
||||
final VirtualMachine.PowerState state = libvirtComputingResource.convertToPowerState(ps);
|
||||
|
||||
s_logger.debug("VM " + domain.getName() + ": powerstate = " + ps + "; vm state=" + state.toString());
|
||||
|
||||
if (state == VirtualMachine.PowerState.PowerOff) {
|
||||
try {
|
||||
UnmanagedInstanceTO instance = getUnmanagedInstance(libvirtComputingResource, domain, conn);
|
||||
unmanagedInstances.put(instance.getName(), instance);
|
||||
} catch (Exception e) {
|
||||
s_logger.error("Error while fetching instance details", e);
|
||||
}
|
||||
}
|
||||
domain.free();
|
||||
}
|
||||
s_logger.debug("Found Vms: "+ unmanagedInstances.size());
|
||||
return new GetRemoteVmsAnswer(command, "", unmanagedInstances);
|
||||
} catch (final LibvirtException e) {
|
||||
s_logger.error("Error while listing stopped Vms on remote host: "+ e.getMessage());
|
||||
return new Answer(command, false, result);
|
||||
}
|
||||
}
|
||||
|
||||
private UnmanagedInstanceTO getUnmanagedInstance(LibvirtComputingResource libvirtComputingResource, Domain domain, Connect conn) {
|
||||
try {
|
||||
final LibvirtDomainXMLParser parser = new LibvirtDomainXMLParser();
|
||||
parser.parseDomainXML(domain.getXMLDesc(1));
|
||||
|
||||
final UnmanagedInstanceTO instance = new UnmanagedInstanceTO();
|
||||
instance.setName(domain.getName());
|
||||
if (parser.getCpuModeDef() != null) {
|
||||
instance.setCpuCoresPerSocket(parser.getCpuModeDef().getCoresPerSocket());
|
||||
}
|
||||
Long memory = domain.getMaxMemory();
|
||||
instance.setMemory(memory.intValue()/1024);
|
||||
if (parser.getCpuTuneDef() !=null) {
|
||||
instance.setCpuSpeed(parser.getCpuTuneDef().getShares());
|
||||
}
|
||||
instance.setPowerState(getPowerState(libvirtComputingResource.getVmState(conn,domain.getName())));
|
||||
instance.setNics(getUnmanagedInstanceNics(parser.getInterfaces()));
|
||||
instance.setDisks(getUnmanagedInstanceDisks(parser.getDisks(),libvirtComputingResource, domain));
|
||||
instance.setVncPassword(parser.getVncPasswd() + "aaaaaaaaaaaaaa"); // Suffix back extra characters for DB compatibility
|
||||
|
||||
return instance;
|
||||
} catch (Exception e) {
|
||||
s_logger.debug("Unable to retrieve unmanaged instance info. ", e);
|
||||
throw new CloudRuntimeException("Unable to retrieve unmanaged instance info. " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private UnmanagedInstanceTO.PowerState getPowerState(VirtualMachine.PowerState vmPowerState) {
|
||||
switch (vmPowerState) {
|
||||
case PowerOn:
|
||||
return UnmanagedInstanceTO.PowerState.PowerOn;
|
||||
case PowerOff:
|
||||
return UnmanagedInstanceTO.PowerState.PowerOff;
|
||||
default:
|
||||
return UnmanagedInstanceTO.PowerState.PowerUnknown;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private List<UnmanagedInstanceTO.Nic> getUnmanagedInstanceNics(List<LibvirtVMDef.InterfaceDef> interfaces) {
|
||||
final ArrayList<UnmanagedInstanceTO.Nic> nics = new ArrayList<>(interfaces.size());
|
||||
int counter = 0;
|
||||
for (LibvirtVMDef.InterfaceDef interfaceDef : interfaces) {
|
||||
final UnmanagedInstanceTO.Nic nic = new UnmanagedInstanceTO.Nic();
|
||||
nic.setNicId(String.valueOf(counter++));
|
||||
nic.setMacAddress(interfaceDef.getMacAddress());
|
||||
nic.setAdapterType(interfaceDef.getModel().toString());
|
||||
nic.setNetwork(interfaceDef.getDevName());
|
||||
nic.setPciSlot(interfaceDef.getSlot().toString());
|
||||
nic.setVlan(interfaceDef.getVlanTag());
|
||||
nics.add(nic);
|
||||
}
|
||||
return nics;
|
||||
}
|
||||
|
||||
private List<UnmanagedInstanceTO.Disk> getUnmanagedInstanceDisks(List<LibvirtVMDef.DiskDef> disksInfo,
|
||||
LibvirtComputingResource libvirtComputingResource,
|
||||
Domain dm){
|
||||
final ArrayList<UnmanagedInstanceTO.Disk> disks = new ArrayList<>(disksInfo.size());
|
||||
int counter = 0;
|
||||
for (LibvirtVMDef.DiskDef diskDef : disksInfo) {
|
||||
if (diskDef.getDeviceType() != LibvirtVMDef.DiskDef.DeviceType.DISK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final UnmanagedInstanceTO.Disk disk = new UnmanagedInstanceTO.Disk();
|
||||
|
||||
disk.setPosition(counter);
|
||||
|
||||
Long size;
|
||||
try {
|
||||
DomainBlockInfo blockInfo = dm.blockInfo(diskDef.getSourcePath());
|
||||
size = blockInfo.getCapacity();
|
||||
} catch (LibvirtException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
disk.setCapacity(size);
|
||||
disk.setDiskId(String.valueOf(counter++));
|
||||
disk.setLabel(diskDef.getDiskLabel());
|
||||
disk.setController(diskDef.getBusType().toString());
|
||||
|
||||
|
||||
Pair<String, String> sourceHostPath = getSourceHostPath(libvirtComputingResource, diskDef.getSourcePath());
|
||||
if (sourceHostPath != null) {
|
||||
disk.setDatastoreHost(sourceHostPath.first());
|
||||
disk.setDatastorePath(sourceHostPath.second());
|
||||
} else {
|
||||
disk.setDatastorePath(diskDef.getSourcePath());
|
||||
disk.setDatastoreHost(diskDef.getSourceHost());
|
||||
}
|
||||
|
||||
disk.setDatastoreType(diskDef.getDiskType().toString());
|
||||
disk.setDatastorePort(diskDef.getSourceHostPort());
|
||||
disks.add(disk);
|
||||
}
|
||||
return disks;
|
||||
}
|
||||
|
||||
private Pair<String, String> getSourceHostPath(LibvirtComputingResource libvirtComputingResource, String diskPath) {
|
||||
int pathEnd = diskPath.lastIndexOf("/");
|
||||
if (pathEnd >= 0) {
|
||||
diskPath = diskPath.substring(0, pathEnd);
|
||||
return libvirtComputingResource.getSourceHostPath(diskPath);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,227 @@
|
||||
// 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.hypervisor.kvm.resource.wrapper;
|
||||
|
||||
import com.cloud.agent.api.GetUnmanagedInstancesAnswer;
|
||||
import com.cloud.agent.api.GetUnmanagedInstancesCommand;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtDomainXMLParser;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef;
|
||||
import com.cloud.resource.CommandWrapper;
|
||||
import com.cloud.resource.ResourceWrapper;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import org.apache.cloudstack.utils.qemu.QemuImg;
|
||||
import org.apache.cloudstack.utils.qemu.QemuImgException;
|
||||
import org.apache.cloudstack.utils.qemu.QemuImgFile;
|
||||
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.libvirt.Connect;
|
||||
import org.libvirt.Domain;
|
||||
import org.libvirt.LibvirtException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@ResourceWrapper(handles=GetUnmanagedInstancesCommand.class)
|
||||
public final class LibvirtGetUnmanagedInstancesCommandWrapper extends CommandWrapper<GetUnmanagedInstancesCommand, GetUnmanagedInstancesAnswer, LibvirtComputingResource> {
|
||||
private static final Logger LOGGER = Logger.getLogger(LibvirtGetUnmanagedInstancesCommandWrapper.class);
|
||||
|
||||
@Override
|
||||
public GetUnmanagedInstancesAnswer execute(GetUnmanagedInstancesCommand command, LibvirtComputingResource libvirtComputingResource) {
|
||||
LOGGER.info("Fetching unmanaged instance on host");
|
||||
|
||||
HashMap<String, UnmanagedInstanceTO> unmanagedInstances = new HashMap<>();
|
||||
try {
|
||||
final LibvirtUtilitiesHelper libvirtUtilitiesHelper = libvirtComputingResource.getLibvirtUtilitiesHelper();
|
||||
final Connect conn = libvirtUtilitiesHelper.getConnection();
|
||||
final List<Domain> domains = getDomains(command, libvirtComputingResource, conn);
|
||||
|
||||
for (Domain domain : domains) {
|
||||
UnmanagedInstanceTO instance = getUnmanagedInstance(libvirtComputingResource, domain, conn);
|
||||
if (instance != null) {
|
||||
unmanagedInstances.put(instance.getName(), instance);
|
||||
domain.free();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
String err = String.format("Error listing unmanaged instances: %s", e.getMessage());
|
||||
LOGGER.error(err, e);
|
||||
return new GetUnmanagedInstancesAnswer(command, err);
|
||||
}
|
||||
|
||||
return new GetUnmanagedInstancesAnswer(command, "OK", unmanagedInstances);
|
||||
}
|
||||
|
||||
private List<Domain> getDomains(GetUnmanagedInstancesCommand command,
|
||||
LibvirtComputingResource libvirtComputingResource,
|
||||
Connect conn) throws LibvirtException, CloudRuntimeException {
|
||||
final List<Domain> domains = new ArrayList<>();
|
||||
final String vmNameCmd = command.getInstanceName();
|
||||
if (StringUtils.isNotBlank(vmNameCmd)) {
|
||||
final Domain domain = libvirtComputingResource.getDomain(conn, vmNameCmd);
|
||||
if (domain == null) {
|
||||
String msg = String.format("VM %s not found", vmNameCmd);
|
||||
LOGGER.error(msg);
|
||||
throw new CloudRuntimeException(msg);
|
||||
}
|
||||
|
||||
checkIfVmExists(vmNameCmd,domain);
|
||||
checkIfVmIsManaged(command,vmNameCmd,domain);
|
||||
|
||||
domains.add(domain);
|
||||
} else {
|
||||
final List<String> allVmNames = libvirtComputingResource.getAllVmNames(conn);
|
||||
for (String name : allVmNames) {
|
||||
if (!command.hasManagedInstance(name)) {
|
||||
final Domain domain = libvirtComputingResource.getDomain(conn, name);
|
||||
domains.add(domain);
|
||||
}
|
||||
}
|
||||
}
|
||||
return domains;
|
||||
}
|
||||
|
||||
private void checkIfVmExists(String vmNameCmd,final Domain domain) throws LibvirtException {
|
||||
if (StringUtils.isNotEmpty(vmNameCmd) &&
|
||||
!vmNameCmd.equals(domain.getName())) {
|
||||
LOGGER.error("GetUnmanagedInstancesCommand: exact vm name not found " + vmNameCmd);
|
||||
throw new CloudRuntimeException("GetUnmanagedInstancesCommand: exact vm name not found " + vmNameCmd);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkIfVmIsManaged(GetUnmanagedInstancesCommand command,String vmNameCmd,final Domain domain) throws LibvirtException {
|
||||
if (command.hasManagedInstance(domain.getName())) {
|
||||
LOGGER.error("GetUnmanagedInstancesCommand: vm already managed " + vmNameCmd);
|
||||
throw new CloudRuntimeException("GetUnmanagedInstancesCommand: vm already managed " + vmNameCmd);
|
||||
}
|
||||
}
|
||||
private UnmanagedInstanceTO getUnmanagedInstance(LibvirtComputingResource libvirtComputingResource, Domain domain, Connect conn) {
|
||||
try {
|
||||
final LibvirtDomainXMLParser parser = new LibvirtDomainXMLParser();
|
||||
parser.parseDomainXML(domain.getXMLDesc(1));
|
||||
|
||||
final UnmanagedInstanceTO instance = new UnmanagedInstanceTO();
|
||||
instance.setName(domain.getName());
|
||||
|
||||
instance.setCpuCores((int) LibvirtComputingResource.countDomainRunningVcpus(domain));
|
||||
instance.setCpuSpeed(parser.getCpuTuneDef().getShares()/instance.getCpuCores());
|
||||
|
||||
if (parser.getCpuModeDef() != null) {
|
||||
instance.setCpuCoresPerSocket(parser.getCpuModeDef().getCoresPerSocket());
|
||||
}
|
||||
instance.setPowerState(getPowerState(libvirtComputingResource.getVmState(conn,domain.getName())));
|
||||
instance.setMemory((int) LibvirtComputingResource.getDomainMemory(domain) / 1024);
|
||||
instance.setNics(getUnmanagedInstanceNics(parser.getInterfaces()));
|
||||
instance.setDisks(getUnmanagedInstanceDisks(parser.getDisks(),libvirtComputingResource));
|
||||
instance.setVncPassword(parser.getVncPasswd() + "aaaaaaaaaaaaaa"); // Suffix back extra characters for DB compatibility
|
||||
|
||||
return instance;
|
||||
} catch (Exception e) {
|
||||
LOGGER.info("Unable to retrieve unmanaged instance info. " + e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private UnmanagedInstanceTO.PowerState getPowerState(VirtualMachine.PowerState vmPowerState) {
|
||||
switch (vmPowerState) {
|
||||
case PowerOn:
|
||||
return UnmanagedInstanceTO.PowerState.PowerOn;
|
||||
case PowerOff:
|
||||
return UnmanagedInstanceTO.PowerState.PowerOff;
|
||||
default:
|
||||
return UnmanagedInstanceTO.PowerState.PowerUnknown;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private List<UnmanagedInstanceTO.Nic> getUnmanagedInstanceNics(List<LibvirtVMDef.InterfaceDef> interfaces) {
|
||||
final ArrayList<UnmanagedInstanceTO.Nic> nics = new ArrayList<>(interfaces.size());
|
||||
int counter = 0;
|
||||
for (LibvirtVMDef.InterfaceDef interfaceDef : interfaces) {
|
||||
final UnmanagedInstanceTO.Nic nic = new UnmanagedInstanceTO.Nic();
|
||||
nic.setNicId(String.valueOf(counter++));
|
||||
nic.setMacAddress(interfaceDef.getMacAddress());
|
||||
nic.setAdapterType(interfaceDef.getModel().toString());
|
||||
nic.setNetwork(interfaceDef.getDevName());
|
||||
nic.setPciSlot(interfaceDef.getSlot().toString());
|
||||
nic.setVlan(interfaceDef.getVlanTag());
|
||||
nics.add(nic);
|
||||
}
|
||||
return nics;
|
||||
}
|
||||
|
||||
private List<UnmanagedInstanceTO.Disk> getUnmanagedInstanceDisks(List<LibvirtVMDef.DiskDef> disksInfo, LibvirtComputingResource libvirtComputingResource){
|
||||
final ArrayList<UnmanagedInstanceTO.Disk> disks = new ArrayList<>(disksInfo.size());
|
||||
int counter = 0;
|
||||
for (LibvirtVMDef.DiskDef diskDef : disksInfo) {
|
||||
if (diskDef.getDeviceType() != LibvirtVMDef.DiskDef.DeviceType.DISK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final UnmanagedInstanceTO.Disk disk = new UnmanagedInstanceTO.Disk();
|
||||
Long size = null;
|
||||
String imagePath = null;
|
||||
try {
|
||||
QemuImgFile file = new QemuImgFile(diskDef.getSourcePath());
|
||||
QemuImg qemu = new QemuImg(0);
|
||||
Map<String, String> info = qemu.info(file);
|
||||
size = Long.parseLong(info.getOrDefault("virtual_size", "0"));
|
||||
imagePath = info.getOrDefault("image", null);
|
||||
} catch (QemuImgException | LibvirtException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
disk.setPosition(counter);
|
||||
disk.setCapacity(size);
|
||||
disk.setDiskId(String.valueOf(counter++));
|
||||
disk.setLabel(diskDef.getDiskLabel());
|
||||
disk.setController(diskDef.getBusType().toString());
|
||||
|
||||
|
||||
Pair<String, String> sourceHostPath = getSourceHostPath(libvirtComputingResource, diskDef.getSourcePath());
|
||||
if (sourceHostPath != null) {
|
||||
disk.setDatastoreHost(sourceHostPath.first());
|
||||
disk.setDatastorePath(sourceHostPath.second());
|
||||
} else {
|
||||
disk.setDatastorePath(diskDef.getSourcePath());
|
||||
disk.setDatastoreHost(diskDef.getSourceHost());
|
||||
}
|
||||
|
||||
disk.setDatastoreType(diskDef.getDiskType().toString());
|
||||
disk.setDatastorePort(diskDef.getSourceHostPort());
|
||||
disk.setImagePath(imagePath);
|
||||
disk.setDatastoreName(imagePath.substring(imagePath.lastIndexOf("/")));
|
||||
disks.add(disk);
|
||||
}
|
||||
return disks;
|
||||
}
|
||||
|
||||
private Pair<String, String> getSourceHostPath(LibvirtComputingResource libvirtComputingResource, String diskPath) {
|
||||
int pathEnd = diskPath.lastIndexOf("/");
|
||||
if (pathEnd >= 0) {
|
||||
diskPath = diskPath.substring(0, pathEnd);
|
||||
return libvirtComputingResource.getSourceHostPath(diskPath);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
// 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.hypervisor.kvm.resource.wrapper;
|
||||
|
||||
import com.cloud.agent.api.PrepareUnmanageVMInstanceAnswer;
|
||||
import com.cloud.agent.api.PrepareUnmanageVMInstanceCommand;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
|
||||
import com.cloud.resource.CommandWrapper;
|
||||
import com.cloud.resource.ResourceWrapper;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.libvirt.Connect;
|
||||
import org.libvirt.Domain;
|
||||
|
||||
@ResourceWrapper(handles=PrepareUnmanageVMInstanceCommand.class)
|
||||
public final class LibvirtPrepareUnmanageVMInstanceCommandWrapper extends CommandWrapper<PrepareUnmanageVMInstanceCommand, PrepareUnmanageVMInstanceAnswer, LibvirtComputingResource> {
|
||||
private static final Logger LOGGER = Logger.getLogger(LibvirtPrepareUnmanageVMInstanceCommandWrapper.class);
|
||||
@Override
|
||||
public PrepareUnmanageVMInstanceAnswer execute(PrepareUnmanageVMInstanceCommand command, LibvirtComputingResource libvirtComputingResource) {
|
||||
final String vmName = command.getInstanceName();
|
||||
final LibvirtUtilitiesHelper libvirtUtilitiesHelper = libvirtComputingResource.getLibvirtUtilitiesHelper();
|
||||
LOGGER.debug(String.format("Verify if KVM instance: [%s] is available before Unmanaging VM.", vmName));
|
||||
try {
|
||||
final Connect conn = libvirtUtilitiesHelper.getConnectionByVmName(vmName);
|
||||
final Domain domain = libvirtComputingResource.getDomain(conn, vmName);
|
||||
if (domain == null) {
|
||||
LOGGER.error("Prepare Unmanage VMInstanceCommand: vm not found " + vmName);
|
||||
new PrepareUnmanageVMInstanceAnswer(command, false, String.format("Cannot find VM with name [%s] in KVM host.", vmName));
|
||||
}
|
||||
} catch (Exception e){
|
||||
LOGGER.error("PrepareUnmanagedInstancesCommand failed due to " + e.getMessage());
|
||||
return new PrepareUnmanageVMInstanceAnswer(command, false, "Error: " + e.getMessage());
|
||||
}
|
||||
|
||||
return new PrepareUnmanageVMInstanceAnswer(command, true, "OK");
|
||||
}
|
||||
}
|
||||
@ -34,7 +34,10 @@ import java.util.stream.Stream;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.storage.StoragePool;
|
||||
import com.cloud.storage.StoragePoolHostVO;
|
||||
import com.cloud.storage.VMTemplateStoragePoolVO;
|
||||
import com.cloud.storage.dao.StoragePoolHostDao;
|
||||
import com.cloud.storage.dao.VMTemplatePoolDao;
|
||||
import com.cloud.host.Host;
|
||||
import com.cloud.host.dao.HostDao;
|
||||
@ -572,6 +575,9 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
||||
@Inject
|
||||
private PublicIpQuarantineDao publicIpQuarantineDao;
|
||||
|
||||
@Inject
|
||||
private StoragePoolHostDao storagePoolHostDao;
|
||||
|
||||
private SearchCriteria<ServiceOfferingJoinVO> getMinimumCpuServiceOfferingJoinSearchCriteria(int cpu) {
|
||||
SearchCriteria<ServiceOfferingJoinVO> sc = _srvOfferingJoinDao.createSearchCriteria();
|
||||
SearchCriteria<ServiceOfferingJoinVO> sc1 = _srvOfferingJoinDao.createSearchCriteria();
|
||||
@ -2816,23 +2822,24 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
||||
|
||||
@Override
|
||||
public ListResponse<StoragePoolResponse> searchForStoragePools(ListStoragePoolsCmd cmd) {
|
||||
Pair<List<StoragePoolJoinVO>, Integer> result = cmd.getHostId() != null ? searchForLocalStorages(cmd) : searchForStoragePoolsInternal(cmd);
|
||||
Pair<List<StoragePoolJoinVO>, Integer> result = (ScopeType.HOST.name().equalsIgnoreCase(cmd.getScope()) && cmd.getHostId() != null) ?
|
||||
searchForLocalStorages(cmd) : searchForStoragePoolsInternal(cmd);
|
||||
return createStoragesPoolResponse(result);
|
||||
}
|
||||
|
||||
private Pair<List<StoragePoolJoinVO>, Integer> searchForLocalStorages(ListStoragePoolsCmd cmd) {
|
||||
long id = cmd.getHostId();
|
||||
String scope = ScopeType.HOST.toString();
|
||||
Pair<List<StoragePoolJoinVO>, Integer> localStorages;
|
||||
|
||||
ListHostsCmd listHostsCmd = new ListHostsCmd();
|
||||
listHostsCmd.setId(id);
|
||||
Pair<List<HostJoinVO>, Integer> hosts = searchForServersInternal(listHostsCmd);
|
||||
|
||||
cmd.setScope(scope);
|
||||
localStorages = searchForStoragePoolsInternal(cmd);
|
||||
|
||||
return localStorages;
|
||||
List<StoragePoolHostVO> localstoragePools = storagePoolHostDao.listByHostId(id);
|
||||
Long[] poolIds = new Long[localstoragePools.size()];
|
||||
int i = 0;
|
||||
for(StoragePoolHostVO localstoragePool : localstoragePools) {
|
||||
StoragePool storagePool = storagePoolDao.findById(localstoragePool.getPoolId());
|
||||
if (storagePool != null && storagePool.isLocal()) {
|
||||
poolIds[i++] = localstoragePool.getPoolId();
|
||||
}
|
||||
}
|
||||
List<StoragePoolJoinVO> pools = _poolJoinDao.searchByIds(poolIds);
|
||||
return new Pair<>(pools, pools.size());
|
||||
}
|
||||
|
||||
private ListResponse<StoragePoolResponse> createStoragesPoolResponse(Pair<List<StoragePoolJoinVO>, Integer> storagePools) {
|
||||
|
||||
@ -129,6 +129,7 @@ import org.apache.cloudstack.userdata.UserDataManager;
|
||||
import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
|
||||
import org.apache.cloudstack.utils.security.ParserUtils;
|
||||
import org.apache.cloudstack.vm.schedule.VMScheduleManager;
|
||||
import org.apache.cloudstack.vm.UnmanagedVMsManager;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
@ -235,7 +236,6 @@ import com.cloud.host.Host;
|
||||
import com.cloud.host.HostVO;
|
||||
import com.cloud.host.Status;
|
||||
import com.cloud.host.dao.HostDao;
|
||||
import com.cloud.hypervisor.Hypervisor;
|
||||
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||
import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao;
|
||||
import com.cloud.hypervisor.kvm.dpdk.DpdkHelper;
|
||||
@ -4489,6 +4489,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
||||
|
||||
setVmRequiredFieldsForImport(isImport, vm, zone, hypervisorType, host, lastHost, powerState);
|
||||
|
||||
setVncPasswordForKvmIfAvailable(customParameters, vm);
|
||||
|
||||
vm.setUserVmType(vmType);
|
||||
_vmDao.persist(vm);
|
||||
for (String key : customParameters.keySet()) {
|
||||
@ -4891,7 +4893,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
||||
Long hostId = cmd.getHostId();
|
||||
Map<VirtualMachineProfile.Param, Object> additionalParams = new HashMap<>();
|
||||
Map<Long, DiskOffering> diskOfferingMap = cmd.getDataDiskTemplateToDiskOfferingMap();
|
||||
Map<String, String> details = cmd.getDetails();
|
||||
if (cmd instanceof DeployVMCmdByAdmin) {
|
||||
DeployVMCmdByAdmin adminCmd = (DeployVMCmdByAdmin)cmd;
|
||||
podId = adminCmd.getPodId();
|
||||
@ -8207,7 +8208,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
||||
public UserVm importVM(final DataCenter zone, final Host host, final VirtualMachineTemplate template, final String instanceName, final String displayName,
|
||||
final Account owner, final String userData, final Account caller, final Boolean isDisplayVm, final String keyboard,
|
||||
final long accountId, final long userId, final ServiceOffering serviceOffering, final String sshPublicKeys,
|
||||
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, final LinkedHashMap<String, List<NicProfile>> networkNicMap) throws InsufficientCapacityException {
|
||||
if (zone == null) {
|
||||
throw new InvalidParameterValueException("Unable to import virtual machine with invalid zone");
|
||||
}
|
||||
@ -8227,7 +8229,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
||||
final Boolean dynamicScalingEnabled = checkIfDynamicScalingCanBeEnabled(null, serviceOffering, template, zone.getId());
|
||||
return commitUserVm(true, zone, host, lastHost, template, hostName, displayName, owner,
|
||||
null, null, userData, null, null, isDisplayVm, keyboard,
|
||||
accountId, userId, serviceOffering, template.getFormat().equals(ImageFormat.ISO), sshPublicKeys, null,
|
||||
accountId, userId, serviceOffering, template.getFormat().equals(ImageFormat.ISO), sshPublicKeys, networkNicMap,
|
||||
id, instanceName, uuidName, hypervisorType, customParameters,
|
||||
null, null, null, powerState, dynamicScalingEnabled, null, serviceOffering.getDiskOfferingId(), null);
|
||||
}
|
||||
@ -8247,8 +8249,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
||||
return false;
|
||||
}
|
||||
|
||||
if (vm.getHypervisorType() != Hypervisor.HypervisorType.VMware) {
|
||||
throw new UnsupportedServiceException("Unmanaging a VM is currently allowed for VMware VMs only");
|
||||
if (!UnmanagedVMsManager.isSupported(vm.getHypervisorType())) {
|
||||
throw new UnsupportedServiceException("Unmanaging a VM is currently not supported on hypervisor " +
|
||||
vm.getHypervisorType().toString());
|
||||
}
|
||||
|
||||
List<VolumeVO> volumes = _volsDao.findByInstance(vm.getId());
|
||||
@ -8427,4 +8430,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
||||
public Boolean getDestroyRootVolumeOnVmDestruction(Long domainId){
|
||||
return DestroyRootVolumeOnVmDestruction.valueIn(domainId);
|
||||
}
|
||||
|
||||
private void setVncPasswordForKvmIfAvailable(Map<String, String> customParameters, UserVmVO vm){
|
||||
if (customParameters.containsKey(VmDetailConstants.KVM_VNC_PASSWORD)
|
||||
&& StringUtils.isNotEmpty(customParameters.get(VmDetailConstants.KVM_VNC_PASSWORD))) {
|
||||
vm.setVncPassword(customParameters.get(VmDetailConstants.KVM_VNC_PASSWORD));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -19,8 +19,14 @@ package org.apache.cloudstack.vm;
|
||||
|
||||
import com.cloud.agent.AgentManager;
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.CheckVolumeAnswer;
|
||||
import com.cloud.agent.api.CheckVolumeCommand;
|
||||
import com.cloud.agent.api.ConvertInstanceAnswer;
|
||||
import com.cloud.agent.api.ConvertInstanceCommand;
|
||||
import com.cloud.agent.api.CopyRemoteVolumeAnswer;
|
||||
import com.cloud.agent.api.CopyRemoteVolumeCommand;
|
||||
import com.cloud.agent.api.GetRemoteVmsAnswer;
|
||||
import com.cloud.agent.api.GetRemoteVmsCommand;
|
||||
import com.cloud.agent.api.GetUnmanagedInstancesAnswer;
|
||||
import com.cloud.agent.api.GetUnmanagedInstancesCommand;
|
||||
import com.cloud.agent.api.to.DataStoreTO;
|
||||
@ -32,9 +38,12 @@ import com.cloud.dc.VmwareDatacenterVO;
|
||||
import com.cloud.dc.dao.ClusterDao;
|
||||
import com.cloud.dc.dao.DataCenterDao;
|
||||
import com.cloud.dc.dao.VmwareDatacenterDao;
|
||||
import com.cloud.deploy.DeployDestination;
|
||||
import com.cloud.deploy.DeploymentPlanningManager;
|
||||
import com.cloud.event.ActionEventUtils;
|
||||
import com.cloud.event.UsageEventUtils;
|
||||
import com.cloud.exception.AgentUnavailableException;
|
||||
import com.cloud.exception.InsufficientServerCapacityException;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.exception.OperationTimedoutException;
|
||||
import com.cloud.exception.PermissionDeniedException;
|
||||
@ -50,6 +59,7 @@ import com.cloud.network.Network;
|
||||
import com.cloud.network.NetworkModel;
|
||||
import com.cloud.network.dao.NetworkDao;
|
||||
import com.cloud.network.dao.NetworkVO;
|
||||
import com.cloud.offering.NetworkOffering;
|
||||
import com.cloud.offering.ServiceOffering;
|
||||
import com.cloud.resource.ResourceManager;
|
||||
import com.cloud.resource.ResourceState;
|
||||
@ -59,8 +69,11 @@ import com.cloud.storage.DataStoreRole;
|
||||
import com.cloud.storage.DiskOfferingVO;
|
||||
import com.cloud.storage.ScopeType;
|
||||
import com.cloud.storage.Storage;
|
||||
import com.cloud.storage.StoragePool;
|
||||
import com.cloud.storage.StoragePoolHostVO;
|
||||
import com.cloud.storage.VMTemplateStoragePoolVO;
|
||||
import com.cloud.storage.VMTemplateVO;
|
||||
import com.cloud.storage.Volume;
|
||||
import com.cloud.storage.VolumeApiService;
|
||||
import com.cloud.storage.VolumeVO;
|
||||
import com.cloud.storage.dao.DiskOfferingDao;
|
||||
@ -79,7 +92,9 @@ import com.cloud.user.UserVO;
|
||||
import com.cloud.user.dao.UserDao;
|
||||
import com.cloud.uservm.UserVm;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.db.EntityManager;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.vm.DiskProfile;
|
||||
import com.cloud.vm.NicProfile;
|
||||
import com.cloud.vm.NicVO;
|
||||
import com.cloud.vm.UserVmManager;
|
||||
@ -96,6 +111,8 @@ import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.command.admin.vm.ImportUnmanagedInstanceCmd;
|
||||
import org.apache.cloudstack.api.command.admin.vm.ImportVmCmd;
|
||||
import org.apache.cloudstack.api.command.admin.vm.ListUnmanagedInstancesCmd;
|
||||
import org.apache.cloudstack.api.command.admin.vm.ListVmsForImportCmd;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.UserVmResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
||||
@ -123,10 +140,12 @@ import org.mockito.junit.MockitoJUnitRunner;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyMap;
|
||||
@ -148,6 +167,10 @@ public class UnmanagedVMsManagerImplTest {
|
||||
@Mock
|
||||
private ClusterDao clusterDao;
|
||||
@Mock
|
||||
private ClusterVO clusterVO;
|
||||
@Mock
|
||||
private UserVmVO userVm;
|
||||
@Mock
|
||||
private ResourceManager resourceManager;
|
||||
@Mock
|
||||
private VMTemplatePoolDao templatePoolDao;
|
||||
@ -212,6 +235,10 @@ public class UnmanagedVMsManagerImplTest {
|
||||
private VMInstanceVO virtualMachine;
|
||||
@Mock
|
||||
private NicVO nicVO;
|
||||
@Mock
|
||||
EntityManager entityMgr;
|
||||
@Mock
|
||||
DeploymentPlanningManager deploymentPlanningManager;
|
||||
|
||||
private static final long virtualMachineId = 1L;
|
||||
|
||||
@ -275,6 +302,7 @@ public class UnmanagedVMsManagerImplTest {
|
||||
hosts.add(hostVO);
|
||||
when(hostVO.checkHostServiceOfferingTags(Mockito.any())).thenReturn(true);
|
||||
when(resourceManager.listHostsInClusterByStatus(Mockito.anyLong(), Mockito.any(Status.class))).thenReturn(hosts);
|
||||
when(resourceManager.listAllUpAndEnabledHostsInOneZoneByHypervisor(any(Hypervisor.HypervisorType.class), Mockito.anyLong())).thenReturn(hosts);
|
||||
List<VMTemplateStoragePoolVO> templates = new ArrayList<>();
|
||||
when(templatePoolDao.listAll()).thenReturn(templates);
|
||||
List<VolumeVO> volumes = new ArrayList<>();
|
||||
@ -284,6 +312,9 @@ public class UnmanagedVMsManagerImplTest {
|
||||
map.put(instance.getName(), instance);
|
||||
Answer answer = new GetUnmanagedInstancesAnswer(cmd, "", map);
|
||||
when(agentManager.easySend(Mockito.anyLong(), Mockito.any(GetUnmanagedInstancesCommand.class))).thenReturn(answer);
|
||||
GetRemoteVmsCommand remoteVmListcmd = Mockito.mock(GetRemoteVmsCommand.class);
|
||||
Answer remoteVmListAnswer = new GetRemoteVmsAnswer(remoteVmListcmd, "", map);
|
||||
when(agentManager.easySend(Mockito.anyLong(), any(GetRemoteVmsCommand.class))).thenReturn(remoteVmListAnswer);
|
||||
DataCenterVO zone = Mockito.mock(DataCenterVO.class);
|
||||
when(zone.getId()).thenReturn(1L);
|
||||
when(dataCenterDao.findById(Mockito.anyLong())).thenReturn(zone);
|
||||
@ -323,7 +354,7 @@ public class UnmanagedVMsManagerImplTest {
|
||||
when(userVmManager.importVM(nullable(DataCenter.class), nullable(Host.class), nullable(VirtualMachineTemplate.class), nullable(String.class), nullable(String.class),
|
||||
nullable(Account.class), nullable(String.class), nullable(Account.class), nullable(Boolean.class), nullable(String.class),
|
||||
nullable(Long.class), nullable(Long.class), nullable(ServiceOffering.class), nullable(String.class),
|
||||
nullable(String.class), nullable(Hypervisor.HypervisorType.class), nullable(Map.class), nullable(VirtualMachine.PowerState.class))).thenReturn(userVm);
|
||||
nullable(String.class), nullable(Hypervisor.HypervisorType.class), nullable(Map.class), nullable(VirtualMachine.PowerState.class), nullable(LinkedHashMap.class))).thenReturn(userVm);
|
||||
NetworkVO networkVO = Mockito.mock(NetworkVO.class);
|
||||
when(networkVO.getGuestType()).thenReturn(Network.GuestType.L2);
|
||||
when(networkVO.getBroadcastUri()).thenReturn(URI.create(String.format("vlan://%d", instanceNic.getVlan())));
|
||||
@ -426,19 +457,71 @@ public class UnmanagedVMsManagerImplTest {
|
||||
|
||||
@Test(expected = UnsupportedServiceException.class)
|
||||
public void unmanageVMInstanceExistingVMSnapshotsTest() {
|
||||
when(virtualMachine.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.None);
|
||||
unmanagedVMsManager.unmanageVMInstance(virtualMachineId);
|
||||
}
|
||||
|
||||
@Test(expected = UnsupportedServiceException.class)
|
||||
public void unmanageVMInstanceExistingVolumeSnapshotsTest() {
|
||||
when(virtualMachine.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.None);
|
||||
unmanagedVMsManager.unmanageVMInstance(virtualMachineId);
|
||||
}
|
||||
|
||||
@Test(expected = UnsupportedServiceException.class)
|
||||
public void unmanageVMInstanceExistingISOAttachedTest() {
|
||||
when(virtualMachine.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.None);
|
||||
unmanagedVMsManager.unmanageVMInstance(virtualMachineId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListRemoteInstancesTest() {
|
||||
ListVmsForImportCmd cmd = Mockito.mock(ListVmsForImportCmd.class);
|
||||
when(cmd.getHypervisor()).thenReturn(Hypervisor.HypervisorType.KVM.toString());
|
||||
when(cmd.getUsername()).thenReturn("user");
|
||||
when(cmd.getPassword()).thenReturn("pass");
|
||||
ListResponse response = unmanagedVMsManager.listVmsForImport(cmd);
|
||||
Assert.assertEquals(1, response.getCount().intValue());
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testListRemoteInstancesTestNonKVM() {
|
||||
ListVmsForImportCmd cmd = Mockito.mock(ListVmsForImportCmd.class);
|
||||
unmanagedVMsManager.listVmsForImport(cmd);
|
||||
}
|
||||
@Test
|
||||
public void testImportFromExternalTest() throws InsufficientServerCapacityException {
|
||||
String vmname = "TestInstance";
|
||||
ImportVmCmd cmd = Mockito.mock(ImportVmCmd.class);
|
||||
when(cmd.getHypervisor()).thenReturn(Hypervisor.HypervisorType.KVM.toString());
|
||||
when(cmd.getName()).thenReturn(vmname);
|
||||
when(cmd.getUsername()).thenReturn("user");
|
||||
when(cmd.getPassword()).thenReturn("pass");
|
||||
when(cmd.getImportSource()).thenReturn("external");
|
||||
when(cmd.getDomainId()).thenReturn(null);
|
||||
VMTemplateVO template = Mockito.mock(VMTemplateVO.class);
|
||||
when(templateDao.findByName(anyString())).thenReturn(template);
|
||||
HostVO host = Mockito.mock(HostVO.class);
|
||||
when(userVmDao.getNextInSequence(Long.class, "id")).thenReturn(1L);
|
||||
DeployDestination mockDest = Mockito.mock(DeployDestination.class);
|
||||
when(deploymentPlanningManager.planDeployment(any(), any(), any(), any())).thenReturn(mockDest);
|
||||
DiskProfile diskProfile = Mockito.mock(DiskProfile.class);
|
||||
when(volumeManager.allocateRawVolume(any(), any(), any(), any(), any(), any(), any(), any(), any(), any()))
|
||||
.thenReturn(diskProfile);
|
||||
Map<Volume, StoragePool> storage = new HashMap<>();
|
||||
VolumeVO volume = Mockito.mock(VolumeVO.class);
|
||||
StoragePoolVO storagePool = Mockito.mock(StoragePoolVO.class);
|
||||
storage.put(volume, storagePool);
|
||||
when(mockDest.getStorageForDisks()).thenReturn(storage);
|
||||
when(mockDest.getHost()).thenReturn(host);
|
||||
when(volumeDao.findById(anyLong())).thenReturn(volume);
|
||||
CopyRemoteVolumeAnswer copyAnswer = Mockito.mock(CopyRemoteVolumeAnswer.class);
|
||||
when(copyAnswer.getResult()).thenReturn(true);
|
||||
when(agentManager.easySend(anyLong(), any(CopyRemoteVolumeCommand.class))).thenReturn(copyAnswer);
|
||||
try (MockedStatic<UsageEventUtils> ignored = Mockito.mockStatic(UsageEventUtils.class)) {
|
||||
unmanagedVMsManager.importVm(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
private void baseBasicParametersCheckForImportInstance(String name, Long domainId, String accountName) {
|
||||
unmanagedVMsManager.basicParametersCheckForImportInstance(name, domainId, accountName);
|
||||
}
|
||||
@ -518,7 +601,7 @@ public class UnmanagedVMsManagerImplTest {
|
||||
when(importVmCmd.getClusterId()).thenReturn(clusterId);
|
||||
when(importVmCmd.getDomainId()).thenReturn(null);
|
||||
when(importVmCmd.getImportSource()).thenReturn(VmImportService.ImportSource.VMWARE.toString());
|
||||
when(importVmCmd.getHost()).thenReturn(host);
|
||||
when(importVmCmd.getHostIp()).thenReturn(host);
|
||||
when(importVmCmd.getNicNetworkList()).thenReturn(Map.of("NIC 1", networkId));
|
||||
when(importVmCmd.getConvertInstanceHostId()).thenReturn(null);
|
||||
when(importVmCmd.getConvertStoragePoolId()).thenReturn(null);
|
||||
@ -544,9 +627,6 @@ public class UnmanagedVMsManagerImplTest {
|
||||
if (selectConvertHost) {
|
||||
when(importVmCmd.getConvertInstanceHostId()).thenReturn(convertHostId);
|
||||
when(hostDao.findById(convertHostId)).thenReturn(convertHost);
|
||||
} else {
|
||||
when(hostDao.listByClusterAndHypervisorType(clusterId, Hypervisor.HypervisorType.KVM))
|
||||
.thenReturn(List.of(convertHost));
|
||||
}
|
||||
|
||||
DataStoreTO dataStoreTO = mock(DataStoreTO.class);
|
||||
@ -613,6 +693,57 @@ public class UnmanagedVMsManagerImplTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImportFromLocalDisk() throws InsufficientServerCapacityException {
|
||||
testImportFromDisk("local");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImportFromsharedStorage() throws InsufficientServerCapacityException {
|
||||
testImportFromDisk("shared");
|
||||
}
|
||||
|
||||
private void testImportFromDisk(String source) throws InsufficientServerCapacityException {
|
||||
String vmname = "testVm";
|
||||
ImportVmCmd cmd = Mockito.mock(ImportVmCmd.class);
|
||||
when(cmd.getHypervisor()).thenReturn(Hypervisor.HypervisorType.KVM.toString());
|
||||
when(cmd.getName()).thenReturn(vmname);
|
||||
when(cmd.getImportSource()).thenReturn(source);
|
||||
when(cmd.getDiskPath()).thenReturn("/var/lib/libvirt/images/test.qcow2");
|
||||
when(cmd.getDomainId()).thenReturn(null);
|
||||
VMTemplateVO template = Mockito.mock(VMTemplateVO.class);
|
||||
when(templateDao.findByName(anyString())).thenReturn(template);
|
||||
HostVO host = Mockito.mock(HostVO.class);
|
||||
when(hostDao.findById(anyLong())).thenReturn(host);
|
||||
NetworkOffering netOffering = Mockito.mock(NetworkOffering.class);
|
||||
when(entityMgr.findById(NetworkOffering.class, 0L)).thenReturn(netOffering);
|
||||
when(userVmDao.getNextInSequence(Long.class, "id")).thenReturn(1L);
|
||||
DeployDestination mockDest = Mockito.mock(DeployDestination.class);
|
||||
when(deploymentPlanningManager.planDeployment(any(), any(), any(), any())).thenReturn(mockDest);
|
||||
DiskProfile diskProfile = Mockito.mock(DiskProfile.class);
|
||||
when(volumeManager.allocateRawVolume(any(), any(), any(), any(), any(), any(), any(), any(), any(), any()))
|
||||
.thenReturn(diskProfile);
|
||||
Map<Volume, StoragePool> storage = new HashMap<>();
|
||||
VolumeVO volume = Mockito.mock(VolumeVO.class);
|
||||
StoragePoolVO storagePool = Mockito.mock(StoragePoolVO.class);
|
||||
storage.put(volume, storagePool);
|
||||
when(mockDest.getStorageForDisks()).thenReturn(storage);
|
||||
when(mockDest.getHost()).thenReturn(host);
|
||||
when(volumeDao.findById(anyLong())).thenReturn(volume);
|
||||
CheckVolumeAnswer answer = Mockito.mock(CheckVolumeAnswer.class);
|
||||
when(answer.getResult()).thenReturn(true);
|
||||
when(agentManager.easySend(anyLong(), any(CheckVolumeCommand.class))).thenReturn(answer);
|
||||
List<StoragePoolVO> storagePools = new ArrayList<>();
|
||||
storagePools.add(storagePool);
|
||||
when(primaryDataStoreDao.findLocalStoragePoolsByHostAndTags(anyLong(), any())).thenReturn(storagePools);
|
||||
when(primaryDataStoreDao.findById(anyLong())).thenReturn(storagePool);
|
||||
when(volumeApiService.doesTargetStorageSupportDiskOffering(any(StoragePool.class), any())).thenReturn(true);
|
||||
StoragePoolHostVO storagePoolHost = Mockito.mock(StoragePoolHostVO.class);
|
||||
when(storagePoolHostDao.findByPoolHost(anyLong(), anyLong())).thenReturn(storagePoolHost);
|
||||
try (MockedStatic<UsageEventUtils> ignored = Mockito.mockStatic(UsageEventUtils.class)) {
|
||||
unmanagedVMsManager.importVm(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
public void testImportVmFromVmwareToKvmExistingVcenter() throws OperationTimedoutException, AgentUnavailableException {
|
||||
baseTestImportVmFromVmwareToKvm(VcenterParameter.EXISTING, false, false);
|
||||
}
|
||||
|
||||
@ -269,7 +269,9 @@ known_categories = {
|
||||
'createBucket': 'Object Store',
|
||||
'updateBucket': 'Object Store',
|
||||
'deleteBucket': 'Object Store',
|
||||
'listBuckets': 'Object Store'
|
||||
'listBuckets': 'Object Store',
|
||||
'listVmsForImport': 'Virtual Machine',
|
||||
'importVm': 'Virtual Machine'
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -138,6 +138,7 @@
|
||||
"label.action.image.store.read.only": "Make image store read-only",
|
||||
"label.action.image.store.read.write": "Make image store read-write",
|
||||
"label.action.import.export.instances": "Import-Export Instances",
|
||||
"label.action.ingest.instances": "Ingest instances",
|
||||
"label.action.iso.permission": "Update ISO permissions",
|
||||
"label.action.iso.share": "Update ISO sharing",
|
||||
"label.action.lock.account": "Lock Account",
|
||||
@ -673,7 +674,11 @@
|
||||
"label.deployasis": "Read Instance settings from OVA",
|
||||
"label.deploymentplanner": "Deployment planner",
|
||||
"label.desc.db.stats": "Database Statistics",
|
||||
"label.desc.importexportinstancewizard": "Import and export Instances to/from an existing VMware cluster.",
|
||||
"label.desc.importexportinstancewizard": "Import and export Instances to/from an existing VMware or KVM cluster.",
|
||||
"label.desc.import.ext.kvm.wizard": "Import libvirt domain from KVM Host",
|
||||
"label.desc.import.local.kvm.wizard": "Import QCOW image from Local Storage",
|
||||
"label.desc.import.shared.kvm.wizard": "Import QCOW image from Shared Storage",
|
||||
"label.desc.ingesttinstancewizard": "Ingest instances from an external KVM host",
|
||||
"label.desc.importmigratefromvmwarewizard": "Import instances from VMware into a KVM cluster",
|
||||
"label.desc.usage.stats": "Usage Server Statistics",
|
||||
"label.description": "Description",
|
||||
@ -683,10 +688,10 @@
|
||||
"label.desthost": "Destination host",
|
||||
"label.destination": "Destination",
|
||||
"label.destination.cluster": "Destination Cluster",
|
||||
"label.destination.hypervisor": "Destination Hypervisor",
|
||||
"label.destination.pod": "Destination Pod",
|
||||
"label.destination.zone": "Destination Zone",
|
||||
"label.destinationphysicalnetworkid": "Destination physical Network ID",
|
||||
"label.destination.hypervisor": "Destination Hypervisor",
|
||||
"label.destinationtype": "Destination Type",
|
||||
"label.destipprefix": "Destination Network Address",
|
||||
"label.destipprefixlen": "Destination Prefix Length",
|
||||
@ -717,6 +722,8 @@
|
||||
"label.disconnected": "Last disconnected",
|
||||
"label.disk": "Disk",
|
||||
"label.disk.offerings": "Disk offerings",
|
||||
"label.disk.path": "Disk Path",
|
||||
"label.disk.tooltip": "Disk Image filename in the selected Storage Pool",
|
||||
"label.disk.selection": "Disk selection",
|
||||
"label.disk.size": "Disk size",
|
||||
"label.disk.usage.info": "Disk usage information",
|
||||
@ -866,6 +873,7 @@
|
||||
"label.expunged": "Expunged",
|
||||
"label.expunging": "Expunging",
|
||||
"label.export.rules": "Export Rules",
|
||||
"label.ext.hostname.tooltip": "External Host Name or IP Address",
|
||||
"label.external.managed": "ExternalManaged",
|
||||
"label.external": "External",
|
||||
"label.external.link": "External link",
|
||||
@ -876,6 +884,7 @@
|
||||
"label.f5.ip.loadbalancer": "F5 BIG-IP load balancer.",
|
||||
"label.failed": "Failed",
|
||||
"label.featured": "Featured",
|
||||
"label.fetch.instances": "Fetch Instances",
|
||||
"label.fetch.latest": "Fetch latest",
|
||||
"label.files": "Alternate files to retrieve",
|
||||
"label.filter": "Filter",
|
||||
@ -977,6 +986,7 @@
|
||||
"label.hostcontrolstate": "Control Plane Status",
|
||||
"label.hostid": "Host",
|
||||
"label.hostname": "Host",
|
||||
"label.hostname.tooltip": "Destination Host. Volume should be located in local storage of this Host",
|
||||
"label.hostnamelabel": "Host name",
|
||||
"label.hosts": "Hosts",
|
||||
"label.hosttags": "Host tags",
|
||||
@ -1013,6 +1023,7 @@
|
||||
"label.info": "Info",
|
||||
"label.info.upper": "INFO",
|
||||
"label.infrastructure": "Infrastructure",
|
||||
"label.ingest.instance": "Ingest Instance",
|
||||
"label.ingress": "Ingress",
|
||||
"label.ingress.rule": "Ingress Rule",
|
||||
"label.initial": "Inital",
|
||||
@ -1503,6 +1514,7 @@
|
||||
"label.password": "Password",
|
||||
"label.password.default": "Default Password",
|
||||
"label.password.reset.confirm": "Password has been reset to ",
|
||||
"label.password.tooltip": "The password for the Host",
|
||||
"label.passwordenabled": "Password enabled",
|
||||
"label.path": "Path",
|
||||
"label.patp": "Palo Alto threat profile",
|
||||
@ -1677,6 +1689,7 @@
|
||||
"label.release.dedicated.pod": "Release dedicated pod",
|
||||
"label.release.dedicated.zone": "Release dedicated zone",
|
||||
"label.releasing.ip": "Releasing IP",
|
||||
"label.remote.instances": "Remote Instances",
|
||||
"label.remove": "Remove",
|
||||
"label.remove.annotation": "Remove comment",
|
||||
"label.remove.egress.rule": "Remove egress rule",
|
||||
@ -1802,6 +1815,7 @@
|
||||
"label.scheduled.snapshots": "Scheduled Snapshots",
|
||||
"label.schedules": "Schedules",
|
||||
"label.scope": "Scope",
|
||||
"label.scope.tooltip": "Primary Storage Pool Scope",
|
||||
"label.search": "Search",
|
||||
"label.secondary.isolated.vlan.type.isolated": "Isolated",
|
||||
"label.secondary.isolated.vlan.type.promiscuous": "Promiscuous",
|
||||
@ -1831,6 +1845,7 @@
|
||||
"label.select.project": "Select project",
|
||||
"label.select.projects": "Select projects",
|
||||
"label.select.ps": "Select primary storage",
|
||||
"label.select.root.disk": "Select the ROOT disk",
|
||||
"label.select.source.vcenter.datacenter": "Select the source VMware vCenter Datacenter",
|
||||
"label.select.tier": "Select Network Tier",
|
||||
"label.select.zones": "Select zones",
|
||||
@ -1986,6 +2001,7 @@
|
||||
"label.storagemotionenabled": "Storage motion enabled",
|
||||
"label.storagepolicy": "Storage policy",
|
||||
"label.storagepool": "Storage pool",
|
||||
"label.storagepool.tooltip": "Destination Storage Pool. Volume should be located in this Storage Pool",
|
||||
"label.storagetags": "Storage tags",
|
||||
"label.storagetype": "Storage type",
|
||||
"label.strict": "Strict",
|
||||
@ -2075,6 +2091,8 @@
|
||||
"label.timeout": "Timeout",
|
||||
"label.timeout.in.second ": " Timeout (seconds)",
|
||||
"label.timezone": "Timezone",
|
||||
"label.tmppath": "Temp Path",
|
||||
"label.tmppath.tooltip": "Temporary Path to store disk images on External Host before copying to destination storage pool. Default is /tmp",
|
||||
"label.to": "to",
|
||||
"label.token": "Token",
|
||||
"label.token.for.dashboard.login": "Token for dashboard login can be retrieved using following command",
|
||||
@ -2180,6 +2198,7 @@
|
||||
"label.userdata": "Userdata",
|
||||
"label.userdatal2": "User data",
|
||||
"label.username": "Username",
|
||||
"label.username.tooltip": "The Username for the Host",
|
||||
"label.users": "Users",
|
||||
"label.usersource": "User type",
|
||||
"label.using.cli": "Using CLI",
|
||||
@ -2678,6 +2697,10 @@
|
||||
"message.desc.create.ssh.key.pair": "Please fill in the following data to create or register a ssh key pair.<br><br>(1) If public key is set, CloudStack will register the public key. You can use it through your private key.<br><br>(2) If public key is not set, CloudStack will create a new SSH key pair. In this case, please copy and save the private key. CloudStack will not keep it.<br>",
|
||||
"message.desc.created.ssh.key.pair": "Created a SSH key pair.",
|
||||
"message.desc.host": "Each cluster must contain at least one host (computer) for guest Instances to run on. We will add the first host now. For a host to function in CloudStack, you must install hypervisor software on the host, assign an IP address to the host, and ensure the host is connected to the CloudStack management server.<br/><br/>Give the host's DNS or IP address, the user name (usually root) and password, and any labels you use to categorize hosts.",
|
||||
"message.desc.importingestinstancewizard": "This feature only applies to libvirt based KVM instances. Only Stopped instances can be ingested",
|
||||
"message.desc.import.ext.kvm.wizard": "Import libvirt domain from External KVM Host not managed by CloudStack",
|
||||
"message.desc.import.local.kvm.wizard": "Import QCOW image from Local Storage of selected KVM Host",
|
||||
"message.desc.import.shared.kvm.wizard": "Import QCOW image from selected Primary Storage Pool",
|
||||
"message.desc.importexportinstancewizard": "By choosing to manage an Instance, CloudStack takes over the orchestration of that Instance. Unmanaging an Instance removes CloudStack ability to manage it. In both cases, the Instance is left running and no changes are done to the VM on the hypervisor.<br><br>For KVM, managing a VM is an experimental feature.",
|
||||
"message.desc.importmigratefromvmwarewizard": "By selecting an existing or external VMware Datacenter and an instance to import, CloudStack migrates the selected instance from VMware to KVM on a conversion host using virt-v2v and imports it into a KVM cluster",
|
||||
"message.desc.primary.storage": "Each cluster must contain one or more primary storage servers. We will add the first one now. Primary storage contains the disk volumes for all the Instances running on hosts in the cluster. Use any standards-compliant protocol that is supported by the underlying hypervisor.",
|
||||
|
||||
@ -424,7 +424,7 @@ export default {
|
||||
label: 'label.action.unmanage.virtualmachine',
|
||||
message: 'message.action.unmanage.virtualmachine',
|
||||
dataView: true,
|
||||
show: (record) => { return ['Running', 'Stopped'].includes(record.state) && record.hypervisor === 'VMware' }
|
||||
show: (record) => { return ['Running', 'Stopped'].includes(record.state) && ['VMware', 'KVM'].includes(record.hypervisor) }
|
||||
},
|
||||
{
|
||||
api: 'expungeVirtualMachine',
|
||||
|
||||
@ -173,7 +173,7 @@ export default {
|
||||
disabled = true
|
||||
}
|
||||
if (disabled === false && maxMemory && this.minimumMemory > 0 &&
|
||||
((item.iscustomized === false && maxMemory < this.minimumMemory) ||
|
||||
((item.iscustomized === false && ((maxMemory < this.minimumMemory) || this.exactMatch && maxMemory !== this.minimumMemory)) ||
|
||||
(item.iscustomized === true && maxMemory < this.minimumMemory))) {
|
||||
disabled = true
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
<span>{{ record.displaytext || record.name }}</span>
|
||||
<div v-if="record.meta">
|
||||
<div v-for="meta in record.meta" :key="meta.key">
|
||||
<a-tag style="margin-top: 5px" :key="meta.key">{{ meta.key + ': ' + meta.value }}</a-tag>
|
||||
<a-tag v-if="(isKVMUnmanage && meta.key !== 'datastore') || !isKVMUnmanage" style="margin-top: 5px" :key="meta.key">{{ meta.key + ': ' + meta.value }}</a-tag>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -104,6 +104,10 @@ export default {
|
||||
autoSelectLabel: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
isKVMUnmanage: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
<div>
|
||||
<a-spin :spinning="loading" v-ctrl-enter="handleSubmit">
|
||||
<a-row :gutter="12">
|
||||
<a-col :md="24" :lg="7">
|
||||
<a-col :md="24" :lg="7" v-if="!isDiskImport">
|
||||
<info-card
|
||||
class="vm-info-card"
|
||||
:isStatic="true"
|
||||
@ -111,7 +111,7 @@
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item name="templateid" ref="templateid" v-if="cluster.hypervisortype === 'KVM' && !selectedVmwareVcenter">
|
||||
<a-form-item name="templateid" ref="templateid" v-if="cluster.hypervisortype === 'KVM' && !selectedVmwareVcenter && !isDiskImport && !isExternalImport">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.templatename')" :tooltip="apiParams.templateid.description + '. ' + $t('message.template.import.vm.temporary')"/>
|
||||
</template>
|
||||
@ -120,7 +120,7 @@
|
||||
:value="templateType"
|
||||
@change="changeTemplateType">
|
||||
<a-row :gutter="12">
|
||||
<a-col :md="24" :lg="12">
|
||||
<a-col :md="24" :lg="12" v-if="this.cluster.hypervisortype === 'VMWare'">
|
||||
<a-radio value="auto">
|
||||
{{ $t('label.template.temporary.import') }}
|
||||
</a-radio>
|
||||
@ -235,7 +235,7 @@
|
||||
<tooltip-label :title="$t('label.disk.selection')" :tooltip="apiParams.datadiskofferinglist.description"/>
|
||||
</template>
|
||||
</a-form-item>
|
||||
<a-form-item name="rootdiskid" ref="rootdiskid" :label="$t('label.rootdisk')">
|
||||
<a-form-item name="rootdiskid" ref="rootdiskid" :label="$t('label.select.root.disk')">
|
||||
<a-select
|
||||
v-model:value="form.rootdiskid"
|
||||
defaultActiveFirstOption
|
||||
@ -244,11 +244,26 @@
|
||||
:filterOption="(input, option) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}"
|
||||
@change="val => { selectedRootDiskIndex = val }">
|
||||
@change="onSelectRootDisk">
|
||||
<a-select-option v-for="(opt, optIndex) in resource.disk" :key="optIndex" :label="opt.label || opt.id">
|
||||
{{ opt.label || opt.id }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
<a-table
|
||||
:columns="selectedRootDiskColumns"
|
||||
:dataSource="selectedRootDiskSources"
|
||||
:pagination="false">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'name'">
|
||||
<span>{{ record.displaytext || record.name }}</span>
|
||||
<div v-if="record.meta">
|
||||
<div v-for="meta in record.meta" :key="meta.key">
|
||||
<a-tag style="margin-top: 5px" :key="meta.key">{{ meta.key + ': ' + meta.value }}</a-tag>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-form-item>
|
||||
<multi-disk-selection
|
||||
:items="dataDisks"
|
||||
@ -256,6 +271,7 @@
|
||||
:selectionEnabled="false"
|
||||
:customOfferingsAllowed="true"
|
||||
:autoSelectCustomOffering="true"
|
||||
:isKVMUnmanage="isKVMUnmanage"
|
||||
:autoSelectLabel="$t('label.auto.assign.diskoffering.disk.size')"
|
||||
@select-multi-disk-offering="updateMultiDiskOffering" />
|
||||
</div>
|
||||
@ -283,17 +299,40 @@
|
||||
:zoneId="cluster.zoneid"
|
||||
:selectionEnabled="false"
|
||||
:filterUnimplementedNetworks="true"
|
||||
filterMatchKey="broadcasturi"
|
||||
:hypervisor="this.cluster.hypervisortype"
|
||||
:filterMatchKey="isKVMUnmanage ? undefined : 'broadcasturi'"
|
||||
@select-multi-network="updateMultiNetworkOffering" />
|
||||
</div>
|
||||
<a-row v-else style="margin: 12px 0">
|
||||
<a-row v-else style="margin: 12px 0" >
|
||||
<div v-if="!isExternalImport && !isDiskImport">
|
||||
<a-alert type="warning">
|
||||
<template #message>
|
||||
<div v-html="$t('message.warn.importing.instance.without.nic')"></div>
|
||||
</template>
|
||||
</a-alert>
|
||||
</div>
|
||||
</a-row>
|
||||
<div v-if="isDiskImport">
|
||||
<a-form-item name="networkid" ref="networkid">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.network')"/>
|
||||
</template>
|
||||
<a-select
|
||||
v-model:value="form.networkid"
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
:filterOption="(input, option) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}"
|
||||
:loading="optionsLoading.networks">
|
||||
<a-select-option v-for="network in networkSelectOptions" :key="network.value" :label="network.label">
|
||||
<span>
|
||||
{{ network.label }}
|
||||
</span>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</div>
|
||||
<a-row v-if="!selectedVmwareVcenter" :gutter="12">
|
||||
<a-col :md="24" :lg="12">
|
||||
<a-form-item name="migrateallowed" ref="migrateallowed">
|
||||
@ -358,6 +397,26 @@ export default {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
host: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
pool: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
resource: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
isOpen: {
|
||||
type: Boolean,
|
||||
required: false
|
||||
},
|
||||
zoneid: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
importsource: {
|
||||
type: String,
|
||||
required: false
|
||||
@ -366,12 +425,24 @@ export default {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
resource: {
|
||||
type: Object,
|
||||
required: true
|
||||
exthost: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
isOpen: {
|
||||
type: Boolean,
|
||||
username: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
password: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
tmppath: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
diskpath: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
selectedVmwareVcenter: {
|
||||
@ -384,12 +455,14 @@ export default {
|
||||
options: {
|
||||
domains: [],
|
||||
projects: [],
|
||||
networks: [],
|
||||
templates: []
|
||||
},
|
||||
rowCount: {},
|
||||
optionsLoading: {
|
||||
domains: false,
|
||||
projects: false,
|
||||
networks: false,
|
||||
templates: false
|
||||
},
|
||||
domains: [],
|
||||
@ -397,7 +470,7 @@ export default {
|
||||
selectedDomainId: null,
|
||||
templates: [],
|
||||
templateLoading: false,
|
||||
templateType: 'auto',
|
||||
templateType: this.defaultTemplateType(),
|
||||
totalComputeOfferings: 0,
|
||||
computeOfferings: [],
|
||||
computeOfferingLoading: false,
|
||||
@ -426,7 +499,15 @@ export default {
|
||||
storagePoolsForConversion: [],
|
||||
selectedStorageOptionForConversion: null,
|
||||
selectedStoragePoolForConversion: null,
|
||||
showStoragePoolsForConversion: false
|
||||
showStoragePoolsForConversion: false,
|
||||
selectedRootDiskColumns: [
|
||||
{
|
||||
key: 'name',
|
||||
dataIndex: 'name',
|
||||
title: this.$t('label.rootdisk')
|
||||
}
|
||||
],
|
||||
selectedRootDiskSources: []
|
||||
}
|
||||
},
|
||||
beforeCreate () {
|
||||
@ -461,6 +542,15 @@ export default {
|
||||
showicon: true
|
||||
}
|
||||
},
|
||||
networks: {
|
||||
list: 'listNetworks',
|
||||
isLoad: true,
|
||||
field: 'networkid',
|
||||
options: {
|
||||
zoneid: this.zoneid,
|
||||
details: 'min'
|
||||
}
|
||||
},
|
||||
templates: {
|
||||
list: 'listTemplates',
|
||||
isLoad: true,
|
||||
@ -479,6 +569,21 @@ export default {
|
||||
}
|
||||
return false
|
||||
},
|
||||
isDiskImport () {
|
||||
if (this.importsource === 'local' || this.importsource === 'shared') {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
isExternalImport () {
|
||||
if (this.importsource === 'external') {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
isKVMUnmanage () {
|
||||
return this.hypervisor && this.hypervisor === 'kvm' && (this.importsource === 'unmanaged' || this.importsource === 'external')
|
||||
},
|
||||
domainSelectOptions () {
|
||||
var domains = this.options.domains.map((domain) => {
|
||||
return {
|
||||
@ -507,6 +612,19 @@ export default {
|
||||
})
|
||||
return projects
|
||||
},
|
||||
networkSelectOptions () {
|
||||
var networks = this.options.networks.map((network) => {
|
||||
return {
|
||||
label: network.name + ' (' + network.displaytext + ')',
|
||||
value: network.id
|
||||
}
|
||||
})
|
||||
networks.unshift({
|
||||
label: '',
|
||||
value: null
|
||||
})
|
||||
return networks
|
||||
},
|
||||
templateSelectOptions () {
|
||||
return this.options.templates.map((template) => {
|
||||
return {
|
||||
@ -540,6 +658,9 @@ export default {
|
||||
var nic = { ...nicEntry }
|
||||
nic.name = nic.name || nic.id
|
||||
nic.displaytext = nic.name
|
||||
if (this.isExternalImport && nic.vlanid === -1) {
|
||||
delete nic.vlanid
|
||||
}
|
||||
if (nic.vlanid) {
|
||||
nic.broadcasturi = 'vlan://' + nic.vlanid
|
||||
if (nic.isolatedpvlan) {
|
||||
@ -592,6 +713,9 @@ export default {
|
||||
page: 1
|
||||
})
|
||||
this.fetchKvmHostsForConversion()
|
||||
if (this.resource.disk.length > 1) {
|
||||
this.updateSelectedRootDisk()
|
||||
}
|
||||
},
|
||||
getMeta (obj, metaKeys) {
|
||||
var meta = []
|
||||
@ -724,6 +848,12 @@ export default {
|
||||
updateMultiNetworkOffering (data) {
|
||||
this.nicsNetworksMapping = data
|
||||
},
|
||||
defaultTemplateType () {
|
||||
if (this.cluster.hypervisortype === 'VMWare') {
|
||||
return 'auto'
|
||||
}
|
||||
return 'custom'
|
||||
},
|
||||
changeTemplateType (e) {
|
||||
this.templateType = e.target.value
|
||||
if (this.templateType === 'auto') {
|
||||
@ -834,6 +964,17 @@ export default {
|
||||
}
|
||||
]
|
||||
},
|
||||
onSelectRootDisk (val) {
|
||||
this.selectedRootDiskIndex = val
|
||||
this.updateSelectedRootDisk()
|
||||
},
|
||||
updateSelectedRootDisk () {
|
||||
var rootDisk = this.resource.disk[this.selectedRootDiskIndex]
|
||||
rootDisk.size = rootDisk.capacity / (1024 * 1024 * 1024)
|
||||
rootDisk.name = `${rootDisk.label} (${rootDisk.size} GB)`
|
||||
rootDisk.meta = this.getMeta(rootDisk, { controller: 'controller', datastorename: 'datastore', position: 'position' })
|
||||
this.selectedRootDiskSources = [rootDisk]
|
||||
},
|
||||
handleSubmit (e) {
|
||||
e.preventDefault()
|
||||
if (this.loading) return
|
||||
@ -843,12 +984,32 @@ export default {
|
||||
name: this.resource.name,
|
||||
clusterid: this.cluster.id,
|
||||
displayname: values.displayname,
|
||||
zoneid: this.zoneid,
|
||||
importsource: this.importsource,
|
||||
hypervisor: this.hypervisor
|
||||
hypervisor: this.hypervisor,
|
||||
host: this.exthost,
|
||||
hostname: values.hostname,
|
||||
username: this.username,
|
||||
password: this.password,
|
||||
hostid: this.host.id,
|
||||
storageid: this.pool.id,
|
||||
diskpath: this.diskpath,
|
||||
temppath: this.tmppath
|
||||
}
|
||||
var importapi = 'importUnmanagedInstance'
|
||||
if (this.isExternalImport || this.isDiskImport || this.selectedVmwareVcenter) {
|
||||
importapi = 'importVm'
|
||||
if (this.isDiskImport) {
|
||||
if (!values.networkid) {
|
||||
this.$notification.error({
|
||||
message: this.$t('message.request.failed'),
|
||||
description: this.$t('message.please.enter.valid.value') + ': ' + this.$t('label.network')
|
||||
})
|
||||
return
|
||||
}
|
||||
params.name = values.displayname
|
||||
params.networkid = values.networkid
|
||||
}
|
||||
}
|
||||
if (!this.computeOffering || !this.computeOffering.id) {
|
||||
this.$notification.error({
|
||||
@ -892,6 +1053,16 @@ export default {
|
||||
})
|
||||
}
|
||||
}
|
||||
if (this.isDiskImport) {
|
||||
var storageType = this.computeOffering.storagetype
|
||||
if (this.importsource !== storageType) {
|
||||
this.$notification.error({
|
||||
message: this.$t('message.request.failed'),
|
||||
description: 'Incompatible Storage. Import Source is: ' + this.importsource + '. Storage Type in service offering is: ' + storageType
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
if (this.selectedVmwareVcenter) {
|
||||
if (this.selectedVmwareVcenter.existingvcenterid) {
|
||||
params.existingvcenterid = this.selectedVmwareVcenter.existingvcenterid
|
||||
@ -934,6 +1105,7 @@ export default {
|
||||
}
|
||||
var nicNetworkIndex = 0
|
||||
var nicIpIndex = 0
|
||||
var networkcheck = new Set()
|
||||
for (var nicId in this.nicsNetworksMapping) {
|
||||
if (!this.nicsNetworksMapping[nicId].network) {
|
||||
this.$notification.error({
|
||||
@ -944,6 +1116,16 @@ export default {
|
||||
}
|
||||
params['nicnetworklist[' + nicNetworkIndex + '].nic'] = nicId
|
||||
params['nicnetworklist[' + nicNetworkIndex + '].network'] = this.nicsNetworksMapping[nicId].network
|
||||
var netId = this.nicsNetworksMapping[nicId].network
|
||||
if (!networkcheck.has(netId)) {
|
||||
networkcheck.add(netId)
|
||||
} else {
|
||||
this.$notification.error({
|
||||
message: this.$t('message.request.failed'),
|
||||
description: 'Same network cannot be assigned to multiple Nics'
|
||||
})
|
||||
return
|
||||
}
|
||||
nicNetworkIndex++
|
||||
if ('ipAddress' in this.nicsNetworksMapping[nicId]) {
|
||||
if (!this.nicsNetworksMapping[nicId].ipAddress) {
|
||||
@ -1010,7 +1192,7 @@ export default {
|
||||
for (var field of fields) {
|
||||
this.updateFieldValue(field, undefined)
|
||||
}
|
||||
this.templateType = 'auto'
|
||||
this.templateType = this.defaultTemplateType()
|
||||
this.updateComputeOffering(undefined)
|
||||
this.switches = {}
|
||||
},
|
||||
@ -1022,33 +1204,33 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@import url('../../style/index');
|
||||
.ant-table-selection-column {
|
||||
@import url('../../style/index');
|
||||
.ant-table-selection-column {
|
||||
// Fix for the table header if the row selection use radio buttons instead of checkboxes
|
||||
> div:empty {
|
||||
width: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-collapse-borderless > .ant-collapse-item {
|
||||
.ant-collapse-borderless > .ant-collapse-item {
|
||||
border: 1px solid @border-color-split;
|
||||
border-radius: @border-radius-base !important;
|
||||
margin: 0 0 1.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.form-layout {
|
||||
.form-layout {
|
||||
width: 120vw;
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
width: 550px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.action-button {
|
||||
.action-button {
|
||||
text-align: right;
|
||||
|
||||
button {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -48,7 +48,8 @@
|
||||
</a-alert>
|
||||
<br />
|
||||
<a-row :gutter="12">
|
||||
<a-col :md="24" :lg="12">
|
||||
<a-card class="source-dest-card">
|
||||
<a-col :md="24" :lg="48">
|
||||
<a-form
|
||||
style="min-width: 170px"
|
||||
:ref="formRef"
|
||||
@ -91,12 +92,73 @@
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col v-if="showExtHost" :md="24" :lg="12">
|
||||
<a-form-item
|
||||
name="hostname"
|
||||
ref="hostname">
|
||||
<template #label>
|
||||
<tooltip-label
|
||||
:title="$t('label.hostname')"
|
||||
:tooltip="$t('label.ext.hostname.tooltip')"/>
|
||||
</template>
|
||||
<a-input
|
||||
v-model:value="form.hostname"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col v-if="showExtHost" :md="24" :lg="12">
|
||||
<a-form-item
|
||||
name="username"
|
||||
ref="username">
|
||||
<template #label>
|
||||
<tooltip-label
|
||||
:title="$t('label.username')"
|
||||
:tooltip="$t('label.username.tooltip')"/>
|
||||
</template>
|
||||
<a-input
|
||||
v-model:value="form.username"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col v-if="showExtHost" :md="24" :lg="12">
|
||||
<a-form-item
|
||||
name="password"
|
||||
ref="password">
|
||||
<template #label>
|
||||
<tooltip-label
|
||||
:title="$t('label.password')"
|
||||
:tooltip="$t('label.password.tooltip')"/>
|
||||
</template>
|
||||
<a-input-password
|
||||
v-model:value="form.password"
|
||||
></a-input-password>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col v-if="showExtHost" :md="24" :lg="12">
|
||||
<a-form-item
|
||||
name="tmppath"
|
||||
ref="tmppath">
|
||||
<template #label>
|
||||
<tooltip-label
|
||||
:title="$t('label.tmppath')"
|
||||
:tooltip="$t('label.tmppath.tooltip')"/>
|
||||
</template>
|
||||
<a-input
|
||||
v-model:value="form.tmppath"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-form>
|
||||
</a-col>
|
||||
</a-card>
|
||||
<!-- ------------ -->
|
||||
<!-- RIGHT COLUMN -->
|
||||
<!-- ------------ -->
|
||||
<a-col :md="24" :lg="12">
|
||||
<a-card class="source-dest-card">
|
||||
<template #title>
|
||||
Destination
|
||||
</template>
|
||||
<a-col :md="24" :lg="48">
|
||||
<a-form
|
||||
style="min-width: 170px"
|
||||
:ref="formRef"
|
||||
@ -104,6 +166,22 @@
|
||||
:rules="rules"
|
||||
layout="vertical"
|
||||
>
|
||||
<a-form-item v-if="showPool" name="scope" ref="scope">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.scope')" :tooltip="$t('label.scope.tooltip')"/>
|
||||
</template>
|
||||
<a-select
|
||||
v-model:value="this.poolscope"
|
||||
@change="onSelectPoolScope"
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
:filterOption="(input, option) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}" >
|
||||
<a-select-option :value="'cluster'" :label="$t('label.clusterid')"> {{ $t('label.clusterid') }} </a-select-option>
|
||||
<a-select-option :value="'zone'" :label="$t('label.zoneid')"> {{ $t('label.zoneid') }} </a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
name="zoneid"
|
||||
ref="zoneid"
|
||||
@ -129,6 +207,7 @@
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="showPod"
|
||||
name="podid"
|
||||
ref="podid"
|
||||
:label="isMigrateFromVmware ? $t('label.destination.pod') : $t('label.podid')">
|
||||
@ -143,6 +222,7 @@
|
||||
></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="showCluster"
|
||||
name="clusterid"
|
||||
ref="clusterid"
|
||||
:label="isMigrateFromVmware ? $t('label.destination.cluster') : $t('label.clusterid')">
|
||||
@ -162,12 +242,84 @@
|
||||
@listedVmwareUnmanagedInstances="($e) => onListUnmanagedInstancesFromVmware($e)"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="showHost"
|
||||
name="hostid"
|
||||
ref="hostid">
|
||||
<template #label>
|
||||
<tooltip-label
|
||||
:title="$t('label.hostname')"
|
||||
:tooltip="$t('label.hostname.tooltip')"/>
|
||||
</template>
|
||||
<a-select
|
||||
v-model:value="form.hostid"
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
:filterOption="filterOption"
|
||||
:options="hostSelectOptions"
|
||||
:loading="optionLoading.hosts"
|
||||
@change="onSelectHostId"
|
||||
></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="isDiskImport"
|
||||
name="poolid"
|
||||
ref="poolid">
|
||||
<template #label>
|
||||
<tooltip-label
|
||||
:title="$t('label.storagepool')"
|
||||
:tooltip="$t('label.storagepool.tooltip')"/>
|
||||
</template>
|
||||
<a-select
|
||||
v-model:value="form.poolid"
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
:filterOption="filterOption"
|
||||
:options="poolSelectOptions"
|
||||
:loading="optionLoading.pools"
|
||||
@change="onSelectPoolId"
|
||||
></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="showDiskPath"
|
||||
name="diskpath"
|
||||
ref="diskpath">
|
||||
<template #label>
|
||||
<tooltip-label
|
||||
:title="$t('label.disk')"
|
||||
:tooltip="$t('label.disk.tooltip')"/>
|
||||
</template>
|
||||
<a-input
|
||||
v-model:value="form.diskpath"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
<a-col v-if="showDiskPath" :md="24" :lg="8">
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="onImportInstanceAction">
|
||||
<template #icon><import-outlined /></template>
|
||||
{{ $t('label.import.instance') }}
|
||||
</a-button>
|
||||
</a-col>
|
||||
</a-form>
|
||||
</a-col>
|
||||
</a-card>
|
||||
</a-row>
|
||||
<a-row v-if="showExtHost">
|
||||
<a-col class="fetch-instances-column">
|
||||
<div>
|
||||
<a-button
|
||||
shape="round"
|
||||
type="primary"
|
||||
@click="() => { fetchExtKVMInstances() }">
|
||||
{{ $t('label.fetch.instances') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-divider />
|
||||
<a-row :gutter="12">
|
||||
<a-col :md="24" :lg="!isMigrateFromVmware ? 12 : 24">
|
||||
<a-col v-if="!isDiskImport" :md="24" :lg="(!isMigrateFromVmware && showManagedInstances) ? 12 : 24">
|
||||
<a-card class="instances-card">
|
||||
<template #title>
|
||||
{{ $t('label.unmanaged.instances') }}
|
||||
@ -192,6 +344,7 @@
|
||||
</span>
|
||||
</template>
|
||||
<a-table
|
||||
v-if="!isExternal"
|
||||
class="instances-card-table"
|
||||
:loading="unmanagedInstancesLoading"
|
||||
:rowSelection="unmanagedInstanceSelection"
|
||||
@ -208,6 +361,24 @@
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<a-table
|
||||
v-if="isExternal"
|
||||
class="instances-card-table"
|
||||
:loading="unmanagedInstancesLoading"
|
||||
:rowSelection="unmanagedInstanceSelection"
|
||||
:rowKey="(record, index) => index"
|
||||
:columns="externalInstancesColumns"
|
||||
:data-source="unmanagedInstances"
|
||||
:pagination="false"
|
||||
size="middle"
|
||||
:rowClassName="getRowClassName"
|
||||
>
|
||||
<template #bodyCell="{ column, text }">
|
||||
<template v-if="column.key === 'state'">
|
||||
<status :text="text ? text : ''" displayText />
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<div class="instances-card-footer">
|
||||
<a-pagination
|
||||
class="row-element"
|
||||
@ -235,7 +406,7 @@
|
||||
</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :md="24" :lg="12" v-if="!isMigrateFromVmware">
|
||||
<a-col :md="24" :lg="12" v-if="!isMigrateFromVmware && showManagedInstances">
|
||||
<a-card class="instances-card">
|
||||
<template #title>
|
||||
{{ $t('label.managed.instances') }}
|
||||
@ -295,6 +466,7 @@
|
||||
</a-pagination>
|
||||
<div :span="24" class="action-button-right">
|
||||
<a-button
|
||||
|
||||
:disabled="!(('unmanageVirtualMachine' in $store.getters.apis) && managedInstancesSelectedRowKeys.length > 0)"
|
||||
type="primary"
|
||||
@click="onUnmanageInstanceAction">
|
||||
@ -324,13 +496,22 @@
|
||||
class="importform"
|
||||
:resource="selectedUnmanagedInstance"
|
||||
:cluster="selectedCluster"
|
||||
:host="selectedHost"
|
||||
:pool="selectedPool"
|
||||
:importsource="selectedSourceAction"
|
||||
:zoneid="this.zoneId"
|
||||
:hypervisor="this.destinationHypervisor"
|
||||
:exthost="this.values?.hostname || ''"
|
||||
:username="this.values?.username || ''"
|
||||
:password="this.values?.password || ''"
|
||||
:tmppath="this.values?.tmppath || ''"
|
||||
:diskpath="this.values?.diskpath || ''"
|
||||
:isOpen="showUnmanageForm"
|
||||
:selectedVmwareVcenter="selectedVmwareVcenter"
|
||||
@refresh-data="fetchInstances"
|
||||
@close-action="closeImportUnmanagedInstanceForm"
|
||||
@loading-changed="updateManageInstanceActionLoading"
|
||||
@track-import-jobid="trackImportJobId"
|
||||
/>
|
||||
</a-modal>
|
||||
</div>
|
||||
@ -340,7 +521,7 @@
|
||||
|
||||
<script>
|
||||
import { message } from 'ant-design-vue'
|
||||
import { ref, reactive } from 'vue'
|
||||
import { ref, reactive, toRaw } from 'vue'
|
||||
import { api } from '@/api'
|
||||
import _ from 'lodash'
|
||||
import Breadcrumb from '@/components/widgets/Breadcrumb'
|
||||
@ -349,9 +530,11 @@ import SearchView from '@/components/view/SearchView'
|
||||
import ImportUnmanagedInstances from '@/views/tools/ImportUnmanagedInstance'
|
||||
import ResourceIcon from '@/components/view/ResourceIcon'
|
||||
import SelectVmwareVcenter from '@/views/tools/SelectVmwareVcenter'
|
||||
import TooltipLabel from '@/components/widgets/TooltipLabel.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TooltipLabel,
|
||||
Breadcrumb,
|
||||
Status,
|
||||
SearchView,
|
||||
@ -387,8 +570,8 @@ export default {
|
||||
sourceDestHypervisors: {
|
||||
kvm: 'kvm'
|
||||
},
|
||||
wizardTitle: 'Import libvirt domain from KVM Host',
|
||||
wizardDescription: 'Import libvirt domain from KVM Host'
|
||||
wizardTitle: this.$t('label.desc.import.ext.kvm.wizard'),
|
||||
wizardDescription: this.$t('message.desc.import.ext.kvm.wizard')
|
||||
},
|
||||
{
|
||||
name: 'local',
|
||||
@ -396,8 +579,8 @@ export default {
|
||||
sourceDestHypervisors: {
|
||||
kvm: 'kvm'
|
||||
},
|
||||
wizardTitle: 'Import QCOW image from Local Storage',
|
||||
wizardDescription: 'Import QCOW image from Local Storage'
|
||||
wizardTitle: this.$t('label.desc.import.local.kvm.wizard'),
|
||||
wizardDescription: this.$t('message.desc.import.local.kvm.wizard')
|
||||
},
|
||||
{
|
||||
name: 'shared',
|
||||
@ -405,8 +588,8 @@ export default {
|
||||
sourceDestHypervisors: {
|
||||
kvm: 'kvm'
|
||||
},
|
||||
wizardTitle: 'Import QCOW image from Shared Storage',
|
||||
wizardDescription: 'Import QCOW image from Shared Storage'
|
||||
wizardTitle: this.$t('label.desc.import.shared.kvm.wizard'),
|
||||
wizardDescription: this.$t('message.desc.import.shared.kvm.wizard')
|
||||
}
|
||||
]
|
||||
const unmanagedInstancesColumns = [
|
||||
@ -433,6 +616,18 @@ export default {
|
||||
dataIndex: 'osdisplayname'
|
||||
}
|
||||
]
|
||||
const externalInstancesColumns = [
|
||||
{
|
||||
title: this.$t('label.name'),
|
||||
dataIndex: 'name',
|
||||
width: 200
|
||||
},
|
||||
{
|
||||
key: 'state',
|
||||
title: this.$t('label.state'),
|
||||
dataIndex: 'powerstate'
|
||||
}
|
||||
]
|
||||
const managedInstancesColumns = [
|
||||
{
|
||||
key: 'name',
|
||||
@ -463,7 +658,9 @@ export default {
|
||||
hypervisors: [],
|
||||
zones: [],
|
||||
pods: [],
|
||||
clusters: []
|
||||
clusters: [],
|
||||
hosts: [],
|
||||
pools: []
|
||||
},
|
||||
rowCount: {},
|
||||
optionLoading: {
|
||||
@ -471,7 +668,9 @@ export default {
|
||||
hypervisors: false,
|
||||
zones: false,
|
||||
pods: false,
|
||||
clusters: false
|
||||
clusters: false,
|
||||
hosts: false,
|
||||
pools: false
|
||||
},
|
||||
page: {
|
||||
unmanaged: 1,
|
||||
@ -498,15 +697,28 @@ export default {
|
||||
wizardTitle: this.$t('label.desc.importexportinstancewizard'),
|
||||
wizardDescription: this.$t('message.desc.importexportinstancewizard'),
|
||||
zone: {},
|
||||
pod: {},
|
||||
cluster: {},
|
||||
values: undefined,
|
||||
zoneId: undefined,
|
||||
podId: undefined,
|
||||
clusterId: undefined,
|
||||
hostname: undefined,
|
||||
username: undefined,
|
||||
password: undefined,
|
||||
hostId: undefined,
|
||||
poolId: undefined,
|
||||
diskpath: undefined,
|
||||
tmppath: undefined,
|
||||
poolscope: 'cluster',
|
||||
listInstancesApi: {
|
||||
unmanaged: 'listUnmanagedInstances',
|
||||
managed: 'listVirtualMachines',
|
||||
migratefromvmware: 'listVmwareDcVms'
|
||||
migratefromvmware: 'listVmwareDcVms',
|
||||
external: 'listVmsForImport'
|
||||
},
|
||||
unmanagedInstancesColumns,
|
||||
externalInstancesColumns,
|
||||
AllSourceActions,
|
||||
unmanagedInstancesLoading: false,
|
||||
unmanagedInstances: [],
|
||||
@ -542,8 +754,8 @@ export default {
|
||||
isUnmanaged () {
|
||||
return this.selectedSourceAction === 'unmanaged'
|
||||
},
|
||||
isUnmanagedOrExternal () {
|
||||
return ((this.isUnmanaged) || this.selectedSourceAction === 'external')
|
||||
isExternal () {
|
||||
return this.selectedSourceAction === 'external'
|
||||
},
|
||||
isMigrateFromVmware () {
|
||||
return this.selectedSourceAction === 'vmware'
|
||||
@ -551,6 +763,43 @@ export default {
|
||||
isDestinationKVM () {
|
||||
return this.destinationHypervisor === 'kvm'
|
||||
},
|
||||
showPod () {
|
||||
if (this.selectedSourceAction === 'shared') {
|
||||
return this.poolscope !== 'zone'
|
||||
}
|
||||
return (this.selectedSourceAction !== 'external')
|
||||
},
|
||||
showCluster () {
|
||||
if (this.selectedSourceAction === 'shared') {
|
||||
return this.poolscope !== 'zone'
|
||||
}
|
||||
return (this.selectedSourceAction !== 'external')
|
||||
},
|
||||
showHost () {
|
||||
return (this.selectedSourceAction === 'local')
|
||||
},
|
||||
showPool () {
|
||||
return (this.selectedSourceAction === 'shared')
|
||||
},
|
||||
showExtHost () {
|
||||
return (this.selectedSourceAction === 'external')
|
||||
},
|
||||
showDiskPath () {
|
||||
return ((this.selectedSourceAction === 'local') || (this.selectedSourceAction === 'shared'))
|
||||
},
|
||||
showManagedInstances () {
|
||||
return ((this.selectedSourceAction !== 'local') && (this.selectedSourceAction !== 'shared') && (this.selectedSourceAction !== 'external'))
|
||||
},
|
||||
isDiskImport () {
|
||||
return ((this.selectedSourceAction === 'local') || (this.selectedSourceAction === 'shared'))
|
||||
},
|
||||
getPoolScope () {
|
||||
if (this.selectedSourceAction === 'local') {
|
||||
return 'host'
|
||||
} else {
|
||||
return this.poolscope
|
||||
}
|
||||
},
|
||||
params () {
|
||||
return {
|
||||
zones: {
|
||||
@ -578,6 +827,28 @@ export default {
|
||||
hypervisor: this.destinationHypervisor
|
||||
},
|
||||
field: 'clusterid'
|
||||
},
|
||||
hosts: {
|
||||
list: 'listHosts',
|
||||
isLoad: false,
|
||||
options: {
|
||||
zoneid: _.get(this.zone, 'id'),
|
||||
podid: this.podId,
|
||||
clusterid: this.clusterId
|
||||
},
|
||||
field: 'hostid'
|
||||
},
|
||||
pools: {
|
||||
list: 'listStoragePools',
|
||||
isLoad: false,
|
||||
options: {
|
||||
zoneid: _.get(this.zone, 'id'),
|
||||
podid: this.podId,
|
||||
clusterid: this.clusterId,
|
||||
hostid: this.hostId,
|
||||
scope: this.getPoolScope
|
||||
},
|
||||
field: 'poolid'
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -616,6 +887,24 @@ export default {
|
||||
})
|
||||
return options
|
||||
},
|
||||
hostSelectOptions () {
|
||||
const options = this.options.hosts.map((host) => {
|
||||
return {
|
||||
label: host.name,
|
||||
value: host.id
|
||||
}
|
||||
})
|
||||
return options
|
||||
},
|
||||
poolSelectOptions () {
|
||||
const options = this.options.pools.map((pool) => {
|
||||
return {
|
||||
label: pool.name,
|
||||
value: pool.id
|
||||
}
|
||||
})
|
||||
return options
|
||||
},
|
||||
unmanagedInstanceSelection () {
|
||||
return {
|
||||
type: 'radio',
|
||||
@ -637,6 +926,22 @@ export default {
|
||||
return _.find(this.options.clusters, (option) => option.id === this.clusterId)
|
||||
}
|
||||
return {}
|
||||
},
|
||||
selectedHost () {
|
||||
if (this.options.hosts &&
|
||||
this.options.hosts.length > 0 &&
|
||||
this.hostId) {
|
||||
return _.find(this.options.hosts, (option) => option.id === this.hostId)
|
||||
}
|
||||
return {}
|
||||
},
|
||||
selectedPool () {
|
||||
if (this.options.pools &&
|
||||
this.options.pools.length > 0 &&
|
||||
this.poolId) {
|
||||
return _.find(this.options.pools, (option) => option.id === this.poolId)
|
||||
}
|
||||
return {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -645,7 +950,11 @@ export default {
|
||||
this.form = reactive({
|
||||
sourceHypervisor: this.sourceHypervisor
|
||||
})
|
||||
this.rules = reactive({})
|
||||
this.rules = reactive({
|
||||
hostname: [{ required: true, message: this.$t('message.error.input.value') }],
|
||||
username: [{ required: true, message: this.$t('message.error.input.value') }],
|
||||
password: [{ required: true, message: this.$t('message.error.input.value') }]
|
||||
})
|
||||
},
|
||||
fetchData () {
|
||||
this.unmanagedInstances = []
|
||||
@ -672,7 +981,7 @@ export default {
|
||||
param.loading = true
|
||||
param.opts = []
|
||||
const options = param.options || {}
|
||||
if (!('listall' in options) && !['zones', 'pods', 'clusters'].includes(name)) {
|
||||
if (!('listall' in options) && !['zones', 'pods', 'clusters', 'hosts', 'pools'].includes(name)) {
|
||||
options.listall = true
|
||||
}
|
||||
api(param.list, options).then((response) => {
|
||||
@ -710,13 +1019,13 @@ export default {
|
||||
return 'dark-row'
|
||||
},
|
||||
handleFetchOptionsSuccess (name, param) {
|
||||
if (['zones', 'pods', 'clusters'].includes(name)) {
|
||||
if (['zones', 'pods', 'clusters', 'hosts', 'pools'].includes(name)) {
|
||||
let paramid = ''
|
||||
const query = Object.assign({}, this.$route.query)
|
||||
if (query[param.field] && _.find(this.options[name], (option) => option.id === query[param.field])) {
|
||||
paramid = query[param.field]
|
||||
}
|
||||
if (!paramid && this.options[name].length === 1) {
|
||||
if (!paramid && this.options[name].length > 0) {
|
||||
paramid = (this.options[name])[0].id
|
||||
}
|
||||
if (paramid) {
|
||||
@ -729,6 +1038,12 @@ export default {
|
||||
} else if (name === 'clusters') {
|
||||
this.form.clusterid = paramid
|
||||
this.onSelectClusterId(paramid)
|
||||
} else if (name === 'hosts') {
|
||||
this.form.hostid = paramid
|
||||
this.onSelectHostId(paramid)
|
||||
} else if (name === 'pools') {
|
||||
this.form.poolid = paramid
|
||||
this.onSelectPoolId(paramid)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -775,15 +1090,19 @@ export default {
|
||||
this.zoneId = value
|
||||
this.podId = null
|
||||
this.clusterId = null
|
||||
this.hostId = null
|
||||
this.poolId = null
|
||||
this.zone = _.find(this.options.zones, (option) => option.id === value)
|
||||
this.resetLists()
|
||||
this.form.clusterid = undefined
|
||||
this.form.podid = undefined
|
||||
this.form.poolid = undefined
|
||||
this.updateQuery('zoneid', value)
|
||||
this.fetchOptions(this.params.pods, 'pods')
|
||||
},
|
||||
onSelectPodId (value) {
|
||||
this.podId = value
|
||||
this.pod = _.find(this.options.pods, (option) => option.id === value)
|
||||
this.resetLists()
|
||||
this.clusterId = null
|
||||
this.form.clusterid = undefined
|
||||
@ -792,17 +1111,44 @@ export default {
|
||||
},
|
||||
onSelectClusterId (value) {
|
||||
this.clusterId = value
|
||||
this.cluster = _.find(this.options.clusters, (option) => option.id === value)
|
||||
this.resetLists()
|
||||
this.updateQuery('clusterid', value)
|
||||
if (this.isUnmanaged) {
|
||||
this.fetchInstances()
|
||||
} else if (this.showHost) {
|
||||
this.fetchOptions(this.params.hosts, 'hosts', value)
|
||||
} else if (this.showPool) {
|
||||
this.fetchOptions(this.params.pools, 'pools', value)
|
||||
}
|
||||
},
|
||||
onSelectHostId (value) {
|
||||
this.hostId = value
|
||||
this.updateQuery('scope', 'local')
|
||||
this.fetchOptions(this.params.pools, 'pools', value)
|
||||
},
|
||||
onSelectPoolId (value) {
|
||||
this.poolId = value
|
||||
},
|
||||
onSelectPoolScope (value) {
|
||||
this.poolscope = value
|
||||
this.poolId = null
|
||||
this.updateQuery('scope', value)
|
||||
this.fetchOptions(this.params.pools, 'pools', value)
|
||||
},
|
||||
fetchInstances () {
|
||||
this.fetchUnmanagedInstances()
|
||||
if (this.isUnmanaged) {
|
||||
this.fetchManagedInstances()
|
||||
} else if (this.kvmOption === 'external') {
|
||||
this.fetchExternalInstances()
|
||||
}
|
||||
},
|
||||
fetchUnmanagedInstances (page, pageSize) {
|
||||
if (this.isExternal) {
|
||||
this.fetchExtKVMInstances(page, pageSize)
|
||||
return
|
||||
}
|
||||
const params = {
|
||||
clusterid: this.clusterId
|
||||
}
|
||||
@ -847,6 +1193,51 @@ export default {
|
||||
this.unmanagedInstancesLoading = false
|
||||
})
|
||||
},
|
||||
fetchExtKVMInstances (page, pageSize) {
|
||||
const params = {
|
||||
zoneid: this.zoneid
|
||||
}
|
||||
const query = Object.assign({}, this.$route.query)
|
||||
this.page.unmanaged = page || parseInt(query.unmanagedpage) || this.page.unmanaged
|
||||
this.updateQuery('unmanagedpage', this.page.unmanaged)
|
||||
params.page = this.page.unmanaged
|
||||
this.pageSize.unmanaged = pageSize || this.pageSize.unmanaged
|
||||
params.pagesize = this.pageSize.unmanaged
|
||||
this.unmanagedInstances = []
|
||||
this.unmanagedInstancesSelectedRowKeys = []
|
||||
if (this.searchParams.unmanaged.keyword) {
|
||||
params.keyword = this.searchParams.unmanaged.keyword
|
||||
}
|
||||
this.values = toRaw(this.form)
|
||||
this.unmanagedInstancesLoading = true
|
||||
params.zoneid = this.zoneId
|
||||
params.host = this.values.hostname
|
||||
params.username = this.values.username
|
||||
params.password = this.values.password
|
||||
params.hypervisor = this.destinationHypervisor
|
||||
var details = ['host', 'username', 'password']
|
||||
for (var detail of details) {
|
||||
if (!params[detail]) {
|
||||
this.$notification.error({
|
||||
message: this.$t('message.request.failed'),
|
||||
description: this.$t('message.please.enter.valid.value') + ': ' + this.$t('label.' + detail.toLowerCase())
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
this.searchParams.unmanaged = params
|
||||
api(this.listInstancesApi.external, params).then(json => {
|
||||
const listUnmanagedInstances = json.listvmsforimportresponse.unmanagedinstance
|
||||
if (this.arrayHasItems(listUnmanagedInstances)) {
|
||||
this.unmanagedInstances = this.unmanagedInstances.concat(listUnmanagedInstances)
|
||||
}
|
||||
this.itemCount.unmanaged = json.listvmsforimportresponse.count
|
||||
}).catch(error => {
|
||||
this.$notifyError(error)
|
||||
}).finally(() => {
|
||||
this.unmanagedInstancesLoading = false
|
||||
})
|
||||
},
|
||||
searchUnmanagedInstances (params) {
|
||||
this.searchParams.unmanaged.keyword = params.searchQuery
|
||||
this.fetchUnmanagedInstances()
|
||||
@ -903,6 +1294,9 @@ export default {
|
||||
},
|
||||
updateManageInstanceActionLoading (value) {
|
||||
this.importUnmanagedInstanceLoading = value
|
||||
if (!value) {
|
||||
this.fetchInstances()
|
||||
}
|
||||
},
|
||||
onManageInstanceAction () {
|
||||
this.selectedUnmanagedInstance = {}
|
||||
@ -925,6 +1319,25 @@ export default {
|
||||
this.showUnmanageForm = true
|
||||
}
|
||||
},
|
||||
onImportInstanceAction () {
|
||||
this.selectedUnmanagedInstance = {}
|
||||
this.values = toRaw(this.form)
|
||||
if (!this.values.diskpath) {
|
||||
this.$notification.error({
|
||||
message: this.$t('message.request.failed'),
|
||||
description: this.$t('message.please.enter.valid.value') + ': ' + this.$t('label.disk.path')
|
||||
})
|
||||
return
|
||||
}
|
||||
if (this.showPool && !this.values.poolid) {
|
||||
this.$notification.error({
|
||||
message: this.$t('message.request.failed'),
|
||||
description: this.$t('message.please.enter.valid.value') + ': ' + this.$t('label.storagepool')
|
||||
})
|
||||
return
|
||||
}
|
||||
this.showUnmanageForm = true
|
||||
},
|
||||
closeImportUnmanagedInstanceForm () {
|
||||
this.selectedUnmanagedInstance = {}
|
||||
this.showUnmanageForm = false
|
||||
@ -950,6 +1363,21 @@ export default {
|
||||
}
|
||||
})
|
||||
},
|
||||
trackImportJobId (details) {
|
||||
const jobId = details[0]
|
||||
const name = details[1]
|
||||
this.$pollJob({
|
||||
jobId,
|
||||
title: this.$t('label.import.instance'),
|
||||
description: this.$t('label.import.instance'),
|
||||
loadingMessage: `${this.$t('label.import.instance')} ${name} ${this.$t('label.in.progress')}`,
|
||||
catchMessage: this.$t('error.fetching.async.job.result'),
|
||||
successMessage: this.$t('message.success.import.instance') + ' ' + name,
|
||||
successMethod: (result) => {
|
||||
this.fetchInstances()
|
||||
}
|
||||
})
|
||||
},
|
||||
unmanageInstances () {
|
||||
for (var index of this.managedInstancesSelectedRowKeys) {
|
||||
const vm = this.managedInstances[index]
|
||||
@ -987,21 +1415,25 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
:deep(.ant-table-small) > .ant-table-content > .ant-table-body {
|
||||
:deep(.ant-table-small) > .ant-table-content > .ant-table-body {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.importform {
|
||||
.importform {
|
||||
width: 80vw;
|
||||
}
|
||||
.instances-card {
|
||||
}
|
||||
.instances-card {
|
||||
height: 100%;
|
||||
}
|
||||
.instances-card-table {
|
||||
}
|
||||
.source-dest-card {
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
}
|
||||
.instances-card-table {
|
||||
overflow-y: auto;
|
||||
margin-bottom: 100px;
|
||||
}
|
||||
.instances-card-footer {
|
||||
}
|
||||
.instances-card-footer {
|
||||
height: 100px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
@ -1009,19 +1441,24 @@ export default {
|
||||
margin-left: 10px;
|
||||
right: 0;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.row-element {
|
||||
}
|
||||
.row-element {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.action-button-left {
|
||||
}
|
||||
.action-button-left {
|
||||
text-align: left;
|
||||
}
|
||||
.action-button-right {
|
||||
}
|
||||
.action-button-right {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
.fetch-instances-column {
|
||||
width: 50%;
|
||||
margin-left: 50%;
|
||||
padding-left: 24px;
|
||||
}
|
||||
|
||||
.breadcrumb-card {
|
||||
.breadcrumb-card {
|
||||
margin-left: -24px;
|
||||
margin-right: -24px;
|
||||
margin-top: -16px;
|
||||
|
||||
@ -65,6 +65,10 @@ public class SshHelper {
|
||||
}
|
||||
|
||||
public static void scpFrom(String host, int port, String user, File permKeyFile, String localTargetDirectory, String remoteTargetFile) throws Exception {
|
||||
scpFrom(host, port, user, permKeyFile, null, localTargetDirectory, remoteTargetFile);
|
||||
}
|
||||
|
||||
public static void scpFrom(String host, int port, String user, File permKeyFile, String password, String localTargetDirectory, String remoteTargetFile) throws Exception {
|
||||
com.trilead.ssh2.Connection conn = null;
|
||||
com.trilead.ssh2.SCPClient scpClient = null;
|
||||
|
||||
@ -72,11 +76,20 @@ public class SshHelper {
|
||||
conn = new com.trilead.ssh2.Connection(host, port);
|
||||
conn.connect(null, DEFAULT_CONNECT_TIMEOUT, DEFAULT_KEX_TIMEOUT);
|
||||
|
||||
if (!conn.authenticateWithPublicKey(user, permKeyFile, null)) {
|
||||
if (permKeyFile == null) {
|
||||
if (!conn.authenticateWithPassword(user, password)) {
|
||||
String msg = "Failed to authentication SSH user " + user + " on host " + host;
|
||||
s_logger.error(msg);
|
||||
throw new Exception(msg);
|
||||
}
|
||||
} else {
|
||||
if (!conn.authenticateWithPublicKey(user, permKeyFile, password)) {
|
||||
String msg = "Failed to authentication SSH user " + user + " on host " + host;
|
||||
s_logger.error(msg);
|
||||
throw new Exception(msg);
|
||||
}
|
||||
}
|
||||
|
||||
scpClient = conn.createSCPClient();
|
||||
|
||||
scpClient.get(remoteTargetFile, localTargetDirectory);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user