Skip to content

Commit 1d05fea

Browse files
committed
Merge branch '4.11'
2 parents 85750f9 + 52b02de commit 1d05fea

File tree

14 files changed

+474
-194
lines changed

14 files changed

+474
-194
lines changed

Diff for: core/src/main/java/com/cloud/resource/RequestWrapper.java

+15-8
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@
2929
import com.cloud.agent.api.Command;
3030

3131
public abstract class RequestWrapper {
32+
static public class CommandNotSupported extends NullPointerException {
33+
public CommandNotSupported(String msg) {
34+
super(msg);
35+
}
36+
public CommandNotSupported(String msg, Throwable cause) {
37+
super(msg);
38+
initCause(cause);
39+
}
40+
}
3241

3342
private static final Logger s_logger = Logger.getLogger(RequestWrapper.class);
3443

@@ -52,7 +61,7 @@ protected Hashtable<Class<? extends Command>, CommandWrapper> retrieveResource(f
5261

5362
keepResourceClass = keepResourceClass2;
5463
} catch (final ClassCastException e) {
55-
throw new NullPointerException("No key found for '" + command.getClass() + "' in the Map!");
64+
throw new CommandNotSupported("No key found for '" + command.getClass() + "' in the Map!");
5665
}
5766
}
5867
return resource;
@@ -69,14 +78,14 @@ protected CommandWrapper<Command, Answer, ServerResource> retrieveCommands(final
6978
final Class<? extends Command> commandClass2 = (Class<? extends Command>) keepCommandClass.getSuperclass();
7079

7180
if (commandClass2 == null) {
72-
throw new NullPointerException("All the COMMAND hierarchy tree has been visited but no compliant key has been found for '" + commandClass + "'.");
81+
throw new CommandNotSupported("All the COMMAND hierarchy tree has been visited but no compliant key has been found for '" + commandClass + "'.");
7382
}
7483

7584
commandWrapper = resourceCommands.get(commandClass2);
7685

7786
keepCommandClass = commandClass2;
7887
} catch (final ClassCastException e) {
79-
throw new NullPointerException("No key found for '" + keepCommandClass.getClass() + "' in the Map!");
88+
throw new CommandNotSupported("No key found for '" + keepCommandClass.getClass() + "' in the Map!");
8089
} catch (final NullPointerException e) {
8190
// Will now traverse all the resource hierarchy. Returning null
8291
// is not a problem.
@@ -102,18 +111,16 @@ protected CommandWrapper<Command, Answer, ServerResource> retryWhenAllFails(fina
102111
final Class<? extends ServerResource> resourceClass2 = (Class<? extends ServerResource>) keepResourceClass.getSuperclass();
103112

104113
if (resourceClass2 == null) {
105-
throw new NullPointerException("All the SERVER-RESOURCE hierarchy tree has been visited but no compliant key has been found for '" + command.getClass() + "'.");
114+
throw new CommandNotSupported("All the SERVER-RESOURCE hierarchy tree has been visited but no compliant key has been found for '" + command.getClass() + "'.");
106115
}
107116

108117
final Hashtable<Class<? extends Command>, CommandWrapper> resourceCommands2 = retrieveResource(command,
109118
(Class<? extends ServerResource>) keepResourceClass.getSuperclass());
110119
keepResourceClass = resourceClass2;
111120

112121
commandWrapper = retrieveCommands(command.getClass(), resourceCommands2);
113-
} catch (final ClassCastException e) {
114-
throw new NullPointerException("No key found for '" + command.getClass() + "' in the Map!");
115-
} catch (final NullPointerException e) {
116-
throw e;
122+
} catch (final ClassCastException | NullPointerException e) {
123+
throw new CommandNotSupported("No key found for '" + command.getClass() + "' in the Map!", e);
117124
}
118125
}
119126
return commandWrapper;

Diff for: engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDao.java

+2
Original file line numberDiff line numberDiff line change
@@ -150,4 +150,6 @@ public interface VMInstanceDao extends GenericDao<VMInstanceVO, Long>, StateDao<
150150
VMInstanceVO findVMByHostNameInZone(String hostName, long zoneId);
151151

152152
boolean isPowerStateUpToDate(long instanceId);
153+
154+
List<VMInstanceVO> listNonMigratingVmsByHostEqualsLastHost(long hostId);
153155
}

Diff for: engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java

+15
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase<VMInstanceVO, Long> implem
9393
protected GenericSearchBuilder<VMInstanceVO, String> DistinctHostNameSearch;
9494
protected SearchBuilder<VMInstanceVO> HostAndStateSearch;
9595
protected SearchBuilder<VMInstanceVO> StartingWithNoHostSearch;
96+
protected SearchBuilder<VMInstanceVO> NotMigratingSearch;
9697

9798
@Inject
9899
ResourceTagDao _tagsDao;
@@ -280,6 +281,11 @@ protected void init() {
280281
DistinctHostNameSearch.join("nicSearch", nicSearch, DistinctHostNameSearch.entity().getId(), nicSearch.entity().getInstanceId(), JoinBuilder.JoinType.INNER);
281282
DistinctHostNameSearch.done();
282283

284+
NotMigratingSearch = createSearchBuilder();
285+
NotMigratingSearch.and("host", NotMigratingSearch.entity().getHostId(), Op.EQ);
286+
NotMigratingSearch.and("lastHost", NotMigratingSearch.entity().getLastHostId(), Op.EQ);
287+
NotMigratingSearch.and("state", NotMigratingSearch.entity().getState(), Op.NEQ);
288+
NotMigratingSearch.done();
283289
}
284290

285291
@Override
@@ -304,6 +310,15 @@ public List<VMInstanceVO> listByHostId(long hostid) {
304310
return listBy(sc);
305311
}
306312

313+
@Override
314+
public List<VMInstanceVO> listNonMigratingVmsByHostEqualsLastHost(long hostId) {
315+
SearchCriteria<VMInstanceVO> sc = NotMigratingSearch.create();
316+
sc.setParameters("host", hostId);
317+
sc.setParameters("lastHost", hostId);
318+
sc.setParameters("state", State.Migrating);
319+
return listBy(sc);
320+
}
321+
307322
@Override
308323
public List<VMInstanceVO> listByZoneId(long zoneId) {
309324
SearchCriteria<VMInstanceVO> sc = AllFieldsSearch.create();

Diff for: plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java

+10-1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import javax.xml.parsers.DocumentBuilderFactory;
4848
import javax.xml.parsers.ParserConfigurationException;
4949

50+
import com.cloud.resource.RequestWrapper;
5051
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
5152
import org.apache.cloudstack.storage.to.VolumeObjectTO;
5253
import org.apache.cloudstack.utils.hypervisor.HypervisorUtils;
@@ -1432,13 +1433,21 @@ public boolean stop() {
14321433
return true;
14331434
}
14341435

1436+
/**
1437+
* This finds a command wrapper to handle the command and executes it.
1438+
* If no wrapper is found an {@see UnsupportedAnswer} is sent back.
1439+
* Any other exceptions are to be caught and wrapped in an generic {@see Answer}, marked as failed.
1440+
*
1441+
* @param cmd the instance of a {@see Command} to execute.
1442+
* @return the for the {@see Command} appropriate {@see Answer} or {@see UnsupportedAnswer}
1443+
*/
14351444
@Override
14361445
public Answer executeRequest(final Command cmd) {
14371446

14381447
final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance();
14391448
try {
14401449
return wrapper.execute(cmd, this);
1441-
} catch (final Exception e) {
1450+
} catch (final RequestWrapper.CommandNotSupported cmde) {
14421451
return Answer.createUnsupportedCommandAnswer(cmd);
14431452
}
14441453
}

Diff for: plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRequestWrapper.java

+3
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ public Answer execute(final Command command, final ServerResource serverResource
7272
commandWrapper = retryWhenAllFails(command, resourceClass, resourceCommands);
7373
}
7474

75+
if (commandWrapper == null) {
76+
throw new CommandNotSupported("No way to handle " + command.getClass());
77+
}
7578
return commandWrapper.execute(command, serverResource);
7679
}
7780
}

Diff for: plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java

+27
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
import javax.xml.xpath.XPathExpressionException;
3939
import javax.xml.xpath.XPathFactory;
4040

41+
import com.cloud.agent.api.Command;
42+
import com.cloud.agent.api.UnsupportedAnswer;
4143
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.CpuTuneDef;
4244
import org.apache.commons.lang.SystemUtils;
4345
import org.joda.time.Duration;
@@ -5191,4 +5193,29 @@ public void testSetQuotaAndPeriodMinQuota() {
51915193
Assert.assertEquals(CpuTuneDef.MIN_QUOTA, cpuTuneDef.getQuota());
51925194
Assert.assertEquals((int) (CpuTuneDef.MIN_QUOTA / pct), cpuTuneDef.getPeriod());
51935195
}
5196+
5197+
@Test
5198+
public void testUnknownCommand() {
5199+
libvirtComputingResource = new LibvirtComputingResource();
5200+
Command cmd = new Command() {
5201+
@Override public boolean executeInSequence() {
5202+
return false;
5203+
}
5204+
};
5205+
Answer ans = libvirtComputingResource.executeRequest(cmd);
5206+
assertTrue(ans instanceof UnsupportedAnswer);
5207+
}
5208+
5209+
@Test
5210+
public void testKnownCommand() {
5211+
libvirtComputingResource = new LibvirtComputingResource();
5212+
Command cmd = new PingTestCommand() {
5213+
@Override public boolean executeInSequence() {
5214+
throw new NullPointerException("test succeeded");
5215+
}
5216+
};
5217+
Answer ans = libvirtComputingResource.executeRequest(cmd);
5218+
assertFalse(ans instanceof UnsupportedAnswer);
5219+
assertTrue(ans instanceof Answer);
5220+
}
51945221
}

Diff for: server/src/main/java/com/cloud/network/router/NicProfileHelperImpl.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import javax.inject.Inject;
2323

24+
import com.cloud.utils.exception.CloudRuntimeException;
2425
import org.cloud.network.router.deployment.RouterDeploymentDefinition;
2526

2627
import com.cloud.network.IpAddressManager;
@@ -30,7 +31,6 @@
3031
import com.cloud.network.Networks.BroadcastDomainType;
3132
import com.cloud.network.vpc.PrivateIpAddress;
3233
import com.cloud.network.vpc.PrivateIpVO;
33-
import com.cloud.network.vpc.Vpc;
3434
import com.cloud.network.vpc.VpcGateway;
3535
import com.cloud.network.vpc.VpcManager;
3636
import com.cloud.network.vpc.dao.PrivateIpDao;
@@ -65,9 +65,11 @@ public NicProfile createPrivateNicProfileForGateway(final VpcGateway privateGate
6565
PrivateIpVO ipVO = _privateIpDao.allocateIpAddress(privateNetwork.getDataCenterId(), privateNetwork.getId(), privateGateway.getIp4Address());
6666

6767
final Long vpcId = privateGateway.getVpcId();
68-
final Vpc activeVpc = _vpcMgr.getActiveVpc(vpcId);
69-
if (activeVpc.isRedundant() && ipVO == null) {
68+
if (ipVO == null) {
7069
ipVO = _privateIpDao.findByIpAndVpcId(vpcId, privateGateway.getIp4Address());
70+
if (ipVO == null) {
71+
throw new CloudRuntimeException("cannot find IP address " + privateGateway.getIp4Address() + " to reuse for private gateway on vpc (id==" + vpcId + ")");
72+
}
7173
}
7274

7375
Nic privateNic = null;

Diff for: server/src/main/java/com/cloud/resource/ResourceManagerImpl.java

+74-6
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import javax.inject.Inject;
3131
import javax.naming.ConfigurationException;
3232

33+
import com.cloud.vm.dao.UserVmDetailsDao;
3334
import org.apache.cloudstack.api.ApiConstants;
3435
import org.apache.cloudstack.api.command.admin.cluster.AddClusterCmd;
3536
import org.apache.cloudstack.api.command.admin.cluster.DeleteClusterCmd;
@@ -53,6 +54,8 @@
5354
import com.cloud.agent.AgentManager;
5455
import com.cloud.agent.api.Answer;
5556
import com.cloud.agent.api.Command;
57+
import com.cloud.agent.api.GetVncPortCommand;
58+
import com.cloud.agent.api.GetVncPortAnswer;
5659
import com.cloud.agent.api.GetGPUStatsAnswer;
5760
import com.cloud.agent.api.GetGPUStatsCommand;
5861
import com.cloud.agent.api.GetHostStatsAnswer;
@@ -74,6 +77,7 @@
7477
import com.cloud.capacity.dao.CapacityDao;
7578
import com.cloud.cluster.ClusterManager;
7679
import com.cloud.configuration.Config;
80+
import com.cloud.configuration.ConfigurationManager;
7781
import com.cloud.dc.ClusterDetailsDao;
7882
import com.cloud.dc.ClusterDetailsVO;
7983
import com.cloud.dc.ClusterVO;
@@ -247,7 +251,11 @@ public void setDiscoverers(final List<? extends Discoverer> discoverers) {
247251
@Inject
248252
private VMTemplateDao _templateDao;
249253
@Inject
254+
private ConfigurationManager _configMgr;
255+
@Inject
250256
private ClusterVSMMapDao _clusterVSMMapDao;
257+
@Inject
258+
private UserVmDetailsDao userVmDetailsDao;
251259

252260
private final long _nodeId = ManagementServerNode.getManagementServerId();
253261

@@ -606,7 +614,7 @@ public List<? extends Host> discoverHosts(final AddSecondaryStorageCmd cmd) thro
606614

607615
private List<HostVO> discoverHostsFull(final Long dcId, final Long podId, Long clusterId, final String clusterName, String url, String username, String password,
608616
final String hypervisorType, final List<String> hostTags, final Map<String, String> params, final boolean deferAgentCreation) throws IllegalArgumentException, DiscoveryException,
609-
InvalidParameterValueException {
617+
InvalidParameterValueException {
610618
URI uri = null;
611619

612620
// Check if the zone exists in the system
@@ -1282,6 +1290,68 @@ public Host maintain(final PrepareForMaintenanceCmd cmd) {
12821290
}
12831291
}
12841292

1293+
/**
1294+
* Add VNC details as user VM details for each VM in 'vms' (KVM hosts only)
1295+
*/
1296+
protected void setKVMVncAccess(long hostId, List<VMInstanceVO> vms) {
1297+
for (VMInstanceVO vm : vms) {
1298+
GetVncPortAnswer vmVncPortAnswer = (GetVncPortAnswer) _agentMgr.easySend(hostId, new GetVncPortCommand(vm.getId(), vm.getInstanceName()));
1299+
if (vmVncPortAnswer != null) {
1300+
userVmDetailsDao.addDetail(vm.getId(), "kvm.vnc.address", vmVncPortAnswer.getAddress(), true);
1301+
userVmDetailsDao.addDetail(vm.getId(), "kvm.vnc.port", String.valueOf(vmVncPortAnswer.getPort()), true);
1302+
}
1303+
}
1304+
}
1305+
1306+
/**
1307+
* Configure VNC access for host VMs which have failed migrating to another host while trying to enter Maintenance mode
1308+
*/
1309+
protected void configureVncAccessForKVMHostFailedMigrations(HostVO host, List<VMInstanceVO> failedMigrations) {
1310+
if (host.getHypervisorType().equals(HypervisorType.KVM)) {
1311+
_agentMgr.pullAgentOutMaintenance(host.getId());
1312+
setKVMVncAccess(host.getId(), failedMigrations);
1313+
_agentMgr.pullAgentToMaintenance(host.getId());
1314+
}
1315+
}
1316+
1317+
/**
1318+
* Set host into ErrorInMaintenance state, as errors occurred during VM migrations. Do the following:
1319+
* - Cancel scheduled migrations for those which have already failed
1320+
* - Configure VNC access for VMs (KVM hosts only)
1321+
*/
1322+
protected boolean setHostIntoErrorInMaintenance(HostVO host, List<VMInstanceVO> failedMigrations) throws NoTransitionException {
1323+
s_logger.debug("Unable to migrate " + failedMigrations.size() + " VM(s) from host " + host.getUuid());
1324+
_haMgr.cancelScheduledMigrations(host);
1325+
configureVncAccessForKVMHostFailedMigrations(host, failedMigrations);
1326+
resourceStateTransitTo(host, ResourceState.Event.UnableToMigrate, _nodeId);
1327+
return false;
1328+
}
1329+
1330+
/**
1331+
* Safely transit host into Maintenance mode
1332+
*/
1333+
protected boolean setHostIntoMaintenance(HostVO host) throws NoTransitionException {
1334+
s_logger.debug("Host " + host.getUuid() + " entering in Maintenance");
1335+
resourceStateTransitTo(host, ResourceState.Event.InternalEnterMaintenance, _nodeId);
1336+
ActionEventUtils.onCompletedActionEvent(CallContext.current().getCallingUserId(), CallContext.current().getCallingAccountId(),
1337+
EventVO.LEVEL_INFO, EventTypes.EVENT_MAINTENANCE_PREPARE,
1338+
"completed maintenance for host " + host.getId(), 0);
1339+
return true;
1340+
}
1341+
1342+
/**
1343+
* Return true if host goes into Maintenance mode, only when:
1344+
* - No Running, Migrating or Failed migrations (host_id = last_host_id) for the host
1345+
*/
1346+
protected boolean isHostInMaintenance(HostVO host, List<VMInstanceVO> runningVms, List<VMInstanceVO> migratingVms, List<VMInstanceVO> failedMigrations) throws NoTransitionException {
1347+
if (CollectionUtils.isEmpty(runningVms) && CollectionUtils.isEmpty(migratingVms)) {
1348+
return CollectionUtils.isEmpty(failedMigrations) ?
1349+
setHostIntoMaintenance(host) :
1350+
setHostIntoErrorInMaintenance(host, failedMigrations);
1351+
}
1352+
return false;
1353+
}
1354+
12851355
@Override
12861356
public boolean checkAndMaintain(final long hostId) {
12871357
boolean hostInMaintenance = false;
@@ -1291,11 +1361,9 @@ public boolean checkAndMaintain(final long hostId) {
12911361
if (host.getType() != Host.Type.Storage) {
12921362
final List<VMInstanceVO> vos = _vmDao.listByHostId(hostId);
12931363
final List<VMInstanceVO> vosMigrating = _vmDao.listVmsMigratingFromHost(hostId);
1294-
if (vos.isEmpty() && vosMigrating.isEmpty()) {
1295-
resourceStateTransitTo(host, ResourceState.Event.InternalEnterMaintenance, _nodeId);
1296-
hostInMaintenance = true;
1297-
ActionEventUtils.onCompletedActionEvent(CallContext.current().getCallingUserId(), CallContext.current().getCallingAccountId(), EventVO.LEVEL_INFO, EventTypes.EVENT_MAINTENANCE_PREPARE, "completed maintenance for host " + hostId, 0);
1298-
}
1364+
final List<VMInstanceVO> failedVmMigrations = _vmDao.listNonMigratingVmsByHostEqualsLastHost(hostId);
1365+
1366+
hostInMaintenance = isHostInMaintenance(host, vos, vosMigrating, failedVmMigrations);
12991367
}
13001368
} catch (final NoTransitionException e) {
13011369
s_logger.debug("Cannot transmit host " + host.getId() + "to Maintenance state", e);

Diff for: server/src/main/java/com/cloud/servlet/ConsoleProxyServlet.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import javax.servlet.http.HttpServletResponse;
3636
import javax.servlet.http.HttpSession;
3737

38+
import com.cloud.resource.ResourceState;
3839
import org.apache.commons.codec.binary.Base64;
3940
import org.apache.log4j.Logger;
4041
import org.springframework.stereotype.Component;
@@ -418,7 +419,14 @@ private String composeConsoleAccessUrl(String rootUrl, VirtualMachine vm, HostVO
418419
StringBuffer sb = new StringBuffer(rootUrl);
419420
String host = hostVo.getPrivateIpAddress();
420421

421-
Pair<String, Integer> portInfo = _ms.getVncPort(vm);
422+
Pair<String, Integer> portInfo;
423+
if (hostVo.getResourceState().equals(ResourceState.ErrorInMaintenance)) {
424+
UserVmDetailVO detailAddress = _userVmDetailsDao.findDetail(vm.getId(), "kvm.vnc.address");
425+
UserVmDetailVO detailPort = _userVmDetailsDao.findDetail(vm.getId(), "kvm.vnc.port");
426+
portInfo = new Pair<>(detailAddress.getValue(), Integer.valueOf(detailPort.getValue()));
427+
} else {
428+
portInfo = _ms.getVncPort(vm);
429+
}
422430
if (s_logger.isDebugEnabled())
423431
s_logger.debug("Port info " + portInfo.first());
424432

0 commit comments

Comments
 (0)