mirror of https://github.com/apache/cloudstack.git
vmware: allow configuring appliances on the VM instance wizard when OVF properties are available (#3271)
Problem: In Vmware, appliances that have options that are required to be answered before deployments are configurable through vSphere vCenter user interface but it is not possible from the CloudStack user interface. Root cause: CloudStack does not handle vApp configuration options during deployments if the appliance contains configurable options. These configurations are mandatory for VM deployment from the appliance on Vmware vSphere vCenter. As shown in the image below, Vmware detects there are mandatory configurations that the administrator must set before deploy the VM from the appliance (in red on the image below): Solution: On template registration, after it is downloaded to secondary storage, the OVF file is examined and OVF properties are extracted from the file when available. OVF properties extracted from templates after being downloaded to secondary storage are stored on the new table 'template_ovf_properties'. A new optional section is added to the VM deployment wizard in the UI: If the selected template does not contain OVF properties, then the optional section is not displayed on the wizard. If the selected template contains OVF properties, then the optional new section is displayed. Each OVF property is displayed and the user must complete every property before proceeding to the next section. If any configuration property is empty, then a dialog is displayed indicating that there are empty properties which must be set before proceeding image The specific OVF properties set on deployment are stored on the 'user_vm_details' table with the prefix: 'ovfproperties-'. The VM is configured with the vApp configuration section containing the values that the user provided on the wizard.
This commit is contained in:
parent
f56c50fd6a
commit
3c2af55d81
|
|
@ -19,6 +19,7 @@ package com.cloud.agent.api.storage;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
|
@ -38,7 +39,9 @@ import org.apache.commons.lang.math.NumberUtils;
|
|||
import org.apache.log4j.Logger;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import com.cloud.agent.api.to.DatadiskTO;
|
||||
|
|
@ -69,6 +72,92 @@ public class OVFHelper {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text value of a node's child with name "childNodeName", null if not present
|
||||
* Example:
|
||||
* <Node>
|
||||
* <childNodeName>Text value</childNodeName>
|
||||
* </Node>
|
||||
*/
|
||||
private String getChildNodeValue(Node node, String childNodeName) {
|
||||
if (node != null && node.hasChildNodes()) {
|
||||
NodeList childNodes = node.getChildNodes();
|
||||
for (int i = 0; i < childNodes.getLength(); i++) {
|
||||
Node value = childNodes.item(i);
|
||||
if (value != null && value.getNodeName().equals(childNodeName)) {
|
||||
return value.getTextContent();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create OVFProperty class from the parsed node. Note that some fields may not be present.
|
||||
* The key attribute is required
|
||||
*/
|
||||
protected OVFPropertyTO createOVFPropertyFromNode(Node node) {
|
||||
Element property = (Element) node;
|
||||
String key = property.getAttribute("ovf:key");
|
||||
if (StringUtils.isBlank(key)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String value = property.getAttribute("ovf:value");
|
||||
String type = property.getAttribute("ovf:type");
|
||||
String qualifiers = property.getAttribute("ovf:qualifiers");
|
||||
String userConfigurableStr = property.getAttribute("ovf:userConfigurable");
|
||||
boolean userConfigurable = StringUtils.isNotBlank(userConfigurableStr) &&
|
||||
userConfigurableStr.equalsIgnoreCase("true");
|
||||
String passStr = property.getAttribute("ovf:password");
|
||||
boolean password = StringUtils.isNotBlank(passStr) && passStr.equalsIgnoreCase("true");
|
||||
String label = getChildNodeValue(node, "Label");
|
||||
String description = getChildNodeValue(node, "Description");
|
||||
return new OVFPropertyTO(key, type, value, qualifiers, userConfigurable, label, description, password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve OVF properties from a parsed OVF file, with attribute 'ovf:userConfigurable' set to true
|
||||
*/
|
||||
private List<OVFPropertyTO> getConfigurableOVFPropertiesFromDocument(Document doc) {
|
||||
List<OVFPropertyTO> props = new ArrayList<>();
|
||||
NodeList properties = doc.getElementsByTagName("Property");
|
||||
if (properties != null) {
|
||||
for (int i = 0; i < properties.getLength(); i++) {
|
||||
Node node = properties.item(i);
|
||||
if (node == null) {
|
||||
continue;
|
||||
}
|
||||
OVFPropertyTO prop = createOVFPropertyFromNode(node);
|
||||
if (prop != null && prop.isUserConfigurable()) {
|
||||
props.add(prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get properties from OVF file located on ovfFilePath
|
||||
*/
|
||||
public List<OVFPropertyTO> getOVFPropertiesFromFile(String ovfFilePath) throws ParserConfigurationException, IOException, SAXException {
|
||||
if (StringUtils.isBlank(ovfFilePath)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
File ovfFile = new File(ovfFilePath);
|
||||
final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(ovfFile);
|
||||
return getConfigurableOVFPropertiesFromDocument(doc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get properties from OVF XML string
|
||||
*/
|
||||
protected List<OVFPropertyTO> getOVFPropertiesXmlString(final String ovfFilePath) throws ParserConfigurationException, IOException, SAXException {
|
||||
InputSource is = new InputSource(new StringReader(ovfFilePath));
|
||||
final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is);
|
||||
return getConfigurableOVFPropertiesFromDocument(doc);
|
||||
}
|
||||
|
||||
public List<DatadiskTO> getOVFVolumeInfo(final String ovfFilePath) {
|
||||
if (StringUtils.isBlank(ovfFilePath)) {
|
||||
return new ArrayList<DatadiskTO>();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// 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.storage;
|
||||
|
||||
public interface OVFProperty {
|
||||
|
||||
Long getTemplateId();
|
||||
String getKey();
|
||||
String getType();
|
||||
String getValue();
|
||||
String getQualifiers();
|
||||
Boolean isUserConfigurable();
|
||||
String getLabel();
|
||||
String getDescription();
|
||||
Boolean isPassword();
|
||||
}
|
||||
|
|
@ -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 com.cloud.agent.api.storage;
|
||||
|
||||
import com.cloud.agent.api.LogLevel;
|
||||
|
||||
/**
|
||||
* Used to represent travel objects like:
|
||||
* <Property ovf:key="RouteDefault" ovf:type="string" ovf:qualifiers="ValueMap{"Default Route","Remote HTTP and SSH Client Routes"}" ovf:value="Default Route" ovf:userConfigurable="true">
|
||||
* <Label>Select Route Type</Label>
|
||||
* <Description>Select the route/gateway type.
|
||||
* Choose "Default Route" to route all traffic through the Management gateway. Use this option when enabling Smart Licensing registration at initial deployment.
|
||||
* Choose "Remote HTTP and SSH Client Routes" to route only traffic destined for the management client(s), when they are on remote networks.</Description>
|
||||
* </Property>
|
||||
*/
|
||||
public class OVFPropertyTO implements OVFProperty {
|
||||
|
||||
private String key;
|
||||
private String type;
|
||||
@LogLevel(LogLevel.Log4jLevel.Off)
|
||||
private String value;
|
||||
private String qualifiers;
|
||||
private Boolean userConfigurable;
|
||||
private String label;
|
||||
private String description;
|
||||
private Boolean password;
|
||||
|
||||
public OVFPropertyTO() {
|
||||
}
|
||||
|
||||
public OVFPropertyTO(String key, String value, boolean password) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public OVFPropertyTO(String key, String type, String value, String qualifiers, boolean userConfigurable,
|
||||
String label, String description, boolean password) {
|
||||
this.key = key;
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
this.qualifiers = qualifiers;
|
||||
this.userConfigurable = userConfigurable;
|
||||
this.label = label;
|
||||
this.description = description;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getTemplateId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getQualifiers() {
|
||||
return qualifiers;
|
||||
}
|
||||
|
||||
public void setQualifiers(String qualifiers) {
|
||||
this.qualifiers = qualifiers;
|
||||
}
|
||||
|
||||
public Boolean isUserConfigurable() {
|
||||
return userConfigurable;
|
||||
}
|
||||
|
||||
public void setUserConfigurable(Boolean userConfigurable) {
|
||||
this.userConfigurable = userConfigurable;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Boolean isPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(Boolean password) {
|
||||
this.password = password;
|
||||
}
|
||||
}
|
||||
|
|
@ -20,7 +20,10 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.cloud.agent.api.LogLevel;
|
||||
import com.cloud.agent.api.storage.OVFPropertyTO;
|
||||
import com.cloud.template.VirtualMachineTemplate.BootloaderType;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import com.cloud.vm.VirtualMachine.Type;
|
||||
|
||||
|
|
@ -75,6 +78,8 @@ public class VirtualMachineTO {
|
|||
|
||||
Map<String, String> guestOsDetails = new HashMap<String, String>();
|
||||
Map<String, String> extraConfig = new HashMap<>();
|
||||
@LogLevel(LogLevel.Log4jLevel.Off)
|
||||
Pair<String, List<OVFPropertyTO>> ovfProperties;
|
||||
|
||||
public VirtualMachineTO(long id, String instanceName, VirtualMachine.Type type, int cpus, Integer speed, long minRam, long maxRam, BootloaderType bootloader,
|
||||
String os, boolean enableHA, boolean limitCpuUse, String vncPassword) {
|
||||
|
|
@ -367,4 +372,12 @@ public class VirtualMachineTO {
|
|||
public Map<String, String> getExtraConfig() {
|
||||
return extraConfig;
|
||||
}
|
||||
|
||||
public Pair<String, List<OVFPropertyTO>> getOvfProperties() {
|
||||
return ovfProperties;
|
||||
}
|
||||
|
||||
public void setOvfProperties(Pair<String, List<OVFPropertyTO>> ovfProperties) {
|
||||
this.ovfProperties = ovfProperties;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -217,7 +217,8 @@ public interface UserVmService {
|
|||
Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod,
|
||||
String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIp, Boolean displayVm, String keyboard,
|
||||
List<Long> affinityGroupIdList, Map<String, String> customParameter, String customId, Map<String, Map<Integer, String>> dhcpOptionMap,
|
||||
Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap) throws InsufficientCapacityException,
|
||||
Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap,
|
||||
Map<String, String> userVmOVFProperties) throws InsufficientCapacityException,
|
||||
ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException;
|
||||
|
||||
/**
|
||||
|
|
@ -298,7 +299,8 @@ public interface UserVmService {
|
|||
List<Long> securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor,
|
||||
HTTPMethod httpmethod, String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard,
|
||||
List<Long> affinityGroupIdList, Map<String, String> customParameters, String customId, Map<String, Map<Integer, String>> dhcpOptionMap,
|
||||
Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap) throws InsufficientCapacityException,
|
||||
Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap,
|
||||
Map<String, String> userVmOVFProperties) throws InsufficientCapacityException,
|
||||
ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException;
|
||||
|
||||
/**
|
||||
|
|
@ -376,7 +378,8 @@ public interface UserVmService {
|
|||
UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> networkIdList, Account owner,
|
||||
String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData,
|
||||
String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard, List<Long> affinityGroupIdList,
|
||||
Map<String, String> customParameters, String customId, Map<String, Map<Integer, String>> dhcpOptionMap, Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap)
|
||||
Map<String, String> customParameters, String customId, Map<String, Map<Integer, String>> dhcpOptionMap, Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap,
|
||||
Map<String, String> templateOvfPropertiesMap)
|
||||
|
||||
throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException;
|
||||
|
||||
|
|
|
|||
|
|
@ -199,6 +199,7 @@ public class ApiConstants {
|
|||
public static final String ISO_GUEST_OS_NONE = "None";
|
||||
public static final String JOB_ID = "jobid";
|
||||
public static final String JOB_STATUS = "jobstatus";
|
||||
public static final String LABEL = "label";
|
||||
public static final String LASTNAME = "lastname";
|
||||
public static final String LEVEL = "level";
|
||||
public static final String LENGTH = "length";
|
||||
|
|
@ -233,6 +234,7 @@ public class ApiConstants {
|
|||
public static final String OS_NAME_FOR_HYPERVISOR = "osnameforhypervisor";
|
||||
public static final String OUTOFBANDMANAGEMENT_POWERSTATE = "outofbandmanagementpowerstate";
|
||||
public static final String OUTOFBANDMANAGEMENT_ENABLED = "outofbandmanagementenabled";
|
||||
public static final String OVF_PROPERTIES = "ovfproperties";
|
||||
public static final String PARAMS = "params";
|
||||
public static final String PARENT_ID = "parentid";
|
||||
public static final String PARENT_DOMAIN_ID = "parentdomainid";
|
||||
|
|
@ -277,6 +279,7 @@ public class ApiConstants {
|
|||
public static final String RESPONSE = "response";
|
||||
public static final String REVERTABLE = "revertable";
|
||||
public static final String REGISTERED = "registered";
|
||||
public static final String QUALIFIERS = "qualifiers";
|
||||
public static final String QUERY_FILTER = "queryfilter";
|
||||
public static final String SCHEDULE = "schedule";
|
||||
public static final String SCOPE = "scope";
|
||||
|
|
@ -334,6 +337,7 @@ public class ApiConstants {
|
|||
public static final String USER_ID = "userid";
|
||||
public static final String USE_SSL = "ssl";
|
||||
public static final String USERNAME = "username";
|
||||
public static final String USER_CONFIGURABLE = "userconfigurable";
|
||||
public static final String USER_SECURITY_GROUP_LIST = "usersecuritygrouplist";
|
||||
public static final String USE_VIRTUAL_NETWORK = "usevirtualnetwork";
|
||||
public static final String Update_IN_SEQUENCE = "updateinsequence";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
// 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.user.template;
|
||||
|
||||
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 org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.BaseListCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.TemplateOVFPropertyResponse;
|
||||
import org.apache.cloudstack.api.response.TemplateResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
|
||||
@APICommand(name = ListTemplateOVFProperties.APINAME,
|
||||
description = "List template OVF properties if available.",
|
||||
responseObject = TemplateOVFPropertyResponse.class,
|
||||
authorized = {RoleType.Admin, RoleType.DomainAdmin, RoleType.ResourceAdmin, RoleType.User})
|
||||
public class ListTemplateOVFProperties extends BaseListCmd {
|
||||
|
||||
public static final String APINAME = "listTemplateOvfProperties";
|
||||
|
||||
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = TemplateResponse.class,
|
||||
description = "the template ID", required = true)
|
||||
private Long templateId;
|
||||
|
||||
public Long getTemplateId() {
|
||||
return templateId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
|
||||
ListResponse<TemplateOVFPropertyResponse> response = _queryService.listTemplateOVFProperties(this);
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return CallContext.current().getCallingAccount().getId();
|
||||
}
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@ import java.util.LinkedHashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.cloud.agent.api.LogLevel;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.affinity.AffinityGroupResponse;
|
||||
import org.apache.cloudstack.api.ACL;
|
||||
|
|
@ -205,6 +206,11 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityG
|
|||
@Parameter(name = ApiConstants.COPY_IMAGE_TAGS, type = CommandType.BOOLEAN, since = "4.13", description = "if true the image tags (if any) will be copied to the VM, default value is false")
|
||||
private Boolean copyImageTags;
|
||||
|
||||
@Parameter(name = ApiConstants.OVF_PROPERTIES, type = CommandType.MAP, since = "4.13",
|
||||
description = "used to specify the OVF properties.")
|
||||
@LogLevel(LogLevel.Log4jLevel.Off)
|
||||
private Map vmOvfProperties;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
|
@ -253,6 +259,19 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityG
|
|||
return customparameterMap;
|
||||
}
|
||||
|
||||
public Map<String, String> getVmOVFProperties() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
if (MapUtils.isNotEmpty(vmOvfProperties)) {
|
||||
Collection parameterCollection = vmOvfProperties.values();
|
||||
Iterator iterator = parameterCollection.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
HashMap<String, String> entry = (HashMap<String, String>)iterator.next();
|
||||
map.put(entry.get("key"), entry.get("value"));
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public String getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,124 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
package org.apache.cloudstack.api.response;
|
||||
|
||||
import com.cloud.agent.api.storage.OVFProperty;
|
||||
import com.cloud.serializer.Param;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseResponse;
|
||||
import org.apache.cloudstack.api.EntityReference;
|
||||
|
||||
@EntityReference(value = OVFProperty.class)
|
||||
public class TemplateOVFPropertyResponse extends BaseResponse {
|
||||
|
||||
@SerializedName(ApiConstants.KEY)
|
||||
@Param(description = "the ovf property key")
|
||||
private String key;
|
||||
|
||||
@SerializedName(ApiConstants.TYPE)
|
||||
@Param(description = "the ovf property type")
|
||||
private String type;
|
||||
|
||||
@SerializedName(ApiConstants.VALUE)
|
||||
@Param(description = "the ovf property value")
|
||||
private String value;
|
||||
|
||||
@SerializedName(ApiConstants.PASSWORD)
|
||||
@Param(description = "is the ovf property a password")
|
||||
private Boolean password;
|
||||
|
||||
@SerializedName(ApiConstants.QUALIFIERS)
|
||||
@Param(description = "the ovf property qualifiers")
|
||||
private String qualifiers;
|
||||
|
||||
@SerializedName(ApiConstants.USER_CONFIGURABLE)
|
||||
@Param(description = "is the ovf property user configurable")
|
||||
private Boolean userConfigurable;
|
||||
|
||||
@SerializedName(ApiConstants.LABEL)
|
||||
@Param(description = "the ovf property label")
|
||||
private String label;
|
||||
|
||||
@SerializedName(ApiConstants.DESCRIPTION)
|
||||
@Param(description = "the ovf property label")
|
||||
private String description;
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getQualifiers() {
|
||||
return qualifiers;
|
||||
}
|
||||
|
||||
public void setQualifiers(String qualifiers) {
|
||||
this.qualifiers = qualifiers;
|
||||
}
|
||||
|
||||
public Boolean getUserConfigurable() {
|
||||
return userConfigurable;
|
||||
}
|
||||
|
||||
public void setUserConfigurable(Boolean userConfigurable) {
|
||||
this.userConfigurable = userConfigurable;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Boolean getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(Boolean password) {
|
||||
this.password = password;
|
||||
}
|
||||
}
|
||||
|
|
@ -202,7 +202,7 @@ public class TemplateResponse extends BaseResponseWithTagInformation implements
|
|||
private Boolean requiresHvm;
|
||||
|
||||
public TemplateResponse() {
|
||||
tags = new LinkedHashSet<ResourceTagResponse>();
|
||||
tags = new LinkedHashSet<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ import org.apache.cloudstack.api.command.user.project.ListProjectsCmd;
|
|||
import org.apache.cloudstack.api.command.user.resource.ListDetailOptionsCmd;
|
||||
import org.apache.cloudstack.api.command.user.securitygroup.ListSecurityGroupsCmd;
|
||||
import org.apache.cloudstack.api.command.user.tag.ListTagsCmd;
|
||||
import org.apache.cloudstack.api.command.user.template.ListTemplateOVFProperties;
|
||||
import org.apache.cloudstack.api.command.user.template.ListTemplatesCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.ListVMsCmd;
|
||||
import org.apache.cloudstack.api.command.user.vmgroup.ListVMGroupsCmd;
|
||||
|
|
@ -71,6 +72,7 @@ import org.apache.cloudstack.api.response.SecurityGroupResponse;
|
|||
import org.apache.cloudstack.api.response.ServiceOfferingResponse;
|
||||
import org.apache.cloudstack.api.response.StoragePoolResponse;
|
||||
import org.apache.cloudstack.api.response.StorageTagResponse;
|
||||
import org.apache.cloudstack.api.response.TemplateOVFPropertyResponse;
|
||||
import org.apache.cloudstack.api.response.TemplateResponse;
|
||||
import org.apache.cloudstack.api.response.UserResponse;
|
||||
import org.apache.cloudstack.api.response.UserVmResponse;
|
||||
|
|
@ -166,4 +168,6 @@ public interface QueryService {
|
|||
ListResponse<HostTagResponse> searchForHostTags(ListHostTagsCmd cmd);
|
||||
|
||||
ListResponse<ManagementServerResponse> listManagementServers(ListMgmtsCmd cmd);
|
||||
|
||||
ListResponse<TemplateOVFPropertyResponse> listTemplateOVFProperties(ListTemplateOVFProperties cmd);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
// 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.storage;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.SAXParseException;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class OVFHelperTest {
|
||||
|
||||
private String ovfFileProductSection =
|
||||
"<ProductSection>" +
|
||||
"<Info>VM Arguments</Info>" +
|
||||
"<Property ovf:key=\"va-ssh-public-key\" ovf:type=\"string\" ovf:userConfigurable=\"true\" ovf:value=\"\">" +
|
||||
"<Label>Set the SSH public key allowed to access the appliance</Label>" +
|
||||
"<Description>This will enable the SSHD service and configure the specified public key</Description>" +
|
||||
"</Property>" +
|
||||
"<Property ovf:key=\"user-data\" ovf:type=\"string\" ovf:userConfigurable=\"true\" ovf:value=\"\">" +
|
||||
"<Label>User data to be made available inside the instance</Label>" +
|
||||
"<Description>This allows to pass any text to the appliance. The value should be encoded in base64</Description>" +
|
||||
"</Property>" +
|
||||
"</ProductSection>";
|
||||
|
||||
private OVFHelper ovfHelper = new OVFHelper();
|
||||
|
||||
@Test
|
||||
public void testGetOVFPropertiesValidOVF() throws IOException, SAXException, ParserConfigurationException {
|
||||
List<OVFPropertyTO> props = ovfHelper.getOVFPropertiesXmlString(ovfFileProductSection);
|
||||
Assert.assertEquals(2, props.size());
|
||||
}
|
||||
|
||||
@Test(expected = SAXParseException.class)
|
||||
public void testGetOVFPropertiesInvalidOVF() throws IOException, SAXException, ParserConfigurationException {
|
||||
ovfHelper.getOVFPropertiesXmlString(ovfFileProductSection + "xxxxxxxxxxxxxxxxx");
|
||||
}
|
||||
}
|
||||
|
|
@ -20,9 +20,11 @@
|
|||
package com.cloud.agent.api.storage;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.Command;
|
||||
import com.cloud.agent.api.LogLevel;
|
||||
import com.cloud.storage.VMTemplateStorageResourceAssoc;
|
||||
import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
|
||||
|
||||
|
|
@ -36,6 +38,8 @@ public class DownloadAnswer extends Answer {
|
|||
private long templateSize = 0L;
|
||||
private long templatePhySicalSize = 0L;
|
||||
private String checkSum;
|
||||
@LogLevel(LogLevel.Log4jLevel.Off)
|
||||
private List<OVFPropertyTO> ovfProperties;
|
||||
|
||||
public String getCheckSum() {
|
||||
return checkSum;
|
||||
|
|
@ -146,4 +150,11 @@ public class DownloadAnswer extends Answer {
|
|||
return templatePhySicalSize;
|
||||
}
|
||||
|
||||
public List<OVFPropertyTO> getOvfProperties() {
|
||||
return ovfProperties;
|
||||
}
|
||||
|
||||
public void setOvfProperties(List<OVFPropertyTO> ovfProperties) {
|
||||
this.ovfProperties = ovfProperties;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ import java.util.Map;
|
|||
import javax.naming.ConfigurationException;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
import com.cloud.agent.api.storage.OVFPropertyTO;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
|
|
@ -106,6 +108,11 @@ public class OVAProcessor extends AdapterBase implements Processor {
|
|||
try {
|
||||
OVFHelper ovfHelper = new OVFHelper();
|
||||
List<DatadiskTO> disks = ovfHelper.getOVFVolumeInfo(ovfFile);
|
||||
List<OVFPropertyTO> ovfProperties = ovfHelper.getOVFPropertiesFromFile(ovfFile);
|
||||
if (CollectionUtils.isNotEmpty(ovfProperties)) {
|
||||
s_logger.info("Found " + ovfProperties.size() + " configurable OVF properties");
|
||||
info.ovfProperties = ovfProperties;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
s_logger.info("The ovf file " + ovfFile + " is invalid ", e);
|
||||
throw new InternalErrorException("OVA package has bad ovf file " + e.getMessage(), e);
|
||||
|
|
|
|||
|
|
@ -21,7 +21,9 @@ package com.cloud.storage.template;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.agent.api.storage.OVFPropertyTO;
|
||||
import com.cloud.exception.InternalErrorException;
|
||||
import com.cloud.storage.Storage.ImageFormat;
|
||||
import com.cloud.utils.component.Adapter;
|
||||
|
|
@ -52,6 +54,7 @@ public interface Processor extends Adapter {
|
|||
public long virtualSize;
|
||||
public String filename;
|
||||
public boolean isCorrupted;
|
||||
public List<OVFPropertyTO> ovfProperties;
|
||||
}
|
||||
|
||||
long getVirtualSize(File file) throws IOException;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,167 @@
|
|||
// 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.storage;
|
||||
|
||||
import com.cloud.agent.api.storage.OVFProperty;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "template_ovf_properties")
|
||||
public class TemplateOVFPropertyVO implements OVFProperty {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "id")
|
||||
private long id;
|
||||
|
||||
@Column(name = "template_id")
|
||||
private Long templateId;
|
||||
|
||||
@Column(name = "key")
|
||||
private String key;
|
||||
|
||||
@Column(name = "type")
|
||||
private String type;
|
||||
|
||||
@Column(name = "value")
|
||||
private String value;
|
||||
|
||||
@Column(name = "qualifiers")
|
||||
private String qualifiers;
|
||||
|
||||
@Column(name = "password")
|
||||
private Boolean password;
|
||||
|
||||
@Column(name = "user_configurable")
|
||||
private Boolean userConfigurable;
|
||||
|
||||
@Column(name = "label")
|
||||
private String label;
|
||||
|
||||
@Column(name = "description")
|
||||
private String description;
|
||||
|
||||
public TemplateOVFPropertyVO() {
|
||||
}
|
||||
|
||||
public TemplateOVFPropertyVO(Long templateId, String key, String type, String value, String qualifiers,
|
||||
Boolean userConfigurable, String label, String description, Boolean password) {
|
||||
this.templateId = templateId;
|
||||
this.key = key;
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
this.qualifiers = qualifiers;
|
||||
this.userConfigurable = userConfigurable;
|
||||
this.label = label;
|
||||
this.description = description;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getTemplateId() {
|
||||
return templateId;
|
||||
}
|
||||
|
||||
public void setTemplateId(Long templateId) {
|
||||
this.templateId = templateId;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getQualifiers() {
|
||||
return qualifiers;
|
||||
}
|
||||
|
||||
public void setQualifiers(String qualifiers) {
|
||||
this.qualifiers = qualifiers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isUserConfigurable() {
|
||||
return userConfigurable;
|
||||
}
|
||||
|
||||
public void setUserConfigurable(Boolean userConfigurable) {
|
||||
this.userConfigurable = userConfigurable;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Boolean isPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(Boolean password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PROP - templateId=%s> key=%s value=%s type=%s qual=%s conf=%s label=%s desc=%s password=%s",
|
||||
templateId, key, value, type, qualifiers, userConfigurable, label, description, password);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
// 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.storage.dao;
|
||||
|
||||
import com.cloud.storage.TemplateOVFPropertyVO;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface TemplateOVFPropertiesDao extends GenericDao<TemplateOVFPropertyVO, Long> {
|
||||
|
||||
boolean existsOption(long templateId, String key);
|
||||
TemplateOVFPropertyVO findByTemplateAndKey(long templateId, String key);
|
||||
void saveOptions(List<TemplateOVFPropertyVO> opts);
|
||||
List<TemplateOVFPropertyVO> listByTemplateId(long templateId);
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
// 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.storage.dao;
|
||||
|
||||
import com.cloud.storage.TemplateOVFPropertyVO;
|
||||
import com.cloud.utils.db.GenericDaoBase;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
import com.cloud.utils.db.TransactionLegacy;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class TemplateOVFPropertiesDaoImpl extends GenericDaoBase<TemplateOVFPropertyVO, Long> implements TemplateOVFPropertiesDao {
|
||||
|
||||
private final static Logger s_logger = Logger.getLogger(TemplateOVFPropertiesDaoImpl.class);
|
||||
|
||||
SearchBuilder<TemplateOVFPropertyVO> OptionsSearchBuilder;
|
||||
|
||||
public TemplateOVFPropertiesDaoImpl() {
|
||||
super();
|
||||
OptionsSearchBuilder = createSearchBuilder();
|
||||
OptionsSearchBuilder.and("templateid", OptionsSearchBuilder.entity().getTemplateId(), SearchCriteria.Op.EQ);
|
||||
OptionsSearchBuilder.and("key", OptionsSearchBuilder.entity().getKey(), SearchCriteria.Op.EQ);
|
||||
OptionsSearchBuilder.done();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean existsOption(long templateId, String key) {
|
||||
return findByTemplateAndKey(templateId, key) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TemplateOVFPropertyVO findByTemplateAndKey(long templateId, String key) {
|
||||
SearchCriteria<TemplateOVFPropertyVO> sc = OptionsSearchBuilder.create();
|
||||
sc.setParameters("templateid", templateId);
|
||||
sc.setParameters("key", key);
|
||||
return findOneBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveOptions(List<TemplateOVFPropertyVO> opts) {
|
||||
if (CollectionUtils.isEmpty(opts)) {
|
||||
return;
|
||||
}
|
||||
TransactionLegacy txn = TransactionLegacy.currentTxn();
|
||||
txn.start();
|
||||
for (TemplateOVFPropertyVO opt : opts) {
|
||||
persist(opt);
|
||||
}
|
||||
txn.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TemplateOVFPropertyVO> listByTemplateId(long templateId) {
|
||||
SearchCriteria<TemplateOVFPropertyVO> sc = OptionsSearchBuilder.create();
|
||||
sc.setParameters("templateid", templateId);
|
||||
return listBy(sc);
|
||||
}
|
||||
}
|
||||
|
|
@ -287,4 +287,5 @@
|
|||
<bean id="annotationDaoImpl" class="org.apache.cloudstack.annotation.dao.AnnotationDaoImpl" />
|
||||
<bean id="directDownloadCertificateDaoImpl" class="org.apache.cloudstack.direct.download.DirectDownloadCertificateDaoImpl" />
|
||||
<bean id="directDownloadCertificateHostMapDaoImpl" class="org.apache.cloudstack.direct.download.DirectDownloadCertificateHostMapDaoImpl" />
|
||||
<bean id="templateOVFPropertiesDaoImpl" class="com.cloud.storage.dao.TemplateOVFPropertiesDaoImpl" />
|
||||
</beans>
|
||||
|
|
|
|||
|
|
@ -386,3 +386,19 @@ CREATE TABLE `cloud`.`direct_download_certificate_host_map` (
|
|||
CONSTRAINT `fk_direct_download_certificate_host_map__certificate_id` FOREIGN KEY (`certificate_id`) REFERENCES `direct_download_certificate` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- [Vmware] Allow configuring appliances on the VM instance wizard when OVF properties are available
|
||||
CREATE TABLE `cloud`.`template_ovf_properties` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`template_id` bigint(20) unsigned NOT NULL,
|
||||
`key` VARCHAR(100) NOT NULL,
|
||||
`type` VARCHAR(45) DEFAULT NULL,
|
||||
`value` VARCHAR(100) DEFAULT NULL,
|
||||
`password` TINYINT(1) NOT NULL DEFAULT '0',
|
||||
`qualifiers` TEXT DEFAULT NULL,
|
||||
`user_configurable` TINYINT(1) NOT NULL DEFAULT '0',
|
||||
`label` TEXT DEFAULT NULL,
|
||||
`description` TEXT DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
CONSTRAINT `fk_template_ovf_properties__template_id` FOREIGN KEY (`template_id`) REFERENCES `vm_template`(`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,12 @@ import java.util.Map;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.agent.api.storage.OVFPropertyTO;
|
||||
import com.cloud.storage.Upload;
|
||||
import com.cloud.storage.dao.TemplateOVFPropertiesDao;
|
||||
import com.cloud.storage.TemplateOVFPropertyVO;
|
||||
import com.cloud.utils.crypt.DBEncryptionUtil;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
|
||||
|
|
@ -99,6 +104,8 @@ public abstract class BaseImageStoreDriverImpl implements ImageStoreDriver {
|
|||
AccountDao _accountDao;
|
||||
@Inject
|
||||
ResourceLimitService _resourceLimitMgr;
|
||||
@Inject
|
||||
TemplateOVFPropertiesDao templateOvfPropertiesDao;
|
||||
|
||||
protected String _proxy = null;
|
||||
|
||||
|
|
@ -162,6 +169,29 @@ public abstract class BaseImageStoreDriverImpl implements ImageStoreDriver {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist OVF properties as template details for template with id = templateId
|
||||
*/
|
||||
private void persistOVFProperties(List<OVFPropertyTO> ovfProperties, long templateId) {
|
||||
List<TemplateOVFPropertyVO> listToPersist = new ArrayList<>();
|
||||
for (OVFPropertyTO property : ovfProperties) {
|
||||
if (!templateOvfPropertiesDao.existsOption(templateId, property.getKey())) {
|
||||
TemplateOVFPropertyVO option = new TemplateOVFPropertyVO(templateId, property.getKey(), property.getType(),
|
||||
property.getValue(), property.getQualifiers(), property.isUserConfigurable(),
|
||||
property.getLabel(), property.getDescription(), property.isPassword());
|
||||
if (property.isPassword()) {
|
||||
String encryptedPassword = DBEncryptionUtil.encrypt(property.getValue());
|
||||
option.setValue(encryptedPassword);
|
||||
}
|
||||
listToPersist.add(option);
|
||||
}
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(listToPersist)) {
|
||||
s_logger.debug("Persisting " + listToPersist.size() + " OVF properties for template " + templateId);
|
||||
templateOvfPropertiesDao.saveOptions(listToPersist);
|
||||
}
|
||||
}
|
||||
|
||||
protected Void createTemplateAsyncCallback(AsyncCallbackDispatcher<? extends BaseImageStoreDriverImpl, DownloadAnswer> callback,
|
||||
CreateContext<CreateCmdResult> context) {
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
|
|
@ -170,10 +200,14 @@ public abstract class BaseImageStoreDriverImpl implements ImageStoreDriver {
|
|||
DownloadAnswer answer = callback.getResult();
|
||||
DataObject obj = context.data;
|
||||
DataStore store = obj.getDataStore();
|
||||
List<OVFPropertyTO> ovfProperties = answer.getOvfProperties();
|
||||
|
||||
TemplateDataStoreVO tmpltStoreVO = _templateStoreDao.findByStoreTemplate(store.getId(), obj.getId());
|
||||
if (tmpltStoreVO != null) {
|
||||
if (tmpltStoreVO.getDownloadState() == VMTemplateStorageResourceAssoc.Status.DOWNLOADED) {
|
||||
if (CollectionUtils.isNotEmpty(ovfProperties)) {
|
||||
persistOVFProperties(ovfProperties, obj.getId());
|
||||
}
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("Template is already in DOWNLOADED state, ignore further incoming DownloadAnswer");
|
||||
}
|
||||
|
|
@ -213,6 +247,9 @@ public abstract class BaseImageStoreDriverImpl implements ImageStoreDriver {
|
|||
templateDaoBuilder.setChecksum(answer.getCheckSum());
|
||||
_templateDao.update(obj.getId(), templateDaoBuilder);
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(ovfProperties)) {
|
||||
persistOVFProperties(ovfProperties, obj.getId());
|
||||
}
|
||||
|
||||
CreateCmdResult result = new CreateCmdResult(null, null);
|
||||
caller.complete(result);
|
||||
|
|
|
|||
|
|
@ -23,14 +23,22 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.agent.api.MigrateVmToPoolCommand;
|
||||
import com.cloud.agent.api.UnregisterVMCommand;
|
||||
import com.cloud.agent.api.storage.OVFPropertyTO;
|
||||
import com.cloud.agent.api.to.VolumeTO;
|
||||
import com.cloud.dc.ClusterDetailsDao;
|
||||
import com.cloud.storage.StoragePool;
|
||||
import com.cloud.storage.TemplateOVFPropertyVO;
|
||||
import com.cloud.storage.VMTemplateStoragePoolVO;
|
||||
import com.cloud.storage.VMTemplateStorageResourceAssoc;
|
||||
import com.cloud.storage.dao.TemplateOVFPropertiesDao;
|
||||
import com.cloud.storage.dao.VMTemplatePoolDao;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
|
||||
|
|
@ -43,6 +51,7 @@ import org.apache.cloudstack.storage.command.StorageSubSystemCommand;
|
|||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
import org.apache.cloudstack.storage.to.VolumeObjectTO;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
|
|
@ -151,6 +160,10 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co
|
|||
PrimaryDataStoreDao _storagePoolDao;
|
||||
@Inject
|
||||
VolumeDataFactory _volFactory;
|
||||
@Inject
|
||||
private VMTemplatePoolDao templateSpoolDao;
|
||||
@Inject
|
||||
private TemplateOVFPropertiesDao templateOVFPropertiesDao;
|
||||
|
||||
protected VMwareGuru() {
|
||||
super();
|
||||
|
|
@ -354,9 +367,66 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co
|
|||
} else {
|
||||
to.setPlatformEmulator(guestOsMapping.getGuestOsName());
|
||||
}
|
||||
|
||||
List<OVFPropertyTO> ovfProperties = new ArrayList<>();
|
||||
for (String detailKey : details.keySet()) {
|
||||
if (detailKey.startsWith(ApiConstants.OVF_PROPERTIES)) {
|
||||
String ovfPropKey = detailKey.replace(ApiConstants.OVF_PROPERTIES + "-", "");
|
||||
TemplateOVFPropertyVO templateOVFPropertyVO = templateOVFPropertiesDao.findByTemplateAndKey(vm.getTemplateId(), ovfPropKey);
|
||||
if (templateOVFPropertyVO == null) {
|
||||
s_logger.warn(String.format("OVF property %s not found on template, discarding", ovfPropKey));
|
||||
continue;
|
||||
}
|
||||
String ovfValue = details.get(detailKey);
|
||||
boolean isPassword = templateOVFPropertyVO.isPassword();
|
||||
OVFPropertyTO propertyTO = new OVFPropertyTO(ovfPropKey, ovfValue, isPassword);
|
||||
ovfProperties.add(propertyTO);
|
||||
}
|
||||
}
|
||||
|
||||
if (CollectionUtils.isNotEmpty(ovfProperties)) {
|
||||
removeOvfPropertiesFromDetails(ovfProperties, details);
|
||||
String templateInstallPath = null;
|
||||
List<DiskTO> rootDiskList = vm.getDisks().stream().filter(x -> x.getType() == Volume.Type.ROOT).collect(Collectors.toList());
|
||||
if (rootDiskList.size() != 1) {
|
||||
throw new CloudRuntimeException("Did not find only one root disk for VM " + vm.getHostName());
|
||||
}
|
||||
|
||||
DiskTO rootDiskTO = rootDiskList.get(0);
|
||||
DataStoreTO dataStore = rootDiskTO.getData().getDataStore();
|
||||
StoragePoolVO storagePoolVO = _storagePoolDao.findByUuid(dataStore.getUuid());
|
||||
long dataCenterId = storagePoolVO.getDataCenterId();
|
||||
List<StoragePoolVO> pools = _storagePoolDao.listByDataCenterId(dataCenterId);
|
||||
for (StoragePoolVO pool : pools) {
|
||||
VMTemplateStoragePoolVO ref = templateSpoolDao.findByPoolTemplate(pool.getId(), vm.getTemplateId());
|
||||
if (ref != null && ref.getDownloadState() == VMTemplateStorageResourceAssoc.Status.DOWNLOADED) {
|
||||
templateInstallPath = ref.getInstallPath();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (templateInstallPath == null) {
|
||||
throw new CloudRuntimeException("Did not find the template install path for template " +
|
||||
vm.getTemplateId() + " on zone " + dataCenterId);
|
||||
}
|
||||
|
||||
Pair<String, List<OVFPropertyTO>> pair = new Pair<>(templateInstallPath, ovfProperties);
|
||||
to.setOvfProperties(pair);
|
||||
}
|
||||
|
||||
return to;
|
||||
}
|
||||
|
||||
/*
|
||||
Remove OVF properties from details to be sent to hypervisor (avoid duplicate data)
|
||||
*/
|
||||
private void removeOvfPropertiesFromDetails(List<OVFPropertyTO> ovfProperties, Map<String, String> details) {
|
||||
for (OVFPropertyTO propertyTO : ovfProperties) {
|
||||
String key = propertyTO.getKey();
|
||||
details.remove(ApiConstants.OVF_PROPERTIES + "-" + key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide in which cases nested virtualization should be enabled based on (1){@code globalNestedV}, (2){@code globalNestedVPerVM}, (3){@code localNestedV}<br/>
|
||||
* Nested virtualization should be enabled when one of this cases:
|
||||
|
|
|
|||
|
|
@ -44,6 +44,19 @@ import java.util.UUID;
|
|||
import javax.naming.ConfigurationException;
|
||||
import javax.xml.datatype.XMLGregorianCalendar;
|
||||
|
||||
import com.cloud.agent.api.storage.OVFPropertyTO;
|
||||
import com.cloud.utils.crypt.DBEncryptionUtil;
|
||||
import com.vmware.vim25.ArrayUpdateOperation;
|
||||
import com.vmware.vim25.VAppOvfSectionInfo;
|
||||
import com.vmware.vim25.VAppOvfSectionSpec;
|
||||
import com.vmware.vim25.VAppProductInfo;
|
||||
import com.vmware.vim25.VAppProductSpec;
|
||||
import com.vmware.vim25.VAppPropertyInfo;
|
||||
import com.vmware.vim25.VAppPropertySpec;
|
||||
import com.vmware.vim25.VmConfigInfo;
|
||||
import com.vmware.vim25.VmConfigSpec;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.storage.command.CopyCommand;
|
||||
import org.apache.cloudstack.storage.command.StorageSubSystemCommand;
|
||||
|
|
@ -2229,6 +2242,23 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||
// config video card
|
||||
configureVideoCard(vmMo, vmSpec, vmConfigSpec);
|
||||
|
||||
// Set OVF properties (if available)
|
||||
Pair<String, List<OVFPropertyTO>> ovfPropsMap = vmSpec.getOvfProperties();
|
||||
VmConfigInfo templateVappConfig = null;
|
||||
List<OVFPropertyTO> ovfProperties = null;
|
||||
if (ovfPropsMap != null) {
|
||||
String vmTemplate = ovfPropsMap.first();
|
||||
s_logger.info("Find VM template " + vmTemplate);
|
||||
VirtualMachineMO vmTemplateMO = dcMo.findVm(vmTemplate);
|
||||
templateVappConfig = vmTemplateMO.getConfigInfo().getVAppConfig();
|
||||
ovfProperties = ovfPropsMap.second();
|
||||
// Set OVF properties (if available)
|
||||
if (CollectionUtils.isNotEmpty(ovfProperties)) {
|
||||
s_logger.info("Copying OVF properties from template and setting them to the values the user provided");
|
||||
copyVAppConfigsFromTemplate(templateVappConfig, ovfProperties, vmConfigSpec);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Configure VM
|
||||
//
|
||||
|
|
@ -2316,6 +2346,87 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the ovf section spec from existing vApp configuration
|
||||
*/
|
||||
protected List<VAppOvfSectionSpec> copyVAppConfigOvfSectionFromOVF(VmConfigInfo vAppConfig) {
|
||||
List<VAppOvfSectionInfo> ovfSection = vAppConfig.getOvfSection();
|
||||
List<VAppOvfSectionSpec> specs = new ArrayList<>();
|
||||
for (VAppOvfSectionInfo info : ovfSection) {
|
||||
VAppOvfSectionSpec spec = new VAppOvfSectionSpec();
|
||||
spec.setInfo(info);
|
||||
spec.setOperation(ArrayUpdateOperation.ADD);
|
||||
specs.add(spec);
|
||||
}
|
||||
return specs;
|
||||
}
|
||||
|
||||
private Map<String, Pair<String, Boolean>> getOVFMap(List<OVFPropertyTO> props) {
|
||||
Map<String, Pair<String, Boolean>> map = new HashMap<>();
|
||||
for (OVFPropertyTO prop : props) {
|
||||
Pair<String, Boolean> pair = new Pair<>(prop.getValue(), prop.isPassword());
|
||||
map.put(prop.getKey(), pair);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the properties section from existing vApp configuration and values set on ovfProperties
|
||||
*/
|
||||
protected List<VAppPropertySpec> copyVAppConfigPropertySectionFromOVF(VmConfigInfo vAppConfig, List<OVFPropertyTO> ovfProperties) {
|
||||
List<VAppPropertyInfo> productFromOvf = vAppConfig.getProperty();
|
||||
List<VAppPropertySpec> specs = new ArrayList<>();
|
||||
Map<String, Pair<String, Boolean>> ovfMap = getOVFMap(ovfProperties);
|
||||
for (VAppPropertyInfo info : productFromOvf) {
|
||||
VAppPropertySpec spec = new VAppPropertySpec();
|
||||
if (ovfMap.containsKey(info.getId())) {
|
||||
Pair<String, Boolean> pair = ovfMap.get(info.getId());
|
||||
String value = pair.first();
|
||||
boolean isPassword = pair.second();
|
||||
info.setValue(isPassword ? DBEncryptionUtil.decrypt(value) : value);
|
||||
}
|
||||
spec.setInfo(info);
|
||||
spec.setOperation(ArrayUpdateOperation.ADD);
|
||||
specs.add(spec);
|
||||
}
|
||||
return specs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the product section spec from existing vApp configuration
|
||||
*/
|
||||
protected List<VAppProductSpec> copyVAppConfigProductSectionFromOVF(VmConfigInfo vAppConfig) {
|
||||
List<VAppProductInfo> productFromOvf = vAppConfig.getProduct();
|
||||
List<VAppProductSpec> specs = new ArrayList<>();
|
||||
for (VAppProductInfo info : productFromOvf) {
|
||||
VAppProductSpec spec = new VAppProductSpec();
|
||||
spec.setInfo(info);
|
||||
spec.setOperation(ArrayUpdateOperation.ADD);
|
||||
specs.add(spec);
|
||||
}
|
||||
return specs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the vApp configuration to vmConfig spec, copying existing configuration from vAppConfig
|
||||
* and seting properties values from ovfProperties
|
||||
*/
|
||||
protected void copyVAppConfigsFromTemplate(VmConfigInfo vAppConfig,
|
||||
List<OVFPropertyTO> ovfProperties,
|
||||
VirtualMachineConfigSpec vmConfig) throws Exception {
|
||||
VmConfigSpec vmConfigSpec = new VmConfigSpec();
|
||||
vmConfigSpec.getEula().addAll(vAppConfig.getEula());
|
||||
vmConfigSpec.setInstallBootStopDelay(vAppConfig.getInstallBootStopDelay());
|
||||
vmConfigSpec.setInstallBootRequired(vAppConfig.isInstallBootRequired());
|
||||
vmConfigSpec.setIpAssignment(vAppConfig.getIpAssignment());
|
||||
vmConfigSpec.getOvfEnvironmentTransport().addAll(vAppConfig.getOvfEnvironmentTransport());
|
||||
vmConfigSpec.getProduct().addAll(copyVAppConfigProductSectionFromOVF(vAppConfig));
|
||||
vmConfigSpec.getProperty().addAll(copyVAppConfigPropertySectionFromOVF(vAppConfig, ovfProperties));
|
||||
vmConfigSpec.getOvfSection().addAll(copyVAppConfigOvfSectionFromOVF(vAppConfig));
|
||||
vmConfig.setVAppConfig(vmConfigSpec);
|
||||
}
|
||||
|
||||
private String appendFileType(String path, String fileType) {
|
||||
if (path.toLowerCase().endsWith(fileType.toLowerCase())) {
|
||||
return path;
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import java.util.concurrent.ExecutorService;
|
|||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.vmware.vim25.VmConfigInfo;
|
||||
import org.apache.cloudstack.agent.directdownload.DirectDownloadCommand;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
|
@ -517,12 +518,18 @@ public class VmwareStorageProcessor implements StorageProcessor {
|
|||
hyperHost.importVmFromOVF(srcFileName, vmName, datastoreMo, "thin");
|
||||
|
||||
VirtualMachineMO vmMo = hyperHost.findVmOnHyperHost(vmName);
|
||||
VmConfigInfo vAppConfig;
|
||||
if (vmMo == null) {
|
||||
String msg =
|
||||
"Failed to import OVA template. secondaryStorage: " + secondaryStorageUrl + ", templatePathAtSecondaryStorage: " + templatePathAtSecondaryStorage +
|
||||
", templateName: " + templateName + ", templateUuid: " + templateUuid;
|
||||
s_logger.error(msg);
|
||||
throw new Exception(msg);
|
||||
} else {
|
||||
vAppConfig = vmMo.getConfigInfo().getVAppConfig();
|
||||
if (vAppConfig != null) {
|
||||
s_logger.info("Found vApp configuration");
|
||||
}
|
||||
}
|
||||
|
||||
OVAProcessor processor = new OVAProcessor();
|
||||
|
|
@ -536,7 +543,9 @@ public class VmwareStorageProcessor implements StorageProcessor {
|
|||
// the same template may be deployed with multiple copies at per-datastore per-host basis,
|
||||
// save the original template name from CloudStack DB as the UUID to associate them.
|
||||
vmMo.setCustomFieldValue(CustomFieldConstants.CLOUD_UUID, templateName);
|
||||
vmMo.markAsTemplate();
|
||||
if (vAppConfig == null) {
|
||||
vmMo.markAsTemplate();
|
||||
}
|
||||
} else {
|
||||
vmMo.destroy();
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@ import java.util.stream.Stream;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.agent.api.storage.OVFProperty;
|
||||
import com.cloud.storage.TemplateOVFPropertyVO;
|
||||
import com.cloud.storage.dao.TemplateOVFPropertiesDao;
|
||||
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
|
||||
import org.apache.cloudstack.affinity.AffinityGroupDomainMapVO;
|
||||
import org.apache.cloudstack.affinity.AffinityGroupResponse;
|
||||
|
|
@ -70,6 +73,7 @@ import org.apache.cloudstack.api.command.user.project.ListProjectsCmd;
|
|||
import org.apache.cloudstack.api.command.user.resource.ListDetailOptionsCmd;
|
||||
import org.apache.cloudstack.api.command.user.securitygroup.ListSecurityGroupsCmd;
|
||||
import org.apache.cloudstack.api.command.user.tag.ListTagsCmd;
|
||||
import org.apache.cloudstack.api.command.user.template.ListTemplateOVFProperties;
|
||||
import org.apache.cloudstack.api.command.user.template.ListTemplatesCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.ListVMsCmd;
|
||||
import org.apache.cloudstack.api.command.user.vmgroup.ListVMGroupsCmd;
|
||||
|
|
@ -98,6 +102,7 @@ import org.apache.cloudstack.api.response.SecurityGroupResponse;
|
|||
import org.apache.cloudstack.api.response.ServiceOfferingResponse;
|
||||
import org.apache.cloudstack.api.response.StoragePoolResponse;
|
||||
import org.apache.cloudstack.api.response.StorageTagResponse;
|
||||
import org.apache.cloudstack.api.response.TemplateOVFPropertyResponse;
|
||||
import org.apache.cloudstack.api.response.TemplateResponse;
|
||||
import org.apache.cloudstack.api.response.UserResponse;
|
||||
import org.apache.cloudstack.api.response.UserVmResponse;
|
||||
|
|
@ -387,6 +392,9 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
|||
@Inject
|
||||
ManagementServerHostDao managementServerHostDao;
|
||||
|
||||
@Inject
|
||||
TemplateOVFPropertiesDao templateOVFPropertiesDao;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
|
|
@ -3838,6 +3846,29 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
|||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListResponse<TemplateOVFPropertyResponse> listTemplateOVFProperties(ListTemplateOVFProperties cmd) {
|
||||
ListResponse<TemplateOVFPropertyResponse> response = new ListResponse<>();
|
||||
List<TemplateOVFPropertyResponse> result = new ArrayList<>();
|
||||
Long templateId = cmd.getTemplateId();
|
||||
List<TemplateOVFPropertyVO> ovfProperties = templateOVFPropertiesDao.listByTemplateId(templateId);
|
||||
for (OVFProperty property : ovfProperties) {
|
||||
TemplateOVFPropertyResponse propertyResponse = new TemplateOVFPropertyResponse();
|
||||
propertyResponse.setKey(property.getKey());
|
||||
propertyResponse.setType(property.getType());
|
||||
propertyResponse.setValue(property.getValue());
|
||||
propertyResponse.setQualifiers(property.getQualifiers());
|
||||
propertyResponse.setUserConfigurable(property.isUserConfigurable());
|
||||
propertyResponse.setLabel(property.getLabel());
|
||||
propertyResponse.setDescription(property.getDescription());
|
||||
propertyResponse.setPassword(property.isPassword());
|
||||
propertyResponse.setObjectName("ovfproperty");
|
||||
result.add(propertyResponse);
|
||||
}
|
||||
response.setResponses(result);
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConfigComponentName() {
|
||||
return QueryService.class.getSimpleName();
|
||||
|
|
|
|||
|
|
@ -28,7 +28,9 @@ import java.util.stream.Collectors;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.vm.UserVmManager;
|
||||
import org.apache.cloudstack.affinity.AffinityGroupResponse;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiConstants.VMDetails;
|
||||
import org.apache.cloudstack.api.ResponseObject.ResponseView;
|
||||
import org.apache.cloudstack.api.response.NicExtraDhcpOptionResponse;
|
||||
|
|
@ -311,7 +313,10 @@ public class UserVmJoinDaoImpl extends GenericDaoBaseWithTagInformation<UserVmJo
|
|||
if (vmDetails != null) {
|
||||
Map<String, String> resourceDetails = new HashMap<String, String>();
|
||||
for (UserVmDetailVO userVmDetailVO : vmDetails) {
|
||||
resourceDetails.put(userVmDetailVO.getName(), userVmDetailVO.getValue());
|
||||
if (!userVmDetailVO.getName().startsWith(ApiConstants.OVF_PROPERTIES) ||
|
||||
(UserVmManager.DisplayVMOVFProperties.value() && userVmDetailVO.getName().startsWith(ApiConstants.OVF_PROPERTIES))) {
|
||||
resourceDetails.put(userVmDetailVO.getName(), userVmDetailVO.getValue());
|
||||
}
|
||||
}
|
||||
// Remove blacklisted settings if user is not admin
|
||||
if (caller.getType() != Account.ACCOUNT_TYPE_ADMIN) {
|
||||
|
|
|
|||
|
|
@ -1325,18 +1325,18 @@ public class AutoScaleManagerImpl<Type> extends ManagerBase implements AutoScale
|
|||
vm = _userVmService.createBasicSecurityGroupVirtualMachine(zone, serviceOffering, template, null, owner, "autoScaleVm-" + asGroup.getId() + "-" +
|
||||
getCurrentTimeStampString(),
|
||||
"autoScaleVm-" + asGroup.getId() + "-" + getCurrentTimeStampString(), null, null, null, HypervisorType.XenServer, HTTPMethod.GET, null, null, null,
|
||||
null, true, null, null, null, null, null, null);
|
||||
null, true, null, null, null, null, null, null, null);
|
||||
} else {
|
||||
if (zone.isSecurityGroupEnabled()) {
|
||||
vm = _userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, template, null, null,
|
||||
owner, "autoScaleVm-" + asGroup.getId() + "-" + getCurrentTimeStampString(),
|
||||
"autoScaleVm-" + asGroup.getId() + "-" + getCurrentTimeStampString(), null, null, null, HypervisorType.XenServer, HTTPMethod.GET, null, null,
|
||||
null, null, true, null, null, null, null, null, null);
|
||||
null, null, true, null, null, null, null, null, null, null);
|
||||
|
||||
} else {
|
||||
vm = _userVmService.createAdvancedVirtualMachine(zone, serviceOffering, template, null, owner, "autoScaleVm-" + asGroup.getId() + "-" +
|
||||
getCurrentTimeStampString(), "autoScaleVm-" + asGroup.getId() + "-" + getCurrentTimeStampString(),
|
||||
null, null, null, HypervisorType.XenServer, HTTPMethod.GET, null, null, null, addrs, true, null, null, null, null, null, null);
|
||||
null, null, null, HypervisorType.XenServer, HTTPMethod.GET, null, null, null, addrs, true, null, null, null, null, null, null, null);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -446,6 +446,7 @@ import org.apache.cloudstack.api.command.user.template.CreateTemplateCmd;
|
|||
import org.apache.cloudstack.api.command.user.template.DeleteTemplateCmd;
|
||||
import org.apache.cloudstack.api.command.user.template.ExtractTemplateCmd;
|
||||
import org.apache.cloudstack.api.command.user.template.GetUploadParamsForTemplateCmd;
|
||||
import org.apache.cloudstack.api.command.user.template.ListTemplateOVFProperties;
|
||||
import org.apache.cloudstack.api.command.user.template.ListTemplatePermissionsCmd;
|
||||
import org.apache.cloudstack.api.command.user.template.ListTemplatesCmd;
|
||||
import org.apache.cloudstack.api.command.user.template.RegisterTemplateCmd;
|
||||
|
|
@ -3104,6 +3105,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
|
|||
cmdList.add(RevokeTemplateDirectDownloadCertificateCmd.class);
|
||||
cmdList.add(ListMgmtsCmd.class);
|
||||
cmdList.add(GetUploadParamsForIsoCmd.class);
|
||||
cmdList.add(ListTemplateOVFProperties.class);
|
||||
|
||||
// Out-of-band management APIs for admins
|
||||
cmdList.add(EnableOutOfBandManagementForHostCmd.class);
|
||||
|
|
|
|||
|
|
@ -44,12 +44,14 @@ import com.cloud.utils.Pair;
|
|||
*
|
||||
*/
|
||||
public interface UserVmManager extends UserVmService {
|
||||
static final String EnableDynamicallyScaleVmCK = "enable.dynamic.scale.vm";
|
||||
static final String AllowUserExpungeRecoverVmCK ="allow.user.expunge.recover.vm";
|
||||
static final ConfigKey<Boolean> EnableDynamicallyScaleVm = new ConfigKey<Boolean>("Advanced", Boolean.class, EnableDynamicallyScaleVmCK, "false",
|
||||
String EnableDynamicallyScaleVmCK = "enable.dynamic.scale.vm";
|
||||
String AllowUserExpungeRecoverVmCK ="allow.user.expunge.recover.vm";
|
||||
ConfigKey<Boolean> EnableDynamicallyScaleVm = new ConfigKey<Boolean>("Advanced", Boolean.class, EnableDynamicallyScaleVmCK, "false",
|
||||
"Enables/Disables dynamically scaling a vm", true, ConfigKey.Scope.Zone);
|
||||
static final ConfigKey<Boolean> AllowUserExpungeRecoverVm = new ConfigKey<Boolean>("Advanced", Boolean.class, AllowUserExpungeRecoverVmCK, "false",
|
||||
ConfigKey<Boolean> AllowUserExpungeRecoverVm = new ConfigKey<Boolean>("Advanced", Boolean.class, AllowUserExpungeRecoverVmCK, "false",
|
||||
"Determines whether users can expunge or recover their vm", true, ConfigKey.Scope.Account);
|
||||
ConfigKey<Boolean> DisplayVMOVFProperties = new ConfigKey<Boolean>("Advanced", Boolean.class, "vm.display.ovf.properties", "false",
|
||||
"Set display of VMs OVF properties as part of VM details", true);
|
||||
|
||||
static final int MAX_USER_DATA_LENGTH_BYTES = 2048;
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ import java.util.stream.Stream;
|
|||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.storage.TemplateOVFPropertyVO;
|
||||
import com.cloud.storage.dao.TemplateOVFPropertiesDao;
|
||||
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
|
||||
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
||||
import org.apache.cloudstack.affinity.AffinityGroupService;
|
||||
|
|
@ -484,6 +486,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
private DpdkHelper dpdkHelper;
|
||||
@Inject
|
||||
private ResourceTagDao resourceTagDao;
|
||||
@Inject
|
||||
private TemplateOVFPropertiesDao templateOVFPropertiesDao;
|
||||
|
||||
private ScheduledExecutorService _executor = null;
|
||||
private ScheduledExecutorService _vmIpFetchExecutor = null;
|
||||
|
|
@ -531,7 +535,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
private static final ConfigKey<Boolean> VmDestroyForcestop = new ConfigKey<Boolean>("Advanced", Boolean.class, "vm.destroy.forcestop", "false",
|
||||
"On destroy, force-stop takes this value ", true);
|
||||
|
||||
|
||||
@Override
|
||||
public UserVmVO getVirtualMachine(long vmId) {
|
||||
return _vmDao.findById(vmId);
|
||||
|
|
@ -2482,6 +2485,15 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
}
|
||||
}
|
||||
}
|
||||
for (String detailName : details.keySet()) {
|
||||
if (detailName.startsWith(ApiConstants.OVF_PROPERTIES)) {
|
||||
String ovfPropKey = detailName.replace(ApiConstants.OVF_PROPERTIES + "-", "");
|
||||
TemplateOVFPropertyVO ovfPropertyVO = templateOVFPropertiesDao.findByTemplateAndKey(vmInstance.getTemplateId(), ovfPropKey);
|
||||
if (ovfPropertyVO != null && ovfPropertyVO.isPassword()) {
|
||||
details.put(detailName, DBEncryptionUtil.encrypt(details.get(detailName)));
|
||||
}
|
||||
}
|
||||
}
|
||||
vmInstance.setDetails(details);
|
||||
_vmDao.saveDetails(vmInstance);
|
||||
}
|
||||
|
|
@ -3074,7 +3086,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
public UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> securityGroupIdList,
|
||||
Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod,
|
||||
String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard, List<Long> affinityGroupIdList,
|
||||
Map<String, String> customParametes, String customId, Map<String, Map<Integer, String>> dhcpOptionMap, Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException,
|
||||
Map<String, String> customParametes, String customId, Map<String, Map<Integer, String>> dhcpOptionMap,
|
||||
Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap, Map<String, String> userVmOVFProperties) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException,
|
||||
StorageUnavailableException, ResourceAllocationException {
|
||||
|
||||
Account caller = CallContext.current().getCallingAccount();
|
||||
|
|
@ -3122,7 +3135,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
}
|
||||
|
||||
return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, securityGroupIdList, group, httpmethod,
|
||||
userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayVm, keyboard, affinityGroupIdList, customParametes, customId, dhcpOptionMap, dataDiskTemplateToDiskOfferingMap);
|
||||
userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayVm, keyboard, affinityGroupIdList, customParametes, customId, dhcpOptionMap,
|
||||
dataDiskTemplateToDiskOfferingMap, userVmOVFProperties);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -3131,7 +3145,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
public UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> networkIdList,
|
||||
List<Long> securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor,
|
||||
HTTPMethod httpmethod, String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard,
|
||||
List<Long> affinityGroupIdList, Map<String, String> customParameters, String customId, Map<String, Map<Integer, String>> dhcpOptionMap, Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap) throws InsufficientCapacityException, ConcurrentOperationException,
|
||||
List<Long> affinityGroupIdList, Map<String, String> customParameters, String customId, Map<String, Map<Integer, String>> dhcpOptionMap,
|
||||
Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap, Map<String, String> userVmOVFProperties) throws InsufficientCapacityException, ConcurrentOperationException,
|
||||
ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException {
|
||||
|
||||
Account caller = CallContext.current().getCallingAccount();
|
||||
|
|
@ -3233,7 +3248,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
}
|
||||
|
||||
return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, securityGroupIdList, group, httpmethod,
|
||||
userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayVm, keyboard, affinityGroupIdList, customParameters, customId, dhcpOptionMap, dataDiskTemplateToDiskOfferingMap);
|
||||
userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayVm, keyboard, affinityGroupIdList, customParameters, customId, dhcpOptionMap, dataDiskTemplateToDiskOfferingMap,
|
||||
userVmOVFProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -3241,7 +3257,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
public UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> networkIdList, Account owner,
|
||||
String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData,
|
||||
String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, Boolean displayvm, String keyboard, List<Long> affinityGroupIdList,
|
||||
Map<String, String> customParametrs, String customId, Map<String, Map<Integer, String>> dhcpOptionsMap, Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException,
|
||||
Map<String, String> customParametrs, String customId, Map<String, Map<Integer, String>> dhcpOptionsMap, Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap,
|
||||
Map<String, String> userVmOVFPropertiesMap) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException,
|
||||
StorageUnavailableException, ResourceAllocationException {
|
||||
|
||||
Account caller = CallContext.current().getCallingAccount();
|
||||
|
|
@ -3338,7 +3355,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
verifyExtraDhcpOptionsNetwork(dhcpOptionsMap, networkList);
|
||||
|
||||
return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, null, group, httpmethod, userData,
|
||||
sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayvm, keyboard, affinityGroupIdList, customParametrs, customId, dhcpOptionsMap, dataDiskTemplateToDiskOfferingMap);
|
||||
sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayvm, keyboard, affinityGroupIdList, customParametrs, customId, dhcpOptionsMap,
|
||||
dataDiskTemplateToDiskOfferingMap, userVmOVFPropertiesMap);
|
||||
}
|
||||
|
||||
private void verifyExtraDhcpOptionsNetwork(Map<String, Map<Integer, String>> dhcpOptionsMap, List<NetworkVO> networkList) throws InvalidParameterValueException {
|
||||
|
|
@ -3370,7 +3388,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
private UserVm createVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate tmplt, String hostName, String displayName, Account owner,
|
||||
Long diskOfferingId, Long diskSize, List<NetworkVO> networkList, List<Long> securityGroupIdList, String group, HTTPMethod httpmethod, String userData,
|
||||
String sshKeyPair, HypervisorType hypervisor, Account caller, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, Boolean isDisplayVm, String keyboard,
|
||||
List<Long> affinityGroupIdList, Map<String, String> customParameters, String customId, Map<String, Map<Integer, String>> dhcpOptionMap, Map<Long, DiskOffering> datadiskTemplateToDiskOfferringMap) throws InsufficientCapacityException, ResourceUnavailableException,
|
||||
List<Long> affinityGroupIdList, Map<String, String> customParameters, String customId, Map<String, Map<Integer, String>> dhcpOptionMap,
|
||||
Map<Long, DiskOffering> datadiskTemplateToDiskOfferringMap,
|
||||
Map<String, String> userVmOVFPropertiesMap) throws InsufficientCapacityException, ResourceUnavailableException,
|
||||
ConcurrentOperationException, StorageUnavailableException, ResourceAllocationException {
|
||||
|
||||
_accountMgr.checkAccess(caller, null, true, owner);
|
||||
|
|
@ -3744,7 +3764,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
}
|
||||
|
||||
UserVmVO vm = commitUserVm(zone, template, hostName, displayName, owner, diskOfferingId, diskSize, userData, caller, isDisplayVm, keyboard, accountId, userId, offering,
|
||||
isIso, sshPublicKey, networkNicMap, id, instanceName, uuidName, hypervisorType, customParameters, dhcpOptionMap, datadiskTemplateToDiskOfferringMap);
|
||||
isIso, sshPublicKey, networkNicMap, id, instanceName, uuidName, hypervisorType, customParameters, dhcpOptionMap,
|
||||
datadiskTemplateToDiskOfferringMap, userVmOVFPropertiesMap);
|
||||
|
||||
// Assign instance to the group
|
||||
try {
|
||||
|
|
@ -3804,7 +3825,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
private UserVmVO commitUserVm(final DataCenter zone, final VirtualMachineTemplate template, final String hostName, final String displayName, final Account owner,
|
||||
final Long diskOfferingId, final Long diskSize, final String userData, final Account caller, final Boolean isDisplayVm, final String keyboard,
|
||||
final long accountId, final long userId, final ServiceOfferingVO offering, final boolean isIso, final String sshPublicKey, final LinkedHashMap<String, NicProfile> networkNicMap,
|
||||
final long id, final String instanceName, final String uuidName, final HypervisorType hypervisorType, final Map<String, String> customParameters, final Map<String, Map<Integer, String>> extraDhcpOptionMap, final Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap) throws InsufficientCapacityException {
|
||||
final long id, final String instanceName, final String uuidName, final HypervisorType hypervisorType, final Map<String, String> customParameters, final Map<String,
|
||||
Map<Integer, String>> extraDhcpOptionMap, final Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap,
|
||||
Map<String, String> userVmOVFPropertiesMap) throws InsufficientCapacityException {
|
||||
return Transaction.execute(new TransactionCallbackWithException<UserVmVO, InsufficientCapacityException>() {
|
||||
@Override
|
||||
public UserVmVO doInTransaction(TransactionStatus status) throws InsufficientCapacityException {
|
||||
|
|
@ -3890,6 +3913,29 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
}
|
||||
}
|
||||
vm.setDetail(VmDetailConstants.DEPLOY_VM, "true");
|
||||
|
||||
if (MapUtils.isNotEmpty(userVmOVFPropertiesMap)) {
|
||||
for (String key : userVmOVFPropertiesMap.keySet()) {
|
||||
String detailKey = ApiConstants.OVF_PROPERTIES + "-" + key;
|
||||
String value = userVmOVFPropertiesMap.get(key);
|
||||
|
||||
// Sanitize boolean values to expected format and encrypt passwords
|
||||
if (StringUtils.isNotBlank(value)) {
|
||||
if (value.equalsIgnoreCase("True")) {
|
||||
value = "True";
|
||||
} else if (value.equalsIgnoreCase("False")) {
|
||||
value = "False";
|
||||
} else {
|
||||
TemplateOVFPropertyVO ovfPropertyVO = templateOVFPropertiesDao.findByTemplateAndKey(vm.getTemplateId(), key);
|
||||
if (ovfPropertyVO.isPassword()) {
|
||||
value = DBEncryptionUtil.encrypt(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
vm.setDetail(detailKey, value);
|
||||
}
|
||||
}
|
||||
|
||||
_vmDao.saveDetails(vm);
|
||||
|
||||
s_logger.debug("Allocating in the DB for vm");
|
||||
|
|
@ -4230,7 +4276,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
Map<String, String> details = userVmDetailsDao.listDetailsKeyPairs(vm.getId());
|
||||
vm.setDetails(details);
|
||||
|
||||
|
||||
// add userdata info into vm profile
|
||||
Nic defaultNic = _networkModel.getDefaultNic(vm.getId());
|
||||
if(defaultNic != null) {
|
||||
|
|
@ -5005,19 +5050,22 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
Boolean displayVm = cmd.isDisplayVm();
|
||||
String keyboard = cmd.getKeyboard();
|
||||
Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap = cmd.getDataDiskTemplateToDiskOfferingMap();
|
||||
Map<String, String> userVmOVFProperties = cmd.getVmOVFProperties();
|
||||
if (zone.getNetworkType() == NetworkType.Basic) {
|
||||
if (cmd.getNetworkIds() != null) {
|
||||
throw new InvalidParameterValueException("Can't specify network Ids in Basic zone");
|
||||
} else {
|
||||
vm = createBasicSecurityGroupVirtualMachine(zone, serviceOffering, template, getSecurityGroupIdList(cmd), owner, name, displayName, diskOfferingId,
|
||||
size , group , cmd.getHypervisor(), cmd.getHttpMethod(), userData , sshKeyPairName , cmd.getIpToNetworkMap(), addrs, displayVm , keyboard , cmd.getAffinityGroupIdList(),
|
||||
cmd.getDetails(), cmd.getCustomId(), cmd.getDhcpOptionsMap(), dataDiskTemplateToDiskOfferingMap);
|
||||
cmd.getDetails(), cmd.getCustomId(), cmd.getDhcpOptionsMap(),
|
||||
dataDiskTemplateToDiskOfferingMap, userVmOVFProperties);
|
||||
}
|
||||
} else {
|
||||
if (zone.isSecurityGroupEnabled()) {
|
||||
vm = createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, template, cmd.getNetworkIds(), getSecurityGroupIdList(cmd), owner, name,
|
||||
displayName, diskOfferingId, size, group, cmd.getHypervisor(), cmd.getHttpMethod(), userData, sshKeyPairName, cmd.getIpToNetworkMap(), addrs, displayVm, keyboard,
|
||||
cmd.getAffinityGroupIdList(), cmd.getDetails(), cmd.getCustomId(), cmd.getDhcpOptionsMap(), dataDiskTemplateToDiskOfferingMap);
|
||||
cmd.getAffinityGroupIdList(), cmd.getDetails(), cmd.getCustomId(), cmd.getDhcpOptionsMap(),
|
||||
dataDiskTemplateToDiskOfferingMap, userVmOVFProperties);
|
||||
|
||||
} else {
|
||||
if (cmd.getSecurityGroupIdList() != null && !cmd.getSecurityGroupIdList().isEmpty()) {
|
||||
|
|
@ -5025,7 +5073,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
}
|
||||
vm = createAdvancedVirtualMachine(zone, serviceOffering, template, cmd.getNetworkIds(), owner, name, displayName, diskOfferingId, size, group,
|
||||
cmd.getHypervisor(), cmd.getHttpMethod(), userData, sshKeyPairName, cmd.getIpToNetworkMap(), addrs, displayVm, keyboard, cmd.getAffinityGroupIdList(), cmd.getDetails(),
|
||||
cmd.getCustomId(), cmd.getDhcpOptionsMap(), dataDiskTemplateToDiskOfferingMap);
|
||||
cmd.getCustomId(), cmd.getDhcpOptionsMap(), dataDiskTemplateToDiskOfferingMap, userVmOVFProperties);
|
||||
}
|
||||
}
|
||||
// check if this templateId has a child ISO
|
||||
|
|
@ -6688,7 +6736,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
@Override
|
||||
public ConfigKey<?>[] getConfigKeys() {
|
||||
return new ConfigKey<?>[] {EnableDynamicallyScaleVm, AllowUserExpungeRecoverVm, VmIpFetchWaitInterval, VmIpFetchTrialMax, VmIpFetchThreadPoolMax,
|
||||
VmIpFetchTaskWorkers, AllowDeployVmIfGivenHostFails, EnableAdditionalVmConfig};
|
||||
VmIpFetchTaskWorkers, AllowDeployVmIfGivenHostFails, EnableAdditionalVmConfig, DisplayVMOVFProperties};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import java.util.concurrent.Executors;
|
|||
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.agent.api.storage.OVFPropertyTO;
|
||||
import com.cloud.storage.template.Processor;
|
||||
import com.cloud.storage.template.S3TemplateDownloader;
|
||||
import com.cloud.storage.template.TemplateDownloader;
|
||||
|
|
@ -61,6 +62,7 @@ import org.apache.cloudstack.storage.command.DownloadProgressCommand;
|
|||
import org.apache.cloudstack.storage.command.DownloadProgressCommand.RequestType;
|
||||
import org.apache.cloudstack.storage.resource.NfsSecondaryStorageResource;
|
||||
import org.apache.cloudstack.storage.resource.SecondaryStorageResource;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.agent.api.storage.DownloadAnswer;
|
||||
|
|
@ -124,6 +126,7 @@ public class DownloadManagerImpl extends ManagerBase implements DownloadManager
|
|||
private long templatePhysicalSize;
|
||||
private final long id;
|
||||
private final ResourceType resourceType;
|
||||
private List<OVFPropertyTO> ovfProperties;
|
||||
|
||||
public DownloadJob(TemplateDownloader td, String jobId, long id, String tmpltName, ImageFormat format, boolean hvm, Long accountId, String descr, String cksum,
|
||||
String installPathPrefix, ResourceType resourceType) {
|
||||
|
|
@ -218,6 +221,14 @@ public class DownloadManagerImpl extends ManagerBase implements DownloadManager
|
|||
public void setCheckSum(String checksum) {
|
||||
this.checksum = checksum;
|
||||
}
|
||||
|
||||
public List<OVFPropertyTO> getOvfProperties() {
|
||||
return ovfProperties;
|
||||
}
|
||||
|
||||
public void setOvfProperties(List<OVFPropertyTO> ovfProperties) {
|
||||
this.ovfProperties = ovfProperties;
|
||||
}
|
||||
}
|
||||
|
||||
public static final Logger s_logger = Logger.getLogger(DownloadManagerImpl.class);
|
||||
|
|
@ -471,6 +482,9 @@ public class DownloadManagerImpl extends ManagerBase implements DownloadManager
|
|||
}
|
||||
dnld.setTemplatesize(info.virtualSize);
|
||||
dnld.setTemplatePhysicalSize(info.size);
|
||||
if (CollectionUtils.isNotEmpty(info.ovfProperties)) {
|
||||
dnld.setOvfProperties(info.ovfProperties);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -771,6 +785,9 @@ public class DownloadManagerImpl extends ManagerBase implements DownloadManager
|
|||
answer =
|
||||
new DownloadAnswer(jobId, getDownloadPct(jobId), getDownloadError(jobId), getDownloadStatus2(jobId), getDownloadLocalPath(jobId),
|
||||
getInstallPath(jobId), getDownloadTemplateSize(jobId), getDownloadTemplatePhysicalSize(jobId), getDownloadCheckSum(jobId));
|
||||
if (CollectionUtils.isNotEmpty(dj.getOvfProperties())) {
|
||||
answer.setOvfProperties(dj.getOvfProperties());
|
||||
}
|
||||
jobs.remove(jobId);
|
||||
return answer;
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -5819,6 +5819,11 @@ textarea {
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
.multi-wizard .select-container .select .ovf-property {
|
||||
max-width: 352px;
|
||||
padding-left: 21px;
|
||||
}
|
||||
|
||||
.multi-wizard .select-container .select .select-desc .name {
|
||||
margin: 0 0 5px;
|
||||
font-weight: bold;
|
||||
|
|
|
|||
|
|
@ -433,6 +433,15 @@
|
|||
<div class="select-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pre-step 8: Configure OVF properties if available -->
|
||||
<div class="step ovf-properties" wizard-step-id="ovfProperties">
|
||||
<div class="content">
|
||||
<!-- Existing key pairs -->
|
||||
<div class="select-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 8: Review -->
|
||||
<div class="step review" wizard-step-id="review">
|
||||
<div class="main-desc">
|
||||
|
|
|
|||
|
|
@ -1277,6 +1277,7 @@ var dictionary = {
|
|||
"label.outofbandmanagement.username":"Username",
|
||||
"label.override.guest.traffic":"Override Guest-Traffic",
|
||||
"label.override.public.traffic":"Override Public-Traffic",
|
||||
"label.ovf.properties":"OVF Properties",
|
||||
"label.ovm.traffic.label":"OVM traffic label",
|
||||
"label.ovm3.cluster":"Native Clustering",
|
||||
"label.ovm3.pool":"Native Pooling",
|
||||
|
|
@ -2253,6 +2254,7 @@ var dictionary = {
|
|||
"message.outofbandmanagement.disable":"Disable Out-of-band Management",
|
||||
"message.outofbandmanagement.enable":"Enable Out-of-band Management",
|
||||
"message.outofbandmanagement.issue":"Issue Out-of-band Management Power Action",
|
||||
"message.ovf.properties.available":"There are OVF properties available for customizing the selected appliance. Please edit the values accordingly.",
|
||||
"message.password.has.been.reset.to":"Password has been reset to",
|
||||
"message.password.of.the.vm.has.been.reset.to":"Password of the VM has been reset to",
|
||||
"message.pending.projects.1":"You have pending project invitations:",
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
// under the License.
|
||||
|
||||
(function($, cloudStack) {
|
||||
var zoneObjs, hypervisorObjs, featuredTemplateObjs, communityTemplateObjs, myTemplateObjs, sharedTemplateObjs, featuredIsoObjs, communityIsoObjs, myIsoObjs, sharedIsoObjs, serviceOfferingObjs, community, networkObjs;
|
||||
var zoneObjs, hypervisorObjs, featuredTemplateObjs, communityTemplateObjs, myTemplateObjs, sharedTemplateObjs, featuredIsoObjs, communityIsoObjs, myIsoObjs, sharedIsoObjs, serviceOfferingObjs, community, networkObjs, ovfProps;
|
||||
var selectedZoneObj, selectedTemplateObj, selectedHypervisor, selectedDiskOfferingObj;
|
||||
var selectedTemplateOrIso; //'select-template', 'select-iso'
|
||||
var step6ContainerType = 'nothing-to-select'; //'nothing-to-select', 'select-network', 'select-security-group', 'select-advanced-sg'(advanced sg-enabled zone)
|
||||
|
|
@ -533,7 +533,6 @@
|
|||
// if the user is leveraging a template, then we can show custom IOPS, if applicable
|
||||
var canShowCustomIopsForServiceOffering = (args.currentData["select-template"] != "select-iso" ? true : false);
|
||||
|
||||
|
||||
// get serviceOfferingObjs
|
||||
var zoneid = args.currentData["zoneid"];
|
||||
$(window).removeData("cloudStack.module.instanceWizard.serviceOfferingObjs");
|
||||
|
|
@ -960,11 +959,41 @@
|
|||
});
|
||||
}
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
url: createURL("listTemplateOvfProperties&id=" + selectedTemplateObj.id),
|
||||
dataType: "json",
|
||||
async: false,
|
||||
success: function(json) {
|
||||
ovfProps = json.listtemplateovfpropertiesresponse.ovfproperty;
|
||||
}
|
||||
});
|
||||
|
||||
var $step = $('.step.sshkeyPairs:visible');
|
||||
if (ovfProps == null || ovfProps.length === 0) {
|
||||
$step.addClass('next-skip-ovf-properties');
|
||||
} else {
|
||||
$step.removeClass('next-skip-ovf-properties');
|
||||
}
|
||||
},
|
||||
|
||||
// Step PRE-8: Configure OVF Properties (if available) for the template
|
||||
function(args) {
|
||||
args.response.success({
|
||||
data: {
|
||||
ovfProperties: ovfProps
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// Step 8: Review
|
||||
function(args) {
|
||||
return false;
|
||||
var $step = $('.step.review:visible');
|
||||
if (ovfProps == null || ovfProps.length === 0) {
|
||||
$step.addClass('previous-skip-ovf-properties');
|
||||
} else {
|
||||
$step.removeClass('previous-skip-ovf-properties');
|
||||
}
|
||||
}
|
||||
],
|
||||
action: function(args) {
|
||||
|
|
@ -1004,6 +1033,25 @@
|
|||
hypervisor : selectedHypervisor
|
||||
});
|
||||
|
||||
var deployOvfProperties = [];
|
||||
if (ovfProps != null && ovfProps.length > 0) {
|
||||
$(ovfProps).each(function(index, prop) {
|
||||
var selectField = args.$wizard.find('select[id="ovf-property-'+prop.key+'"]');
|
||||
var inputField = args.$wizard.find('input[id="ovf-property-'+prop.key+'"]');
|
||||
var propValue = inputField.val() ? inputField.val() : selectField.val();
|
||||
if (propValue !== undefined) {
|
||||
deployOvfProperties.push({
|
||||
key: prop.key,
|
||||
value: propValue
|
||||
});
|
||||
}
|
||||
});
|
||||
for (var k = 0; k < deployOvfProperties.length; k++) {
|
||||
deployVmData["ovfproperties[" + k + "].key"] = deployOvfProperties[k].key;
|
||||
deployVmData["ovfproperties[" + k + "].value"] = deployOvfProperties[k].value;
|
||||
}
|
||||
}
|
||||
|
||||
if (args.$wizard.find('input[name=rootDiskSize]').parent().css('display') != 'none') {
|
||||
if (args.$wizard.find('input[name=rootDiskSize]').val().length > 0) {
|
||||
$.extend(deployVmData, {
|
||||
|
|
|
|||
|
|
@ -1822,6 +1822,21 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
tabFilter: function (args) {
|
||||
$.ajax({
|
||||
url: createURL("listTemplateOvfProperties&id=" + args.context.templates[0].id),
|
||||
dataType: "json",
|
||||
async: false,
|
||||
success: function(json) {
|
||||
ovfprops = json.listtemplateovfpropertiesresponse.ovfproperty;
|
||||
}
|
||||
});
|
||||
var hiddenTabs = [];
|
||||
if (ovfprops == null || ovfprops.length === 0) {
|
||||
hiddenTabs.push("ovfpropertiestab");
|
||||
}
|
||||
return hiddenTabs;
|
||||
},
|
||||
tabs: {
|
||||
details: {
|
||||
title: 'label.details',
|
||||
|
|
@ -2583,7 +2598,57 @@
|
|||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* OVF properties tab (only displayed when OVF properties are available)
|
||||
*/
|
||||
ovfpropertiestab: {
|
||||
title: 'label.ovf.properties',
|
||||
listView: {
|
||||
id: 'ovfproperties',
|
||||
fields: {
|
||||
label: {
|
||||
label: 'label.label'
|
||||
},
|
||||
description: {
|
||||
label: 'label.description'
|
||||
},
|
||||
value: {
|
||||
label: 'label.value'
|
||||
}
|
||||
},
|
||||
hideSearchBar: true,
|
||||
dataProvider: function(args) {
|
||||
$.ajax({
|
||||
url: createURL("listTemplateOvfProperties"),
|
||||
data: {
|
||||
id: args.context.templates[0].id
|
||||
},
|
||||
success: function(json) {
|
||||
var ovfprops = json.listtemplateovfpropertiesresponse.ovfproperty;
|
||||
var listDetails = [];
|
||||
for (index in ovfprops){
|
||||
var prop = ovfprops[index];
|
||||
var det = {};
|
||||
det['label'] = prop['label'];
|
||||
det['description'] = prop['description'];
|
||||
det['value'] = prop['value'];
|
||||
listDetails.push(det);
|
||||
}
|
||||
args.response.success({
|
||||
data: listDetails
|
||||
});
|
||||
},
|
||||
|
||||
error: function(json) {
|
||||
args.response.error(parseXMLHttpResponse(json));
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,6 +123,98 @@
|
|||
});
|
||||
};
|
||||
|
||||
var makeSelectsOvfProperties = function (data, fields) {
|
||||
var $selects = $('<div>');
|
||||
|
||||
$(data).each(function() {
|
||||
var item = this;
|
||||
var key = item[fields.key];
|
||||
var type = item[fields.type];
|
||||
var value = item[fields.value];
|
||||
var qualifiers = item[fields.qualifiers];
|
||||
var label = item[fields.label];
|
||||
var description = item[fields.description];
|
||||
var password = item[fields.password];
|
||||
|
||||
var propertyField;
|
||||
|
||||
var fieldType = password ? "password" : "text";
|
||||
if (type && type.toUpperCase() == "BOOLEAN") {
|
||||
propertyField = $('<select id=ovf-property-' + key + '>')
|
||||
.append($('<option>').attr({value: "True"}).html("True"))
|
||||
.append($('<option>').attr({value: "False"}).html("False"));
|
||||
} else if (type && (type.includes("int") || type.includes("real"))) {
|
||||
if (qualifiers && qualifiers.includes("MinValue") && qualifiers.includes("MaxValue")) {
|
||||
var split = qualifiers.split(",");
|
||||
var minValue = split[0].replace("MinValue(","").slice(0, -1);
|
||||
var maxValue = split[1].replace("MaxValue(","").slice(0, -1);
|
||||
fieldType = "number";
|
||||
propertyField = $('<input id=ovf-property-'+key+'>')
|
||||
.attr({type: fieldType, min: minValue, max:maxValue})
|
||||
.addClass('name').val(_s(this[fields.value]));
|
||||
} else {
|
||||
propertyField = $('<input id=ovf-property-'+key+'>')
|
||||
.attr({type: fieldType})
|
||||
.addClass('name').val(_s(this[fields.value]))
|
||||
}
|
||||
} else if (type && type.toUpperCase() == "STRING") {
|
||||
if (qualifiers) {
|
||||
propertyField = $('<select id=ovf-property-'+key+'>')
|
||||
if (qualifiers.startsWith("ValueMap")) {
|
||||
var possibleValues = qualifiers.replace("ValueMap","").substr(1).slice(0, -1).split(",");
|
||||
$(possibleValues).each(function() {
|
||||
var qualifier = this.substr(1).slice(0, -1); //remove first and last quotes
|
||||
var option = $('<option>')
|
||||
.attr({
|
||||
value: qualifier,
|
||||
type: fieldType
|
||||
})
|
||||
.html(qualifier)
|
||||
propertyField.append(option);
|
||||
});
|
||||
} else if (qualifiers.startsWith("MaxLen")) {
|
||||
var length = qualifiers.replace("MaxLen(","").slice(0,-1);
|
||||
propertyField = $('<input id=ovf-property-'+key+'>')
|
||||
.attr({maxlength : length, type: fieldType})
|
||||
.addClass('name').val(_s(this[fields.value]))
|
||||
}
|
||||
} else {
|
||||
propertyField = $('<input id=ovf-property-'+key+'>')
|
||||
.attr({type: fieldType})
|
||||
.addClass('name').val(_s(this[fields.value]))
|
||||
}
|
||||
} else {
|
||||
propertyField = $('<input id=ovf-property-'+key+'>')
|
||||
.attr({type: fieldType})
|
||||
.addClass('name').val(_s(this[fields.value]))
|
||||
}
|
||||
|
||||
var $select = $('<div>')
|
||||
.addClass('select')
|
||||
.append(
|
||||
$('<div>')
|
||||
.addClass('select-desc')
|
||||
.addClass('ovf-property')
|
||||
.append($('<div>').addClass('name').html(_s(this[fields.label])))
|
||||
.append(propertyField)
|
||||
.append($('<div>').addClass('desc').html(_s(this[fields.description])))
|
||||
.data('json-obj', this)
|
||||
);
|
||||
$selects.append($select);
|
||||
});
|
||||
|
||||
cloudStack.evenOdd($selects, 'div.select', {
|
||||
even: function($elem) {
|
||||
$elem.addClass('even');
|
||||
},
|
||||
odd: function($elem) {
|
||||
$elem.addClass('odd');
|
||||
}
|
||||
});
|
||||
|
||||
return $selects.children();
|
||||
};
|
||||
|
||||
var makeSelects = function(name, data, fields, options, selectedObj, selectedObjNonEditable) {
|
||||
var $selects = $('<div>');
|
||||
options = options ? options : {};
|
||||
|
|
@ -1233,10 +1325,31 @@
|
|||
};
|
||||
},
|
||||
|
||||
'ovfProperties': function($step, formData) {
|
||||
return {
|
||||
response: {
|
||||
success: function(args) {
|
||||
$step.find('.content .select-container').append(
|
||||
makeSelectsOvfProperties(args.data.ovfProperties, {
|
||||
key: 'key',
|
||||
type: 'type',
|
||||
value: 'value',
|
||||
qualifiers: 'qualifiers',
|
||||
label: 'label',
|
||||
description : 'description',
|
||||
password : 'password'
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
'review': function($step, formData) {
|
||||
$step.find('[wizard-field]').each(function() {
|
||||
var field = $(this).attr('wizard-field');
|
||||
var fieldName;
|
||||
|
||||
var $input = $wizard.find('[wizard-field=' + field + ']').filter(function() {
|
||||
return ($(this).is(':selected') ||
|
||||
$(this).is(':checked') ||
|
||||
|
|
@ -1425,6 +1538,37 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Step 7 - Skip OVF properties tab if there are no OVF properties for the template
|
||||
if ($activeStep.hasClass('sshkeyPairs')) {
|
||||
if ($activeStep.hasClass('next-skip-ovf-properties')) {
|
||||
showStep(8);
|
||||
}
|
||||
}
|
||||
|
||||
// Optional Step - Pre-step 8
|
||||
if ($activeStep.hasClass('ovf-properties')) {
|
||||
var ok = true;
|
||||
if ($activeStep.find('input').length > 0) { //if no checkbox is checked
|
||||
$.each($activeStep.find('input'), function(index, item) {
|
||||
var item = $activeStep.find('input#' + item.id);
|
||||
var internalCheck = true;
|
||||
if (this.maxLength && this.maxLength !== -1) {
|
||||
internalCheck = item.val().length <= this.maxLength;
|
||||
} else if (this.min && this.max) {
|
||||
var numberValue = parseFloat(item.val());
|
||||
internalCheck = numberValue >= this.min && numberValue <= this.max;
|
||||
}
|
||||
ok = ok && internalCheck;
|
||||
});
|
||||
}
|
||||
if (!ok) {
|
||||
cloudStack.dialog.notice({
|
||||
message: 'Please enter valid values for every property'
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$form.valid()) {
|
||||
if ($form.find('input.error:visible, select.error:visible').length) {
|
||||
return false;
|
||||
|
|
@ -1459,6 +1603,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
if ($activeStep.hasClass('review')) {
|
||||
if ($activeStep.hasClass('previous-skip-ovf-properties')) {
|
||||
showStep(7);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -391,7 +391,7 @@ public class ClusterMO extends BaseMO implements VmwareHypervisorHost {
|
|||
|
||||
@Override
|
||||
public boolean createBlankVm(String vmName, String vmInternalCSName, int cpuCount, int cpuSpeedMHz, int cpuReservedMHz, boolean limitCpuUse, int memoryMB,
|
||||
int memoryReserveMB, String guestOsIdentifier, ManagedObjectReference morDs, boolean snapshotDirToParent, Pair<String, String> controllerInfo, Boolean systemVm) throws Exception {
|
||||
int memoryReserveMB, String guestOsIdentifier, ManagedObjectReference morDs, boolean snapshotDirToParent, Pair<String, String> controllerInfo, Boolean systemVm) throws Exception {
|
||||
|
||||
if (s_logger.isTraceEnabled())
|
||||
s_logger.trace("vCenter API trace - createBlankVm(). target MOR: " + _mor.getValue() + ", vmName: " + vmName + ", cpuCount: " + cpuCount + ", cpuSpeedMhz: " +
|
||||
|
|
|
|||
|
|
@ -771,7 +771,7 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost {
|
|||
|
||||
@Override
|
||||
public boolean createBlankVm(String vmName, String vmInternalCSName, int cpuCount, int cpuSpeedMHz, int cpuReservedMHz, boolean limitCpuUse, int memoryMB,
|
||||
int memoryReserveMB, String guestOsIdentifier, ManagedObjectReference morDs, boolean snapshotDirToParent, Pair<String, String> controllerInfo, Boolean systemVm) throws Exception {
|
||||
int memoryReserveMB, String guestOsIdentifier, ManagedObjectReference morDs, boolean snapshotDirToParent, Pair<String, String> controllerInfo, Boolean systemVm) throws Exception {
|
||||
|
||||
if (s_logger.isTraceEnabled())
|
||||
s_logger.trace("vCenter API trace - createBlankVm(). target MOR: " + _mor.getValue() + ", vmName: " + vmName + ", cpuCount: " + cpuCount + ", cpuSpeedMhz: " +
|
||||
|
|
|
|||
|
|
@ -1439,8 +1439,8 @@ public class HypervisorHostHelper {
|
|||
}
|
||||
|
||||
public static boolean createBlankVm(VmwareHypervisorHost host, String vmName, String vmInternalCSName, int cpuCount, int cpuSpeedMHz, int cpuReservedMHz,
|
||||
boolean limitCpuUse, int memoryMB, int memoryReserveMB, String guestOsIdentifier, ManagedObjectReference morDs, boolean snapshotDirToParent,
|
||||
Pair<String, String> controllerInfo, Boolean systemVm) throws Exception {
|
||||
boolean limitCpuUse, int memoryMB, int memoryReserveMB, String guestOsIdentifier, ManagedObjectReference morDs, boolean snapshotDirToParent,
|
||||
Pair<String, String> controllerInfo, Boolean systemVm) throws Exception {
|
||||
|
||||
if (s_logger.isInfoEnabled())
|
||||
s_logger.info("Create blank VM. cpuCount: " + cpuCount + ", cpuSpeed(MHz): " + cpuSpeedMHz + ", mem(Mb): " + memoryMB);
|
||||
|
|
@ -1508,6 +1508,7 @@ public class HypervisorHostHelper {
|
|||
videoDeviceSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD);
|
||||
|
||||
vmConfig.getDeviceChange().add(videoDeviceSpec);
|
||||
|
||||
if (host.createVm(vmConfig)) {
|
||||
// Here, when attempting to find the VM, we need to use the name
|
||||
// with which we created it. This is the only such place where
|
||||
|
|
|
|||
|
|
@ -2963,10 +2963,18 @@ public class VirtualMachineMO extends BaseMO {
|
|||
List<VirtualDevice> devices = (List<VirtualDevice>)_context.getVimClient().
|
||||
getDynamicProperty(_mor, "config.hardware.device");
|
||||
if(devices != null && devices.size() > 0) {
|
||||
long isoDevices = devices.stream()
|
||||
.filter(x -> x instanceof VirtualCdrom && x.getBacking() instanceof VirtualCdromIsoBackingInfo)
|
||||
.count();
|
||||
for(VirtualDevice device : devices) {
|
||||
if(device instanceof VirtualCdrom && device.getBacking() instanceof VirtualCdromIsoBackingInfo &&
|
||||
((VirtualCdromIsoBackingInfo)device.getBacking()).getFileName().equals(filename)) {
|
||||
return device;
|
||||
if(device instanceof VirtualCdrom && device.getBacking() instanceof VirtualCdromIsoBackingInfo) {
|
||||
if (((VirtualCdromIsoBackingInfo)device.getBacking()).getFileName().equals(filename)) {
|
||||
return device;
|
||||
} else if (isoDevices == 1L){
|
||||
s_logger.warn(String.format("VM ISO filename %s differs from the expected filename %s",
|
||||
((VirtualCdromIsoBackingInfo)device.getBacking()).getFileName(), filename));
|
||||
return device;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,8 +58,8 @@ public interface VmwareHypervisorHost {
|
|||
boolean createVm(VirtualMachineConfigSpec vmSpec) throws Exception;
|
||||
|
||||
boolean createBlankVm(String vmName, String vmInternalCSName, int cpuCount, int cpuSpeedMHz, int cpuReservedMHz, boolean limitCpuUse, int memoryMB,
|
||||
int memoryReserveMB, String guestOsIdentifier, ManagedObjectReference morDs, boolean snapshotDirToParent,
|
||||
Pair<String, String> controllerInfo, Boolean systemVm) throws Exception;
|
||||
int memoryReserveMB, String guestOsIdentifier, ManagedObjectReference morDs, boolean snapshotDirToParent,
|
||||
Pair<String, String> controllerInfo, Boolean systemVm) throws Exception;
|
||||
|
||||
void importVmFromOVF(String ovfFilePath, String vmName, DatastoreMO dsMo, String diskOption) throws Exception;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue