diff --git a/agent/src/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java b/agent/src/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java index ebc86633f8c..7ae873581d8 100644 --- a/agent/src/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java +++ b/agent/src/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java @@ -113,29 +113,29 @@ public class ConsoleProxyResource extends ServerResourceBase implements ServerRe String certificate = cmd.getCertificate(); //write the cert to /etc/cloud/consoleproxy/cert/ - String strDirectoy ="/etc/cloud/consoleproxy/cert/"; - boolean dirCreated = (new File(strDirectoy)).mkdir(); - if (dirCreated) { - s_logger.info("Directory: " + strDirectoy + " created"); - - //copy cert to the dir - try { + boolean dirCreated = false; + String strDirectoy = "/etc/cloud/consoleproxy/cert/"; + dirCreated = (new File(strDirectoy)).mkdirs(); + + if (dirCreated) + { + if(s_logger.isDebugEnabled()) + s_logger.info("Directory: " + strDirectoy + " created"); + //copy cert to the dir FileWriter fstream = new FileWriter("/etc/cloud/consoleproxy/cert/customcert"); BufferedWriter out = new BufferedWriter(fstream); out.write(certificate); //Close the output stream out.close(); - }catch (Exception e){ - s_logger.warn("Unable to write file to /etc/cloud/consoleproxy/cert/ on console proxy", e); - } + success = true; } - success = true; - return new Answer(cmd, success, "Cert string in the console proxy resource status:"); + return new Answer(cmd, success, "Custom certificate update required status"); }catch (Exception e) { - s_logger.error("Unable to read the cert string in console proxy resource"); + s_logger.error("Unable to read the cert string in console proxy resource",e); + success = false; } - return new Answer(cmd, success, "Cert string in the console proxy resource status:"); + return new Answer(cmd, success, "Custom certificate response from the updatecertificate flow"); } protected Answer execute(final CheckConsoleProxyLoadCommand cmd) { diff --git a/build/package.xml b/build/package.xml index 0cac5782a98..b5e571a6574 100755 --- a/build/package.xml +++ b/build/package.xml @@ -207,6 +207,13 @@ + + + + + + + diff --git a/core/src/com/cloud/host/dao/HostDao.java b/core/src/com/cloud/host/dao/HostDao.java index 78eeea89bfb..b68d7755245 100644 --- a/core/src/com/cloud/host/dao/HostDao.java +++ b/core/src/com/cloud/host/dao/HostDao.java @@ -135,7 +135,9 @@ public interface HostDao extends GenericDao { long getNextSequence(long hostId); - void loadDetails(HostVO host); + void loadDetails(HostVO host); + + HostVO findConsoleProxyHost(String name, Type type); } diff --git a/core/src/com/cloud/host/dao/HostDaoImpl.java b/core/src/com/cloud/host/dao/HostDaoImpl.java index 47d3134e0b6..3acc97db6a5 100644 --- a/core/src/com/cloud/host/dao/HostDaoImpl.java +++ b/core/src/com/cloud/host/dao/HostDaoImpl.java @@ -79,6 +79,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao protected final SearchBuilder UnmanagedDirectConnectSearch; protected final SearchBuilder MaintenanceCountSearch; protected final SearchBuilder ClusterSearch; + protected final SearchBuilder ConsoleProxyHostSearch; protected final Attribute _statusAttr; protected final Attribute _msIdAttr; @@ -154,6 +155,11 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao ClusterSearch = createSearchBuilder(); ClusterSearch.and("cluster", ClusterSearch.entity().getClusterId(), SearchCriteria.Op.EQ); ClusterSearch.done(); + + ConsoleProxyHostSearch = createSearchBuilder(); + ConsoleProxyHostSearch.and("name", ConsoleProxyHostSearch.entity().getName(), SearchCriteria.Op.EQ); + ConsoleProxyHostSearch.and("type", ConsoleProxyHostSearch.entity().getType(), SearchCriteria.Op.EQ); + ConsoleProxyHostSearch.done(); PodSearch = createSearchBuilder(); PodSearch.and("pod", PodSearch.entity().getPodId(), SearchCriteria.Op.EQ); @@ -442,7 +448,20 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao SearchCriteria sc = DcSearch.create("dc", dcId); return listBy(sc); } - + + @Override + public HostVO findConsoleProxyHost(String name, Type type) { + SearchCriteria sc = ConsoleProxyHostSearch.create(); + sc.setParameters("name", name); + sc.setParameters("type", type); + ListhostList = listBy(sc); + + if(hostList==null || hostList.size() == 0) + return null; + else + return hostList.get(0); + } + public List listByHostPod(long podId) { SearchCriteria sc = PodSearch.create("pod", podId); return listBy(sc); diff --git a/server/src/com/cloud/api/ApiDispatcher.java b/server/src/com/cloud/api/ApiDispatcher.java index 12e45a8df8a..822a7cdee17 100644 --- a/server/src/com/cloud/api/ApiDispatcher.java +++ b/server/src/com/cloud/api/ApiDispatcher.java @@ -36,6 +36,7 @@ import com.cloud.configuration.ConfigurationManager; import com.cloud.consoleproxy.ConsoleProxyManager; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.PermissionDeniedException; +import com.cloud.exception.ResourceAllocationException; import com.cloud.network.DomainRouterService; import com.cloud.network.NetworkManager; import com.cloud.network.security.NetworkGroupManager; @@ -154,6 +155,8 @@ public class ApiDispatcher { throw new ServerApiException(BaseCmd.PARAM_ERROR, cause.getMessage()); } else if (cause instanceof PermissionDeniedException) { throw new ServerApiException(BaseCmd.ACCOUNT_ERROR, cause.getMessage()); + } else if (cause instanceof ResourceAllocationException){ + throw new ServerApiException(BaseCmd.UNSUPPORTED_ACTION_ERROR, cause.getMessage()); } s_logger.warn("Exception executing method " + methodName + " for command " + cmd.getClass().getSimpleName(), ite); throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Unable to execute method " + methodName + " for command " + cmd.getClass().getSimpleName() + ", internal error in the implementation."); diff --git a/server/src/com/cloud/api/commands/UploadCustomCertificateCmd.java b/server/src/com/cloud/api/commands/UploadCustomCertificateCmd.java index 887f4676ded..53f3066f3e9 100644 --- a/server/src/com/cloud/api/commands/UploadCustomCertificateCmd.java +++ b/server/src/com/cloud/api/commands/UploadCustomCertificateCmd.java @@ -19,13 +19,15 @@ package com.cloud.api.commands; import org.apache.log4j.Logger; -import com.cloud.api.BaseCmd; +import com.cloud.api.BaseAsyncCmd; import com.cloud.api.Implementation; import com.cloud.api.Parameter; import com.cloud.api.response.StatusResponse; +import com.cloud.event.EventTypes; +import com.cloud.user.Account; @Implementation(method="uploadCertificate") -public class UploadCustomCertificateCmd extends BaseCmd { +public class UploadCustomCertificateCmd extends BaseAsyncCmd { public static final Logger s_logger = Logger.getLogger(UploadCustomCertificateCmd.class.getName()); private static final String s_name = "uploadcustomcertificateresponse"; @@ -37,11 +39,7 @@ public class UploadCustomCertificateCmd extends BaseCmd { return path; } - @Override - public String getName() { - return s_name; - } - + @Override @SuppressWarnings("unchecked") public StatusResponse getResponse() { Boolean status = (Boolean)getResponseObject(); @@ -51,4 +49,29 @@ public class UploadCustomCertificateCmd extends BaseCmd { response.setResponseName(getName()); return response; } + + @Override + public String getEventType() { + return EventTypes.EVENT_VOLUME_CREATE; + } + + @Override + public String getEventDescription() { + return ("Uploading custom certificate to the db, and applying it to the cpvm"); + } + + @Override + public String getName() { + return s_name; + } + + public static String getResultObjectName() { + return "volume"; + } + + @Override + public long getAccountId() { + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked + } + } diff --git a/server/src/com/cloud/api/doc/ApiXmlDocReader.java b/server/src/com/cloud/api/doc/ApiXmlDocReader.java index b4f3f1f4680..b5b19937073 100644 --- a/server/src/com/cloud/api/doc/ApiXmlDocReader.java +++ b/server/src/com/cloud/api/doc/ApiXmlDocReader.java @@ -236,14 +236,11 @@ public class ApiXmlDocReader { } } - - out.close(); } catch (IOException e) { e.printStackTrace(); } - } } diff --git a/server/src/com/cloud/api/doc/ApiXmlDocWriter.java b/server/src/com/cloud/api/doc/ApiXmlDocWriter.java index aed4f87669c..922819f0409 100644 --- a/server/src/com/cloud/api/doc/ApiXmlDocWriter.java +++ b/server/src/com/cloud/api/doc/ApiXmlDocWriter.java @@ -129,7 +129,6 @@ public class ApiXmlDocWriter { // //Get fields from superclass Class superClass = clas.getSuperclass(); String superName = superClass.getName(); -// while (BaseCmd.class.isAssignableFrom(superClass) && !superClass.getName().equals(BaseCmd.class.getName())) { if (!superName.equals(BaseCmd.class.getName()) && !superName.equals(BaseAsyncCmd.class.getName()) && !superName.equals(BaseAsyncCreateCmd.class.getName())) { Field[] superClassFields = superClass.getDeclaredFields(); if (superClassFields != null) { @@ -140,8 +139,6 @@ public class ApiXmlDocWriter { } superClass = superClass.getSuperclass(); } - -// } for (Field f : fields) { Parameter parameterAnnotation = f.getAnnotation(Parameter.class); diff --git a/server/src/com/cloud/consoleproxy/AgentBasedConsoleProxyManager.java b/server/src/com/cloud/consoleproxy/AgentBasedConsoleProxyManager.java index 875d535941e..623c0afd9e7 100644 --- a/server/src/com/cloud/consoleproxy/AgentBasedConsoleProxyManager.java +++ b/server/src/com/cloud/consoleproxy/AgentBasedConsoleProxyManager.java @@ -34,6 +34,7 @@ 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.StopCommand; import com.cloud.api.ServerApiException; import com.cloud.api.commands.DestroyConsoleProxyCmd; @@ -54,9 +55,10 @@ import com.cloud.utils.component.Inject; import com.cloud.vm.ConsoleProxyVO; import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; -import com.cloud.vm.VirtualMachine.Type; import com.cloud.vm.VirtualMachineManager; import com.cloud.vm.VirtualMachineName; +import com.cloud.vm.VirtualMachine.Type; +import com.cloud.vm.dao.ConsoleProxyDao; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDao; @@ -74,13 +76,13 @@ public class AgentBasedConsoleProxyManager implements ConsoleProxyManager, Virtu @Inject private VMInstanceDao _instanceDao; private ConsoleProxyListener _listener; - protected int _consoleProxyUrlPort = ConsoleProxyManager.DEFAULT_PROXY_URL_PORT; protected int _consoleProxyPort = ConsoleProxyManager.DEFAULT_PROXY_VNC_PORT; protected boolean _sslEnabled = false; @Inject AgentManager _agentMgr; - + @Inject + protected ConsoleProxyDao _cpDao; public int getVncPort(VMInstanceVO vm) { if (vm.getHostId() == null) { return -1; @@ -324,5 +326,11 @@ public class AgentBasedConsoleProxyManager implements ConsoleProxyManager, Virtu @Override public boolean destroyConsoleProxy(DestroyConsoleProxyCmd cmd) throws ServerApiException { return false; - } + } + + @Override + public boolean applyCustomCertToNewProxy(StartupProxyCommand cmd) { + // TODO Auto-generated method stub + return false; + } } diff --git a/server/src/com/cloud/consoleproxy/AgentHook.java b/server/src/com/cloud/consoleproxy/AgentHook.java index a25e555e92f..4874718e218 100644 --- a/server/src/com/cloud/consoleproxy/AgentHook.java +++ b/server/src/com/cloud/consoleproxy/AgentHook.java @@ -7,6 +7,7 @@ import com.cloud.agent.api.AgentControlAnswer; import com.cloud.agent.api.ConsoleAccessAuthenticationCommand; import com.cloud.agent.api.ConsoleProxyLoadReportCommand; import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupProxyCommand; import com.cloud.host.HostVO; import com.cloud.host.Status; @@ -16,4 +17,5 @@ public interface AgentHook { void onAgentConnect(HostVO host, StartupCommand cmd); public void onAgentDisconnect(long agentId, Status state); + boolean applyCustomCertToNewProxy(StartupProxyCommand cmd); } diff --git a/server/src/com/cloud/consoleproxy/ConsoleProxyListener.java b/server/src/com/cloud/consoleproxy/ConsoleProxyListener.java index ef653085f23..61bdc14147b 100644 --- a/server/src/com/cloud/consoleproxy/ConsoleProxyListener.java +++ b/server/src/com/cloud/consoleproxy/ConsoleProxyListener.java @@ -25,6 +25,7 @@ import com.cloud.agent.api.Command; import com.cloud.agent.api.ConsoleAccessAuthenticationCommand; import com.cloud.agent.api.ConsoleProxyLoadReportCommand; import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupProxyCommand; import com.cloud.host.HostVO; import com.cloud.host.Status; @@ -66,6 +67,10 @@ public class ConsoleProxyListener implements Listener { @Override public void processConnect(HostVO host, StartupCommand cmd) { _proxyMgr.onAgentConnect(host, cmd); + + if (cmd instanceof StartupProxyCommand) { + _proxyMgr.applyCustomCertToNewProxy((StartupProxyCommand)cmd); + } } @Override diff --git a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java index 875d7fe483f..08d2515e758 100644 --- a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java +++ b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java @@ -56,9 +56,11 @@ import com.cloud.agent.api.Start2Command; import com.cloud.agent.api.StartConsoleProxyAnswer; import com.cloud.agent.api.StartConsoleProxyCommand; import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupProxyCommand; import com.cloud.agent.api.StopAnswer; import com.cloud.agent.api.StopCommand; import com.cloud.agent.api.proxy.ConsoleProxyLoadAnswer; +import com.cloud.agent.api.proxy.UpdateCertificateCommand; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.agent.api.to.VirtualMachineTO.SshMonitor; @@ -70,13 +72,15 @@ import com.cloud.async.AsyncJobExecutor; import com.cloud.async.AsyncJobManager; import com.cloud.async.AsyncJobVO; import com.cloud.async.BaseAsyncJobExecutor; +import com.cloud.certificate.CertificateVO; +import com.cloud.certificate.dao.CertificateDao; import com.cloud.cluster.ClusterManager; import com.cloud.configuration.Config; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.dc.DataCenterVO; import com.cloud.dc.HostPodVO; -import com.cloud.dc.Vlan.VlanType; import com.cloud.dc.VlanVO; +import com.cloud.dc.Vlan.VlanType; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.HostPodDao; import com.cloud.dc.dao.VlanDao; @@ -97,8 +101,8 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.exception.StorageUnavailableException; import com.cloud.ha.HighAvailabilityManager; import com.cloud.host.Host; -import com.cloud.host.Host.Type; import com.cloud.host.HostVO; +import com.cloud.host.Host.Type; import com.cloud.host.dao.HostDao; import com.cloud.info.ConsoleProxyConnectionInfo; import com.cloud.info.ConsoleProxyInfo; @@ -109,10 +113,10 @@ import com.cloud.info.RunningHostInfoAgregator; import com.cloud.info.RunningHostInfoAgregator.ZoneHostInfo; import com.cloud.maid.StackMaid; import com.cloud.network.IpAddrAllocator; -import com.cloud.network.IpAddrAllocator.networkInfo; -import com.cloud.network.Network.TrafficType; import com.cloud.network.NetworkConfigurationVO; import com.cloud.network.NetworkManager; +import com.cloud.network.IpAddrAllocator.networkInfo; +import com.cloud.network.Network.TrafficType; import com.cloud.network.dao.IPAddressDao; import com.cloud.offering.NetworkOffering; import com.cloud.offerings.NetworkOfferingVO; @@ -124,9 +128,9 @@ import com.cloud.storage.GuestOSVO; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePoolVO; import com.cloud.storage.VMTemplateHostVO; -import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.VolumeVO; +import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; import com.cloud.storage.dao.GuestOSDao; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VMTemplateHostDao; @@ -155,12 +159,12 @@ import com.cloud.vm.NicProfile; import com.cloud.vm.State; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; -import com.cloud.vm.VirtualMachine.Event; import com.cloud.vm.VirtualMachineGuru; import com.cloud.vm.VirtualMachineManager; import com.cloud.vm.VirtualMachineName; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.VmManager; +import com.cloud.vm.VirtualMachine.Event; import com.cloud.vm.dao.ConsoleProxyDao; import com.cloud.vm.dao.VMInstanceDao; import com.google.gson.Gson; @@ -229,12 +233,12 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, VirtualMach private HostDao _hostDao; @Inject private ConfigurationDao _configDao; - + @Inject + private CertificateDao _certDao; @Inject private VMInstanceDao _instanceDao; @Inject private AccountDao _accountDao; - @Inject private VMTemplateHostDao _vmTemplateHostDao; @Inject private AgentManager _agentMgr; @Inject private StorageManager _storageMgr; @@ -2432,4 +2436,57 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, VirtualMach public boolean processDeploymentResult(Commands cmds, ConsoleProxyVO proxy, VirtualMachineProfile profile, DeployDestination dest) { return true; } + + @Override + public boolean applyCustomCertToNewProxy(StartupProxyCommand cmd){ + //this is the case for updating cust cert on each new starting proxy, if such cert exists + //get cert from db + List certList = _certDao.listAll(); + + if(certList.size()>0){ + CertificateVO cert = certList.get(0);//there will only be 1 cert in db for now + String certStr = cert.getCertificate(); + long proxyVmId = ((StartupProxyCommand)cmd).getProxyVmId(); + ConsoleProxyVO consoleProxy = _consoleProxyDao.findById(proxyVmId); + //find corresponding host + if(consoleProxy!=null){ + HostVO consoleProxyHost = _hostDao.findConsoleProxyHost(consoleProxy.getName(), Type.ConsoleProxy); + //now send a command to console proxy + UpdateCertificateCommand certCmd = new UpdateCertificateCommand(certStr); + try { + Answer updateCertAns = _agentMgr.send(consoleProxyHost.getId(), certCmd); + if(updateCertAns.getResult() == true) + { + //we have the cert copied over on cpvm + long eventId = saveScheduledEvent(User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM, EventTypes.EVENT_PROXY_REBOOT, "rebooting console proxy with Id: "+consoleProxy.getId()); + rebootProxy(consoleProxy.getId(), eventId); + //when cp reboots, the context will be reinit with the new cert + s_logger.info("Successfully rebooted console proxy resource after custom certificate application"); + return true; + } + } catch (AgentUnavailableException e) { + s_logger.warn("Unable to send update certificate command to the console proxy resource", e); + return false; + } catch (OperationTimedoutException e) { + s_logger.warn("Unable to send update certificate command to the console proxy resource", e); + return false; + } + } + }else{ + return false;//no cert + } + return false; + } + + private Long saveScheduledEvent(Long userId, Long accountId, String type, String description) + { + EventVO event = new EventVO(); + event.setUserId(userId); + event.setAccountId(accountId); + event.setType(type); + event.setState(EventState.Scheduled); + event.setDescription("Scheduled async job for "+description); + event = _eventDao.persist(event); + return event.getId(); + } } diff --git a/server/src/com/cloud/server/ManagementServer.java b/server/src/com/cloud/server/ManagementServer.java index e620d53ef25..8ac62e001f1 100755 --- a/server/src/com/cloud/server/ManagementServer.java +++ b/server/src/com/cloud/server/ManagementServer.java @@ -1219,5 +1219,5 @@ public interface ManagementServer { */ String[] getHypervisors(ListHypervisorsCmd cmd); - boolean uploadCertificate(UploadCustomCertificateCmd cmd); + boolean uploadCertificate(UploadCustomCertificateCmd cmd) throws ResourceAllocationException; } diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index d1df3adbf23..dbd6b488b0b 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -26,6 +26,7 @@ import java.net.URLEncoder; import java.net.UnknownHostException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.security.cert.Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; @@ -148,6 +149,7 @@ import com.cloud.async.dao.AsyncJobDao; import com.cloud.async.executor.ExtractJobResultObject; import com.cloud.capacity.CapacityVO; import com.cloud.capacity.dao.CapacityDao; +import com.cloud.certificate.CertificateVO; import com.cloud.certificate.dao.CertificateDao; import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationManager; @@ -174,6 +176,7 @@ import com.cloud.dc.dao.PodVlanMapDao; import com.cloud.dc.dao.VlanDao; import com.cloud.domain.DomainVO; import com.cloud.domain.dao.DomainDao; +import com.cloud.event.EventState; import com.cloud.event.EventTypes; import com.cloud.event.EventUtils; import com.cloud.event.EventVO; @@ -6931,41 +6934,64 @@ public class ManagementServerImpl implements ManagementServer { } return version; } + + private Long saveScheduledEvent(Long userId, Long accountId, String type, String description) + { + EventVO event = new EventVO(); + event.setUserId(userId); + event.setAccountId(accountId); + event.setType(type); + event.setState(EventState.Scheduled); + event.setDescription("Scheduled async job for "+description); + event = _eventDao.persist(event); + return event.getId(); + } @Override - public boolean uploadCertificate(UploadCustomCertificateCmd cmd) { + public boolean uploadCertificate(UploadCustomCertificateCmd cmd) throws ResourceAllocationException { + //limit no.of certs uploaded to 1 + if(_certDao.listAll().size()>0){ + throw new ResourceAllocationException("There is already a custom certificate in the db"); + } + String certificatePath = cmd.getPath(); Long certVOId = _certDao.persistCustomCertToDb(certificatePath);//0 implies failure - if (certVOId!=null && certVOId!=0) { - //certficate uploaded to db successfully + if (certVOId!=null && certVOId!=0) + { + //certficate uploaded to db successfully + //get a list of all Console proxies from the cp table + List cpList = _consoleProxyDao.listAll(); - //get a list of all hosts from host table - List hosts = _hostDao.listAll(); - - List consoleProxyList = new ArrayList(); - - //find the console proxies, and send the command to them - for(HostVO host : hosts) { - if(host.getType().equals(com.cloud.host.Host.Type.ConsoleProxy)){ - consoleProxyList.add(host); - } - } - - for(HostVO consoleProxy : consoleProxyList){ + for(ConsoleProxyVO cp : cpList) + { + HostVO cpHost = _hostDao.findConsoleProxyHost(cp.getName(), com.cloud.host.Host.Type.ConsoleProxy); + //now send a command to each console proxy UpdateCertificateCommand certCmd = new UpdateCertificateCommand(_certDao.findById(certVOId).getCertificate()); try { - Answer updateCertAns = _agentMgr.send(consoleProxy.getId(), certCmd); + Answer updateCertAns = _agentMgr.send(cpHost.getId(), certCmd); + if(updateCertAns.getResult() == true) + { + //we have the cert copied over on cpvm + long eventId = saveScheduledEvent(User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM, EventTypes.EVENT_PROXY_REBOOT, "rebooting console proxy with Id: "+cp.getId()); + _consoleProxyMgr.rebootProxy(cp.getId(), eventId); + //when cp reboots, the context will be reinit with the new cert + } } catch (AgentUnavailableException e) { - s_logger.warn("Unable to send command to the console proxy resource", e); + s_logger.warn("Unable to send update certificate command to the console proxy resource", e); } catch (OperationTimedoutException e) { - s_logger.warn("Unable to send command to the console proxy resource", e); + s_logger.warn("Unable to send update certificate command to the console proxy resource", e); } + } + + return true; + } + else + { + return false; } - - return false; } @Override diff --git a/server/src/com/cloud/storage/StorageManagerImpl.java b/server/src/com/cloud/storage/StorageManagerImpl.java index 842e0330602..047ca9fb469 100755 --- a/server/src/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/com/cloud/storage/StorageManagerImpl.java @@ -2487,9 +2487,12 @@ public class StorageManagerImpl implements StorageManager { // Check that the volume is stored on shared storage - if (!volumeOnSharedStoragePool(volume)) { - throw new InvalidParameterValueException("Please specify a volume that has been created on a shared storage pool."); - } + // NOTE: We used to ensure the volume is on shared storage before deleting. However, this seems like an unnecessary check since all we allow + // is deleting a detached volume. Is there a technical reason why the volume has to be on shared storage? If so, uncomment this...otherwise, + // just delete the detached volume regardless of storage pool. +// if (!volumeOnSharedStoragePool(volume)) { +// throw new InvalidParameterValueException("Please specify a volume that has been created on a shared storage pool."); +// } // Check that the volume is not currently attached to any VM if (volume.getInstanceId() != null) { diff --git a/ui/new/css/main.css b/ui/new/css/main.css index b1b2738e638..3563d418fbe 100644 --- a/ui/new/css/main.css +++ b/ui/new/css/main.css @@ -2006,6 +2006,7 @@ a:hover.search_button { height:auto; float:left; text-align:left; + background:#FFF url(../images/midmenu_hover.gif) repeat-x top left; color:#CCC; font-size:11px; font-weight:normal; @@ -2251,6 +2252,25 @@ a:hover.search_button { color:#999; } +.midmenu_emptymsgbox { + width:220px; + height:auto; + float:left; + background:#d4d4d4 url(../images/midmenu_emptymsg.gif) repeat-x top left; + margin:0 0 0 0; + padding:0; +} + +.midmenu_emptymsgbox p{ + width:200px; + height:auto; + float:left; + color:#666; + font-size:11px; + font-weight:normal; + margin:0 0 0 0; + padding:0; +} .main_contentarea_with_midmenu { width:auto; @@ -2448,6 +2468,30 @@ a:hover.search_button { color:#333; font-size:11px; } +.grid_row_cell .error_text { + width:92%; + height:16px; + float:left; + margin:0 0 0 10px; + display:inline; + padding:0 0 0 2px; + border:1px solid #999; + background:#ffe5e5; + color:#333; + font-size:11px; +} + +.errormsg { + width:80%; + height:auto; + float:left; + margin:3px 0 0 10px; + display:inline; + padding:0 0 0 2px; + color:#a90000; + font-size:11px; + font-weight:normal; +} .grid_row_cell .select { width:92%; @@ -3043,7 +3087,7 @@ a:hover.search_button { height:auto; float:left; position:absolute; - background:#FFF url(../images/midmenu_hover.gif) repeat top left; + background:#FFF repeat top left; border:1px solid #CCC; top:18px; right:1px; diff --git a/ui/new/images/midmenu_emptymsg.gif b/ui/new/images/midmenu_emptymsg.gif new file mode 100644 index 00000000000..d668944b8c8 Binary files /dev/null and b/ui/new/images/midmenu_emptymsg.gif differ diff --git a/ui/new/index.jsp b/ui/new/index.jsp index 38bb2f0e4f5..6ff6900c345 100644 --- a/ui/new/index.jsp +++ b/ui/new/index.jsp @@ -316,7 +316,7 @@ long milliseconds = new Date().getTime();
- +

No Items Available

diff --git a/ui/new/jsp/ipaddress.jsp b/ui/new/jsp/ipaddress.jsp index 1526309e42f..875a1eb2f8d 100644 --- a/ui/new/jsp/ipaddress.jsp +++ b/ui/new/jsp/ipaddress.jsp @@ -166,15 +166,15 @@
- +
- +
- @@ -220,19 +220,19 @@
- +
- +
- +
- diff --git a/ui/new/scripts/cloud.core2.resource.js b/ui/new/scripts/cloud.core2.resource.js index 4c34dae8391..5b8c7f6467e 100644 --- a/ui/new/scripts/cloud.core2.resource.js +++ b/ui/new/scripts/cloud.core2.resource.js @@ -191,8 +191,7 @@ function clusterJSONToTreeNode(json, $clusterNode) { } //$menuItem1 is either $leftmenuItem1 or $midmenuItem1 -function showPage($pageToShow, $menuItem1) { - clearMiddleMenu(); +function showPage($pageToShow, $menuItem1) { if($pageToShow.length == 0) { //resource.jsp is not loaded in right panel $("#right_panel").load("jsp/resource.jsp", function(){ showPage2($($pageToShow.selector), $menuItem1); //$pageToShow is still empty (i.e. $pageToShow.length == 0), So, select the element again. @@ -223,11 +222,16 @@ function showPage2($pageToShow, $menuItem1) { $pageToShow.data("jsonObj", jsonObj); } - if($pageToShow.attr("id") == "resource_page") { + if($pageToShow.attr("id") == "resource_page") { + clearMiddleMenu(); + hideMiddleMenu(); + initAddZoneButton($("#midmenu_add_link")); initDialog("dialog_add_zone"); } - else if($pageToShow.attr("id") == "zone_page") { + else if($pageToShow.attr("id") == "zone_page") { + hideMiddleMenu(); + initAddPodButton($("#midmenu_add_link")); initAddVLANButton($("#midmenu_add2_link")); initAddSecondaryStorageButton($("#midmenu_add3_link")); @@ -249,11 +253,12 @@ function showPage2($pageToShow, $menuItem1) { //var afterSwitchFnArray = [afterSwitchToDetailsTab, afterSwitchToNetworkTab, afterSwitchToSecondaryStorageTab]; switchBetweenDifferentTabs(tabArray, tabContentArray); $zonePage.find("#tab_details").click(); - - hideMiddleMenu(); + zoneJsonToRightPanel($menuItem1); } - else if($pageToShow.attr("id") == "pod_page") { + else if($pageToShow.attr("id") == "pod_page") { + hideMiddleMenu(); + initAddHostButton($("#midmenu_add_link")); initAddPrimaryStorageButton($("#midmenu_add2_link")); @@ -264,21 +269,22 @@ function showPage2($pageToShow, $menuItem1) { if (getHypervisorType() == 'kvm') $("#dialog_add_pool").find("#add_pool_protocol").empty().html(''); bindEventHandlerToDialogAddPool(); - - showMiddleMenu(); + podJsonToRightPanel($menuItem1); - var podId = jsonObj.id; - $("#midmenu_container").empty(); - listMidMenuItems2(("listHosts&type=Routing&podid="+podId), "listhostsresponse", "host", hostToMidmenu, hostToRightPanel, hostGetMidmenuId, false, false); - listMidMenuItems2(("listStoragePools&podid="+podId), "liststoragepoolsresponse", "storagepool", primarystorageToMidmenu, primarystorageToRightPanel, primarystorageGetMidmenuId, false, false); + //var podId = jsonObj.id; + //$("#midmenu_container").empty(); + //listMidMenuItems2(("listHosts&type=Routing&podid="+podId), "listhostsresponse", "host", hostToMidmenu, hostToRightPanel, hostGetMidmenuId, false, false); + //listMidMenuItems2(("listStoragePools&podid="+podId), "liststoragepoolsresponse", "storagepool", primarystorageToMidmenu, primarystorageToRightPanel, primarystorageGetMidmenuId, false, false); } else if($pageToShow.attr("id") == "cluster_page") { + clearMiddleMenu(); + showMiddleMenu(); + $("#midmenu_add_link").unbind("click").hide(); $("#midmenu_add2_link").unbind("click").hide(); - $("#midmenu_add3_link").unbind("click").hide(); - - showMiddleMenu(); + $("#midmenu_add3_link").unbind("click").hide(); + clusterJsonToRightPanel($menuItem1); var clusterId = jsonObj.id; @@ -1274,9 +1280,10 @@ function initAddHostButton($midmenuAddLink1) { var password = trim($thisDialog.find("#host_password").val()); array1.push("&password="+encodeURIComponent(password)); - + + var newClusterName; if(clusterRadio == "new_cluster_radio") { - var newClusterName = trim($thisDialog.find("#new_cluster_name").val()); + newClusterName = trim($thisDialog.find("#new_cluster_name").val()); array1.push("&clustername="+todb(newClusterName)); } else if(clusterRadio == "existing_cluster_radio") { @@ -1304,6 +1311,7 @@ function initAddHostButton($midmenuAddLink1) { $thisDialog.find("#spinning_wheel").hide(); $thisDialog.dialog("close"); + showMiddleMenu(); var $midmenuItem1 = $("#midmenu_item").clone(); $("#midmenu_container").append($midmenuItem1.fadeIn("slow")); var items = json.addhostresponse.host; @@ -1319,13 +1327,16 @@ function initAddHostButton($midmenuAddLink1) { } } - if(clusterRadio == "new_cluster_radio") - $thisDialog.find("#new_cluster_name").val(""); - - refreshClusterUnderPod($("#pod_" + podObj.id)); + if(clusterRadio == "new_cluster_radio") { + refreshClusterUnderPod($("#pod_" + podObj.id), newClusterName); + $thisDialog.find("#new_cluster_name").val(""); + } }, error: function(XMLHttpResponse) { - refreshClusterUnderPod($("#pod_" + podObj.id)); + if(clusterRadio == "new_cluster_radio") { + refreshClusterUnderPod($("#pod_" + podObj.id), newClusterName); + $thisDialog.find("#new_cluster_name").val(""); //even AddHost fails, new cluster is still created. So, we clean up new cluster field to avoid the same one gets created twice. + } handleErrorInDialog(XMLHttpResponse, $thisDialog); } }); @@ -1338,7 +1349,7 @@ function initAddHostButton($midmenuAddLink1) { }); } -function refreshClusterUnderPod($podNode) { +function refreshClusterUnderPod($podNode, newClusterName) { var podId = $podNode.data("podId"); $.ajax({ data: createURL("command=listClusters&podid="+podId+maxPageSize), @@ -1349,9 +1360,14 @@ function refreshClusterUnderPod($podNode) { var container = $podNode.find("#clusters_container").empty(); if (items != null && items.length > 0) { for (var i = 0; i < items.length; i++) { - var clusterTemplate = $("#leftmenu_cluster_node_template").clone(true); - clusterJSONToTreeNode(items[i], clusterTemplate); - container.append(clusterTemplate.show()); + var $clusterNode = $("#leftmenu_cluster_node_template").clone(true); + var item = items[i]; + clusterJSONToTreeNode(item, $clusterNode); + container.append($clusterNode.show()); + + if(newClusterName != null && fromdb(item.name) == newClusterName) { + $clusterNode.find("#cluster_name").click(); + } } $podNode.find("#pod_arrow").removeClass("white_nonexpanded_close").addClass("expanded_open"); $podNode.find("#pod_content").show(); diff --git a/ui/scripts/cloud.core.storage.js b/ui/scripts/cloud.core.storage.js index 580b03046d8..10ca6570729 100644 --- a/ui/scripts/cloud.core.storage.js +++ b/ui/scripts/cloud.core.storage.js @@ -959,7 +959,7 @@ function showStorageTab(domainId, targetTab) { switch (linkAction) { case "volume_action_delete" : //check if this volume is attached to a virtual machine. If yes, can't be deleted. - if(vmname != null && (vmname != "" || vmname != "none")) { + if(vmname != null && vmname != "" && vmname != "none") { $("#dialog_alert").html("

This volume is attached to virtual machine " + vmname + " and can't be deleted.

") $("#dialog_alert").dialog("open"); return;