Merge branch '4.19' into 4.20

This commit is contained in:
Daan Hoogland 2025-01-24 17:01:42 +01:00
commit 34d2a3bc86
17 changed files with 189 additions and 715 deletions

View File

@ -16,465 +16,20 @@
// under the License. // under the License.
package org.apache.cloudstack.api.response; package org.apache.cloudstack.api.response;
import java.util.Date;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;
import org.apache.cloudstack.api.EntityReference; import org.apache.cloudstack.api.EntityReference;
import com.cloud.host.Host; import com.cloud.host.Host;
import com.cloud.host.Status;
import com.cloud.serializer.Param; import com.cloud.serializer.Param;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
@EntityReference(value = Host.class) @EntityReference(value = Host.class)
public class HostForMigrationResponse extends BaseResponse { public class HostForMigrationResponse extends HostResponse {
@SerializedName(ApiConstants.ID)
@Param(description = "the ID of the host")
private String id;
@SerializedName(ApiConstants.NAME)
@Param(description = "the name of the host")
private String name;
@SerializedName(ApiConstants.STATE)
@Param(description = "the state of the host")
private Status state;
@SerializedName("disconnected")
@Param(description = "true if the host is disconnected. False otherwise.")
private Date disconnectedOn;
@SerializedName(ApiConstants.TYPE)
@Param(description = "the host type")
private Host.Type hostType;
@SerializedName("oscategoryid")
@Param(description = "the OS category ID of the host")
private String osCategoryId;
@SerializedName("oscategoryname")
@Param(description = "the OS category name of the host")
private String osCategoryName;
@SerializedName(ApiConstants.IP_ADDRESS)
@Param(description = "the IP address of the host")
private String ipAddress;
@SerializedName(ApiConstants.ZONE_ID)
@Param(description = "the Zone ID of the host")
private String zoneId;
@SerializedName(ApiConstants.ZONE_NAME)
@Param(description = "the Zone name of the host")
private String zoneName;
@SerializedName(ApiConstants.POD_ID)
@Param(description = "the Pod ID of the host")
private String podId;
@SerializedName("podname")
@Param(description = "the Pod name of the host")
private String podName;
@SerializedName("version")
@Param(description = "the host version")
private String version;
@SerializedName(ApiConstants.HYPERVISOR)
@Param(description = "the host hypervisor")
private String hypervisor;
@SerializedName("cpunumber")
@Param(description = "the CPU number of the host")
private Integer cpuNumber;
@SerializedName("cpuspeed")
@Param(description = "the CPU speed of the host")
private Long cpuSpeed;
@Deprecated
@SerializedName("cpuallocated")
@Param(description = "the amount of the host's CPU currently allocated")
private String cpuAllocated;
@SerializedName("cpuallocatedvalue")
@Param(description = "the amount of the host's CPU currently allocated in MHz")
private Long cpuAllocatedValue;
@SerializedName("cpuallocatedpercentage")
@Param(description = "the amount of the host's CPU currently allocated in percentage")
private String cpuAllocatedPercentage;
@SerializedName("cpuallocatedwithoverprovisioning")
@Param(description = "the amount of the host's CPU currently allocated after applying the cpu.overprovisioning.factor")
private String cpuAllocatedWithOverprovisioning;
@SerializedName("cpuused")
@Param(description = "the amount of the host's CPU currently used")
private String cpuUsed;
@SerializedName("cpuwithoverprovisioning")
@Param(description = "the amount of the host's CPU after applying the cpu.overprovisioning.factor ")
private String cpuWithOverprovisioning;
@Deprecated
@SerializedName("memorytotal")
@Param(description = "the memory total of the host, this parameter is deprecated use memorywithoverprovisioning")
private Long memoryTotal;
@SerializedName("memorywithoverprovisioning")
@Param(description = "the amount of the host's memory after applying the mem.overprovisioning.factor ")
private String memWithOverprovisioning;
@SerializedName("averageload")
@Param(description = "the cpu average load on the host")
private Long averageLoad;
@SerializedName("networkkbsread")
@Param(description = "the incoming network traffic on the host")
private Long networkKbsRead;
@SerializedName("networkkbswrite")
@Param(description = "the outgoing network traffic on the host")
private Long networkKbsWrite;
@Deprecated
@SerializedName("memoryallocated")
@Param(description = "the amount of the host's memory currently allocated")
private String memoryAllocated;
@SerializedName("memoryallocatedpercentage")
@Param(description = "the amount of the host's memory currently allocated in percentage")
private String memoryAllocatedPercentage;
@SerializedName("memoryallocatedbytes")
@Param(description = "the amount of the host's memory currently allocated in bytes")
private Long memoryAllocatedBytes;
@SerializedName("memoryused")
@Param(description = "the amount of the host's memory currently used")
private Long memoryUsed;
@SerializedName("disksizetotal")
@Param(description = "the total disk size of the host")
private Long diskSizeTotal;
@SerializedName("disksizeallocated")
@Param(description = "the host's currently allocated disk size")
private Long diskSizeAllocated;
@SerializedName("capabilities")
@Param(description = "capabilities of the host")
private String capabilities;
@SerializedName("lastpinged")
@Param(description = "the date and time the host was last pinged")
private Date lastPinged;
@SerializedName("managementserverid")
@Param(description = "the management server ID of the host")
private Long managementServerId;
@SerializedName("clusterid")
@Param(description = "the cluster ID of the host")
private String clusterId;
@SerializedName("clustername")
@Param(description = "the cluster name of the host")
private String clusterName;
@SerializedName("clustertype")
@Param(description = "the cluster type of the cluster that host belongs to")
private String clusterType;
@SerializedName("islocalstorageactive")
@Param(description = "true if local storage is active, false otherwise")
private Boolean localStorageActive;
@SerializedName(ApiConstants.CREATED)
@Param(description = "the date and time the host was created")
private Date created;
@SerializedName("removed")
@Param(description = "the date and time the host was removed")
private Date removed;
@SerializedName("events")
@Param(description = "events available for the host")
private String events;
@SerializedName("hosttags")
@Param(description = "comma-separated list of tags for the host")
private String hostTags;
@SerializedName("explicithosttags")
@Param(description = "comma-separated list of explicit host tags for the host", since = "4.20.0")
private String explicitHostTags;
@SerializedName("implicithosttags")
@Param(description = "comma-separated list of implicit host tags for the host", since = "4.20.0")
private String implicitHostTags;
@SerializedName("hasenoughcapacity")
@Param(description = "true if this host has enough CPU and RAM capacity to migrate a VM to it, false otherwise")
private Boolean hasEnoughCapacity;
@SerializedName("suitableformigration")
@Param(description = "true if this host is suitable(has enough capacity and satisfies all conditions like hosttags, " +
"max guests vm limit etc) to migrate a VM to it , false otherwise")
private Boolean suitableForMigration;
@SerializedName("requiresStorageMotion") @SerializedName("requiresStorageMotion")
@Param(description = "true if migrating a vm to this host requires storage motion, false otherwise") @Param(description = "true if migrating a vm to this host requires storage motion, false otherwise")
private Boolean requiresStorageMotion; private Boolean requiresStorageMotion;
@SerializedName("resourcestate")
@Param(description = "the resource state of the host")
private String resourceState;
@SerializedName(ApiConstants.HYPERVISOR_VERSION)
@Param(description = "the hypervisor version")
private String hypervisorVersion;
@SerializedName(ApiConstants.HA_HOST)
@Param(description = "true if the host is Ha host (dedicated to vms started by HA process; false otherwise")
private Boolean haHost;
@Override
public String getObjectId() {
return getId();
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setState(Status state) {
this.state = state;
}
public void setDisconnectedOn(Date disconnectedOn) {
this.disconnectedOn = disconnectedOn;
}
public void setHostType(Host.Type hostType) {
this.hostType = hostType;
}
public void setOsCategoryId(String osCategoryId) {
this.osCategoryId = osCategoryId;
}
public void setOsCategoryName(String osCategoryName) {
this.osCategoryName = osCategoryName;
}
public void setIpAddress(String ipAddress) {
this.ipAddress = ipAddress;
}
public void setZoneId(String zoneId) {
this.zoneId = zoneId;
}
public void setZoneName(String zoneName) {
this.zoneName = zoneName;
}
public void setPodId(String podId) {
this.podId = podId;
}
public void setPodName(String podName) {
this.podName = podName;
}
public void setVersion(String version) {
this.version = version;
}
public void setHypervisor(String hypervisor) {
this.hypervisor = hypervisor;
}
public void setCpuNumber(Integer cpuNumber) {
this.cpuNumber = cpuNumber;
}
public void setCpuSpeed(Long cpuSpeed) {
this.cpuSpeed = cpuSpeed;
}
public String getCpuAllocated() {
return cpuAllocated;
}
public void setCpuAllocated(String cpuAllocated) {
this.cpuAllocated = cpuAllocated;
}
public void setCpuAllocatedValue(Long cpuAllocatedValue) {
this.cpuAllocatedValue = cpuAllocatedValue;
}
public void setCpuAllocatedPercentage(String cpuAllocatedPercentage) {
this.cpuAllocatedPercentage = cpuAllocatedPercentage;
}
public void setCpuAllocatedWithOverprovisioning(String cpuAllocatedWithOverprovisioning) {
this.cpuAllocatedWithOverprovisioning = cpuAllocatedWithOverprovisioning;
}
public void setCpuUsed(String cpuUsed) {
this.cpuUsed = cpuUsed;
}
public void setAverageLoad(Long averageLoad) {
this.averageLoad = averageLoad;
}
public void setNetworkKbsRead(Long networkKbsRead) {
this.networkKbsRead = networkKbsRead;
}
public void setNetworkKbsWrite(Long networkKbsWrite) {
this.networkKbsWrite = networkKbsWrite;
}
public void setMemoryAllocated(String memoryAllocated) {
this.memoryAllocated = memoryAllocated;
}
public void setMemoryAllocatedPercentage(String memoryAllocatedPercentage) {
this.memoryAllocatedPercentage = memoryAllocatedPercentage;
}
public void setMemoryAllocatedBytes(Long memoryAllocatedBytes) {
this.memoryAllocatedBytes = memoryAllocatedBytes;
}
public void setMemoryUsed(Long memoryUsed) {
this.memoryUsed = memoryUsed;
}
public void setDiskSizeTotal(Long diskSizeTotal) {
this.diskSizeTotal = diskSizeTotal;
}
public void setDiskSizeAllocated(Long diskSizeAllocated) {
this.diskSizeAllocated = diskSizeAllocated;
}
public void setCapabilities(String capabilities) {
this.capabilities = capabilities;
}
public void setLastPinged(Date lastPinged) {
this.lastPinged = lastPinged;
}
public void setManagementServerId(Long managementServerId) {
this.managementServerId = managementServerId;
}
public void setClusterId(String clusterId) {
this.clusterId = clusterId;
}
public void setClusterName(String clusterName) {
this.clusterName = clusterName;
}
public void setClusterType(String clusterType) {
this.clusterType = clusterType;
}
public void setLocalStorageActive(Boolean localStorageActive) {
this.localStorageActive = localStorageActive;
}
public void setCreated(Date created) {
this.created = created;
}
public void setRemoved(Date removed) {
this.removed = removed;
}
public void setEvents(String events) {
this.events = events;
}
public String getHostTags() {
return hostTags;
}
public void setHostTags(String hostTags) {
this.hostTags = hostTags;
}
public void setExplicitHostTags(String explicitHostTags) {
this.explicitHostTags = explicitHostTags;
}
public void setImplicitHostTags(String implicitHostTags) {
this.implicitHostTags = implicitHostTags;
}
public void setHasEnoughCapacity(Boolean hasEnoughCapacity) {
this.hasEnoughCapacity = hasEnoughCapacity;
}
public void setSuitableForMigration(Boolean suitableForMigration) {
this.suitableForMigration = suitableForMigration;
}
public void setRequiresStorageMotion(Boolean requiresStorageMotion) { public void setRequiresStorageMotion(Boolean requiresStorageMotion) {
this.requiresStorageMotion = requiresStorageMotion; this.requiresStorageMotion = requiresStorageMotion;
} }
public String getResourceState() {
return resourceState;
}
public void setResourceState(String resourceState) {
this.resourceState = resourceState;
}
public String getCpuWithOverprovisioning() {
return cpuWithOverprovisioning;
}
public void setCpuWithOverprovisioning(String cpuWithOverprovisioning) {
this.cpuWithOverprovisioning = cpuWithOverprovisioning;
}
public void setMemWithOverprovisioning(String memWithOverprovisioning){
this.memWithOverprovisioning=memWithOverprovisioning;
}
public void setHypervisorVersion(String hypervisorVersion) {
this.hypervisorVersion = hypervisorVersion;
}
public Boolean getHaHost() {
return haHost;
}
public void setHaHost(Boolean haHost) {
this.haHost = haHost;
}
public void setMemoryTotal(Long memoryTotal) {
this.memoryTotal = memoryTotal;
}
} }

View File

@ -54,6 +54,7 @@ import org.apache.cloudstack.managed.context.ManagedContextRunnable;
import org.apache.cloudstack.outofbandmanagement.dao.OutOfBandManagementDao; import org.apache.cloudstack.outofbandmanagement.dao.OutOfBandManagementDao;
import org.apache.cloudstack.utils.identity.ManagementServerNode; import org.apache.cloudstack.utils.identity.ManagementServerNode;
import org.apache.commons.collections.MapUtils; import org.apache.commons.collections.MapUtils;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
import com.cloud.agent.AgentManager; import com.cloud.agent.AgentManager;
@ -618,30 +619,25 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl
logger.debug("Sending Connect to listener: {}", monitor.second().getClass().getSimpleName()); logger.debug("Sending Connect to listener: {}", monitor.second().getClass().getSimpleName());
for (int i = 0; i < cmd.length; i++) { for (int i = 0; i < cmd.length; i++) {
try { try {
logger.debug("process connection to issue {} forRebalance == {}", ReflectionToStringBuilderUtils.reflectCollection(cmd[i]), forRebalance);
monitor.second().processConnect(host, cmd[i], forRebalance); monitor.second().processConnect(host, cmd[i], forRebalance);
} catch (final Exception e) { } catch (final ConnectionException ce) {
if (e instanceof ConnectionException) {
final ConnectionException ce = (ConnectionException)e;
if (ce.isSetupError()) { if (ce.isSetupError()) {
logger.warn("Monitor {} says there is an error in the connect process for {} due to {}", logger.warn("Monitor {} says there is an error in the connect process for {} due to {}", monitor.second().getClass().getSimpleName(), hostId, ce.getMessage());
monitor.second().getClass().getSimpleName(), host, e.getMessage());
handleDisconnectWithoutInvestigation(attache, Event.AgentDisconnected, true, true); handleDisconnectWithoutInvestigation(attache, Event.AgentDisconnected, true, true);
throw ce; throw ce;
} else { } else {
logger.info("Monitor {} says not to continue the connect process for {} due to {}", logger.info("Monitor {} says not to continue the connect process for {} due to {}", monitor.second().getClass().getSimpleName(), hostId, ce.getMessage());
monitor.second().getClass().getSimpleName(), host, e.getMessage());
handleDisconnectWithoutInvestigation(attache, Event.ShutdownRequested, true, true); handleDisconnectWithoutInvestigation(attache, Event.ShutdownRequested, true, true);
return attache; return attache;
} }
} else if (e instanceof HypervisorVersionChangedException) { } catch (final HypervisorVersionChangedException hvce) {
handleDisconnectWithoutInvestigation(attache, Event.ShutdownRequested, true, true); handleDisconnectWithoutInvestigation(attache, Event.ShutdownRequested, true, true);
throw new CloudRuntimeException(String.format("Unable to connect %s", attache), e); throw new CloudRuntimeException("Unable to connect " + attache.getId(), hvce);
} else { } catch (final Exception e) {
logger.error("Monitor {} says there is an error in the connect process for {} due to {}", logger.error("Monitor {} says there is an error in the connect process for {} due to {}", monitor.second().getClass().getSimpleName(), hostId, e.getMessage(), e);
monitor.second().getClass().getSimpleName(), host, e.getMessage(), e);
handleDisconnectWithoutInvestigation(attache, Event.AgentDisconnected, true, true); handleDisconnectWithoutInvestigation(attache, Event.AgentDisconnected, true, true);
throw new CloudRuntimeException(String.format("Unable to connect %s", attache), e); throw new CloudRuntimeException("Unable to connect " + attache.getId(), e);
}
} }
} }
} }

View File

@ -437,7 +437,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
protected static final String LOCAL_STORAGE_PATH = "local.storage.path"; protected static final String LOCAL_STORAGE_PATH = "local.storage.path";
protected static final String LOCAL_STORAGE_UUID = "local.storage.uuid"; protected static final String LOCAL_STORAGE_UUID = "local.storage.uuid";
protected static final String DEFAULT_LOCAL_STORAGE_PATH = "/var/lib/libvirt/images/"; public static final String DEFAULT_LOCAL_STORAGE_PATH = "/var/lib/libvirt/images";
protected List<String> localStoragePaths = new ArrayList<>(); protected List<String> localStoragePaths = new ArrayList<>();
protected List<String> localStorageUUIDs = new ArrayList<>(); protected List<String> localStorageUUIDs = new ArrayList<>();
@ -2661,7 +2661,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
Map<String, String> details = vmTO.getDetails(); Map<String, String> details = vmTO.getDetails();
boolean isIothreadsEnabled = details != null && details.containsKey(VmDetailConstants.IOTHREADS); boolean isIothreadsEnabled = details != null && details.containsKey(VmDetailConstants.IOTHREADS);
devices.addDevice(createSCSIDef(vcpus, isIothreadsEnabled)); addSCSIControllers(devices, vcpus, vmTO.getDisks().length, isIothreadsEnabled);
} }
return devices; return devices;
} }
@ -2699,8 +2699,19 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
* Creates Virtio SCSI controller. <br> * Creates Virtio SCSI controller. <br>
* The respective Virtio SCSI XML definition is generated only if the VM's Disk Bus is of ISCSI. * The respective Virtio SCSI XML definition is generated only if the VM's Disk Bus is of ISCSI.
*/ */
protected SCSIDef createSCSIDef(int vcpus, boolean isIothreadsEnabled) { protected SCSIDef createSCSIDef(short index, int vcpus, boolean isIothreadsEnabled) {
return new SCSIDef((short)0, 0, 0, 9, 0, vcpus, isIothreadsEnabled); return new SCSIDef(index, 0, 0, 9 + index, 0, vcpus, isIothreadsEnabled);
}
private void addSCSIControllers(DevicesDef devices, int vcpus, int diskCount, boolean isIothreadsEnabled) {
int controllers = diskCount / 7;
if (diskCount % 7 != 0) {
controllers++;
}
for (int i = 0; i < controllers; i++) {
devices.addDevice(createSCSIDef((short)i, vcpus, isIothreadsEnabled));
}
} }
protected ConsoleDef createConsoleDef() { protected ConsoleDef createConsoleDef() {

View File

@ -22,12 +22,20 @@ package com.cloud.hypervisor.kvm.resource.wrapper;
import com.cloud.agent.api.Answer; import com.cloud.agent.api.Answer;
import com.cloud.agent.api.DeleteStoragePoolCommand; import com.cloud.agent.api.DeleteStoragePoolCommand;
import com.cloud.agent.api.to.StorageFilerTO; import com.cloud.agent.api.to.StorageFilerTO;
import com.cloud.agent.dao.impl.PropertiesStorage;
import com.cloud.agent.properties.AgentProperties;
import com.cloud.agent.properties.AgentPropertiesFileHandler;
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager; import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager;
import com.cloud.resource.CommandWrapper; import com.cloud.resource.CommandWrapper;
import com.cloud.resource.ResourceWrapper; import com.cloud.resource.ResourceWrapper;
import com.cloud.storage.Storage;
import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.CloudRuntimeException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.stream.Collectors;
@ResourceWrapper(handles = DeleteStoragePoolCommand.class) @ResourceWrapper(handles = DeleteStoragePoolCommand.class)
public final class LibvirtDeleteStoragePoolCommandWrapper extends CommandWrapper<DeleteStoragePoolCommand, Answer, LibvirtComputingResource> { public final class LibvirtDeleteStoragePoolCommandWrapper extends CommandWrapper<DeleteStoragePoolCommand, Answer, LibvirtComputingResource> {
@Override @Override
@ -35,15 +43,57 @@ public final class LibvirtDeleteStoragePoolCommandWrapper extends CommandWrapper
try { try {
// if getRemoveDatastore() is true, then we are dealing with managed storage and can skip the delete logic here // if getRemoveDatastore() is true, then we are dealing with managed storage and can skip the delete logic here
if (!command.getRemoveDatastore()) { if (!command.getRemoveDatastore()) {
final StorageFilerTO pool = command.getPool(); handleStoragePoolDeletion(command, libvirtComputingResource);
final KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr();
storagePoolMgr.deleteStoragePool(pool.getType(), pool.getUuid());
} }
return new Answer(command); return new Answer(command);
} catch (final CloudRuntimeException e) { } catch (final CloudRuntimeException e) {
return new Answer(command, false, e.toString()); return new Answer(command, false, e.toString());
} }
} }
private void handleStoragePoolDeletion(final DeleteStoragePoolCommand command, final LibvirtComputingResource libvirtComputingResource) {
final StorageFilerTO pool = command.getPool();
final KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr();
storagePoolMgr.deleteStoragePool(pool.getType(), pool.getUuid());
if (isLocalStorageAndNotHavingDefaultPath(pool, libvirtComputingResource)) {
updateLocalStorageProperties(pool);
}
}
private boolean isLocalStorageAndNotHavingDefaultPath(final StorageFilerTO pool, final LibvirtComputingResource libvirtComputingResource) {
return Storage.StoragePoolType.Filesystem.equals(pool.getType())
&& !libvirtComputingResource.DEFAULT_LOCAL_STORAGE_PATH.equals(pool.getPath());
}
private void updateLocalStorageProperties(final StorageFilerTO pool) {
String localStoragePath = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.LOCAL_STORAGE_PATH);
String localStorageUuid = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.LOCAL_STORAGE_UUID);
String uuidToRemove = pool.getUuid();
String pathToRemove = pool.getPath();
if (localStorageUuid != null && uuidToRemove != null) {
localStorageUuid = Arrays.stream(localStorageUuid.split(","))
.filter(uuid -> !uuid.equals(uuidToRemove))
.collect(Collectors.joining(","));
}
if (localStoragePath != null && pathToRemove != null) {
localStoragePath = Arrays.stream(localStoragePath.split(","))
.filter(path -> !path.equals(pathToRemove))
.collect(Collectors.joining(","));
}
PropertiesStorage agentProperties = new PropertiesStorage();
agentProperties.configure("AgentProperties", new HashMap<String, Object>());
if (localStorageUuid != null) {
agentProperties.persist(AgentProperties.LOCAL_STORAGE_UUID.getName(), localStorageUuid);
}
if (localStoragePath != null) {
agentProperties.persist(AgentProperties.LOCAL_STORAGE_PATH.getName(), localStoragePath);
}
}
} }

View File

@ -461,6 +461,9 @@ public class LibvirtComputingResourceTest {
to.setDetails(new HashMap<>()); to.setDetails(new HashMap<>());
to.setPlatformEmulator("Other PV Virtio-SCSI"); to.setPlatformEmulator("Other PV Virtio-SCSI");
final DiskTO diskTO = Mockito.mock(DiskTO.class);
to.setDisks(new DiskTO[]{diskTO});
GuestDef guest = new GuestDef(); GuestDef guest = new GuestDef();
guest.setGuestType(GuestType.KVM); guest.setGuestType(GuestType.KVM);
@ -648,7 +651,7 @@ public class LibvirtComputingResourceTest {
public void testCreateSCSIDef() { public void testCreateSCSIDef() {
VirtualMachineTO to = createDefaultVM(false); VirtualMachineTO to = createDefaultVM(false);
SCSIDef scsiDef = libvirtComputingResourceSpy.createSCSIDef(to.getCpus(), false); SCSIDef scsiDef = libvirtComputingResourceSpy.createSCSIDef((short)0, to.getCpus(), false);
Document domainDoc = parse(scsiDef.toString()); Document domainDoc = parse(scsiDef.toString());
verifyScsi(to, domainDoc, ""); verifyScsi(to, domainDoc, "");
} }

View File

@ -568,7 +568,7 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
if (template != null) { if (template != null) {
response.setTemplateId(template.getUuid()); response.setTemplateId(template.getUuid());
} }
ServiceOfferingVO offering = serviceOfferingDao.findById(kubernetesCluster.getServiceOfferingId()); ServiceOfferingVO offering = serviceOfferingDao.findByIdIncludingRemoved(kubernetesCluster.getServiceOfferingId());
if (offering != null) { if (offering != null) {
response.setServiceOfferingId(offering.getUuid()); response.setServiceOfferingId(offering.getUuid());
response.setServiceOfferingName(offering.getName()); response.setServiceOfferingName(offering.getName());

View File

@ -113,9 +113,7 @@ public class HostJoinDaoImpl extends GenericDaoBase<HostJoinVO, Long> implements
return result; return result;
} }
@Override private void setNewHostResponseBase(HostJoinVO host, EnumSet<HostDetails> details, HostResponse hostResponse) {
public HostResponse newHostResponse(HostJoinVO host, EnumSet<HostDetails> details) {
HostResponse hostResponse = new HostResponse();
hostResponse.setId(host.getUuid()); hostResponse.setId(host.getUuid());
hostResponse.setCapabilities(host.getCapabilities()); hostResponse.setCapabilities(host.getCapabilities());
hostResponse.setClusterId(host.getClusterUuid()); hostResponse.setClusterId(host.getClusterUuid());
@ -187,7 +185,6 @@ public class HostJoinDaoImpl extends GenericDaoBase<HostJoinVO, Long> implements
DecimalFormat decimalFormat = new DecimalFormat("#.##"); DecimalFormat decimalFormat = new DecimalFormat("#.##");
if (host.getType() == Host.Type.Routing) { if (host.getType() == Host.Type.Routing) {
float cpuOverprovisioningFactor = ApiDBUtils.getCpuOverprovisioningFactor(host.getClusterId()); float cpuOverprovisioningFactor = ApiDBUtils.getCpuOverprovisioningFactor(host.getClusterId());
hostResponse.setCpuNumber((int)(host.getCpus() * cpuOverprovisioningFactor));
if (details.contains(HostDetails.all) || details.contains(HostDetails.capacity)) { if (details.contains(HostDetails.all) || details.contains(HostDetails.capacity)) {
// set allocated capacities // set allocated capacities
Long mem = host.getMemReservedCapacity() + host.getMemUsedCapacity(); Long mem = host.getMemReservedCapacity() + host.getMemUsedCapacity();
@ -298,124 +295,19 @@ public class HostJoinDaoImpl extends GenericDaoBase<HostJoinVO, Long> implements
hostResponse.setUsername(host.getUsername()); hostResponse.setUsername(host.getUsername());
hostResponse.setObjectName("host"); hostResponse.setObjectName("host");
}
@Override
public HostResponse newHostResponse(HostJoinVO host, EnumSet<HostDetails> details) {
HostResponse hostResponse = new HostResponse();
setNewHostResponseBase(host, details, hostResponse);
return hostResponse; return hostResponse;
} }
@Override @Override
public HostForMigrationResponse newHostForMigrationResponse(HostJoinVO host, EnumSet<HostDetails> details) { public HostForMigrationResponse newHostForMigrationResponse(HostJoinVO host, EnumSet<HostDetails> details) {
HostForMigrationResponse hostResponse = new HostForMigrationResponse(); HostForMigrationResponse hostResponse = new HostForMigrationResponse();
hostResponse.setId(host.getUuid()); setNewHostResponseBase(host, details, hostResponse);
hostResponse.setCapabilities(host.getCapabilities());
hostResponse.setClusterId(host.getClusterUuid());
hostResponse.setCpuNumber(host.getCpus());
hostResponse.setZoneId(host.getZoneUuid());
hostResponse.setDisconnectedOn(host.getDisconnectedOn());
hostResponse.setHypervisor(host.getHypervisorType().getHypervisorDisplayName());
hostResponse.setHostType(host.getType());
hostResponse.setLastPinged(new Date(host.getLastPinged()));
hostResponse.setManagementServerId(host.getManagementServerId());
hostResponse.setName(host.getName());
hostResponse.setPodId(host.getPodUuid());
hostResponse.setRemoved(host.getRemoved());
hostResponse.setCpuSpeed(host.getSpeed());
hostResponse.setState(host.getStatus());
hostResponse.setIpAddress(host.getPrivateIpAddress());
hostResponse.setVersion(host.getVersion());
hostResponse.setCreated(host.getCreated());
if (details.contains(HostDetails.all) || details.contains(HostDetails.capacity) || details.contains(HostDetails.stats) || details.contains(HostDetails.events)) {
hostResponse.setOsCategoryId(host.getOsCategoryUuid());
hostResponse.setOsCategoryName(host.getOsCategoryName());
hostResponse.setZoneName(host.getZoneName());
hostResponse.setPodName(host.getPodName());
if (host.getClusterId() > 0) {
hostResponse.setClusterName(host.getClusterName());
hostResponse.setClusterType(host.getClusterType().toString());
}
}
DecimalFormat decimalFormat = new DecimalFormat("#.##");
if (host.getType() == Host.Type.Routing) {
if (details.contains(HostDetails.all) || details.contains(HostDetails.capacity)) {
// set allocated capacities
Long mem = host.getMemReservedCapacity() + host.getMemUsedCapacity();
Long cpu = host.getCpuReservedCapacity() + host.getCpuUsedCapacity();
hostResponse.setMemoryTotal(host.getTotalMemory());
Float memWithOverprovisioning = host.getTotalMemory() * ApiDBUtils.getMemOverprovisioningFactor(host.getClusterId());
hostResponse.setMemWithOverprovisioning(decimalFormat.format(memWithOverprovisioning));
String memoryAllocatedPercentage = decimalFormat.format((float) mem / memWithOverprovisioning * 100.0f) +"%";
hostResponse.setMemoryAllocated(memoryAllocatedPercentage);
hostResponse.setMemoryAllocatedPercentage(memoryAllocatedPercentage);
hostResponse.setMemoryAllocatedBytes(mem);
String hostTags = host.getTag();
hostResponse.setHostTags(hostTags);
hostResponse.setHaHost(containsHostHATag(hostTags));
hostResponse.setImplicitHostTags(host.getImplicitTag());
hostResponse.setHypervisorVersion(host.getHypervisorVersion());
hostResponse.setCpuAllocatedValue(cpu);
String cpuAlloc = decimalFormat.format(((float)cpu / (float)(host.getCpus() * host.getSpeed())) * 100f) + "%";
hostResponse.setCpuAllocated(cpuAlloc);
hostResponse.setCpuAllocatedPercentage(cpuAlloc);
float cpuWithOverprovisioning = host.getCpus() * host.getSpeed() * ApiDBUtils.getCpuOverprovisioningFactor(host.getClusterId());
hostResponse.setCpuAllocatedWithOverprovisioning(calculateResourceAllocatedPercentage(cpu, cpuWithOverprovisioning));
hostResponse.setCpuWithOverprovisioning(decimalFormat.format(cpuWithOverprovisioning));
}
if (details.contains(HostDetails.all) || details.contains(HostDetails.stats)) {
// set CPU/RAM/Network stats
String cpuUsed = null;
HostStats hostStats = ApiDBUtils.getHostStatistics(host.getId());
if (hostStats != null) {
float cpuUtil = (float)hostStats.getCpuUtilization();
cpuUsed = decimalFormat.format(cpuUtil) + "%";
hostResponse.setCpuUsed(cpuUsed);
hostResponse.setMemoryUsed((new Double(hostStats.getUsedMemory())).longValue());
hostResponse.setNetworkKbsRead((new Double(hostStats.getNetworkReadKBs())).longValue());
hostResponse.setNetworkKbsWrite((new Double(hostStats.getNetworkWriteKBs())).longValue());
}
}
} else if (host.getType() == Host.Type.SecondaryStorage) {
StorageStats secStorageStats = ApiDBUtils.getSecondaryStorageStatistics(host.getId());
if (secStorageStats != null) {
hostResponse.setDiskSizeTotal(secStorageStats.getCapacityBytes());
hostResponse.setDiskSizeAllocated(secStorageStats.getByteUsed());
}
}
hostResponse.setLocalStorageActive(ApiDBUtils.isLocalStorageActiveOnHost(host.getId()));
if (details.contains(HostDetails.all) || details.contains(HostDetails.events)) {
Set<com.cloud.host.Status.Event> possibleEvents = host.getStatus().getPossibleEvents();
if ((possibleEvents != null) && !possibleEvents.isEmpty()) {
String events = "";
Iterator<com.cloud.host.Status.Event> iter = possibleEvents.iterator();
while (iter.hasNext()) {
com.cloud.host.Status.Event event = iter.next();
events += event.toString();
if (iter.hasNext()) {
events += "; ";
}
}
hostResponse.setEvents(events);
}
}
hostResponse.setResourceState(host.getResourceState().toString());
// set async job
hostResponse.setJobId(host.getJobUuid());
hostResponse.setJobStatus(host.getJobStatus());
hostResponse.setObjectName("host");
return hostResponse; return hostResponse;
} }

View File

@ -808,7 +808,9 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
if (!(dc.isLocalStorageEnabled() || useLocalStorageForSystemVM)) { if (!(dc.isLocalStorageEnabled() || useLocalStorageForSystemVM)) {
return null; return null;
} }
DataStore store; DataStore store = null;
DataStoreProvider provider = _dataStoreProviderMgr.getDefaultPrimaryDataStoreProvider();
DataStoreLifeCycle lifeCycle = provider.getDataStoreLifeCycle();
try { try {
String hostAddress = pInfo.getHost(); String hostAddress = pInfo.getHost();
if (host.getHypervisorType() == Hypervisor.HypervisorType.VMware) { if (host.getHypervisorType() == Hypervisor.HypervisorType.VMware) {
@ -834,8 +836,6 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
} }
} }
DataStoreProvider provider = _dataStoreProviderMgr.getDefaultPrimaryDataStoreProvider();
DataStoreLifeCycle lifeCycle = provider.getDataStoreLifeCycle();
if (pool == null) { if (pool == null) {
Map<String, Object> params = new HashMap<>(); Map<String, Object> params = new HashMap<>();
String name = pInfo.getName() != null ? pInfo.getName() : createLocalStoragePoolName(host, pInfo); String name = pInfo.getName() != null ? pInfo.getName() : createLocalStoragePoolName(host, pInfo);
@ -864,7 +864,15 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
} }
} catch (Exception e) { } catch (Exception e) {
logger.warn("Unable to setup the local storage pool for " + host, e); logger.warn("Unable to setup the local storage pool for {}", host, e);
try {
if (store != null) {
logger.debug("Trying to delete storage pool entry if exists {}", store);
lifeCycle.deleteDataStore(store);
}
} catch (Exception ex) {
logger.debug("Failed to clean up local storage pool: {}", ex.getMessage());
}
throw new ConnectionException(true, "Unable to setup the local storage pool for " + host, e); throw new ConnectionException(true, "Unable to setup the local storage pool for " + host, e);
} }

View File

@ -2315,6 +2315,8 @@
"label.traffictype": "Traffic type", "label.traffictype": "Traffic type",
"label.transportzoneuuid": "Transport zone UUID", "label.transportzoneuuid": "Transport zone UUID",
"label.trigger.shutdown": "Trigger Safe Shutdown", "label.trigger.shutdown": "Trigger Safe Shutdown",
"label.true": "True",
"label.false": "False",
"label.try.again": "Try again", "label.try.again": "Try again",
"label.tuesday": "Tuesday", "label.tuesday": "Tuesday",
"label.two.factor.authentication.secret.key": "Your Two factor authentication secret key", "label.two.factor.authentication.secret.key": "Your Two factor authentication secret key",

View File

@ -87,6 +87,9 @@
<router-link :to="{ path: $route.path + '/' + record.uuid, query: { zoneid: $route.query.zoneid } }" v-else-if="record.uuid && $route.query.zoneid">{{ $t(text.toLowerCase()) }}</router-link> <router-link :to="{ path: $route.path + '/' + record.uuid, query: { zoneid: $route.query.zoneid } }" v-else-if="record.uuid && $route.query.zoneid">{{ $t(text.toLowerCase()) }}</router-link>
<router-link :to="{ path: $route.path }" v-else>{{ $t(text.toLowerCase()) }}</router-link> <router-link :to="{ path: $route.path }" v-else>{{ $t(text.toLowerCase()) }}</router-link>
</span> </span>
<span v-else-if="$route.path.startsWith('/guestnetwork') && record.id && record.displaynetwork === false">
<router-link :to="{ path: $route.path + '/' + record.id, query: { displaynetwork: false } }" v-if="record.id">{{ $t(text.toLowerCase()) }}</router-link>
</span>
<span v-else> <span v-else>
<router-link :to="{ path: $route.path + '/' + record.id }" v-if="record.id">{{ text }}</router-link> <router-link :to="{ path: $route.path + '/' + record.id }" v-if="record.id">{{ text }}</router-link>
<router-link :to="{ path: $route.path + '/' + record.name }" v-else>{{ text }}</router-link> <router-link :to="{ path: $route.path + '/' + record.name }" v-else>{{ text }}</router-link>
@ -231,6 +234,10 @@
<template v-if="column.key === 'allocationstate'"> <template v-if="column.key === 'allocationstate'">
<status :text="text ? text : ''" displayText /> <status :text="text ? text : ''" displayText />
</template> </template>
<template v-if="column.key === 'redundantstate'">
<status v-if="record && record.isredundantrouter" :text="text ? text : ''" displayText />
<status v-else :text="'N/A'" displayText :styles="{ 'min-width': '80px' }" />
</template>
<template v-if="column.key === 'resourcestate'"> <template v-if="column.key === 'resourcestate'">
<status :text="text ? text : ''" displayText /> <status :text="text ? text : ''" displayText />
</template> </template>

View File

@ -311,9 +311,12 @@ export default {
if (item === 'isencrypted' && !('listVolumes' in this.$store.getters.apis)) { if (item === 'isencrypted' && !('listVolumes' in this.$store.getters.apis)) {
return true return true
} }
if (item === 'displaynetwork' && this.$store.getters.userInfo.roletype !== 'Admin') {
return true
}
if (['zoneid', 'domainid', 'imagestoreid', 'storageid', 'state', 'account', 'hypervisor', 'level', if (['zoneid', 'domainid', 'imagestoreid', 'storageid', 'state', 'account', 'hypervisor', 'level',
'clusterid', 'podid', 'groupid', 'entitytype', 'accounttype', 'systemvmtype', 'scope', 'provider', 'clusterid', 'podid', 'groupid', 'entitytype', 'accounttype', 'systemvmtype', 'scope', 'provider',
'type', 'scope', 'managementserverid', 'serviceofferingid', 'diskofferingid', 'networkid', 'usagetype', 'restartrequired'].includes(item) 'type', 'scope', 'managementserverid', 'serviceofferingid', 'diskofferingid', 'networkid', 'usagetype', 'restartrequired', 'displaynetwork'].includes(item)
) { ) {
type = 'list' type = 'list'
} else if (item === 'tags') { } else if (item === 'tags') {
@ -335,6 +338,12 @@ export default {
return arrayField return arrayField
}, },
fetchStaticFieldData (arrayField) { fetchStaticFieldData (arrayField) {
if (arrayField.includes('displaynetwork')) {
const typeIndex = this.fields.findIndex(item => item.name === 'displaynetwork')
this.fields[typeIndex].loading = true
this.fields[typeIndex].opts = this.fetchBoolean()
this.fields[typeIndex].loading = false
}
if (arrayField.includes('type')) { if (arrayField.includes('type')) {
if (this.$route.path === '/guestnetwork' || this.$route.path.includes('/guestnetwork/')) { if (this.$route.path === '/guestnetwork' || this.$route.path.includes('/guestnetwork/')) {
const typeIndex = this.fields.findIndex(item => item.name === 'type') const typeIndex = this.fields.findIndex(item => item.name === 'type')
@ -1009,6 +1018,18 @@ export default {
} }
return types return types
}, },
fetchBoolean () {
const types = []
types.push({
id: 'true',
name: 'label.true'
})
types.push({
id: 'false',
name: 'label.false'
})
return types
},
fetchAccountTypes () { fetchAccountTypes () {
const types = [] const types = []
if (this.apiName.indexOf('listAccounts') > -1) { if (this.apiName.indexOf('listAccounts') > -1) {

View File

@ -119,6 +119,7 @@ export default {
case 'up': case 'up':
case 'success': case 'success':
case 'poweron': case 'poweron':
case 'primary':
status = 'success' status = 'success'
break break
case 'alert': case 'alert':
@ -155,6 +156,7 @@ export default {
case 'pending': case 'pending':
case 'unsecure': case 'unsecure':
case 'warning': case 'warning':
case 'backup':
status = 'warning' status = 'warning'
break break
} }

View File

@ -47,14 +47,14 @@ export default {
return fields return fields
}, },
details: () => { details: () => {
var fields = ['name', 'id', 'description', 'type', 'traffictype', 'vpcid', 'vlan', 'broadcasturi', 'cidr', 'ip6cidr', 'netmask', 'gateway', 'asnumber', 'aclname', 'ispersistent', 'restartrequired', 'reservediprange', 'redundantrouter', 'networkdomain', 'egressdefaultpolicy', 'zonename', 'account', 'domainpath', 'associatednetwork', 'associatednetworkid', 'ip4routing', 'ip6routing', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2', 'publicmtu', 'privatemtu'] var fields = ['name', 'id', 'description', 'type', 'traffictype', 'vpcid', 'vlan', 'broadcasturi', 'cidr', 'ip6cidr', 'netmask', 'gateway', 'aclname', 'ispersistent', 'restartrequired', 'reservediprange', 'redundantrouter', 'networkdomain', 'egressdefaultpolicy', 'zonename', 'account', 'domainpath', 'associatednetwork', 'associatednetworkid', 'ip6firewall', 'ip6routing', 'ip6routes', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2', 'publicmtu', 'privatemtu']
if (!isAdmin()) { if (!isAdmin()) {
fields = fields.filter(function (e) { return e !== 'broadcasturi' }) fields = fields.filter(function (e) { return e !== 'broadcasturi' })
} }
return fields return fields
}, },
filters: ['all', 'account', 'domainpath', 'shared'], filters: ['all', 'account', 'domainpath', 'shared'],
searchFilters: ['keyword', 'zoneid', 'domainid', 'account', 'type', 'restartrequired', 'tags'], searchFilters: ['keyword', 'zoneid', 'domainid', 'account', 'type', 'restartrequired', 'displaynetwork', 'tags'],
related: [{ related: [{
name: 'vm', name: 'vm',
title: 'label.instances', title: 'label.instances',
@ -66,15 +66,7 @@ export default {
}, { }, {
name: 'egress.rules', name: 'egress.rules',
component: shallowRef(defineAsyncComponent(() => import('@/views/network/EgressRulesTab.vue'))), component: shallowRef(defineAsyncComponent(() => import('@/views/network/EgressRulesTab.vue'))),
show: (record, route, user) => { return record.type === 'Isolated' && !record.ip4routing && !('vpcname' in record) && 'listEgressFirewallRules' in store.getters.apis && (['Admin', 'DomainAdmin'].includes(user.roletype) || record.account === user.account || record.projectid) } show: (record, route, user) => { return record.type === 'Isolated' && !('vpcname' in record) && 'listEgressFirewallRules' in store.getters.apis && (['Admin', 'DomainAdmin'].includes(user.roletype) || record.account === user.account || record.projectid) }
}, {
name: 'bgp.peers',
component: shallowRef(defineAsyncComponent(() => import('@/views/infra/zone/BgpPeersTab.vue'))),
show: (record, route, user) => { return !record.vpcid && ['Admin'].includes(user.roletype) && record.ip4routing === 'Dynamic' }
}, {
name: 'routing.firewall',
component: shallowRef(defineAsyncComponent(() => import('@/views/network/RoutingFirewallRulesTab.vue'))),
show: (record, route, user) => { return record.type === 'Isolated' && record.ip4routing && !('vpcname' in record) && 'listRoutingFirewallRules' in store.getters.apis && (['Admin', 'DomainAdmin'].includes(user.roletype) || record.account === user.account || record.projectid) }
}, { }, {
name: 'ip.v6.firewall', name: 'ip.v6.firewall',
component: shallowRef(defineAsyncComponent(() => import('@/views/network/Ipv6FirewallRulesTab.vue'))), component: shallowRef(defineAsyncComponent(() => import('@/views/network/Ipv6FirewallRulesTab.vue'))),
@ -82,7 +74,7 @@ export default {
}, { }, {
name: (record) => { return record.type === 'Shared' ? 'ip.addresses' : 'public.ip.addresses' }, name: (record) => { return record.type === 'Shared' ? 'ip.addresses' : 'public.ip.addresses' },
component: shallowRef(defineAsyncComponent(() => import('@/views/network/IpAddressesTab.vue'))), component: shallowRef(defineAsyncComponent(() => import('@/views/network/IpAddressesTab.vue'))),
show: (record, route, user) => { return 'listPublicIpAddresses' in store.getters.apis && (record.type === 'Shared' || (record.type === 'Isolated' && !record.ip4routing && !('vpcname' in record) && (['Admin', 'DomainAdmin'].includes(user.roletype) || record.account === user.account || record.projectid))) } show: (record, route, user) => { return 'listPublicIpAddresses' in store.getters.apis && (record.type === 'Shared' || (record.type === 'Isolated' && !('vpcname' in record) && (['Admin', 'DomainAdmin'].includes(user.roletype) || record.account === user.account || record.projectid))) }
}, { }, {
name: 'virtual.routers', name: 'virtual.routers',
component: shallowRef(defineAsyncComponent(() => import('@/views/network/RoutersTab.vue'))), component: shallowRef(defineAsyncComponent(() => import('@/views/network/RoutersTab.vue'))),
@ -148,9 +140,7 @@ export default {
icon: 'edit-outlined', icon: 'edit-outlined',
label: 'label.update.network', label: 'label.update.network',
dataView: true, dataView: true,
disabled: (record, user) => { disabled: (record, user) => { return (record.account !== user.userInfo.account && !['Admin', 'DomainAdmin'].includes(user.userInfo.roletype)) },
return !record.projectid && (record.account !== user.userInfo.account && !['Admin', 'DomainAdmin'].includes(user.userInfo.roletype))
},
popup: true, popup: true,
component: shallowRef(defineAsyncComponent(() => import('@/views/network/UpdateNetwork.vue'))) component: shallowRef(defineAsyncComponent(() => import('@/views/network/UpdateNetwork.vue')))
}, },
@ -160,9 +150,7 @@ export default {
label: 'label.restart.network', label: 'label.restart.network',
message: 'message.restart.network', message: 'message.restart.network',
dataView: true, dataView: true,
disabled: (record, user) => { disabled: (record, user) => { return (record.account !== user.userInfo.account && !['Admin', 'DomainAdmin'].includes(user.userInfo.roletype)) },
return !record.projectid && (record.account !== user.userInfo.account && !['Admin', 'DomainAdmin'].includes(user.userInfo.roletype))
},
args: (record, store, isGroupAction) => { args: (record, store, isGroupAction) => {
var fields = [] var fields = []
if (isGroupAction || record.vpcid == null) { if (isGroupAction || record.vpcid == null) {
@ -201,9 +189,7 @@ export default {
label: 'label.action.delete.network', label: 'label.action.delete.network',
message: 'message.action.delete.network', message: 'message.action.delete.network',
dataView: true, dataView: true,
disabled: (record, user) => { disabled: (record, user) => { return (record.account !== user.userInfo.account && !['Admin', 'DomainAdmin'].includes(user.userInfo.roletype)) },
return !record.projectid && (record.account !== user.userInfo.account && !['Admin', 'DomainAdmin'].includes(user.userInfo.roletype))
},
groupAction: true, groupAction: true,
popup: true, popup: true,
groupMap: (selection) => { return selection.map(x => { return { id: x } }) } groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
@ -225,8 +211,8 @@ export default {
fields.push(...['domain', 'zonename']) fields.push(...['domain', 'zonename'])
return fields return fields
}, },
details: ['name', 'id', 'displaytext', 'cidr', 'networkdomain', 'ip4routing', 'ip4routes', 'ip6routes', 'ispersistent', 'redundantvpcrouter', 'restartrequired', 'zonename', 'account', 'domain', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2', 'publicmtu'], details: ['name', 'id', 'displaytext', 'cidr', 'networkdomain', 'ip6routes', 'ispersistent', 'redundantvpcrouter', 'restartrequired', 'zonename', 'account', 'domain', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2', 'publicmtu'],
searchFilters: ['name', 'zoneid', 'domainid', 'account', 'restartrequired', 'tags'], searchFilters: ['name', 'zoneid', 'domainid', 'account', 'tags'],
related: [{ related: [{
name: 'vm', name: 'vm',
title: 'label.instances', title: 'label.instances',
@ -315,7 +301,10 @@ export default {
return false return false
} }
const listZoneHaveSGEnabled = store.getters.zones.filter(zone => zone.securitygroupsenabled === true) const listZoneHaveSGEnabled = store.getters.zones.filter(zone => zone.securitygroupsenabled === true)
return (listZoneHaveSGEnabled && listZoneHaveSGEnabled.length > 0) || store.getters.showSecurityGroups if (!listZoneHaveSGEnabled || listZoneHaveSGEnabled.length === 0) {
return false
}
return true
}, },
actions: [ actions: [
{ {
@ -866,54 +855,6 @@ export default {
} }
] ]
}, },
{
name: 'asnumbers',
title: 'label.asnumbers',
icon: 'partition-outlined',
permission: ['listASNumbers'],
show: () => {
if (!store.getters.zones || store.getters.zones.length === 0) {
return false
}
const AdvancedZonesWithRoutedmode = store.getters.zones.filter(zone => zone.routedmodeenabled)
if (isAdmin() && (AdvancedZonesWithRoutedmode && AdvancedZonesWithRoutedmode.length > 0)) {
return true
}
return false
},
filters: ['all', 'allocatedonly', 'free'],
columns: ['asnumber', 'allocationstate', 'asnrange', 'associatednetworkname', 'vpcname', 'allocated', 'account', 'domain', 'zonename'],
searchFilters: ['zoneid', 'associatednetworkid', 'account', 'domainid'],
resourceType: 'ASNumber',
actions: [
{
api: 'releaseASNumber',
icon: 'delete-outlined',
label: 'label.action.release.asnumber',
message: 'message.action.release.asnumber',
show: (record) => { return record.allocationstate === 'Allocated' },
args: ['zoneid', 'asnumber'],
mapping: {
zoneid: {
value: (record) => { return record.zoneid }
},
asnumber: {
value: (record) => { return record.asnumber }
}
},
dataView: true,
groupAction: true,
popup: true,
groupShow: (selectedItems, storegetters) => {
return selectedItems.length === 1 && selectedItems[0].allocationstate === 'Allocated'
},
groupMap: (selectedId, values, records) => {
const record = records.filter(x => { return x.id === selectedId[0] })
return record
}
}
]
},
{ {
name: 'privategw', name: 'privategw',
title: 'label.private.gateway', title: 'label.private.gateway',
@ -1440,46 +1381,6 @@ export default {
} }
return true return true
} }
},
{
name: 'ipv4subnets',
title: 'label.ipv4.subnets',
icon: 'pic-center-outlined',
permission: ['listIpv4SubnetsForGuestNetwork'],
columns: ['subnet', 'zonename', 'parentsubnet', 'networkname', 'vpcname', 'created', 'allocated'],
details: ['subnet', 'zonename', 'zoneid', 'parentsubnet', 'networkname', 'networkid', 'vpcname', 'vpcid', 'created', 'allocated', 'state'],
searchFilters: ['zoneid'],
show: () => {
if (!store.getters.zones || store.getters.zones.length === 0) {
return false
}
const AdvancedZonesWithRoutedmode = store.getters.zones.filter(zone => zone.routedmodeenabled)
if (isAdmin() && (AdvancedZonesWithRoutedmode && AdvancedZonesWithRoutedmode.length > 0)) {
return true
}
return false
},
actions: [
{
api: 'createIpv4SubnetForGuestNetwork',
icon: 'plus-outlined',
label: 'label.add.ipv4.subnet',
listView: true,
popup: true,
component: shallowRef(defineAsyncComponent(() => import('@/views/network/CreateIpv4SubnetForNetwork.vue')))
},
{
api: 'deleteIpv4SubnetForGuestNetwork',
icon: 'delete-outlined',
label: 'label.delete.ipv4.subnet',
message: 'message.action.delete.ipv4.subnet',
dataView: true,
show: (record) => { return !record.networkid },
groupAction: true,
popup: true,
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
}
]
} }
] ]
} }

View File

@ -920,6 +920,9 @@ export default {
this.loading = true this.loading = true
if (this.$route.params && this.$route.params.id) { if (this.$route.params && this.$route.params.id) {
params.id = this.$route.params.id params.id = this.$route.params.id
if (['listNetworks'].includes(this.apiName) && 'displaynetwork' in this.$route.query) {
params.displaynetwork = this.$route.query.displaynetwork
}
if (['listSSHKeyPairs'].includes(this.apiName)) { if (['listSSHKeyPairs'].includes(this.apiName)) {
if (!this.$isValidUuid(params.id)) { if (!this.$isValidUuid(params.id)) {
delete params.id delete params.id

View File

@ -174,6 +174,12 @@
<template #title> <template #title>
<div class="center"> <div class="center">
<h3><cloud-outlined /> {{ $t('label.compute') }}</h3> <h3><cloud-outlined /> {{ $t('label.compute') }}</h3>
<a-switch
:checked-children="$t('label.allocated') + ' ' + $t('label.capacity')"
:un-checked-children="$t('label.used') + ' ' + $t('label.capacity')"
v-model:checked="this.displayAllocatedCompute"
@change="val => { this.displayAllocatedCompute = val }"
/>
</div> </div>
</template> </template>
<div> <div>
@ -184,15 +190,19 @@
</div> </div>
<a-progress <a-progress
status="active" status="active"
:percent="statsMap[ctype]?.capacitytotal > 0 ? parseFloat(100.0 * statsMap[ctype]?.capacityused / statsMap[ctype]?.capacitytotal) : 0" :percent="statsMap[ctype]?.capacitytotal > 0 ?
:format="p => statsMap[ctype]?.capacitytotal > 0 ? parseFloat(100.0 * statsMap[ctype]?.capacityused / statsMap[ctype]?.capacitytotal).toFixed(2) + '%' : '0%'" displayPercentUsedOrAllocated(statsMap[ctype]?.capacityused, statsMap[ctype]?.capacityallocated, statsMap[ctype]?.capacitytotal)
: 0"
:format="p => statsMap[ctype]?.capacitytotal > 0 ?
displayPercentFormatUsedOrAllocated(statsMap[ctype]?.capacityused, statsMap[ctype]?.capacityallocated, statsMap[ctype]?.capacitytotal)
: '0%'"
stroke-color="#52c41a" stroke-color="#52c41a"
size="small" size="small"
style="width:95%; float: left" style="width:95%; float: left"
/> />
<br/> <br/>
<div style="text-align: center"> <div style="text-align: center">
{{ displayData(ctype, statsMap[ctype]?.capacityused) }} {{ $t('label.allocated') }} | {{ displayData(ctype, statsMap[ctype]?.capacitytotal) }} {{ $t('label.total') }} {{ displayDataUsedOrAllocated(ctype, statsMap[ctype]?.capacityused, statsMap[ctype]?.capacityallocated) }} {{ this.displayAllocatedCompute ? $t('label.allocated') : $t('label.used') }} | {{ displayData(ctype, statsMap[ctype]?.capacitytotal) }} {{ $t('label.total') }}
</div> </div>
</div> </div>
</div> </div>
@ -346,6 +356,7 @@ export default {
zones: [], zones: [],
zoneSelected: {}, zoneSelected: {},
statsMap: {}, statsMap: {},
displayAllocatedCompute: false,
data: { data: {
pods: 0, pods: 0,
clusters: 0, clusters: 0,
@ -402,6 +413,18 @@ export default {
} }
return 'normal' return 'normal'
}, },
displayPercentUsedOrAllocated (used, allocated, total) {
var value = this.displayAllocatedCompute ? allocated : used
return parseFloat(100.0 * value / total)
},
displayPercentFormatUsedOrAllocated (used, allocated, total) {
var value = this.displayAllocatedCompute ? allocated : used
return parseFloat(100.0 * value / total).toFixed(2) + '%'
},
displayDataUsedOrAllocated (dataType, used, allocated) {
var value = this.displayAllocatedCompute ? allocated : used
return this.displayData(dataType, value)
},
displayData (dataType, value) { displayData (dataType, value) {
if (!value) { if (!value) {
value = 0 value = 0

View File

@ -781,7 +781,7 @@ export default {
const csvData = this.csv({ data: this.acls }) const csvData = this.csv({ data: this.acls })
const hiddenElement = document.createElement('a') const hiddenElement = document.createElement('a')
hiddenElement.href = 'data:text/csv;charset=utf-8,' + encodeURI(csvData) hiddenElement.href = 'data:text/csv;charset=utf-8,' + encodeURIComponent(csvData)
hiddenElement.target = '_blank' hiddenElement.target = '_blank'
hiddenElement.download = 'AclRules-' + this.resource.name + '-' + this.resource.id + '.csv' hiddenElement.download = 'AclRules-' + this.resource.name + '-' + this.resource.id + '.csv'
hiddenElement.click() hiddenElement.click()

View File

@ -213,7 +213,7 @@
@change="updateMtu()"/> @change="updateMtu()"/>
<div style="color: red" v-if="errorPrivateMtu" v-html="errorPrivateMtu.replace('%x', privateMtuMax)"></div> <div style="color: red" v-if="errorPrivateMtu" v-html="errorPrivateMtu.replace('%x', privateMtuMax)"></div>
</a-form-item> </a-form-item>
<a-form-item v-if="!isObjectEmpty(selectedNetworkOffering) && selectedNetworkOffering.specifyvlan"> <a-form-item ref="vlan" name="vlan" v-if="!isObjectEmpty(selectedNetworkOffering) && selectedNetworkOffering.specifyvlan">
<template #label> <template #label>
<tooltip-label :title="$t('label.vlan')" :tooltip="$t('label.vlan')"/> <tooltip-label :title="$t('label.vlan')" :tooltip="$t('label.vlan')"/>
</template> </template>