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 extends ResourceLimit> 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 extends ResourceLimit> 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 extends AsyncJob> 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 extends AsyncJob> 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