// 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.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.HttpURLConnection; import java.util.ArrayList; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.UUID; import javax.naming.ConfigurationException; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.log4j.Logger; import com.cloud.agent.Agent.ExitStatus; import com.cloud.agent.dao.StorageComponent; import com.cloud.agent.dao.impl.PropertiesStorage; import com.cloud.host.Host; 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.script.Script; public class AgentShell implements IAgentShell { private static final Logger s_logger = Logger.getLogger(AgentShell.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 _host; private String _privateIp; private int _port; private int _proxyPort; private int _workers; private String _guid; 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 AgentShell() { } @Override public Properties getProperties() { return _properties; } @Override public BackoffAlgorithm getBackoffAlgorithm() { return _backoff; } @Override public int getPingRetries() { return _pingRetries; } @Override public String getVersion() { return _version; } @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; } public Map<String, Object> getCmdLineProperties() { 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); } @Override public void upgradeAgent(final String url) { s_logger.info("Updating agent with binary from " + url); synchronized (this) { final Class<?> c = this.getClass(); String path = c.getResource(c.getSimpleName() + ".class") .toExternalForm(); final int begin = path.indexOf(File.separator); int end = path.lastIndexOf("!"); end = path.lastIndexOf(File.separator, end); path = path.substring(begin, end); s_logger.debug("Current binaries reside at " + path); File file = null; try { file = File.createTempFile("agent-", "-" + Long.toString(new Date().getTime())); wget(url, file); } catch (final IOException e) { s_logger.warn( "Exception while downloading agent update package, ", e); throw new CloudRuntimeException("Unable to update from " + url + ", exception:" + e.getMessage(), e); } if (s_logger.isDebugEnabled()) { s_logger.debug("Unzipping " + file.getAbsolutePath() + " to " + path); } final Script unzip = new Script("unzip", 120000, s_logger); unzip.add("-o", "-q"); // overwrite and quiet unzip.add(file.getAbsolutePath()); unzip.add("-d", path); final String result = unzip.execute(); if (result != null) { throw new CloudRuntimeException( "Unable to unzip the retrieved file: " + result); } if (s_logger.isDebugEnabled()) { s_logger.debug("Closing the connection to the management server"); } } if (s_logger.isDebugEnabled()) { s_logger.debug("Exiting to start the new agent."); } System.exit(ExitStatus.Upgrade.value()); } public static void wget(String url, File file) throws IOException { final HttpClient client = new HttpClient(); final GetMethod method = new GetMethod(url); int response; response = client.executeMethod(method); if (response != HttpURLConnection.HTTP_OK) { s_logger.warn("Retrieving from " + url + " gives response code: " + response); throw new CloudRuntimeException("Unable to download from " + url + ". Response code is " + response); } final InputStream is = method.getResponseBodyAsStream(); s_logger.debug("Downloading content into " + file.getAbsolutePath()); final FileOutputStream fos = new FileOutputStream(file); byte[] buffer = new byte[4096]; int len = 0; while ((len = is.read(buffer)) > 0) fos.write(buffer, 0, len); fos.close(); try { is.close(); } catch (IOException e) { s_logger.warn("Exception while closing download stream from " + url + ", ", e); } } 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 = UUID.randomUUID().toString(); } return true; } 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 launchAgent() throws ConfigurationException { String resourceClassNames = getProperty(null, "resource"); s_logger.trace("resource=" + resourceClassNames); if (resourceClassNames != null) { launchAgentFromClassInfo(resourceClassNames); return; } launchAgentFromTypeInfo(); } private boolean needConsoleProxy() { for (Agent agent : _agents) { if (agent.getResource().getType().equals(Host.Type.ConsoleProxy) || agent.getResource().getType().equals(Host.Type.Routing)) return true; } return false; } private int getConsoleProxyPort() { int port = NumbersUtil.parseInt( getProperty(null, "consoleproxy.httpListenPort"), 443); return port; } private void openPortWithIptables(int port) { // TODO } private void launchConsoleProxy() throws ConfigurationException { if (!needConsoleProxy()) { if (s_logger.isInfoEnabled()) s_logger.info("Storage only agent, no need to start console proxy on it"); return; } int port = getConsoleProxyPort(); openPortWithIptables(port); _consoleProxyMain = new Thread(new Runnable() { public void run() { try { Class<?> consoleProxyClazz = Class.forName("com.cloud.consoleproxy.ConsoleProxy"); try { Method method = consoleProxyClazz.getMethod("start", Properties.class); method.invoke(null, _properties); } catch (SecurityException e) { s_logger.error("Unable to launch console proxy due to SecurityException"); System.exit(ExitStatus.Error.value()); } catch (NoSuchMethodException e) { s_logger.error("Unable to launch console proxy due to NoSuchMethodException"); System.exit(ExitStatus.Error.value()); } catch (IllegalArgumentException e) { s_logger.error("Unable to launch console proxy due to IllegalArgumentException"); System.exit(ExitStatus.Error.value()); } catch (IllegalAccessException e) { s_logger.error("Unable to launch console proxy due to IllegalAccessException"); System.exit(ExitStatus.Error.value()); } catch (InvocationTargetException e) { s_logger.error("Unable to launch console proxy due to InvocationTargetException"); System.exit(ExitStatus.Error.value()); } } catch (final ClassNotFoundException e) { s_logger.error("Unable to launch console proxy due to ClassNotFoundException"); System.exit(ExitStatus.Error.value()); } } }, "Console-Proxy-Main"); _consoleProxyMain.setDaemon(true); _consoleProxyMain.start(); } 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 + " due to: " + e.toString()); } catch (final SecurityException e) { throw new ConfigurationException( "Security excetion when loading resource: " + name + " due to: " + e.toString()); } catch (final NoSuchMethodException e) { throw new ConfigurationException( "Method not found excetion when loading resource: " + name + " due to: " + e.toString()); } catch (final IllegalArgumentException e) { throw new ConfigurationException( "Illegal argument excetion when loading resource: " + name + " due to: " + e.toString()); } catch (final InstantiationException e) { throw new ConfigurationException( "Instantiation excetion when loading resource: " + name + " due to: " + e.toString()); } catch (final IllegalAccessException e) { throw new ConfigurationException( "Illegal access exception when loading resource: " + name + " due to: " + e.toString()); } catch (final InvocationTargetException e) { throw new ConfigurationException( "Invocation target exception when loading resource: " + name + " due to: " + e.toString()); } } } 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(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) { if (Boolean.parseBoolean(getProperty(null, "developer"))) { instance = UUID.randomUUID().toString(); } else { 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(pidDir, run); launchAgent(); // // For both KVM & Xen-Server hypervisor, we have switched to // VM-based console proxy solution, disable launching // of console proxy here // // launchConsoleProxy(); // try { while (!_exit) Thread.sleep(1000); } catch (InterruptedException e) { } } 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()); } } public void stop() { _exit = true; if (_consoleProxyMain != null) { _consoleProxyMain.interrupt(); } } public static void main(String[] args) { AgentShell shell = new AgentShell(); Runtime.getRuntime().addShutdownHook(new ShutdownThread(shell)); shell.run(args); } private static class ShutdownThread extends Thread { AgentShell _shell; public ShutdownThread(AgentShell shell) { this._shell = shell; } @Override public void run() { _shell.stop(); } } }