// 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(); } } }