// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with 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. package com.cloud.agent; import java.io.File; 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.Collections; 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.daemon.Daemon; import org.apache.commons.daemon.DaemonContext; import org.apache.commons.daemon.DaemonInitException; import org.apache.commons.lang.math.NumberUtils; import org.apache.log4j.Logger; import org.apache.log4j.xml.DOMConfigurator; import com.cloud.agent.Agent.ExitStatus; import com.cloud.agent.dao.StorageComponent; import com.cloud.agent.dao.impl.PropertiesStorage; import com.cloud.resource.ServerResource; import com.cloud.utils.LogUtils; 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.exception.CloudRuntimeException; public class AgentShell implements IAgentShell, Daemon { 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 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; } @Override 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); } void loadProperties() throws ConfigurationException { final File file = PropertiesUtil.findConfigFile("agent.properties"); if (null == file) { throw new ConfigurationException("Unable to find agent.properties."); } s_logger.info("agent.properties found at " + file.getAbsolutePath()); try { PropertiesUtil.loadFromFile(_properties, 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 (String param : args) { final String[] tokens = param.split("="); if (tokens.length != 2) { System.out.println("Invalid Parameter: " + param); continue; } final String paramName = tokens[0]; final String paramValue = tokens[1]; // save command line properties _cmdLineProperties.put(paramName, paramValue); if (paramName.equalsIgnoreCase("port")) { port = paramValue; } else if (paramName.equalsIgnoreCase("threads") || paramName.equalsIgnoreCase("workers")) { workers = paramValue; } else if (paramName.equalsIgnoreCase("host")) { host = paramValue; } else if (paramName.equalsIgnoreCase("zone")) { zone = paramValue; } else if (paramName.equalsIgnoreCase("pod")) { pod = paramValue; } else if (paramName.equalsIgnoreCase("guid")) { guid = paramValue; } else if (paramName.equalsIgnoreCase("eth1ip")) { _privateIp = paramValue; } } if (port == null) { port = getProperty(null, "port"); } _port = NumberUtils.toInt(port, 8250); _proxyPort = NumberUtils.toInt(getProperty(null, "consoleproxy.httpListenPort"), 443); if (workers == null) { workers = getProperty(null, "workers"); } _workers = NumberUtils.toInt(workers, 5); if (_workers <= 0) { _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(); _properties.setProperty("guid", _guid); } return true; } @Override public void init(DaemonContext dc) throws DaemonInitException { s_logger.debug("Initializing AgentShell from JSVC"); try { init(dc.getArguments()); } catch (ConfigurationException ex) { throw new DaemonInitException("Initialization failed", ex); } } public void init(String[] args) throws ConfigurationException { // PropertiesUtil is used both in management server and agent packages, // it searches path under class path and common J2EE containers // For KVM agent, do it specially here File file = new File("/etc/cloudstack/agent/log4j-cloud.xml"); if (!file.exists()) { file = PropertiesUtil.findConfigFile("log4j-cloud.xml"); } if (null != file) { DOMConfigurator.configureAndWatch(file.getAbsolutePath()); s_logger.info("Agent started"); } else { s_logger.error("Could not start the Agent because the absolut path of the \"log4j-cloud.xml\" file cannot be determined."); } 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); loadProperties(); parseCommand(args); if (s_logger.isDebugEnabled()) { List<String> properties = Collections.list((Enumeration<String>)_properties.propertyNames()); for (String property : properties) { s_logger.debug("Found property: " + property); } } 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()); } 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 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++; } @Override public void start() { try { /* By default we only search for log4j.xml */ LogUtils.initLog4j("log4j-cloud.xml"); boolean ipv6disabled = false; String ipv6 = getProperty(null, "ipv6disabled"); if (ipv6 != null) { ipv6disabled = Boolean.parseBoolean(ipv6); } boolean ipv6prefer = false; String ipv6p = getProperty(null, "ipv6prefer"); if (ipv6p != null) { ipv6prefer = Boolean.parseBoolean(ipv6p); } if (ipv6disabled) { s_logger.info("Preferring IPv4 address family for agent connection"); System.setProperty("java.net.preferIPv4Stack", "true"); if (ipv6prefer) { s_logger.info("ipv6prefer is set to true, but ipv6disabled is false. Not preferring IPv6 for agent connection"); } } else { if (ipv6prefer) { s_logger.info("Preferring IPv6 address family for agent connection"); System.setProperty("java.net.preferIPv6Addresses", "true"); } else { s_logger.info("Using default Java settings for IPv6 preference for agent connection"); } } 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(); try { while (!_exit) Thread.sleep(1000); } catch (InterruptedException e) { s_logger.debug("[ignored] AgentShell was interupted."); } } 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 void stop() { _exit = true; } @Override public void destroy() { } public static void main(String[] args) { try { s_logger.debug("Initializing AgentShell from main"); AgentShell shell = new AgentShell(); shell.init(args); shell.start(); } catch (ConfigurationException e) { System.out.println(e.getMessage()); } } }