mirror of
				https://github.com/apache/cloudstack.git
				synced 2025-10-26 08:42:29 +01:00 
			
		
		
		
	Merge remote-tracking branch 'origin/4.15'
This commit is contained in:
		
						commit
						1abd10199c
					
				| @ -72,7 +72,7 @@ public class RegisterTemplateCmd extends BaseCmd implements UserCmd { | ||||
|     private String format; | ||||
| 
 | ||||
|     @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, required = true, description = "the target hypervisor for the template") | ||||
|     private String hypervisor; | ||||
|     protected String hypervisor; | ||||
| 
 | ||||
|     @Parameter(name = ApiConstants.IS_FEATURED, type = CommandType.BOOLEAN, description = "true if this template is a featured template, false otherwise") | ||||
|     private Boolean featured; | ||||
| @ -162,6 +162,11 @@ public class RegisterTemplateCmd extends BaseCmd implements UserCmd { | ||||
|                 description = "true if template should bypass Secondary Storage and be downloaded to Primary Storage on deployment") | ||||
|     private Boolean directDownload; | ||||
| 
 | ||||
|     @Parameter(name=ApiConstants.DEPLOY_AS_IS, | ||||
|             type = CommandType.BOOLEAN, | ||||
|             description = "(VMware only) true if VM deployments should preserve all the configurations defined for this template", since = "4.15.1") | ||||
|     protected Boolean deployAsIs; | ||||
| 
 | ||||
|     ///////////////////////////////////////////////////// | ||||
|     /////////////////// Accessors /////////////////////// | ||||
|     ///////////////////////////////////////////////////// | ||||
| @ -274,8 +279,9 @@ public class RegisterTemplateCmd extends BaseCmd implements UserCmd { | ||||
|         return directDownload == null ? false : directDownload; | ||||
|     } | ||||
| 
 | ||||
|     public Boolean isDeployAsIs() { | ||||
|         return hypervisor != null && hypervisor.equalsIgnoreCase(Hypervisor.HypervisorType.VMware.toString()); | ||||
|     public boolean isDeployAsIs() { | ||||
|         return Hypervisor.HypervisorType.VMware.toString().equalsIgnoreCase(hypervisor) && | ||||
|                 Boolean.TRUE.equals(deployAsIs); | ||||
|     } | ||||
| 
 | ||||
|     ///////////////////////////////////////////////////// | ||||
| @ -341,7 +347,7 @@ public class RegisterTemplateCmd extends BaseCmd implements UserCmd { | ||||
|                     "Parameter directdownload is only allowed for KVM templates"); | ||||
|         } | ||||
| 
 | ||||
|         if (!getHypervisor().equalsIgnoreCase(Hypervisor.HypervisorType.VMware.toString()) && osTypeId == null) { | ||||
|         if (!isDeployAsIs() && osTypeId == null) { | ||||
|             throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Please provide a guest OS type"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -20,6 +20,7 @@ | ||||
| package org.apache.cloudstack.api.command.user.template; | ||||
| 
 | ||||
| import com.cloud.exception.ResourceAllocationException; | ||||
| import com.cloud.hypervisor.Hypervisor; | ||||
| import com.cloud.template.TemplateApiService; | ||||
| import org.apache.cloudstack.api.ApiErrorCode; | ||||
| import org.apache.cloudstack.api.ServerApiException; | ||||
| @ -32,7 +33,7 @@ import org.mockito.runners.MockitoJUnitRunner; | ||||
| import java.util.ArrayList; | ||||
| 
 | ||||
| @RunWith(MockitoJUnitRunner.class) | ||||
| public class RegisterTemplateCmdTest{ | ||||
| public class RegisterTemplateCmdTest { | ||||
| 
 | ||||
|     @InjectMocks | ||||
|     private RegisterTemplateCmd registerTemplateCmd; | ||||
| @ -108,4 +109,34 @@ public class RegisterTemplateCmdTest{ | ||||
|             registerTemplateCmd.zoneId = 1L; | ||||
|             Assert.assertEquals((Long)1L,registerTemplateCmd.getZoneIds().get(0)); | ||||
|     } | ||||
| 
 | ||||
|     private void testIsDeployAsIsBase(Hypervisor.HypervisorType hypervisorType, Boolean deployAsIsParameter, boolean expectedResult) { | ||||
|         registerTemplateCmd = new RegisterTemplateCmd(); | ||||
|         registerTemplateCmd.hypervisor = hypervisorType.name(); | ||||
|         registerTemplateCmd.deployAsIs = deployAsIsParameter; | ||||
|         boolean isDeployAsIs = registerTemplateCmd.isDeployAsIs(); | ||||
|         Assert.assertEquals(expectedResult, isDeployAsIs); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testIsDeployAsIsVmwareNullAsIs() { | ||||
|         testIsDeployAsIsBase(Hypervisor.HypervisorType.VMware, null, false); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testIsDeployAsIsVmwareNotAsIs() { | ||||
|         testIsDeployAsIsBase(Hypervisor.HypervisorType.VMware, false, false); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testIsDeployAsIsVmwareAsIs() { | ||||
|         testIsDeployAsIsBase(Hypervisor.HypervisorType.VMware, true, true); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testIsDeployAsIsNonVmware() { | ||||
|         testIsDeployAsIsBase(Hypervisor.HypervisorType.KVM, true, false); | ||||
|         testIsDeployAsIsBase(Hypervisor.HypervisorType.XenServer, true, false); | ||||
|         testIsDeployAsIsBase(Hypervisor.HypervisorType.Any, true, false); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -262,5 +262,11 @@ public interface VirtualMachineManager extends Manager { | ||||
| 
 | ||||
|     UserVm restoreVirtualMachine(long vmId, Long newTemplateId) throws ResourceUnavailableException, InsufficientCapacityException; | ||||
| 
 | ||||
|     /** | ||||
|      * Returns true if the VM's Root volume is allocated at a local storage pool | ||||
|      */ | ||||
|     boolean isRootVolumeOnLocalStorage(long vmId); | ||||
| 
 | ||||
|     Pair<Long, Long> findClusterAndHostIdForVm(long vmId); | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -3828,22 +3828,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac | ||||
| 
 | ||||
|         final ServiceOfferingVO currentServiceOffering = _offeringDao.findByIdIncludingRemoved(vmInstance.getId(), vmInstance.getServiceOfferingId()); | ||||
| 
 | ||||
|         // Check that the service offering being upgraded to has the same Guest IP type as the VM's current service offering | ||||
|         // NOTE: With the new network refactoring in 2.2, we shouldn't need the check for same guest IP type anymore. | ||||
|         /* | ||||
|          * if (!currentServiceOffering.getGuestIpType().equals(newServiceOffering.getGuestIpType())) { String errorMsg = | ||||
|          * "The service offering being upgraded to has a guest IP type: " + newServiceOffering.getGuestIpType(); errorMsg += | ||||
|          * ". Please select a service offering with the same guest IP type as the VM's current service offering (" + | ||||
|          * currentServiceOffering.getGuestIpType() + ")."; throw new InvalidParameterValueException(errorMsg); } | ||||
|          */ | ||||
| 
 | ||||
|         // Check that the service offering being upgraded to has the same storage pool preference as the VM's current service | ||||
|         // offering | ||||
|         if (currentServiceOffering.isUseLocalStorage() != newServiceOffering.isUseLocalStorage()) { | ||||
|             throw new InvalidParameterValueException("Unable to upgrade virtual machine " + vmInstance.toString() + | ||||
|                     ", cannot switch between local storage and shared storage service offerings.  Current offering " + "useLocalStorage=" + | ||||
|                     currentServiceOffering.isUseLocalStorage() + ", target offering useLocalStorage=" + newServiceOffering.isUseLocalStorage()); | ||||
|         } | ||||
|         checkIfNewOfferingStorageScopeMatchesStoragePool(vmInstance, newServiceOffering); | ||||
| 
 | ||||
|         // if vm is a system vm, check if it is a system service offering, if yes return with error as it cannot be used for user vms | ||||
|         if (currentServiceOffering.isSystemUse() != newServiceOffering.isSystemUse()) { | ||||
| @ -3865,6 +3850,39 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Throws an InvalidParameterValueException in case the new service offerings does not match the storage scope (e.g. local or shared). | ||||
|      */ | ||||
|     protected void checkIfNewOfferingStorageScopeMatchesStoragePool(VirtualMachine vmInstance, ServiceOffering newServiceOffering) { | ||||
|         boolean isRootVolumeOnLocalStorage = isRootVolumeOnLocalStorage(vmInstance.getId()); | ||||
| 
 | ||||
|         if (newServiceOffering.isUseLocalStorage() && !isRootVolumeOnLocalStorage) { | ||||
|             String message = String .format("Unable to upgrade virtual machine %s, target offering use local storage but the storage pool where " | ||||
|                     + "the volume is allocated is a shared storage.", vmInstance.toString()); | ||||
|             throw new InvalidParameterValueException(message); | ||||
|         } | ||||
| 
 | ||||
|         if (!newServiceOffering.isUseLocalStorage() && isRootVolumeOnLocalStorage) { | ||||
|             String message = String.format("Unable to upgrade virtual machine %s, target offering use shared storage but the storage pool where " | ||||
|                     + "the volume is allocated is a local storage.", vmInstance.toString()); | ||||
|             throw new InvalidParameterValueException(message); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public boolean isRootVolumeOnLocalStorage(long vmId) { | ||||
|         ScopeType poolScope = ScopeType.ZONE; | ||||
|         List<VolumeVO> volumes = _volsDao.findByInstanceAndType(vmId, Type.ROOT); | ||||
|         if(CollectionUtils.isNotEmpty(volumes)) { | ||||
|             VolumeVO rootDisk = volumes.get(0); | ||||
|             Long poolId = rootDisk.getPoolId(); | ||||
|             if (poolId != null) { | ||||
|                 StoragePoolVO storagePoolVO = _storagePoolDao.findById(poolId); | ||||
|                 poolScope = storagePoolVO.getScope(); | ||||
|             } | ||||
|         } | ||||
|         return ScopeType.HOST == poolScope; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean upgradeVmDb(final long vmId, final ServiceOffering newServiceOffering, ServiceOffering currentServiceOffering) { | ||||
| 
 | ||||
|  | ||||
| @ -31,6 +31,7 @@ import java.util.HashMap; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| 
 | ||||
| import com.cloud.exception.InvalidParameterValueException; | ||||
| import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator; | ||||
| import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; | ||||
| import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; | ||||
| @ -624,4 +625,59 @@ public class VirtualMachineManagerImplTest { | ||||
|         assertTrue(VirtualMachineManagerImpl.matches(tags,three)); | ||||
|         assertTrue(VirtualMachineManagerImpl.matches(others,three)); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void isRootVolumeOnLocalStorageTestOnLocal() { | ||||
|         prepareAndTestIsRootVolumeOnLocalStorage(ScopeType.HOST, true); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void isRootVolumeOnLocalStorageTestCluster() { | ||||
|         prepareAndTestIsRootVolumeOnLocalStorage(ScopeType.CLUSTER, false); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void isRootVolumeOnLocalStorageTestZone() { | ||||
|         prepareAndTestIsRootVolumeOnLocalStorage(ScopeType.ZONE, false); | ||||
|     } | ||||
| 
 | ||||
|     private void prepareAndTestIsRootVolumeOnLocalStorage(ScopeType scope, boolean expected) { | ||||
|         StoragePoolVO storagePoolVoMock = Mockito.mock(StoragePoolVO.class); | ||||
|         Mockito.doReturn(storagePoolVoMock).when(storagePoolDaoMock).findById(Mockito.anyLong()); | ||||
|         Mockito.doReturn(scope).when(storagePoolVoMock).getScope(); | ||||
|         List<VolumeVO> mockedVolumes = new ArrayList<>(); | ||||
|         mockedVolumes.add(volumeVoMock); | ||||
|         Mockito.doReturn(mockedVolumes).when(volumeDaoMock).findByInstanceAndType(Mockito.anyLong(), Mockito.any()); | ||||
| 
 | ||||
|         boolean result = virtualMachineManagerImpl.isRootVolumeOnLocalStorage(0l); | ||||
| 
 | ||||
|         Assert.assertEquals(expected, result); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void checkIfNewOfferingStorageScopeMatchesStoragePoolTestLocalLocal() { | ||||
|         prepareAndRunCheckIfNewOfferingStorageScopeMatchesStoragePool(true, true); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void checkIfNewOfferingStorageScopeMatchesStoragePoolTestSharedShared() { | ||||
|         prepareAndRunCheckIfNewOfferingStorageScopeMatchesStoragePool(false, false); | ||||
|     } | ||||
| 
 | ||||
|     @Test (expected = InvalidParameterValueException.class) | ||||
|     public void checkIfNewOfferingStorageScopeMatchesStoragePoolTestLocalShared() { | ||||
|         prepareAndRunCheckIfNewOfferingStorageScopeMatchesStoragePool(true, false); | ||||
|     } | ||||
| 
 | ||||
|     @Test (expected = InvalidParameterValueException.class) | ||||
|     public void checkIfNewOfferingStorageScopeMatchesStoragePoolTestSharedLocal() { | ||||
|         prepareAndRunCheckIfNewOfferingStorageScopeMatchesStoragePool(false, true); | ||||
|     } | ||||
| 
 | ||||
|     private void prepareAndRunCheckIfNewOfferingStorageScopeMatchesStoragePool(boolean isRootOnLocal, boolean isOfferingUsingLocal) { | ||||
|         Mockito.doReturn(isRootOnLocal).when(virtualMachineManagerImpl).isRootVolumeOnLocalStorage(Mockito.anyLong()); | ||||
|         Mockito.doReturn("vmInstanceMockedToString").when(vmInstanceMock).toString(); | ||||
|         Mockito.doReturn(isOfferingUsingLocal).when(serviceOfferingMock).isUseLocalStorage(); | ||||
|         virtualMachineManagerImpl.checkIfNewOfferingStorageScopeMatchesStoragePool(vmInstanceMock, serviceOfferingMock); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -200,7 +200,7 @@ public class VMInstanceVO implements VirtualMachine, FiniteStateObject<State, Vi | ||||
|     @Column(name = "backup_external_id") | ||||
|     protected String backupExternalId; | ||||
| 
 | ||||
|     @Column(name = "backup_volumes") | ||||
|     @Column(name = "backup_volumes", length = 65535) | ||||
|     protected String backupVolumes; | ||||
| 
 | ||||
|     public VMInstanceVO(long id, long serviceOfferingId, String name, String instanceName, Type type, Long vmTemplateId, HypervisorType hypervisorType, long guestOSId, | ||||
|  | ||||
| @ -80,6 +80,7 @@ Requires: iptables-services | ||||
| Requires: qemu-img | ||||
| Requires: python3-pip | ||||
| Requires: python3-setuptools | ||||
| Requires: libgcrypt > 1.8.3 | ||||
| Group:     System Environment/Libraries | ||||
| %description management | ||||
| The CloudStack management server is the central point of coordination, | ||||
| @ -109,6 +110,7 @@ Requires: perl | ||||
| Requires: python3-libvirt | ||||
| Requires: qemu-img | ||||
| Requires: qemu-kvm | ||||
| Requires: libgcrypt > 1.8.3 | ||||
| Provides: cloud-agent | ||||
| Group: System Environment/Libraries | ||||
| %description agent | ||||
|  | ||||
| @ -32,6 +32,7 @@ import java.util.stream.Stream; | ||||
| import javax.inject.Inject; | ||||
| 
 | ||||
| import com.cloud.storage.dao.VMTemplateDetailsDao; | ||||
| import com.cloud.vm.VirtualMachineManager; | ||||
| import org.apache.cloudstack.acl.ControlledEntity.ACLType; | ||||
| import org.apache.cloudstack.affinity.AffinityGroupDomainMapVO; | ||||
| import org.apache.cloudstack.affinity.AffinityGroupResponse; | ||||
| @ -424,6 +425,9 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q | ||||
|     @Inject | ||||
|     private UserDao userDao; | ||||
| 
 | ||||
|     @Inject | ||||
|     private VirtualMachineManager virtualMachineManager; | ||||
| 
 | ||||
|     /* | ||||
|      * (non-Javadoc) | ||||
|      * | ||||
| @ -2962,8 +2966,10 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q | ||||
|                 sc.addAnd("id", SearchCriteria.Op.NEQ, currentVmOffering.getId()); | ||||
|             } | ||||
| 
 | ||||
|             // 1. Only return offerings with the same storage type | ||||
|             sc.addAnd("useLocalStorage", SearchCriteria.Op.EQ, currentVmOffering.isUseLocalStorage()); | ||||
|             boolean isRootVolumeUsingLocalStorage = virtualMachineManager.isRootVolumeOnLocalStorage(vmId); | ||||
| 
 | ||||
|             // 1. Only return offerings with the same storage type than the storage pool where the VM's root volume is allocated | ||||
|             sc.addAnd("useLocalStorage", SearchCriteria.Op.EQ, isRootVolumeUsingLocalStorage); | ||||
| 
 | ||||
|             // 2.In case vm is running return only offerings greater than equal to current offering compute. | ||||
|             if (vmInstance.getState() == VirtualMachine.State.Running) { | ||||
|  | ||||
| @ -291,7 +291,7 @@ public abstract class TemplateAdapterBase extends AdapterBase implements Templat | ||||
|         } | ||||
| 
 | ||||
|         Map details = cmd.getDetails(); | ||||
|         if (hypervisorType == HypervisorType.VMware) { | ||||
|         if (cmd.isDeployAsIs()) { | ||||
|             if (MapUtils.isNotEmpty(details)) { | ||||
|                 if (details.containsKey(VmDetailConstants.ROOT_DISK_CONTROLLER)) { | ||||
|                     s_logger.info("Ignoring the rootDiskController detail provided, as we honour what is defined in the template"); | ||||
|  | ||||
| @ -260,8 +260,8 @@ class TestKubernetesCluster(cloudstackTestCase): | ||||
| 
 | ||||
|         if validateList(templates)[0] != PASS: | ||||
|             details = None | ||||
|             if hypervisor in ["vmware"]: | ||||
|                 details = [{"keyboard": "us"}] | ||||
|             if hypervisor in ["vmware"] and "details" in cks_template: | ||||
|                 details = cks_template["details"] | ||||
|             template = Template.register(cls.apiclient, cks_template, zoneid=cls.zone.id, hypervisor=hypervisor.lower(), randomize_name=False, details=details) | ||||
|             template.download(cls.apiclient) | ||||
|             return template, False | ||||
|  | ||||
| @ -59,6 +59,7 @@ class TestVMWareStoragePolicies(cloudstackTestCase): | ||||
|             cls.apiclient, | ||||
|             cls.zone.id, | ||||
|             cls.hypervisor, | ||||
|             deploy_as_is=cls.hypervisor.lower() == "vmware" | ||||
|         ) | ||||
|         cls._cleanup.append(cls.network_offering) | ||||
|         return | ||||
|  | ||||
| @ -84,6 +84,7 @@ class TestCreateTemplateWithChecksum(cloudstackTestCase): | ||||
|             self.test_template.displaytext = 'test sha-1' | ||||
|             self.test_template.url = "http://dl.openvm.eu/cloudstack/macchinina/x86_64/macchinina-vmware.ova" | ||||
|             self.test_template.format = "OVA" | ||||
|             self.test_template.ostypeid = self.getOsType("Other Linux (64-bit)") | ||||
|             self.md5 = "27f3c56a8c7ec7b2f3ff2199f7078006" | ||||
|             self.sha256 = "a7b04c1eb507f3f5de844bda352df1ea5e20335b465409493ca6ae07dfd0a158" | ||||
| 
 | ||||
|  | ||||
| @ -308,7 +308,8 @@ class TestVolumes(cloudstackTestCase): | ||||
|             cls.apiclient, | ||||
|             cls.zone.id, | ||||
|             cls.services["ostype"], | ||||
|             cls.hypervisor | ||||
|             cls.hypervisor, | ||||
|             deploy_as_is=cls.hypervisor.lower() == "vmware" | ||||
|         ) | ||||
|         if cls.template == FAILED: | ||||
|             assert False, "get_suitable_test_template() failed to return template with description %s" % cls.services["ostype"] | ||||
|  | ||||
| @ -1405,8 +1405,6 @@ class Template: | ||||
|         elif "hypervisor" in services: | ||||
|             cmd.hypervisor = services["hypervisor"] | ||||
| 
 | ||||
|         if cmd.hypervisor.lower() not in ["vmware"]: | ||||
|             # Since version 4.15 VMware templates honour the guest OS defined in the template | ||||
|         if "ostypeid" in services: | ||||
|             cmd.ostypeid = services["ostypeid"] | ||||
|         elif "ostype" in services: | ||||
| @ -1440,6 +1438,7 @@ class Template: | ||||
|         cmd.isdynamicallyscalable = services["isdynamicallyscalable"] if "isdynamicallyscalable" in services else False | ||||
|         cmd.passwordenabled = services[ | ||||
|             "passwordenabled"] if "passwordenabled" in services else False | ||||
|         cmd.deployasis = services["deployasis"] if "deployasis" in services else False | ||||
| 
 | ||||
|         if account: | ||||
|             cmd.account = account | ||||
|  | ||||
| @ -348,7 +348,7 @@ def get_template( | ||||
|     return list_templatesout[0] | ||||
| 
 | ||||
| 
 | ||||
| def get_test_template(apiclient, zone_id=None, hypervisor=None, test_templates=None): | ||||
| def get_test_template(apiclient, zone_id=None, hypervisor=None, test_templates=None, deploy_as_is=False): | ||||
|     """ | ||||
|     @Name : get_test_template | ||||
|     @Desc : Retrieves the test template used to running tests. When the template | ||||
| @ -373,6 +373,8 @@ def get_test_template(apiclient, zone_id=None, hypervisor=None, test_templates=N | ||||
|         return FAILED | ||||
| 
 | ||||
|     test_template = test_templates[hypervisor] | ||||
|     if deploy_as_is: | ||||
|         test_template['deployasis'] = True | ||||
| 
 | ||||
|     cmd = listTemplates.listTemplatesCmd() | ||||
|     cmd.name = test_template['name'] | ||||
| @ -513,7 +515,7 @@ def get_windows_template( | ||||
| 
 | ||||
|     return FAILED | ||||
| 
 | ||||
| def get_suitable_test_template(apiclient, zoneid, ostypeid, hypervisor): | ||||
| def get_suitable_test_template(apiclient, zoneid, ostypeid, hypervisor, deploy_as_is=False): | ||||
|     ''' | ||||
|     @Name : get_suitable_test_template | ||||
|     @Desc : Retrieves the test template information based upon inputs provided | ||||
| @ -525,11 +527,12 @@ def get_suitable_test_template(apiclient, zoneid, ostypeid, hypervisor): | ||||
|               template Information matching the inputs | ||||
|     ''' | ||||
|     template = FAILED | ||||
|     if hypervisor.lower() in ["xenserver"]: | ||||
|     if hypervisor.lower() in ["xenserver"] or (hypervisor.lower() in ["vmware"] and deploy_as_is): | ||||
|         template = get_test_template( | ||||
|             apiclient, | ||||
|             zoneid, | ||||
|             hypervisor) | ||||
|             hypervisor, | ||||
|             deploy_as_is=deploy_as_is) | ||||
|     if template == FAILED: | ||||
|         template = get_template( | ||||
|             apiclient, | ||||
|  | ||||
| @ -718,7 +718,7 @@ | ||||
| "label.demote.project.owner": "Demote account to Regular role", | ||||
| "label.demote.project.owner.user": "Demote user to Regular role", | ||||
| "label.deny": "Deny", | ||||
| "label.deployasis":"Deploy As-Is", | ||||
| "label.deployasis":"Read VM settings from OVA", | ||||
| "label.deploymentplanner": "Deployment planner", | ||||
| "label.description": "Description", | ||||
| "label.destcidr": "Destination CIDR", | ||||
|  | ||||
| @ -23,7 +23,7 @@ | ||||
|     <a-form-item class="pair-select-container" :label="selectLabel" v-if="this.checked"> | ||||
|       <a-select | ||||
|         v-decorator="[selectDecorator, { | ||||
|           initialValue: this.getSelectInitialValue() | ||||
|           initialValue: selectedOption ? selectedOption : this.getSelectInitialValue() | ||||
|         }]" | ||||
|         showSearch | ||||
|         optionFilterProp="children" | ||||
| @ -80,23 +80,16 @@ export default { | ||||
|       return array !== null && array !== undefined && Array.isArray(array) && array.length > 0 | ||||
|     }, | ||||
|     getSelectInitialValue () { | ||||
|       if (this.arrayHasItems(this.selectOptions)) { | ||||
|         for (var i = 0; i < this.selectOptions.length; i++) { | ||||
|           if (this.selectOptions[i].enabled !== false) { | ||||
|             return this.selectOptions[i].name | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|       return '' | ||||
|       const provider = this.selectOptions?.filter(x => x.enabled)?.[0]?.name || '' | ||||
|       this.handleSelectChange(provider) | ||||
|       return provider | ||||
|     }, | ||||
|     handleCheckChange (e) { | ||||
|       this.checked = e.target.checked | ||||
|       if (this.checked && this.arrayHasItems(this.selectOptions)) { | ||||
|         this.selectedOption = this.selectOptions[0].name | ||||
|       } | ||||
|       this.$emit('handle-checkpair-change', this.resourceKey, this.checked, '') | ||||
|     }, | ||||
|     handleSelectChange (val) { | ||||
|       this.selectedOption = val | ||||
|       this.$emit('handle-checkpair-change', this.resourceKey, this.checked, val) | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @ -96,7 +96,7 @@ export default { | ||||
|       filter: '' | ||||
|     } | ||||
|   }, | ||||
|   beforeMount () { | ||||
|   created () { | ||||
|     switch (this.$route.meta.name) { | ||||
|       case 'account': | ||||
|         this.scopeKey = 'accountid' | ||||
| @ -119,8 +119,6 @@ export default { | ||||
|       default: | ||||
|         this.scopeKey = '' | ||||
|     } | ||||
|   }, | ||||
|   created () { | ||||
|     this.fetchData() | ||||
|   }, | ||||
|   watch: { | ||||
|  | ||||
| @ -317,7 +317,7 @@ export default { | ||||
|           label: 'label.action.reset.password', | ||||
|           message: 'message.action.instance.reset.password', | ||||
|           dataView: true, | ||||
|           show: (record) => { return ['Running', 'Stopped'].includes(record.state) && record.passwordenabled }, | ||||
|           show: (record) => { return ['Stopped'].includes(record.state) && record.passwordenabled }, | ||||
|           response: (result) => { return result.virtualmachine && result.virtualmachine.password ? `The password of VM <b>${result.virtualmachine.displayname}</b> is <b>${result.virtualmachine.password}</b>` : null } | ||||
|         }, | ||||
|         { | ||||
|  | ||||
| @ -249,6 +249,7 @@ export default { | ||||
|           } | ||||
|         } | ||||
| 
 | ||||
|         const resourceName = params.displayname || params.displaytext || params.name || this.resource.name | ||||
|         let hasJobId = false | ||||
|         api(this.action.api, params).then(json => { | ||||
|           for (const obj in json) { | ||||
| @ -270,6 +271,18 @@ export default { | ||||
|             } | ||||
|           } | ||||
|           if (!hasJobId) { | ||||
|             var message = this.action.successMessage ? this.$t(this.action.successMessage) : this.$t(this.action.label) + | ||||
|               (resourceName ? ' - ' + resourceName : '') | ||||
|             var duration = 2 | ||||
|             if (this.action.additionalMessage) { | ||||
|               message = message + ' - ' + this.$t(this.action.successMessage) | ||||
|               duration = 5 | ||||
|             } | ||||
|             this.$message.success({ | ||||
|               content: message, | ||||
|               key: this.action.label + resourceName, | ||||
|               duration: duration | ||||
|             }) | ||||
|             this.parentUpdActionData(json) | ||||
|             this.parentFetchData() | ||||
|           } | ||||
|  | ||||
| @ -208,8 +208,18 @@ | ||||
|               :default-checked="xenServerProvider" /> | ||||
|           </a-form-item> | ||||
|         </a-row> | ||||
| 
 | ||||
|         <a-form-item :label="$t('label.deployasis')" v-if="hyperVMWShow"> | ||||
|           <a-switch | ||||
|             v-decorator="['deployasis', { | ||||
|               initialValue: false, | ||||
|             }]" | ||||
|             :checked="deployasis" | ||||
|             @change="val => deployasis = val"/> | ||||
|         </a-form-item> | ||||
| 
 | ||||
|         <a-row :gutter="12" v-if="hyperKVMShow || hyperVMWShow"> | ||||
|           <a-col :md="24" :lg="24" v-if="hyperKVMShow"> | ||||
|           <a-col :md="24" :lg="24" v-if="hyperKVMShow || (hyperVMWShow && !deployasis)"> | ||||
|             <a-form-item :label="$t('label.rootdiskcontrollertype')"> | ||||
|               <a-select | ||||
|                 v-decorator="['rootDiskControllerType', { | ||||
| @ -230,7 +240,7 @@ | ||||
|             </a-form-item> | ||||
|           </a-col> | ||||
|           <a-col :md="24" :lg="24"> | ||||
|             <a-form-item v-if="hyperVMWShow" :label="$t('label.keyboardtype')"> | ||||
|             <a-form-item v-if="hyperVMWShow && !deployasis" :label="$t('label.keyboardtype')"> | ||||
|               <a-select | ||||
|                 v-decorator="['keyboardType', { | ||||
|                   rules: [ | ||||
| @ -248,7 +258,7 @@ | ||||
|             </a-form-item> | ||||
|           </a-col> | ||||
|         </a-row> | ||||
|         <a-row :gutter="12" v-if="!hyperVMWShow"> | ||||
|         <a-row :gutter="12" v-if="!hyperVMWShow || (hyperVMWShow && !deployasis)"> | ||||
|           <a-col :md="24" :lg="24"> | ||||
|             <a-form-item :label="$t('label.ostypeid')"> | ||||
|               <a-select | ||||
| @ -378,6 +388,7 @@ export default { | ||||
|       hyperKVMShow: false, | ||||
|       hyperXenServerShow: false, | ||||
|       hyperVMWShow: false, | ||||
|       deployasis: false, | ||||
|       zoneError: '', | ||||
|       zoneErrorMessage: '', | ||||
|       loading: false, | ||||
| @ -763,6 +774,7 @@ export default { | ||||
|       this.hyperXenServerShow = false | ||||
|       this.hyperVMWShow = false | ||||
|       this.hyperKVMShow = false | ||||
|       this.deployasis = false | ||||
|       this.allowDirectDownload = false | ||||
| 
 | ||||
|       this.resetSelect() | ||||
| @ -794,10 +806,6 @@ export default { | ||||
|               continue | ||||
|             } | ||||
|             params[key] = input.join() | ||||
|           } else if (key === 'zoneid') { | ||||
|             params[key] = input | ||||
|           } else if (key === 'ostypeid') { | ||||
|             params[key] = input | ||||
|           } else if (key === 'hypervisor') { | ||||
|             params[key] = this.hyperVisor.opts[input].name | ||||
|           } else if (key === 'groupenabled') { | ||||
|  | ||||
| @ -64,10 +64,10 @@ | ||||
|       :maskClosable="false" | ||||
|       :footer="null" | ||||
|       :cancelText="$t('label.cancel')" | ||||
|       @cancel="showCreateForm = false" | ||||
|       @cancel="closeAction" | ||||
|       centered | ||||
|       width="auto"> | ||||
|       <CreateNetwork :resource="{ zoneid: resource.zoneid }"/> | ||||
|       <CreateNetwork :resource="{ zoneid: resource.zoneid }" @close-action="closeAction"/> | ||||
|     </a-modal> | ||||
| 
 | ||||
|   </a-spin> | ||||
| @ -161,6 +161,9 @@ export default { | ||||
|     handleOpenShowCreateForm () { | ||||
|       this.showCreateForm = true | ||||
|     }, | ||||
|     closeAction () { | ||||
|       this.showCreateForm = false | ||||
|     }, | ||||
|     changePage (page, pageSize) { | ||||
|       this.page = page | ||||
|       this.pageSize = pageSize | ||||
|  | ||||
| @ -82,7 +82,7 @@ | ||||
|                 <div>{{ acl.traffictype }}</div> | ||||
|               </div> | ||||
|               <div class="list__col"> | ||||
|                 <div class="list__label">{{ $t('label.reason') }}</div> | ||||
|                 <div class="list__label">{{ $t('label.description') }}</div> | ||||
|                 <div>{{ acl.reason }}</div> | ||||
|               </div> | ||||
|             </div> | ||||
| @ -184,7 +184,7 @@ | ||||
|             <a-select-option value="egress">{{ $t('label.egress') }}</a-select-option> | ||||
|           </a-select> | ||||
|         </a-form-item> | ||||
|         <a-form-item :label="$t('label.reason')"> | ||||
|         <a-form-item :label="$t('label.description')"> | ||||
|           <a-textarea | ||||
|             v-decorator="['reason']" | ||||
|             :autosize="{ minRows: 2 }" | ||||
|  | ||||
| @ -554,7 +554,7 @@ export default { | ||||
|         vpcid: this.resource.vpcid | ||||
|       }).then(json => { | ||||
|         this.tiers.data = json.listnetworksresponse.network || [] | ||||
|         this.selectedTier = this.tiers.data && this.tiers.data[0].id ? this.tiers.data[0].id : null | ||||
|         this.selectedTier = this.tiers.data?.[0]?.id ? this.tiers.data[0].id : null | ||||
|         this.$forceUpdate() | ||||
|       }).catch(error => { | ||||
|         this.$notifyError(error) | ||||
|  | ||||
| @ -503,7 +503,6 @@ export default { | ||||
|       this.addVmModalNicLoading = false | ||||
|       this.nics = [] | ||||
|       this.resetTagInputs() | ||||
|       this.resetAllRules() | ||||
|     }, | ||||
|     openTagsModal (id) { | ||||
|       this.tagsModalLoading = true | ||||
|  | ||||
| @ -385,7 +385,13 @@ export default { | ||||
|         publicIps: {}, | ||||
|         snats: {}, | ||||
|         vms: {} | ||||
|       }, | ||||
|       lbProviderMap: { | ||||
|         publicLb: { | ||||
|           vpc: ['VpcVirtualRouter', 'Netscaler'] | ||||
|         } | ||||
|       }, | ||||
|       publicLBExists: false | ||||
|     } | ||||
|   }, | ||||
|   created () { | ||||
| @ -417,6 +423,7 @@ export default { | ||||
|         this.fetchLoadBalancers(network.id) | ||||
|         this.fetchVMs(network.id) | ||||
|       } | ||||
|       this.publicLBNetworkExists() | ||||
|     }, | ||||
|     fetchNetworkAclList () { | ||||
|       this.fetchLoading = true | ||||
| @ -435,6 +442,38 @@ export default { | ||||
|         this.modalLoading = false | ||||
|       }) | ||||
|     }, | ||||
|     getNetworkOffering (networkId) { | ||||
|       return new Promise((resolve, reject) => { | ||||
|         api('listNetworkOfferings', { | ||||
|           id: networkId | ||||
|         }).then(json => { | ||||
|           var networkOffering = json.listnetworkofferingsresponse.networkoffering[0] | ||||
|           resolve(networkOffering) | ||||
|         }).catch(e => { | ||||
|           reject(e) | ||||
|         }) | ||||
|       }) | ||||
|     }, | ||||
|     publicLBNetworkExists () { | ||||
|       api('listNetworks', { | ||||
|         vpcid: this.resource.id, | ||||
|         supportedservices: 'LB' | ||||
|       }).then(async json => { | ||||
|         var lbNetworks = json.listnetworksresponse.network || [] | ||||
|         if (lbNetworks.length > 0) { | ||||
|           this.publicLBExists = true | ||||
|           for (var idx = 0; idx < lbNetworks.length; idx++) { | ||||
|             const lbNetworkOffering = await this.getNetworkOffering(lbNetworks[idx].networkofferingid) | ||||
|             const index = lbNetworkOffering.service.map(svc => { return svc.name }).indexOf('Lb') | ||||
|             if (index !== -1 && | ||||
|               this.lbProviderMap.publicLb.vpc.indexOf(lbNetworkOffering.service.map(svc => { return svc.provider[0].name })[index]) !== -1) { | ||||
|               this.publicLBExists = true | ||||
|               break | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     fetchNetworkOfferings () { | ||||
|       this.fetchLoading = true | ||||
|       this.modalLoading = true | ||||
| @ -445,6 +484,17 @@ export default { | ||||
|         state: 'Enabled' | ||||
|       }).then(json => { | ||||
|         this.networkOfferings = json.listnetworkofferingsresponse.networkoffering || [] | ||||
|         var filteredOfferings = [] | ||||
|         if (this.publicLBExists) { | ||||
|           for (var index in this.networkOfferings) { | ||||
|             const offering = this.networkOfferings[index] | ||||
|             const idx = offering.service.map(svc => { return svc.name }).indexOf('Lb') | ||||
|             if (idx === -1 || this.lbProviderMap.publicLb.vpc.indexOf(offering.service.map(svc => { return svc.provider[0].name })[idx]) === -1) { | ||||
|               filteredOfferings.push(offering) | ||||
|             } | ||||
|           } | ||||
|           this.networkOfferings = filteredOfferings | ||||
|         } | ||||
|         this.$nextTick(function () { | ||||
|           this.form.setFieldsValue({ | ||||
|             networkOffering: this.networkOfferings[0].id | ||||
|  | ||||
| @ -680,7 +680,7 @@ | ||||
|             :loading="domainLoading" | ||||
|             :placeholder="this.$t('label.domainid')"> | ||||
|             <a-select-option v-for="(opt, optIndex) in this.domains" :key="optIndex"> | ||||
|               {{ opt.name || opt.description }} | ||||
|               {{ opt.path || opt.name || opt.description }} | ||||
|             </a-select-option> | ||||
|           </a-select> | ||||
|         </a-form-item> | ||||
|  | ||||
| @ -368,7 +368,7 @@ | ||||
|             :loading="domainLoading" | ||||
|             :placeholder="this.$t('label.domainid')"> | ||||
|             <a-select-option v-for="(opt, optIndex) in this.domains" :key="optIndex"> | ||||
|               {{ opt.name || opt.description }} | ||||
|               {{ opt.path || opt.name || opt.description }} | ||||
|             </a-select-option> | ||||
|           </a-select> | ||||
|         </a-form-item> | ||||
|  | ||||
| @ -228,7 +228,7 @@ | ||||
|                   message: `${this.$t('message.error.select')}` | ||||
|                 } | ||||
|               ], | ||||
|               initialValue: 0 | ||||
|               initialValue: this.serviceOfferings.length > 0 ? this.serviceOfferings[0].id : '' | ||||
|             }]" | ||||
|             showSearch | ||||
|             optionFilterProp="children" | ||||
| @ -237,7 +237,7 @@ | ||||
|             }" | ||||
|             :loading="serviceOfferingLoading" | ||||
|             :placeholder="this.$t('label.serviceofferingid')"> | ||||
|             <a-select-option v-for="(opt, optIndex) in this.serviceOfferings" :key="optIndex"> | ||||
|             <a-select-option v-for="(opt) in this.serviceOfferings" :key="opt.id"> | ||||
|               {{ opt.name || opt.description }} | ||||
|             </a-select-option> | ||||
|           </a-select> | ||||
| @ -410,7 +410,7 @@ | ||||
|             :loading="domainLoading" | ||||
|             :placeholder="this.$t('label.domain')"> | ||||
|             <a-select-option v-for="(opt, optIndex) in this.domains" :key="optIndex"> | ||||
|               {{ opt.name || opt.description }} | ||||
|               {{ opt.path || opt.name || opt.description }} | ||||
|             </a-select-option> | ||||
|           </a-select> | ||||
|         </a-form-item> | ||||
|  | ||||
| @ -108,7 +108,7 @@ | ||||
|             :loading="domainLoading" | ||||
|             :placeholder="this.$t('label.domain')"> | ||||
|             <a-select-option v-for="(opt, optIndex) in this.domains" :key="optIndex"> | ||||
|               {{ opt.name || opt.description }} | ||||
|               {{ opt.path || opt.name || opt.description }} | ||||
|             </a-select-option> | ||||
|           </a-select> | ||||
|         </a-form-item> | ||||
|  | ||||
| @ -48,7 +48,7 @@ | ||||
|             :loading="domainLoading" | ||||
|             :placeholder="this.apiParams.domainid.description"> | ||||
|             <a-select-option v-for="(opt, optIndex) in this.domains" :key="optIndex"> | ||||
|               {{ opt.name || opt.description }} | ||||
|               {{ opt.path || opt.name || opt.description }} | ||||
|             </a-select-option> | ||||
|           </a-select> | ||||
|         </a-form-item> | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user