<commit_before>/*
 * This file is part of the OpenNMS(R) Application.
 *
 * OpenNMS(R) is Copyright (C) 2008 The OpenNMS Group, Inc.  All rights reserved.
 * OpenNMS(R) is a derivative work, containing both original code, included code and modified
 * code that was published under the GNU General Public License. Copyrights for modified
 * and included code are below.
 *
 * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
 *
 * Modifications:
 * 
 * Created: December 16, 2008
 *
 * Copyright (C) 2008 The OpenNMS Group, Inc.  All rights reserved.
 *
 * This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * For more information contact:
 *      OpenNMS Licensing       <<EMAIL>>
 *      http://www.opennms.org/
 *      http://www.opennms.com/
 */
package org.opennms.netmgt.provision;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Category;
import org.opennms.core.utils.LogUtils;
import org.opennms.core.utils.ThreadCategory;
import org.opennms.netmgt.EventConstants;
import org.opennms.netmgt.config.RWSConfig;
import org.opennms.netmgt.config.RancidAdapterConfig;
import org.opennms.netmgt.config.RancidAdapterConfigFactory;
import org.opennms.netmgt.dao.NodeDao;
import org.opennms.netmgt.model.OnmsAssetRecord;
import org.opennms.netmgt.model.OnmsCategory;
import org.opennms.netmgt.model.OnmsIpInterface;
import org.opennms.netmgt.model.OnmsNode;
import org.opennms.netmgt.model.events.EventBuilder;
import org.opennms.netmgt.model.events.EventForwarder;
import org.opennms.netmgt.model.events.annotations.EventHandler;
import org.opennms.netmgt.model.events.annotations.EventListener;
import org.opennms.netmgt.xml.event.Event;
import org.opennms.netmgt.xml.event.Parm;
import org.opennms.rancid.ConnectionProperties;
import org.opennms.rancid.RWSClientApi;
import org.opennms.rancid.RancidApiException;
import org.opennms.rancid.RancidNode;
import org.opennms.rancid.RancidNodeAuthentication;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.Assert;

/**
 * A Rancid provisioning adapter for integration with OpenNMS Provisoning daemon API.
 * 
 * @author <a href="mailto:<EMAIL>"><NAME></a>
 * @author <a href="mailto:<EMAIL>"><NAME></a>
 *
 */

@EventListener(name="RancidProvisioningAdapter")
public class RancidProvisioningAdapter extends SimpleQueuedProvisioningAdapter implements InitializingBean {
    
    private NodeDao m_nodeDao;
    private volatile EventForwarder m_eventForwarder;

    private RWSConfig m_rwsConfig;
    private RancidAdapterConfig m_rancidAdapterConfig;
    private ConnectionProperties m_cp;
    
    private List<String> m_rancid_categories;
    
    private TransactionTemplate m_template;
    
    public TransactionTemplate getTemplate() {
        return m_template;
    }
    public void setTemplate(TransactionTemplate template) {
        m_template = template;
    }

    private static final String MESSAGE_PREFIX = "Rancid provisioning failed: ";
    private static final String ADAPTER_NAME="RancidProvisioningAdapter";
    private static final String RANCID_COMMENT="node provisioned by opennms";

    public static final String NAME = "RancidProvisioningAdapter";
    private volatile static ConcurrentMap<Integer, RancidNode> m_onmsNodeRancidNodeMap;
    private volatile static ConcurrentMap<Integer, String> m_onmsNodeIpMap;
    

    @Override
    AdapterOperationSchedule createScheduleForNode(final int nodeId, AdapterOperationType adapterOperationType) {
        log().debug("Scheduling: " + adapterOperationType + " for nodeid: " + nodeId);
        if (adapterOperationType.equals(AdapterOperationType.CONFIG_CHANGE)) {
            updateRancidNodeState(nodeId, true);

            String ipaddress = m_onmsNodeIpMap.get(nodeId);
            //String ipaddress = (String) m_template.execute(new TransactionCallback() {
            //    public Object doInTransaction(TransactionStatus arg0) {
            //        return getSuitableIpForRancid(nodeId);
            //    }
            //});
            
            log().debug("Found Suitable ip address: " + ipaddress);
            long initialDelay = m_rancidAdapterConfig.getDelay(ipaddress);
            int retries = m_rancidAdapterConfig.getRetries(ipaddress);
            log().debug("Setting initialDelay(sec): " + initialDelay);
            log().debug("Setting retries(sec): " + retries);
            
            return new AdapterOperationSchedule(initialDelay,60, retries, TimeUnit.SECONDS);
        }
        return new AdapterOperationSchedule();
    }

    public void afterPropertiesSet() throws Exception {

        RWSClientApi.init();
        Assert.notNull(m_rwsConfig, "Rancid Provisioning Adapter requires RWSConfig property to be set.");
        
        m_cp = getRWSConnection();
        
        Assert.notNull(m_nodeDao, "Rancid Provisioning Adapter requires nodeDao property to be set.");
        
        getRancidCategories();
        
        m_template.execute(new TransactionCallback() {
            public Object doInTransaction(TransactionStatus arg0) {
                buildRancidNodeMap();
                return null;
            }
        });        
    }

    private void getRancidCategories() {
        
        try {
            m_rancid_categories = RWSClientApi.getRWSResourceDeviceTypesPatternList(m_cp).getResource();
        } catch (RancidApiException e) {
            ConnectionProperties cp = getStandByRWSConnection();
            if (cp != null) {
                try {
                    m_rancid_categories = RWSClientApi.getRWSResourceDeviceTypesPatternList(m_cp).getResource();
                } catch (RancidApiException e1) {
                    log().error("Rancid provisioning Adapter was not able to retieve rancid categories from RWS server");
                    m_rancid_categories = new ArrayList<String>();
                    m_rancid_categories.add("cisco");
                }
            }
        }
    }

    private void buildRancidNodeMap() {
        List<OnmsNode> nodes = m_nodeDao.findAllProvisionedNodes();
        m_onmsNodeRancidNodeMap = new ConcurrentHashMap<Integer, RancidNode>(nodes.size());
        m_onmsNodeIpMap = new ConcurrentHashMap<Integer, String>(nodes.size());

        for (OnmsNode onmsNode : nodes) {
            String ipaddr = getSuitableIpForRancid(onmsNode);
            if (ipaddr != null)
                m_onmsNodeIpMap.putIfAbsent(onmsNode.getId(), ipaddr);
            
            RancidNode rNode = getSuitableRancidNode(onmsNode);
            if (rNode != null) {            
                m_onmsNodeRancidNodeMap.putIfAbsent(onmsNode.getId(), rNode);
            }
        }
    }

    private ConnectionProperties getRWSConnection() {
        return m_rwsConfig.getBase();
    }

    private ConnectionProperties getStandByRWSConnection() {
        return m_rwsConfig.getNextStandBy();
    }

    public void doAdd(int nodeId, ConnectionProperties cp, boolean retry) throws ProvisioningAdapterException {
        log().debug("RANCID PROVISIONING ADAPTER CALLED addNode: nodeid: " + nodeId);
        try {
            final OnmsNode node = m_nodeDao.get(nodeId);                                                                                                                                                                                            
            Assert.notNull(node, "Rancid Provisioning Adapter addNode method failed to return node for given nodeId:"+nodeId);

            String ipaddress = (String) m_template.execute(new TransactionCallback() {
                public Object doInTransaction(TransactionStatus arg0) {
                    return getSuitableIpForRancid(node);
                }
            });

            m_onmsNodeIpMap.putIfAbsent(nodeId, ipaddress);


            RancidNode rNode = getSuitableRancidNode(node);
            
            if (m_onmsNodeRancidNodeMap.containsValue(rNode)) {
                log().warn("Rancid Provisioning Adapter: Error Duplicate node: " + node);
                ProvisioningAdapterException e = new ProvisioningAdapterException("Duplicate node has been provided: "+node); 
                sendAndThrow(nodeId, e);
                return;
            }

            rNode.setStateUp(true);

            m_onmsNodeRancidNodeMap.put(Integer.valueOf(nodeId), rNode);

            RWSClientApi.createRWSRancidNode(cp, rNode);
            RWSClientApi.createOrUpdateRWSAuthNode(cp, rNode.getAuth());
                        
        } catch (ProvisioningAdapterException ae) {    
            sendAndThrow(nodeId, ae);
        } catch (Exception e) {
            cp = getStandByRWSConnection();
            if (retry && cp != null) {
                log().info("Rancid Provisioning Adapter: retry Add on standByConn: " + cp.getUrl());
                doAdd(nodeId, cp, false);
            } else {
                sendAndThrow(nodeId, e);            
            }
        }
    }

    public void doUpdate(int nodeId, ConnectionProperties cp, boolean retry) throws ProvisioningAdapterException {
        log().debug("RANCID PROVISIONING ADAPTER CALLED updateNode: nodeid: " + nodeId);
            
        RancidNode rLocalNode = m_onmsNodeRancidNodeMap.get(Integer.valueOf(nodeId));

        log().debug("RANCID PROVISIONING ADAPTER found Node in local map: " + rLocalNode.toString());
        
        try {
            RancidNode rRemoteNode = RWSClientApi.getRWSRancidNodeTLO(cp, rLocalNode.getGroup(), rLocalNode.getDeviceName());
            log().debug("RANCID PROVISIONING ADAPTER found Node in router.db : " + rRemoteNode.toString());
            if (!rLocalNode.getDeviceType().equalsIgnoreCase(rRemoteNode.getDeviceType())) {
                try {
                    // don't change the status of the node in update operation
                    rLocalNode.setStateUp(rRemoteNode.isStateUp());
                    log().debug("RANCID PROVISIONING ADAPTER updating router.db data");
                    RWSClientApi.updateRWSRancidNode(cp, rLocalNode);
                    if ( updateAuth(rLocalNode.getAuth(), RWSClientApi.getRWSAuthNode(cp, rLocalNode.getDeviceName())) ) {
                        log().debug("RANCID PROVISIONING ADAPTER updating authentication data");
                        RWSClientApi.updateRWSAuthNode(cp, rLocalNode.getAuth());                                                        
                    }
                } catch (Exception e) {
                    log().error("RANCID PROVISIONING ADAPTER Failed to update node: " + nodeId + " Exception: " + e.getMessage());
                }
            }
        } catch (RancidApiException re) {
            if (re.getRancidCode() ==RancidApiException.OTHER_ERROR) {
                log().info("RANCID PROVISIONING ADAPTER: addng the node: " + rLocalNode.toString());
                try {
                    RWSClientApi.createRWSRancidNode(cp, rLocalNode);
                    RWSClientApi.createOrUpdateRWSAuthNode(cp, rLocalNode.getAuth());
                } catch (RancidApiException e) {
                    log().error("RANCID PROVISIONING ADAPTER Failed to create node: " + nodeId + " Exception: " + e.getMessage());
                }
            } else {
                cp = getStandByRWSConnection();
                if (retry && cp != null) {
                    log().info("Rancid Provisioning Adapter: retry Update on standByConn: " + cp.getUrl());
                    doUpdate(nodeId, cp, false);
                } else {
                    sendAndThrow(nodeId, re);            
                }
            } 
        }
     
    }
    
    public void doDelete(int nodeId,ConnectionProperties cp, boolean retry) throws ProvisioningAdapterException {

        log().debug("RANCID PROVISIONING ADAPTER CALLED deleteNode: nodeid: " + nodeId);
        
        /*
         * The work to maintain the hashmap boils down to needing to do deletes, so
         * here we go.
         */
        try {

            if (m_onmsNodeRancidNodeMap.containsKey(Integer.valueOf(nodeId))) {
                RancidNode rNode = m_onmsNodeRancidNodeMap.get(Integer.valueOf(nodeId));

                RWSClientApi.deleteRWSRancidNode(cp, rNode);
                RWSClientApi.deleteRWSAuthNode(cp, rNode.getAuth());

                m_onmsNodeRancidNodeMap.remove(Integer.valueOf(nodeId));
                m_onmsNodeIpMap.remove(Integer.valueOf(nodeId));
            } else {
                log().warn("No node found in nodeRancid Map for nodeid: " + nodeId);                
            }
            
        } catch (Exception e) {
            cp = getStandByRWSConnection();
            if (retry && cp != null) {
                log().info("Rancid Provisioning Adapter: retry Delete on standByConn: " + cp.getUrl());
                doDelete(nodeId, cp, false);
            } else {
                sendAndThrow(nodeId, e);            
            }
        }
    }

    public void doNodeConfigChanged(int nodeId,ConnectionProperties cp, boolean retry) throws ProvisioningAdapterException {
        log().debug("RANCID PROVISIONING ADAPTER CALLED DoNodeConfigChanged: nodeid: " + nodeId);
            if (m_onmsNodeRancidNodeMap.containsKey(Integer.valueOf(nodeId))) {
                updateConfiguration(nodeId,m_onmsNodeRancidNodeMap.get(Integer.valueOf(nodeId)),cp, retry);
            } else {
                log().warn("No node found in nodeRancid Map for nodeid: " + nodeId);
            }
    }

    private void updateConfiguration(int nodeid, RancidNode rNode,ConnectionProperties cp, boolean retry) throws ProvisioningAdapterException {
        log().debug("Updating Rancid Router.db configuration: node: " + rNode.getDeviceName() + " type: " + rNode.getDeviceType() + " group: " + rNode.getGroup());
        try {
                RWSClientApi.updateRWSRancidNode(cp, rNode);
        } catch (Exception e) {
            cp = getStandByRWSConnection();
            if (retry && cp != null) {
                log().info("Rancid Provisioning Adapter: retry update on standByConn: " + cp.getUrl());
                updateConfiguration(nodeid, rNode, cp, false);
            } else {
                sendAndThrow(nodeid, e);            
            }
        }
    }

    private void sendAndThrow(int nodeId, Exception e) {
        log().debug("RANCID PROVISIONING ADAPTER CALLED sendAndThrow: nodeid: " + nodeId);
        log().debug("RANCID PROVISIONING ADAPTER CALLED sendAndThrow: Exception: " + e.getMessage());
        Event event = buildEvent(EventConstants.PROVISIONING_ADAPTER_FAILED, nodeId).addParam("reason", MESSAGE_PREFIX+e.getLocalizedMessage()).getEvent();
        m_eventForwarder.sendNow(event);
        throw new ProvisioningAdapterException(MESSAGE_PREFIX, e);
    }

    private EventBuilder buildEvent(String uei, int nodeId) {
        EventBuilder builder = new EventBuilder(uei, "Provisioner", new Date());
        builder.setNodeid(nodeId);
        return builder;
    }

    public NodeDao getNodeDao() {
        return m_nodeDao;
    }
    public void setNodeDao(NodeDao dao) {
        m_nodeDao = dao;
    }
    
    public void setEventForwarder(EventForwarder eventForwarder) {
        m_eventForwarder = eventForwarder;
    }

    public EventForwarder getEventForwarder() {
        return m_eventForwarder;
    }
    
    private static Category log() {
        return ThreadCategory.getInstance(RancidProvisioningAdapter.class);
    }

    public RWSConfig getRwsConfig() {
        return m_rwsConfig;
    }

    public void setRwsConfig(RWSConfig rwsConfig) {
        m_rwsConfig = rwsConfig;
    }

    public RancidAdapterConfig getRancidAdapterConfig() {
        return m_rancidAdapterConfig;
    }

    public void setRancidAdapterConfig(RancidAdapterConfig rancidAdapterConfig) {
        m_rancidAdapterConfig = rancidAdapterConfig;
    }

    public String getName() {
        return ADAPTER_NAME;
    }

    private String getSuitableIpForRancid(OnmsNode node){
        log().debug("getSuitableIpForRancid: node: " + node.getNodeId() + " Foreign Source: " + node.getForeignSource());
        OnmsIpInterface primaryInterface = node.getPrimaryInterface();
        String ipaddr = "127.0.0.1";
        if (primaryInterface == null) {
            log().debug("getSuitableIpForRancid: found null Snmp Primary Interface, getting interfaces");
            Set<OnmsIpInterface> ipInterfaces = node.getIpInterfaces();
            for (OnmsIpInterface onmsIpInterface : ipInterfaces) {
                log().debug("getSuitableIpForRancid: trying Interface with id: " + onmsIpInterface.getId());
                if (onmsIpInterface.getIpAddress() != null) 
                    ipaddr = onmsIpInterface.getIpAddress();
                else 
                    log().debug("getSuitableIpForRancid: found null ip address on Interface with id: " + onmsIpInterface.getId());

            }
        } else {        
            log().debug("getSuitableIpForRancid: found Snmp Primary Interface");
            if (primaryInterface.getIpAddress() != null )
                ipaddr = primaryInterface.getIpAddress();
            else 
                log().debug("getSuitableIpForRancid: found null ip address on Primary Interface");
        }
        return ipaddr;
    }
    
    private RancidNode getSuitableRancidNode(OnmsNode node) {
        

        //The group should be the foreign source of the node

        String group = node.getForeignSource();

        if (group == null) return null;
        RancidNode r_node = new RancidNode(group, node.getLabel());

        String ipaddress = m_onmsNodeIpMap.get(node.getId());
        log().debug("Found Suitable ip address: " + ipaddress + " for node: " + node.getLabel() );
        if (m_rancidAdapterConfig.useCategories(ipaddress)) {
           r_node.setDeviceType(getTypeFromCategories(node)); 
        } else {
            r_node.setDeviceType(getTypeFromSysObjectId(node.getSysObjectId()));
        }
        r_node.setStateUp(false);
        r_node.setComment(RANCID_COMMENT);
        r_node.setAuth(getSuitableRancidNodeAuthentication(node));
        return r_node;
        

    }
    
    private String getTypeFromSysObjectId(String sysoid) {
        String rancidType = m_rancidAdapterConfig.getType(sysoid);
        log().debug("Rancid configuration file: Rancid devicetype found: " + rancidType);
        return rancidType;
    }
    
    private String getTypeFromCategories(OnmsNode node) {
        log().debug("Using Categories to get Rancid devicetype for node: " + node.getLabel());
        for (String rancidType: m_rancid_categories) {
            for (OnmsCategory nodecategory: node.getCategories()) {
                if (nodecategory.getName().equalsIgnoreCase(rancidType)) {
                    log().debug("Found Matching Category: Rancid devicetype found: " + rancidType);
                    return rancidType;
                }
            }
        }
        log().warn("No Matching Category found: trying to get devicetype using config file");
        return getTypeFromCategories(node);
    }

    private RancidNodeAuthentication getSuitableRancidNodeAuthentication(OnmsNode node) {
        // RancidAutentication
        RancidNodeAuthentication r_auth_node = new RancidNodeAuthentication();
        r_auth_node.setDeviceName(node.getLabel());
        OnmsAssetRecord asset_node = node.getAssetRecord();

        if (asset_node.getUsername() != null) {
            r_auth_node.setUser(asset_node.getUsername());
        }
        
        if (asset_node.getPassword() != null) {
            r_auth_node.setPassword(asset_node.getPassword());
        }

        if (asset_node.getEnable() != null) {
            r_auth_node.setEnablePass(asset_node.getEnable());
        }
        
        if (asset_node.getAutoenable() != null) {
            r_auth_node.setAutoEnable(asset_node.getAutoenable().equals(OnmsAssetRecord.AUTOENABLED));
        }
        
        if (asset_node.getConnection() != null) {
            r_auth_node.setConnectionMethod(asset_node.getConnection());
        } else {
            r_auth_node.setConnectionMethod("telnet");
        }
        
        return r_auth_node;
    }

    @Override
    public boolean isNodeReady(final AdapterOperation op) {
        boolean ready = true;
        if (op.getType() == AdapterOperationType.CONFIG_CHANGE) {
            log().debug("Config Change Operation for node: " + op.getNodeId());
//            String ipaddress = (String)  m_template.execute(new TransactionCallback() {
//                public Object doInTransaction(TransactionStatus arg0) {
//                    return getSuitableIpForRancid(op.getNodeId());
//                }
//            });
     
            ready =
            m_rancidAdapterConfig.isCurTimeInSchedule(m_onmsNodeIpMap.get(op.getNodeId()));
        }
        log().debug("is Node Ready: " + ready + " For Operation " + op.getType() + " for node: " + op.getNodeId());
        return ready;
    }

    @Override
    public void processPendingOperationForNode(final AdapterOperation op) throws ProvisioningAdapterException {
        log().debug("Procession Operation: " + op.getType() + " for node: " + op.getNodeId() );
        if (op.getType() == AdapterOperationType.ADD) {
            m_template.execute(new TransactionCallback() {
                public Object doInTransaction(TransactionStatus arg0) {
                    doAdd(op.getNodeId(),m_cp,true);
                    return null;
                }
            });
        } else if (op.getType() == AdapterOperationType.UPDATE) {
            m_template.execute(new TransactionCallback() {
                public Object doInTransaction(TransactionStatus arg0) {
                    doUpdate(op.getNodeId(),m_cp,true);
                    return null;
                }
            });
        } else if (op.getType() == AdapterOperationType.DELETE) {
            m_template.execute(new TransactionCallback() {
                public Object doInTransaction(TransactionStatus arg0) {
                    doDelete(op.getNodeId(),m_cp,true);
                    return null;
                }
            });
        } else if (op.getType() == AdapterOperationType.CONFIG_CHANGE) {
            m_template.execute(new TransactionCallback() {
                public Object doInTransaction(TransactionStatus arg0) {
                    doNodeConfigChanged(op.getNodeId(),m_cp,true);
                    return null;
                }
            });
        }
    }

    @EventHandler(uei = EventConstants.RELOAD_DAEMON_CONFIG_UEI)
    public void handleReloadConfigEvent(Event event) {
        if (isReloadConfigEventTarget(event)) {
            LogUtils.debugf(this, "reloading the rancid adapter configuration");
            try {
                RancidAdapterConfigFactory.reload();
                m_template.execute(new TransactionCallback() {
                    public Object doInTransaction(TransactionStatus arg0) {
                        buildRancidNodeMap();
                        return null;
                    }
                });        
            } catch (Exception e) {
                LogUtils.infof(this, e, "unable to reload rancid adapter configuration");
            }
        }
    }

    private boolean isReloadConfigEventTarget(Event event) {
        boolean isTarget = false;
        
        List<Parm> parmCollection = event.getParms().getParmCollection();

        for (Parm parm : parmCollection) {
            if (EventConstants.PARM_DAEMON_NAME.equals(parm.getParmName()) && "Provisiond.RancidProvisioningAdapter".equalsIgnoreCase(parm.getValue().getContent())) {
                isTarget = true;
                break;
            }
        }
        
        log().debug("isReloadConfigEventTarget: Provisiond.RancidProvisioningAdapter was target of reload event: " + isTarget);
        return isTarget;
    }

    @EventHandler(uei = EventConstants.RANCID_DOWNLOAD_FAILURE_UEI)
    public void handleRancidDownLoadFailure(Event e) {
        log().debug("get Event uei/id: " + e.getUei() + "/" + e.getDbid());
        if (e.hasNodeid()) {
            int nodeId = Long.valueOf(e.getNodeid()).intValue();
            if (m_onmsNodeRancidNodeMap.containsKey(Integer.valueOf(nodeId))) {
                updateRancidNodeState(nodeId, false);
            } else {
                log().warn("node does not exist with nodeid: " + e.getNodeid());
            }
        }
    }

    @EventHandler(uei = EventConstants.RANCID_DOWNLOAD_SUCCESS_UEI)
    public void handleRancidDownLoadSuccess(Event e) {
        log().debug("get Event uei/id: " + e.getUei() + "/" + e.getDbid());
        if (e.hasNodeid() ) {
            int nodeId = Long.valueOf(e.getNodeid()).intValue();
            if (m_onmsNodeRancidNodeMap.containsKey(Integer.valueOf(nodeId))) {
                updateRancidNodeState(nodeId, false);
            } else {
                log().warn("node does not exist with nodeid: " + e.getNodeid());
            }
        }
    }

    @EventHandler(uei = EventConstants.RANCID_GROUP_PROCESSING_COMPLETED_UEI)
    public void handleRancidGroupProcessingCompleted(Event e) {
        log().debug("get Event uei/id: " + e.getUei() + "/" + e.getDbid());
        if (e.getParms() != null ) {
            Iterator<Parm> ite = e.getParms().iterateParm();
            while (ite.hasNext()) {
                Parm parm = ite.next();
                log().debug("parm name: " + parm.getParmName());
                if (parm.getParmName().equals(".1.3.6.1.4.1.31543.1.1.2.1.1.3")) {
                    updateGroupConfiguration(parm.getValue().getContent());
                    break;
                }
            }
        }
    }
    
    private void updateGroupConfiguration(String group) {
        Iterator<Integer> ite = m_onmsNodeRancidNodeMap.keySet().iterator();
        while (ite.hasNext()) {
            Integer nodeId = ite.next();
            RancidNode rnode = m_onmsNodeRancidNodeMap.get(nodeId);
            if (group.equals(rnode.getGroup())) {
                updateConfiguration(nodeId.intValue(), rnode, m_cp, true);
            }
        }

    }
    
    private void updateRancidNodeState(int nodeid, boolean up) {
        RancidNode rnode = m_onmsNodeRancidNodeMap.get(Integer.valueOf(nodeid));
        rnode.setStateUp(up);
        m_onmsNodeRancidNodeMap.put(nodeid, rnode);
    }

    private boolean updateAuth(RancidNodeAuthentication localNode, RancidNodeAuthentication remoteNode) {
        if (!localNode.getUser().equals(remoteNode.getUser())) return true;
        if (!localNode.getPassword().equals(remoteNode.getPassword())) return true;
        if (!localNode.getEnablePass().equals(remoteNode.getEnablePass())) return true;
        if (!localNode.getConnectionMethodString().equals(remoteNode.getConnectionMethodString())) return true;
        if (localNode.isAutoEnable() && !remoteNode.isAutoEnable()) return true;
        if (!localNode.isAutoEnable() && remoteNode.isAutoEnable()) return true;
        return false;
    }
}<commit_msg>Fixed Bug #3534 --The RancidAdpater does not updates updates nodes<commit_after>/*
 * This file is part of the OpenNMS(R) Application.
 *
 * OpenNMS(R) is Copyright (C) 2008 The OpenNMS Group, Inc.  All rights reserved.
 * OpenNMS(R) is a derivative work, containing both original code, included code and modified
 * code that was published under the GNU General Public License. Copyrights for modified
 * and included code are below.
 *
 * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
 *
 * Modifications:
 * 
 * Created: December 16, 2008
 *
 * Copyright (C) 2008 The OpenNMS Group, Inc.  All rights reserved.
 *
 * This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * For more information contact:
 *      OpenNMS Licensing       <<EMAIL>>
 *      http://www.opennms.org/
 *      http://www.opennms.com/
 */
package org.opennms.netmgt.provision;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Category;
import org.opennms.core.utils.LogUtils;
import org.opennms.core.utils.ThreadCategory;
import org.opennms.netmgt.EventConstants;
import org.opennms.netmgt.config.RWSConfig;
import org.opennms.netmgt.config.RancidAdapterConfig;
import org.opennms.netmgt.config.RancidAdapterConfigFactory;
import org.opennms.netmgt.dao.NodeDao;
import org.opennms.netmgt.model.OnmsAssetRecord;
import org.opennms.netmgt.model.OnmsCategory;
import org.opennms.netmgt.model.OnmsIpInterface;
import org.opennms.netmgt.model.OnmsNode;
import org.opennms.netmgt.model.events.EventBuilder;
import org.opennms.netmgt.model.events.EventForwarder;
import org.opennms.netmgt.model.events.annotations.EventHandler;
import org.opennms.netmgt.model.events.annotations.EventListener;
import org.opennms.netmgt.xml.event.Event;
import org.opennms.netmgt.xml.event.Parm;
import org.opennms.rancid.ConnectionProperties;
import org.opennms.rancid.RWSClientApi;
import org.opennms.rancid.RancidApiException;
import org.opennms.rancid.RancidNode;
import org.opennms.rancid.RancidNodeAuthentication;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.Assert;

/**
 * A Rancid provisioning adapter for integration with OpenNMS Provisoning daemon API.
 * 
 * @author <a href="mailto:<EMAIL>"><NAME></a>
 *
 */

@EventListener(name="RancidProvisioningAdapter")
public class RancidProvisioningAdapter extends SimpleQueuedProvisioningAdapter implements InitializingBean {
    
    private NodeDao m_nodeDao;
    private volatile EventForwarder m_eventForwarder;

    private RWSConfig m_rwsConfig;
    private RancidAdapterConfig m_rancidAdapterConfig;
    private ConnectionProperties m_cp;
    
    private List<String> m_rancid_categories;
    
    private TransactionTemplate m_template;
    
    public TransactionTemplate getTemplate() {
        return m_template;
    }
    public void setTemplate(TransactionTemplate template) {
        m_template = template;
    }

    private static final String MESSAGE_PREFIX = "Rancid provisioning failed: ";
    private static final String ADAPTER_NAME="RancidProvisioningAdapter";
    private static final String RANCID_COMMENT="node provisioned by opennms";

    public static final String NAME = "RancidProvisioningAdapter";
    private volatile static ConcurrentMap<Integer, RancidNode> m_onmsNodeRancidNodeMap;
    private volatile static ConcurrentMap<Integer, String> m_onmsNodeIpMap;
    

    @Override
    AdapterOperationSchedule createScheduleForNode(final int nodeId, AdapterOperationType adapterOperationType) {
        log().debug("Scheduling: " + adapterOperationType + " for nodeid: " + nodeId);
        if (adapterOperationType.equals(AdapterOperationType.CONFIG_CHANGE)) {
            updateRancidNodeState(nodeId, true);

            String ipaddress = m_onmsNodeIpMap.get(nodeId);
            //String ipaddress = (String) m_template.execute(new TransactionCallback() {
            //    public Object doInTransaction(TransactionStatus arg0) {
            //        return getSuitableIpForRancid(nodeId);
            //    }
            //});
            
            log().debug("Found Suitable ip address: " + ipaddress);
            long initialDelay = m_rancidAdapterConfig.getDelay(ipaddress);
            int retries = m_rancidAdapterConfig.getRetries(ipaddress);
            log().debug("Setting initialDelay(sec): " + initialDelay);
            log().debug("Setting retries(sec): " + retries);
            
            return new AdapterOperationSchedule(initialDelay,60, retries, TimeUnit.SECONDS);
        }
        return new AdapterOperationSchedule();
    }

    public void afterPropertiesSet() throws Exception {

        RWSClientApi.init();
        Assert.notNull(m_rwsConfig, "Rancid Provisioning Adapter requires RWSConfig property to be set.");
        
        m_cp = getRWSConnection();
        
        Assert.notNull(m_nodeDao, "Rancid Provisioning Adapter requires nodeDao property to be set.");
        
        getRancidCategories();
        
        m_template.execute(new TransactionCallback() {
            public Object doInTransaction(TransactionStatus arg0) {
                buildRancidNodeMap();
                return null;
            }
        });        
    }

    private void getRancidCategories() {
        
        try {
            m_rancid_categories = RWSClientApi.getRWSResourceDeviceTypesPatternList(m_cp).getResource();
        } catch (RancidApiException e) {
            ConnectionProperties cp = getStandByRWSConnection();
            if (cp != null) {
                try {
                    m_rancid_categories = RWSClientApi.getRWSResourceDeviceTypesPatternList(m_cp).getResource();
                } catch (RancidApiException e1) {
                    log().warn("getRancidCategories: not able to retrieve rancid categories from RWS server");
                    m_rancid_categories = new ArrayList<String>();
                    m_rancid_categories.add("cisco");
                    log().warn("getRancidCategories: setting categories list to 'cisco'");
                }
            }
        }
    }

    private void buildRancidNodeMap() {
        List<OnmsNode> nodes = m_nodeDao.findAllProvisionedNodes();
        m_onmsNodeRancidNodeMap = new ConcurrentHashMap<Integer, RancidNode>(nodes.size());
        m_onmsNodeIpMap = new ConcurrentHashMap<Integer, String>(nodes.size());

        for (OnmsNode onmsNode : nodes) {
            String ipaddr = getSuitableIpForRancid(onmsNode);
            if (ipaddr != null)
                m_onmsNodeIpMap.putIfAbsent(onmsNode.getId(), ipaddr);
            
            RancidNode rNode = getSuitableRancidNode(onmsNode);
            if (rNode != null) {            
                m_onmsNodeRancidNodeMap.putIfAbsent(onmsNode.getId(), rNode);
            }
        }
    }

    private ConnectionProperties getRWSConnection() {
        return m_rwsConfig.getBase();
    }

    private ConnectionProperties getStandByRWSConnection() {
        return m_rwsConfig.getNextStandBy();
    }

    public void doAdd(int nodeId, ConnectionProperties cp, boolean retry) throws ProvisioningAdapterException {
        log().debug("doAdd: adding nodeid: " + nodeId);

        final OnmsNode node = m_nodeDao.get(nodeId);                                                                                                                                                                                            
        Assert.notNull(node, "doAdd: failed to return node for given nodeId:"+nodeId);

        String ipaddress = (String) m_template.execute(new TransactionCallback() {
            public Object doInTransaction(TransactionStatus arg0) {
                return getSuitableIpForRancid(node);
            }
        });

        
        RancidNode rNode = getSuitableRancidNode(node);
        rNode.setStateUp(true);
        
        try {            
            if (m_onmsNodeRancidNodeMap.containsValue(rNode)) {
                log().error("doAdd: Error Duplicate node: " + node);
                ProvisioningAdapterException e = new ProvisioningAdapterException("Duplicate node has been added: "+node); 
                sendAndThrow(nodeId, e);
                return;
            }
            log().debug("doAdd: adding to router.db node: " + node.getLabel());

            RWSClientApi.createRWSRancidNode(cp, rNode);
            m_onmsNodeIpMap.putIfAbsent(nodeId, ipaddress);
            m_onmsNodeRancidNodeMap.put(Integer.valueOf(nodeId), rNode);

            RWSClientApi.createOrUpdateRWSAuthNode(cp, rNode.getAuth());
            
                        
        } catch (ProvisioningAdapterException ae) {    
            sendAndThrow(nodeId, ae);
        } catch (Exception e) {
            cp = getStandByRWSConnection();
            if (retry && cp != null) {
                log().info("doAdd: retry Add on standByConn: " + cp.getUrl());
                doAdd(nodeId, cp, false);
            } else {
                sendAndThrow(nodeId, e);            
            }
        }
    }

    public void doUpdate(int nodeId, ConnectionProperties cp, boolean retry) throws ProvisioningAdapterException {
        log().debug("doUpdate: updating nodeid: " + nodeId);
            
        RancidNode rLocalNode = m_onmsNodeRancidNodeMap.get(Integer.valueOf(nodeId));
        log().debug("doUpdate: found local map Node: " + rLocalNode.toString());
        
        final OnmsNode node = m_nodeDao.get(nodeId);
        Assert.notNull(node, "doAdd: failed to return node for given nodeId:"+nodeId);
 
        String ipaddress = (String) m_template.execute(new TransactionCallback() {
            public Object doInTransaction(TransactionStatus arg0) {
                return getSuitableIpForRancid(node);
            }
        });

        m_onmsNodeIpMap.put(nodeId, ipaddress);

        RancidNode rUpdatedNode = getSuitableRancidNode(node);
        log().debug("doUpdate: found updated Node : " + rUpdatedNode.toString());

        if (rLocalNode.getDeviceName().equalsIgnoreCase(rUpdatedNode.getDeviceName())) {            
            try {
                RancidNode rRemoteNode = RWSClientApi.getRWSRancidNodeTLO(cp, rLocalNode.getGroup(), rLocalNode.getDeviceName());
                RancidNodeAuthentication rRemoteNodeAuth = RWSClientApi.getRWSAuthNode(cp, rLocalNode.getDeviceName());
                log().debug("doUpdate: found Node in router.db : " + rRemoteNode.toString());
                if (!rUpdatedNode.getDeviceType().equalsIgnoreCase(rRemoteNode.getDeviceType())) {
                    try {
                        // don't change the status of the node in update operation
                        rUpdatedNode.setStateUp(rRemoteNode.isStateUp());
                        log().debug("doUpdate: updating router.db");
                        RWSClientApi.updateRWSRancidNode(cp, rLocalNode);
                    } catch (Exception e) {
                        log().error("doUpdate: failed to update node: " + nodeId + " Exception: " + e.getMessage());
                    }
                }
                
                if ( updateAuth(rUpdatedNode.getAuth(), rRemoteNodeAuth) ) {
                    log().debug("doUpdate: updating authentication data");
                    try {
                        RWSClientApi.updateRWSAuthNode(cp, rUpdatedNode.getAuth());                                                        
                    } catch (Exception e) {
                        log().error("doUpdate: Failed to update node authentication data: " + nodeId + " Exception: " + e.getMessage());
                    }
                }
                
                rUpdatedNode.setStateUp(rLocalNode.isStateUp());
                m_onmsNodeRancidNodeMap.put(nodeId, rUpdatedNode);
            
            } catch (RancidApiException re) {
                if (re.getRancidCode() ==RancidApiException.OTHER_ERROR) {
                    log().warn("doUpdate: node not found in router.db: " + rUpdatedNode.toString());
                    try {
                        log().debug("doUpdate: adding Node to router.db for nodeid: " + nodeId);
                        rUpdatedNode.setStateUp(true);
                        RWSClientApi.createRWSRancidNode(cp, rUpdatedNode);
                        RWSClientApi.createOrUpdateRWSAuthNode(cp, rUpdatedNode.getAuth());
                        m_onmsNodeRancidNodeMap.put(nodeId, rUpdatedNode);
                    } catch (RancidApiException e) {
                        log().error("doUpdate: Failed to create node: " + nodeId + " Exception: " + e.getMessage());
                        sendAndThrow(nodeId, e);            
                    }
                } else {
                    cp = getStandByRWSConnection();
                    if (retry && cp != null) {
                        log().info("doUpdate: retry Update on standByConn: " + cp.getUrl());
                        doUpdate(nodeId, cp, false);
                    } else {
                        sendAndThrow(nodeId, re);            
                    }
                } 
            }
        } else {
            log().debug("doUpdate: the device name is changed for Nodeid: " + nodeId);
        
            log().debug("doUpdate: calling doDelete for NodeId: " + nodeId);
            doDelete(nodeId, cp, retry);
            
            try {
                log().debug("doUpdate: adding Node to router.db for nodeid: " + nodeId);
                rUpdatedNode.setStateUp(true);
                RWSClientApi.createRWSRancidNode(cp, rUpdatedNode);
                RWSClientApi.createOrUpdateRWSAuthNode(cp, rUpdatedNode.getAuth());
                m_onmsNodeRancidNodeMap.put(nodeId, rUpdatedNode);
                m_onmsNodeIpMap.put(nodeId, ipaddress);
            } catch (RancidApiException e) {
                log().error("doUpdate: Failed to create node: " + nodeId + " Exception: " + e.getMessage());
                sendAndThrow(nodeId, e);            
            }
        }
    }
    
    public void doDelete(int nodeId,ConnectionProperties cp, boolean retry) throws ProvisioningAdapterException {

        log().debug("doDelete: deleting nodeid: " + nodeId);
        
        /*
         * The work to maintain the hashmap boils down to needing to do deletes, so
         * here we go.
         */
        try {

            if (m_onmsNodeRancidNodeMap.containsKey(Integer.valueOf(nodeId))) {
                RancidNode rNode = m_onmsNodeRancidNodeMap.get(Integer.valueOf(nodeId));

                RWSClientApi.deleteRWSRancidNode(cp, rNode);
                RWSClientApi.deleteRWSAuthNode(cp, rNode.getAuth());

                m_onmsNodeRancidNodeMap.remove(Integer.valueOf(nodeId));
                m_onmsNodeIpMap.remove(Integer.valueOf(nodeId));
            } else {
                log().warn("doDelete: no device found in node Rancid Map for nodeid: " + nodeId);                
            }
            
        } catch (Exception e) {
            cp = getStandByRWSConnection();
            if (retry && cp != null) {
                log().info("doDelete: retry Delete on standByConn: " + cp.getUrl());
                doDelete(nodeId, cp, false);
            } else {
                sendAndThrow(nodeId, e);            
            }
        }
    }

    public void doNodeConfigChanged(int nodeId,ConnectionProperties cp, boolean retry) throws ProvisioningAdapterException {
        log().debug("doNodeConfigChanged: nodeid: " + nodeId);
            if (m_onmsNodeRancidNodeMap.containsKey(Integer.valueOf(nodeId))) {
                updateConfiguration(nodeId,m_onmsNodeRancidNodeMap.get(Integer.valueOf(nodeId)),cp, retry);
            } else {
                log().warn("doNodeConfigChanged: No node found in nodeRancid Map for nodeid: " + nodeId);
            }
    }

    private void updateConfiguration(int nodeid, RancidNode rNode,ConnectionProperties cp, boolean retry) throws ProvisioningAdapterException {
        log().debug("updateConfiguration: Updating Rancid Router.db configuration for node: " + rNode.getDeviceName() + " type: " + rNode.getDeviceType() + " group: " + rNode.getGroup());
        try {
                RWSClientApi.updateRWSRancidNode(cp, rNode);
        } catch (Exception e) {
            cp = getStandByRWSConnection();
            if (retry && cp != null) {
                log().info("updateConfiguration: retry update on standByConn: " + cp.getUrl());
                updateConfiguration(nodeid, rNode, cp, false);
            } else {
                sendAndThrow(nodeid, e);            
            }
        }
    }

    private void sendAndThrow(int nodeId, Exception e) {
        log().debug("sendAndThrow: error working on nodeid: " + nodeId);
        log().debug("sendAndThrow: Exception: " + e.getMessage());
        Event event = buildEvent(EventConstants.PROVISIONING_ADAPTER_FAILED, nodeId).addParam("reason", MESSAGE_PREFIX+e.getLocalizedMessage()).getEvent();
        m_eventForwarder.sendNow(event);
        throw new ProvisioningAdapterException(MESSAGE_PREFIX, e);
    }

    private EventBuilder buildEvent(String uei, int nodeId) {
        EventBuilder builder = new EventBuilder(uei, "Provisioner", new Date());
        builder.setNodeid(nodeId);
        return builder;
    }

    public NodeDao getNodeDao() {
        return m_nodeDao;
    }
    public void setNodeDao(NodeDao dao) {
        m_nodeDao = dao;
    }
    
    public void setEventForwarder(EventForwarder eventForwarder) {
        m_eventForwarder = eventForwarder;
    }

    public EventForwarder getEventForwarder() {
        return m_eventForwarder;
    }
    
    private static Category log() {
        return ThreadCategory.getInstance(RancidProvisioningAdapter.class);
    }

    public RWSConfig getRwsConfig() {
        return m_rwsConfig;
    }

    public void setRwsConfig(RWSConfig rwsConfig) {
        m_rwsConfig = rwsConfig;
    }

    public RancidAdapterConfig getRancidAdapterConfig() {
        return m_rancidAdapterConfig;
    }

    public void setRancidAdapterConfig(RancidAdapterConfig rancidAdapterConfig) {
        m_rancidAdapterConfig = rancidAdapterConfig;
    }

    public String getName() {
        return ADAPTER_NAME;
    }

    private String getSuitableIpForRancid(OnmsNode node){
        log().debug("getSuitableIpForRancid: node: " + node.getNodeId() + " Foreign Source: " + node.getForeignSource());
        OnmsIpInterface primaryInterface = node.getPrimaryInterface();
        String ipaddr = "127.0.0.1";
        if (primaryInterface == null) {
            log().debug("getSuitableIpForRancid: found null Snmp Primary Interface, getting interfaces");
            Set<OnmsIpInterface> ipInterfaces = node.getIpInterfaces();
            for (OnmsIpInterface onmsIpInterface : ipInterfaces) {
                log().debug("getSuitableIpForRancid: trying Interface with id: " + onmsIpInterface.getId());
                if (onmsIpInterface.getIpAddress() != null) 
                    ipaddr = onmsIpInterface.getIpAddress();
                else 
                    log().debug("getSuitableIpForRancid: found null ip address on Interface with id: " + onmsIpInterface.getId());

            }
        } else {        
            log().debug("getSuitableIpForRancid: found Snmp Primary Interface");
            if (primaryInterface.getIpAddress() != null )
                ipaddr = primaryInterface.getIpAddress();
            else 
                log().debug("getSuitableIpForRancid: found null ip address on Primary Interface");
        }
        return ipaddr;
    }
    
    private RancidNode getSuitableRancidNode(OnmsNode node) {
        

        //The group should be the foreign source of the node

        String group = node.getForeignSource();

        if (group == null) return null;
        RancidNode r_node = new RancidNode(group, node.getLabel());

        String ipaddress = m_onmsNodeIpMap.get(node.getId());

        if (m_rancidAdapterConfig.useCategories(ipaddress)) {
            log().debug("getSuitableRancidNode: Using Categories to get Rancid devicetype for node: " + node.getLabel());
            r_node.setDeviceType(getTypeFromCategories(node)); 
        } else {
            log().debug("getSuitableRancidNode: Using Sysoid to get Rancid devicetype for node: " + node.getLabel());
            r_node.setDeviceType(getTypeFromSysObjectId(node.getSysObjectId()));
        }
        r_node.setStateUp(false);
        r_node.setComment(RANCID_COMMENT);
        r_node.setAuth(getSuitableRancidNodeAuthentication(node));
        return r_node;
        

    }
    
    private String getTypeFromSysObjectId(String sysoid) {
        String rancidType = m_rancidAdapterConfig.getType(sysoid);
        log().debug("getTypeFromSysObjectId: Rancid devicetype found: " + rancidType + " for sysOid: " + sysoid);
        return rancidType;
    }
    
    private String getTypeFromCategories(OnmsNode node) {
        for (String rancidType: m_rancid_categories) {
            for (OnmsCategory nodecategory: node.getCategories()) {
                if (nodecategory.getName().equalsIgnoreCase(rancidType)) {
                    log().debug("getTypeFromCategories: Found Matching Category: Rancid devicetype found: " + rancidType);
                    return rancidType;
                }
            }
        }
        log().warn("getTypeFromCategories: No Matching Category found: trying to get devicetype using config file");
        return getTypeFromCategories(node);
    }

    private RancidNodeAuthentication getSuitableRancidNodeAuthentication(OnmsNode node) {
        // RancidAutentication
        RancidNodeAuthentication r_auth_node = new RancidNodeAuthentication();
        r_auth_node.setDeviceName(node.getLabel());
        OnmsAssetRecord asset_node = node.getAssetRecord();

        if (asset_node.getUsername() != null) {
            r_auth_node.setUser(asset_node.getUsername());
        }
        
        if (asset_node.getPassword() != null) {
            r_auth_node.setPassword(asset_node.getPassword());
        }

        if (asset_node.getEnable() != null) {
            r_auth_node.setEnablePass(asset_node.getEnable());
        }
        
        if (asset_node.getAutoenable() != null) {
            r_auth_node.setAutoEnable(asset_node.getAutoenable().equals(OnmsAssetRecord.AUTOENABLED));
        }
        
        if (asset_node.getConnection() != null) {
            r_auth_node.setConnectionMethod(asset_node.getConnection());
        } else {
            r_auth_node.setConnectionMethod("telnet");
        }
        
        return r_auth_node;
    }

    @Override
    public boolean isNodeReady(final AdapterOperation op) {
        boolean ready = true;
        if (op.getType() == AdapterOperationType.CONFIG_CHANGE) {
            ready =
            m_rancidAdapterConfig.isCurTimeInSchedule(m_onmsNodeIpMap.get(op.getNodeId()));
        }
        log().debug("isNodeReady: " + ready + " For Operation " + op.getType() + " for node: " + op.getNodeId());
        return ready;
    }

    @Override
    public void processPendingOperationForNode(final AdapterOperation op) throws ProvisioningAdapterException {
        log().debug("processPendingOperationForNode: " + op.getType() + " for node: " + op.getNodeId() );
        if (op.getType() == AdapterOperationType.ADD) {
            m_template.execute(new TransactionCallback() {
                public Object doInTransaction(TransactionStatus arg0) {
                    doAdd(op.getNodeId(),m_cp,true);
                    return null;
                }
            });
        } else if (op.getType() == AdapterOperationType.UPDATE) {
            m_template.execute(new TransactionCallback() {
                public Object doInTransaction(TransactionStatus arg0) {
                    doUpdate(op.getNodeId(),m_cp,true);
                    return null;
                }
            });
        } else if (op.getType() == AdapterOperationType.DELETE) {
            m_template.execute(new TransactionCallback() {
                public Object doInTransaction(TransactionStatus arg0) {
                    doDelete(op.getNodeId(),m_cp,true);
                    return null;
                }
            });
        } else if (op.getType() == AdapterOperationType.CONFIG_CHANGE) {
            m_template.execute(new TransactionCallback() {
                public Object doInTransaction(TransactionStatus arg0) {
                    doNodeConfigChanged(op.getNodeId(),m_cp,true);
                    return null;
                }
            });
        }
    }

    @EventHandler(uei = EventConstants.RELOAD_DAEMON_CONFIG_UEI)
    public void handleReloadConfigEvent(Event event) {
        if (isReloadConfigEventTarget(event)) {
            LogUtils.debugf(this, "reloading the rancid adapter configuration");
            try {
                RancidAdapterConfigFactory.reload();
                m_template.execute(new TransactionCallback() {
                    public Object doInTransaction(TransactionStatus arg0) {
                        buildRancidNodeMap();
                        return null;
                    }
                });        
            } catch (Exception e) {
                LogUtils.infof(this, e, "unable to reload rancid adapter configuration");
            }
        }
    }

    private boolean isReloadConfigEventTarget(Event event) {
        boolean isTarget = false;
        
        List<Parm> parmCollection = event.getParms().getParmCollection();

        for (Parm parm : parmCollection) {
            if (EventConstants.PARM_DAEMON_NAME.equals(parm.getParmName()) && "Provisiond.RancidProvisioningAdapter".equalsIgnoreCase(parm.getValue().getContent())) {
                isTarget = true;
                break;
            }
        }
        
        log().debug("isReloadConfigEventTarget: Provisiond.RancidProvisioningAdapter was target of reload event: " + isTarget);
        return isTarget;
    }

    @EventHandler(uei = EventConstants.RANCID_DOWNLOAD_FAILURE_UEI)
    public void handleRancidDownLoadFailure(Event e) {
        log().debug("handleRancidDownLoadFailure: get Event uei/id: " + e.getUei() + "/" + e.getDbid());
        if (e.hasNodeid()) {
            int nodeId = Long.valueOf(e.getNodeid()).intValue();
            if (m_onmsNodeRancidNodeMap.containsKey(Integer.valueOf(nodeId))) {
                updateRancidNodeState(nodeId, false);
            } else {
                log().warn("node does not exist with nodeid: " + e.getNodeid());
            }
        }
    }

    @EventHandler(uei = EventConstants.RANCID_DOWNLOAD_SUCCESS_UEI)
    public void handleRancidDownLoadSuccess(Event e) {
        log().debug("handleRancidDownLoadSuccess: get Event uei/id: " + e.getUei() + "/" + e.getDbid());
        if (e.hasNodeid() ) {
            int nodeId = Long.valueOf(e.getNodeid()).intValue();
            if (m_onmsNodeRancidNodeMap.containsKey(Integer.valueOf(nodeId))) {
                updateRancidNodeState(nodeId, false);
            } else {
                log().warn("node does not exist with nodeid: " + e.getNodeid());
            }
        }
    }

    @EventHandler(uei = EventConstants.RANCID_GROUP_PROCESSING_COMPLETED_UEI)
    public void handleRancidGroupProcessingCompleted(Event e) {
        log().debug("handleRancidGroupProcessingCompleted: get Event uei/id: " + e.getUei() + "/" + e.getDbid());
        if (e.getParms() != null ) {
            Iterator<Parm> ite = e.getParms().iterateParm();
            while (ite.hasNext()) {
                Parm parm = ite.next();
                log().debug("handleRancidGroupProcessingCompleted: parm name: " + parm.getParmName());
                if (parm.getParmName().equals(".1.3.6.1.4.1.31543.1.1.2.1.1.3")) {
                    updateGroupConfiguration(parm.getValue().getContent());
                    break;
                }
            }
        }
    }
    
    private void updateGroupConfiguration(String group) {
        Iterator<Integer> ite = m_onmsNodeRancidNodeMap.keySet().iterator();
        while (ite.hasNext()) {
            Integer nodeId = ite.next();
            RancidNode rnode = m_onmsNodeRancidNodeMap.get(nodeId);
            if (group.equals(rnode.getGroup())) {
                updateConfiguration(nodeId.intValue(), rnode, m_cp, true);
            }
        }

    }
    
    private void updateRancidNodeState(int nodeid, boolean up) {
        RancidNode rnode = m_onmsNodeRancidNodeMap.get(Integer.valueOf(nodeid));
        rnode.setStateUp(up);
        m_onmsNodeRancidNodeMap.put(nodeid, rnode);
    }

    private boolean updateAuth(RancidNodeAuthentication localNode, RancidNodeAuthentication remoteNode) {
        if (!localNode.getUser().equals(remoteNode.getUser())) return true;
        if (!localNode.getPassword().equals(remoteNode.getPassword())) return true;
        if (!localNode.getEnablePass().equals(remoteNode.getEnablePass())) return true;
        if (!localNode.getConnectionMethodString().equalsIgnoreCase(remoteNode.getConnectionMethodString())) return true;
        if (localNode.isAutoEnable()) return !remoteNode.isAutoEnable();
        if (!localNode.isAutoEnable()) return remoteNode.isAutoEnable();
        return false;
    }
}
{'fraction_non_alphanumeric': 0.09704366879504255, 'fraction_numerical': 0.0022938528168170223, 'mean_word_length': 2.307365679669365, 'pattern_counts': {'":': 0, '<': 41, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 4}, 'pii_count': 6, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 22, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7665829', 'n_tokens_mistral': 16485, 'n_tokens_neox': 16043, 'n_words': 3283}