/******************************************************************************* * This file is part of OpenNMS(R). * * Copyright (C) 2008-2011 The OpenNMS Group, Inc. * OpenNMS(R) is Copyright (C) 1999-2011 The OpenNMS Group, Inc. * * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc. * * OpenNMS(R) 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 (at your option) any later version. * * OpenNMS(R) 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 OpenNMS(R). If not, see: * http://www.gnu.org/licenses/ * * For more information contact: * OpenNMS(R) Licensing <license@opennms.org> * http://www.opennms.org/ * http://www.opennms.com/ *******************************************************************************/ package org.opennms.netmgt.vmmgr; import java.io.File; import java.io.InputStream; import java.lang.reflect.UndeclaredThrowableException; import java.net.Authenticator; import java.net.ConnectException; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.PasswordAuthentication; import java.net.URL; import org.apache.log4j.PropertyConfigurator; import org.opennms.core.utils.LogUtils; import org.opennms.core.utils.ThreadCategory; import org.opennms.netmgt.config.ServiceConfigFactory; import org.opennms.netmgt.config.service.Argument; import org.opennms.netmgt.config.service.Invoke; import org.opennms.netmgt.config.service.Service; /** * <p> * The Manager is responsible for launching/starting all services in the VM * that it is started for. The Manager operates in two modes, normal and * server * </p> * <p> * normal mode: In the normal mode, the Manager starts all services configured * for its VM in the service-configuration.xml and starts listening for * control events on the 'control-broadcast' JMS topic for stop control * messages for itself * </p> * <p> * server mode: In the server mode, the Manager starts up and listens on the * 'control-broadcast' JMS topic for 'start' control messages for services in * its VM and a stop control message for itself. When a start for a service is * received, it launches only that service and sends a successful 'running' or * an 'error' response to the Controller * </p> * <p> * <strong>Note: </strong>The Manager is NOT intelligent - if it receives a * stop control event, it will exit - does not check to see if the services * its started are all stopped * <p> * * @author <a href="mailto:weave@oculan.com">Brian Weaver</a> * @author <a href="mailto:sowmya@opennms.org">Sowmya Nataraj</a> */ public class Controller { private static final String JMX_HTTP_ADAPTER_NAME = ":Name=HttpAdaptorMgmt"; /** * Default invoker URL. This is used for getting status information from a * running OpenNMS instance. */ public static final String DEFAULT_INVOKER_URL = "http://127.0.0.1:8181/invoke?objectname=OpenNMS%3AName=Manager"; /** * The log4j category used to log debug messsages and statements. */ private static final String LOG4J_CATEGORY = "OpenNMS.Manager"; /** * Default read timeout for HTTP requests in milliseconds. * The default is zero which means wait forever. */ private static final int DEFAULT_HTTP_REQUEST_READ_TIMEOUT = 0; private boolean m_verbose = false; private String m_invokeUrl = DEFAULT_INVOKER_URL; private Authenticator m_authenticator; private int m_httpRequestReadTimeout = DEFAULT_HTTP_REQUEST_READ_TIMEOUT; /** * <p>Constructor for Controller.</p> */ public Controller() { } /** * <p>main</p> * * @param argv an array of {@link java.lang.String} objects. */ public static void main(String[] argv) { configureLog4j(); ThreadCategory.setPrefix(LOG4J_CATEGORY); Controller c = new Controller(); for (int i = 0; i < argv.length; i++) { if (argv[i].equals("-h")) { System.out.println("Usage: java org.opennms.netmgt.vmmgr.Controller " + "[<options>] <command>"); System.out.println("Accepted options:"); System.out.println(" -t <timeout> HTTP connection timeout in seconds. Defaults to 30."); System.out.println(" -u <URL> Alternate invoker URL."); System.out.println(" -v Verbose mode."); System.out.println(""); System.out.println("Accepted commands: start, stop, status"); System.out.println(""); System.out.println("The default invoker URL is: " + DEFAULT_INVOKER_URL); System.exit(0); } else if (argv[i].equals("-t")) { c.setHttpRequestReadTimeout(Integer.parseInt(argv[i + 1]) * 1000); i++; } else if (argv[i].equals("-v")) { c.setVerbose(true); } else if (argv[i].equals("-u")) { c.setInvokeUrl(argv[i + 1]); i++; } else if (i != (argv.length - 1)) { System.err.println("Invalid command-line option: \"" + argv[i] + "\". Use \"-h\" option for help."); System.exit(1); } else { break; } } if (argv.length == 0) { System.err.println("You must specify a command. Use \"-h\"" + " option for help"); System.exit(1); } c.setAuthenticator(c.createAuthenticatorUsingConfigCredentials()); String command = argv[argv.length - 1]; if ("start".equals(command)) { c.start(); } else if ("stop".equals(command)) { System.exit(c.stop()); } else if ("status".equals(command)) { System.exit(c.status()); } else if ("check".equals(command)) { System.exit(c.check()); } else if ("exit".equals(command)) { System.exit(c.exit()); } else { System.err.println("Invalid command \"" + command + "\"."); System.err.println("Use \"-h\" option for help."); System.exit(1); } } private static void configureLog4j() { File homeDir = new File(System.getProperty("opennms.home")); File etcDir = new File(homeDir, "etc"); File controllerProperties = new File(etcDir, "log4j-controller.properties"); PropertyConfigurator.configure(controllerProperties.getAbsolutePath()); } /** * Start the OpenNMS daemon. Never returns. */ public void start() { Starter starter = new Starter(); starter.startDaemon(); } /** * <p>stop</p> * * @return a int. */ public int stop() { return invokeOperation("stop"); } /** * <p>status</p> * * @return a int. */ public int status() { Authenticator.setDefault(getAuthenticator()); StatusGetter statusGetter = new StatusGetter(); statusGetter.setVerbose(isVerbose()); String url = getInvokeUrl() + "&operation=status"; try { statusGetter.setInvokeURL(new URL(url)); } catch (MalformedURLException e) { String message = "Error creating URL object for invoke URL: '" + url + "'"; System.err.println(message); LogUtils.errorf(this, e, message); } try { statusGetter.queryStatus(); } catch (Throwable t) { String message = "Error invoking status command"; System.err.println(message); LogUtils.errorf(this, t, message); return 1; } switch (statusGetter.getStatus()) { case NOT_RUNNING: case CONNECTION_REFUSED: return 3; // According to LSB: 3 - service not running case PARTIALLY_RUNNING: /* * According to LSB: reserved for application So, I say * 160 - partially running */ return 160; case RUNNING: return 0; // everything should be good and running default: LogUtils.errorf(this, "Unknown status returned from statusGetter.getStatus(): %s", statusGetter.getStatus()); return 1; } } /** * <p>check</p> * * @return a int. */ public int check() { try { new DatabaseChecker().check(); } catch (final Throwable t) { LogUtils.errorf(this, t, "error invoking check command"); return 1; } return 0; } /** * <p>exit</p> * * @return a int. */ public int exit() { return invokeOperation("doSystemExit"); } int invokeOperation(String operation) { Authenticator.setDefault(getAuthenticator()); String urlString = getInvokeUrl() + "&operation=" + operation; try { URL invoke = new URL(urlString); HttpURLConnection connection = (HttpURLConnection) invoke.openConnection(); connection.setReadTimeout(getHttpRequestReadTimeout()); InputStream in = connection.getInputStream(); int ch; while ((ch = in.read()) != -1) { System.out.write((char) ch); } in.close(); System.out.println(""); System.out.flush(); } catch (final ConnectException e) { LogUtils.errorf(this, e, "error when attempting to fetch URL \"%s\"", urlString); if (isVerbose()) { System.out.println(e.getMessage() + " when attempting to fetch URL \"" + urlString + "\""); } return 1; } catch (final Throwable t) { LogUtils.errorf(this, t, "error invoking %s operation", operation); System.out.println("error invoking " + operation + " operation"); return 1; } return 0; } /* * Create an Authenticator so that we can provide authentication, if * needed, when go to connect to the URL */ Authenticator createAuthenticatorUsingConfigCredentials() { Service service = getConfiguredService(JMX_HTTP_ADAPTER_NAME); if (service == null) { // Didn't find the service we were looking for LogUtils.warnf(this, "Could not find configured service for '%s'", JMX_HTTP_ADAPTER_NAME); return null; } org.opennms.netmgt.config.service.Attribute[] attribs = service.getAttribute(); if (attribs == null) { // the AuthenticationMethod is not set, so no authentication return null; } boolean usingBasic = false; for (org.opennms.netmgt.config.service.Attribute attrib : attribs) { if (attrib.getName().equals("AuthenticationMethod")) { if (!attrib.getValue().getContent().equals("basic")) { LogUtils.errorf(this, "AuthenticationMethod is \"%s\", but only \"basic\" is supported", attrib.getValue()); return null; } usingBasic = true; break; } } if (!usingBasic) { // AuthenticationMethod is not set to basic, so no authentication return null; } Invoke[] invokes = service.getInvoke(); if (invokes == null) { // No username or password could be set return null; } String username = null; String password = null; for (Invoke invoke : invokes) { if (invoke.getMethod().equals("addAuthorization")) { Argument[] args = invoke.getArgument(); if (args != null && args.length == 2 && args[0].getContent().equals("manager")) { username = args[0].getContent(); password = args[1].getContent(); break; } } } if (username == null || password == null) { // Didn't find a username or password return null; } final String username_f = username; final String password_f = password; return new Authenticator() { protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(username_f, password_f.toCharArray()); } }; } private ServiceConfigFactory getServiceConfigFactory() { try { ServiceConfigFactory.init(); return ServiceConfigFactory.getInstance(); } catch (Throwable t) { throw new UndeclaredThrowableException(t); } } private Service getConfiguredService(String serviceName) { ServiceConfigFactory sfact = getServiceConfigFactory(); Service[] services = sfact.getServices(); for (Service service : services) { if (service.getName().equals(serviceName)) { return service; } } return null; } /** * <p>isVerbose</p> * * @return a boolean. */ public boolean isVerbose() { return m_verbose; } /** * <p>setVerbose</p> * * @param verbose a boolean. */ public void setVerbose(boolean verbose) { m_verbose = verbose; } /** * <p>getInvokeUrl</p> * * @return a {@link java.lang.String} object. */ public String getInvokeUrl() { return m_invokeUrl; } /** * <p>setInvokeUrl</p> * * @param invokerUrl a {@link java.lang.String} object. */ public void setInvokeUrl(String invokerUrl) { m_invokeUrl = invokerUrl; } /** * <p>getAuthenticator</p> * * @return a {@link java.net.Authenticator} object. */ public Authenticator getAuthenticator() { return m_authenticator; } /** * <p>setAuthenticator</p> * * @param authenticator a {@link java.net.Authenticator} object. */ public void setAuthenticator(Authenticator authenticator) { m_authenticator = authenticator; } /** * <p>getHttpRequestReadTimeout</p> * * @return a int. */ public int getHttpRequestReadTimeout() { return m_httpRequestReadTimeout; } /** * <p>setHttpRequestReadTimeout</p> * * @param httpRequestReadTimeout a int. */ public void setHttpRequestReadTimeout(int httpRequestReadTimeout) { m_httpRequestReadTimeout = httpRequestReadTimeout; } }