mirror of https://github.com/apache/cloudstack.git
522 lines
17 KiB
Java
522 lines
17 KiB
Java
// Copyright 2012 Citrix Systems, Inc. Licensed under the
|
|
// Apache License, Version 2.0 (the "License"); you may not use this
|
|
// file except in compliance with the License. Citrix Systems, Inc.
|
|
// reserves all rights not expressly granted by the License.
|
|
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
// Automatically generated by addcopyright.py at 04/03/2012
|
|
package com.cloud.agent;
|
|
|
|
import java.io.File;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.IOException;
|
|
import java.lang.reflect.Constructor;
|
|
import java.lang.reflect.InvocationTargetException;
|
|
import java.util.ArrayList;
|
|
import java.util.Enumeration;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Properties;
|
|
|
|
import javax.naming.ConfigurationException;
|
|
|
|
import org.apache.log4j.Logger;
|
|
|
|
import com.cloud.agent.Agent.ExitStatus;
|
|
import com.cloud.agent.api.Command;
|
|
import com.cloud.agent.api.StartupVMMAgentCommand;
|
|
import com.cloud.agent.dao.StorageComponent;
|
|
import com.cloud.agent.dao.impl.PropertiesStorage;
|
|
import com.cloud.agent.transport.Request;
|
|
import com.cloud.resource.ServerResource;
|
|
import com.cloud.utils.NumbersUtil;
|
|
import com.cloud.utils.ProcessUtil;
|
|
import com.cloud.utils.PropertiesUtil;
|
|
import com.cloud.utils.backoff.BackoffAlgorithm;
|
|
import com.cloud.utils.backoff.impl.ConstantTimeBackoff;
|
|
import com.cloud.utils.component.Adapters;
|
|
import com.cloud.utils.component.ComponentLocator;
|
|
import com.cloud.utils.exception.CloudRuntimeException;
|
|
import com.cloud.utils.net.MacAddress;
|
|
import com.cloud.utils.nio.HandlerFactory;
|
|
import com.cloud.utils.nio.Link;
|
|
import com.cloud.utils.nio.NioServer;
|
|
import com.cloud.utils.nio.Task;
|
|
import com.cloud.utils.nio.Task.Type;
|
|
|
|
/**
|
|
* Implementation of agent shell to run the agents on System Center Virtual Machine manager
|
|
**/
|
|
|
|
public class VmmAgentShell implements IAgentShell, HandlerFactory {
|
|
|
|
private static final Logger s_logger = Logger.getLogger(VmmAgentShell.class.getName());
|
|
private final Properties _properties = new Properties();
|
|
private final Map<String, Object> _cmdLineProperties = new HashMap<String, Object>();
|
|
private StorageComponent _storage;
|
|
private BackoffAlgorithm _backoff;
|
|
private String _version;
|
|
private String _zone;
|
|
private String _pod;
|
|
private String _cluster;
|
|
private String _host;
|
|
private String _privateIp;
|
|
private int _port;
|
|
private int _proxyPort;
|
|
private int _workers;
|
|
private String _guid;
|
|
static private NioServer _connection;
|
|
static private int _listenerPort=9000;
|
|
private int _nextAgentId = 1;
|
|
private volatile boolean _exit = false;
|
|
private int _pingRetries;
|
|
private Thread _consoleProxyMain = null;
|
|
private final List<Agent> _agents = new ArrayList<Agent>();
|
|
|
|
public VmmAgentShell() {
|
|
}
|
|
|
|
@Override
|
|
public Properties getProperties() {
|
|
return _properties;
|
|
}
|
|
|
|
@Override
|
|
public BackoffAlgorithm getBackoffAlgorithm() {
|
|
return _backoff;
|
|
}
|
|
|
|
@Override
|
|
public int getPingRetries() {
|
|
return _pingRetries;
|
|
}
|
|
|
|
@Override
|
|
public String getZone() {
|
|
return _zone;
|
|
}
|
|
|
|
@Override
|
|
public String getPod() {
|
|
return _pod;
|
|
}
|
|
|
|
@Override
|
|
public String getHost() {
|
|
return _host;
|
|
}
|
|
|
|
@Override
|
|
public String getPrivateIp() {
|
|
return _privateIp;
|
|
}
|
|
|
|
@Override
|
|
public int getPort() {
|
|
return _port;
|
|
}
|
|
|
|
@Override
|
|
public int getProxyPort() {
|
|
return _proxyPort;
|
|
}
|
|
|
|
@Override
|
|
public int getWorkers() {
|
|
return _workers;
|
|
}
|
|
|
|
@Override
|
|
public String getGuid() {
|
|
return _guid;
|
|
}
|
|
|
|
@Override
|
|
public void upgradeAgent(String url) {
|
|
// TODO Auto-generated method stub
|
|
|
|
}
|
|
|
|
@Override
|
|
public String getVersion() {
|
|
return _version;
|
|
}
|
|
|
|
@Override
|
|
public Map<String, Object> getCmdLineProperties() {
|
|
// TODO Auto-generated method stub
|
|
return _cmdLineProperties;
|
|
}
|
|
|
|
public String getProperty(String prefix, String name) {
|
|
if(prefix != null)
|
|
return _properties.getProperty(prefix + "." + name);
|
|
|
|
return _properties.getProperty(name);
|
|
}
|
|
|
|
@Override
|
|
public String getPersistentProperty(String prefix, String name) {
|
|
if(prefix != null)
|
|
return _storage.get(prefix + "." + name);
|
|
return _storage.get(name);
|
|
}
|
|
|
|
@Override
|
|
public void setPersistentProperty(String prefix, String name, String value) {
|
|
if(prefix != null)
|
|
_storage.persist(prefix + "." + name, value);
|
|
else
|
|
_storage.persist(name, value);
|
|
}
|
|
|
|
private void loadProperties() throws ConfigurationException {
|
|
final File file = PropertiesUtil.findConfigFile("agent.properties");
|
|
if (file == null) {
|
|
throw new ConfigurationException("Unable to find agent.properties.");
|
|
}
|
|
|
|
s_logger.info("agent.properties found at " + file.getAbsolutePath());
|
|
|
|
try {
|
|
_properties.load(new FileInputStream(file));
|
|
} 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);
|
|
}
|
|
}
|
|
|
|
protected boolean parseCommand(final String[] args) throws ConfigurationException {
|
|
String host = null;
|
|
String workers = null;
|
|
String port = null;
|
|
String zone = null;
|
|
String pod = null;
|
|
String guid = null;
|
|
for (int i = 0; i < args.length; i++) {
|
|
final String[] tokens = args[i].split("=");
|
|
if (tokens.length != 2) {
|
|
System.out.println("Invalid Parameter: " + args[i]);
|
|
continue;
|
|
}
|
|
|
|
// save command line properties
|
|
_cmdLineProperties.put(tokens[0], tokens[1]);
|
|
|
|
if (tokens[0].equalsIgnoreCase("port")) {
|
|
port = tokens[1];
|
|
} else if (tokens[0].equalsIgnoreCase("threads")) {
|
|
workers = tokens[1];
|
|
} else if (tokens[0].equalsIgnoreCase("host")) {
|
|
host = tokens[1];
|
|
} else if(tokens[0].equalsIgnoreCase("zone")) {
|
|
zone = tokens[1];
|
|
} else if(tokens[0].equalsIgnoreCase("pod")) {
|
|
pod = tokens[1];
|
|
} else if(tokens[0].equalsIgnoreCase("guid")) {
|
|
guid = tokens[1];
|
|
} else if(tokens[0].equalsIgnoreCase("eth1ip")) {
|
|
_privateIp = tokens[1];
|
|
}
|
|
}
|
|
|
|
if (port == null) {
|
|
port = getProperty(null, "port");
|
|
}
|
|
|
|
_port = NumbersUtil.parseInt(port, 8250);
|
|
|
|
_proxyPort = NumbersUtil.parseInt(getProperty(null, "consoleproxy.httpListenPort"), 443);
|
|
|
|
if (workers == null) {
|
|
workers = getProperty(null, "workers");
|
|
}
|
|
|
|
_workers = NumbersUtil.parseInt(workers, 5);
|
|
|
|
if (host == null) {
|
|
host = getProperty(null, "host");
|
|
}
|
|
|
|
if (host == null) {
|
|
host = "localhost";
|
|
}
|
|
_host = host;
|
|
|
|
if(zone != null)
|
|
_zone = zone;
|
|
else
|
|
_zone = getProperty(null, "zone");
|
|
if (_zone == null || (_zone.startsWith("@") && _zone.endsWith("@"))) {
|
|
_zone = "default";
|
|
}
|
|
|
|
if(pod != null)
|
|
_pod = pod;
|
|
else
|
|
_pod = getProperty(null, "pod");
|
|
if (_pod == null || (_pod.startsWith("@") && _pod.endsWith("@"))) {
|
|
_pod = "default";
|
|
}
|
|
|
|
if (_host == null || (_host.startsWith("@") && _host.endsWith("@"))) {
|
|
throw new ConfigurationException("Host is not configured correctly: " + _host);
|
|
}
|
|
|
|
final String retries = getProperty(null, "ping.retries");
|
|
_pingRetries = NumbersUtil.parseInt(retries, 5);
|
|
|
|
String value = getProperty(null, "developer");
|
|
boolean developer = Boolean.parseBoolean(value);
|
|
|
|
if(guid != null)
|
|
_guid = guid;
|
|
else
|
|
_guid = getProperty(null, "guid");
|
|
if (_guid == null) {
|
|
if (!developer) {
|
|
throw new ConfigurationException("Unable to find the guid");
|
|
}
|
|
_guid = MacAddress.getMacAddress().toString(":");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private void launchAgentFromTypeInfo() throws ConfigurationException {
|
|
String typeInfo = getProperty(null, "type");
|
|
if (typeInfo == null) {
|
|
s_logger.error("Unable to retrieve the type");
|
|
throw new ConfigurationException("Unable to retrieve the type of this agent.");
|
|
}
|
|
s_logger.trace("Launching agent based on type=" + typeInfo);
|
|
}
|
|
|
|
private void launchAgent() throws ConfigurationException {
|
|
String resourceClassNames = getProperty(null, "resource");
|
|
s_logger.trace("resource=" + resourceClassNames);
|
|
if(resourceClassNames != null) {
|
|
launchAgentFromClassInfo(resourceClassNames);
|
|
return;
|
|
}
|
|
|
|
launchAgentFromTypeInfo();
|
|
}
|
|
|
|
private void init(String[] args) throws ConfigurationException{
|
|
|
|
final ComponentLocator locator = ComponentLocator.getLocator("agent");
|
|
|
|
final Class<?> c = this.getClass();
|
|
_version = c.getPackage().getImplementationVersion();
|
|
if (_version == null) {
|
|
throw new CloudRuntimeException("Unable to find the implementation version of this agent");
|
|
}
|
|
s_logger.info("Implementation Version is " + _version);
|
|
|
|
parseCommand(args);
|
|
|
|
_storage = locator.getManager(StorageComponent.class);
|
|
if (_storage == null) {
|
|
s_logger.info("Defaulting to using properties file for storage");
|
|
_storage = new PropertiesStorage();
|
|
_storage.configure("Storage", new HashMap<String, Object>());
|
|
}
|
|
|
|
// merge with properties from command line to let resource access command line parameters
|
|
for(Map.Entry<String, Object> cmdLineProp : getCmdLineProperties().entrySet()) {
|
|
_properties.put(cmdLineProp.getKey(), cmdLineProp.getValue());
|
|
}
|
|
|
|
final Adapters adapters = locator.getAdapters(BackoffAlgorithm.class);
|
|
final Enumeration en = adapters.enumeration();
|
|
while (en.hasMoreElements()) {
|
|
_backoff = (BackoffAlgorithm)en.nextElement();
|
|
break;
|
|
}
|
|
if (en.hasMoreElements()) {
|
|
s_logger.info("More than one backoff algorithm specified. Using the first one ");
|
|
}
|
|
|
|
if (_backoff == null) {
|
|
s_logger.info("Defaulting to the constant time backoff algorithm");
|
|
_backoff = new ConstantTimeBackoff();
|
|
_backoff.configure("ConstantTimeBackoff", new HashMap<String, Object>());
|
|
}
|
|
}
|
|
|
|
private void launchAgentFromClassInfo(String resourceClassNames) throws ConfigurationException {
|
|
String[] names = resourceClassNames.split("\\|");
|
|
for(String name: names) {
|
|
Class<?> impl;
|
|
try {
|
|
impl = Class.forName(name);
|
|
final Constructor<?> constructor = impl.getDeclaredConstructor();
|
|
constructor.setAccessible(true);
|
|
ServerResource resource = (ServerResource)constructor.newInstance();
|
|
launchAgent(getNextAgentId(), resource);
|
|
} catch (final ClassNotFoundException e) {
|
|
throw new ConfigurationException("Resource class not found: " + name);
|
|
} catch (final SecurityException e) {
|
|
throw new ConfigurationException("Security excetion when loading resource: " + name);
|
|
} catch (final NoSuchMethodException e) {
|
|
throw new ConfigurationException("Method not found excetion when loading resource: " + name);
|
|
} catch (final IllegalArgumentException e) {
|
|
throw new ConfigurationException("Illegal argument excetion when loading resource: " + name);
|
|
} catch (final InstantiationException e) {
|
|
throw new ConfigurationException("Instantiation excetion when loading resource: " + name);
|
|
} catch (final IllegalAccessException e) {
|
|
throw new ConfigurationException("Illegal access exception when loading resource: " + name);
|
|
} catch (final InvocationTargetException e) {
|
|
throw new ConfigurationException("Invocation target exception when loading resource: " + name);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void launchAgent(int localAgentId, ServerResource resource) throws ConfigurationException {
|
|
// we don't track agent after it is launched for now
|
|
Agent agent = new Agent(this, localAgentId, resource);
|
|
_agents.add(agent);
|
|
agent.start();
|
|
}
|
|
|
|
public synchronized int getNextAgentId() {
|
|
return _nextAgentId++;
|
|
}
|
|
|
|
private void run(String[] args) {
|
|
|
|
try {
|
|
System.setProperty("java.net.preferIPv4Stack","true");
|
|
loadProperties();
|
|
init(args);
|
|
|
|
String instance = getProperty(null, "instance");
|
|
if (instance == null) {
|
|
instance = "";
|
|
} else {
|
|
instance += ".";
|
|
}
|
|
|
|
// TODO need to do this check. For Agentshell running on windows needs different approach
|
|
//final String run = "agent." + instance + "pid";
|
|
//s_logger.debug("Checking to see if " + run + "exists.");
|
|
//ProcessUtil.pidCheck(run);
|
|
|
|
|
|
// TODO: For Hyper-V agent.properties need to be revamped to support multiple agents
|
|
// corresponding to multiple clusters but running on a SCVMM host
|
|
|
|
// read the persistent storage and launch the agents
|
|
//launchAgent();
|
|
|
|
// FIXME get rid of this approach of agent listening for boot strap commands from the management server
|
|
|
|
// now listen for bootstrap request from the management server and launch agents
|
|
_connection = new NioServer("VmmAgentShell", _listenerPort, 1, this);
|
|
_connection.start();
|
|
s_logger.info("SCVMM agent is listening on port " +_listenerPort + " for bootstrap command from management server");
|
|
while(_connection.isRunning());
|
|
} catch(final ConfigurationException e) {
|
|
s_logger.error("Unable to start agent: " + e.getMessage());
|
|
System.out.println("Unable to start agent: " + e.getMessage());
|
|
System.exit(ExitStatus.Configuration.value());
|
|
} catch (final Exception e) {
|
|
s_logger.error("Unable to start agent: ", e);
|
|
System.out.println("Unable to start agent: " + e.getMessage());
|
|
System.exit(ExitStatus.Error.value());
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Task create(com.cloud.utils.nio.Task.Type type, Link link,
|
|
byte[] data) {
|
|
return new AgentBootStrapHandler(type, link, data);
|
|
}
|
|
|
|
public void stop() {
|
|
_exit = true;
|
|
if(_consoleProxyMain != null) {
|
|
_consoleProxyMain.interrupt();
|
|
}
|
|
}
|
|
|
|
public static void main(String[] args) {
|
|
|
|
VmmAgentShell shell = new VmmAgentShell();
|
|
Runtime.getRuntime().addShutdownHook(new ShutdownThread(shell));
|
|
shell.run(args);
|
|
}
|
|
|
|
// class to handle the bootstrap command from the management server
|
|
private class AgentBootStrapHandler extends Task {
|
|
|
|
public AgentBootStrapHandler(Task.Type type, Link link, byte[] data) {
|
|
super(type, link, data);
|
|
}
|
|
|
|
@Override
|
|
protected void doTask(Task task) throws Exception {
|
|
final Type type = task.getType();
|
|
s_logger.info("recieved task of type "+ type.toString() +" to handle in BootStrapTakHandler");
|
|
if (type == Task.Type.DATA)
|
|
{
|
|
final byte[] data = task.getData();
|
|
final Request request = Request.parse(data);
|
|
final Command cmd = request.getCommand();
|
|
|
|
if (cmd instanceof StartupVMMAgentCommand) {
|
|
|
|
StartupVMMAgentCommand vmmCmd = (StartupVMMAgentCommand) cmd;
|
|
|
|
_zone = Long.toString(vmmCmd.getDataCenter());
|
|
_cmdLineProperties.put("zone", _zone);
|
|
|
|
_pod = Long.toString(vmmCmd.getPod());
|
|
_cmdLineProperties.put("pod", _pod);
|
|
|
|
_cluster = vmmCmd.getClusterName();
|
|
_cmdLineProperties.put("cluster", _cluster);
|
|
|
|
_guid = vmmCmd.getGuid();
|
|
_cmdLineProperties.put("guid", _guid);
|
|
|
|
_host = vmmCmd.getManagementServerIP();
|
|
_port = NumbersUtil.parseInt(vmmCmd.getport(), 8250);
|
|
|
|
s_logger.info("Recieved boot strap command from management server with parameters " +
|
|
" Zone:"+ _zone + " "+
|
|
" Cluster:"+ _cluster + " "+
|
|
" pod:"+_pod + " "+
|
|
" host:"+ _host +" "+
|
|
" port:"+_port);
|
|
|
|
launchAgentFromClassInfo("com.cloud.hypervisor.hyperv.resource.HypervResource");
|
|
|
|
// TODO: persist the info in agent.properties for agent restarts
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static class ShutdownThread extends Thread {
|
|
VmmAgentShell _shell;
|
|
public ShutdownThread(VmmAgentShell shell) {
|
|
this._shell = shell;
|
|
}
|
|
|
|
@Override
|
|
public void run() {
|
|
_shell.stop();
|
|
}
|
|
}
|
|
|
|
} |