diff --git a/agent/.classpath b/agent/.classpath index 1a244747b88..79017ec22a9 100644 --- a/agent/.classpath +++ b/agent/.classpath @@ -7,6 +7,7 @@ + diff --git a/agent/src/com/cloud/agent/AgentShell.java b/agent/src/com/cloud/agent/AgentShell.java index 49c9dc5f2cd..9089a70ba84 100644 --- a/agent/src/com/cloud/agent/AgentShell.java +++ b/agent/src/com/cloud/agent/AgentShell.java @@ -533,10 +533,14 @@ public class AgentShell implements IAgentShell { instance = ""; } else { instance += "."; - } + } + + String pidDir = getProperty(null, "piddir"); + + final String run = "agent." + instance + "pid"; s_logger.debug("Checking to see if " + run + "exists."); - ProcessUtil.pidCheck(run); + ProcessUtil.pidCheck(pidDir, run); launchAgent(); diff --git a/agent/src/com/cloud/agent/configuration/AgentComponentLibraryBase.java b/agent/src/com/cloud/agent/configuration/AgentComponentLibraryBase.java new file mode 100644 index 00000000000..c7df72d4208 --- /dev/null +++ b/agent/src/com/cloud/agent/configuration/AgentComponentLibraryBase.java @@ -0,0 +1,73 @@ +/** + * Copyright (C) 2011 Cloud.com. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later +version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.agent.configuration; + +import java.util.List; +import java.util.Map; + +import com.cloud.utils.component.Adapter; +import com.cloud.utils.component.ComponentLibraryBase; +import com.cloud.utils.component.ComponentLocator.ComponentInfo; +import com.cloud.utils.component.Manager; +import com.cloud.utils.component.SystemIntegrityChecker; +import com.cloud.utils.db.GenericDao; + + +public class AgentComponentLibraryBase extends ComponentLibraryBase { + + @Override + public List getSystemIntegrityCheckers() { + return null; + } + + @Override + public Map>> getDaos() { + return null; + } + + @Override + public Map> getManagers() { + if (_managers.size() == 0) { + populateManagers(); + } + return _managers; + } + + @Override + public Map>> getAdapters() { + if (_adapters.size() == 0) { + populateAdapters(); + } + return _adapters; + } + + @Override + public Map, Class> getFactories() { + return null; + } + + protected void populateManagers() { + //addManager("StackMaidManager", StackMaidManagerImpl.class); + } + + protected void populateAdapters() { + + } + +} diff --git a/agent/src/com/cloud/agent/dhcp/DhcpSnooper.java b/agent/src/com/cloud/agent/dhcp/DhcpSnooper.java new file mode 100644 index 00000000000..03ed267163f --- /dev/null +++ b/agent/src/com/cloud/agent/dhcp/DhcpSnooper.java @@ -0,0 +1,41 @@ +/** + * Copyright (C) 2011 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.agent.dhcp; + +import java.net.InetAddress; +import java.util.List; +import java.util.Map; + +import com.cloud.utils.Pair; +import com.cloud.utils.component.Adapter; + +public interface DhcpSnooper extends Adapter{ + + public InetAddress getIPAddr(String macAddr, String vmName); + + public InetAddress getDhcpServerIP(); + + public void cleanup(String macAddr, String vmName); + + public Map syncIpAddr(); + + public boolean stop(); + + public void initializeMacTable(List> macVmNameList); + +} \ No newline at end of file diff --git a/agent/src/com/cloud/agent/dhcp/FakeDhcpSnooper.java b/agent/src/com/cloud/agent/dhcp/FakeDhcpSnooper.java new file mode 100644 index 00000000000..e6e2266ef12 --- /dev/null +++ b/agent/src/com/cloud/agent/dhcp/FakeDhcpSnooper.java @@ -0,0 +1,124 @@ +package com.cloud.agent.dhcp; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.utils.Pair; +import com.cloud.utils.net.NetUtils; + +@Local(value = {DhcpSnooper.class}) +public class FakeDhcpSnooper implements DhcpSnooper { + private static final Logger s_logger = Logger.getLogger(FakeDhcpSnooper.class); + private Queue _ipAddresses = new ConcurrentLinkedQueue(); + private Map _macIpMap = new ConcurrentHashMap(); + private Map _vmIpMap = new ConcurrentHashMap(); + + @Override + public boolean configure(String name, Map params) + throws ConfigurationException { + String guestIpRange = (String)params.get("guest.ip.range"); + if (guestIpRange != null) { + String [] guestIps = guestIpRange.split("-"); + if (guestIps.length == 2) { + long start = NetUtils.ip2Long(guestIps[0]); + long end = NetUtils.ip2Long(guestIps[1]); + while (start <= end) { + _ipAddresses.offer(NetUtils.long2Ip(start++)); + } + } + } + return true; + } + + @Override + public boolean start() { + return true; + } + + @Override + public String getName() { + return "FakeDhcpSnooper"; + } + + @Override + public InetAddress getIPAddr(String macAddr, String vmName) { + String ipAddr = _ipAddresses.poll(); + if (ipAddr == null) { + s_logger.warn("No ip addresses left in queue"); + return null; + } + try { + InetAddress inetAddr = InetAddress.getByName(ipAddr); + _macIpMap.put(macAddr.toLowerCase(), ipAddr); + _vmIpMap.put(vmName, inetAddr); + s_logger.info("Got ip address " + ipAddr + " for vm " + vmName + " mac=" + macAddr.toLowerCase()); + return inetAddr; + } catch (UnknownHostException e) { + s_logger.warn("Failed to get InetAddress for " + ipAddr); + return null; + } + } + + @Override + public void cleanup(String macAddr, String vmName) { + try { + if (macAddr == null) { + return; + } + InetAddress inetAddr = _vmIpMap.remove(vmName); + String ipAddr = inetAddr.getHostName(); + for (Map.Entry entry: _macIpMap.entrySet()) { + if (entry.getValue().equalsIgnoreCase(ipAddr)){ + macAddr = entry.getKey(); + break; + } + } + ipAddr = _macIpMap.remove(macAddr); + + s_logger.info("Cleaning up for mac address: " + macAddr + " ip=" + ipAddr + " inetAddr=" + inetAddr); + if (ipAddr != null) { + _ipAddresses.offer(ipAddr); + } + } catch (Exception e) { + s_logger.debug("Failed to cleanup: " + e.toString()); + } + } + + @Override + public Map syncIpAddr() { + return _vmIpMap; + } + + @Override + public boolean stop() { + return false; + } + + @Override + public void initializeMacTable(List> macVmNameList) { + + + } + + @Override + public InetAddress getDhcpServerIP() { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/agent/src/com/cloud/agent/mockvm/MockVm.java b/agent/src/com/cloud/agent/mockvm/MockVm.java new file mode 100644 index 00000000000..ba43d9066ac --- /dev/null +++ b/agent/src/com/cloud/agent/mockvm/MockVm.java @@ -0,0 +1,75 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.agent.mockvm; + +import com.cloud.vm.VirtualMachine.State; + +// As storage is mapped from storage device, can virtually treat that VM here does +// not need any local storage resource, therefore we don't have attribute here for storage +public class MockVm { + + private String vmName; + private State state = State.Stopped; + + private long ramSize; // unit of Mbytes + private int cpuCount; + private int utilization; // in percentage + private int vncPort; // 0-based allocation, real port number needs to be applied with base + + public MockVm() { + } + + public MockVm(String vmName, State state, long ramSize, int cpuCount, int utilization, int vncPort) { + this.vmName = vmName; + this.state = state; + this.ramSize = ramSize; + this.cpuCount = cpuCount; + this.utilization = utilization; + this.vncPort = vncPort; + } + + public String getName() { + return vmName; + } + + public State getState() { + return state; + } + + public void setState(State state) { + this.state = state; + } + + public long getRamSize() { + return ramSize; + } + + public int getCpuCount() { + return cpuCount; + } + + public int getUtilization() { + return utilization; + } + + public int getVncPort() { + return vncPort; + } +} + diff --git a/agent/src/com/cloud/agent/mockvm/MockVmMgr.java b/agent/src/com/cloud/agent/mockvm/MockVmMgr.java new file mode 100644 index 00000000000..632e2eabaa9 --- /dev/null +++ b/agent/src/com/cloud/agent/mockvm/MockVmMgr.java @@ -0,0 +1,319 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.agent.mockvm; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Random; +import java.util.Set; + +import org.apache.log4j.Logger; + +import com.cloud.agent.api.to.VirtualMachineTO; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.VirtualMachine.State; + +public class MockVmMgr implements VmMgr { + private static final Logger s_logger = Logger.getLogger(MockVmMgr.class); + + private static final int DEFAULT_DOM0_MEM_MB = 128; + private static final Random randSeed = new Random(); + + private final Map vms = new HashMap(); + private long vncPortMap = 0; + + private Map _params = null; + + public MockVmMgr() { + } + + @Override + public Set getCurrentVMs() { + HashSet vmNameSet = new HashSet(); + synchronized(this) { + for(String vmName : vms.keySet()) + vmNameSet.add(vmName); + } + return vmNameSet; + } + + @Override + public String startVM(String vmName, String vnetId, String gateway, String dns, + String privateIP, String privateMac, String privateMask, + String publicIP, String publicMac, String publicMask, + int cpuCount, int cpuUtilization, long ramSize, + String localPath, String vncPassword) { + + if(s_logger.isInfoEnabled()) { + StringBuffer sb = new StringBuffer(); + sb.append("Start VM. name: " + vmName + ", vnet: " + vnetId + ", dns: " + dns); + sb.append(", privateIP: " + privateIP + ", privateMac: " + privateMac + ", privateMask: " + privateMask); + sb.append(", publicIP: " + publicIP + ", publicMac: " + publicMac + ", publicMask: " + publicMask); + sb.append(", cpu count: " + cpuCount + ", cpuUtilization: " + cpuUtilization + ", ram : " + ramSize); + sb.append(", localPath: " + localPath); + s_logger.info(sb.toString()); + } + + synchronized(this) { + MockVm vm = vms.get(vmName); + if(vm == null) { + if(ramSize > getHostFreeMemory()) + return "Out of memory"; + + int vncPort = allocVncPort(); + if(vncPort < 0) + return "Unable to allocate VNC port"; + + vm = new MockVm(vmName, State.Running, ramSize, cpuCount, cpuUtilization, + vncPort); + vms.put(vmName, vm); + } + } + + return null; + } + + @Override + public String stopVM(String vmName, boolean force) { + if(s_logger.isInfoEnabled()) + s_logger.info("Stop VM. name: " + vmName); + + synchronized(this) { + MockVm vm = vms.get(vmName); + if(vm != null) { + vm.setState(State.Stopped); + freeVncPort(vm.getVncPort()); + } + } + + return null; + } + + @Override + public String rebootVM(String vmName) { + if(s_logger.isInfoEnabled()) + s_logger.info("Reboot VM. name: " + vmName); + + synchronized(this) { + MockVm vm = vms.get(vmName); + if(vm != null) + vm.setState(State.Running); + } + return null; + } + + @Override + public boolean migrate(String vmName, String params) { + if(s_logger.isInfoEnabled()) + s_logger.info("Migrate VM. name: " + vmName); + + synchronized(this) { + MockVm vm = vms.get(vmName); + if(vm != null) { + vm.setState(State.Stopped); + freeVncPort(vm.getVncPort()); + + vms.remove(vmName); + return true; + } + } + + return false; + } + + public MockVm getVm(String vmName) { + synchronized(this) { + MockVm vm = vms.get(vmName); + return vm; + } + } + + @Override + public State checkVmState(String vmName) { + + synchronized(this) { + MockVm vm = vms.get(vmName); + if(vm != null) + return vm.getState(); + } + return State.Unknown; + } + + @Override + public Map getVmStates() { + Map states = new HashMap(); + + synchronized(this) { + for(MockVm vm : vms.values()) { + states.put(vm.getName(), vm.getState()); + } + } + return states; + } + + @Override + public void cleanupVM(String vmName, String local, String vnet) { + synchronized(this) { + MockVm vm = vms.get(vmName); + if(vm != null) { + freeVncPort(vm.getVncPort()); + } + vms.remove(vmName); + } + } + + @Override + public double getHostCpuUtilization() { + return 0.0d; + } + + @Override + public int getHostCpuCount() { + + return getConfiguredProperty("cpus", 4); + } + + @Override + public long getHostCpuSpeed() { + return getConfiguredProperty("cpuspeed", 4000L) ; + + } + + @Override + public long getHostTotalMemory() { // total memory in bytes + return getConfiguredProperty("memory", 16000L); + + } + + @Override + public long getHostFreeMemory() { // free memory in bytes + long memSize = getHostTotalMemory(); + memSize -= getHostDom0Memory(); + + synchronized(this) { + for(MockVm vm : vms.values()) { + if(vm.getState() != State.Stopped) + memSize -= vm.getRamSize(); + } + } + + return memSize; + } + + @Override + public long getHostDom0Memory() { // memory size in bytes + return DEFAULT_DOM0_MEM_MB*1024*1024L; + } + + @Override + public String cleanupVnet(String vnetId) { + return null; + } + + @Override + public Integer getVncPort(String name) { + synchronized(this) { + MockVm vm = vms.get(name); + if(vm != null) + return vm.getVncPort(); + } + + return new Integer(-1); + } + + public int allocVncPort() { + for(int i = 0; i < 64; i++) { + if( ((1L << i) & vncPortMap) == 0 ) { + vncPortMap |= (1L << i); + return i; + } + } + return -1; + } + + public void freeVncPort(int port) { + vncPortMap &= ~(1L << port); + } + + + @Override + public MockVm createVmFromSpec(VirtualMachineTO vmSpec) { + String vmName = vmSpec.getName(); + long ramSize = vmSpec.getMinRam(); + int utilizationPercent = randSeed.nextInt() % 100; + MockVm vm = null; + + synchronized(this) { + vm = vms.get(vmName); + if(vm == null) { + if (ramSize > getHostFreeMemory()) { + s_logger.debug("host is out of memory"); + throw new CloudRuntimeException("Host is out of Memory"); + } + + int vncPort = allocVncPort(); + if(vncPort < 0){ + s_logger.debug("Unable to allocate VNC port"); + throw new CloudRuntimeException("Unable to allocate vnc port"); + } + + vm = new MockVm(vmName, State.Running, ramSize, vmSpec.getCpus(), utilizationPercent, vncPort); + vms.put(vmName, vm); + } + } + return vm; + } + + @Override + public void createVbd(VirtualMachineTO vmSpec, String vmName, MockVm vm) { + // TODO Auto-generated method stub + + } + + @Override + public void createVif(VirtualMachineTO vmSpec, String vmName, MockVm vm) { + // TODO Auto-generated method stub + + } + + @Override + public void configure(Map params) { + _params = params; + } + + protected Long getConfiguredProperty(String key, Long defaultValue) { + String val = (String)_params.get(key); + + if (val != null) { + Long result = Long.parseLong(val); + return result; + } + return defaultValue; + } + + protected Integer getConfiguredProperty(String key, Integer defaultValue) { + String val = (String)_params.get(key); + + if (val != null) { + Integer result = Integer.parseInt(val); + return result; + } + return defaultValue; + } +} diff --git a/agent/src/com/cloud/agent/mockvm/VmMgr.java b/agent/src/com/cloud/agent/mockvm/VmMgr.java new file mode 100644 index 00000000000..81597d7171f --- /dev/null +++ b/agent/src/com/cloud/agent/mockvm/VmMgr.java @@ -0,0 +1,64 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.agent.mockvm; + +import java.util.Map; +import java.util.Set; + +import com.cloud.agent.api.to.VirtualMachineTO; +import com.cloud.vm.VirtualMachine.State; + +public interface VmMgr { + public Set getCurrentVMs(); + + public String startVM(String vmName, String vnetId, String gateway, String dns, + String privateIP, String privateMac, String privateMask, + String publicIP, String publicMac, String publicMask, + int cpuCount, int cpuUtilization, long ramSize, + String localPath, String vncPassword); + + public String stopVM(String vmName, boolean force); + public String rebootVM(String vmName); + public void cleanupVM(String vmName, String local, String vnet); + + public boolean migrate(String vmName, String params); + + public MockVm getVm(String vmName); + + public State checkVmState(String vmName); + public Map getVmStates(); + public Integer getVncPort(String name); + + public String cleanupVnet(String vnetId); + + public double getHostCpuUtilization(); + public int getHostCpuCount(); + public long getHostCpuSpeed(); + public long getHostTotalMemory(); + public long getHostFreeMemory(); + public long getHostDom0Memory(); + + public MockVm createVmFromSpec(VirtualMachineTO vmSpec); + + public void createVbd(VirtualMachineTO vmSpec, String vmName, MockVm vm); + + public void createVif(VirtualMachineTO vmSpec, String vmName, MockVm vm); + + public void configure(Map params); +} diff --git a/agent/src/com/cloud/agent/resource/computing/FakeComputingResource.java b/agent/src/com/cloud/agent/resource/computing/FakeComputingResource.java new file mode 100644 index 00000000000..ad9facf7633 --- /dev/null +++ b/agent/src/com/cloud/agent/resource/computing/FakeComputingResource.java @@ -0,0 +1,652 @@ +/** + * Copyright (C) 2011 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.agent.resource.computing; + + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.UUID; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.AttachIsoCommand; +import com.cloud.agent.api.AttachVolumeCommand; +import com.cloud.agent.api.CheckHealthAnswer; +import com.cloud.agent.api.CheckHealthCommand; +import com.cloud.agent.api.CheckStateAnswer; +import com.cloud.agent.api.CheckStateCommand; +import com.cloud.agent.api.CheckVirtualMachineAnswer; +import com.cloud.agent.api.CheckVirtualMachineCommand; +import com.cloud.agent.api.CleanupNetworkRulesCmd; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.GetHostStatsAnswer; +import com.cloud.agent.api.GetHostStatsCommand; +import com.cloud.agent.api.GetStorageStatsAnswer; +import com.cloud.agent.api.GetStorageStatsCommand; +import com.cloud.agent.api.GetVmStatsAnswer; +import com.cloud.agent.api.GetVmStatsCommand; +import com.cloud.agent.api.ModifySshKeysCommand; +import com.cloud.agent.api.ModifyStoragePoolAnswer; +import com.cloud.agent.api.ModifyStoragePoolCommand; +import com.cloud.agent.api.PingCommand; +import com.cloud.agent.api.PingRoutingCommand; +import com.cloud.agent.api.PingTestCommand; +import com.cloud.agent.api.ReadyAnswer; +import com.cloud.agent.api.ReadyCommand; +import com.cloud.agent.api.RebootAnswer; +import com.cloud.agent.api.RebootCommand; +import com.cloud.agent.api.SecurityIngressRuleAnswer; +import com.cloud.agent.api.SecurityIngressRulesCmd; +import com.cloud.agent.api.StartAnswer; +import com.cloud.agent.api.StartCommand; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupRoutingCommand; +import com.cloud.agent.api.StartupStorageCommand; +import com.cloud.agent.api.StopAnswer; +import com.cloud.agent.api.StopCommand; +import com.cloud.agent.api.StoragePoolInfo; +import com.cloud.agent.api.routing.SavePasswordCommand; +import com.cloud.agent.api.routing.VmDataCommand; +import com.cloud.agent.api.storage.CreateAnswer; +import com.cloud.agent.api.storage.CreateCommand; +import com.cloud.agent.api.storage.DestroyCommand; +import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer; +import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand; +import com.cloud.agent.api.to.NicTO; +import com.cloud.agent.api.to.VirtualMachineTO; +import com.cloud.agent.api.to.VolumeTO; +import com.cloud.agent.dhcp.DhcpSnooper; +import com.cloud.agent.dhcp.FakeDhcpSnooper; +import com.cloud.agent.mockvm.MockVm; +import com.cloud.agent.mockvm.MockVmMgr; +import com.cloud.agent.mockvm.VmMgr; +import com.cloud.agent.vmdata.JettyVmDataServer; +import com.cloud.agent.vmdata.VmDataServer; +import com.cloud.host.Host.Type; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.network.Networks.RouterPrivateIpStrategy; +import com.cloud.network.Networks.TrafficType; +import com.cloud.resource.ServerResource; +import com.cloud.resource.ServerResourceBase; +import com.cloud.storage.Storage; +import com.cloud.storage.Storage.StoragePoolType; +import com.cloud.storage.Volume; +import com.cloud.storage.VolumeVO; +import com.cloud.storage.template.TemplateInfo; +import com.cloud.utils.PropertiesUtil; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.Script; +import com.cloud.vm.VirtualMachine.State; + +/** + * Pretends to be a computing resource + * + */ +@Local(value={ServerResource.class}) +public class FakeComputingResource extends ServerResourceBase implements ServerResource { + private static final Logger s_logger = Logger.getLogger(FakeComputingResource.class); + private Map _params; + private VmMgr _vmManager = new MockVmMgr(); + protected HashMap _vms = new HashMap(20); + protected DhcpSnooper _dhcpSnooper = new FakeDhcpSnooper(); + protected VmDataServer _vmDataServer = new JettyVmDataServer(); + + + @Override + public Type getType() { + return Type.Routing; + } + + @Override + public StartupCommand[] initialize() { + Map changes = null; + + + final List info = getHostInfo(); + + final StartupRoutingCommand cmd = new StartupRoutingCommand((Integer)info.get(0), (Long)info.get(1), (Long)info.get(2), (Long)info.get(4), (String)info.get(3), HypervisorType.KVM, RouterPrivateIpStrategy.HostLocal, changes); + fillNetworkInformation(cmd); + cmd.getHostDetails().putAll(getVersionStrings()); + cmd.setCluster(getConfiguredProperty("cluster", "1")); + StoragePoolInfo pi = initializeLocalStorage(); + StartupStorageCommand sscmd = new StartupStorageCommand(); + sscmd.setPoolInfo(pi); + sscmd.setGuid(pi.getUuid()); + sscmd.setDataCenter((String)_params.get("zone")); + sscmd.setResourceType(Storage.StorageResourceType.STORAGE_POOL); + + return new StartupCommand[]{cmd, sscmd}; + + } + + private Map getVersionStrings() { + Map result = new HashMap(); + String hostOs = (String) _params.get("Host.OS"); + String hostOsVer = (String) _params.get("Host.OS.Version"); + String hostOsKernVer = (String) _params.get("Host.OS.Kernel.Version"); + result.put("Host.OS", hostOs==null?"Fedora":hostOs); + result.put("Host.OS.Version", hostOsVer==null?"14":hostOsVer); + result.put("Host.OS.Kernel.Version", hostOsKernVer==null?"2.6.35.6-45.fc14.x86_64":hostOsKernVer); + return result; + } + + protected void fillNetworkInformation(final StartupCommand cmd) { + + cmd.setPrivateIpAddress((String)_params.get("private.ip.address")); + cmd.setPrivateMacAddress((String)_params.get("private.mac.address")); + cmd.setPrivateNetmask((String)_params.get("private.ip.netmask")); + + cmd.setStorageIpAddress((String)_params.get("private.ip.address")); + cmd.setStorageMacAddress((String)_params.get("private.mac.address")); + cmd.setStorageNetmask((String)_params.get("private.ip.netmask")); + cmd.setGatewayIpAddress((String)_params.get("gateway.ip.address")); + + } + + protected StoragePoolInfo initializeLocalStorage() { + String hostIp = (String)_params.get("private.ip.address"); + String localStoragePath = (String)_params.get("local.storage.path"); + String lh = hostIp + localStoragePath; + String uuid = UUID.nameUUIDFromBytes(lh.getBytes()).toString(); + + String capacity = (String)_params.get("local.storage.capacity"); + String available = (String)_params.get("local.storage.avail"); + + return new StoragePoolInfo(uuid, hostIp, localStoragePath, + localStoragePath, StoragePoolType.Filesystem, + Long.parseLong(capacity), Long.parseLong(available)); + + } + + @Override + public PingCommand getCurrentStatus(long id) { + final HashMap newStates = new HashMap(); + _dhcpSnooper.syncIpAddr(); + return new PingRoutingCommand(com.cloud.host.Host.Type.Routing, id, newStates); + } + + @Override + public Answer executeRequest(Command cmd) { + try { + if (cmd instanceof ReadyCommand) { + return execute((ReadyCommand)cmd); + }else if (cmd instanceof ModifySshKeysCommand) { + return execute((ModifySshKeysCommand)cmd);//TODO: remove + } else if (cmd instanceof GetHostStatsCommand) { + return execute((GetHostStatsCommand)cmd); + } else if (cmd instanceof PrimaryStorageDownloadCommand) { + return execute((PrimaryStorageDownloadCommand) cmd); + + } else if (cmd instanceof StopCommand) { + return execute((StopCommand)cmd); + } else if (cmd instanceof GetVmStatsCommand) { + return execute((GetVmStatsCommand)cmd); + } else if (cmd instanceof RebootCommand) { + return execute((RebootCommand)cmd); + } else if (cmd instanceof CheckStateCommand) { + return executeRequest(cmd); + } else if (cmd instanceof CheckHealthCommand) { + return execute((CheckHealthCommand)cmd); + } else if (cmd instanceof PingTestCommand) { + return execute((PingTestCommand)cmd); + } else if (cmd instanceof CheckVirtualMachineCommand) { + return execute((CheckVirtualMachineCommand)cmd); + } else if (cmd instanceof ReadyCommand) { + return execute((ReadyCommand)cmd); + } else if (cmd instanceof StopCommand) { + return execute((StopCommand)cmd); + } else if (cmd instanceof CreateCommand) { + return execute((CreateCommand) cmd); + } else if (cmd instanceof DestroyCommand) { + return execute((DestroyCommand) cmd); + } else if (cmd instanceof PrimaryStorageDownloadCommand) { + return execute((PrimaryStorageDownloadCommand) cmd); + } else if (cmd instanceof GetStorageStatsCommand) { + return execute((GetStorageStatsCommand) cmd); + } else if (cmd instanceof ModifyStoragePoolCommand) { + return execute((ModifyStoragePoolCommand) cmd); + } else if (cmd instanceof SecurityIngressRulesCmd) { + return execute((SecurityIngressRulesCmd) cmd); + } else if (cmd instanceof StartCommand ) { + return execute((StartCommand) cmd); + } else if (cmd instanceof CleanupNetworkRulesCmd) { + return execute((CleanupNetworkRulesCmd)cmd); + } else if (cmd instanceof SavePasswordCommand) { + return execute((SavePasswordCommand)cmd); + } else if (cmd instanceof VmDataCommand) { + return execute((VmDataCommand)cmd); + } else { + s_logger.warn("Unsupported command "); + return Answer.createUnsupportedCommandAnswer(cmd); + } + } catch (final IllegalArgumentException e) { + return new Answer(cmd, false, e.getMessage()); + } + } + + private Answer execute(CleanupNetworkRulesCmd cmd) { + return new Answer(cmd); + } + + private Answer execute(SecurityIngressRulesCmd cmd) { + s_logger.info("Programmed network rules for vm " + cmd.getVmName() + " guestIp=" + cmd.getGuestIp() + ", numrules=" + cmd.getRuleSet().length); + return new SecurityIngressRuleAnswer(cmd); + } + + private Answer execute(ModifyStoragePoolCommand cmd) { + long capacity = getConfiguredProperty("local.storage.capacity", 10000000000L); + long used = 10000000L; + long available = capacity - used; + if (cmd.getAdd()) { + + ModifyStoragePoolAnswer answer = new ModifyStoragePoolAnswer(cmd, + capacity, used, new HashMap()); + + if (s_logger.isInfoEnabled()) + s_logger + .info("Sending ModifyStoragePoolCommand answer with capacity: " + + capacity + + ", used: " + + used + + ", available: " + available); + return answer; + } else { + if (s_logger.isInfoEnabled()) + s_logger + .info("ModifyNetfsStoragePoolCmd is not add command, cmd: " + + cmd.toString()); + return new Answer(cmd); + } + } + + private Answer execute(GetStorageStatsCommand cmd) { + return new GetStorageStatsAnswer(cmd, getConfiguredProperty("local.storage.capacity", 100000000000L), 0L); + } + + protected synchronized ReadyAnswer execute(ReadyCommand cmd) { + return new ReadyAnswer(cmd); + } + + private Answer execute(PrimaryStorageDownloadCommand cmd) { + return new PrimaryStorageDownloadAnswer(cmd.getLocalPath(), 16000000L); + } + + private Answer execute(ModifySshKeysCommand cmd) { + return new Answer(cmd, true, null); + } + + @Override + protected String getDefaultScriptsDir() { + return null; + } + + + protected String getConfiguredProperty(String key, String defaultValue) { + String val = (String)_params.get(key); + return val==null?defaultValue:val; + } + + protected Long getConfiguredProperty(String key, Long defaultValue) { + String val = (String)_params.get(key); + + if (val != null) { + Long result = Long.parseLong(val); + return result; + } + return defaultValue; + } + + protected List getHostInfo() { + final ArrayList info = new ArrayList(); + long speed = getConfiguredProperty("cpuspeed", 4000L) ; + long cpus = getConfiguredProperty("cpus", 4L); + long ram = getConfiguredProperty("memory", 16000L*1024L*1024L); + long dom0ram = Math.min(ram/10, 768*1024*1024L); + + + String cap = getConfiguredProperty("capabilities", "hvm"); + info.add((int)cpus); + info.add(speed); + info.add(ram); + info.add(cap); + info.add(dom0ram); + return info; + + } + private Map getSimulatorProperties() throws ConfigurationException { + final File file = PropertiesUtil.findConfigFile("simulator.properties"); + if (file == null) { + throw new ConfigurationException("Unable to find simulator.properties."); + } + + s_logger.info("simulator.properties found at " + file.getAbsolutePath()); + Properties properties = new Properties(); + try { + properties.load(new FileInputStream(file)); + + + final Map params = PropertiesUtil.toMap(properties); + + + return params; + } catch (final FileNotFoundException ex) { + throw new CloudRuntimeException("Cannot find the file: " + file.getAbsolutePath(), ex); + } catch (final IOException ex) { + throw new CloudRuntimeException("IOException in reading " + file.getAbsolutePath(), ex); + } + } + + + + @Override + public boolean configure(String name, Map params) + throws ConfigurationException { + Map simProps = getSimulatorProperties(); + params.putAll(simProps); + setParams(params); + _vmManager.configure(params); + _dhcpSnooper.configure(name, params); + _vmDataServer.configure(name, params); + return true; + } + + public void setParams(Map _params) { + this._params = _params; + } + + public Map getParams() { + return _params; + } + + + protected synchronized StartAnswer execute(StartCommand cmd) { + VmMgr vmMgr = getVmManager(); + + VirtualMachineTO vmSpec = cmd.getVirtualMachine(); + String vmName = vmSpec.getName(); + State state = State.Stopped; + + try { + if (!_vms.containsKey(vmName)) { + synchronized (_vms) { + _vms.put(vmName, State.Starting); + } + + MockVm vm = vmMgr.createVmFromSpec(vmSpec); + vmMgr.createVbd(vmSpec, vmName, vm); + vmMgr.createVif(vmSpec, vmName, vm); + + state = State.Running; + for (NicTO nic: cmd.getVirtualMachine().getNics()) { + if (nic.getType() == TrafficType.Guest) { + InetAddress addr = _dhcpSnooper.getIPAddr(nic.getMac(), vmName); + nic.setIp(addr.getHostAddress()); + } + } + _vmDataServer.handleVmStarted(cmd.getVirtualMachine()); + return new StartAnswer(cmd); + } else { + String msg = "There is already a VM having the same name " + + vmName; + s_logger.warn(msg); + return new StartAnswer(cmd, msg); + } + } catch (Exception ex) { + + } finally { + synchronized (_vms) { + _vms.put(vmName, state); + } + } + return new StartAnswer(cmd); + } + + protected synchronized StopAnswer execute(StopCommand cmd) { + VmMgr vmMgr = getVmManager(); + + StopAnswer answer = null; + String vmName = cmd.getVmName(); + + Integer port = vmMgr.getVncPort(vmName); + Long bytesReceived = null; + Long bytesSent = null; + + + + State state = null; + synchronized (_vms) { + state = _vms.get(vmName); + _vms.put(vmName, State.Stopping); + } + try { + String result = vmMgr.stopVM(vmName, false); + if (result != null) { + s_logger.info("Trying destroy on " + vmName); + if (result == Script.ERR_TIMEOUT) { + result = vmMgr.stopVM(vmName, true); + } + + s_logger.warn("Couldn't stop " + vmName); + + if (result != null) { + return new StopAnswer(cmd, result); + } + } + + answer = new StopAnswer(cmd, null, port, bytesSent, bytesReceived); + + + String result2 = vmMgr.cleanupVnet(cmd.getVnet()); + if (result2 != null) { + result = result2 + (result != null ? ("\n" + result) : ""); + answer = new StopAnswer(cmd, result, port, bytesSent, + bytesReceived); + } + + _dhcpSnooper.cleanup(vmName, null); + + return answer; + } finally { + if (answer == null || !answer.getResult()) { + synchronized (_vms) { + _vms.put(vmName, state); + } + } + } + } + + protected Answer execute(final VmDataCommand cmd) { + return _vmDataServer.handleVmDataCommand(cmd); + } + + protected Answer execute(final SavePasswordCommand cmd) { + return new Answer(cmd); + } + + protected Answer execute(RebootCommand cmd) { + VmMgr vmMgr = getVmManager(); + vmMgr.rebootVM(cmd.getVmName()); + return new RebootAnswer(cmd, "success", 0L, 0L); + } + + private Answer execute(PingTestCommand cmd) { + return new Answer(cmd); + } + + protected GetVmStatsAnswer execute(GetVmStatsCommand cmd) { + return null; + } + + private VmMgr getVmManager() { + return _vmManager; + } + + protected Answer execute(GetHostStatsCommand cmd) { + VmMgr vmMgr = getVmManager(); + return new GetHostStatsAnswer(cmd, vmMgr.getHostCpuUtilization(), vmMgr + .getHostFreeMemory(), vmMgr.getHostTotalMemory(), 0, 0, + "SimulatedHost"); + } + + protected CheckStateAnswer execute(CheckStateCommand cmd) { + State state = getVmManager().checkVmState(cmd.getVmName()); + return new CheckStateAnswer(cmd, state); + } + + protected CheckHealthAnswer execute(CheckHealthCommand cmd) { + return new CheckHealthAnswer(cmd, true); + } + + + protected CheckVirtualMachineAnswer execute( + final CheckVirtualMachineCommand cmd) { + VmMgr vmMgr = getVmManager(); + final String vmName = cmd.getVmName(); + + final State state = vmMgr.checkVmState(vmName); + Integer vncPort = null; + if (state == State.Running) { + vncPort = vmMgr.getVncPort(vmName); + synchronized (_vms) { + _vms.put(vmName, State.Running); + } + } + return new CheckVirtualMachineAnswer(cmd, state, vncPort); + } + + + protected Answer execute(final AttachVolumeCommand cmd) { + return new Answer(cmd); + } + + protected Answer execute(final AttachIsoCommand cmd) { + return new Answer(cmd); + } + + protected CreateAnswer execute(final CreateCommand cmd) { + try { + + VolumeTO vol = new VolumeTO(cmd.getVolumeId(), + Volume.Type.ROOT, + Storage.StorageResourceType.STORAGE_POOL, + com.cloud.storage.Storage.StoragePoolType.LVM, cmd + .getPool().getUuid(), "dummy", "/mountpoint", + "dummyPath", 1000L, null); + return new CreateAnswer(cmd, vol); + } catch (Throwable th) { + return new CreateAnswer(cmd, new Exception("Unexpected exception")); + } + } + + + + protected HashMap sync() { + Map newStates; + Map oldStates = null; + + HashMap changes = new HashMap(); + + synchronized (_vms) { + newStates = getVmManager().getVmStates(); + oldStates = new HashMap(_vms.size()); + oldStates.putAll(_vms); + + for (Map.Entry entry : newStates.entrySet()) { + String vm = entry.getKey(); + + State newState = entry.getValue(); + State oldState = oldStates.remove(vm); + + if (s_logger.isTraceEnabled()) { + s_logger + .trace("VM " + + vm + + ": xen has state " + + newState + + " and we have state " + + (oldState != null ? oldState.toString() + : "null")); + } + + if (oldState == null) { + _vms.put(vm, newState); + changes.put(vm, newState); + } else if (oldState == State.Starting) { + if (newState == State.Running) { + _vms.put(vm, newState); + } else if (newState == State.Stopped) { + s_logger.debug("Ignoring vm " + vm + + " because of a lag in starting the vm."); + } + } else if (oldState == State.Stopping) { + if (newState == State.Stopped) { + _vms.put(vm, newState); + } else if (newState == State.Running) { + s_logger.debug("Ignoring vm " + vm + + " because of a lag in stopping the vm. "); + } + } else if (oldState != newState) { + _vms.put(vm, newState); + changes.put(vm, newState); + } + } + + for (Map.Entry entry : oldStates.entrySet()) { + String vm = entry.getKey(); + State oldState = entry.getValue(); + + if (s_logger.isTraceEnabled()) { + s_logger.trace("VM " + vm + + " is now missing from xen so reporting stopped"); + } + + if (oldState == State.Stopping) { + s_logger.debug("Ignoring VM " + vm + + " in transition state stopping."); + _vms.remove(vm); + } else if (oldState == State.Starting) { + s_logger.debug("Ignoring VM " + vm + + " in transition state starting."); + } else if (oldState == State.Stopped) { + _vms.remove(vm); + } else { + changes.put(entry.getKey(), State.Stopped); + } + } + } + + return changes; + } + + protected Answer execute(DestroyCommand cmd) { + return new Answer(cmd, true, null); + } +} diff --git a/agent/src/com/cloud/agent/resource/computing/LibvirtComputingResource.java b/agent/src/com/cloud/agent/resource/computing/LibvirtComputingResource.java index 163c8db48e3..0e02982aedc 100644 --- a/agent/src/com/cloud/agent/resource/computing/LibvirtComputingResource.java +++ b/agent/src/com/cloud/agent/resource/computing/LibvirtComputingResource.java @@ -27,6 +27,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.net.InetAddress; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; @@ -77,7 +78,6 @@ import com.cloud.agent.api.CleanupNetworkRulesCmd; import com.cloud.agent.api.Command; import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand; import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand; -import com.cloud.agent.api.CreateStoragePoolCommand; import com.cloud.agent.api.CreateVolumeFromSnapshotAnswer; import com.cloud.agent.api.CreateVolumeFromSnapshotCommand; import com.cloud.agent.api.DeleteSnapshotBackupAnswer; @@ -230,12 +230,13 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv private String _dcId; private String _pod; private String _clusterId; + private long _hvVersion; private KVMHAMonitor _monitor; private final String _SSHKEYSPATH = "/root/.ssh"; private final String _SSHPRVKEYPATH = _SSHKEYSPATH + File.separator + "id_rsa.cloud"; private final String _SSHPUBKEYPATH = _SSHKEYSPATH + File.separator + "id_rsa.pub.cloud"; - private final String _mountPoint = "/mnt"; + private String _mountPoint = "/mnt"; StorageLayer _storage; private static final class KeyValueInterpreter extends OutputInterpreter { @@ -315,13 +316,13 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv protected List _vmsKilled = new ArrayList(); private VirtualRoutingResource _virtRouterResource; - private LibvirtStorageResource _storageResource; + protected LibvirtStorageResource _storageResource; private String _pingTestPath; private int _dom0MinMem; - private enum defineOps { + protected enum defineOps { UNDEFINE_VM, DEFINE_VM } @@ -553,6 +554,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } } + + _localStoragePath = (String)params.get("local.storage.path"); if (_localStoragePath == null) { _localStoragePath = "/var/lib/libvirt/images/"; @@ -642,9 +645,14 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv s_logger.debug("Failed to found the local gateway"); } + _mountPoint = (String)params.get("mount.path"); + if (_mountPoint == null) { + _mountPoint = "/mnt"; + } + _storageResource = new LibvirtStorageResource(this, _storage, _createvmPath, _timeout, _mountPoint, _monitor); - + return true; } @@ -1759,7 +1767,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv _vms.put(vmName, state); } } else { - destroy_network_rules_for_vm(vmName); + destroy_network_rules_for_vm(conn, vmName); cleanupVM(conn, vmName, getVnetId(VirtualMachineName.getVnet(vmName))); } @@ -1972,10 +1980,18 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } try { Connect conn = LibvirtConnection.getConnection(); - destroy_network_rules_for_vm(vmName); + + String macAddress = null; + if (vmName.startsWith("i-")) { + List nics = getInterfaces(conn, vmName); + macAddress = nics.get(0).getMacAddress(); + } + + destroy_network_rules_for_vm(conn, vmName); String result = stopVM(conn, vmName, defineOps.UNDEFINE_VM); final String result2 = cleanupVnet(conn, cmd.getVnet()); + if (result != null && result2 != null) { result = result2 + result; } @@ -2068,13 +2084,13 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv - private void handleVmStartFailure(Connect conn, String vmName, LibvirtVMDef vm) { + protected void handleVmStartFailure(Connect conn, String vmName, LibvirtVMDef vm) { if (vm != null && vm.getDevices() != null) { cleanupVMNetworks(conn, vm.getDevices().getInterfaces()); } } - private LibvirtVMDef createVMFromSpec(VirtualMachineTO vmTO) { + protected LibvirtVMDef createVMFromSpec(VirtualMachineTO vmTO) { LibvirtVMDef vm = new LibvirtVMDef(); vm.setHvsType(_hypervisorType); vm.setDomainName(vmTO.getName()); @@ -2128,7 +2144,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv return vm; } - private void createVifs(Connect conn, VirtualMachineTO vmSpec, LibvirtVMDef vm) throws InternalErrorException, LibvirtException { + protected void createVifs(Connect conn, VirtualMachineTO vmSpec, LibvirtVMDef vm) throws InternalErrorException, LibvirtException { NicTO[] nics = vmSpec.getNics(); for (int i = 0; i < nics.length; i++) { for (NicTO nic : vmSpec.getNics()) { @@ -2165,14 +2181,14 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv NicTO[] nics = vmSpec.getNics(); for (NicTO nic : nics) { - if (nic.isSecurityGroupEnabled()) { - if (vmSpec.getType() != VirtualMachine.Type.User) { - default_network_rules_for_systemvm(conn, vmName); - } else { - default_network_rules(conn, vmName, nic, vmSpec.getId()); - } - } - } + if (nic.isSecurityGroupEnabled()) { + if (vmSpec.getType() != VirtualMachine.Type.User) { + default_network_rules_for_systemvm(conn, vmName); + } else { + default_network_rules(conn, vmName, nic, vmSpec.getId()); + } + } + } // Attach each data volume to the VM, if there is a deferred attached disk for (DiskDef disk : vm.getDevices().getDisks()) { @@ -2180,6 +2196,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv attachOrDetachDisk(conn, true, vmName, disk.getDiskPath(), disk.getDiskSeq()); } } + state = State.Running; return new StartAnswer(cmd); } catch (Exception e) { @@ -2208,7 +2225,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } } - private void createVbd(Connect conn, VirtualMachineTO vmSpec, String vmName, LibvirtVMDef vm) throws InternalErrorException, LibvirtException, URISyntaxException{ + protected void createVbd(Connect conn, VirtualMachineTO vmSpec, String vmName, LibvirtVMDef vm) throws InternalErrorException, LibvirtException, URISyntaxException{ for (VolumeTO volume : vmSpec.getDisks()) { String volPath = getVolumePath(conn, volume); @@ -2445,6 +2462,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv @Override public PingCommand getCurrentStatus(long id) { final HashMap newStates = sync(); + if (!_can_bridge_firewall) { return new PingRoutingCommand(com.cloud.host.Host.Type.Routing, id, newStates); } else { @@ -2485,6 +2503,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv cmd.getHostDetails().putAll(getVersionStrings()); cmd.setPool(_pool); cmd.setCluster(_clusterId); + cmd.setGatewayIpAddress(_localGateway); StartupStorageCommand sscmd = null; try { @@ -2710,7 +2729,6 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv Domain dm = null; for (int i =0; i < ids.length; i++) { try { - s_logger.debug("domid" + ids[i]); dm = conn.domainLookupByID(ids[i]); DomainInfo.DomainState ps = dm.getInfo().state; @@ -3105,9 +3123,6 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } private String setVnetBrName(String vnetId) { - if (vnetId.equalsIgnoreCase(Vlan.UNTAGGED)) { - return _guestBridgeName; - } return "cloudVirBr" + vnetId; } private String getVnetIdFromBrName(String vnetBrName) { @@ -3125,7 +3140,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv return conn.domainLookupByUUID(UUID.nameUUIDFromBytes(vmName.getBytes())); } - private List getInterfaces(Connect conn, String vmName) { + protected List getInterfaces(Connect conn, String vmName) { LibvirtDomainXMLParser parser = new LibvirtDomainXMLParser(); Domain dm = null; try { @@ -3263,15 +3278,22 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv return true; } - private boolean destroy_network_rules_for_vm(String vmName) { + protected boolean destroy_network_rules_for_vm(Connect conn, String vmName) { if (!_can_bridge_firewall) { return false; } - + String vif = null; + List intfs = getInterfaces(conn, vmName); + if (intfs.size() > 0) { + InterfaceDef intf = intfs.get(0); + vif = intf.getDevName(); + } Script cmd = new Script(_securityGroupPath, _timeout, s_logger); cmd.add("destroy_network_rules_for_vm"); - cmd.add("--vmname"); - cmd.add(vmName); + cmd.add("--vmname", vmName); + if (vif != null) { + cmd.add("--vif", vif); + } String result = cmd.execute(); if (result != null) { return false; @@ -3279,7 +3301,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv return true; } - private boolean default_network_rules(Connect conn, String vmName, NicTO nic, Long vmId) { + protected boolean default_network_rules(Connect conn, String vmName, NicTO nic, Long vmId) { if (!_can_bridge_firewall) { return false; } @@ -3297,7 +3319,9 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv cmd.add("default_network_rules"); cmd.add("--vmname", vmName); cmd.add("--vmid", vmId.toString()); - cmd.add("--vmip", nic.getIp()); + if (nic.getIp() != null) { + cmd.add("--vmip", nic.getIp()); + } cmd.add("--vmmac", nic.getMac()); cmd.add("--vif", vif); cmd.add("--brname", brname); @@ -3308,7 +3332,41 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv return true; } - private boolean default_network_rules_for_systemvm(Connect conn, String vmName) { + protected boolean post_default_network_rules(Connect conn, String vmName, NicTO nic, Long vmId, InetAddress dhcpServerIp, String hostIp, String hostMacAddr) { + if (!_can_bridge_firewall) { + return false; + } + + List intfs = getInterfaces(conn, vmName); + if (intfs.size() < nic.getDeviceId()) { + return false; + } + + InterfaceDef intf = intfs.get(nic.getDeviceId()); + String brname = intf.getBrName(); + String vif = intf.getDevName(); + + Script cmd = new Script(_securityGroupPath, _timeout, s_logger); + cmd.add("post_default_network_rules"); + cmd.add("--vmname", vmName); + cmd.add("--vmid", vmId.toString()); + cmd.add("--vmip", nic.getIp()); + cmd.add("--vmmac", nic.getMac()); + cmd.add("--vif", vif); + cmd.add("--brname", brname); + if (dhcpServerIp != null) + cmd.add("--dhcpSvr", dhcpServerIp.getHostAddress()); + + cmd.add("--hostIp", hostIp); + cmd.add("--hostMacAddr", hostMacAddr); + String result = cmd.execute(); + if (result != null) { + return false; + } + return true; + } + + protected boolean default_network_rules_for_systemvm(Connect conn, String vmName) { if (!_can_bridge_firewall) { return false; } @@ -3448,7 +3506,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv private void createControlNetwork(Connect conn) throws LibvirtException { _virtRouterResource.createControlNetwork(_linkLocalBridgeName); } - + private Answer execute(NetworkRulesSystemVmCommand cmd) { boolean success = false; Connect conn; @@ -3462,4 +3520,6 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv return new Answer(cmd, success, ""); } + + } diff --git a/agent/src/com/cloud/agent/resource/computing/LibvirtStorageResource.java b/agent/src/com/cloud/agent/resource/computing/LibvirtStorageResource.java index 1bc1a63623e..ff230087765 100644 --- a/agent/src/com/cloud/agent/resource/computing/LibvirtStorageResource.java +++ b/agent/src/com/cloud/agent/resource/computing/LibvirtStorageResource.java @@ -194,10 +194,24 @@ public class LibvirtStorageResource { } public StoragePool getStoragePoolbyURI(Connect conn, URI uri) throws LibvirtException { - String sourcePath = uri.getPath(); - sourcePath = sourcePath.replace("//", "/"); - String sourceHost = uri.getHost(); - String uuid = UUID.nameUUIDFromBytes(new String(sourceHost + sourcePath).getBytes()).toString(); + String sourcePath; + String uuid; + String sourceHost = ""; + String protocal; + if (uri.getScheme().equalsIgnoreCase("local")) { + sourcePath = _mountPoint + File.separator + uri.toString().replace("local:///", ""); + sourcePath = sourcePath.replace("//", "/"); + uuid = UUID.nameUUIDFromBytes(new String(sourcePath).getBytes()).toString(); + protocal = "DIR"; + } else { + sourcePath = uri.getPath(); + sourcePath = sourcePath.replace("//", "/"); + sourceHost = uri.getHost(); + uuid = UUID.nameUUIDFromBytes(new String(sourceHost + sourcePath).getBytes()).toString(); + protocal = "NFS"; + } + + String targetPath = _mountPoint + File.separator + uuid; StoragePool sp = null; try { @@ -207,12 +221,29 @@ public class LibvirtStorageResource { if (sp == null) { try { - _storageLayer.mkdir(targetPath); - LibvirtStoragePoolDef spd = new LibvirtStoragePoolDef(poolType.NETFS, uuid, uuid, - sourceHost, sourcePath, targetPath); - s_logger.debug(spd.toString()); - addStoragePool(uuid); + LibvirtStoragePoolDef spd = null; + if (protocal.equalsIgnoreCase("NFS")) { + _storageLayer.mkdir(targetPath); + spd = new LibvirtStoragePoolDef(poolType.NETFS, uuid, uuid, + sourceHost, sourcePath, targetPath); + s_logger.debug(spd.toString()); + addStoragePool(uuid); + synchronized (getStoragePool(uuid)) { + sp = conn.storagePoolDefineXML(spd.toString(), 0); + + if (sp == null) { + s_logger.debug("Failed to define storage pool"); + return null; + } + sp.create(0); + } + } else if (protocal.equalsIgnoreCase("DIR")) { + _storageLayer.mkdir(targetPath); + spd = new LibvirtStoragePoolDef(poolType.DIR, uuid, uuid, + null, null, sourcePath); + } + synchronized (getStoragePool(uuid)) { sp = conn.storagePoolDefineXML(spd.toString(), 0); diff --git a/agent/src/com/cloud/agent/resource/computing/LibvirtVMDef.java b/agent/src/com/cloud/agent/resource/computing/LibvirtVMDef.java index e40453e80b2..607385948cb 100644 --- a/agent/src/com/cloud/agent/resource/computing/LibvirtVMDef.java +++ b/agent/src/com/cloud/agent/resource/computing/LibvirtVMDef.java @@ -486,6 +486,9 @@ public class LibvirtVMDef { public String getDevName() { return _networkName; } + public String getMacAddress() { + return _macAddr; + } @Override public String toString() { diff --git a/agent/src/com/cloud/agent/vmdata/JettyVmDataServer.java b/agent/src/com/cloud/agent/vmdata/JettyVmDataServer.java new file mode 100644 index 00000000000..20dac0641ff --- /dev/null +++ b/agent/src/com/cloud/agent/vmdata/JettyVmDataServer.java @@ -0,0 +1,348 @@ +/** + * Copyright (C) 2011 Cloud.com. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later +version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.agent.vmdata; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.File; +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.log4j.Logger; +import org.mortbay.jetty.Connector; +import org.mortbay.jetty.Handler; +import org.mortbay.jetty.Server; +import org.mortbay.jetty.handler.DefaultHandler; +import org.mortbay.jetty.handler.HandlerList; +import org.mortbay.jetty.handler.ResourceHandler; +import org.mortbay.jetty.nio.SelectChannelConnector; +import org.mortbay.jetty.servlet.Context; +import org.mortbay.jetty.servlet.ServletHolder; +import org.mortbay.thread.QueuedThreadPool; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.routing.VmDataCommand; +import com.cloud.agent.api.to.NicTO; +import com.cloud.agent.api.to.VirtualMachineTO; +import com.cloud.network.Networks.TrafficType; +import com.cloud.storage.JavaStorageLayer; +import com.cloud.storage.StorageLayer; +import com.cloud.utils.net.NetUtils; + +/** + * Serves vm data using embedded Jetty server + * + */ +@Local (value={VmDataServer.class}) +public class JettyVmDataServer implements VmDataServer { + private static final Logger s_logger = Logger.getLogger(JettyVmDataServer.class); + + public static final String USER_DATA = "user-data"; + public static final String META_DATA = "meta-data"; + protected String _vmDataDir; + protected Server _jetty; + protected String _hostIp; + protected Map _ipVmMap = new HashMap(); + protected StorageLayer _fs = new JavaStorageLayer(); + + public class VmDataServlet extends HttpServlet { + + private static final long serialVersionUID = -1640031398971742349L; + + JettyVmDataServer _vmDataServer; + String _dataType; //userdata or meta-data + + + public VmDataServlet(JettyVmDataServer dataServer, String dataType) { + this._vmDataServer = dataServer; + this._dataType = dataType; + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + int port = req.getServerPort(); + if (port != 80 && port != 8000) { + resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Request not understood"); + return; + } + if (_dataType.equalsIgnoreCase(USER_DATA)) { + handleUserData(req, resp); + } else if (_dataType.equalsIgnoreCase(META_DATA)) { + handleMetaData(req, resp); + } + } + + protected void handleUserData(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + String requester = req.getRemoteAddr(); + resp.setContentType("text/html"); + resp.setStatus(HttpServletResponse.SC_OK); + String userData = _vmDataServer.getVmDataItem(requester, USER_DATA); + if (userData != null){ + resp.getWriter().println(); + } else { + resp.setStatus(HttpServletResponse.SC_NOT_FOUND); + resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Request not found"); + } + + } + + protected void handleMetaData(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + String metadataItem = req.getPathInfo(); + String requester = req.getRemoteAddr(); + resp.setContentType("text/html"); + resp.setStatus(HttpServletResponse.SC_OK); + String metaData = _vmDataServer.getVmDataItem(requester, metadataItem); + if (metaData != null) { + resp.getWriter().println(_vmDataServer.getVmDataItem(requester, metadataItem)); + } else { + resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Request not found"); + } + } + + } + + @Override + public boolean configure(String name, Map params) + throws ConfigurationException { + boolean success = true; + try { + int vmDataPort = 80; + int fileservingPort = 8000; + _vmDataDir = (String)params.get("vm.data.dir"); + String port = (String) params.get("vm.data.port"); + if (port != null) { + vmDataPort = Integer.parseInt(port); + } + port = (String) params.get("file.server.port"); + if (port != null) { + fileservingPort = Integer.parseInt(port); + } + _hostIp = (String) params.get("host.ip"); + + if (_vmDataDir == null) { + _vmDataDir = "/var/www/html"; + } + success = _fs.mkdirs(_vmDataDir); + success = success && buildIpVmMap(); + if (success) { + setupJetty(vmDataPort, fileservingPort); + } + } catch (Exception e) { + s_logger.warn("Failed to configure jetty", e); + throw new ConfigurationException("Failed to configure jetty!!"); + } + return success; + } + + protected boolean buildIpVmMap() { + String[] dirs = _fs.listFiles(_vmDataDir); + for (String dir: dirs) { + String [] path = dir.split("/"); + String vm = path[path.length -1]; + if (vm.startsWith("i-")) { + String [] dataFiles = _fs.listFiles(dir); + for (String dfile: dataFiles) { + String path2[] = dfile.split("/"); + String ipv4file = path2[path2.length -1]; + if (ipv4file.equalsIgnoreCase("local-ipv4")){ + try { + BufferedReader input = new BufferedReader(new FileReader(dfile)); + String line = null; + while (( line = input.readLine()) != null){ + if (NetUtils.isValidIp(line)) { + _ipVmMap.put(line, dir); + s_logger.info("Found ip " + line + " for vm " + vm); + } else { + s_logger.info("Invalid ip " + line + " for vm " + vm); + } + } + } catch (FileNotFoundException e) { + s_logger.warn("Failed to find file " + dfile); + } catch (IOException e) { + s_logger.warn("Failed to get ip address of " + vm); + } + + } + } + } + } + return true; + } + + public String getVmDataItem(String requester, String dataItem) { + String vmName = _ipVmMap.get(requester); + if (vmName == null){ + return null; + } + String vmDataFile = _vmDataDir + File.separator + vmName + dataItem; + try { + BufferedReader input = new BufferedReader(new FileReader(vmDataFile)); + StringBuilder result = new StringBuilder(); + String line = null; + while ((line = input.readLine()) != null) { + result.append(line); + } + input.close(); + return result.toString(); + } catch (FileNotFoundException e) { + s_logger.warn("Failed to find requested file " + vmDataFile); + return null; + } catch (IOException e) { + s_logger.warn("Failed to read requested file " + vmDataFile); + return null; + } + } + + private void setupJetty(int vmDataPort, int fileservingPort) throws Exception { + _jetty = new Server(); + + SelectChannelConnector connector0 = new SelectChannelConnector(); + connector0.setHost(_hostIp); + connector0.setPort(fileservingPort); + connector0.setMaxIdleTime(30000); + connector0.setRequestBufferSize(8192); + + SelectChannelConnector connector1 = new SelectChannelConnector(); + connector1.setHost(_hostIp); + connector1.setPort(vmDataPort); + connector1.setThreadPool(new QueuedThreadPool(5)); + connector1.setMaxIdleTime(30000); + connector1.setRequestBufferSize(8192); + + _jetty.setConnectors(new Connector[]{ connector0, connector1}); + + Context root = new Context(_jetty,"/latest",Context.SESSIONS); + root.setResourceBase(_vmDataDir); + root.addServlet(new ServletHolder(new VmDataServlet(this, USER_DATA)), "/user-data"); + root.addServlet(new ServletHolder(new VmDataServlet(this, META_DATA)), "/meta-data"); + + ResourceHandler resource_handler = new ResourceHandler(); + resource_handler.setResourceBase("/var/lib/images/"); + + HandlerList handlers = new HandlerList(); + handlers.setHandlers(new Handler[] { root, resource_handler, new DefaultHandler() }); + _jetty.setHandler(handlers); + + _jetty.start(); + //_jetty.join(); + } + + @Override + public boolean start() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean stop() { + return true; + } + + + @Override + public String getName() { + return "JettyVmDataServer"; + } + + + @Override + public Answer handleVmDataCommand(VmDataCommand cmd) { + String vmDataDir = _vmDataDir + File.separator + cmd.getVmName(); + try { + _fs.cleanup(vmDataDir, _vmDataDir); + _fs.mkdirs(vmDataDir); + } catch (IOException e1) { + s_logger.warn("Failed to cleanup vm data dir " + vmDataDir, e1); + return new Answer(cmd, false, "Failed to cleanup or create directory " + vmDataDir); + } + + + for (String [] item : cmd.getVmData()) { + try { + _fs.create(vmDataDir, item[1]); + String vmDataFile = vmDataDir + File.separator + item[1]; + if (item[2] != null) { + BufferedWriter writer = new BufferedWriter(new FileWriter(vmDataFile)); + writer.write(item[2]); + writer.close(); + } + } catch (IOException e) { + s_logger.warn("Failed to write vm data item " + item[1], e); + return new Answer(cmd, false, "Failed to write vm data item " + item[1]); + } + } + return new Answer(cmd); + + } + + + @Override + public void handleVmStarted(VirtualMachineTO vm) { + for (NicTO nic: vm.getNics()) { + if (nic.getType() == TrafficType.Guest) { + if (nic.getIp() != null) { + String ipv4File = _vmDataDir + File.separator + vm.getName() + File.separator + "local-ipv4"; + try { + _fs.create(_vmDataDir + File.separator + vm.getName(), "local-ipv4"); + BufferedWriter writer = new BufferedWriter(new FileWriter(ipv4File)); + writer.write(nic.getIp()); + _ipVmMap.put(nic.getIp(), vm.getName()); + writer.close(); + } catch (IOException e) { + s_logger.warn("Failed to create or write to local-ipv4 file " + ipv4File,e); + } + + + } + + } + } + } + + + @Override + public void handleVmStopped(String vmName) { + String vmDataDir = _vmDataDir + File.separator + vmName; + try { + _fs.cleanup(vmDataDir, _vmDataDir); + _fs.mkdirs(vmDataDir); + } catch (IOException e1) { + s_logger.warn("Failed to cleanup vm data dir " + vmDataDir, e1); + } + + } + +} diff --git a/agent/src/com/cloud/agent/vmdata/VmDataServer.java b/agent/src/com/cloud/agent/vmdata/VmDataServer.java new file mode 100644 index 00000000000..ec70bd707d7 --- /dev/null +++ b/agent/src/com/cloud/agent/vmdata/VmDataServer.java @@ -0,0 +1,38 @@ +/** + * Copyright (C) 2011 Cloud.com. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later +version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.agent.vmdata; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.routing.VmDataCommand; +import com.cloud.agent.api.to.VirtualMachineTO; +import com.cloud.utils.component.Manager; + +/** + * Maintains vm data (user data, meta-data, password) that can be fetched via HTTP + * by user vms + * + */ +public interface VmDataServer extends Manager { + + public Answer handleVmDataCommand(VmDataCommand cmd); + + public void handleVmStarted(VirtualMachineTO vm); + + public void handleVmStopped(String vmName); +} diff --git a/api/src/com/cloud/agent/api/StartAnswer.java b/api/src/com/cloud/agent/api/StartAnswer.java index 9ed07ca463f..97232f6927c 100644 --- a/api/src/com/cloud/agent/api/StartAnswer.java +++ b/api/src/com/cloud/agent/api/StartAnswer.java @@ -21,19 +21,30 @@ */ package com.cloud.agent.api; +import com.cloud.agent.api.to.VirtualMachineTO; + public class StartAnswer extends Answer { + VirtualMachineTO vm; + protected StartAnswer() { } public StartAnswer(StartCommand cmd, String msg) { super(cmd, false, msg); + this.vm = cmd.getVirtualMachine(); } public StartAnswer(StartCommand cmd, Exception e) { super(cmd, false, e.getMessage()); + this.vm = cmd.getVirtualMachine(); } public StartAnswer(StartCommand cmd) { super(cmd, true, null); + this.vm = cmd.getVirtualMachine(); + } + + public VirtualMachineTO getVirtualMachine() { + return vm; } } diff --git a/api/src/com/cloud/agent/api/StartupCommand.java b/api/src/com/cloud/agent/api/StartupCommand.java index 0fbbc05ef7d..b52dcbada86 100755 --- a/api/src/com/cloud/agent/api/StartupCommand.java +++ b/api/src/com/cloud/agent/api/StartupCommand.java @@ -63,7 +63,7 @@ public class StartupCommand extends Command { public StartupCommand(Long id, Host.Type type, String name, String dataCenter, String pod, String guid, String version, String gatewayIpAddress) { this(id, type, name, dataCenter, pod, guid, version); this.gatewayIpAddress = gatewayIpAddress; - } + } public Host.Type getHostType() { return type; @@ -278,6 +278,7 @@ public class StartupCommand extends Command { public void setGatewayIpAddress(String gatewayIpAddress) { this.gatewayIpAddress = gatewayIpAddress; } + @Override public boolean executeInSequence() { diff --git a/api/src/com/cloud/agent/api/routing/VmDataCommand.java b/api/src/com/cloud/agent/api/routing/VmDataCommand.java index 3d222c3fbc8..762c7f7d393 100644 --- a/api/src/com/cloud/agent/api/routing/VmDataCommand.java +++ b/api/src/com/cloud/agent/api/routing/VmDataCommand.java @@ -24,6 +24,7 @@ import java.util.List; public class VmDataCommand extends NetworkElementCommand { String vmIpAddress; + String vmName; List vmData; protected VmDataCommand() { @@ -35,9 +36,19 @@ public class VmDataCommand extends NetworkElementCommand { } public VmDataCommand(String vmIpAddress) { - this.vmIpAddress = vmIpAddress; - this.vmData = new ArrayList(); + this(vmIpAddress, null); } + + public String getVmName() { + return vmName; + } + + public VmDataCommand(String vmIpAddress, String vmName) { + this.vmName = vmName; + this.vmIpAddress = vmIpAddress; + this.vmData = new ArrayList(); + } + public String getVmIpAddress() { return vmIpAddress; diff --git a/api/src/com/cloud/api/ApiConstants.java b/api/src/com/cloud/api/ApiConstants.java index e4de439beae..0414a9855e5 100755 --- a/api/src/com/cloud/api/ApiConstants.java +++ b/api/src/com/cloud/api/ApiConstants.java @@ -221,7 +221,7 @@ public class ApiConstants { public static final String PING_STORAGE_SERVER_IP = "pingstorageserverip"; public static final String PING_DIR = "pingdir"; public static final String TFTP_DIR = "tftpdir"; - public static final String PING_CIFS_USERNAME = "pingcifsusername"; + public static final String PZING_CIFS_USERNAME = "pingcifsusername"; public static final String PING_CIFS_PASSWORD = "pingcifspassword"; public static final String CHECKSUM="checksum"; public static final String NETWORK_DEVICE_TYPE = "networkdevicetype"; diff --git a/api/src/com/cloud/user/AccountService.java b/api/src/com/cloud/user/AccountService.java index ff7c432d562..e0c0e58f6dd 100644 --- a/api/src/com/cloud/user/AccountService.java +++ b/api/src/com/cloud/user/AccountService.java @@ -1,181 +1,181 @@ -/** - * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. - * - * This software is licensed under the GNU General Public License v3 or later. - * - * It is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -package com.cloud.user; - -import java.util.List; - -import com.cloud.api.commands.CreateAccountCmd; -import com.cloud.api.commands.CreateUserCmd; -import com.cloud.api.commands.DeleteAccountCmd; -import com.cloud.api.commands.DeleteUserCmd; -import com.cloud.api.commands.DisableAccountCmd; -import com.cloud.api.commands.DisableUserCmd; -import com.cloud.api.commands.EnableAccountCmd; -import com.cloud.api.commands.EnableUserCmd; -import com.cloud.api.commands.ListResourceLimitsCmd; -import com.cloud.api.commands.LockUserCmd; -import com.cloud.api.commands.UpdateAccountCmd; -import com.cloud.api.commands.UpdateResourceLimitCmd; -import com.cloud.api.commands.UpdateUserCmd; -import com.cloud.configuration.ResourceLimit; -import com.cloud.domain.Domain; -import com.cloud.exception.ConcurrentOperationException; -import com.cloud.exception.ResourceUnavailableException; -import com.cloud.utils.Pair; - -public interface AccountService { - - /** - * Creates a new user, stores the password as is so encrypted passwords are recommended. - * - * @param cmd - * the create command that has the username, email, password, account name, domain, timezone, etc. for creating - * the user. - * @return the user if created successfully, null otherwise - */ - UserAccount createAccount(CreateAccountCmd cmd); - - /** - * Deletes a user by userId - * - * @param cmd - * - the delete command defining the id of the user to be deleted. - * @return true if delete was successful, false otherwise - */ - boolean deleteUserAccount(DeleteAccountCmd cmd); - - /** - * Disables a user by userId - * - * @param cmd - * the command wrapping the userId parameter - * @return UserAccount object - */ - UserAccount disableUser(DisableUserCmd cmd); - - /** - * Enables a user - * - * @param cmd - * - the command containing userId - * @return UserAccount object - */ - UserAccount enableUser(EnableUserCmd cmd); - - /** - * Locks a user by userId. A locked user cannot access the API, but will still have running VMs/IP addresses allocated/etc. - * - * @param userId - * @return UserAccount object - */ - UserAccount lockUser(LockUserCmd cmd); - - /** - * Update a user by userId - * - * @param userId - * @return UserAccount object - */ - UserAccount updateUser(UpdateUserCmd cmd); - - /** - * Disables an account by accountName and domainId - * - * @param disabled - * account if success - * @return true if disable was successful, false otherwise - */ - Account disableAccount(DisableAccountCmd cmd) throws ConcurrentOperationException, ResourceUnavailableException; - - /** - * Enables an account by accountId - * - * @param cmd - * - the enableAccount command defining the accountId to be deleted. - * @return account object - */ - Account enableAccount(EnableAccountCmd cmd); - - /** - * Locks an account by accountId. A locked account cannot access the API, but will still have running VMs/IP addresses - * allocated/etc. - * - * @param cmd - * - the LockAccount command defining the accountId to be locked. - * @return account object - */ - Account lockAccount(DisableAccountCmd cmd); - - /** - * Updates an account name - * - * @param cmd - * - the parameter containing accountId - * @return updated account object - */ - - Account updateAccount(UpdateAccountCmd cmd); - - /** - * Updates an existing resource limit with the specified details. If a limit doesn't exist, will create one. - * - * @param cmd - * the command that wraps the domainId, accountId, type, and max parameters - * @return the updated/created resource limit - */ - ResourceLimit updateResourceLimit(UpdateResourceLimitCmd cmd); - - /** - * Search for resource limits for the given id and/or account and/or type and/or domain. - * - * @param cmd - * the command wrapping the id, type, account, and domain - * @return a list of limits that match the criteria - */ - List searchForLimits(ListResourceLimitsCmd cmd); - - Account getSystemAccount(); - - User getSystemUser(); - - User createUser(CreateUserCmd cmd); - boolean deleteUser(DeleteUserCmd deleteUserCmd); - - boolean isAdmin(short accountType); - - Account finalizeOwner(Account caller, String accountName, Long domainId); - - Pair finalizeAccountDomainForList(Account caller, String accountName, Long domainId); - - Account getActiveAccount(String accountName, Long domainId); - - Account getActiveAccount(Long accountId); - - Account getAccount(Long accountId); - - User getActiveUser(long userId); - - Domain getDomain(long id); - - boolean isRootAdmin(short accountType); - - User getActiveUserByRegistrationToken(String registrationToken); - - void markUserRegistered(long userId); - -} +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.user; + +import java.util.List; + +import com.cloud.api.commands.CreateAccountCmd; +import com.cloud.api.commands.CreateUserCmd; +import com.cloud.api.commands.DeleteAccountCmd; +import com.cloud.api.commands.DeleteUserCmd; +import com.cloud.api.commands.DisableAccountCmd; +import com.cloud.api.commands.DisableUserCmd; +import com.cloud.api.commands.EnableAccountCmd; +import com.cloud.api.commands.EnableUserCmd; +import com.cloud.api.commands.ListResourceLimitsCmd; +import com.cloud.api.commands.LockUserCmd; +import com.cloud.api.commands.UpdateAccountCmd; +import com.cloud.api.commands.UpdateResourceLimitCmd; +import com.cloud.api.commands.UpdateUserCmd; +import com.cloud.configuration.ResourceLimit; +import com.cloud.domain.Domain; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.utils.Pair; + +public interface AccountService { + + /** + * Creates a new user, stores the password as is so encrypted passwords are recommended. + * + * @param cmd + * the create command that has the username, email, password, account name, domain, timezone, etc. for creating + * the user. + * @return the user if created successfully, null otherwise + */ + UserAccount createAccount(CreateAccountCmd cmd); + + /** + * Deletes a user by userId + * + * @param cmd + * - the delete command defining the id of the user to be deleted. + * @return true if delete was successful, false otherwise + */ + boolean deleteUserAccount(DeleteAccountCmd cmd); + + /** + * Disables a user by userId + * + * @param cmd + * the command wrapping the userId parameter + * @return UserAccount object + */ + UserAccount disableUser(DisableUserCmd cmd); + + /** + * Enables a user + * + * @param cmd + * - the command containing userId + * @return UserAccount object + */ + UserAccount enableUser(EnableUserCmd cmd); + + /** + * Locks a user by userId. A locked user cannot access the API, but will still have running VMs/IP addresses allocated/etc. + * + * @param userId + * @return UserAccount object + */ + UserAccount lockUser(LockUserCmd cmd); + + /** + * Update a user by userId + * + * @param userId + * @return UserAccount object + */ + UserAccount updateUser(UpdateUserCmd cmd); + + /** + * Disables an account by accountName and domainId + * + * @param disabled + * account if success + * @return true if disable was successful, false otherwise + */ + Account disableAccount(DisableAccountCmd cmd) throws ConcurrentOperationException, ResourceUnavailableException; + + /** + * Enables an account by accountId + * + * @param cmd + * - the enableAccount command defining the accountId to be deleted. + * @return account object + */ + Account enableAccount(EnableAccountCmd cmd); + + /** + * Locks an account by accountId. A locked account cannot access the API, but will still have running VMs/IP addresses + * allocated/etc. + * + * @param cmd + * - the LockAccount command defining the accountId to be locked. + * @return account object + */ + Account lockAccount(DisableAccountCmd cmd); + + /** + * Updates an account name + * + * @param cmd + * - the parameter containing accountId + * @return updated account object + */ + + Account updateAccount(UpdateAccountCmd cmd); + + /** + * Updates an existing resource limit with the specified details. If a limit doesn't exist, will create one. + * + * @param cmd + * the command that wraps the domainId, accountId, type, and max parameters + * @return the updated/created resource limit + */ + ResourceLimit updateResourceLimit(UpdateResourceLimitCmd cmd); + + /** + * Search for resource limits for the given id and/or account and/or type and/or domain. + * + * @param cmd + * the command wrapping the id, type, account, and domain + * @return a list of limits that match the criteria + */ + List searchForLimits(ListResourceLimitsCmd cmd); + + Account getSystemAccount(); + + User getSystemUser(); + + User createUser(CreateUserCmd cmd); + boolean deleteUser(DeleteUserCmd deleteUserCmd); + + boolean isAdmin(short accountType); + + Account finalizeOwner(Account caller, String accountName, Long domainId); + + Pair finalizeAccountDomainForList(Account caller, String accountName, Long domainId); + + Account getActiveAccount(String accountName, Long domainId); + + Account getActiveAccount(Long accountId); + + Account getAccount(Long accountId); + + User getActiveUser(long userId); + + Domain getDomain(long id); + + boolean isRootAdmin(short accountType); + + User getActiveUserByRegistrationToken(String registrationToken); + + void markUserRegistered(long userId); + +} diff --git a/build/build-cloud.xml b/build/build-cloud.xml index 321b4ebc415..6964445914a 100755 --- a/build/build-cloud.xml +++ b/build/build-cloud.xml @@ -443,6 +443,7 @@ + diff --git a/build/package.xml b/build/package.xml index dc2e77c2867..aacac8a544d 100755 --- a/build/package.xml +++ b/build/package.xml @@ -70,6 +70,8 @@ + + diff --git a/client/tomcatconf/components.xml.in b/client/tomcatconf/components.xml.in index 006a2190551..17a6d3d3c54 100755 --- a/client/tomcatconf/components.xml.in +++ b/client/tomcatconf/components.xml.in @@ -93,6 +93,7 @@ + diff --git a/cloud.spec b/cloud.spec index 91cb99d5d7d..0a903e5bd88 100644 --- a/cloud.spec +++ b/cloud.spec @@ -221,6 +221,7 @@ Requires: jpackage-utils Requires: %{name}-daemonize Requires: /sbin/service Requires: /sbin/chkconfig +Requires: jnetpcap Group: System Environment/Libraries %package baremetal-agent @@ -298,6 +299,13 @@ The Cloud.com command line tools contain a few Python modules that can call clou %if %{_premium} +%package premium-agent +Summary: Cloud.com premium agent +Requires: cloud-agent +Group: System Environment/Libraries +%description premium-agent +The Cloud.com premium agent + %package test Summary: Cloud.com test suite Requires: java >= 1.6.0 @@ -507,6 +515,8 @@ fi %{_javadir}/%{name}-xmlrpc-common-3.*.jar %{_javadir}/%{name}-xmlrpc-client-3.*.jar %{_javadir}/%{name}-jstl-1.2.jar +%{_javadir}/jetty-6.1.26.jar +%{_javadir}/jetty-util-6.1.26.jar %{_javadir}/%{name}-axis.jar %{_javadir}/%{name}-commons-discovery.jar @@ -586,6 +596,9 @@ fi %attr(0755,root,root) %{_bindir}/%{name}-setup-agent %dir %attr(0770,root,root) %{_localstatedir}/log/%{name}/agent +%files premium-agent +%{_javadir}/cloud-agent-extras.jar +%attr(0755,root,root) %{_bindir}/mycloud-setup-agent %files console-proxy %defattr(0644,root,root,0755) diff --git a/core/src/com/cloud/agent/StartupCommandProcessor.java b/core/src/com/cloud/agent/StartupCommandProcessor.java new file mode 100644 index 00000000000..3d316254045 --- /dev/null +++ b/core/src/com/cloud/agent/StartupCommandProcessor.java @@ -0,0 +1,41 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.agent; + +import com.cloud.agent.api.StartupCommand; +import com.cloud.exception.ConnectionException; +import com.cloud.utils.component.Adapter; + +/** + * HostCreator hooks into the AgentManager to be notified of agent connections before the + * AgentManager knows about the agent that's connecting. + */ +public interface StartupCommandProcessor extends Adapter { + + /** + * This method is called by AgentManager when an agent made a + * connection to this server before the AgentManager knows about this agent + * @param agentId id of the agent + * @param cmd command sent by the agent to the server on startup. + * @return true if handled by the creator + * @throws ConnectionException if host has problems + */ + boolean processInitialConnect(StartupCommand[] cmd) throws ConnectionException; + + +} diff --git a/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java b/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java index 314f26c0064..17b9ddebc81 100755 --- a/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java +++ b/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java @@ -515,14 +515,39 @@ public class VirtualRoutingResource implements Manager { } public void cleanupPrivateNetwork(String privNwName, String privBrName){ - if (isDNSmasqRunning(privNwName)) { - stopDnsmasq(privNwName); - } if (isBridgeExists(privBrName)) { deleteBridge(privBrName); } - - + } + + private void deletExitingLinkLocalRoutTable(String linkLocalBr) { + Script command = new Script("/bin/bash", _timeout); + command.add("-c"); + command.add("ip route | grep " + NetUtils.getLinkLocalCIDR()); + OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser(); + String result = command.execute(parser); + boolean foundLinkLocalBr = false; + if (result == null && parser.getLines() != null) { + String[] lines = parser.getLines().split("\\n"); + for (String line : lines) { + String[] tokens = line.split(" "); + if (!tokens[2].equalsIgnoreCase(linkLocalBr)) { + Script.runSimpleBashScript("ip route del " + NetUtils.getLinkLocalCIDR()); + } else { + foundLinkLocalBr = true; + } + } + } + if (!foundLinkLocalBr) { + Script.runSimpleBashScript("ip route add " + NetUtils.getLinkLocalCIDR() + " dev " + linkLocalBr + " src " + NetUtils.getLinkLocalGateway()); + } + } + + public void createControlNetwork(String privBrName) { + deletExitingLinkLocalRoutTable(privBrName); + if (!isBridgeExists(privBrName)) { + Script.runSimpleBashScript("brctl addbr " + privBrName + "; ifconfig " + privBrName + " up;", _timeout); + } } // protected Answer execute(final SetFirewallRuleCommand cmd) { diff --git a/core/src/com/cloud/host/HostVO.java b/core/src/com/cloud/host/HostVO.java index 16961299d04..247233340e1 100644 --- a/core/src/com/cloud/host/HostVO.java +++ b/core/src/com/cloud/host/HostVO.java @@ -347,7 +347,8 @@ public class HostVO implements Host { this.guid = guid; this.status = Status.Up; this.totalMemory = 0; - this.dom0MinMemory = 0; + this.dom0MinMemory = 0; + this.hostAllocationState = Host.HostAllocationState.Enabled; } protected HostVO() { diff --git a/core/src/com/cloud/resource/ServerResourceBase.java b/core/src/com/cloud/resource/ServerResourceBase.java index c10da164984..e5a026ca65a 100755 --- a/core/src/com/cloud/resource/ServerResourceBase.java +++ b/core/src/com/cloud/resource/ServerResourceBase.java @@ -61,7 +61,7 @@ public abstract class ServerResourceBase implements ServerResource { protected abstract String getDefaultScriptsDir(); @Override - public boolean configure(final String name, final Map params) throws ConfigurationException { + public boolean configure(final String name, Map params) throws ConfigurationException { _name = name; String publicNic = (String)params.get("public.network.device"); @@ -123,7 +123,10 @@ public abstract class ServerResourceBase implements ServerResource { throw new ConfigurationException("Private NIC is not configured"); } } - + String infos[] = NetUtils.getNetworkParams(_privateNic); + params.put("host.ip", infos[0]); + params.put("host.mac.address", infos[1]); + return true; } diff --git a/core/src/com/cloud/storage/JavaStorageLayer.java b/core/src/com/cloud/storage/JavaStorageLayer.java index d8268c44661..47cf6cd2caa 100644 --- a/core/src/com/cloud/storage/JavaStorageLayer.java +++ b/core/src/com/cloud/storage/JavaStorageLayer.java @@ -32,7 +32,18 @@ import javax.naming.ConfigurationException; public class JavaStorageLayer implements StorageLayer { String _name; + boolean _makeWorldWriteable = true; + + public JavaStorageLayer() { + super(); + } + + public JavaStorageLayer(boolean makeWorldWriteable) { + this(); + this._makeWorldWriteable = makeWorldWriteable; + } + @Override public boolean cleanup(String path, String rootPath) throws IOException { assert path.startsWith(rootPath) : path + " does not start with " + rootPath; @@ -121,8 +132,11 @@ public class JavaStorageLayer implements StorageLayer { if (file.exists()) { return file.isDirectory(); } - - return (file.mkdirs() && setWorldReadableAndWriteable(file)); + if (_makeWorldWriteable) { + return (file.mkdirs() && setWorldReadableAndWriteable(file)); + } else { + return file.mkdirs(); + } } } @@ -146,7 +160,9 @@ public class JavaStorageLayer implements StorageLayer { for (String dirPath : dirPaths) { dir = new File(dirPath); if (!dir.exists()) { - success = dir.mkdir() && setWorldReadableAndWriteable(dir); + success = dir.mkdir(); + if (_makeWorldWriteable) + success = success && setWorldReadableAndWriteable(dir); } } diff --git a/server/src/com/cloud/storage/secondary/LocalSecondaryStorageResource.java b/core/src/com/cloud/storage/resource/LocalSecondaryStorageResource.java similarity index 98% rename from server/src/com/cloud/storage/secondary/LocalSecondaryStorageResource.java rename to core/src/com/cloud/storage/resource/LocalSecondaryStorageResource.java index 7a73d62007d..a19c2de700a 100644 --- a/server/src/com/cloud/storage/secondary/LocalSecondaryStorageResource.java +++ b/core/src/com/cloud/storage/resource/LocalSecondaryStorageResource.java @@ -15,7 +15,7 @@ * along with this program. If not, see . * */ -package com.cloud.storage.secondary; +package com.cloud.storage.resource; import java.util.HashMap; import java.util.Map; @@ -161,7 +161,7 @@ public class LocalSecondaryStorageResource extends ServerResourceBase implements final StartupStorageCommand cmd = new StartupStorageCommand(_parent, StoragePoolType.Filesystem, 1024l*1024l*1024l*1024l, _dlMgr.gatherTemplateInfo()); cmd.setResourceType(Storage.StorageResourceType.SECONDARY_STORAGE); - cmd.setIqn(null); + cmd.setIqn("local://"); fillNetworkInformation(cmd); cmd.setDataCenter(_dc); cmd.setPod(_pod); diff --git a/debian/cloud-agent-premium.config b/debian/cloud-agent-premium.config new file mode 100644 index 00000000000..e69de29bb2d diff --git a/debian/cloud-agent-premium.install b/debian/cloud-agent-premium.install new file mode 100644 index 00000000000..2b009c14826 --- /dev/null +++ b/debian/cloud-agent-premium.install @@ -0,0 +1,2 @@ +/usr/share/java/cloud-agent-extras.jar +/usr/bin/mycloud-setup-agent diff --git a/debian/cloud-deps.install b/debian/cloud-deps.install index 223bc3d199d..0487311e873 100644 --- a/debian/cloud-deps.install +++ b/debian/cloud-deps.install @@ -4,6 +4,8 @@ /usr/share/java/cloud-ehcache.jar /usr/share/java/cloud-email.jar /usr/share/java/cloud-gson.jar +/usr/share/java/jetty-6.1.26.jar +/usr/share/java/jetty-util-6.1.26.jar /usr/share/java/cloud-httpcore-4.0.jar /usr/share/java/cloud-jna.jar /usr/share/java/cloud-libvirt-0.4.5.jar diff --git a/debian/control b/debian/control index 04f89d07f5e..53509d74877 100644 --- a/debian/control +++ b/debian/control @@ -141,12 +141,21 @@ Provides: vmops-agent Conflicts: vmops-agent Replaces: vmops-agent Architecture: any -Depends: openjdk-6-jre, cloud-utils (= ${source:Version}), cloud-core (= ${source:Version}), cloud-deps (= ${source:Version}), python, cloud-python (= ${source:Version}), cloud-agent-libs (= ${source:Version}), cloud-agent-scripts (= ${source:Version}), libcommons-httpclient-java, libcommons-collections-java, libcommons-dbcp-java, libcommons-pool-java, libcommons-logging-java, libvirt0, cloud-daemonize, sysvinit-utils, chkconfig, qemu-kvm, libvirt-bin, cgroup-bin, augeas-tools, uuid-runtime, rsync, grep, iproute, vlan +Depends: openjdk-6-jre, cloud-utils (= ${source:Version}), cloud-core (= ${source:Version}), cloud-deps (= ${source:Version}), python, cloud-python (= ${source:Version}), cloud-agent-libs (= ${source:Version}), cloud-agent-scripts (= ${source:Version}), libcommons-httpclient-java, libcommons-collections-java, libcommons-dbcp-java, libcommons-pool-java, libcommons-logging-java, libvirt0, cloud-daemonize, sysvinit-utils, chkconfig, qemu-kvm, libvirt-bin, cgroup-bin, augeas-tools, uuid-runtime, rsync, grep, iproute, jnetpcap, ebtables Description: Cloud.com agent The Cloud.com agent is in charge of managing shared computing resources in a Cloud.com Cloud Stack-powered cloud. Install this package if this computer will participate in your cloud. +Package: cloud-agent-premium +Architecture: any +Depends: cloud-agent +Description: Cloud.com agent premium + The Cloud.com agent is in charge of managing shared computing resources in + a Cloud.com Cloud Stack-powered cloud. Install this package if this computer + will participate in your cloud. + + Package: cloud-console-proxy Architecture: any Depends: openjdk-6-jre, cloud-utils (= ${source:Version}), cloud-core (= ${source:Version}), cloud-deps (= ${source:Version}), cloud-agent-libs (= ${source:Version}), python, cloud-python (= ${source:Version}), libcommons-httpclient-java, libcommons-collections-java, libcommons-dbcp-java, libcommons-pool-java, libcommons-logging-java, cloud-daemonize, sysvinit-utils, chkconfig, augeas-tools, uuid-runtime, grep, iproute diff --git a/deps/.classpath b/deps/.classpath index 47b98081a11..12d1be68561 100644 --- a/deps/.classpath +++ b/deps/.classpath @@ -49,5 +49,8 @@ + + + diff --git a/deps/jetty-6.1.26.jar b/deps/jetty-6.1.26.jar new file mode 100644 index 00000000000..2cbe07aeefa Binary files /dev/null and b/deps/jetty-6.1.26.jar differ diff --git a/deps/jetty-util-6.1.26.jar b/deps/jetty-util-6.1.26.jar new file mode 100644 index 00000000000..cd237528add Binary files /dev/null and b/deps/jetty-util-6.1.26.jar differ diff --git a/python/bindir/mycloud-setup-agent b/python/bindir/mycloud-setup-agent new file mode 100755 index 00000000000..1dc53388ffb --- /dev/null +++ b/python/bindir/mycloud-setup-agent @@ -0,0 +1,866 @@ +#!/usr/bin/python +from subprocess import PIPE, Popen +from signal import alarm, signal, SIGALRM, SIGKILL +import tempfile +import shutil +import os +import logging +import sys +import re +import traceback +import socket +import uuid + +class CloudRuntimeException(Exception): + def __init__(self, errMsg): + self.errMsg = errMsg + def __str__(self): + return self.errMsg + +def formatExceptionInfo(maxTBlevel=5): + cla, exc, trbk = sys.exc_info() + excTb = traceback.format_tb(trbk, maxTBlevel) + msg = str(exc) + "\n" + for tb in excTb: + msg += tb + return msg +class bash: + def __init__(self, args, timeout=600): + self.args = args + logging.debug("execute:%s"%args) + self.timeout = timeout + self.process = None + self.success = False + self.run() + + def run(self): + class Alarm(Exception): + pass + def alarm_handler(signum, frame): + raise Alarm + + try: + self.process = Popen(self.args, shell=True, stdout=PIPE, stderr=PIPE) + if self.timeout != -1: + signal(SIGALRM, alarm_handler) + alarm(self.timeout) + + try: + self.stdout, self.stderr = self.process.communicate() + if self.timeout != -1: + alarm(0) + except Alarm: + os.kill(self.process.pid, SIGKILL) + raise CloudRuntimeException("Timeout during command execution") + + self.success = self.process.returncode == 0 + except: + raise CloudRuntimeException(formatExceptionInfo()) + + if not self.success: + raise CloudRuntimeException(self.getStderr()) + + def isSuccess(self): + return self.success + + def getStdout(self): + return self.stdout.strip("\n") + + def getLines(self): + return self.stdout.split("\n") + + def getStderr(self): + return self.stderr.strip("\n") + + + +def initLoging(logFile=None): + try: + if logFile is None: + logging.basicConfig(level=logging.DEBUG) + else: + logging.basicConfig(filename=logFile, level=logging.DEBUG) + except: + logging.basicConfig(level=logging.DEBUG) + +class configFileOps(): + class entry(): + def __init__(self, name, value): + self.name = name + self.value = value + self.state = "new" + def setState(self, state): + self.state = state + def getState(self): + return self.state + + def __init__(self, fileName): + self.fileName = fileName + self.entries = [] + self.backups = [] + + def addEntry(self, name, value): + e = self.entry(name, value) + self.entries.append(e) + + def getEntry(self, name): + try: + ctx = file(self.fileName).read(-1) + match = re.search("^" + name + ".*", ctx, re.MULTILINE) + if match is None: + return "" + line = match.group(0).split("=") + return line[1] + except: + return "" + + def save(self): + fh, abs_path = tempfile.mkstemp() + new_file = open(abs_path, "w") + + fp = open(self.fileName, "r") + + for line in fp.readlines(): + for entry in self.entries: + if line.startswith(entry.name): + line = entry.name + "=" + entry.value + "\n" + entry.setState("set") + new_file.write(line) + + for entry in self.entries: + if entry.getState() != "set": + new_file.write("\n" + entry.name + "=" + entry.value + "\n") + + new_file.close() + fp.close() + + shutil.move(abs_path, self.fileName) + + def replace_line(self, startswith,stanza,always_add=False): + lines = [ s.strip() for s in file(self.fileName).readlines() ] + newlines = [] + replaced = False + for line in lines: + if re.search(startswith, line): + if stanza is not None: + newlines.append(stanza) + self.backups.append([line, stanza]) + replaced = True + else: newlines.append(line) + if not replaced and always_add: + newlines.append(stanza) + self.backups.append([None, stanza]) + newlines = [ s + '\n' for s in newlines ] + file(self.fileName,"w").writelines(newlines) + + def replace_or_add_line(self, startswith,stanza): + return self.replace_line(startswith,stanza,always_add=True) + + def add_lines(self, lines): + fp = file(self.fileName).read(-1) + sh = re.escape(lines) + match = re.search(sh, fp, re.MULTILINE) + if match is not None: + return + + fp += lines + file(self.fileName, "w").write(fp) + self.backups.append([None, lines]) + + def replace_lines(self, src, dst, addToBackup=True): + fp = file(self.fileName).read(-1) + sh = re.escape(src) + if dst is None: + dst = "" + repl,nums = re.subn(sh, dst, fp) + if nums <=0: + return + file(self.fileName, "w").write(repl) + if addToBackup: + self.backups.append([src, dst]) + + def backup(self): + for oldLine, newLine in self.backups: + self.replace_lines(newLine, oldLine, False) + +class networkConfig(): + class devInfo(): + def __init__(self, macAddr, ipAddr, netmask, gateway, type, name): + self.name = name + self.macAdrr = macAddr + self.ipAddr = ipAddr + self.netmask = netmask + self.gateway = gateway + self.type = type + self.name = name + #dhcp or static + self.method = None + + @staticmethod + def getDefaultNetwork(): + cmd = bash("route -n|awk \'/^0.0.0.0/ {print $2,$8}\'") + if not cmd.isSuccess(): + logging.debug("Failed to get default route") + return None + + result = cmd.getStdout().split(" ") + gateway = result[0] + dev = result[1] + + pdi = networkConfig.getDevInfo(dev) + logging.debug("Found default network device:%s"%pdi.name) + pdi.gateway = gateway + return pdi + + @staticmethod + def createBridge(dev, brName): + if not networkConfig.isBridgeSupported(): + logging.debug("bridge is not supported") + return False + if networkConfig.isBridgeEnslavedWithDevices(brName): + logging.debug("bridge: %s has devices enslaved"%brName) + return False + + cmds = "" + if not networkConfig.isBridge(brName): + cmds = "brctl addbr %s ;"%brName + + cmds += "ifconfig %s up;"%brName + cmds += "brctl addif %s %s"%(brName, dev) + return bash(cmds).isSuccess() + + @staticmethod + def isBridgeEnslavedWithDevices(brName): + if not networkConfig.isBridge(brName): + return False + + if not os.listdir("/sys/class/net/%s/brif"%brName): + return False + + return True + + @staticmethod + def isBridgeSupported(): + if os.path.exists("/proc/sys/net/bridge"): + return True + + return bash("modprobe bridge").isSucess() + + @staticmethod + def isNetworkDev(devName): + return os.path.exists("/sys/class/net/%s"%devName) + + @staticmethod + def isBridgePort(devName): + return os.path.exists("/sys/class/net/%s/brport"%devName) + + @staticmethod + def isBridge(devName): + return os.path.exists("/sys/class/net/%s/bridge"%devName) + + @staticmethod + def getBridge(devName): + bridgeName = None + if os.path.exists("/sys/class/net/%s/brport/bridge"%devName): + realPath = os.path.realpath("/sys/class/net/%s/brport/bridge"%devName) + bridgeName = realPath.split("/")[-1] + return bridgeName + + @staticmethod + def getEnslavedDev(br, brPort): + if not networkConfig.isBridgeEnslavedWithDevices(br): + return None + + for dev in os.listdir("/sys/class/net/%s/brif"%br): + br_port = int(file("/sys/class/net/%s/brif/%s/port_no"%(br,dev)).readline().strip("\n"), 16) + if br_port == brPort: + return dev + + return None + + @staticmethod + def getDevInfo(dev): + if not networkConfig.isNetworkDev(dev): + logging.debug("dev: " + dev + " is not a network device") + return None + + netmask = None + ipAddr = None + macAddr = None + + cmd = bash("ifconfig " + dev) + if not cmd.isSuccess(): + logging.debug("Failed to get address from ifconfig") + return None + + for line in cmd.getLines(): + if line.find("HWaddr") != -1: + macAddr = line.split("HWaddr ")[1].strip(" ") + elif line.find("inet ") != -1: + m = re.search("addr:(.*)\ *Bcast:(.*)\ *Mask:(.*)", line) + if m is not None: + ipAddr = m.group(1).rstrip(" ") + netmask = m.group(3).rstrip(" ") + + if networkConfig.isBridgePort(dev): + type = "brport" + elif networkConfig.isBridge(dev): + type = "bridge" + else: + type = "dev" + + return networkConfig.devInfo(macAddr, ipAddr, netmask, None, type, dev) + +class networkConfigUbuntu(networkConfig): + def __init__(self, syscfg): + self.syscfg = syscfg + self.netCfgFile = "/etc/network/interfaces" + self.brName = None + self.dev = None + self.status = None + self.cfoHandlers = [] + + def getNetworkMethod(self, line): + if line.find("static") != -1: + return "static" + elif line.find("dhcp") != -1: + return "dhcp" + else: + logging.debug("Failed to find the network method from:%s"%line) + return None + + def matchEndOfStanzas(self, line): + if line.match("\^ *iface|\^ *mapping|\^ *auto | \^ *allow-") is not None: + return True + else: + return False + + def addBridge(self, br, dev): + bash("ifdown %s"%dev.name) + for line in file(self.netCfgFile).readlines(): + match = re.match("^ *iface %s.*"%dev.name, line) + if match is not None: + dev.method = self.getNetworkMethod(match.group(0)) + bridgeCfg = "\niface %s inet manual\n \ + auto %s\n \ + iface %s inet %s\n \ + bridge_ports %s\n"%(dev.name, br, br, dev.method, dev.name) + cfo = configFileOps(self.netCfgFile) + cfo.replace_line("^ *iface %s.*"%dev.name, bridgeCfg) + self.cfoHandlers.append(cfo) + + def addDev(self, br, dev): + logging.debug("Haven't implement yet") + + def addBridgeAndDev(self, br, dev): + logging.debug("Haven't implement yet") + + def writeToCfgFile(self, br, dev): + cfg = file(self.netCfgFile).read() + ifaceDev = re.search("^ *iface %s.*"%dev.name, cfg, re.MULTILINE) + ifaceBr = re.search("^ *iface %s.*"%br, cfg, re.MULTILINE) + if ifaceDev is not None and ifaceBr is not None: + logging.debug("%s:%s already configured"%(br, dev.name)) + return True + elif ifaceDev is not None and ifaceBr is None: + #reconfig bridge + self.addBridge(br, dev) + elif ifaceDev is None and ifaceBr is not None: + #reconfig dev + self.addDev(br, dev) + else: + #both need to be reconfigured + self.addbridgeAndDev(br, dev) + + def cfgNetwork(self, dev=None, brName=None): + if dev is None: + device = networkConfig.getDefaultNetwork() + else: + device = networkConfig.getDevInfo(dev) + + if device.type == "dev": + #Need to create a bridge on it + if brName is None: + brName = "cloudbr0" + ''' + if not networkConfig.createBridge(device.name, brName): + logging.debug("Failed to create bridge:%s on dev %s"%(brName, device.name)) + return False + ''' + + self.writeToCfgFile(brName, device) + elif device.type == "brport": + brName = networkConfig.getBridge(dev) + brDevice = networkConfig.getDevInfo(brName) + self.writeToCfgFile(brDevice, device) + elif device.type == "bridge": + #Fixme, assuming the outgoing physcial device is on port 1 + enslavedDev = networkConfig.getEnslavedDev(device.name, 1) + brDevice = device + device = networkConfig.getDevInfo(enslavedDev) + brName = brDevice.name + self.writeToCfgFile(brName, device) + + self.brName = brName + self.dev = device.name + + def config(self): + writeProgressBar("Configure Network...", None) + try: + self.cfgNetwork(self.syscfg.env.defaultNic) + self.netMgrRunning = self.syscfg.isServiceRunning("network-manager") + if self.netMgrRunning: + self.syscfg.stopService("network-manager") + self.syscfg.disableService("network-manager") + + bash("ifup %s"%self.brName) + + self.syscfg.env.nics.append(self.brName) + self.syscfg.env.nics.append(self.brName) + self.syscfg.env.nics.append(self.brName) + + writeProgressBar(None, True) + self.status = True + except: + logging.debug(formatExceptionInfo()) + writeProgressBar(None, False) + self.status = False + + def restore(self): + if self.status is None: + return + #restore cfg file at first + writeProgressBar("Restoring Network...", None) + + for cfo in self.cfoHandlers: + cfo.backup() + + try: + if self.netMgrRunning: + self.syscfg.enableService("network-manager") + self.syscfg.startService("network-manager") + + bash("/etc/init.d/networking stop") + bash("/etc/init.d/networking start") + writeProgressBar(None, True) + except: + logging.debug(formatExceptionInfo()) + writeProgressBar(None, False) + +class cgroupConfigUbuntu(): + def __init__(self, syscfg): + self.syscfg = syscfg + self.backup = [] + self.status = None + self.cfoHandlers = [] + + def config(self): + writeProgressBar("Configure cgroup...", None) + + try: + cfo = configFileOps("/etc/cgconfig.conf") + addConfig = "group virt {\n \ + cpu {\n \ + cpu.shares = 9216;\n \ + }\n \ + }\n" + cfo.add_lines(addConfig) + self.cfoHandlers.append(cfo) + + self.syscfg.stopService("cgconfig") + self.syscfg.enableService("cgconfig",forcestart=True) + + cfo = configFileOps("/etc/cgrules.conf") + cfgline = "root:/usr/sbin/libvirtd cpu virt/\n" + cfo.add_lines(cfgline) + self.cfoHandlers.append(cfo) + + self.syscfg.stopService("cgred") + self.syscfg.enableService("cgred") + + writeProgressBar(None, True) + self.status = True + except: + logging.debug(formatExceptionInfo()) + writeProgressBar(None, False) + self.status = False + raise CloudRuntimeException("Failed to configure cgroup, please see the /var/log/cloud/setupAgent.log for detail") + + def restore(self): + if self.status is None: + return + writeProgressBar("Restoring cgroup...", None) + try: + for cfo in self.cfoHandlers: + cfo.backup() + + self.syscfg.stopService("cgconfig") + self.syscfg.enableService("cgconfig",forcestart=True) + self.syscfg.stopService("cgred") + self.syscfg.enableService("cgred") + writeProgressBar(None, True) + except: + writeProgressBar(None, False) + +class securityPolicyConfigUbuntu(): + def __init__(self, syscfg): + self.syscfg = syscfg + self.status = None + + def config(self): + writeProgressBar("Configure Security Policy...", None) + + try: + if bash("service apparmor status").getStdout() == "": + self.spRunning = False + return + bash("service apparmor stop") + bash("update-rc.d -f apparmor remove") + + self.status = True + writeProgressBar(None, True) + except: + logging.debug(formatExceptionInfo()) + self.status = False + writeProgressBar(None, False) + raise CloudRuntimeException("Failed to configure apparmor, please see the /var/log/cloud/setupAgent.log for detail, or you can manually disable it before starting cloudKit") + + + def restore(self): + if self.status is None: + return + writeProgressBar("Restoring Security Policy...", None) + try: + self.syscfg.enableService("apparmor") + self.syscfg.startService("apparmor") + writeProgressBar(None, True) + except: + writeProgressBar(None, False) + + +class libvirtConfigUbuntu(): + def __init__(self, syscfg): + self.syscfg = syscfg + self.status = None + self.cfoHandlers = [] + + def setupLiveMigration(self): + stanzas = ( + "listen_tcp=1", + 'tcp_port="16509"', + 'auth_tcp="none"', + "listen_tls=0", + ) + + cfo = configFileOps("/etc/libvirt/libvirtd.conf") + for stanza in stanzas: + startswith = stanza.split("=")[0] + '=' + cfo.replace_or_add_line(startswith,stanza) + + self.cfoHandlers.append(cfo) + + + if os.path.exists("/etc/init/libvirt-bin.conf"): + cfo = configFileOps("/etc/init/libvirt-bin.conf") + cfo.replace_line("exec /usr/sbin/libvirtd","exec /usr/sbin/libvirtd -d -l") + self.cfoHandlers.append(cfo) + else: + cfo = configFileOps("/etc/default/libvirt-bin") + cfo.replace_or_add_line("libvirtd_opts=","libvirtd_opts='-l -d'") + self.cfoHandlers.append(cfo) + + def config(self): + writeProgressBar("Configure Libvirt...", None) + + try: + cfgline = "export CGROUP_DAEMON='cpu:/virt'" + libvirtfile = "/etc/default/libvirt-bin" + cfo = configFileOps(libvirtfile) + cfo.add_lines(cfgline) + self.cfoHandlers.append(cfo) + + self.setupLiveMigration() + + cfgline = "cgroup_controllers = [ \"cpu\" ]\n" \ + "security_driver = \"none\"\n" + filename = "/etc/libvirt/qemu.conf" + + cfo = configFileOps(filename) + cfo.add_lines(cfgline) + self.cfoHandlers.append(cfo) + + self.syscfg.stopService("libvirt-bin") + self.syscfg.enableService("libvirt-bin") + + writeProgressBar(None, True) + self.status = True + except: + logging.debug(formatExceptionInfo()) + writeProgressBar(None, False) + self.status = False + raise CloudRuntimeException("Failed to configure libvirt, please see the /var/log/cloud/setupAgent.log for detail") + + + def restore(self): + if self.status is None: + return + + writeProgressBar("Restoring Libvirt...", None) + for cfo in self.cfoHandlers: + cfo.backup() + + try: + self.syscfg.stopService("libvirt-bin") + self.syscfg.startService("libvirt-bin") + writeProgressBar(None, True) + except: + logging.debug(formatExceptionInfo()) + writeProgressBar(None, False) + +class firewawllConfigUbuntu(): + def __init__(self, syscfg): + self.syscfg = syscfg + self.status = None + + def config(self): + writeProgressBar("Configure Firewall...", None) + + try: + ports = "22 1798 16509".split() + for p in ports: + bash("ufw allow %s"%p) + bash("ufw allow proto tcp from any to any port 5900:6100") + bash("ufw allow proto tcp from any to any port 49152:49216") + self.syscfg.stopService("ufw") + self.syscfg.startService("ufw") + + writeProgressBar(None, True) + self.status = True + except: + logging.debug(formatExceptionInfo()) + writeProgressBar(None, False) + self.status = False + raise CloudRuntimeException("Failed to configure firewall, please see the /var/log/cloud/setupAgent.log for detail") + def restore(self): + if self.status is None: + return + + print "not implemented yet" + return + +class cloudKitConfig(): + def __init__(self, syscfg): + self.syscfg = syscfg + self.status = None + + def config(self): + writeProgressBar("Configure CloudKit...", None) + + try: + cfo = configFileOps("/etc/cloud/agent/agent.properties") + cfo.addEntry("host", self.syscfg.env.mgtSvr) + cfo.addEntry("zone", self.syscfg.env.zoneToken) + cfo.addEntry("port", "443") + cfo.addEntry("private.network.device", self.syscfg.env.nics[0]) + cfo.addEntry("public.network.device", self.syscfg.env.nics[1]) + cfo.addEntry("guest.network.device", self.syscfg.env.nics[2]) + cfo.addEntry("guid", str(self.syscfg.env.uuid)) + cfo.addEntry("mount.path", "/mnt") + cfo.addEntry("resource", "com.cloud.storage.resource.LocalSecondaryStorageResource|com.cloud.agent.resource.computing.CloudZonesComputingResource") + cfo.save() + + self.syscfg.stopService("cloud-agent") + self.syscfg.startService("cloud-agent") + writeProgressBar(None, True) + self.status = True + except: + logging.debug(formatExceptionInfo()) + writeProgressBar(None, False) + self.status = False + raise CloudRuntimeException("Failed to configure cloudKit, please see the /var/log/cloud/setupAgent.log for detail") + + def restore(self): + pass + +Unknown = 0 +Fedora = 1 +CentOS = 2 +RHEL6 = 3 +RHEL5 = 4 +Ubuntu = 5 + +class DistributionDetector(): + def __init__(self): + self.distro = Unknown + + if os.path.exists("/etc/fedora-release"): + self.distro = Fedora + elif os.path.exists("/etc/centos-release"): + self.distro = CentOS + elif os.path.exists("/etc/redhat-release"): + version = file("/etc/redhat-release").readline() + if version.find("Red Hat Enterprise Linux Server release 6") != -1: + self.distro = RHEL6 + elif version.find("CentOS release") != -1: + self.distro = CentOS + else: + self.distro = RHEL5 + elif os.path.exists("/etc/legal") and "Ubuntu" in file("/etc/legal").read(-1): + self.distro = Ubuntu + else: + self.distro = Unknown + + def getVersion(self): + return self.distro + +class sysConfig(object): + @staticmethod + def getSysConfigFactory(glbEnv): + distribution = DistributionDetector().getVersion() + if distribution == Ubuntu: + return sysConfigUbuntu(glbEnv) + else: + return sysConfig() + + def config(self): + pass + + def restore(self): + pass + +class sysConfigUbuntu(sysConfig): + def __init__(self, glbEnv): + self.env = glbEnv + self.services = [cgroupConfigUbuntu(self), + securityPolicyConfigUbuntu(self), + networkConfigUbuntu(self), + libvirtConfigUbuntu(self), + firewawllConfigUbuntu(self), + cloudKitConfig(self)] + + def config(self): + if not self.check(): + return False + for service in self.services: + service.config() + + + def restore(self): + for service in self.services: + service.restore() + + def check(self): + try: + return self.isKVMEnabled() + except: + raise CloudRuntimeException("Checking KVM...[Failed]\nPlease enable KVM on this machine\n") + + def isServiceRunning(self, servicename): + try: + o = bash("service " + servicename + " status") + if "start/running" in o.getStdout(): + return True + else: + return False + except: + return False + + def stopService(self, servicename,force=False): + if self.isServiceRunning(servicename) or force: + return bash("service " + servicename +" stop").isSuccess() + + def disableService(self, servicename): + self.stopService(servicename) + bash("update-rc.d -f " + servicename + " remove") + + def startService(self, servicename,force=False): + if not self.isServiceRunning(servicename) or force: + bash("service " + servicename + " start") + + def enableService(self, servicename,forcestart=False): + bash("update-rc.d -f " + servicename + " remove") + bash("update-rc.d -f " + servicename + " start 2 3 4 5 .") + self.startService(servicename,force=forcestart) + + def isKVMEnabled(self): + return bash("kvm-ok").isSuccess() + +def getUserInputs(): + print "Welcome to CloudKit Setup:" + + cfo = configFileOps("/etc/cloud/agent/agent.properties") + oldMgt = cfo.getEntry("host") + + mgtSvr = raw_input("Please input the Management Server Name/IP:[%s]"%oldMgt) + if mgtSvr == "": + mgtSvr = oldMgt + try: + socket.getaddrinfo(mgtSvr, 443) + except: + print "Failed to resolve %s. Please input correct server name or IP."%mgtSvr + exit(1) + + oldToken = cfo.getEntry("zone") + zoneToken = raw_input("Please input the Zone Token:[%s]"%oldToken) + + if zoneToken == "": + zoneToken = oldToken + + try: + defaultNic = networkConfig.getDefaultNetwork() + except: + print "Failed to get default route. Please configure your network to have a default route" + exit(1) + + defNic = defaultNic.name + network = raw_input("Please choose which network used to create VM:[%s]"%defNic) + if network == "": + if defNic == "": + print "You need to specifiy one of Nic or bridge on your system" + exit(1) + elif network == "": + network = defNic + + return [mgtSvr,zoneToken, network] + +def writeProgressBar(msg, result): + if msg is not None: + output = "%-30s"%msg + elif result is True: + output = "[%-2s]\n"%"OK" + elif result is False: + output = "[%-6s]\n"%"Failed" + sys.stdout.write(output) + sys.stdout.flush() + +class globalEnv(): + pass + +if __name__ == '__main__': + #todo: check executing permission + initLoging("/var/log/cloud/setupAgent.log") + + userInputs = getUserInputs() + glbEnv = globalEnv() + + glbEnv.mgtSvr = userInputs[0] + glbEnv.zoneToken = userInputs[1] + glbEnv.defaultNic = userInputs[2] + glbEnv.nics = [] + #generate UUID + glbEnv.uuid = configFileOps("/etc/cloud/agent/agent.properties").getEntry("guid") + if glbEnv.uuid == "": + glbEnv.uuid = uuid.uuid1() + + print "Starting to configure your system:" + syscfg = sysConfig.getSysConfigFactory(glbEnv) + try: + syscfg.config() + print "Cloudkit setup is Done!" + except CloudRuntimeException, e: + print e + print "Try to restore your system:" + try: + syscfg.restore() + except: + pass diff --git a/scripts/vm/network/security_group.py b/scripts/vm/network/security_group.py index 9e39f34a7fb..de9d230f26e 100755 --- a/scripts/vm/network/security_group.py +++ b/scripts/vm/network/security_group.py @@ -1,25 +1,4 @@ #!/usr/bin/python - - - - # - # Copyright (C) 2010 Cloud.com, Inc. All rights reserved. - # - # This software is licensed under the GNU General Public License v3 or later. - # - # It is free software: you can redistribute it and/or modify - # it under the terms of the GNU General Public License as published by - # the Free Software Foundation, either version 3 of the License, or any later version. - # This program is distributed in the hope that it will be useful, - # but WITHOUT ANY WARRANTY; without even the implied warranty of - # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - # GNU General Public License for more details. - # - # You should have received a copy of the GNU General Public License - # along with this program. If not, see . - # - - import cloud_utils from cloud_utils import Command import logging @@ -84,7 +63,7 @@ def ipset(ipsetname, proto, start, end, ips): return result ''' -def destroy_network_rules_for_vm(vm_name): +def destroy_network_rules_for_vm(vm_name, vif=None): vmchain = vm_name vmchain_default = None @@ -116,6 +95,16 @@ def destroy_network_rules_for_vm(vm_name): except: logging.debug("Ignoring failure to delete chain " + vmchain) + if vif is not None: + try: + dnats = execute("iptables -t nat -S | grep " + vif + " | sed 's/-A/-D/'").split("\n") + for dnat in dnats: + try: + execute("iptables -t nat " + dnat) + except: + logging.debug("Igoring failure to delete dnat: " + dnat) + except: + pass remove_rule_log_for_vm(vm_name) if 1 in [ vm_name.startswith(c) for c in ['r-', 's-', 'v-'] ]: @@ -162,7 +151,8 @@ def default_ebtables_rules(vm_name, vm_ip, vm_mac, vif): execute("ebtables -t nat -A " + vmchain_in + " -s ! " + vm_mac + " -j DROP") execute("ebtables -t nat -A " + vmchain_in + " -p ARP -s ! " + vm_mac + " -j DROP") execute("ebtables -t nat -A " + vmchain_in + " -p ARP --arp-mac-src ! " + vm_mac + " -j DROP") - execute("ebtables -t nat -A " + vmchain_in + " -p ARP --arp-ip-src ! " + vm_ip + " -j DROP") + if vm_ip is not None: + execute("ebtables -t nat -A " + vmchain_in + " -p ARP --arp-ip-src ! " + vm_ip + " -j DROP") execute("ebtables -t nat -A " + vmchain_in + " -p ARP --arp-op Request -j ACCEPT") execute("ebtables -t nat -A " + vmchain_in + " -p ARP --arp-op Reply -j ACCEPT") execute("ebtables -t nat -A " + vmchain_in + " -p ARP -j DROP") @@ -172,7 +162,8 @@ def default_ebtables_rules(vm_name, vm_ip, vm_mac, vif): try: execute("ebtables -t nat -A " + vmchain_out + " -p ARP --arp-op Reply --arp-mac-dst ! " + vm_mac + " -j DROP") - execute("ebtables -t nat -A " + vmchain_out + " -p ARP --arp-ip-dst ! " + vm_ip + " -j DROP") + if vm_ip is not None: + execute("ebtables -t nat -A " + vmchain_out + " -p ARP --arp-ip-dst ! " + vm_ip + " -j DROP") execute("ebtables -t nat -A " + vmchain_out + " -p ARP --arp-op Request -j ACCEPT") execute("ebtables -t nat -A " + vmchain_out + " -p ARP --arp-op Reply -j ACCEPT") execute("ebtables -t nat -A " + vmchain_out + " -p ARP -j DROP") @@ -244,7 +235,8 @@ def default_network_rules(vm_name, vm_id, vm_ip, vm_mac, vif, brname): execute("iptables -A " + vmchain_default + " -m physdev --physdev-is-bridged --physdev-out " + vif + " -p udp --dport 68 --sport 67 -j ACCEPT") #don't let vm spoof its ip address - execute("iptables -A " + vmchain_default + " -m physdev --physdev-is-bridged --physdev-in " + vif + " --source " + vm_ip + " -j ACCEPT") + if vm_ip is not None: + execute("iptables -A " + vmchain_default + " -m physdev --physdev-is-bridged --physdev-in " + vif + " --source " + vm_ip + " -j ACCEPT") execute("iptables -A " + vmchain_default + " -j " + vmchain) execute("iptables -A " + vmchain + " -j DROP") except: @@ -253,12 +245,42 @@ def default_network_rules(vm_name, vm_id, vm_ip, vm_mac, vif, brname): default_ebtables_rules(vmchain, vm_ip, vm_mac, vif) - if write_rule_log_for_vm(vmName, vm_id, vm_ip, domID, '_initial_', '-1') == False: - logging.debug("Failed to log default network rules, ignoring") + if vm_ip is not None: + if write_rule_log_for_vm(vmName, vm_id, vm_ip, domID, '_initial_', '-1') == False: + logging.debug("Failed to log default network rules, ignoring") logging.debug("Programmed default rules for vm " + vm_name) return 'true' +def post_default_network_rules(vm_name, vm_id, vm_ip, vm_mac, vif, brname, dhcpSvr, hostIp, hostMacAddr): + vmchain_default = '-'.join(vm_name.split('-')[:-1]) + "-def" + vmchain_in = vm_name + "-in" + vmchain_out = vm_name + "-out" + domID = getvmId(vm_name) + try: + execute("iptables -I " + vmchain_default + " 4 -m physdev --physdev-is-bridged --physdev-in " + vif + " --source " + vm_ip + " -j ACCEPT") + except: + pass + try: + execute("iptables -t nat -A PREROUTING -p tcp -m physdev --physdev-in " + vif + " -m tcp --dport 80 -d " + dhcpSvr + " -j DNAT --to-destination " + hostIp + ":80") + except: + pass + + try: + execute("ebtables -t nat -A PREROUTING -i " + vif + " -p IPv4 --ip-protocol tcp --ip-destination-port 80 -j dnat --to-destination " + hostMacAddr) + except: + pass + + try: + execute("ebtables -t nat -I " + vmchain_in + " 4 -p ARP --arp-ip-src ! " + vm_ip + " -j DROP") + except: + pass + try: + execute("ebtables -t nat -I " + vmchain_out + " 2 -p ARP --arp-ip-dst ! " + vm_ip + " -j DROP") + except: + pass + if write_rule_log_for_vm(vm_name, vm_id, vm_ip, domID, '_initial_', '-1') == False: + logging.debug("Failed to log default network rules, ignoring") def delete_rules_for_vm_in_bridge_firewall_chain(vmName): vm_name = vmName if vm_name.startswith('i-') or vm_name.startswith('r-'): @@ -582,6 +604,9 @@ if __name__ == '__main__': parser.add_option("--seq", dest="seq") parser.add_option("--rules", dest="rules") parser.add_option("--brname", dest="brname") + parser.add_option("--dhcpSvr", dest="dhcpSvr") + parser.add_option("--hostIp", dest="hostIp") + parser.add_option("--hostMacAddr", dest="hostMacAddr") (option, args) = parser.parse_args() cmd = args[0] if cmd == "can_bridge_firewall": @@ -589,7 +614,7 @@ if __name__ == '__main__': elif cmd == "default_network_rules": default_network_rules(option.vmName, option.vmID, option.vmIP, option.vmMAC, option.vif, option.brname) elif cmd == "destroy_network_rules_for_vm": - destroy_network_rules_for_vm(option.vmName) + destroy_network_rules_for_vm(option.vmName, option.vif) elif cmd == "default_network_rules_systemvm": default_network_rules_systemvm(option.vmName, option.brname) elif cmd == "get_rule_logs_for_vms": @@ -598,3 +623,5 @@ if __name__ == '__main__': add_network_rules(option.vmName, option.vmID, option.vmIP, option.sig, option.seq, option.vmMAC, option.rules, option.vif, option.brname) elif cmd == "cleanup_rules": cleanup_rules() + elif cmd == "post_default_network_rules": + post_default_network_rules(option.vmName, option.vmID, option.vmIP, option.vmMAC, option.vif, option.brname, option.dhcpSvr, option.hostIp, option.hostMacAddr) \ No newline at end of file diff --git a/server/src/com/cloud/agent/AgentManager.java b/server/src/com/cloud/agent/AgentManager.java index 0f8aa39b75b..e26ce661ccb 100755 --- a/server/src/com/cloud/agent/AgentManager.java +++ b/server/src/com/cloud/agent/AgentManager.java @@ -140,7 +140,25 @@ public interface AgentManager extends Manager { * @return id to unregister if needed. */ int registerForHostEvents(Listener listener, boolean connections, boolean commands, boolean priority); - + + + /** + * Register to listen for initial agent connections. + * @param creator + * @param priority in listening for events. + * @return id to unregister if needed. + */ + int registerForInitialConnects(StartupCommandProcessor creator, boolean priority); + + + /** + * Register to listen for initial agent connections. + * @param creator + * @param priority in listening for events. + * @return id to unregister if needed. + */ + int registerForInitialConnects(StartupCommandProcessor creator, boolean priority); + /** * Unregister for listening to host events. * diff --git a/server/src/com/cloud/agent/manager/AgentManagerImpl.java b/server/src/com/cloud/agent/manager/AgentManagerImpl.java index 33ae309b51a..1f328c58b6e 100755 --- a/server/src/com/cloud/agent/manager/AgentManagerImpl.java +++ b/server/src/com/cloud/agent/manager/AgentManagerImpl.java @@ -43,6 +43,7 @@ import javax.naming.ConfigurationException; import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; +import com.cloud.agent.StartupCommandProcessor; import com.cloud.agent.Listener; import com.cloud.agent.api.AgentControlAnswer; import com.cloud.agent.api.AgentControlCommand; @@ -210,6 +211,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, ResourceS protected ConcurrentHashMap _agents = new ConcurrentHashMap(10007); protected List> _hostMonitors = new ArrayList>(17); protected List> _cmdMonitors = new ArrayList>(17); + protected List> _creationMonitors = new ArrayList>(17); protected int _monitorId = 0; protected NioServer _connection; @@ -424,6 +426,23 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, ResourceS } } + @Override + public int registerForInitialConnects(final StartupCommandProcessor creator,boolean priority) { + synchronized (_hostMonitors) { + _monitorId++; + + if (priority) { + _creationMonitors.add(0, new Pair( + _monitorId, creator)); + } else { + _creationMonitors.add(0, new Pair( + _monitorId, creator)); + } + } + + return _monitorId; + } + @Override public void unregisterForHostEvents(final int id) { s_logger.debug("Deregistering " + id); @@ -1741,6 +1760,22 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, ResourceS return attache; } + protected boolean notifyCreatorsOfConnection(StartupCommand[] cmd) throws ConnectionException { + boolean handled = false; + for (Pair monitor : _creationMonitors) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Sending Connect to creator: " + + monitor.second().getClass().getSimpleName()); + } + handled = monitor.second().processInitialConnect(cmd); + if (handled) { + break; + } + } + + return handled; + } + @Override public boolean start() { startDirectlyConnectedHosts(); @@ -2488,24 +2523,27 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, ResourceS return result; } - public AgentAttache handleConnect(final Link link, final StartupCommand[] startup) throws IllegalArgumentException, ConnectionException { - HostVO server = createHost(startup, null, null, false, null, null); - if (server == null) { - return null; - } - long id = server.getId(); +public AgentAttache handleConnect(final Link link, + final StartupCommand[] startup) throws IllegalArgumentException, + ConnectionException { + HostVO server = null; + boolean handled = notifyCreatorsOfConnection(startup); + if (!handled) { + server = createHost(startup, null, null, false, null, null); + } else { + server = _hostDao.findByGuid(startup[0].getGuid()); + } + + if (server == null) { + return null; + } + long id = server.getId(); - AgentAttache attache = createAttache(id, server, link); + AgentAttache attache = createAttache(id, server, link); - attache = notifyMonitorsOfConnection(attache, startup); + attache = notifyMonitorsOfConnection(attache, startup); - return attache; - } - - public AgentAttache findAgent(long hostId) { - synchronized (_agents) { - return _agents.get(hostId); - } + return attache; } protected AgentAttache createAttache(long id, HostVO server, Link link) { diff --git a/server/src/com/cloud/agent/manager/authn/AgentAuthnException.java b/server/src/com/cloud/agent/manager/authn/AgentAuthnException.java new file mode 100644 index 00000000000..b9aee8f6467 --- /dev/null +++ b/server/src/com/cloud/agent/manager/authn/AgentAuthnException.java @@ -0,0 +1,36 @@ +/** + * Copyright (C) 2011 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.agent.manager.authn; + +/** + * Exception indicates authentication and/OR authorization problem + * + */ +public class AgentAuthnException extends Exception{ + + private static final long serialVersionUID = 3303508953403051189L; + + public AgentAuthnException(String message, Throwable cause) { + super(message, cause); + } + + public AgentAuthnException(String message) { + super(message); + } + +} diff --git a/server/src/com/cloud/agent/manager/authn/AgentAuthorizer.java b/server/src/com/cloud/agent/manager/authn/AgentAuthorizer.java new file mode 100644 index 00000000000..9520e630ada --- /dev/null +++ b/server/src/com/cloud/agent/manager/authn/AgentAuthorizer.java @@ -0,0 +1,27 @@ +/** + * Copyright (C) 2011 Cloud.com. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later +version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.agent.manager.authn; + +import com.cloud.agent.api.StartupCommand; +import com.cloud.utils.component.Adapter; + + +public interface AgentAuthorizer extends Adapter { + boolean authorizeAgent(StartupCommand [] cmd) throws AgentAuthnException; +} diff --git a/server/src/com/cloud/agent/manager/authn/impl/BasicAgentAuthManager.java b/server/src/com/cloud/agent/manager/authn/impl/BasicAgentAuthManager.java new file mode 100644 index 00000000000..6f975afb341 --- /dev/null +++ b/server/src/com/cloud/agent/manager/authn/impl/BasicAgentAuthManager.java @@ -0,0 +1,66 @@ +package com.cloud.agent.manager.authn.impl; + +import java.util.Map; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.StartupCommandProcessor; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.manager.authn.AgentAuthnException; +import com.cloud.agent.manager.authn.AgentAuthorizer; +import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.exception.ConnectionException; +import com.cloud.host.dao.HostDao; +import com.cloud.utils.component.Inject; + +@Local(value={AgentAuthorizer.class, StartupCommandProcessor.class}) +public class BasicAgentAuthManager implements AgentAuthorizer, StartupCommandProcessor { + private static final Logger s_logger = Logger.getLogger(BasicAgentAuthManager.class); + @Inject HostDao _hostDao = null; + @Inject ConfigurationDao _configDao = null; + @Inject AgentManager _agentManager = null; + + @Override + public boolean processInitialConnect(StartupCommand[] cmd) + throws ConnectionException { + try { + authorizeAgent(cmd); + } catch (AgentAuthnException e) { + throw new ConnectionException(true, "Failed to authenticate/authorize", e); + } + s_logger.debug("Authorized agent with guid " + cmd[0].getGuid()); + return false;//so that the next host creator can process it + } + + @Override + public boolean authorizeAgent(StartupCommand[] cmd) throws AgentAuthnException{ + return true; + } + + @Override + public boolean configure(String name, Map params) + throws ConfigurationException { + _agentManager.registerForInitialConnects(this, true); + return true; + } + + @Override + public String getName() { + return getClass().getName(); + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + +} diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index c12feab7331..b60cf419d18 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -1508,6 +1508,8 @@ public class ApiResponseHelper implements ResponseGenerator { templateResponse.setChecksum(template.getChecksum()); templateResponse.setSourceTemplateId(template.getSourceTemplateId()); + templateResponse.setChecksum(template.getChecksum()); + templateResponse.setObjectName("template"); responses.add(templateResponse); } diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java index d5849cd425f..92b411144ad 100755 --- a/server/src/com/cloud/api/ApiServer.java +++ b/server/src/com/cloud/api/ApiServer.java @@ -1,889 +1,889 @@ -/** - * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. - * - * This software is licensed under the GNU General Public License v3 or later. - * - * It is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -package com.cloud.api; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InterruptedIOException; -import java.io.UnsupportedEncodingException; -import java.net.InetAddress; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.URLDecoder; -import java.net.URLEncoder; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.TimeZone; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; - -import org.apache.http.ConnectionClosedException; -import org.apache.http.HttpException; -import org.apache.http.HttpRequest; -import org.apache.http.HttpResponse; -import org.apache.http.HttpServerConnection; -import org.apache.http.HttpStatus; -import org.apache.http.entity.BasicHttpEntity; -import org.apache.http.impl.DefaultHttpResponseFactory; -import org.apache.http.impl.DefaultHttpServerConnection; -import org.apache.http.impl.NoConnectionReuseStrategy; -import org.apache.http.impl.SocketHttpServerConnection; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.CoreConnectionPNames; -import org.apache.http.params.CoreProtocolPNames; -import org.apache.http.params.HttpParams; -import org.apache.http.protocol.BasicHttpContext; -import org.apache.http.protocol.BasicHttpProcessor; -import org.apache.http.protocol.HttpContext; -import org.apache.http.protocol.HttpRequestHandler; -import org.apache.http.protocol.HttpRequestHandlerRegistry; -import org.apache.http.protocol.HttpService; -import org.apache.http.protocol.ResponseConnControl; -import org.apache.http.protocol.ResponseContent; -import org.apache.http.protocol.ResponseDate; -import org.apache.http.protocol.ResponseServer; -import org.apache.log4j.Logger; - -import com.cloud.api.response.ApiResponseSerializer; -import com.cloud.api.response.ExceptionResponse; -import com.cloud.api.response.ListResponse; -import com.cloud.async.AsyncJob; -import com.cloud.async.AsyncJobManager; -import com.cloud.async.AsyncJobVO; -import com.cloud.cluster.StackMaid; -import com.cloud.configuration.ConfigurationVO; -import com.cloud.configuration.dao.ConfigurationDao; -import com.cloud.domain.Domain; -import com.cloud.domain.DomainVO; -import com.cloud.event.EventUtils; -import com.cloud.exception.CloudAuthenticationException; -import com.cloud.exception.InvalidParameterValueException; -import com.cloud.exception.PermissionDeniedException; -import com.cloud.server.ManagementServer; -import com.cloud.user.Account; -import com.cloud.user.AccountService; -import com.cloud.user.User; -import com.cloud.user.UserAccount; -import com.cloud.user.UserContext; -import com.cloud.utils.Pair; -import com.cloud.utils.PropertiesUtil; -import com.cloud.utils.component.ComponentLocator; -import com.cloud.utils.concurrency.NamedThreadFactory; -import com.cloud.utils.db.SearchCriteria; -import com.cloud.utils.db.Transaction; -import com.cloud.utils.encoding.Base64; - -public class ApiServer implements HttpRequestHandler { - private static final Logger s_logger = Logger.getLogger(ApiServer.class.getName()); - private static final Logger s_accessLogger = Logger.getLogger("apiserver." + ApiServer.class.getName()); - - public static final short ADMIN_COMMAND = 1; - public static final short DOMAIN_ADMIN_COMMAND = 4; - public static final short RESOURCE_DOMAIN_ADMIN_COMMAND = 2; - public static final short USER_COMMAND = 8; - private Properties _apiCommands = null; - private ApiDispatcher _dispatcher; - private ManagementServer _ms = null; - private AccountService _accountMgr = null; - private AsyncJobManager _asyncMgr = null; - private Account _systemAccount = null; - private User _systemUser = null; - - private static int _workerCount = 0; - - private static ApiServer s_instance = null; - private static List s_userCommands = null; - private static List s_resellerCommands = null; // AKA domain-admin - private static List s_adminCommands = null; - private static List s_resourceDomainAdminCommands = null; - private static List s_allCommands = null; - - private static ExecutorService _executor = new ThreadPoolExecutor(10, 150, 60, TimeUnit.SECONDS, new LinkedBlockingQueue(), new NamedThreadFactory("ApiServer")); - - static { - s_userCommands = new ArrayList(); - s_resellerCommands = new ArrayList(); - s_adminCommands = new ArrayList(); - s_resourceDomainAdminCommands = new ArrayList(); - s_allCommands = new ArrayList(); - } - - private ApiServer() { - } - - public static void initApiServer(String[] apiConfig) { - if (s_instance == null) { - s_instance = new ApiServer(); - s_instance.init(apiConfig); - } - } - - public static ApiServer getInstance() { - // initApiServer(); - return s_instance; - } - - public Properties get_apiCommands() { - return _apiCommands; - } - - public void init(String[] apiConfig) { - try { - BaseCmd.setComponents(new ApiResponseHelper()); - BaseListCmd.configure(); - _apiCommands = new Properties(); - Properties preProcessedCommands = new Properties(); - if (apiConfig != null) { - for (String configFile : apiConfig) { - File commandsFile = PropertiesUtil.findConfigFile(configFile); - preProcessedCommands.load(new FileInputStream(commandsFile)); - } - for (Object key : preProcessedCommands.keySet()) { - String preProcessedCommand = preProcessedCommands.getProperty((String) key); - String[] commandParts = preProcessedCommand.split(";"); - _apiCommands.put(key, commandParts[0]); - if (commandParts.length > 1) { - try { - short cmdPermissions = Short.parseShort(commandParts[1]); - if ((cmdPermissions & ADMIN_COMMAND) != 0) { - s_adminCommands.add((String) key); - } - if ((cmdPermissions & RESOURCE_DOMAIN_ADMIN_COMMAND) != 0) { - s_resourceDomainAdminCommands.add((String) key); - } - if ((cmdPermissions & DOMAIN_ADMIN_COMMAND) != 0) { - s_resellerCommands.add((String) key); - } - if ((cmdPermissions & USER_COMMAND) != 0) { - s_userCommands.add((String) key); - } - } catch (NumberFormatException nfe) { - s_logger.info("Malformed command.properties permissions value, key = " + key + ", value = " + preProcessedCommand); - } - } - } - - s_allCommands.addAll(s_adminCommands); - s_allCommands.addAll(s_resourceDomainAdminCommands); - s_allCommands.addAll(s_userCommands); - s_allCommands.addAll(s_resellerCommands); - } - } catch (FileNotFoundException fnfex) { - s_logger.error("Unable to find properites file", fnfex); - } catch (IOException ioex) { - s_logger.error("Exception loading properties file", ioex); - } - - _ms = (ManagementServer) ComponentLocator.getComponent(ManagementServer.Name); - ComponentLocator locator = ComponentLocator.getLocator(ManagementServer.Name); - _accountMgr = locator.getManager(AccountService.class); - _asyncMgr = locator.getManager(AsyncJobManager.class); - _systemAccount = _accountMgr.getSystemAccount(); - _systemUser = _accountMgr.getSystemUser(); - _dispatcher = ApiDispatcher.getInstance(); - - int apiPort = 8096; // default port - ConfigurationDao configDao = locator.getDao(ConfigurationDao.class); - SearchCriteria sc = configDao.createSearchCriteria(); - sc.addAnd("name", SearchCriteria.Op.EQ, "integration.api.port"); - List values = configDao.search(sc, null); - if ((values != null) && (values.size() > 0)) { - ConfigurationVO apiPortConfig = values.get(0); - apiPort = Integer.parseInt(apiPortConfig.getValue()); - } - - ListenerThread listenerThread = new ListenerThread(this, apiPort); - listenerThread.start(); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Override - public void handle(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, IOException { - // get some information for the access log... - StringBuffer sb = new StringBuffer(); - HttpServerConnection connObj = (HttpServerConnection) context.getAttribute("http.connection"); - if (connObj instanceof SocketHttpServerConnection) { - InetAddress remoteAddr = ((SocketHttpServerConnection) connObj).getRemoteAddress(); - sb.append(remoteAddr.toString() + " -- "); - } - sb.append(request.getRequestLine()); - - try { - String uri = request.getRequestLine().getUri(); - int requestParamsStartIndex = uri.indexOf('?'); - if (requestParamsStartIndex >= 0) { - uri = uri.substring(requestParamsStartIndex + 1); - } - - String[] paramArray = uri.split("&"); - if (paramArray.length < 1) { - s_logger.info("no parameters received for request: " + uri + ", aborting..."); - return; - } - - Map parameterMap = new HashMap(); - - String responseType = BaseCmd.RESPONSE_TYPE_XML; - for (String paramEntry : paramArray) { - String[] paramValue = paramEntry.split("="); - if (paramValue.length != 2) { - s_logger.info("malformed parameter: " + paramEntry + ", skipping"); - continue; - } - if ("response".equalsIgnoreCase(paramValue[0])) { - responseType = paramValue[1]; - } else { - // according to the servlet spec, the parameter map should be in the form (name=String, value=String[]), so - // parameter values will be stored in an array - parameterMap.put(/* name */paramValue[0], /* value */new String[] { paramValue[1] }); - } - } - try { - // always trust commands from API port, user context will always be UID_SYSTEM/ACCOUNT_ID_SYSTEM - UserContext.registerContext(_systemUser.getId(), _systemAccount, null, true); - sb.insert(0, "(userId=" + User.UID_SYSTEM + " accountId=" + Account.ACCOUNT_ID_SYSTEM + " sessionId=" + null + ") "); - String responseText = handleRequest(parameterMap, true, responseType, sb); - sb.append(" 200 " + ((responseText == null) ? 0 : responseText.length())); - - writeResponse(response, responseText, HttpStatus.SC_OK, responseType, null); - } catch (ServerApiException se) { - String responseText = getSerializedApiError(se.getErrorCode(), se.getDescription(), parameterMap, responseType); - writeResponse(response, responseText, se.getErrorCode(), responseType, se.getDescription()); - sb.append(" " + se.getErrorCode() + " " + se.getDescription()); - } catch (RuntimeException e) { - // log runtime exception like NullPointerException to help identify the source easier - s_logger.error("Unhandled exception, ", e); - throw e; - } - } finally { - s_accessLogger.info(sb.toString()); - UserContext.unregisterContext(); - } - } - - @SuppressWarnings("rawtypes") - public String handleRequest(Map params, boolean decode, String responseType, StringBuffer auditTrailSb) throws ServerApiException { - String response = null; - String[] command = null; - try { - command = (String[]) params.get("command"); - if (command == null) { - s_logger.error("invalid request, no command sent"); - if (s_logger.isTraceEnabled()) { - s_logger.trace("dumping request parameters"); - for (Object key : params.keySet()) { - String keyStr = (String) key; - String[] value = (String[]) params.get(key); - s_logger.trace(" key: " + keyStr + ", value: " + ((value == null) ? "'null'" : value[0])); - } - } - throw new ServerApiException(BaseCmd.UNSUPPORTED_ACTION_ERROR, "Invalid request, no command sent"); - } else { - Map paramMap = new HashMap(); - Set keys = params.keySet(); - Iterator keysIter = keys.iterator(); - while (keysIter.hasNext()) { - String key = (String) keysIter.next(); - if ("command".equalsIgnoreCase(key)) { - continue; - } - String[] value = (String[]) params.get(key); - - String decodedValue = null; - if (decode) { - try { - decodedValue = URLDecoder.decode(value[0], "UTF-8"); - } catch (UnsupportedEncodingException usex) { - s_logger.warn(key + " could not be decoded, value = " + value[0]); - throw new ServerApiException(BaseCmd.PARAM_ERROR, key + " could not be decoded, received value " + value[0]); - } catch (IllegalArgumentException iae) { - s_logger.warn(key + " could not be decoded, value = " + value[0]); - throw new ServerApiException(BaseCmd.PARAM_ERROR, key + " could not be decoded, received value " + value[0] + " which contains illegal characters eg.%"); - } - } else { - decodedValue = value[0]; - } - paramMap.put(key, decodedValue); - } - String cmdClassName = _apiCommands.getProperty(command[0]); - if (cmdClassName != null) { - Class cmdClass = Class.forName(cmdClassName); - BaseCmd cmdObj = (BaseCmd) cmdClass.newInstance(); - - cmdObj.setResponseType(responseType); - // This is where the command is either serialized, or directly dispatched - response = queueCommand(cmdObj, paramMap); - buildAuditTrail(auditTrailSb, command[0], response); - } else { - if (!command[0].equalsIgnoreCase("login") && !command[0].equalsIgnoreCase("logout")) { - String errorString = "Unknown API command: " + ((command == null) ? "null" : command[0]); - s_logger.warn(errorString); - auditTrailSb.append(" " + errorString); - throw new ServerApiException(BaseCmd.UNSUPPORTED_ACTION_ERROR, errorString); - } - } - } - } catch (Exception ex) { - if (ex instanceof InvalidParameterValueException) { - throw new ServerApiException(BaseCmd.PARAM_ERROR, ex.getMessage()); - } else if (ex instanceof PermissionDeniedException) { - throw new ServerApiException(BaseCmd.ACCOUNT_ERROR, ex.getMessage()); - } else if (ex instanceof ServerApiException) { - throw (ServerApiException) ex; - } else { - s_logger.error("unhandled exception executing api command: " + ((command == null) ? "null" : command[0]), ex); - throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Internal server error, unable to execute request."); - } - } - return response; - } - - private String queueCommand(BaseCmd cmdObj, Map params) { - UserContext ctx = UserContext.current(); - Long userId = ctx.getCallerUserId(); - Account account = ctx.getCaller(); - if (cmdObj instanceof BaseAsyncCmd) { - Long objectId = null; - if (cmdObj instanceof BaseAsyncCreateCmd) { - BaseAsyncCreateCmd createCmd = (BaseAsyncCreateCmd) cmdObj; - _dispatcher.dispatchCreateCmd(createCmd, params); - objectId = createCmd.getEntityId(); - params.put("id", objectId.toString()); - } else { - ApiDispatcher.setupParameters(cmdObj, params); - } - - BaseAsyncCmd asyncCmd = (BaseAsyncCmd) cmdObj; - - if (userId != null) { - params.put("ctxUserId", userId.toString()); - } - if (account != null) { - params.put("ctxAccountId", String.valueOf(account.getId())); - } - - long startEventId = ctx.getStartEventId(); - asyncCmd.setStartEventId(startEventId); - - // save the scheduled event - Long eventId = EventUtils.saveScheduledEvent((userId == null) ? User.UID_SYSTEM : userId, asyncCmd.getEntityOwnerId(), asyncCmd.getEventType(), asyncCmd.getEventDescription(), - startEventId); - if (startEventId == 0) { - // There was no create event before, set current event id as start eventId - startEventId = eventId; - } - - params.put("ctxStartEventId", String.valueOf(startEventId)); - - ctx.setAccountId(asyncCmd.getEntityOwnerId()); - - AsyncJobVO job = new AsyncJobVO(); - job.setInstanceId((objectId == null) ? asyncCmd.getInstanceId() : objectId); - job.setInstanceType(asyncCmd.getInstanceType()); - job.setUserId(userId); - job.setAccountId(asyncCmd.getEntityOwnerId()); - - job.setCmd(cmdObj.getClass().getName()); - job.setCmdInfo(ApiGsonHelper.getBuilder().create().toJson(params)); - - long jobId = _asyncMgr.submitAsyncJob(job); - - if (jobId == 0L) { - String errorMsg = "Unable to schedule async job for command " + job.getCmd(); - s_logger.warn(errorMsg); - throw new ServerApiException(BaseCmd.INTERNAL_ERROR, errorMsg); - } - - if (objectId != null) { - return ((BaseAsyncCreateCmd) asyncCmd).getResponse(jobId, objectId); - } - return ApiResponseSerializer.toSerializedString(asyncCmd.getResponse(jobId), asyncCmd.getResponseType()); - } else { - _dispatcher.dispatch(cmdObj, params); - - // if the command is of the listXXXCommand, we will need to also return the - // the job id and status if possible - if (cmdObj instanceof BaseListCmd) { - buildAsyncListResponse((BaseListCmd) cmdObj, account); - } - return ApiResponseSerializer.toSerializedString((ResponseObject) cmdObj.getResponseObject(), cmdObj.getResponseType()); - } - } - - private void buildAsyncListResponse(BaseListCmd command, Account account) { - List responses = ((ListResponse) command.getResponseObject()).getResponses(); - if (responses != null && responses.size() > 0) { - List jobs = null; - - // list all jobs for ROOT admin - if (account.getType() == Account.ACCOUNT_TYPE_ADMIN) { - jobs = _asyncMgr.findInstancePendingAsyncJobs(command.getInstanceType(), null); - } else { - jobs = _asyncMgr.findInstancePendingAsyncJobs(command.getInstanceType(), account.getId()); - } - - if (jobs.size() == 0) { - return; - } - - // Using maps might possibly be more efficient if the set is large enough but for now, we'll just do a - // comparison of two lists. Either way, there shouldn't be too many async jobs active for the account. - for (AsyncJob job : jobs) { - if (job.getInstanceId() == null) { - continue; - } - for (ResponseObject response : responses) { - if (response.getObjectId() != null && job.getInstanceId().longValue() == response.getObjectId().longValue()) { - response.setJobId(job.getId()); - response.setJobStatus(job.getStatus()); - } - } - } - } - } - - private void buildAuditTrail(StringBuffer auditTrailSb, String command, String result) { - if (result == null) { - return; - } - auditTrailSb.append(" " + HttpServletResponse.SC_OK + " "); - auditTrailSb.append(result); - /* - * if (command.equals("queryAsyncJobResult")){ //For this command we need to also log job status and job resultcode for - * (Pair pair : resultValues){ String key = pair.first(); if (key.equals("jobstatus")){ - * auditTrailSb.append(" "); auditTrailSb.append(key); auditTrailSb.append("="); auditTrailSb.append(pair.second()); - * }else if (key.equals("jobresultcode")){ auditTrailSb.append(" "); auditTrailSb.append(key); auditTrailSb.append("="); - * auditTrailSb.append(pair.second()); } } }else { for (Pair pair : resultValues){ if - * (pair.first().equals("jobid")){ // Its an async job so report the jobid auditTrailSb.append(" "); - * auditTrailSb.append(pair.first()); auditTrailSb.append("="); auditTrailSb.append(pair.second()); } } } - */ - } - - private static boolean isCommandAvailable(String commandName) { - boolean isCommandAvailable = false; - isCommandAvailable = s_allCommands.contains(commandName); - return isCommandAvailable; - } - - public boolean verifyRequest(Map requestParameters, Long userId) throws ServerApiException { - try { - String apiKey = null; - String secretKey = null; - String signature = null; - String unsignedRequest = null; - - String[] command = (String[]) requestParameters.get("command"); - if (command == null) { - s_logger.info("missing command, ignoring request..."); - return false; - } - - String commandName = command[0]; - - // if userId not null, that mean that user is logged in - if (userId != null) { - Long accountId = ApiDBUtils.findUserById(userId).getAccountId(); - Account userAccount = _ms.findAccountById(accountId); - short accountType = userAccount.getType(); - - if (!isCommandAvailable(accountType, commandName)) { - s_logger.warn("The given command:" + commandName + " does not exist"); - throw new ServerApiException(BaseCmd.UNSUPPORTED_ACTION_ERROR, "The given command:" + commandName + " does not exist"); - } - return true; - } else { - // check against every available command to see if the command exists or not - if (!isCommandAvailable(commandName) && !commandName.equals("login") && !commandName.equals("logout")) { - s_logger.warn("The given command:" + commandName + " does not exist"); - throw new ServerApiException(BaseCmd.UNSUPPORTED_ACTION_ERROR, "The given command:" + commandName + " does not exist"); - } - } - - // - build a request string with sorted params, make sure it's all lowercase - // - sign the request, verify the signature is the same - List parameterNames = new ArrayList(); - - for (Object paramNameObj : requestParameters.keySet()) { - parameterNames.add((String) paramNameObj); // put the name in a list that we'll sort later - } - - Collections.sort(parameterNames); - - for (String paramName : parameterNames) { - // parameters come as name/value pairs in the form String/String[] - String paramValue = ((String[]) requestParameters.get(paramName))[0]; - - if ("signature".equalsIgnoreCase(paramName)) { - signature = paramValue; - } else { - if ("apikey".equalsIgnoreCase(paramName)) { - apiKey = paramValue; - } - - if (unsignedRequest == null) { - unsignedRequest = paramName + "=" + URLEncoder.encode(paramValue, "UTF-8").replaceAll("\\+", "%20"); - } else { - unsignedRequest = unsignedRequest + "&" + paramName + "=" + URLEncoder.encode(paramValue, "UTF-8").replaceAll("\\+", "%20"); - } - } - } - - // if api/secret key are passed to the parameters - if ((signature == null) || (apiKey == null)) { - if (s_logger.isDebugEnabled()) { - s_logger.info("expired session, missing signature, or missing apiKey -- ignoring request...sig: " + signature + ", apiKey: " + apiKey); - } - return false; // no signature, bad request - } - - Transaction txn = Transaction.open(Transaction.CLOUD_DB); - txn.close(); - User user = null; - // verify there is a user with this api key - Pair userAcctPair = _ms.findUserByApiKey(apiKey); - if (userAcctPair == null) { - s_logger.info("apiKey does not map to a valid user -- ignoring request, apiKey: " + apiKey); - return false; - } - - user = userAcctPair.first(); - Account account = userAcctPair.second(); - - if (user.getState() != Account.State.enabled || !account.getState().equals(Account.State.enabled)) { - s_logger.info("disabled or locked user accessing the api, userid = " + user.getId() + "; name = " + user.getUsername() + "; state: " + user.getState() + "; accountState: " - + account.getState()); - return false; - } - - UserContext.updateContext(user.getId(), account, null); - - if (!isCommandAvailable(account.getType(), commandName)) { - s_logger.warn("The given command:" + commandName + " does not exist"); - throw new ServerApiException(BaseCmd.UNSUPPORTED_ACTION_ERROR, "The given command:" + commandName + " does not exist"); - } - - // verify secret key exists - secretKey = user.getSecretKey(); - if (secretKey == null) { - s_logger.info("User does not have a secret key associated with the account -- ignoring request, username: " + user.getUsername()); - return false; - } - - unsignedRequest = unsignedRequest.toLowerCase(); - - Mac mac = Mac.getInstance("HmacSHA1"); - SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(), "HmacSHA1"); - mac.init(keySpec); - mac.update(unsignedRequest.getBytes()); - byte[] encryptedBytes = mac.doFinal(); - String computedSignature = Base64.encodeBytes(encryptedBytes); - boolean equalSig = signature.equals(computedSignature); - if (!equalSig) { - s_logger.info("User signature: " + signature + " is not equaled to computed signature: " + computedSignature); - } - return equalSig; - } catch (Exception ex) { - if (ex instanceof ServerApiException && ((ServerApiException) ex).getErrorCode() == BaseCmd.UNSUPPORTED_ACTION_ERROR) { - throw (ServerApiException) ex; - } - s_logger.error("unable to verifty request signature", ex); - } - return false; - } - - public void loginUser(HttpSession session, String username, String password, Long domainId, String domainPath, Map requestParameters) throws CloudAuthenticationException { - // We will always use domainId first. If that does not exist, we will use domain name. If THAT doesn't exist - // we will default to ROOT - if (domainId == null) { - if (domainPath == null || domainPath.trim().length() == 0) { - domainId = DomainVO.ROOT_DOMAIN; - } else { - Domain domainObj = _ms.findDomainByPath(domainPath); - if (domainObj != null) { - domainId = domainObj.getId(); - } else { // if an unknown path is passed in, fail the login call - throw new CloudAuthenticationException("Unable to find the domain from the path " + domainPath); - } - } - } - - UserAccount userAcct = _ms.authenticateUser(username, password, domainId, requestParameters); - if (userAcct != null) { - String timezone = userAcct.getTimezone(); - float offsetInHrs = 0f; - if (timezone != null) { - TimeZone t = TimeZone.getTimeZone(timezone); - s_logger.info("Current user logged in under " + timezone + " timezone"); - - java.util.Date date = new java.util.Date(); - long longDate = date.getTime(); - float offsetInMs = (t.getOffset(longDate)); - offsetInHrs = offsetInMs / (1000 * 60 * 60); - s_logger.info("Timezone offset from UTC is: " + offsetInHrs); - } - - Account account = _ms.findAccountById(userAcct.getAccountId()); - - // set the userId and account object for everyone - session.setAttribute("userid", userAcct.getId()); - session.setAttribute("username", userAcct.getUsername()); - session.setAttribute("firstname", userAcct.getFirstname()); - session.setAttribute("lastname", userAcct.getLastname()); - session.setAttribute("accountobj", account); - session.setAttribute("account", account.getAccountName()); - session.setAttribute("domainid", account.getDomainId()); - session.setAttribute("type", Short.valueOf(account.getType()).toString()); - session.setAttribute("registrationtoken", userAcct.getRegistrationToken()); - session.setAttribute("registered", new Boolean(userAcct.isRegistered()).toString()); - - if (timezone != null) { - session.setAttribute("timezone", timezone); - session.setAttribute("timezoneoffset", Float.valueOf(offsetInHrs).toString()); - } - - // (bug 5483) generate a session key that the user must submit on every request to prevent CSRF, add that - // to the login response so that session-based authenticators know to send the key back - SecureRandom sesssionKeyRandom = new SecureRandom(); - byte sessionKeyBytes[] = new byte[20]; - sesssionKeyRandom.nextBytes(sessionKeyBytes); - String sessionKey = Base64.encodeBytes(sessionKeyBytes); - session.setAttribute("sessionkey", sessionKey); - - return; - } - throw new CloudAuthenticationException("Unable to find user " + username + " in domain " + domainId); - } - - public void logoutUser(long userId) { - _ms.logoutUser(Long.valueOf(userId)); - return; - } - - public boolean verifyUser(Long userId) { - User user = _ms.findUserById(userId); - Account account = null; - if (user != null) { - account = _ms.findAccountById(user.getAccountId()); - } - - if ((user == null) || (user.getRemoved() != null) || !user.getState().equals(Account.State.enabled) || (account == null) || !account.getState().equals(Account.State.enabled)) { - s_logger.warn("Deleted/Disabled/Locked user with id=" + userId + " attempting to access public API"); - return false; - } - return true; - } - - public static boolean isCommandAvailable(short accountType, String commandName) { - boolean isCommandAvailable = false; - switch (accountType) { - case Account.ACCOUNT_TYPE_ADMIN: - isCommandAvailable = s_adminCommands.contains(commandName); - break; - case Account.ACCOUNT_TYPE_DOMAIN_ADMIN: - isCommandAvailable = s_resellerCommands.contains(commandName); - break; - case Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN: - isCommandAvailable = s_resourceDomainAdminCommands.contains(commandName); - break; - case Account.ACCOUNT_TYPE_NORMAL: - isCommandAvailable = s_userCommands.contains(commandName); - break; - } - return isCommandAvailable; - } - - // FIXME: rather than isError, we might was to pass in the status code to give more flexibility - private void writeResponse(HttpResponse resp, final String responseText, final int statusCode, String responseType, String reasonPhrase) { - try { - resp.setStatusCode(statusCode); - resp.setReasonPhrase(reasonPhrase); - - BasicHttpEntity body = new BasicHttpEntity(); - if (BaseCmd.RESPONSE_TYPE_JSON.equalsIgnoreCase(responseType)) { - // JSON response - body.setContentType("text/javascript"); - if (responseText == null) { - body.setContent(new ByteArrayInputStream("{ \"error\" : { \"description\" : \"Internal Server Error\" } }".getBytes("UTF-8"))); - } - } else { - body.setContentType("text/xml"); - if (responseText == null) { - body.setContent(new ByteArrayInputStream("Internal Server Error".getBytes("UTF-8"))); - } - } - - if (responseText != null) { - body.setContent(new ByteArrayInputStream(responseText.getBytes("UTF-8"))); - } - resp.setEntity(body); - } catch (Exception ex) { - s_logger.error("error!", ex); - } - } - - // FIXME: the following two threads are copied from - // http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/httpcore/src/examples/org/apache/http/examples/ElementalHttpServer.java - // we have to cite a license if we are using this code directly, so we need to add the appropriate citation or modify the - // code to be very specific to our needs - static class ListenerThread extends Thread { - private HttpService _httpService = null; - private ServerSocket _serverSocket = null; - private HttpParams _params = null; - - public ListenerThread(ApiServer requestHandler, int port) { - try { - _serverSocket = new ServerSocket(port); - } catch (IOException ioex) { - s_logger.error("error initializing api server", ioex); - return; - } - - _params = new BasicHttpParams(); - _params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 30000).setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024) - .setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false).setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true) - .setParameter(CoreProtocolPNames.ORIGIN_SERVER, "HttpComponents/1.1"); - - // Set up the HTTP protocol processor - BasicHttpProcessor httpproc = new BasicHttpProcessor(); - httpproc.addInterceptor(new ResponseDate()); - httpproc.addInterceptor(new ResponseServer()); - httpproc.addInterceptor(new ResponseContent()); - httpproc.addInterceptor(new ResponseConnControl()); - - // Set up request handlers - HttpRequestHandlerRegistry reqistry = new HttpRequestHandlerRegistry(); - reqistry.register("*", requestHandler); - - // Set up the HTTP service - _httpService = new HttpService(httpproc, new NoConnectionReuseStrategy(), new DefaultHttpResponseFactory()); - _httpService.setParams(_params); - _httpService.setHandlerResolver(reqistry); - } - - @Override - public void run() { - s_logger.info("ApiServer listening on port " + _serverSocket.getLocalPort()); - while (!Thread.interrupted()) { - try { - // Set up HTTP connection - Socket socket = _serverSocket.accept(); - DefaultHttpServerConnection conn = new DefaultHttpServerConnection(); - conn.bind(socket, _params); - - // Execute a new worker task to handle the request - _executor.execute(new WorkerTask(_httpService, conn, _workerCount++)); - } catch (InterruptedIOException ex) { - break; - } catch (IOException e) { - s_logger.error("I/O error initializing connection thread", e); - break; - } - } - } - } - - static class WorkerTask implements Runnable { - private final HttpService _httpService; - private final HttpServerConnection _conn; - - public WorkerTask(final HttpService httpService, final HttpServerConnection conn, final int count) { - _httpService = httpService; - _conn = conn; - } - - @Override - public void run() { - HttpContext context = new BasicHttpContext(null); - try { - while (!Thread.interrupted() && _conn.isOpen()) { - try { - _httpService.handleRequest(_conn, context); - _conn.close(); - } finally { - StackMaid.current().exitCleanup(); - } - } - } catch (ConnectionClosedException ex) { - if (s_logger.isTraceEnabled()) { - s_logger.trace("ApiServer: Client closed connection"); - } - } catch (IOException ex) { - if (s_logger.isTraceEnabled()) { - s_logger.trace("ApiServer: IOException - " + ex); - } - } catch (HttpException ex) { - s_logger.warn("ApiServer: Unrecoverable HTTP protocol violation" + ex); - } finally { - try { - _conn.shutdown(); - } catch (IOException ignore) { - } - } - } - } - - public String getSerializedApiError(int errorCode, String errorText, Map apiCommandParams, String responseType) { - String responseName = null; - String cmdClassName = null; - - String responseText = null; - - try { - if (errorCode == BaseCmd.UNSUPPORTED_ACTION_ERROR || apiCommandParams == null || apiCommandParams.isEmpty()) { - responseName = "errorresponse"; - } else { - String cmdName = ((String[]) apiCommandParams.get("command"))[0]; - cmdClassName = _apiCommands.getProperty(cmdName); - if (cmdClassName != null) { - Class claz = Class.forName(cmdClassName); - responseName = ((BaseCmd) claz.newInstance()).getCommandName(); - } else { - responseName = "errorresponse"; - } - } - - ExceptionResponse apiResponse = new ExceptionResponse(); - apiResponse.setErrorCode(errorCode); - apiResponse.setErrorText(errorText); - apiResponse.setResponseName(responseName); - responseText = ApiResponseSerializer.toSerializedString(apiResponse, responseType); - - } catch (Exception e) { - s_logger.error("Exception responding to http request", e); - } - return responseText; - } -} +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.api; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InterruptedIOException; +import java.io.UnsupportedEncodingException; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.TimeZone; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.apache.http.ConnectionClosedException; +import org.apache.http.HttpException; +import org.apache.http.HttpRequest; +import org.apache.http.HttpResponse; +import org.apache.http.HttpServerConnection; +import org.apache.http.HttpStatus; +import org.apache.http.entity.BasicHttpEntity; +import org.apache.http.impl.DefaultHttpResponseFactory; +import org.apache.http.impl.DefaultHttpServerConnection; +import org.apache.http.impl.NoConnectionReuseStrategy; +import org.apache.http.impl.SocketHttpServerConnection; +import org.apache.http.params.BasicHttpParams; +import org.apache.http.params.CoreConnectionPNames; +import org.apache.http.params.CoreProtocolPNames; +import org.apache.http.params.HttpParams; +import org.apache.http.protocol.BasicHttpContext; +import org.apache.http.protocol.BasicHttpProcessor; +import org.apache.http.protocol.HttpContext; +import org.apache.http.protocol.HttpRequestHandler; +import org.apache.http.protocol.HttpRequestHandlerRegistry; +import org.apache.http.protocol.HttpService; +import org.apache.http.protocol.ResponseConnControl; +import org.apache.http.protocol.ResponseContent; +import org.apache.http.protocol.ResponseDate; +import org.apache.http.protocol.ResponseServer; +import org.apache.log4j.Logger; + +import com.cloud.api.response.ApiResponseSerializer; +import com.cloud.api.response.ExceptionResponse; +import com.cloud.api.response.ListResponse; +import com.cloud.async.AsyncJob; +import com.cloud.async.AsyncJobManager; +import com.cloud.async.AsyncJobVO; +import com.cloud.cluster.StackMaid; +import com.cloud.configuration.ConfigurationVO; +import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.domain.Domain; +import com.cloud.domain.DomainVO; +import com.cloud.event.EventUtils; +import com.cloud.exception.CloudAuthenticationException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.PermissionDeniedException; +import com.cloud.server.ManagementServer; +import com.cloud.user.Account; +import com.cloud.user.AccountService; +import com.cloud.user.User; +import com.cloud.user.UserAccount; +import com.cloud.user.UserContext; +import com.cloud.utils.Pair; +import com.cloud.utils.PropertiesUtil; +import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.concurrency.NamedThreadFactory; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.encoding.Base64; + +public class ApiServer implements HttpRequestHandler { + private static final Logger s_logger = Logger.getLogger(ApiServer.class.getName()); + private static final Logger s_accessLogger = Logger.getLogger("apiserver." + ApiServer.class.getName()); + + public static final short ADMIN_COMMAND = 1; + public static final short DOMAIN_ADMIN_COMMAND = 4; + public static final short RESOURCE_DOMAIN_ADMIN_COMMAND = 2; + public static final short USER_COMMAND = 8; + private Properties _apiCommands = null; + private ApiDispatcher _dispatcher; + private ManagementServer _ms = null; + private AccountService _accountMgr = null; + private AsyncJobManager _asyncMgr = null; + private Account _systemAccount = null; + private User _systemUser = null; + + private static int _workerCount = 0; + + private static ApiServer s_instance = null; + private static List s_userCommands = null; + private static List s_resellerCommands = null; // AKA domain-admin + private static List s_adminCommands = null; + private static List s_resourceDomainAdminCommands = null; + private static List s_allCommands = null; + + private static ExecutorService _executor = new ThreadPoolExecutor(10, 150, 60, TimeUnit.SECONDS, new LinkedBlockingQueue(), new NamedThreadFactory("ApiServer")); + + static { + s_userCommands = new ArrayList(); + s_resellerCommands = new ArrayList(); + s_adminCommands = new ArrayList(); + s_resourceDomainAdminCommands = new ArrayList(); + s_allCommands = new ArrayList(); + } + + private ApiServer() { + } + + public static void initApiServer(String[] apiConfig) { + if (s_instance == null) { + s_instance = new ApiServer(); + s_instance.init(apiConfig); + } + } + + public static ApiServer getInstance() { + // initApiServer(); + return s_instance; + } + + public Properties get_apiCommands() { + return _apiCommands; + } + + public void init(String[] apiConfig) { + try { + BaseCmd.setComponents(new ApiResponseHelper()); + BaseListCmd.configure(); + _apiCommands = new Properties(); + Properties preProcessedCommands = new Properties(); + if (apiConfig != null) { + for (String configFile : apiConfig) { + File commandsFile = PropertiesUtil.findConfigFile(configFile); + preProcessedCommands.load(new FileInputStream(commandsFile)); + } + for (Object key : preProcessedCommands.keySet()) { + String preProcessedCommand = preProcessedCommands.getProperty((String) key); + String[] commandParts = preProcessedCommand.split(";"); + _apiCommands.put(key, commandParts[0]); + if (commandParts.length > 1) { + try { + short cmdPermissions = Short.parseShort(commandParts[1]); + if ((cmdPermissions & ADMIN_COMMAND) != 0) { + s_adminCommands.add((String) key); + } + if ((cmdPermissions & RESOURCE_DOMAIN_ADMIN_COMMAND) != 0) { + s_resourceDomainAdminCommands.add((String) key); + } + if ((cmdPermissions & DOMAIN_ADMIN_COMMAND) != 0) { + s_resellerCommands.add((String) key); + } + if ((cmdPermissions & USER_COMMAND) != 0) { + s_userCommands.add((String) key); + } + } catch (NumberFormatException nfe) { + s_logger.info("Malformed command.properties permissions value, key = " + key + ", value = " + preProcessedCommand); + } + } + } + + s_allCommands.addAll(s_adminCommands); + s_allCommands.addAll(s_resourceDomainAdminCommands); + s_allCommands.addAll(s_userCommands); + s_allCommands.addAll(s_resellerCommands); + } + } catch (FileNotFoundException fnfex) { + s_logger.error("Unable to find properites file", fnfex); + } catch (IOException ioex) { + s_logger.error("Exception loading properties file", ioex); + } + + _ms = (ManagementServer) ComponentLocator.getComponent(ManagementServer.Name); + ComponentLocator locator = ComponentLocator.getLocator(ManagementServer.Name); + _accountMgr = locator.getManager(AccountService.class); + _asyncMgr = locator.getManager(AsyncJobManager.class); + _systemAccount = _accountMgr.getSystemAccount(); + _systemUser = _accountMgr.getSystemUser(); + _dispatcher = ApiDispatcher.getInstance(); + + int apiPort = 8096; // default port + ConfigurationDao configDao = locator.getDao(ConfigurationDao.class); + SearchCriteria sc = configDao.createSearchCriteria(); + sc.addAnd("name", SearchCriteria.Op.EQ, "integration.api.port"); + List values = configDao.search(sc, null); + if ((values != null) && (values.size() > 0)) { + ConfigurationVO apiPortConfig = values.get(0); + apiPort = Integer.parseInt(apiPortConfig.getValue()); + } + + ListenerThread listenerThread = new ListenerThread(this, apiPort); + listenerThread.start(); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public void handle(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, IOException { + // get some information for the access log... + StringBuffer sb = new StringBuffer(); + HttpServerConnection connObj = (HttpServerConnection) context.getAttribute("http.connection"); + if (connObj instanceof SocketHttpServerConnection) { + InetAddress remoteAddr = ((SocketHttpServerConnection) connObj).getRemoteAddress(); + sb.append(remoteAddr.toString() + " -- "); + } + sb.append(request.getRequestLine()); + + try { + String uri = request.getRequestLine().getUri(); + int requestParamsStartIndex = uri.indexOf('?'); + if (requestParamsStartIndex >= 0) { + uri = uri.substring(requestParamsStartIndex + 1); + } + + String[] paramArray = uri.split("&"); + if (paramArray.length < 1) { + s_logger.info("no parameters received for request: " + uri + ", aborting..."); + return; + } + + Map parameterMap = new HashMap(); + + String responseType = BaseCmd.RESPONSE_TYPE_XML; + for (String paramEntry : paramArray) { + String[] paramValue = paramEntry.split("="); + if (paramValue.length != 2) { + s_logger.info("malformed parameter: " + paramEntry + ", skipping"); + continue; + } + if ("response".equalsIgnoreCase(paramValue[0])) { + responseType = paramValue[1]; + } else { + // according to the servlet spec, the parameter map should be in the form (name=String, value=String[]), so + // parameter values will be stored in an array + parameterMap.put(/* name */paramValue[0], /* value */new String[] { paramValue[1] }); + } + } + try { + // always trust commands from API port, user context will always be UID_SYSTEM/ACCOUNT_ID_SYSTEM + UserContext.registerContext(_systemUser.getId(), _systemAccount, null, true); + sb.insert(0, "(userId=" + User.UID_SYSTEM + " accountId=" + Account.ACCOUNT_ID_SYSTEM + " sessionId=" + null + ") "); + String responseText = handleRequest(parameterMap, true, responseType, sb); + sb.append(" 200 " + ((responseText == null) ? 0 : responseText.length())); + + writeResponse(response, responseText, HttpStatus.SC_OK, responseType, null); + } catch (ServerApiException se) { + String responseText = getSerializedApiError(se.getErrorCode(), se.getDescription(), parameterMap, responseType); + writeResponse(response, responseText, se.getErrorCode(), responseType, se.getDescription()); + sb.append(" " + se.getErrorCode() + " " + se.getDescription()); + } catch (RuntimeException e) { + // log runtime exception like NullPointerException to help identify the source easier + s_logger.error("Unhandled exception, ", e); + throw e; + } + } finally { + s_accessLogger.info(sb.toString()); + UserContext.unregisterContext(); + } + } + + @SuppressWarnings("rawtypes") + public String handleRequest(Map params, boolean decode, String responseType, StringBuffer auditTrailSb) throws ServerApiException { + String response = null; + String[] command = null; + try { + command = (String[]) params.get("command"); + if (command == null) { + s_logger.error("invalid request, no command sent"); + if (s_logger.isTraceEnabled()) { + s_logger.trace("dumping request parameters"); + for (Object key : params.keySet()) { + String keyStr = (String) key; + String[] value = (String[]) params.get(key); + s_logger.trace(" key: " + keyStr + ", value: " + ((value == null) ? "'null'" : value[0])); + } + } + throw new ServerApiException(BaseCmd.UNSUPPORTED_ACTION_ERROR, "Invalid request, no command sent"); + } else { + Map paramMap = new HashMap(); + Set keys = params.keySet(); + Iterator keysIter = keys.iterator(); + while (keysIter.hasNext()) { + String key = (String) keysIter.next(); + if ("command".equalsIgnoreCase(key)) { + continue; + } + String[] value = (String[]) params.get(key); + + String decodedValue = null; + if (decode) { + try { + decodedValue = URLDecoder.decode(value[0], "UTF-8"); + } catch (UnsupportedEncodingException usex) { + s_logger.warn(key + " could not be decoded, value = " + value[0]); + throw new ServerApiException(BaseCmd.PARAM_ERROR, key + " could not be decoded, received value " + value[0]); + } catch (IllegalArgumentException iae) { + s_logger.warn(key + " could not be decoded, value = " + value[0]); + throw new ServerApiException(BaseCmd.PARAM_ERROR, key + " could not be decoded, received value " + value[0] + " which contains illegal characters eg.%"); + } + } else { + decodedValue = value[0]; + } + paramMap.put(key, decodedValue); + } + String cmdClassName = _apiCommands.getProperty(command[0]); + if (cmdClassName != null) { + Class cmdClass = Class.forName(cmdClassName); + BaseCmd cmdObj = (BaseCmd) cmdClass.newInstance(); + + cmdObj.setResponseType(responseType); + // This is where the command is either serialized, or directly dispatched + response = queueCommand(cmdObj, paramMap); + buildAuditTrail(auditTrailSb, command[0], response); + } else { + if (!command[0].equalsIgnoreCase("login") && !command[0].equalsIgnoreCase("logout")) { + String errorString = "Unknown API command: " + ((command == null) ? "null" : command[0]); + s_logger.warn(errorString); + auditTrailSb.append(" " + errorString); + throw new ServerApiException(BaseCmd.UNSUPPORTED_ACTION_ERROR, errorString); + } + } + } + } catch (Exception ex) { + if (ex instanceof InvalidParameterValueException) { + throw new ServerApiException(BaseCmd.PARAM_ERROR, ex.getMessage()); + } else if (ex instanceof PermissionDeniedException) { + throw new ServerApiException(BaseCmd.ACCOUNT_ERROR, ex.getMessage()); + } else if (ex instanceof ServerApiException) { + throw (ServerApiException) ex; + } else { + s_logger.error("unhandled exception executing api command: " + ((command == null) ? "null" : command[0]), ex); + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Internal server error, unable to execute request."); + } + } + return response; + } + + private String queueCommand(BaseCmd cmdObj, Map params) { + UserContext ctx = UserContext.current(); + Long userId = ctx.getCallerUserId(); + Account account = ctx.getCaller(); + if (cmdObj instanceof BaseAsyncCmd) { + Long objectId = null; + if (cmdObj instanceof BaseAsyncCreateCmd) { + BaseAsyncCreateCmd createCmd = (BaseAsyncCreateCmd) cmdObj; + _dispatcher.dispatchCreateCmd(createCmd, params); + objectId = createCmd.getEntityId(); + params.put("id", objectId.toString()); + } else { + ApiDispatcher.setupParameters(cmdObj, params); + } + + BaseAsyncCmd asyncCmd = (BaseAsyncCmd) cmdObj; + + if (userId != null) { + params.put("ctxUserId", userId.toString()); + } + if (account != null) { + params.put("ctxAccountId", String.valueOf(account.getId())); + } + + long startEventId = ctx.getStartEventId(); + asyncCmd.setStartEventId(startEventId); + + // save the scheduled event + Long eventId = EventUtils.saveScheduledEvent((userId == null) ? User.UID_SYSTEM : userId, asyncCmd.getEntityOwnerId(), asyncCmd.getEventType(), asyncCmd.getEventDescription(), + startEventId); + if (startEventId == 0) { + // There was no create event before, set current event id as start eventId + startEventId = eventId; + } + + params.put("ctxStartEventId", String.valueOf(startEventId)); + + ctx.setAccountId(asyncCmd.getEntityOwnerId()); + + AsyncJobVO job = new AsyncJobVO(); + job.setInstanceId((objectId == null) ? asyncCmd.getInstanceId() : objectId); + job.setInstanceType(asyncCmd.getInstanceType()); + job.setUserId(userId); + job.setAccountId(asyncCmd.getEntityOwnerId()); + + job.setCmd(cmdObj.getClass().getName()); + job.setCmdInfo(ApiGsonHelper.getBuilder().create().toJson(params)); + + long jobId = _asyncMgr.submitAsyncJob(job); + + if (jobId == 0L) { + String errorMsg = "Unable to schedule async job for command " + job.getCmd(); + s_logger.warn(errorMsg); + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, errorMsg); + } + + if (objectId != null) { + return ((BaseAsyncCreateCmd) asyncCmd).getResponse(jobId, objectId); + } + return ApiResponseSerializer.toSerializedString(asyncCmd.getResponse(jobId), asyncCmd.getResponseType()); + } else { + _dispatcher.dispatch(cmdObj, params); + + // if the command is of the listXXXCommand, we will need to also return the + // the job id and status if possible + if (cmdObj instanceof BaseListCmd) { + buildAsyncListResponse((BaseListCmd) cmdObj, account); + } + return ApiResponseSerializer.toSerializedString((ResponseObject) cmdObj.getResponseObject(), cmdObj.getResponseType()); + } + } + + private void buildAsyncListResponse(BaseListCmd command, Account account) { + List responses = ((ListResponse) command.getResponseObject()).getResponses(); + if (responses != null && responses.size() > 0) { + List jobs = null; + + // list all jobs for ROOT admin + if (account.getType() == Account.ACCOUNT_TYPE_ADMIN) { + jobs = _asyncMgr.findInstancePendingAsyncJobs(command.getInstanceType(), null); + } else { + jobs = _asyncMgr.findInstancePendingAsyncJobs(command.getInstanceType(), account.getId()); + } + + if (jobs.size() == 0) { + return; + } + + // Using maps might possibly be more efficient if the set is large enough but for now, we'll just do a + // comparison of two lists. Either way, there shouldn't be too many async jobs active for the account. + for (AsyncJob job : jobs) { + if (job.getInstanceId() == null) { + continue; + } + for (ResponseObject response : responses) { + if (response.getObjectId() != null && job.getInstanceId().longValue() == response.getObjectId().longValue()) { + response.setJobId(job.getId()); + response.setJobStatus(job.getStatus()); + } + } + } + } + } + + private void buildAuditTrail(StringBuffer auditTrailSb, String command, String result) { + if (result == null) { + return; + } + auditTrailSb.append(" " + HttpServletResponse.SC_OK + " "); + auditTrailSb.append(result); + /* + * if (command.equals("queryAsyncJobResult")){ //For this command we need to also log job status and job resultcode for + * (Pair pair : resultValues){ String key = pair.first(); if (key.equals("jobstatus")){ + * auditTrailSb.append(" "); auditTrailSb.append(key); auditTrailSb.append("="); auditTrailSb.append(pair.second()); + * }else if (key.equals("jobresultcode")){ auditTrailSb.append(" "); auditTrailSb.append(key); auditTrailSb.append("="); + * auditTrailSb.append(pair.second()); } } }else { for (Pair pair : resultValues){ if + * (pair.first().equals("jobid")){ // Its an async job so report the jobid auditTrailSb.append(" "); + * auditTrailSb.append(pair.first()); auditTrailSb.append("="); auditTrailSb.append(pair.second()); } } } + */ + } + + private static boolean isCommandAvailable(String commandName) { + boolean isCommandAvailable = false; + isCommandAvailable = s_allCommands.contains(commandName); + return isCommandAvailable; + } + + public boolean verifyRequest(Map requestParameters, Long userId) throws ServerApiException { + try { + String apiKey = null; + String secretKey = null; + String signature = null; + String unsignedRequest = null; + + String[] command = (String[]) requestParameters.get("command"); + if (command == null) { + s_logger.info("missing command, ignoring request..."); + return false; + } + + String commandName = command[0]; + + // if userId not null, that mean that user is logged in + if (userId != null) { + Long accountId = ApiDBUtils.findUserById(userId).getAccountId(); + Account userAccount = _ms.findAccountById(accountId); + short accountType = userAccount.getType(); + + if (!isCommandAvailable(accountType, commandName)) { + s_logger.warn("The given command:" + commandName + " does not exist"); + throw new ServerApiException(BaseCmd.UNSUPPORTED_ACTION_ERROR, "The given command:" + commandName + " does not exist"); + } + return true; + } else { + // check against every available command to see if the command exists or not + if (!isCommandAvailable(commandName) && !commandName.equals("login") && !commandName.equals("logout")) { + s_logger.warn("The given command:" + commandName + " does not exist"); + throw new ServerApiException(BaseCmd.UNSUPPORTED_ACTION_ERROR, "The given command:" + commandName + " does not exist"); + } + } + + // - build a request string with sorted params, make sure it's all lowercase + // - sign the request, verify the signature is the same + List parameterNames = new ArrayList(); + + for (Object paramNameObj : requestParameters.keySet()) { + parameterNames.add((String) paramNameObj); // put the name in a list that we'll sort later + } + + Collections.sort(parameterNames); + + for (String paramName : parameterNames) { + // parameters come as name/value pairs in the form String/String[] + String paramValue = ((String[]) requestParameters.get(paramName))[0]; + + if ("signature".equalsIgnoreCase(paramName)) { + signature = paramValue; + } else { + if ("apikey".equalsIgnoreCase(paramName)) { + apiKey = paramValue; + } + + if (unsignedRequest == null) { + unsignedRequest = paramName + "=" + URLEncoder.encode(paramValue, "UTF-8").replaceAll("\\+", "%20"); + } else { + unsignedRequest = unsignedRequest + "&" + paramName + "=" + URLEncoder.encode(paramValue, "UTF-8").replaceAll("\\+", "%20"); + } + } + } + + // if api/secret key are passed to the parameters + if ((signature == null) || (apiKey == null)) { + if (s_logger.isDebugEnabled()) { + s_logger.info("expired session, missing signature, or missing apiKey -- ignoring request...sig: " + signature + ", apiKey: " + apiKey); + } + return false; // no signature, bad request + } + + Transaction txn = Transaction.open(Transaction.CLOUD_DB); + txn.close(); + User user = null; + // verify there is a user with this api key + Pair userAcctPair = _ms.findUserByApiKey(apiKey); + if (userAcctPair == null) { + s_logger.info("apiKey does not map to a valid user -- ignoring request, apiKey: " + apiKey); + return false; + } + + user = userAcctPair.first(); + Account account = userAcctPair.second(); + + if (user.getState() != Account.State.enabled || !account.getState().equals(Account.State.enabled)) { + s_logger.info("disabled or locked user accessing the api, userid = " + user.getId() + "; name = " + user.getUsername() + "; state: " + user.getState() + "; accountState: " + + account.getState()); + return false; + } + + UserContext.updateContext(user.getId(), account, null); + + if (!isCommandAvailable(account.getType(), commandName)) { + s_logger.warn("The given command:" + commandName + " does not exist"); + throw new ServerApiException(BaseCmd.UNSUPPORTED_ACTION_ERROR, "The given command:" + commandName + " does not exist"); + } + + // verify secret key exists + secretKey = user.getSecretKey(); + if (secretKey == null) { + s_logger.info("User does not have a secret key associated with the account -- ignoring request, username: " + user.getUsername()); + return false; + } + + unsignedRequest = unsignedRequest.toLowerCase(); + + Mac mac = Mac.getInstance("HmacSHA1"); + SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(), "HmacSHA1"); + mac.init(keySpec); + mac.update(unsignedRequest.getBytes()); + byte[] encryptedBytes = mac.doFinal(); + String computedSignature = Base64.encodeBytes(encryptedBytes); + boolean equalSig = signature.equals(computedSignature); + if (!equalSig) { + s_logger.info("User signature: " + signature + " is not equaled to computed signature: " + computedSignature); + } + return equalSig; + } catch (Exception ex) { + if (ex instanceof ServerApiException && ((ServerApiException) ex).getErrorCode() == BaseCmd.UNSUPPORTED_ACTION_ERROR) { + throw (ServerApiException) ex; + } + s_logger.error("unable to verifty request signature", ex); + } + return false; + } + + public void loginUser(HttpSession session, String username, String password, Long domainId, String domainPath, Map requestParameters) throws CloudAuthenticationException { + // We will always use domainId first. If that does not exist, we will use domain name. If THAT doesn't exist + // we will default to ROOT + if (domainId == null) { + if (domainPath == null || domainPath.trim().length() == 0) { + domainId = DomainVO.ROOT_DOMAIN; + } else { + Domain domainObj = _ms.findDomainByPath(domainPath); + if (domainObj != null) { + domainId = domainObj.getId(); + } else { // if an unknown path is passed in, fail the login call + throw new CloudAuthenticationException("Unable to find the domain from the path " + domainPath); + } + } + } + + UserAccount userAcct = _ms.authenticateUser(username, password, domainId, requestParameters); + if (userAcct != null) { + String timezone = userAcct.getTimezone(); + float offsetInHrs = 0f; + if (timezone != null) { + TimeZone t = TimeZone.getTimeZone(timezone); + s_logger.info("Current user logged in under " + timezone + " timezone"); + + java.util.Date date = new java.util.Date(); + long longDate = date.getTime(); + float offsetInMs = (t.getOffset(longDate)); + offsetInHrs = offsetInMs / (1000 * 60 * 60); + s_logger.info("Timezone offset from UTC is: " + offsetInHrs); + } + + Account account = _ms.findAccountById(userAcct.getAccountId()); + + // set the userId and account object for everyone + session.setAttribute("userid", userAcct.getId()); + session.setAttribute("username", userAcct.getUsername()); + session.setAttribute("firstname", userAcct.getFirstname()); + session.setAttribute("lastname", userAcct.getLastname()); + session.setAttribute("accountobj", account); + session.setAttribute("account", account.getAccountName()); + session.setAttribute("domainid", account.getDomainId()); + session.setAttribute("type", Short.valueOf(account.getType()).toString()); + session.setAttribute("registrationtoken", userAcct.getRegistrationToken()); + session.setAttribute("registered", new Boolean(userAcct.isRegistered()).toString()); + + if (timezone != null) { + session.setAttribute("timezone", timezone); + session.setAttribute("timezoneoffset", Float.valueOf(offsetInHrs).toString()); + } + + // (bug 5483) generate a session key that the user must submit on every request to prevent CSRF, add that + // to the login response so that session-based authenticators know to send the key back + SecureRandom sesssionKeyRandom = new SecureRandom(); + byte sessionKeyBytes[] = new byte[20]; + sesssionKeyRandom.nextBytes(sessionKeyBytes); + String sessionKey = Base64.encodeBytes(sessionKeyBytes); + session.setAttribute("sessionkey", sessionKey); + + return; + } + throw new CloudAuthenticationException("Unable to find user " + username + " in domain " + domainId); + } + + public void logoutUser(long userId) { + _ms.logoutUser(Long.valueOf(userId)); + return; + } + + public boolean verifyUser(Long userId) { + User user = _ms.findUserById(userId); + Account account = null; + if (user != null) { + account = _ms.findAccountById(user.getAccountId()); + } + + if ((user == null) || (user.getRemoved() != null) || !user.getState().equals(Account.State.enabled) || (account == null) || !account.getState().equals(Account.State.enabled)) { + s_logger.warn("Deleted/Disabled/Locked user with id=" + userId + " attempting to access public API"); + return false; + } + return true; + } + + public static boolean isCommandAvailable(short accountType, String commandName) { + boolean isCommandAvailable = false; + switch (accountType) { + case Account.ACCOUNT_TYPE_ADMIN: + isCommandAvailable = s_adminCommands.contains(commandName); + break; + case Account.ACCOUNT_TYPE_DOMAIN_ADMIN: + isCommandAvailable = s_resellerCommands.contains(commandName); + break; + case Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN: + isCommandAvailable = s_resourceDomainAdminCommands.contains(commandName); + break; + case Account.ACCOUNT_TYPE_NORMAL: + isCommandAvailable = s_userCommands.contains(commandName); + break; + } + return isCommandAvailable; + } + + // FIXME: rather than isError, we might was to pass in the status code to give more flexibility + private void writeResponse(HttpResponse resp, final String responseText, final int statusCode, String responseType, String reasonPhrase) { + try { + resp.setStatusCode(statusCode); + resp.setReasonPhrase(reasonPhrase); + + BasicHttpEntity body = new BasicHttpEntity(); + if (BaseCmd.RESPONSE_TYPE_JSON.equalsIgnoreCase(responseType)) { + // JSON response + body.setContentType("text/javascript"); + if (responseText == null) { + body.setContent(new ByteArrayInputStream("{ \"error\" : { \"description\" : \"Internal Server Error\" } }".getBytes("UTF-8"))); + } + } else { + body.setContentType("text/xml"); + if (responseText == null) { + body.setContent(new ByteArrayInputStream("Internal Server Error".getBytes("UTF-8"))); + } + } + + if (responseText != null) { + body.setContent(new ByteArrayInputStream(responseText.getBytes("UTF-8"))); + } + resp.setEntity(body); + } catch (Exception ex) { + s_logger.error("error!", ex); + } + } + + // FIXME: the following two threads are copied from + // http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/httpcore/src/examples/org/apache/http/examples/ElementalHttpServer.java + // we have to cite a license if we are using this code directly, so we need to add the appropriate citation or modify the + // code to be very specific to our needs + static class ListenerThread extends Thread { + private HttpService _httpService = null; + private ServerSocket _serverSocket = null; + private HttpParams _params = null; + + public ListenerThread(ApiServer requestHandler, int port) { + try { + _serverSocket = new ServerSocket(port); + } catch (IOException ioex) { + s_logger.error("error initializing api server", ioex); + return; + } + + _params = new BasicHttpParams(); + _params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 30000).setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024) + .setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false).setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true) + .setParameter(CoreProtocolPNames.ORIGIN_SERVER, "HttpComponents/1.1"); + + // Set up the HTTP protocol processor + BasicHttpProcessor httpproc = new BasicHttpProcessor(); + httpproc.addInterceptor(new ResponseDate()); + httpproc.addInterceptor(new ResponseServer()); + httpproc.addInterceptor(new ResponseContent()); + httpproc.addInterceptor(new ResponseConnControl()); + + // Set up request handlers + HttpRequestHandlerRegistry reqistry = new HttpRequestHandlerRegistry(); + reqistry.register("*", requestHandler); + + // Set up the HTTP service + _httpService = new HttpService(httpproc, new NoConnectionReuseStrategy(), new DefaultHttpResponseFactory()); + _httpService.setParams(_params); + _httpService.setHandlerResolver(reqistry); + } + + @Override + public void run() { + s_logger.info("ApiServer listening on port " + _serverSocket.getLocalPort()); + while (!Thread.interrupted()) { + try { + // Set up HTTP connection + Socket socket = _serverSocket.accept(); + DefaultHttpServerConnection conn = new DefaultHttpServerConnection(); + conn.bind(socket, _params); + + // Execute a new worker task to handle the request + _executor.execute(new WorkerTask(_httpService, conn, _workerCount++)); + } catch (InterruptedIOException ex) { + break; + } catch (IOException e) { + s_logger.error("I/O error initializing connection thread", e); + break; + } + } + } + } + + static class WorkerTask implements Runnable { + private final HttpService _httpService; + private final HttpServerConnection _conn; + + public WorkerTask(final HttpService httpService, final HttpServerConnection conn, final int count) { + _httpService = httpService; + _conn = conn; + } + + @Override + public void run() { + HttpContext context = new BasicHttpContext(null); + try { + while (!Thread.interrupted() && _conn.isOpen()) { + try { + _httpService.handleRequest(_conn, context); + _conn.close(); + } finally { + StackMaid.current().exitCleanup(); + } + } + } catch (ConnectionClosedException ex) { + if (s_logger.isTraceEnabled()) { + s_logger.trace("ApiServer: Client closed connection"); + } + } catch (IOException ex) { + if (s_logger.isTraceEnabled()) { + s_logger.trace("ApiServer: IOException - " + ex); + } + } catch (HttpException ex) { + s_logger.warn("ApiServer: Unrecoverable HTTP protocol violation" + ex); + } finally { + try { + _conn.shutdown(); + } catch (IOException ignore) { + } + } + } + } + + public String getSerializedApiError(int errorCode, String errorText, Map apiCommandParams, String responseType) { + String responseName = null; + String cmdClassName = null; + + String responseText = null; + + try { + if (errorCode == BaseCmd.UNSUPPORTED_ACTION_ERROR || apiCommandParams == null || apiCommandParams.isEmpty()) { + responseName = "errorresponse"; + } else { + String cmdName = ((String[]) apiCommandParams.get("command"))[0]; + cmdClassName = _apiCommands.getProperty(cmdName); + if (cmdClassName != null) { + Class claz = Class.forName(cmdClassName); + responseName = ((BaseCmd) claz.newInstance()).getCommandName(); + } else { + responseName = "errorresponse"; + } + } + + ExceptionResponse apiResponse = new ExceptionResponse(); + apiResponse.setErrorCode(errorCode); + apiResponse.setErrorText(errorText); + apiResponse.setResponseName(responseName); + responseText = ApiResponseSerializer.toSerializedString(apiResponse, responseType); + + } catch (Exception e) { + s_logger.error("Exception responding to http request", e); + } + return responseText; + } +} diff --git a/server/src/com/cloud/capacity/CapacityManagerImpl.java b/server/src/com/cloud/capacity/CapacityManagerImpl.java index 6eb9c077928..29c25791c86 100644 --- a/server/src/com/cloud/capacity/CapacityManagerImpl.java +++ b/server/src/com/cloud/capacity/CapacityManagerImpl.java @@ -29,6 +29,9 @@ import javax.naming.ConfigurationException; import org.apache.log4j.Logger; +import com.cloud.agent.AgentManager; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupRoutingCommand; import com.cloud.capacity.dao.CapacityDao; import com.cloud.configuration.Config; import com.cloud.configuration.dao.ConfigurationDao; @@ -60,12 +63,15 @@ public class CapacityManagerImpl implements CapacityManager , StateListener capacitySC = _capacityDao + .createSearchCriteria(); + capacitySC.addAnd("hostOrPoolId", SearchCriteria.Op.EQ, server.getId()); + capacitySC.addAnd("dataCenterId", SearchCriteria.Op.EQ, + server.getDataCenterId()); + capacitySC.addAnd("podId", SearchCriteria.Op.EQ, server.getPodId()); + + + if (startup instanceof StartupRoutingCommand) { + SearchCriteria capacityCPU = _capacityDao + .createSearchCriteria(); + capacityCPU.addAnd("hostOrPoolId", SearchCriteria.Op.EQ, + server.getId()); + capacityCPU.addAnd("dataCenterId", SearchCriteria.Op.EQ, + server.getDataCenterId()); + capacityCPU + .addAnd("podId", SearchCriteria.Op.EQ, server.getPodId()); + capacityCPU.addAnd("capacityType", SearchCriteria.Op.EQ, + CapacityVO.CAPACITY_TYPE_CPU); + List capacityVOCpus = _capacityDao.search(capacitySC, + null); + + if (capacityVOCpus != null && !capacityVOCpus.isEmpty()) { + CapacityVO CapacityVOCpu = capacityVOCpus.get(0); + long newTotalCpu = (long) (server.getCpus().longValue() + * server.getSpeed().longValue() * _cpuOverProvisioningFactor); + if ((CapacityVOCpu.getTotalCapacity() <= newTotalCpu) + || ((CapacityVOCpu.getUsedCapacity() + CapacityVOCpu + .getReservedCapacity()) <= newTotalCpu)) { + CapacityVOCpu.setTotalCapacity(newTotalCpu); + } else if ((CapacityVOCpu.getUsedCapacity() + + CapacityVOCpu.getReservedCapacity() > newTotalCpu) + && (CapacityVOCpu.getUsedCapacity() < newTotalCpu)) { + CapacityVOCpu.setReservedCapacity(0); + CapacityVOCpu.setTotalCapacity(newTotalCpu); + } else { + s_logger.debug("What? new cpu is :" + newTotalCpu + + ", old one is " + CapacityVOCpu.getUsedCapacity() + + "," + CapacityVOCpu.getReservedCapacity() + "," + + CapacityVOCpu.getTotalCapacity()); + } + _capacityDao.update(CapacityVOCpu.getId(), CapacityVOCpu); + } else { + CapacityVO capacity = new CapacityVO( + server.getId(), + server.getDataCenterId(), + server.getPodId(), + server.getClusterId(), + 0L, + (long) (server.getCpus().longValue() + * server.getSpeed().longValue() * _cpuOverProvisioningFactor), + CapacityVO.CAPACITY_TYPE_CPU); + _capacityDao.persist(capacity); + } + + SearchCriteria capacityMem = _capacityDao + .createSearchCriteria(); + capacityMem.addAnd("hostOrPoolId", SearchCriteria.Op.EQ, + server.getId()); + capacityMem.addAnd("dataCenterId", SearchCriteria.Op.EQ, + server.getDataCenterId()); + capacityMem + .addAnd("podId", SearchCriteria.Op.EQ, server.getPodId()); + capacityMem.addAnd("capacityType", SearchCriteria.Op.EQ, + CapacityVO.CAPACITY_TYPE_MEMORY); + List capacityVOMems = _capacityDao.search(capacityMem, + null); + + if (capacityVOMems != null && !capacityVOMems.isEmpty()) { + CapacityVO CapacityVOMem = capacityVOMems.get(0); + long newTotalMem = server.getTotalMemory(); + if (CapacityVOMem.getTotalCapacity() <= newTotalMem + || (CapacityVOMem.getUsedCapacity() + + CapacityVOMem.getReservedCapacity() <= newTotalMem)) { + CapacityVOMem.setTotalCapacity(newTotalMem); + } else if (CapacityVOMem.getUsedCapacity() + + CapacityVOMem.getReservedCapacity() > newTotalMem + && CapacityVOMem.getUsedCapacity() < newTotalMem) { + CapacityVOMem.setReservedCapacity(0); + CapacityVOMem.setTotalCapacity(newTotalMem); + } else { + s_logger.debug("What? new cpu is :" + newTotalMem + + ", old one is " + CapacityVOMem.getUsedCapacity() + + "," + CapacityVOMem.getReservedCapacity() + "," + + CapacityVOMem.getTotalCapacity()); + } + _capacityDao.update(CapacityVOMem.getId(), CapacityVOMem); + } else { + CapacityVO capacity = new CapacityVO(server.getId(), + server.getDataCenterId(), server.getPodId(), server.getClusterId(), 0L, + server.getTotalMemory(), + CapacityVO.CAPACITY_TYPE_MEMORY); + _capacityDao.persist(capacity); + } + } + + } + + + } diff --git a/server/src/com/cloud/capacity/ComputeCapacityListener.java b/server/src/com/cloud/capacity/ComputeCapacityListener.java new file mode 100644 index 00000000000..612a1aba0b9 --- /dev/null +++ b/server/src/com/cloud/capacity/ComputeCapacityListener.java @@ -0,0 +1,195 @@ +/** + * Copyright (C) 2010 Cloud.com. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later +version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.capacity; + +import java.util.List; + +import org.apache.log4j.Logger; + +import com.cloud.agent.Listener; +import com.cloud.agent.api.AgentControlAnswer; +import com.cloud.agent.api.AgentControlCommand; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupRoutingCommand; +import com.cloud.agent.api.StartupStorageCommand; +import com.cloud.capacity.dao.CapacityDao; +import com.cloud.exception.ConnectionException; +import com.cloud.host.HostVO; +import com.cloud.host.Status; +import com.cloud.utils.db.SearchCriteria; + + +public class ComputeCapacityListener implements Listener { + private static final Logger s_logger = Logger.getLogger(ComputeCapacityListener.class); + CapacityDao _capacityDao; + float _cpuOverProvisioningFactor = 1.0f; + + + public ComputeCapacityListener(CapacityDao _capacityDao, + float _overProvisioningFactor) { + super(); + this._capacityDao = _capacityDao; + this._cpuOverProvisioningFactor = _overProvisioningFactor; + } + + + @Override + public boolean processAnswers(long agentId, long seq, Answer[] answers) { + return false; + } + + + @Override + public boolean processCommands(long agentId, long seq, Command[] commands) { + return false; + } + + + @Override + public AgentControlAnswer processControlCommand(long agentId, + AgentControlCommand cmd) { + + return null; + } + + + @Override + public void processConnect(HostVO server, StartupCommand startup) throws ConnectionException { + if (!(startup instanceof StartupRoutingCommand)) { + return; + } + + + SearchCriteria capacityCPU = _capacityDao + .createSearchCriteria(); + capacityCPU.addAnd("hostOrPoolId", SearchCriteria.Op.EQ, + server.getId()); + capacityCPU.addAnd("dataCenterId", SearchCriteria.Op.EQ, + server.getDataCenterId()); + capacityCPU + .addAnd("podId", SearchCriteria.Op.EQ, server.getPodId()); + capacityCPU.addAnd("capacityType", SearchCriteria.Op.EQ, + CapacityVO.CAPACITY_TYPE_CPU); + List capacityVOCpus = _capacityDao.search(capacityCPU, + null); + + if (capacityVOCpus != null && !capacityVOCpus.isEmpty()) { + CapacityVO CapacityVOCpu = capacityVOCpus.get(0); + long newTotalCpu = (long) (server.getCpus().longValue() + * server.getSpeed().longValue() * _cpuOverProvisioningFactor); + if ((CapacityVOCpu.getTotalCapacity() <= newTotalCpu) + || ((CapacityVOCpu.getUsedCapacity() + CapacityVOCpu + .getReservedCapacity()) <= newTotalCpu)) { + CapacityVOCpu.setTotalCapacity(newTotalCpu); + } else if ((CapacityVOCpu.getUsedCapacity() + + CapacityVOCpu.getReservedCapacity() > newTotalCpu) + && (CapacityVOCpu.getUsedCapacity() < newTotalCpu)) { + CapacityVOCpu.setReservedCapacity(0); + CapacityVOCpu.setTotalCapacity(newTotalCpu); + } else { + s_logger.debug("What? new cpu is :" + newTotalCpu + + ", old one is " + CapacityVOCpu.getUsedCapacity() + + "," + CapacityVOCpu.getReservedCapacity() + "," + + CapacityVOCpu.getTotalCapacity()); + } + _capacityDao.update(CapacityVOCpu.getId(), CapacityVOCpu); + } else { + CapacityVO capacity = new CapacityVO( + server.getId(), + server.getDataCenterId(), + server.getPodId(), + server.getClusterId(), + 0L, + (long) (server.getCpus().longValue() + * server.getSpeed().longValue() * _cpuOverProvisioningFactor), + CapacityVO.CAPACITY_TYPE_CPU); + _capacityDao.persist(capacity); + } + + SearchCriteria capacityMem = _capacityDao + .createSearchCriteria(); + capacityMem.addAnd("hostOrPoolId", SearchCriteria.Op.EQ, + server.getId()); + capacityMem.addAnd("dataCenterId", SearchCriteria.Op.EQ, + server.getDataCenterId()); + capacityMem + .addAnd("podId", SearchCriteria.Op.EQ, server.getPodId()); + capacityMem.addAnd("capacityType", SearchCriteria.Op.EQ, + CapacityVO.CAPACITY_TYPE_MEMORY); + List capacityVOMems = _capacityDao.search(capacityMem, + null); + + if (capacityVOMems != null && !capacityVOMems.isEmpty()) { + CapacityVO CapacityVOMem = capacityVOMems.get(0); + long newTotalMem = server.getTotalMemory(); + if (CapacityVOMem.getTotalCapacity() <= newTotalMem + || (CapacityVOMem.getUsedCapacity() + + CapacityVOMem.getReservedCapacity() <= newTotalMem)) { + CapacityVOMem.setTotalCapacity(newTotalMem); + } else if (CapacityVOMem.getUsedCapacity() + + CapacityVOMem.getReservedCapacity() > newTotalMem + && CapacityVOMem.getUsedCapacity() < newTotalMem) { + CapacityVOMem.setReservedCapacity(0); + CapacityVOMem.setTotalCapacity(newTotalMem); + } else { + s_logger.debug("What? new cpu is :" + newTotalMem + + ", old one is " + CapacityVOMem.getUsedCapacity() + + "," + CapacityVOMem.getReservedCapacity() + "," + + CapacityVOMem.getTotalCapacity()); + } + _capacityDao.update(CapacityVOMem.getId(), CapacityVOMem); + } else { + CapacityVO capacity = new CapacityVO(server.getId(), + server.getDataCenterId(), server.getPodId(), server.getClusterId(), 0L, + server.getTotalMemory(), + CapacityVO.CAPACITY_TYPE_MEMORY); + _capacityDao.persist(capacity); + } + + } + + + + + @Override + public boolean processDisconnect(long agentId, Status state) { + return false; + } + + + @Override + public boolean isRecurring() { + return false; + } + + + @Override + public int getTimeout() { + return 0; + } + + + @Override + public boolean processTimeout(long agentId, long seq) { + return false; + } + +} diff --git a/server/src/com/cloud/capacity/StorageCapacityListener.java b/server/src/com/cloud/capacity/StorageCapacityListener.java new file mode 100644 index 00000000000..1937214d070 --- /dev/null +++ b/server/src/com/cloud/capacity/StorageCapacityListener.java @@ -0,0 +1,127 @@ +/** + * Copyright (C) 2010 Cloud.com. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later +version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.capacity; + +import java.util.List; + +import com.cloud.agent.Listener; +import com.cloud.agent.api.AgentControlAnswer; +import com.cloud.agent.api.AgentControlCommand; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupStorageCommand; +import com.cloud.capacity.dao.CapacityDao; +import com.cloud.exception.ConnectionException; +import com.cloud.host.HostVO; +import com.cloud.host.Status; +import com.cloud.storage.Storage; +import com.cloud.utils.db.SearchCriteria; + + +public class StorageCapacityListener implements Listener { + + CapacityDao _capacityDao; + float _overProvisioningFactor = 1.0f; + + + public StorageCapacityListener(CapacityDao _capacityDao, + float _overProvisioningFactor) { + super(); + this._capacityDao = _capacityDao; + this._overProvisioningFactor = _overProvisioningFactor; + } + + + @Override + public boolean processAnswers(long agentId, long seq, Answer[] answers) { + return false; + } + + + @Override + public boolean processCommands(long agentId, long seq, Command[] commands) { + return false; + } + + + @Override + public AgentControlAnswer processControlCommand(long agentId, + AgentControlCommand cmd) { + + return null; + } + + + @Override + public void processConnect(HostVO server, StartupCommand startup) throws ConnectionException { + + if (!(startup instanceof StartupStorageCommand)) { + return; + } + SearchCriteria capacitySC = _capacityDao.createSearchCriteria(); + capacitySC.addAnd("hostOrPoolId", SearchCriteria.Op.EQ, server.getId()); + capacitySC.addAnd("dataCenterId", SearchCriteria.Op.EQ, + server.getDataCenterId()); + capacitySC.addAnd("podId", SearchCriteria.Op.EQ, server.getPodId()); + List capacities = _capacityDao.search(capacitySC, null); + + // remove old entries, we'll recalculate them anyway + if ((capacities != null) && !capacities.isEmpty()) { + for (CapacityVO capacity : capacities) { + _capacityDao.remove(capacity.getId()); + } + } + + StartupStorageCommand ssCmd = (StartupStorageCommand) startup; + if (ssCmd.getResourceType() == Storage.StorageResourceType.STORAGE_HOST) { + CapacityVO capacity = new CapacityVO(server.getId(), + server.getDataCenterId(), server.getPodId(), server.getClusterId(), 0L, + (long) (server.getTotalSize() * _overProvisioningFactor), + CapacityVO.CAPACITY_TYPE_STORAGE_ALLOCATED); + _capacityDao.persist(capacity); + } + + } + + + @Override + public boolean processDisconnect(long agentId, Status state) { + return false; + } + + + @Override + public boolean isRecurring() { + return false; + } + + + @Override + public int getTimeout() { + return 0; + } + + + @Override + public boolean processTimeout(long agentId, long seq) { + return false; + } + +} diff --git a/server/src/com/cloud/cluster/ClusterManagerImpl.java b/server/src/com/cloud/cluster/ClusterManagerImpl.java index e5f671605cb..8b8d4999ecc 100644 --- a/server/src/com/cloud/cluster/ClusterManagerImpl.java +++ b/server/src/com/cloud/cluster/ClusterManagerImpl.java @@ -16,1115 +16,1115 @@ * */ -package com.cloud.cluster; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.rmi.RemoteException; -import java.sql.Connection; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Date; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -import javax.ejb.Local; -import javax.naming.ConfigurationException; - -import org.apache.log4j.Logger; - -import com.cloud.agent.AgentManager; -import com.cloud.agent.AgentManager.OnError; -import com.cloud.agent.Listener; -import com.cloud.agent.api.Answer; -import com.cloud.agent.api.ChangeAgentCommand; -import com.cloud.agent.api.Command; -import com.cloud.agent.manager.Commands; -import com.cloud.cluster.dao.ManagementServerHostDao; -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.Event; -import com.cloud.host.dao.HostDao; -import com.cloud.serializer.GsonHelper; -import com.cloud.utils.DateUtil; -import com.cloud.utils.NumbersUtil; -import com.cloud.utils.Profiler; -import com.cloud.utils.PropertiesUtil; -import com.cloud.utils.component.Adapters; -import com.cloud.utils.component.ComponentLocator; -import com.cloud.utils.concurrency.NamedThreadFactory; -import com.cloud.utils.db.DB; -import com.cloud.utils.db.Transaction; -import com.cloud.utils.events.SubscriptionMgr; -import com.cloud.utils.exception.CloudRuntimeException; -import com.cloud.utils.exception.ExceptionUtil; -import com.cloud.utils.mgmt.JmxUtil; -import com.cloud.utils.net.NetUtils; -import com.google.gson.Gson; - -@Local(value={ClusterManager.class}) -public class ClusterManagerImpl implements ClusterManager { - private static final Logger s_logger = Logger.getLogger(ClusterManagerImpl.class); - - private static final int EXECUTOR_SHUTDOWN_TIMEOUT = 1000; // 1 second - - private final List listeners = new ArrayList(); - private final Map activePeers = new HashMap(); - private int heartbeatInterval = ClusterManager.DEFAULT_HEARTBEAT_INTERVAL; - private int heartbeatThreshold = ClusterManager.DEFAULT_HEARTBEAT_THRESHOLD; - - private final Map clusterPeers; - private final Map asyncCalls; - private final Gson gson; - - private AgentManager _agentMgr; - - private final ScheduledExecutorService _heartbeatScheduler = Executors.newScheduledThreadPool(1, new NamedThreadFactory("Cluster-Heartbeat")); - - private final ExecutorService _notificationExecutor = Executors.newFixedThreadPool(1, new NamedThreadFactory("Cluster-Notification")); - private List _notificationMsgs = new ArrayList(); - private Connection _heartbeatConnection = null; - - private final ExecutorService _executor; - - private ClusterServiceAdapter _currentServiceAdapter; - - private ManagementServerHostDao _mshostDao; - private HostDao _hostDao; - - // - // pay attention to _mshostId and _msid - // _mshostId is the primary key of management host table - // _msid is the unique persistent identifier that peer name is based upon - // - private Long _mshostId = null; - protected long _msid = ManagementServerNode.getManagementServerId(); - protected long _runId = System.currentTimeMillis(); - - private boolean _peerScanInited = false; - - private String _name; - private String _clusterNodeIP = "127.0.0.1"; - - public ClusterManagerImpl() { - clusterPeers = new HashMap(); - asyncCalls = new HashMap(); - - gson = GsonHelper.getBuilder().create(); - - // executor to perform remote-calls in another thread context, to avoid potential - // recursive remote calls between nodes - // - _executor = Executors.newCachedThreadPool(new NamedThreadFactory("Cluster-Worker")); - } - - @Override - public Answer[] sendToAgent(Long hostId, Command [] cmds, boolean stopOnError) - throws AgentUnavailableException, OperationTimedoutException { - Commands commands = new Commands(stopOnError ? OnError.Stop : OnError.Continue); - for (Command cmd : cmds) { - commands.addCommand(cmd); - } - return _agentMgr.send(hostId, commands); - } - - @Override - public long sendToAgent(Long hostId, Command[] cmds, boolean stopOnError, Listener listener) - throws AgentUnavailableException { - Commands commands = new Commands(stopOnError ? OnError.Stop : OnError.Continue); - for (Command cmd : cmds) { - commands.addCommand(cmd); - } - return _agentMgr.send(hostId, commands, listener); - } - - @Override - public boolean executeAgentUserRequest(long agentId, Event event) throws AgentUnavailableException { - return _agentMgr.executeUserRequest(agentId, event); - } - - @Override - public Boolean propagateAgentEvent(long agentId, Event event) throws AgentUnavailableException { - final String msPeer = getPeerName(agentId); - if (msPeer == null) { - return null; - } - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Propagating agent change request event:" + event.toString() + " to agent:"+ agentId); - } - Command[] cmds = new Command[1]; - cmds[0] = new ChangeAgentCommand(agentId, event); - - Answer[] answers = execute(msPeer, agentId, cmds, true); - if (answers == null) { - throw new AgentUnavailableException(agentId); - } - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Result for agent change is " + answers[0].getResult()); - } - - return answers[0].getResult(); - } - - /** - * called by DatabaseUpgradeChecker to see if there are other peers running. - * @param notVersion If version is passed in, the peers CANNOT be running at this - * version. If version is null, return true if any peer is - * running regardless of version. - * @return true if there are peers running and false if not. - */ - public static final boolean arePeersRunning(String notVersion) { - return false; //TODO: Leaving this for Kelven to take care of. - } - - @Override - public void broadcast(long agentId, Command[] cmds) { - Date cutTime = DateUtil.currentGMTTime(); - - List peers = _mshostDao.getActiveList(new Date(cutTime.getTime() - heartbeatThreshold)); - for (ManagementServerHostVO peer : peers) { - String peerName = Long.toString(peer.getMsid()); - if (getSelfPeerName().equals(peerName)) { - continue; // Skip myself. - } - try { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Forwarding " + cmds[0].toString() + " to " + peer.getMsid()); - } - Answer[] answers = execute(peerName, agentId, cmds, true); - } catch (Exception e) { - s_logger.warn("Caught exception while talkign to " + peer.getMsid()); - } - } - } - - @Override - public Answer[] execute(String strPeer, long agentId, Command [] cmds, boolean stopOnError) { - ClusterService peerService = null; - - if(s_logger.isDebugEnabled()) { - s_logger.debug(getSelfPeerName() + " -> " + strPeer + "." + agentId + " " + - gson.toJson(cmds, Command[].class)); - } - - for(int i = 0; i < 2; i++) { - try { - peerService = getPeerService(strPeer); - } catch (RemoteException e) { - s_logger.error("Unable to get cluster service on peer : " + strPeer); - } - - if(peerService != null) { - try { - if(s_logger.isDebugEnabled()) { - s_logger.debug("Send " + getSelfPeerName() + " -> " + strPeer + "." + agentId + " to remote"); - } - - long startTick = System.currentTimeMillis(); - String strResult = peerService.execute(getSelfPeerName(), agentId, gson.toJson(cmds, Command[].class), stopOnError); - if(s_logger.isDebugEnabled()) { - s_logger.debug("Completed " + getSelfPeerName() + " -> " + strPeer + "." + agentId + "in " + - (System.currentTimeMillis() - startTick) + " ms, result: " + strResult); - } - - if(strResult != null) { - try { - return gson.fromJson(strResult, Answer[].class); - } catch(Throwable e) { - s_logger.error("Exception on parsing gson package from remote call to " + strPeer); - } - } - - return null; - } catch (RemoteException e) { - invalidatePeerService(strPeer); - - if(s_logger.isInfoEnabled()) { - s_logger.info("Exception on remote execution, peer: " + strPeer + ", iteration: " - + i + ", exception message :" + e.getMessage()); - } - } - } - } - - return null; - } - - @Override - public long executeAsync(String strPeer, long agentId, Command[] cmds, boolean stopOnError, Listener listener) { - - ClusterService peerService = null; - - if(s_logger.isDebugEnabled()) { - s_logger.debug("Async " + getSelfPeerName() + " -> " + strPeer + "." + agentId + " " + - gson.toJson(cmds, Command[].class)); - } - - for(int i = 0; i < 2; i++) { - try { - peerService = getPeerService(strPeer); - } catch (RemoteException e) { - s_logger.error("Unable to get cluster service on peer : " + strPeer); - } - - if(peerService != null) { - try { - long seq = 0; - synchronized(String.valueOf(agentId).intern()) { - if(s_logger.isDebugEnabled()) { - s_logger.debug("Send Async " + getSelfPeerName() + " -> " + strPeer + "." + agentId + " to remote"); - } - - long startTick = System.currentTimeMillis(); - seq = peerService.executeAsync(getSelfPeerName(), agentId, gson.toJson(cmds, Command[].class), stopOnError); - - if(seq > 0) { - if(s_logger.isDebugEnabled()) { - s_logger.debug("Completed Async " + getSelfPeerName() + " -> " + strPeer + "." + agentId - + " in " + (System.currentTimeMillis() - startTick) + " ms" - + ", register local listener " + strPeer + "/" + seq); - } - - registerAsyncCall(strPeer, seq, listener); - } else { - s_logger.warn("Completed Async " + getSelfPeerName() + " -> " + strPeer + "." + agentId - + " in " + (System.currentTimeMillis() - startTick) + " ms, return indicates failure, seq: " + seq); - } - } - return seq; - } catch (RemoteException e) { - invalidatePeerService(strPeer); - - if(s_logger.isInfoEnabled()) { - s_logger.info("Exception on remote execution -> " + strPeer + ", iteration : " + i); - } - } - } - } - - return 0L; - } - - @Override - public boolean onAsyncResult(String executingPeer, long agentId, long seq, Answer[] answers) { - - if(s_logger.isDebugEnabled()) { - s_logger.debug("Process Async-call result from remote peer " + executingPeer + ", {" + - agentId + "-" + seq + "} answers: " + (answers != null ? gson.toJson(answers, Answer[].class): "null")); - } - - Listener listener = null; - synchronized(String.valueOf(agentId).intern()) { - // need to synchronize it with executeAsync() to make sure listener have been registered - // before this callback reaches back - listener = getAsyncCallListener(executingPeer, seq); - } - - if(listener != null) { - long startTick = System.currentTimeMillis(); - - if(s_logger.isDebugEnabled()) { - s_logger.debug("Processing answer {" + agentId + "-" + seq + "} from remote peer " + executingPeer); - } - - listener.processAnswers(agentId, seq, answers); - - if(s_logger.isDebugEnabled()) { - s_logger.debug("Answer {" + agentId + "-" + seq + "} is processed in " + - (System.currentTimeMillis() - startTick) + " ms"); - } - - if(!listener.isRecurring()) { - if(s_logger.isDebugEnabled()) { - s_logger.debug("Listener is not recurring after async-result callback {" + - agentId + "-" + seq + "}, unregister it"); - } - - unregisterAsyncCall(executingPeer, seq); - } else { - if(s_logger.isDebugEnabled()) { - s_logger.debug("Listener is recurring after async-result callback {" + agentId - +"-" + seq + "}, will keep it"); - } - return true; - } - } else { - if(s_logger.isInfoEnabled()) { - s_logger.info("Async-call Listener has not been registered yet for {" + agentId - +"-" + seq + "}"); - } - } - return false; - } - - @Override - public boolean forwardAnswer(String targetPeer, long agentId, long seq, Answer[] answers) { - - if(s_logger.isDebugEnabled()) { - s_logger.debug("Forward -> " + targetPeer + " Async-call answer {" + agentId + "-" + seq + - "} " + (answers != null? gson.toJson(answers, Answer[].class):"")); - } - - final String targetPeerF = targetPeer; - final Answer[] answersF = answers; - final long agentIdF = agentId; - final long seqF = seq; - - ClusterService peerService = null; - - for(int i = 0; i < 2; i++) { - try { - peerService = getPeerService(targetPeerF); - } catch (RemoteException e) { - s_logger.error("cluster service for peer " + targetPeerF + " no longer exists"); - } - - if(peerService != null) { - try { - boolean result = false; - - long startTick = System.currentTimeMillis(); - if(s_logger.isDebugEnabled()) { - s_logger.debug("Start forwarding Async-call answer {" + agentId + "-" + seq + "} to remote"); - } - - result = peerService.onAsyncResult(getSelfPeerName(), agentIdF, seqF, gson.toJson(answersF, Answer[].class)); - - if(s_logger.isDebugEnabled()) { - s_logger.debug("Completed forwarding Async-call answer {" + agentId + "-" + seq + "} in " + - (System.currentTimeMillis() - startTick) + " ms, return result: " + result); - } - - return result; - } catch (RemoteException e) { - s_logger.warn("Exception in performing remote call, ", e); - invalidatePeerService(targetPeerF); - } - } else { - s_logger.warn("Remote peer " + targetPeer + " no longer exists to process answer {" + agentId + "-" - + seq + "}"); - } - } - - return false; - } - - @Override - public String getPeerName(long agentHostId) { - - HostVO host = _hostDao.findById(agentHostId); - if(host != null && host.getManagementServerId() != null) { - if(getSelfPeerName().equals(Long.toString(host.getManagementServerId()))) { - return null; - } - - return Long.toString(host.getManagementServerId()); - } - return null; - } - - @Override - public ManagementServerHostVO getPeer(String mgmtServerId) { - return _mshostDao.findByMsid(Long.valueOf(mgmtServerId)); - } - - @Override - public String getSelfPeerName() { - return Long.toString(_msid); - } - - @Override - public String getSelfNodeIP() { - return _clusterNodeIP; - } - - @Override - public void registerListener(ClusterManagerListener listener) { - // Note : we don't check duplicates - synchronized(listeners) { - listeners.add(listener); - } - } - - @Override - public void unregisterListener(ClusterManagerListener listener) { - synchronized(listeners) { - listeners.remove(listener); - } - } - - public void notifyNodeJoined(List nodeList) { - synchronized(listeners) { - for(ClusterManagerListener listener : listeners) { - listener.onManagementNodeJoined(nodeList, _mshostId); - } - } - - SubscriptionMgr.getInstance().notifySubscribers(ClusterManager.ALERT_SUBJECT, this, - new ClusterNodeJoinEventArgs(_mshostId, nodeList)); - } - - public void notifyNodeLeft(List nodeList) { - synchronized(listeners) { - for(ClusterManagerListener listener : listeners) { - listener.onManagementNodeLeft(nodeList, _mshostId); - } - } - - SubscriptionMgr.getInstance().notifySubscribers(ClusterManager.ALERT_SUBJECT, this, - new ClusterNodeLeftEventArgs(_mshostId, nodeList)); - } - - public void notifyNodeIsolated() { - synchronized(listeners) { - for(ClusterManagerListener listener : listeners) { - listener.onManagementNodeIsolated(); - } - } - } - - public ClusterService getPeerService(String strPeer) throws RemoteException { - synchronized(clusterPeers) { - if(clusterPeers.containsKey(strPeer)) { - return clusterPeers.get(strPeer); - } - } - - ClusterService service = _currentServiceAdapter.getPeerService(strPeer); - - if(service != null) { - synchronized(clusterPeers) { - // re-check the peer map again to deal with the - // race conditions - if(!clusterPeers.containsKey(strPeer)) { - clusterPeers.put(strPeer, service); - } - } - } - - return service; - } - - public void invalidatePeerService(String strPeer) { - synchronized(clusterPeers) { - if(clusterPeers.containsKey(strPeer)) { - clusterPeers.remove(strPeer); - } - } - } - - private void registerAsyncCall(String strPeer, long seq, Listener listener) { - String key = strPeer + "/" + seq; - - synchronized(asyncCalls) { - if(!asyncCalls.containsKey(key)) { - asyncCalls.put(key, listener); - } - } - } - - private Listener getAsyncCallListener(String strPeer, long seq) { - String key = strPeer + "/" + seq; - - synchronized(asyncCalls) { - if(asyncCalls.containsKey(key)) { - return asyncCalls.get(key); - } - } - - return null; - } - - private void unregisterAsyncCall(String strPeer, long seq) { - String key = strPeer + "/" + seq; - - synchronized(asyncCalls) { - if(asyncCalls.containsKey(key)) { - asyncCalls.remove(key); - } - } - } - - private Runnable getHeartbeatTask() { - return new Runnable() { - @Override - public void run() { - try { - if(s_logger.isTraceEnabled()) { - s_logger.trace("Cluster manager heartbeat update, id:" + _mshostId); - } - - Connection conn = getHeartbeatConnection(); - _mshostDao.update(conn, _mshostId, getCurrentRunId(), DateUtil.currentGMTTime()); - - if(s_logger.isTraceEnabled()) { - s_logger.trace("Cluster manager peer-scan, id:" + _mshostId); - } - - if(!_peerScanInited) { - _peerScanInited = true; - initPeerScan(conn); - } - - peerScan(conn); - } catch(CloudRuntimeException e) { - s_logger.error("Runtime DB exception ", e.getCause()); - - if(e.getCause() instanceof ClusterInvalidSessionException) { - s_logger.error("Invalid cluster session found"); - queueNotification(new ClusterManagerMessage(ClusterManagerMessage.MessageType.nodeIsolated)); - } - - if(isRootCauseConnectionRelated(e.getCause())) { - s_logger.error("DB communication problem detected"); - queueNotification(new ClusterManagerMessage(ClusterManagerMessage.MessageType.nodeIsolated)); - } - - invalidHeartbeatConnection(); - } catch (Throwable e) { - if(isRootCauseConnectionRelated(e.getCause())) { - s_logger.error("DB communication problem detected"); - queueNotification(new ClusterManagerMessage(ClusterManagerMessage.MessageType.nodeIsolated)); - } - - s_logger.error("Problem with the cluster heartbeat!", e); - } - } - }; - } - - private boolean isRootCauseConnectionRelated(Throwable e) { - while(e != null) { - if(e instanceof com.mysql.jdbc.CommunicationsException || e instanceof com.mysql.jdbc.exceptions.jdbc4.CommunicationsException) - return true; - - e = e.getCause(); - } - - return false; - } - - private Connection getHeartbeatConnection() throws SQLException { - if(_heartbeatConnection != null) { - return _heartbeatConnection; - } - - _heartbeatConnection = Transaction.getStandaloneConnectionWithException(); - return _heartbeatConnection; - } - - private void invalidHeartbeatConnection() { - if(_heartbeatConnection != null) { - try { - _heartbeatConnection.close(); - } catch (SQLException e) { - s_logger.warn("Unable to close hearbeat DB connection. ", e); - } - - _heartbeatConnection = null; - } - } - - private Runnable getNotificationTask() { - return new Runnable() { - @Override - public void run() { - while(true) { - synchronized(_notificationMsgs) { - try { - _notificationMsgs.wait(1000); - } catch (InterruptedException e) { - } - } - - ClusterManagerMessage msg = null; - while((msg = getNextNotificationMessage()) != null) { - try { - switch(msg.getMessageType()) { - case nodeAdded: - if(msg.getNodes() != null && msg.getNodes().size() > 0) { - Profiler profiler = new Profiler(); - profiler.start(); - - notifyNodeJoined(msg.getNodes()); - - profiler.stop(); - if(profiler.getDuration() > 1000) { - if(s_logger.isDebugEnabled()) { - s_logger.debug("Notifying management server join event took " + profiler.getDuration() + " ms"); - } - } else { - s_logger.warn("Notifying management server join event took " + profiler.getDuration() + " ms"); - } - } - break; - - case nodeRemoved: - if(msg.getNodes() != null && msg.getNodes().size() > 0) { - Profiler profiler = new Profiler(); - profiler.start(); - - notifyNodeLeft(msg.getNodes()); - - profiler.stop(); - if(profiler.getDuration() > 1000) { - if(s_logger.isDebugEnabled()) { - s_logger.debug("Notifying management server leave event took " + profiler.getDuration() + " ms"); - } - } else { - s_logger.warn("Notifying management server leave event took " + profiler.getDuration() + " ms"); - } - } - break; - - case nodeIsolated: - notifyNodeIsolated(); - break; - - default : - assert(false); - break; - } - - } catch (Throwable e) { - s_logger.warn("Unexpected exception during cluster notification. ", e); - } - } - - try { Thread.currentThread().sleep(1000); } catch (InterruptedException e) {} - } - } - }; - } - - private void queueNotification(ClusterManagerMessage msg) { - synchronized(this._notificationMsgs) { - this._notificationMsgs.add(msg); - this._notificationMsgs.notifyAll(); - } - } - - private ClusterManagerMessage getNextNotificationMessage() { - synchronized(this._notificationMsgs) { - if(this._notificationMsgs.size() > 0) - return this._notificationMsgs.remove(0); - } - - return null; - } - - private void initPeerScan(Connection conn) { - // upon startup, for all inactive management server nodes that we see at startup time, we will send notification also to help upper layer perform - // missed cleanup - Date cutTime = DateUtil.currentGMTTime(); - List inactiveList = _mshostDao.getInactiveList(conn, new Date(cutTime.getTime() - heartbeatThreshold)); - if(inactiveList.size() > 0) { - this.queueNotification(new ClusterManagerMessage(ClusterManagerMessage.MessageType.nodeRemoved, inactiveList)); - } - } - - private void peerScan(Connection conn) { - Date cutTime = DateUtil.currentGMTTime(); - - List currentList = _mshostDao.getActiveList(conn, new Date(cutTime.getTime() - heartbeatThreshold)); - - List removedNodeList = new ArrayList(); - List invalidatedNodeList = new ArrayList(); - - if(_mshostId != null) { - // only if we have already attached to cluster, will we start to check leaving nodes - for(Map.Entry entry : activePeers.entrySet()) { - - ManagementServerHostVO current = getInListById(entry.getKey(), currentList); - if(current == null) { - if(entry.getKey().longValue() != _mshostId.longValue()) { - if(s_logger.isDebugEnabled()) { - s_logger.debug("Detected management node left, id:" + entry.getKey() + ", nodeIP:" + entry.getValue().getServiceIP()); - } - removedNodeList.add(entry.getValue()); - } - } else { - if(current.getRunid() == 0) { - if(entry.getKey().longValue() != _mshostId.longValue()) { - if(s_logger.isDebugEnabled()) { - s_logger.debug("Detected management node left because of invalidated session, id:" + entry.getKey() + ", nodeIP:" + entry.getValue().getServiceIP()); - } - invalidatedNodeList.add(entry.getValue()); - } - } else { - if(entry.getValue().getRunid() != current.getRunid()) { - if(s_logger.isDebugEnabled()) { - s_logger.debug("Detected management node left and rejoined quickly, id:" + entry.getKey() + ", nodeIP:" + entry.getValue().getServiceIP()); - } - - entry.getValue().setRunid(current.getRunid()); - } - } - } - } - } - - // process invalidated node list - if(invalidatedNodeList.size() > 0) { - for(ManagementServerHostVO mshost : invalidatedNodeList) { - activePeers.remove(mshost.getId()); - try { - JmxUtil.unregisterMBean("ClusterManager", "Node " + mshost.getId()); - } catch(Exception e) { - s_logger.warn("Unable to deregiester cluster node from JMX monitoring due to exception " + e.toString()); - } - } - - this.queueNotification(new ClusterManagerMessage(ClusterManagerMessage.MessageType.nodeRemoved, invalidatedNodeList)); - } - - // process removed node list - Iterator it = removedNodeList.iterator(); - while(it.hasNext()) { - ManagementServerHostVO mshost = it.next(); - if(!pingManagementNode(mshost)) { - s_logger.warn("Management node " + mshost.getId() + " is detected inactive by timestamp and also not pingable"); - activePeers.remove(mshost.getId()); - _mshostDao.invalidateRunSession(conn, mshost.getId(), mshost.getRunid()); - try { - JmxUtil.unregisterMBean("ClusterManager", "Node " + mshost.getId()); - } catch(Exception e) { - s_logger.warn("Unable to deregiester cluster node from JMX monitoring due to exception " + e.toString()); - } - } else { - s_logger.info("Management node " + mshost.getId() + " is detected inactive by timestamp but is pingable"); - it.remove(); - } - } - - if(removedNodeList.size() > 0) - this.queueNotification(new ClusterManagerMessage(ClusterManagerMessage.MessageType.nodeRemoved, removedNodeList)); - - List newNodeList = new ArrayList(); - for(ManagementServerHostVO mshost : currentList) { - if(!activePeers.containsKey(mshost.getId())) { - activePeers.put(mshost.getId(), mshost); - - if(s_logger.isDebugEnabled()) { - s_logger.debug("Detected management node joined, id:" + mshost.getId() + ", nodeIP:" + mshost.getServiceIP()); - } - newNodeList.add(mshost); - - try { - JmxUtil.registerMBean("ClusterManager", "Node " + mshost.getId(), new ClusterManagerMBeanImpl(this, mshost)); - } catch(Exception e) { - s_logger.warn("Unable to regiester cluster node into JMX monitoring due to exception " + ExceptionUtil.toString(e)); - } - } - } - - if(newNodeList.size() > 0) - this.queueNotification(new ClusterManagerMessage(ClusterManagerMessage.MessageType.nodeAdded, newNodeList)); - } - - private static ManagementServerHostVO getInListById(Long id, List l) { - for(ManagementServerHostVO mshost : l) { - if(mshost.getId() == id) { - return mshost; - } - } - return null; - } - - @Override - public String getName() { - return _name; - } - - @Override @DB - public boolean start() { - if(s_logger.isInfoEnabled()) { - s_logger.info("Starting cluster manager, msid : " + _msid); - } - - Transaction txn = Transaction.currentTxn(); - try { - txn.start(); - - final Class c = this.getClass(); - String version = c.getPackage().getImplementationVersion(); - - ManagementServerHostVO mshost = _mshostDao.findByMsid(_msid); - if(mshost == null) { - mshost = new ManagementServerHostVO(); - mshost.setMsid(_msid); - mshost.setRunid(this.getCurrentRunId()); - mshost.setName(NetUtils.getHostName()); - mshost.setVersion(version); - mshost.setServiceIP(_clusterNodeIP); - mshost.setServicePort(_currentServiceAdapter.getServicePort()); - mshost.setLastUpdateTime(DateUtil.currentGMTTime()); - mshost.setRemoved(null); - mshost.setAlertCount(0); - mshost.setState(ManagementServerNode.State.Up); - _mshostDao.persist(mshost); - - if(s_logger.isInfoEnabled()) { - s_logger.info("New instance of management server msid " + _msid + " is being started"); - } - } else { - if(s_logger.isInfoEnabled()) { - s_logger.info("Management server " + _msid + " is being started"); - } - - _mshostDao.update(mshost.getId(), getCurrentRunId(), NetUtils.getHostName(), version, - _clusterNodeIP, _currentServiceAdapter.getServicePort(), DateUtil.currentGMTTime()); - } - - txn.commit(); - - _mshostId = mshost.getId(); - if(s_logger.isInfoEnabled()) { - s_logger.info("Management server (host id : " + _mshostId + ") is available at " + _clusterNodeIP + ":" + _currentServiceAdapter.getServicePort()); - } - - // use seperated thread for heartbeat updates - _heartbeatScheduler.scheduleAtFixedRate(getHeartbeatTask(), heartbeatInterval, - heartbeatInterval, TimeUnit.MILLISECONDS); - _notificationExecutor.submit(getNotificationTask()); - } catch (Throwable e) { - s_logger.error("Unexpected exception : ", e); - txn.rollback(); - - throw new CloudRuntimeException("Unable to initialize cluster info into database"); - } - - if(s_logger.isInfoEnabled()) { - s_logger.info("Cluster manager is started"); - } - - return true; - } - - @Override - public boolean stop() { - if(_mshostId != null) { - _mshostDao.remove(_mshostId); - } - - _heartbeatScheduler.shutdownNow(); - _executor.shutdownNow(); - - try { - _heartbeatScheduler.awaitTermination(EXECUTOR_SHUTDOWN_TIMEOUT, TimeUnit.MILLISECONDS); - _executor.awaitTermination(EXECUTOR_SHUTDOWN_TIMEOUT, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - } - - if(s_logger.isInfoEnabled()) { - s_logger.info("Cluster manager is stopped"); - } - - return true; - } - - @Override - public boolean configure(String name, Map params) throws ConfigurationException { - if(s_logger.isInfoEnabled()) { - s_logger.info("Start configuring cluster manager : " + name); - } - _name = name; - - ComponentLocator locator = ComponentLocator.getCurrentLocator(); - _agentMgr = locator.getManager(AgentManager.class); - if (_agentMgr == null) { - throw new ConfigurationException("Unable to get " + AgentManager.class.getName()); - } - - _mshostDao = locator.getDao(ManagementServerHostDao.class); - if(_mshostDao == null) { - throw new ConfigurationException("Unable to get " + ManagementServerHostDao.class.getName()); - } - - _hostDao = locator.getDao(HostDao.class); - if(_hostDao == null) { - throw new ConfigurationException("Unable to get " + HostDao.class.getName()); - } - - ConfigurationDao configDao = locator.getDao(ConfigurationDao.class); - if (configDao == null) { - throw new ConfigurationException("Unable to get the configuration dao."); - } - - Map configs = configDao.getConfiguration("management-server", params); - - String value = configs.get("cluster.heartbeat.interval"); - if(value != null) { - heartbeatInterval = NumbersUtil.parseInt(value, ClusterManager.DEFAULT_HEARTBEAT_INTERVAL); - } - - value = configs.get("cluster.heartbeat.threshold"); - if(value != null) { - heartbeatThreshold = NumbersUtil.parseInt(value, ClusterManager.DEFAULT_HEARTBEAT_THRESHOLD); - } - - File dbPropsFile = PropertiesUtil.findConfigFile("db.properties"); - Properties dbProps = new Properties(); - try { - dbProps.load(new FileInputStream(dbPropsFile)); - } catch (FileNotFoundException e) { - throw new ConfigurationException("Unable to find db.properties"); - } catch (IOException e) { - throw new ConfigurationException("Unable to load db.properties content"); - } - _clusterNodeIP = dbProps.getProperty("cluster.node.IP"); - if(_clusterNodeIP == null) { - _clusterNodeIP = "127.0.0.1"; - } - _clusterNodeIP = _clusterNodeIP.trim(); - - if(s_logger.isInfoEnabled()) { - s_logger.info("Cluster node IP : " + _clusterNodeIP); - } - - if(!NetUtils.isLocalAddress(_clusterNodeIP)) { - throw new ConfigurationException("cluster node IP should be valid local address where the server is running, please check your configuration"); - } - - Adapters adapters = locator.getAdapters(ClusterServiceAdapter.class); - if (adapters == null || !adapters.isSet()) { - throw new ConfigurationException("Unable to get cluster service adapters"); - } - Enumeration it = adapters.enumeration(); - if(it.hasMoreElements()) { - _currentServiceAdapter = it.nextElement(); - } - - if(_currentServiceAdapter == null) { - throw new ConfigurationException("Unable to set current cluster service adapter"); - } - - checkConflicts(); - - if(s_logger.isInfoEnabled()) { - s_logger.info("Cluster manager is configured."); - } - return true; - } - - @Override - public long getManagementNodeId() { - return _msid; - } - - @Override - public long getCurrentRunId() { - return _runId; - } - - @Override - public boolean isManagementNodeAlive(long msid) { - ManagementServerHostVO mshost = _mshostDao.findByMsid(msid); - if(mshost != null) { - if(mshost.getLastUpdateTime().getTime() >= DateUtil.currentGMTTime().getTime() - heartbeatThreshold) { - return true; - } - } - - return false; - } - - @Override - public boolean pingManagementNode(long msid) { - ManagementServerHostVO mshost = _mshostDao.findByMsid(msid); - if(mshost == null) { - return false; - } - - return pingManagementNode(mshost); - } - - private boolean pingManagementNode(ManagementServerHostVO mshost) { - - String targetIp = mshost.getServiceIP(); - if("127.0.0.1".equals(targetIp) || "0.0.0.0".equals(targetIp)) { - s_logger.info("ping management node cluster service can not be performed on self"); - return false; - } - - String targetPeer = String.valueOf(mshost.getMsid()); - ClusterService peerService = null; - for(int i = 0; i < 2; i++) { - try { - peerService = getPeerService(targetPeer); - } catch (RemoteException e) { - s_logger.error("cluster service for peer " + targetPeer + " no longer exists"); - } - - if(peerService != null) { - try { - return peerService.ping(getSelfPeerName()); - } catch (RemoteException e) { - s_logger.warn("Exception in performing remote call, ", e); - invalidatePeerService(targetPeer); - } - } else { - s_logger.warn("Remote peer " + mshost.getMsid() + " no longer exists"); - } - } - - return false; - } - - - @Override - public int getHeartbeatThreshold() { - return this.heartbeatThreshold; - } - - public int getHeartbeatInterval() { - return this.heartbeatInterval; - } - - public void setHeartbeatThreshold(int threshold) { - heartbeatThreshold = threshold; - } - - private void checkConflicts() throws ConfigurationException { - Date cutTime = DateUtil.currentGMTTime(); - List peers = _mshostDao.getActiveList(new Date(cutTime.getTime() - heartbeatThreshold)); - for(ManagementServerHostVO peer : peers) { - String peerIP = peer.getServiceIP().trim(); - if(_clusterNodeIP.equals(peerIP)) { - if("127.0.0.1".equals(_clusterNodeIP)) { - if(pingManagementNode(peer.getMsid())) { - String msg = "Detected another management node with localhost IP is already running, please check your cluster configuration"; - s_logger.error(msg); - throw new ConfigurationException(msg); - } else { - String msg = "Detected another management node with localhost IP is considered as running in DB, however it is not pingable, we will continue cluster initialization with this management server node"; - s_logger.info(msg); - } - } else { - if(pingManagementNode(peer.getMsid())) { - String msg = "Detected that another management node with the same IP " + peer.getServiceIP() + " is already running, please check your cluster configuration"; - s_logger.error(msg); - throw new ConfigurationException(msg); - } else { - String msg = "Detected that another management node with the same IP " + peer.getServiceIP() + " is considered as running in DB, however it is not pingable, we will continue cluster initialization with this management server node"; - s_logger.info(msg); - } - } - } - } - } -} +package com.cloud.cluster; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.rmi.RemoteException; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Date; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.AgentManager.OnError; +import com.cloud.agent.Listener; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.ChangeAgentCommand; +import com.cloud.agent.api.Command; +import com.cloud.agent.manager.Commands; +import com.cloud.cluster.dao.ManagementServerHostDao; +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.Event; +import com.cloud.host.dao.HostDao; +import com.cloud.serializer.GsonHelper; +import com.cloud.utils.DateUtil; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.Profiler; +import com.cloud.utils.PropertiesUtil; +import com.cloud.utils.component.Adapters; +import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.concurrency.NamedThreadFactory; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.events.SubscriptionMgr; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.exception.ExceptionUtil; +import com.cloud.utils.mgmt.JmxUtil; +import com.cloud.utils.net.NetUtils; +import com.google.gson.Gson; + +@Local(value={ClusterManager.class}) +public class ClusterManagerImpl implements ClusterManager { + private static final Logger s_logger = Logger.getLogger(ClusterManagerImpl.class); + + private static final int EXECUTOR_SHUTDOWN_TIMEOUT = 1000; // 1 second + + private final List listeners = new ArrayList(); + private final Map activePeers = new HashMap(); + private int heartbeatInterval = ClusterManager.DEFAULT_HEARTBEAT_INTERVAL; + private int heartbeatThreshold = ClusterManager.DEFAULT_HEARTBEAT_THRESHOLD; + + private final Map clusterPeers; + private final Map asyncCalls; + private final Gson gson; + + private AgentManager _agentMgr; + + private final ScheduledExecutorService _heartbeatScheduler = Executors.newScheduledThreadPool(1, new NamedThreadFactory("Cluster-Heartbeat")); + + private final ExecutorService _notificationExecutor = Executors.newFixedThreadPool(1, new NamedThreadFactory("Cluster-Notification")); + private List _notificationMsgs = new ArrayList(); + private Connection _heartbeatConnection = null; + + private final ExecutorService _executor; + + private ClusterServiceAdapter _currentServiceAdapter; + + private ManagementServerHostDao _mshostDao; + private HostDao _hostDao; + + // + // pay attention to _mshostId and _msid + // _mshostId is the primary key of management host table + // _msid is the unique persistent identifier that peer name is based upon + // + private Long _mshostId = null; + protected long _msid = ManagementServerNode.getManagementServerId(); + protected long _runId = System.currentTimeMillis(); + + private boolean _peerScanInited = false; + + private String _name; + private String _clusterNodeIP = "127.0.0.1"; + + public ClusterManagerImpl() { + clusterPeers = new HashMap(); + asyncCalls = new HashMap(); + + gson = GsonHelper.getBuilder().create(); + + // executor to perform remote-calls in another thread context, to avoid potential + // recursive remote calls between nodes + // + _executor = Executors.newCachedThreadPool(new NamedThreadFactory("Cluster-Worker")); + } + + @Override + public Answer[] sendToAgent(Long hostId, Command [] cmds, boolean stopOnError) + throws AgentUnavailableException, OperationTimedoutException { + Commands commands = new Commands(stopOnError ? OnError.Stop : OnError.Continue); + for (Command cmd : cmds) { + commands.addCommand(cmd); + } + return _agentMgr.send(hostId, commands); + } + + @Override + public long sendToAgent(Long hostId, Command[] cmds, boolean stopOnError, Listener listener) + throws AgentUnavailableException { + Commands commands = new Commands(stopOnError ? OnError.Stop : OnError.Continue); + for (Command cmd : cmds) { + commands.addCommand(cmd); + } + return _agentMgr.send(hostId, commands, listener); + } + + @Override + public boolean executeAgentUserRequest(long agentId, Event event) throws AgentUnavailableException { + return _agentMgr.executeUserRequest(agentId, event); + } + + @Override + public Boolean propagateAgentEvent(long agentId, Event event) throws AgentUnavailableException { + final String msPeer = getPeerName(agentId); + if (msPeer == null) { + return null; + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Propagating agent change request event:" + event.toString() + " to agent:"+ agentId); + } + Command[] cmds = new Command[1]; + cmds[0] = new ChangeAgentCommand(agentId, event); + + Answer[] answers = execute(msPeer, agentId, cmds, true); + if (answers == null) { + throw new AgentUnavailableException(agentId); + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Result for agent change is " + answers[0].getResult()); + } + + return answers[0].getResult(); + } + + /** + * called by DatabaseUpgradeChecker to see if there are other peers running. + * @param notVersion If version is passed in, the peers CANNOT be running at this + * version. If version is null, return true if any peer is + * running regardless of version. + * @return true if there are peers running and false if not. + */ + public static final boolean arePeersRunning(String notVersion) { + return false; //TODO: Leaving this for Kelven to take care of. + } + + @Override + public void broadcast(long agentId, Command[] cmds) { + Date cutTime = DateUtil.currentGMTTime(); + + List peers = _mshostDao.getActiveList(new Date(cutTime.getTime() - heartbeatThreshold)); + for (ManagementServerHostVO peer : peers) { + String peerName = Long.toString(peer.getMsid()); + if (getSelfPeerName().equals(peerName)) { + continue; // Skip myself. + } + try { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Forwarding " + cmds[0].toString() + " to " + peer.getMsid()); + } + Answer[] answers = execute(peerName, agentId, cmds, true); + } catch (Exception e) { + s_logger.warn("Caught exception while talkign to " + peer.getMsid()); + } + } + } + + @Override + public Answer[] execute(String strPeer, long agentId, Command [] cmds, boolean stopOnError) { + ClusterService peerService = null; + + if(s_logger.isDebugEnabled()) { + s_logger.debug(getSelfPeerName() + " -> " + strPeer + "." + agentId + " " + + gson.toJson(cmds, Command[].class)); + } + + for(int i = 0; i < 2; i++) { + try { + peerService = getPeerService(strPeer); + } catch (RemoteException e) { + s_logger.error("Unable to get cluster service on peer : " + strPeer); + } + + if(peerService != null) { + try { + if(s_logger.isDebugEnabled()) { + s_logger.debug("Send " + getSelfPeerName() + " -> " + strPeer + "." + agentId + " to remote"); + } + + long startTick = System.currentTimeMillis(); + String strResult = peerService.execute(getSelfPeerName(), agentId, gson.toJson(cmds, Command[].class), stopOnError); + if(s_logger.isDebugEnabled()) { + s_logger.debug("Completed " + getSelfPeerName() + " -> " + strPeer + "." + agentId + "in " + + (System.currentTimeMillis() - startTick) + " ms, result: " + strResult); + } + + if(strResult != null) { + try { + return gson.fromJson(strResult, Answer[].class); + } catch(Throwable e) { + s_logger.error("Exception on parsing gson package from remote call to " + strPeer); + } + } + + return null; + } catch (RemoteException e) { + invalidatePeerService(strPeer); + + if(s_logger.isInfoEnabled()) { + s_logger.info("Exception on remote execution, peer: " + strPeer + ", iteration: " + + i + ", exception message :" + e.getMessage()); + } + } + } + } + + return null; + } + + @Override + public long executeAsync(String strPeer, long agentId, Command[] cmds, boolean stopOnError, Listener listener) { + + ClusterService peerService = null; + + if(s_logger.isDebugEnabled()) { + s_logger.debug("Async " + getSelfPeerName() + " -> " + strPeer + "." + agentId + " " + + gson.toJson(cmds, Command[].class)); + } + + for(int i = 0; i < 2; i++) { + try { + peerService = getPeerService(strPeer); + } catch (RemoteException e) { + s_logger.error("Unable to get cluster service on peer : " + strPeer); + } + + if(peerService != null) { + try { + long seq = 0; + synchronized(String.valueOf(agentId).intern()) { + if(s_logger.isDebugEnabled()) { + s_logger.debug("Send Async " + getSelfPeerName() + " -> " + strPeer + "." + agentId + " to remote"); + } + + long startTick = System.currentTimeMillis(); + seq = peerService.executeAsync(getSelfPeerName(), agentId, gson.toJson(cmds, Command[].class), stopOnError); + + if(seq > 0) { + if(s_logger.isDebugEnabled()) { + s_logger.debug("Completed Async " + getSelfPeerName() + " -> " + strPeer + "." + agentId + + " in " + (System.currentTimeMillis() - startTick) + " ms" + + ", register local listener " + strPeer + "/" + seq); + } + + registerAsyncCall(strPeer, seq, listener); + } else { + s_logger.warn("Completed Async " + getSelfPeerName() + " -> " + strPeer + "." + agentId + + " in " + (System.currentTimeMillis() - startTick) + " ms, return indicates failure, seq: " + seq); + } + } + return seq; + } catch (RemoteException e) { + invalidatePeerService(strPeer); + + if(s_logger.isInfoEnabled()) { + s_logger.info("Exception on remote execution -> " + strPeer + ", iteration : " + i); + } + } + } + } + + return 0L; + } + + @Override + public boolean onAsyncResult(String executingPeer, long agentId, long seq, Answer[] answers) { + + if(s_logger.isDebugEnabled()) { + s_logger.debug("Process Async-call result from remote peer " + executingPeer + ", {" + + agentId + "-" + seq + "} answers: " + (answers != null ? gson.toJson(answers, Answer[].class): "null")); + } + + Listener listener = null; + synchronized(String.valueOf(agentId).intern()) { + // need to synchronize it with executeAsync() to make sure listener have been registered + // before this callback reaches back + listener = getAsyncCallListener(executingPeer, seq); + } + + if(listener != null) { + long startTick = System.currentTimeMillis(); + + if(s_logger.isDebugEnabled()) { + s_logger.debug("Processing answer {" + agentId + "-" + seq + "} from remote peer " + executingPeer); + } + + listener.processAnswers(agentId, seq, answers); + + if(s_logger.isDebugEnabled()) { + s_logger.debug("Answer {" + agentId + "-" + seq + "} is processed in " + + (System.currentTimeMillis() - startTick) + " ms"); + } + + if(!listener.isRecurring()) { + if(s_logger.isDebugEnabled()) { + s_logger.debug("Listener is not recurring after async-result callback {" + + agentId + "-" + seq + "}, unregister it"); + } + + unregisterAsyncCall(executingPeer, seq); + } else { + if(s_logger.isDebugEnabled()) { + s_logger.debug("Listener is recurring after async-result callback {" + agentId + +"-" + seq + "}, will keep it"); + } + return true; + } + } else { + if(s_logger.isInfoEnabled()) { + s_logger.info("Async-call Listener has not been registered yet for {" + agentId + +"-" + seq + "}"); + } + } + return false; + } + + @Override + public boolean forwardAnswer(String targetPeer, long agentId, long seq, Answer[] answers) { + + if(s_logger.isDebugEnabled()) { + s_logger.debug("Forward -> " + targetPeer + " Async-call answer {" + agentId + "-" + seq + + "} " + (answers != null? gson.toJson(answers, Answer[].class):"")); + } + + final String targetPeerF = targetPeer; + final Answer[] answersF = answers; + final long agentIdF = agentId; + final long seqF = seq; + + ClusterService peerService = null; + + for(int i = 0; i < 2; i++) { + try { + peerService = getPeerService(targetPeerF); + } catch (RemoteException e) { + s_logger.error("cluster service for peer " + targetPeerF + " no longer exists"); + } + + if(peerService != null) { + try { + boolean result = false; + + long startTick = System.currentTimeMillis(); + if(s_logger.isDebugEnabled()) { + s_logger.debug("Start forwarding Async-call answer {" + agentId + "-" + seq + "} to remote"); + } + + result = peerService.onAsyncResult(getSelfPeerName(), agentIdF, seqF, gson.toJson(answersF, Answer[].class)); + + if(s_logger.isDebugEnabled()) { + s_logger.debug("Completed forwarding Async-call answer {" + agentId + "-" + seq + "} in " + + (System.currentTimeMillis() - startTick) + " ms, return result: " + result); + } + + return result; + } catch (RemoteException e) { + s_logger.warn("Exception in performing remote call, ", e); + invalidatePeerService(targetPeerF); + } + } else { + s_logger.warn("Remote peer " + targetPeer + " no longer exists to process answer {" + agentId + "-" + + seq + "}"); + } + } + + return false; + } + + @Override + public String getPeerName(long agentHostId) { + + HostVO host = _hostDao.findById(agentHostId); + if(host != null && host.getManagementServerId() != null) { + if(getSelfPeerName().equals(Long.toString(host.getManagementServerId()))) { + return null; + } + + return Long.toString(host.getManagementServerId()); + } + return null; + } + + @Override + public ManagementServerHostVO getPeer(String mgmtServerId) { + return _mshostDao.findByMsid(Long.valueOf(mgmtServerId)); + } + + @Override + public String getSelfPeerName() { + return Long.toString(_msid); + } + + @Override + public String getSelfNodeIP() { + return _clusterNodeIP; + } + + @Override + public void registerListener(ClusterManagerListener listener) { + // Note : we don't check duplicates + synchronized(listeners) { + listeners.add(listener); + } + } + + @Override + public void unregisterListener(ClusterManagerListener listener) { + synchronized(listeners) { + listeners.remove(listener); + } + } + + public void notifyNodeJoined(List nodeList) { + synchronized(listeners) { + for(ClusterManagerListener listener : listeners) { + listener.onManagementNodeJoined(nodeList, _mshostId); + } + } + + SubscriptionMgr.getInstance().notifySubscribers(ClusterManager.ALERT_SUBJECT, this, + new ClusterNodeJoinEventArgs(_mshostId, nodeList)); + } + + public void notifyNodeLeft(List nodeList) { + synchronized(listeners) { + for(ClusterManagerListener listener : listeners) { + listener.onManagementNodeLeft(nodeList, _mshostId); + } + } + + SubscriptionMgr.getInstance().notifySubscribers(ClusterManager.ALERT_SUBJECT, this, + new ClusterNodeLeftEventArgs(_mshostId, nodeList)); + } + + public void notifyNodeIsolated() { + synchronized(listeners) { + for(ClusterManagerListener listener : listeners) { + listener.onManagementNodeIsolated(); + } + } + } + + public ClusterService getPeerService(String strPeer) throws RemoteException { + synchronized(clusterPeers) { + if(clusterPeers.containsKey(strPeer)) { + return clusterPeers.get(strPeer); + } + } + + ClusterService service = _currentServiceAdapter.getPeerService(strPeer); + + if(service != null) { + synchronized(clusterPeers) { + // re-check the peer map again to deal with the + // race conditions + if(!clusterPeers.containsKey(strPeer)) { + clusterPeers.put(strPeer, service); + } + } + } + + return service; + } + + public void invalidatePeerService(String strPeer) { + synchronized(clusterPeers) { + if(clusterPeers.containsKey(strPeer)) { + clusterPeers.remove(strPeer); + } + } + } + + private void registerAsyncCall(String strPeer, long seq, Listener listener) { + String key = strPeer + "/" + seq; + + synchronized(asyncCalls) { + if(!asyncCalls.containsKey(key)) { + asyncCalls.put(key, listener); + } + } + } + + private Listener getAsyncCallListener(String strPeer, long seq) { + String key = strPeer + "/" + seq; + + synchronized(asyncCalls) { + if(asyncCalls.containsKey(key)) { + return asyncCalls.get(key); + } + } + + return null; + } + + private void unregisterAsyncCall(String strPeer, long seq) { + String key = strPeer + "/" + seq; + + synchronized(asyncCalls) { + if(asyncCalls.containsKey(key)) { + asyncCalls.remove(key); + } + } + } + + private Runnable getHeartbeatTask() { + return new Runnable() { + @Override + public void run() { + try { + if(s_logger.isTraceEnabled()) { + s_logger.trace("Cluster manager heartbeat update, id:" + _mshostId); + } + + Connection conn = getHeartbeatConnection(); + _mshostDao.update(conn, _mshostId, getCurrentRunId(), DateUtil.currentGMTTime()); + + if(s_logger.isTraceEnabled()) { + s_logger.trace("Cluster manager peer-scan, id:" + _mshostId); + } + + if(!_peerScanInited) { + _peerScanInited = true; + initPeerScan(conn); + } + + peerScan(conn); + } catch(CloudRuntimeException e) { + s_logger.error("Runtime DB exception ", e.getCause()); + + if(e.getCause() instanceof ClusterInvalidSessionException) { + s_logger.error("Invalid cluster session found"); + queueNotification(new ClusterManagerMessage(ClusterManagerMessage.MessageType.nodeIsolated)); + } + + if(isRootCauseConnectionRelated(e.getCause())) { + s_logger.error("DB communication problem detected"); + queueNotification(new ClusterManagerMessage(ClusterManagerMessage.MessageType.nodeIsolated)); + } + + invalidHeartbeatConnection(); + } catch (Throwable e) { + if(isRootCauseConnectionRelated(e.getCause())) { + s_logger.error("DB communication problem detected"); + queueNotification(new ClusterManagerMessage(ClusterManagerMessage.MessageType.nodeIsolated)); + } + + s_logger.error("Problem with the cluster heartbeat!", e); + } + } + }; + } + + private boolean isRootCauseConnectionRelated(Throwable e) { + while(e != null) { + if(e instanceof com.mysql.jdbc.CommunicationsException || e instanceof com.mysql.jdbc.exceptions.jdbc4.CommunicationsException) + return true; + + e = e.getCause(); + } + + return false; + } + + private Connection getHeartbeatConnection() throws SQLException { + if(_heartbeatConnection != null) { + return _heartbeatConnection; + } + + _heartbeatConnection = Transaction.getStandaloneConnectionWithException(); + return _heartbeatConnection; + } + + private void invalidHeartbeatConnection() { + if(_heartbeatConnection != null) { + try { + _heartbeatConnection.close(); + } catch (SQLException e) { + s_logger.warn("Unable to close hearbeat DB connection. ", e); + } + + _heartbeatConnection = null; + } + } + + private Runnable getNotificationTask() { + return new Runnable() { + @Override + public void run() { + while(true) { + synchronized(_notificationMsgs) { + try { + _notificationMsgs.wait(1000); + } catch (InterruptedException e) { + } + } + + ClusterManagerMessage msg = null; + while((msg = getNextNotificationMessage()) != null) { + try { + switch(msg.getMessageType()) { + case nodeAdded: + if(msg.getNodes() != null && msg.getNodes().size() > 0) { + Profiler profiler = new Profiler(); + profiler.start(); + + notifyNodeJoined(msg.getNodes()); + + profiler.stop(); + if(profiler.getDuration() > 1000) { + if(s_logger.isDebugEnabled()) { + s_logger.debug("Notifying management server join event took " + profiler.getDuration() + " ms"); + } + } else { + s_logger.warn("Notifying management server join event took " + profiler.getDuration() + " ms"); + } + } + break; + + case nodeRemoved: + if(msg.getNodes() != null && msg.getNodes().size() > 0) { + Profiler profiler = new Profiler(); + profiler.start(); + + notifyNodeLeft(msg.getNodes()); + + profiler.stop(); + if(profiler.getDuration() > 1000) { + if(s_logger.isDebugEnabled()) { + s_logger.debug("Notifying management server leave event took " + profiler.getDuration() + " ms"); + } + } else { + s_logger.warn("Notifying management server leave event took " + profiler.getDuration() + " ms"); + } + } + break; + + case nodeIsolated: + notifyNodeIsolated(); + break; + + default : + assert(false); + break; + } + + } catch (Throwable e) { + s_logger.warn("Unexpected exception during cluster notification. ", e); + } + } + + try { Thread.currentThread().sleep(1000); } catch (InterruptedException e) {} + } + } + }; + } + + private void queueNotification(ClusterManagerMessage msg) { + synchronized(this._notificationMsgs) { + this._notificationMsgs.add(msg); + this._notificationMsgs.notifyAll(); + } + } + + private ClusterManagerMessage getNextNotificationMessage() { + synchronized(this._notificationMsgs) { + if(this._notificationMsgs.size() > 0) + return this._notificationMsgs.remove(0); + } + + return null; + } + + private void initPeerScan(Connection conn) { + // upon startup, for all inactive management server nodes that we see at startup time, we will send notification also to help upper layer perform + // missed cleanup + Date cutTime = DateUtil.currentGMTTime(); + List inactiveList = _mshostDao.getInactiveList(conn, new Date(cutTime.getTime() - heartbeatThreshold)); + if(inactiveList.size() > 0) { + this.queueNotification(new ClusterManagerMessage(ClusterManagerMessage.MessageType.nodeRemoved, inactiveList)); + } + } + + private void peerScan(Connection conn) { + Date cutTime = DateUtil.currentGMTTime(); + + List currentList = _mshostDao.getActiveList(conn, new Date(cutTime.getTime() - heartbeatThreshold)); + + List removedNodeList = new ArrayList(); + List invalidatedNodeList = new ArrayList(); + + if(_mshostId != null) { + // only if we have already attached to cluster, will we start to check leaving nodes + for(Map.Entry entry : activePeers.entrySet()) { + + ManagementServerHostVO current = getInListById(entry.getKey(), currentList); + if(current == null) { + if(entry.getKey().longValue() != _mshostId.longValue()) { + if(s_logger.isDebugEnabled()) { + s_logger.debug("Detected management node left, id:" + entry.getKey() + ", nodeIP:" + entry.getValue().getServiceIP()); + } + removedNodeList.add(entry.getValue()); + } + } else { + if(current.getRunid() == 0) { + if(entry.getKey().longValue() != _mshostId.longValue()) { + if(s_logger.isDebugEnabled()) { + s_logger.debug("Detected management node left because of invalidated session, id:" + entry.getKey() + ", nodeIP:" + entry.getValue().getServiceIP()); + } + invalidatedNodeList.add(entry.getValue()); + } + } else { + if(entry.getValue().getRunid() != current.getRunid()) { + if(s_logger.isDebugEnabled()) { + s_logger.debug("Detected management node left and rejoined quickly, id:" + entry.getKey() + ", nodeIP:" + entry.getValue().getServiceIP()); + } + + entry.getValue().setRunid(current.getRunid()); + } + } + } + } + } + + // process invalidated node list + if(invalidatedNodeList.size() > 0) { + for(ManagementServerHostVO mshost : invalidatedNodeList) { + activePeers.remove(mshost.getId()); + try { + JmxUtil.unregisterMBean("ClusterManager", "Node " + mshost.getId()); + } catch(Exception e) { + s_logger.warn("Unable to deregiester cluster node from JMX monitoring due to exception " + e.toString()); + } + } + + this.queueNotification(new ClusterManagerMessage(ClusterManagerMessage.MessageType.nodeRemoved, invalidatedNodeList)); + } + + // process removed node list + Iterator it = removedNodeList.iterator(); + while(it.hasNext()) { + ManagementServerHostVO mshost = it.next(); + if(!pingManagementNode(mshost)) { + s_logger.warn("Management node " + mshost.getId() + " is detected inactive by timestamp and also not pingable"); + activePeers.remove(mshost.getId()); + _mshostDao.invalidateRunSession(conn, mshost.getId(), mshost.getRunid()); + try { + JmxUtil.unregisterMBean("ClusterManager", "Node " + mshost.getId()); + } catch(Exception e) { + s_logger.warn("Unable to deregiester cluster node from JMX monitoring due to exception " + e.toString()); + } + } else { + s_logger.info("Management node " + mshost.getId() + " is detected inactive by timestamp but is pingable"); + it.remove(); + } + } + + if(removedNodeList.size() > 0) + this.queueNotification(new ClusterManagerMessage(ClusterManagerMessage.MessageType.nodeRemoved, removedNodeList)); + + List newNodeList = new ArrayList(); + for(ManagementServerHostVO mshost : currentList) { + if(!activePeers.containsKey(mshost.getId())) { + activePeers.put(mshost.getId(), mshost); + + if(s_logger.isDebugEnabled()) { + s_logger.debug("Detected management node joined, id:" + mshost.getId() + ", nodeIP:" + mshost.getServiceIP()); + } + newNodeList.add(mshost); + + try { + JmxUtil.registerMBean("ClusterManager", "Node " + mshost.getId(), new ClusterManagerMBeanImpl(this, mshost)); + } catch(Exception e) { + s_logger.warn("Unable to regiester cluster node into JMX monitoring due to exception " + ExceptionUtil.toString(e)); + } + } + } + + if(newNodeList.size() > 0) + this.queueNotification(new ClusterManagerMessage(ClusterManagerMessage.MessageType.nodeAdded, newNodeList)); + } + + private static ManagementServerHostVO getInListById(Long id, List l) { + for(ManagementServerHostVO mshost : l) { + if(mshost.getId() == id) { + return mshost; + } + } + return null; + } + + @Override + public String getName() { + return _name; + } + + @Override @DB + public boolean start() { + if(s_logger.isInfoEnabled()) { + s_logger.info("Starting cluster manager, msid : " + _msid); + } + + Transaction txn = Transaction.currentTxn(); + try { + txn.start(); + + final Class c = this.getClass(); + String version = c.getPackage().getImplementationVersion(); + + ManagementServerHostVO mshost = _mshostDao.findByMsid(_msid); + if(mshost == null) { + mshost = new ManagementServerHostVO(); + mshost.setMsid(_msid); + mshost.setRunid(this.getCurrentRunId()); + mshost.setName(NetUtils.getHostName()); + mshost.setVersion(version); + mshost.setServiceIP(_clusterNodeIP); + mshost.setServicePort(_currentServiceAdapter.getServicePort()); + mshost.setLastUpdateTime(DateUtil.currentGMTTime()); + mshost.setRemoved(null); + mshost.setAlertCount(0); + mshost.setState(ManagementServerNode.State.Up); + _mshostDao.persist(mshost); + + if(s_logger.isInfoEnabled()) { + s_logger.info("New instance of management server msid " + _msid + " is being started"); + } + } else { + if(s_logger.isInfoEnabled()) { + s_logger.info("Management server " + _msid + " is being started"); + } + + _mshostDao.update(mshost.getId(), getCurrentRunId(), NetUtils.getHostName(), version, + _clusterNodeIP, _currentServiceAdapter.getServicePort(), DateUtil.currentGMTTime()); + } + + txn.commit(); + + _mshostId = mshost.getId(); + if(s_logger.isInfoEnabled()) { + s_logger.info("Management server (host id : " + _mshostId + ") is available at " + _clusterNodeIP + ":" + _currentServiceAdapter.getServicePort()); + } + + // use seperated thread for heartbeat updates + _heartbeatScheduler.scheduleAtFixedRate(getHeartbeatTask(), heartbeatInterval, + heartbeatInterval, TimeUnit.MILLISECONDS); + _notificationExecutor.submit(getNotificationTask()); + } catch (Throwable e) { + s_logger.error("Unexpected exception : ", e); + txn.rollback(); + + throw new CloudRuntimeException("Unable to initialize cluster info into database"); + } + + if(s_logger.isInfoEnabled()) { + s_logger.info("Cluster manager is started"); + } + + return true; + } + + @Override + public boolean stop() { + if(_mshostId != null) { + _mshostDao.remove(_mshostId); + } + + _heartbeatScheduler.shutdownNow(); + _executor.shutdownNow(); + + try { + _heartbeatScheduler.awaitTermination(EXECUTOR_SHUTDOWN_TIMEOUT, TimeUnit.MILLISECONDS); + _executor.awaitTermination(EXECUTOR_SHUTDOWN_TIMEOUT, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + } + + if(s_logger.isInfoEnabled()) { + s_logger.info("Cluster manager is stopped"); + } + + return true; + } + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + if(s_logger.isInfoEnabled()) { + s_logger.info("Start configuring cluster manager : " + name); + } + _name = name; + + ComponentLocator locator = ComponentLocator.getCurrentLocator(); + _agentMgr = locator.getManager(AgentManager.class); + if (_agentMgr == null) { + throw new ConfigurationException("Unable to get " + AgentManager.class.getName()); + } + + _mshostDao = locator.getDao(ManagementServerHostDao.class); + if(_mshostDao == null) { + throw new ConfigurationException("Unable to get " + ManagementServerHostDao.class.getName()); + } + + _hostDao = locator.getDao(HostDao.class); + if(_hostDao == null) { + throw new ConfigurationException("Unable to get " + HostDao.class.getName()); + } + + ConfigurationDao configDao = locator.getDao(ConfigurationDao.class); + if (configDao == null) { + throw new ConfigurationException("Unable to get the configuration dao."); + } + + Map configs = configDao.getConfiguration("management-server", params); + + String value = configs.get("cluster.heartbeat.interval"); + if(value != null) { + heartbeatInterval = NumbersUtil.parseInt(value, ClusterManager.DEFAULT_HEARTBEAT_INTERVAL); + } + + value = configs.get("cluster.heartbeat.threshold"); + if(value != null) { + heartbeatThreshold = NumbersUtil.parseInt(value, ClusterManager.DEFAULT_HEARTBEAT_THRESHOLD); + } + + File dbPropsFile = PropertiesUtil.findConfigFile("db.properties"); + Properties dbProps = new Properties(); + try { + dbProps.load(new FileInputStream(dbPropsFile)); + } catch (FileNotFoundException e) { + throw new ConfigurationException("Unable to find db.properties"); + } catch (IOException e) { + throw new ConfigurationException("Unable to load db.properties content"); + } + _clusterNodeIP = dbProps.getProperty("cluster.node.IP"); + if(_clusterNodeIP == null) { + _clusterNodeIP = "127.0.0.1"; + } + _clusterNodeIP = _clusterNodeIP.trim(); + + if(s_logger.isInfoEnabled()) { + s_logger.info("Cluster node IP : " + _clusterNodeIP); + } + + if(!NetUtils.isLocalAddress(_clusterNodeIP)) { + throw new ConfigurationException("cluster node IP should be valid local address where the server is running, please check your configuration"); + } + + Adapters adapters = locator.getAdapters(ClusterServiceAdapter.class); + if (adapters == null || !adapters.isSet()) { + throw new ConfigurationException("Unable to get cluster service adapters"); + } + Enumeration it = adapters.enumeration(); + if(it.hasMoreElements()) { + _currentServiceAdapter = it.nextElement(); + } + + if(_currentServiceAdapter == null) { + throw new ConfigurationException("Unable to set current cluster service adapter"); + } + + checkConflicts(); + + if(s_logger.isInfoEnabled()) { + s_logger.info("Cluster manager is configured."); + } + return true; + } + + @Override + public long getManagementNodeId() { + return _msid; + } + + @Override + public long getCurrentRunId() { + return _runId; + } + + @Override + public boolean isManagementNodeAlive(long msid) { + ManagementServerHostVO mshost = _mshostDao.findByMsid(msid); + if(mshost != null) { + if(mshost.getLastUpdateTime().getTime() >= DateUtil.currentGMTTime().getTime() - heartbeatThreshold) { + return true; + } + } + + return false; + } + + @Override + public boolean pingManagementNode(long msid) { + ManagementServerHostVO mshost = _mshostDao.findByMsid(msid); + if(mshost == null) { + return false; + } + + return pingManagementNode(mshost); + } + + private boolean pingManagementNode(ManagementServerHostVO mshost) { + + String targetIp = mshost.getServiceIP(); + if("127.0.0.1".equals(targetIp) || "0.0.0.0".equals(targetIp)) { + s_logger.info("ping management node cluster service can not be performed on self"); + return false; + } + + String targetPeer = String.valueOf(mshost.getMsid()); + ClusterService peerService = null; + for(int i = 0; i < 2; i++) { + try { + peerService = getPeerService(targetPeer); + } catch (RemoteException e) { + s_logger.error("cluster service for peer " + targetPeer + " no longer exists"); + } + + if(peerService != null) { + try { + return peerService.ping(getSelfPeerName()); + } catch (RemoteException e) { + s_logger.warn("Exception in performing remote call, ", e); + invalidatePeerService(targetPeer); + } + } else { + s_logger.warn("Remote peer " + mshost.getMsid() + " no longer exists"); + } + } + + return false; + } + + + @Override + public int getHeartbeatThreshold() { + return this.heartbeatThreshold; + } + + public int getHeartbeatInterval() { + return this.heartbeatInterval; + } + + public void setHeartbeatThreshold(int threshold) { + heartbeatThreshold = threshold; + } + + private void checkConflicts() throws ConfigurationException { + Date cutTime = DateUtil.currentGMTTime(); + List peers = _mshostDao.getActiveList(new Date(cutTime.getTime() - heartbeatThreshold)); + for(ManagementServerHostVO peer : peers) { + String peerIP = peer.getServiceIP().trim(); + if(_clusterNodeIP.equals(peerIP)) { + if("127.0.0.1".equals(_clusterNodeIP)) { + if(pingManagementNode(peer.getMsid())) { + String msg = "Detected another management node with localhost IP is already running, please check your cluster configuration"; + s_logger.error(msg); + throw new ConfigurationException(msg); + } else { + String msg = "Detected another management node with localhost IP is considered as running in DB, however it is not pingable, we will continue cluster initialization with this management server node"; + s_logger.info(msg); + } + } else { + if(pingManagementNode(peer.getMsid())) { + String msg = "Detected that another management node with the same IP " + peer.getServiceIP() + " is already running, please check your cluster configuration"; + s_logger.error(msg); + throw new ConfigurationException(msg); + } else { + String msg = "Detected that another management node with the same IP " + peer.getServiceIP() + " is considered as running in DB, however it is not pingable, we will continue cluster initialization with this management server node"; + s_logger.info(msg); + } + } + } + } + } +} diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index 093eb1f16db..7d453aec9f0 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -238,7 +238,8 @@ public enum Config { DefaultMaxAccountTemplates("Account Defaults", ManagementServer.class, Long.class, "max.account.templates", "20", "The default maximum number of templates that can be deployed for an account", null), DefaultMaxAccountSnapshots("Account Defaults", ManagementServer.class, Long.class, "max.account.snapshots", "20", "The default maximum number of snapshots that can be created for an account", null), DefaultMaxAccountVolumes("Account Defaults", ManagementServer.class, Long.class, "max.account.volumes", "20", "The default maximum number of volumes that can be created for an account", null); - + EndpointeUrl("Advanced", ManagementServer.class, String.class, "endpointe.url", "http://localhost:8080/client/api", "Endpointe Url", null); + private final String _category; private final Class _componentClass; private final Class _type; diff --git a/server/src/com/cloud/configuration/ZoneConfig.java b/server/src/com/cloud/configuration/ZoneConfig.java new file mode 100644 index 00000000000..80400c70434 --- /dev/null +++ b/server/src/com/cloud/configuration/ZoneConfig.java @@ -0,0 +1,87 @@ +/** + * Copyright (C) 2011 Cloud.com. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later +version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.configuration; + +import java.util.ArrayList; +import java.util.List; + +public enum ZoneConfig { + EnableSecStorageVm( Boolean.class, "enable.secstorage.vm", "true", "Enables secondary storage vm service", null), + EnableConsoleProxyVm( Boolean.class, "enable.consoleproxy.vm", "true", "Enables console proxy vm service", null), + MaxHosts( Long.class, "max.hosts", null, "Maximum number of hosts the Zone can have", null), + MaxVirtualMachines( Long.class, "max.vms", null, "Maximum number of VMs the Zone can have", null), + ZoneMode( String.class, "zone.mode", null, "Mode of the Zone", "Free,Basic,Advanced"), + HasNoPublicIp(Boolean.class, "has.no.public.ip", "false", "True if Zone has no public IP", null), + DhcpStrategy(String.class, "zone.dhcp.strategy", "cloudstack-systemvm", "Who controls DHCP", "cloudstack-systemvm,cloudstack-external,external"); + + + private final Class _type; + private final String _name; + private final String _defaultValue; + private final String _description; + private final String _range; + + private static final List _zoneConfigKeys = new ArrayList(); + + static { + // Add keys into List + for (ZoneConfig c : ZoneConfig.values()) { + String key = c.key(); + _zoneConfigKeys.add(key); + } + } + + private ZoneConfig( Class type, String name, String defaultValue, String description, String range) { + + _type = type; + _name = name; + _defaultValue = defaultValue; + _description = description; + _range = range; + } + + public Class getType() { + return _type; + } + + public String getName() { + return _name; + } + + public String getDefaultValue() { + return _defaultValue; + } + + public String getDescription() { + return _description; + } + + public String getRange() { + return _range; + } + + public String key() { + return _name; + } + + public static boolean doesKeyExist(String key){ + return _zoneConfigKeys.contains(key); + } + +} diff --git a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java index d9ac5fe75ab..308dbd1f216 100644 --- a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java +++ b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java @@ -260,7 +260,10 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx @Override public ConsoleProxyInfo assignProxy(final long dataCenterId, final long vmId) { - + if (!isConsoleProxyVmRequired(dataCenterId)) { + return null; + } + final Pair result = new Pair(this, null); _requestHandlerScheduler.execute(new Runnable() { diff --git a/server/src/com/cloud/dc/DataCenterVO.java b/server/src/com/cloud/dc/DataCenterVO.java index 63f58951172..5ca3322470c 100644 --- a/server/src/com/cloud/dc/DataCenterVO.java +++ b/server/src/com/cloud/dc/DataCenterVO.java @@ -16,8 +16,8 @@ * */ -package com.cloud.dc; - +package com.cloud.dc; + import java.util.Map; import javax.persistence.Column; @@ -34,38 +34,38 @@ import javax.persistence.Transient; import com.cloud.network.Network.Provider; import com.cloud.org.Grouping; import com.cloud.utils.NumbersUtil; - -@Entity -@Table(name="data_center") -public class DataCenterVO implements DataCenter { - - @Id - @GeneratedValue(strategy=GenerationType.IDENTITY) - @Column(name="id") - private long id; - - @Column(name="name") - private String name = null; - - @Column(name="description") - private String description = null; - - @Column(name="dns1") - private String dns1 = null; - - @Column(name="dns2") - private String dns2 = null; - - @Column(name="internal_dns1") - private String internalDns1 = null; - - @Column(name="internal_dns2") - private String internalDns2 = null; - - @Column(name="router_mac_address", updatable = false, nullable=false) - private String routerMacAddress = "02:00:00:00:00:01"; - - @Column(name="vnet") + +@Entity +@Table(name="data_center") +public class DataCenterVO implements DataCenter { + + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + @Column(name="id") + private long id; + + @Column(name="name") + private String name = null; + + @Column(name="description") + private String description = null; + + @Column(name="dns1") + private String dns1 = null; + + @Column(name="dns2") + private String dns2 = null; + + @Column(name="internal_dns1") + private String internalDns1 = null; + + @Column(name="internal_dns2") + private String internalDns2 = null; + + @Column(name="router_mac_address", updatable = false, nullable=false) + private String routerMacAddress = "02:00:00:00:00:01"; + + @Column(name="vnet") private String vnet = null; @Column(name="guest_network_cidr") @@ -168,12 +168,12 @@ public class DataCenterVO implements DataCenter { } public DataCenterVO(long id, String name, String description, String dns1, String dns2, String dns3, String dns4, String vnet, String guestCidr, String domain, Long domainId, NetworkType zoneType, String zoneToken) { - this(name, description, dns1, dns2, dns3, dns4, vnet, guestCidr, domain, domainId, zoneType, false, zoneToken); + this(name, description, dns1, dns2, dns3, dns4, vnet, guestCidr, domain, domainId, zoneType, false, zoneToken); this.id = id; - this.allocationState = Grouping.AllocationState.Enabled; - } - - public DataCenterVO(String name, String description, String dns1, String dns2, String dns3, String dns4, String vnet, String guestCidr, String domain, Long domainId, NetworkType zoneType, boolean securityGroupEnabled, String zoneToken) { + this.allocationState = Grouping.AllocationState.Enabled; + } + + public DataCenterVO(String name, String description, String dns1, String dns2, String dns3, String dns4, String vnet, String guestCidr, String domain, Long domainId, NetworkType zoneType, boolean securityGroupEnabled, String zoneToken) { this.name = name; this.description = description; this.dns1 = dns1; @@ -231,81 +231,81 @@ public class DataCenterVO implements DataCenter { public void setDomainId(Long domainId) { this.domainId = domainId; - } - + } + @Override - public String getDescription() { - return description; - } - - public String getRouterMacAddress() { - return routerMacAddress; - } - - public void setVnet(String vnet) { - this.vnet = vnet; - } - + public String getDescription() { + return description; + } + + public String getRouterMacAddress() { + return routerMacAddress; + } + + public void setVnet(String vnet) { + this.vnet = vnet; + } + @Override - public String getDns1() { - return dns1; - } - + public String getDns1() { + return dns1; + } + @Override - public String getVnet() { - return vnet; - } - + public String getVnet() { + return vnet; + } + @Override - public String getDns2() { - return dns2; - } - + public String getDns2() { + return dns2; + } + @Override - public String getInternalDns1() { - return internalDns1; - } - + public String getInternalDns1() { + return internalDns1; + } + @Override - public String getInternalDns2() { - return internalDns2; - } - - protected DataCenterVO() { - } - + public String getInternalDns2() { + return internalDns2; + } + + protected DataCenterVO() { + } + @Override - public long getId() { - return id; - } - + public long getId() { + return id; + } + @Override - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public void setDns1(String dns1) { - this.dns1 = dns1; - } - - public void setDns2(String dns2) { - this.dns2 = dns2; - } - - public void setInternalDns1(String dns3) { - this.internalDns1 = dns3; - } - - public void setInternalDns2(String dns4) { - this.internalDns2 = dns4; - } - - public void setRouterMacAddress(String routerMacAddress) { - this.routerMacAddress = routerMacAddress; + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public void setDns1(String dns1) { + this.dns1 = dns1; + } + + public void setDns2(String dns2) { + this.dns2 = dns2; + } + + public void setInternalDns1(String dns3) { + this.internalDns1 = dns3; + } + + public void setInternalDns2(String dns4) { + this.internalDns2 = dns4; + } + + public void setRouterMacAddress(String routerMacAddress) { + this.routerMacAddress = routerMacAddress; } @Override @@ -397,5 +397,5 @@ public class DataCenterVO implements DataCenter { public void setZoneToken(String zoneToken) { this.zoneToken = zoneToken; - } -} + } +} diff --git a/server/src/com/cloud/dc/DcDetailVO.java b/server/src/com/cloud/dc/DcDetailVO.java index 1d3c841adac..803c1c23de0 100644 --- a/server/src/com/cloud/dc/DcDetailVO.java +++ b/server/src/com/cloud/dc/DcDetailVO.java @@ -25,7 +25,7 @@ import javax.persistence.Id; import javax.persistence.Table; @Entity -@Table(name="datacenter_details") +@Table(name="data_center_details") public class DcDetailVO { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) diff --git a/server/src/com/cloud/dc/dao/DataCenterDao.java b/server/src/com/cloud/dc/dao/DataCenterDao.java index a00020ff281..ec8588dfbbf 100644 --- a/server/src/com/cloud/dc/dao/DataCenterDao.java +++ b/server/src/com/cloud/dc/dao/DataCenterDao.java @@ -16,47 +16,47 @@ * */ -package com.cloud.dc.dao; - +package com.cloud.dc.dao; + import java.util.List; import com.cloud.dc.DataCenterVO; import com.cloud.dc.DataCenterVnetVO; import com.cloud.utils.Pair; import com.cloud.utils.db.GenericDao; - -public interface DataCenterDao extends GenericDao { - DataCenterVO findByName(String name); - - /** - * @param id data center id - * @return a pair of mac address strings. The first one is private and second is public. - */ - String[] getNextAvailableMacAddressPair(long id); - String[] getNextAvailableMacAddressPair(long id, long mask); + +public interface DataCenterDao extends GenericDao { + DataCenterVO findByName(String name); + + /** + * @param id data center id + * @return a pair of mac address strings. The first one is private and second is public. + */ + String[] getNextAvailableMacAddressPair(long id); + String[] getNextAvailableMacAddressPair(long id, long mask); Pair allocatePrivateIpAddress(long id, long podId, long instanceId, String reservationId); - String allocateLinkLocalIpAddress(long id, long podId, long instanceId, String reservationId); - String allocateVnet(long dcId, long accountId, String reservationId); - - void releaseVnet(String vnet, long dcId, long accountId, String reservationId); + String allocateLinkLocalIpAddress(long id, long podId, long instanceId, String reservationId); + String allocateVnet(long dcId, long accountId, String reservationId); + + void releaseVnet(String vnet, long dcId, long accountId, String reservationId); void releasePrivateIpAddress(String ipAddress, long dcId, Long instanceId); void releasePrivateIpAddress(long nicId, String reservationId); void releaseLinkLocalIpAddress(String ipAddress, long dcId, Long instanceId); void releaseLinkLocalIpAddress(long nicId, String reservationId); - + boolean deletePrivateIpAddressByPod(long podId); - boolean deleteLinkLocalIpAddressByPod(long podId); - - void addPrivateIpAddress(long dcId,long podId, String start, String end); + boolean deleteLinkLocalIpAddressByPod(long podId); + + void addPrivateIpAddress(long dcId,long podId, String start, String end); void addLinkLocalIpAddress(long dcId,long podId, String start, String end); List findVnet(long dcId, String vnet); - - void addVnet(long dcId, int start, int end); - - void deleteVnet(long dcId); - - List listAllocatedVnets(long dcId); + + void addVnet(long dcId, int start, int end); + + void deleteVnet(long dcId); + + List listAllocatedVnets(long dcId); String allocatePodVlan(long podId, long accountId); @@ -74,4 +74,5 @@ public interface DataCenterDao extends GenericDao { List listDisabledZones(); List listEnabledZones(); DataCenterVO findByToken(String zoneToken); -} + DataCenterVO findByTokenOrIdOrName(String tokenIdOrName); +} diff --git a/server/src/com/cloud/dc/dao/DataCenterDaoImpl.java b/server/src/com/cloud/dc/dao/DataCenterDaoImpl.java index d76fb4defad..1957098e6e7 100644 --- a/server/src/com/cloud/dc/dao/DataCenterDaoImpl.java +++ b/server/src/com/cloud/dc/dao/DataCenterDaoImpl.java @@ -16,8 +16,8 @@ * */ -package com.cloud.dc.dao; - +package com.cloud.dc.dao; + import java.util.List; import java.util.Map; import java.util.Random; @@ -44,42 +44,42 @@ import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SequenceFetcher; import com.cloud.utils.db.Transaction; import com.cloud.utils.net.NetUtils; - -/** - * @config - * {@table - * || Param Name | Description | Values | Default || - * || mac.address.prefix | prefix to attach to all public and private mac addresses | number | 06 || - * } - **/ -@Local(value={DataCenterDao.class}) -public class DataCenterDaoImpl extends GenericDaoBase implements DataCenterDao { - private static final Logger s_logger = Logger.getLogger(DataCenterDaoImpl.class); - + +/** + * @config + * {@table + * || Param Name | Description | Values | Default || + * || mac.address.prefix | prefix to attach to all public and private mac addresses | number | 06 || + * } + **/ +@Local(value={DataCenterDao.class}) +public class DataCenterDaoImpl extends GenericDaoBase implements DataCenterDao { + private static final Logger s_logger = Logger.getLogger(DataCenterDaoImpl.class); + protected SearchBuilder NameSearch; protected SearchBuilder ListZonesByDomainIdSearch; protected SearchBuilder PublicZonesSearch; protected SearchBuilder ChildZonesSearch; protected SearchBuilder securityGroupSearch; - protected SearchBuilder DisabledZonesSearch; + protected SearchBuilder DisabledZonesSearch; protected SearchBuilder TokenSearch; - + protected final DataCenterIpAddressDaoImpl _ipAllocDao = ComponentLocator.inject(DataCenterIpAddressDaoImpl.class); - protected final DataCenterLinkLocalIpAddressDaoImpl _LinkLocalIpAllocDao = ComponentLocator.inject(DataCenterLinkLocalIpAddressDaoImpl.class); + protected final DataCenterLinkLocalIpAddressDaoImpl _LinkLocalIpAllocDao = ComponentLocator.inject(DataCenterLinkLocalIpAddressDaoImpl.class); protected final DataCenterVnetDaoImpl _vnetAllocDao = ComponentLocator.inject(DataCenterVnetDaoImpl.class); - protected final PodVlanDaoImpl _podVlanAllocDao = ComponentLocator.inject(PodVlanDaoImpl.class); + protected final PodVlanDaoImpl _podVlanAllocDao = ComponentLocator.inject(PodVlanDaoImpl.class); protected long _prefix; protected Random _rand = new Random(System.currentTimeMillis()); protected TableGenerator _tgMacAddress; protected final DcDetailsDaoImpl _detailsDao = ComponentLocator.inject(DcDetailsDaoImpl.class); - - - @Override - public DataCenterVO findByName(String name) { - SearchCriteria sc = NameSearch.create(); - sc.setParameters("name", name); - return findOneBy(sc); + + + @Override + public DataCenterVO findByName(String name) { + SearchCriteria sc = NameSearch.create(); + sc.setParameters("name", name); + return findOneBy(sc); } @Override @@ -115,21 +115,21 @@ public class DataCenterDaoImpl extends GenericDaoBase implem SearchCriteria sc = securityGroupSearch.create(); sc.setParameters("isSgEnabled", true); return listBy(sc); - } - - @Override - public void releaseVnet(String vnet, long dcId, long accountId, String reservationId) { - _vnetAllocDao.release(vnet, dcId, accountId, reservationId); + } + + @Override + public void releaseVnet(String vnet, long dcId, long accountId, String reservationId) { + _vnetAllocDao.release(vnet, dcId, accountId, reservationId); } @Override public List findVnet(long dcId, String vnet) { return _vnetAllocDao.findVnet(dcId, vnet); - } - - @Override - public void releasePrivateIpAddress(String ipAddress, long dcId, Long instanceId) { - _ipAllocDao.releaseIpAddress(ipAddress, dcId, instanceId); + } + + @Override + public void releasePrivateIpAddress(String ipAddress, long dcId, Long instanceId) { + _ipAllocDao.releaseIpAddress(ipAddress, dcId, instanceId); } @Override @@ -145,26 +145,26 @@ public class DataCenterDaoImpl extends GenericDaoBase implem @Override public void releaseLinkLocalIpAddress(String ipAddress, long dcId, Long instanceId) { _LinkLocalIpAllocDao.releaseIpAddress(ipAddress, dcId, instanceId); - } - - @Override - public boolean deletePrivateIpAddressByPod(long podId) { - return _ipAllocDao.deleteIpAddressByPod(podId); + } + + @Override + public boolean deletePrivateIpAddressByPod(long podId) { + return _ipAllocDao.deleteIpAddressByPod(podId); } @Override public boolean deleteLinkLocalIpAddressByPod(long podId) { return _LinkLocalIpAllocDao.deleteIpAddressByPod(podId); - } - - @Override - public String allocateVnet(long dataCenterId, long accountId, String reservationId) { - DataCenterVnetVO vo = _vnetAllocDao.take(dataCenterId, accountId, reservationId); - if (vo == null) { - return null; - } - - return vo.getVnet(); + } + + @Override + public String allocateVnet(long dataCenterId, long accountId, String reservationId) { + DataCenterVnetVO vo = _vnetAllocDao.take(dataCenterId, accountId, reservationId); + if (vo == null) { + return null; + } + + return vo.getVnet(); } @Override @@ -174,34 +174,34 @@ public class DataCenterDaoImpl extends GenericDaoBase implem return null; } return vo.getVlan(); - } - - @Override - public String[] getNextAvailableMacAddressPair(long id) { - return getNextAvailableMacAddressPair(id, 0); - } - - @Override + } + + @Override + public String[] getNextAvailableMacAddressPair(long id) { + return getNextAvailableMacAddressPair(id, 0); + } + + @Override public String[] getNextAvailableMacAddressPair(long id, long mask) { SequenceFetcher fetch = SequenceFetcher.getInstance(); - long seq = fetch.getNextSequence(Long.class, _tgMacAddress, id); - seq = seq | _prefix | ((id & 0x7f) << 32); + long seq = fetch.getNextSequence(Long.class, _tgMacAddress, id); + seq = seq | _prefix | ((id & 0x7f) << 32); seq |= mask; seq |= ((_rand.nextInt(Short.MAX_VALUE) << 16) & 0x00000000ffff0000l); - String[] pair = new String[2]; - pair[0] = NetUtils.long2Mac(seq); - pair[1] = NetUtils.long2Mac(seq | 0x1l << 39); - return pair; - } - - @Override - public Pair allocatePrivateIpAddress(long dcId, long podId, long instanceId, String reservationId) { - DataCenterIpAddressVO vo = _ipAllocDao.takeIpAddress(dcId, podId, instanceId, reservationId); - if (vo == null) { - return null; - } - return new Pair(vo.getIpAddress(), vo.getMacAddress()); + String[] pair = new String[2]; + pair[0] = NetUtils.long2Mac(seq); + pair[1] = NetUtils.long2Mac(seq | 0x1l << 39); + return pair; + } + + @Override + public Pair allocatePrivateIpAddress(long dcId, long podId, long instanceId, String reservationId) { + DataCenterIpAddressVO vo = _ipAllocDao.takeIpAddress(dcId, podId, instanceId, reservationId); + if (vo == null) { + return null; + } + return new Pair(vo.getIpAddress(), vo.getMacAddress()); } @Override @@ -211,56 +211,56 @@ public class DataCenterDaoImpl extends GenericDaoBase implem return null; } return vo.getIpAddress(); - } - - @Override - public void addVnet(long dcId, int start, int end) { - _vnetAllocDao.add(dcId, start, end); - } - - @Override - public void deleteVnet(long dcId) { - _vnetAllocDao.delete(dcId); - } - - @Override - public List listAllocatedVnets(long dcId) { - return _vnetAllocDao.listAllocatedVnets(dcId); - } - - @Override - public void addPrivateIpAddress(long dcId,long podId, String start, String end) { - _ipAllocDao.addIpRange(dcId, podId, start, end); + } + + @Override + public void addVnet(long dcId, int start, int end) { + _vnetAllocDao.add(dcId, start, end); + } + + @Override + public void deleteVnet(long dcId) { + _vnetAllocDao.delete(dcId); + } + + @Override + public List listAllocatedVnets(long dcId) { + return _vnetAllocDao.listAllocatedVnets(dcId); + } + + @Override + public void addPrivateIpAddress(long dcId,long podId, String start, String end) { + _ipAllocDao.addIpRange(dcId, podId, start, end); } @Override public void addLinkLocalIpAddress(long dcId,long podId, String start, String end) { _LinkLocalIpAllocDao.addIpRange(dcId, podId, start, end); - } - - @Override - public boolean configure(String name, Map params) throws ConfigurationException { - if (!super.configure(name, params)) { - return false; - } - - String value = (String)params.get("mac.address.prefix"); - _prefix = (long)NumbersUtil.parseInt(value, 06) << 40; - - if (!_ipAllocDao.configure("Ip Alloc", params)) { - return false; - } - - if (!_vnetAllocDao.configure("vnet Alloc", params)) { - return false; - } - return true; - } - + } + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + if (!super.configure(name, params)) { + return false; + } + + String value = (String)params.get("mac.address.prefix"); + _prefix = (long)NumbersUtil.parseInt(value, 06) << 40; + + if (!_ipAllocDao.configure("Ip Alloc", params)) { + return false; + } + + if (!_vnetAllocDao.configure("vnet Alloc", params)) { + return false; + } + return true; + } + protected DataCenterDaoImpl() { - super(); + super(); NameSearch = createSearchBuilder(); - NameSearch.and("name", NameSearch.entity().getName(), SearchCriteria.Op.EQ); + NameSearch.and("name", NameSearch.entity().getName(), SearchCriteria.Op.EQ); NameSearch.done(); ListZonesByDomainIdSearch = createSearchBuilder(); @@ -288,7 +288,7 @@ public class DataCenterDaoImpl extends GenericDaoBase implem TokenSearch.done(); _tgMacAddress = _tgs.get("macAddress"); - assert _tgMacAddress != null : "Couldn't get mac address table generator"; + assert _tgMacAddress != null : "Couldn't get mac address table generator"; } @Override @DB @@ -338,4 +338,21 @@ public class DataCenterDaoImpl extends GenericDaoBase implem return dcs; } -} + + @Override + public DataCenterVO findByTokenOrIdOrName(String tokenOrIdOrName) { + DataCenterVO result = findByToken(tokenOrIdOrName); + if (result == null) { + result = findByName(tokenOrIdOrName); + if (result == null) { + try { + Long dcId = Long.parseLong(tokenOrIdOrName); + return findById(dcId); + } catch (NumberFormatException nfe) { + + } + } + } + return result; + } +} diff --git a/server/src/com/cloud/host/dao/HostDao.java b/server/src/com/cloud/host/dao/HostDao.java index 2ea271e3779..6255f64ab73 100644 --- a/server/src/com/cloud/host/dao/HostDao.java +++ b/server/src/com/cloud/host/dao/HostDao.java @@ -14,11 +14,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . * - */ -package com.cloud.host.dao; - + */ +package com.cloud.host.dao; + import java.util.Date; import java.util.List; +import java.util.Map; import com.cloud.host.Host; import com.cloud.host.Host.Type; @@ -28,119 +29,115 @@ import com.cloud.host.Status.Event; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.info.RunningHostCountInfo; import com.cloud.utils.db.GenericDao; - -/** - * Data Access Object for server - * - */ -public interface HostDao extends GenericDao { + +/** + * Data Access Object for server + * + */ +public interface HostDao extends GenericDao { List listBy(Host.Type type, Long clusterId, Long podId, long dcId); long countBy(long clusterId, Status... statuses); - - List listByDataCenter(long dcId); - List listByHostPod(long podId); - List listByStatus(Status... status); + + List listByDataCenter(long dcId); + List listByHostPod(long podId); + List listByStatus(Status... status); List listBy(Host.Type type, long dcId); - List listAllBy(Host.Type type, long dcId); - HostVO findSecondaryStorageHost(long dcId); - List listByCluster(long clusterId); - /** - * Lists all secondary storage hosts, across all zones - * @return list of Hosts - */ - List listSecondaryStorageHosts(); - - /** - * Mark all hosts in Up or Orphaned state as disconnected. This method - * is used at AgentManager startup to reset all of the connections. - * - * @param msId management server id. - * @param statuses states of the host. - */ - void markHostsAsDisconnected(long msId, Status... states); - - List findLostHosts(long timeout); - - List findHostsLike(String hostName); - - /** - * Find hosts that are directly connected. - */ - List findDirectlyConnectedHosts(); - - List findDirectAgentToLoad(long msid, long lastPingSecondsAfter, Long limit); - - /** - * Mark the host as disconnected if it is in one of these states. - * The management server id is set to null. - * The lastPinged timestamp is set to current. - * The state is set to the state passed in. - * The disconnectedOn timestamp is set to current. - * - * @param host host to be marked - * @param state state to be set to. - * @param ifStates only if it is one of these states. - * @return true if it's updated; false if not. - */ - boolean disconnect(HostVO host, Event event, long msId); - - boolean connect(HostVO host, long msId); - - HostVO findByStorageIpAddressInDataCenter(long dcId, String privateIpAddress); - HostVO findByPrivateIpAddressInDataCenter(long dcId, String privateIpAddress); - - /** - * find a host by its mac address - * @param macAddress - * @return HostVO or null if not found. - */ + List listAllBy(Host.Type type, long dcId); + HostVO findSecondaryStorageHost(long dcId); + List listByCluster(long clusterId); + /** + * Lists all secondary storage hosts, across all zones + * @return list of Hosts + */ + List listSecondaryStorageHosts(); + + /** + * Mark all hosts in Up or Orphaned state as disconnected. This method + * is used at AgentManager startup to reset all of the connections. + * + * @param msId management server id. + * @param statuses states of the host. + */ + void markHostsAsDisconnected(long msId, Status... states); + + List findLostHosts(long timeout); + + List findHostsLike(String hostName); + + /** + * Find hosts that are directly connected. + */ + List findDirectlyConnectedHosts(); + + List findDirectAgentToLoad(long msid, long lastPingSecondsAfter, Long limit); + + /** + * Mark the host as disconnected if it is in one of these states. + * The management server id is set to null. + * The lastPinged timestamp is set to current. + * The state is set to the state passed in. + * The disconnectedOn timestamp is set to current. + * + * @param host host to be marked + * @param state state to be set to. + * @param ifStates only if it is one of these states. + * @return true if it's updated; false if not. + */ + boolean disconnect(HostVO host, Event event, long msId); + + boolean connect(HostVO host, long msId); + + HostVO findByStorageIpAddressInDataCenter(long dcId, String privateIpAddress); + HostVO findByPrivateIpAddressInDataCenter(long dcId, String privateIpAddress); + + /** + * find a host by its mac address + * @param macAddress + * @return HostVO or null if not found. + */ public HostVO findByGuid(String macAddress); - - public HostVO findByName(String name); - - - /** - * find all hosts of a certain type in a data center - * @param type - * @param routingCapable - * @param dcId - * @return - */ - List listByTypeDataCenter(Host.Type type, long dcId); - - /** - * find all hosts of a particular type - * @param type - * @return - */ - List listByType(Type type); - - /** - * Find hosts that have not responded to a ping regardless of state - * @param timeout - * @param type - * @return - */ - List findLostHosts2(long timeout, Type type); - - /** - * update the host and changes the status depending on the Event and - * the current status. If the status changed between - * @param host host object to change - * @param event event that happened. - * @param management server who's making this update - * @return true if updated; false if not. - */ - boolean updateStatus(HostVO host, Event event, long msId); - - List getRunningHostCounts(Date cutTime); - - long getNextSequence(long hostId); - - void loadDetails(HostVO host); + + + /** + * find all hosts of a certain type in a data center + * @param type + * @param routingCapable + * @param dcId + * @return + */ + List listByTypeDataCenter(Host.Type type, long dcId); + + /** + * find all hosts of a particular type + * @param type + * @return + */ + List listByType(Type type); + + /** + * Find hosts that have not responded to a ping regardless of state + * @param timeout + * @param type + * @return + */ + List findLostHosts2(long timeout, Type type); + + /** + * update the host and changes the status depending on the Event and + * the current status. If the status changed between + * @param host host object to change + * @param event event that happened. + * @param management server who's making this update + * @return true if updated; false if not. + */ + boolean updateStatus(HostVO host, Event event, long msId); + + List getRunningHostCounts(Date cutTime); - void saveDetails(HostVO host); + long getNextSequence(long hostId); + + void loadDetails(HostVO host); HostVO findConsoleProxyHost(String name, Type type); @@ -162,6 +159,9 @@ public interface HostDao extends GenericDao { void loadHostTags(HostVO host); List listByHostTag(Host.Type type, Long clusterId, Long podId, long dcId, String hostTag); + + long countRoutingHostsByDataCenter(long dcId); + + List listSecondaryStorageHosts(long dataCenterId); - long countRoutingHostsByDataCenter(long dcId); -} +} diff --git a/server/src/com/cloud/host/dao/HostDaoImpl.java b/server/src/com/cloud/host/dao/HostDaoImpl.java index e5b3fd7f735..8261fa2e129 100644 --- a/server/src/com/cloud/host/dao/HostDaoImpl.java +++ b/server/src/com/cloud/host/dao/HostDaoImpl.java @@ -14,14 +14,15 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . * - */ -package com.cloud.host.dao; - + */ +package com.cloud.host.dao; + import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TimeZone; @@ -54,153 +55,148 @@ import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.db.Transaction; import com.cloud.utils.db.UpdateBuilder; import com.cloud.utils.exception.CloudRuntimeException; - + @Local(value = { HostDao.class }) @DB(txn=false) @TableGenerator(name="host_req_sq", table="op_host", pkColumnName="id", valueColumnName="sequence", allocationSize=1) -public class HostDaoImpl extends GenericDaoBase implements HostDao { +public class HostDaoImpl extends GenericDaoBase implements HostDao { private static final Logger s_logger = Logger.getLogger(HostDaoImpl.class); - - protected final SearchBuilder TypePodDcStatusSearch; - - protected final SearchBuilder IdStatusSearch; - protected final SearchBuilder TypeDcSearch; - protected final SearchBuilder TypeDcStatusSearch; - protected final SearchBuilder LastPingedSearch; - protected final SearchBuilder LastPingedSearch2; - protected final SearchBuilder MsStatusSearch; - protected final SearchBuilder DcPrivateIpAddressSearch; - protected final SearchBuilder DcStorageIpAddressSearch; - - protected final SearchBuilder GuidSearch; - protected final SearchBuilder DcSearch; - protected final SearchBuilder PodSearch; - protected final SearchBuilder TypeSearch; - protected final SearchBuilder StatusSearch; + + protected final SearchBuilder TypePodDcStatusSearch; + + protected final SearchBuilder IdStatusSearch; + protected final SearchBuilder TypeDcSearch; + protected final SearchBuilder TypeDcStatusSearch; + protected final SearchBuilder LastPingedSearch; + protected final SearchBuilder LastPingedSearch2; + protected final SearchBuilder MsStatusSearch; + protected final SearchBuilder DcPrivateIpAddressSearch; + protected final SearchBuilder DcStorageIpAddressSearch; + + protected final SearchBuilder GuidSearch; + protected final SearchBuilder DcSearch; + protected final SearchBuilder PodSearch; + protected final SearchBuilder TypeSearch; + protected final SearchBuilder StatusSearch; protected final SearchBuilder NameLikeSearch; - protected final SearchBuilder NameSearch; - protected final SearchBuilder SequenceSearch; + protected final SearchBuilder SequenceSearch; protected final SearchBuilder DirectlyConnectedSearch; protected final SearchBuilder UnmanagedDirectConnectSearch; protected final SearchBuilder UnmanagedExternalNetworkApplianceSearch; - protected final SearchBuilder MaintenanceCountSearch; + protected final SearchBuilder MaintenanceCountSearch; protected final SearchBuilder ClusterSearch; protected final SearchBuilder ConsoleProxyHostSearch; protected final SearchBuilder AvailHypevisorInZone; protected final GenericSearchBuilder HostsInStatusSearch; protected final GenericSearchBuilder CountRoutingByDc; - - protected final Attribute _statusAttr; - protected final Attribute _msIdAttr; - protected final Attribute _pingTimeAttr; - - protected final DetailsDaoImpl _detailsDao = ComponentLocator.inject(DetailsDaoImpl.class); - protected final HostTagsDaoImpl _hostTagsDao = ComponentLocator.inject(HostTagsDaoImpl.class); + + protected final Attribute _statusAttr; + protected final Attribute _msIdAttr; + protected final Attribute _pingTimeAttr; + + protected final DetailsDaoImpl _detailsDao = ComponentLocator.inject(DetailsDaoImpl.class); + protected final HostTagsDaoImpl _hostTagsDao = ComponentLocator.inject(HostTagsDaoImpl.class); - public HostDaoImpl() { + public HostDaoImpl() { MaintenanceCountSearch = createSearchBuilder(); MaintenanceCountSearch.and("cluster", MaintenanceCountSearch.entity().getClusterId(), SearchCriteria.Op.EQ); MaintenanceCountSearch.and("status", MaintenanceCountSearch.entity().getStatus(), SearchCriteria.Op.IN); MaintenanceCountSearch.done(); - - TypePodDcStatusSearch = createSearchBuilder(); - HostVO entity = TypePodDcStatusSearch.entity(); - TypePodDcStatusSearch.and("type", entity.getType(), SearchCriteria.Op.EQ); - TypePodDcStatusSearch.and("pod", entity.getPodId(), SearchCriteria.Op.EQ); + + TypePodDcStatusSearch = createSearchBuilder(); + HostVO entity = TypePodDcStatusSearch.entity(); + TypePodDcStatusSearch.and("type", entity.getType(), SearchCriteria.Op.EQ); + TypePodDcStatusSearch.and("pod", entity.getPodId(), SearchCriteria.Op.EQ); TypePodDcStatusSearch.and("dc", entity.getDataCenterId(), SearchCriteria.Op.EQ); - TypePodDcStatusSearch.and("cluster", entity.getClusterId(), SearchCriteria.Op.EQ); - TypePodDcStatusSearch.and("status", entity.getStatus(), SearchCriteria.Op.EQ); - TypePodDcStatusSearch.done(); - - LastPingedSearch = createSearchBuilder(); - LastPingedSearch.and("ping", LastPingedSearch.entity().getLastPinged(), SearchCriteria.Op.LT); - LastPingedSearch.and("state", LastPingedSearch.entity().getStatus(), SearchCriteria.Op.IN); - LastPingedSearch.done(); - - LastPingedSearch2 = createSearchBuilder(); - LastPingedSearch2.and("ping", LastPingedSearch2.entity().getLastPinged(), SearchCriteria.Op.LT); - LastPingedSearch2.and("type", LastPingedSearch2.entity().getType(), SearchCriteria.Op.EQ); - LastPingedSearch2.done(); - - MsStatusSearch = createSearchBuilder(); - MsStatusSearch.and("ms", MsStatusSearch.entity().getManagementServerId(), SearchCriteria.Op.EQ); - MsStatusSearch.and("statuses", MsStatusSearch.entity().getStatus(), SearchCriteria.Op.IN); - MsStatusSearch.done(); - - TypeDcSearch = createSearchBuilder(); - TypeDcSearch.and("type", TypeDcSearch.entity().getType(), SearchCriteria.Op.EQ); - TypeDcSearch.and("dc", TypeDcSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); - TypeDcSearch.done(); - - TypeDcStatusSearch = createSearchBuilder(); - TypeDcStatusSearch.and("type", TypeDcStatusSearch.entity().getType(), SearchCriteria.Op.EQ); - TypeDcStatusSearch.and("dc", TypeDcStatusSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); - TypeDcStatusSearch.and("status", TypeDcStatusSearch.entity().getStatus(), SearchCriteria.Op.EQ); - TypeDcStatusSearch.done(); - - IdStatusSearch = createSearchBuilder(); - IdStatusSearch.and("id", IdStatusSearch.entity().getId(), SearchCriteria.Op.EQ); - IdStatusSearch.and("states", IdStatusSearch.entity().getStatus(), SearchCriteria.Op.IN); - IdStatusSearch.done(); - - DcPrivateIpAddressSearch = createSearchBuilder(); - DcPrivateIpAddressSearch.and("privateIpAddress", DcPrivateIpAddressSearch.entity().getPrivateIpAddress(), SearchCriteria.Op.EQ); - DcPrivateIpAddressSearch.and("dc", DcPrivateIpAddressSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); - DcPrivateIpAddressSearch.done(); - - DcStorageIpAddressSearch = createSearchBuilder(); - DcStorageIpAddressSearch.and("storageIpAddress", DcStorageIpAddressSearch.entity().getStorageIpAddress(), SearchCriteria.Op.EQ); - DcStorageIpAddressSearch.and("dc", DcStorageIpAddressSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); - DcStorageIpAddressSearch.done(); - - GuidSearch = createSearchBuilder(); - GuidSearch.and("guid", GuidSearch.entity().getGuid(), SearchCriteria.Op.EQ); - GuidSearch.done(); - - DcSearch = createSearchBuilder(); - DcSearch.and("dc", DcSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + TypePodDcStatusSearch.and("cluster", entity.getClusterId(), SearchCriteria.Op.EQ); + TypePodDcStatusSearch.and("status", entity.getStatus(), SearchCriteria.Op.EQ); + TypePodDcStatusSearch.done(); + + LastPingedSearch = createSearchBuilder(); + LastPingedSearch.and("ping", LastPingedSearch.entity().getLastPinged(), SearchCriteria.Op.LT); + LastPingedSearch.and("state", LastPingedSearch.entity().getStatus(), SearchCriteria.Op.IN); + LastPingedSearch.done(); + + LastPingedSearch2 = createSearchBuilder(); + LastPingedSearch2.and("ping", LastPingedSearch2.entity().getLastPinged(), SearchCriteria.Op.LT); + LastPingedSearch2.and("type", LastPingedSearch2.entity().getType(), SearchCriteria.Op.EQ); + LastPingedSearch2.done(); + + MsStatusSearch = createSearchBuilder(); + MsStatusSearch.and("ms", MsStatusSearch.entity().getManagementServerId(), SearchCriteria.Op.EQ); + MsStatusSearch.and("statuses", MsStatusSearch.entity().getStatus(), SearchCriteria.Op.IN); + MsStatusSearch.done(); + + TypeDcSearch = createSearchBuilder(); + TypeDcSearch.and("type", TypeDcSearch.entity().getType(), SearchCriteria.Op.EQ); + TypeDcSearch.and("dc", TypeDcSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + TypeDcSearch.done(); + + TypeDcStatusSearch = createSearchBuilder(); + TypeDcStatusSearch.and("type", TypeDcStatusSearch.entity().getType(), SearchCriteria.Op.EQ); + TypeDcStatusSearch.and("dc", TypeDcStatusSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + TypeDcStatusSearch.and("status", TypeDcStatusSearch.entity().getStatus(), SearchCriteria.Op.EQ); + TypeDcStatusSearch.done(); + + IdStatusSearch = createSearchBuilder(); + IdStatusSearch.and("id", IdStatusSearch.entity().getId(), SearchCriteria.Op.EQ); + IdStatusSearch.and("states", IdStatusSearch.entity().getStatus(), SearchCriteria.Op.IN); + IdStatusSearch.done(); + + DcPrivateIpAddressSearch = createSearchBuilder(); + DcPrivateIpAddressSearch.and("privateIpAddress", DcPrivateIpAddressSearch.entity().getPrivateIpAddress(), SearchCriteria.Op.EQ); + DcPrivateIpAddressSearch.and("dc", DcPrivateIpAddressSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + DcPrivateIpAddressSearch.done(); + + DcStorageIpAddressSearch = createSearchBuilder(); + DcStorageIpAddressSearch.and("storageIpAddress", DcStorageIpAddressSearch.entity().getStorageIpAddress(), SearchCriteria.Op.EQ); + DcStorageIpAddressSearch.and("dc", DcStorageIpAddressSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + DcStorageIpAddressSearch.done(); + + GuidSearch = createSearchBuilder(); + GuidSearch.and("guid", GuidSearch.entity().getGuid(), SearchCriteria.Op.EQ); + GuidSearch.done(); + + DcSearch = createSearchBuilder(); + DcSearch.and("dc", DcSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); DcSearch.done(); ClusterSearch = createSearchBuilder(); ClusterSearch.and("cluster", ClusterSearch.entity().getClusterId(), SearchCriteria.Op.EQ); - ClusterSearch.done(); + 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); - PodSearch.done(); - - TypeSearch = createSearchBuilder(); - TypeSearch.and("type", TypeSearch.entity().getType(), SearchCriteria.Op.EQ); - TypeSearch.done(); - - StatusSearch =createSearchBuilder(); - StatusSearch.and("status", StatusSearch.entity().getStatus(), SearchCriteria.Op.IN); - StatusSearch.done(); - - NameLikeSearch = createSearchBuilder(); - NameLikeSearch.and("name", NameLikeSearch.entity().getName(), SearchCriteria.Op.LIKE); + + PodSearch = createSearchBuilder(); + PodSearch.and("pod", PodSearch.entity().getPodId(), SearchCriteria.Op.EQ); + PodSearch.done(); + + TypeSearch = createSearchBuilder(); + TypeSearch.and("type", TypeSearch.entity().getType(), SearchCriteria.Op.EQ); + TypeSearch.done(); + + StatusSearch =createSearchBuilder(); + StatusSearch.and("status", StatusSearch.entity().getStatus(), SearchCriteria.Op.IN); + StatusSearch.done(); + + NameLikeSearch = createSearchBuilder(); + NameLikeSearch.and("name", NameLikeSearch.entity().getName(), SearchCriteria.Op.LIKE); NameLikeSearch.done(); - NameSearch = createSearchBuilder(); - NameSearch.and("name", NameSearch.entity().getName(), SearchCriteria.Op.EQ); - NameSearch.done(); - - SequenceSearch = createSearchBuilder(); - SequenceSearch.and("id", SequenceSearch.entity().getId(), SearchCriteria.Op.EQ); -// SequenceSearch.addRetrieve("sequence", SequenceSearch.entity().getSequence()); - SequenceSearch.done(); - - DirectlyConnectedSearch = createSearchBuilder(); - DirectlyConnectedSearch.and("resource", DirectlyConnectedSearch.entity().getResource(), SearchCriteria.Op.NNULL); + SequenceSearch = createSearchBuilder(); + SequenceSearch.and("id", SequenceSearch.entity().getId(), SearchCriteria.Op.EQ); +// SequenceSearch.addRetrieve("sequence", SequenceSearch.entity().getSequence()); + SequenceSearch.done(); + + DirectlyConnectedSearch = createSearchBuilder(); + DirectlyConnectedSearch.and("resource", DirectlyConnectedSearch.entity().getResource(), SearchCriteria.Op.NNULL); DirectlyConnectedSearch.done(); - UnmanagedDirectConnectSearch = createSearchBuilder(); + UnmanagedDirectConnectSearch = createSearchBuilder(); UnmanagedDirectConnectSearch.and("resource", UnmanagedDirectConnectSearch.entity().getResource(), SearchCriteria.Op.NNULL); UnmanagedDirectConnectSearch.and("server", UnmanagedDirectConnectSearch.entity().getManagementServerId(), SearchCriteria.Op.NULL); UnmanagedDirectConnectSearch.and("lastPinged", UnmanagedDirectConnectSearch.entity().getLastPinged(), SearchCriteria.Op.LTEQ); @@ -241,13 +237,13 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao CountRoutingByDc.and("dc", CountRoutingByDc.entity().getDataCenterId(), SearchCriteria.Op.EQ); CountRoutingByDc.and("type", CountRoutingByDc.entity().getType(), SearchCriteria.Op.EQ); CountRoutingByDc.and("status", CountRoutingByDc.entity().getStatus(), SearchCriteria.Op.EQ); - CountRoutingByDc.done(); - - _statusAttr = _allAttributes.get("status"); - _msIdAttr = _allAttributes.get("managementServerId"); - _pingTimeAttr = _allAttributes.get("lastPinged"); - - assert (_statusAttr != null && _msIdAttr != null && _pingTimeAttr != null) : "Couldn't find one of these attributes"; + CountRoutingByDc.done(); + + _statusAttr = _allAttributes.get("status"); + _msIdAttr = _allAttributes.get("managementServerId"); + _pingTimeAttr = _allAttributes.get("lastPinged"); + + assert (_statusAttr != null && _msIdAttr != null && _pingTimeAttr != null) : "Couldn't find one of these attributes"; } @Override @@ -259,77 +255,77 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao List hosts = listBy(sc); return hosts.size(); - } - - @Override - public HostVO findSecondaryStorageHost(long dcId) { - SearchCriteria sc = TypeDcSearch.create(); - sc.setParameters("type", Host.Type.SecondaryStorage); - sc.setParameters("dc", dcId); - List storageHosts = listBy(sc); - - if (storageHosts == null || storageHosts.size() != 1) { - return null; - } else { - return storageHosts.get(0); - } - } - - @Override - public List listSecondaryStorageHosts() { - SearchCriteria sc = TypeSearch.create(); - sc.setParameters("type", Host.Type.SecondaryStorage); - List secondaryStorageHosts = listIncludingRemovedBy(sc); - - return secondaryStorageHosts; - } - - @Override - public List findDirectlyConnectedHosts() { - SearchCriteria sc = DirectlyConnectedSearch.create(); - return search(sc, null); - } - - @Override - public List findDirectAgentToLoad(long msid, long lastPingSecondsAfter, Long limit) { - SearchCriteria sc = UnmanagedDirectConnectSearch.create(); - sc.setParameters("lastPinged", lastPingSecondsAfter); - return search(sc, new Filter(HostVO.class, "clusterId", true, 0L, limit)); } - - @Override - public void markHostsAsDisconnected(long msId, Status... states) { - SearchCriteria sc = MsStatusSearch.create(); - sc.setParameters("ms", msId); - sc.setParameters("statuses", (Object[])states); - - HostVO host = createForUpdate(); + + @Override + public HostVO findSecondaryStorageHost(long dcId) { + SearchCriteria sc = TypeDcSearch.create(); + sc.setParameters("type", Host.Type.SecondaryStorage); + sc.setParameters("dc", dcId); + List storageHosts = listBy(sc); + + if (storageHosts == null || storageHosts.size() != 1) { + return null; + } else { + return storageHosts.get(0); + } + } + + @Override + public List listSecondaryStorageHosts() { + SearchCriteria sc = TypeSearch.create(); + sc.setParameters("type", Host.Type.SecondaryStorage); + List secondaryStorageHosts = listIncludingRemovedBy(sc); + + return secondaryStorageHosts; + } + + @Override + public List findDirectlyConnectedHosts() { + SearchCriteria sc = DirectlyConnectedSearch.create(); + return search(sc, null); + } + + @Override + public List findDirectAgentToLoad(long msid, long lastPingSecondsAfter, Long limit) { + SearchCriteria sc = UnmanagedDirectConnectSearch.create(); + sc.setParameters("lastPinged", lastPingSecondsAfter); + return search(sc, new Filter(HostVO.class, "clusterId", true, 0L, limit)); + } + + @Override + public void markHostsAsDisconnected(long msId, Status... states) { + SearchCriteria sc = MsStatusSearch.create(); + sc.setParameters("ms", msId); + sc.setParameters("statuses", (Object[])states); + + HostVO host = createForUpdate(); host.setManagementServerId(null); - host.setLastPinged((System.currentTimeMillis() >> 10) - ( 10 * 60 )); - host.setDisconnectedOn(new Date()); - - UpdateBuilder ub = getUpdateBuilder(host); - ub.set(host, "status", Status.Disconnected); - - update(ub, sc, null); - } - - @Override - public List listBy(Host.Type type, Long clusterId, Long podId, long dcId) { + host.setLastPinged((System.currentTimeMillis() >> 10) - ( 10 * 60 )); + host.setDisconnectedOn(new Date()); + + UpdateBuilder ub = getUpdateBuilder(host); + ub.set(host, "status", Status.Disconnected); + + update(ub, sc, null); + } + + @Override + public List listBy(Host.Type type, Long clusterId, Long podId, long dcId) { SearchCriteria sc = TypePodDcStatusSearch.create(); - if ( type != null ) { + if ( type != null ) { sc.setParameters("type", type.toString()); } if (clusterId != null) { sc.setParameters("cluster", clusterId); } - if (podId != null ) { + if (podId != null ) { sc.setParameters("pod", podId); } sc.setParameters("dc", dcId); - sc.setParameters("status", Status.Up.toString()); - - return listBy(sc); + sc.setParameters("status", Status.Up.toString()); + + return listBy(sc); } @Override @@ -385,15 +381,15 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao return listBy(sc); } - - @Override - public List listBy(Host.Type type, long dcId) { - SearchCriteria sc = TypeDcStatusSearch.create(); - sc.setParameters("type", type.toString()); - sc.setParameters("dc", dcId); - sc.setParameters("status", Status.Up.toString()); - - return listBy(sc); + + @Override + public List listBy(Host.Type type, long dcId) { + SearchCriteria sc = TypeDcStatusSearch.create(); + sc.setParameters("type", type.toString()); + sc.setParameters("dc", dcId); + sc.setParameters("status", Status.Up.toString()); + + return listBy(sc); } @Override @@ -403,50 +399,50 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao sc.setParameters("dc", dcId); return listBy(sc); - } - - @Override - public HostVO findByPrivateIpAddressInDataCenter(long dcId, String privateIpAddress) { - SearchCriteria sc = DcPrivateIpAddressSearch.create(); - sc.setParameters("dc", dcId); - sc.setParameters("privateIpAddress", privateIpAddress); - - return findOneBy(sc); - } - - @Override - public HostVO findByStorageIpAddressInDataCenter(long dcId, String privateIpAddress) { - SearchCriteria sc = DcStorageIpAddressSearch.create(); - sc.setParameters("dc", dcId); - sc.setParameters("storageIpAddress", privateIpAddress); - - return findOneBy(sc); - } - - @Override - public void loadDetails(HostVO host) { - Map details =_detailsDao.findDetails(host.getId()); - host.setDetails(details); - } + } + + @Override + public HostVO findByPrivateIpAddressInDataCenter(long dcId, String privateIpAddress) { + SearchCriteria sc = DcPrivateIpAddressSearch.create(); + sc.setParameters("dc", dcId); + sc.setParameters("privateIpAddress", privateIpAddress); + + return findOneBy(sc); + } + + @Override + public HostVO findByStorageIpAddressInDataCenter(long dcId, String privateIpAddress) { + SearchCriteria sc = DcStorageIpAddressSearch.create(); + sc.setParameters("dc", dcId); + sc.setParameters("storageIpAddress", privateIpAddress); + + return findOneBy(sc); + } + + @Override + public void loadDetails(HostVO host) { + Map details =_detailsDao.findDetails(host.getId()); + host.setDetails(details); + } @Override public void loadHostTags(HostVO host){ List hostTags = _hostTagsDao.gethostTags(host.getId()); host.setHostTags(hostTags); - } + } - @Override - public boolean updateStatus(HostVO host, Event event, long msId) { - Status oldStatus = host.getStatus(); - long oldPingTime = host.getLastPinged(); - Status newStatus = oldStatus.getNextStatus(event); + @Override + public boolean updateStatus(HostVO host, Event event, long msId) { + Status oldStatus = host.getStatus(); + long oldPingTime = host.getLastPinged(); + Status newStatus = oldStatus.getNextStatus(event); if ( host == null ) { return false; } - - if (newStatus == null) { - return false; - } + + if (newStatus == null) { + return false; + } SearchBuilder sb = createSearchBuilder(); sb.and("status", sb.entity().getStatus(), SearchCriteria.Op.EQ); @@ -458,112 +454,106 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao sb.closeParen(); } sb.done(); - - SearchCriteria sc = sb.create(); - - sc.setParameters("status", oldStatus); - sc.setParameters("id", host.getId()); - if (newStatus.checkManagementServer()) { + + SearchCriteria sc = sb.create(); + + sc.setParameters("status", oldStatus); + sc.setParameters("id", host.getId()); + if (newStatus.checkManagementServer()) { sc.setParameters("ping", oldPingTime); - sc.setParameters("msid", msId); - } - - UpdateBuilder ub = getUpdateBuilder(host); - ub.set(host, _statusAttr, newStatus); + sc.setParameters("msid", msId); + } + + UpdateBuilder ub = getUpdateBuilder(host); + ub.set(host, _statusAttr, newStatus); if (newStatus.updateManagementServer()) { if (newStatus.lostConnection()) { ub.set(host, _msIdAttr, null); - } else { + } else { ub.set(host, _msIdAttr, msId); } - if( event.equals(Event.Ping) || event.equals(Event.AgentConnected)) { + if( event.equals(Event.Ping) || event.equals(Event.AgentConnected)) { ub.set(host, _pingTimeAttr, System.currentTimeMillis() >> 10); - } + } } if ( event.equals(Event.ManagementServerDown)) { ub.set(host, _pingTimeAttr, (( System.currentTimeMillis() >> 10) - ( 10 * 60 ))); - } - int result = update(ub, sc, null); - assert result <= 1 : "How can this update " + result + " rows? "; - - if (s_logger.isDebugEnabled() && result == 0) { - HostVO vo = findById(host.getId()); - assert vo != null : "How how how? : " + host.getId(); - - StringBuilder str = new StringBuilder("Unable to update host for event:").append(event.toString()); - str.append(". New=[status=").append(newStatus.toString()).append(":msid=").append(newStatus.lostConnection() ? "null" : msId).append(":lastpinged=").append(host.getLastPinged()).append("]"); - str.append("; Old=[status=").append(oldStatus.toString()).append(":msid=").append(msId).append(":lastpinged=").append(oldPingTime).append("]"); - str.append("; DB=[status=").append(vo.getStatus().toString()).append(":msid=").append(vo.getManagementServerId()).append(":lastpinged=").append(vo.getLastPinged()).append("]"); - s_logger.debug(str.toString()); - } - return result > 0; - } - - @Override - public boolean disconnect(HostVO host, Event event, long msId) { - host.setDisconnectedOn(new Date()); + } + int result = update(ub, sc, null); + assert result <= 1 : "How can this update " + result + " rows? "; + + if (s_logger.isDebugEnabled() && result == 0) { + HostVO vo = findById(host.getId()); + assert vo != null : "How how how? : " + host.getId(); + + StringBuilder str = new StringBuilder("Unable to update host for event:").append(event.toString()); + str.append(". New=[status=").append(newStatus.toString()).append(":msid=").append(newStatus.lostConnection() ? "null" : msId).append(":lastpinged=").append(host.getLastPinged()).append("]"); + str.append("; Old=[status=").append(oldStatus.toString()).append(":msid=").append(msId).append(":lastpinged=").append(oldPingTime).append("]"); + str.append("; DB=[status=").append(vo.getStatus().toString()).append(":msid=").append(vo.getManagementServerId()).append(":lastpinged=").append(vo.getLastPinged()).append("]"); + s_logger.debug(str.toString()); + } + return result > 0; + } + + @Override + public boolean disconnect(HostVO host, Event event, long msId) { + host.setDisconnectedOn(new Date()); if(event!=null && event.equals(Event.Remove)) { host.setGuid(null); host.setClusterId(null); - } - return updateStatus(host, event, msId); - } - - @Override @DB - public boolean connect(HostVO host, long msId) { - Transaction txn = Transaction.currentTxn(); - long id = host.getId(); - txn.start(); - - if (!updateStatus(host, Event.AgentConnected, msId)) { - return false; - } - - txn.commit(); - return true; - } - - @Override - public HostVO findByGuid(String guid) { - SearchCriteria sc = GuidSearch.create("guid", guid); - return findOneBy(sc); + } + return updateStatus(host, event, msId); + } + + @Override @DB + public boolean connect(HostVO host, long msId) { + Transaction txn = Transaction.currentTxn(); + long id = host.getId(); + txn.start(); + + if (!updateStatus(host, Event.AgentConnected, msId)) { + return false; + } + + txn.commit(); + return true; + } + + @Override + public HostVO findByGuid(String guid) { + SearchCriteria sc = GuidSearch.create("guid", guid); + return findOneBy(sc); + } + + @Override + public List findLostHosts(long timeout) { + SearchCriteria sc = LastPingedSearch.create(); + sc.setParameters("ping", timeout); + sc.setParameters("state", Status.Up.toString(), Status.Updating.toString(), + Status.Disconnected.toString(), Status.Down.toString()); + return listBy(sc); } - @Override - public HostVO findByName(String name) { - SearchCriteria sc = NameSearch.create("name", name); - return findOneBy(sc); - } - - @Override - public List findLostHosts(long timeout) { - SearchCriteria sc = LastPingedSearch.create(); - sc.setParameters("ping", timeout); - sc.setParameters("state", Status.Up.toString(), Status.Updating.toString(), - Status.Disconnected.toString(), Status.Down.toString()); - return listBy(sc); - } - @Override - public List findHostsLike(String hostName) { - SearchCriteria sc = NameLikeSearch.create(); - sc.setParameters("name", "%" + hostName + "%"); - return listBy(sc); - } - - @Override - public List findLostHosts2(long timeout, Type type) { - SearchCriteria sc = LastPingedSearch2.create(); - sc.setParameters("ping", timeout); - sc.setParameters("type", type.toString()); - return listBy(sc); - } - - @Override - public List listByDataCenter(long dcId) { - SearchCriteria sc = DcSearch.create("dc", dcId); - return listBy(sc); - } + public List findHostsLike(String hostName) { + SearchCriteria sc = NameLikeSearch.create(); + sc.setParameters("name", "%" + hostName + "%"); + return listBy(sc); + } + + @Override + public List findLostHosts2(long timeout, Type type) { + SearchCriteria sc = LastPingedSearch2.create(); + sc.setParameters("ping", timeout); + sc.setParameters("type", type.toString()); + return listBy(sc); + } + + @Override + public List listByDataCenter(long dcId) { + SearchCriteria sc = DcSearch.create("dc", dcId); + return listBy(sc); + } @Override public HostVO findConsoleProxyHost(String name, Type type) { @@ -578,43 +568,42 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao return hostList.get(0); } } - + @Override - public List listByHostPod(long podId) { - SearchCriteria sc = PodSearch.create("pod", podId); - return listBy(sc); - } - - @Override - public List listByStatus(Status... status) { - SearchCriteria sc = StatusSearch.create(); - sc.setParameters("status", (Object[])status); - return listBy(sc); - } - - @Override - public List listByTypeDataCenter(Type type, long dcId) { - SearchCriteria sc = TypeDcSearch.create(); - sc.setParameters("type", type.toString()); - sc.setParameters("dc", dcId); - - return listBy(sc); - } - - @Override - public List listByType(Type type) { - SearchCriteria sc = TypeSearch.create(); - sc.setParameters("type", type.toString()); - return listBy(sc); - } + public List listByHostPod(long podId) { + SearchCriteria sc = PodSearch.create("pod", podId); + return listBy(sc); + } + + @Override + public List listByStatus(Status... status) { + SearchCriteria sc = StatusSearch.create(); + sc.setParameters("status", (Object[])status); + return listBy(sc); + } - @Override - public void saveDetails(HostVO host) { - Map details = host.getDetails(); - if (details == null) { - return; - } - _detailsDao.persist(host.getId(), details); + @Override + public List listByTypeDataCenter(Type type, long dcId) { + SearchCriteria sc = TypeDcSearch.create(); + sc.setParameters("type", type.toString()); + sc.setParameters("dc", dcId); + + return listBy(sc); + } + + @Override + public List listByType(Type type) { + SearchCriteria sc = TypeSearch.create(); + sc.setParameters("type", type.toString()); + return listBy(sc); + } + + protected void saveDetails(HostVO host) { + Map details = host.getDetails(); + if (details == null) { + return; + } + _detailsDao.persist(host.getId(), details); } protected void saveHostTags(HostVO host) { @@ -623,15 +612,15 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao return; } _hostTagsDao.persist(host.getId(), hostTags); - } - - @Override @DB + } + + @Override @DB public HostVO persist(HostVO host) { final String InsertSequenceSql = "INSERT INTO op_host(id) VALUES(?)"; - - Transaction txn = Transaction.currentTxn(); - txn.start(); - + + Transaction txn = Transaction.currentTxn(); + txn.start(); + HostVO dbHost = super.persist(host); try { @@ -646,71 +635,71 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao loadDetails(dbHost); saveHostTags(host); loadHostTags(dbHost); - - txn.commit(); - - return dbHost; + + txn.commit(); + + return dbHost; } - @Override @DB - public boolean update(Long hostId, HostVO host) { - Transaction txn = Transaction.currentTxn(); - txn.start(); - - boolean persisted = super.update(hostId, host); - if (!persisted) { - return persisted; - } - - saveDetails(host); + @Override @DB + public boolean update(Long hostId, HostVO host) { + Transaction txn = Transaction.currentTxn(); + txn.start(); + + boolean persisted = super.update(hostId, host); + if (!persisted) { + return persisted; + } + + saveDetails(host); saveHostTags(host); - - txn.commit(); - - return persisted; - } - - @Override @DB - public List getRunningHostCounts(Date cutTime) { + + txn.commit(); + + return persisted; + } + + @Override @DB + public List getRunningHostCounts(Date cutTime) { String sql = "select * from (" + - "select h.data_center_id, h.type, count(*) as count from host as h INNER JOIN mshost as m ON h.mgmt_server_id=m.msid " + - "where h.status='Up' and h.type='SecondaryStorage' and m.last_update > ? " + - "group by h.data_center_id, h.type " + - "UNION ALL " + - "select h.data_center_id, h.type, count(*) as count from host as h INNER JOIN mshost as m ON h.mgmt_server_id=m.msid " + - "where h.status='Up' and h.type='Routing' and m.last_update > ? " + - "group by h.data_center_id, h.type) as t " + - "ORDER by t.data_center_id, t.type"; - + "select h.data_center_id, h.type, count(*) as count from host as h INNER JOIN mshost as m ON h.mgmt_server_id=m.msid " + + "where h.status='Up' and h.type='SecondaryStorage' and m.last_update > ? " + + "group by h.data_center_id, h.type " + + "UNION ALL " + + "select h.data_center_id, h.type, count(*) as count from host as h INNER JOIN mshost as m ON h.mgmt_server_id=m.msid " + + "where h.status='Up' and h.type='Routing' and m.last_update > ? " + + "group by h.data_center_id, h.type) as t " + + "ORDER by t.data_center_id, t.type"; + ArrayList l = new ArrayList(); - Transaction txn = Transaction.currentTxn();; - PreparedStatement pstmt = null; - try { + Transaction txn = Transaction.currentTxn();; + PreparedStatement pstmt = null; + try { pstmt = txn.prepareAutoCloseStatement(sql); String gmtCutTime = DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), cutTime); pstmt.setString(1, gmtCutTime); pstmt.setString(2, gmtCutTime); - - ResultSet rs = pstmt.executeQuery(); - while(rs.next()) { - RunningHostCountInfo info = new RunningHostCountInfo(); - info.setDcId(rs.getLong(1)); - info.setHostType(rs.getString(2)); - info.setCount(rs.getInt(3)); - - l.add(info); - } - } catch (SQLException e) { - } catch (Throwable e) { - } - return l; - } - + + ResultSet rs = pstmt.executeQuery(); + while(rs.next()) { + RunningHostCountInfo info = new RunningHostCountInfo(); + info.setDcId(rs.getLong(1)); + info.setHostType(rs.getString(2)); + info.setCount(rs.getInt(3)); + + l.add(info); + } + } catch (SQLException e) { + } catch (Throwable e) { + } + return l; + } + @Override - public long getNextSequence(long hostId) { - if (s_logger.isTraceEnabled()) { - s_logger.trace("getNextSequence(), hostId: " + hostId); + public long getNextSequence(long hostId) { + if (s_logger.isTraceEnabled()) { + s_logger.trace("getNextSequence(), hostId: " + hostId); } TableGenerator tg = _tgs.get("host_req_sq"); @@ -764,8 +753,18 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao sc.setParameters("type", Host.Type.Routing); sc.setParameters("status", Status.Up.toString()); return customSearch(sc, null).get(0); - } -} - - - + } + + @Override + public List listSecondaryStorageHosts(long dataCenterId) { + SearchCriteria sc = TypeDcSearch.create(); + sc.setParameters("type", Host.Type.SecondaryStorage); + sc.setParameters("dc", dataCenterId); + List secondaryStorageHosts = listIncludingRemovedBy(sc); + + return secondaryStorageHosts; + } +} + + + diff --git a/server/src/com/cloud/hypervisor/kvm/discoverer/KvmServerDiscoverer.java b/server/src/com/cloud/hypervisor/kvm/discoverer/KvmServerDiscoverer.java index 540a70e049e..ece32d98e8a 100644 --- a/server/src/com/cloud/hypervisor/kvm/discoverer/KvmServerDiscoverer.java +++ b/server/src/com/cloud/hypervisor/kvm/discoverer/KvmServerDiscoverer.java @@ -166,11 +166,7 @@ public class KvmServerDiscoverer extends DiscovererBase implements Discoverer, return null; } - s_logger.debug("copying " + _setupAgentPath + " to host"); - SCPClient scp = new SCPClient(sshConnection); - scp.put(_setupAgentPath, "/usr/bin", "0755"); - - String parameters = " -h " + _hostIp + " -z " + dcId + " -p " + podId + " -c " + clusterId + " -u " + guid; + String parameters = " --host=" + _hostIp + " --zone=" + dcId + " --pod=" + podId + " --cluster=" + clusterId + " --guid=" + guid + " -a"; if (_kvmPublicNic != null) { parameters += " -P " + _kvmPublicNic; @@ -180,7 +176,7 @@ public class KvmServerDiscoverer extends DiscovererBase implements Discoverer, parameters += " -N " + _kvmPrivateNic; } - SSHCmdHelper.sshExecuteCmd(sshConnection, "/usr/bin/setup_agent.sh " + parameters + " 1>&2", 3); + SSHCmdHelper.sshExecuteCmd(sshConnection, "cloud-setup-agent " + parameters + " >& /dev/null", 3); KvmDummyResourceBase kvmResource = new KvmDummyResourceBase(); Map params = new HashMap(); diff --git a/server/src/com/cloud/network/guru/DirectPodBasedNetworkGuru.java b/server/src/com/cloud/network/guru/DirectPodBasedNetworkGuru.java index 6284ce6f2c4..686153faea0 100644 --- a/server/src/com/cloud/network/guru/DirectPodBasedNetworkGuru.java +++ b/server/src/com/cloud/network/guru/DirectPodBasedNetworkGuru.java @@ -81,21 +81,30 @@ public class DirectPodBasedNetworkGuru extends DirectNetworkGuru { public NicProfile allocate(Network network, NicProfile nic, VirtualMachineProfile vm) throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException, ConcurrentOperationException { - DataCenter dc = _dcDao.findById(network.getDataCenterId()); + DataCenterVO dc = _dcDao.findById(network.getDataCenterId()); + ReservationStrategy rsStrategy = ReservationStrategy.Start; + _dcDao.loadDetails(dc); + String dhcpStrategy = dc.getDetail(ZoneConfig.DhcpStrategy.key()); + if ("external".equalsIgnoreCase(dhcpStrategy)) { + rsStrategy = ReservationStrategy.Create; + } NetworkOffering offering = _networkOfferingDao.findByIdIncludingRemoved(network.getNetworkOfferingId()); - + if (!canHandle(offering, dc)) { return null; } - + if (nic == null) { - nic = new NicProfile(ReservationStrategy.Start, null, null, null, null); + nic = new NicProfile(rsStrategy, null, null, null, null); } else if (nic.getIp4Address() == null) { nic.setStrategy(ReservationStrategy.Start); } else { nic.setStrategy(ReservationStrategy.Create); } - + if (rsStrategy == ReservationStrategy.Create) { + String mac = _networkMgr.getNextAvailableMacAddressInNetwork(network.getId()); + nic.setMacAddress(mac); + } return nic; } diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index 3fdc52397dc..7e28ce18fb5 100755 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -17,18 +17,12 @@ */ package com.cloud.network.router; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.MalformedURLException; -import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.StringTokenizer; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -151,6 +145,7 @@ import com.cloud.user.dao.UserStatisticsDao; import com.cloud.uservm.UserVm; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; +import com.cloud.utils.PasswordGenerator; import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.component.Inject; import com.cloud.utils.concurrency.NamedThreadFactory; @@ -178,7 +173,8 @@ import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDao; /** - * VirtualNetworkApplianceManagerImpl manages the different types of virtual network appliances available in the Cloud Stack. + * VirtualNetworkApplianceManagerImpl manages the different types of + * virtual network appliances available in the Cloud Stack. */ @Local(value = { VirtualNetworkApplianceManager.class, VirtualNetworkApplianceService.class }) public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplianceManager, VirtualNetworkApplianceService, VirtualMachineGuru { @@ -269,11 +265,11 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian NetworkDao _networkDao; @Inject LoadBalancingRulesManager _lbMgr; - @Inject + @Inject PortForwardingRulesDao _pfRulesDao; @Inject RemoteAccessVpnDao _vpnDao; - @Inject + @Inject VMInstanceDao _instanceDao; @Inject NicDao _nicDao; @@ -287,7 +283,6 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian int _routerStatsInterval = 300; private ServiceOfferingVO _offering; - private String trafficSentinelHostname; ScheduledExecutorService _executor; @@ -311,26 +306,26 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian } @Override - public boolean destroyRouter(final long routerId) throws ResourceUnavailableException, ConcurrentOperationException { + public boolean destroyRouter(final long routerId) throws ResourceUnavailableException, ConcurrentOperationException{ UserContext context = UserContext.current(); User user = _accountMgr.getActiveUser(context.getCallerUserId()); if (s_logger.isDebugEnabled()) { s_logger.debug("Attempting to destroy router " + routerId); } - + DomainRouterVO router = _routerDao.findById(routerId); if (router == null) { return true; } boolean result = _itMgr.expunge(router, user, _accountMgr.getAccount(router.getAccountId())); - + return result; } @Override @DB - public VirtualRouter upgradeRouter(UpgradeRouterCmd cmd) { + public VirtualRouter upgradeRouter(UpgradeRouterCmd cmd) throws InvalidParameterValueException, PermissionDeniedException { Long routerId = cmd.getId(); Long serviceOfferingId = cmd.getServiceOfferingId(); Account account = UserContext.current().getCaller(); @@ -353,19 +348,18 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian if (newServiceOffering == null) { throw new InvalidParameterValueException("Unable to find service offering with id " + serviceOfferingId); } - + // Check that the router is stopped if (!router.getState().equals(State.Stopped)) { s_logger.warn("Unable to upgrade router " + router.toString() + " in state " + router.getState()); - throw new InvalidParameterValueException("Unable to upgrade router " + router.toString() + " in state " + router.getState() - + "; make sure the router is stopped and not in an error state before upgrading."); + throw new InvalidParameterValueException("Unable to upgrade router " + router.toString() + " in state " + router.getState() + "; make sure the router is stopped and not in an error state before upgrading."); } ServiceOfferingVO currentServiceOffering = _serviceOfferingDao.findById(router.getServiceOfferingId()); if (currentServiceOffering.getUseLocalStorage() != newServiceOffering.getUseLocalStorage()) { - throw new InvalidParameterValueException("Can't upgrade, due to new local storage status : " + newServiceOffering.getUseLocalStorage() + " is different from " - + "curruent local storage status: " + currentServiceOffering.getUseLocalStorage()); + throw new InvalidParameterValueException("Can't upgrade, due to new local storage status : " + newServiceOffering.getUseLocalStorage() + + " is different from " + "curruent local storage status: " + currentServiceOffering.getUseLocalStorage()); } router.setServiceOfferingId(serviceOfferingId); @@ -377,63 +371,47 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian } - private String rot13(final String password) { - final StringBuffer newPassword = new StringBuffer(""); - - for (int i = 0; i < password.length(); i++) { - char c = password.charAt(i); - - if ((c >= 'a' && c <= 'm') || ((c >= 'A' && c <= 'M'))) { - c += 13; - } else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z')) { - c -= 13; - } - - newPassword.append(c); - } - - return newPassword.toString(); - } @Override - public boolean savePasswordToRouter(Network network, NicProfile nic, VirtualMachineProfile profile) throws ResourceUnavailableException { + public boolean savePasswordToRouter(Network network, NicProfile nic, VirtualMachineProfile profile) throws ResourceUnavailableException{ DomainRouterVO router = _routerDao.findByNetwork(network.getId()); if (router == null) { s_logger.warn("Unable save password, router doesn't exist in network " + network.getId()); throw new CloudRuntimeException("Unable to save password to router"); } - + UserVm userVm = profile.getVirtualMachine(); - String password = (String) profile.getParameter(Param.VmPassword); - String encodedPassword = rot13(password); - + String password = (String)profile.getParameter(Param.VmPassword); + String encodedPassword = PasswordGenerator.rot13(password); + Commands cmds = new Commands(OnError.Continue); - SavePasswordCommand cmd = new SavePasswordCommand(encodedPassword, nic.getIp4Address(), userVm.getHostName()); + SavePasswordCommand cmd = new SavePasswordCommand(encodedPassword, nic.getIp4Address(), userVm.getName()); cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, router.getPrivateIpAddress()); cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); cmds.addCommand("password", cmd); return sendCommandsToRouter(router, cmds); } - + + @Override public VirtualRouter stopRouter(long routerId, boolean forced) throws ResourceUnavailableException, ConcurrentOperationException { UserContext context = UserContext.current(); Account account = context.getCaller(); - + // verify parameters DomainRouterVO router = _routerDao.findById(routerId); if (router == null) { throw new InvalidParameterValueException("Unable to find router by id " + routerId + "."); } - + _accountMgr.checkAccess(account, router); - + UserVO user = _userDao.findById(UserContext.current().getCallerUserId()); - + return stop(router, forced, user, account); } - + @DB public void processStopOrRebootAnswer(final DomainRouterVO router, Answer answer) { final Transaction txn = Transaction.currentTxn(); @@ -490,19 +468,24 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian } /* - * final GetVmStatsCommand cmd = new GetVmStatsCommand(router, router.getInstanceName()); final Answer answer = - * _agentMgr.easySend(router.getHostId(), cmd); if (answer == null) { return false; } + * final GetVmStatsCommand cmd = new GetVmStatsCommand(router, + * router.getInstanceName()); + * final Answer answer = _agentMgr.easySend(router.getHostId(), cmd); + * if (answer == null) { + * return false; + * } * * final GetVmStatsAnswer stats = (GetVmStatsAnswer)answer; * - * netStats.putAll(stats.getNetworkStats()); diskStats.putAll(stats.getDiskStats()); + * netStats.putAll(stats.getNetworkStats()); + * diskStats.putAll(stats.getDiskStats()); */ return true; } @Override - public VirtualRouter rebootRouter(long routerId, boolean restartNetwork) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { + public VirtualRouter rebootRouter(long routerId, boolean restartNetwork) throws InvalidParameterValueException, PermissionDeniedException, ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { Account caller = UserContext.current().getCaller(); // verify parameters @@ -514,16 +497,16 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian if ((caller != null) && !_domainDao.isChildDomain(caller.getDomainId(), router.getDomainId())) { throw new PermissionDeniedException("Unable to reboot domain router with id " + routerId + ". Permission denied"); } - - // Can reboot domain router only in Running state + + //Can reboot domain router only in Running state if (router == null || router.getState() != State.Running) { s_logger.warn("Unable to reboot, virtual router is not in the right state " + router.getState()); throw new ResourceUnavailableException("Unable to reboot domR, it is not in right state " + router.getState(), DataCenter.class, router.getDataCenterId()); } - + UserVO user = _userDao.findById(UserContext.current().getCallerUserId()); s_logger.debug("Stopping and starting router " + router + " as a part of router reboot"); - + if (stop(router, false, user, caller) != null) { return startRouter(routerId, restartNetwork); } else { @@ -566,13 +549,11 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian _itMgr.registerGuru(VirtualMachine.Type.DomainRouter, this); boolean useLocalStorage = Boolean.parseBoolean(configs.get(Config.SystemVMUseLocalStorage.key())); - _offering = new ServiceOfferingVO("System Offering For Software Router", 1, _routerRamSize, _routerCpuMHz, null, null, true, null, useLocalStorage, true, null, true); + _offering = new ServiceOfferingVO("System Offering For Software Router", 1, _routerRamSize, _routerCpuMHz, 0, 0, true, null, useLocalStorage, true, null, true); _offering.setUniqueName("Cloud.Com-SoftwareRouter"); _offering = _serviceOfferingDao.persistSystemServiceOffering(_offering); _systemAcct = _accountService.getSystemAccount(); - - trafficSentinelHostname = configs.get("traffic.sentinel.hostname"); s_logger.info("DomainRouterManager is configured."); @@ -607,10 +588,10 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian return VirtualMachineName.getRouterId(vmName); } - private VmDataCommand generateVmDataCommand(DomainRouterVO router, String vmPrivateIpAddress, String userData, String serviceOffering, String zoneName, String guestIpAddress, String vmName, - String vmInstanceName, long vmId, String publicKey) { - VmDataCommand cmd = new VmDataCommand(vmPrivateIpAddress); - + private VmDataCommand generateVmDataCommand(DomainRouterVO router, String vmPrivateIpAddress, + String userData, String serviceOffering, String zoneName, String guestIpAddress, String vmName, String vmInstanceName, long vmId, String publicKey) { + VmDataCommand cmd = new VmDataCommand(vmPrivateIpAddress, vmName); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, router.getPrivateIpAddress()); cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); @@ -625,14 +606,6 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian cmd.addVmData("metadata", "vm-id", String.valueOf(vmId)); cmd.addVmData("metadata", "public-keys", publicKey); - String cloudIdentifier = _configDao.getValue("cloud.identifier"); - if (cloudIdentifier == null) { - cloudIdentifier = ""; - } else { - cloudIdentifier = "CloudStack-{" + cloudIdentifier + "}"; - } - cmd.addVmData("metadata", "cloud-identifier", cloudIdentifier); - return cmd; } @@ -643,74 +616,13 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian @Override public void run() { - //Direct Network Usage - URL trafficSentinel; - try { - //Query traffic Sentinel - if(trafficSentinelHostname != null){ - trafficSentinel = new URL(trafficSentinelHostname+"/inmsf/Query?script=var+q+%3D+Query.topN(%22historytrmx%22,%0D%0A+++++++++++++++++%22ipsource,bytes%22,%0D%0A+++++++++++++++++%22sourcezone+!%3D+EXTERNAL" + - "+%26+destinationzone+%3D+EXTERNAL%22,%0D%0A+++++++++++++++++%22end+-+5+minutes,+end%22,%0D%0A+++++++++++++++++%22bytes%22,%0D%0A+++++++++++++++++100000);%0D%0A%0D%0Avar+totalsSent+%3D+" + - "{};%0D%0A%0D%0Avar+t+%3D+q.run(%0D%0A++function(row,table)+{%0D%0A++++if(row[0])+{++++%0D%0A++++++totalsSent[row[0]]+%3D+row[1];%0D%0A++++}%0D%0A++});%0D%0A%0D%0Avar+totalsRcvd+%3D+{};" + - "%0D%0A%0D%0Avar+q+%3D+Query.topN(%22historytrmx%22,%0D%0A+++++++++++++++++%22ipdestination,bytes%22,%0D%0A+++++++++++++++++%22destinationzone+!%3D+EXTERNAL+%26+sourcezone+%3D+EXTERNAL%22," + - "%0D%0A+++++++++++++++++%22end+-+5minutes,+end%22,%0D%0A+++++++++++++++++%22bytes%22,%0D%0A+++++++++++++++++100000);%0D%0A%0D%0Avar+t+%3D+q.run(%0D%0A++function(row,table)+{%0D%0A++++" + - "if(row[0])+{%0D%0A++++++totalsRcvd[row[0]]+%3D+row[1];%0D%0A++++}%0D%0A++});%0D%0A%0D%0Afor+(var+addr+in+totalsSent)+{%0D%0A++++var+TS+%3D+0;%0D%0A++++var+TR+%3D+0;%0D%0A++++if(totalsSent[addr])" + - "+TS+%3D+totalsSent[addr];%0D%0A++++if(totalsRcvd[addr])+TR+%3D+totalsRcvd[addr];%0D%0A++++println(addr+%2B+%22,%22+%2B+TS+%2B+%22,%22+%2B+TR);%0D%0A}&authenticate=basic&resultFormat=txt"); - - BufferedReader in = new BufferedReader( - new InputStreamReader(trafficSentinel.openStream())); - - String inputLine; - - while ((inputLine = in.readLine()) != null){ - //Parse the script output - StringTokenizer st = new StringTokenizer(inputLine, ","); - if(st.countTokens() == 3){ - String publicIp = st.nextToken(); - //Find the account owning the IP - IPAddressVO ipaddress = _ipAddressDao.findByIpAddress(publicIp); - if(ipaddress == null || ipaddress.getAccountId() == Account.ACCOUNT_ID_SYSTEM){ - continue; - } - Long bytesSent = new Long(st.nextToken()); - Long bytesRcvd = new Long(st.nextToken()); - if(bytesSent == null || bytesRcvd == null){ - s_logger.debug("Incorrect bytes for IP: "+publicIp); - } - Transaction txn = Transaction.open(Transaction.CLOUD_DB); - txn.start(); - //update user_statistics - UserStatisticsVO stats = _statsDao.lock(ipaddress.getAccountId(), ipaddress.getDataCenterId(), null, 0L, "DirectNetwork"); - if (stats == null) { - stats = new UserStatisticsVO(ipaddress.getAccountId(), ipaddress.getDataCenterId(), null, 0L, "DirectNetwork", null); - stats.setCurrentBytesSent(bytesSent); - stats.setCurrentBytesReceived(bytesRcvd); - _statsDao.persist(stats); - } else { - stats.setCurrentBytesSent(stats.getCurrentBytesSent() + bytesSent); - stats.setCurrentBytesReceived(stats.getCurrentBytesReceived() + bytesRcvd); - _statsDao.update(stats.getId(), stats); - } - txn.commit(); - txn.close(); - } - } - - in.close(); - } - } catch (MalformedURLException e1) { - s_logger.info("Invalid T raffic Sentinel URL",e1); - } catch (IOException e) { - s_logger.debug("Error in direct network usage accounting",e); - } - - final List routers = _routerDao.listUpByHostId(null); s_logger.debug("Found " + routers.size() + " running routers. "); for (DomainRouterVO router : routers) { String privateIP = router.getPrivateIpAddress(); if (privateIP != null) { - final NetworkUsageCommand usageCmd = new NetworkUsageCommand(privateIP, router.getHostName()); + final NetworkUsageCommand usageCmd = new NetworkUsageCommand(privateIP, router.getName()); final NetworkUsageAnswer answer = (NetworkUsageAnswer) _agentMgr.easySend(router.getHostId(), usageCmd); if (answer != null) { Transaction txn = Transaction.open(Transaction.CLOUD_DB); @@ -727,16 +639,16 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian } if (stats.getCurrentBytesReceived() > answer.getBytesReceived()) { if (s_logger.isDebugEnabled()) { - s_logger.debug("Received # of bytes that's less than the last one. Assuming something went wrong and persisting it. Reported: " + answer.getBytesReceived() - + " Stored: " + stats.getCurrentBytesReceived()); + s_logger.debug("Received # of bytes that's less than the last one. Assuming something went wrong and persisting it. Reported: " + + answer.getBytesReceived() + " Stored: " + stats.getCurrentBytesReceived()); } stats.setNetBytesReceived(stats.getNetBytesReceived() + stats.getCurrentBytesReceived()); } stats.setCurrentBytesReceived(answer.getBytesReceived()); if (stats.getCurrentBytesSent() > answer.getBytesSent()) { if (s_logger.isDebugEnabled()) { - s_logger.debug("Received # of bytes that's less than the last one. Assuming something went wrong and persisting it. Reported: " + answer.getBytesSent() - + " Stored: " + stats.getCurrentBytesSent()); + s_logger.debug("Received # of bytes that's less than the last one. Assuming something went wrong and persisting it. Reported: " + + answer.getBytesSent() + " Stored: " + stats.getCurrentBytesSent()); } stats.setNetBytesSent(stats.getNetBytesSent() + stats.getCurrentBytesSent()); } @@ -745,7 +657,8 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian txn.commit(); } catch (Exception e) { txn.rollback(); - s_logger.warn("Unable to update user statistics for account: " + router.getAccountId() + " Rx: " + answer.getBytesReceived() + "; Tx: " + answer.getBytesSent()); + s_logger.warn("Unable to update user statistics for account: " + router.getAccountId() + " Rx: " + + answer.getBytesReceived() + "; Tx: " + answer.getBytesSent()); } finally { txn.close(); } @@ -760,7 +673,6 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian } @Override - @DB public DomainRouterVO deployVirtualRouter(Network guestNetwork, DeployDestination dest, Account owner, Map params) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException { long dcId = dest.getDataCenter().getId(); @@ -768,173 +680,143 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian if (s_logger.isDebugEnabled()) { s_logger.debug("Starting a router for network configurations: virtual=" + guestNetwork + " in " + dest); } + + assert guestNetwork.getState() == Network.State.Implemented || guestNetwork.getState() == Network.State.Setup || guestNetwork.getState() == Network.State.Implementing: "Network is not yet fully implemented: " + + guestNetwork; + assert guestNetwork.getTrafficType() == TrafficType.Guest; - // lock guest network - Long guestNetworkId = guestNetwork.getId(); - guestNetwork = _networkDao.acquireInLockTable(guestNetworkId); + DataCenterDeployment plan = new DataCenterDeployment(dcId); - if (guestNetwork == null) { - throw new ConcurrentOperationException("Unable to acquire network configuration: " + guestNetworkId); + DomainRouterVO router = _routerDao.findByNetwork(guestNetwork.getId()); + if (router == null) { + long id = _routerDao.getNextInSequence(Long.class, "id"); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Creating the router " + id); + } + + PublicIp sourceNatIp = _networkMgr.assignSourceNatIpAddress(owner, guestNetwork, _accountService.getSystemUser().getId()); + + List offerings = _networkMgr.getSystemAccountNetworkOfferings(NetworkOfferingVO.SystemControlNetwork); + NetworkOfferingVO controlOffering = offerings.get(0); + NetworkVO controlConfig = _networkMgr.setupNetwork(_systemAcct, controlOffering, plan, null, null, false, false).get(0); + + List> networks = new ArrayList>(3); + NetworkOfferingVO publicOffering = _networkMgr.getSystemAccountNetworkOfferings(NetworkOfferingVO.SystemPublicNetwork).get(0); + List publicNetworks = _networkMgr.setupNetwork(_systemAcct, publicOffering, plan, null, null, false, false); + NicProfile defaultNic = new NicProfile(); + defaultNic.setDefaultNic(true); + defaultNic.setIp4Address(sourceNatIp.getAddress().addr()); + defaultNic.setGateway(sourceNatIp.getGateway()); + defaultNic.setNetmask(sourceNatIp.getNetmask()); + defaultNic.setMacAddress(sourceNatIp.getMacAddress()); + defaultNic.setBroadcastType(BroadcastDomainType.Vlan); + defaultNic.setBroadcastUri(BroadcastDomainType.Vlan.toUri(sourceNatIp.getVlanTag())); + defaultNic.setIsolationUri(IsolationType.Vlan.toUri(sourceNatIp.getVlanTag())); + defaultNic.setDeviceId(2); + networks.add(new Pair(publicNetworks.get(0), defaultNic)); + NicProfile gatewayNic = new NicProfile(); + gatewayNic.setIp4Address(guestNetwork.getGateway()); + gatewayNic.setBroadcastUri(guestNetwork.getBroadcastUri()); + gatewayNic.setBroadcastType(guestNetwork.getBroadcastDomainType()); + gatewayNic.setIsolationUri(guestNetwork.getBroadcastUri()); + gatewayNic.setMode(guestNetwork.getMode()); + + String gatewayCidr = guestNetwork.getCidr(); + gatewayNic.setNetmask(NetUtils.getCidrNetmask(gatewayCidr)); + networks.add(new Pair((NetworkVO) guestNetwork, gatewayNic)); + networks.add(new Pair(controlConfig, null)); + + /*Before starting router, already know the hypervisor type*/ + VMTemplateVO template = _templateDao.findRoutingTemplate(dest.getCluster().getHypervisorType()); + router = new DomainRouterVO(id, _offering.getId(), VirtualMachineName.getRouterName(id, _instance), template.getId(), + template.getHypervisorType(), template.getGuestOSId(), owner.getDomainId(), owner.getId(), guestNetwork.getId(), _offering.getOfferHA()); + router = _itMgr.allocate(router, template, _offering, networks, plan, null, owner); } - try { - - assert guestNetwork.getState() == Network.State.Implemented || guestNetwork.getState() == Network.State.Setup || guestNetwork.getState() == Network.State.Implementing : "Network is not yet fully implemented: " - + guestNetwork; - assert guestNetwork.getTrafficType() == TrafficType.Guest; - - DataCenterDeployment plan = new DataCenterDeployment(dcId); - - DomainRouterVO router = _routerDao.findByNetwork(guestNetwork.getId()); - if (router == null) { - long id = _routerDao.getNextInSequence(Long.class, "id"); - if (s_logger.isDebugEnabled()) { - s_logger.debug("Creating the router " + id); - } - - PublicIp sourceNatIp = _networkMgr.assignSourceNatIpAddress(owner, guestNetwork, _accountService.getSystemUser().getId()); - - List offerings = _networkMgr.getSystemAccountNetworkOfferings(NetworkOfferingVO.SystemControlNetwork); - NetworkOfferingVO controlOffering = offerings.get(0); - NetworkVO controlConfig = _networkMgr.setupNetwork(_systemAcct, controlOffering, plan, null, null, false, false).get(0); - - List> networks = new ArrayList>(3); - NetworkOfferingVO publicOffering = _networkMgr.getSystemAccountNetworkOfferings(NetworkOfferingVO.SystemPublicNetwork).get(0); - List publicNetworks = _networkMgr.setupNetwork(_systemAcct, publicOffering, plan, null, null, false, false); - NicProfile defaultNic = new NicProfile(); - defaultNic.setDefaultNic(true); - defaultNic.setIp4Address(sourceNatIp.getAddress().addr()); - defaultNic.setGateway(sourceNatIp.getGateway()); - defaultNic.setNetmask(sourceNatIp.getNetmask()); - defaultNic.setMacAddress(sourceNatIp.getMacAddress()); - defaultNic.setBroadcastType(BroadcastDomainType.Vlan); - defaultNic.setBroadcastUri(BroadcastDomainType.Vlan.toUri(sourceNatIp.getVlanTag())); - defaultNic.setIsolationUri(IsolationType.Vlan.toUri(sourceNatIp.getVlanTag())); - defaultNic.setDeviceId(2); - networks.add(new Pair(publicNetworks.get(0), defaultNic)); - NicProfile gatewayNic = new NicProfile(); - gatewayNic.setIp4Address(guestNetwork.getGateway()); - gatewayNic.setBroadcastUri(guestNetwork.getBroadcastUri()); - gatewayNic.setBroadcastType(guestNetwork.getBroadcastDomainType()); - gatewayNic.setIsolationUri(guestNetwork.getBroadcastUri()); - gatewayNic.setMode(guestNetwork.getMode()); - - String gatewayCidr = guestNetwork.getCidr(); - gatewayNic.setNetmask(NetUtils.getCidrNetmask(gatewayCidr)); - networks.add(new Pair((NetworkVO) guestNetwork, gatewayNic)); - networks.add(new Pair(controlConfig, null)); - - /* Before starting router, already know the hypervisor type */ - VMTemplateVO template = _templateDao.findRoutingTemplate(dest.getCluster().getHypervisorType()); - router = new DomainRouterVO(id, _offering.getId(), VirtualMachineName.getRouterName(id, _instance), template.getId(), template.getHypervisorType(), template.getGuestOSId(), - owner.getDomainId(), owner.getId(), guestNetwork.getId(), _offering.getOfferHA()); - router = _itMgr.allocate(router, template, _offering, networks, plan, null, owner); - } - - State state = router.getState(); - if (state != State.Running) { - router = this.start(router, _accountService.getSystemUser(), _accountService.getSystemAccount(), params); - } - - // Creating stats entry for router - UserStatisticsVO stats = _userStatsDao.findBy(owner.getId(), dcId, null, router.getId(), router.getType().toString()); - if (stats == null) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Creating user statistics for the account: " + owner.getId() + " Router Id: " + router.getId()); - } - stats = new UserStatisticsVO(owner.getId(), dcId, null, router.getId(), router.getType().toString(), guestNetwork.getId()); - _userStatsDao.persist(stats); - } - return router; - } finally { - _networkDao.releaseFromLockTable(guestNetworkId); + State state = router.getState(); + if (state != State.Running) { + router = this.start(router, _accountService.getSystemUser(), _accountService.getSystemAccount(), params); } + + // Creating stats entry for router + UserStatisticsVO stats = _userStatsDao.findBy(owner.getId(), dcId, null, router.getId(), router.getType().toString()); + if (stats == null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Creating user statistics for the account: " + owner.getId() + " Router Id: "+router.getId()); + } + stats = new UserStatisticsVO(owner.getId(), dcId, null, router.getId(), router.getType().toString(), guestNetwork.getId()); + _userStatsDao.persist(stats); + } + return router; } @Override - @DB - public DomainRouterVO deployDhcp(Network guestNetwork, DeployDestination dest, Account owner, Map params) throws InsufficientCapacityException, StorageUnavailableException, - ConcurrentOperationException, ResourceUnavailableException { + public DomainRouterVO deployDhcp(Network guestNetwork, DeployDestination dest, Account owner, Map params) throws InsufficientCapacityException, + StorageUnavailableException, ConcurrentOperationException, ResourceUnavailableException { long dcId = dest.getDataCenter().getId(); - - // lock guest network - Long guestNetworkId = guestNetwork.getId(); - guestNetwork = _networkDao.acquireInLockTable(guestNetworkId); - - if (guestNetwork == null) { - throw new ConcurrentOperationException("Unable to acquire network configuration: " + guestNetworkId); + + NetworkOffering offering = _networkOfferingDao.findByIdIncludingRemoved(guestNetwork.getNetworkOfferingId()); + if (offering.isSystemOnly() || guestNetwork.getIsShared()) { + owner = _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM); } - try { - - NetworkOffering offering = _networkOfferingDao.findByIdIncludingRemoved(guestNetwork.getNetworkOfferingId()); - if (offering.isSystemOnly() || guestNetwork.getIsShared()) { - owner = _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM); - } + if (s_logger.isDebugEnabled()) { + s_logger.debug("Starting a dhcp for network configurations: dhcp=" + guestNetwork + " in " + dest); + } + assert guestNetwork.getState() == Network.State.Implemented || guestNetwork.getState() == Network.State.Setup || guestNetwork.getState() == Network.State.Implementing : "Network is not yet fully implemented: " + + guestNetwork; + DataCenterDeployment plan = new DataCenterDeployment(dcId); + DataCenter dc = _dcDao.findById(dcId); + DomainRouterVO router = null; + Long podId = dest.getPod().getId(); + + //In Basic zone and Guest network we have to start domR per pod, not per network + if ((dc.getNetworkType() == NetworkType.Basic || guestNetwork.isSecurityGroupEnabled()) && guestNetwork.getTrafficType() == TrafficType.Guest ) { + router = _routerDao.findByNetworkAndPod(guestNetwork.getId(), podId); + } else { + router = _routerDao.findByNetwork(guestNetwork.getId()); + } + + if (router == null) { + long id = _routerDao.getNextInSequence(Long.class, "id"); if (s_logger.isDebugEnabled()) { - s_logger.debug("Starting a dhcp for network configurations: dhcp=" + guestNetwork + " in " + dest); - } - assert guestNetwork.getState() == Network.State.Implemented || guestNetwork.getState() == Network.State.Setup || guestNetwork.getState() == Network.State.Implementing : "Network is not yet fully implemented: " - + guestNetwork; - - DataCenterDeployment plan = null; - DataCenter dc = _dcDao.findById(dcId); - DomainRouterVO router = null; - Long podId = dest.getPod().getId(); - - // In Basic zone and Guest network we have to start domR per pod, not per network - if ((dc.getNetworkType() == NetworkType.Basic || guestNetwork.isSecurityGroupEnabled()) && guestNetwork.getTrafficType() == TrafficType.Guest) { - router = _routerDao.findByNetworkAndPod(guestNetwork.getId(), podId); - plan = new DataCenterDeployment(dcId, podId, null, null, null); - } else { - router = _routerDao.findByNetwork(guestNetwork.getId()); - plan = new DataCenterDeployment(dcId); + s_logger.debug("Creating the router " + id); } - if (router == null) { - long id = _routerDao.getNextInSequence(Long.class, "id"); - if (s_logger.isDebugEnabled()) { - s_logger.debug("Creating the router " + id); - } + List offerings = _networkMgr.getSystemAccountNetworkOfferings(NetworkOfferingVO.SystemControlNetwork); + NetworkOfferingVO controlOffering = offerings.get(0); + NetworkVO controlConfig = _networkMgr.setupNetwork(_systemAcct, controlOffering, plan, null, null, false, false).get(0); - List offerings = _networkMgr.getSystemAccountNetworkOfferings(NetworkOfferingVO.SystemControlNetwork); - NetworkOfferingVO controlOffering = offerings.get(0); - NetworkVO controlConfig = _networkMgr.setupNetwork(_systemAcct, controlOffering, plan, null, null, false, false).get(0); + List> networks = new ArrayList>(3); + NicProfile gatewayNic = new NicProfile(); + gatewayNic.setDefaultNic(true); + networks.add(new Pair((NetworkVO) guestNetwork, gatewayNic)); + networks.add(new Pair(controlConfig, null)); - List> networks = new ArrayList>(3); - NicProfile gatewayNic = new NicProfile(); - gatewayNic.setDefaultNic(true); - networks.add(new Pair((NetworkVO) guestNetwork, gatewayNic)); - networks.add(new Pair(controlConfig, null)); - - /* Before starting router, already know the hypervisor type */ - VMTemplateVO template = _templateDao.findRoutingTemplate(dest.getCluster().getHypervisorType()); - - router = new DomainRouterVO(id, _offering.getId(), VirtualMachineName.getRouterName(id, _instance), template.getId(), template.getHypervisorType(), template.getGuestOSId(), - owner.getDomainId(), owner.getId(), guestNetwork.getId(), _offering.getOfferHA()); - router.setRole(Role.DHCP_USERDATA); - router = _itMgr.allocate(router, template, _offering, networks, plan, null, owner); - } - - State state = router.getState(); - if (state != State.Running) { - router = this.start(router, _accountService.getSystemUser(), _accountService.getSystemAccount(), params); - } - // Creating stats entry for router - UserStatisticsVO stats = _userStatsDao.findBy(owner.getId(), dcId, null, router.getId(), router.getType().toString()); - if (stats == null) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Creating user statistics for the account: " + owner.getId() + " Router Id: " + router.getId()); - } - stats = new UserStatisticsVO(owner.getId(), dcId, null, router.getId(), router.getType().toString(), guestNetwork.getId()); - _userStatsDao.persist(stats); - } - - return router; - } finally { - _networkDao.releaseFromLockTable(guestNetworkId); + /*Before starting router, already know the hypervisor type*/ + VMTemplateVO template = _templateDao.findRoutingTemplate(dest.getCluster().getHypervisorType()); + + router = new DomainRouterVO(id, _offering.getId(), VirtualMachineName.getRouterName(id, _instance), template.getId(), + template.getHypervisorType(), template.getGuestOSId(), owner.getDomainId(), owner.getId(), guestNetwork.getId(), _offering.getOfferHA()); + router.setRole(Role.DHCP_USERDATA); + router = _itMgr.allocate(router, template, _offering, networks, plan, null, owner); } + State state = router.getState(); + if (state != State.Running) { + router = this.start(router, _accountService.getSystemUser(), _accountService.getSystemAccount(), params); + } + // Creating stats entry for router + UserStatisticsVO stats = _userStatsDao.findBy(owner.getId(), dcId, null, router.getId(), router.getType().toString()); + if (stats == null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Creating user statistics for the account: " + owner.getId() + " Router Id: "+router.getId()); + } + stats = new UserStatisticsVO(owner.getId(), dcId, null, router.getId(), router.getType().toString(), guestNetwork.getId()); + _userStatsDao.persist(stats); + } + + return router; } @Override @@ -947,14 +829,14 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian String dhcpRange = null; DataCenter dc = dest.getDataCenter(); - + if (dc.getNetworkType() == NetworkType.Advanced) { String cidr = network.getCidr(); if (cidr != null) { dhcpRange = NetUtils.getDhcpRange(cidr); } - } - + } + if (router.getRole() == Role.DHCP_USERDATA) { type = "dhcpsrvr"; } else { @@ -973,50 +855,44 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian buf.append(" eth").append(deviceId).append("ip=").append(nic.getIp4Address()); buf.append(" eth").append(deviceId).append("mask=").append(nic.getNetmask()); if (nic.isDefaultNic()) { - buf.append(" gateway=").append(nic.getGateway()); + buf.append(" gateway=").append(nic.getGateway()); defaultDns1 = nic.getDns1(); defaultDns2 = nic.getDns2(); - + if (dc.getNetworkType() == NetworkType.Basic) { long cidrSize = NetUtils.getCidrSize(nic.getNetmask()); String cidr = NetUtils.getCidrSubNet(nic.getGateway(), cidrSize); if (cidr != null) { dhcpRange = NetUtils.getIpRangeStartIpFromCidr(cidr, cidrSize); } - } + } } if (nic.getTrafficType() == TrafficType.Management) { buf.append(" localgw=").append(dest.getPod().getGateway()); } else if (nic.getTrafficType() == TrafficType.Control) { - + // DOMR control command is sent over management server in VMware if (dest.getHost().getHypervisorType() == HypervisorType.VMware) { - if (s_logger.isInfoEnabled()) { + if(s_logger.isInfoEnabled()) { s_logger.info("Check if we need to add management server explicit route to DomR. pod cidr: " + dest.getPod().getCidrAddress() + "/" + dest.getPod().getCidrSize() - + ", pod gateway: " + dest.getPod().getGateway() + ", management host: " + _mgmt_host); + + ", pod gateway: " + dest.getPod().getGateway() + ", management host: " + _mgmt_host); } - - if (s_logger.isInfoEnabled()) { - s_logger.info("Add management server explicit route to DomR."); - } - - // always add management explicit route, for basic networking setup, DomR may have two interfaces while both - // are on the same subnet - _mgmt_cidr = _configDao.getValue(Config.ManagementNetwork.key()); - if (NetUtils.isValidCIDR(_mgmt_cidr)) { - buf.append(" mgmtcidr=").append(_mgmt_cidr); - buf.append(" localgw=").append(dest.getPod().getGateway()); - } - - /* - * if(!NetUtils.sameSubnetCIDR(_mgmt_host, dest.getPod().getGateway(), dest.getPod().getCidrSize())) { - * if(s_logger.isInfoEnabled()) { s_logger.info("Add management server explicit route to DomR."); } - * - * _mgmt_cidr = _configDao.getValue(Config.ManagementNetwork.key()); if (NetUtils.isValidCIDR(_mgmt_cidr)) { - * buf.append(" mgmtcidr=").append(_mgmt_cidr); buf.append(" localgw=").append(dest.getPod().getGateway()); - * } } else { if(s_logger.isInfoEnabled()) { - * s_logger.info("Management server host is at same subnet at pod private network"); } } - */ + + if(!NetUtils.sameSubnetCIDR(_mgmt_host, dest.getPod().getGateway(), dest.getPod().getCidrSize())) { + if(s_logger.isInfoEnabled()) { + s_logger.info("Add management server explicit route to DomR."); + } + + _mgmt_cidr = _configDao.getValue(Config.ManagementNetwork.key()); + if (NetUtils.isValidCIDR(_mgmt_cidr)) { + buf.append(" mgmtcidr=").append(_mgmt_cidr); + buf.append(" localgw=").append(dest.getPod().getGateway()); + } + } else { + if(s_logger.isInfoEnabled()) { + s_logger.info("Management server host is at same subnet at pod private network, don't add explict route to DomR"); + } + } } controlNic = nic; @@ -1029,11 +905,11 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian String domain = network.getNetworkDomain(); if (domain != null) { buf.append(" domain=" + domain); - } - + } + if (!network.isDefault() && network.getGuestType() == GuestIpType.Direct) { buf.append(" defaultroute=false"); - + String virtualNetworkElementNicIP = _networkMgr.getIpOfNetworkElementInVirtualNetwork(network.getAccountId(), network.getDataCenterId()); if (!network.getIsShared() && virtualNetworkElementNicIP != null) { defaultDns1 = virtualNetworkElementNicIP; @@ -1043,7 +919,7 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian } else { buf.append(" defaultroute=true"); } - + buf.append(" dns1=").append(defaultDns1); if (defaultDns2 != null) { buf.append(" dns2=").append(defaultDns2); @@ -1061,78 +937,79 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian } @Override - public boolean finalizeDeployment(Commands cmds, VirtualMachineProfile profile, DeployDestination dest, ReservationContext context) throws ResourceUnavailableException { + public boolean finalizeDeployment(Commands cmds, VirtualMachineProfile profile, DeployDestination dest, ReservationContext context) throws ResourceUnavailableException{ DomainRouterVO router = profile.getVirtualMachine(); List nics = profile.getNics(); for (NicProfile nic : nics) { - if (nic.getTrafficType() == TrafficType.Public) { - router.setPublicIpAddress(nic.getIp4Address()); - router.setPublicNetmask(nic.getNetmask()); - router.setPublicMacAddress(nic.getMacAddress()); - } else if (nic.getTrafficType() == TrafficType.Guest) { - router.setGuestIpAddress(nic.getIp4Address()); - } else if (nic.getTrafficType() == TrafficType.Control) { - router.setPrivateIpAddress(nic.getIp4Address()); - router.setPrivateMacAddress(nic.getMacAddress()); - } + if (nic.getTrafficType() == TrafficType.Public) { + router.setPublicIpAddress(nic.getIp4Address()); + router.setPublicNetmask(nic.getNetmask()); + router.setPublicMacAddress(nic.getMacAddress()); + } else if (nic.getTrafficType() == TrafficType.Guest) { + router.setGuestIpAddress(nic.getIp4Address()); + } else if (nic.getTrafficType() == TrafficType.Control) { + router.setPrivateIpAddress(nic.getIp4Address()); + router.setPrivateMacAddress(nic.getMacAddress()); + } } _routerDao.update(router.getId(), router); - + finalizeCommandsOnStart(cmds, profile); return true; } - + @Override public boolean finalizeCommandsOnStart(Commands cmds, VirtualMachineProfile profile) { - DomainRouterVO router = profile.getVirtualMachine(); + DomainRouterVO router = profile.getVirtualMachine(); + + NicProfile controlNic = null; + for (NicProfile nic : profile.getNics()) { + if (nic.getTrafficType() == TrafficType.Control && nic.getIp4Address() != null) { + controlNic = nic; + } + } - NicProfile controlNic = null; - for (NicProfile nic : profile.getNics()) { - if (nic.getTrafficType() == TrafficType.Control && nic.getIp4Address() != null) { - controlNic = nic; - } - } - - if (controlNic == null) { - s_logger.error("Control network doesn't exist for the router " + router); - return false; - } - - cmds.addCommand("checkSsh", new CheckSshCommand(profile.getInstanceName(), controlNic.getIp4Address(), 3922, 5, 20)); - - // restart network if restartNetwork = false is not specified in profile parameters + if (controlNic == null) { + s_logger.error("Control network doesn't exist for the router " + router); + return false; + } + + cmds.addCommand("checkSsh", new CheckSshCommand(profile.getInstanceName(), controlNic.getIp4Address(), 3922, 5, 20)); + + //restart network if restartNetwork = false is not specified in profile parameters boolean restartNetwork = true; - if (profile.getParameter(Param.RestartNetwork) != null && (Boolean) profile.getParameter(Param.RestartNetwork) == false) { + if (profile.getParameter(Param.RestartNetwork) != null && (Boolean)profile.getParameter(Param.RestartNetwork) == false) { restartNetwork = false; } - // The commands should be sent for domR only, skip for DHCP + //The commands should be sent for domR only, skip for DHCP if (router.getRole() == VirtualRouter.Role.DHCP_FIREWALL_LB_PASSWD_USERDATA && restartNetwork) { s_logger.debug("Resending ipAssoc, port forwarding, load balancing rules as a part of Virtual router start"); long networkId = router.getNetworkId(); long ownerId = router.getAccountId(); long zoneId = router.getDataCenterId(); - + + final List userIps = _networkMgr.listPublicIpAddressesInVirtualNetwork(ownerId, zoneId, null, null); List publicIps = new ArrayList(); if (userIps != null && !userIps.isEmpty()) { for (IPAddressVO userIp : userIps) { - PublicIp publicIp = new PublicIp(userIp, _vlanDao.findById(userIp.getVlanId()), NetUtils.createSequenceBasedMacAddress(userIp.getMacAddress())); + PublicIp publicIp = new PublicIp(userIp, _vlanDao.findById(userIp.getVlanId()), userIp.getMacAddress()); publicIps.add(publicIp); } } - + s_logger.debug("Found " + publicIps.size() + " ip(s) to apply as a part of domR " + router + " start."); - - if (!publicIps.isEmpty()) { - - // Re-apply public ip addresses - should come before PF/LB/VPN - createAssociateIPCommands(router, publicIps, cmds, 0); - - List vpns = new ArrayList(); + + if (!publicIps.isEmpty()) { + + //Re-apply public ip addresses - should come before PF/LB/VPN + createAssociateIPCommands(router, publicIps, cmds, 0); + + List vpns = new ArrayList(); List pfRules = new ArrayList(); List staticNatFirewallRules = new ArrayList(); - + for (PublicIpAddress ip : publicIps) { pfRules.addAll(_pfRulesDao.listForApplication(ip.getId())); staticNatFirewallRules.addAll(_rulesDao.listByIpAndPurpose(ip.getId(), Purpose.StaticNat)); @@ -1142,32 +1019,32 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian vpns.add(vpn); } } - - // Re-apply port forwarding rules + + //Re-apply port forwarding rules s_logger.debug("Found " + pfRules.size() + " port forwarding rule(s) to apply as a part of domR " + router + " start."); if (!pfRules.isEmpty()) { createApplyPortForwardingRulesCommands(pfRules, router, cmds); - } - - // Re-apply static nat rules + } + + //Re-apply static nat rules s_logger.debug("Found " + staticNatFirewallRules.size() + " static nat rule(s) to apply as a part of domR " + router + " start."); if (!staticNatFirewallRules.isEmpty()) { List staticNatRules = new ArrayList(); for (FirewallRule rule : staticNatFirewallRules) { staticNatRules.add(_rulesMgr.buildStaticNatRule(rule)); - } + } createApplyStaticNatRulesCommands(staticNatRules, router, cmds); } - - // Re-apply vpn rules + + //Re-apply vpn rules s_logger.debug("Found " + vpns.size() + " vpn(s) to apply as a part of domR " + router + " start."); if (!vpns.isEmpty()) { for (RemoteAccessVpn vpn : vpns) { createApplyVpnCommands(vpn, router, cmds); } - } - - // Re-apply load balancing rules + } + + //Re-apply load balancing rules List lbs = _loadBalancerDao.listByNetworkId(networkId); List lbRules = new ArrayList(); for (LoadBalancerVO lb : lbs) { @@ -1175,24 +1052,24 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian LoadBalancingRule loadBalancing = new LoadBalancingRule(lb, dstList); lbRules.add(loadBalancing); } - + s_logger.debug("Found " + lbRules.size() + " load balancing rule(s) to apply as a part of domR " + router + " start."); if (!lbRules.isEmpty()) { createApplyLoadBalancingRulesCommands(lbRules, router, cmds); - } - } + } + } } - - // Resend dhcp + + //Resend dhcp s_logger.debug("Reapplying dhcp entries as a part of domR " + router + " start..."); createDhcpEntriesCommands(router, cmds); - - // Resend user data + + //Resend user data s_logger.debug("Reapplying vm data (userData and metaData) entries as a part of domR " + router + " start..."); createVmDataCommands(router, cmds); // Network usage command to create iptables rules - cmds.addCommand("networkUsage", new NetworkUsageCommand(controlNic.getIp4Address(), router.getHostName(), "create")); - + cmds.addCommand("networkUsage", new NetworkUsageCommand(controlNic.getIp4Address(), router.getName(), "create")); + return true; } @@ -1203,7 +1080,7 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian s_logger.warn("Unable to ssh to the VM: " + answer.getDetails()); return false; } - + return true; } @@ -1215,26 +1092,26 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian processStopOrRebootAnswer(domR, answer); } } - + @Override public void finalizeExpunge(DomainRouterVO vm) { } @Override public boolean startRemoteAccessVpn(Network network, RemoteAccessVpn vpn) throws ResourceUnavailableException { - + DomainRouterVO router = _routerDao.findByNetwork(network.getId()); if (router == null) { s_logger.warn("Failed to start remote access VPN: no router found for account and zone"); - throw new ResourceUnavailableException("Failed to start remote access VPN: no router found for account and zone", DataCenter.class, network.getDataCenterId()); + throw new ResourceUnavailableException("Unable to apply lb rules", DataCenter.class, network.getDataCenterId()); } if (router.getState() != State.Running) { - s_logger.warn("Failed to start remote access VPN: router not in right state " + router.getState()); - throw new ResourceUnavailableException("Failed to start remote access VPN: router not in right state " + router.getState(), DataCenter.class, network.getDataCenterId()); + s_logger.warn("Failed to start remote access VPN: router not in running state"); + throw new ResourceUnavailableException("Unable to assign ip addresses, domR is not in right state " + router.getState(), DataCenter.class, network.getDataCenterId()); } - + Commands cmds = new Commands(OnError.Stop); - + createApplyVpnCommands(vpn, router, cmds); try { @@ -1245,42 +1122,43 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian } Answer answer = cmds.getAnswer("users"); if (!answer.getResult()) { - s_logger.error("Unable to start vpn: unable add users to vpn in zone " + router.getDataCenterId() + " for account " + vpn.getAccountId() + " on domR: " + router.getInstanceName() - + " due to " + answer.getDetails()); - throw new ResourceUnavailableException("Unable to start vpn: Unable to add users to vpn in zone " + router.getDataCenterId() + " for account " + vpn.getAccountId() + " on domR: " - + router.getInstanceName() + " due to " + answer.getDetails(), DataCenter.class, router.getDataCenterId()); + s_logger.error("Unable to start vpn: unable add users to vpn in zone " + router.getDataCenterId() + " for account " + vpn.getAccountId() + + " on domR: " + router.getInstanceName() + " due to " + answer.getDetails()); + throw new ResourceUnavailableException("Unable to start vpn: Unable to add users to vpn in zone " + router.getDataCenterId() + " for account " + + vpn.getAccountId() + " on domR: " + router.getInstanceName() + " due to " + answer.getDetails(), DataCenter.class, + router.getDataCenterId()); } answer = cmds.getAnswer("startVpn"); if (!answer.getResult()) { - s_logger.error("Unable to start vpn in zone " + router.getDataCenterId() + " for account " + vpn.getAccountId() + " on domR: " + router.getInstanceName() + " due to " - + answer.getDetails()); - throw new ResourceUnavailableException("Unable to start vpn in zone " + router.getDataCenterId() + " for account " + vpn.getAccountId() + " on domR: " + router.getInstanceName() - + " due to " + answer.getDetails(), DataCenter.class, router.getDataCenterId()); + s_logger.error("Unable to start vpn in zone " + router.getDataCenterId() + " for account " + vpn.getAccountId() + " on domR: " + + router.getInstanceName() + " due to " + answer.getDetails()); + throw new ResourceUnavailableException("Unable to start vpn in zone " + router.getDataCenterId() + " for account " + vpn.getAccountId() + + " on domR: " + router.getInstanceName() + " due to " + answer.getDetails(), DataCenter.class, router.getDataCenterId()); } return true; } @Override - public boolean deleteRemoteAccessVpn(Network network, RemoteAccessVpn vpn) throws ResourceUnavailableException { - + public boolean deleteRemoteAccessVpn(Network network, RemoteAccessVpn vpn) throws ResourceUnavailableException{ + DomainRouterVO router = getRouter(vpn.getAccountId(), network.getDataCenterId()); if (router == null) { s_logger.warn("Failed to delete remote access VPN: no router found for account and zone"); - throw new ResourceUnavailableException("Failed to delete remote access VPN", DataCenter.class, network.getDataCenterId()); + throw new ResourceUnavailableException("Unable to apply lb rules", DataCenter.class, network.getDataCenterId()); } if (router.getState() != State.Running) { - s_logger.warn("Failed to delete remote access VPN: domR is not in right state " + router.getState()); + s_logger.warn("Failed to delete remote access VPN: router not in running state"); throw new ResourceUnavailableException("Failed to delete remote access VPN: domR is not in right state " + router.getState(), DataCenter.class, network.getDataCenterId()); } - Commands cmds = new Commands(OnError.Continue); - IpAddress ip = _networkMgr.getIp(vpn.getServerAddressId()); - - RemoteAccessVpnCfgCommand removeVpnCmd = new RemoteAccessVpnCfgCommand(false, ip.getAddress().addr(), vpn.getLocalIp(), vpn.getIpRange(), vpn.getIpsecPresharedKey()); - removeVpnCmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, router.getPrivateIpAddress()); - removeVpnCmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); - cmds.addCommand(removeVpnCmd); - - return sendCommandsToRouter(router, cmds); + Commands cmds = new Commands(OnError.Continue); + IpAddress ip = _networkMgr.getIp(vpn.getServerAddressId()); + + RemoteAccessVpnCfgCommand removeVpnCmd = new RemoteAccessVpnCfgCommand(false, ip.getAddress().addr(), vpn.getLocalIp(), vpn.getIpRange(), vpn.getIpsecPresharedKey()); + removeVpnCmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, router.getPrivateIpAddress()); + removeVpnCmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); + cmds.addCommand(removeVpnCmd); + + return sendCommandsToRouter(router, cmds); } private DomainRouterVO start(DomainRouterVO router, User user, Account caller, Map params) throws StorageUnavailableException, InsufficientCapacityException, @@ -1292,30 +1170,31 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian return null; } } - + @Override public DomainRouterVO stop(VirtualRouter router, boolean forced, User user, Account caller) throws ConcurrentOperationException, ResourceUnavailableException { s_logger.debug("Stopping router " + router); try { - if (_itMgr.advanceStop((DomainRouterVO) router, forced, user, caller)) { + if (_itMgr.advanceStop((DomainRouterVO)router, forced, user, caller)) { return _routerDao.findById(router.getId()); - } else { + } else { return null; } } catch (OperationTimedoutException e) { throw new CloudRuntimeException("Unable to stop " + router, e); } } + @Override - public VirtualRouter addVirtualMachineIntoNetwork(Network network, NicProfile nic, VirtualMachineProfile profile, DeployDestination dest, ReservationContext context, Boolean startDhcp) - throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException { - + public VirtualRouter addVirtualMachineIntoNetwork(Network network, NicProfile nic, VirtualMachineProfile profile, DeployDestination dest, + ReservationContext context, Boolean startDhcp) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException { + DomainRouterVO router = startDhcp ? deployDhcp(network, dest, profile.getOwner(), profile.getParameters()) : deployVirtualRouter(network, dest, profile.getOwner(), profile.getParameters()); _userVmDao.loadDetails((UserVmVO) profile.getVirtualMachine()); - - String password = (String) profile.getParameter(VirtualMachineProfile.Param.VmPassword); + + String password = (String)profile.getParameter(VirtualMachineProfile.Param.VmPassword); String userData = profile.getVirtualMachine().getUserData(); String sshPublicKey = profile.getVirtualMachine().getDetail("SSH.PublicKey"); Commands cmds = new Commands(OnError.Stop); @@ -1328,16 +1207,17 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian routerControlIpAddress = n.getIp4Address(); } } - - DhcpEntryCommand dhcpCommand = new DhcpEntryCommand(nic.getMacAddress(), nic.getIp4Address(), profile.getVirtualMachine().getHostName()); + + DhcpEntryCommand dhcpCommand = new DhcpEntryCommand(nic.getMacAddress(), nic.getIp4Address(), profile.getVirtualMachine() + .getName()); dhcpCommand.setAccessDetail(NetworkElementCommand.ROUTER_IP, routerControlIpAddress); dhcpCommand.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); cmds.addCommand("dhcp", dhcpCommand); - // password should be set only on default network element + //password should be set only on default network element if (password != null && network.isDefault()) { - final String encodedPassword = rot13(password); - SavePasswordCommand cmd = new SavePasswordCommand(encodedPassword, nic.getIp4Address(), profile.getVirtualMachine().getHostName()); + final String encodedPassword = PasswordGenerator.rot13(password); + SavePasswordCommand cmd = new SavePasswordCommand(encodedPassword, nic.getIp4Address(), profile.getVirtualMachine().getName()); cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, router.getPrivateIpAddress()); cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); cmds.addCommand("password", cmd); @@ -1345,11 +1225,11 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian String serviceOffering = _serviceOfferingDao.findByIdIncludingRemoved(profile.getServiceOfferingId()).getDisplayText(); String zoneName = _dcDao.findById(network.getDataCenterId()).getName(); - + cmds.addCommand( "vmdata", - generateVmDataCommand(router, nic.getIp4Address(), userData, serviceOffering, zoneName, nic.getIp4Address(), profile.getVirtualMachine().getHostName(), profile.getVirtualMachine() - .getInstanceName(), profile.getId(), sshPublicKey)); + generateVmDataCommand(router, nic.getIp4Address(), userData, serviceOffering, zoneName, + nic.getIp4Address(), profile.getVirtualMachine().getName(), profile.getVirtualMachine().getInstanceName(), profile.getId(), sshPublicKey)); try { _agentMgr.send(router.getHostId(), cmds); @@ -1359,8 +1239,9 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian Answer answer = cmds.getAnswer("dhcp"); if (!answer.getResult()) { - s_logger.error("Unable to set dhcp entry for " + profile + " on domR: " + router.getHostName() + " due to " + answer.getDetails()); - throw new ResourceUnavailableException("Unable to set dhcp entry for " + profile + " due to " + answer.getDetails(), DataCenter.class, router.getDataCenterId()); + s_logger.error("Unable to set dhcp entry for " + profile + " on domR: " + router.getName() + " due to " + answer.getDetails()); + throw new ResourceUnavailableException("Unable to set dhcp entry for " + profile + " due to " + answer.getDetails(), DataCenter.class, + router.getDataCenterId()); } answer = cmds.getAnswer("password"); @@ -1380,10 +1261,10 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian @Override public DomainRouterVO persist(DomainRouterVO router) { return _routerDao.persist(router); - } - + } + @Override - public String[] applyVpnUsers(Network network, List users) throws ResourceUnavailableException { + public String[] applyVpnUsers(Network network, List users) throws ResourceUnavailableException{ DomainRouterVO router = _routerDao.findByNetwork(network.getId()); if (router == null) { s_logger.warn("Failed to add/remove VPN users: no router found for account and zone"); @@ -1393,27 +1274,25 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian s_logger.warn("Failed to add/remove VPN users: router not in running state"); throw new ResourceUnavailableException("Unable to assign ip addresses, domR is not in right state " + router.getState(), DataCenter.class, network.getDataCenterId()); } - + Commands cmds = new Commands(OnError.Continue); List addUsers = new ArrayList(); List removeUsers = new ArrayList(); - for (VpnUser user : users) { + for (VpnUser user: users) { if (user.getState() == VpnUser.State.Add || user.getState() == VpnUser.State.Active) { addUsers.add(user); } else if (user.getState() == VpnUser.State.Revoke) { removeUsers.add(user); } } - + VpnUsersCfgCommand cmd = new VpnUsersCfgCommand(addUsers, removeUsers); cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, router.getPrivateIpAddress()); cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); cmds.addCommand(cmd); - - // Currently we receive just one answer from the agent. In the future we have to parse individual answers and set - // results accordingly - boolean agentResult = sendCommandsToRouter(router, cmds); - ; + + //Currently we receive just one answer from the agent. In the future we have to parse individual answers and set results accordingly + boolean agentResult = sendCommandsToRouter(router, cmds);; String[] result = new String[users.size()]; for (int i = 0; i < result.length; i++) { if (agentResult) { @@ -1422,7 +1301,7 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian result[i] = String.valueOf(agentResult); } } - + return result; } @@ -1451,18 +1330,18 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian throw new InvalidParameterValueException("Unable to find router by id " + routerId + "."); } _accountMgr.checkAccess(account, router); - + Account owner = _accountMgr.getAccount(router.getAccountId()); - - // Check if all networks are implemented for the domR; if not - implement them + + //Check if all networks are implemented for the domR; if not - implement them DataCenter dc = _dcDao.findById(router.getDataCenterId()); HostPodVO pod = _podDao.findById(router.getPodId()); DeployDestination dest = new DeployDestination(dc, pod, null, null); - + ReservationContext context = new ReservationContextImpl(null, null, caller, owner); - + List nics = _nicDao.listByVmId(routerId); - + for (NicVO nic : nics) { if (!_networkMgr.startNetwork(nic.getNetworkId(), dest, context)) { s_logger.warn("Failed to start network id=" + nic.getNetworkId() + " as a part of domR start"); @@ -1480,11 +1359,11 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian return this.start(router, user, account, params); } - private void createAssociateIPCommands(final DomainRouterVO router, final List ips, Commands cmds, long vmId) { - - // Ensure that in multiple vlans case we first send all ip addresses of vlan1, then all ip addresses of vlan2, etc.. + private void createAssociateIPCommands(final DomainRouterVO router, final List ips, Commands cmds, long vmId) { + + //Ensure that in multiple vlans case we first send all ip addresses of vlan1, then all ip addresses of vlan2, etc.. Map> vlanIpMap = new HashMap>(); - for (final PublicIpAddress ipAddress : ips) { + for (final PublicIpAddress ipAddress: ips) { String vlanTag = ipAddress.getVlanTag(); ArrayList ipList = vlanIpMap.get(vlanTag); if (ipList == null) { @@ -1493,47 +1372,48 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian ipList.add(ipAddress); vlanIpMap.put(vlanTag, ipList); } - - for (Map.Entry> vlanAndIp : vlanIpMap.entrySet()) { + + for (Map.Entry> vlanAndIp: vlanIpMap.entrySet()) { List ipAddrList = vlanAndIp.getValue(); - // Source nat ip address should always be sent first + //Source nat ip address should always be sent first Collections.sort(ipAddrList, new Comparator() { @Override public int compare(PublicIpAddress o1, PublicIpAddress o2) { boolean s1 = o1.isSourceNat(); boolean s2 = o2.isSourceNat(); return (s1 ^ s2) ? ((s1 ^ true) ? 1 : -1) : 0; - } - }); - - IpAddressTO[] ipsToSend = new IpAddressTO[ipAddrList.size()]; - int i = 0; - boolean firstIP = true; - for (final PublicIpAddress ipAddr : ipAddrList) { - - boolean add = (ipAddr.getState() == IpAddress.State.Releasing ? false : true); - boolean sourceNat = ipAddr.isSourceNat(); - String vlanId = ipAddr.getVlanTag(); - String vlanGateway = ipAddr.getGateway(); - String vlanNetmask = ipAddr.getNetmask(); - String vifMacAddress = ipAddr.getMacAddress(); - - String vmGuestAddress = null; - - // Get network rate - required for IpAssoc - Integer networkRate = _networkMgr.getNetworkRate(ipAddr.getNetworkId(), null); - - IpAddressTO ip = new IpAddressTO(ipAddr.getAddress().addr(), add, firstIP, sourceNat, vlanId, vlanGateway, vlanNetmask, vifMacAddress, vmGuestAddress, networkRate); - ipsToSend[i++] = ip; - firstIP = false; - } - IPAssocCommand cmd = new IPAssocCommand(ipsToSend); - cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, router.getPrivateIpAddress()); - cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); - cmds.addCommand("IPAssocCommand", cmd); + } }); + + IpAddressTO[] ipsToSend = new IpAddressTO[ipAddrList.size()]; + int i = 0; + boolean firstIP = true; + for (final PublicIpAddress ipAddr: ipAddrList) { + + boolean add = (ipAddr.getState() == IpAddress.State.Releasing ? false : true); + boolean sourceNat = ipAddr.isSourceNat(); + String vlanId = ipAddr.getVlanTag(); + String vlanGateway = ipAddr.getGateway(); + String vlanNetmask = ipAddr.getNetmask(); + String vifMacAddress = ipAddr.getMacAddress(); + + String vmGuestAddress = null; + + //Get network rate - required for IpAssoc + Network network = _networkMgr.getNetwork(ipAddr.getNetworkId()); + NetworkOffering no = _configMgr.getNetworkOffering(network.getNetworkOfferingId()); + Integer networkRate = _configMgr.getNetworkRate(no.getId()); + + IpAddressTO ip = new IpAddressTO(ipAddr.getAddress().addr(), add, firstIP, sourceNat, vlanId, vlanGateway, vlanNetmask, vifMacAddress, vmGuestAddress, networkRate); + ipsToSend[i++] = ip; + firstIP = false; + } + IPAssocCommand cmd = new IPAssocCommand(ipsToSend); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, router.getPrivateIpAddress()); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); + cmds.addCommand("IPAssocCommand", cmd); } } - + private void createApplyPortForwardingRulesCommands(List rules, DomainRouterVO router, Commands cmds) { List rulesTO = null; if (rules != null) { @@ -1544,13 +1424,13 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian rulesTO.add(ruleTO); } } - + SetPortForwardingRulesCommand cmd = new SetPortForwardingRulesCommand(rulesTO); cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, router.getPrivateIpAddress()); cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); cmds.addCommand(cmd); } - + private void createApplyStaticNatRulesCommands(List rules, DomainRouterVO router, Commands cmds) { List rulesTO = null; if (rules != null) { @@ -1561,23 +1441,23 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian rulesTO.add(ruleTO); } } - + SetStaticNatRulesCommand cmd = new SetStaticNatRulesCommand(rulesTO); cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, router.getPrivateIpAddress()); cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); cmds.addCommand(cmd); } - + private void createApplyLoadBalancingRulesCommands(List rules, DomainRouterVO router, Commands cmds) { - + LoadBalancerTO[] lbs = new LoadBalancerTO[rules.size()]; int i = 0; for (LoadBalancingRule rule : rules) { boolean revoked = (rule.getState().equals(FirewallRule.State.Revoke)); String protocol = rule.getProtocol(); String algorithm = rule.getAlgorithm(); - - String srcIp = _networkMgr.getIp(rule.getSourceIpAddressId()).getAddress().addr(); + + String srcIp = _networkMgr.getIp(rule.getSourceIpAddressId()).getAddress().addr(); int srcPort = rule.getSourcePortStart(); List destinations = rule.getDestinations(); LoadBalancerTO lb = new LoadBalancerTO(srcIp, srcPort, protocol, algorithm, revoked, false, destinations); @@ -1588,35 +1468,37 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, router.getPrivateIpAddress()); cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); cmds.addCommand(cmd); - + } - + private void createApplyVpnCommands(RemoteAccessVpn vpn, DomainRouterVO router, Commands cmds) { List vpnUsers = _vpnUsersDao.listByAccount(vpn.getAccountId()); List addUsers = new ArrayList(); List removeUsers = new ArrayList(); - for (VpnUser user : vpnUsers) { + for (VpnUser user: vpnUsers) { if (user.getState() == VpnUser.State.Add) { addUsers.add(user); } else if (user.getState() == VpnUser.State.Revoke) { removeUsers.add(user); } } - + VpnUsersCfgCommand addUsersCmd = new VpnUsersCfgCommand(addUsers, removeUsers); addUsersCmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, router.getPrivateIpAddress()); addUsersCmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); - + IpAddress ip = _networkMgr.getIp(vpn.getServerAddressId()); - - RemoteAccessVpnCfgCommand startVpnCmd = new RemoteAccessVpnCfgCommand(true, ip.getAddress().addr(), vpn.getLocalIp(), vpn.getIpRange(), vpn.getIpsecPresharedKey()); + + RemoteAccessVpnCfgCommand startVpnCmd = new RemoteAccessVpnCfgCommand(true, ip.getAddress().addr(), + vpn.getLocalIp(), vpn.getIpRange(), vpn.getIpsecPresharedKey()); startVpnCmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, router.getPrivateIpAddress()); startVpnCmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); - + cmds.addCommand("users", addUsersCmd); cmds.addCommand("startVpn", startVpnCmd); } - + + private void createVmDataCommands(DomainRouterVO router, Commands cmds) { long networkId = router.getNetworkId(); List vms = _userVmDao.listByNetworkId(networkId); @@ -1627,33 +1509,34 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian s_logger.debug("Creating user data entry for vm " + vm + " on domR " + router); String serviceOffering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getServiceOfferingId()).getDisplayText(); String zoneName = _dcDao.findById(router.getDataCenterId()).getName(); - cmds.addCommand("vmdata", - generateVmDataCommand(router, nic.getIp4Address(), vm.getUserData(), serviceOffering, zoneName, nic.getIp4Address(), vm.getHostName(), vm.getInstanceName(), vm.getId(), null)); + cmds.addCommand( + "vmdata", + generateVmDataCommand(router, nic.getIp4Address(), vm.getUserData(), serviceOffering, zoneName, + nic.getIp4Address(), vm.getName(), vm.getInstanceName(), vm.getId(), null)); } - } + } } } - + private void createDhcpEntriesCommands(DomainRouterVO router, Commands cmds) { long networkId = router.getNetworkId(); List vms = _userVmDao.listByNetworkId(networkId); - if (!vms.isEmpty()) { + if (vms != null && !vms.isEmpty()) { for (UserVmVO vm : vms) { - if (vm.getState() != State.Destroyed && vm.getState() != State.Expunging) { - NicVO nic = _nicDao.findByInstanceIdAndNetworkId(networkId, vm.getId()); - if (nic != null) { - s_logger.debug("Creating dhcp entry for vm " + vm + " on domR " + router + "."); - - DhcpEntryCommand dhcpCommand = new DhcpEntryCommand(nic.getMacAddress(), nic.getIp4Address(), vm.getHostName()); - dhcpCommand.setAccessDetail(NetworkElementCommand.ROUTER_IP, router.getPrivateIpAddress()); - dhcpCommand.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); - cmds.addCommand("dhcp", dhcpCommand); - } + NicVO nic = _nicDao.findByInstanceIdAndNetworkId(networkId, vm.getId()); + if (nic != null) { + s_logger.debug("Creating dhcp entry for vm " + vm + " on domR " + router + "."); + + DhcpEntryCommand dhcpCommand = new DhcpEntryCommand(nic.getMacAddress(), nic.getIp4Address(), vm.getName()); + dhcpCommand.setAccessDetail(NetworkElementCommand.ROUTER_IP, router.getPrivateIpAddress()); + dhcpCommand.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); + cmds.addCommand("dhcp", dhcpCommand); } } } } - + + private boolean sendCommandsToRouter(final DomainRouterVO router, Commands cmds) throws AgentUnavailableException { Answer[] answers = null; try { @@ -1689,9 +1572,9 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian if (router.getState() == State.Running) { Commands cmds = new Commands(OnError.Continue); - // Have to resend all already associated ip addresses + //Have to resend all already associated ip addresses createAssociateIPCommands(router, ipAddress, cmds, 0); - + return sendCommandsToRouter(router, cmds); } else if (router.getState() == State.Stopped) { return true; @@ -1700,19 +1583,19 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian throw new ResourceUnavailableException("Unable to assign ip addresses, domR is not in right state " + router.getState(), DataCenter.class, network.getDataCenterId()); } } - + @Override - public boolean applyFirewallRules(Network network, List rules) throws ResourceUnavailableException { + public boolean applyFirewallRules(Network network, List rules) throws ResourceUnavailableException { DomainRouterVO router = _routerDao.findByNetwork(network.getId()); if (router == null) { s_logger.warn("Unable to apply lb rules, virtual router doesn't exist in the network " + network.getId()); throw new ResourceUnavailableException("Unable to apply lb rules", DataCenter.class, network.getDataCenterId()); } - + if (router.getState() == State.Running) { if (rules != null && !rules.isEmpty()) { if (rules.get(0).getPurpose() == Purpose.LoadBalancing) { - // for load balancer we have to resend all lb rules for the network + //for load balancer we have to resend all lb rules for the network List lbs = _loadBalancerDao.listByNetworkId(network.getId()); List lbRules = new ArrayList(); for (LoadBalancerVO lb : lbs) { @@ -1720,53 +1603,55 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian LoadBalancingRule loadBalancing = new LoadBalancingRule(lb, dstList); lbRules.add(loadBalancing); } - + return applyLBRules(router, lbRules); - } else if (rules.get(0).getPurpose() == Purpose.PortForwarding) { - return applyPortForwardingRules(router, (List) rules); - } else if (rules.get(0).getPurpose() == Purpose.StaticNat) { - return applyStaticNatRules(router, (List) rules); - - } else { + } else if (rules.get(0).getPurpose() == Purpose.PortForwarding) { + return applyPortForwardingRules(router, (List)rules); + } else if (rules.get(0).getPurpose() == Purpose.StaticNat) { + return applyStaticNatRules(router, (List)rules); + + }else { s_logger.warn("Unable to apply rules of purpose: " + rules.get(0).getPurpose()); return false; } } else { return true; } - } else if (router.getState() == State.Stopped || router.getState() == State.Stopping) { + } else if (router.getState() == State.Stopped || router.getState() == State.Stopping){ s_logger.debug("Router is in " + router.getState() + ", so not sending apply firewall rules commands to the backend"); return true; } else { s_logger.warn("Unable to apply firewall rules, virtual router is not in the right state " + router.getState()); throw new ResourceUnavailableException("Unable to apply firewall rules, virtual router is not in the right state", VirtualRouter.class, router.getId()); } - } + } + protected boolean applyLBRules(DomainRouterVO router, List rules) throws ResourceUnavailableException { Commands cmds = new Commands(OnError.Continue); - createApplyLoadBalancingRulesCommands(rules, router, cmds); - // Send commands to router - return sendCommandsToRouter(router, cmds); + createApplyLoadBalancingRulesCommands(rules, router, cmds); + //Send commands to router + return sendCommandsToRouter(router, cmds); } - protected boolean applyPortForwardingRules(DomainRouterVO router, List rules) throws ResourceUnavailableException { + protected boolean applyPortForwardingRules(DomainRouterVO router, List rules) throws ResourceUnavailableException { Commands cmds = new Commands(OnError.Continue); - createApplyPortForwardingRulesCommands(rules, router, cmds); - // Send commands to router - return sendCommandsToRouter(router, cmds); + createApplyPortForwardingRulesCommands(rules, router, cmds); + //Send commands to router + return sendCommandsToRouter(router, cmds); } - - protected boolean applyStaticNatRules(DomainRouterVO router, List rules) throws ResourceUnavailableException { + + + protected boolean applyStaticNatRules(DomainRouterVO router, List rules) throws ResourceUnavailableException { Commands cmds = new Commands(OnError.Continue); - createApplyStaticNatRulesCommands(rules, router, cmds); - // Send commands to router - return sendCommandsToRouter(router, cmds); + createApplyStaticNatRulesCommands(rules, router, cmds); + //Send commands to router + return sendCommandsToRouter(router, cmds); } - + @Override public VirtualRouter getRouterForNetwork(long networkId) { return _routerDao.findByNetwork(networkId); - + } } diff --git a/server/src/com/cloud/servlet/RegisterCompleteServlet.java b/server/src/com/cloud/servlet/RegisterCompleteServlet.java index 807951448c9..690ff96a9ee 100644 --- a/server/src/com/cloud/servlet/RegisterCompleteServlet.java +++ b/server/src/com/cloud/servlet/RegisterCompleteServlet.java @@ -16,8 +16,8 @@ * */ -package com.cloud.servlet; - +package com.cloud.servlet; + import java.util.List; import javax.servlet.ServletContextEvent; @@ -39,22 +39,22 @@ import com.cloud.user.UserVO; import com.cloud.user.dao.UserDao; import com.cloud.utils.SerialVersionUID; import com.cloud.utils.component.ComponentLocator; - -public class RegisterCompleteServlet extends HttpServlet implements ServletContextListener { - public static final Logger s_logger = Logger.getLogger(RegisterCompleteServlet.class.getName()); - - static final long serialVersionUID = SerialVersionUID.CloudStartupServlet; - + +public class RegisterCompleteServlet extends HttpServlet implements ServletContextListener { + public static final Logger s_logger = Logger.getLogger(RegisterCompleteServlet.class.getName()); + + static final long serialVersionUID = SerialVersionUID.CloudStartupServlet; + protected static AccountService _accountSvc = null; protected static ConfigurationDao _configDao = null; - protected static UserDao _userDao = null; - - @Override + protected static UserDao _userDao = null; + + @Override public void init() throws ServletException { ComponentLocator locator = ComponentLocator.getLocator(ManagementServer.Name); _accountSvc = locator.getManager(AccountService.class); _configDao = locator.getDao(ConfigurationDao.class); - _userDao = locator.getDao(UserDao.class); + _userDao = locator.getDao(UserDao.class); } @Override @@ -118,5 +118,5 @@ public class RegisterCompleteServlet extends HttpServlet implements ServletConte } catch (Exception ex) { s_logger.error("unknown exception writing register complete response", ex); } - } -} + } +} diff --git a/server/src/com/cloud/storage/StorageManager.java b/server/src/com/cloud/storage/StorageManager.java index 60c487f04b5..8cbae728cdd 100755 --- a/server/src/com/cloud/storage/StorageManager.java +++ b/server/src/com/cloud/storage/StorageManager.java @@ -26,6 +26,7 @@ import com.cloud.capacity.CapacityVO; import com.cloud.dc.DataCenterVO; import com.cloud.dc.HostPodVO; import com.cloud.deploy.DeployDestination; +import com.cloud.deploy.DeploymentPlan; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientStorageCapacityException; import com.cloud.exception.StorageUnavailableException; @@ -72,6 +73,13 @@ public interface StorageManager extends Manager { * @return secondary storage host */ public HostVO getSecondaryStorageHost(long zoneId); + + /** + * Returns the secondary storage host + * @param zoneId + * @return secondary storage host + */ + public VMTemplateHostVO findVmTemplateHost(long templateId, long dcId, Long podId); /** * Moves a volume from its current storage pool to a storage pool with enough capacity in the specified zone, pod, or cluster diff --git a/server/src/com/cloud/storage/StorageManagerImpl.java b/server/src/com/cloud/storage/StorageManagerImpl.java index 2248d2b0384..de95531fb71 100755 --- a/server/src/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/com/cloud/storage/StorageManagerImpl.java @@ -90,6 +90,7 @@ import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.HostPodDao; import com.cloud.deploy.DeployDestination; +import com.cloud.deploy.DeploymentPlan; import com.cloud.domain.Domain; import com.cloud.domain.dao.DomainDao; import com.cloud.event.ActionEvent; @@ -2887,7 +2888,28 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag } @Override + public StoragePool getStoragePool(long id) { return _storagePoolDao.findById(id); } + + public VMTemplateHostVO findVmTemplateHost(long templateId, long dcId, Long podId) { + List secHosts = _hostDao.listSecondaryStorageHosts(dcId); + if (secHosts.size() == 1) { + VMTemplateHostVO templateHostVO = _templateHostDao.findByHostTemplate(secHosts.get(0).getId(), templateId); + return templateHostVO; + } + if (podId != null) { + List templHosts = _templateHostDao.listByTemplateStatus(templateId, dcId, podId, VMTemplateStorageResourceAssoc.Status.DOWNLOADED); + for (VMTemplateHostVO templHost: templHosts) { + return templHost; + } + } + List templHosts = _templateHostDao.listByTemplateStatus(templateId, dcId, VMTemplateStorageResourceAssoc.Status.DOWNLOADED); + for (VMTemplateHostVO templHost: templHosts) { + return templHost; + } + return null; + } + } diff --git a/server/src/com/cloud/storage/allocator/AbstractStoragePoolAllocator.java b/server/src/com/cloud/storage/allocator/AbstractStoragePoolAllocator.java index edb63315bf8..f4ffdc53ab0 100755 --- a/server/src/com/cloud/storage/allocator/AbstractStoragePoolAllocator.java +++ b/server/src/com/cloud/storage/allocator/AbstractStoragePoolAllocator.java @@ -31,6 +31,7 @@ import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.dc.ClusterVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.deploy.DataCenterDeployment; +import com.cloud.deploy.DeploymentPlan; import com.cloud.deploy.DeploymentPlanner.ExcludeList; import com.cloud.host.Host; import com.cloud.host.HostVO; @@ -74,7 +75,7 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement @Inject StoragePoolHostDao _poolHostDao; @Inject ConfigurationDao _configDao; @Inject ClusterDao _clusterDao; - float _storageOverprovisioningFactor; + int _storageOverprovisioningFactor; long _extraBytesPerVolume = 0; Random _rand; boolean _dontMatter; @@ -87,7 +88,7 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement Map configs = _configDao.getConfiguration(null, params); String globalStorageOverprovisioningFactor = configs.get("storage.overprovisioning.factor"); - _storageOverprovisioningFactor = NumbersUtil.parseFloat(globalStorageOverprovisioningFactor, 2); + _storageOverprovisioningFactor = NumbersUtil.parseInt(globalStorageOverprovisioningFactor, 2); _extraBytesPerVolume = 0; @@ -132,7 +133,7 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement } protected boolean checkPool(ExcludeList avoid, StoragePoolVO pool, DiskProfile dskCh, VMTemplateVO template, List templatesInPool, - StatsCollector sc) { + StatsCollector sc, DeploymentPlan plan) { if (s_logger.isDebugEnabled()) { s_logger.debug("Checking if storage pool is suitable, name: " + pool.getName()+ " ,poolId: "+ pool.getId()); @@ -225,28 +226,25 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement } if ((template != null) && !tmpinstalled) { - // If the template that was passed into this allocator is not installed in the storage pool, - // add 3 * (template size on secondary storage) to the running total - HostVO secondaryStorageHost = _storageMgr.getSecondaryStorageHost(pool.getDataCenterId()); - if (secondaryStorageHost == null) { - return false; - } else { - VMTemplateHostVO templateHostVO = _templateHostDao.findByHostTemplate(secondaryStorageHost.getId(), template.getId()); - if (templateHostVO == null) { - s_logger.debug("Cannot allocate this pool " + pool.getId() + " since no entry found in template_host_ref, hostId: " + secondaryStorageHost.getId() + " and templateId: "+template.getId()); - return false; - } else { - s_logger.debug("For template: " + template.getName() + ", using template size multiplier: " + 2); - long templateSize = templateHostVO.getSize(); - long templatePhysicalSize = templateHostVO.getPhysicalSize(); - totalAllocatedSize += (templateSize + _extraBytesPerVolume) + (templatePhysicalSize + _extraBytesPerVolume); - } - } + // If the template that was passed into this allocator is not installed in the storage pool, + // add 3 * (template size on secondary storage) to the running total + VMTemplateHostVO templateHostVO = _storageMgr.findVmTemplateHost(template.getId(), plan.getDataCenterId(), plan.getPodId()); + + if (templateHostVO == null) { + s_logger.info("Did not find template downloaded on secondary hosts in zone " + plan.getDataCenterId()); + return false; + } else { + s_logger.debug("For template: " + template.getName() + ", using template size multiplier: " + 2); + long templateSize = templateHostVO.getSize(); + long templatePhysicalSize = templateHostVO.getPhysicalSize(); + totalAllocatedSize += (templateSize + _extraBytesPerVolume) + (templatePhysicalSize + _extraBytesPerVolume); + } + } long askingSize = dskCh.getSize(); - float storageOverprovisioningFactor = 1; + int storageOverprovisioningFactor = 1; if (pool.getPoolType() == StoragePoolType.NetworkFilesystem) { storageOverprovisioningFactor = _storageOverprovisioningFactor; } diff --git a/server/src/com/cloud/storage/allocator/FirstFitStoragePoolAllocator.java b/server/src/com/cloud/storage/allocator/FirstFitStoragePoolAllocator.java index 527a194f513..fdf0e3e2e21 100644 --- a/server/src/com/cloud/storage/allocator/FirstFitStoragePoolAllocator.java +++ b/server/src/com/cloud/storage/allocator/FirstFitStoragePoolAllocator.java @@ -88,7 +88,7 @@ public class FirstFitStoragePoolAllocator extends AbstractStoragePoolAllocator { if(suitablePools.size() == returnUpTo){ break; } - if (checkPool(avoid, pool, dskCh, template, null, sc)) { + if (checkPool(avoid, pool, dskCh, template, null, sc, plan)) { suitablePools.add(pool); } } diff --git a/server/src/com/cloud/storage/allocator/RandomStoragePoolAllocator.java b/server/src/com/cloud/storage/allocator/RandomStoragePoolAllocator.java index ccf67658b22..c4c6410dc19 100644 --- a/server/src/com/cloud/storage/allocator/RandomStoragePoolAllocator.java +++ b/server/src/com/cloud/storage/allocator/RandomStoragePoolAllocator.java @@ -78,7 +78,7 @@ public class RandomStoragePoolAllocator extends AbstractStoragePoolAllocator { if(suitablePools.size() == returnUpTo){ break; } - if (checkPool(avoid, pool, dskCh, template, null, sc)) { + if (checkPool(avoid, pool, dskCh, template, null, sc, plan)) { suitablePools.add(pool); } } diff --git a/server/src/com/cloud/storage/dao/VMTemplateHostDaoImpl.java b/server/src/com/cloud/storage/dao/VMTemplateHostDaoImpl.java index 0d666fa0f76..1bd94834282 100755 --- a/server/src/com/cloud/storage/dao/VMTemplateHostDaoImpl.java +++ b/server/src/com/cloud/storage/dao/VMTemplateHostDaoImpl.java @@ -58,7 +58,10 @@ public class VMTemplateHostDaoImpl extends GenericDaoBase toBeDownloaded = new HashSet(); List allTemplates = _templateDao.listAllInZone(storageHost.getDataCenterId()); @@ -500,6 +505,15 @@ public class DownloadMonitorImpl implements DownloadMonitor { tmpltHost.setSize(tmpltInfo.getSize()); tmpltHost.setPhysicalSize(tmpltInfo.getPhysicalSize()); _vmTemplateHostDao.persist(tmpltHost); + VMTemplateZoneVO tmpltZoneVO = _vmTemplateZoneDao.findByZoneTemplate(zoneId, tmplt.getId()); + if (tmpltZoneVO == null){ + tmpltZoneVO = new VMTemplateZoneVO(zoneId, tmplt.getId(), new Date()); + _vmTemplateZoneDao.persist(tmpltZoneVO); + } else { + tmpltZoneVO.setLastUpdated(new Date()); + _vmTemplateZoneDao.update(tmpltZoneVO.getId(), tmpltZoneVO); + } + } continue; @@ -511,6 +525,14 @@ public class DownloadMonitorImpl implements DownloadMonitor { s_logger.info("Template Sync did not find " + uniqueName + " on the server " + sserverId + ", will request download shortly"); VMTemplateHostVO templtHost = new VMTemplateHostVO(sserverId, tmplt.getId(), new Date(), 0, Status.NOT_DOWNLOADED, null, null, null, null, tmplt.getUrl()); _vmTemplateHostDao.persist(templtHost); + VMTemplateZoneVO tmpltZoneVO = _vmTemplateZoneDao.findByZoneTemplate(zoneId, tmplt.getId()); + if (tmpltZoneVO == null){ + tmpltZoneVO = new VMTemplateZoneVO(zoneId, tmplt.getId(), new Date()); + _vmTemplateZoneDao.persist(tmpltZoneVO); + } else { + tmpltZoneVO.setLastUpdated(new Date()); + _vmTemplateZoneDao.update(tmpltZoneVO.getId(), tmpltZoneVO); + } } } diff --git a/server/src/com/cloud/storage/secondary/SecondaryStorageDiscoverer.java b/server/src/com/cloud/storage/secondary/SecondaryStorageDiscoverer.java index 5d8b0586b5a..88d1ac7dea8 100644 --- a/server/src/com/cloud/storage/secondary/SecondaryStorageDiscoverer.java +++ b/server/src/com/cloud/storage/secondary/SecondaryStorageDiscoverer.java @@ -46,6 +46,7 @@ import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VMTemplateHostDao; import com.cloud.storage.dao.VMTemplateZoneDao; import com.cloud.storage.resource.DummySecondaryStorageResource; +import com.cloud.storage.resource.LocalSecondaryStorageResource; import com.cloud.storage.resource.NfsSecondaryStorageResource; import com.cloud.utils.component.Inject; import com.cloud.utils.net.NfsUtils; diff --git a/server/src/com/cloud/template/TemplateManagerImpl.java b/server/src/com/cloud/template/TemplateManagerImpl.java index 916883352e2..92bdeafecbc 100755 --- a/server/src/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/com/cloud/template/TemplateManagerImpl.java @@ -343,20 +343,13 @@ public class TemplateManagerImpl implements TemplateManager, Manager, TemplateSe } } - SearchCriteria sc = HostTemplateStatesSearch.create(); - sc.setParameters("id", templateId); - sc.setParameters("state", Status.DOWNLOADED); - sc.setJoinParameters("host", "dcId", pool.getDataCenterId()); - - List templateHostRefs = _tmpltHostDao.search(sc, null); + templateHostRef = _storageMgr.findVmTemplateHost(templateId, pool.getDataCenterId(), pool.getPodId()); - if (templateHostRefs.size() == 0) { + if (templateHostRef == null) { s_logger.debug("Unable to find a secondary storage host who has completely downloaded the template."); return null; } - templateHostRef = templateHostRefs.get(0); - HostVO sh = _hostDao.findById(templateHostRef.getHostId()); origUrl = sh.getStorageUrl(); if (origUrl == null) { @@ -398,7 +391,7 @@ public class TemplateManagerImpl implements TemplateManager, Manager, TemplateSe String url = origUrl + "/" + templateHostRef.getInstallPath(); PrimaryStorageDownloadCommand dcmd = new PrimaryStorageDownloadCommand(template.getUniqueName(), url, template.getFormat(), template.getAccountId(), pool.getId(), pool.getUuid()); - HostVO secondaryStorageHost = _hostDao.findSecondaryStorageHost(pool.getDataCenterId()); + HostVO secondaryStorageHost = _hostDao.findById(templateHostRef.getHostId()); assert(secondaryStorageHost != null); dcmd.setSecondaryStorageUrl(secondaryStorageHost.getStorageUrl()); // TODO temporary hacking, hard-coded to NFS primary data store diff --git a/server/src/com/cloud/user/AccountManagerImpl.java b/server/src/com/cloud/user/AccountManagerImpl.java index 17f9e7124d8..98e7856d5cb 100755 --- a/server/src/com/cloud/user/AccountManagerImpl.java +++ b/server/src/com/cloud/user/AccountManagerImpl.java @@ -1163,7 +1163,7 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag if (!user.getPassword().equals(dbUser.getPassword())) { throw new CloudRuntimeException("The user " + username + " being creating is using a password that is different than what's in the db"); } - + System.out.println("user: " + dbUser.getId()); return _userAccountDao.findById(dbUser.getId()); } catch (Exception e) { if (e instanceof CloudRuntimeException) { diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 2a1a106db14..b5ec9ef63dd 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -2531,13 +2531,54 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VM_START, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), vm.getHostName(), vm.getServiceOfferingId(), vm.getTemplateId(), vm .getHypervisorType().toString()); _usageEventDao.persist(usageEvent); + Answer startAnswer = cmds.getAnswer(StartAnswer.class); + String returnedIp = null; + String originalIp = null; + if (startAnswer != null) { + StartAnswer startAns = (StartAnswer) startAnswer; + VirtualMachineTO vmTO = startAns.getVirtualMachine(); + for (NicTO nicTO: vmTO.getNics()) { + if (nicTO.getType() == TrafficType.Guest) { + returnedIp = nicTO.getIp(); + } + } + } List nics = _nicDao.listByVmId(vm.getId()); + NicVO guestNic = null; for (NicVO nic : nics) { NetworkVO network = _networkDao.findById(nic.getNetworkId()); long isDefault = (nic.isDefaultNic()) ? 1 : 0; usageEvent = new UsageEventVO(EventTypes.EVENT_NETWORK_OFFERING_ASSIGN, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), vm.getHostName(), network.getNetworkOfferingId(), null, isDefault); _usageEventDao.persist(usageEvent); + if (network.getTrafficType() == TrafficType.Guest) { + originalIp = nic.getIp4Address(); + guestNic = nic; + } + } + boolean ipChanged = false; + if (originalIp != null && !originalIp.equalsIgnoreCase(returnedIp)) { + if (returnedIp != null && guestNic != null) { + guestNic.setIp4Address(returnedIp); + ipChanged = true; + } + } + if (returnedIp != null && !returnedIp.equalsIgnoreCase(originalIp)) { + if (guestNic != null) { + guestNic.setIp4Address(returnedIp); + ipChanged = true; + } + } + if (ipChanged) { + DataCenterVO dc = _dcDao.findById(vm.getDataCenterId()); + UserVmVO userVm = profile.getVirtualMachine(); + if (dc.getDhcpProvider().equalsIgnoreCase(Provider.ExternalDhcpServer.getName())){ + _nicDao.update(guestNic.getId(), guestNic); + userVm.setPrivateIpAddress(guestNic.getIp4Address()); + _vmDao.update(userVm.getId(), userVm); + + s_logger.info("Detected that ip changed in the answer, updated nic in the db with new ip " + returnedIp); + } } return true; diff --git a/thirdparty/.classpath b/thirdparty/.classpath index e45a046da93..54e3cd8d798 100644 --- a/thirdparty/.classpath +++ b/thirdparty/.classpath @@ -1,7 +1,6 @@ - - -^ - - + + + + diff --git a/thirdparty/jnetpcap.jar b/thirdparty/jnetpcap.jar new file mode 100644 index 00000000000..01b9001f714 Binary files /dev/null and b/thirdparty/jnetpcap.jar differ diff --git a/tools/test/apisession.py b/tools/test/apisession.py new file mode 100644 index 00000000000..9b683579ace --- /dev/null +++ b/tools/test/apisession.py @@ -0,0 +1,51 @@ +import cookielib +import hashlib +import json +import os +import random +import sys +import urllib2 +import urllib + + +class ApiSession: + """an ApiSession represents one api session, with cookies.""" + def __init__(self, url, username, password): + self._username = username + self._password = hashlib.md5(password).hexdigest() + self._url = url + self._cj = cookielib.CookieJar() + self._opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self._cj)) + + def _get(self, parameters): + encoded = urllib.urlencode(parameters) + url = "%s?%s"% (self._url, encoded) + try: + f = self._opener.open(url) + return f.read() + except urllib2.HTTPError as exn: + print "Command %s failed" % parameters['command'] + print "Reason: %s" % json.loads(exn.read()) + + def GET(self, parameters): + parameters['sessionkey'] = self._sessionkey + parameters['response'] = 'json' + return self._get(parameters) + + def _post(self, parameters): + return self._opener.open(self._url, urllib.urlencode(parameters)).read() + + def POST(self, parameters): + parameters['sessionkey'] = self._sessionkey + parameters['response'] = 'json' + return self._post(parameters) + + def login(self): + params = {'command':'login', 'response': 'json'} + params['username'] = self._username + params['password'] = self._password + result = self._get(params) + jsonresult = json.loads(result) + jsessionid = None + self._sessionkey = jsonresult['loginresponse']['sessionkey'] + diff --git a/tools/test/cloudkit.py b/tools/test/cloudkit.py new file mode 100644 index 00000000000..2b5f6e2ef61 --- /dev/null +++ b/tools/test/cloudkit.py @@ -0,0 +1,58 @@ +from apisession import ApiSession +from physicalresource import ZoneCreator +from globalconfig import GlobalConfig +from db import Database +import random +import uuid + +def fix_default_db(): + database = Database() + statement=""" + UPDATE vm_template SET url='%s' + WHERE unique_name='%s' """ + database.update(statement % ('http://nfs1.lab.vmops.com/templates/dummy/systemvm.vhd', 'routing-1')) + database.update(statement % ('http://nfs1.lab.vmops.com/templates/dummy/systemvm.qcow2', 'routing-3')) + database.update(statement % ('http://nfs1.lab.vmops.com/templates/dummy/systemvm.ova', 'routing-8')) + + database.update(statement % ('http://nfs1.lab.vmops.com/templates/dummy/builtin.vhd', 'centos53-x86_64')) + database.update(statement % ('http://nfs1.lab.vmops.com/templates/dummy/builtin.qcow2', 'centos55-x86_64')) + database.update(statement % ('http://nfs1.lab.vmops.com/templates/dummy/builtin.ova', 'centos53-x64')) + statement="""UPDATE vm_template SET checksum=NULL""" + database.update(statement) + + statement="""UPDATE disk_offering set use_local_storage=1""" + database.update(statement) + +def config(): + config = GlobalConfig(api) + config.update('use.local.storage', 'true') + config.update('max.template.iso.size', '20') + +def create_zone(): + zonecreator = ZoneCreator(api, random.randint(2,1000)) + zoneid = zonecreator.create() + database = Database() + statement="""INSERT INTO data_center_details (dc_id, name, value) + VALUES (%s, '%s', '0')""" + database.update(statement % (zoneid, 'enable.secstorage.vm')) + database.update(statement % (zoneid, 'enable.consoleproxy.vm')) + statement="""INSERT INTO data_center_details (dc_id, name, value) + VALUES (%s, '%s', '%s')""" + database.update(statement % (zoneid, 'zone.dhcp.strategy', 'external')) + + statement="""UPDATE data_center set dhcp_provider='ExternalDhcpServer' where id=%s""" + database.update(statement % (zoneid)) + +if __name__ == "__main__": + fix_default_db() + + api = ApiSession('http://localhost:8080/client/api', 'admin', 'password') + api.login() + + config() + + create_zone() + + print "guid=%s" % str(uuid.uuid4()) + + diff --git a/tools/test/db.py b/tools/test/db.py new file mode 100644 index 00000000000..13c2dca7e35 --- /dev/null +++ b/tools/test/db.py @@ -0,0 +1,18 @@ +import MySQLdb + +class Database: + """Database connection""" + def __init__(self, host='localhost', username='cloud', password='cloud', db='cloud'): + self._conn = MySQLdb.connect (host, username, password, db) + + def update(self, statement): + cursor = self._conn.cursor () + #print statement + cursor.execute (statement) + #print "Number of rows updated: %d" % cursor.rowcount + cursor.close () + self._conn.commit () + + def __del__(self): + self._conn.close () + diff --git a/tools/test/globalconfig.py b/tools/test/globalconfig.py new file mode 100644 index 00000000000..e51ce481dc3 --- /dev/null +++ b/tools/test/globalconfig.py @@ -0,0 +1,18 @@ +import json +import os +import random +import sys + +class GlobalConfig: + """Updates global configuration values""" + def __init__(self, api): + self._api = api + + def update(self, key, value): + jsonresult = self._api.GET({'command': 'updateConfiguration', 'name':key, + 'value':value}) + if jsonresult is None: + print "Failed to update configuration" + return 1 + + return 0 diff --git a/tools/test/physicalresource.py b/tools/test/physicalresource.py new file mode 100644 index 00000000000..c00045bb70d --- /dev/null +++ b/tools/test/physicalresource.py @@ -0,0 +1,54 @@ +import json +import os +import random +import sys + +class ZoneCreator: + """Creates a zone (and a pod and cluster for now)""" + def __init__(self, api, zonenum, dns1='192.168.10.254', + dns2='192.168.10.253', internaldns='192.168.10.254'): + self._api = api + self._zonenum = zonenum + self._zonename = "ZONE%04d"%zonenum + self._dns1 = dns1 + self._dns2 = dns2 + self._internaldns = internaldns + + def create(self): + jsonresult = self._api.GET({'command': 'createZone', 'networktype':'Basic', + 'name':self._zonename, 'dns1':self._dns1, 'dns2':self._dns2, + 'internaldns1':self._internaldns}) + if jsonresult is None: + print "Failed to create zone" + return 0 + jsonobj = json.loads(jsonresult) + self._zoneid = jsonobj['createzoneresponse']['zone']['id'] + self._zonetoken = jsonobj['createzoneresponse']['zone']['zonetoken'] + print "Zone %s is created"%self._zonename + print "zone=%s"%self._zonetoken + #self.createPod() + return self._zoneid + + + def createPod(self): + self._podname = "POD%04d"%self._zonenum + self._clustername = "CLUSTER%04d"%self._zonenum + jsonresult = self._api.GET({'command': 'createPod', 'zoneId':self._zoneid, + 'name':self._podname, 'gateway':'192.168.1.1', 'netmask':'255.255.255.0', + 'startIp':'192.168.1.100', 'endIp':'192.168.1.150'}) + if jsonresult is None: + print "Failed to create pod" + return 2 + jsonobj = json.loads(jsonresult) + podid = jsonobj['createpodresponse']['pod']['id'] + jsonresult = self._api.GET({'command': 'addCluster', 'zoneId':self._zoneid, + 'clustername':self._clustername, 'podId':podid, 'hypervisor':'KVM', + 'clustertype':'CloudManaged'}) + if jsonresult is None: + print "Failed to create cluster" + return 3 + jsonobj = json.loads(jsonresult) + clusterid = jsonobj['addclusterresponse']['cluster'][0]['id'] + print "pod=%s"%podid + print "cluster=%s"%clusterid + diff --git a/tools/test/vm.py b/tools/test/vm.py new file mode 100644 index 00000000000..c6d4913f499 --- /dev/null +++ b/tools/test/vm.py @@ -0,0 +1,52 @@ +import json +import os +import random +import sys +import time +#http://localhost:8080/client/api?_=1303171692177&command=listTemplates&templatefilter=featured&zoneid=1&pagesize=6&page=1&response=json&sessionkey=%2Bh3Gh4BffWpQdk4nXmcC88uEk9k%3D +#http://localhost:8080/client/api?_=1303171711292&command=deployVirtualMachine&zoneId=1&hypervisor=KVM&templateId=4&serviceOfferingId=7&response=json&sessionkey=%2Bh3Gh4BffWpQdk4nXmcC88uEk9k%3D +#http://localhost:8080/client/api?_=1303171934824&command=queryAsyncJobResult&jobId=20&response=json&sessionkey=%2Bh3Gh4BffWpQdk4nXmcC88uEk9k%3D + +class VMCreator: + """Creates a VM """ + def __init__(self, api, params): + self._api = api + self._params = params + + def create(self): + cmd = {'command': 'deployVirtualMachine'} + cmd.update(self._params) + jsonresult = self._api.GET(cmd) + if jsonresult is None: + print "Failed to create VM" + return 0 + jsonobj = json.loads(jsonresult) + self._jobid = jsonobj['deployvirtualmachineresponse']['jobid'] + self._vmid = jsonobj['deployvirtualmachineresponse']['id'] + print "VM %s creation is scheduled, job=%s"%(self._vmid, self._jobid) + + + def poll(self, tries, wait): + jobstatus = -1 + while jobstatus < 1 and tries > 0: + time.sleep(wait) + cmd = {'command': 'queryAsyncJobResult', 'jobId': self._jobid} + jsonresult = self._api.GET(cmd) + if jsonresult is None: + print "Failed to query VM creation job" + return -1 + jsonobj = json.loads(jsonresult) + jobstatus = jsonobj['queryasyncjobresultresponse']['jobstatus'] + print jobstatus, type(jobstatus) + tries = tries - 1 + + if jobstatus == 1: + jsonobj = json.loads(jsonresult) + jobresult = jsonobj['queryasyncjobresultresponse']['jobresult'] + vm = jobresult['virtualmachine'] + print vm + else: + print "Failed to create vm" + + return jobstatus + diff --git a/tools/test/vmcreate.py b/tools/test/vmcreate.py new file mode 100644 index 00000000000..0a32f39dc88 --- /dev/null +++ b/tools/test/vmcreate.py @@ -0,0 +1,19 @@ +from apisession import ApiSession +from vm import VMCreator + +#http://localhost:8080/client/api?_=1303171711292&command=deployVirtualMachine&zoneId=1&hypervisor=KVM&templateId=4&serviceOfferingId=7&response=json&sessionkey=%2Bh3Gh4BffWpQdk4nXmcC88uEk9k%3D + +def create_vm(): + vmcreator = VMCreator(api, {'zoneId':1, 'hypervisor':'KVM', 'templateId':4, + 'serviceOfferingId':7, + 'userdata':'dGhpcyBpcyBhIHRlc3QK'}) + vmid = vmcreator.create() + vmcreator.poll(10, 3) + +if __name__ == "__main__": + api = ApiSession('http://localhost:8080/client/api', 'admin', 'password') + api.login() + + create_vm() + + diff --git a/ui/cloudkit/cloudkit.jsp b/ui/cloudkit/cloudkit.jsp new file mode 100644 index 00000000000..148e8edc22d --- /dev/null +++ b/ui/cloudkit/cloudkit.jsp @@ -0,0 +1,385 @@ +<% long now = System.currentTimeMillis(); %> + + + + + + + + + + + + + + + + + + + + + + + + + + + myCloud + + + + + + + + + diff --git a/ui/cloudkit/css/main.css b/ui/cloudkit/css/main.css new file mode 100644 index 00000000000..06a6ee3ebd0 --- /dev/null +++ b/ui/cloudkit/css/main.css @@ -0,0 +1,1551 @@ +@charset "UTF-8"; +/* CSS Document */ + + +*{ + margin:0; + padding:0; +} + +html,body{ + font-family:Arial, Helvetica, sans-serif; + font-size:11px; + color:#111111; + font-weight:normal; + background:#cbcbcb url(../images/main_bg.gif) repeat-x top left; + margin:0; + padding:0; + +} + + +a { + width:auto; + height:auto; + color:#2c8bbc; + font-size:11px; + font-weight:normal; + text-align:left; + text-decoration:none; + margin:0; + padding:0; +} + +a:link, a:visited { + text-decoration:none; +} + +a:hover { + text-decoration:underline; +} + + +#loginmain { + width:680px; + height:auto; + margin:0 auto; + padding:0; +} + +#main { + width:957px; + height:auto; + margin:0 auto; + padding:0; +} + +#header { + width:957px; + height:62px; + float:left; + background:url(../images/header_bg.gif) repeat-x top left; + margin:0; + padding:0; +} + +.logo { + width:440px; + height:22px; + float:left; + background:url(../images/logo.gif) no-repeat top left; + margin:20px 0 0 24px; + display:inline; + padding:0; +} + +.user_links { + width:auto; + height:20px; + float:right; + margin:15px 20px 0 0; + padding:0; +} + +.user_links p{ + width:auto; + height:auto; + float:left; + font-size:11px; + color:#FFF; + font-weight:normal; + text-align:left; + margin:0 0 0 15px; + display:inline; + padding:0; +} + +.user_links a{ + width:auto; + height:auto; + float:left; + font-size:11px; + font-weight:normal; + text-align:left; + color:#c6e3fe; + text-decoration:none; + margin:0; + padding:0; +} + +.user_links a:link, .user_links a:visited { + text-decoration:none; +} + +.user_links a:hover { + text-decoration:underline; +} + +.main_regcontentbg { + width:100%; + min-height:700px; + height:auto; + float:left; + background:#FFF url(../images/main_contentbg.gif) repeat-x top left; + margin:0; + padding:0 0 40px 0; +} + +.main_contentbg { + width:100%; + min-height:700px; + height:auto; + float:left; + background:#FFF; + margin:0; + padding:0 0 40px 0; +} + +.main_regleft { + width:388px; + height:auto; + float:left; + margin:0; + padding:0; +} + +.main_regright { + width:569px; + height:auto; + float:right; + margin:0; + padding:0; +} + +.header_bot { + width:569px; + height:103px; + float:left; + margin:0; + padding:0; +} + +.header_cloudbg { + width:166px; + height:103px; + float:right; + background:url(../images/header_cloudbg.gif) no-repeat top left; + margin:0; + padding:0; +} + +.header_comments { + width:400px; + height:auto; + float:left; + margin:10px 0 0 0; + padding:0; +} + +.header_comments p{ + width:auto; + height:auto; + float:left; + color:#FFF; + font-size:24px; + font-weight:bold; + text-align:left; + margin:0 0 0 15px; + display:inline; + padding:0; +} + +.header_comments span{ + width:auto; + height:auto; + color:#6884a2; + font-size:24px; + font-weight:bold; + text-align:left; + margin:0; + padding:0; +} + +.main_rightcontentbox { + width:569px; + height:auto; + float:left; + background:#FFF; + margin:0; + padding:0; +} + +.login_box { + width:681px; + height:auto; + float:left; + margin:30% 0 0 0; + display:inline; + padding:0; +} + +.login_box_top { + width:681px; + height:77px; + float:left; + background:url(../images/login_top.png) no-repeat top left; + margin:0; + padding:0; + overflow:hidden; +} +.login_logo { + width:450px; + height:26px; + float:left; + background:url(../images/login_logo.gif) no-repeat top left; + margin:52px 0 0 40px; + display:inline; + padding:0; +} + +.login_box_mid { + width:681px; + height:auto; + float:left; + background:url(../images/login_mid.png) repeat-y top left; + margin:0; + padding:0; +} + +.login_box_mid h2{ + width:500px; + height:auto; + float:left; + color:#dbedff; + font-weight:normal; + text-align:left; + font-size:24px; + margin:30px 0 0 40px; + display:inline; + padding:0; +} + +.login_formbox { + width:500px; + height:auto; + float:left; + margin:0 0 0 40px; + display:inline; + padding:0; + list-style:none; +} + +.login_formbox li { + width:500px; + height:auto; + float:left; + margin:22px 0 0 0; + padding:0; + list-style:none; +} + +.login_formbox label { + width:100px; + height:auto; + float:left; + color:#FFF; + font-size:15px; + font-weight:normal; + margin:7px 0 0 0; + padding:0; + list-style:none; +} + +.login_formbox .text { + width:240px; + height:24px; + float:left; + color:#666; + background:#FFF; + border:1px solid #dbdbdb; + font-size:12px; + font-weight:normal; + margin:0 0 0 10px; + padding:0; + list-style:none; +} + +.login_submitbox { + width:500px; + height:35px; + float:left; + margin:40px 0 0 0; + padding:0; +} + +.login_submitbox a { + width:auto; + height:auto; + float:left; + color:#2c8bbc; + font-size:14px; + font-weight:normal; + margin:8px 0 0 15px; + display:inline; + padding:0; + text-decoration:none; +} + +.login_submitbox a:link, .login_submitbox a:visited { + text-decoration:none; +} + +.login_submitbox a:hover { + text-decoration:underline; +} + + +.login_button { + width:143px; + height:35px; + float:left; + background:url(../images/login_button.png) no-repeat top left; + margin:0; + padding:0; + border:none; +} + +.login_button:hover { + background:url(../images/login_button_hover.png) no-repeat top left; +} + +.login_errormsgbox { + width:500px; + height:auto; + float:left; + background:#ffecec; + border:1px solid #ffaeae; + margin:15px 0 0 0; + padding:0 0 10px 0; +} + +.login_errormsgbox p{ + width:470px; + height:auto; + float:left; + margin:10px 0 0 10px; + padding:0; + color:#333; + text-align:left; + font-weight:normal; +} + +.login_box_bot { + width:681px; + height:117px; + float:left; + background:url(../images/login_bot.png) no-repeat top left; + margin:0; + padding:0; +} + +.main_rightcontentarea { + width:555px; + height:auto; + float:left; + background:#FFF; + margin:30px 0 0 10px; + display:inline; + padding:0; +} + +.main_rightcontentarea h2 { + width:555px; + height:auto; + float:left; + color:#092e64; + font-size:20px; + font-weight:normal; + line-height:30px; + text-align:left; + margin:0; + padding:0; +} + +.main_rightcontentarea h3 { + width:555px; + height:auto; + float:left; + color:#092e64; + font-size:20px; + font-weight:normal; + line-height:30px; + text-align:left; + margin:20px 0 0 0; + padding:0; +} + +.main_rightcontentarea_banner { + width:522px; + height:114px; + float:left; + background:url(../images/banner.gif) no-repeat top left; + margin:30px 0 0 0; + padding:0; +} + +.main_regbulletbox { + width:555px; + height:auto; + float:left; + margin:0; + padding:0; + list-style:none; +} + + +.main_regbulletbox li{ + width:520px; + height:auto; + float:left; + margin:0; + padding:0; + list-style:none; + background:url(../images/orange_bullet.gif) no-repeat 0 4px; + color:#333; + font-size:13px; + font-weight:normal; + margin:10px 0 0 0; + padding:0 0 5px 15px; +} + +.registration_formpanel{ + width:375px; + height:auto; + float:left; + margin:0 0 0 13px; + display:inline; + padding:0; +} + +.registration_formpanel_left { + width:13px; + height:175px; + float:left; + background:url(../images/regbox_left.gif) no-repeat top left; + margin:0; + padding:0; +} + +.registration_formpanel_mid { + width:345px; + height:auto; + float:left; + margin:0; + padding:0; +} + +.registration_formpanel_midtop { + width:345px; + height:18px; + float:left; + background:url(../images/regbox_midtop.gif) repeat-x top left; + margin:0; + padding:0; +} + +.registration_formbox { + width:345px; + height:auto; + float:left; + background:#FFF url(../images/reg_formbg.gif) repeat-x top left; + border:1px solid #c7c7c7; + margin:0; + padding:0; +} + + +.registration_titlebox { + width:315px; + height:42px; + float:left; + background:url(../images/reg_titlebox.gif) no-repeat top left; + margin:11px 0 0 -1px; + padding:0; +} + +.registration_titlebox h2{ + width:auto; + height:auto; + float:left; + color:#FFF; + font-size:16px; + font-weight:normal; + text-align:left; + margin:10px 0 0 10px; + display:inline; + padding:0; +} + +.regwizard_container { + width:300px; + height:auto; + float:left; + margin:20px 0 0 20px; + display:inline; + padding:0; +} + +.regwizard_container h3 { + width:300px; + height:auto; + float:left; + color:#bdbdbd; + font-size:18px; + font-weight:bold; + text-align:left; + margin:0; + padding:0; +} + +.regwizard_formbox { + width:300px; + height:auto; + float:left; + margin:0; + padding:0 0 20px 0; + list-style:none; +} + +.regwizard_formbox p{ + width:auto; + height:auto; + float:left; + font-size:11px; + color:#333; + text-align:left; + margin:11px 0 0 10px; + padding:0; +} + + + +.regwizard_formbox li { + width:150px; + height:auto; + float:left; + margin:18px 0 0 0; + padding:0; + list-style:none; +} + +.regwizard_formbox label { + width:95%; + height:auto; + float:left; + color:#073668; + font-size:14px; + font-weight:normal; + text-align:left; + margin:0; + padding:0; +} + +.regwizard_formbox .text { + width:130px; + height:22px; + float:left; + background:#FFF; + border:1px solid #c3c3c3; + color:#333; + font-size:12px; + font-weight:normal; + text-align:left; + margin:7px 0 0 0; + padding:0; +} + + +.regwizard_formbox .select { + width:130px; + height:22px; + float:left; + background:#FFF; + border:1px solid #c3c3c3; + color:#333; + font-size:12px; + font-weight:normal; + text-align:left; + margin:7px 0 0 0; + padding:0; +} + +.regwizard_formbox .checkbox { + width:15px; + height:15px; + float:left; + background:#FFF; + border:1px solid #c3c3c3; + color:#333; + font-size:12px; + font-weight:normal; + text-align:left; + margin:7px 0 0 0; + padding:0; +} + +.reg_submitbox { + width:100%; + height:auto; + float:left; + margin:25px 0 0 0; + padding:0; +} + +.reg_submitbutton { + width:224px; + height:52px; + float:left; + text-align:center; + background:url(../images/submit_button.gif) no-repeat top left; + margin:0 0 0 35px; + display:inline; + padding:0; + border:none; +} + +.reg_submitbutton:hover { + background:url(../images/submit_button_hover.gif) no-repeat top left; + +} + +.registration_formpanel_right { + width:15px; + height:175px; + float:left; + background:url(../images/regbox_right.gif) no-repeat top left; + margin:0; + padding:0; +} + +#footer { + width:100%; + height:90px; + float:left; + background:#e4e4e4; + margin:0 0 0 0; + padding:0; +} + + +.footer_left { + width:750px; + height:90px; + float:left; + margin:0; + padding:0; +} + +.footer_left p{ + width:auto; + height:auto; + float:left; + font-size:11px; + font-weight:normal; + color:#999; + margin:30px 0 0 15px; + display:inline; + padding:0; +} + +.footer_right { + width:200px; + height:90px; + float:left; + margin:0; + padding:0; +} + +.poweredby { + width:98px; + height:32px; + float:right; + background:url(../images/pweredby.gif) no-repeat top left; + text-decoration:none; + margin:20px 20px 0 0; + display:inline; + padding:0; +} + +.poweredby:hover { + background:url(../images/pweredby.gif) no-repeat top left; +} + +.db_tabcontent { + width:920px; + height:auto; + float:left; + margin:0; + padding:0; +} +.db_gridcontainer { + width:920px; + height:auto; + float:left; + margin:40px 0 0 17px; + padding:0; + position:relative; + z-index:1; +} + +.db_gridcontainer_topbox { + width:920px; + height:30px; + float:left; + margin:0; + padding:0; +} + +.db_gridcontainer_topbox_left { + width:450px; + height:30px; + float:left; + margin:0; + padding:0; +} + +.db_gridcontainer_topbox_left h2{ + width:auto; + height:auto; + float:left; + color:#648bb3; + font-size:20px; + font-weight:normal; + background:url(../images/title_sidebar.gif) no-repeat top right; + text-align:left; + margin:2px 0 0 0; + padding:0 20px 0 0; +} + +.db_grid_searchbox { + width:175px; + height:18px; + float:left; + background:#FFF; + border:1px solid #d9d9d9; + margin:2px 0 0 20px; + display:inline; + padding:0; +} + +.db_grid_searchbox .text { + width:133px; + height:16px; + float:left; + background:#FFF; + border:1px solid #FFF; + margin:0 0 0 5px; + display:inline; + padding:0; +} + + +.db_grid_searchicon { + width:13px; + height:14px; + float:left; + background:url(../images/search_icon.gif) no-repeat top left; + margin:2px 0 0 3px; + display:inline; + padding:0; +} + +.db_grid_search_closeicon { + width:13px; + height:14px; + float:left; + background:url(../images/search_closeicon.gif) no-repeat top left; + margin:2px 0 0 3px; + display:inline; + padding:0; + text-decoration:none; +} + +.db_grid_search_closeicon:hover { + background:url(../images/search_closeicon_hover.gif) no-repeat top left; + text-decoration:none; +} + +.db_gridcontainer_refreshbox{ + width:auto; + height:20px; + float:left; + color:#648bb3; + font-size:20px; + font-weight:normal; + text-align:left; + margin:0 0 0 10px; + padding:0 0 0 5px; +} + +.db_gridcontainer_refreshbox a{ + width:auto; + height:aut; + float:left; + color:#2c8bbc; + font-size:12px; + font-weight:normal; + text-align:left; + margin:6px 0 0 0; + padding:0; + text-decoration:underline; +} + +.db_gridcontainer_refreshbox a:link, .db_gridcontainer_refreshbox a:visited{ + text-decoration:underline; +} + +.db_gridcontainer_refreshbox a:hover{ + text-decoration:none; +} + + +.db_refreshbutton { + width:20px; + height:18px; + float:left; + background:url(../images/refresh_button.gif) no-repeat top left; + text-decoration:none; + margin:2px 0 0 0; + padding:0; +} + +.db_refreshbutton:hover { + background:url(../images/refresh_button_hover.gif) no-repeat top left; + +} + +.db_gridcontainer_topbox_right { + width:450px; + height:27px; + float:right; + margin:0; + padding:0; +} + +.db_grid_tabbox { + width:auto; + height:30px; + float:right; + margin:0; + padding:0; +} + +.db_grid_tabs { + width:100px; + height:23px; + float:left; + text-align:center; + font-size:11px; + font-weight:normal; + margin:0; + padding:7px 0 0 0; + text-decoration:none; +} + +.db_grid_tabs.on{ + background:#a4c5e7 url(../images/grid_tabbg.gif) repeat-x top left; + color:#FFF; + border:1px solid #CCC; + border-bottom:none; + font-weight:bold; +} + +.db_grid_tabs.off{ + background:none; + color:#CCC; + text-decoration:none; + cursor:pointer; + cursor:hand; +} + +.db_grid_tabs.off:hover{ + background:none; + color:#333; + text-decoration:none; +} + + +.db_gridbox { + width:918px; + height:auto; + float:left; + background:#FFF; + border:1px solid #CCC; + border-top:none; + margin:0; + padding:0; + overflow:hidden; +} + +.db_maingrid { + width:918px; + height:575px; + float:left; + margin:0; + padding:0; + overflow-x:hidden; + overflow-y:scoll; + overflow-y:auto; +} + +.db_gridrows { + width:100%; + height:auto; + float:left; + background:#FFF url(../images/db_gridrowbg.gif) repeat-x bottom left; + margin:0; + padding:0; + position:relative; +} + +.db_gridrows.header { + background:url(../images/db_gridheaderbg.gif) repeat-x top left; + height:31px; +} + + +.db_gridcolumns{ + width:auto; + height:auto; + float:left; + margin:0; + padding:0; + overflow:hidden; +} + +.db_gridcolumns.header { + height:31px; +} + +.db_gridcelltitles { + width:auto; + height:auto; + float:left; + font-size:11px; + font-weight:normal; + margin:15px 0 0 8px; + display:inline; + padding:0 0 15px 0; +} + +.db_gridcelltitles.green{ + color:#546c00; + font-weight:bold; +} + +.db_gridcelltitles.red{ + color:#c50000; + font-weight:bold; +} + +.db_gridcelltitles.gray{ + color:#666; + font-weight:bold; + +} + +.db_gridcelltitles.header{ + color:#333; + font-weight:bold; + margin:9px 0 0 8px; + padding:0; +} + +.db_statistics_icon { + width:21px; + height:21px; + float:left; + background:url(../images/statistics_icon.gif) no-repeat top left; + margin:10px 0 0 8px; + display:inline; + padding:0; +} + +.db_statistics_icon:hover { + background:url(../images/statistics_icon_hover.gif) no-repeat top left; +} + + + +.db_delete_icon { + width:21px; + height:21px; + float:left; + background:url(../images/delete_icon.gif) no-repeat top left; + margin:10px 0 0 8px; + display:inline; + padding:0; +} + +.db_delete_icon:hover { + background:url(../images/delete_icon_hover.gif) no-repeat top left; +} + +.db_grid_navigationpanel { + width:100%; + height:25px; + float:left; + background:url(../images/grid_navbg.gif) repeat-x top left; + margin:0; + padding:0; +} + +.db_gridb_paginationbox { + width:auto; + height:auto; + float:left; + margin:7px 0 0 20px; + display:inline; + padding:0; +} + +.db_gridb_paginationbox p{ + width:auto; + height:auto; + float:left; + color:#999; + font-size:11px; + font-weight:normal; + margin: 0 0; + padding:0; +} + +.db_gridb_paginationbox span{ + width:auto; + height:auto; + color:#999; + font-size:11px; + font-weight:bold; + margin: 0 0; + padding:0; +} + +.db_gridb_navbox { + width:auto; + height:auto; + float:right; + margin:7px 20px 0 0; + padding:0; +} + + +.db_gridmsgbox { + width:100%; + height:auto; + float:left; + background:#fff9d3 url(../images/stats_rowodd.gif) repeat-x bottom left; + border-bottom:1px solid #CCC; + margin:0; + padding:0; +} + +.db_gridmsgbox_content { + width:750px; + height:auto; + float:left; + margin:0 0 0 10px; + display:inline; + padding:0 0 10px 0; +} + +.db_gridmsgbox_content p { + width:auto; + height:auto; + float:left; + color:#333; + font-size:11px; + font-weight:bold; + text-align:left; + margin:10px 0 0 0; + padding:0; +} + +.db_gridmsg_button { + width:84px; + height:20px; + float:left; + background:url(../images/gridmsg_button.png) no-repeat top left; + color:#FFF; + text-align:center; + margin:8px 0 0 10px; + display:inline; + text-decoration:none; + padding:4px 0 5px 0; +} + +.db_gridmsg_button:hover { + background:url(../images/gridmsg_button_hover.png) no-repeat top left; + text-decoration:none; + +} + + +.db_gridb_navbox a:link, .db_gridb_navbox a:visited { + width:auto; + height:auto; + float:left; + color:#333; + font-size:11px; + font-weight:normal; + margin:0 0 0 25px; + display:inline; + padding:0; + text-decoration:none; +} + +.db_gridb_navbox a:hover { + width:auto; + height:auto; + float:left; + color:#333; + font-size:11px; + font-weight:normal; + margin:0 0 0 25px; + display:inline; + padding:0; + text-decoration:underline; +} + +.dbinstruction_contentarea { + width:890px; + height:auto; + float:left; + margin:0 0 0 15px; + display:inline; + padding:0; +} + + +.dbinstruction_submenubox { + width:880px; + height:auto; + float:left; + background:#FFF url(../images/db_gridrowbg.gif) repeat-x left bottom; + margin:0; + padding:0; +} + +.dbinstruction_submenubox_content { + width:800px; + height:auto; + float:left; + margin:10px 0 0 10px; + display:inline; + padding:0 0 15px 0; + list-style:none; +} + +.dbinstruction_submenubox_content li { + width:600px; + height:auto; + float:left; + margin:15px 0 0 10px; + list-style:none; + font-size:15px; + background:url(../images/menu_icon.png) no-repeat top left; + padding:1px 0 0 35px; +} + +.dbinstruction_submenubox_content a { + color:#2c8bbc; + text-align:left; + font-size:15px; + font-weight:normal; + text-decoration:none; +} + +.dbinstruction_submenubox_content a:link,.dbinstruction_submenubox_content a:visited { + text-decoration:none; +} + +.dbinstruction_submenubox_content a:hover { + text-decoration:underline; +} + + +.dbinstruction_contentarea p { + width:880px; + height:auto; + float:left; + color:#333; + font-size:12px; + font-weight:normal; + line-height:16px; + margin:15px 0 0 0; + padding:0; +} + + +.dbinstruction_contentarea h3 { + width:880px; + height:auto; + float:left; + color:#f19d00; + font-size:18px; + font-weight:normal; + margin:30px 0 0 0; + padding:0; +} + +.dbinstruction_contentarea h4 { + width:880px; + height:auto; + float:left; + color:#666; + font-size:13px; + font-weight:bold; + margin:20px 0 0 0; + padding:0; +} + +.db_downlaodbox { + width:880px; + height:78px; + float:left; + background:url(../images/instructiondownload_bg.gif) no-repeat top left; + margin:10px 0 0 0; + padding:0; +} + +.db_downlaodbox p{ + width:400px; + height:auto; + float:left; + color:#666; + font-size:14px; + font-weight:normal; + text-align:left; + margin:20px 0 0 30px; + display:inline; + padding:0; +} + +.db_instructiondownlaodbutton { + width:133px; + height:21px; + float:right; + background:url(../images/download_button.png) no-repeat top left; + margin:15px 50px 0 0; + color:#FFF; + font-size:13px; + text-align:center; + text-decoration:none; + padding:6px 0 0 15px; +} + +.db_instructiondownlaodbutton:hover { + background:url(../images/download_button_hover.png) no-repeat top left; + text-decoration:none; + +} + +.dbinstruction_bulletbox { + width:880px; + height:auto; + float:left; + color:#333; + font-size:11px; + font-weight:normal; + list-style:none; + margin:0; + padding:0; +} + +.dbinstruction_bulletbox_codebox { + width:820px; + height:auto; + float:left; + background:#f1f1f1; + color:#333; + font-size:11px; + font-weight:normal; + list-style:none; + margin:7px 0 0 0; + padding:5px 0 5px 10px; +} + +.dbinstruction_bulletbox li{ + width:800px; + height:auto; + float:left; + margin:0; + padding:0; + list-style:none; + background:url(../images/orange_bullet.gif) no-repeat 0 4px; + color:#333; + font-size:12px; + font-weight:normal; + margin:10px 0 0 0; + padding:0 0 5px 15px; +} + + +.overlay_black{ + display: block; + position: absolute; + top: 0%; + left: 0%; + width: 100%; + min-height: 1000px; + height: 100%; + background-color: black; + z-index:5; + -moz-opacity: 0.4; + opacity:.40; + filter: alpha(opacity=40); + overflow:hidden; +} + +.overlay_dialogbox { + width:485px; + height:auto; + float:left; + position:absolute; + background:#FFF; + border:1px solid #666; + margin:0; + padding:0; + z-index:10; + top:25%; + left:36%; +} + +.overlay_dialogbox_top { + width:525px; + height:29px; + float:left; + background:url(../images/overlaybox_top.png) no-repeat top left; + margin:0; + padding:0; + overflow:hidden; +} + +.overlay_dialogbox_closeicon { + width:33px; + height:32px; + float:right; + background:url(../images/overlaybox_closeicon.png) no-repeat top left; + margin:-2px 0 0 0; + padding:0; + text-decoration:none; +} + + +.overlay_dialogbox_closeicon:hover { + background:url(../images/overlaybox_closeicon_hover.png) no-repeat top left; +} + +.overlay_dialogbox_mid { + width:525px; + height:auto; + float:left; + background:url(../images/overlaybox_mid.png) repeat-y top left; + margin:0; + padding:0; +} + +.overlay_dialogbox_contentarea { + width:450px; + height:auto; + float:left; + margin:15px 0 0 16px; + display:inline; + padding:0 0 15px 0; +} + +.overlay_dialogbox_titlearea { + width:450px; + height:25px; + float:left; + background:url(../images/db_gridheaderbg.gif) repeat-x top left; + border:1px solid #CCC; + margin:0; + padding:0; +} + +.overlay_dialogbox_titlearea h2{ + width:400px; + height:auto; + float:left; + color:#FFF; + font-size:14px; + font-weight:normal; + text-align:left; + margin:5px 0 0 10px; + display:inline; + padding:0; +} + +.overlay_dialogbox_content { + width:450px; + height:auto; + float:left; + margin:15px 0 0 0; + padding:0; +} + +.overlay_dialogbox_content p { + width:450px; + height:auto; + float:left; + margin:10px 0 0 0; + color:#333; + font-size:11px; + font-weight:normal; + padding:0; +} + + +.db_stats_gridbox { + width:450px; + height:auto; + float:left; + margin:0; + padding:0; + border:1px solid #CCC; +} + +.db_stats_gridrow { + width:450px; + height:auto; + float:left; + background:url(../images/stats_row.gif) repeat-x bottom left; + margin:0; + padding:0; + border-bottom:1px solid #CCC; +} + +.db_stats_gridcolumns { + width:auto; + height:auto; + float:left; + margin:0; + padding:0; +} + +.db_stats_gridcelltitles { + width:auto; + height:auto; + float:left; + font-size:11px; + font-weight:normal; + margin:8px 0 0 8px; + display:inline; + padding:0 0 8px 0; +} + +.overlay_dialogbox_confirmationbox { + width:450px; + height:auto; + float:left; + border-top:1px solid #CCC; + margin:20px 0 0 0; + padding:0; +} + +.overlay_dialogbox_confirmationbuttonbox { + width:auto; + height:aut; + float:right; + margin:8px 0 0 0; + padding:0; +} + +.overlay_dialogbox_confirmationbuttonbox a{ + width:auto; + height:aut; + float:left; + color:#2c8bbc; + font-size:12px; + font-weight:normal; + text-align:left; + margin:8px 0 0 0; + padding:0; + text-decoration:none; +} + +.overlay_dialogbox_confirmationbuttonbox a:link, .overlay_dialogbox_confirmationbuttonbox a:visited{ + text-decoration:none; +} + +.overlay_dialogbox_confirmationbuttonbox a:hover{ + text-decoration:underline; +} + + +.overlay_dialog_button { + width:98px; + height:19px; + float:left; + background:url(../images/dialogbox_button.gif) no-repeat top left; + border:none; + color:#FFF; + text-align:center; + font-size:11px; + font-weight:normal; + margin:0 0 0 15px; + display:inline; + padding:7px 0 0 0; + cursor:button; + cursor:pointer; +} + +.overlay_dialog_button:hover { + background:url(../images/dialogbox_button_hover.gif) no-repeat top left; +} + + + + +.overlay_dialogbox_bot { + width:525px; + height:29px; + float:left; + background:url(../images/overlaybox_bot.png) no-repeat top left; + margin:0; + padding:0; +} + + + + diff --git a/ui/cloudkit/images/.DS_Store b/ui/cloudkit/images/.DS_Store new file mode 100644 index 00000000000..cdba7b3090c Binary files /dev/null and b/ui/cloudkit/images/.DS_Store differ diff --git a/ui/cloudkit/images/banner.gif b/ui/cloudkit/images/banner.gif new file mode 100644 index 00000000000..d5ddaef6f99 Binary files /dev/null and b/ui/cloudkit/images/banner.gif differ diff --git a/ui/cloudkit/images/db_gridheaderbg.gif b/ui/cloudkit/images/db_gridheaderbg.gif new file mode 100644 index 00000000000..6f023cedc0f Binary files /dev/null and b/ui/cloudkit/images/db_gridheaderbg.gif differ diff --git a/ui/cloudkit/images/db_gridrowbg.gif b/ui/cloudkit/images/db_gridrowbg.gif new file mode 100644 index 00000000000..417de28bfce Binary files /dev/null and b/ui/cloudkit/images/db_gridrowbg.gif differ diff --git a/ui/cloudkit/images/delete_icon.gif b/ui/cloudkit/images/delete_icon.gif new file mode 100644 index 00000000000..085c9693c85 Binary files /dev/null and b/ui/cloudkit/images/delete_icon.gif differ diff --git a/ui/cloudkit/images/delete_icon_hover.gif b/ui/cloudkit/images/delete_icon_hover.gif new file mode 100644 index 00000000000..e9674e7b0d4 Binary files /dev/null and b/ui/cloudkit/images/delete_icon_hover.gif differ diff --git a/ui/cloudkit/images/dialogbox_button.gif b/ui/cloudkit/images/dialogbox_button.gif new file mode 100644 index 00000000000..f5938848933 Binary files /dev/null and b/ui/cloudkit/images/dialogbox_button.gif differ diff --git a/ui/cloudkit/images/dialogbox_button_hover.gif b/ui/cloudkit/images/dialogbox_button_hover.gif new file mode 100644 index 00000000000..326e8aca39f Binary files /dev/null and b/ui/cloudkit/images/dialogbox_button_hover.gif differ diff --git a/ui/cloudkit/images/download_button.png b/ui/cloudkit/images/download_button.png new file mode 100644 index 00000000000..ac54972dac4 Binary files /dev/null and b/ui/cloudkit/images/download_button.png differ diff --git a/ui/cloudkit/images/download_button_hover.png b/ui/cloudkit/images/download_button_hover.png new file mode 100644 index 00000000000..460e74df40a Binary files /dev/null and b/ui/cloudkit/images/download_button_hover.png differ diff --git a/ui/cloudkit/images/grid_navbg.gif b/ui/cloudkit/images/grid_navbg.gif new file mode 100644 index 00000000000..f57ed8b56ff Binary files /dev/null and b/ui/cloudkit/images/grid_navbg.gif differ diff --git a/ui/cloudkit/images/grid_tabbg.gif b/ui/cloudkit/images/grid_tabbg.gif new file mode 100644 index 00000000000..d74246f3655 Binary files /dev/null and b/ui/cloudkit/images/grid_tabbg.gif differ diff --git a/ui/cloudkit/images/gridmsg_button.png b/ui/cloudkit/images/gridmsg_button.png new file mode 100644 index 00000000000..2d498777785 Binary files /dev/null and b/ui/cloudkit/images/gridmsg_button.png differ diff --git a/ui/cloudkit/images/gridmsg_button_hover.png b/ui/cloudkit/images/gridmsg_button_hover.png new file mode 100644 index 00000000000..e49624a07a4 Binary files /dev/null and b/ui/cloudkit/images/gridmsg_button_hover.png differ diff --git a/ui/cloudkit/images/header_bg.gif b/ui/cloudkit/images/header_bg.gif new file mode 100644 index 00000000000..67be3be3782 Binary files /dev/null and b/ui/cloudkit/images/header_bg.gif differ diff --git a/ui/cloudkit/images/header_bot.gif b/ui/cloudkit/images/header_bot.gif new file mode 100644 index 00000000000..bbc3782f069 Binary files /dev/null and b/ui/cloudkit/images/header_bot.gif differ diff --git a/ui/cloudkit/images/header_cloudbg.gif b/ui/cloudkit/images/header_cloudbg.gif new file mode 100644 index 00000000000..33ace16a8aa Binary files /dev/null and b/ui/cloudkit/images/header_cloudbg.gif differ diff --git a/ui/cloudkit/images/instructiondownload_bg.gif b/ui/cloudkit/images/instructiondownload_bg.gif new file mode 100644 index 00000000000..8f3d4f181d1 Binary files /dev/null and b/ui/cloudkit/images/instructiondownload_bg.gif differ diff --git a/ui/cloudkit/images/login_bot.gif b/ui/cloudkit/images/login_bot.gif new file mode 100644 index 00000000000..1607dbf4af7 Binary files /dev/null and b/ui/cloudkit/images/login_bot.gif differ diff --git a/ui/cloudkit/images/login_bot.png b/ui/cloudkit/images/login_bot.png new file mode 100644 index 00000000000..51bdbc3b129 Binary files /dev/null and b/ui/cloudkit/images/login_bot.png differ diff --git a/ui/cloudkit/images/login_button.gif b/ui/cloudkit/images/login_button.gif new file mode 100644 index 00000000000..f80453671e3 Binary files /dev/null and b/ui/cloudkit/images/login_button.gif differ diff --git a/ui/cloudkit/images/login_button.png b/ui/cloudkit/images/login_button.png new file mode 100644 index 00000000000..3d626c20ec5 Binary files /dev/null and b/ui/cloudkit/images/login_button.png differ diff --git a/ui/cloudkit/images/login_button_hover.gif b/ui/cloudkit/images/login_button_hover.gif new file mode 100644 index 00000000000..97f631c1fd3 Binary files /dev/null and b/ui/cloudkit/images/login_button_hover.gif differ diff --git a/ui/cloudkit/images/login_button_hover.png b/ui/cloudkit/images/login_button_hover.png new file mode 100644 index 00000000000..65ee97ee6f7 Binary files /dev/null and b/ui/cloudkit/images/login_button_hover.png differ diff --git a/ui/cloudkit/images/login_logo.gif b/ui/cloudkit/images/login_logo.gif new file mode 100644 index 00000000000..1b1895f0562 Binary files /dev/null and b/ui/cloudkit/images/login_logo.gif differ diff --git a/ui/cloudkit/images/login_mid.gif b/ui/cloudkit/images/login_mid.gif new file mode 100644 index 00000000000..530860c8b80 Binary files /dev/null and b/ui/cloudkit/images/login_mid.gif differ diff --git a/ui/cloudkit/images/login_mid.png b/ui/cloudkit/images/login_mid.png new file mode 100644 index 00000000000..e926624a010 Binary files /dev/null and b/ui/cloudkit/images/login_mid.png differ diff --git a/ui/cloudkit/images/login_top.gif b/ui/cloudkit/images/login_top.gif new file mode 100644 index 00000000000..c9d577c3597 Binary files /dev/null and b/ui/cloudkit/images/login_top.gif differ diff --git a/ui/cloudkit/images/login_top.png b/ui/cloudkit/images/login_top.png new file mode 100644 index 00000000000..ebabbb36ab6 Binary files /dev/null and b/ui/cloudkit/images/login_top.png differ diff --git a/ui/cloudkit/images/logo.gif b/ui/cloudkit/images/logo.gif new file mode 100644 index 00000000000..8caea42afeb Binary files /dev/null and b/ui/cloudkit/images/logo.gif differ diff --git a/ui/cloudkit/images/main_bg.gif b/ui/cloudkit/images/main_bg.gif new file mode 100644 index 00000000000..e90090949df Binary files /dev/null and b/ui/cloudkit/images/main_bg.gif differ diff --git a/ui/cloudkit/images/main_contentbg.gif b/ui/cloudkit/images/main_contentbg.gif new file mode 100644 index 00000000000..068ff0c8947 Binary files /dev/null and b/ui/cloudkit/images/main_contentbg.gif differ diff --git a/ui/cloudkit/images/menu_icon.png b/ui/cloudkit/images/menu_icon.png new file mode 100644 index 00000000000..aab668109af Binary files /dev/null and b/ui/cloudkit/images/menu_icon.png differ diff --git a/ui/cloudkit/images/orange_bullet.gif b/ui/cloudkit/images/orange_bullet.gif new file mode 100644 index 00000000000..f93679e87ba Binary files /dev/null and b/ui/cloudkit/images/orange_bullet.gif differ diff --git a/ui/cloudkit/images/overlaybox_bot.png b/ui/cloudkit/images/overlaybox_bot.png new file mode 100644 index 00000000000..1293d9a5c31 Binary files /dev/null and b/ui/cloudkit/images/overlaybox_bot.png differ diff --git a/ui/cloudkit/images/overlaybox_closeicon.png b/ui/cloudkit/images/overlaybox_closeicon.png new file mode 100644 index 00000000000..7df01472f05 Binary files /dev/null and b/ui/cloudkit/images/overlaybox_closeicon.png differ diff --git a/ui/cloudkit/images/overlaybox_closeicon_hover.png b/ui/cloudkit/images/overlaybox_closeicon_hover.png new file mode 100644 index 00000000000..8a853a1244f Binary files /dev/null and b/ui/cloudkit/images/overlaybox_closeicon_hover.png differ diff --git a/ui/cloudkit/images/overlaybox_mid.png b/ui/cloudkit/images/overlaybox_mid.png new file mode 100644 index 00000000000..7eb3ade6186 Binary files /dev/null and b/ui/cloudkit/images/overlaybox_mid.png differ diff --git a/ui/cloudkit/images/overlaybox_top.png b/ui/cloudkit/images/overlaybox_top.png new file mode 100644 index 00000000000..8c72498c132 Binary files /dev/null and b/ui/cloudkit/images/overlaybox_top.png differ diff --git a/ui/cloudkit/images/pweredby.gif b/ui/cloudkit/images/pweredby.gif new file mode 100644 index 00000000000..08458769867 Binary files /dev/null and b/ui/cloudkit/images/pweredby.gif differ diff --git a/ui/cloudkit/images/refresh_button.gif b/ui/cloudkit/images/refresh_button.gif new file mode 100644 index 00000000000..2a400a23cdb Binary files /dev/null and b/ui/cloudkit/images/refresh_button.gif differ diff --git a/ui/cloudkit/images/refresh_button_hover.gif b/ui/cloudkit/images/refresh_button_hover.gif new file mode 100644 index 00000000000..40608b0ddaa Binary files /dev/null and b/ui/cloudkit/images/refresh_button_hover.gif differ diff --git a/ui/cloudkit/images/reg_formbg.gif b/ui/cloudkit/images/reg_formbg.gif new file mode 100644 index 00000000000..69c99b722ea Binary files /dev/null and b/ui/cloudkit/images/reg_formbg.gif differ diff --git a/ui/cloudkit/images/reg_titlebox.gif b/ui/cloudkit/images/reg_titlebox.gif new file mode 100644 index 00000000000..396668958df Binary files /dev/null and b/ui/cloudkit/images/reg_titlebox.gif differ diff --git a/ui/cloudkit/images/regbox_left.gif b/ui/cloudkit/images/regbox_left.gif new file mode 100644 index 00000000000..8502db1f915 Binary files /dev/null and b/ui/cloudkit/images/regbox_left.gif differ diff --git a/ui/cloudkit/images/regbox_midtop.gif b/ui/cloudkit/images/regbox_midtop.gif new file mode 100644 index 00000000000..6bbb41dfa40 Binary files /dev/null and b/ui/cloudkit/images/regbox_midtop.gif differ diff --git a/ui/cloudkit/images/regbox_right.gif b/ui/cloudkit/images/regbox_right.gif new file mode 100644 index 00000000000..76a6741152f Binary files /dev/null and b/ui/cloudkit/images/regbox_right.gif differ diff --git a/ui/cloudkit/images/search_closeicon.gif b/ui/cloudkit/images/search_closeicon.gif new file mode 100644 index 00000000000..53314862177 Binary files /dev/null and b/ui/cloudkit/images/search_closeicon.gif differ diff --git a/ui/cloudkit/images/search_closeicon_hover.gif b/ui/cloudkit/images/search_closeicon_hover.gif new file mode 100644 index 00000000000..b76df834d1c Binary files /dev/null and b/ui/cloudkit/images/search_closeicon_hover.gif differ diff --git a/ui/cloudkit/images/search_icon.gif b/ui/cloudkit/images/search_icon.gif new file mode 100644 index 00000000000..f8513c0b26a Binary files /dev/null and b/ui/cloudkit/images/search_icon.gif differ diff --git a/ui/cloudkit/images/statistics_icon.gif b/ui/cloudkit/images/statistics_icon.gif new file mode 100644 index 00000000000..1fded07b312 Binary files /dev/null and b/ui/cloudkit/images/statistics_icon.gif differ diff --git a/ui/cloudkit/images/statistics_icon_hover.gif b/ui/cloudkit/images/statistics_icon_hover.gif new file mode 100644 index 00000000000..716176f88f2 Binary files /dev/null and b/ui/cloudkit/images/statistics_icon_hover.gif differ diff --git a/ui/cloudkit/images/statiticsarrow.png b/ui/cloudkit/images/statiticsarrow.png new file mode 100644 index 00000000000..baa2aa2883b Binary files /dev/null and b/ui/cloudkit/images/statiticsarrow.png differ diff --git a/ui/cloudkit/images/stats_row.gif b/ui/cloudkit/images/stats_row.gif new file mode 100644 index 00000000000..46d7e1d3e23 Binary files /dev/null and b/ui/cloudkit/images/stats_row.gif differ diff --git a/ui/cloudkit/images/stats_roweven.gif b/ui/cloudkit/images/stats_roweven.gif new file mode 100644 index 00000000000..68cce5d0c82 Binary files /dev/null and b/ui/cloudkit/images/stats_roweven.gif differ diff --git a/ui/cloudkit/images/stats_rowodd.gif b/ui/cloudkit/images/stats_rowodd.gif new file mode 100644 index 00000000000..6564c58436f Binary files /dev/null and b/ui/cloudkit/images/stats_rowodd.gif differ diff --git a/ui/cloudkit/images/submit_button.gif b/ui/cloudkit/images/submit_button.gif new file mode 100644 index 00000000000..8c9cecf8263 Binary files /dev/null and b/ui/cloudkit/images/submit_button.gif differ diff --git a/ui/cloudkit/images/submit_button_hover.gif b/ui/cloudkit/images/submit_button_hover.gif new file mode 100644 index 00000000000..7320f1f9985 Binary files /dev/null and b/ui/cloudkit/images/submit_button_hover.gif differ diff --git a/ui/cloudkit/images/title_sidebar.gif b/ui/cloudkit/images/title_sidebar.gif new file mode 100644 index 00000000000..7f1055f1b8b Binary files /dev/null and b/ui/cloudkit/images/title_sidebar.gif differ diff --git a/ui/cloudkit/login.html b/ui/cloudkit/login.html new file mode 100644 index 00000000000..6de39576ceb --- /dev/null +++ b/ui/cloudkit/login.html @@ -0,0 +1,53 @@ + + + + +Untitled Document + + + + +
+ +
+ +
+ +
+ + diff --git a/ui/cloudkit/login.jsp b/ui/cloudkit/login.jsp new file mode 100644 index 00000000000..46268deedee --- /dev/null +++ b/ui/cloudkit/login.jsp @@ -0,0 +1,66 @@ +<% long now = System.currentTimeMillis(); %> + + + + + + + + + + + + + + + + + + + + + + + + + + + myCloud - Login + + + + + + diff --git a/ui/cloudkit/scripts/cloudkit.docs.js b/ui/cloudkit/scripts/cloudkit.docs.js new file mode 100644 index 00000000000..5532828fcde --- /dev/null +++ b/ui/cloudkit/scripts/cloudkit.docs.js @@ -0,0 +1,19 @@ + /** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +$(document).ready(function() { +}); diff --git a/ui/cloudkit/scripts/cloudkit.hosts.js b/ui/cloudkit/scripts/cloudkit.hosts.js new file mode 100644 index 00000000000..25338f1b019 --- /dev/null +++ b/ui/cloudkit/scripts/cloudkit.hosts.js @@ -0,0 +1,287 @@ + /** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +function convertBytes(bytes) { + if (bytes < 1024 * 1024) { + return (bytes / 1024).toFixed(2) + " KB"; + } else if (bytes < 1024 * 1024 * 1024) { + return (bytes / 1024 / 1024).toFixed(2) + " MB"; + } else if (bytes < 1024 * 1024 * 1024 * 1024) { + return (bytes / 1024 / 1024 / 1024).toFixed(2) + " GB"; + } else { + return (bytes / 1024 / 1024 / 1024 / 1024).toFixed(2) + " TB"; + } +} + +function convertHz(hz) { + if (hz == null) + return ""; + + if (hz < 1000) { + return hz + " MHZ"; + } else { + return (hz / 1000).toFixed(2) + " GHZ"; + } +} + +$(document).ready(function() { + if (g_loginResponse == null) { + logout(); + return; + } + + // setup dialog + var dialog = $("#dialog_overlay"); + dialog.find("#dialog_cancel, #dialog_ok").bind("click", function(event) { + dialog.hide(); + }); + dialog.find("#dialog_confirm").bind("click", function(event) { + var id = $(this).data("hostid"); + $.ajax({ + data: createURL("command=deleteHost&id="+id), + success: function(json) { + $("#host_"+id).slideUp("slow", function() { + $(this).remove(); + }); + } + }); + dialog.hide(); + }); + + // setup host template and container + var hostTemplate = $("#host_template"); + var hostContainer = $("#host_container"); + + hostContainer.bind("click", function(event) { + var $container = $(this); + var target = $(event.target); + var targetId = target.attr("id"); + + switch (targetId) { + case "host_details" : + var details = dialog.find("#dialog_host_details").show(); + dialog.find("#dialog_delete_host").hide(); + var jsonObj = target.data("jsonObj"); + + details.find("#host_id").text(jsonObj.id); + details.find("#host_cpu_total").text((jsonObj.cpuspeed == null)? "": (jsonObj.cpunumber + "x" + convertHz(jsonObj.cpuspeed))); + details.find("#host_cpu_allocated").text((jsonObj.cpuallocated == null)? "": jsonObj.cpuallocated); + details.find("#host_cpu_used").text((jsonObj.cpuused == null)? "": jsonObj.cpuused); + details.find("#host_mem_total").text((jsonObj.memorytotal == null)? "": convertBytes(jsonObj.memorytotal)); + details.find("#host_mem_allocated").text((jsonObj.memoryallocated == null)? "": convertBytes(jsonObj.memoryallocated)); + details.find("#host_mem_used").text((jsonObj.memeoryused == null)? "": convertBytes(jsonObj.memeoryused)); + details.find("#host_net_read").text((jsonObj.networkkbsread == null)? "": convertBytes(jsonObj.networkkbsread * 1024)); + details.find("#host_net_sent").text((jsonObj.networkkbswrite == null)? "": convertBytes(jsonObj.networkkbswrite *1024)); + + if (jsonObj.created != null) { + var created = new Date(); + created.setISO8601(jsonObj.created); + details.find("#host_added").text(created.format("m/d/Y H:i:s")); + } + else { + details.find("#host_added").text(""); + } + dialog.show(); + break; + case "host_delete" : + dialog.find("#dialog_host_details").hide(); + dialog.find("#dialog_delete_host").show(); + var jsonObj = target.data("jsonObj"); + dialog.find("#hostname").text(jsonObj.name); + dialog.find("#dialog_confirm").data("hostid", jsonObj.id); + dialog.show(); + break; + } + }); + + + var page = 1; + var midmenuItemCount = 13; + function listHosts() { + if(page > 1) + $("#prev_page_button").show(); + else + $("#prev_page_button").hide(); + + var cmdString = "command=listHosts&type=Routing"+"&pagesize="+midmenuItemCount+"&page="+page; + var searchInput = $("#search_panel").find("#search_input").val(); + if (searchInput != null && searchInput.length > 0) + cmdString += ("&keyword="+searchInput); + + $.ajax({ + data: createURL(cmdString), + success: function(json) { + hostContainer.empty(); + $("#page_number").text(page); + var hosts = json.listhostsresponse.host; + if (hosts != null && hosts.length >0) { + if(hosts.length >= midmenuItemCount) + $("#next_page_button").show(); + else + $("#next_page_button").hide(); + + for (var i = 0; i < hosts.length; i++) { + var host = hosts[i]; + var template = hostTemplate.clone(true).attr("id", "host_"+host.id); + template.find("#host_details").data("jsonObj", host); + template.find("#host_delete").data("jsonObj", host); + template.find("#hostname").text(host.name); + template.find("#ip").text(host.ipaddress); + template.find("#version").text(host.version); + + if (host.disconnected != null) { + var disconnected = new Date(); + disconnected.setISO8601(host.disconnected); + template.find("#disconnected").text(disconnected.format("m/d/Y H:i:s")); + } + var state = host.state; + template.find("#state").text(state); + if (state == 'Up') { + template.find("#state").removeClass("red").addClass("green"); + } + else { + template.find("#state").removeClass("green").addClass("red"); + } + hostContainer.append(template.show()); + } + } + else { + $("#next_page_button").hide(); + } + } + }); + } + + + // *** Setup tab clicks (begin) *** + $("#tab_hosts").bind("click", function(event) { + $(this).removeClass("off").addClass("on"); + $("#tab_docs").removeClass("on").addClass("off"); + $("#tab_docs_content").hide(); + $("#tab_hosts_content").show(); + $("#search_input").val(""); + $("#search_panel").show(); + page = 1; //reset pagination to the first page + listHosts(); + return false; + }); + + $("#tab_docs").bind("click", function(event) { + $(this).removeClass("off").addClass("on"); + $("#tab_hosts").removeClass("on").addClass("off"); + $("#tab_hosts_content").hide(); + $("#tab_docs_content").show(); + $("#search_panel").hide(); + + $.ajax({ + data: createURL("command=listZones&domainid="+g_loginResponse.domainid), // Setup zone key + success: function(json) { + var zones = json.listzonesresponse.zone; + if (zones != null && zones.length >0) { + $("#zone_token").text(zones[0].zonetoken); + } + } + }); + + return false; + }); + // *** Setup tab clicks (end) *** + + // *** Pagination (begin) *** + $("#tab_hosts_content").find("#prev_page_button").bind("click", function(event){ + page-- + listHosts(); + return false; + }); + + $("#tab_hosts_content").find("#next_page_button").bind("click", function(event){ + page++ + listHosts(); + return false; + }); + // *** Pagination (begin) *** + + // *** Search (begin) *** + $("#search_input").bind("keypress", function(event) { + if(event.keyCode == keycode_Enter) { + page = 1; //reset pagination to the first page + listHosts(); + } + else { + $("#clear_search_button").show(); + } + return true; + }); + $("#clear_search_button").bind("click", function(event){ + $("#search_input").val(""); + page = 1; //reset pagination to the first page + listHosts(); + $(this).hide(); + return false; + }); + // *** Search (end) *** + + // *** Refresh button (begin) *** + $("#refresh_button").bind("click", function(event){ + listHosts(); + return false; + }); + // *** Refresh button (end) *** + + var oneHostUp = false; + var atLeastOneHost = false; + $.ajax({ + data: createURL("command=listHosts&type=Routing"), + success: function(json) { + hostContainer.empty(); + var hosts = json.listhostsresponse.host; + if (hosts != null && hosts.length >0) { + atLeastOneHost = true; + for (var i = 0; i < hosts.length; i++) { + var host = hosts[i]; + if (host.state == 'Up') { + oneHostUp = true; + } + } + } + } + }); + + if (g_loginResponse.registered == "false") { + if (!atLeastOneHost) { + $("#tab_docs").click(); + } + else if (oneHostUp) { + $("#registration_complete_link").attr("href","https://my.rightscale.com/cloud_registrations/my_cloud/cloud_stack/new?callback_url="+encodeURIComponent("http://216.38.159.3:8080/client/cloudkit/complete?token="+g_loginResponse.registrationtoken)); + $("#registration_complete_container").show(); + $("#tab_hosts").click(); + } + else { + $("#tab_hosts").click(); + } + } + else { + $("#registration_complete_container").hide(); + $("#tab_hosts").click(); + } + + $("#registration_complete_doc_link").attr("href","https://my.rightscale.com/cloud_registrations/my_cloud/cloud_stack/new?callback_url="+encodeURIComponent("http://216.38.159.3:8080/client/cloudkit/complete?token="+g_loginResponse.registrationtoken)); + $("#main").show(); +}); + + diff --git a/ui/cloudkit/scripts/cloudkit.js b/ui/cloudkit/scripts/cloudkit.js index 7d1967a477d..df4895c68f8 100644 --- a/ui/cloudkit/scripts/cloudkit.js +++ b/ui/cloudkit/scripts/cloudkit.js @@ -1,52 +1,52 @@ - /** - * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. - * - * This software is licensed under the GNU General Public License v3 or later. - * - * It is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -var g_loginResponse = null; -$.urlParam = function(name){ var results = new RegExp('[\\?&]' + name + '=([^&#]*)').exec(window.location.href); if (!results) { return 0; } return results[1] || 0;} - + /** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +var g_loginResponse = null; +$.urlParam = function(name){ var results = new RegExp('[\\?&]' + name + '=([^&#]*)').exec(window.location.href); if (!results) { return 0; } return results[1] || 0;} + function logout() { - window.location='/client/cloudkit/login.jsp'; + window.location='/client/cloudkit/login.jsp'; g_loginResponse = null; return true; -} +} -$(document).ready(function() { +$(document).ready(function() { - var url = $.urlParam("loginUrl"); - if (url != undefined && url != null && url.length > 0) { - url = unescape("/client/api?"+url); + var url = $.urlParam("loginUrl"); + if (url != undefined && url != null && url.length > 0) { + url = unescape("/client/api?"+url); $.ajax({ url: url, dataType: "json", async: false, - success: function(json) { - g_loginResponse = json.loginresponse; - $("#registration_complete_link").attr("href","https://my.rightscale.com/cloud_registrations/cloudkit/new?callback_url="+encodeURIComponent("http://localhost:8080/client/cloudkit/complete?token="+g_loginResponse.registrationtoken)); + success: function(json) { + g_loginResponse = json.loginresponse; + $("#registration_complete_link").attr("href","https://my.rightscale.com/cloud_registrations/cloudkit/new?callback_url="+encodeURIComponent("http://localhost:8080/client/cloudkit/complete?token="+g_loginResponse.registrationtoken)); }, - error: function() { - logout(); + error: function() { + logout(); }, beforeSend: function(XMLHttpRequest) { return true; } - }); - } else { - logout(); - } + }); + } else { + logout(); + } }); diff --git a/ui/cloudkit/scripts/cloudkit.login.js b/ui/cloudkit/scripts/cloudkit.login.js new file mode 100644 index 00000000000..ae1bbba4036 --- /dev/null +++ b/ui/cloudkit/scripts/cloudkit.login.js @@ -0,0 +1,74 @@ + /** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +$.urlParam = function(name){ var results = new RegExp('[\\?&]' + name + '=([^&#]*)').exec(window.location.href); if (!results) { return 0; } return results[1] || 0;} + +$(document).ready(function() { + var url = $.urlParam("loginUrl"); + if (url != undefined && url != null && url.length > 0) { + // single signon process + url = unescape("/client/api?"+url); + $.ajax({ + url: url, + dataType: "json", + async: false, + type: "POST", + success: function(json) { + login(json); + }, + error: function() { + logout(); + }, + beforeSend: function(XMLHttpRequest) { + return true; + } + }); + } else { + $("#loginmain").show(); + + $("#login_submit").click("click", function(event) { + var username = escape($("#login_username").val()); + var password = $.md5($("#login_password").val()); + var domain = escape("/"+username+"_domain"); + $.ajax({ + url: "/client/api?command=login&response=json&username="+username+"&password="+password+"&domain="+domain, + dataType: "json", + async: false, + type: "POST", + success: function(json) { + $("#login_error").hide(); + login(json); + }, + error: function(XMLHttpRequest) { + $("#login_password").val(""); + $("#login_error").show(); + $("#login_username").focus(); + }, + beforeSend: function(XMLHttpRequest) { + return true; + } + }); + return false; + }); + + $("#login_form").keypress(function(event) { + if(event.keyCode == keycode_Enter) { + $("#login_submit").click(); + } + }); + } +}); \ No newline at end of file diff --git a/ui/cloudkit/scripts/json2.js b/ui/cloudkit/scripts/json2.js new file mode 100644 index 00000000000..8fe2d9c8ecf --- /dev/null +++ b/ui/cloudkit/scripts/json2.js @@ -0,0 +1,480 @@ +/* + http://www.JSON.org/json2.js + 2011-02-23 + + Public Domain. + + NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + + See http://www.JSON.org/js.html + + + This code should be minified before deployment. + See http://javascript.crockford.com/jsmin.html + + USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO + NOT CONTROL. + + + This file creates a global JSON object containing two methods: stringify + and parse. + + JSON.stringify(value, replacer, space) + value any JavaScript value, usually an object or array. + + replacer an optional parameter that determines how object + values are stringified for objects. It can be a + function or an array of strings. + + space an optional parameter that specifies the indentation + of nested structures. If it is omitted, the text will + be packed without extra whitespace. If it is a number, + it will specify the number of spaces to indent at each + level. If it is a string (such as '\t' or ' '), + it contains the characters used to indent at each level. + + This method produces a JSON text from a JavaScript value. + + When an object value is found, if the object contains a toJSON + method, its toJSON method will be called and the result will be + stringified. A toJSON method does not serialize: it returns the + value represented by the name/value pair that should be serialized, + or undefined if nothing should be serialized. The toJSON method + will be passed the key associated with the value, and this will be + bound to the value + + For example, this would serialize Dates as ISO strings. + + Date.prototype.toJSON = function (key) { + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + return this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z'; + }; + + You can provide an optional replacer method. It will be passed the + key and value of each member, with this bound to the containing + object. The value that is returned from your method will be + serialized. If your method returns undefined, then the member will + be excluded from the serialization. + + If the replacer parameter is an array of strings, then it will be + used to select the members to be serialized. It filters the results + such that only members with keys listed in the replacer array are + stringified. + + Values that do not have JSON representations, such as undefined or + functions, will not be serialized. Such values in objects will be + dropped; in arrays they will be replaced with null. You can use + a replacer function to replace those with JSON values. + JSON.stringify(undefined) returns undefined. + + The optional space parameter produces a stringification of the + value that is filled with line breaks and indentation to make it + easier to read. + + If the space parameter is a non-empty string, then that string will + be used for indentation. If the space parameter is a number, then + the indentation will be that many spaces. + + Example: + + text = JSON.stringify(['e', {pluribus: 'unum'}]); + // text is '["e",{"pluribus":"unum"}]' + + + text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); + // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' + + text = JSON.stringify([new Date()], function (key, value) { + return this[key] instanceof Date ? + 'Date(' + this[key] + ')' : value; + }); + // text is '["Date(---current time---)"]' + + + JSON.parse(text, reviver) + This method parses a JSON text to produce an object or array. + It can throw a SyntaxError exception. + + The optional reviver parameter is a function that can filter and + transform the results. It receives each of the keys and values, + and its return value is used instead of the original value. + If it returns what it received, then the structure is not modified. + If it returns undefined then the member is deleted. + + Example: + + // Parse the text. Values that look like ISO date strings will + // be converted to Date objects. + + myData = JSON.parse(text, function (key, value) { + var a; + if (typeof value === 'string') { + a = +/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); + if (a) { + return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], + +a[5], +a[6])); + } + } + return value; + }); + + myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { + var d; + if (typeof value === 'string' && + value.slice(0, 5) === 'Date(' && + value.slice(-1) === ')') { + d = new Date(value.slice(5, -1)); + if (d) { + return d; + } + } + return value; + }); + + + This is a reference implementation. You are free to copy, modify, or + redistribute. +*/ + +/*jslint evil: true, strict: false, regexp: false */ + +/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, + call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, + getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, + lastIndex, length, parse, prototype, push, replace, slice, stringify, + test, toJSON, toString, valueOf +*/ + + +// Create a JSON object only if one does not already exist. We create the +// methods in a closure to avoid creating global variables. + +var JSON; +if (!JSON) { + JSON = {}; +} + +(function () { + "use strict"; + + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + if (typeof Date.prototype.toJSON !== 'function') { + + Date.prototype.toJSON = function (key) { + + return isFinite(this.valueOf()) ? + this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z' : null; + }; + + String.prototype.toJSON = + Number.prototype.toJSON = + Boolean.prototype.toJSON = function (key) { + return this.valueOf(); + }; + } + + var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + gap, + indent, + meta = { // table of character substitutions + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '"' : '\\"', + '\\': '\\\\' + }, + rep; + + + function quote(string) { + +// If the string contains no control characters, no quote characters, and no +// backslash characters, then we can safely slap some quotes around it. +// Otherwise we must also replace the offending characters with safe escape +// sequences. + + escapable.lastIndex = 0; + return escapable.test(string) ? '"' + string.replace(escapable, function (a) { + var c = meta[a]; + return typeof c === 'string' ? c : + '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }) + '"' : '"' + string + '"'; + } + + + function str(key, holder) { + +// Produce a string from holder[key]. + + var i, // The loop counter. + k, // The member key. + v, // The member value. + length, + mind = gap, + partial, + value = holder[key]; + +// If the value has a toJSON method, call it to obtain a replacement value. + + if (value && typeof value === 'object' && + typeof value.toJSON === 'function') { + value = value.toJSON(key); + } + +// If we were called with a replacer function, then call the replacer to +// obtain a replacement value. + + if (typeof rep === 'function') { + value = rep.call(holder, key, value); + } + +// What happens next depends on the value's type. + + switch (typeof value) { + case 'string': + return quote(value); + + case 'number': + +// JSON numbers must be finite. Encode non-finite numbers as null. + + return isFinite(value) ? String(value) : 'null'; + + case 'boolean': + case 'null': + +// If the value is a boolean or null, convert it to a string. Note: +// typeof null does not produce 'null'. The case is included here in +// the remote chance that this gets fixed someday. + + return String(value); + +// If the type is 'object', we might be dealing with an object or an array or +// null. + + case 'object': + +// Due to a specification blunder in ECMAScript, typeof null is 'object', +// so watch out for that case. + + if (!value) { + return 'null'; + } + +// Make an array to hold the partial results of stringifying this object value. + + gap += indent; + partial = []; + +// Is the value an array? + + if (Object.prototype.toString.apply(value) === '[object Array]') { + +// The value is an array. Stringify every element. Use null as a placeholder +// for non-JSON values. + + length = value.length; + for (i = 0; i < length; i += 1) { + partial[i] = str(i, value) || 'null'; + } + +// Join all of the elements together, separated with commas, and wrap them in +// brackets. + + v = partial.length === 0 ? '[]' : gap ? + '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : + '[' + partial.join(',') + ']'; + gap = mind; + return v; + } + +// If the replacer is an array, use it to select the members to be stringified. + + if (rep && typeof rep === 'object') { + length = rep.length; + for (i = 0; i < length; i += 1) { + if (typeof rep[i] === 'string') { + k = rep[i]; + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } else { + +// Otherwise, iterate through all of the keys in the object. + + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } + +// Join all of the member texts together, separated with commas, +// and wrap them in braces. + + v = partial.length === 0 ? '{}' : gap ? + '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : + '{' + partial.join(',') + '}'; + gap = mind; + return v; + } + } + +// If the JSON object does not yet have a stringify method, give it one. + + if (typeof JSON.stringify !== 'function') { + JSON.stringify = function (value, replacer, space) { + +// The stringify method takes a value and an optional replacer, and an optional +// space parameter, and returns a JSON text. The replacer can be a function +// that can replace values, or an array of strings that will select the keys. +// A default replacer method can be provided. Use of the space parameter can +// produce text that is more easily readable. + + var i; + gap = ''; + indent = ''; + +// If the space parameter is a number, make an indent string containing that +// many spaces. + + if (typeof space === 'number') { + for (i = 0; i < space; i += 1) { + indent += ' '; + } + +// If the space parameter is a string, it will be used as the indent string. + + } else if (typeof space === 'string') { + indent = space; + } + +// If there is a replacer, it must be a function or an array. +// Otherwise, throw an error. + + rep = replacer; + if (replacer && typeof replacer !== 'function' && + (typeof replacer !== 'object' || + typeof replacer.length !== 'number')) { + throw new Error('JSON.stringify'); + } + +// Make a fake root object containing our value under the key of ''. +// Return the result of stringifying the value. + + return str('', {'': value}); + }; + } + + +// If the JSON object does not yet have a parse method, give it one. + + if (typeof JSON.parse !== 'function') { + JSON.parse = function (text, reviver) { + +// The parse method takes a text and an optional reviver function, and returns +// a JavaScript value if the text is a valid JSON text. + + var j; + + function walk(holder, key) { + +// The walk method is used to recursively walk the resulting structure so +// that modifications can be made. + + var k, v, value = holder[key]; + if (value && typeof value === 'object') { + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } else { + delete value[k]; + } + } + } + } + return reviver.call(holder, key, value); + } + + +// Parsing happens in four stages. In the first stage, we replace certain +// Unicode characters with escape sequences. JavaScript handles many characters +// incorrectly, either silently deleting them, or treating them as line endings. + + text = String(text); + cx.lastIndex = 0; + if (cx.test(text)) { + text = text.replace(cx, function (a) { + return '\\u' + + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }); + } + +// In the second stage, we run the text against regular expressions that look +// for non-JSON patterns. We are especially concerned with '()' and 'new' +// because they can cause invocation, and '=' because it can cause mutation. +// But just to be safe, we want to reject all unexpected forms. + +// We split the second stage into 4 regexp operations in order to work around +// crippling inefficiencies in IE's and Safari's regexp engines. First we +// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we +// replace all simple value tokens with ']' characters. Third, we delete all +// open brackets that follow a colon or comma or that begin the text. Finally, +// we look to see that the remaining characters are only whitespace or ']' or +// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. + + if (/^[\],:{}\s]*$/ + .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') + .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') + .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { + +// In the third stage we use the eval function to compile the text into a +// JavaScript structure. The '{' operator is subject to a syntactic ambiguity +// in JavaScript: it can begin a block or an object literal. We wrap the text +// in parens to eliminate the ambiguity. + + j = eval('(' + text + ')'); + +// In the optional fourth stage, we recursively walk the new structure, passing +// each name/value pair to a reviver function for possible transformation. + + return typeof reviver === 'function' ? + walk({'': j}, '') : j; + } + +// If the text is not JSON parseable, then a SyntaxError is thrown. + + throw new SyntaxError('JSON.parse'); + }; + } +}()); \ No newline at end of file diff --git a/utils/src/com/cloud/utils/PasswordGenerator.java b/utils/src/com/cloud/utils/PasswordGenerator.java index 27099106c99..e1326616634 100644 --- a/utils/src/com/cloud/utils/PasswordGenerator.java +++ b/utils/src/com/cloud/utils/PasswordGenerator.java @@ -79,6 +79,24 @@ public class PasswordGenerator { } + public static String rot13(final String password) { + final StringBuffer newPassword = new StringBuffer(""); + + for (int i = 0; i < password.length(); i++) { + char c = password.charAt(i); + + if ((c >= 'a' && c <= 'm') || ((c >= 'A' && c <= 'M'))) { + c += 13; + } else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z')) { + c -= 13; + } + + newPassword.append(c); + } + + return newPassword.toString(); + } + public static void main(String [] args) { for (int i=0; i < 100; i++) { System.out.println("PSK: " + generatePresharedKey(24)); diff --git a/utils/src/com/cloud/utils/ProcessUtil.java b/utils/src/com/cloud/utils/ProcessUtil.java index c474117dc9f..0f9f0849eb8 100644 --- a/utils/src/com/cloud/utils/ProcessUtil.java +++ b/utils/src/com/cloud/utils/ProcessUtil.java @@ -40,9 +40,9 @@ public class ProcessUtil { private static final Logger s_logger = Logger.getLogger(ProcessUtil.class.getName()); // paths cannot be hardcoded - public static void pidCheck(String run) throws ConfigurationException { + public static void pidCheck(String pidDir, String run) throws ConfigurationException { - String dir = "/var/run"; + String dir = pidDir==null?"/var/run":pidDir; try { final File propsFile = PropertiesUtil.findConfigFile("environment.properties"); diff --git a/utils/src/com/cloud/utils/nio/Link.java b/utils/src/com/cloud/utils/nio/Link.java index f285eeda7d2..83d8865cb12 100755 --- a/utils/src/com/cloud/utils/nio/Link.java +++ b/utils/src/com/cloud/utils/nio/Link.java @@ -307,6 +307,10 @@ public class Link { headBuf.putInt(dataRemaining); headBuf.flip(); + if (headRemaining + dataRemaining > 65535) { + throw new IOException("Fail to send a too big packet! Size: " + (headRemaining + dataRemaining)); + } + while (headRemaining > 0) { if (s_logger.isTraceEnabled()) { s_logger.trace("Writing Header " + headRemaining); diff --git a/wscript_build b/wscript_build index 4ae1b793a74..877e32094d9 100644 --- a/wscript_build +++ b/wscript_build @@ -353,7 +353,7 @@ build_jars () build_python_and_daemonize () build_premium () build_thirdparty_dir() -#build_dependences () +build_dependences () build_console_proxy () build_patches () build_systemvm_patch () diff --git a/wscript_configure b/wscript_configure index 78fc17b8dbb..25933de6dc8 100644 --- a/wscript_configure +++ b/wscript_configure @@ -30,6 +30,7 @@ systemjars = { "commons-pool.jar", "commons-httpclient.jar", "ws-commons-util.jar", + "jnetpcap.jar", ), 'Fedora': ( @@ -255,7 +256,7 @@ msclasspath = [ in_javadir("%s-%s.jar"%(conf.env.PACKAGE,x)) for x in "utils api conf.env.MSCLASSPATH = pathsep.join(msclasspath) # the agent and simulator classpaths point to JARs required to run these two applications -agentclasspath = [ in_javadir("%s-%s.jar"%(conf.env.PACKAGE,x)) for x in "utils api core server server-extras agent console-common console-proxy core-extras".split() ] +agentclasspath = [ in_javadir("%s-%s.jar"%(conf.env.PACKAGE,x)) for x in "utils api core server server-extras agent console-common console-proxy core-extras agent-extras".split() ] conf.env.AGENTCLASSPATH = pathsep.join(agentclasspath) conf.env.AGENTSIMULATORCLASSPATH = pathsep.join(agentclasspath+[in_javadir("%s-agent-simulator.jar"%conf.env.PACKAGE)])