// -*- 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/rib/redist_xrl.cc,v 1.30 2007/02/16 22:47:07 pavlin Exp $"
#include <list>
#include <string>
#include "rib_module.h"
#include "libxorp/xorp.h"
#include "libxorp/xlog.h"
#include "libxorp/debug.h"
#include "libxorp/safe_callback_obj.hh"
#include "libxipc/xrl_router.hh"
#include "xrl/interfaces/redist4_xif.hh"
#include "xrl/interfaces/redist6_xif.hh"
#include "xrl/interfaces/redist_transaction4_xif.hh"
#include "xrl/interfaces/redist_transaction6_xif.hh"
#include "route.hh"
#include "redist_xrl.hh"
#include "profile_vars.hh"
/**
* Base class for RedistXrlOutput Tasks. Classes derived from this
* store enough state to dispatch XRL, or other task, at some
* subsequent time in the future.
*/
template <typename A>
class RedistXrlTask : public CallbackSafeObject
{
public:
RedistXrlTask(RedistXrlOutput<A>* parent)
: _parent(parent), _attempts(0)
{}
virtual ~RedistXrlTask() {}
/**
* @return true on success, false if XrlRouter could not dispatch
* request.
*/
virtual bool dispatch(XrlRouter& xrl_router, Profile& profile) = 0;
/**
* Get number of times dispatch() invoked on instance.
*/
inline uint32_t dispatch_attempts() const { return _attempts; }
protected:
inline void incr_dispatch_attempts() { _attempts++; }
inline RedistXrlOutput<A>* parent() { return _parent; }
inline const RedistXrlOutput<A>* parent() const { return _parent; }
inline void signal_complete_ok() { _parent->task_completed(this); }
inline void signal_fatal_failure() { _parent->task_failed_fatally(this); }
private:
RedistXrlOutput<A>* _parent;
uint32_t _attempts;
};
// ----------------------------------------------------------------------------
// Task declarations
template <typename A>
class AddRoute : public RedistXrlTask<A>
{
public:
AddRoute(RedistXrlOutput<A>* parent, const IPRouteEntry<A>& ipr);
virtual bool dispatch(XrlRouter& xrl_router, Profile& profile);
void dispatch_complete(const XrlError& xe);
protected:
IPNet<A> _net;
A _nexthop;
string _ifname;
string _vifname;
uint32_t _metric;
uint32_t _admin_distance;
string _protocol_origin;
};
template <typename A>
class DeleteRoute : public RedistXrlTask<A>
{
public:
DeleteRoute(RedistXrlOutput<A>* parent, const IPRouteEntry<A>& ipr);
virtual bool dispatch(XrlRouter& xrl_router, Profile& profile);
void dispatch_complete(const XrlError& xe);
protected:
IPNet<A> _net;
A _nexthop;
string _ifname;
string _vifname;
uint32_t _metric;
uint32_t _admin_distance;
string _protocol_origin;
};
template <typename A>
class StartingRouteDump : public RedistXrlTask<A>
{
public:
StartingRouteDump(RedistXrlOutput<A>* parent);
virtual bool dispatch(XrlRouter& xrl_router, Profile& profile);
void dispatch_complete(const XrlError& xe);
};
template <typename A>
class FinishingRouteDump : public RedistXrlTask<A>
{
public:
FinishingRouteDump(RedistXrlOutput<A>* parent);
virtual bool dispatch(XrlRouter& xrl_router, Profile& profile);
void dispatch_complete(const XrlError& xe);
};
template <typename A>
class Pause : public RedistXrlTask<A>
{
public:
Pause(RedistXrlOutput<A>* parent, uint32_t ms);
virtual bool dispatch(XrlRouter& xrl_router, Profile& profile);
void expire();
private:
XorpTimer _t;
uint32_t _p_ms;
};
// ----------------------------------------------------------------------------
// AddRoute implementation
template <typename A>
AddRoute<A>::AddRoute(RedistXrlOutput<A>* parent, const IPRouteEntry<A>& ipr)
: RedistXrlTask<A>(parent),
_net(ipr.net()),
_nexthop(ipr.nexthop_addr()),
_ifname(ipr.vif()->ifname()),
_vifname(ipr.vif()->name()),
_metric(ipr.metric()),
_admin_distance(ipr.admin_distance()),
_protocol_origin(ipr.protocol().name())
{
}
template <>
bool
AddRoute<IPv4>::dispatch(XrlRouter& xrl_router, Profile& profile)
{
if (profile.enabled(profile_route_rpc_out))
profile.log(profile_route_rpc_out,
c_format("add %s", _net.str().c_str()));
RedistXrlOutput<IPv4>* p = this->parent();
XrlRedist4V0p1Client cl(&xrl_router);
return cl.send_add_route(p->xrl_target_name().c_str(),
_net, _nexthop, _ifname, _vifname, _metric,
_admin_distance, p->cookie(),
_protocol_origin,
callback(this, &AddRoute<IPv4>::dispatch_complete)
);
}
template <>
bool
AddRoute<IPv6>::dispatch(XrlRouter& xrl_router, Profile& profile)
{
if (profile.enabled(profile_route_rpc_out))
profile.log(profile_route_rpc_out,
c_format("add %s", _net.str().c_str()));
RedistXrlOutput<IPv6>* p = this->parent();
XrlRedist6V0p1Client cl(&xrl_router);
return cl.send_add_route(p->xrl_target_name().c_str(),
_net, _nexthop, _ifname, _vifname, _metric,
_admin_distance, p->cookie(),
_protocol_origin,
callback(this, &AddRoute<IPv6>::dispatch_complete)
);
}
template <typename A>
void
AddRoute<A>::dispatch_complete(const XrlError& xe)
{
if (xe == XrlError::OKAY()) {
this->signal_complete_ok();
return;
} else if (xe == XrlError::COMMAND_FAILED()) {
XLOG_ERROR("Failed to redistribute route add for %s: %s",
_net.str().c_str(),
xe.str().c_str());
this->signal_complete_ok();
return;
}
// For now all errors are signalled fatal
XLOG_ERROR("Fatal error during route redistribution: %s",
xe.str().c_str());
this->signal_fatal_failure();
}
// ----------------------------------------------------------------------------
// DeleteRoute implementation
template <typename A>
DeleteRoute<A>::DeleteRoute(RedistXrlOutput<A>* parent,
const IPRouteEntry<A>& ipr)
: RedistXrlTask<A>(parent),
_net(ipr.net()),
_nexthop(ipr.nexthop_addr()),
_ifname(ipr.vif()->ifname()),
_vifname(ipr.vif()->name()),
_metric(ipr.metric()),
_admin_distance(ipr.admin_distance()),
_protocol_origin(ipr.protocol().name())
{
}
template <>
bool
DeleteRoute<IPv4>::dispatch(XrlRouter& xrl_router, Profile& profile)
{
if (profile.enabled(profile_route_rpc_out))
profile.log(profile_route_rpc_out,
c_format("delete %s", _net.str().c_str()));
RedistXrlOutput<IPv4>* p = this->parent();
XrlRedist4V0p1Client cl(&xrl_router);
return cl.send_delete_route(p->xrl_target_name().c_str(),
_net, _nexthop, _ifname, _vifname, _metric,
_admin_distance, p->cookie(),
_protocol_origin,
callback(this,
&DeleteRoute<IPv4>::dispatch_complete)
);
}
template <>
bool
DeleteRoute<IPv6>::dispatch(XrlRouter& xrl_router, Profile& profile)
{
if (profile.enabled(profile_route_rpc_out))
profile.log(profile_route_rpc_out,
c_format("delete %s", _net.str().c_str()));
RedistXrlOutput<IPv6>* p = this->parent();
XrlRedist6V0p1Client cl(&xrl_router);
return cl.send_delete_route(p->xrl_target_name().c_str(),
_net, _nexthop, _ifname, _vifname, _metric,
_admin_distance, p->cookie(),
_protocol_origin,
callback(this,
&DeleteRoute<IPv6>::dispatch_complete)
);
}
template <typename A>
void
DeleteRoute<A>::dispatch_complete(const XrlError& xe)
{
if (xe == XrlError::OKAY()) {
this->signal_complete_ok();
return;
} else if (xe == XrlError::COMMAND_FAILED()) {
XLOG_ERROR("Failed to redistribute route delete for %s: %s",
_net.str().c_str(),
xe.str().c_str());
this->signal_complete_ok();
return;
}
// XXX For now all errors signalled as fatal
XLOG_ERROR("Fatal error during route redistribution: %s",
xe.str().c_str());
this->signal_fatal_failure();
}
// ----------------------------------------------------------------------------
// StartingRouteDump implementation
template <typename A>
StartingRouteDump<A>::StartingRouteDump(RedistXrlOutput<A>* parent)
: RedistXrlTask<A>(parent)
{
}
template <>
bool
StartingRouteDump<IPv4>::dispatch(XrlRouter& xrl_router, Profile&)
{
RedistXrlOutput<IPv4>* p = this->parent();
XrlRedist4V0p1Client cl(&xrl_router);
return cl.send_starting_route_dump(
p->xrl_target_name().c_str(),
p->cookie(),
callback(this, &StartingRouteDump<IPv4>::dispatch_complete)
);
}
template <>
bool
StartingRouteDump<IPv6>::dispatch(XrlRouter& xrl_router, Profile&)
{
RedistXrlOutput<IPv6>* p = this->parent();
XrlRedist6V0p1Client cl(&xrl_router);
return cl.send_starting_route_dump(
p->xrl_target_name().c_str(),
p->cookie(),
callback(this, &StartingRouteDump<IPv6>::dispatch_complete)
);
}
template <typename A>
void
StartingRouteDump<A>::dispatch_complete(const XrlError& xe)
{
if (xe == XrlError::OKAY()) {
this->signal_complete_ok();
return;
} else if (xe == XrlError::COMMAND_FAILED()) {
XLOG_ERROR("Failed to send starting route dump: %s",
xe.str().c_str());
this->signal_complete_ok();
return;
}
// XXX For now all errors signalled as fatal
XLOG_ERROR("Fatal error during route redistribution: %s",
xe.str().c_str());
this->signal_fatal_failure();
}
// ----------------------------------------------------------------------------
// FinishingRouteDump implementation
template <typename A>
FinishingRouteDump<A>::FinishingRouteDump(RedistXrlOutput<A>* parent)
: RedistXrlTask<A>(parent)
{
}
template <>
bool
FinishingRouteDump<IPv4>::dispatch(XrlRouter& xrl_router, Profile&)
{
RedistXrlOutput<IPv4>* p = this->parent();
XrlRedist4V0p1Client cl(&xrl_router);
return cl.send_finishing_route_dump(
p->xrl_target_name().c_str(),
p->cookie(),
callback(this, &FinishingRouteDump<IPv4>::dispatch_complete)
);
}
template <>
bool
FinishingRouteDump<IPv6>::dispatch(XrlRouter& xrl_router, Profile&)
{
RedistXrlOutput<IPv6>* p = this->parent();
XrlRedist6V0p1Client cl(&xrl_router);
return cl.send_finishing_route_dump(
p->xrl_target_name().c_str(),
p->cookie(),
callback(this, &FinishingRouteDump<IPv6>::dispatch_complete)
);
}
template <typename A>
void
FinishingRouteDump<A>::dispatch_complete(const XrlError& xe)
{
if (xe == XrlError::OKAY()) {
this->signal_complete_ok();
return;
} else if (xe == XrlError::COMMAND_FAILED()) {
XLOG_ERROR("Failed to send finishing route dump: %s",
xe.str().c_str());
this->signal_complete_ok();
return;
}
// XXX For now all errors signalled as fatal
XLOG_ERROR("Fatal error during route redistribution: %s",
xe.str().c_str());
this->signal_fatal_failure();
}
// ----------------------------------------------------------------------------
// Pause implementation
template <typename A>
Pause<A>::Pause(RedistXrlOutput<A>* parent, uint32_t ms)
: RedistXrlTask<A>(parent), _p_ms(ms)
{
}
template <typename A>
bool
Pause<A>::dispatch(XrlRouter& xrl_router, Profile&)
{
this->incr_dispatch_attempts();
EventLoop& e = xrl_router.eventloop();
_t = e.new_oneoff_after_ms(_p_ms, callback(this, &Pause<A>::expire));
return true;
}
template <typename A>
void
Pause<A>::expire()
{
this->signal_complete_ok();
}
// ----------------------------------------------------------------------------
// RedistXrlOutput implementation
template <typename A>
RedistXrlOutput<A>::RedistXrlOutput(Redistributor<A>* redistributor,
XrlRouter& xrl_router,
Profile& profile,
const string& from_protocol,
const string& xrl_target_name,
const IPNet<A>& network_prefix,
const string& cookie)
: RedistOutput<A>(redistributor), _xrl_router(xrl_router),
_profile(profile),
_from_protocol(from_protocol), _target_name(xrl_target_name),
_network_prefix(network_prefix), _cookie(cookie), _queued(0),
_inflight(0), _flow_controlled(0), _callback_pending(0)
{
}
template <typename A>
RedistXrlOutput<A>::~RedistXrlOutput()
{
while (_taskq.empty() == false) {
delete _taskq.front();
_taskq.pop_front();
}
while (_flyingq.empty() == false) {
delete _flyingq.front();
_flyingq.pop_front();
}
}
template <typename A>
void
RedistXrlOutput<A>::enqueue_task(Task* task)
{
_taskq.push_back(task);
_queued++;
}
template <typename A>
void
RedistXrlOutput<A>::add_route(const IPRouteEntry<A>& ipr)
{
if (! _network_prefix.contains(ipr.net()))
return; // The target is not interested in this route
if (_profile.enabled(profile_route_rpc_in))
_profile.log(profile_route_rpc_in,
c_format("add %s", ipr.net().str().c_str()));
enqueue_task(new AddRoute<A>(this, ipr));
if (_queued == 1)
start_next_task();
}
template <typename A>
void
RedistXrlOutput<A>::delete_route(const IPRouteEntry<A>& ipr)
{
if (! _network_prefix.contains(ipr.net()))
return; // The target is not interested in this route
if (_profile.enabled(profile_route_rpc_in))
_profile.log(profile_route_rpc_in,
c_format("delete %s", ipr.net().str().c_str()));
enqueue_task(new DeleteRoute<A>(this, ipr));
if (_queued == 1)
start_next_task();
}
template <typename A>
void
RedistXrlOutput<A>::starting_route_dump()
{
enqueue_task(new StartingRouteDump<A>(this));
if (_queued == 1)
start_next_task();
}
template <typename A>
void
RedistXrlOutput<A>::finishing_route_dump()
{
enqueue_task(new FinishingRouteDump<A>(this));
if (_queued == 1)
start_next_task();
}
template <typename A>
void
RedistXrlOutput<A>::start_next_task()
{
XLOG_ASSERT(_queued >= 1);
if (_inflight)
return;
while (_queued && !_flow_controlled && !_callback_pending) {
RedistXrlTask<A>* t = _taskq.front();
if (t->dispatch(_xrl_router, _profile) == false) {
// Dispatch of task failed. XrlRouter is presumeably
// backlogged.
XLOG_WARNING("Dispatch failed, %d XRLs inflight", _inflight);
if (_inflight == 0) {
// Insert a delay and dispatch that to cause later
// attempt at failing task.
// This should never happen under normal circumstances!
t = new Pause<A>(this, RETRY_PAUSE_MS);
t->dispatch(_xrl_router, _profile);
incr_inflight();
}
_flow_controlled = true;
return;
} else {
incr_inflight();
_flyingq.push_back(t);
_taskq.pop_front();
_queued--;
}
}
}
template <typename A>
void
RedistXrlOutput<A>::task_completed(RedistXrlTask<A>* task)
{
if (task == _flyingq.front())
_flyingq.pop_front();
else {
XLOG_WARNING("task != _flyingq.front()");
_flyingq.remove(task);
}
decr_inflight();
delete task;
if (this->_queued != 0)
this->start_next_task();
}
template <typename A>
void
RedistXrlOutput<A>::task_failed_fatally(RedistXrlTask<A>* task)
{
if (task == _flyingq.front())
_flyingq.pop_front();
else {
XLOG_WARNING("task != _flyingq.front()");
_flyingq.remove(task);
}
decr_inflight();
delete task;
this->announce_fatal_error();
}
// ----------------------------------------------------------------------------
// RedistTransactionXrlOutput Commands
template <typename A>
class AddTransactionRoute : public AddRoute<A> {
public:
AddTransactionRoute(RedistTransactionXrlOutput<A>* parent,
const IPRouteEntry<A>& ipr)
: AddRoute<A>(parent, ipr) {
parent->incr_transaction_size();
}
virtual bool dispatch(XrlRouter& xrl_router, Profile& profile);
};
template <typename A>
class DeleteTransactionRoute : public DeleteRoute<A> {
public:
DeleteTransactionRoute(RedistTransactionXrlOutput<A>* parent,
const IPRouteEntry<A>& ipr)
: DeleteRoute<A>(parent, ipr) {
parent->incr_transaction_size();
}
virtual bool dispatch(XrlRouter& xrl_router, Profile& profile);
};
template <typename A>
class StartTransaction : public RedistXrlTask<A> {
public:
StartTransaction(RedistTransactionXrlOutput<A>* parent)
: RedistXrlTask<A>(parent) {
parent->reset_transaction_size();
}
virtual bool dispatch(XrlRouter& xrl_router, Profile& profile);
void dispatch_complete(const XrlError& xe, const uint32_t* tid);
};
template <typename A>
class CommitTransaction : public RedistXrlTask<A> {
public:
CommitTransaction(RedistTransactionXrlOutput<A>* parent)
: RedistXrlTask<A>(parent) {
parent->reset_transaction_size();
}
virtual bool dispatch(XrlRouter& xrl_router, Profile& profile);
void dispatch_complete(const XrlError& xe);
};
template <typename A>
class AbortTransaction : public RedistXrlTask<A> {
public:
AbortTransaction(RedistTransactionXrlOutput<A>* parent)
: RedistXrlTask<A>(parent) {
parent->reset_transaction_size();
}
virtual bool dispatch(XrlRouter& xrl_router, Profile& profile);
void dispatch_complete(const XrlError& xe);
};
// ----------------------------------------------------------------------------
// AddTransactionRoute implementation
template <>
bool
AddTransactionRoute<IPv4>::dispatch(XrlRouter& xrl_router, Profile& profile)
{
RedistTransactionXrlOutput<IPv4>* p =
reinterpret_cast<RedistTransactionXrlOutput<IPv4>*>(this->parent());
if (p->transaction_in_error() || ! p->transaction_in_progress()) {
XLOG_ERROR("Transaction error: failed to redistribute "
"route add for %s", _net.str().c_str());
this->signal_complete_ok();
return true; // XXX: we return true to avoid retransmission
}
if (profile.enabled(profile_route_rpc_out))
profile.log(profile_route_rpc_out,
c_format("add %s %s %s %u",
p->xrl_target_name().c_str(),
_net.str().c_str(),
_nexthop.str().c_str(),
XORP_UINT_CAST(_metric)));
XrlRedistTransaction4V0p1Client cl(&xrl_router);
return cl.send_add_route(p->xrl_target_name().c_str(),
p->tid(),
_net, _nexthop, _ifname, _vifname, _metric,
_admin_distance, p->cookie(),
_protocol_origin,
callback(static_cast<AddRoute<IPv4>*>(this),
&AddRoute<IPv4>::dispatch_complete)
);
}
template <>
bool
AddTransactionRoute<IPv6>::dispatch(XrlRouter& xrl_router, Profile& profile)
{
RedistTransactionXrlOutput<IPv6>* p =
reinterpret_cast<RedistTransactionXrlOutput<IPv6>*>(this->parent());
if (p->transaction_in_error() || ! p->transaction_in_progress()) {
XLOG_ERROR("Transaction error: failed to redistribute "
"route add for %s", _net.str().c_str());
this->signal_complete_ok();
return true; // XXX: we return true to avoid retransmission
}
if (profile.enabled(profile_route_rpc_out))
profile.log(profile_route_rpc_out,
c_format("add %s %s %s %u",
p->xrl_target_name().c_str(),
_net.str().c_str(),
_nexthop.str().c_str(),
XORP_UINT_CAST(_metric)));
XrlRedistTransaction6V0p1Client cl(&xrl_router);
return cl.send_add_route(p->xrl_target_name().c_str(),
p->tid(),
_net, _nexthop, _ifname, _vifname, _metric,
_admin_distance, p->cookie(),
_protocol_origin,
callback(static_cast<AddRoute<IPv6>*>(this),
&AddRoute<IPv6>::dispatch_complete)
);
}
// ----------------------------------------------------------------------------
// DeleteTransactionRoute implementation
template <>
bool
DeleteTransactionRoute<IPv4>::dispatch(XrlRouter& xrl_router, Profile& profile)
{
RedistTransactionXrlOutput<IPv4>* p =
reinterpret_cast<RedistTransactionXrlOutput<IPv4>*>(this->parent());
if (p->transaction_in_error() || ! p->transaction_in_progress()) {
XLOG_ERROR("Transaction error: failed to redistribute "
"route delete for %s", _net.str().c_str());
this->signal_complete_ok();
return true; // XXX: we return true to avoid retransmission
}
if (profile.enabled(profile_route_rpc_out))
profile.log(profile_route_rpc_out,
c_format("delete %s %s",
p->xrl_target_name().c_str(),
_net.str().c_str()));
XrlRedistTransaction4V0p1Client cl(&xrl_router);
return cl.send_delete_route(p->xrl_target_name().c_str(),
p->tid(),
_net, _nexthop, _ifname, _vifname, _metric,
_admin_distance, p->cookie(),
_protocol_origin,
callback(static_cast<DeleteRoute<IPv4>*>(this),
&DeleteRoute<IPv4>::dispatch_complete)
);
}
template <>
bool
DeleteTransactionRoute<IPv6>::dispatch(XrlRouter& xrl_router, Profile& profile)
{
RedistTransactionXrlOutput<IPv6>* p =
reinterpret_cast<RedistTransactionXrlOutput<IPv6>*>(this->parent());
if (p->transaction_in_error() || ! p->transaction_in_progress()) {
XLOG_ERROR("Transaction error: failed to redistribute "
"route delete for %s", _net.str().c_str());
this->signal_complete_ok();
return true; // XXX: we return true to avoid retransmission
}
if (profile.enabled(profile_route_rpc_out))
profile.log(profile_route_rpc_out,
c_format("delete %s %s",
p->xrl_target_name().c_str(),
_net.str().c_str()));
XrlRedistTransaction6V0p1Client cl(&xrl_router);
return cl.send_delete_route(p->xrl_target_name().c_str(),
p->tid(),
_net, _nexthop, _ifname, _vifname, _metric,
_admin_distance, p->cookie(),
_protocol_origin,
callback(static_cast<DeleteRoute<IPv6>*>(this),
&DeleteRoute<IPv6>::dispatch_complete)
);
}
// ----------------------------------------------------------------------------
// StartTransaction implementation
template <>
bool
StartTransaction<IPv4>::dispatch(XrlRouter& xrl_router, Profile&)
{
RedistTransactionXrlOutput<IPv4>* p =
reinterpret_cast<RedistTransactionXrlOutput<IPv4>*>(this->parent());
p->set_callback_pending(true);
p->set_tid(0);
p->set_transaction_in_progress(true);
p->set_transaction_in_error(false);
XrlRedistTransaction4V0p1Client cl(&xrl_router);
return cl.send_start_transaction(
p->xrl_target_name().c_str(),
callback(this, &StartTransaction<IPv4>::dispatch_complete));
}
template <>
bool
StartTransaction<IPv6>::dispatch(XrlRouter& xrl_router, Profile&)
{
RedistTransactionXrlOutput<IPv6>* p =
reinterpret_cast<RedistTransactionXrlOutput<IPv6>*>(this->parent());
p->set_callback_pending(true);
p->set_tid(0);
p->set_transaction_in_progress(true);
p->set_transaction_in_error(false);
XrlRedistTransaction6V0p1Client cl(&xrl_router);
return cl.send_start_transaction(
p->xrl_target_name().c_str(),
callback(this, &StartTransaction<IPv6>::dispatch_complete));
}
template <typename A>
void
StartTransaction<A>::dispatch_complete(const XrlError& xe, const uint32_t* tid)
{
RedistTransactionXrlOutput<A>* p =
reinterpret_cast<RedistTransactionXrlOutput<A>*>(this->parent());
p->set_callback_pending(false);
if (xe == XrlError::OKAY()) {
p->set_tid(*tid);
this->signal_complete_ok();
return;
} else if (xe == XrlError::COMMAND_FAILED()) {
XLOG_ERROR("Failed to start transaction: %s", xe.str().c_str());
p->set_transaction_in_progress(false);
p->set_transaction_in_error(true);
this->signal_complete_ok();
return;
}
// For now all errors are signalled fatal
XLOG_ERROR("Fatal error during start transaction: %s",
xe.str().c_str());
this->signal_fatal_failure();
}
// ----------------------------------------------------------------------------
// CommitTransaction implementation
template <>
bool
CommitTransaction<IPv4>::dispatch(XrlRouter& xrl_router, Profile&)
{
RedistTransactionXrlOutput<IPv4>* p =
reinterpret_cast<RedistTransactionXrlOutput<IPv4>*>(this->parent());
uint32_t tid = p->tid();
p->set_tid(0); // XXX: reset the tid
p->set_transaction_in_progress(false);
p->set_transaction_in_error(false);
XrlRedistTransaction4V0p1Client cl(&xrl_router);
return cl.send_commit_transaction(
p->xrl_target_name().c_str(),
tid,
callback(this, &CommitTransaction<IPv4>::dispatch_complete));
}
template <>
bool
CommitTransaction<IPv6>::dispatch(XrlRouter& xrl_router, Profile&)
{
RedistTransactionXrlOutput<IPv6>* p =
reinterpret_cast<RedistTransactionXrlOutput<IPv6>*>(this->parent());
uint32_t tid = p->tid();
p->set_tid(0); // XXX: reset the tid
p->set_transaction_in_progress(false);
p->set_transaction_in_error(false);
XrlRedistTransaction6V0p1Client cl(&xrl_router);
return cl.send_commit_transaction(
p->xrl_target_name().c_str(),
tid,
callback(this, &CommitTransaction<IPv6>::dispatch_complete));
}
template <typename A>
void
CommitTransaction<A>::dispatch_complete(const XrlError& xe)
{
if (xe == XrlError::OKAY()) {
this->signal_complete_ok();
return;
} else if (xe == XrlError::COMMAND_FAILED()) {
XLOG_ERROR("Failed to commit transaction: %s", xe.str().c_str());
this->signal_complete_ok();
return;
}
// For now all errors are signalled fatal
XLOG_ERROR("Fatal error during commit transaction: %s",
xe.str().c_str());
this->signal_fatal_failure();
}
// ----------------------------------------------------------------------------
// AbortTransaction implementation
template <>
bool
AbortTransaction<IPv4>::dispatch(XrlRouter& xrl_router, Profile&)
{
RedistTransactionXrlOutput<IPv4>* p =
reinterpret_cast<RedistTransactionXrlOutput<IPv4>*>(this->parent());
uint32_t tid = p->tid();
p->set_tid(0); // XXX: reset the tid
p->set_transaction_in_progress(false);
p->set_transaction_in_error(false);
XrlRedistTransaction4V0p1Client cl(&xrl_router);
return cl.send_abort_transaction(
p->xrl_target_name().c_str(),
tid,
callback(this, &AbortTransaction<IPv4>::dispatch_complete));
}
template <>
bool
AbortTransaction<IPv6>::dispatch(XrlRouter& xrl_router, Profile&)
{
RedistTransactionXrlOutput<IPv6>* p =
reinterpret_cast<RedistTransactionXrlOutput<IPv6>*>(this->parent());
uint32_t tid = p->tid();
p->set_tid(0); // XXX: reset the tid
p->set_transaction_in_progress(false);
p->set_transaction_in_error(false);
XrlRedistTransaction6V0p1Client cl(&xrl_router);
return cl.send_abort_transaction(
p->xrl_target_name().c_str(),
tid,
callback(this, &AbortTransaction<IPv6>::dispatch_complete));
}
template <typename A>
void
AbortTransaction<A>::dispatch_complete(const XrlError& xe)
{
if (xe == XrlError::OKAY()) {
this->signal_complete_ok();
return;
} else if (xe == XrlError::COMMAND_FAILED()) {
XLOG_ERROR("Failed to abort transaction: %s", xe.str().c_str());
this->signal_complete_ok();
return;
}
// For now all errors are signalled fatal
XLOG_ERROR("Fatal error during abort transaction: %s",
xe.str().c_str());
this->signal_fatal_failure();
}
// ----------------------------------------------------------------------------
// RedistTransactionXrlOutput implementation
template <typename A>
RedistTransactionXrlOutput<A>::RedistTransactionXrlOutput(
Redistributor<A>* redistributor,
XrlRouter& xrl_router,
Profile& profile,
const string& from_protocol,
const string& xrl_target_name,
const IPNet<A>& network_prefix,
const string& cookie
)
: RedistXrlOutput<A>(redistributor, xrl_router, profile, from_protocol,
xrl_target_name, network_prefix, cookie),
_tid(0),
_transaction_in_progress(false),
_transaction_in_error(false),
_transaction_size(0)
{
}
template <typename A>
void
RedistTransactionXrlOutput<A>::add_route(const IPRouteEntry<A>& ipr)
{
if (this->_profile.enabled(profile_route_rpc_in))
this->_profile.log(profile_route_rpc_in,
c_format("add %s %s %s %u",
ipr.protocol().name().c_str(),
ipr.net().str().c_str(),
ipr.nexthop()->str().c_str(),
XORP_UINT_CAST(ipr.metric())));
bool no_running_tasks = (this->_queued == 0);
if (this->transaction_size() == 0)
this->enqueue_task(new StartTransaction<A>(this));
//
// If the accumulated transaction size is too large, commit the
// current transaction and start a new one.
//
if (this->transaction_size() >= MAX_TRANSACTION_SIZE) {
enqueue_task(new CommitTransaction<A>(this));
enqueue_task(new StartTransaction<A>(this));
}
enqueue_task(new AddTransactionRoute<A>(this, ipr));
if (no_running_tasks)
this->start_next_task();
}
template <typename A>
void
RedistTransactionXrlOutput<A>::delete_route(const IPRouteEntry<A>& ipr)
{
if (this->_profile.enabled(profile_route_rpc_in))
this->_profile.log(profile_route_rpc_in,
c_format("add %s %s",
ipr.protocol().name().c_str(),
ipr.net().str().c_str()));
bool no_running_tasks = (this->_queued == 0);
if (this->transaction_size() == 0)
enqueue_task(new StartTransaction<A>(this));
//
// If the accumulated transaction size is too large, commit the
// current transaction and start a new one.
//
if (this->transaction_size() >= MAX_TRANSACTION_SIZE) {
enqueue_task(new CommitTransaction<A>(this));
enqueue_task(new StartTransaction<A>(this));
}
enqueue_task(new DeleteTransactionRoute<A>(this, ipr));
if (no_running_tasks)
this->start_next_task();
}
template <typename A>
void
RedistTransactionXrlOutput<A>::starting_route_dump()
{
}
template <typename A>
void
RedistTransactionXrlOutput<A>::finishing_route_dump()
{
}
template <typename A>
void
RedistTransactionXrlOutput<A>::task_completed(Task* task)
{
if (task == this->_flyingq.front())
this->_flyingq.pop_front();
else {
XLOG_WARNING("task != this->_flyingq.front()");
this->_flyingq.remove(task);
}
this->decr_inflight();
delete task;
if (this->_queued != 0) {
this->start_next_task();
return;
}
if (transaction_in_progress()) {
//
// If transaction in progress, and this is the last add/delete,
// then send "commit transaction".
//
enqueue_task(new CommitTransaction<A>(this));
this->start_next_task();
return;
}
}
// ----------------------------------------------------------------------------
// Instantiations
template class RedistXrlOutput<IPv4>;
template class RedistXrlOutput<IPv6>;
template class RedistTransactionXrlOutput<IPv4>;
template class RedistTransactionXrlOutput<IPv6>;
syntax highlighted by Code2HTML, v. 0.9.1