mirror of https://github.com/apache/cloudstack.git
QuickCloud: copy authorization code from ConsoleProxyManagerImpl
QuickCloud: refactor to avoid copy paste of authentication and startup code
This commit is contained in:
parent
790d2ce82e
commit
3d78019e57
|
|
@ -23,48 +23,28 @@ import javax.inject.Inject;
|
||||||
import javax.naming.ConfigurationException;
|
import javax.naming.ConfigurationException;
|
||||||
|
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import com.cloud.agent.AgentManager;
|
import com.cloud.agent.AgentManager;
|
||||||
import com.cloud.agent.api.AgentControlAnswer;
|
|
||||||
import com.cloud.agent.api.ConsoleAccessAuthenticationAnswer;
|
|
||||||
import com.cloud.agent.api.ConsoleAccessAuthenticationCommand;
|
|
||||||
import com.cloud.agent.api.ConsoleProxyLoadReportCommand;
|
|
||||||
import com.cloud.agent.api.GetVncPortAnswer;
|
import com.cloud.agent.api.GetVncPortAnswer;
|
||||||
import com.cloud.agent.api.GetVncPortCommand;
|
import com.cloud.agent.api.GetVncPortCommand;
|
||||||
import com.cloud.agent.api.StartupCommand;
|
|
||||||
import com.cloud.agent.api.StartupProxyCommand;
|
import com.cloud.agent.api.StartupProxyCommand;
|
||||||
import com.cloud.agent.api.StopAnswer;
|
|
||||||
import com.cloud.agent.api.to.NicTO;
|
|
||||||
import com.cloud.agent.api.to.VirtualMachineTO;
|
|
||||||
import com.cloud.agent.manager.Commands;
|
|
||||||
import com.cloud.configuration.dao.ConfigurationDao;
|
import com.cloud.configuration.dao.ConfigurationDao;
|
||||||
import com.cloud.deploy.DeployDestination;
|
|
||||||
import com.cloud.exception.ConcurrentOperationException;
|
|
||||||
import com.cloud.exception.InsufficientCapacityException;
|
|
||||||
import com.cloud.exception.ResourceUnavailableException;
|
|
||||||
import com.cloud.host.HostVO;
|
import com.cloud.host.HostVO;
|
||||||
import com.cloud.host.Status;
|
|
||||||
import com.cloud.host.dao.HostDao;
|
import com.cloud.host.dao.HostDao;
|
||||||
import com.cloud.info.ConsoleProxyInfo;
|
import com.cloud.info.ConsoleProxyInfo;
|
||||||
import com.cloud.network.Network;
|
import com.cloud.keystore.KeystoreManager;
|
||||||
import com.cloud.utils.NumbersUtil;
|
import com.cloud.utils.NumbersUtil;
|
||||||
import com.cloud.utils.component.ManagerBase;
|
import com.cloud.utils.component.ManagerBase;
|
||||||
import com.cloud.vm.ConsoleProxyVO;
|
import com.cloud.vm.ConsoleProxyVO;
|
||||||
import com.cloud.vm.ReservationContext;
|
|
||||||
import com.cloud.vm.UserVmVO;
|
import com.cloud.vm.UserVmVO;
|
||||||
import com.cloud.vm.VMInstanceVO;
|
import com.cloud.vm.VMInstanceVO;
|
||||||
import com.cloud.vm.VirtualMachine;
|
|
||||||
import com.cloud.vm.VirtualMachineGuru;
|
|
||||||
import com.cloud.vm.VirtualMachineManager;
|
import com.cloud.vm.VirtualMachineManager;
|
||||||
import com.cloud.vm.VirtualMachineName;
|
|
||||||
import com.cloud.vm.VirtualMachineProfile;
|
|
||||||
import com.cloud.vm.dao.ConsoleProxyDao;
|
import com.cloud.vm.dao.ConsoleProxyDao;
|
||||||
import com.cloud.vm.dao.UserVmDao;
|
import com.cloud.vm.dao.UserVmDao;
|
||||||
import com.cloud.vm.dao.VMInstanceDao;
|
import com.cloud.vm.dao.VMInstanceDao;
|
||||||
|
|
||||||
@Local(value = { ConsoleProxyManager.class })
|
@Local(value = { ConsoleProxyManager.class })
|
||||||
public class AgentBasedConsoleProxyManager extends ManagerBase implements ConsoleProxyManager, VirtualMachineGuru<ConsoleProxyVO>, AgentHook {
|
public class AgentBasedConsoleProxyManager extends ManagerBase implements ConsoleProxyManager {
|
||||||
private static final Logger s_logger = Logger.getLogger(AgentBasedConsoleProxyManager.class);
|
private static final Logger s_logger = Logger.getLogger(AgentBasedConsoleProxyManager.class);
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
|
|
@ -85,9 +65,25 @@ public class AgentBasedConsoleProxyManager extends ManagerBase implements Consol
|
||||||
VirtualMachineManager _itMgr;
|
VirtualMachineManager _itMgr;
|
||||||
@Inject
|
@Inject
|
||||||
protected ConsoleProxyDao _cpDao;
|
protected ConsoleProxyDao _cpDao;
|
||||||
|
@Inject
|
||||||
|
protected KeystoreManager _ksMgr;
|
||||||
|
|
||||||
@Inject ConfigurationDao _configDao;
|
@Inject ConfigurationDao _configDao;
|
||||||
|
|
||||||
|
public class AgentBasedAgentHook extends AgentHookBase {
|
||||||
|
|
||||||
|
public AgentBasedAgentHook(VMInstanceDao instanceDao, HostDao hostDao, ConfigurationDao cfgDao,
|
||||||
|
KeystoreManager ksMgr, AgentManager agentMgr) {
|
||||||
|
super(instanceDao, hostDao, cfgDao, ksMgr, agentMgr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected HostVO findConsoleProxyHost(StartupProxyCommand cmd) {
|
||||||
|
return _hostDao.findByGuid(cmd.getGuid());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public int getVncPort(VMInstanceVO vm) {
|
public int getVncPort(VMInstanceVO vm) {
|
||||||
if (vm.getHostId() == null) {
|
if (vm.getHostId() == null) {
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -123,11 +119,10 @@ public class AgentBasedConsoleProxyManager extends ManagerBase implements Consol
|
||||||
|
|
||||||
_consoleProxyUrlDomain = configs.get("consoleproxy.url.domain");
|
_consoleProxyUrlDomain = configs.get("consoleproxy.url.domain");
|
||||||
|
|
||||||
_listener = new ConsoleProxyListener(this);
|
_listener =
|
||||||
|
new ConsoleProxyListener(new AgentBasedAgentHook(_instanceDao, _hostDao, _configDao, _ksMgr, _agentMgr));
|
||||||
_agentMgr.registerForHostEvents(_listener, true, true, false);
|
_agentMgr.registerForHostEvents(_listener, true, true, false);
|
||||||
|
|
||||||
_itMgr.registerGuru(VirtualMachine.Type.ConsoleProxy, this);
|
|
||||||
|
|
||||||
if (s_logger.isInfoEnabled()) {
|
if (s_logger.isInfoEnabled()) {
|
||||||
s_logger.info("AgentBasedConsoleProxyManager has been configured. SSL enabled: " + _sslEnabled);
|
s_logger.info("AgentBasedConsoleProxyManager has been configured. SSL enabled: " + _sslEnabled);
|
||||||
}
|
}
|
||||||
|
|
@ -177,64 +172,8 @@ public class AgentBasedConsoleProxyManager extends ManagerBase implements Consol
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoadReport(ConsoleProxyLoadReportCommand cmd) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AgentControlAnswer onConsoleAccessAuthentication(ConsoleAccessAuthenticationCommand cmd) {
|
|
||||||
long vmId = 0;
|
|
||||||
|
|
||||||
if (cmd.getVmId() != null && cmd.getVmId().isEmpty()) {
|
|
||||||
if (s_logger.isTraceEnabled()) {
|
|
||||||
s_logger.trace("Invalid vm id sent from proxy(happens when proxy session has terminated)");
|
|
||||||
}
|
|
||||||
return new ConsoleAccessAuthenticationAnswer(cmd, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
vmId = Long.parseLong(cmd.getVmId());
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
s_logger.error("Invalid vm id " + cmd.getVmId() + " sent from console access authentication", e);
|
|
||||||
return new ConsoleAccessAuthenticationAnswer(cmd, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO authentication channel between console proxy VM and management
|
|
||||||
// server needs to be secured,
|
|
||||||
// the data is now being sent through private network, but this is
|
|
||||||
// apparently not enough
|
|
||||||
VMInstanceVO vm = _instanceDao.findById(vmId);
|
|
||||||
if (vm == null) {
|
|
||||||
return new ConsoleAccessAuthenticationAnswer(cmd, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vm.getHostId() == null) {
|
|
||||||
s_logger.warn("VM " + vmId + " lost host info, failed authentication request");
|
|
||||||
return new ConsoleAccessAuthenticationAnswer(cmd, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
HostVO host = _hostDao.findById(vm.getHostId());
|
|
||||||
if (host == null) {
|
|
||||||
s_logger.warn("VM " + vmId + "'s host does not exist, fail authentication request");
|
|
||||||
return new ConsoleAccessAuthenticationAnswer(cmd, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
String sid = cmd.getSid();
|
|
||||||
if (sid == null || !sid.equals(vm.getVncPassword())) {
|
|
||||||
s_logger.warn("sid " + sid + " in url does not match stored sid " + vm.getVncPassword());
|
|
||||||
return new ConsoleAccessAuthenticationAnswer(cmd, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ConsoleAccessAuthenticationAnswer(cmd, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAgentConnect(HostVO host, StartupCommand cmd) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAgentDisconnect(long agentId, Status state) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConsoleProxyVO startProxy(long proxyVmId) {
|
public ConsoleProxyVO startProxy(long proxyVmId) {
|
||||||
|
|
@ -269,91 +208,8 @@ public class AgentBasedConsoleProxyManager extends ManagerBase implements Consol
|
||||||
public void resumeLastManagementState() {
|
public void resumeLastManagementState() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startAgentHttpHandlerInVM(StartupProxyCommand startupCmd) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return _name;
|
return _name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Long convertToId(String vmName) {
|
|
||||||
if (!VirtualMachineName.isValidConsoleProxyName(vmName, _instance)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return VirtualMachineName.getConsoleProxyId(vmName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ConsoleProxyVO findByName(String name) {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ConsoleProxyVO findById(long id) {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ConsoleProxyVO persist(ConsoleProxyVO vm) {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean finalizeVirtualMachineProfile(VirtualMachineProfile<ConsoleProxyVO> profile, DeployDestination dest, ReservationContext context) {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean finalizeDeployment(Commands cmds, VirtualMachineProfile<ConsoleProxyVO> profile, DeployDestination dest, ReservationContext context) {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean finalizeCommandsOnStart(Commands cmds, VirtualMachineProfile<ConsoleProxyVO> profile) {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean finalizeStart(VirtualMachineProfile<ConsoleProxyVO> profile, long hostId, Commands cmds, ReservationContext context) {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void finalizeStop(VirtualMachineProfile<ConsoleProxyVO> profile, StopAnswer answer) {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void finalizeExpunge(ConsoleProxyVO proxy) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean plugNic(Network network, NicTO nic, VirtualMachineTO vm,
|
|
||||||
ReservationContext context, DeployDestination dest) throws ConcurrentOperationException, ResourceUnavailableException,
|
|
||||||
InsufficientCapacityException {
|
|
||||||
//not supported
|
|
||||||
throw new UnsupportedOperationException("Plug nic is not supported for vm of type " + vm.getType());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean unplugNic(Network network, NicTO nic, VirtualMachineTO vm,
|
|
||||||
ReservationContext context, DeployDestination dest) throws ConcurrentOperationException, ResourceUnavailableException {
|
|
||||||
//not supported
|
|
||||||
throw new UnsupportedOperationException("Unplug nic is not supported for vm of type " + vm.getType());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void prepareStop(VirtualMachineProfile<ConsoleProxyVO> profile) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,266 @@
|
||||||
|
// 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 com.cloud.consoleproxy;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
import com.cloud.agent.AgentManager;
|
||||||
|
import com.cloud.agent.api.AgentControlAnswer;
|
||||||
|
import com.cloud.agent.api.Answer;
|
||||||
|
import com.cloud.agent.api.ConsoleAccessAuthenticationAnswer;
|
||||||
|
import com.cloud.agent.api.ConsoleAccessAuthenticationCommand;
|
||||||
|
import com.cloud.agent.api.ConsoleProxyLoadReportCommand;
|
||||||
|
import com.cloud.agent.api.GetVncPortAnswer;
|
||||||
|
import com.cloud.agent.api.GetVncPortCommand;
|
||||||
|
import com.cloud.agent.api.StartupCommand;
|
||||||
|
import com.cloud.agent.api.StartupProxyCommand;
|
||||||
|
import com.cloud.agent.api.proxy.StartConsoleProxyAgentHttpHandlerCommand;
|
||||||
|
import com.cloud.configuration.Config;
|
||||||
|
import com.cloud.configuration.dao.ConfigurationDao;
|
||||||
|
import com.cloud.exception.AgentUnavailableException;
|
||||||
|
import com.cloud.exception.OperationTimedoutException;
|
||||||
|
import com.cloud.host.HostVO;
|
||||||
|
import com.cloud.host.Status;
|
||||||
|
import com.cloud.host.dao.HostDao;
|
||||||
|
import com.cloud.keystore.KeystoreManager;
|
||||||
|
import com.cloud.servlet.ConsoleProxyServlet;
|
||||||
|
import com.cloud.utils.Ternary;
|
||||||
|
import com.cloud.vm.VirtualMachine;
|
||||||
|
import com.cloud.vm.dao.VMInstanceDao;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class to manage interactions with agent-based console access
|
||||||
|
* Extracted from ConsoleProxyManagerImpl so that other console proxy managers
|
||||||
|
* can reuse
|
||||||
|
*/
|
||||||
|
public abstract class AgentHookBase implements AgentHook {
|
||||||
|
private static final Logger s_logger = Logger.getLogger(AgentHookBase.class);
|
||||||
|
|
||||||
|
VMInstanceDao _instanceDao;
|
||||||
|
HostDao _hostDao;
|
||||||
|
ConfigurationDao _configDao;
|
||||||
|
AgentManager _agentMgr;
|
||||||
|
KeystoreManager _ksMgr;
|
||||||
|
final Random _random = new Random(System.currentTimeMillis());
|
||||||
|
private String _hashKey;
|
||||||
|
|
||||||
|
|
||||||
|
public AgentHookBase(VMInstanceDao instanceDao, HostDao hostDao, ConfigurationDao cfgDao, KeystoreManager ksMgr,
|
||||||
|
AgentManager agentMgr) {
|
||||||
|
this._instanceDao = instanceDao;
|
||||||
|
this._hostDao = hostDao;
|
||||||
|
this._agentMgr = agentMgr;
|
||||||
|
this._configDao = cfgDao;
|
||||||
|
this._ksMgr = ksMgr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHashKey() {
|
||||||
|
// although we may have race condition here, database transaction
|
||||||
|
// serialization should give us the same key
|
||||||
|
if (_hashKey == null) {
|
||||||
|
_hashKey =
|
||||||
|
_configDao.getValueAndInitIfNotExist(Config.HashKey.key(), Config.HashKey.getCategory(), UUID
|
||||||
|
.randomUUID().toString());
|
||||||
|
}
|
||||||
|
return _hashKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AgentControlAnswer onConsoleAccessAuthentication(ConsoleAccessAuthenticationCommand cmd) {
|
||||||
|
Long vmId = null;
|
||||||
|
|
||||||
|
String ticketInUrl = cmd.getTicket();
|
||||||
|
if (ticketInUrl == null) {
|
||||||
|
s_logger.error("Access ticket could not be found, you could be running an old version of console proxy. vmId: "
|
||||||
|
+ cmd.getVmId());
|
||||||
|
return new ConsoleAccessAuthenticationAnswer(cmd, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_logger.isDebugEnabled()) {
|
||||||
|
s_logger.debug("Console authentication. Ticket in url for " + cmd.getHost() + ":" + cmd.getPort() + "-"
|
||||||
|
+ cmd.getVmId() + " is " + ticketInUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cmd.isReauthenticating()) {
|
||||||
|
String ticket =
|
||||||
|
ConsoleProxyServlet.genAccessTicket(cmd.getHost(), cmd.getPort(), cmd.getSid(), cmd.getVmId());
|
||||||
|
if (s_logger.isDebugEnabled()) {
|
||||||
|
s_logger.debug("Console authentication. Ticket in 1 minute boundary for " + cmd.getHost() + ":"
|
||||||
|
+ cmd.getPort() + "-" + cmd.getVmId() + " is " + ticket);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ticket.equals(ticketInUrl)) {
|
||||||
|
Date now = new Date();
|
||||||
|
// considering of minute round-up
|
||||||
|
String minuteEarlyTicket =
|
||||||
|
ConsoleProxyServlet.genAccessTicket(cmd.getHost(), cmd.getPort(), cmd.getSid(), cmd.getVmId(),
|
||||||
|
new Date(now.getTime() - 60 * 1000));
|
||||||
|
|
||||||
|
if (s_logger.isDebugEnabled()) {
|
||||||
|
s_logger.debug("Console authentication. Ticket in 2-minute boundary for " + cmd.getHost() + ":"
|
||||||
|
+ cmd.getPort() + "-" + cmd.getVmId() + " is " + minuteEarlyTicket);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!minuteEarlyTicket.equals(ticketInUrl)) {
|
||||||
|
s_logger.error("Access ticket expired or has been modified. vmId: " + cmd.getVmId()
|
||||||
|
+ "ticket in URL: " + ticketInUrl + ", tickets to check against: " + ticket + ","
|
||||||
|
+ minuteEarlyTicket);
|
||||||
|
return new ConsoleAccessAuthenticationAnswer(cmd, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd.getVmId() != null && cmd.getVmId().isEmpty()) {
|
||||||
|
if (s_logger.isDebugEnabled()) {
|
||||||
|
s_logger.debug("Invalid vm id sent from proxy(happens when proxy session has terminated)");
|
||||||
|
}
|
||||||
|
return new ConsoleAccessAuthenticationAnswer(cmd, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualMachine vm = _instanceDao.findByUuid(cmd.getVmId());
|
||||||
|
if (vm == null) {
|
||||||
|
vm = _instanceDao.findById(Long.parseLong(cmd.getVmId()));
|
||||||
|
}
|
||||||
|
if (vm == null) {
|
||||||
|
s_logger.error("Invalid vm id " + cmd.getVmId() + " sent from console access authentication");
|
||||||
|
return new ConsoleAccessAuthenticationAnswer(cmd, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vm.getHostId() == null) {
|
||||||
|
s_logger.warn("VM " + vmId + " lost host info, failed authentication request");
|
||||||
|
return new ConsoleAccessAuthenticationAnswer(cmd, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
HostVO host = _hostDao.findById(vm.getHostId());
|
||||||
|
if (host == null) {
|
||||||
|
s_logger.warn("VM " + vmId + "'s host does not exist, fail authentication request");
|
||||||
|
return new ConsoleAccessAuthenticationAnswer(cmd, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
String sid = cmd.getSid();
|
||||||
|
if (sid == null || !sid.equals(vm.getVncPassword())) {
|
||||||
|
s_logger.warn("sid " + sid + " in url does not match stored sid " + vm.getVncPassword());
|
||||||
|
return new ConsoleAccessAuthenticationAnswer(cmd, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd.isReauthenticating()) {
|
||||||
|
ConsoleAccessAuthenticationAnswer authenticationAnswer = new ConsoleAccessAuthenticationAnswer(cmd, true);
|
||||||
|
authenticationAnswer.setReauthenticating(true);
|
||||||
|
|
||||||
|
s_logger.info("Re-authentication request, ask host " + vm.getHostId() + " for new console info");
|
||||||
|
GetVncPortAnswer answer =
|
||||||
|
(GetVncPortAnswer) _agentMgr.easySend(vm.getHostId(),
|
||||||
|
new GetVncPortCommand(vm.getId(), vm.getInstanceName()));
|
||||||
|
|
||||||
|
if (answer != null && answer.getResult()) {
|
||||||
|
Ternary<String, String, String> parsedHostInfo = ConsoleProxyServlet.parseHostInfo(answer.getAddress());
|
||||||
|
|
||||||
|
if (parsedHostInfo.second() != null && parsedHostInfo.third() != null) {
|
||||||
|
|
||||||
|
s_logger.info("Re-authentication result. vm: " + vm.getId() + ", tunnel url: "
|
||||||
|
+ parsedHostInfo.second() + ", tunnel session: " + parsedHostInfo.third());
|
||||||
|
|
||||||
|
authenticationAnswer.setTunnelUrl(parsedHostInfo.second());
|
||||||
|
authenticationAnswer.setTunnelSession(parsedHostInfo.third());
|
||||||
|
} else {
|
||||||
|
s_logger.info("Re-authentication result. vm: " + vm.getId() + ", host address: "
|
||||||
|
+ parsedHostInfo.first() + ", port: " + answer.getPort());
|
||||||
|
|
||||||
|
authenticationAnswer.setHost(parsedHostInfo.first());
|
||||||
|
authenticationAnswer.setPort(answer.getPort());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s_logger.warn("Re-authentication request failed");
|
||||||
|
|
||||||
|
authenticationAnswer.setSuccess(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return authenticationAnswer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ConsoleAccessAuthenticationAnswer(cmd, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startAgentHttpHandlerInVM(StartupProxyCommand startupCmd) {
|
||||||
|
StartConsoleProxyAgentHttpHandlerCommand cmd = null;
|
||||||
|
if (_configDao.isPremium()) {
|
||||||
|
String storePassword = String.valueOf(_random.nextLong());
|
||||||
|
byte[] ksBits =
|
||||||
|
_ksMgr.getKeystoreBits(ConsoleProxyManager.CERTIFICATE_NAME, ConsoleProxyManager.CERTIFICATE_NAME,
|
||||||
|
storePassword);
|
||||||
|
|
||||||
|
assert (ksBits != null);
|
||||||
|
if (ksBits == null) {
|
||||||
|
s_logger.error("Could not find and construct a valid SSL certificate");
|
||||||
|
}
|
||||||
|
cmd = new StartConsoleProxyAgentHttpHandlerCommand(ksBits, storePassword);
|
||||||
|
cmd.setEncryptorPassword(getHashKey());
|
||||||
|
} else {
|
||||||
|
cmd = new StartConsoleProxyAgentHttpHandlerCommand();
|
||||||
|
cmd.setEncryptorPassword(getHashKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
HostVO consoleProxyHost = findConsoleProxyHost(startupCmd);
|
||||||
|
|
||||||
|
assert (consoleProxyHost != null);
|
||||||
|
|
||||||
|
Answer answer = _agentMgr.send(consoleProxyHost.getId(), cmd);
|
||||||
|
if (answer == null || !answer.getResult()) {
|
||||||
|
s_logger.error("Console proxy agent reported that it failed to execute http handling startup command");
|
||||||
|
} else {
|
||||||
|
s_logger.info("Successfully sent out command to start HTTP handling in console proxy agent");
|
||||||
|
}
|
||||||
|
} catch (AgentUnavailableException e) {
|
||||||
|
s_logger.error("Unable to send http handling startup command to the console proxy resource for proxy:"
|
||||||
|
+ startupCmd.getProxyVmId(), e);
|
||||||
|
} catch (OperationTimedoutException e) {
|
||||||
|
s_logger.error(
|
||||||
|
"Unable to send http handling startup command(time out) to the console proxy resource for proxy:"
|
||||||
|
+ startupCmd.getProxyVmId(), e);
|
||||||
|
} catch (OutOfMemoryError e) {
|
||||||
|
s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched");
|
||||||
|
System.exit(1);
|
||||||
|
} catch (Exception e) {
|
||||||
|
s_logger.error(
|
||||||
|
"Unexpected exception when sending http handling startup command(time out) to the console proxy resource for proxy:"
|
||||||
|
+ startupCmd.getProxyVmId(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract HostVO findConsoleProxyHost(StartupProxyCommand cmd);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadReport(ConsoleProxyLoadReportCommand cmd) {
|
||||||
|
// no-op since we do not auto-scale
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAgentConnect(HostVO host, StartupCommand cmd) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAgentDisconnect(long agentId, Status state) {
|
||||||
|
// no-op since we do not autoscale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -23,7 +23,6 @@ import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Random;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import javax.ejb.Local;
|
import javax.ejb.Local;
|
||||||
|
|
@ -35,13 +34,8 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
import com.cloud.agent.AgentManager;
|
import com.cloud.agent.AgentManager;
|
||||||
import com.cloud.agent.api.AgentControlAnswer;
|
|
||||||
import com.cloud.agent.api.Answer;
|
import com.cloud.agent.api.Answer;
|
||||||
import com.cloud.agent.api.ConsoleAccessAuthenticationAnswer;
|
|
||||||
import com.cloud.agent.api.ConsoleAccessAuthenticationCommand;
|
|
||||||
import com.cloud.agent.api.ConsoleProxyLoadReportCommand;
|
import com.cloud.agent.api.ConsoleProxyLoadReportCommand;
|
||||||
import com.cloud.agent.api.GetVncPortAnswer;
|
|
||||||
import com.cloud.agent.api.GetVncPortCommand;
|
|
||||||
import com.cloud.agent.api.RebootCommand;
|
import com.cloud.agent.api.RebootCommand;
|
||||||
import com.cloud.agent.api.StartupCommand;
|
import com.cloud.agent.api.StartupCommand;
|
||||||
import com.cloud.agent.api.StartupProxyCommand;
|
import com.cloud.agent.api.StartupProxyCommand;
|
||||||
|
|
@ -49,7 +43,6 @@ import com.cloud.agent.api.StopAnswer;
|
||||||
import com.cloud.agent.api.check.CheckSshAnswer;
|
import com.cloud.agent.api.check.CheckSshAnswer;
|
||||||
import com.cloud.agent.api.check.CheckSshCommand;
|
import com.cloud.agent.api.check.CheckSshCommand;
|
||||||
import com.cloud.agent.api.proxy.ConsoleProxyLoadAnswer;
|
import com.cloud.agent.api.proxy.ConsoleProxyLoadAnswer;
|
||||||
import com.cloud.agent.api.proxy.StartConsoleProxyAgentHttpHandlerCommand;
|
|
||||||
import com.cloud.agent.api.to.NicTO;
|
import com.cloud.agent.api.to.NicTO;
|
||||||
import com.cloud.agent.api.to.VirtualMachineTO;
|
import com.cloud.agent.api.to.VirtualMachineTO;
|
||||||
import com.cloud.agent.manager.Commands;
|
import com.cloud.agent.manager.Commands;
|
||||||
|
|
@ -66,10 +59,8 @@ import com.cloud.dc.dao.DataCenterDao;
|
||||||
import com.cloud.dc.dao.HostPodDao;
|
import com.cloud.dc.dao.HostPodDao;
|
||||||
import com.cloud.deploy.DataCenterDeployment;
|
import com.cloud.deploy.DataCenterDeployment;
|
||||||
import com.cloud.deploy.DeployDestination;
|
import com.cloud.deploy.DeployDestination;
|
||||||
import com.cloud.exception.AgentUnavailableException;
|
|
||||||
import com.cloud.exception.ConcurrentOperationException;
|
import com.cloud.exception.ConcurrentOperationException;
|
||||||
import com.cloud.exception.InsufficientCapacityException;
|
import com.cloud.exception.InsufficientCapacityException;
|
||||||
import com.cloud.exception.OperationTimedoutException;
|
|
||||||
import com.cloud.exception.ResourceUnavailableException;
|
import com.cloud.exception.ResourceUnavailableException;
|
||||||
import com.cloud.exception.StorageUnavailableException;
|
import com.cloud.exception.StorageUnavailableException;
|
||||||
import com.cloud.host.Host;
|
import com.cloud.host.Host;
|
||||||
|
|
@ -106,7 +97,6 @@ import com.cloud.resource.ServerResource;
|
||||||
import com.cloud.resource.UnableDeleteHostException;
|
import com.cloud.resource.UnableDeleteHostException;
|
||||||
import com.cloud.service.ServiceOfferingVO;
|
import com.cloud.service.ServiceOfferingVO;
|
||||||
import com.cloud.service.dao.ServiceOfferingDao;
|
import com.cloud.service.dao.ServiceOfferingDao;
|
||||||
import com.cloud.servlet.ConsoleProxyServlet;
|
|
||||||
import com.cloud.storage.StorageManager;
|
import com.cloud.storage.StorageManager;
|
||||||
import com.cloud.storage.StoragePoolStatus;
|
import com.cloud.storage.StoragePoolStatus;
|
||||||
import com.cloud.storage.VMTemplateHostVO;
|
import com.cloud.storage.VMTemplateHostVO;
|
||||||
|
|
@ -123,7 +113,6 @@ import com.cloud.user.UserContext;
|
||||||
import com.cloud.utils.DateUtil;
|
import com.cloud.utils.DateUtil;
|
||||||
import com.cloud.utils.NumbersUtil;
|
import com.cloud.utils.NumbersUtil;
|
||||||
import com.cloud.utils.Pair;
|
import com.cloud.utils.Pair;
|
||||||
import com.cloud.utils.Ternary;
|
|
||||||
import com.cloud.utils.component.ManagerBase;
|
import com.cloud.utils.component.ManagerBase;
|
||||||
import com.cloud.utils.db.DB;
|
import com.cloud.utils.db.DB;
|
||||||
import com.cloud.utils.db.GlobalLock;
|
import com.cloud.utils.db.GlobalLock;
|
||||||
|
|
@ -167,7 +156,7 @@ import com.google.gson.GsonBuilder;
|
||||||
//
|
//
|
||||||
@Local(value = { ConsoleProxyManager.class, ConsoleProxyService.class })
|
@Local(value = { ConsoleProxyManager.class, ConsoleProxyService.class })
|
||||||
public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxyManager,
|
public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxyManager,
|
||||||
AgentHook, VirtualMachineGuru<ConsoleProxyVO>, SystemVmLoadScanHandler<Long>, ResourceStateAdapter {
|
VirtualMachineGuru<ConsoleProxyVO>, SystemVmLoadScanHandler<Long>, ResourceStateAdapter {
|
||||||
private static final Logger s_logger = Logger.getLogger(ConsoleProxyManagerImpl.class);
|
private static final Logger s_logger = Logger.getLogger(ConsoleProxyManagerImpl.class);
|
||||||
|
|
||||||
private static final int DEFAULT_CAPACITY_SCAN_INTERVAL = 30000; // 30 seconds
|
private static final int DEFAULT_CAPACITY_SCAN_INTERVAL = 30000; // 30 seconds
|
||||||
|
|
@ -455,7 +444,131 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
|
||||||
private KeystoreDao _ksDao;
|
private KeystoreDao _ksDao;
|
||||||
@Inject
|
@Inject
|
||||||
private KeystoreManager _ksMgr;
|
private KeystoreManager _ksMgr;
|
||||||
private final Random _random = new Random(System.currentTimeMillis());
|
|
||||||
|
public class VmBasedAgentHook extends AgentHookBase {
|
||||||
|
|
||||||
|
public VmBasedAgentHook(VMInstanceDao instanceDao, HostDao hostDao, ConfigurationDao cfgDao,
|
||||||
|
KeystoreManager ksMgr, AgentManager agentMgr) {
|
||||||
|
super(instanceDao, hostDao, cfgDao, ksMgr, agentMgr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadReport(ConsoleProxyLoadReportCommand cmd) {
|
||||||
|
if (cmd.getLoadInfo() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsoleProxyStatus status = null;
|
||||||
|
try {
|
||||||
|
GsonBuilder gb = new GsonBuilder();
|
||||||
|
gb.setVersion(1.3);
|
||||||
|
Gson gson = gb.create();
|
||||||
|
status = gson.fromJson(cmd.getLoadInfo(), ConsoleProxyStatus.class);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
s_logger.warn("Unable to parse load info from proxy, proxy vm id : " + cmd.getProxyVmId() + ", info : " + cmd.getLoadInfo());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status != null) {
|
||||||
|
int count = 0;
|
||||||
|
if (status.getConnections() != null) {
|
||||||
|
count = status.getConnections().length;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] details = null;
|
||||||
|
if (cmd.getLoadInfo() != null) {
|
||||||
|
details = cmd.getLoadInfo().getBytes(Charset.forName("US-ASCII"));
|
||||||
|
}
|
||||||
|
_consoleProxyDao.update(cmd.getProxyVmId(), count, DateUtil.currentGMTTime(), details);
|
||||||
|
} else {
|
||||||
|
if (s_logger.isTraceEnabled()) {
|
||||||
|
s_logger.trace("Unable to get console proxy load info, id : " + cmd.getProxyVmId());
|
||||||
|
}
|
||||||
|
|
||||||
|
_consoleProxyDao.update(cmd.getProxyVmId(), 0, DateUtil.currentGMTTime(), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAgentConnect(HostVO host, StartupCommand cmd) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAgentDisconnect(long agentId, com.cloud.host.Status state) {
|
||||||
|
|
||||||
|
if (state == com.cloud.host.Status.Alert || state == com.cloud.host.Status.Disconnected) {
|
||||||
|
// be it either in alert or in disconnected state, the agent
|
||||||
|
// process
|
||||||
|
// may be gone in the VM,
|
||||||
|
// we will be reacting to stop the corresponding VM and let the
|
||||||
|
// scan
|
||||||
|
// process to
|
||||||
|
HostVO host = _hostDao.findById(agentId);
|
||||||
|
if (host.getType() == Type.ConsoleProxy) {
|
||||||
|
String name = host.getName();
|
||||||
|
if (s_logger.isInfoEnabled()) {
|
||||||
|
s_logger.info("Console proxy agent disconnected, proxy: " + name);
|
||||||
|
}
|
||||||
|
if (name != null && name.startsWith("v-")) {
|
||||||
|
String[] tokens = name.split("-");
|
||||||
|
long proxyVmId = 0;
|
||||||
|
try {
|
||||||
|
proxyVmId = Long.parseLong(tokens[1]);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
s_logger.error("Unexpected exception " + e.getMessage(), e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final ConsoleProxyVO proxy = _consoleProxyDao.findById(proxyVmId);
|
||||||
|
if (proxy != null) {
|
||||||
|
|
||||||
|
// Disable this feature for now, as it conflicts
|
||||||
|
// with
|
||||||
|
// the case of allowing user to reboot console proxy
|
||||||
|
// when rebooting happens, we will receive
|
||||||
|
// disconnect
|
||||||
|
// here and we can't enter into stopping process,
|
||||||
|
// as when the rebooted one comes up, it will kick
|
||||||
|
// off a
|
||||||
|
// newly started one and trigger the process
|
||||||
|
// continue on forever
|
||||||
|
|
||||||
|
/*
|
||||||
|
* _capacityScanScheduler.execute(new Runnable() {
|
||||||
|
* public void run() { if(s_logger.isInfoEnabled())
|
||||||
|
* s_logger.info("Stop console proxy " +
|
||||||
|
* proxy.getName() +
|
||||||
|
* " VM because of that the agent running inside it has disconnected"
|
||||||
|
* ); stopProxy(proxy.getId()); } });
|
||||||
|
*/
|
||||||
|
} else {
|
||||||
|
if (s_logger.isInfoEnabled()) {
|
||||||
|
s_logger.info("Console proxy agent disconnected but corresponding console proxy VM no longer exists in DB, proxy: "
|
||||||
|
+ name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assert (false) : "Invalid console proxy name: " + name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected HostVO findConsoleProxyHost(StartupProxyCommand startupCmd) {
|
||||||
|
long proxyVmId = startupCmd.getProxyVmId();
|
||||||
|
ConsoleProxyVO consoleProxy = _consoleProxyDao.findById(proxyVmId);
|
||||||
|
if (consoleProxy == null) {
|
||||||
|
s_logger.info("Proxy " + proxyVmId + " is no longer in DB, skip sending startup command");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert (consoleProxy != null);
|
||||||
|
return findConsoleProxyHostByName(consoleProxy.getHostName());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConsoleProxyInfo assignProxy(final long dataCenterId, final long vmId) {
|
public ConsoleProxyInfo assignProxy(final long dataCenterId, final long vmId) {
|
||||||
|
|
@ -847,181 +960,9 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoadReport(ConsoleProxyLoadReportCommand cmd) {
|
|
||||||
if (cmd.getLoadInfo() == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConsoleProxyStatus status = null;
|
|
||||||
try {
|
|
||||||
GsonBuilder gb = new GsonBuilder();
|
|
||||||
gb.setVersion(1.3);
|
|
||||||
Gson gson = gb.create();
|
|
||||||
status = gson.fromJson(cmd.getLoadInfo(), ConsoleProxyStatus.class);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
s_logger.warn("Unable to parse load info from proxy, proxy vm id : " + cmd.getProxyVmId() + ", info : " + cmd.getLoadInfo());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status != null) {
|
public void handleAgentDisconnect(long agentId, com.cloud.host.Status state) {
|
||||||
int count = 0;
|
|
||||||
if (status.getConnections() != null) {
|
|
||||||
count = status.getConnections().length;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] details = null;
|
|
||||||
if (cmd.getLoadInfo() != null) {
|
|
||||||
details = cmd.getLoadInfo().getBytes(Charset.forName("US-ASCII"));
|
|
||||||
}
|
|
||||||
_consoleProxyDao.update(cmd.getProxyVmId(), count, DateUtil.currentGMTTime(), details);
|
|
||||||
} else {
|
|
||||||
if (s_logger.isTraceEnabled()) {
|
|
||||||
s_logger.trace("Unable to get console proxy load info, id : " + cmd.getProxyVmId());
|
|
||||||
}
|
|
||||||
|
|
||||||
_consoleProxyDao.update(cmd.getProxyVmId(), 0, DateUtil.currentGMTTime(), null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AgentControlAnswer onConsoleAccessAuthentication(ConsoleAccessAuthenticationCommand cmd) {
|
|
||||||
Long vmId = null;
|
|
||||||
|
|
||||||
String ticketInUrl = cmd.getTicket();
|
|
||||||
if (ticketInUrl == null) {
|
|
||||||
s_logger.error("Access ticket could not be found, you could be running an old version of console proxy. vmId: " + cmd.getVmId());
|
|
||||||
return new ConsoleAccessAuthenticationAnswer(cmd, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s_logger.isDebugEnabled()) {
|
|
||||||
s_logger.debug("Console authentication. Ticket in url for " + cmd.getHost() + ":" + cmd.getPort() + "-" + cmd.getVmId() + " is " + ticketInUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!cmd.isReauthenticating()) {
|
|
||||||
String ticket = ConsoleProxyServlet.genAccessTicket(cmd.getHost(), cmd.getPort(), cmd.getSid(), cmd.getVmId());
|
|
||||||
if (s_logger.isDebugEnabled()) {
|
|
||||||
s_logger.debug("Console authentication. Ticket in 1 minute boundary for " + cmd.getHost() + ":" + cmd.getPort() + "-" + cmd.getVmId() + " is " + ticket);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ticket.equals(ticketInUrl)) {
|
|
||||||
Date now = new Date();
|
|
||||||
// considering of minute round-up
|
|
||||||
String minuteEarlyTicket = ConsoleProxyServlet.genAccessTicket(cmd.getHost(), cmd.getPort(), cmd.getSid(), cmd.getVmId(), new Date(now.getTime() - 60 * 1000));
|
|
||||||
|
|
||||||
if (s_logger.isDebugEnabled()) {
|
|
||||||
s_logger.debug("Console authentication. Ticket in 2-minute boundary for " + cmd.getHost() + ":" + cmd.getPort() + "-" + cmd.getVmId() + " is " + minuteEarlyTicket);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!minuteEarlyTicket.equals(ticketInUrl)) {
|
|
||||||
s_logger.error("Access ticket expired or has been modified. vmId: " + cmd.getVmId() + "ticket in URL: " + ticketInUrl + ", tickets to check against: " + ticket + ","
|
|
||||||
+ minuteEarlyTicket);
|
|
||||||
return new ConsoleAccessAuthenticationAnswer(cmd, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cmd.getVmId() != null && cmd.getVmId().isEmpty()) {
|
|
||||||
if (s_logger.isDebugEnabled()) {
|
|
||||||
s_logger.debug("Invalid vm id sent from proxy(happens when proxy session has terminated)");
|
|
||||||
}
|
|
||||||
return new ConsoleAccessAuthenticationAnswer(cmd, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
VirtualMachine vm = _instanceDao.findByUuid(cmd.getVmId());
|
|
||||||
if (vm == null) {
|
|
||||||
vm = _instanceDao.findById(Long.parseLong(cmd.getVmId()));
|
|
||||||
}
|
|
||||||
if (vm == null) {
|
|
||||||
s_logger.error("Invalid vm id " + cmd.getVmId() + " sent from console access authentication");
|
|
||||||
return new ConsoleAccessAuthenticationAnswer(cmd, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vm.getHostId() == null) {
|
|
||||||
s_logger.warn("VM " + vmId + " lost host info, failed authentication request");
|
|
||||||
return new ConsoleAccessAuthenticationAnswer(cmd, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
HostVO host = _hostDao.findById(vm.getHostId());
|
|
||||||
if (host == null) {
|
|
||||||
s_logger.warn("VM " + vmId + "'s host does not exist, fail authentication request");
|
|
||||||
return new ConsoleAccessAuthenticationAnswer(cmd, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
String sid = cmd.getSid();
|
|
||||||
if (sid == null || !sid.equals(vm.getVncPassword())) {
|
|
||||||
s_logger.warn("sid " + sid + " in url does not match stored sid " + vm.getVncPassword());
|
|
||||||
return new ConsoleAccessAuthenticationAnswer(cmd, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(cmd.isReauthenticating()) {
|
|
||||||
ConsoleAccessAuthenticationAnswer authenticationAnswer = new ConsoleAccessAuthenticationAnswer(cmd, true);
|
|
||||||
authenticationAnswer.setReauthenticating(true);
|
|
||||||
|
|
||||||
s_logger.info("Re-authentication request, ask host " + vm.getHostId() + " for new console info");
|
|
||||||
GetVncPortAnswer answer = (GetVncPortAnswer) _agentMgr.easySend(vm.getHostId(), new
|
|
||||||
GetVncPortCommand(vm.getId(), vm.getInstanceName()));
|
|
||||||
|
|
||||||
if (answer != null && answer.getResult()) {
|
|
||||||
Ternary<String, String, String> parsedHostInfo = ConsoleProxyServlet.parseHostInfo(answer.getAddress());
|
|
||||||
|
|
||||||
if(parsedHostInfo.second() != null && parsedHostInfo.third() != null) {
|
|
||||||
|
|
||||||
s_logger.info("Re-authentication result. vm: " + vm.getId() + ", tunnel url: " + parsedHostInfo.second()
|
|
||||||
+ ", tunnel session: " + parsedHostInfo.third());
|
|
||||||
|
|
||||||
authenticationAnswer.setTunnelUrl(parsedHostInfo.second());
|
|
||||||
authenticationAnswer.setTunnelSession(parsedHostInfo.third());
|
|
||||||
} else {
|
|
||||||
s_logger.info("Re-authentication result. vm: " + vm.getId() + ", host address: " + parsedHostInfo.first()
|
|
||||||
+ ", port: " + answer.getPort());
|
|
||||||
|
|
||||||
authenticationAnswer.setHost(parsedHostInfo.first());
|
|
||||||
authenticationAnswer.setPort(answer.getPort());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
s_logger.warn("Re-authentication request failed");
|
|
||||||
|
|
||||||
authenticationAnswer.setSuccess(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return authenticationAnswer;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ConsoleAccessAuthenticationAnswer(cmd, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAgentConnect(HostVO host, StartupCommand cmd) {
|
|
||||||
// if (host.getType() == Type.ConsoleProxy) {
|
|
||||||
// // TODO we can use this event to mark the proxy is up and
|
|
||||||
// // functioning instead of
|
|
||||||
// // pinging the console proxy VM command port
|
|
||||||
// //
|
|
||||||
// // for now, just log a message
|
|
||||||
// if (s_logger.isInfoEnabled()) {
|
|
||||||
// s_logger.info("Console proxy agent is connected. proxy: " + host.getName());
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /* update public/private ip address */
|
|
||||||
// if (_IpAllocator != null && _IpAllocator.exteralIpAddressAllocatorEnabled()) {
|
|
||||||
// try {
|
|
||||||
// ConsoleProxyVO console = findConsoleProxyByHost(host);
|
|
||||||
// if (console == null) {
|
|
||||||
// s_logger.debug("Can't find console proxy ");
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// console.setPrivateIpAddress(cmd.getPrivateIpAddress());
|
|
||||||
// console.setPublicIpAddress(cmd.getPublicIpAddress());
|
|
||||||
// console.setPublicNetmask(cmd.getPublicNetmask());
|
|
||||||
// _consoleProxyDao.persist(console);
|
|
||||||
// } catch (NumberFormatException e) {
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAgentDisconnect(long agentId, com.cloud.host.Status state) {
|
|
||||||
if (state == com.cloud.host.Status.Alert || state == com.cloud.host.Status.Disconnected) {
|
if (state == com.cloud.host.Status.Alert || state == com.cloud.host.Status.Disconnected) {
|
||||||
// be it either in alert or in disconnected state, the agent process
|
// be it either in alert or in disconnected state, the agent process
|
||||||
// may be gone in the VM,
|
// may be gone in the VM,
|
||||||
|
|
@ -1496,7 +1437,9 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
|
||||||
value = agentMgrConfigs.get("port");
|
value = agentMgrConfigs.get("port");
|
||||||
_mgmt_port = NumbersUtil.parseInt(value, 8250);
|
_mgmt_port = NumbersUtil.parseInt(value, 8250);
|
||||||
|
|
||||||
_listener = new ConsoleProxyListener(this);
|
_listener =
|
||||||
|
new ConsoleProxyListener(new VmBasedAgentHook(_instanceDao, _hostDao, _configDao, _ksMgr,
|
||||||
|
_agentMgr));
|
||||||
_agentMgr.registerForHostEvents(_listener, true, true, false);
|
_agentMgr.registerForHostEvents(_listener, true, true, false);
|
||||||
|
|
||||||
_itMgr.registerGuru(VirtualMachine.Type.ConsoleProxy, this);
|
_itMgr.registerGuru(VirtualMachine.Type.ConsoleProxy, this);
|
||||||
|
|
@ -1719,52 +1662,7 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
|
||||||
_consoleProxyDao.update(proxy.getId(), proxy);
|
_consoleProxyDao.update(proxy.getId(), proxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startAgentHttpHandlerInVM(StartupProxyCommand startupCmd) {
|
|
||||||
StartConsoleProxyAgentHttpHandlerCommand cmd = null;
|
|
||||||
if (_configDao.isPremium()) {
|
|
||||||
String storePassword = String.valueOf(_random.nextLong());
|
|
||||||
byte[] ksBits = _ksMgr.getKeystoreBits(ConsoleProxyManager.CERTIFICATE_NAME, ConsoleProxyManager.CERTIFICATE_NAME, storePassword);
|
|
||||||
|
|
||||||
assert (ksBits != null);
|
|
||||||
if (ksBits == null) {
|
|
||||||
s_logger.error("Could not find and construct a valid SSL certificate");
|
|
||||||
}
|
|
||||||
cmd = new StartConsoleProxyAgentHttpHandlerCommand(ksBits, storePassword);
|
|
||||||
cmd.setEncryptorPassword(getHashKey());
|
|
||||||
} else {
|
|
||||||
cmd = new StartConsoleProxyAgentHttpHandlerCommand();
|
|
||||||
cmd.setEncryptorPassword(getHashKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
long proxyVmId = startupCmd.getProxyVmId();
|
|
||||||
ConsoleProxyVO consoleProxy = _consoleProxyDao.findById(proxyVmId);
|
|
||||||
if (consoleProxy == null) {
|
|
||||||
s_logger.info("Proxy " + proxyVmId + " is no longer in DB, skip sending startup command");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert (consoleProxy != null);
|
|
||||||
HostVO consoleProxyHost = findConsoleProxyHostByName(consoleProxy.getHostName());
|
|
||||||
|
|
||||||
Answer answer = _agentMgr.send(consoleProxyHost.getId(), cmd);
|
|
||||||
if (answer == null || !answer.getResult()) {
|
|
||||||
s_logger.error("Console proxy agent reported that it failed to execute http handling startup command");
|
|
||||||
} else {
|
|
||||||
s_logger.info("Successfully sent out command to start HTTP handling in console proxy agent");
|
|
||||||
}
|
|
||||||
} catch (AgentUnavailableException e) {
|
|
||||||
s_logger.error("Unable to send http handling startup command to the console proxy resource for proxy:" + startupCmd.getProxyVmId(), e);
|
|
||||||
} catch (OperationTimedoutException e) {
|
|
||||||
s_logger.error("Unable to send http handling startup command(time out) to the console proxy resource for proxy:" + startupCmd.getProxyVmId(), e);
|
|
||||||
} catch (OutOfMemoryError e) {
|
|
||||||
s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched");
|
|
||||||
System.exit(1);
|
|
||||||
} catch (Exception e) {
|
|
||||||
s_logger.error("Unexpected exception when sending http handling startup command(time out) to the console proxy resource for proxy:" + startupCmd.getProxyVmId(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConsoleProxyVO persist(ConsoleProxyVO proxy) {
|
public ConsoleProxyVO persist(ConsoleProxyVO proxy) {
|
||||||
|
|
|
||||||
|
|
@ -16,29 +16,59 @@
|
||||||
// under the License.
|
// under the License.
|
||||||
package com.cloud.consoleproxy;
|
package com.cloud.consoleproxy;
|
||||||
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
import javax.ejb.Local;
|
import javax.ejb.Local;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.naming.ConfigurationException;
|
import javax.naming.ConfigurationException;
|
||||||
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
import com.cloud.agent.api.StartupCommand;
|
||||||
|
import com.cloud.agent.api.StartupProxyCommand;
|
||||||
import com.cloud.configuration.dao.ConfigurationDao;
|
import com.cloud.configuration.dao.ConfigurationDao;
|
||||||
import com.cloud.host.Host.Type;
|
import com.cloud.host.Host.Type;
|
||||||
import com.cloud.host.HostVO;
|
import com.cloud.host.HostVO;
|
||||||
|
import com.cloud.host.dao.HostDao;
|
||||||
import com.cloud.info.ConsoleProxyInfo;
|
import com.cloud.info.ConsoleProxyInfo;
|
||||||
|
import com.cloud.keystore.KeystoreDao;
|
||||||
|
import com.cloud.keystore.KeystoreManager;
|
||||||
import com.cloud.resource.ResourceManager;
|
import com.cloud.resource.ResourceManager;
|
||||||
|
import com.cloud.resource.ResourceStateAdapter;
|
||||||
|
import com.cloud.resource.ServerResource;
|
||||||
|
import com.cloud.resource.UnableDeleteHostException;
|
||||||
|
import com.cloud.utils.NumbersUtil;
|
||||||
import com.cloud.vm.VMInstanceVO;
|
import com.cloud.vm.VMInstanceVO;
|
||||||
import com.cloud.vm.dao.ConsoleProxyDao;
|
import com.cloud.vm.dao.ConsoleProxyDao;
|
||||||
|
import com.cloud.vm.dao.VMInstanceDao;
|
||||||
|
|
||||||
@Local(value={ConsoleProxyManager.class})
|
@Local(value={ConsoleProxyManager.class})
|
||||||
public class StaticConsoleProxyManager extends AgentBasedConsoleProxyManager implements ConsoleProxyManager {
|
public class StaticConsoleProxyManager extends AgentBasedConsoleProxyManager implements ConsoleProxyManager,
|
||||||
String _ip = null;
|
ResourceStateAdapter {
|
||||||
@Inject ConsoleProxyDao _proxyDao;
|
private static final Logger s_logger = Logger.getLogger(StaticConsoleProxyManager.class);
|
||||||
@Inject ResourceManager _resourceMgr;
|
|
||||||
@Inject ConfigurationDao _configDao;
|
@Inject
|
||||||
|
ConsoleProxyDao _proxyDao;
|
||||||
|
@Inject
|
||||||
|
ResourceManager _resourceMgr;
|
||||||
|
@Inject
|
||||||
|
ConfigurationDao _configDao;
|
||||||
|
@Inject
|
||||||
|
private VMInstanceDao _instanceDao;
|
||||||
|
@Inject
|
||||||
|
KeystoreDao _ksDao;
|
||||||
|
@Inject
|
||||||
|
private KeystoreManager _ksMgr;
|
||||||
|
@Inject
|
||||||
|
private HostDao _hostDao;
|
||||||
|
private final Random _random = new Random(System.currentTimeMillis());
|
||||||
|
private String _hashKey;
|
||||||
|
private String _ip = null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected HostVO findHost(VMInstanceVO vm) {
|
protected HostVO findHost(VMInstanceVO vm) {
|
||||||
|
|
@ -50,20 +80,52 @@ public class StaticConsoleProxyManager extends AgentBasedConsoleProxyManager imp
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConsoleProxyInfo assignProxy(long dataCenterId, long userVmId) {
|
public ConsoleProxyInfo assignProxy(long dataCenterId, long userVmId) {
|
||||||
return new ConsoleProxyInfo(false, _ip, _consoleProxyPort, _consoleProxyUrlPort, _consoleProxyUrlDomain);
|
return new ConsoleProxyInfo(_sslEnabled, _ip, _consoleProxyPort, _consoleProxyUrlPort, _consoleProxyUrlDomain);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
|
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
|
||||||
super.configure(name, params);
|
super.configure(name, params);
|
||||||
|
_ip = _configDao.getValue("consoleproxy.static.publicIp");
|
||||||
Map<String, String> dbParams = _configDao.getConfiguration("ManagementServer", params);
|
|
||||||
|
|
||||||
_ip = dbParams.get("public.ip");
|
|
||||||
if (_ip == null) {
|
if (_ip == null) {
|
||||||
_ip = "127.0.0.1";
|
_ip = "127.0.0.1";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
String value = (String) params.get("consoleproxy.sslEnabled");
|
||||||
|
if (value != null && value.equalsIgnoreCase("true")) {
|
||||||
|
_sslEnabled = true;
|
||||||
|
}
|
||||||
|
int defaultPort = 8088;
|
||||||
|
if (_sslEnabled)
|
||||||
|
defaultPort = 8443;
|
||||||
|
_consoleProxyUrlPort = NumbersUtil.parseInt(_configDao.getValue("consoleproxy.static.port"), defaultPort);
|
||||||
|
|
||||||
|
_resourceMgr.registerResourceStateAdapter(this.getClass().getSimpleName(), this);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HostVO createHostVOForConnectedAgent(HostVO host, StartupCommand[] cmd) {
|
||||||
|
if (!(cmd[0] instanceof StartupProxyCommand)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
host.setType(com.cloud.host.Host.Type.ConsoleProxy);
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HostVO createHostVOForDirectConnectAgent(HostVO host, StartupCommand[] startup, ServerResource resource,
|
||||||
|
Map<String, String> details, List<String> hostTags) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DeleteHostAnswer deleteHost(HostVO host, boolean isForced, boolean isForceDeleteStorage)
|
||||||
|
throws UnableDeleteHostException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue