mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 01:32:18 +02:00
Merge branch 'main' of https://github.com/apache/cloudstack into change-cp-settings-to-zonelevel
This commit is contained in:
commit
a5f557f81b
@ -209,4 +209,9 @@ public class CreateSnapshotFromVMSnapshotCmd extends BaseAsyncCreateCmd {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getApiResourceId() {
|
||||
return getEntityId();
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,6 +188,21 @@ public class BridgeVifDriver extends VifDriverBase {
|
||||
return vNetId != null && protocol != null && !vNetId.equalsIgnoreCase("untagged");
|
||||
}
|
||||
|
||||
protected String createStorageVnetBridgeIfNeeded(NicTO nic, String trafficLabel,
|
||||
String storageBrName) throws InternalErrorException {
|
||||
if (!Networks.BroadcastDomainType.Storage.equals(nic.getBroadcastType()) || nic.getBroadcastUri() == null) {
|
||||
return storageBrName;
|
||||
}
|
||||
String vNetId = Networks.BroadcastDomainType.getValue(nic.getBroadcastUri());
|
||||
String protocol = Networks.BroadcastDomainType.Vlan.scheme();
|
||||
if (!isValidProtocolAndVnetId(vNetId, protocol)) {
|
||||
return storageBrName;
|
||||
}
|
||||
logger.debug(String.format("creating a vNet dev and bridge for %s traffic per traffic label %s",
|
||||
Networks.TrafficType.Storage.name(), trafficLabel));
|
||||
return createVnetBr(vNetId, storageBrName, protocol);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LibvirtVMDef.InterfaceDef plug(NicTO nic, String guestOsType, String nicAdapter, Map<String, String> extraConfig) throws InternalErrorException, LibvirtException {
|
||||
|
||||
@ -254,15 +269,7 @@ public class BridgeVifDriver extends VifDriverBase {
|
||||
intf.defBridgeNet(_bridges.get("private"), null, nic.getMac(), getGuestNicModel(guestOsType, nicAdapter));
|
||||
} else if (nic.getType() == Networks.TrafficType.Storage) {
|
||||
String storageBrName = nic.getName() == null ? _bridges.get("private") : nic.getName();
|
||||
if (nic.getBroadcastType() == Networks.BroadcastDomainType.Storage) {
|
||||
vNetId = Networks.BroadcastDomainType.getValue(nic.getBroadcastUri());
|
||||
protocol = Networks.BroadcastDomainType.Vlan.scheme();
|
||||
}
|
||||
if (isValidProtocolAndVnetId(vNetId, protocol)) {
|
||||
logger.debug(String.format("creating a vNet dev and bridge for %s traffic per traffic label %s",
|
||||
Networks.TrafficType.Storage.name(), trafficLabel));
|
||||
storageBrName = createVnetBr(vNetId, storageBrName, protocol);
|
||||
}
|
||||
storageBrName = createStorageVnetBridgeIfNeeded(nic, trafficLabel, storageBrName);
|
||||
intf.defBridgeNet(storageBrName, null, nic.getMac(), getGuestNicModel(guestOsType, nicAdapter));
|
||||
}
|
||||
if (nic.getPxeDisable()) {
|
||||
@ -295,7 +302,7 @@ public class BridgeVifDriver extends VifDriverBase {
|
||||
return "brvx-" + vnetId;
|
||||
}
|
||||
|
||||
private String createVnetBr(String vNetId, String pifKey, String protocol) throws InternalErrorException {
|
||||
protected String createVnetBr(String vNetId, String pifKey, String protocol) throws InternalErrorException {
|
||||
String nic = _pifs.get(pifKey);
|
||||
if (nic == null || isVxlanOrNetris(protocol)) {
|
||||
// if not found in bridge map, maybe traffic label refers to pif already?
|
||||
|
||||
@ -48,6 +48,7 @@ import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.RngDef.RngBackendModel;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.WatchDogDef;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.WatchDogDef.WatchDogAction;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.WatchDogDef.WatchDogModel;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.GuestDef;
|
||||
|
||||
public class LibvirtDomainXMLParser {
|
||||
protected Logger logger = LogManager.getLogger(getClass());
|
||||
@ -63,6 +64,8 @@ public class LibvirtDomainXMLParser {
|
||||
private LibvirtVMDef.CpuTuneDef cpuTuneDef;
|
||||
private LibvirtVMDef.CpuModeDef cpuModeDef;
|
||||
private String name;
|
||||
private GuestDef.BootType bootType;
|
||||
private GuestDef.BootMode bootMode;
|
||||
|
||||
public boolean parseDomainXML(String domXML) {
|
||||
DocumentBuilder builder;
|
||||
@ -388,6 +391,7 @@ public class LibvirtDomainXMLParser {
|
||||
}
|
||||
extractCpuTuneDef(rootElement);
|
||||
extractCpuModeDef(rootElement);
|
||||
extractBootDef(rootElement);
|
||||
return true;
|
||||
} catch (ParserConfigurationException e) {
|
||||
logger.debug(e.toString());
|
||||
@ -516,6 +520,14 @@ public class LibvirtDomainXMLParser {
|
||||
return cpuModeDef;
|
||||
}
|
||||
|
||||
public GuestDef.BootType getBootType() {
|
||||
return bootType;
|
||||
}
|
||||
|
||||
public GuestDef.BootMode getBootMode() {
|
||||
return bootMode;
|
||||
}
|
||||
|
||||
private void extractCpuTuneDef(final Element rootElement) {
|
||||
NodeList cpuTunesList = rootElement.getElementsByTagName("cputune");
|
||||
if (cpuTunesList.getLength() > 0) {
|
||||
@ -569,4 +581,26 @@ public class LibvirtDomainXMLParser {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void extractBootDef(final Element rootElement) {
|
||||
bootType = GuestDef.BootType.BIOS;
|
||||
bootMode = GuestDef.BootMode.LEGACY;
|
||||
Element osElement = (Element) rootElement.getElementsByTagName("os").item(0);
|
||||
if (osElement == null) {
|
||||
return;
|
||||
}
|
||||
NodeList loaderList = osElement.getElementsByTagName("loader");
|
||||
if (loaderList.getLength() == 0) {
|
||||
return;
|
||||
}
|
||||
Element loader = (Element) loaderList.item(0);
|
||||
String type = loader.getAttribute("type");
|
||||
String secure = loader.getAttribute("secure");
|
||||
if ("pflash".equalsIgnoreCase(type) || loader.getTextContent().toLowerCase().contains("uefi")) {
|
||||
bootType = GuestDef.BootType.UEFI;
|
||||
}
|
||||
if ("yes".equalsIgnoreCase(secure)) {
|
||||
bootMode = GuestDef.BootMode.SECURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,7 +65,7 @@ public class LibvirtVMDef {
|
||||
}
|
||||
}
|
||||
|
||||
enum BootType {
|
||||
public enum BootType {
|
||||
UEFI("UEFI"), BIOS("BIOS");
|
||||
|
||||
String _type;
|
||||
@ -80,7 +80,7 @@ public class LibvirtVMDef {
|
||||
}
|
||||
}
|
||||
|
||||
enum BootMode {
|
||||
public enum BootMode {
|
||||
LEGACY("LEGACY"), SECURE("SECURE");
|
||||
|
||||
String _mode;
|
||||
|
||||
@ -135,6 +135,12 @@ public final class LibvirtGetUnmanagedInstancesCommandWrapper extends CommandWra
|
||||
instance.setNics(getUnmanagedInstanceNics(parser.getInterfaces()));
|
||||
instance.setDisks(getUnmanagedInstanceDisks(parser.getDisks(),libvirtComputingResource, conn, domain.getName()));
|
||||
instance.setVncPassword(getFormattedVncPassword(parser.getVncPasswd()));
|
||||
if (parser.getBootType() != null) {
|
||||
instance.setBootType(parser.getBootType().toString());
|
||||
}
|
||||
if (parser.getBootMode() != null) {
|
||||
instance.setBootMode(parser.getBootMode().toString());
|
||||
}
|
||||
|
||||
return instance;
|
||||
} catch (Exception e) {
|
||||
|
||||
@ -16,24 +16,29 @@
|
||||
// under the License.
|
||||
package com.cloud.hypervisor.kvm.resource;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import com.cloud.agent.api.to.NicTO;
|
||||
import com.cloud.exception.InternalErrorException;
|
||||
import com.cloud.network.Networks;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class BridgeVifDriverTest {
|
||||
|
||||
private BridgeVifDriver driver;
|
||||
private static final String BRIDGE_NAME = "cloudbr1";
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
driver = new BridgeVifDriver();
|
||||
}
|
||||
@Spy
|
||||
@InjectMocks
|
||||
private BridgeVifDriver driver = new BridgeVifDriver();
|
||||
|
||||
@Test
|
||||
public void isBroadcastTypeVlanOrVxlan() {
|
||||
@ -58,4 +63,41 @@ public class BridgeVifDriverTest {
|
||||
Assert.assertTrue(driver.isValidProtocolAndVnetId("123", "vlan"));
|
||||
Assert.assertTrue(driver.isValidProtocolAndVnetId("456", "vxlan"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createStorageVnetBridgeIfNeededReturnsStorageBrNameWhenBroadcastTypeIsNotStorageButValidValues() throws InternalErrorException {
|
||||
NicTO nic = new NicTO();
|
||||
nic.setBroadcastType(Networks.BroadcastDomainType.Storage);
|
||||
int vlan = 123;
|
||||
String newBridge = "br-" + vlan;
|
||||
nic.setBroadcastUri(Networks.BroadcastDomainType.Storage.toUri(vlan));
|
||||
Mockito.doReturn(newBridge).when(driver).createVnetBr(Mockito.anyString(), Mockito.anyString(), Mockito.anyString());
|
||||
String result = driver.createStorageVnetBridgeIfNeeded(nic, "trafficLabel", BRIDGE_NAME);
|
||||
Assert.assertEquals(newBridge, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createStorageVnetBridgeIfNeededReturnsStorageBrNameWhenBroadcastTypeIsNotStorage() throws InternalErrorException {
|
||||
NicTO nic = new NicTO();
|
||||
nic.setBroadcastType(Networks.BroadcastDomainType.Vlan);
|
||||
String result = driver.createStorageVnetBridgeIfNeeded(nic, "trafficLabel", BRIDGE_NAME);
|
||||
Assert.assertEquals(BRIDGE_NAME, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createStorageVnetBridgeIfNeededReturnsStorageBrNameWhenBroadcastUriIsNull() throws InternalErrorException {
|
||||
NicTO nic = new NicTO();
|
||||
nic.setBroadcastType(Networks.BroadcastDomainType.Storage);
|
||||
String result = driver.createStorageVnetBridgeIfNeeded(nic, "trafficLabel", BRIDGE_NAME);
|
||||
Assert.assertEquals(BRIDGE_NAME, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createStorageVnetBridgeIfNeededCreatesVnetBridgeWhenUntaggedVlan() throws InternalErrorException, URISyntaxException {
|
||||
NicTO nic = new NicTO();
|
||||
nic.setBroadcastType(Networks.BroadcastDomainType.Storage);
|
||||
nic.setBroadcastUri(new URI(Networks.BroadcastDomainType.Storage.scheme() + "://untagged"));
|
||||
String result = driver.createStorageVnetBridgeIfNeeded(nic, "trafficLabel", BRIDGE_NAME);
|
||||
Assert.assertEquals(BRIDGE_NAME, result);
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,8 +20,13 @@
|
||||
package com.cloud.hypervisor.kvm.resource;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.ChannelDef;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef;
|
||||
@ -31,10 +36,15 @@ import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.WatchDogDef;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.apache.cloudstack.utils.qemu.QemuObject;
|
||||
import org.apache.cloudstack.utils.security.ParserUtils;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class LibvirtDomainXMLParserTest extends TestCase {
|
||||
@ -386,4 +396,84 @@ public class LibvirtDomainXMLParserTest extends TestCase {
|
||||
Assert.assertEquals("CPU cores count is parsed", 4, libvirtDomainXMLParser.getCpuModeDef().getCoresPerSocket());
|
||||
Assert.assertEquals("CPU threads count is parsed", 2, libvirtDomainXMLParser.getCpuModeDef().getThreadsPerCore());
|
||||
}
|
||||
|
||||
private LibvirtDomainXMLParser parseElementFromXML(String xml) {
|
||||
LibvirtDomainXMLParser parser = new LibvirtDomainXMLParser();
|
||||
DocumentBuilder builder;
|
||||
try {
|
||||
builder = ParserUtils.getSaferDocumentBuilderFactory().newDocumentBuilder();
|
||||
InputSource is = new InputSource();
|
||||
is.setCharacterStream(new StringReader(xml));
|
||||
Document doc = builder.parse(is);
|
||||
Element element = doc.getDocumentElement();
|
||||
parser.extractBootDef(element);
|
||||
} catch (ParserConfigurationException | IOException | SAXException e) {
|
||||
Assert.fail("Failed to parse XML: " + e.getMessage());
|
||||
}
|
||||
return parser;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractBootDefParsesUEFISecureBootCorrectly() {
|
||||
String xml = "<domain type='kvm'>" +
|
||||
"<os>" +
|
||||
"<loader type='pflash' secure='yes'>/path/to/uefi/loader</loader>" +
|
||||
"</os>" +
|
||||
"</domain>";
|
||||
|
||||
LibvirtDomainXMLParser parser = parseElementFromXML(xml);
|
||||
|
||||
assertEquals(LibvirtVMDef.GuestDef.BootType.UEFI, parser.getBootType());
|
||||
assertEquals(LibvirtVMDef.GuestDef.BootMode.SECURE, parser.getBootMode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractBootDefParsesUEFILegacyBootCorrectly() {
|
||||
String xml = "<domain type='kvm'>" +
|
||||
"<os>" +
|
||||
"<loader type='pflash' secure='no'>/path/to/uefi/loader</loader>" +
|
||||
"</os>" +
|
||||
"</domain>";
|
||||
|
||||
LibvirtDomainXMLParser parser = parseElementFromXML(xml);
|
||||
|
||||
assertEquals(LibvirtVMDef.GuestDef.BootType.UEFI, parser.getBootType());
|
||||
assertEquals(LibvirtVMDef.GuestDef.BootMode.LEGACY, parser.getBootMode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractBootDefDefaultsToBIOSLegacyWhenNoLoaderPresent() {
|
||||
String xml = "<domain type='kvm'>" +
|
||||
"<os>" +
|
||||
"<type arch='x86_64'>hvm</type>" +
|
||||
"</os>" +
|
||||
"</domain>";
|
||||
|
||||
LibvirtDomainXMLParser parser = parseElementFromXML(xml);
|
||||
|
||||
assertEquals(LibvirtVMDef.GuestDef.BootType.BIOS, parser.getBootType());
|
||||
assertEquals(LibvirtVMDef.GuestDef.BootMode.LEGACY, parser.getBootMode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractBootDefHandlesEmptyOSSection() {
|
||||
String xml = "<domain type='kvm'>" +
|
||||
"<os></os>" +
|
||||
"</domain>";
|
||||
|
||||
LibvirtDomainXMLParser parser = parseElementFromXML(xml);
|
||||
|
||||
assertEquals(LibvirtVMDef.GuestDef.BootType.BIOS, parser.getBootType());
|
||||
assertEquals(LibvirtVMDef.GuestDef.BootMode.LEGACY, parser.getBootMode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractBootDefHandlesMissingOSSection() {
|
||||
String xml = "<domain type='kvm'></domain>";
|
||||
|
||||
LibvirtDomainXMLParser parser = parseElementFromXML(xml);
|
||||
|
||||
assertEquals(LibvirtVMDef.GuestDef.BootType.BIOS, parser.getBootType());
|
||||
assertEquals(LibvirtVMDef.GuestDef.BootMode.LEGACY, parser.getBootMode());
|
||||
}
|
||||
}
|
||||
|
||||
@ -416,18 +416,33 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
||||
logTransitStateAndThrow(logLevel, message, null, null, ex);
|
||||
}
|
||||
|
||||
private boolean isKubernetesServiceNetworkOfferingConfigured(DataCenter zone) {
|
||||
private boolean isKubernetesServiceNetworkOfferingConfigured(DataCenter zone, Long networkId) {
|
||||
// Check network offering
|
||||
String networkOfferingName = KubernetesClusterNetworkOffering.value();
|
||||
if (networkOfferingName == null || networkOfferingName.isEmpty()) {
|
||||
logger.warn(String.format("Global setting %s is empty. Admin has not yet specified the network offering to be used for provisioning isolated network for the cluster", KubernetesClusterNetworkOffering.key()));
|
||||
if (StringUtils.isEmpty(networkOfferingName) && networkId == null) {
|
||||
logger.warn("Global setting: {} is empty. Admin has not yet specified the network offering to be used for provisioning isolated network for the cluster nor has a pre-created network been passed", KubernetesClusterNetworkOffering.key());
|
||||
return false;
|
||||
}
|
||||
NetworkOfferingVO networkOffering = networkOfferingDao.findByUniqueName(networkOfferingName);
|
||||
if (networkOffering == null) {
|
||||
logger.warn(String.format("Unable to find the network offering %s to be used for provisioning Kubernetes cluster", networkOfferingName));
|
||||
return false;
|
||||
NetworkOfferingVO networkOffering = null;
|
||||
if (networkId != null) {
|
||||
NetworkVO network = networkDao.findById(networkId);
|
||||
if (network == null) {
|
||||
logger.warn("Unable to find the network with ID: {} passed for the Kubernetes cluster", networkId);
|
||||
return false;
|
||||
}
|
||||
networkOffering = networkOfferingDao.findById(network.getNetworkOfferingId());
|
||||
if (networkOffering == null) {
|
||||
logger.warn("Unable to find the network offering of the network: {} ({}) to be used for provisioning Kubernetes cluster", network.getName(), network.getUuid());
|
||||
return false;
|
||||
}
|
||||
} else if (StringUtils.isNotEmpty(networkOfferingName)) {
|
||||
networkOffering = networkOfferingDao.findByUniqueName(networkOfferingName);
|
||||
if (networkOffering == null) {
|
||||
logger.warn("Unable to find the network offering: {} to be used for provisioning Kubernetes cluster", networkOfferingName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (networkOffering.getState() == NetworkOffering.State.Disabled) {
|
||||
logger.warn("Network offering: {} is not enabled", networkOffering);
|
||||
return false;
|
||||
@ -462,8 +477,8 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isKubernetesServiceConfigured(DataCenter zone) {
|
||||
if (!isKubernetesServiceNetworkOfferingConfigured(zone)) {
|
||||
private boolean isKubernetesServiceConfigured(DataCenter zone, Long networkId) {
|
||||
if (!isKubernetesServiceNetworkOfferingConfigured(zone, networkId)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -1018,7 +1033,7 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
||||
|
||||
DataCenter zone = validateAndGetZoneForKubernetesCreateParameters(zoneId, networkId);
|
||||
|
||||
if (!isKubernetesServiceConfigured(zone)) {
|
||||
if (!isKubernetesServiceConfigured(zone, networkId)) {
|
||||
throw new CloudRuntimeException("Kubernetes service has not been configured properly to provision Kubernetes clusters");
|
||||
}
|
||||
|
||||
|
||||
@ -32,6 +32,8 @@ import java.util.Map;
|
||||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.event.ActionEvent;
|
||||
import com.cloud.event.EventTypes;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
@ -545,6 +547,7 @@ public class InternalLoadBalancerVMManagerImpl extends ManagerBase implements In
|
||||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_INTERNAL_LB_VM_STOP, eventDescription = "stopping internal LB VM", async = true)
|
||||
public VirtualRouter stopInternalLbVm(final long vmId, final boolean forced, final Account caller, final long callerUserId) throws ConcurrentOperationException, ResourceUnavailableException {
|
||||
final DomainRouterVO internalLbVm = _internalLbVmDao.findById(vmId);
|
||||
if (internalLbVm == null || internalLbVm.getRole() != Role.INTERNAL_LB_VM) {
|
||||
@ -974,6 +977,7 @@ public class InternalLoadBalancerVMManagerImpl extends ManagerBase implements In
|
||||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_INTERNAL_LB_VM_START, eventDescription = "starting internal LB VM", async = true)
|
||||
public VirtualRouter startInternalLbVm(final long internalLbVmId, final Account caller, final long callerUserId) throws StorageUnavailableException, InsufficientCapacityException,
|
||||
ConcurrentOperationException, ResourceUnavailableException {
|
||||
|
||||
|
||||
@ -16,6 +16,9 @@
|
||||
// under the License.
|
||||
package org.apache.cloudstack.internallbvmmgr;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
@ -24,13 +27,15 @@ import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.event.ActionEventUtils;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.network.lb.InternalLoadBalancerVMService;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
import org.mockito.BDDMockito;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
@ -82,6 +87,8 @@ public class InternalLBVMServiceTest extends TestCase {
|
||||
@Inject
|
||||
AccountDao _accountDao;
|
||||
|
||||
private MockedStatic<ActionEventUtils> actionEventUtilsMocked;
|
||||
|
||||
long validVmId = 1L;
|
||||
long nonExistingVmId = 2L;
|
||||
long nonInternalLbVmId = 3L;
|
||||
@ -105,7 +112,7 @@ public class InternalLBVMServiceTest extends TestCase {
|
||||
|
||||
Mockito.when(_accountMgr.getSystemUser()).thenReturn(new UserVO(1));
|
||||
Mockito.when(_accountMgr.getSystemAccount()).thenReturn(new AccountVO(2));
|
||||
Mockito.when(_accountDao.findByIdIncludingRemoved(ArgumentMatchers.anyLong())).thenReturn(new AccountVO(2));
|
||||
Mockito.when(_accountDao.findByIdIncludingRemoved(anyLong())).thenReturn(new AccountVO(2));
|
||||
CallContext.register(_accountMgr.getSystemUser(), _accountMgr.getSystemAccount());
|
||||
|
||||
final DomainRouterVO validVm =
|
||||
@ -120,11 +127,16 @@ public class InternalLBVMServiceTest extends TestCase {
|
||||
Mockito.when(_domainRouterDao.findById(validVmId)).thenReturn(validVm);
|
||||
Mockito.when(_domainRouterDao.findById(nonExistingVmId)).thenReturn(null);
|
||||
Mockito.when(_domainRouterDao.findById(nonInternalLbVmId)).thenReturn(nonInternalLbVm);
|
||||
|
||||
actionEventUtilsMocked = Mockito.mockStatic(ActionEventUtils.class);
|
||||
BDDMockito.given(ActionEventUtils.onStartedActionEvent(anyLong(), anyLong(), anyString(), anyString(), anyLong(), anyString(), anyBoolean(), anyLong()))
|
||||
.willReturn(1L);
|
||||
}
|
||||
|
||||
@Override
|
||||
@After
|
||||
public void tearDown() {
|
||||
actionEventUtilsMocked.close();
|
||||
CallContext.unregister();
|
||||
}
|
||||
|
||||
|
||||
@ -121,6 +121,10 @@ public class PrimeraAdapter implements ProviderAdapter {
|
||||
public void disconnect() {
|
||||
logger.info("PrimeraAdapter:disconnect(): closing session");
|
||||
try {
|
||||
//Delete session safely without triggering refreshSession
|
||||
if (key != null && _client != null) {
|
||||
logout();
|
||||
}
|
||||
_client.close();
|
||||
} catch (IOException e) {
|
||||
logger.warn("PrimeraAdapter:refreshSession(): Error closing client connection", e);
|
||||
@ -130,6 +134,40 @@ public class PrimeraAdapter implements ProviderAdapter {
|
||||
}
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* Delete session directly without going through refreshSession to avoid infinite recursion
|
||||
*/
|
||||
private void logout() {
|
||||
CloseableHttpResponse response = null;
|
||||
try {
|
||||
logger.debug("PrimeraAdapter:logout(): Delete session directly");
|
||||
HttpDelete request = new HttpDelete(url + "/credentials/" + key);
|
||||
request.addHeader("Content-Type", "application/json");
|
||||
request.addHeader("Accept", "application/json");
|
||||
request.addHeader("X-HP3PAR-WSAPI-SessionKey", key);
|
||||
|
||||
response = (CloseableHttpResponse) _client.execute(request);
|
||||
final int statusCode = response.getStatusLine().getStatusCode();
|
||||
|
||||
if (statusCode == 200 || statusCode == 404) {
|
||||
logger.debug("PrimeraAdapter:logout(): Session deleted successfully or was already expired");
|
||||
} else if (statusCode == 401 || statusCode == 403) {
|
||||
logger.warn("PrimeraAdapter:logout(): Session already invalid or expired during deletion");
|
||||
} else {
|
||||
logger.warn("PrimeraAdapter:logout(): Unexpected response when deleting session: {}", statusCode);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.warn("PrimeraAdapter:logout(): Error deleting session: {}", e.getMessage());
|
||||
} finally {
|
||||
if (response != null) {
|
||||
try {
|
||||
response.close();
|
||||
} catch (IOException e) {
|
||||
logger.debug("PrimeraAdapter:logout(): Error closing response from session deletion", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProviderVolume create(ProviderAdapterContext context, ProviderAdapterDataObject dataIn,
|
||||
|
||||
@ -54,7 +54,9 @@ public class TotpUserTwoFactorAuthenticator extends AdapterBase implements UserT
|
||||
logger.info("2FA matches user's input");
|
||||
return;
|
||||
}
|
||||
throw new CloudTwoFactorAuthenticationException("two-factor authentication code provided is invalid");
|
||||
String msg = "two-factor authentication code provided is invalid";
|
||||
logger.error(msg);
|
||||
throw new CloudTwoFactorAuthenticationException(msg);
|
||||
}
|
||||
|
||||
private String get2FAKey(UserAccount userAccount) {
|
||||
|
||||
@ -172,10 +172,10 @@ public class TemplateJoinDaoImpl extends GenericDaoBaseWithTagInformation<Templa
|
||||
}
|
||||
} else if (template.getDownloadState() == Status.BYPASSED) {
|
||||
templateStatus = "Bypassed Secondary Storage";
|
||||
}else if (template.getErrorString()==null){
|
||||
} else if (template.getErrorString() == null) {
|
||||
templateStatus = template.getTemplateState().toString();
|
||||
}else {
|
||||
templateStatus = template.getErrorString();
|
||||
} else {
|
||||
templateStatus = template.getErrorString().trim();
|
||||
}
|
||||
} else if (template.getDownloadState() == Status.DOWNLOADED) {
|
||||
templateStatus = "Download Complete";
|
||||
|
||||
@ -5371,10 +5371,6 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
||||
vlan.getVlanTag()));
|
||||
}
|
||||
}
|
||||
if (NetUtils.isSameIsolationId(vlanId, vlan.getVlanTag()) && !vlanIp6Gateway.equals(vlan.getIp6Gateway())) {
|
||||
throw new InvalidParameterValueException(String.format("The IP range with tag: %s has already been added with gateway %s. Please specify a different tag.",
|
||||
vlan.getVlanTag(), vlan.getIp6Gateway()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1399,6 +1399,8 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
|
||||
}
|
||||
|
||||
@Override
|
||||
@DB
|
||||
@ActionEvent(eventType = EventTypes.EVENT_MAINTENANCE_CANCEL, eventDescription = "cancel maintenance for host", async = true)
|
||||
public Host cancelMaintenance(final CancelHostMaintenanceCmd cmd) {
|
||||
final Long hostId = cmd.getId();
|
||||
|
||||
@ -1422,6 +1424,8 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
|
||||
}
|
||||
|
||||
@Override
|
||||
@DB
|
||||
@ActionEvent(eventType = EventTypes.EVENT_HOST_RECONNECT, eventDescription = "reconnecting host", async = true)
|
||||
public Host reconnectHost(ReconnectHostCmd cmd) throws AgentUnavailableException {
|
||||
Long hostId = cmd.getId();
|
||||
|
||||
@ -1628,6 +1632,8 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
|
||||
}
|
||||
|
||||
@Override
|
||||
@DB
|
||||
@ActionEvent(eventType = EventTypes.EVENT_MAINTENANCE_PREPARE, eventDescription = "prepare maintenance for host", async = true)
|
||||
public Host maintain(final PrepareForHostMaintenanceCmd cmd) {
|
||||
final Long hostId = cmd.getId();
|
||||
final HostVO host = _hostDao.findById(hostId);
|
||||
|
||||
@ -622,6 +622,8 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
|
||||
}
|
||||
|
||||
@Override
|
||||
@DB
|
||||
@ActionEvent(eventType = EventTypes.EVENT_SNAPSHOT_CREATE, eventDescription = "creating snapshot from VM snapshot", async = true)
|
||||
public Snapshot backupSnapshotFromVmSnapshot(Long snapshotId, Long vmId, Long volumeId, Long vmSnapshotId) {
|
||||
VMInstanceVO vm = _vmDao.findById(vmId);
|
||||
if (vm == null) {
|
||||
|
||||
@ -18,6 +18,29 @@
|
||||
*/
|
||||
package org.apache.cloudstack.vm.schedule;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.command.user.vm.CreateVMScheduleCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.DeleteVMScheduleCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.ListVMScheduleCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.UpdateVMScheduleCmd;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.VMScheduleResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.vm.schedule.dao.VMScheduleDao;
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.scheduling.support.CronExpression;
|
||||
|
||||
import com.cloud.api.query.MutualExclusiveIdsManagerBase;
|
||||
import com.cloud.event.ActionEvent;
|
||||
import com.cloud.event.EventTypes;
|
||||
@ -32,26 +55,6 @@ import com.cloud.utils.db.TransactionCallback;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.vm.UserVmManager;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.command.user.vm.CreateVMScheduleCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.DeleteVMScheduleCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.ListVMScheduleCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.UpdateVMScheduleCmd;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.VMScheduleResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.vm.schedule.dao.VMScheduleDao;
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.scheduling.support.CronExpression;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class VMScheduleManagerImpl extends MutualExclusiveIdsManagerBase implements VMScheduleManager, PluggableService {
|
||||
|
||||
@ -205,6 +208,9 @@ public class VMScheduleManagerImpl extends MutualExclusiveIdsManagerBase impleme
|
||||
Date cmdStartDate = cmd.getStartDate();
|
||||
Date cmdEndDate = cmd.getEndDate();
|
||||
Boolean enabled = cmd.getEnabled();
|
||||
final String originalTimeZone = vmSchedule.getTimeZone();
|
||||
final Date originalStartDate = vmSchedule.getStartDate();
|
||||
final Date originalEndDate = vmSchedule.getEndDate();
|
||||
|
||||
TimeZone timeZone;
|
||||
String timeZoneId;
|
||||
@ -231,7 +237,13 @@ public class VMScheduleManagerImpl extends MutualExclusiveIdsManagerBase impleme
|
||||
startDate = Date.from(DateUtil.getZoneDateTime(cmdStartDate, timeZone.toZoneId()).toInstant());
|
||||
}
|
||||
|
||||
validateStartDateEndDate(Objects.requireNonNullElse(startDate, DateUtils.addMinutes(new Date(), 1)), endDate, timeZone);
|
||||
if (ObjectUtils.anyNotNull(cmdStartDate, cmdEndDate, cmdTimeZone) &&
|
||||
(!Objects.equals(originalTimeZone, timeZoneId) ||
|
||||
!Objects.equals(originalStartDate, startDate) ||
|
||||
!Objects.equals(originalEndDate, endDate))) {
|
||||
validateStartDateEndDate(Objects.requireNonNullElse(startDate, DateUtils.addMinutes(new Date(), 1)),
|
||||
endDate, timeZone);
|
||||
}
|
||||
|
||||
if (enabled != null) {
|
||||
vmSchedule.setEnabled(enabled);
|
||||
|
||||
@ -33,7 +33,7 @@
|
||||
"headless": true,
|
||||
"http_directory": "http",
|
||||
"iso_checksum": "sha512:892cf1185a214d16ff62a18c6b89cdcd58719647c99916f6214bfca6f9915275d727b666c0b8fbf022c425ef18647e9759974abf7fc440431c39b50c296a98d3",
|
||||
"iso_url": "https://cdimage.debian.org/mirror/cdimage/release/12.11.0/arm64/iso-cd/debian-12.11.0-arm64-netinst.iso",
|
||||
"iso_url": "https://cdimage.debian.org/mirror/cdimage/archive/12.11.0/arm64/iso-cd/debian-12.11.0-arm64-netinst.iso",
|
||||
"net_device": "virtio-net",
|
||||
"output_directory": "../dist",
|
||||
"qemu_binary": "qemu-system-aarch64",
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
"headless": true,
|
||||
"http_directory": "http",
|
||||
"iso_checksum": "sha512:892cf1185a214d16ff62a18c6b89cdcd58719647c99916f6214bfca6f9915275d727b666c0b8fbf022c425ef18647e9759974abf7fc440431c39b50c296a98d3",
|
||||
"iso_url": "https://cdimage.debian.org/mirror/cdimage/release/12.11.0/arm64/iso-cd/debian-12.11.0-arm64-netinst.iso",
|
||||
"iso_url": "https://cdimage.debian.org/mirror/cdimage/archive/12.11.0/arm64/iso-cd/debian-12.11.0-arm64-netinst.iso",
|
||||
"net_device": "virtio-net",
|
||||
"output_directory": "../dist",
|
||||
"qemu_binary": "qemu-system-aarch64",
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
"headless": true,
|
||||
"http_directory": "http",
|
||||
"iso_checksum": "sha512:0921d8b297c63ac458d8a06f87cd4c353f751eb5fe30fd0d839ca09c0833d1d9934b02ee14bbd0c0ec4f8917dde793957801ae1af3c8122cdf28dde8f3c3e0da",
|
||||
"iso_url": "https://cdimage.debian.org/mirror/cdimage/release/12.11.0/amd64/iso-cd/debian-12.11.0-amd64-netinst.iso",
|
||||
"iso_url": "https://cdimage.debian.org/mirror/cdimage/archive/12.11.0/amd64/iso-cd/debian-12.11.0-amd64-netinst.iso",
|
||||
"net_device": "virtio-net",
|
||||
"output_directory": "../dist",
|
||||
"qemuargs": [
|
||||
|
||||
@ -663,10 +663,14 @@
|
||||
/>
|
||||
</template>
|
||||
<template v-if="column.key === 'domain'">
|
||||
<router-link
|
||||
v-if="record.domainid && !record.domainid.toString().includes(',') && $store.getters.userInfo.roletype !== 'User'"
|
||||
:to="{ path: '/domain/' + record.domainid, query: { tab: 'details' } }"
|
||||
>{{ text }}</router-link>
|
||||
<span v-if="record.domainid && $store.getters.userInfo.roletype !== 'User'">
|
||||
<template v-for="(id, idx) in record.domainid.split(',')" :key="id">
|
||||
<router-link :to="{ path: '/domain/' + id, query: { tab: 'details' } }">
|
||||
{{ record.domain.split(',')[idx] || id }}
|
||||
</router-link>
|
||||
<span v-if="idx < record.domainid.split(',').length - 1">, </span>
|
||||
</template>
|
||||
</span>
|
||||
<span v-else>{{ text }}</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'domainpath'">
|
||||
|
||||
@ -496,9 +496,17 @@ const user = {
|
||||
}).catch(() => {
|
||||
resolve()
|
||||
}).finally(() => {
|
||||
const paths = ['/', '/client']
|
||||
const hostname = window.location.hostname
|
||||
const domains = [undefined, hostname, `.${hostname}`]
|
||||
Object.keys(Cookies.get()).forEach(cookieName => {
|
||||
Cookies.remove(cookieName)
|
||||
Cookies.remove(cookieName, { path: '/client' })
|
||||
paths.forEach(path => {
|
||||
domains.forEach(domain => {
|
||||
const options = { path }
|
||||
if (domain) options.domain = domain
|
||||
Cookies.remove(cookieName, options)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1879,11 +1879,13 @@ export default {
|
||||
const query = Object.assign({}, this.$route.query)
|
||||
delete query.templatefilter
|
||||
delete query.isofilter
|
||||
delete query.account
|
||||
delete query.domainid
|
||||
delete query.state
|
||||
delete query.annotationfilter
|
||||
delete query.leased
|
||||
if (!['publicip'].includes(this.$route.name)) {
|
||||
delete query.account
|
||||
delete query.domainid
|
||||
}
|
||||
if (this.$route.name === 'template') {
|
||||
query.templatefilter = filter
|
||||
} else if (this.$route.name === 'iso') {
|
||||
|
||||
@ -228,8 +228,8 @@ export default {
|
||||
getAPI('listDiskOfferings', {
|
||||
id: this.selectedOffering.diskofferingid
|
||||
}).then(response => {
|
||||
const diskOfferings = response.listdiskofferingsresponse.diskoffering || []
|
||||
if (this.diskOfferings) {
|
||||
const diskOfferings = response?.listdiskofferingsresponse?.diskoffering || []
|
||||
if (diskOfferings?.length > 0) {
|
||||
this.selectedDiskOffering = diskOfferings[0]
|
||||
}
|
||||
}).catch(error => {
|
||||
|
||||
@ -85,7 +85,7 @@
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.directdownload')" :tooltip="apiParams.directdownload.description"/>
|
||||
</template>
|
||||
<a-switch v-model:checked="form.directdownload"/>
|
||||
<a-switch v-model:checked="form.directdownload" @change="handleDirectDownloadChange"/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item ref="checksum" name="checksum">
|
||||
@ -110,7 +110,7 @@
|
||||
}"
|
||||
:loading="zoneLoading"
|
||||
:placeholder="apiParams.zoneid.description">
|
||||
<a-select-option :value="opt.id" v-for="opt in zones" :key="opt.id" :label="opt.name || opt.description">
|
||||
<a-select-option :value="opt.id" v-for="opt in zoneList" :key="opt.id" :label="opt.name || opt.description">
|
||||
<span>
|
||||
<resource-icon v-if="opt.icon" :image="opt.icon.base64image" size="1x" style="margin-right: 5px"/>
|
||||
<global-outlined v-else style="margin-right: 5px" />
|
||||
@ -361,17 +361,18 @@ export default {
|
||||
},
|
||||
created () {
|
||||
this.initForm()
|
||||
this.zones = []
|
||||
if (this.$store.getters.userInfo.roletype === 'Admin' && this.currentForm === 'Create') {
|
||||
this.zones = [
|
||||
{
|
||||
id: '-1',
|
||||
name: this.$t('label.all.zone')
|
||||
}
|
||||
]
|
||||
}
|
||||
this.initZones()
|
||||
this.fetchData()
|
||||
},
|
||||
computed: {
|
||||
zoneList () {
|
||||
let filteredZones = this.zones
|
||||
if (!this.form.directdownload) {
|
||||
filteredZones = this.zones.filter(zone => zone.type !== 'Edge')
|
||||
}
|
||||
return filteredZones
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initForm () {
|
||||
this.formRef = ref()
|
||||
@ -390,6 +391,17 @@ export default {
|
||||
ostypeid: [{ required: true, message: this.$t('message.error.select') }]
|
||||
})
|
||||
},
|
||||
initZones () {
|
||||
this.zones = []
|
||||
if (this.$store.getters.userInfo.roletype === 'Admin' && this.currentForm === 'Create') {
|
||||
this.zones = [
|
||||
{
|
||||
id: '-1',
|
||||
name: this.$t('label.all.zone')
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
fetchData () {
|
||||
this.fetchZoneData()
|
||||
this.fetchOsType()
|
||||
@ -412,11 +424,10 @@ export default {
|
||||
const listZones = json.listzonesresponse.zone
|
||||
if (listZones) {
|
||||
this.zones = this.zones.concat(listZones)
|
||||
this.zones = this.zones.filter(zone => zone.type !== 'Edge')
|
||||
}
|
||||
}).finally(() => {
|
||||
this.zoneLoading = false
|
||||
this.form.zoneid = (this.zones[0].id ? this.zones[0].id : '')
|
||||
this.form.zoneid = this.zoneList?.[0]?.id || ''
|
||||
})
|
||||
},
|
||||
fetchOsType () {
|
||||
@ -467,6 +478,12 @@ export default {
|
||||
this.fileList = newFileList
|
||||
this.form.file = undefined
|
||||
},
|
||||
handleDirectDownloadChange () {
|
||||
if (this.form.zoneid && this.zoneList.find(entry => entry.id === this.form.zoneid)) {
|
||||
return
|
||||
}
|
||||
this.form.zoneid = this.zoneList?.[0]?.id || ''
|
||||
},
|
||||
beforeUpload (file) {
|
||||
this.fileList = [file]
|
||||
this.form.file = file
|
||||
@ -531,7 +548,7 @@ export default {
|
||||
}
|
||||
switch (key) {
|
||||
case 'zoneid':
|
||||
var zone = this.zones.filter(zone => zone.id === input)
|
||||
var zone = this.zoneList.filter(zone => zone.id === input)
|
||||
params[key] = zone[0].id
|
||||
break
|
||||
case 'ostypeid':
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user