// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*-

// Copyright (c) 2001-2007 International Computer Science Institute
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software")
// to deal in the Software without restriction, subject to the conditions
// listed in the XORP LICENSE file. These conditions include: you must
// preserve this copyright notice, and you cannot mention the copyright
// holders in advertising related to the Software without their permission.
// The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
// notice is a summary of the XORP LICENSE file; the license in that file is
// legally binding.

#ident "$XORP: xorp/fea/xrl_ifmanager.cc,v 1.22 2007/02/16 22:45:52 pavlin Exp $"

#include "fea_module.h"

#include "libxorp/xorp.h"
#include "libxorp/xlog.h"
#include "libxorp/debug.h"

#include "xrl_ifmanager.hh"

static const char* MAX_TRANSACTIONS_HIT =
		"Resource limit on number of pending transactions hit.";

static const char* BAD_ID =
			"Expired or invalid transaction id presented.";

static const char* MISSING_IF = "Interface %s does not exist.";

static const char* MISSING_VIF = "Vif %s on interface %s does not exist.";

static const char* MISSING_ADDR =
			"Address %s on Vif %s on Interface %s does not exist.";

XrlCmdError
XrlInterfaceManager::get_if_from_config(const IfTree&	it,
					const string&		ifname,
					const IfTreeInterface*&	fi) const
{
    IfTree::IfMap::const_iterator ii = it.get_if(ifname);
    if (ii == it.ifs().end()) {
	return XrlCmdError::COMMAND_FAILED(c_format(MISSING_IF,
						    ifname.c_str()));
    }

    fi = &ii->second;
    return XrlCmdError::OKAY();
}

XrlCmdError
XrlInterfaceManager::get_vif_from_config(const IfTree&	it,
					 const string&		ifname,
					 const string&		vif,
					 const IfTreeVif*&	fv) const
{
    IfTree::IfMap::const_iterator ii = it.get_if(ifname);
    if (ii == it.ifs().end()) {
	return XrlCmdError::COMMAND_FAILED(c_format(MISSING_IF,
						    ifname.c_str()));
    }

    IfTreeInterface::VifMap::const_iterator vi = ii->second.get_vif(vif);
    if (vi == ii->second.vifs().end()) {
	return XrlCmdError::COMMAND_FAILED(c_format(MISSING_VIF,
						    vif.c_str(),
						    ifname.c_str()));
    }

    fv = &vi->second;
    return XrlCmdError::OKAY();
}

XrlCmdError
XrlInterfaceManager::get_addr_from_config(const IfTree&	it,
					  const string&		ifname,
					  const string&		vif,
					  const IPv4&		addr,
					  const IfTreeAddr4*&	fa) const
{
    IfTree::IfMap::const_iterator ii = it.get_if(ifname);
    if (ii == it.ifs().end()) {
	return XrlCmdError::COMMAND_FAILED(c_format(MISSING_IF,
						    ifname.c_str()));
    }

    IfTreeInterface::VifMap::const_iterator vi = ii->second.get_vif(vif);
    if (vi == ii->second.vifs().end()) {
	return XrlCmdError::COMMAND_FAILED(c_format(MISSING_VIF,
						    vif.c_str(),
						    ifname.c_str()));
    }

    IfTreeVif::V4Map::const_iterator ai = vi->second.get_addr(addr);
    if (ai == vi->second.v4addrs().end()) {
	return XrlCmdError::COMMAND_FAILED(c_format(MISSING_ADDR,
						    addr.str().c_str(),
						    vif.c_str(),
						    ifname.c_str()));
    }

    fa = &ai->second;
    return XrlCmdError::OKAY();
}

XrlCmdError
XrlInterfaceManager::get_addr_from_config(const IfTree&	it,
					  const string&		ifname,
					  const string&		vif,
					  const IPv6&		addr,
					  const IfTreeAddr6*&	fa) const
{
    IfTree::IfMap::const_iterator ii = it.get_if(ifname);
    if (ii == it.ifs().end()) {
	return XrlCmdError::COMMAND_FAILED(c_format(MISSING_IF,
						    ifname.c_str()));
    }

    IfTreeInterface::VifMap::const_iterator vi = ii->second.get_vif(vif);
    if (vi == ii->second.vifs().end()) {
	return XrlCmdError::COMMAND_FAILED(c_format(MISSING_VIF,
						    vif.c_str(),
						    ifname.c_str()));
    }

    IfTreeVif::V6Map::const_iterator ai = vi->second.get_addr(addr);
    if (ai == vi->second.v6addrs().end()) {
	return XrlCmdError::COMMAND_FAILED(c_format(MISSING_ADDR,
						    addr.str().c_str(),
						    vif.c_str(),
						    ifname.c_str()));
    }

    fa = &ai->second;
    return XrlCmdError::OKAY();
}

ProcessStatus
XrlInterfaceManager::status(string& reason) const
{
    if (_itm.pending() > 0) {
	reason = "There are transactions pending";
	return PROC_NOT_READY;
    }
    return PROC_READY;
}

XrlCmdError
XrlInterfaceManager::start_transaction(uint32_t& tid)
{
    if (_itm.start(tid))
	return XrlCmdError::OKAY();
    return XrlCmdError::COMMAND_FAILED(MAX_TRANSACTIONS_HIT);
}


XrlCmdError
XrlInterfaceManager::abort_transaction(uint32_t tid)
{
    if (_itm.abort(tid))
	return XrlCmdError::OKAY();
    return XrlCmdError::COMMAND_FAILED(BAD_ID);
}

XrlCmdError
XrlInterfaceManager::add(uint32_t tid, const Operation& op)
{
    uint32_t n_ops = 0;

    if (_itm.retrieve_size(tid, n_ops) == false)
	return XrlCmdError::COMMAND_FAILED(BAD_ID);

    if (_itm.add(tid, op))
	return XrlCmdError::OKAY();

    return XrlCmdError::COMMAND_FAILED("Unknown resource shortage");
}

XrlCmdError
XrlInterfaceManager::commit_transaction(uint32_t tid)
{
    IfTree& local_config = iftree();		// The current configuration
    IfTree& old_local_config = old_iftree();	// The previous configuration
    bool is_error = false;
    string errmsg;

    //
    // XXX: Pull in advance the current config, in case it is needed
    // by some of the transaction operations.
    //
    ifconfig().pull_config();

    if (_itm.commit(tid) != true)
	return XrlCmdError::COMMAND_FAILED(BAD_ID);

    if (_itm.error().empty() != true)
	return XrlCmdError::COMMAND_FAILED(_itm.error());

    //
    // If we get here we have updated the local copy of the config
    // successfully.
    //

    //
    // Push the configuration
    //
    do {
	if (ifconfig().push_config(local_config) == true)
	    break;		// Success

	errmsg = ifconfig().push_error();
	is_error = true;

	// Reverse-back to the previously working configuration
	IfTree restore_config = old_local_config;
	restore_config.prepare_replacement_state(local_config);
	local_config = restore_config;
	if (ifconfig().push_config(local_config) == true)
	    break;		// Continue with finalizing the reverse-back

	// Failed to reverse back
	string errmsg2 = ifconfig().push_error();
	errmsg2 = c_format("[Also, failed to reverse-back to the previous "
			   "config: %s]",
			   errmsg2.c_str());
	errmsg = errmsg + " " + errmsg2;
	break;
    } while (false);

    //
    // Pull the new device configuration
    //
    const IfTree& dev_config = ifconfig().pull_config();
    debug_msg("DEV CONFIG %s\n", dev_config.str().c_str());
    debug_msg("LOCAL CONFIG %s\n", local_config.str().c_str());

    //
    // Align with device configuration, so that any stuff that failed
    // in push is not held over in config.
    //
    local_config.align_with(dev_config);
    debug_msg("LOCAL CONFIG AFTER ALIGN %s\n", local_config.str().c_str());

    //
    // Prune deleted state that was never added earlier
    //
    local_config.prune_bogus_deleted_state(old_local_config);

    //
    // Propagate the configuration changes to all listeners.
    //
    if (! is_error)
	ifconfig().report_updates(local_config, false);

    //
    // Flush-out config state
    //
    local_config.finalize_state();

    //
    // Save a backup copy of the configuration state
    //
    if (! is_error) {
	old_local_config = local_config;
	old_local_config.finalize_state();
    }

    if (is_error)
	return XrlCmdError::COMMAND_FAILED(errmsg);
    else
	return XrlCmdError::OKAY();
}


syntax highlighted by Code2HTML, v. 0.9.1