mirror of https://github.com/apache/cloudstack.git
Merge branch '4.19'
This commit is contained in:
commit
cb9b3134f7
|
|
@ -29,9 +29,9 @@ import org.apache.cloudstack.api.response.PodResponse;
|
|||
import org.apache.cloudstack.api.response.ZoneResponse;
|
||||
import org.apache.cloudstack.config.Configuration;
|
||||
import org.apache.cloudstack.ha.HAConfig;
|
||||
import org.apache.cloudstack.quota.QuotaTariff;
|
||||
import org.apache.cloudstack.storage.object.Bucket;
|
||||
import org.apache.cloudstack.storage.object.ObjectStore;
|
||||
import org.apache.cloudstack.quota.QuotaTariff;
|
||||
import org.apache.cloudstack.usage.Usage;
|
||||
import org.apache.cloudstack.vm.schedule.VMSchedule;
|
||||
|
||||
|
|
@ -1229,4 +1229,8 @@ public class EventTypes {
|
|||
public static boolean isVpcEvent(String eventType) {
|
||||
return EventTypes.EVENT_VPC_CREATE.equals(eventType) || EventTypes.EVENT_VPC_DELETE.equals(eventType);
|
||||
}
|
||||
|
||||
public static void addEntityEventDetail(String event, Class<?> clazz) {
|
||||
entityEventDetails.put(event, clazz);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import org.apache.cloudstack.acl.ControlledEntity;
|
|||
import com.cloud.uservm.UserVm;
|
||||
import com.cloud.utils.component.Adapter;
|
||||
|
||||
public interface KubernetesClusterHelper extends Adapter {
|
||||
public interface KubernetesServiceHelper extends Adapter {
|
||||
|
||||
ControlledEntity findByUuid(String uuid);
|
||||
ControlledEntity findByVmId(long vmId);
|
||||
|
|
@ -20,6 +20,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
import com.cloud.dc.DataCenter;
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.api.command.admin.address.ReleasePodIpCmdByAdmin;
|
||||
import org.apache.cloudstack.api.command.admin.network.DedicateGuestVlanRangeCmd;
|
||||
import org.apache.cloudstack.api.command.admin.network.ListDedicatedGuestVlanRangesCmd;
|
||||
|
|
@ -102,6 +103,10 @@ public interface NetworkService {
|
|||
|
||||
Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapacityException, ConcurrentOperationException, ResourceAllocationException;
|
||||
|
||||
Network createGuestNetwork(long networkOfferingId, String name, String displayText, Account owner,
|
||||
PhysicalNetwork physicalNetwork, long zoneId, ControlledEntity.ACLType aclType) throws
|
||||
InsufficientCapacityException, ConcurrentOperationException, ResourceAllocationException;
|
||||
|
||||
Pair<List<? extends Network>, Integer> searchForNetworks(ListNetworksCmd cmd);
|
||||
|
||||
boolean deleteNetwork(long networkId, boolean forced);
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ import com.cloud.dc.DataCenter;
|
|||
import com.cloud.exception.ConcurrentOperationException;
|
||||
import com.cloud.exception.InsufficientCapacityException;
|
||||
import com.cloud.exception.ManagementServerException;
|
||||
import com.cloud.exception.OperationTimedoutException;
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.exception.StorageUnavailableException;
|
||||
|
|
@ -66,10 +67,7 @@ public interface UserVmService {
|
|||
/**
|
||||
* Destroys one virtual machine
|
||||
*
|
||||
* @param userId
|
||||
* the id of the user performing the action
|
||||
* @param vmId
|
||||
* the id of the virtual machine.
|
||||
* @param cmd the API Command Object containg the parameters to use for this service action
|
||||
* @throws ConcurrentOperationException
|
||||
* @throws ResourceUnavailableException
|
||||
*/
|
||||
|
|
@ -112,6 +110,8 @@ public interface UserVmService {
|
|||
|
||||
UserVm rebootVirtualMachine(RebootVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException, ResourceAllocationException;
|
||||
|
||||
void startVirtualMachine(UserVm vm) throws OperationTimedoutException, ResourceUnavailableException, InsufficientCapacityException;
|
||||
|
||||
UserVm updateVirtualMachine(UpdateVMCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException;
|
||||
|
||||
/**
|
||||
|
|
@ -148,14 +148,6 @@ public interface UserVmService {
|
|||
* Creates a Basic Zone User VM in the database and returns the VM to the
|
||||
* caller.
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param sshKeyPair
|
||||
* - name of the ssh key pair used to login to the virtual
|
||||
* machine
|
||||
* @param cpuSpeed
|
||||
* @param memory
|
||||
* @param cpuNumber
|
||||
* @param zone
|
||||
* - availability zone for the virtual machine
|
||||
* @param serviceOffering
|
||||
|
|
@ -231,9 +223,6 @@ public interface UserVmService {
|
|||
* Creates a User VM in Advanced Zone (Security Group feature is enabled) in
|
||||
* the database and returns the VM to the caller.
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param type
|
||||
* @param zone
|
||||
* - availability zone for the virtual machine
|
||||
* @param serviceOffering
|
||||
|
|
@ -309,14 +298,6 @@ public interface UserVmService {
|
|||
* Creates a User VM in Advanced Zone (Security Group feature is disabled)
|
||||
* in the database and returns the VM to the caller.
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param sshKeyPair
|
||||
* - name of the ssh key pair used to login to the virtual
|
||||
* machine
|
||||
* @param cpuSpeed
|
||||
* @param memory
|
||||
* @param cpuNumber
|
||||
* @param zone
|
||||
* - availability zone for the virtual machine
|
||||
* @param serviceOffering
|
||||
|
|
|
|||
|
|
@ -17,7 +17,9 @@
|
|||
package org.apache.cloudstack.api;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.cloudstack.region.PortableIp;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
|
|
@ -81,15 +83,22 @@ public enum ApiCommandResourceType {
|
|||
ManagementServer(org.apache.cloudstack.management.ManagementServerHost.class),
|
||||
ObjectStore(org.apache.cloudstack.storage.object.ObjectStore.class),
|
||||
Bucket(org.apache.cloudstack.storage.object.Bucket.class),
|
||||
QuotaTariff(org.apache.cloudstack.quota.QuotaTariff.class);
|
||||
QuotaTariff(org.apache.cloudstack.quota.QuotaTariff.class),
|
||||
KubernetesCluster(null),
|
||||
KubernetesSupportedVersion(null);
|
||||
|
||||
private final Class<?> clazz;
|
||||
|
||||
static final Map<ApiCommandResourceType, Class<?>> additionalClassMappings = new HashMap<>();
|
||||
|
||||
private ApiCommandResourceType(Class<?> clazz) {
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
public Class<?> getAssociatedClass() {
|
||||
if (this.clazz == null && additionalClassMappings.containsKey(this)) {
|
||||
return additionalClassMappings.get(this);
|
||||
}
|
||||
return this.clazz;
|
||||
}
|
||||
|
||||
|
|
@ -119,4 +128,8 @@ public enum ApiCommandResourceType {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void setClassMapping(ApiCommandResourceType type, Class<?> clazz) {
|
||||
additionalClassMappings.put(type, clazz);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,11 @@ public class CreateServiceOfferingCmd extends BaseCmd {
|
|||
@Parameter(name = ApiConstants.CPU_NUMBER, type = CommandType.INTEGER, required = false, description = "the CPU number of the service offering")
|
||||
private Integer cpuNumber;
|
||||
|
||||
@Parameter(name = ApiConstants.CPU_SPEED, type = CommandType.INTEGER, required = false, description = "the CPU speed of the service offering in MHz.")
|
||||
@Parameter(name = ApiConstants.CPU_SPEED, type = CommandType.INTEGER, required = false, description = "For VMware and Xen based hypervisors this is the CPU speed of the service offering in MHz.\n" +
|
||||
"For the KVM hypervisor," +
|
||||
" the values of the parameters cpuSpeed and cpuNumber will be used to calculate the `shares` value. This value is used by the KVM hypervisor to calculate how much time" +
|
||||
" the VM will have access to the host's CPU. The `shares` value does not have a unit, and its purpose is being a weight value for the host to compare between its guest" +
|
||||
" VMs. For more information, see https://libvirt.org/formatdomain.html#cpu-tuning.")
|
||||
private Integer cpuSpeed;
|
||||
|
||||
@Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "The display text of the service offering, defaults to 'name'.")
|
||||
|
|
|
|||
|
|
@ -97,8 +97,8 @@ public class ListVMsCmd extends BaseListRetrieveOnlyResourceCountCmd implements
|
|||
collectionType = CommandType.STRING,
|
||||
description = "comma separated list of vm details requested, "
|
||||
+ "value can be a list of [all, group, nics, stats, secgrp, tmpl, servoff, diskoff, backoff, iso, volume, min, affgrp]."
|
||||
+ " If no parameter is passed in, the details will be defaulted to all. When return.vm.stats.on.vm.list is true, the default" +
|
||||
"details change to [group, nics, secgrp, tmpl, servoff, diskoff, backoff, iso, volume, min, affgrp], thus the stats will not be returned. ")
|
||||
+ " When no parameters are passed, all the details are returned if list.vm.default.details.stats is true (default),"
|
||||
+ " otherwise when list.vm.default.details.stats is false the API response will exclude the stats details.")
|
||||
private List<String> viewDetails;
|
||||
|
||||
@Parameter(name = ApiConstants.TEMPLATE_ID, type = CommandType.UUID, entityType = TemplateResponse.class, description = "list vms by template")
|
||||
|
|
|
|||
|
|
@ -125,9 +125,8 @@ public interface QueryService {
|
|||
static final ConfigKey<Boolean> SharePublicTemplatesWithOtherDomains = new ConfigKey<>("Advanced", Boolean.class, "share.public.templates.with.other.domains", "true",
|
||||
"If false, templates of this domain will not show up in the list templates of other domains.", true, ConfigKey.Scope.Domain);
|
||||
|
||||
ConfigKey<Boolean> ReturnVmStatsOnVmList = new ConfigKey<>("Advanced", Boolean.class, "return.vm.stats.on.vm.list", "true",
|
||||
"If false, changes the listVirtualMachines default details to [group, nics, secgrp, tmpl, servoff, diskoff, backoff, iso, volume, min, affgrp], so that the VMs' stats" +
|
||||
" are not returned by default when listing VMs; only when the 'stats' or 'all' detail is informed.", true, ConfigKey.Scope.Global);
|
||||
ConfigKey<Boolean> ReturnVmStatsOnVmList = new ConfigKey<>("Advanced", Boolean.class, "list.vm.default.details.stats", "true",
|
||||
"Determines whether VM stats should be returned when details are not explicitly specified in listVirtualMachines API request. When false, details default to [group, nics, secgrp, tmpl, servoff, diskoff, backoff, iso, volume, min, affgrp]. When true, all details are returned including 'stats'.", true, ConfigKey.Scope.Global);
|
||||
|
||||
ListResponse<UserResponse> searchForUsers(ListUsersCmd cmd) throws PermissionDeniedException;
|
||||
|
||||
|
|
|
|||
|
|
@ -339,7 +339,7 @@
|
|||
class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
|
||||
</bean>
|
||||
|
||||
<bean id="kubernetesClusterHelperRegistry"
|
||||
<bean id="kubernetesServiceHelperRegistry"
|
||||
class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
|
||||
</bean>
|
||||
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@
|
|||
>
|
||||
|
||||
<bean class="org.apache.cloudstack.spring.lifecycle.registry.RegistryLifecycle">
|
||||
<property name="registry" ref="kubernetesClusterHelperRegistry" />
|
||||
<property name="typeClass" value="com.cloud.kubernetes.cluster.KubernetesClusterHelper" />
|
||||
<property name="registry" ref="kubernetesServiceHelperRegistry" />
|
||||
<property name="typeClass" value="com.cloud.kubernetes.cluster.KubernetesServiceHelper" />
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
|
|
|
|||
|
|
@ -1185,8 +1185,9 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
|
|||
logger.error("Unable to destroy existing volume [{}] due to [{}].", volumeToString, e.getMessage());
|
||||
}
|
||||
// In case of VMware VM will continue to use the old root disk until expunged, so force expunge old root disk
|
||||
if (vm.getHypervisorType() == HypervisorType.VMware) {
|
||||
logger.info("Trying to expunge volume [{}] from primary data storage.", volumeToString);
|
||||
// For system VM we do not need volume entry in Destroy state
|
||||
if (vm.getHypervisorType() == HypervisorType.VMware || vm.getType().isUsedBySystem()) {
|
||||
logger.info(String.format("Trying to expunge volume [%s] from primary data storage.", volumeToString));
|
||||
AsyncCallFuture<VolumeApiResult> future = volService.expungeVolumeAsync(volFactory.getVolume(existingVolume.getId()));
|
||||
try {
|
||||
future.get();
|
||||
|
|
|
|||
|
|
@ -18,3 +18,7 @@
|
|||
--;
|
||||
-- Schema upgrade cleanup from 4.19.0.0 to 4.19.1.0
|
||||
--;
|
||||
|
||||
-- List VMs response optimisation, don't sum during API handling
|
||||
UPDATE cloud.configuration set value='false' where name='vm.stats.increment.metrics';
|
||||
DELETE from cloud.configuration where name='vm.stats.increment.metrics.in.memory';
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ FROM
|
|||
LEFT JOIN `networks` ON ((`nics`.`network_id` = `networks`.`id`)))
|
||||
LEFT JOIN `vpc` ON (((`networks`.`vpc_id` = `vpc`.`id`)
|
||||
AND ISNULL(`vpc`.`removed`))))
|
||||
LEFT JOIN `user_ip_address` ON ((`user_ip_address`.`vm_id` = `vm_instance`.`id`)))
|
||||
LEFT JOIN `user_ip_address` FORCE INDEX(`fk_user_ip_address__vm_id`) ON ((`user_ip_address`.`vm_id` = `vm_instance`.`id`)))
|
||||
LEFT JOIN `user_vm_details` `ssh_details` ON (((`ssh_details`.`vm_id` = `vm_instance`.`id`)
|
||||
AND (`ssh_details`.`name` = 'SSH.KeyPairNames'))))
|
||||
LEFT JOIN `resource_tags` ON (((`resource_tags`.`resource_id` = `vm_instance`.`id`)
|
||||
|
|
|
|||
|
|
@ -214,6 +214,7 @@ public class LibvirtConvertInstanceCommandWrapperTest {
|
|||
|
||||
LibvirtDomainXMLParser parser = Mockito.mock(LibvirtDomainXMLParser.class);
|
||||
Mockito.when(parser.getDisks()).thenReturn(List.of(diskDef));
|
||||
Mockito.doReturn(new Pair<String, String>(null, null)).when(convertInstanceCommandWrapper).getNfsStoragePoolHostAndPath(destinationPool);
|
||||
|
||||
List<UnmanagedInstanceTO.Disk> unmanagedInstanceDisks = convertInstanceCommandWrapper.getUnmanagedInstanceDisks(disks, parser);
|
||||
Assert.assertEquals(1, unmanagedInstanceDisks.size());
|
||||
|
|
|
|||
|
|
@ -40,12 +40,11 @@ import java.util.concurrent.TimeUnit;
|
|||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.uservm.UserVm;
|
||||
import com.cloud.vm.UserVmService;
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.acl.SecurityChecker;
|
||||
import org.apache.cloudstack.annotation.AnnotationService;
|
||||
import org.apache.cloudstack.annotation.dao.AnnotationDao;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiConstants.VMDetails;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
|
|
@ -90,6 +89,7 @@ import com.cloud.dc.dao.ClusterDao;
|
|||
import com.cloud.dc.dao.DataCenterDao;
|
||||
import com.cloud.deploy.DeployDestination;
|
||||
import com.cloud.domain.Domain;
|
||||
import com.cloud.event.ActionEvent;
|
||||
import com.cloud.exception.ConcurrentOperationException;
|
||||
import com.cloud.exception.InsufficientCapacityException;
|
||||
import com.cloud.exception.InsufficientServerCapacityException;
|
||||
|
|
@ -157,6 +157,7 @@ import com.cloud.user.UserAccount;
|
|||
import com.cloud.user.UserVO;
|
||||
import com.cloud.user.dao.SSHKeyPairDao;
|
||||
import com.cloud.user.dao.UserDao;
|
||||
import com.cloud.uservm.UserVm;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.Ternary;
|
||||
import com.cloud.utils.component.ComponentContext;
|
||||
|
|
@ -174,6 +175,7 @@ import com.cloud.utils.exception.CloudRuntimeException;
|
|||
import com.cloud.utils.fsm.NoTransitionException;
|
||||
import com.cloud.utils.fsm.StateMachine2;
|
||||
import com.cloud.utils.net.NetUtils;
|
||||
import com.cloud.vm.UserVmService;
|
||||
import com.cloud.vm.VMInstanceVO;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
|
|
@ -864,13 +866,15 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
logger.info(String.format("Creating network for account ID: %s from the network offering ID: %s as part of Kubernetes cluster: %s deployment process", owner.getUuid(), networkOffering.getUuid(), clusterName));
|
||||
}
|
||||
|
||||
CallContext networkContext = CallContext.register(CallContext.current(), ApiCommandResourceType.Network);
|
||||
try {
|
||||
network = networkMgr.createGuestNetwork(networkOffering.getId(), clusterName + "-network", owner.getAccountName() + "-network",
|
||||
null, null, null, false, null, owner, null, physicalNetwork, zone.getId(),
|
||||
ControlledEntity.ACLType.Account, null, null, null, null, true, null,
|
||||
null, null, null, null, null, null, null, null, null);
|
||||
network = networkService.createGuestNetwork(networkOffering.getId(), clusterName + "-network",
|
||||
owner.getAccountName() + "-network", owner, physicalNetwork, zone.getId(),
|
||||
ControlledEntity.ACLType.Account);
|
||||
} catch (ConcurrentOperationException | InsufficientCapacityException | ResourceAllocationException e) {
|
||||
logAndThrow(Level.ERROR, String.format("Unable to create network for the Kubernetes cluster: %s", clusterName));
|
||||
} finally {
|
||||
CallContext.unregister();
|
||||
}
|
||||
}
|
||||
return network;
|
||||
|
|
@ -1139,6 +1143,8 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = KubernetesClusterEventTypes.EVENT_KUBERNETES_CLUSTER_CREATE,
|
||||
eventDescription = "creating Kubernetes cluster", create = true)
|
||||
public KubernetesCluster createUnmanagedKubernetesCluster(CreateKubernetesClusterCmd cmd) throws CloudRuntimeException {
|
||||
if (!KubernetesServiceEnabled.value()) {
|
||||
logAndThrow(Level.ERROR, "Kubernetes Service plugin is disabled");
|
||||
|
|
@ -1185,10 +1191,13 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
if (logger.isInfoEnabled()) {
|
||||
logger.info(String.format("Kubernetes cluster with name: %s and ID: %s has been created", cluster.getName(), cluster.getUuid()));
|
||||
}
|
||||
CallContext.current().putContextParameter(KubernetesCluster.class, cluster.getUuid());
|
||||
return cluster;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = KubernetesClusterEventTypes.EVENT_KUBERNETES_CLUSTER_CREATE,
|
||||
eventDescription = "creating Kubernetes cluster", create = true)
|
||||
public KubernetesCluster createManagedKubernetesCluster(CreateKubernetesClusterCmd cmd) throws CloudRuntimeException {
|
||||
if (!KubernetesServiceEnabled.value()) {
|
||||
logAndThrow(Level.ERROR, "Kubernetes Service plugin is disabled");
|
||||
|
|
@ -1245,6 +1254,7 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
if (logger.isInfoEnabled()) {
|
||||
logger.info(String.format("Kubernetes cluster name: %s and ID: %s has been created", cluster.getName(), cluster.getUuid()));
|
||||
}
|
||||
CallContext.current().putContextParameter(KubernetesCluster.class, cluster.getUuid());
|
||||
return cluster;
|
||||
}
|
||||
|
||||
|
|
@ -1271,29 +1281,64 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
return securityGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = KubernetesClusterEventTypes.EVENT_KUBERNETES_CLUSTER_CREATE,
|
||||
eventDescription = "creating Kubernetes cluster", async = true)
|
||||
public void startKubernetesCluster(CreateKubernetesClusterCmd cmd) throws CloudRuntimeException {
|
||||
final Long id = cmd.getEntityId();
|
||||
if (KubernetesCluster.ClusterType.valueOf(cmd.getClusterType()) != KubernetesCluster.ClusterType.CloudManaged) {
|
||||
return;
|
||||
}
|
||||
final KubernetesClusterVO kubernetesCluster = kubernetesClusterDao.findById(id);
|
||||
if (kubernetesCluster == null) {
|
||||
throw new InvalidParameterValueException("Failed to find Kubernetes cluster with given ID");
|
||||
}
|
||||
if (!startKubernetesCluster(kubernetesCluster, true)) {
|
||||
throw new CloudRuntimeException(String.format("Failed to start created Kubernetes cluster: %s",
|
||||
kubernetesCluster.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = KubernetesClusterEventTypes.EVENT_KUBERNETES_CLUSTER_START,
|
||||
eventDescription = "starting Kubernetes cluster", async = true)
|
||||
public void startKubernetesCluster(StartKubernetesClusterCmd cmd) throws CloudRuntimeException {
|
||||
final Long id = cmd.getId();
|
||||
if (id == null || id < 1L) {
|
||||
throw new InvalidParameterValueException("Invalid Kubernetes cluster ID provided");
|
||||
}
|
||||
final KubernetesClusterVO kubernetesCluster = kubernetesClusterDao.findById(id);
|
||||
if (kubernetesCluster == null) {
|
||||
throw new InvalidParameterValueException("Given Kubernetes cluster was not found");
|
||||
}
|
||||
if (!isCommandSupported(kubernetesCluster, cmd.getActualCommandName())) {
|
||||
throw new InvalidParameterValueException(String.format("Start kubernetes cluster is not supported for " +
|
||||
"an externally managed cluster (%s)", kubernetesCluster.getName()));
|
||||
}
|
||||
if (!startKubernetesCluster(kubernetesCluster, false)) {
|
||||
throw new CloudRuntimeException(String.format("Failed to start Kubernetes cluster: %s",
|
||||
kubernetesCluster.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start operation can be performed at two different life stages of Kubernetes cluster. First when a freshly created cluster
|
||||
* in which case there are no resources provisioned for the Kubernetes cluster. So during start all the resources
|
||||
* are provisioned from scratch. Second kind of start, happens on Stopped Kubernetes cluster, in which all resources
|
||||
* are provisioned (like volumes, nics, networks etc). It just that VM's are not in running state. So just
|
||||
* start the VM's (which can possibly implicitly start the network also).
|
||||
* @param kubernetesClusterId
|
||||
* @param kubernetesCluster
|
||||
* @param onCreate
|
||||
* @return
|
||||
* @throws CloudRuntimeException
|
||||
*/
|
||||
|
||||
@Override
|
||||
public boolean startKubernetesCluster(long kubernetesClusterId, boolean onCreate) throws CloudRuntimeException {
|
||||
public boolean startKubernetesCluster(KubernetesClusterVO kubernetesCluster, boolean onCreate) throws CloudRuntimeException {
|
||||
if (!KubernetesServiceEnabled.value()) {
|
||||
logAndThrow(Level.ERROR, "Kubernetes Service plugin is disabled");
|
||||
}
|
||||
final KubernetesClusterVO kubernetesCluster = kubernetesClusterDao.findById(kubernetesClusterId);
|
||||
if (kubernetesCluster == null) {
|
||||
throw new InvalidParameterValueException("Failed to find Kubernetes cluster with given ID");
|
||||
}
|
||||
if (kubernetesCluster.getRemoved() != null) {
|
||||
throw new InvalidParameterValueException(String.format("Kubernetes cluster : %s is already deleted", kubernetesCluster.getName()));
|
||||
throw new InvalidParameterValueException(String.format("Kubernetes cluster : %s is already deleted",
|
||||
kubernetesCluster.getName()));
|
||||
}
|
||||
accountManager.checkAccess(CallContext.current().getCallingAccount(), SecurityChecker.AccessType.OperateEntry, false, kubernetesCluster);
|
||||
if (kubernetesCluster.getState().equals(KubernetesCluster.State.Running)) {
|
||||
|
|
@ -1351,6 +1396,8 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = KubernetesClusterEventTypes.EVENT_KUBERNETES_CLUSTER_STOP,
|
||||
eventDescription = "stopping Kubernetes cluster", async = true)
|
||||
public boolean stopKubernetesCluster(StopKubernetesClusterCmd cmd) throws CloudRuntimeException {
|
||||
long kubernetesClusterId = cmd.getId();
|
||||
if (!KubernetesServiceEnabled.value()) {
|
||||
|
|
@ -1385,6 +1432,8 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = KubernetesClusterEventTypes.EVENT_KUBERNETES_CLUSTER_DELETE,
|
||||
eventDescription = "deleting Kubernetes cluster", async = true)
|
||||
public boolean deleteKubernetesCluster(DeleteKubernetesClusterCmd cmd) throws CloudRuntimeException {
|
||||
if (!KubernetesServiceEnabled.value()) {
|
||||
logAndThrow(Level.ERROR, "Kubernetes Service plugin is disabled");
|
||||
|
|
@ -1487,13 +1536,13 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
if (clusterType != null) {
|
||||
sc.setParameters("cluster_type", clusterType);
|
||||
}
|
||||
List<KubernetesClusterVO> kubernetesClusters = kubernetesClusterDao.search(sc, searchFilter);
|
||||
for (KubernetesClusterVO cluster : kubernetesClusters) {
|
||||
Pair<List<KubernetesClusterVO>, Integer> kubernetesClustersAndCount = kubernetesClusterDao.searchAndCount(sc, searchFilter);
|
||||
for (KubernetesClusterVO cluster : kubernetesClustersAndCount.first()) {
|
||||
KubernetesClusterResponse clusterResponse = createKubernetesClusterResponse(cluster.getId());
|
||||
responsesList.add(clusterResponse);
|
||||
}
|
||||
ListResponse<KubernetesClusterResponse> response = new ListResponse<KubernetesClusterResponse>();
|
||||
response.setResponses(responsesList);
|
||||
ListResponse<KubernetesClusterResponse> response = new ListResponse<>();
|
||||
response.setResponses(responsesList, kubernetesClustersAndCount.second());
|
||||
return response;
|
||||
}
|
||||
|
||||
|
|
@ -1527,6 +1576,8 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = KubernetesClusterEventTypes.EVENT_KUBERNETES_CLUSTER_SCALE,
|
||||
eventDescription = "scaling Kubernetes cluster", async = true)
|
||||
public boolean scaleKubernetesCluster(ScaleKubernetesClusterCmd cmd) throws CloudRuntimeException {
|
||||
if (!KubernetesServiceEnabled.value()) {
|
||||
logAndThrow(Level.ERROR, "Kubernetes Service plugin is disabled");
|
||||
|
|
@ -1534,22 +1585,29 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
validateKubernetesClusterScaleParameters(cmd);
|
||||
|
||||
KubernetesClusterVO kubernetesCluster = kubernetesClusterDao.findById(cmd.getId());
|
||||
final Long clusterSize = cmd.getClusterSize();
|
||||
if (clusterSize != null) {
|
||||
CallContext.current().setEventDetails(String.format("Kubernetes cluster ID: %s scaling from size: %d to %d",
|
||||
kubernetesCluster.getUuid(), kubernetesCluster.getNodeCount(), clusterSize));
|
||||
}
|
||||
String[] keys = getServiceUserKeys(kubernetesCluster);
|
||||
KubernetesClusterScaleWorker scaleWorker =
|
||||
new KubernetesClusterScaleWorker(kubernetesClusterDao.findById(cmd.getId()),
|
||||
serviceOfferingDao.findById(cmd.getServiceOfferingId()),
|
||||
cmd.getClusterSize(),
|
||||
cmd.getNodeIds(),
|
||||
cmd.isAutoscalingEnabled(),
|
||||
cmd.getMinSize(),
|
||||
cmd.getMaxSize(),
|
||||
this);
|
||||
serviceOfferingDao.findById(cmd.getServiceOfferingId()),
|
||||
clusterSize,
|
||||
cmd.getNodeIds(),
|
||||
cmd.isAutoscalingEnabled(),
|
||||
cmd.getMinSize(),
|
||||
cmd.getMaxSize(),
|
||||
this);
|
||||
scaleWorker.setKeys(keys);
|
||||
scaleWorker = ComponentContext.inject(scaleWorker);
|
||||
return scaleWorker.scaleCluster();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = KubernetesClusterEventTypes.EVENT_KUBERNETES_CLUSTER_UPGRADE,
|
||||
eventDescription = "upgrading Kubernetes cluster", async = true)
|
||||
public boolean upgradeKubernetesCluster(UpgradeKubernetesClusterCmd cmd) throws CloudRuntimeException {
|
||||
if (!KubernetesServiceEnabled.value()) {
|
||||
logAndThrow(Level.ERROR, "Kubernetes Service plugin is disabled");
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import org.apache.cloudstack.api.command.user.kubernetes.cluster.GetKubernetesCl
|
|||
import org.apache.cloudstack.api.command.user.kubernetes.cluster.ListKubernetesClustersCmd;
|
||||
import org.apache.cloudstack.api.command.user.kubernetes.cluster.RemoveVirtualMachinesFromKubernetesClusterCmd;
|
||||
import org.apache.cloudstack.api.command.user.kubernetes.cluster.ScaleKubernetesClusterCmd;
|
||||
import org.apache.cloudstack.api.command.user.kubernetes.cluster.StartKubernetesClusterCmd;
|
||||
import org.apache.cloudstack.api.command.user.kubernetes.cluster.StopKubernetesClusterCmd;
|
||||
import org.apache.cloudstack.api.command.user.kubernetes.cluster.UpgradeKubernetesClusterCmd;
|
||||
import org.apache.cloudstack.api.response.KubernetesClusterConfigResponse;
|
||||
|
|
@ -98,7 +99,9 @@ public interface KubernetesClusterService extends PluggableService, Configurable
|
|||
|
||||
KubernetesCluster createManagedKubernetesCluster(CreateKubernetesClusterCmd cmd) throws CloudRuntimeException;
|
||||
|
||||
boolean startKubernetesCluster(long kubernetesClusterId, boolean onCreate) throws CloudRuntimeException;
|
||||
void startKubernetesCluster(CreateKubernetesClusterCmd cmd) throws CloudRuntimeException;
|
||||
|
||||
void startKubernetesCluster(StartKubernetesClusterCmd cmd) throws CloudRuntimeException;
|
||||
|
||||
boolean stopKubernetesCluster(StopKubernetesClusterCmd cmd) throws CloudRuntimeException;
|
||||
|
||||
|
|
|
|||
|
|
@ -16,33 +16,59 @@
|
|||
// under the License.
|
||||
package com.cloud.kubernetes.cluster;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.config.Configurable;
|
||||
|
||||
import com.cloud.event.EventTypes;
|
||||
import com.cloud.kubernetes.cluster.dao.KubernetesClusterDao;
|
||||
import com.cloud.kubernetes.cluster.dao.KubernetesClusterVmMapDao;
|
||||
import com.cloud.kubernetes.version.KubernetesSupportedVersion;
|
||||
import com.cloud.kubernetes.version.KubernetesVersionEventTypes;
|
||||
import com.cloud.uservm.UserVm;
|
||||
import com.cloud.utils.component.AdapterBase;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.vm.UserVmManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.config.Configurable;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Objects;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class KubernetesClusterHelperImpl extends AdapterBase implements KubernetesClusterHelper, Configurable {
|
||||
private static final Logger logger = LogManager.getLogger(KubernetesClusterHelperImpl.class);
|
||||
public class KubernetesServiceHelperImpl extends AdapterBase implements KubernetesServiceHelper, Configurable {
|
||||
private static final Logger logger = LogManager.getLogger(KubernetesServiceHelperImpl.class);
|
||||
|
||||
@Inject
|
||||
private KubernetesClusterDao kubernetesClusterDao;
|
||||
@Inject
|
||||
private KubernetesClusterVmMapDao kubernetesClusterVmMapDao;
|
||||
|
||||
protected void setEventTypeEntityDetails(Class<?> eventTypeDefinedClass, Class<?> entityClass) {
|
||||
Field[] declaredFields = eventTypeDefinedClass.getDeclaredFields();
|
||||
for (Field field : declaredFields) {
|
||||
int modifiers = field.getModifiers();
|
||||
if (!Modifier.isPublic(modifiers) || !Modifier.isStatic(modifiers)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
Object value = field.get(null);
|
||||
if (ObjectUtils.allNotNull(value, value.toString())) {
|
||||
EventTypes.addEntityEventDetail(value.toString(), entityClass);
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControlledEntity findByUuid(String uuid) {
|
||||
return kubernetesClusterDao.findByUuid(uuid);
|
||||
|
|
@ -79,11 +105,21 @@ public class KubernetesClusterHelperImpl extends AdapterBase implements Kubernet
|
|||
|
||||
@Override
|
||||
public String getConfigComponentName() {
|
||||
return KubernetesClusterHelper.class.getSimpleName();
|
||||
return KubernetesServiceHelper.class.getSimpleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigKey<?>[] getConfigKeys() {
|
||||
return new ConfigKey<?>[]{};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean start() {
|
||||
setEventTypeEntityDetails(KubernetesClusterEventTypes.class, KubernetesCluster.class);
|
||||
setEventTypeEntityDetails(KubernetesVersionEventTypes.class, KubernetesSupportedVersion.class);
|
||||
ApiCommandResourceType.setClassMapping(ApiCommandResourceType.KubernetesCluster, KubernetesCluster.class);
|
||||
ApiCommandResourceType.setClassMapping(ApiCommandResourceType.KubernetesSupportedVersion,
|
||||
KubernetesSupportedVersion.class);
|
||||
return super.start();
|
||||
}
|
||||
}
|
||||
|
|
@ -34,9 +34,11 @@ import javax.inject.Inject;
|
|||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.ca.CAManager;
|
||||
import org.apache.cloudstack.config.ApiServiceConfiguration;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
|
|
@ -92,7 +94,7 @@ import com.cloud.utils.ssh.SshHelper;
|
|||
import com.cloud.vm.UserVmDetailVO;
|
||||
import com.cloud.vm.UserVmService;
|
||||
import com.cloud.vm.UserVmVO;
|
||||
import com.cloud.vm.VirtualMachineManager;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import com.cloud.vm.VmDetailConstants;
|
||||
import com.cloud.vm.dao.UserVmDao;
|
||||
import com.cloud.vm.dao.UserVmDetailsDao;
|
||||
|
|
@ -150,8 +152,6 @@ public class KubernetesClusterActionWorker {
|
|||
@Inject
|
||||
protected VlanDao vlanDao;
|
||||
@Inject
|
||||
protected VirtualMachineManager itMgr;
|
||||
@Inject
|
||||
protected LaunchPermissionDao launchPermissionDao;
|
||||
@Inject
|
||||
public ProjectService projectService;
|
||||
|
|
@ -475,6 +475,8 @@ public class KubernetesClusterActionWorker {
|
|||
}
|
||||
|
||||
for (UserVm vm : clusterVMs) {
|
||||
CallContext vmContext = CallContext.register(CallContext.current(), ApiCommandResourceType.VirtualMachine);
|
||||
vmContext.putContextParameter(VirtualMachine.class, vm.getUuid());
|
||||
try {
|
||||
templateService.attachIso(iso.getId(), vm.getId(), true);
|
||||
if (logger.isInfoEnabled()) {
|
||||
|
|
@ -482,6 +484,8 @@ public class KubernetesClusterActionWorker {
|
|||
}
|
||||
} catch (CloudRuntimeException ex) {
|
||||
logTransitStateAndThrow(Level.ERROR, String.format("Failed to attach binaries ISO for VM : %s in the Kubernetes cluster name: %s", vm.getDisplayName(), kubernetesCluster.getName()), kubernetesCluster.getId(), failedEvent, ex);
|
||||
} finally {
|
||||
CallContext.unregister();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -493,10 +497,14 @@ public class KubernetesClusterActionWorker {
|
|||
protected void detachIsoKubernetesVMs(List<UserVm> clusterVMs) {
|
||||
for (UserVm vm : clusterVMs) {
|
||||
boolean result = false;
|
||||
CallContext vmContext = CallContext.register(CallContext.current(), ApiCommandResourceType.VirtualMachine);
|
||||
vmContext.putContextParameter(VirtualMachine.class, vm.getUuid());
|
||||
try {
|
||||
result = templateService.detachIso(vm.getId(), true);
|
||||
} catch (CloudRuntimeException ex) {
|
||||
logger.warn(String.format("Failed to detach binaries ISO from VM : %s in the Kubernetes cluster : %s ", vm.getDisplayName(), kubernetesCluster.getName()), ex);
|
||||
} finally {
|
||||
CallContext.unregister();
|
||||
}
|
||||
if (result) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import javax.inject.Inject;
|
|||
|
||||
import org.apache.cloudstack.annotation.AnnotationService;
|
||||
import org.apache.cloudstack.annotation.dao.AnnotationDao;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
|
|
@ -93,6 +94,9 @@ public class KubernetesClusterDestroyWorker extends KubernetesClusterResourceMod
|
|||
if (userVM == null || userVM.isRemoved()) {
|
||||
continue;
|
||||
}
|
||||
CallContext vmContext = CallContext.register(CallContext.current(),
|
||||
ApiCommandResourceType.VirtualMachine);
|
||||
vmContext.setEventResourceId(vmID);
|
||||
try {
|
||||
UserVm vm = userVmService.destroyVm(vmID, true);
|
||||
if (!userVmManager.expunge(userVM)) {
|
||||
|
|
@ -106,6 +110,8 @@ public class KubernetesClusterDestroyWorker extends KubernetesClusterResourceMod
|
|||
} catch (ResourceUnavailableException | ConcurrentOperationException e) {
|
||||
logger.warn(String.format("Failed to destroy VM : %s part of the Kubernetes cluster : %s cleanup. Moving on with destroying remaining resources provisioned for the Kubernetes cluster", userVM.getDisplayName(), kubernetesCluster.getName()), e);
|
||||
return false;
|
||||
} finally {
|
||||
CallContext.unregister();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,14 +31,13 @@ import java.util.stream.Collectors;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.offering.NetworkOffering;
|
||||
import com.cloud.offerings.dao.NetworkOfferingDao;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.command.user.firewall.CreateFirewallRuleCmd;
|
||||
import org.apache.cloudstack.api.command.user.network.CreateNetworkACLCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.StartVMCmd;
|
||||
import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
|
@ -86,7 +85,9 @@ import com.cloud.network.vpc.NetworkACLItem;
|
|||
import com.cloud.network.vpc.NetworkACLItemDao;
|
||||
import com.cloud.network.vpc.NetworkACLItemVO;
|
||||
import com.cloud.network.vpc.NetworkACLService;
|
||||
import com.cloud.offering.NetworkOffering;
|
||||
import com.cloud.offering.ServiceOffering;
|
||||
import com.cloud.offerings.dao.NetworkOfferingDao;
|
||||
import com.cloud.resource.ResourceManager;
|
||||
import com.cloud.storage.Volume;
|
||||
import com.cloud.storage.VolumeApiService;
|
||||
|
|
@ -323,18 +324,19 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
}
|
||||
|
||||
protected void startKubernetesVM(final UserVm vm) throws ManagementServerException {
|
||||
CallContext vmContext = null;
|
||||
if (!ApiCommandResourceType.VirtualMachine.equals(CallContext.current().getEventResourceType())); {
|
||||
vmContext = CallContext.register(CallContext.current(), ApiCommandResourceType.VirtualMachine);
|
||||
vmContext.setEventResourceId(vm.getId());
|
||||
}
|
||||
try {
|
||||
StartVMCmd startVm = new StartVMCmd();
|
||||
startVm = ComponentContext.inject(startVm);
|
||||
Field f = startVm.getClass().getDeclaredField("id");
|
||||
f.setAccessible(true);
|
||||
f.set(startVm, vm.getId());
|
||||
itMgr.advanceStart(vm.getUuid(), null, null);
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info(String.format("Started VM : %s in the Kubernetes cluster : %s", vm.getDisplayName(), kubernetesCluster.getName()));
|
||||
}
|
||||
} catch (IllegalAccessException | NoSuchFieldException | OperationTimedoutException | ResourceUnavailableException | InsufficientCapacityException ex) {
|
||||
userVmManager.startVirtualMachine(vm);
|
||||
} catch (OperationTimedoutException | ResourceUnavailableException | InsufficientCapacityException ex) {
|
||||
throw new ManagementServerException(String.format("Failed to start VM in the Kubernetes cluster : %s", kubernetesCluster.getName()), ex);
|
||||
} finally {
|
||||
if (vmContext != null) {
|
||||
CallContext.unregister();
|
||||
}
|
||||
}
|
||||
|
||||
UserVm startVm = userVmDao.findById(vm.getId());
|
||||
|
|
@ -347,19 +349,23 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
ResourceUnavailableException, InsufficientCapacityException {
|
||||
List<UserVm> nodes = new ArrayList<>();
|
||||
for (int i = offset + 1; i <= nodeCount; i++) {
|
||||
UserVm vm = createKubernetesNode(publicIpAddress);
|
||||
addKubernetesClusterVm(kubernetesCluster.getId(), vm.getId(), false);
|
||||
if (kubernetesCluster.getNodeRootDiskSize() > 0) {
|
||||
resizeNodeVolume(vm);
|
||||
}
|
||||
startKubernetesVM(vm);
|
||||
vm = userVmDao.findById(vm.getId());
|
||||
if (vm == null) {
|
||||
throw new ManagementServerException(String.format("Failed to provision worker VM for Kubernetes cluster : %s" , kubernetesCluster.getName()));
|
||||
}
|
||||
nodes.add(vm);
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info(String.format("Provisioned node VM : %s in to the Kubernetes cluster : %s", vm.getDisplayName(), kubernetesCluster.getName()));
|
||||
CallContext vmContext = CallContext.register(CallContext.current(), ApiCommandResourceType.VirtualMachine);
|
||||
try {
|
||||
UserVm vm = createKubernetesNode(publicIpAddress);
|
||||
vmContext.setEventResourceId(vm.getId());
|
||||
addKubernetesClusterVm(kubernetesCluster.getId(), vm.getId(), false);
|
||||
if (kubernetesCluster.getNodeRootDiskSize() > 0) {
|
||||
resizeNodeVolume(vm);
|
||||
}
|
||||
startKubernetesVM(vm);
|
||||
vm = userVmDao.findById(vm.getId());
|
||||
if (vm == null) {
|
||||
throw new ManagementServerException(String.format("Failed to provision worker VM for Kubernetes cluster : %s", kubernetesCluster.getName()));
|
||||
}
|
||||
nodes.add(vm);
|
||||
logger.info("Provisioned node VM : {} in to the Kubernetes cluster : {}", vm.getDisplayName(), kubernetesCluster.getName());
|
||||
} finally {
|
||||
CallContext.unregister();
|
||||
}
|
||||
}
|
||||
return nodes;
|
||||
|
|
@ -630,6 +636,7 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
|
||||
protected void createFirewallRules(IpAddress publicIp, List<Long> clusterVMIds, boolean apiRule) throws ManagementServerException {
|
||||
// Firewall rule for SSH access on each node VM
|
||||
CallContext.register(CallContext.current(), null);
|
||||
try {
|
||||
int endPort = CLUSTER_NODES_DEFAULT_START_SSH_PORT + clusterVMIds.size() - 1;
|
||||
provisionFirewallRules(publicIp, owner, CLUSTER_NODES_DEFAULT_START_SSH_PORT, endPort);
|
||||
|
|
@ -638,11 +645,14 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
}
|
||||
} catch (NoSuchFieldException | IllegalAccessException | ResourceUnavailableException | NetworkRuleConflictException e) {
|
||||
throw new ManagementServerException(String.format("Failed to provision firewall rules for SSH access for the Kubernetes cluster : %s", kubernetesCluster.getName()), e);
|
||||
} finally {
|
||||
CallContext.unregister();
|
||||
}
|
||||
if (!apiRule) {
|
||||
return;
|
||||
}
|
||||
// Firewall rule for API access for control node VMs
|
||||
CallContext.register(CallContext.current(), null);
|
||||
try {
|
||||
provisionFirewallRules(publicIp, owner, CLUSTER_API_PORT, CLUSTER_API_PORT);
|
||||
if (logger.isInfoEnabled()) {
|
||||
|
|
@ -651,6 +661,8 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
}
|
||||
} catch (NoSuchFieldException | IllegalAccessException | ResourceUnavailableException | NetworkRuleConflictException e) {
|
||||
throw new ManagementServerException(String.format("Failed to provision firewall rules for API access for the Kubernetes cluster : %s", kubernetesCluster.getName()), e);
|
||||
} finally {
|
||||
CallContext.unregister();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -691,6 +703,7 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
return;
|
||||
}
|
||||
// ACL rule for API access for control node VMs
|
||||
CallContext.register(CallContext.current(), null);
|
||||
try {
|
||||
provisionVpcTierAllowPortACLRule(network, CLUSTER_API_PORT, CLUSTER_API_PORT);
|
||||
if (logger.isInfoEnabled()) {
|
||||
|
|
@ -699,7 +712,10 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
}
|
||||
} catch (NoSuchFieldException | IllegalAccessException | ResourceUnavailableException | InvalidParameterValueException | PermissionDeniedException e) {
|
||||
throw new ManagementServerException(String.format("Failed to provision firewall rules for API access for the Kubernetes cluster : %s", kubernetesCluster.getName()), e);
|
||||
} finally {
|
||||
CallContext.unregister();
|
||||
}
|
||||
CallContext.register(CallContext.current(), null);
|
||||
try {
|
||||
provisionVpcTierAllowPortACLRule(network, DEFAULT_SSH_PORT, DEFAULT_SSH_PORT);
|
||||
if (logger.isInfoEnabled()) {
|
||||
|
|
@ -708,6 +724,8 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
}
|
||||
} catch (NoSuchFieldException | IllegalAccessException | ResourceUnavailableException | InvalidParameterValueException | PermissionDeniedException e) {
|
||||
throw new ManagementServerException(String.format("Failed to provision firewall rules for API access for the Kubernetes cluster : %s", kubernetesCluster.getName()), e);
|
||||
} finally {
|
||||
CallContext.unregister();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,9 @@ import java.util.stream.Collectors;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.InternalIdentity;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
|
|
@ -318,6 +320,9 @@ public class KubernetesClusterScaleWorker extends KubernetesClusterResourceModif
|
|||
if (!removeKubernetesClusterNode(publicIpAddress, sshPort, userVM, 3, 30000)) {
|
||||
logTransitStateAndThrow(Level.ERROR, String.format("Scaling failed for Kubernetes cluster : %s, failed to remove Kubernetes node: %s running on VM : %s", kubernetesCluster.getName(), userVM.getHostName(), userVM.getDisplayName()), kubernetesCluster.getId(), KubernetesCluster.Event.OperationFailed);
|
||||
}
|
||||
CallContext vmContext = CallContext.register(CallContext.current(),
|
||||
ApiCommandResourceType.VirtualMachine);
|
||||
vmContext.setEventResourceId(userVM.getId());
|
||||
try {
|
||||
UserVm vm = userVmService.destroyVm(userVM.getId(), true);
|
||||
if (!userVmManager.expunge(userVM)) {
|
||||
|
|
@ -327,6 +332,8 @@ public class KubernetesClusterScaleWorker extends KubernetesClusterResourceModif
|
|||
} catch (ResourceUnavailableException e) {
|
||||
logTransitStateAndThrow(Level.ERROR, String.format("Scaling Kubernetes cluster %s failed, unable to remove VM ID: %s",
|
||||
kubernetesCluster.getName() , userVM.getDisplayName()), kubernetesCluster.getId(), KubernetesCluster.Event.OperationFailed, e);
|
||||
} finally {
|
||||
CallContext.unregister();
|
||||
}
|
||||
kubernetesClusterVmMapDao.expunge(vmMapVO.getId());
|
||||
if (System.currentTimeMillis() > scaleTimeoutTime) {
|
||||
|
|
@ -438,10 +445,10 @@ public class KubernetesClusterScaleWorker extends KubernetesClusterResourceModif
|
|||
if (existingServiceOffering == null) {
|
||||
logAndThrow(Level.ERROR, String.format("Scaling Kubernetes cluster : %s failed, service offering for the Kubernetes cluster not found!", kubernetesCluster.getName()));
|
||||
}
|
||||
final boolean autscalingChanged = isAutoscalingChanged();
|
||||
final boolean autoscalingChanged = isAutoscalingChanged();
|
||||
final boolean serviceOfferingScalingNeeded = serviceOffering != null && serviceOffering.getId() != existingServiceOffering.getId();
|
||||
|
||||
if (autscalingChanged) {
|
||||
if (autoscalingChanged) {
|
||||
boolean autoScaled = autoscaleCluster(this.isAutoscalingEnabled, minSize, maxSize);
|
||||
if (autoScaled && serviceOfferingScalingNeeded) {
|
||||
scaleKubernetesClusterOffering();
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ package com.cloud.kubernetes.cluster.actionworkers;
|
|||
import java.util.List;
|
||||
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
|
||||
import com.cloud.exception.ConcurrentOperationException;
|
||||
import com.cloud.kubernetes.cluster.KubernetesCluster;
|
||||
|
|
@ -44,11 +46,15 @@ public class KubernetesClusterStopWorker extends KubernetesClusterActionWorker {
|
|||
if (vm == null) {
|
||||
logTransitStateAndThrow(Level.ERROR, String.format("Failed to find all VMs in Kubernetes cluster : %s", kubernetesCluster.getName()), kubernetesCluster.getId(), KubernetesCluster.Event.OperationFailed);
|
||||
}
|
||||
CallContext vmContext = CallContext.register(CallContext.current(), ApiCommandResourceType.VirtualMachine);
|
||||
vmContext.setEventResourceId(vm.getId());
|
||||
try {
|
||||
userVmService.stopVirtualMachine(vm.getId(), false);
|
||||
} catch (ConcurrentOperationException ex) {
|
||||
logger.warn(String.format("Failed to stop VM : %s in Kubernetes cluster : %s",
|
||||
vm.getDisplayName(), kubernetesCluster.getName()), ex);
|
||||
} finally {
|
||||
CallContext.unregister();
|
||||
}
|
||||
}
|
||||
for (final UserVm userVm : clusterVMs) {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import java.util.List;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.command.admin.kubernetes.version.AddKubernetesSupportedVersionCmd;
|
||||
import org.apache.cloudstack.api.command.admin.kubernetes.version.DeleteKubernetesSupportedVersionCmd;
|
||||
|
|
@ -31,6 +32,7 @@ import org.apache.cloudstack.api.command.user.iso.RegisterIsoCmd;
|
|||
import org.apache.cloudstack.api.command.user.kubernetes.version.ListKubernetesSupportedVersionsCmd;
|
||||
import org.apache.cloudstack.api.response.KubernetesSupportedVersionResponse;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.cloud.api.query.dao.TemplateJoinDao;
|
||||
|
|
@ -51,6 +53,7 @@ import com.cloud.storage.dao.VMTemplateZoneDao;
|
|||
import com.cloud.template.TemplateApiService;
|
||||
import com.cloud.template.VirtualMachineTemplate;
|
||||
import com.cloud.user.AccountManager;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.component.ComponentContext;
|
||||
import com.cloud.utils.component.ManagerBase;
|
||||
import com.cloud.utils.db.Filter;
|
||||
|
|
@ -117,13 +120,14 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
|
|||
return response;
|
||||
}
|
||||
|
||||
private ListResponse<KubernetesSupportedVersionResponse> createKubernetesSupportedVersionListResponse(List<KubernetesSupportedVersionVO> versions) {
|
||||
private ListResponse<KubernetesSupportedVersionResponse> createKubernetesSupportedVersionListResponse(
|
||||
List<KubernetesSupportedVersionVO> versions, Integer count) {
|
||||
List<KubernetesSupportedVersionResponse> responseList = new ArrayList<>();
|
||||
for (KubernetesSupportedVersionVO version : versions) {
|
||||
responseList.add(createKubernetesSupportedVersionResponse(version));
|
||||
}
|
||||
ListResponse<KubernetesSupportedVersionResponse> response = new ListResponse<>();
|
||||
response.setResponses(responseList);
|
||||
response.setResponses(responseList, count);
|
||||
return response;
|
||||
}
|
||||
|
||||
|
|
@ -157,6 +161,7 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
|
|||
|
||||
private VirtualMachineTemplate registerKubernetesVersionIso(final Long zoneId, final String versionName, final String isoUrl, final String isoChecksum, final boolean directDownload) throws IllegalAccessException, NoSuchFieldException,
|
||||
IllegalArgumentException, ResourceAllocationException {
|
||||
CallContext.register(CallContext.current(), ApiCommandResourceType.Iso);
|
||||
String isoName = String.format("%s-Kubernetes-Binaries-ISO", versionName);
|
||||
RegisterIsoCmd registerIsoCmd = new RegisterIsoCmd();
|
||||
registerIsoCmd = ComponentContext.inject(registerIsoCmd);
|
||||
|
|
@ -174,15 +179,25 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
|
|||
registerIsoCmd.setDirectDownload(directDownload);
|
||||
registerIsoCmd.setAccountName(accountManager.getSystemAccount().getAccountName());
|
||||
registerIsoCmd.setDomainId(accountManager.getSystemAccount().getDomainId());
|
||||
return templateService.registerIso(registerIsoCmd);
|
||||
try {
|
||||
return templateService.registerIso(registerIsoCmd);
|
||||
} finally {
|
||||
CallContext.unregister();
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteKubernetesVersionIso(long templateId) throws IllegalAccessException, NoSuchFieldException,
|
||||
IllegalArgumentException {
|
||||
CallContext isoContext = CallContext.register(CallContext.current(), ApiCommandResourceType.Iso);
|
||||
isoContext.setEventResourceId(templateId);
|
||||
DeleteIsoCmd deleteIsoCmd = new DeleteIsoCmd();
|
||||
deleteIsoCmd = ComponentContext.inject(deleteIsoCmd);
|
||||
deleteIsoCmd.setId(templateId);
|
||||
templateService.deleteIso(deleteIsoCmd);
|
||||
try {
|
||||
templateService.deleteIso(deleteIsoCmd);
|
||||
} finally {
|
||||
CallContext.unregister();
|
||||
}
|
||||
}
|
||||
|
||||
public static int compareSemanticVersions(String v1, String v2) throws IllegalArgumentException {
|
||||
|
|
@ -278,14 +293,17 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
|
|||
if(keyword != null){
|
||||
sc.setParameters("keyword", "%" + keyword + "%");
|
||||
}
|
||||
List <KubernetesSupportedVersionVO> versions = kubernetesSupportedVersionDao.search(sc, searchFilter);
|
||||
versions = filterKubernetesSupportedVersions(versions, minimumSemanticVersion);
|
||||
Pair<List<KubernetesSupportedVersionVO>, Integer> versionsAndCount =
|
||||
kubernetesSupportedVersionDao.searchAndCount(sc, searchFilter);
|
||||
List<KubernetesSupportedVersionVO> versions =
|
||||
filterKubernetesSupportedVersions(versionsAndCount.first(), minimumSemanticVersion);
|
||||
|
||||
return createKubernetesSupportedVersionListResponse(versions);
|
||||
return createKubernetesSupportedVersionListResponse(versions, versionsAndCount.second());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = KubernetesVersionEventTypes.EVENT_KUBERNETES_VERSION_ADD, eventDescription = "Adding Kubernetes supported version")
|
||||
@ActionEvent(eventType = KubernetesVersionEventTypes.EVENT_KUBERNETES_VERSION_ADD,
|
||||
eventDescription = "Adding Kubernetes supported version")
|
||||
public KubernetesSupportedVersionResponse addKubernetesSupportedVersion(final AddKubernetesSupportedVersionCmd cmd) {
|
||||
if (!KubernetesClusterService.KubernetesServiceEnabled.value()) {
|
||||
throw new CloudRuntimeException("Kubernetes Service plugin is disabled");
|
||||
|
|
@ -337,12 +355,14 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
|
|||
|
||||
KubernetesSupportedVersionVO supportedVersionVO = new KubernetesSupportedVersionVO(name, semanticVersion, template.getId(), zoneId, minimumCpu, minimumRamSize);
|
||||
supportedVersionVO = kubernetesSupportedVersionDao.persist(supportedVersionVO);
|
||||
CallContext.current().putContextParameter(KubernetesSupportedVersion.class, supportedVersionVO.getUuid());
|
||||
|
||||
return createKubernetesSupportedVersionResponse(supportedVersionVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = KubernetesVersionEventTypes.EVENT_KUBERNETES_VERSION_DELETE, eventDescription = "Deleting Kubernetes supported version", async = true)
|
||||
@ActionEvent(eventType = KubernetesVersionEventTypes.EVENT_KUBERNETES_VERSION_DELETE,
|
||||
eventDescription = "deleting Kubernetes supported version", async = true)
|
||||
public boolean deleteKubernetesSupportedVersion(final DeleteKubernetesSupportedVersionCmd cmd) {
|
||||
if (!KubernetesClusterService.KubernetesServiceEnabled.value()) {
|
||||
throw new CloudRuntimeException("Kubernetes Service plugin is disabled");
|
||||
|
|
@ -373,7 +393,8 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
|
|||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = KubernetesVersionEventTypes.EVENT_KUBERNETES_VERSION_UPDATE, eventDescription = "Updating Kubernetes supported version")
|
||||
@ActionEvent(eventType = KubernetesVersionEventTypes.EVENT_KUBERNETES_VERSION_UPDATE,
|
||||
eventDescription = "Updating Kubernetes supported version")
|
||||
public KubernetesSupportedVersionResponse updateKubernetesSupportedVersion(final UpdateKubernetesSupportedVersionCmd cmd) {
|
||||
if (!KubernetesClusterService.KubernetesServiceEnabled.value()) {
|
||||
throw new CloudRuntimeException("Kubernetes Service plugin is disabled");
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import javax.inject.Inject;
|
|||
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiErrorCode;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
|
|
@ -134,6 +135,11 @@ public class AddKubernetesSupportedVersionCmd extends BaseCmd implements AdminCm
|
|||
return CallContext.current().getCallingAccountId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCommandResourceType getApiResourceType() {
|
||||
return ApiCommandResourceType.KubernetesSupportedVersion;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import javax.inject.Inject;
|
|||
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiErrorCode;
|
||||
import org.apache.cloudstack.api.BaseAsyncCmd;
|
||||
|
|
@ -85,6 +86,11 @@ public class DeleteKubernetesSupportedVersionCmd extends BaseAsyncCmd implements
|
|||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCommandResourceType getApiResourceType() {
|
||||
return ApiCommandResourceType.KubernetesSupportedVersion;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import javax.inject.Inject;
|
|||
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiErrorCode;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
|
|
@ -29,6 +30,7 @@ import org.apache.cloudstack.api.ResponseObject;
|
|||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.command.admin.AdminCmd;
|
||||
import org.apache.cloudstack.api.response.KubernetesSupportedVersionResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
|
||||
import com.cloud.exception.ConcurrentOperationException;
|
||||
import com.cloud.kubernetes.version.KubernetesSupportedVersion;
|
||||
|
|
@ -73,7 +75,17 @@ public class UpdateKubernetesSupportedVersionCmd extends BaseCmd implements Admi
|
|||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return 0;
|
||||
return CallContext.current().getCallingAccountId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCommandResourceType getApiResourceType() {
|
||||
return ApiCommandResourceType.KubernetesSupportedVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getApiResourceId() {
|
||||
return getId();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -272,26 +272,23 @@ public class CreateKubernetesClusterCmd extends BaseAsyncCreateCmd {
|
|||
|
||||
@Override
|
||||
public String getCreateEventDescription() {
|
||||
return "creating Kubernetes cluster";
|
||||
return "Creating Kubernetes cluster";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventDescription() {
|
||||
return "Creating Kubernetes cluster. Cluster Id: " + getEntityId();
|
||||
return "Creating Kubernetes cluster Id: " + getEntityId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCommandResourceType getApiResourceType() {
|
||||
return ApiCommandResourceType.VirtualMachine;
|
||||
return ApiCommandResourceType.KubernetesCluster;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
try {
|
||||
if (KubernetesCluster.ClusterType.valueOf(getClusterType()) == KubernetesCluster.ClusterType.CloudManaged
|
||||
&& !kubernetesClusterService.startKubernetesCluster(getEntityId(), true)) {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to start Kubernetes cluster");
|
||||
}
|
||||
kubernetesClusterService.startKubernetesCluster(this);
|
||||
KubernetesClusterResponse response = kubernetesClusterService.createKubernetesClusterResponse(getEntityId());
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import javax.inject.Inject;
|
|||
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiErrorCode;
|
||||
import org.apache.cloudstack.api.BaseAsyncCmd;
|
||||
|
|
@ -112,6 +113,16 @@ public class DeleteKubernetesClusterCmd extends BaseAsyncCmd {
|
|||
return KubernetesClusterEventTypes.EVENT_KUBERNETES_CLUSTER_DELETE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCommandResourceType getApiResourceType() {
|
||||
return ApiCommandResourceType.KubernetesCluster;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getApiResourceId() {
|
||||
return getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventDescription() {
|
||||
String description = "Deleting Kubernetes cluster";
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import com.cloud.kubernetes.cluster.KubernetesClusterService;
|
|||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiErrorCode;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
|
|
@ -83,6 +84,11 @@ public class RemoveVirtualMachinesFromKubernetesClusterCmd extends BaseListCmd {
|
|||
return CallContext.current().getCallingAccount().getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCommandResourceType getApiResourceType() {
|
||||
return ApiCommandResourceType.KubernetesCluster;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws ServerApiException {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import org.apache.cloudstack.acl.RoleType;
|
|||
import org.apache.cloudstack.acl.SecurityChecker;
|
||||
import org.apache.cloudstack.api.ACL;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiErrorCode;
|
||||
import org.apache.cloudstack.api.BaseAsyncCmd;
|
||||
|
|
@ -144,6 +145,11 @@ public class ScaleKubernetesClusterCmd extends BaseAsyncCmd {
|
|||
return CallContext.current().getCallingAccount().getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCommandResourceType getApiResourceType() {
|
||||
return ApiCommandResourceType.KubernetesCluster;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import javax.inject.Inject;
|
|||
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiErrorCode;
|
||||
import org.apache.cloudstack.api.BaseAsyncCmd;
|
||||
|
|
@ -85,33 +86,20 @@ public class StartKubernetesClusterCmd extends BaseAsyncCmd {
|
|||
return CallContext.current().getCallingAccount().getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCommandResourceType getApiResourceType() {
|
||||
return ApiCommandResourceType.KubernetesCluster;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
public KubernetesCluster validateRequest() {
|
||||
if (getId() == null || getId() < 1L) {
|
||||
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid Kubernetes cluster ID provided");
|
||||
}
|
||||
final KubernetesCluster kubernetesCluster = kubernetesClusterService.findById(getId());
|
||||
if (kubernetesCluster == null) {
|
||||
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Given Kubernetes cluster was not found");
|
||||
}
|
||||
if (!kubernetesClusterService.isCommandSupported(kubernetesCluster, getActualCommandName())) {
|
||||
throw new ServerApiException(ApiErrorCode.PARAM_ERROR,
|
||||
String.format("Start kubernetes cluster is not supported for an externally managed cluster (%s)", kubernetesCluster.getName()));
|
||||
}
|
||||
return kubernetesCluster;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws ServerApiException, ConcurrentOperationException {
|
||||
final KubernetesCluster kubernetesCluster = validateRequest();
|
||||
try {
|
||||
if (!kubernetesClusterService.startKubernetesCluster(kubernetesCluster.getId(), false)) {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to start Kubernetes cluster ID: %d", getId()));
|
||||
}
|
||||
final KubernetesClusterResponse response = kubernetesClusterService.createKubernetesClusterResponse(kubernetesCluster.getId());
|
||||
kubernetesClusterService.startKubernetesCluster(this);
|
||||
final KubernetesClusterResponse response = kubernetesClusterService.createKubernetesClusterResponse(getId());
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
} catch (CloudRuntimeException ex) {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import javax.inject.Inject;
|
|||
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiErrorCode;
|
||||
import org.apache.cloudstack.api.BaseAsyncCmd;
|
||||
|
|
@ -86,6 +87,11 @@ public class StopKubernetesClusterCmd extends BaseAsyncCmd {
|
|||
return CallContext.current().getCallingAccount().getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCommandResourceType getApiResourceType() {
|
||||
return ApiCommandResourceType.KubernetesCluster;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import javax.inject.Inject;
|
|||
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiErrorCode;
|
||||
import org.apache.cloudstack.api.BaseAsyncCmd;
|
||||
|
|
@ -96,6 +97,11 @@ public class UpgradeKubernetesClusterCmd extends BaseAsyncCmd {
|
|||
return CallContext.current().getCallingAccount().getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCommandResourceType getApiResourceType() {
|
||||
return ApiCommandResourceType.KubernetesCluster;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -34,8 +34,8 @@
|
|||
<bean id="kubernetesClusterVmMapDaoImpl" class="com.cloud.kubernetes.cluster.dao.KubernetesClusterVmMapDaoImpl" />
|
||||
<bean id="kubernetesClusterManagerImpl" class="com.cloud.kubernetes.cluster.KubernetesClusterManagerImpl" />
|
||||
|
||||
<bean id="kubernetesClusterHelper" class="com.cloud.kubernetes.cluster.KubernetesClusterHelperImpl" >
|
||||
<property name="name" value="KubernetesClusterHelper" />
|
||||
<bean id="kubernetesServiceHelper" class="com.cloud.kubernetes.cluster.KubernetesServiceHelperImpl" >
|
||||
<property name="name" value="KubernetesServiceHelper" />
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
|
|
|
|||
|
|
@ -31,20 +31,20 @@ import com.cloud.utils.exception.CloudRuntimeException;
|
|||
import com.cloud.vm.UserVmManager;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class KubernetesClusterHelperImplTest {
|
||||
public class KubernetesServiceHelperImplTest {
|
||||
@Mock
|
||||
KubernetesClusterVmMapDao kubernetesClusterVmMapDao;
|
||||
@Mock
|
||||
KubernetesClusterDao kubernetesClusterDao;
|
||||
|
||||
@InjectMocks
|
||||
KubernetesClusterHelperImpl kubernetesClusterHelper = new KubernetesClusterHelperImpl();
|
||||
KubernetesServiceHelperImpl kubernetesServiceHelper = new KubernetesServiceHelperImpl();
|
||||
|
||||
@Test
|
||||
public void testCheckVmCanBeDestroyedNotCKSNode() {
|
||||
UserVm vm = Mockito.mock(UserVm.class);
|
||||
Mockito.when(vm.getUserVmType()).thenReturn("");
|
||||
kubernetesClusterHelper.checkVmCanBeDestroyed(vm);
|
||||
kubernetesServiceHelper.checkVmCanBeDestroyed(vm);
|
||||
Mockito.verify(kubernetesClusterVmMapDao, Mockito.never()).findByVmId(Mockito.anyLong());
|
||||
}
|
||||
|
||||
|
|
@ -54,7 +54,7 @@ public class KubernetesClusterHelperImplTest {
|
|||
Mockito.when(vm.getId()).thenReturn(1L);
|
||||
Mockito.when(vm.getUserVmType()).thenReturn(UserVmManager.CKS_NODE);
|
||||
Mockito.when(kubernetesClusterVmMapDao.findByVmId(1L)).thenReturn(null);
|
||||
kubernetesClusterHelper.checkVmCanBeDestroyed(vm);
|
||||
kubernetesServiceHelper.checkVmCanBeDestroyed(vm);
|
||||
}
|
||||
|
||||
@Test(expected = CloudRuntimeException.class)
|
||||
|
|
@ -66,6 +66,6 @@ public class KubernetesClusterHelperImplTest {
|
|||
Mockito.when(map.getClusterId()).thenReturn(1L);
|
||||
Mockito.when(kubernetesClusterVmMapDao.findByVmId(1L)).thenReturn(map);
|
||||
Mockito.when(kubernetesClusterDao.findById(1L)).thenReturn(Mockito.mock(KubernetesClusterVO.class));
|
||||
kubernetesClusterHelper.checkVmCanBeDestroyed(vm);
|
||||
kubernetesServiceHelper.checkVmCanBeDestroyed(vm);
|
||||
}
|
||||
}
|
||||
|
|
@ -35,6 +35,7 @@ import org.apache.cloudstack.api.response.ListResponse;
|
|||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
|
|
@ -66,6 +67,7 @@ import com.cloud.user.AccountManager;
|
|||
import com.cloud.user.AccountVO;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.user.UserVO;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.component.ComponentContext;
|
||||
import com.cloud.utils.db.Filter;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
|
|
@ -142,14 +144,13 @@ public class KubernetesVersionServiceTest {
|
|||
when(versionVO.getSemanticVersion()).thenReturn(KubernetesVersionService.MIN_KUBERNETES_VERSION);
|
||||
versionVOs.add(versionVO);
|
||||
when(kubernetesSupportedVersionDao.findById(Mockito.anyLong())).thenReturn(versionVO);
|
||||
when(kubernetesSupportedVersionDao.search(Mockito.any(SearchCriteria.class), Mockito.any(Filter.class))).thenReturn(versionVOs);
|
||||
ListResponse<KubernetesSupportedVersionResponse> response =
|
||||
kubernetesVersionService.listKubernetesSupportedVersions(
|
||||
cmd);
|
||||
Assert.assertNotNull(response);
|
||||
Assert.assertEquals(Integer.valueOf(1), response.getCount());
|
||||
Assert.assertEquals(1, response.getResponses().size());
|
||||
Assert.assertEquals(KubernetesVersionService.MIN_KUBERNETES_VERSION, response.getResponses().get(0).getSemanticVersion());
|
||||
when(kubernetesSupportedVersionDao.searchAndCount(Mockito.any(SearchCriteria.class),
|
||||
Mockito.any(Filter.class))).thenReturn(new Pair<>(versionVOs, versionVOs.size()));
|
||||
ListResponse<KubernetesSupportedVersionResponse> versionsResponse =
|
||||
kubernetesVersionService.listKubernetesSupportedVersions(cmd);
|
||||
Assert.assertEquals(versionVOs.size(), versionsResponse.getCount().intValue());
|
||||
Assert.assertTrue(CollectionUtils.isNotEmpty(versionsResponse.getResponses()));
|
||||
Assert.assertEquals(versionVOs.size(), versionsResponse.getResponses().size());
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
|
|
@ -214,9 +215,12 @@ public class KubernetesVersionServiceTest {
|
|||
when(cmd.getMinimumRamSize()).thenReturn(KubernetesClusterService.MIN_KUBERNETES_CLUSTER_NODE_RAM_SIZE);
|
||||
Account systemAccount = new AccountVO("system", 1L, "", Account.Type.ADMIN, "uuid");
|
||||
when(accountManager.getSystemAccount()).thenReturn(systemAccount);
|
||||
try (MockedStatic<ComponentContext> mockedComponentContext = Mockito.mockStatic(ComponentContext.class)) {
|
||||
CallContext callContext = Mockito.mock(CallContext.class);
|
||||
try (MockedStatic<ComponentContext> mockedComponentContext = Mockito.mockStatic(ComponentContext.class);
|
||||
MockedStatic<CallContext> mockedCallContext = Mockito.mockStatic(CallContext.class)) {
|
||||
mockedComponentContext.when(() -> ComponentContext.inject(Mockito.any(RegisterIsoCmd.class))).thenReturn(
|
||||
new RegisterIsoCmd());
|
||||
mockedCallContext.when(CallContext::current).thenReturn(callContext);
|
||||
|
||||
when(templateService.registerIso(Mockito.any(RegisterIsoCmd.class))).thenReturn(
|
||||
Mockito.mock(VirtualMachineTemplate.class));
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
|||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
import org.apache.cloudstack.storage.datastore.util.LinstorConfigurationManager;
|
||||
import org.apache.cloudstack.storage.datastore.util.LinstorUtil;
|
||||
import org.apache.cloudstack.storage.snapshot.SnapshotObject;
|
||||
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
|
||||
import org.apache.cloudstack.storage.to.VolumeObjectTO;
|
||||
import org.apache.cloudstack.storage.volume.VolumeObject;
|
||||
|
|
@ -231,7 +232,7 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
|
|||
|
||||
try
|
||||
{
|
||||
ApiCallRcList answers = linstorApi.resourceSnapshotDelete(rscDefName, snapshotName);
|
||||
ApiCallRcList answers = linstorApi.resourceSnapshotDelete(rscDefName, snapshotName, Collections.emptyList());
|
||||
if (answers.hasError())
|
||||
{
|
||||
for (ApiCallRc answer : answers)
|
||||
|
|
@ -1004,25 +1005,29 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
|
|||
* @param api Linstor Developer api object
|
||||
* @param pool StoragePool this resource resides on
|
||||
* @param rscName rscName of the snapshotted resource
|
||||
* @param snapshotInfo snapshot info of the snapshot
|
||||
* @param snapshotName Name of the snapshot to copy from
|
||||
* @param snapshotObject snapshot object of the origCmd, so the path can be modified
|
||||
* @param origCmd original LinstorBackupSnapshotCommand that needs to have a patched path
|
||||
* @return answer from agent operation
|
||||
* @throws ApiException if any Linstor api operation fails
|
||||
*/
|
||||
private Answer copyFromTemporaryResource(
|
||||
DevelopersApi api, StoragePoolVO pool, String rscName, SnapshotInfo snapshotInfo, CopyCommand origCmd)
|
||||
DevelopersApi api,
|
||||
StoragePoolVO pool,
|
||||
String rscName,
|
||||
String snapshotName,
|
||||
SnapshotObject snapshotObject,
|
||||
CopyCommand origCmd)
|
||||
throws ApiException {
|
||||
Answer answer;
|
||||
String restoreName = rscName + "-rst";
|
||||
String snapshotName = LinstorUtil.RSC_PREFIX + snapshotInfo.getUuid();
|
||||
String devName = restoreResourceFromSnapshot(api, pool, rscName, snapshotName, restoreName);
|
||||
|
||||
Optional<RemoteHostEndPoint> optEPAny = getLinstorEP(api, restoreName);
|
||||
if (optEPAny.isPresent()) {
|
||||
// patch the src device path to the temporary linstor resource
|
||||
SnapshotObjectTO soTO = (SnapshotObjectTO)snapshotInfo.getTO();
|
||||
soTO.setPath(devName);
|
||||
origCmd.setSrcTO(soTO);
|
||||
snapshotObject.setPath(devName);
|
||||
origCmd.setSrcTO(snapshotObject.getTO());
|
||||
answer = optEPAny.get().sendMessage(origCmd);
|
||||
} else{
|
||||
answer = new Answer(origCmd, false, "Unable to get matching Linstor endpoint.");
|
||||
|
|
@ -1032,13 +1037,36 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
|
|||
return answer;
|
||||
}
|
||||
|
||||
/**
|
||||
* vmsnapshots don't have our typical snapshot path set
|
||||
* instead the path is the internal snapshot name e.g.: {vm}_VS_{datestr}
|
||||
* we have to find out and modify the path here before
|
||||
* @return the original snapshotObject.getPath()
|
||||
*/
|
||||
private String setCorrectSnapshotPath(DevelopersApi api, String rscName, SnapshotObject snapshotObject)
|
||||
throws ApiException {
|
||||
String originalPath = LinstorUtil.RSC_PREFIX + snapshotObject.getUuid();
|
||||
if (!(snapshotObject.getPath().startsWith("/dev/mapper/") ||
|
||||
snapshotObject.getPath().startsWith("zfs://"))) {
|
||||
originalPath = snapshotObject.getPath();
|
||||
com.linbit.linstor.api.model.StoragePool linStoragePool =
|
||||
LinstorUtil.getDiskfulStoragePool(api, rscName);
|
||||
if (linStoragePool == null) {
|
||||
throw new CloudRuntimeException("Linstor: Unable to find storage pool for resource " + rscName);
|
||||
}
|
||||
final String path = LinstorUtil.getSnapshotPath(linStoragePool, rscName, snapshotObject.getPath());
|
||||
snapshotObject.setPath(path);
|
||||
}
|
||||
return originalPath;
|
||||
}
|
||||
|
||||
protected Answer copySnapshot(DataObject srcData, DataObject destData) {
|
||||
String value = _configDao.getValue(Config.BackupSnapshotWait.toString());
|
||||
int _backupsnapshotwait = NumbersUtil.parseInt(
|
||||
value, Integer.parseInt(Config.BackupSnapshotWait.getDefaultValue()));
|
||||
|
||||
SnapshotInfo snapshotInfo = (SnapshotInfo)srcData;
|
||||
Boolean snapshotFullBackup = snapshotInfo.getFullBackup();
|
||||
SnapshotObject snapshotObject = (SnapshotObject)srcData;
|
||||
Boolean snapshotFullBackup = snapshotObject.getFullBackup();
|
||||
final StoragePoolVO pool = _storagePoolDao.findById(srcData.getDataStore().getId());
|
||||
final DevelopersApi api = LinstorUtil.getLinstorAPI(pool.getHostAddress());
|
||||
boolean fullSnapshot = true;
|
||||
|
|
@ -1049,28 +1077,30 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
|
|||
options.put("fullSnapshot", fullSnapshot + "");
|
||||
options.put(SnapshotInfo.BackupSnapshotAfterTakingSnapshot.key(),
|
||||
String.valueOf(SnapshotInfo.BackupSnapshotAfterTakingSnapshot.value()));
|
||||
options.put("volumeSize", snapshotInfo.getBaseVolume().getSize() + "");
|
||||
options.put("volumeSize", snapshotObject.getBaseVolume().getSize() + "");
|
||||
|
||||
try {
|
||||
final String rscName = LinstorUtil.RSC_PREFIX + snapshotObject.getBaseVolume().getUuid();
|
||||
String snapshotName = setCorrectSnapshotPath(api, rscName, snapshotObject);
|
||||
|
||||
CopyCommand cmd = new LinstorBackupSnapshotCommand(
|
||||
srcData.getTO(),
|
||||
snapshotObject.getTO(),
|
||||
destData.getTO(),
|
||||
_backupsnapshotwait,
|
||||
VirtualMachineManager.ExecuteInSequence.value());
|
||||
cmd.setOptions(options);
|
||||
|
||||
String rscName = LinstorUtil.RSC_PREFIX + snapshotInfo.getBaseVolume().getUuid();
|
||||
Optional<RemoteHostEndPoint> optEP = getDiskfullEP(api, rscName);
|
||||
Answer answer;
|
||||
if (optEP.isPresent()) {
|
||||
answer = optEP.get().sendMessage(cmd);
|
||||
} else {
|
||||
logger.debug("No diskfull endpoint found to copy image, creating diskless endpoint");
|
||||
answer = copyFromTemporaryResource(api, pool, rscName, snapshotInfo, cmd);
|
||||
answer = copyFromTemporaryResource(api, pool, rscName, snapshotName, snapshotObject, cmd);
|
||||
}
|
||||
return answer;
|
||||
} catch (Exception e) {
|
||||
logger.debug("copy snapshot failed: ", e);
|
||||
logger.debug("copy snapshot failed, please cleanup snapshot manually: ", e);
|
||||
throw new CloudRuntimeException(e.toString());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -113,7 +113,8 @@ public class LinstorUtil {
|
|||
Collections.singletonList(storagePoolName),
|
||||
Collections.emptyList(),
|
||||
null,
|
||||
null
|
||||
null,
|
||||
true
|
||||
);
|
||||
return sps != null ? sps : Collections.emptyList();
|
||||
}
|
||||
|
|
@ -167,7 +168,8 @@ public class LinstorUtil {
|
|||
rscGrps.get(0).getSelectFilter().getStoragePoolList(),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
null,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,371 @@
|
|||
//
|
||||
//Licensed to the Apache Software Foundation (ASF) under one
|
||||
//or more contributor license agreements. See the NOTICE file
|
||||
//distributed with this work for additional information
|
||||
//regarding copyright ownership. The ASF licenses this file
|
||||
//to you under the Apache License, Version 2.0 (the
|
||||
//"License"); you may not use this file except in compliance
|
||||
//with the License. You may obtain a copy of the License at
|
||||
//
|
||||
//http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//Unless required by applicable law or agreed to in writing,
|
||||
//software distributed under the License is distributed on an
|
||||
//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
//KIND, either express or implied. See the License for the
|
||||
//specific language governing permissions and limitations
|
||||
//under the License.
|
||||
//
|
||||
package org.apache.cloudstack.storage.snapshot;
|
||||
|
||||
import com.linbit.linstor.api.ApiException;
|
||||
import com.linbit.linstor.api.DevelopersApi;
|
||||
import com.linbit.linstor.api.model.ApiCallRcList;
|
||||
import com.linbit.linstor.api.model.CreateMultiSnapshotRequest;
|
||||
import com.linbit.linstor.api.model.Snapshot;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.agent.api.VMSnapshotTO;
|
||||
import com.cloud.event.EventTypes;
|
||||
import com.cloud.event.UsageEventUtils;
|
||||
import com.cloud.storage.DiskOfferingVO;
|
||||
import com.cloud.storage.VolumeVO;
|
||||
import com.cloud.storage.dao.DiskOfferingDao;
|
||||
import com.cloud.storage.dao.VolumeDao;
|
||||
import com.cloud.uservm.UserVm;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.utils.fsm.NoTransitionException;
|
||||
import com.cloud.vm.UserVmVO;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import com.cloud.vm.dao.UserVmDao;
|
||||
import com.cloud.vm.snapshot.VMSnapshot;
|
||||
import com.cloud.vm.snapshot.VMSnapshotVO;
|
||||
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority;
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
import org.apache.cloudstack.storage.datastore.util.LinstorUtil;
|
||||
import org.apache.cloudstack.storage.to.VolumeObjectTO;
|
||||
import org.apache.cloudstack.storage.vmsnapshot.DefaultVMSnapshotStrategy;
|
||||
import org.apache.cloudstack.storage.vmsnapshot.VMSnapshotHelper;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class LinstorVMSnapshotStrategy extends DefaultVMSnapshotStrategy {
|
||||
private static final Logger log = Logger.getLogger(LinstorVMSnapshotStrategy.class);
|
||||
|
||||
@Inject
|
||||
private VMSnapshotHelper _vmSnapshotHelper;
|
||||
@Inject
|
||||
private UserVmDao _userVmDao;
|
||||
@Inject
|
||||
private VMSnapshotDao vmSnapshotDao;
|
||||
@Inject
|
||||
private VolumeDao volumeDao;
|
||||
@Inject
|
||||
private DiskOfferingDao diskOfferingDao;
|
||||
@Inject
|
||||
private PrimaryDataStoreDao _storagePoolDao;
|
||||
|
||||
private void linstorCreateMultiSnapshot(
|
||||
DevelopersApi api, VMSnapshotVO vmSnapshotVO, List<VolumeObjectTO> volumeTOs)
|
||||
throws ApiException {
|
||||
CreateMultiSnapshotRequest cmsReq = new CreateMultiSnapshotRequest();
|
||||
for (VolumeObjectTO vol : volumeTOs) {
|
||||
Snapshot snap = new Snapshot();
|
||||
snap.setName(vmSnapshotVO.getName());
|
||||
snap.setResourceName(LinstorUtil.RSC_PREFIX + vol.getPath());
|
||||
log.debug(String.format("Add volume %s;%s to snapshot", vol.getName(), snap.getResourceName()));
|
||||
cmsReq.addSnapshotsItem(snap);
|
||||
}
|
||||
log.debug(String.format("Creating multi snapshot %s", vmSnapshotVO.getName()));
|
||||
ApiCallRcList answers = api.createMultiSnapshot(cmsReq);
|
||||
log.debug(String.format("Created multi snapshot %s", vmSnapshotVO.getName()));
|
||||
if (answers.hasError()) {
|
||||
throw new CloudRuntimeException(
|
||||
"Error creating vm snapshots: " + LinstorUtil.getBestErrorMessage(answers));
|
||||
}
|
||||
}
|
||||
|
||||
private VMSnapshotVO findAndSetCurrentSnapshot(long vmId, VMSnapshotVO vmSnapshotVO) {
|
||||
VMSnapshotTO current = null;
|
||||
VMSnapshotVO currentSnapshot = vmSnapshotDao.findCurrentSnapshotByVmId(vmId);
|
||||
if (currentSnapshot != null) {
|
||||
current = _vmSnapshotHelper.getSnapshotWithParents(currentSnapshot);
|
||||
}
|
||||
|
||||
if (current == null) {
|
||||
vmSnapshotVO.setParent(null);
|
||||
} else {
|
||||
vmSnapshotVO.setParent(current.getId());
|
||||
}
|
||||
|
||||
return vmSnapshotVO;
|
||||
}
|
||||
|
||||
private long getNewChainSizeAndPublishCreate(VMSnapshot vmSnapshot, List<VolumeObjectTO> volumeTOs, UserVm userVm) {
|
||||
long new_chain_size = 0;
|
||||
for (VolumeObjectTO volumeObjectTO : volumeTOs) {
|
||||
publishUsageEvents(EventTypes.EVENT_VM_SNAPSHOT_CREATE, vmSnapshot, userVm, volumeObjectTO);
|
||||
new_chain_size += volumeObjectTO.getSize();
|
||||
log.info("EventTypes.EVENT_VM_SNAPSHOT_CREATE publishUsageEvent" + volumeObjectTO);
|
||||
}
|
||||
return new_chain_size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VMSnapshot takeVMSnapshot(VMSnapshot vmSnapshot) {
|
||||
log.info("Take vm snapshot: " + vmSnapshot.getName());
|
||||
UserVm userVm = _userVmDao.findById(vmSnapshot.getVmId());
|
||||
VMSnapshotVO vmSnapshotVO = (VMSnapshotVO) vmSnapshot;
|
||||
|
||||
try {
|
||||
_vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshotVO, VMSnapshot.Event.CreateRequested);
|
||||
} catch (NoTransitionException e) {
|
||||
throw new CloudRuntimeException("No transition: " + e.getMessage());
|
||||
}
|
||||
|
||||
boolean result = false;
|
||||
try {
|
||||
final List<VolumeObjectTO> volumeTOs = _vmSnapshotHelper.getVolumeTOList(userVm.getId());
|
||||
final StoragePoolVO storagePool = _storagePoolDao.findById(volumeTOs.get(0).getPoolId());
|
||||
final DevelopersApi api = LinstorUtil.getLinstorAPI(storagePool.getHostAddress());
|
||||
|
||||
long prev_chain_size = 0;
|
||||
long virtual_size = 0;
|
||||
for (VolumeObjectTO volume : volumeTOs) {
|
||||
virtual_size += volume.getSize();
|
||||
VolumeVO volumeVO = volumeDao.findById(volume.getId());
|
||||
prev_chain_size += volumeVO.getVmSnapshotChainSize() == null ? 0 : volumeVO.getVmSnapshotChainSize();
|
||||
}
|
||||
|
||||
findAndSetCurrentSnapshot(userVm.getId(), vmSnapshotVO);
|
||||
|
||||
linstorCreateMultiSnapshot(api, vmSnapshotVO, volumeTOs);
|
||||
|
||||
log.debug(String.format("finalize vm snapshot create for %s", vmSnapshotVO.getName()));
|
||||
finalizeCreate(vmSnapshotVO, volumeTOs);
|
||||
|
||||
result = _vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationSucceeded);
|
||||
long new_chain_size = getNewChainSizeAndPublishCreate(vmSnapshot, volumeTOs, userVm);
|
||||
publishUsageEvents(
|
||||
EventTypes.EVENT_VM_SNAPSHOT_ON_PRIMARY, vmSnapshot, userVm, new_chain_size - prev_chain_size, virtual_size);
|
||||
return vmSnapshot;
|
||||
} catch (Exception e) {
|
||||
log.debug("Could not create VM snapshot:" + e.getMessage());
|
||||
throw new CloudRuntimeException("Could not create VM snapshot:" + e.getMessage());
|
||||
} finally {
|
||||
if (!result) {
|
||||
try {
|
||||
_vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationFailed);
|
||||
log.info(String.format("VMSnapshot.Event.OperationFailed vmSnapshot=%s", vmSnapshot));
|
||||
} catch (NoTransitionException nte) {
|
||||
log.error("Cannot set vm state:" + nte.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StrategyPriority canHandle(Long vmId, Long rootPoolId, boolean snapshotMemory) {
|
||||
if (snapshotMemory) {
|
||||
return StrategyPriority.CANT_HANDLE;
|
||||
}
|
||||
return allVolumesOnLinstor(vmId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StrategyPriority canHandle(VMSnapshot vmSnapshot) {
|
||||
VMSnapshotVO vmSnapshotVO = (VMSnapshotVO) vmSnapshot;
|
||||
if (vmSnapshotVO.getType() != VMSnapshot.Type.Disk) {
|
||||
return StrategyPriority.CANT_HANDLE;
|
||||
}
|
||||
return allVolumesOnLinstor(vmSnapshot.getVmId());
|
||||
}
|
||||
|
||||
private StrategyPriority allVolumesOnLinstor(Long vmId) {
|
||||
List<VolumeObjectTO> volumeTOs = _vmSnapshotHelper.getVolumeTOList(vmId);
|
||||
if (volumeTOs == null || volumeTOs.isEmpty()) {
|
||||
return StrategyPriority.CANT_HANDLE;
|
||||
}
|
||||
for (VolumeObjectTO volumeTO : volumeTOs) {
|
||||
Long poolId = volumeTO.getPoolId();
|
||||
StoragePoolVO pool = _storagePoolDao.findById(poolId);
|
||||
if (!pool.getStorageProviderName().equals(LinstorUtil.PROVIDER_NAME)) {
|
||||
return StrategyPriority.CANT_HANDLE;
|
||||
}
|
||||
}
|
||||
return StrategyPriority.HIGHEST;
|
||||
}
|
||||
|
||||
private String linstorDeleteSnapshot(final DevelopersApi api, final String rscName, final String snapshotName) {
|
||||
String resultMsg = null;
|
||||
try {
|
||||
ApiCallRcList answers = api.resourceSnapshotDelete(rscName, snapshotName, Collections.emptyList());
|
||||
if (answers.hasError()) {
|
||||
resultMsg = LinstorUtil.getBestErrorMessage(answers);
|
||||
}
|
||||
} catch (ApiException apiEx) {
|
||||
log.error("Linstor: ApiEx - " + apiEx.getBestMessage());
|
||||
resultMsg = apiEx.getBestMessage();
|
||||
}
|
||||
|
||||
return resultMsg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteVMSnapshot(VMSnapshot vmSnapshot) {
|
||||
UserVmVO userVm = _userVmDao.findById(vmSnapshot.getVmId());
|
||||
VMSnapshotVO vmSnapshotVO = (VMSnapshotVO) vmSnapshot;
|
||||
try {
|
||||
_vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.ExpungeRequested);
|
||||
} catch (NoTransitionException e) {
|
||||
log.debug("Failed to change vm snapshot state with event ExpungeRequested");
|
||||
throw new CloudRuntimeException(
|
||||
"Failed to change vm snapshot state with event ExpungeRequested: " + e.getMessage());
|
||||
}
|
||||
|
||||
List<VolumeObjectTO> volumeTOs = _vmSnapshotHelper.getVolumeTOList(vmSnapshot.getVmId());
|
||||
final StoragePoolVO storagePool = _storagePoolDao.findById(volumeTOs.get(0).getPoolId());
|
||||
final DevelopersApi api = LinstorUtil.getLinstorAPI(storagePool.getHostAddress());
|
||||
|
||||
final String snapshotName = vmSnapshotVO.getName();
|
||||
final List<String> failedToDelete = new ArrayList<>();
|
||||
for (VolumeObjectTO volumeObjectTO : volumeTOs) {
|
||||
final String rscName = LinstorUtil.RSC_PREFIX + volumeObjectTO.getUuid();
|
||||
String err = linstorDeleteSnapshot(api, rscName, snapshotName);
|
||||
|
||||
if (err != null)
|
||||
{
|
||||
String errMsg = String.format("Unable to delete Linstor resource %s snapshot %s: %s",
|
||||
rscName, snapshotName, err);
|
||||
log.error(errMsg);
|
||||
failedToDelete.add(errMsg);
|
||||
}
|
||||
log.info("Linstor: Deleted snapshot " + snapshotName + " for resource " + rscName);
|
||||
}
|
||||
|
||||
if (!failedToDelete.isEmpty()) {
|
||||
throw new CloudRuntimeException(StringUtils.join(failedToDelete, "\n"));
|
||||
}
|
||||
|
||||
finalizeDelete(vmSnapshotVO, volumeTOs);
|
||||
vmSnapshotDao.remove(vmSnapshot.getId());
|
||||
|
||||
long full_chain_size = 0;
|
||||
for (VolumeObjectTO volumeTo : volumeTOs) {
|
||||
publishUsageEvents(EventTypes.EVENT_VM_SNAPSHOT_DELETE, vmSnapshot, userVm, volumeTo);
|
||||
full_chain_size += volumeTo.getSize();
|
||||
}
|
||||
publishUsageEvents(EventTypes.EVENT_VM_SNAPSHOT_OFF_PRIMARY, vmSnapshot, userVm, full_chain_size, 0L);
|
||||
return true;
|
||||
}
|
||||
|
||||
private String linstorRevertSnapshot(final DevelopersApi api, final String rscName, final String snapshotName) {
|
||||
String resultMsg = null;
|
||||
try {
|
||||
ApiCallRcList answers = api.resourceSnapshotRollback(rscName, snapshotName);
|
||||
if (answers.hasError()) {
|
||||
resultMsg = LinstorUtil.getBestErrorMessage(answers);
|
||||
}
|
||||
} catch (ApiException apiEx) {
|
||||
log.error("Linstor: ApiEx - " + apiEx.getBestMessage());
|
||||
resultMsg = apiEx.getBestMessage();
|
||||
}
|
||||
|
||||
return resultMsg;
|
||||
}
|
||||
|
||||
private boolean revertVMSnapshotOperation(VMSnapshot vmSnapshot, long userVmId) throws NoTransitionException {
|
||||
VMSnapshotVO vmSnapshotVO = (VMSnapshotVO) vmSnapshot;
|
||||
List<VolumeObjectTO> volumeTOs = _vmSnapshotHelper.getVolumeTOList(userVmId);
|
||||
|
||||
final StoragePoolVO storagePool = _storagePoolDao.findById(volumeTOs.get(0).getPoolId());
|
||||
final DevelopersApi api = LinstorUtil.getLinstorAPI(storagePool.getHostAddress());
|
||||
final String snapshotName = vmSnapshotVO.getName();
|
||||
|
||||
for (VolumeObjectTO volumeObjectTO : volumeTOs) {
|
||||
final String rscName = LinstorUtil.RSC_PREFIX + volumeObjectTO.getUuid();
|
||||
String err = linstorRevertSnapshot(api, rscName, snapshotName);
|
||||
if (err != null) {
|
||||
throw new CloudRuntimeException(String.format(
|
||||
"Unable to revert Linstor resource %s with snapshot %s: %s", rscName, snapshotName, err));
|
||||
}
|
||||
}
|
||||
finalizeRevert(vmSnapshotVO, volumeTOs);
|
||||
return _vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationSucceeded);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean revertVMSnapshot(VMSnapshot vmSnapshot) {
|
||||
log.debug("Revert vm snapshot: " + vmSnapshot.getName());
|
||||
VMSnapshotVO vmSnapshotVO = (VMSnapshotVO) vmSnapshot;
|
||||
UserVmVO userVm = _userVmDao.findById(vmSnapshot.getVmId());
|
||||
|
||||
if (userVm.getState() == VirtualMachine.State.Running && vmSnapshotVO.getType() == VMSnapshot.Type.Disk) {
|
||||
throw new CloudRuntimeException("Virtual machine should be in stopped state for revert operation");
|
||||
}
|
||||
|
||||
try {
|
||||
_vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshotVO, VMSnapshot.Event.RevertRequested);
|
||||
} catch (NoTransitionException e) {
|
||||
throw new CloudRuntimeException(e.getMessage());
|
||||
}
|
||||
|
||||
boolean result = false;
|
||||
try {
|
||||
result = revertVMSnapshotOperation(vmSnapshot, userVm.getId());
|
||||
} catch (CloudRuntimeException | NoTransitionException e) {
|
||||
String errMsg = String.format(
|
||||
"Error while finalize create vm snapshot [%s] due to %s", vmSnapshot.getName(), e.getMessage());
|
||||
log.error(errMsg, e);
|
||||
throw new CloudRuntimeException(errMsg);
|
||||
} finally {
|
||||
if (!result) {
|
||||
try {
|
||||
_vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationFailed);
|
||||
} catch (NoTransitionException e1) {
|
||||
log.error("Cannot set vm snapshot state due to: " + e1.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void publishUsageEvents(String type, VMSnapshot vmSnapshot, UserVm userVm, VolumeObjectTO volumeTo) {
|
||||
VolumeVO volume = volumeDao.findById(volumeTo.getId());
|
||||
Long diskOfferingId = volume.getDiskOfferingId();
|
||||
Long offeringId = null;
|
||||
if (diskOfferingId != null) {
|
||||
DiskOfferingVO offering = diskOfferingDao.findById(diskOfferingId);
|
||||
if (offering != null && offering.isComputeOnly()) {
|
||||
offeringId = offering.getId();
|
||||
}
|
||||
}
|
||||
UsageEventUtils.publishUsageEvent(type, vmSnapshot.getAccountId(), userVm.getDataCenterId(), userVm.getId(),
|
||||
vmSnapshot.getName(), offeringId, volume.getId(), volumeTo.getSize(), VMSnapshot.class.getName(),
|
||||
vmSnapshot.getUuid());
|
||||
}
|
||||
|
||||
private void publishUsageEvents(
|
||||
String type,
|
||||
VMSnapshot vmSnapshot,
|
||||
UserVm userVm,
|
||||
Long vmSnapSize,
|
||||
Long virtualSize) {
|
||||
try {
|
||||
UsageEventUtils.publishUsageEvent(type, vmSnapshot.getAccountId(), userVm.getDataCenterId(), userVm.getId(),
|
||||
vmSnapshot.getName(), 0L, 0L, vmSnapSize, virtualSize, VMSnapshot.class.getName(),
|
||||
vmSnapshot.getUuid());
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to publish usage event " + type, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -29,6 +29,8 @@
|
|||
|
||||
<bean id="linstorPrimaryDataStoreProviderImpl"
|
||||
class="org.apache.cloudstack.storage.datastore.provider.LinstorPrimaryDatastoreProviderImpl" />
|
||||
<bean id="linstorSnapshotStrategy"
|
||||
class="org.apache.cloudstack.storage.snapshot.LinstorVMSnapshotStrategy" />
|
||||
<bean id="linstorConfigManager"
|
||||
class="org.apache.cloudstack.storage.datastore.util.LinstorConfigurationManager" />
|
||||
</beans>
|
||||
|
|
|
|||
|
|
@ -142,6 +142,14 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
|
|||
return responseObject;
|
||||
}
|
||||
|
||||
protected void checkAndFailOnMissingSAMLSignature(Signature signature) {
|
||||
if (signature == null && SAML2AuthManager.SAMLCheckSignature.value()) {
|
||||
logger.error("Failing SAML login due to missing signature in the SAML response and signature check is enforced. " +
|
||||
"Please check and ensure the IDP configuration has signing certificate or relax the saml2.check.signature setting.");
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Signature is missing from the SAML Response. Please contact the Administrator");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String authenticate(final String command, final Map<String, Object[]> params, final HttpSession session, final InetAddress remoteAddress, final String responseType, final StringBuilder auditTrailSb, final HttpServletRequest req, final HttpServletResponse resp) throws ServerApiException {
|
||||
try {
|
||||
|
|
@ -223,6 +231,7 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
|
|||
session.setAttribute(SAMLPluginConstants.SAML_IDPID, issuer.getValue());
|
||||
|
||||
Signature sig = processedSAMLResponse.getSignature();
|
||||
checkAndFailOnMissingSAMLSignature(sig);
|
||||
if (idpMetadata.getSigningCertificate() != null && sig != null) {
|
||||
BasicX509Credential credential = new BasicX509Credential();
|
||||
credential.setEntityCertificate(idpMetadata.getSigningCertificate());
|
||||
|
|
@ -236,9 +245,8 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
|
|||
params, responseType));
|
||||
}
|
||||
}
|
||||
if (username == null) {
|
||||
username = SAMLUtils.getValueFromAssertions(processedSAMLResponse.getAssertions(), SAML2AuthManager.SAMLUserAttributeName.value());
|
||||
}
|
||||
|
||||
username = SAMLUtils.getValueFromAssertions(processedSAMLResponse.getAssertions(), SAML2AuthManager.SAMLUserAttributeName.value());
|
||||
|
||||
for (Assertion assertion: processedSAMLResponse.getAssertions()) {
|
||||
if (assertion!= null && assertion.getSubject() != null && assertion.getSubject().getNameID() != null) {
|
||||
|
|
@ -270,6 +278,7 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
|
|||
continue;
|
||||
}
|
||||
Signature encSig = assertion.getSignature();
|
||||
checkAndFailOnMissingSAMLSignature(encSig);
|
||||
if (idpMetadata.getSigningCertificate() != null && encSig != null) {
|
||||
BasicX509Credential sigCredential = new BasicX509Credential();
|
||||
sigCredential.setEntityCertificate(idpMetadata.getSigningCertificate());
|
||||
|
|
|
|||
|
|
@ -25,51 +25,54 @@ import java.util.Collection;
|
|||
|
||||
public interface SAML2AuthManager extends PluggableAPIAuthenticator, PluggableService {
|
||||
|
||||
public static final ConfigKey<Boolean> SAMLIsPluginEnabled = new ConfigKey<Boolean>("Advanced", Boolean.class, "saml2.enabled", "false",
|
||||
ConfigKey<Boolean> SAMLIsPluginEnabled = new ConfigKey<Boolean>("Advanced", Boolean.class, "saml2.enabled", "false",
|
||||
"Indicates whether SAML SSO plugin is enabled or not", true);
|
||||
|
||||
public static final ConfigKey<String> SAMLServiceProviderID = new ConfigKey<String>("Advanced", String.class, "saml2.sp.id", "org.apache.cloudstack",
|
||||
ConfigKey<String> SAMLServiceProviderID = new ConfigKey<String>("Advanced", String.class, "saml2.sp.id", "org.apache.cloudstack",
|
||||
"SAML2 Service Provider Identifier String", true);
|
||||
|
||||
public static final ConfigKey<String> SAMLServiceProviderContactPersonName = new ConfigKey<String>("Advanced", String.class, "saml2.sp.contact.person", "CloudStack Developers",
|
||||
ConfigKey<String> SAMLServiceProviderContactPersonName = new ConfigKey<String>("Advanced", String.class, "saml2.sp.contact.person", "CloudStack Developers",
|
||||
"SAML2 Service Provider Contact Person Name", true);
|
||||
|
||||
public static final ConfigKey<String> SAMLServiceProviderContactEmail = new ConfigKey<String>("Advanced", String.class, "saml2.sp.contact.email", "dev@cloudstack.apache.org",
|
||||
ConfigKey<String> SAMLServiceProviderContactEmail = new ConfigKey<String>("Advanced", String.class, "saml2.sp.contact.email", "dev@cloudstack.apache.org",
|
||||
"SAML2 Service Provider Contact Email Address", true);
|
||||
|
||||
public static final ConfigKey<String> SAMLServiceProviderOrgName = new ConfigKey<String>("Advanced", String.class, "saml2.sp.org.name", "Apache CloudStack",
|
||||
ConfigKey<String> SAMLServiceProviderOrgName = new ConfigKey<String>("Advanced", String.class, "saml2.sp.org.name", "Apache CloudStack",
|
||||
"SAML2 Service Provider Organization Name", true);
|
||||
|
||||
public static final ConfigKey<String> SAMLServiceProviderOrgUrl = new ConfigKey<String>("Advanced", String.class, "saml2.sp.org.url", "http://cloudstack.apache.org",
|
||||
ConfigKey<String> SAMLServiceProviderOrgUrl = new ConfigKey<String>("Advanced", String.class, "saml2.sp.org.url", "http://cloudstack.apache.org",
|
||||
"SAML2 Service Provider Organization URL", true);
|
||||
|
||||
public static final ConfigKey<String> SAMLServiceProviderSingleSignOnURL = new ConfigKey<String>("Advanced", String.class, "saml2.sp.sso.url", "http://localhost:8080/client/api?command=samlSso",
|
||||
ConfigKey<String> SAMLServiceProviderSingleSignOnURL = new ConfigKey<String>("Advanced", String.class, "saml2.sp.sso.url", "http://localhost:8080/client/api?command=samlSso",
|
||||
"SAML2 CloudStack Service Provider Single Sign On URL", true);
|
||||
|
||||
public static final ConfigKey<String> SAMLServiceProviderSingleLogOutURL = new ConfigKey<String>("Advanced", String.class, "saml2.sp.slo.url", "http://localhost:8080/client/",
|
||||
ConfigKey<String> SAMLServiceProviderSingleLogOutURL = new ConfigKey<String>("Advanced", String.class, "saml2.sp.slo.url", "http://localhost:8080/client/",
|
||||
"SAML2 CloudStack Service Provider Single Log Out URL", true);
|
||||
|
||||
public static final ConfigKey<String> SAMLCloudStackRedirectionUrl = new ConfigKey<String>("Advanced", String.class, "saml2.redirect.url", "http://localhost:8080/client",
|
||||
ConfigKey<String> SAMLCloudStackRedirectionUrl = new ConfigKey<String>("Advanced", String.class, "saml2.redirect.url", "http://localhost:8080/client",
|
||||
"The CloudStack UI url the SSO should redirected to when successful", true);
|
||||
|
||||
public static final ConfigKey<String> SAMLUserAttributeName = new ConfigKey<String>("Advanced", String.class, "saml2.user.attribute", "uid",
|
||||
ConfigKey<String> SAMLUserAttributeName = new ConfigKey<String>("Advanced", String.class, "saml2.user.attribute", "uid",
|
||||
"Attribute name to be looked for in SAML response that will contain the username", true);
|
||||
|
||||
public static final ConfigKey<String> SAMLIdentityProviderMetadataURL = new ConfigKey<String>("Advanced", String.class, "saml2.idp.metadata.url", "https://openidp.feide.no/simplesaml/saml2/idp/metadata.php",
|
||||
ConfigKey<String> SAMLIdentityProviderMetadataURL = new ConfigKey<String>("Advanced", String.class, "saml2.idp.metadata.url", "https://openidp.feide.no/simplesaml/saml2/idp/metadata.php",
|
||||
"SAML2 Identity Provider Metadata XML Url", true);
|
||||
|
||||
public static final ConfigKey<String> SAMLDefaultIdentityProviderId = new ConfigKey<String>("Advanced", String.class, "saml2.default.idpid", "https://openidp.feide.no",
|
||||
ConfigKey<String> SAMLDefaultIdentityProviderId = new ConfigKey<String>("Advanced", String.class, "saml2.default.idpid", "https://openidp.feide.no",
|
||||
"The default IdP entity ID to use only in case of multiple IdPs", true);
|
||||
|
||||
public static final ConfigKey<String> SAMLSignatureAlgorithm = new ConfigKey<>(String.class, "saml2.sigalg", "Advanced", "SHA1",
|
||||
ConfigKey<String> SAMLSignatureAlgorithm = new ConfigKey<>(String.class, "saml2.sigalg", "Advanced", "SHA1",
|
||||
"The algorithm to use to when signing a SAML request. Default is SHA1, allowed algorithms: SHA1, SHA256, SHA384, SHA512", true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.Select, "SHA1,SHA256,SHA384,SHA512");
|
||||
|
||||
public static final ConfigKey<Boolean> SAMLAppendDomainSuffix = new ConfigKey<Boolean>("Advanced", Boolean.class, "saml2.append.idpdomain", "false",
|
||||
ConfigKey<Boolean> SAMLAppendDomainSuffix = new ConfigKey<Boolean>("Advanced", Boolean.class, "saml2.append.idpdomain", "false",
|
||||
"If enabled, create account/user dialog with SAML SSO enabled will append the IdP domain to the user or account name in the UI dialog", true);
|
||||
|
||||
public static final ConfigKey<Integer> SAMLTimeout = new ConfigKey<Integer>("Advanced", Integer.class, "saml2.timeout", "1800",
|
||||
ConfigKey<Integer> SAMLTimeout = new ConfigKey<Integer>("Advanced", Integer.class, "saml2.timeout", "1800",
|
||||
"SAML2 IDP Metadata refresh interval in seconds, minimum value is set to 300", true);
|
||||
|
||||
ConfigKey<Boolean> SAMLCheckSignature = new ConfigKey<Boolean>("Advanced", Boolean.class, "saml2.check.signature", "false",
|
||||
"Whether SAML2 signature must be checked, when enforced and when the SAML response does not have a signature would lead to login exception", true);
|
||||
|
||||
public SAMLProviderMetadata getSPMetadata();
|
||||
public SAMLProviderMetadata getIdPMetadata(String entityId);
|
||||
public Collection<SAMLProviderMetadata> getAllIdPMetadata();
|
||||
|
|
|
|||
|
|
@ -533,6 +533,6 @@ public class SAML2AuthManagerImpl extends AdapterBase implements SAML2AuthManage
|
|||
SAMLServiceProviderSingleSignOnURL, SAMLServiceProviderSingleLogOutURL,
|
||||
SAMLCloudStackRedirectionUrl, SAMLUserAttributeName,
|
||||
SAMLIdentityProviderMetadataURL, SAMLDefaultIdentityProviderId,
|
||||
SAMLSignatureAlgorithm, SAMLAppendDomainSuffix, SAMLTimeout};
|
||||
SAMLSignatureAlgorithm, SAMLAppendDomainSuffix, SAMLTimeout, SAMLCheckSignature};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -271,6 +271,30 @@ public class SAML2LoginAPIAuthenticatorCmdTest {
|
|||
verifyTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl(false, hasThrownServerApiException, 0, 0);
|
||||
}
|
||||
|
||||
private void overrideDefaultConfigValue(final ConfigKey configKey, final String name, final Object o) throws IllegalAccessException, NoSuchFieldException {
|
||||
Field f = ConfigKey.class.getDeclaredField(name);
|
||||
f.setAccessible(true);
|
||||
f.set(configKey, o);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailOnSAMLSignatureCheckWhenFalse() throws NoSuchFieldException, IllegalAccessException {
|
||||
overrideDefaultConfigValue(SAML2AuthManager.SAMLCheckSignature, "_defaultValue", "false");
|
||||
SAML2LoginAPIAuthenticatorCmd cmd = new SAML2LoginAPIAuthenticatorCmd();
|
||||
try {
|
||||
cmd.checkAndFailOnMissingSAMLSignature(null);
|
||||
} catch(Exception e) {
|
||||
Assert.fail("This shouldn't throw any exception");
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = ServerApiException.class)
|
||||
public void testFailOnSAMLSignatureCheckWhenTrue() throws NoSuchFieldException, IllegalAccessException {
|
||||
overrideDefaultConfigValue(SAML2AuthManager.SAMLCheckSignature, "_defaultValue", "true");
|
||||
SAML2LoginAPIAuthenticatorCmd cmd = new SAML2LoginAPIAuthenticatorCmd();
|
||||
cmd.checkAndFailOnMissingSAMLSignature(null);
|
||||
}
|
||||
|
||||
private UserAccountVO configureTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl(String entity, String configurationValue, Boolean isUserAuthorized)
|
||||
throws IOException {
|
||||
Mockito.when(samlAuthManager.isUserAuthorized(nullable(Long.class), nullable(String.class))).thenReturn(isUserAuthorized);
|
||||
|
|
|
|||
2
pom.xml
2
pom.xml
|
|
@ -171,7 +171,7 @@
|
|||
<cs.nitro.version>10.1</cs.nitro.version>
|
||||
<cs.opensaml.version>2.6.6</cs.opensaml.version>
|
||||
<cs.rados-java.version>0.6.0</cs.rados-java.version>
|
||||
<cs.java-linstor.version>0.3.0</cs.java-linstor.version>
|
||||
<cs.java-linstor.version>0.5.1</cs.java-linstor.version>
|
||||
<cs.reflections.version>0.10.2</cs.reflections.version>
|
||||
<cs.servicemix.version>3.4.4_1</cs.servicemix.version>
|
||||
<cs.servlet.version>4.0.1</cs.servlet.version>
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ public class ActionEventInterceptor implements ComponentMethodInterceptor, Metho
|
|||
if (async) {
|
||||
CallContext ctx = CallContext.current();
|
||||
|
||||
String eventDescription = getEventDescription(actionEvent, ctx);
|
||||
String eventDescription = getEventDescription(actionEvent, ctx, true);
|
||||
Long eventResourceId = getEventResourceId(actionEvent, ctx);
|
||||
String eventResourceType = getEventResourceType(actionEvent, ctx);
|
||||
String eventType = getEventType(actionEvent, ctx);
|
||||
|
|
@ -183,19 +183,24 @@ public class ActionEventInterceptor implements ComponentMethodInterceptor, Metho
|
|||
return type == null ? actionEvent.eventType() : type;
|
||||
}
|
||||
|
||||
protected String getEventDescription(ActionEvent actionEvent, CallContext ctx) {
|
||||
protected String getEventDescription(ActionEvent actionEvent, CallContext ctx, boolean capitalizeFirstLetter) {
|
||||
String eventDescription = ctx.getEventDescription();
|
||||
if (eventDescription == null) {
|
||||
eventDescription = actionEvent.eventDescription();
|
||||
}
|
||||
|
||||
if (ctx.getEventDetails() != null) {
|
||||
eventDescription += ". " + ctx.getEventDetails();
|
||||
}
|
||||
|
||||
if (capitalizeFirstLetter && StringUtils.isNotBlank(eventDescription)) {
|
||||
eventDescription = eventDescription.substring(0, 1).toUpperCase() + eventDescription.substring(1);
|
||||
}
|
||||
return eventDescription;
|
||||
}
|
||||
|
||||
protected String getEventDescription(ActionEvent actionEvent, CallContext ctx) {
|
||||
return getEventDescription(actionEvent, ctx, false);
|
||||
}
|
||||
|
||||
protected Long getEventResourceId(ActionEvent actionEvent, CallContext ctx) {
|
||||
Long resourceId = ctx.getEventResourceId();
|
||||
if (resourceId != null) {
|
||||
|
|
|
|||
|
|
@ -205,6 +205,7 @@ import com.cloud.projects.Project;
|
|||
import com.cloud.projects.ProjectManager;
|
||||
import com.cloud.server.ResourceTag;
|
||||
import com.cloud.server.ResourceTag.ResourceObjectType;
|
||||
import com.cloud.service.ServiceOfferingVO;
|
||||
import com.cloud.tags.ResourceTagVO;
|
||||
import com.cloud.tags.dao.ResourceTagDao;
|
||||
import com.cloud.user.Account;
|
||||
|
|
@ -260,7 +261,6 @@ import com.cloud.vm.dao.NicSecondaryIpVO;
|
|||
import com.cloud.vm.dao.UserVmDao;
|
||||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
import com.googlecode.ipv6.IPv6Address;
|
||||
import com.cloud.service.ServiceOfferingVO;
|
||||
|
||||
/**
|
||||
* NetworkServiceImpl implements NetworkService.
|
||||
|
|
@ -1756,6 +1756,18 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@DB
|
||||
@ActionEvent(eventType = EventTypes.EVENT_NETWORK_CREATE, eventDescription = "creating network")
|
||||
public Network createGuestNetwork(long networkOfferingId, String name, String displayText, Account owner,
|
||||
PhysicalNetwork physicalNetwork, long zoneId, ACLType aclType) throws
|
||||
InsufficientCapacityException, ConcurrentOperationException, ResourceAllocationException {
|
||||
return _networkMgr.createGuestNetwork(networkOfferingId, name, displayText,
|
||||
null, null, null, false, null, owner, null, physicalNetwork, zoneId,
|
||||
aclType, null, null, null, null, true, null,
|
||||
null, null, null, null, null, null, null, null, null);
|
||||
}
|
||||
|
||||
void checkAndSetRouterSourceNatIp(Account owner, CreateNetworkCmd cmd, Network network) throws InsufficientAddressCapacityException, ResourceAllocationException {
|
||||
String sourceNatIp = cmd.getSourceNatIP();
|
||||
if (sourceNatIp == null) {
|
||||
|
|
|
|||
|
|
@ -276,12 +276,9 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc
|
|||
private static final ConfigKey<String> statsOutputUri = new ConfigKey<>("Advanced", String.class, "stats.output.uri", "",
|
||||
"URI to send StatsCollector statistics to. The collector is defined on the URI scheme. Example: graphite://graphite-hostaddress:port or influxdb://influxdb-hostaddress/dbname. Note that the port is optional, if not added the default port for the respective collector (graphite or influxdb) will be used. Additionally, the database name '/dbname' is also optional; default db name is 'cloudstack'. You must create and configure the database if using influxdb.",
|
||||
true);
|
||||
protected static ConfigKey<Boolean> vmStatsIncrementMetrics = new ConfigKey<>("Advanced", Boolean.class, "vm.stats.increment.metrics", "true",
|
||||
protected static ConfigKey<Boolean> vmStatsIncrementMetrics = new ConfigKey<>("Advanced", Boolean.class, "vm.stats.increment.metrics", "false",
|
||||
"When set to 'true', VM metrics(NetworkReadKBs, NetworkWriteKBs, DiskWriteKBs, DiskReadKBs, DiskReadIOs and DiskWriteIOs) that are collected from the hypervisor are summed before being returned."
|
||||
+ "On the other hand, when set to 'false', the VM metrics API will just display the latest metrics collected.", true);
|
||||
private static final ConfigKey<Boolean> VM_STATS_INCREMENT_METRICS_IN_MEMORY = new ConfigKey<>("Advanced", Boolean.class, "vm.stats.increment.metrics.in.memory", "true",
|
||||
"When set to 'true', VM metrics(NetworkReadKBs, NetworkWriteKBs, DiskWriteKBs, DiskReadKBs, DiskReadIOs and DiskWriteIOs) that are collected from the hypervisor are summed and stored in memory. "
|
||||
+ "On the other hand, when set to 'false', the VM metrics API will just display the latest metrics collected.", true);
|
||||
protected static ConfigKey<Integer> vmStatsMaxRetentionTime = new ConfigKey<>("Advanced", Integer.class, "vm.stats.max.retention.time", "720",
|
||||
"The maximum time (in minutes) for keeping VM stats records in the database. The VM stats cleanup process will be disabled if this is set to 0 or less than 0.", true);
|
||||
|
||||
|
|
@ -2129,7 +2126,6 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc
|
|||
public ConfigKey<?>[] getConfigKeys() {
|
||||
return new ConfigKey<?>[] {vmDiskStatsInterval, vmDiskStatsIntervalMin, vmNetworkStatsInterval, vmNetworkStatsIntervalMin, StatsTimeout, statsOutputUri,
|
||||
vmStatsIncrementMetrics, vmStatsMaxRetentionTime, vmStatsCollectUserVMOnly, vmDiskStatsRetentionEnabled, vmDiskStatsMaxRetentionTime,
|
||||
VM_STATS_INCREMENT_METRICS_IN_MEMORY,
|
||||
MANAGEMENT_SERVER_STATUS_COLLECTION_INTERVAL,
|
||||
DATABASE_SERVER_STATUS_COLLECTION_INTERVAL,
|
||||
DATABASE_SERVER_LOAD_HISTORY_RETENTION_NUMBER};
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ import javax.naming.ConfigurationException;
|
|||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import com.cloud.kubernetes.cluster.KubernetesClusterHelper;
|
||||
import com.cloud.kubernetes.cluster.KubernetesServiceHelper;
|
||||
import com.cloud.network.dao.NsxProviderDao;
|
||||
import com.cloud.network.element.NsxProviderVO;
|
||||
import com.cloud.user.AccountVO;
|
||||
|
|
@ -611,7 +611,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
private boolean _dailyOrHourly = false;
|
||||
private int capacityReleaseInterval;
|
||||
private ExecutorService _vmIpFetchThreadExecutor;
|
||||
private List<KubernetesClusterHelper> kubernetesClusterHelpers;
|
||||
private List<KubernetesServiceHelper> kubernetesClusterHelpers;
|
||||
|
||||
|
||||
private String _instance;
|
||||
|
|
@ -625,11 +625,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
private static final int NUM_OF_2K_BLOCKS = 512;
|
||||
private static final int MAX_HTTP_POST_LENGTH = NUM_OF_2K_BLOCKS * MAX_USER_DATA_LENGTH_BYTES;
|
||||
|
||||
public List<KubernetesClusterHelper> getKubernetesClusterHelpers() {
|
||||
public List<KubernetesServiceHelper> getKubernetesClusterHelpers() {
|
||||
return kubernetesClusterHelpers;
|
||||
}
|
||||
|
||||
public void setKubernetesClusterHelpers(final List<KubernetesClusterHelper> kubernetesClusterHelpers) {
|
||||
public void setKubernetesClusterHelpers(final List<KubernetesServiceHelper> kubernetesClusterHelpers) {
|
||||
this.kubernetesClusterHelpers = kubernetesClusterHelpers;
|
||||
}
|
||||
|
||||
|
|
@ -3261,6 +3261,12 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
return startVirtualMachine(cmd.getId(), cmd.getPodId(), cmd.getClusterId(), cmd.getHostId(), additonalParams, cmd.getDeploymentPlanner()).first();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_VM_START, eventDescription = "starting Vm", async = true)
|
||||
public void startVirtualMachine(UserVm vm) throws OperationTimedoutException, ResourceUnavailableException, InsufficientCapacityException {
|
||||
_itMgr.advanceStart(vm.getUuid(), null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_VM_REBOOT, eventDescription = "rebooting Vm", async = true)
|
||||
public UserVm rebootVirtualMachine(RebootVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException, ResourceAllocationException {
|
||||
|
|
@ -3317,9 +3323,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
|
||||
protected void checkPluginsIfVmCanBeDestroyed(UserVm vm) {
|
||||
try {
|
||||
KubernetesClusterHelper kubernetesClusterHelper =
|
||||
ComponentContext.getDelegateComponentOfType(KubernetesClusterHelper.class);
|
||||
kubernetesClusterHelper.checkVmCanBeDestroyed(vm);
|
||||
KubernetesServiceHelper kubernetesServiceHelper =
|
||||
ComponentContext.getDelegateComponentOfType(KubernetesServiceHelper.class);
|
||||
kubernetesServiceHelper.checkVmCanBeDestroyed(vm);
|
||||
} catch (NoSuchBeanDefinitionException ignored) {
|
||||
logger.debug("No KubernetesClusterHelper bean found");
|
||||
}
|
||||
|
|
@ -5508,7 +5514,12 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
final ServiceOfferingVO offering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId());
|
||||
Pair<Boolean, Boolean> cpuCapabilityAndCapacity = _capacityMgr.checkIfHostHasCpuCapabilityAndCapacity(destinationHost, offering, false);
|
||||
if (!cpuCapabilityAndCapacity.first() || !cpuCapabilityAndCapacity.second()) {
|
||||
String errorMsg = "Cannot deploy the VM to specified host " + hostId + "; host has cpu capability? " + cpuCapabilityAndCapacity.first() + ", host has capacity? " + cpuCapabilityAndCapacity.second();
|
||||
String errorMsg;
|
||||
if (!cpuCapabilityAndCapacity.first()) {
|
||||
errorMsg = String.format("Cannot deploy the VM to specified host %d, requested CPU and speed is more than the host capability", hostId);
|
||||
} else {
|
||||
errorMsg = String.format("Cannot deploy the VM to specified host %d, host does not have enough free CPU or RAM, please check the logs", hostId);
|
||||
}
|
||||
logger.info(errorMsg);
|
||||
if (!AllowDeployVmIfGivenHostFails.value()) {
|
||||
throw new InvalidParameterValueException(errorMsg);
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ import com.cloud.event.EventTypes;
|
|||
import com.cloud.exception.PermissionDeniedException;
|
||||
import com.cloud.host.HostVO;
|
||||
import com.cloud.host.dao.HostDao;
|
||||
import com.cloud.kubernetes.cluster.KubernetesClusterHelper;
|
||||
import com.cloud.kubernetes.cluster.KubernetesServiceHelper;
|
||||
import com.cloud.network.as.dao.AutoScaleVmGroupDao;
|
||||
import com.cloud.network.dao.IPAddressDao;
|
||||
import com.cloud.network.dao.NetworkDao;
|
||||
|
|
@ -163,7 +163,7 @@ public final class AnnotationManagerImpl extends ManagerBase implements Annotati
|
|||
EntityManager entityManager;
|
||||
|
||||
private static final List<RoleType> adminRoles = Collections.singletonList(RoleType.Admin);
|
||||
private List<KubernetesClusterHelper> kubernetesClusterHelpers;
|
||||
private List<KubernetesServiceHelper> kubernetesServiceHelpers;
|
||||
public static final Map<EntityType, ApiCommandResourceType> s_typeMap = new HashMap<>();
|
||||
|
||||
static {
|
||||
|
|
@ -198,12 +198,12 @@ public final class AnnotationManagerImpl extends ManagerBase implements Annotati
|
|||
s_typeMap.put(EntityType.OBJECT_STORAGE, ApiCommandResourceType.ObjectStore);
|
||||
}
|
||||
|
||||
public List<KubernetesClusterHelper> getKubernetesClusterHelpers() {
|
||||
return kubernetesClusterHelpers;
|
||||
public List<KubernetesServiceHelper> getKubernetesServiceHelpers() {
|
||||
return kubernetesServiceHelpers;
|
||||
}
|
||||
|
||||
public void setKubernetesClusterHelpers(final List<KubernetesClusterHelper> kubernetesClusterHelpers) {
|
||||
this.kubernetesClusterHelpers = kubernetesClusterHelpers;
|
||||
public void setKubernetesServiceHelpers(final List<KubernetesServiceHelper> kubernetesServiceHelpers) {
|
||||
this.kubernetesServiceHelpers = kubernetesServiceHelpers;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -533,7 +533,7 @@ public final class AnnotationManagerImpl extends ManagerBase implements Annotati
|
|||
case ISO:
|
||||
return templateDao.findByUuid(entityUuid);
|
||||
case KUBERNETES_CLUSTER:
|
||||
return kubernetesClusterHelpers.get(0).findByUuid(entityUuid);
|
||||
return kubernetesServiceHelpers.get(0).findByUuid(entityUuid);
|
||||
case AUTOSCALE_VM_GROUP:
|
||||
return autoScaleVmGroupDao.findByUuid(entityUuid);
|
||||
case MANAGEMENT_SERVER:
|
||||
|
|
|
|||
|
|
@ -2479,11 +2479,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
|||
throw new InvalidParameterValueException("Please specify a valid zone.");
|
||||
}
|
||||
final String hypervisorType = cmd.getHypervisor();
|
||||
if (Hypervisor.HypervisorType.KVM.toString().equalsIgnoreCase(hypervisorType)) {
|
||||
if (StringUtils.isBlank(cmd.getUsername())) {
|
||||
throw new InvalidParameterValueException("Username need to be provided.");
|
||||
}
|
||||
} else {
|
||||
if (!Hypervisor.HypervisorType.KVM.toString().equalsIgnoreCase(hypervisorType)) {
|
||||
throw new InvalidParameterValueException(String.format("VM Import is currently not supported for hypervisor: %s", hypervisorType));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -322,7 +322,7 @@
|
|||
</bean>
|
||||
|
||||
<bean id="annotationService" class="org.apache.cloudstack.annotation.AnnotationManagerImpl">
|
||||
<property name="kubernetesClusterHelpers" value="#{kubernetesClusterHelperRegistry.registered}" />
|
||||
<property name="kubernetesServiceHelpers" value="#{kubernetesServiceHelperRegistry.registered}" />
|
||||
</bean>
|
||||
|
||||
<bean id="indirectAgentLBService" class="org.apache.cloudstack.agent.lb.IndirectAgentLBServiceImpl" />
|
||||
|
|
|
|||
|
|
@ -222,6 +222,13 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Network createGuestNetwork(long networkOfferingId, String name, String displayText, Account owner,
|
||||
PhysicalNetwork physicalNetwork, long zoneId, ACLType aclType) throws InsufficientCapacityException,
|
||||
ConcurrentOperationException, ResourceAllocationException {
|
||||
return null;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.cloud.network.NetworkService#searchForNetworks(com.cloud.api.commands.ListNetworksCmd)
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ from marvin.lib.base import (ServiceOffering,
|
|||
NATRule,
|
||||
Template)
|
||||
from marvin.lib.common import get_test_template, get_zone, list_virtual_machines
|
||||
from marvin.lib.utils import (validateList, cleanup_resources)
|
||||
from marvin.lib.utils import validateList
|
||||
from nose.plugins.attrib import attr
|
||||
from marvin.codes import PASS,FAIL
|
||||
import base64
|
||||
|
|
@ -87,6 +87,7 @@ class TestRegisteredUserdata(cloudstackTestCase):
|
|||
# Get Zone, Domain and Default Built-in template
|
||||
self.domain = get_domain(self.apiclient)
|
||||
self.zone = get_zone(self.apiclient, self.testClient.getZoneForTests())
|
||||
self.hypervisor = self.testClient.getHypervisorInfo()
|
||||
|
||||
#create a user account
|
||||
self.account = Account.create(
|
||||
|
|
@ -96,7 +97,7 @@ class TestRegisteredUserdata(cloudstackTestCase):
|
|||
)
|
||||
|
||||
self.testdata["mode"] = self.zone.networktype
|
||||
self.template = get_template(self.apiclient, self.zone.id, self.testdata["ostype"])
|
||||
self.template = get_test_template(self.apiclient, self.zone.id, self.hypervisor)
|
||||
|
||||
#create a service offering
|
||||
small_service_offering = self.testdata["service_offerings"]["small"]
|
||||
|
|
@ -115,25 +116,21 @@ class TestRegisteredUserdata(cloudstackTestCase):
|
|||
self.testdata["network"],
|
||||
networkofferingid=self.no_isolate.id,
|
||||
zoneid=self.zone.id,
|
||||
accountid="admin",
|
||||
domainid=1
|
||||
accountid=self.account.name,
|
||||
domainid=self.account.domainid
|
||||
)
|
||||
|
||||
#build cleanup list
|
||||
self.cleanup = [
|
||||
self.account,
|
||||
self.no_isolate,
|
||||
self.service_offering,
|
||||
self.isolated_network,
|
||||
self.no_isolate,
|
||||
self.account,
|
||||
]
|
||||
|
||||
|
||||
def tearDown(self):
|
||||
try:
|
||||
cleanup_resources(self.apiclient, self.cleanup)
|
||||
except Exception as e:
|
||||
self.debug("Warning! Exception in tearDown: %s" % e)
|
||||
|
||||
super(TestRegisteredUserdata, self).tearDown()
|
||||
|
||||
@attr(tags=['advanced', 'simulator', 'basic', 'sg'], required_hardware=False)
|
||||
def test_CRUD_operations_userdata(self):
|
||||
|
|
@ -192,22 +189,21 @@ class TestRegisteredUserdata(cloudstackTestCase):
|
|||
self.apiclient,
|
||||
self.services["virtual_machine"],
|
||||
zoneid=self.zone.id,
|
||||
accountid="admin",
|
||||
domainid=1,
|
||||
accountid=self.account.name,
|
||||
domainid=self.account.domainid,
|
||||
serviceofferingid=self.service_offering.id,
|
||||
templateid=self.template.id,
|
||||
networkids=[self.isolated_network.id],
|
||||
userdataid=self.userdata2.userdata.id
|
||||
)
|
||||
self.cleanup.append(self.virtual_machine)
|
||||
self.cleanup.append(self.userdata2)
|
||||
|
||||
networkid = self.virtual_machine.nic[0].networkid
|
||||
src_nat_list = PublicIPAddress.list(
|
||||
self.apiclient,
|
||||
associatednetworkid=networkid,
|
||||
account="admin",
|
||||
domainid=1,
|
||||
account=self.account.name,
|
||||
domainid=self.account.domainid,
|
||||
listall=True,
|
||||
issourcenat=True,
|
||||
)
|
||||
|
|
@ -320,8 +316,8 @@ class TestRegisteredUserdata(cloudstackTestCase):
|
|||
self.apiclient,
|
||||
self.services["virtual_machine"],
|
||||
zoneid=self.zone.id,
|
||||
accountid="admin",
|
||||
domainid=1,
|
||||
accountid=self.account.name,
|
||||
domainid=self.account.domainid,
|
||||
serviceofferingid=self.service_offering.id,
|
||||
templateid=self.template.id,
|
||||
networkids=[self.isolated_network.id],
|
||||
|
|
@ -329,14 +325,13 @@ class TestRegisteredUserdata(cloudstackTestCase):
|
|||
userdatadetails=[{"key1": "value1"}]
|
||||
)
|
||||
self.cleanup.append(self.virtual_machine)
|
||||
self.cleanup.append(self.userdata2)
|
||||
|
||||
networkid = self.virtual_machine.nic[0].networkid
|
||||
src_nat_list = PublicIPAddress.list(
|
||||
self.apiclient,
|
||||
associatednetworkid=networkid,
|
||||
account="admin",
|
||||
domainid=1,
|
||||
account=self.account.name,
|
||||
domainid=self.account.domainid,
|
||||
listall=True,
|
||||
issourcenat=True,
|
||||
)
|
||||
|
|
@ -493,15 +488,14 @@ class TestRegisteredUserdata(cloudstackTestCase):
|
|||
self.apiclient,
|
||||
self.services["virtual_machine"],
|
||||
zoneid=self.zone.id,
|
||||
accountid="admin",
|
||||
domainid=1,
|
||||
accountid=self.account.name,
|
||||
domainid=self.account.domainid,
|
||||
serviceofferingid=self.service_offering.id,
|
||||
templateid=self.template.id,
|
||||
networkids=[self.isolated_network.id],
|
||||
userdataid=self.apiUserdata.userdata.id
|
||||
)
|
||||
self.cleanup.append(self.virtual_machine)
|
||||
self.cleanup.append(self.apiUserdata)
|
||||
|
||||
self.template = Template.linkUserDataToTemplate(
|
||||
self.apiclient,
|
||||
|
|
@ -512,8 +506,8 @@ class TestRegisteredUserdata(cloudstackTestCase):
|
|||
src_nat_list = PublicIPAddress.list(
|
||||
self.apiclient,
|
||||
associatednetworkid=networkid,
|
||||
account="admin",
|
||||
domainid=1,
|
||||
account=self.account.name,
|
||||
domainid=self.account.domainid,
|
||||
listall=True,
|
||||
issourcenat=True,
|
||||
)
|
||||
|
|
@ -623,16 +617,14 @@ class TestRegisteredUserdata(cloudstackTestCase):
|
|||
self.apiclient,
|
||||
self.services["virtual_machine"],
|
||||
zoneid=self.zone.id,
|
||||
accountid="admin",
|
||||
domainid=1,
|
||||
accountid=self.account.name,
|
||||
domainid=self.account.domainid,
|
||||
serviceofferingid=self.service_offering.id,
|
||||
templateid=self.template.id,
|
||||
networkids=[self.isolated_network.id],
|
||||
userdataid=self.apiUserdata.userdata.id
|
||||
)
|
||||
self.cleanup.append(self.virtual_machine)
|
||||
self.cleanup.append(self.apiUserdata)
|
||||
self.cleanup.append(self.templateUserdata)
|
||||
|
||||
self.template = Template.linkUserDataToTemplate(
|
||||
self.apiclient,
|
||||
|
|
@ -643,8 +635,8 @@ class TestRegisteredUserdata(cloudstackTestCase):
|
|||
src_nat_list = PublicIPAddress.list(
|
||||
self.apiclient,
|
||||
associatednetworkid=networkid,
|
||||
account="admin",
|
||||
domainid=1,
|
||||
account=self.account.name,
|
||||
domainid=self.account.domainid,
|
||||
listall=True,
|
||||
issourcenat=True,
|
||||
)
|
||||
|
|
@ -770,8 +762,8 @@ class TestRegisteredUserdata(cloudstackTestCase):
|
|||
self.apiclient,
|
||||
self.services["virtual_machine"],
|
||||
zoneid=self.zone.id,
|
||||
accountid="admin",
|
||||
domainid=1,
|
||||
accountid=self.account.name,
|
||||
domainid=self.account.domainid,
|
||||
serviceofferingid=self.service_offering.id,
|
||||
templateid=self.template.id,
|
||||
networkids=[self.isolated_network.id],
|
||||
|
|
@ -781,9 +773,6 @@ class TestRegisteredUserdata(cloudstackTestCase):
|
|||
self.debug("Deploy VM with userdata passed during deployment failed as expected because template userdata override policy is deny. Exception here is : %s" %
|
||||
e.exception)
|
||||
|
||||
self.cleanup.append(self.apiUserdata)
|
||||
self.cleanup.append(self.templateUserdata)
|
||||
|
||||
self.template = Template.linkUserDataToTemplate(
|
||||
self.apiclient,
|
||||
templateid=self.template.id
|
||||
|
|
@ -809,7 +798,6 @@ class TestRegisteredUserdata(cloudstackTestCase):
|
|||
account=self.account.name,
|
||||
domainid=self.account.domainid
|
||||
)
|
||||
self.cleanup.append(self.userdata)
|
||||
|
||||
list_userdata = UserData.list(self.apiclient, id=self.userdata.userdata.id, listall=True)
|
||||
self.assertNotEqual(
|
||||
|
|
@ -853,4 +841,3 @@ class TestRegisteredUserdata(cloudstackTestCase):
|
|||
self.userapiclient,
|
||||
id=self.userdata.userdata.id
|
||||
)
|
||||
self.cleanup.remove(self.userdata)
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ def replaceVersion(fname, version):
|
|||
with open(fname, 'r') as f:
|
||||
content = f.read()
|
||||
needle = '\nVERSION\s*=\s*[\'"][^\'"]*[\'"]'
|
||||
# Ensure the version is PEP440 compliant
|
||||
version = version.replace('-', '+', 1)
|
||||
replacement = '\nVERSION = "%s"' % version
|
||||
content = re.sub(needle, replacement, content, 1)
|
||||
with open(fname, 'w') as f:
|
||||
|
|
|
|||
|
|
@ -18,7 +18,13 @@
|
|||
<template>
|
||||
<div v-if="resourceType && resourceId" >
|
||||
<a-tooltip v-if="resourceIcon" placement="top" :title="resourceIconTooltip">
|
||||
<render-icon style="font-size: 16px; margin-right: 5px" :icon="resourceIcon" />
|
||||
<font-awesome-icon
|
||||
v-if="resourceIcon && Array.isArray(resourceIcon)"
|
||||
:icon="resourceIcon"
|
||||
size="1x"
|
||||
class="anticon"
|
||||
:style="[$store.getters.darkMode ? { color: 'rgba(255, 255, 255, 0.65)' } : { color: '#888' }]" />
|
||||
<render-icon v-else style="font-size: 16px; margin-right: 5px" :icon="resourceIcon" />
|
||||
</a-tooltip>
|
||||
<a-tag v-else>{{ resourceType }}</a-tag>
|
||||
<router-link v-if="resourceRoute && $router.resolve(resourceRoute)" :to="{ path: resourceRoute }">{{ resourceName || resourceId }}</router-link>
|
||||
|
|
@ -67,3 +73,12 @@ export default {
|
|||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.anticon {
|
||||
margin-right: 5px;
|
||||
vertical-align: center;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -32,9 +32,9 @@ export default {
|
|||
getApiToCall: () => store.getters.metrics ? 'listVirtualMachinesMetrics' : 'listVirtualMachines',
|
||||
resourceType: 'UserVm',
|
||||
params: () => {
|
||||
var params = { details: 'servoff,tmpl,iso,nics,backoff' }
|
||||
var params = { details: 'group,nics,secgrp,tmpl,servoff,diskoff,iso,volume,affgrp' }
|
||||
if (store.getters.metrics) {
|
||||
params = { details: 'servoff,tmpl,iso,nics,backoff,stats' }
|
||||
params = { details: 'all,stats' }
|
||||
}
|
||||
params.isvnf = false
|
||||
return params
|
||||
|
|
@ -542,10 +542,12 @@ export default {
|
|||
return filters
|
||||
},
|
||||
details: ['name', 'description', 'zonename', 'kubernetesversionname', 'autoscalingenabled', 'minsize', 'maxsize', 'size', 'controlnodes', 'cpunumber', 'memory', 'keypair', 'associatednetworkname', 'account', 'domain', 'zonename', 'clustertype', 'created'],
|
||||
tabs: [{
|
||||
name: 'k8s',
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/KubernetesServiceTab.vue')))
|
||||
}],
|
||||
tabs: [
|
||||
{
|
||||
name: 'k8s',
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/KubernetesServiceTab.vue')))
|
||||
}
|
||||
],
|
||||
resourceType: 'KubernetesCluster',
|
||||
actions: [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -370,6 +370,18 @@ export default {
|
|||
searchFilters: ['zoneid', 'minimumsemanticversion'],
|
||||
columns: ['name', 'state', 'semanticversion', 'isostate', 'mincpunumber', 'minmemory', 'zonename'],
|
||||
details: ['name', 'semanticversion', 'supportsautoscaling', 'zoneid', 'zonename', 'isoid', 'isoname', 'isostate', 'mincpunumber', 'minmemory', 'supportsha', 'state', 'created'],
|
||||
tabs: [
|
||||
{
|
||||
name: 'details',
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue')))
|
||||
},
|
||||
{
|
||||
name: 'events',
|
||||
resourceType: 'KubernetesSupportedVersion',
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/components/view/EventsTab.vue'))),
|
||||
show: () => { return 'listEvents' in store.getters.apis }
|
||||
}
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
api: 'addKubernetesSupportedVersion',
|
||||
|
|
|
|||
|
|
@ -390,6 +390,10 @@ export const resourceTypePlugin = {
|
|||
return 'publicip'
|
||||
case 'NetworkAcl':
|
||||
return 'acllist'
|
||||
case 'KubernetesCluster':
|
||||
return 'kubernetes'
|
||||
case 'KubernetesSupportedVersion':
|
||||
return 'kubernetesiso'
|
||||
case 'SystemVm':
|
||||
case 'PhysicalNetwork':
|
||||
case 'Backup':
|
||||
|
|
|
|||
|
|
@ -23,13 +23,19 @@ function filterNumber (value) {
|
|||
}
|
||||
|
||||
function stringComparator (a, b) {
|
||||
return a.toString().localeCompare(b.toString())
|
||||
return a.toString().localeCompare(b.toString(), undefined, { numeric: true })
|
||||
}
|
||||
|
||||
function numericComparator (a, b) {
|
||||
return filterNumber(a) < filterNumber(b) ? 1 : -1
|
||||
}
|
||||
|
||||
function metricComparator (ma, mb) {
|
||||
var a = ('' + ma).replace(/((%)|(Ghz)|(Mhz)|(MiB)|(GiB)|(GB)).*$/g, '')
|
||||
var b = ('' + mb).replace(/((%)|(Ghz)|(Mhz)|(MiB)|(GiB)|(GB)).*$/g, '')
|
||||
return parseFloat(a) < parseFloat(b) ? 1 : -1
|
||||
}
|
||||
|
||||
function ipV4AddressCIDRComparator (a, b) {
|
||||
a = a.split(/[./]/gm)
|
||||
b = b.split(/[./]/gm)
|
||||
|
|
@ -76,6 +82,10 @@ function isNumeric (obj) {
|
|||
return !Array.isArray(obj) && !isNaN(filterNumber(obj))
|
||||
}
|
||||
|
||||
function isMetric (value) {
|
||||
return /^[0-9\\. ]*((%)|(Ghz)|(Mhz)|(MiB)|(GiB)|(GB))/.test(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare elements, attempting to determine type of element to get the best comparison
|
||||
*
|
||||
|
|
@ -97,6 +107,9 @@ export function genericCompare (a, b) {
|
|||
if (isNumeric(a) && isNumeric(b)) {
|
||||
comparator = numericComparator
|
||||
}
|
||||
if (isMetric(a) && isMetric(b)) {
|
||||
comparator = metricComparator
|
||||
}
|
||||
|
||||
return comparator(a, b)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -904,6 +904,7 @@ export default {
|
|||
if (['listVirtualMachinesMetrics'].includes(this.apiName) && this.dataView) {
|
||||
delete params.details
|
||||
delete params.isvnf
|
||||
params.details = 'group,nics,secgrp,tmpl,servoff,diskoff,iso,volume,affgrp'
|
||||
}
|
||||
|
||||
this.loading = true
|
||||
|
|
|
|||
|
|
@ -149,6 +149,9 @@
|
|||
<a-tab-pane :tab="$t('label.loadbalancing')" key="loadbalancing" v-if="publicIpAddress">
|
||||
<LoadBalancing :resource="publicIpAddress" :loading="networkLoading" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane :tab="$t('label.events')" key="events" v-if="'listEvents' in $store.getters.apis">
|
||||
<events-tab :resource="resource" resourceType="KubernetesCluster" :loading="loading" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane :tab="$t('label.annotations')" key="comments" v-if="'listAnnotations' in $store.getters.apis">
|
||||
<AnnotationsTab
|
||||
:resource="resource"
|
||||
|
|
@ -169,6 +172,7 @@ import PortForwarding from '@/views/network/PortForwarding'
|
|||
import LoadBalancing from '@/views/network/LoadBalancing'
|
||||
import Status from '@/components/widgets/Status'
|
||||
import AnnotationsTab from '@/components/view/AnnotationsTab'
|
||||
import EventsTab from '@/components/view/EventsTab'
|
||||
|
||||
export default {
|
||||
name: 'KubernetesServiceTab',
|
||||
|
|
@ -178,7 +182,8 @@ export default {
|
|||
PortForwarding,
|
||||
LoadBalancing,
|
||||
Status,
|
||||
AnnotationsTab
|
||||
AnnotationsTab,
|
||||
EventsTab
|
||||
},
|
||||
mixins: [mixinDevice],
|
||||
inject: ['parentFetchData'],
|
||||
|
|
|
|||
|
|
@ -99,6 +99,14 @@ export default {
|
|||
type: String,
|
||||
default: () => ''
|
||||
},
|
||||
domainid: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
account: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
selectionEnabled: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
|
|
@ -144,7 +152,8 @@ export default {
|
|||
ipAddressesEnabled: {},
|
||||
ipAddresses: {},
|
||||
indexNum: 1,
|
||||
sendValuesTimer: null
|
||||
sendValuesTimer: null,
|
||||
accountNetworkUpdateTimer: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -184,6 +193,14 @@ export default {
|
|||
},
|
||||
zoneId () {
|
||||
this.fetchNetworks()
|
||||
},
|
||||
account () {
|
||||
clearTimeout(this.accountNetworkUpdateTimer)
|
||||
this.accountNetworkUpdateTimer = setTimeout(() => {
|
||||
if (this.account) {
|
||||
this.fetchNetworks()
|
||||
}
|
||||
}, 750)
|
||||
}
|
||||
},
|
||||
created () {
|
||||
|
|
@ -196,13 +213,20 @@ export default {
|
|||
return
|
||||
}
|
||||
this.loading = true
|
||||
api('listNetworks', {
|
||||
var params = {
|
||||
zoneid: this.zoneId,
|
||||
listall: true
|
||||
}).then(response => {
|
||||
}
|
||||
if (this.domainid && this.account) {
|
||||
params.domainid = this.domainid
|
||||
params.account = this.account
|
||||
}
|
||||
api('listNetworks', params).then(response => {
|
||||
this.networks = response.listnetworksresponse.network || []
|
||||
this.orderNetworks()
|
||||
}).catch(() => {
|
||||
this.networks = []
|
||||
}).finally(() => {
|
||||
this.orderNetworks()
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@
|
|||
</template>
|
||||
<a-divider style="margin: 6px 0px; border-width: 0px"/>
|
||||
<a-row :gutter="[10, 10]">
|
||||
<a-col :span="12">
|
||||
<a-col :span="12" v-if="'listVirtualMachines' in $store.getters.apis">
|
||||
<router-link :to="{ path: '/vm' }">
|
||||
<a-statistic
|
||||
:title="$t('label.instances')"
|
||||
|
|
@ -63,7 +63,7 @@
|
|||
</a-statistic>
|
||||
</router-link>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-col :span="12" v-if="'listKubernetesClusters' in $store.getters.apis">
|
||||
<router-link :to="{ path: '/kubernetes' }">
|
||||
<a-statistic
|
||||
:title="$t('label.kubernetes.cluster')"
|
||||
|
|
@ -75,7 +75,7 @@
|
|||
</a-statistic>
|
||||
</router-link>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-col :span="12" v-if="'listVolumes' in $store.getters.apis">
|
||||
<router-link :to="{ path: '/volume' }">
|
||||
<a-statistic
|
||||
:title="$t('label.volumes')"
|
||||
|
|
@ -87,7 +87,7 @@
|
|||
</a-statistic>
|
||||
</router-link>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-col :span="12" v-if="'listSnapshots' in $store.getters.apis">
|
||||
<router-link :to="{ path: '/snapshot' }">
|
||||
<a-statistic
|
||||
:title="$t('label.snapshots')"
|
||||
|
|
@ -99,7 +99,7 @@
|
|||
</a-statistic>
|
||||
</router-link>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-col :span="12" v-if="'listNetworks' in $store.getters.apis">
|
||||
<router-link :to="{ path: '/guestnetwork' }">
|
||||
<a-statistic
|
||||
:title="$t('label.guest.networks')"
|
||||
|
|
@ -111,7 +111,7 @@
|
|||
</a-statistic>
|
||||
</router-link>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-col :span="12" v-if="'listVPCs' in $store.getters.apis">
|
||||
<router-link :to="{ path: '/vpc' }">
|
||||
<a-statistic
|
||||
:title="$t('label.vpcs')"
|
||||
|
|
@ -123,7 +123,7 @@
|
|||
</a-statistic>
|
||||
</router-link>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-col :span="12" v-if="'listPublicIpAddresses' in $store.getters.apis">
|
||||
<router-link :to="{ path: '/publicip' }">
|
||||
<a-statistic
|
||||
:title="$t('label.public.ips')"
|
||||
|
|
@ -135,7 +135,7 @@
|
|||
</a-statistic>
|
||||
</router-link>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-col :span="12" v-if="'listTemplates' in $store.getters.apis">
|
||||
<router-link :to="{ path: '/template', query: { templatefilter: 'self', filter: 'self' } }">
|
||||
<a-statistic
|
||||
:title="$t('label.templates')"
|
||||
|
|
@ -150,7 +150,7 @@
|
|||
</a-row>
|
||||
</chart-card>
|
||||
</a-col>
|
||||
<a-col :xs="{ span: 24 }" :lg="{ span: 12 }" :xl="{ span: 8 }" :xxl="{ span: 8 }">
|
||||
<a-col :xs="{ span: 24 }" :lg="{ span: 12 }" :xl="{ span: 8 }" :xxl="{ span: 8 }" v-if="'listVirtualMachines' in $store.getters.apis">
|
||||
<chart-card :loading="loading" class="dashboard-card">
|
||||
<template #title>
|
||||
<div class="center">
|
||||
|
|
@ -315,7 +315,7 @@
|
|||
</a-list>
|
||||
</chart-card>
|
||||
</a-col>
|
||||
<a-col :xs="{ span: 24 }" :lg="{ span: 12 }" :xl="{ span: 8 }" :xxl="{ span: 8 }">
|
||||
<a-col :xs="{ span: 24 }" :lg="{ span: 12 }" :xl="{ span: 8 }" :xxl="{ span: 8 }" v-if="'listEvents' in $store.getters.apis">
|
||||
<chart-card :loading="loading" class="dashboard-card dashboard-event">
|
||||
<template #title>
|
||||
<div class="center">
|
||||
|
|
@ -482,37 +482,60 @@ export default {
|
|||
}
|
||||
this.listInstances()
|
||||
this.listEvents()
|
||||
this.loading = true
|
||||
api('listKubernetesClusters', { listall: true, page: 1, pagesize: 1 }).then(json => {
|
||||
this.loading = false
|
||||
this.data.kubernetes = json?.listkubernetesclustersresponse?.count
|
||||
})
|
||||
api('listVolumes', { listall: true, page: 1, pagesize: 1 }).then(json => {
|
||||
this.loading = false
|
||||
this.data.volumes = json?.listvolumesresponse?.count
|
||||
})
|
||||
api('listSnapshots', { listall: true, page: 1, pagesize: 1 }).then(json => {
|
||||
this.loading = false
|
||||
this.data.snapshots = json?.listsnapshotsresponse?.count
|
||||
})
|
||||
api('listNetworks', { listall: true, page: 1, pagesize: 1 }).then(json => {
|
||||
this.loading = false
|
||||
this.data.networks = json?.listnetworksresponse?.count
|
||||
})
|
||||
api('listVPCs', { listall: true, page: 1, pagesize: 1 }).then(json => {
|
||||
this.loading = false
|
||||
this.data.vpcs = json?.listvpcsresponse?.count
|
||||
})
|
||||
api('listPublicIpAddresses', { listall: true, page: 1, pagesize: 1 }).then(json => {
|
||||
this.loading = false
|
||||
this.data.ips = json?.listpublicipaddressesresponse?.count
|
||||
})
|
||||
api('listTemplates', { templatefilter: 'self', listall: true, page: 1, pagesize: 1 }).then(json => {
|
||||
this.loading = false
|
||||
this.data.templates = json?.listtemplatesresponse?.count
|
||||
})
|
||||
if ('listKubernetesClusters' in this.$store.getters.apis) {
|
||||
this.loading = true
|
||||
api('listKubernetesClusters', { listall: true, page: 1, pagesize: 1 }).then(json => {
|
||||
this.loading = false
|
||||
this.data.kubernetes = json?.listkubernetesclustersresponse?.count
|
||||
})
|
||||
}
|
||||
if ('listVolumes' in this.$store.getters.apis) {
|
||||
this.loading = true
|
||||
api('listVolumes', { listall: true, page: 1, pagesize: 1 }).then(json => {
|
||||
this.loading = false
|
||||
this.data.volumes = json?.listvolumesresponse?.count
|
||||
})
|
||||
}
|
||||
if ('listSnapshots' in this.$store.getters.apis) {
|
||||
this.loading = true
|
||||
api('listSnapshots', { listall: true, page: 1, pagesize: 1 }).then(json => {
|
||||
this.loading = false
|
||||
this.data.snapshots = json?.listsnapshotsresponse?.count
|
||||
})
|
||||
}
|
||||
if ('listNetworks' in this.$store.getters.apis) {
|
||||
this.loading = true
|
||||
api('listNetworks', { listall: true, page: 1, pagesize: 1 }).then(json => {
|
||||
this.loading = false
|
||||
this.data.networks = json?.listnetworksresponse?.count
|
||||
})
|
||||
}
|
||||
if ('listVPCs' in this.$store.getters.apis) {
|
||||
this.loading = true
|
||||
api('listVPCs', { listall: true, page: 1, pagesize: 1 }).then(json => {
|
||||
this.loading = false
|
||||
this.data.vpcs = json?.listvpcsresponse?.count
|
||||
})
|
||||
}
|
||||
if ('listPublicIpAddresses' in this.$store.getters.apis) {
|
||||
this.loading = true
|
||||
api('listPublicIpAddresses', { listall: true, page: 1, pagesize: 1 }).then(json => {
|
||||
this.loading = false
|
||||
this.data.ips = json?.listpublicipaddressesresponse?.count
|
||||
})
|
||||
}
|
||||
if ('listTemplates' in this.$store.getters.apis) {
|
||||
this.loading = true
|
||||
api('listTemplates', { templatefilter: 'self', listall: true, page: 1, pagesize: 1 }).then(json => {
|
||||
this.loading = false
|
||||
this.data.templates = json?.listtemplatesresponse?.count
|
||||
})
|
||||
}
|
||||
},
|
||||
listInstances (zone) {
|
||||
listInstances () {
|
||||
if (!('listVirtualMachines' in this.$store.getters.apis)) {
|
||||
return
|
||||
}
|
||||
this.loading = true
|
||||
api('listVirtualMachines', { listall: true, details: 'min', page: 1, pagesize: 1 }).then(json => {
|
||||
this.loading = false
|
||||
|
|
@ -528,6 +551,9 @@ export default {
|
|||
})
|
||||
},
|
||||
listEvents () {
|
||||
if (!('listEvents' in this.$store.getters.apis)) {
|
||||
return
|
||||
}
|
||||
const params = {
|
||||
page: 1,
|
||||
pagesize: 8,
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@
|
|||
</a-form-item>
|
||||
<div
|
||||
v-if="form.protocol === 'nfs' || form.protocol === 'SMB' || form.protocol === 'iscsi' || form.protocol === 'vmfs'|| form.protocol === 'Gluster' || form.protocol === 'Linstor' ||
|
||||
(form.protocol === 'PreSetup' && hypervisorType === 'VMware') || form.protocol === 'datastorecluster'">
|
||||
(form.protocol === 'PreSetup' && hypervisorType === 'VMware') || form.protocol === 'datastorecluster' || form.provider === 'Linstor'">
|
||||
<a-form-item name="server" ref="server">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.server')" :tooltip="$t('message.server.description')"/>
|
||||
|
|
@ -376,7 +376,7 @@
|
|||
<a-input v-model:value="form.volume" :placeholder="$t('label.volume')"/>
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div v-if="form.protocol === 'Linstor'">
|
||||
<div v-if="form.protocol === 'Linstor' || form.provider === 'Linstor'">
|
||||
<a-form-item name="capacityIops" ref="capacityIops">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.capacityiops')" :tooltip="apiParams.capacityiops.description"/>
|
||||
|
|
@ -852,13 +852,7 @@ export default {
|
|||
var lun = values.lun
|
||||
url = this.iscsiURL(server, iqn, lun)
|
||||
} else if (values.protocol === 'Linstor') {
|
||||
url = this.linstorURL(server)
|
||||
params.provider = 'Linstor'
|
||||
values.managed = false
|
||||
params['details[0].resourceGroup'] = values.resourcegroup
|
||||
if (values.capacityIops && values.capacityIops.length > 0) {
|
||||
params.capacityIops = values.capacityIops.split(',').join('')
|
||||
}
|
||||
} else if (values.protocol === 'Filesystem') {
|
||||
url = this.filesystemURL(values.host, path)
|
||||
} else if (values.provider === 'Primera') {
|
||||
|
|
@ -870,6 +864,16 @@ export default {
|
|||
params['details[0].api_password'] = values.flashArrayPassword
|
||||
url = values.flashArrayURL
|
||||
}
|
||||
|
||||
if (values.provider === 'Linstor') {
|
||||
url = this.linstorURL(server)
|
||||
values.managed = false
|
||||
params['details[0].resourceGroup'] = values.resourcegroup
|
||||
if (values.capacityIops && values.capacityIops.length > 0) {
|
||||
params.capacityIops = values.capacityIops.split(',').join('')
|
||||
}
|
||||
}
|
||||
|
||||
params.url = url
|
||||
if (values.provider !== 'DefaultPrimary' && values.provider !== 'PowerFlex') {
|
||||
if (values.managed) {
|
||||
|
|
|
|||
|
|
@ -297,6 +297,8 @@
|
|||
<multi-network-selection
|
||||
:items="nics"
|
||||
:zoneId="cluster.zoneid"
|
||||
:domainid="form.domainid"
|
||||
:account="form.account"
|
||||
:selectionEnabled="false"
|
||||
:filterUnimplementedNetworks="true"
|
||||
:hypervisor="this.cluster.hypervisortype"
|
||||
|
|
@ -693,7 +695,9 @@ export default {
|
|||
this.form = reactive({
|
||||
rootdiskid: 0,
|
||||
migrateallowed: this.switches.migrateAllowed,
|
||||
forced: this.switches.forced
|
||||
forced: this.switches.forced,
|
||||
domainid: null,
|
||||
account: null
|
||||
})
|
||||
this.rules = reactive({
|
||||
displayname: [{ required: true, message: this.$t('message.error.input.value') }],
|
||||
|
|
|
|||
Loading…
Reference in New Issue