/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */ /* * Copyright (c) 2018 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ #include #include #include #include #include #include #include #include "VppContractManager.hpp" #include "VppEndPointGroupManager.hpp" #include "VppEndPointManager.hpp" #include "VppExtItfManager.hpp" #include "VppIdGen.hpp" #include "VppLog.hpp" #include "VppManager.hpp" #include "VppRouteManager.hpp" #include "VppSecurityGroupManager.hpp" #include using std::bind; using boost::asio::placeholders::error; namespace VPP { /** * An owner of the objects VPP learns during boot-up */ static const std::string BOOT_KEY = "__boot__"; VppManager::VppManager(opflexagent::Agent &agent_, opflexagent::IdGenerator &idGen_, VOM::HW::cmd_q *q, VOM::stat_reader *sr) : m_runtime(agent_, idGen_) , m_task_queue(agent_.getAgentIOService()) , stopping(false) { VOM::HW::init(q, sr); VOM::OM::init(); m_runtime.system_name = boost::asio::ip::host_name(); m_runtime.agent.getFramework().registerPeerStatusListener(this); } VppManager::~VppManager() { VLOGE << "VppManager exiting"; } void VppManager::start() { VLOGI << "start vpp manager; mode:" << (int)m_runtime.agent.getRendererForwardingMode(); /* * create the update delegators */ m_runtime.is_transport_mode = (opflex::ofcore::OFConstants::TRANSPORT_MODE == m_runtime.agent.getRendererForwardingMode()); m_epm = std::make_shared(m_runtime); m_epgm = std::make_shared(m_runtime); m_sgm = std::make_shared(m_runtime.agent); m_cm = std::make_shared(m_runtime.agent, m_runtime.id_gen); m_rdm = std::make_shared(m_runtime); m_eim = std::make_shared(m_runtime); initPlatformConfig(); /* * make sure the first event in the task Q is the blocking * connection initiation to VPP ... */ m_task_queue.dispatch("init-connection", bind(&VppManager::handleInitConnection, this)); /** * DO BOOT */ /** * ... followed by vpp boot dump */ m_task_queue.dispatch("boot-dump", bind(&VppManager::handleBoot, this)); /** * ... followed by uplink configuration */ m_task_queue.dispatch("uplink-configure", bind(&VppManager::handleUplinkConfigure, this)); /** * ... followed by cross connect configuration */ m_task_queue.dispatch("xconnect-configure", bind(&VppManager::handleXConnectConfigure, this)); } void VppManager::handleCloseConnection() { if (!hw_connected) return; VOM::interface::disable_events(); VOM::HW::disconnect(); VLOGD << "Close VPP connection"; } void VppManager::handleInitConnection() { if (stopping) return; VLOGD << "Open VPP connection"; while (VOM::HW::connect() != true) ; hw_connected = true; /** * We are insterested in getting interface events from VPP */ VOM::interface::enable_events(*this); /** * Scehdule a timer to Poll for HW livensss */ m_poll_timer.reset( new boost::asio::deadline_timer(m_runtime.agent.getAgentIOService())); m_poll_timer->expires_from_now(boost::posix_time::seconds(3)); m_poll_timer->async_wait(bind(&VppManager::handleHWPollTimer, this, error)); /** * Scehdule a timer for HW stats */ m_stats_timer.reset( new boost::asio::deadline_timer(m_runtime.agent.getAgentIOService())); m_stats_timer->expires_from_now(boost::posix_time::seconds(5)); m_stats_timer->async_wait( bind(&VppManager::handleHWStatsTimer, this, error)); } void VppManager::handleUplinkConfigure() { if (stopping) return; m_runtime.uplink.configure(m_runtime.system_name); } void VppManager::handleXConnectConfigure() { if (stopping) return; m_xconnect.configure_xconnect(); } void VppManager::handleSweepTimer(const boost::system::error_code &ec) { if (stopping || ec) return; VLOGI << "sweep boot data"; /* * the sweep timer was not cancelled, continue with purging old state. */ if (hw_connected) VOM::OM::sweep(BOOT_KEY); else if (!stopping) { m_sweep_timer.reset(new boost::asio::deadline_timer( m_runtime.agent.getAgentIOService())); m_sweep_timer->expires_from_now(boost::posix_time::seconds(30)); m_sweep_timer->async_wait( bind(&VppManager::handleSweepTimer, this, error)); } } void VppManager::handleHWPollTimer(const boost::system::error_code &ec) { if (stopping || ec) return; if (hw_connected && VOM::HW::poll()) { /* * re-scehdule a timer to Poll for HW liveness */ m_poll_timer.reset(new boost::asio::deadline_timer( m_runtime.agent.getAgentIOService())); m_poll_timer->expires_from_now(boost::posix_time::seconds(3)); m_poll_timer->async_wait( bind(&VppManager::handleHWPollTimer, this, error)); return; } hw_connected = false; VOM::HW::disconnect(); VLOGD << "Reconnecting ...."; if (VOM::HW::connect()) { VLOGD << "Replay the state after reconnecting ..."; VOM::OM::replay(); hw_connected = true; } if (!stopping) { m_poll_timer.reset(new boost::asio::deadline_timer( m_runtime.agent.getAgentIOService())); m_poll_timer->expires_from_now(boost::posix_time::seconds(1)); m_poll_timer->async_wait( bind(&VppManager::handleHWPollTimer, this, error)); } else { VOM::HW::disconnect(); } } void VppManager::handleHWStatsTimer(const boost::system::error_code &ec) { if (stopping || ec) return; VLOGD << "stats reading"; VOM::HW::read_stats(); m_stats_timer.reset( new boost::asio::deadline_timer(m_runtime.agent.getAgentIOService())); m_stats_timer->expires_from_now(boost::posix_time::seconds(5)); m_stats_timer->async_wait( bind(&VppManager::handleHWStatsTimer, this, error)); } void VppManager::handleBoot() { if (stopping) return; /** * Read the state from VPP */ VOM::OM::populate(BOOT_KEY); } void VppManager::registerModbListeners() { // Initialize policy listeners m_runtime.agent.getEndpointManager().registerListener(this); m_runtime.agent.getServiceManager().registerListener(this); m_runtime.agent.getExtraConfigManager().registerListener(this); m_runtime.agent.getPolicyManager().registerListener(this); } void VppManager::stop() { stopping = true; m_runtime.agent.getEndpointManager().unregisterListener(this); m_runtime.agent.getServiceManager().unregisterListener(this); m_runtime.agent.getExtraConfigManager().unregisterListener(this); m_runtime.agent.getPolicyManager().unregisterListener(this); if (m_stats_timer) { m_stats_timer->cancel(); } if (m_sweep_timer) { m_sweep_timer->cancel(); } if (m_poll_timer) { m_poll_timer->cancel(); } m_task_queue.dispatch("close-connection", bind(&VppManager::handleCloseConnection, this)); VLOGD << "stop VppManager"; } void VppManager::setVirtualRouter(bool virtualRouterEnabled, bool routerAdv, const std::string &virtualRouterMac) { if (virtualRouterEnabled) { m_runtime.vr = std::make_shared(virtualRouterMac); } } void VppManager::endpointUpdated(const std::string &uuid) { if (stopping) return; m_task_queue.dispatch(uuid, bind(&EndPointManager::handle_update, m_epm, uuid)); } void VppManager::externalEndpointUpdated(const std::string &uuid) { if (stopping) return; m_task_queue.dispatch( uuid, bind(&EndPointManager::handle_external_update, m_epm, uuid)); } void VppManager::remoteEndpointUpdated(const std::string &uuid) { if (stopping) return; m_task_queue.dispatch( uuid, bind(&EndPointManager::handle_remote_update, m_epm, uuid)); } void VppManager::serviceUpdated(const std::string &uuid) { if (stopping) return; VLOGI << "Service Update Not supported "; } void VppManager::rdConfigUpdated(const opflex::modb::URI &rdURI) { m_task_queue.dispatch( rdURI.toString(), bind(&RouteManager::handle_domain_update, m_rdm, rdURI)); } void VppManager::egDomainUpdated(const opflex::modb::URI &egURI) { if (stopping) return; m_task_queue.dispatch( egURI.toString(), bind(&EndPointGroupManager::handle_update, m_epgm, egURI)); } void VppManager::domainUpdated(opflex::modb::class_id_t cid, const opflex::modb::URI &domURI) { if (stopping) return; m_task_queue.dispatch( domURI.toString(), bind(&VppManager::handleDomainUpdate, this, cid, domURI)); } void VppManager::secGroupSetUpdated(const EndpointListener::uri_set_t &secGrps) { if (stopping) return; m_task_queue.dispatch( "setSecGrp:", std::bind(&SecurityGroupManager::handle_set_update, m_sgm, secGrps)); } void VppManager::secGroupUpdated(const opflex::modb::URI &uri) { if (stopping) return; m_task_queue.dispatch( "secGrp:", std::bind(&SecurityGroupManager::handle_update, m_sgm, uri)); } void VppManager::contractUpdated(const opflex::modb::URI &contractURI) { if (stopping) return; m_task_queue.dispatch( contractURI.toString(), bind(&ContractManager::handle_update, m_cm, contractURI)); } void VppManager::externalInterfaceUpdated(const opflex::modb::URI &uri) { if (stopping) return; m_task_queue.dispatch(uri.toString(), bind(&ExtItfManager::handle_update, m_eim, uri)); } void VppManager::localRouteUpdated(const opflex::modb::URI &uri) { if (stopping) return; m_task_queue.dispatch(uri.toString(), bind(&RouteManager::handle_route_update, m_rdm, uri)); } void VppManager::handle_interface_event(std::vector e) { if (stopping) return; m_task_queue.dispatch("InterfaceEvent", bind(&VppManager::handleInterfaceEvent, this, e)); } void VppManager::configUpdated(const opflex::modb::URI &configURI) { VLOGI << "Config Updated "; if (stopping) return; m_runtime.agent.getAgentIOService().dispatch( bind(&VppManager::handleConfigUpdate, this, configURI)); } void VppManager::portStatusUpdate(const std::string &portName, uint32_t portNo, bool fromDesc) { if (stopping) return; m_runtime.agent.getAgentIOService().dispatch( bind(&VppManager::handlePortStatusUpdate, this, portName, portNo)); } void VppManager::peerStatusUpdated(const std::string &, int, PeerStatus peerStatus) { if (stopping) return; } void VppManager::handleDomainUpdate(opflex::modb::class_id_t cid, const opflex::modb::URI &domURI) { if (stopping) return; VLOGD << "Updating domain: " << domURI; switch (cid) { case modelgbp::gbp::RoutingDomain::CLASS_ID: m_rdm->handle_domain_update(domURI); break; case modelgbp::gbp::Subnet::CLASS_ID: if (!modelgbp::gbp::Subnet::resolve(m_runtime.agent.getFramework(), domURI)) { VLOGD << "Cleaning up for Subnet: " << domURI; } break; case modelgbp::gbp::BridgeDomain::CLASS_ID: if (!modelgbp::gbp::BridgeDomain::resolve( m_runtime.agent.getFramework(), domURI)) { VLOGD << "Cleaning up for BD: " << domURI; m_runtime.id_gen.erase(cid, domURI); } break; case modelgbp::gbp::FloodDomain::CLASS_ID: if (!modelgbp::gbp::FloodDomain::resolve(m_runtime.agent.getFramework(), domURI)) { VLOGD << "Cleaning up for FD: " << domURI; m_runtime.id_gen.erase(cid, domURI); } break; case modelgbp::gbp::L3ExternalNetwork::CLASS_ID: if (!modelgbp::gbp::L3ExternalNetwork::resolve( m_runtime.agent.getFramework(), domURI)) { VLOGD << "Cleaning up for L3ExtNet: " << domURI; m_runtime.id_gen.erase(cid, domURI); } break; } } void VppManager::handleInterfaceEvent(std::vector events) { if (stopping) return; for (auto &e : events) { VLOGD << "Interface Event: " << e.itf.to_string() << " state: " << e.state.to_string(); } } void VppManager::initPlatformConfig() { boost::optional> config = modelgbp::platform::Config::resolve( m_runtime.agent.getFramework(), m_runtime.agent.getPolicyManager().getOpflexDomain()); } void VppManager::handleConfigUpdate(const opflex::modb::URI &configURI) { VLOGD << "Updating platform config " << configURI; if (stopping) return; initPlatformConfig(); /** * Now that we are known to be opflex connected, * Scehdule a timer to sweep the state we read when we first connected * to VPP. */ m_sweep_timer.reset( new boost::asio::deadline_timer(m_runtime.agent.getAgentIOService())); m_sweep_timer->expires_from_now(boost::posix_time::seconds(30)); m_sweep_timer->async_wait(bind(&VppManager::handleSweepTimer, this, error)); } void VppManager::handlePortStatusUpdate(const std::string &portName, uint32_t) { VLOGD << "Port-status update for " << portName; if (stopping) return; } Uplink & VppManager::uplink() { return m_runtime.uplink; } CrossConnect & VppManager::crossConnect() { return m_xconnect; } }; // namespace opflexagent /* * Local Variables: * eval: (c-set-style "llvm.org") * End: */