org.opennms.upgrade.implementations.SnmpInterfaceRrdMigratorOnline.java Source code

Java tutorial

Introduction

Here is the source code for org.opennms.upgrade.implementations.SnmpInterfaceRrdMigratorOnline.java

Source

/*******************************************************************************
 * This file is part of OpenNMS(R).
 *
 * Copyright (C) 2013-2014 The OpenNMS Group, Inc.
 * OpenNMS(R) is Copyright (C) 1999-2014 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 Affero 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero 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.upgrade.implementations;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.opennms.core.utils.ConfigFileConstants;
import org.opennms.core.utils.DBUtils;
import org.opennms.netmgt.config.DataCollectionConfigFactory;
import org.opennms.netmgt.config.DefaultDataCollectionConfigDao;
import org.opennms.netmgt.rrd.model.RrdConvertUtils;
import org.opennms.netmgt.rrd.model.v1.RRDv1;
import org.opennms.netmgt.rrd.model.v3.RRDv3;
import org.opennms.upgrade.api.AbstractOnmsUpgrade;
import org.opennms.upgrade.api.OnmsUpgradeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.FileSystemResource;

/**
 * The Class RRD/JRB Migrator for SNMP Interfaces Data (Online Version)
 * 
 * <p>1.12 always add the MAC Address to the snmpinterface table if exist, which
 * is different from the 1.10 behavior. For this reason, some interfaces are going
 * to appear twice, and the data must be merged.</p>
 * 
 * <p>This tool requires that OpenNMS 1.12 is running for a while to be sure that
 * all the MAC addresses have been updated, and the directories already exist.</p>
 * 
 * <p>Issues fixed:</p>
 * <ul>
 * <li>NMS-6056</li>
 * </ul>
 * 
 * @author <a href="mailto:agalue@opennms.org">Alejandro Galue</a> 
 */
public class SnmpInterfaceRrdMigratorOnline extends AbstractOnmsUpgrade {

    private static final Logger LOG = LoggerFactory.getLogger(SnmpInterfaceRrdMigratorOnline.class);

    /** The interfaces to merge. */
    private List<SnmpInterfaceUpgrade> interfacesToMerge;

    /**
     * Instantiates a new SNMP interface RRD migrator online.
     *
     * @throws OnmsUpgradeException the OpenNMS upgrade exception
     */
    public SnmpInterfaceRrdMigratorOnline() throws OnmsUpgradeException {
        super();
    }

    /* (non-Javadoc)
     * @see org.opennms.upgrade.api.OnmsUpgrade#getOrder()
     */
    public int getOrder() {
        return 2;
    }

    /* (non-Javadoc)
     * @see org.opennms.upgrade.api.OnmsUpgrade#getDescription()
     */
    public String getDescription() {
        return "Merge SNMP Interface directories (Online Version): NMS-6056";
    }

    /* (non-Javadoc)
     * @see org.opennms.upgrade.api.OnmsUpgrade#requiresOnmsRunning()
     */
    public boolean requiresOnmsRunning() {
        return true;
    }

    /* (non-Javadoc)
     * @see org.opennms.upgrade.api.OnmsUpgrade#preExecute()
     */
    public void preExecute() throws OnmsUpgradeException {
        printMainSettings();
        if (getRrdExtension() == null) {
            throw new OnmsUpgradeException("Can't find the configured extension for JRB/RRD.");
        }
        try {
            // Manually initialization of the DataCollectionConfigDao to avoid bootstrap Spring Framework and create a new connection pool.
            File cfgFile = ConfigFileConstants.getFile(ConfigFileConstants.DATA_COLLECTION_CONF_FILE_NAME);
            DefaultDataCollectionConfigDao config = new DefaultDataCollectionConfigDao();
            config.setConfigResource(new FileSystemResource(cfgFile));
            config.afterPropertiesSet();
            config.getConfiguredResourceTypes();
            DataCollectionConfigFactory.setInstance(config);
        } catch (Exception e) {
            throw new OnmsUpgradeException("Can't initialize datacollection-config.xml because " + e.getMessage());
        }
        interfacesToMerge = getInterfacesToMerge();
    }

    /* (non-Javadoc)
     * @see org.opennms.upgrade.api.OnmsUpgrade#postExecute()
     */
    public void postExecute() throws OnmsUpgradeException {
        // The idea is to remove the backups only if the current interface was updated. A global post-execute is not necessary.
    }

    /* (non-Javadoc)
     * @see org.opennms.upgrade.api.OnmsUpgrade#rollback()
     */
    @Override
    public void rollback() throws OnmsUpgradeException {
        // The idea is to roll back only if the current interface was not updated. A global roll back is not necessary.
    }

    /* (non-Javadoc)
     * @see org.opennms.upgrade.api.OnmsUpgrade#execute()
     */
    @Override
    public void execute() throws OnmsUpgradeException {
        for (SnmpInterfaceUpgrade intf : interfacesToMerge) {
            try {
                createBackup(intf);
                merge(intf.getOldInterfaceDir(), intf.getNewInterfaceDir());
                removeBackup(intf);
            } catch (Exception e) {
                StringWriter w = new StringWriter();
                PrintWriter p = new PrintWriter(w);
                e.printStackTrace(p);
                log("Error: Can't upgrade %s because %s: %s. Rolling back changes\n", intf, e.getMessage(),
                        w.toString());
                restoreBackup(intf);
            }
        }
    }

    /**
     * Crates a backup of the SNMP interface files.
     *
     * @param intf the SNMP interface
     * @throws OnmsUpgradeException the OpenNMS upgrade exception
     */
    private void createBackup(SnmpInterfaceUpgrade intf) throws OnmsUpgradeException {
        File[] targets = { intf.getOldInterfaceDir(), intf.getNewInterfaceDir() };
        for (File target : targets) {
            if (target.exists()) {
                log("Backing up: %s\n", target);
                zipDir(new File(target.getAbsolutePath() + ZIP_EXT), target);
            }
        }
    }

    /**
     * Restore the backup for a specific SNMP interface
     *
     * @param intf the SNMP interface
     * @throws OnmsUpgradeException the OpenNMS upgrade exception
     */
    private void restoreBackup(SnmpInterfaceUpgrade intf) throws OnmsUpgradeException {
        File[] targets = { intf.getOldInterfaceDir(), intf.getNewInterfaceDir() };
        for (File target : targets) {
            File zip = new File(target.getAbsolutePath() + ZIP_EXT);
            try {
                FileUtils.deleteDirectory(target);
            } catch (IOException e1) {
                log("Warning: can't delete directory %s\n", target);
            }
            if (!target.mkdirs()) {
                LOG.warn("Could not make directory: {}", target.getPath());
            }
            if (zip.exists()) {
                unzipFile(zip, target);
                if (!zip.delete()) {
                    LOG.warn("Could not delete file: {}", zip.getPath());
                }
            }
        }
    }

    /**
     * Removes the backups for a specific SNMP interface
     *
     * @param intf the SNMP interface
     * @throws OnmsUpgradeException the OpenNMS upgrade exception
     */
    private void removeBackup(SnmpInterfaceUpgrade intf) {
        File[] targets = { intf.getOldInterfaceDir(), intf.getNewInterfaceDir() };
        for (File target : targets) {
            File zip = new File(target.getAbsolutePath() + ZIP_EXT);
            if (zip.exists()) {
                log("Removing backup: %s\n", zip);
                if (!zip.delete()) {
                    LOG.warn("Could not delete file: {}", zip.getPath());
                }
            }
        }
    }

    /**
     * Gets the interfaces to merge.
     *
     * @return the list of interfaces to merge
     * @throws OnmsUpgradeException the OpenNMS upgrade exception
     */
    protected List<SnmpInterfaceUpgrade> getInterfacesToMerge() throws OnmsUpgradeException {
        List<SnmpInterfaceUpgrade> interfacesToMerge = new ArrayList<SnmpInterfaceUpgrade>();
        Connection conn = getDbConnection();
        final DBUtils db = new DBUtils(getClass());
        db.watch(conn);
        try {
            Statement st = conn.createStatement();
            db.watch(st);
            String query = "SELECT n.nodeid, n.foreignsource, n.foreignid, i.snmpifdescr, i.snmpifname, i.snmpphysaddr from node n, snmpinterface i where n.nodeid = i.nodeid and i.snmpphysaddr is not null order by n.nodeid, i.snmpifdescr";
            ResultSet rs = st.executeQuery(query);
            db.watch(rs);
            int count = 0;
            while (rs.next()) {
                count++;
                SnmpInterfaceUpgrade intf = new SnmpInterfaceUpgrade(rs, isStoreByForeignSourceEnabled());
                if (intf.shouldMerge()) {
                    interfacesToMerge.add(intf);
                }
                log("  Should merge %s ? %s\n", intf, intf.shouldMerge());
            }
            log("  Found %s of %s SNMP interfaces that require a merge.\n", interfacesToMerge.size(), count);
        } catch (Throwable t) {
            log("Error: can't retrieve the required data from the OpenNMS Database or there were problems while processing them.\n");
            String reason = t.getMessage();
            if (reason == null) {
                reason = "Unknown";
            }
            log("Reason(%s): %s\n", t.getClass().getName(), reason);
            t.printStackTrace(); // TODO This is not elegant, but it helps.
        } finally {
            db.cleanUp();
        }
        return interfacesToMerge;
    }

    /**
     * Merge.
     *
     * @param oldDir the old directory
     * @param newDir the new directory
     * @throws Exception the exception
     */
    protected void merge(File oldDir, File newDir) throws Exception {
        log("Merging data from %s to %s\n", oldDir, newDir);
        if (newDir.exists()) {
            File[] rrdFiles = getFiles(oldDir, getRrdExtension());
            if (rrdFiles == null) {
                log("Warning: there are no %s files on %s\n", getRrdExtension(), oldDir);
            } else {
                for (File source : rrdFiles) {
                    File dest = new File(newDir, source.getName());
                    if (dest.exists()) {
                        if (isRrdToolEnabled()) {
                            mergeRrd(source, dest);
                        } else {
                            mergeJrb(source, dest);
                        }
                    } else {
                        log("  Warning: %s doesn't exist\n", dest);
                    }
                }
            }
            try {
                log("  removing old directory %s\n", oldDir.getName());
                FileUtils.deleteDirectory(oldDir);
            } catch (Exception e) {
                log("  Warning: can't delete old directory because %s", e.getMessage());
            }
        } else {
            try {
                log("  moving %s to %s\n", oldDir.getName(), newDir.getName());
                FileUtils.moveDirectory(oldDir, newDir);
            } catch (Exception e) {
                log("  Warning: can't rename directory because %s", e.getMessage());
            }
        }
    }

    /**
     * Merge RRDs.
     *
     * @param source the source RRD
     * @param dest the destination RRD
     * @throws Exception the exception
     */
    protected void mergeRrd(File source, File dest) throws Exception {
        log("  merging RRD %s into %s\n", source, dest);
        RRDv3 rrdSrc = RrdConvertUtils.dumpRrd(source);
        RRDv3 rrdDst = RrdConvertUtils.dumpRrd(dest);
        if (rrdSrc == null || rrdDst == null) {
            log("  Warning: can't load RRDs (ingoring merge).\n");
            return;
        }
        rrdDst.merge(rrdSrc);
        final File outputFile = new File(dest.getCanonicalPath() + ".merged");
        RrdConvertUtils.restoreRrd(rrdDst, outputFile);
        if (dest.exists()) {
            FileUtils.deleteQuietly(dest);
        }
        FileUtils.moveFile(outputFile, dest);
    }

    /**
     * Merge JRBs.
     *
     * @param source the source JRB
     * @param dest the destination JRB
     * @throws Exception the exception
     */
    protected void mergeJrb(File source, File dest) throws Exception {
        log("  merging JRB %s into %s\n", source, dest);
        RRDv1 rrdSrc = RrdConvertUtils.dumpJrb(source);
        RRDv1 rrdDst = RrdConvertUtils.dumpJrb(dest);
        if (rrdSrc == null || rrdDst == null) {
            log("  Warning: can't load JRBs (ingoring merge).\n");
            return;
        }
        rrdDst.merge(rrdSrc);
        final File outputFile = new File(dest.getCanonicalPath() + ".merged");
        RrdConvertUtils.restoreJrb(rrdDst, outputFile);
        if (dest.exists()) {
            FileUtils.deleteQuietly(dest);
        }
        FileUtils.moveFile(outputFile, dest);
    }

    /**
     * Gets the node directory.
     *
     * @param nodeId the node id
     * @param foreignSource the foreign source
     * @param foreignId the foreign id
     * @return the node directory
     */
    protected File getNodeDirectory(int nodeId, String foreignSource, String foreignId) {
        File dir = new File(DataCollectionConfigFactory.getInstance().getRrdPath(), String.valueOf(nodeId));
        if (Boolean.getBoolean("org.opennms.rrd.storeByForeignSource") && !(foreignSource == null)
                && !(foreignId == null)) {
            File fsDir = new File(DataCollectionConfigFactory.getInstance().getRrdPath(),
                    "fs" + File.separatorChar + foreignSource);
            dir = new File(fsDir, foreignId);
        }
        return dir;
    }

}