From b8eab37e437899bc895ba28793a205779156a61a Mon Sep 17 00:00:00 2001 From: Vinay Mahuli Date: Tue, 6 Jun 2017 23:52:27 -0700 Subject: [PATCH 01/68] Adding R3.2.3.x branch to CI Closes-Bug: 1696310 Change-Id: I5f22cdb4d021eb6f16ccbcd900fba4cdf63fe4a9 --- .gitreview | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitreview b/.gitreview index 14ae5bcc383..c3d85fc18f9 100644 --- a/.gitreview +++ b/.gitreview @@ -13,4 +13,4 @@ host=review.opencontrail.org port=29418 project=Juniper/contrail-controller.git -defaultbranch=R3.2 +defaultbranch=R3.2.3.x From 6bfa82a133c38f6ac82f1c369a0601469e9dd274 Mon Sep 17 00:00:00 2001 From: Vinay Vithal Mahuli Date: Wed, 7 Jun 2017 00:17:30 -0700 Subject: [PATCH 02/68] change version info Change-Id: If61f183236d16a1c10181284e63d42f5d2eee9b0 --- src/base/version.info | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/base/version.info b/src/base/version.info index 374a44857e8..e4424a9b05a 100644 --- a/src/base/version.info +++ b/src/base/version.info @@ -1 +1 @@ -3.2.3.0 +3.2.3.1 From 308b45be158135b6d326bd1c7f098da123aa25f8 Mon Sep 17 00:00:00 2001 From: Sahil Date: Mon, 1 May 2017 13:15:22 -0700 Subject: [PATCH 03/68] [Config]: Fix VNC openstack Change-Id: Id519f13a37d2252255b7c2baa0fe103eeefe8ed4 Closes-Bug: 1685954 Closes-Bug: 1685040 --- src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py b/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py index 44a2a7b903a..7002c2093a2 100644 --- a/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py +++ b/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py @@ -1313,6 +1313,7 @@ def _network_vnc_to_neutron(self, net_obj, net_repr='SHOW'): extra_dict['contrail:fq_name'] = net_obj.get_fq_name() net_q_dict['tenant_id'] = net_obj.parent_uuid.replace('-', '') + net_q_dict['project_id'] = net_obj.parent_uuid.replace('-', '') net_q_dict['admin_state_up'] = id_perms.enable if net_obj.is_shared: net_q_dict['shared'] = True @@ -1999,9 +2000,9 @@ def _port_neutron_to_vnc(self, port_q, net_obj, oper): if port_sg_refs: if 'security_groups' in port_q and not port_q['security_groups']: # reset all SG on the port - port_obj.set_security_groups_list([]) + port_obj.set_security_group_list([]) elif len(port_sg_refs) == 1 and port_sg_refs[0]['to'] == SG_NO_RULE_FQ_NAME: - port_obj.set_security_groups_list([]) + port_obj.set_security_group_list([]) else: self._raise_contrail_exception('PortSecurityPortHasSecurityGroup', port_id=port_obj.uuid) From 059b059c6bf0db87cea703b368c97565652bdac8 Mon Sep 17 00:00:00 2001 From: Ananth Suryanarayana Date: Thu, 27 Apr 2017 15:34:46 -0700 Subject: [PATCH 04/68] Add BGP Notification Flag support in GR https://tools.ietf.org/html/draft-ietf-idr-bgp-gr-notification-10 Attempt GR Helper mode for all received notifications (and hold-timer expiry) except HardReset and PeerDeconfigured. Always send Notifications. NotificationFlag support has been in JUNOS already for a while. Older versions of control-node already skips most of the notifications received (and triggers GR helper mode) Also fix a couple of nits found by cpplint Closes-Bug: 1680047 Change-Id: Ic0462e9483dda8ee2f6c1be503bf1c120c3d6786 (cherry picked from commit c45ed8bbbdb2bf008f22b45e04a49d377e26c43e) --- src/bgp/bgp_peer.cc | 224 ++------------------- src/bgp/bgp_peer.h | 4 +- src/bgp/bgp_peer_close.cc | 4 +- src/bgp/bgp_proto.cc | 4 +- src/bgp/bgp_proto.h | 28 +-- src/bgp/state_machine.cc | 4 +- src/bgp/test/bgp_msg_builder_test.cc | 268 ++----------------------- src/bgp/test/bgp_peer_close_gr_test.cc | 82 +++++++- src/bgp/test/bgp_server_test_util.h | 14 +- src/bgp/test/graceful_restart_test.cc | 15 +- 10 files changed, 143 insertions(+), 504 deletions(-) diff --git a/src/bgp/bgp_peer.cc b/src/bgp/bgp_peer.cc index d41a3fa10a8..564f4054b1f 100644 --- a/src/bgp/bgp_peer.cc +++ b/src/bgp/bgp_peer.cc @@ -4,13 +4,14 @@ #include "bgp/bgp_peer.h" +#include +#include +#include + #include #include #include -#include -#include - #include "base/task_annotations.h" #include "bgp/bgp_factory.h" #include "bgp/bgp_log.h" @@ -1280,218 +1281,25 @@ bool BgpPeer::FlushUpdate() { return FlushUpdateUnlocked(); } -// -// Check if hard close can be skipped upon notification message reception -// based on code. Typically, when we want to avail the use of GR/LLGR, it is -// better not to do hard close and do GR instead for as many cases as possible. -// -bool BgpPeer::SkipNotificationReceive(int code, int subcode) const { - switch (static_cast(code)) { - case BgpProto::Notification::MsgHdrErr: - switch (static_cast( - subcode)) { - case BgpProto::Notification::ConnNotSync: - break; - case BgpProto::Notification::BadMsgLength: - break; - case BgpProto::Notification::BadMsgType: - break; - } - break; - case BgpProto::Notification::OpenMsgErr: - switch (static_cast( - subcode)) { - case BgpProto::Notification::UnsupportedVersion: - break; - case BgpProto::Notification::BadPeerAS: - break; - case BgpProto::Notification::BadBgpId: - break; - case BgpProto::Notification::UnsupportedOptionalParam: - break; - case BgpProto::Notification::AuthenticationFailure: - break; - case BgpProto::Notification::UnacceptableHoldTime: - break; - case BgpProto::Notification::UnsupportedCapability: - break; - } - break; - case BgpProto::Notification::UpdateMsgErr: - switch (static_cast( - subcode)) { - case BgpProto::Notification::MalformedAttributeList: - break; - case BgpProto::Notification::UnrecognizedWellKnownAttrib: - break; - case BgpProto::Notification::MissingWellKnownAttrib: - break; - case BgpProto::Notification::AttribFlagsError: - break; - case BgpProto::Notification::AttribLengthError: - break; - case BgpProto::Notification::InvalidOrigin: - break; - case BgpProto::Notification::InvalidNH: - break; - case BgpProto::Notification::OptionalAttribError: - break; - case BgpProto::Notification::InvalidNetworkField: - break; - case BgpProto::Notification::MalformedASPath: - break; - } - break; - case BgpProto::Notification::HoldTimerExp: - return true; - case BgpProto::Notification::FSMErr: - switch (static_cast(subcode)) { - case BgpProto::Notification::UnspecifiedError: - break; - case BgpProto::Notification::OpenSentError: - break; - case BgpProto::Notification::OpenConfirmError: - break; - case BgpProto::Notification::EstablishedError: - break; - } - break; - case BgpProto::Notification::Cease: - switch (static_cast( - subcode)) { - case BgpProto::Notification::Unknown: - return true; - case BgpProto::Notification::MaxPrefixes: - return true; - case BgpProto::Notification::AdminShutdown: - break; - case BgpProto::Notification::PeerDeconfigured: - break; - case BgpProto::Notification::AdminReset: - break; - case BgpProto::Notification::ConnectionRejected: - break; - case BgpProto::Notification::OtherConfigChange: - return true; - case BgpProto::Notification::ConnectionCollision: - break; - case BgpProto::Notification::OutOfResources: - return true; - } - break; - } - return false; +bool BgpPeer::notification() const { + return peer_close_->gr_params().notification(); } -// -// Check if notification send can be skipped. Typically, when we want to avail -// the use of GR/LLGR, it is better not to send a notification message so that -// peer retains the routes (as stale) we have already sent over this session. -// -bool BgpPeer::SkipNotificationSend(int code, int subcode) const { - switch (static_cast(code)) { - case BgpProto::Notification::MsgHdrErr: - switch (static_cast( - subcode)) { - case BgpProto::Notification::ConnNotSync: - break; - case BgpProto::Notification::BadMsgLength: - break; - case BgpProto::Notification::BadMsgType: - break; - } - break; - case BgpProto::Notification::OpenMsgErr: - switch (static_cast( - subcode)) { - case BgpProto::Notification::UnsupportedVersion: - break; - case BgpProto::Notification::BadPeerAS: - break; - case BgpProto::Notification::BadBgpId: - break; - case BgpProto::Notification::UnsupportedOptionalParam: - break; - case BgpProto::Notification::AuthenticationFailure: - break; - case BgpProto::Notification::UnacceptableHoldTime: - break; - case BgpProto::Notification::UnsupportedCapability: - break; - } - break; - case BgpProto::Notification::UpdateMsgErr: - switch (static_cast( - subcode)) { - case BgpProto::Notification::MalformedAttributeList: - break; - case BgpProto::Notification::UnrecognizedWellKnownAttrib: - break; - case BgpProto::Notification::MissingWellKnownAttrib: - break; - case BgpProto::Notification::AttribFlagsError: - break; - case BgpProto::Notification::AttribLengthError: - break; - case BgpProto::Notification::InvalidOrigin: - break; - case BgpProto::Notification::InvalidNH: - break; - case BgpProto::Notification::OptionalAttribError: - break; - case BgpProto::Notification::InvalidNetworkField: - break; - case BgpProto::Notification::MalformedASPath: - break; - } - break; - case BgpProto::Notification::HoldTimerExp: - return true; - case BgpProto::Notification::FSMErr: - switch (static_cast(subcode)) { - case BgpProto::Notification::UnspecifiedError: - break; - case BgpProto::Notification::OpenSentError: - break; - case BgpProto::Notification::OpenConfirmError: - break; - case BgpProto::Notification::EstablishedError: - break; - } - break; - case BgpProto::Notification::Cease: - switch (static_cast( - subcode)) { - case BgpProto::Notification::Unknown: - return true; - case BgpProto::Notification::MaxPrefixes: - return true; - case BgpProto::Notification::AdminShutdown: - break; - case BgpProto::Notification::PeerDeconfigured: - break; - case BgpProto::Notification::AdminReset: - break; - case BgpProto::Notification::ConnectionRejected: - break; - case BgpProto::Notification::OtherConfigChange: - return true; - case BgpProto::Notification::ConnectionCollision: - break; - case BgpProto::Notification::OutOfResources: - return true; - } - break; +// Check if GR Helper mode sould be attempted. +bool BgpPeer::AttemptGRHelperMode(int code, int subcode) const { + if (code == BgpProto::Notification::Cease && + (subcode == BgpProto::Notification::HardReset || + subcode == BgpProto::Notification::PeerDeconfigured)) { + return false; } - return false; + + // If Peer supports Notification (N) bit, then attempt GR-Helper for all + // other notifications, not otherwise. + return notification(); } void BgpPeer::SendNotification(BgpSession *session, int code, int subcode, const string &data) { - // Check if we can skip sending this notification message. - if (peer_close_->IsCloseGraceful() && SkipNotificationSend(code, subcode)) - return; - tbb::spin_mutex::scoped_lock lock(spin_mutex_); session->SendNotification(code, subcode, data); state_machine_->set_last_notification_out(code, subcode, data); diff --git a/src/bgp/bgp_peer.h b/src/bgp/bgp_peer.h index 39433d53d19..c3f17077c26 100644 --- a/src/bgp/bgp_peer.h +++ b/src/bgp/bgp_peer.h @@ -316,8 +316,7 @@ class BgpPeer : public IPeer { KeyType key_type); void ClearListenSocketAuthKey(); void SetSessionSocketAuthKey(TcpSession *session); - bool SkipNotificationSend(int code, int subcode) const; - virtual bool SkipNotificationReceive(int code, int subcode) const; + virtual bool AttemptGRHelperMode(int code, int subcode) const; void Register(BgpTable *table, const RibExportPolicy &policy); void Register(BgpTable *table); bool EndOfRibSendTimerExpired(Address::Family family); @@ -341,6 +340,7 @@ class BgpPeer : public IPeer { virtual void SendEndOfRIBActual(Address::Family family); virtual void SendEndOfRIB(Address::Family family); int membership_req_pending() const { return membership_req_pending_; } + virtual bool notification() const; private: friend class BgpConfigTest; diff --git a/src/bgp/bgp_peer_close.cc b/src/bgp/bgp_peer_close.cc index 53d3c6fbd59..233fbb9f1a0 100644 --- a/src/bgp/bgp_peer_close.cc +++ b/src/bgp/bgp_peer_close.cc @@ -367,7 +367,7 @@ void BgpPeerClose::AddGRCapabilities( // Indicate EOR support by default. if (!time) { BgpProto::OpenMessage::Capability *gr_cap = - BgpProto::OpenMessage::Capability::GR::Encode(0, restarted, + BgpProto::OpenMessage::Capability::GR::Encode(0, restarted, false, afi_flags, gr_families); opt_param->capabilities.push_back(gr_cap); @@ -383,7 +383,7 @@ void BgpPeerClose::AddGRCapabilities( } BgpProto::OpenMessage::Capability *gr_cap = - BgpProto::OpenMessage::Capability::GR::Encode(time, restarted, + BgpProto::OpenMessage::Capability::GR::Encode(time, restarted, true, afi_flags, gr_families); opt_param->capabilities.push_back(gr_cap); } diff --git a/src/bgp/bgp_proto.cc b/src/bgp/bgp_proto.cc index eb78c7815fd..c829ba7b8d4 100644 --- a/src/bgp/bgp_proto.cc +++ b/src/bgp/bgp_proto.cc @@ -118,10 +118,12 @@ int BgpProto::OpenMessage::Validate(BgpPeer *peer) const { BgpProto::OpenMessage::Capability * BgpProto::OpenMessage::Capability::GR::Encode( - uint16_t gr_time, bool restarted, const vector &gr_afi_flags, + uint16_t gr_time, bool restarted, bool notification, + const vector &gr_afi_flags, const vector &gr_families) { assert((gr_time & ~RestartTimeMask) == 0); uint16_t restart_flags = restarted ? RestartedFlag : 0; + restart_flags |= notification ? NotificationFlag : 0; const uint16_t gr_bytes = restart_flags | gr_time; vector restart_cap; diff --git a/src/bgp/bgp_proto.h b/src/bgp/bgp_proto.h index 48d30fcaa3d..d2a4ad5d9b5 100644 --- a/src/bgp/bgp_proto.h +++ b/src/bgp/bgp_proto.h @@ -113,8 +113,6 @@ class BgpProto { return "LongLivedGracefulRestart"; case RouteRefreshCisco: return "RouteRefreshCisco"; - default: - break; } std::ostringstream oss; @@ -126,10 +124,11 @@ class BgpProto { code(code), capability(src, src + size) {} struct GR { - enum { + enum Flags { ForwardingStatePreservedFlag = 0x80, - RestartTimeMask = 0x0FFF, RestartedFlag = 0x8000, + NotificationFlag = 0x4000, + RestartTimeMask = 0x0FFF, }; explicit GR() { Initialize(); } void Initialize() { @@ -149,6 +148,7 @@ class BgpProto { } }; static Capability *Encode(uint16_t gr_time, bool restarted, + bool notification, const std::vector &gr_afi_flags, const std::vector &gr_families); static bool Decode(GR *gr_params, @@ -156,6 +156,9 @@ class BgpProto { static void GetFamilies(const GR &gr_params, std::vector *families); bool restarted() const { return (flags & RestartedFlag) != 0; } + bool notification() const { + return (flags & NotificationFlag) != 0; + } void set_flags(uint16_t gr_cap_bytes) { flags = gr_cap_bytes & ~RestartTimeMask; } @@ -246,8 +249,6 @@ class BgpProto { return "Finite State Machine Error"; case Cease: return "Cease"; - default: - break; } std::ostringstream oss; @@ -267,8 +268,6 @@ class BgpProto { return "Bad Message Length"; case BadMsgType: return "Bad Message Type"; - default: - break; } std::ostringstream oss; @@ -300,8 +299,6 @@ class BgpProto { return "Unacceptable Hold Time"; case UnsupportedCapability: return "Unsupported Capability"; - default: - break; } std::ostringstream oss; @@ -342,8 +339,6 @@ class BgpProto { return "Invalid Network Field"; case MalformedASPath: return "Malformed AS_PATH"; - default: - break; } std::ostringstream oss; @@ -366,8 +361,6 @@ class BgpProto { return "Receive Unexpected Message in OpenConfirm State"; case EstablishedError: return "Receive Unexpected Message in Established State"; - default: - break; } std::ostringstream oss; @@ -383,7 +376,8 @@ class BgpProto { ConnectionRejected = 5, OtherConfigChange = 6, ConnectionCollision = 7, - OutOfResources = 8 + OutOfResources = 8, + HardReset = 9, }; static std::string CeaseSubcodeToString(CeaseSubCode sub_code) { @@ -406,8 +400,8 @@ class BgpProto { return "Connection collision"; case OutOfResources: return "Unable handle peer due to resource limit"; - default: - break; + case HardReset: + return "Received HardReset to skip GR Helper mode"; } std::ostringstream oss; diff --git a/src/bgp/state_machine.cc b/src/bgp/state_machine.cc index f921d7d9d6a..60b90ac6bef 100644 --- a/src/bgp/state_machine.cc +++ b/src/bgp/state_machine.cc @@ -17,6 +17,7 @@ #include "base/task_annotations.h" #include "bgp/bgp_log.h" #include "bgp/bgp_peer.h" +#include "bgp/bgp_peer_close.h" #include "bgp/bgp_peer_types.h" #include "bgp/bgp_server.h" #include "bgp/bgp_session.h" @@ -1359,8 +1360,7 @@ void StateMachine::SendNotificationAndClose(BgpSession *session, int code, set_idle_hold_time(idle_hold_time() ? idle_hold_time() : kIdleHoldTime); reset_hold_time(); - - bool graceful = !code || peer_->SkipNotificationReceive(code, subcode); + bool graceful = !code || peer_->AttemptGRHelperMode(code, subcode); peer_->Close(graceful); } diff --git a/src/bgp/test/bgp_msg_builder_test.cc b/src/bgp/test/bgp_msg_builder_test.cc index 5054b39d351..f27864c9d55 100644 --- a/src/bgp/test/bgp_msg_builder_test.cc +++ b/src/bgp/test/bgp_msg_builder_test.cc @@ -28,6 +28,10 @@ class BgpPeerMock : public BgpPeer { } bool IsReady() const { return true; } + bool notification() const { return notification_; } + void set_notification(bool notification) { notification_ = notification; } +private: + bool notification_; }; class BgpMsgBuilderTest : public testing::Test { @@ -48,8 +52,8 @@ class BgpMsgBuilderTest : public testing::Test { server_.Shutdown(); task_util::WaitForIdle(); } - void TestSkipNotificationSend(int code, int subcode) const; - void TestSkipNotificationReceive(int code, int subcode) const; + void TestAttemptGRHelperMode(bool notification, int code, int subcode) + const; EventManager evm_; BgpServer server_; @@ -183,267 +187,37 @@ TEST_F(BgpMsgBuilderTest, Build) { delete result; } -void BgpMsgBuilderTest::TestSkipNotificationReceive(int code, - int subcode) const { +void BgpMsgBuilderTest::TestAttemptGRHelperMode(bool notification, int code, + int subcode) const { if (code < BgpProto::Notification::MsgHdrErr || code > BgpProto::Notification::Cease) return; - bool skip = peer_->SkipNotificationReceive(code, subcode); - switch (static_cast(code)) { - case BgpProto::Notification::MsgHdrErr: - if (subcode < BgpProto::Notification::ConnNotSync || - subcode > BgpProto::Notification::BadMsgType) - return; - switch (static_cast( - subcode)) { - case BgpProto::Notification::ConnNotSync: - break; - case BgpProto::Notification::BadMsgLength: - break; - case BgpProto::Notification::BadMsgType: - break; - } - break; - case BgpProto::Notification::OpenMsgErr: - if (subcode < BgpProto::Notification::UnsupportedVersion || - subcode > BgpProto::Notification::UnsupportedCapability) - return; - switch (static_cast( - subcode)) { - case BgpProto::Notification::UnsupportedVersion: - break; - case BgpProto::Notification::BadPeerAS: - break; - case BgpProto::Notification::BadBgpId: - break; - case BgpProto::Notification::UnsupportedOptionalParam: - break; - case BgpProto::Notification::AuthenticationFailure: - break; - case BgpProto::Notification::UnacceptableHoldTime: - break; - case BgpProto::Notification::UnsupportedCapability: - break; - } - break; - case BgpProto::Notification::UpdateMsgErr: - if (subcode < BgpProto::Notification::MalformedAttributeList || - subcode > BgpProto::Notification::MalformedASPath) - return; - switch (static_cast( - subcode)) { - case BgpProto::Notification::MalformedAttributeList: - break; - case BgpProto::Notification::UnrecognizedWellKnownAttrib: - break; - case BgpProto::Notification::MissingWellKnownAttrib: - break; - case BgpProto::Notification::AttribFlagsError: - break; - case BgpProto::Notification::AttribLengthError: - break; - case BgpProto::Notification::InvalidOrigin: - break; - case BgpProto::Notification::InvalidNH: - break; - case BgpProto::Notification::OptionalAttribError: - break; - case BgpProto::Notification::InvalidNetworkField: - break; - case BgpProto::Notification::MalformedASPath: - break; - } - break; - case BgpProto::Notification::HoldTimerExp: - EXPECT_TRUE(skip); - return; - case BgpProto::Notification::FSMErr: - if (subcode < BgpProto::Notification::UnspecifiedError || - subcode > BgpProto::Notification::EstablishedError) - return; - switch (static_cast(subcode)) { - case BgpProto::Notification::UnspecifiedError: - break; - case BgpProto::Notification::OpenSentError: - break; - case BgpProto::Notification::OpenConfirmError: - break; - case BgpProto::Notification::EstablishedError: - break; - } - break; - case BgpProto::Notification::Cease: - if (subcode < BgpProto::Notification::Unknown || - subcode > BgpProto::Notification::OutOfResources) - return; - switch (static_cast( - subcode)) { - case BgpProto::Notification::Unknown: - EXPECT_TRUE(skip); - return; - case BgpProto::Notification::MaxPrefixes: - EXPECT_TRUE(skip); - return; - case BgpProto::Notification::AdminShutdown: - break; - case BgpProto::Notification::PeerDeconfigured: - break; - case BgpProto::Notification::AdminReset: - break; - case BgpProto::Notification::ConnectionRejected: - break; - case BgpProto::Notification::OtherConfigChange: - EXPECT_TRUE(skip); - return; - case BgpProto::Notification::ConnectionCollision: - break; - case BgpProto::Notification::OutOfResources: - EXPECT_TRUE(skip); - return; - } - break; + dynamic_cast(peer_)->set_notification(notification); + if (!notification) { + EXPECT_FALSE(peer_->AttemptGRHelperMode(code, subcode)); + return; } - EXPECT_FALSE(skip); -} -void BgpMsgBuilderTest::TestSkipNotificationSend(int code, int subcode) const { - if (code < BgpProto::Notification::MsgHdrErr || - code > BgpProto::Notification::Cease) + if (code == BgpProto::Notification::Cease && + (subcode == BgpProto::Notification::HardReset || + subcode == BgpProto::Notification::PeerDeconfigured)) { + EXPECT_FALSE(peer_->AttemptGRHelperMode(code, subcode)); return; - - bool skip = peer_->SkipNotificationSend(code, subcode); - switch (static_cast(code)) { - case BgpProto::Notification::MsgHdrErr: - if (subcode < BgpProto::Notification::ConnNotSync || - subcode > BgpProto::Notification::BadMsgType) - return; - switch (static_cast( - subcode)) { - case BgpProto::Notification::ConnNotSync: - break; - case BgpProto::Notification::BadMsgLength: - break; - case BgpProto::Notification::BadMsgType: - break; - } - break; - case BgpProto::Notification::OpenMsgErr: - if (subcode < BgpProto::Notification::UnsupportedVersion || - subcode > BgpProto::Notification::UnsupportedCapability) - return; - switch (static_cast( - subcode)) { - case BgpProto::Notification::UnsupportedVersion: - break; - case BgpProto::Notification::BadPeerAS: - break; - case BgpProto::Notification::BadBgpId: - break; - case BgpProto::Notification::UnsupportedOptionalParam: - break; - case BgpProto::Notification::AuthenticationFailure: - break; - case BgpProto::Notification::UnacceptableHoldTime: - break; - case BgpProto::Notification::UnsupportedCapability: - break; - } - break; - case BgpProto::Notification::UpdateMsgErr: - if (subcode < BgpProto::Notification::MalformedAttributeList || - subcode > BgpProto::Notification::MalformedASPath) - return; - switch (static_cast( - subcode)) { - case BgpProto::Notification::MalformedAttributeList: - break; - case BgpProto::Notification::UnrecognizedWellKnownAttrib: - break; - case BgpProto::Notification::MissingWellKnownAttrib: - break; - case BgpProto::Notification::AttribFlagsError: - break; - case BgpProto::Notification::AttribLengthError: - break; - case BgpProto::Notification::InvalidOrigin: - break; - case BgpProto::Notification::InvalidNH: - break; - case BgpProto::Notification::OptionalAttribError: - break; - case BgpProto::Notification::InvalidNetworkField: - break; - case BgpProto::Notification::MalformedASPath: - break; - } - break; - case BgpProto::Notification::HoldTimerExp: - EXPECT_TRUE(skip); - return; - case BgpProto::Notification::FSMErr: - if (subcode < BgpProto::Notification::UnspecifiedError || - subcode > BgpProto::Notification::EstablishedError) - return; - switch (static_cast(subcode)) { - case BgpProto::Notification::UnspecifiedError: - break; - case BgpProto::Notification::OpenSentError: - break; - case BgpProto::Notification::OpenConfirmError: - break; - case BgpProto::Notification::EstablishedError: - break; - } - break; - case BgpProto::Notification::Cease: - if (subcode < BgpProto::Notification::Unknown || - subcode > BgpProto::Notification::OutOfResources) - return; - switch (static_cast( - subcode)) { - case BgpProto::Notification::Unknown: - EXPECT_TRUE(skip); - return; - case BgpProto::Notification::MaxPrefixes: - EXPECT_TRUE(skip); - return; - case BgpProto::Notification::AdminShutdown: - break; - case BgpProto::Notification::PeerDeconfigured: - break; - case BgpProto::Notification::AdminReset: - break; - case BgpProto::Notification::ConnectionRejected: - break; - case BgpProto::Notification::OtherConfigChange: - EXPECT_TRUE(skip); - return; - case BgpProto::Notification::ConnectionCollision: - break; - case BgpProto::Notification::OutOfResources: - EXPECT_TRUE(skip); - return; - } - break; } - EXPECT_FALSE(skip); + + EXPECT_TRUE(peer_->AttemptGRHelperMode(code, subcode)); } -TEST_F(BgpMsgBuilderTest, SkipNotificationsSend) { +TEST_F(BgpMsgBuilderTest, SkipNotificationsReceive) { for (int code = BgpProto::Notification::MsgHdrErr; code <= BgpProto::Notification::Cease; code++) { for (int subcode = 0; subcode <= 12; subcode++) { - TestSkipNotificationSend(code, subcode); + TestAttemptGRHelperMode(true, code, subcode); } - } -} -TEST_F(BgpMsgBuilderTest, SkipNotificationsReceive) { - for (int code = BgpProto::Notification::MsgHdrErr; - code <= BgpProto::Notification::Cease; code++) { for (int subcode = 0; subcode <= 12; subcode++) { - TestSkipNotificationReceive(code, subcode); + TestAttemptGRHelperMode(false, code, subcode); } } } diff --git a/src/bgp/test/bgp_peer_close_gr_test.cc b/src/bgp/test/bgp_peer_close_gr_test.cc index 251f2017df2..03716c6bec9 100644 --- a/src/bgp/test/bgp_peer_close_gr_test.cc +++ b/src/bgp/test/bgp_peer_close_gr_test.cc @@ -151,7 +151,7 @@ TEST_F(BgpPeerCloseGrTest, InGrTimerWaitingState) { TEST_F(BgpPeerCloseGrTest, RestartFlagsTest1) { boost::scoped_ptr cap; uint16_t time = 0; - cap.reset(BgpProto::OpenMessage::Capability::GR::Encode(time, false, + cap.reset(BgpProto::OpenMessage::Capability::GR::Encode(time, false, false, vector(), vector())); std::vector capabilities; capabilities.push_back(cap.get()); @@ -159,13 +159,14 @@ TEST_F(BgpPeerCloseGrTest, RestartFlagsTest1) { bgp_peer_close_test_->set_in_gr_timer_wait_state(false); EXPECT_TRUE(bgp_peer_close_test_->SetGRCapabilities(NULL)); EXPECT_EQ(false, bgp_peer_close_test_->gr_params().restarted()); + EXPECT_EQ(false, bgp_peer_close_test_->gr_params().notification()); EXPECT_EQ(time, bgp_peer_close_test_->gr_params().time); } TEST_F(BgpPeerCloseGrTest, RestartFlagsTest2) { boost::scoped_ptr cap; uint16_t time = 0xFF; - cap.reset(BgpProto::OpenMessage::Capability::GR::Encode(time, true, + cap.reset(BgpProto::OpenMessage::Capability::GR::Encode(time, true, false, vector(), vector())); std::vector capabilities; capabilities.push_back(cap.get()); @@ -173,13 +174,14 @@ TEST_F(BgpPeerCloseGrTest, RestartFlagsTest2) { bgp_peer_close_test_->set_in_gr_timer_wait_state(false); EXPECT_TRUE(bgp_peer_close_test_->SetGRCapabilities(NULL)); EXPECT_EQ(true, bgp_peer_close_test_->gr_params().restarted()); + EXPECT_EQ(false, bgp_peer_close_test_->gr_params().notification()); EXPECT_EQ(time, bgp_peer_close_test_->gr_params().time); } TEST_F(BgpPeerCloseGrTest, RestartFlagsTest3) { boost::scoped_ptr cap; uint16_t time = BgpProto::OpenMessage::Capability::GR::RestartTimeMask; - cap.reset(BgpProto::OpenMessage::Capability::GR::Encode(time, false, + cap.reset(BgpProto::OpenMessage::Capability::GR::Encode(time, false, false, vector(), vector())); std::vector capabilities; capabilities.push_back(cap.get()); @@ -187,13 +189,14 @@ TEST_F(BgpPeerCloseGrTest, RestartFlagsTest3) { bgp_peer_close_test_->set_in_gr_timer_wait_state(false); EXPECT_TRUE(bgp_peer_close_test_->SetGRCapabilities(NULL)); EXPECT_EQ(false, bgp_peer_close_test_->gr_params().restarted()); + EXPECT_EQ(false, bgp_peer_close_test_->gr_params().notification()); EXPECT_EQ(time, bgp_peer_close_test_->gr_params().time); } TEST_F(BgpPeerCloseGrTest, RestartFlagsTest4) { boost::scoped_ptr cap; uint16_t time = BgpProto::OpenMessage::Capability::GR::RestartTimeMask/2; - cap.reset(BgpProto::OpenMessage::Capability::GR::Encode(time, true, + cap.reset(BgpProto::OpenMessage::Capability::GR::Encode(time, true, false, vector(), vector())); std::vector capabilities; capabilities.push_back(cap.get()); @@ -201,6 +204,67 @@ TEST_F(BgpPeerCloseGrTest, RestartFlagsTest4) { bgp_peer_close_test_->set_in_gr_timer_wait_state(false); EXPECT_TRUE(bgp_peer_close_test_->SetGRCapabilities(NULL)); EXPECT_EQ(true, bgp_peer_close_test_->gr_params().restarted()); + EXPECT_EQ(false, bgp_peer_close_test_->gr_params().notification()); + EXPECT_EQ(time, bgp_peer_close_test_->gr_params().time); +} + +TEST_F(BgpPeerCloseGrTest, RestartFlagsTest5) { + boost::scoped_ptr cap; + uint16_t time = 0; + cap.reset(BgpProto::OpenMessage::Capability::GR::Encode(time, false, true, + vector(), vector())); + std::vector capabilities; + capabilities.push_back(cap.get()); + bgp_peer_close_test_->set_capabilities(capabilities); + bgp_peer_close_test_->set_in_gr_timer_wait_state(false); + EXPECT_TRUE(bgp_peer_close_test_->SetGRCapabilities(NULL)); + EXPECT_EQ(false, bgp_peer_close_test_->gr_params().restarted()); + EXPECT_EQ(true, bgp_peer_close_test_->gr_params().notification()); + EXPECT_EQ(time, bgp_peer_close_test_->gr_params().time); +} + +TEST_F(BgpPeerCloseGrTest, RestartFlagsTest6) { + boost::scoped_ptr cap; + uint16_t time = 0xFF; + cap.reset(BgpProto::OpenMessage::Capability::GR::Encode(time, true, true, + vector(), vector())); + std::vector capabilities; + capabilities.push_back(cap.get()); + bgp_peer_close_test_->set_capabilities(capabilities); + bgp_peer_close_test_->set_in_gr_timer_wait_state(false); + EXPECT_TRUE(bgp_peer_close_test_->SetGRCapabilities(NULL)); + EXPECT_EQ(true, bgp_peer_close_test_->gr_params().restarted()); + EXPECT_EQ(true, bgp_peer_close_test_->gr_params().notification()); + EXPECT_EQ(time, bgp_peer_close_test_->gr_params().time); +} + +TEST_F(BgpPeerCloseGrTest, RestartFlagsTest7) { + boost::scoped_ptr cap; + uint16_t time = BgpProto::OpenMessage::Capability::GR::RestartTimeMask; + cap.reset(BgpProto::OpenMessage::Capability::GR::Encode(time, false, true, + vector(), vector())); + std::vector capabilities; + capabilities.push_back(cap.get()); + bgp_peer_close_test_->set_capabilities(capabilities); + bgp_peer_close_test_->set_in_gr_timer_wait_state(false); + EXPECT_TRUE(bgp_peer_close_test_->SetGRCapabilities(NULL)); + EXPECT_EQ(false, bgp_peer_close_test_->gr_params().restarted()); + EXPECT_EQ(true, bgp_peer_close_test_->gr_params().notification()); + EXPECT_EQ(time, bgp_peer_close_test_->gr_params().time); +} + +TEST_F(BgpPeerCloseGrTest, RestartFlagsTest8) { + boost::scoped_ptr cap; + uint16_t time = BgpProto::OpenMessage::Capability::GR::RestartTimeMask/2; + cap.reset(BgpProto::OpenMessage::Capability::GR::Encode(time, true, true, + vector(), vector())); + std::vector capabilities; + capabilities.push_back(cap.get()); + bgp_peer_close_test_->set_capabilities(capabilities); + bgp_peer_close_test_->set_in_gr_timer_wait_state(false); + EXPECT_TRUE(bgp_peer_close_test_->SetGRCapabilities(NULL)); + EXPECT_EQ(true, bgp_peer_close_test_->gr_params().restarted()); + EXPECT_EQ(true, bgp_peer_close_test_->gr_params().notification()); EXPECT_EQ(time, bgp_peer_close_test_->gr_params().time); } @@ -268,18 +332,16 @@ class BgpPeerCloseGrTestParam : public ::testing::TestWithParam { vector llgr_families = ::std::tr1::get<8>(GetParam()); BgpProto::OpenMessage::Capability *cap; - cap = BgpProto::OpenMessage::Capability::GR::Encode(gr_time, 0, - afi_flags, - gr_families); + cap = BgpProto::OpenMessage::Capability::GR::Encode( + gr_time, false, true, afi_flags, gr_families); capabilities_.push_back(cap); afi_flags.clear(); for (size_t i = 0; i < llgr_families.size(); i++) { afi_flags.push_back(llgr_afi_flags); } - cap = BgpProto::OpenMessage::Capability::LLGR::Encode(llgr_time, - llgr_afi_flags, - llgr_families); + cap = BgpProto::OpenMessage::Capability::LLGR::Encode( + llgr_time, llgr_afi_flags, llgr_families); for (size_t i = 0; i < llgr_families.size(); i++) families_str.push_back(Address::FamilyToString(llgr_families[i])); gr_info_.push_back(GRInfo(true, llgr_time, afi_flags, families_str)); diff --git a/src/bgp/test/bgp_server_test_util.h b/src/bgp/test/bgp_server_test_util.h index bd399ab3bd3..93726e45b7f 100644 --- a/src/bgp/test/bgp_server_test_util.h +++ b/src/bgp/test/bgp_server_test_util.h @@ -300,20 +300,20 @@ class BgpPeerTest : public BgpPeer { cond_var_.wait(lock); } - bool SkipNotificationReceiveDefault(int code, int subcode) const { - return BgpPeer::SkipNotificationReceive(code, subcode); + bool AttemptGRHelperModeDefault(int code, int subcode) const { + return BgpPeer::AttemptGRHelperMode(code, subcode); } - virtual bool SkipNotificationReceive(int code, int subcode) const { - if (skip_notification_recv_fnc_.empty()) - return SkipNotificationReceiveDefault(code, subcode); - return skip_notification_recv_fnc_(code, subcode); + virtual bool AttemptGRHelperMode(int code, int subcode) const { + if (attempt_gr_helper_mode_fnc_.empty()) + return AttemptGRHelperModeDefault(code, subcode); + return attempt_gr_helper_mode_fnc_(code, subcode); } boost::function SendUpdate_fnc_; boost::function MpNlriAllowed_fnc_; boost::function IsReady_fnc_; - boost::function skip_notification_recv_fnc_; + boost::function attempt_gr_helper_mode_fnc_; BgpTestUtil util_; diff --git a/src/bgp/test/graceful_restart_test.cc b/src/bgp/test/graceful_restart_test.cc index 30658f0a5e6..c468ff5cd51 100644 --- a/src/bgp/test/graceful_restart_test.cc +++ b/src/bgp/test/graceful_restart_test.cc @@ -348,8 +348,7 @@ class GracefulRestartTest : public ::testing::TestWithParam { void WaitForAgentToBeEstablished(test::NetworkAgentMock *agent); void WaitForPeerToBeEstablished( BgpPeerTest *peer); void BgpPeersAdminUpOrDown(bool down); - bool SkipNotificationReceive(BgpPeerTest *peer, int code, - int subcode) const; + bool AttemptGRHelperMode(BgpPeerTest *peer, int code, int subcode) const; void SandeshStartup(); void SandeshShutdown(); @@ -634,9 +633,9 @@ void GracefulRestartTest::Configure() { BgpPeerTest *peer = bgp_servers_[0]->FindPeerByUuid( BgpConfigManager::kMasterInstance, uuid); peer->set_id(i-1); - peer->skip_notification_recv_fnc_ = - boost::bind(&GracefulRestartTest::SkipNotificationReceive, this, - peer, _1, _2); + peer->attempt_gr_helper_mode_fnc_ = + boost::bind(&GracefulRestartTest::AttemptGRHelperMode, this, peer, + _1, _2); bgp_server_peers_.push_back(peer); } } @@ -1182,12 +1181,12 @@ void GracefulRestartTest::VerifyRoutingInstances(BgpServer *server) { BgpConfigManager::kMasterInstance)); } -bool GracefulRestartTest::SkipNotificationReceive(BgpPeerTest *peer, - int code, int subcode) const { +bool GracefulRestartTest::AttemptGRHelperMode(BgpPeerTest *peer, int code, + int subcode) const { if (code == BgpProto::Notification::Cease && subcode == BgpProto::Notification::AdminShutdown) return true; - return peer->SkipNotificationReceiveDefault(code, subcode); + return peer->AttemptGRHelperModeDefault(code, subcode); } // Invoke stale timer callbacks directly to speed up. From a12133bade9d517e10a57a77199aad1076245768 Mon Sep 17 00:00:00 2001 From: Hampapur Ajay Date: Tue, 25 Apr 2017 10:17:39 -0700 Subject: [PATCH 05/68] If neutron sends empty body in floatingip-update, vnc_openstack receives body without 'resource' key. Simulate one and ensure any port association is cleared in such a case. Change-Id: I7ab14cb45b4896ddd4039f1343178d1984a4ca13 Closes-Bug: 1685314 (cherry picked from commit 4a2080d832f088e6377b30cc5c5ca98035a5cd8a) --- .../vnc_openstack/neutron_plugin_interface.py | 8 ++- .../vnc_openstack/tests/test_basic.py | 56 +++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/config/vnc_openstack/vnc_openstack/neutron_plugin_interface.py b/src/config/vnc_openstack/vnc_openstack/neutron_plugin_interface.py index c5c887925c3..da225c4c921 100644 --- a/src/config/vnc_openstack/vnc_openstack/neutron_plugin_interface.py +++ b/src/config/vnc_openstack/vnc_openstack/neutron_plugin_interface.py @@ -521,8 +521,14 @@ def plugin_update_floatingip(self, context, floatingip): try: cfgdb = self._get_user_cfgdb(context) + if 'resource' in floatingip: + fip_resource = floatingip['resource'] + else: + # non-existent body => clear fip to port assoc per neutron api + # simulate empty update for backend + fip_resource = {} floatingip_info = cfgdb.floatingip_update(context, floatingip['id'], - floatingip['resource']) + fip_resource) return floatingip_info except Exception as e: cgitb_hook(format="text") diff --git a/src/config/vnc_openstack/vnc_openstack/tests/test_basic.py b/src/config/vnc_openstack/vnc_openstack/tests/test_basic.py index e65ef80e0f2..c39f2f9d3df 100644 --- a/src/config/vnc_openstack/vnc_openstack/tests/test_basic.py +++ b/src/config/vnc_openstack/vnc_openstack/tests/test_basic.py @@ -568,6 +568,62 @@ def test_fixed_ip_conflicts_with_floating_ip(self): self.delete_resource('security_group', proj_obj.uuid, sg_q['id']) # end test_fixed_ip_conflicts_with_floating_ip + def test_empty_floating_ip_body_disassociates(self): + proj_obj = self._vnc_lib.project_read(fq_name=['default-domain', 'default-project']) + sg_q = self.create_resource('security_group', proj_obj.uuid) + + pvt_net_q = self.create_resource('network', proj_obj.uuid, + extra_res_fields={'router:external': True}) + pvt_subnet_q = self.create_resource('subnet', proj_obj.uuid, + extra_res_fields={ + 'network_id': pvt_net_q['id'], + 'cidr': '1.1.1.0/24', + 'ip_version': 4, + }) + port_q = self.create_resource('port', proj_obj.uuid, + extra_res_fields={ + 'network_id': pvt_net_q['id'], + 'security_groups': [sg_q['id']], + }) + + + pub_net_q = self.create_resource('network', proj_obj.uuid, + extra_res_fields={'router:external': True}) + pub_subnet_q = self.create_resource('subnet', proj_obj.uuid, + extra_res_fields={ + 'network_id': pub_net_q['id'], + 'cidr': '10.1.1.0/24', + 'ip_version': 4, + }) + fip_q = self.create_resource('floatingip', proj_obj.uuid, + extra_res_fields={ + 'floating_network_id': pub_net_q['id'], + 'port_id': port_q['id'], + }) + + # update fip with no 'resource' key and assert port disassociated + context = {'operation': 'UPDATE', + 'user_id': '', + 'is_admin': False, + 'roles': '', + 'tenant_id': proj_obj.uuid} + data = {'id': fip_q['id']} + body = {'context': context, 'data': data} + resp = self._api_svr_app.post_json('/neutron/floatingip', body) + self.assertEqual(resp.status_code, 200) + fip_read = self.read_resource('floatingip', fip_q['id']) + self.assertEqual(fip_read['port_id'], None) + + # cleanup + self.delete_resource('port', proj_obj.uuid, port_q['id']) + self.delete_resource('subnet', proj_obj.uuid, pvt_subnet_q['id']) + self.delete_resource('network', proj_obj.uuid, pvt_net_q['id']) + self.delete_resource('floatingip', proj_obj.uuid, fip_q['id']) + self.delete_resource('subnet', proj_obj.uuid, pub_subnet_q['id']) + self.delete_resource('network', proj_obj.uuid, pub_net_q['id']) + self.delete_resource('security_group', proj_obj.uuid, sg_q['id']) + # end test_empty_floating_ip_body_disassociates + def test_floating_ip_list(self): proj_objs = [] for i in range(3): From eec7e2f43e6be6913e9b8a376363263989b8851f Mon Sep 17 00:00:00 2001 From: Ananth Suryanarayana Date: Mon, 1 May 2017 15:52:14 -0700 Subject: [PATCH 06/68] Drop new xmpp connection for existing connection with same name If the IP address is different, then it implies mis-configuration of the xmpp agents. In such cases, drop new connection request and retain existing connection intact Since xmpp tasks run concurrently (across different IP addresses), use a XmppConnectionManager scoped mutex to solve concurrency issues among such parallel xmpp connection requests processing Also increase severity of a couple of related log messages Re-enable and fix few UTs that test this very exact scenario Closes-Bug: #1687096 Change-Id: Ic7252060abfc4b34fa184ff163afc217a88bb40c --- src/bgp/test/bgp_xmpp_basic_test.cc | 71 +++++++++++++++++++++-------- src/xmpp/xmpp_connection_manager.h | 2 + src/xmpp/xmpp_state_machine.cc | 26 +++++++++-- 3 files changed, 77 insertions(+), 22 deletions(-) diff --git a/src/bgp/test/bgp_xmpp_basic_test.cc b/src/bgp/test/bgp_xmpp_basic_test.cc index 6d24cd4671f..635b760fb15 100644 --- a/src/bgp/test/bgp_xmpp_basic_test.cc +++ b/src/bgp/test/bgp_xmpp_basic_test.cc @@ -109,7 +109,7 @@ class BgpXmppBasicTest : public ::testing::Test { xs_x_ = new XmppServer(&evm_, test::XmppDocumentMock::kControlNodeJID, &xs_cfg); } else { - xs_x_ = new XmppServer(&evm_, + xs_x_ = new XmppServer(&evm_, test::XmppDocumentMock::kControlNodeJID); } xs_x_->Initialize(0, false); @@ -123,14 +123,17 @@ class BgpXmppBasicTest : public ::testing::Test { } virtual void TearDown() { + XmppStateMachineTest::set_notify_fn(NULL); xs_x_->Shutdown(); task_util::WaitForIdle(); - bs_x_->Shutdown(); - task_util::WaitForIdle(); + TASK_UTIL_EXPECT_EQ(0, xs_x_->ConnectionCount()); cm_x_.reset(); + task_util::WaitForIdle(); TcpServerManager::DeleteServer(xs_x_); xs_x_ = NULL; + bs_x_->Shutdown(); + task_util::WaitForIdle(); evm_.Shutdown(); thread_.Join(); @@ -274,6 +277,7 @@ class BgpXmppBasicTest : public ::testing::Test { bool queue_disable) { if (sm->IsActiveChannel()) return; + tbb::mutex::scoped_lock lock(mutex_); if (create) { xmpp_state_machines_.insert(sm); sm->set_queue_disable(queue_disable); @@ -285,7 +289,7 @@ class BgpXmppBasicTest : public ::testing::Test { protected: EventManager evm_; ServerThread thread_; - BgpServerTestPtr bs_x_; + boost::scoped_ptr bs_x_; XmppServer *xs_x_; XmppLifetimeManagerTest *xltm_x_; bool auth_enabled_; @@ -303,6 +307,7 @@ class BgpXmppBasicTest : public ::testing::Test { test::NetworkAgentMockPtr agent_x3_; boost::scoped_ptr cm_x_; std::set xmpp_state_machines_; + mutable tbb::mutex mutex_; }; bool BgpXmppBasicTest::validate_done_ = false; @@ -1279,7 +1284,7 @@ class BgpXmppBasicParamTest2 : public BgpXmppBasicTest, public ::testing::WithParamInterface { protected: virtual void SetUp() { - bool agent_address_same_ = std::tr1::get<0>(GetParam()); + agent_address_same_ = std::tr1::get<0>(GetParam()); auth_enabled_ = std::tr1::get<1>(GetParam()); LOG(DEBUG, "BgpXmppBasicParamTest Agent Address: " << @@ -1318,13 +1323,19 @@ class BgpXmppBasicParamTest2 : public BgpXmppBasicTest, } void DestroyAgents() { + agent_x1_->SessionDown(); + agent_x2_->SessionDown(); + agent_x3_->SessionDown(); + agent_x1_->Delete(); agent_x2_->Delete(); agent_x3_->Delete(); } + + bool agent_address_same_; }; -TEST_P(BgpXmppBasicParamTest2, DISABLED_DuplicateEndpointName1) { +TEST_P(BgpXmppBasicParamTest2, DuplicateEndpointName1) { CreateAgents(); // Bring up one agent with given name. @@ -1339,18 +1350,34 @@ TEST_P(BgpXmppBasicParamTest2, DISABLED_DuplicateEndpointName1) { agent_x2_->SessionUp(); agent_x3_->SessionUp(); - // Make sure that latter two agents see sessions getting closed. - TASK_UTIL_EXPECT_TRUE( - agent_x2_->get_session_close() >= client_x2_session_close + 3); - TASK_UTIL_EXPECT_TRUE( - agent_x3_->get_session_close() >= client_x3_session_close + 3); - TASK_UTIL_EXPECT_TRUE( - agent_x1_->get_session_close() >= client_x1_session_close); + // Make sure that latter two agents see sessions getting closed if their IP + // address is not the same. + if (!agent_address_same_) { + TASK_UTIL_EXPECT_TRUE( + agent_x2_->get_session_close() >= client_x2_session_close + 3); + TASK_UTIL_EXPECT_TRUE( + agent_x3_->get_session_close() >= client_x3_session_close + 3); + + // Session which was up to begin with should remain up and not flap. + TASK_UTIL_EXPECT_TRUE( + agent_x1_->get_session_close() == client_x1_session_close); + } else { + // Sessions flap, triggering GR Helper mode if configured so. Even + // otherwise, in case like 'reboot' we expect existing session to reset + // if new connection formation is attempted from the same name-address + // combination. + TASK_UTIL_EXPECT_TRUE( + agent_x2_->get_session_close() >= client_x2_session_close + 3); + TASK_UTIL_EXPECT_TRUE( + agent_x3_->get_session_close() >= client_x3_session_close + 3); + TASK_UTIL_EXPECT_TRUE( + agent_x1_->get_session_close() >= client_x1_session_close + 3); + } DestroyAgents(); } -TEST_P(BgpXmppBasicParamTest2, DISABLED_DuplicateEndpointName2) { +TEST_P(BgpXmppBasicParamTest2, DuplicateEndpointName2) { CreateAgents(); // Bring up one agent with given name. @@ -1364,10 +1391,18 @@ TEST_P(BgpXmppBasicParamTest2, DISABLED_DuplicateEndpointName2) { agent_x2_->SessionUp(); // Make sure that second agent sees sessions getting closed. - TASK_UTIL_EXPECT_TRUE( - agent_x2_->get_session_close() >= client_x2_session_close + 3); - TASK_UTIL_EXPECT_TRUE( - agent_x1_->get_session_close() >= client_x1_session_close); + if (!agent_address_same_) { + TASK_UTIL_EXPECT_TRUE( + agent_x2_->get_session_close() >= client_x2_session_close + 3); + TASK_UTIL_EXPECT_TRUE( + agent_x1_->get_session_close() == client_x1_session_close); + + } else { + TASK_UTIL_EXPECT_TRUE( + agent_x2_->get_session_close() >= client_x2_session_close + 3); + TASK_UTIL_EXPECT_TRUE( + agent_x1_->get_session_close() >= client_x1_session_close + 3); + } // Bring down the first agent and make sure that second comes up. agent_x1_->SessionDown(); diff --git a/src/xmpp/xmpp_connection_manager.h b/src/xmpp/xmpp_connection_manager.h index c435506e578..f8d861d1d74 100644 --- a/src/xmpp/xmpp_connection_manager.h +++ b/src/xmpp/xmpp_connection_manager.h @@ -24,12 +24,14 @@ class XmppConnectionManager : public SslServer { void EnqueueSession(XmppSession *session); size_t GetSessionQueueSize() const; virtual LifetimeActor *deleter() = 0; + tbb::mutex &mutex() const { return mutex_; } private: bool DequeueSession(TcpSessionPtr tcp_session); void WorkQueueExitCallback(bool done); WorkQueue session_queue_; + mutable tbb::mutex mutex_; DISALLOW_COPY_AND_ASSIGN(XmppConnectionManager); }; diff --git a/src/xmpp/xmpp_state_machine.cc b/src/xmpp/xmpp_state_machine.cc index 7870cf0786d..a84cd5800fb 100644 --- a/src/xmpp/xmpp_state_machine.cc +++ b/src/xmpp/xmpp_state_machine.cc @@ -1366,6 +1366,9 @@ bool XmppStateMachine::PassiveOpen(XmppSession *session) { // Return true if msg is enqueued for further processing, false otherwise. void XmppStateMachine::ProcessStreamHeaderMessage(XmppSession *session, const XmppStanza::XmppMessage *msg) { + XmppConnectionManager *connection_manager = + dynamic_cast(connection_->server()); + tbb::mutex::scoped_lock lock(connection_manager->mutex()); // Update "To" information which can be used to map an older session session->Connection()->SetTo(msg->from); @@ -1381,6 +1384,21 @@ void XmppStateMachine::ProcessStreamHeaderMessage(XmppSession *session, // check if older connection is under graceful-restart. if (endpoint && endpoint->connection()) { if (connection_ != endpoint->connection()) { + // Close new connection and retain old connection if the endpoint + // IP addresses do not match. + boost::asio::ip::address addr = + endpoint->connection()->endpoint().address(); + if (connection_->endpoint().address() != addr) { + XMPP_WARNING(XmppDeleteConnection, + "Drop new xmpp connection " + session->ToString() + + " as another connection with same name " + msg->from + + " but with different IP address " + addr.to_string() + + " already exists"); + ProcessEvent(xmsm::EvTcpClose(session)); + delete msg; + return; + } + XmppChannelMux *channel = endpoint->connection()->ChannelMux(); // If GR is not supported, then close all new connections until old @@ -1398,16 +1416,16 @@ void XmppStateMachine::ProcessStreamHeaderMessage(XmppSession *session, if (ready) { XmppStateMachine *sm = endpoint->connection()->state_machine(); - XMPP_DEBUG(XmppDeleteConnection, + XMPP_NOTICE(XmppDeleteConnection, "Delete old xmpp connection " + sm->session()->ToString() + " as a new connection as been initiated"); sm->Enqueue(xmsm::EvTcpClose(sm->session())); } - XMPP_DEBUG(XmppDeleteConnection, - "Drop new xmpp connection " + session->ToString() + - " as current connection is still not deleted"); + XMPP_NOTICE(XmppDeleteConnection, + "Drop new xmpp connection " + session->ToString() + + " as current connection is still not deleted"); ProcessEvent(xmsm::EvTcpClose(session)); delete msg; return; From b23a64092b1285de4faa355e801ae9b1f233777b Mon Sep 17 00:00:00 2001 From: Sachin Bansal Date: Mon, 1 May 2017 10:26:32 -0700 Subject: [PATCH 07/68] Reinitialize total_acl_count for each evaluate If all policies are removed from a network, we were not setting the acl counts back to 0. We should initialize it to 0 before evaluating policies. Change-Id: Ie1575095ca117346b3deefa7fe7d22e0ed59c492 Closes-Bug: 1685458 (cherry picked from commit cb6f312ba1a12ad9835fd09f1ec64d112d4f5e2a) --- src/config/schema-transformer/config_db.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config/schema-transformer/config_db.py b/src/config/schema-transformer/config_db.py index 1a15fefd4e7..41a61ece6a9 100644 --- a/src/config/schema-transformer/config_db.py +++ b/src/config/schema-transformer/config_db.py @@ -1125,6 +1125,7 @@ def evaluate(self): old_service_chains = self.service_chains self.connections = set() self.service_chains = {} + self.acl_rule_count = 0 static_acl_entries = None dynamic_acl_entries = None From 5c8b5684189b87aa1e082330a38174c23a16fee2 Mon Sep 17 00:00:00 2001 From: Sahil Date: Mon, 1 May 2017 13:59:45 -0700 Subject: [PATCH 08/68] Address pairs not allowed with no port security Config: Address pairs should not be allowed when port security is not enabled Change-Id: I211bd7e89d82633bb6701ddb55d1527633ea246d Closes-Bug: 1685030 --- .../api-server/tests/test_crud_basic.py | 24 ++++++++++++- src/config/api-server/vnc_cfg_types.py | 35 +++++++++++++++++++ .../vnc_openstack/neutron_plugin_db.py | 26 ++++++++++++-- 3 files changed, 82 insertions(+), 3 deletions(-) diff --git a/src/config/api-server/tests/test_crud_basic.py b/src/config/api-server/tests/test_crud_basic.py index 4213ab247c5..b303e4c7b09 100644 --- a/src/config/api-server/tests/test_crud_basic.py +++ b/src/config/api-server/tests/test_crud_basic.py @@ -755,6 +755,7 @@ def test_service_interface_type_value(self): port_obj = VirtualMachineInterface( str(uuid.uuid4()), parent_obj=Project(), virtual_machine_interface_properties=vmi_prop) + port_obj.uuid = port_obj.name port_obj.set_virtual_network(vn) @@ -762,7 +763,7 @@ def test_service_interface_type_value(self): #service_interface_type are: management|left|right|other[0-9]* with ExpectedException(BadRequest) as e: port_id = self._vnc_lib.virtual_machine_interface_create(port_obj) - # end test_service_interface_type_value + # end test_service_interface_type_value def test_physical_router_credentials(self): phy_rout_name = self.id() + '-phy-router-1' @@ -785,6 +786,27 @@ def test_physical_router_w_no_user_credentials(self): phy_rout_obj = self._vnc_lib.physical_router_read(id=phy_router.uuid) # end test_physical_router_w_no_user_credentials + def test_port_security_and_allowed_address_pairs(self): + vn = VirtualNetwork('vn-%s' %(self.id())) + self._vnc_lib.virtual_network_create(vn) + + port_obj = VirtualMachineInterface( + str(uuid.uuid4()), parent_obj=Project(), + port_security_enabled=False) + port_obj.uuid = port_obj.name + port_obj.set_virtual_network(vn) + + port_id = self._vnc_lib.virtual_machine_interface_create(port_obj) + addr_pair = AllowedAddressPairs(allowed_address_pair= + [AllowedAddressPair( + ip=SubnetType('1.1.1.0', 24), + mac='02:ce:1b:d7:a6:e7')]) + # updating a port with allowed address pair should throw an exception + # when port security enabled is set to false + port_obj.virtual_machine_interface_allowed_address_pairs = addr_pair + with ExpectedException(BadRequest) as e: + self._vnc_lib.virtual_machine_interface_update(port_obj) + # end test_port_security_and_allowed_address_pairs # end class TestCrud diff --git a/src/config/api-server/vnc_cfg_types.py b/src/config/api-server/vnc_cfg_types.py index 46b313cb424..571f59e0031 100644 --- a/src/config/api-server/vnc_cfg_types.py +++ b/src/config/api-server/vnc_cfg_types.py @@ -689,6 +689,31 @@ def _check_vrouter_link(cls, vmi_data, kvp_dict, obj_dict, db_conn): # end _check_vrouter_link + @classmethod + def _check_port_security_and_address_pairs(cls, obj_dict, db_dict={}): + if ('port_security_enabled' not in obj_dict and + 'virtual_machine_interface_allowed_address_pairs' not in obj_dict): + return True, "" + + if 'port_security_enabled' in obj_dict: + port_security = obj_dict.get('port_security_enabled', True) + else: + port_security = db_dict.get('port_security_enabled', True) + + if 'virtual_machine_interface_allowed_address_pairs' in obj_dict: + address_pairs = obj_dict.get( + 'virtual_machine_interface_allowed_address_pairs') + else: + address_pairs = db_dict.get( + 'virtual_machine_interface_allowed_address_pairs') + + if not port_security and address_pairs is not None: + msg = "Allowed address pairs are not allowed when port "\ + "security is disabled" + return (False, (400, msg)) + + return True, "" + @classmethod def pre_dbe_create(cls, tenant_name, obj_dict, db_conn): vn_dict = obj_dict['virtual_network_refs'][0] @@ -755,6 +780,11 @@ def pre_dbe_create(cls, tenant_name, obj_dict, db_conn): 'value': cls.portbindings['VNIC_TYPE_NORMAL']} kvps.append(vnic_type) + (ok, result) = cls._check_port_security_and_address_pairs(obj_dict) + + if not ok: + return ok, result + return True, "" # end pre_dbe_create @@ -846,6 +876,11 @@ def pre_dbe_update(cls, id, fq_name, obj_dict, db_conn, if new_vlan != old_vlan: return (False, (400, "Cannot change Vlan tag")) + (ok,result) = cls._check_port_security_and_address_pairs(obj_dict, + read_result) + if not ok: + return ok, result + return True, "" # end pre_dbe_update diff --git a/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py b/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py index 7002c2093a2..e67d8c18eac 100644 --- a/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py +++ b/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py @@ -3767,7 +3767,18 @@ def port_create(self, context, port_q): # always request for v4 and v6 ip object and handle the failure # create the object - port_id = self._resource_create('virtual_machine_interface', port_obj) + try: + port_id = self._resource_create('virtual_machine_interface', port_obj) + except BadRequest as e: + msg = "Allowed address pairs are not allowed when port "\ + "security is disabled" + if msg == str(e): + self._raise_contrail_exception( + 'AddressPairAndPortSecurityRequired') + else: + self._raise_contrail_exception( + 'BadRequest', resource='port', msg=str(e)) + self._vnc_lib.chown(port_id, tenant_id) # add support, nova boot --nic subnet-id=subnet_uuid subnet_id = port_q.get('subnet_id') @@ -3869,7 +3880,18 @@ def port_update(self, port_id, port_q): port_obj = self._port_neutron_to_vnc(port_q, None, UPDATE) net_id = port_obj.get_virtual_network_refs()[0]['uuid'] net_obj = self._network_read(net_id) - self._virtual_machine_interface_update(port_obj) + try: + self._virtual_machine_interface_update(port_obj) + except BadRequest as e: + msg = "Allowed address pairs are not allowed when port "\ + "security is disabled" + if msg == str(e): + self._raise_contrail_exception( + 'AddressPairAndPortSecurityRequired') + else: + self._raise_contrail_exception( + 'BadRequest', resource='port', msg=str(e)) + port_obj = self._virtual_machine_interface_read(port_id=port_id) ret_port_q = self._port_vnc_to_neutron(port_obj) From 949ab9db5ad525f77901b55a4e07c52f11d5429e Mon Sep 17 00:00:00 2001 From: Hampapur Ajay Date: Tue, 2 May 2017 17:37:18 -0700 Subject: [PATCH 09/68] Call update during create of logical router too, to check/set ref to route-target for any configured route-target values. Change-Id: Ida9bb18c40bf5576fc11c1dcb536b6a3db806f5a Closes-Bug: 1687824 --- src/config/schema-transformer/config_db.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/config/schema-transformer/config_db.py b/src/config/schema-transformer/config_db.py index 41a61ece6a9..cc66972e2bf 100644 --- a/src/config/schema-transformer/config_db.py +++ b/src/config/schema-transformer/config_db.py @@ -3782,9 +3782,9 @@ def __init__(self, name, obj=None): self.route_tables = set() self.rt_list = set() self.configured_route_target_list = None - self.update_vnc_obj() + obj = obj or self.read_vnc_obj(fq_name=name) - rt_ref = self.obj.get_route_target_refs() + rt_ref = obj.get_route_target_refs() old_rt_key = None if rt_ref: rt_key = rt_ref[0]['to'][0] @@ -3797,12 +3797,13 @@ def __init__(self, name, obj=None): rt_key = "target:%s:%d" % ( GlobalSystemConfigST.get_autonomous_system(), rtgt_num) rtgt_obj = RouteTargetST.locate(rt_key) - self.obj.set_route_target(rtgt_obj.obj) - self._vnc_lib.logical_router_update(self.obj) + obj.set_route_target(rtgt_obj.obj) + self._vnc_lib.logical_router_update(obj) if old_rt_key: RouteTargetST.delete_vnc_obj(old_rt_key) self.route_target = rt_key + self.update(obj) # end __init__ def update(self, obj=None): From be17fb3ee3b163c9e9b7f7cc62bb55f1b0e5a06b Mon Sep 17 00:00:00 2001 From: Sundaresan Rajangam Date: Wed, 3 May 2017 17:43:43 -0700 Subject: [PATCH 10/68] Fix duplicate alarm delete messages When a UVE object gets deleted, any alarms associated with that UVE object also gets deleted. However, the corresponding UVE key is not removed from the alarm table in contrail-alarm-gen, resulting in duplicate alarm delete messages being sent continuously. Change-Id: I598f58687179d82c11f531b337508385098c9036 Closes-Bug: #1615782 --- src/opserver/alarmgen.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/opserver/alarmgen.py b/src/opserver/alarmgen.py index cf9f642853f..af82a978372 100644 --- a/src/opserver/alarmgen.py +++ b/src/opserver/alarmgen.py @@ -1259,13 +1259,16 @@ def send_agg_uve(self, redish, inst, part, acq_time, rows): def send_alarm_update(self, tab, uk): ustruct = None alm_copy = [] - for nm, asm in self.tab_alarms[tab][uk].iteritems(): - uai = asm.get_uai() - if uai: - alm_copy.append(copy.deepcopy(uai)) + if not self.tab_alarms[tab][uk]: + del self.tab_alarms[tab][uk] + else: + for nm, asm in self.tab_alarms[tab][uk].iteritems(): + uai = asm.get_uai() + if uai: + alm_copy.append(copy.deepcopy(uai)) if len(alm_copy) == 0: ustruct = UVEAlarms(name = str(uk).split(':',1)[1], deleted = True) - self._logger.info('deleting alarm:') + self._logger.info('deleting alarm: %s' % (uk)) else: ustruct = UVEAlarms(name = str(uk).split(':',1)[1], alarms = alm_copy) From fc71720c64658f515a8ac6a70d534056b4d0154b Mon Sep 17 00:00:00 2001 From: Sahil Date: Tue, 2 May 2017 10:12:06 -0700 Subject: [PATCH 11/68] [ST]: Fixed get_sandhesh_ref_list function Change-Id: I3d60d02f230ccc188ebbda58701423d5120024e4 Closes-Bug: 1685734 --- src/config/schema-transformer/config_db.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/config/schema-transformer/config_db.py b/src/config/schema-transformer/config_db.py index cc66972e2bf..9be036e1829 100644 --- a/src/config/schema-transformer/config_db.py +++ b/src/config/schema-transformer/config_db.py @@ -123,8 +123,11 @@ def handle_st_object_req(self): st_obj.object_uuid = self.obj.uuid except AttributeError: pass - st_obj.obj_refs = [self._get_sandesh_ref_list(field) - for field in self.ref_fields] + st_obj.obj_refs = [] + for field in self.ref_fields: + if self._get_sandesh_ref_list(field): + st_obj.obj_refs.append(self._get_sandesh_ref_list(field)) + st_obj.properties = [sandesh.PropList(field, str(getattr(self, field))) for field in self.prop_fields if hasattr(self, field)] @@ -135,9 +138,12 @@ def _get_sandesh_ref_list(self, ref_type): ref = getattr(self, ref_type) refs = [ref] if ref else [] except AttributeError: - refs = getattr(self, ref_type + 's') - if isinstance(refs, dict): - refs = refs.keys() + try: + refs = getattr(self, ref_type + 's') + if isinstance(refs, dict): + refs = refs.keys() + except AttributeError: + return return sandesh.RefList(ref_type, refs) # end DBBaseST From 5210d090ef9444845cb1c2c34e0a34a33f39e2f5 Mon Sep 17 00:00:00 2001 From: ashoksingh Date: Sat, 6 May 2017 09:12:19 +0530 Subject: [PATCH 12/68] Send additional fields in VMI UVE Send VN UUID, vlan tags and parent interface name in VMI UVE Closes-Bug: #1673490 Change-Id: I47597fb83278d4f322e5d7257576381e86a77e2f --- src/vnsw/agent/uve/interface.sandesh | 8 ++++++++ src/vnsw/agent/uve/interface_uve_table.cc | 12 ++++++++++++ src/vnsw/agent/uve/vm_uve_table.cc | 19 ------------------- src/vnsw/agent/uve/vm_uve_table.h | 1 - src/vnsw/agent/uve/vn_uve_table.cc | 15 --------------- src/vnsw/agent/uve/vn_uve_table.h | 2 -- src/vnsw/agent/uve/vn_uve_table_base.cc | 4 ---- src/vnsw/agent/uve/vn_uve_table_base.h | 2 -- 8 files changed, 20 insertions(+), 43 deletions(-) diff --git a/src/vnsw/agent/uve/interface.sandesh b/src/vnsw/agent/uve/interface.sandesh index 7da4594d863..5c53545e04e 100644 --- a/src/vnsw/agent/uve/interface.sandesh +++ b/src/vnsw/agent/uve/interface.sandesh @@ -117,6 +117,14 @@ struct UveVMInterfaceAgent { /** @display_name:VMI Packet drop Statistics*/ 34: optional vrouter.AgentDropStats drop_stats (stats="raw_drop_stats:DSNon0:", tags="virtual_network") 35: optional vrouter.AgentDropStats drop_stats_1h (stats="raw_drop_stats:DSSum:3600") + /** UUID of VMI's VN */ + 37: optional string vn_uuid; + /** Transmit VLAN tag for packets over this VMI */ + 38: optional u16 tx_vlan; + /** Recieve VLAN tag for packets over this VMI */ + 39: optional u16 rx_vlan; + /** Parent interface name */ + 40: optional string parent_interface; } /** diff --git a/src/vnsw/agent/uve/interface_uve_table.cc b/src/vnsw/agent/uve/interface_uve_table.cc index f5bcc904ba7..f438cc3da41 100644 --- a/src/vnsw/agent/uve/interface_uve_table.cc +++ b/src/vnsw/agent/uve/interface_uve_table.cc @@ -97,6 +97,7 @@ void InterfaceUveTable::UveInterfaceEntry::SetVnVmInfo(UveVMInterfaceAgent *uve) } if (intf_->vn() != NULL) { uve->set_virtual_network(intf_->vn()->GetName()); + uve->set_vn_uuid(to_string(intf_->vn()->GetUuid())); } else { uve->set_virtual_network(""); } @@ -117,6 +118,17 @@ bool InterfaceUveTable::UveInterfaceEntry::FrameInterfaceMsg(const string &name, s_intf->set_ip6_address(intf_->primary_ip6_addr().to_string()); s_intf->set_ip6_active(intf_->ipv6_active()); s_intf->set_is_health_check_active(intf_->is_hc_active()); + s_intf->set_tx_vlan(intf_->tx_vlan_id()); + s_intf->set_rx_vlan(intf_->rx_vlan_id()); + const Interface *parent = intf_->parent(); + if (parent) { + const VmInterface *p_vmi = dynamic_cast(parent); + if (p_vmi) { + s_intf->set_parent_interface(p_vmi->cfg_name()); + } else { + s_intf->set_parent_interface(parent->name()); + } + } vector uve_fip_list; if (intf_->HasFloatingIp(Address::INET)) { diff --git a/src/vnsw/agent/uve/vm_uve_table.cc b/src/vnsw/agent/uve/vm_uve_table.cc index 9107d484e08..ac4e387d557 100644 --- a/src/vnsw/agent/uve/vm_uve_table.cc +++ b/src/vnsw/agent/uve/vm_uve_table.cc @@ -30,25 +30,6 @@ void VmUveTable::UpdateBitmap(const VmEntry* vm, uint8_t proto, } } -VmUveEntry *VmUveTable::InterfaceIdToVmUveEntry(uint32_t id) { - Interface *intf = InterfaceTable::GetInstance()->FindInterface(id); - if (!intf || intf->type() != Interface::VM_INTERFACE) { - return NULL; - } - VmInterface *vm_intf = static_cast(intf); - /* Ignore if Vm interface does not have a VM */ - if (vm_intf->vm() == NULL) { - return NULL; - } - - UveVmMap::iterator it = uve_vm_map_.find(vm_intf->vm()->GetUuid()); - if (it == uve_vm_map_.end()) { - return NULL; - } - - return static_cast(it->second.get()); -} - VmUveTableBase::VmUveEntryPtr VmUveTable::Allocate(const VmEntry *vm) { VmUveEntryPtr uve(new VmUveEntry(agent_, vm->GetCfgName())); return uve; diff --git a/src/vnsw/agent/uve/vm_uve_table.h b/src/vnsw/agent/uve/vm_uve_table.h index 086b2fb07de..b64232ca4c9 100644 --- a/src/vnsw/agent/uve/vm_uve_table.h +++ b/src/vnsw/agent/uve/vm_uve_table.h @@ -21,7 +21,6 @@ class VmUveTable : public VmUveTableBase { bool Process(VmStatData *vm_stat_data); void SendVmStats(void); virtual void DispatchVmStatsMsg(const VirtualMachineStats &uve); - VmUveEntry *InterfaceIdToVmUveEntry(uint32_t id); protected: virtual void VmStatCollectionStart(VmUveVmState *state, const VmEntry *vm); virtual void VmStatCollectionStop(VmUveVmState *state); diff --git a/src/vnsw/agent/uve/vn_uve_table.cc b/src/vnsw/agent/uve/vn_uve_table.cc index 6f67a09b735..456d6e68602 100644 --- a/src/vnsw/agent/uve/vn_uve_table.cc +++ b/src/vnsw/agent/uve/vn_uve_table.cc @@ -109,21 +109,6 @@ void VnUveTable::UpdateInterVnStats(const string &src, const string &dst, entry->UpdateInterVnStats(dst, bytes, pkts, outgoing); } -void VnUveTable::RemoveInterVnStats(const string &vn) { - UveVnMap::iterator it = uve_vn_map_.find(vn); - if (it == uve_vn_map_.end()) { - return; - } - - VnUveEntry * entry = static_cast(it->second.get()); - entry->ClearInterVnStats(); -} - -void VnUveTable::Delete(const VnEntry *vn) { - RemoveInterVnStats(vn->GetName()); - VnUveTableBase::Delete(vn); -} - void VnUveTable::IncrVnAceStats(const std::string &vn, const std::string &u) { if (vn.empty() || u.empty()) { return; diff --git a/src/vnsw/agent/uve/vn_uve_table.h b/src/vnsw/agent/uve/vn_uve_table.h index 2cf4515293a..59667ee699c 100644 --- a/src/vnsw/agent/uve/vn_uve_table.h +++ b/src/vnsw/agent/uve/vn_uve_table.h @@ -43,9 +43,7 @@ class VnUveTable : public VnUveTableBase { private: virtual VnUveEntryPtr Allocate(const VnEntry *vn); virtual VnUveEntryPtr Allocate(); - virtual void Delete(const VnEntry *vn); bool SendUnresolvedVnMsg(const std::string &vn, UveVirtualNetworkAgent &u); - void RemoveInterVnStats(const std::string &vn); DBTableBase::ListenerId vn_listener_id_; DBTableBase::ListenerId intf_listener_id_; diff --git a/src/vnsw/agent/uve/vn_uve_table_base.cc b/src/vnsw/agent/uve/vn_uve_table_base.cc index eae1901a429..7b29e1732e8 100644 --- a/src/vnsw/agent/uve/vn_uve_table_base.cc +++ b/src/vnsw/agent/uve/vn_uve_table_base.cc @@ -153,10 +153,6 @@ void VnUveTableBase::Delete(const std::string &name) { } } -void VnUveTableBase::Delete(const VnEntry *vn) { - Delete(vn->GetName()); -} - VnUveEntryBase* VnUveTableBase::Add(const VnEntry *vn) { VnUveEntryPtr uve = Allocate(vn); pair ret; diff --git a/src/vnsw/agent/uve/vn_uve_table_base.h b/src/vnsw/agent/uve/vn_uve_table_base.h index 4802de3570e..3f574fcdb26 100644 --- a/src/vnsw/agent/uve/vn_uve_table_base.h +++ b/src/vnsw/agent/uve/vn_uve_table_base.h @@ -39,11 +39,9 @@ class VnUveTableBase { void Shutdown(void); void SendVnAclRuleCount(); bool TimerExpiry(); - void DeleteVnEntry(const UveVnMap::iterator &it); protected: void Delete(const std::string &name); - virtual void Delete(const VnEntry *vn); VnUveEntryBase* UveEntryFromVn(const VnEntry *vn); //The following API is made protected for UT. virtual void DispatchVnMsg(const UveVirtualNetworkAgent &uve); From 4b460f31e1985b2618c4d998325891ca55828820 Mon Sep 17 00:00:00 2001 From: arvindvis Date: Fri, 5 May 2017 10:09:22 -0700 Subject: [PATCH 13/68] The syslog collector has issues with handling syslog messages arriving in TCP so taking off the support. The reason being, unlike UDP, TCP sockets read multiple syslog messages in one read until the buffer gets full. This can toss our parsing code. Closes-Bug:#1687475 Change-Id: I87c72e2154e5eecd19657a1de92c6c28b77fcd35 --- src/analytics/syslog_collector.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/analytics/syslog_collector.cc b/src/analytics/syslog_collector.cc index 643917cf17d..bd6c844092e 100644 --- a/src/analytics/syslog_collector.cc +++ b/src/analytics/syslog_collector.cc @@ -585,8 +585,9 @@ void SyslogTcpListener::Shutdown () } void SyslogTcpListener::Start (std::string ipaddress, int port) { - Initialize (port); - LOG(DEBUG, __func__ << " Initialization of TCP syslog listener @" << port); + //Initialize (port); + //LOG(DEBUG, __func__ << " Initialization of TCP syslog listener @" << port); + LOG(ERROR, __func__ << " TCP syslog listener not supported"); } void SyslogTcpListener::ReadMsg(SyslogQueueEntry *sqe) { From b14290bf497212c4e1629f6ccbfaaf1da637a082 Mon Sep 17 00:00:00 2001 From: Manish Date: Fri, 5 May 2017 19:32:52 +0530 Subject: [PATCH 14/68] ECMP load balance is not effective on remote SI compute. Problem: On SI compute routes are imported from CN with ECMP NH. These routes were not notifying change in load balance params if changed. Solution: Notify on change in ecmp hash fields. Note: Any change in hash fields gets applied to new flows. Existing flows continue to use same index as computed before. Closes-bug: #1643842 Conflicts: src/vnsw/agent/controller/controller_route_path.cc Change-Id: I0216e12ba90b152c58deaaf67b477876eddfc9e8 --- src/vnsw/agent/controller/controller_route_path.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vnsw/agent/controller/controller_route_path.cc b/src/vnsw/agent/controller/controller_route_path.cc index 05a71a949f6..a2db8561807 100644 --- a/src/vnsw/agent/controller/controller_route_path.cc +++ b/src/vnsw/agent/controller/controller_route_path.cc @@ -33,6 +33,7 @@ ControllerPeerPath::ControllerPeerPath(const Peer *peer) : bool ControllerEcmpRoute::AddChangePath(Agent *agent, AgentPath *path, const AgentRoute *rt) { CompositeNHKey *comp_key = static_cast(nh_req_.key.get()); + bool ret = false; //Reorder the component NH list, and add a reference to local composite mpls //label if any bool comp_nh_policy = comp_key->GetPolicy(); @@ -45,10 +46,11 @@ bool ControllerEcmpRoute::AddChangePath(Agent *agent, AgentPath *path, if (path->ecmp_load_balance() != ecmp_load_balance_) { path->UpdateEcmpHashFields(agent, ecmp_load_balance_, - nh_req_); + nh_req_); + ret = true; } - return InetUnicastRouteEntry::ModifyEcmpPath(dest_addr_, plen_, vn_list_, + ret |= InetUnicastRouteEntry::ModifyEcmpPath(dest_addr_, plen_, vn_list_, label_, local_ecmp_nh_, vrf_name_, sg_list_, CommunityList(), @@ -56,6 +58,7 @@ bool ControllerEcmpRoute::AddChangePath(Agent *agent, AgentPath *path, tunnel_bmap_, ecmp_load_balance_, nh_req_, agent, path); + return ret; } ControllerVmRoute *ControllerVmRoute::MakeControllerVmRoute(const Peer *peer, From 8cdc01c4f95b8fa1932b06b561ff5f1f7c4a6d21 Mon Sep 17 00:00:00 2001 From: Sahil Date: Fri, 5 May 2017 14:42:05 -0700 Subject: [PATCH 15/68] [VNC openstack]: Added description field Added description field for neutron objects. Change-Id: I79969fb1b3b026bb0480d94900de72a9acf8372a Closes-Bug: 1686476 Closes-Bug: 1685940 --- .../vnc_openstack/neutron_plugin_db.py | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py b/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py index e67d8c18eac..8a57a0f89ca 100644 --- a/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py +++ b/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py @@ -942,7 +942,6 @@ def _svc_instance_neutron_to_vnc(self, si_q, oper): si_vnc = ServiceInstance(name=si_q['name'], parent_obj=project_obj, service_instance_properties=si_prop) - return si_vnc #end _svc_instance_neutron_to_vnc @@ -959,9 +958,8 @@ def _svc_instance_vnc_to_neutron(self, si_obj): vn_obj = self._vnc_lib.virtual_network_read(fq_name_str=vn_fq_name) si_q_dict['external_net'] = str(vn_obj.uuid) + ' ' + vn_obj.name si_q_dict['internal_net'] = '' - return si_q_dict - #end _route_table_vnc_to_neutron + #end _svc_instance_vnc_to_neutron def _route_table_neutron_to_vnc(self, rt_q, oper): if oper == CREATE: @@ -1020,7 +1018,6 @@ def _route_table_vnc_to_neutron(self, rt_obj): for route in rt_q_dict['routes']['route']: if route['next_hop_type']: route['next_hop'] = route['next_hop_type'] - return rt_q_dict # end _route_table_vnc_to_neutron @@ -1037,6 +1034,7 @@ def _security_group_vnc_to_neutron(self, sg_obj, memo_req=None): sg_q_dict['name'] = sg_obj.get_fq_name()[-1] else: sg_q_dict['name'] = sg_obj.display_name + sg_q_dict['description'] = sg_obj.get_id_perms().get_description() # get security group rules @@ -1294,6 +1292,11 @@ def _network_neutron_to_vnc(self, network_q, oper): if 'port_security_enabled' in network_q: net_obj.set_port_security_enabled(network_q['port_security_enabled']) + if 'description' in network_q: + id_perms = net_obj.get_id_perms() + id_perms.set_description(network_q['description']) + net_obj.set_id_perms(id_perms) + return net_obj #end _network_neutron_to_vnc @@ -1366,6 +1369,9 @@ def _network_vnc_to_neutron(self, net_obj, net_repr='SHOW'): net_q_dict.update(extra_dict) net_q_dict['port_security_enabled'] = net_obj.get_port_security_enabled() + if net_obj.get_id_perms().get_description() is not None: + net_q_dict['description'] = net_obj.get_id_perms().get_description() + return net_q_dict #end _network_vnc_to_neutron @@ -1613,6 +1619,11 @@ def _router_neutron_to_vnc(self, router_q, oper): if 'name' in router_q and router_q['name']: rtr_obj.display_name = router_q['name'] + if 'description' in router_q: + id_perms = rtr_obj.get_id_perms() + id_perms.set_description(router_q['description']) + rtr_obj.set_id_perms(id_perms) + return rtr_obj #end _router_neutron_to_vnc @@ -1648,6 +1659,10 @@ def _router_vnc_to_neutron(self, rtr_obj, rtr_repr='SHOW'): if self._contrail_extensions_enabled: rtr_q_dict.update(extra_dict) + + if rtr_obj.get_id_perms().get_description() is not None: + rtr_q_dict['description'] = rtr_obj.get_id_perms().get_description() + return rtr_q_dict #end _router_vnc_to_neutron @@ -2011,6 +2026,11 @@ def _port_neutron_to_vnc(self, port_q, net_obj, oper): id_perms.enable = port_q['admin_state_up'] port_obj.set_id_perms(id_perms) + if 'description' in port_q: + id_perms = port_obj.get_id_perms() + id_perms.set_description(port_q['description']) + port_obj.set_id_perms(id_perms) + if ('extra_dhcp_opts' in port_q): dhcp_options = [] if port_q['extra_dhcp_opts']: @@ -2296,6 +2316,10 @@ def _port_vnc_to_neutron(self, port_obj, port_req_memo=None): if self._contrail_extensions_enabled: port_q_dict.update(extra_dict) port_q_dict['port_security_enabled'] = port_obj.get_port_security_enabled() + + if port_obj.get_id_perms().get_description() is not None: + port_q_dict['description'] = port_obj.get_id_perms().get_description() + return port_q_dict #end _port_vnc_to_neutron From 32b7c298e5a255817092b4c69c522f47d6efe44a Mon Sep 17 00:00:00 2001 From: Sahil Date: Mon, 8 May 2017 18:13:17 -0700 Subject: [PATCH 16/68] Fix to raise exception for default SG update Updating default SG should not be allowed and should raise a 'SecurityGroupCannotUpdateDefault' neutron exception. We are only checking for this in neutron_plugin_db.py and will still allow the update to default SG from vnc_api. Closes-Bug: 1686496 Change-Id: I7eab47b0047dd2e35ec270572d75d38eac3d90bc --- src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py b/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py index 8a57a0f89ca..52d9899d31d 100644 --- a/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py +++ b/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py @@ -1061,6 +1061,9 @@ def _security_group_neutron_to_vnc(self, sg_q, oper): else: sg_vnc = self._vnc_lib.security_group_read(id=sg_q['id']) + if oper == UPDATE and sg_vnc.name == 'default': + self._raise_contrail_exception("SecurityGroupCannotUpdateDefault") + if 'name' in sg_q and sg_q['name']: sg_vnc.display_name = sg_q['name'] if 'description' in sg_q: From b6925255c67927338347daab2f81d823529d423d Mon Sep 17 00:00:00 2001 From: Ananth Suryanarayana Date: Thu, 4 May 2017 17:04:19 -0700 Subject: [PATCH 17/68] Provide Llgr only support o Send GR capability even when configured restart-time is 0 o Enable GR helper even if received restart-time is 0 Fix a few issues in LLGR o Process received notification code-subcode correctly and trigger GR/LLGR Helper mode accordingly o Add UTs to cover most of the scenarios described above Change-Id: Ibcef9c059408a61e8513b6a9f6e1d015128e6515 Closes-Bug: 1688133 --- src/bgp/bgp_peer.cc | 8 ++- src/bgp/bgp_peer.h | 5 +- src/bgp/bgp_peer_close.cc | 19 +++-- src/bgp/bgp_peer_close.h | 1 + src/bgp/bgp_table.cc | 11 ++- src/bgp/bgp_table.h | 2 +- src/bgp/peer_close_manager.h | 1 + src/bgp/state_machine.cc | 49 ++++++------- src/bgp/state_machine.h | 7 +- src/bgp/test/bgp_msg_builder_test.cc | 5 ++ src/bgp/test/bgp_peer_close_gr_test.cc | 11 ++- src/bgp/test/bgp_server_test.cc | 97 ++++++++++++++++++++++---- src/bgp/test/bgp_server_test_util.cc | 4 +- src/bgp/test/bgp_server_test_util.h | 18 ++--- src/bgp/test/bgp_table_export_test.cc | 18 +++-- src/bgp/test/graceful_restart_test.cc | 12 ---- 16 files changed, 166 insertions(+), 102 deletions(-) diff --git a/src/bgp/bgp_peer.cc b/src/bgp/bgp_peer.cc index 564f4054b1f..2b6dc71c1a9 100644 --- a/src/bgp/bgp_peer.cc +++ b/src/bgp/bgp_peer.cc @@ -912,7 +912,6 @@ string BgpPeer::gateway_address_string(Address::Family family) const { // Reset all stored capabilities information and cancel outstanding timers. // void BgpPeer::CustomClose() { - negotiated_families_.clear(); ResetCapabilities(); keepalive_timer_->Cancel(); @@ -1013,11 +1012,11 @@ BgpSession *BgpPeer::CreateSession() { return bgp_session; } -void BgpPeer::SetAdminState(bool down) { +void BgpPeer::SetAdminState(bool down, int subcode) { if (admin_down_ == down) return; admin_down_ = down; - state_machine_->SetAdminState(down); + state_machine_->SetAdminState(down, subcode); if (admin_down_) { BGP_LOG_PEER(Config, this, SandeshLevel::SYS_INFO, BGP_LOG_FLAG_ALL, BGP_PEER_DIR_NA, "Session cleared due to admin down"); @@ -1287,6 +1286,9 @@ bool BgpPeer::notification() const { // Check if GR Helper mode sould be attempted. bool BgpPeer::AttemptGRHelperMode(int code, int subcode) const { + if (!code) + return true; + if (code == BgpProto::Notification::Cease && (subcode == BgpProto::Notification::HardReset || subcode == BgpProto::Notification::PeerDeconfigured)) { diff --git a/src/bgp/bgp_peer.h b/src/bgp/bgp_peer.h index c3f17077c26..d460dcbe8ec 100644 --- a/src/bgp/bgp_peer.h +++ b/src/bgp/bgp_peer.h @@ -113,7 +113,8 @@ class BgpPeer : public IPeer { BgpSession *CreateSession(); - virtual void SetAdminState(bool down); + virtual void SetAdminState(bool down, + int subcode = BgpProto::Notification::AdminShutdown); // Messages @@ -316,7 +317,7 @@ class BgpPeer : public IPeer { KeyType key_type); void ClearListenSocketAuthKey(); void SetSessionSocketAuthKey(TcpSession *session); - virtual bool AttemptGRHelperMode(int code, int subcode) const; + bool AttemptGRHelperMode(int code, int subcode) const; void Register(BgpTable *table, const RibExportPolicy &policy); void Register(BgpTable *table); bool EndOfRibSendTimerExpired(Address::Family family); diff --git a/src/bgp/bgp_peer_close.cc b/src/bgp/bgp_peer_close.cc index 233fbb9f1a0..37dcc9935c2 100644 --- a/src/bgp/bgp_peer_close.cc +++ b/src/bgp/bgp_peer_close.cc @@ -184,11 +184,12 @@ bool BgpPeerClose::IsGRReady() const { return false; } - // Restart time must be non-zero in order to enable GR Helper mode. - if (!gr_params_.time) { + // LLGR should be in effect to enable GR Helper mode with 0 restart time. + if (!gr_params_.time && !IsCloseLongLivedGracefulInternal()) { BGP_LOG_PEER(Message, peer_, SandeshLevel::SYS_DEBUG, BGP_LOG_FLAG_ALL, BGP_PEER_DIR_IN, "GR Helper mode is not enabled because received " - "GR restart time value is 0 seconds"); + "GR restart time value is 0 seconds and (there is no applicable " + "LLGR as well"); return false; } @@ -298,7 +299,10 @@ bool BgpPeerClose::IsLlgrSupportedForFamilies() const { bool BgpPeerClose::IsCloseLongLivedGraceful() const { if (!IsCloseGraceful()) return false; + return BgpPeerClose::IsCloseLongLivedGracefulInternal(); +} +bool BgpPeerClose::IsCloseLongLivedGracefulInternal() const { if (llgr_params_.families.empty()) { BGP_LOG_PEER(Message, peer_, SandeshLevel::SYS_DEBUG, BGP_LOG_FLAG_ALL, BGP_PEER_DIR_IN, @@ -358,14 +362,13 @@ void BgpPeerClose::AddGRCapabilities( BgpProto::OpenMessage::OptParam *opt_param) { vector gr_families; vector afi_flags; - uint16_t time = peer_->server()->GetGracefulRestartTime(); // Indicate to the Peer if we are restarting (equiv when the session is // being formed for the first time. bool restarted = peer_->flap_count() == 0; // Indicate EOR support by default. - if (!time) { + if (!peer_->server()->global_config()->gr_enable()) { BgpProto::OpenMessage::Capability *gr_cap = BgpProto::OpenMessage::Capability::GR::Encode(0, restarted, false, afi_flags, @@ -382,6 +385,7 @@ void BgpPeerClose::AddGRCapabilities( ForwardingStatePreservedFlag); } + uint16_t time = peer_->server()->GetGracefulRestartTime(); BgpProto::OpenMessage::Capability *gr_cap = BgpProto::OpenMessage::Capability::GR::Encode(time, restarted, true, afi_flags, gr_families); @@ -425,9 +429,10 @@ bool BgpPeerClose::SetGRCapabilities(BgpPeerInfoData *peer_info) { void BgpPeerClose::AddLLGRCapabilities( BgpProto::OpenMessage::OptParam *opt_param) { - if (!peer_->server()->GetGracefulRestartTime() || - !peer_->server()->GetLongLivedGracefulRestartTime()) + if (!peer_->server()->global_config()->gr_enable() || + !peer_->server()->GetLongLivedGracefulRestartTime()) { return; + } vector llgr_families; BOOST_FOREACH(Address::Family family, peer_->supported_families()) { diff --git a/src/bgp/bgp_peer_close.h b/src/bgp/bgp_peer_close.h index 08e1c8f6a05..55d074f541b 100644 --- a/src/bgp/bgp_peer_close.h +++ b/src/bgp/bgp_peer_close.h @@ -67,6 +67,7 @@ class BgpPeerClose : public IPeerClose { virtual const std::vector &capabilities() const; bool IsLlgrSupportedForFamilies() const; + bool IsCloseLongLivedGracefulInternal() const; BgpPeer *peer_; uint64_t flap_count_; diff --git a/src/bgp/bgp_table.cc b/src/bgp/bgp_table.cc index 61c79146a5a..81f8fd63179 100644 --- a/src/bgp/bgp_table.cc +++ b/src/bgp/bgp_table.cc @@ -165,7 +165,7 @@ void BgpTable::ProcessRemovePrivate(const RibOut *ribout, BgpAttr *attr) const { // attach NO_EXPORT community instead. // void BgpTable::ProcessLlgrState(const RibOut *ribout, const BgpPath *path, - BgpAttr *attr) { + BgpAttr *attr, bool llgr_stale_comm) { if (!server() || !server()->comm_db()) return; @@ -173,9 +173,6 @@ void BgpTable::ProcessLlgrState(const RibOut *ribout, const BgpPath *path, if (family() == Address::RTARGET) return; - bool llgr_stale_comm = attr->community() && - attr->community()->ContainsValue(CommunityType::LlgrStale); - // If the path is not marked as llgr_stale or if it does not have the // LLGR_STALE community, then no action is necessary. if (!path->IsLlgrStale() && !llgr_stale_comm) @@ -210,8 +207,6 @@ void BgpTable::ProcessLlgrState(const RibOut *ribout, const BgpPath *path, attr->community(), CommunityType::NoExport); attr->set_community(comm); } - - return; } UpdateInfo *BgpTable::GetUpdateInfo(RibOut *ribout, BgpRoute *route, @@ -257,6 +252,8 @@ UpdateInfo *BgpTable::GetUpdateInfo(RibOut *ribout, BgpRoute *route, const IPeer *peer = path->GetPeer(); BgpAttr *clone = NULL; + bool llgr_stale_comm = attr->community() && + attr->community()->ContainsValue(CommunityType::LlgrStale); if (ribout->peer_type() == BgpProto::IBGP) { // Split horizon check. if (peer && peer->PeerType() == BgpProto::IBGP) @@ -371,7 +368,7 @@ UpdateInfo *BgpTable::GetUpdateInfo(RibOut *ribout, BgpRoute *route, } assert(clone); - ProcessLlgrState(ribout, path, clone); + ProcessLlgrState(ribout, path, clone, llgr_stale_comm); // Locate the new BgpAttrPtr. attr_ptr = clone->attr_db()->Locate(clone); diff --git a/src/bgp/bgp_table.h b/src/bgp/bgp_table.h index 9a462ad6577..7488fce3182 100644 --- a/src/bgp/bgp_table.h +++ b/src/bgp/bgp_table.h @@ -169,7 +169,7 @@ class BgpTable : public RouteTable { void ProcessRemovePrivate(const RibOut *ribout, BgpAttr *attr) const; void ProcessLlgrState(const RibOut *ribout, const BgpPath *path, - BgpAttr *attr); + BgpAttr *attr, bool llgr_stale_comm); virtual BgpRoute *TableFind(DBTablePartition *rtp, const DBRequestKey *prefix) = 0; diff --git a/src/bgp/peer_close_manager.h b/src/bgp/peer_close_manager.h index 6f6f362105a..869c6906b7f 100644 --- a/src/bgp/peer_close_manager.h +++ b/src/bgp/peer_close_manager.h @@ -69,6 +69,7 @@ class PeerCloseManager { uint32_t path_flags) const; private: + friend class BgpServerUnitTest; friend class PeerCloseTest; friend class PeerCloseManagerTest; friend class GracefulRestartTest; diff --git a/src/bgp/state_machine.cc b/src/bgp/state_machine.cc index 60b90ac6bef..3fc251559a7 100644 --- a/src/bgp/state_machine.cc +++ b/src/bgp/state_machine.cc @@ -1127,9 +1127,9 @@ void StateMachine::Shutdown(int subcode) { Enqueue(fsm::EvStop(subcode)); } -void StateMachine::SetAdminState(bool down) { +void StateMachine::SetAdminState(bool down, int subcode) { if (down) { - Enqueue(fsm::EvStop(BgpProto::Notification::AdminShutdown)); + Enqueue(fsm::EvStop(subcode)); } else { // Reset all previous state. reset_idle_hold_time(); @@ -1151,21 +1151,24 @@ void StateMachine::UpdateFlapCount() { } } -template -void StateMachine::OnIdle(const Ev &event) { +void StateMachine::PeerClose(int code, int subcode) { UpdateFlapCount(); + peer_->Close(peer_->AttemptGRHelperMode(code, subcode)); + set_idle_hold_time(idle_hold_time() ? idle_hold_time() : kIdleHoldTime); + reset_hold_time(); +} - // Release all resources. - SendNotificationAndClose(peer_->session(), code); +template +void StateMachine::OnIdle(const Ev &event) { + SendNotification(peer_->session(), code); + PeerClose(code, 0); } template void StateMachine::OnIdleCease(const Ev &event) { - UpdateFlapCount(); - - // Release all resources. - SendNotificationAndClose( - peer_->session(), BgpProto::Notification::Cease, event.subcode); + SendNotification(peer_->session(), BgpProto::Notification::Cease, + event.subcode); + PeerClose(BgpProto::Notification::Cease, event.subcode); } // @@ -1174,19 +1177,16 @@ void StateMachine::OnIdleCease(const Ev &event) { // template void StateMachine::OnIdleError(const Ev &event) { - UpdateFlapCount(); - - // Release all resources. - SendNotificationAndClose(event.session, code, event.subcode, event.data); + SendNotification(event.session, code, event.subcode, event.data); + PeerClose(code, event.subcode); } +// Close the peer. No need to send a notification as peer has already closed +// this session by sending us a notification message. void StateMachine::OnIdleNotification(const fsm::EvBgpNotification &event) { - UpdateFlapCount(); - - // Release all resources. - SendNotificationAndClose(peer()->session(), 0); + PeerClose(event.msg->error, event.msg->subcode); set_last_notification_in(event.msg->error, event.msg->subcode, - event.Name()); + event.Name()); } int StateMachine::GetConnectTime() const { @@ -1348,8 +1348,8 @@ BgpSession *StateMachine::passive_session() { return passive_session_; } -void StateMachine::SendNotificationAndClose(BgpSession *session, int code, - int subcode, const std::string &data) { +void StateMachine::SendNotification(BgpSession *session, int code, int subcode, + const std::string &data) { // Prefer the passive session if available since it's operational. if (!session) session = passive_session_; @@ -1357,11 +1357,6 @@ void StateMachine::SendNotificationAndClose(BgpSession *session, int code, session = active_session_; if (session && code != 0) peer_->SendNotification(session, code, subcode, data); - - set_idle_hold_time(idle_hold_time() ? idle_hold_time() : kIdleHoldTime); - reset_hold_time(); - bool graceful = !code || peer_->AttemptGRHelperMode(code, subcode); - peer_->Close(graceful); } // diff --git a/src/bgp/state_machine.h b/src/bgp/state_machine.h index 730d3d05015..942ec1bef0b 100644 --- a/src/bgp/state_machine.h +++ b/src/bgp/state_machine.h @@ -98,7 +98,7 @@ class StateMachine : public sc::state_machine { void Initialize(); void Shutdown(int subcode); - void SetAdminState(bool down); + void SetAdminState(bool down, int subcode); bool IsQueueEmpty() const; template void OnIdle(const Ev &event); @@ -135,8 +135,8 @@ class StateMachine : public sc::state_machine { size_t msgsize = 0); void OnMessageError(BgpSession *session, const ParseErrorContext *context); - void SendNotificationAndClose(BgpSession *session, - int code, int subcode = 0, const std::string &data = std::string()); + void SendNotification(BgpSession *session, int code, int subcode = 0, + const std::string &data = std::string()); bool ProcessNotificationEvent(BgpSession *session); void SetDataCollectionKey(BgpPeerInfo *peer_info) const; @@ -211,6 +211,7 @@ class StateMachine : public sc::state_machine { bool DequeueEvent(EventContainer ec); void DequeueEventDone(bool done); void UpdateFlapCount(); + void PeerClose(int code, int subcode); WorkQueue work_queue_; BgpPeer *peer_; diff --git a/src/bgp/test/bgp_msg_builder_test.cc b/src/bgp/test/bgp_msg_builder_test.cc index f27864c9d55..009cc5d9747 100644 --- a/src/bgp/test/bgp_msg_builder_test.cc +++ b/src/bgp/test/bgp_msg_builder_test.cc @@ -189,6 +189,11 @@ TEST_F(BgpMsgBuilderTest, Build) { void BgpMsgBuilderTest::TestAttemptGRHelperMode(bool notification, int code, int subcode) const { + if (!code) { + EXPECT_TRUE(peer_->AttemptGRHelperMode(code, subcode)); + return; + } + if (code < BgpProto::Notification::MsgHdrErr || code > BgpProto::Notification::Cease) return; diff --git a/src/bgp/test/bgp_peer_close_gr_test.cc b/src/bgp/test/bgp_peer_close_gr_test.cc index 03716c6bec9..98694a4a707 100644 --- a/src/bgp/test/bgp_peer_close_gr_test.cc +++ b/src/bgp/test/bgp_peer_close_gr_test.cc @@ -394,11 +394,6 @@ TEST_P(BgpPeerCloseGrTestParam, TestSetGRCapabilities) { expected = false; } - if (expected && !gr_info->time) { - mismatch = __LINE__; - expected = false; - } - if (expected && !bgp_peer_close_test_->negotiated_families().empty() && bgp_peer_close_test_->PeerNegotiatedFamilies() != bgp_peer_close_test_->negotiated_families()) { @@ -428,7 +423,10 @@ TEST_P(BgpPeerCloseGrTestParam, TestSetGRCapabilities) { } } - if (expected && bgp_peer_close_test_->IsInLlgrTimerWaitState()) { + // If there GR time is zero of if in LLGR timer state, check if LLGR + // should take into effect. + if (expected && (bgp_peer_close_test_->IsInLlgrTimerWaitState() || + !gr_info->time)) { if (!llgr_info || !llgr_info->time || llgr_families.empty()) { mismatch = __LINE__; expected = false; @@ -474,6 +472,7 @@ TEST_P(BgpPeerCloseGrTestParam, TestSetGRCapabilities) { } } } + EXPECT_EQ(expected, bgp_peer_close_test_->SetGRCapabilities(NULL)); if (expected) EXPECT_EQ(0, mismatch); diff --git a/src/bgp/test/bgp_server_test.cc b/src/bgp/test/bgp_server_test.cc index 39c0fc6a0a8..931caa1bf2d 100644 --- a/src/bgp/test/bgp_server_test.cc +++ b/src/bgp/test/bgp_server_test.cc @@ -233,6 +233,8 @@ class BgpServerUnitTest : public ::testing::Test { bool delete_config, vector auth_keys = vector()); + void GRTestCommon(bool hard_reset, size_t expected_stale_count, + size_t expected_llgr_stale_count); auto_ptr evm_; auto_ptr thread_; @@ -247,6 +249,8 @@ class BgpServerUnitTest : public ::testing::Test { as_t a_old_local_as_; as_t b_old_local_as_; bool gr_enabled_; + string gr_time_; + string llgr_time_; }; bool BgpServerUnitTest::validate_done_; @@ -297,8 +301,9 @@ string BgpServerUnitTest::GetConfigStr(int peer_count, config << "\ \ true\ - 600\ - 60000\ + " + gr_time_ + "\ + " + llgr_time_ + + "\ 120\ true\ true\ @@ -2667,12 +2672,13 @@ TEST_F(BgpServerUnitTest, DeleteInProgress) { } } -TEST_F(BgpServerUnitTest, DeleteDuringGR) { +void BgpServerUnitTest::GRTestCommon(bool hard_reset, + size_t expected_stale_count, + size_t expected_llgr_stale_count) { vector families_a; vector families_b; families_a.push_back("inet"); families_b.push_back("inet"); - gr_enabled_ = true; SetupPeers(1, a_->session_manager()->GetPort(), @@ -2734,15 +2740,34 @@ TEST_F(BgpServerUnitTest, DeleteDuringGR) { // Close bgp-session and trigger GR process. Also keep idle-hold time long // in order to hold the state in idle after GR session restart.. string uuid = BgpConfigParser::session_uuid("A", "B", 1); - BgpPeer *peer_a = a_->FindPeerByUuid(BgpConfigManager::kMasterInstance, - uuid); - task_util::TaskFire(boost::bind(&BgpPeer::Clear, peer_a, - BgpProto::Notification::Unknown), "bgp::Config"); + BgpPeerTest *peer_a = static_cast( + a_->FindPeerByUuid(BgpConfigManager::kMasterInstance, uuid)); + BgpPeerTest *peer_b = static_cast( + b_->FindPeerByUuid(BgpConfigManager::kMasterInstance, uuid)); + TASK_UTIL_EXPECT_TRUE(peer_b->IsReady()); + + int subcode = hard_reset ? BgpProto::Notification::HardReset : + BgpProto::Notification::AdminShutdown; + peer_b->SetAdminState(true, subcode); // Trigger GR Helper in peer_a + TASK_UTIL_EXPECT_FALSE(peer_a->IsReady()); + TASK_UTIL_EXPECT_FALSE(peer_b->IsReady()); task_util::WaitForIdle(); - BGP_VERIFY_ROUTE_COUNT(table_a, 1); - BGP_VERIFY_ROUTE_COUNT(table_b, 1); - BGP_VERIFY_ROUTE_PRESENCE(table_a, &key1); - BGP_VERIFY_ROUTE_PRESENCE(table_b, &key1); + + TASK_UTIL_EXPECT_EQ(expected_stale_count, + peer_a->close_manager()->stats().stale); + TASK_UTIL_EXPECT_EQ(expected_llgr_stale_count, + peer_a->close_manager()->stats().llgr_stale); + + // If timer value quite large, verify for the presence of routes as well. + if (!hard_reset && (gr_time_ == "1024" || llgr_time_== "1024")) { + BGP_VERIFY_ROUTE_COUNT(table_a, 1); + BGP_VERIFY_ROUTE_COUNT(table_b, 1); + BGP_VERIFY_ROUTE_PRESENCE(table_a, &key1); + BGP_VERIFY_ROUTE_PRESENCE(table_b, &key1); + TASK_UTIL_EXPECT_EQ(expected_stale_count, table_a->GetStalePathCount()); + TASK_UTIL_EXPECT_EQ(expected_llgr_stale_count, + table_a->GetLlgrStalePathCount()); + } // Delete all the prefixes from A and make sure they are gone from B. // Let TearDown() delete all the peers as well, triggering cleanup in the @@ -2754,6 +2779,54 @@ TEST_F(BgpServerUnitTest, DeleteDuringGR) { BGP_VERIFY_ROUTE_COUNT(table_b, 0); } +TEST_F(BgpServerUnitTest, GR_NonZeroGrTimeAndNonZeroLlgrTime) { + gr_time_ = "1"; + llgr_time_ = "1024"; + GRTestCommon(false, 1, 1); +} + +TEST_F(BgpServerUnitTest, GR_NonZeroGrTimeAndZeroLlgrTime) { + gr_time_ = "1024"; + llgr_time_ = "0"; + GRTestCommon(false, 1, 0); +} + +TEST_F(BgpServerUnitTest, GR_ZeroGrTimeAndNonZeroLlgrTime) { + gr_time_ = "0"; + llgr_time_ = "1024"; + GRTestCommon(false, 1, 1); +} + +TEST_F(BgpServerUnitTest, GR_ZeroGrTimeAndZeroLlgrTime) { + gr_time_ = "0"; + llgr_time_ = "0"; + GRTestCommon(false, 0, 0); +} + +TEST_F(BgpServerUnitTest, GR_NonZeroGrTimeAndNonZeroLlgrTime_HardReset) { + gr_time_ = "1"; + llgr_time_ = "1024"; + GRTestCommon(true, 0, 0); +} + +TEST_F(BgpServerUnitTest, GR_NonZeroGrTimeAndZeroLlgrTime_HardReset) { + gr_time_ = "1024"; + llgr_time_ = "0"; + GRTestCommon(true, 0, 0); +} + +TEST_F(BgpServerUnitTest, GR_ZeroGrTimeAndNonZeroLlgrTime_HardReset) { + gr_time_ = "0"; + llgr_time_ = "1024"; + GRTestCommon(true, 0, 0); +} + +TEST_F(BgpServerUnitTest, GR_ZeroGrTimeAndZeroLlgrTime_HardReset) { + gr_time_ = "0"; + llgr_time_ = "0"; + GRTestCommon(true, 0, 0); +} + TEST_F(BgpServerUnitTest, CloseInProgress) { ConcurrencyScope scope("bgp::Config"); int peer_count = 3; diff --git a/src/bgp/test/bgp_server_test_util.cc b/src/bgp/test/bgp_server_test_util.cc index d32533e36fe..ccd735a7243 100644 --- a/src/bgp/test/bgp_server_test_util.cc +++ b/src/bgp/test/bgp_server_test_util.cc @@ -207,11 +207,11 @@ bool BgpPeerTest::ProcessRequest(Request *request) { CHECK_CONCURRENCY("bgp::Config"); switch (request->type) { case ADMIN_UP: - BgpPeer::SetAdminState(false); + BgpPeer::SetAdminState(false, request->subcode); request->result = true; break; case ADMIN_DOWN: - BgpPeer::SetAdminState(true); + BgpPeer::SetAdminState(true, request->subcode); request->result = true; break; } diff --git a/src/bgp/test/bgp_server_test_util.h b/src/bgp/test/bgp_server_test_util.h index 93726e45b7f..30138d104ea 100644 --- a/src/bgp/test/bgp_server_test_util.h +++ b/src/bgp/test/bgp_server_test_util.h @@ -285,35 +285,26 @@ class BgpPeerTest : public BgpPeer { const int id() const { return id_; } void set_id(int id) { id_ = id; } - virtual void SetAdminState(bool down) { + virtual void SetAdminState(bool down, + int subcode = BgpProto::Notification::AdminShutdown) { if (!ConcurrencyChecker::IsInMainThr()) { - BgpPeer::SetAdminState(down); + BgpPeer::SetAdminState(down, subcode); return; } tbb::interface5::unique_lock lock(work_mutex_); Request request; request.type = down ? ADMIN_DOWN : ADMIN_UP; + request.subcode = subcode; work_queue_.Enqueue(&request); // Wait for the request to get processed. cond_var_.wait(lock); } - bool AttemptGRHelperModeDefault(int code, int subcode) const { - return BgpPeer::AttemptGRHelperMode(code, subcode); - } - - virtual bool AttemptGRHelperMode(int code, int subcode) const { - if (attempt_gr_helper_mode_fnc_.empty()) - return AttemptGRHelperModeDefault(code, subcode); - return attempt_gr_helper_mode_fnc_(code, subcode); - } - boost::function SendUpdate_fnc_; boost::function MpNlriAllowed_fnc_; boost::function IsReady_fnc_; - boost::function attempt_gr_helper_mode_fnc_; BgpTestUtil util_; @@ -322,6 +313,7 @@ class BgpPeerTest : public BgpPeer { struct Request { Request() : result(false) { } RequestType type; + int subcode; bool result; }; bool ProcessRequest(Request *request); diff --git a/src/bgp/test/bgp_table_export_test.cc b/src/bgp/test/bgp_table_export_test.cc index c120e071b75..542751edfe5 100644 --- a/src/bgp/test/bgp_table_export_test.cc +++ b/src/bgp/test/bgp_table_export_test.cc @@ -1427,7 +1427,7 @@ class BgpTableExportParamTest6 : } public: - void VerifyLlgrState() { + bool VerifyLlgrState() { const UpdateInfo &uinfo = uinfo_slist_->front(); const BgpAttr *attr = uinfo.roattr.attr(); @@ -1436,7 +1436,7 @@ class BgpTableExportParamTest6 : if (!path_llgr_ && !comm_llgr_) { EXPECT_EQ(static_cast(NULL), attr->community()); EXPECT_EQ(internal_ ? 100 : 0, attr->local_pref()); - return; + return true; } // If peer supports LLGR, then expect attribute with LLGR_STALE @@ -1445,13 +1445,14 @@ class BgpTableExportParamTest6 : EXPECT_TRUE( attr->community()->ContainsValue(CommunityType::LlgrStale)); EXPECT_EQ(internal_ ? 100 : 0, attr->local_pref()); - return; + return true; } - // Since peer does not support LLGR, expect NoExport community. - // Local preference should be down as well to 1. (for ibgp) + // Since peer does not support LLGR, expect NoExport community and + // local preference as 0. EXPECT_TRUE(attr->community()->ContainsValue(CommunityType::NoExport)); EXPECT_EQ(0, attr->local_pref()); + return true; } bool peer_llgr_; @@ -1482,8 +1483,11 @@ TEST_P(BgpTableExportParamTest6, Llgr) { // Run through the export routine and verify generated LLGR attributes. RunExport(); - VerifyExportAccept(); - VerifyLlgrState(); + if (VerifyLlgrState()) { + VerifyExportAccept(); + } else { + VerifyExportReject(); + } } INSTANTIATE_TEST_CASE_P(Instance, BgpTableExportParamTest6, diff --git a/src/bgp/test/graceful_restart_test.cc b/src/bgp/test/graceful_restart_test.cc index c468ff5cd51..77a1df81acd 100644 --- a/src/bgp/test/graceful_restart_test.cc +++ b/src/bgp/test/graceful_restart_test.cc @@ -348,7 +348,6 @@ class GracefulRestartTest : public ::testing::TestWithParam { void WaitForAgentToBeEstablished(test::NetworkAgentMock *agent); void WaitForPeerToBeEstablished( BgpPeerTest *peer); void BgpPeersAdminUpOrDown(bool down); - bool AttemptGRHelperMode(BgpPeerTest *peer, int code, int subcode) const; void SandeshStartup(); void SandeshShutdown(); @@ -633,9 +632,6 @@ void GracefulRestartTest::Configure() { BgpPeerTest *peer = bgp_servers_[0]->FindPeerByUuid( BgpConfigManager::kMasterInstance, uuid); peer->set_id(i-1); - peer->attempt_gr_helper_mode_fnc_ = - boost::bind(&GracefulRestartTest::AttemptGRHelperMode, this, peer, - _1, _2); bgp_server_peers_.push_back(peer); } } @@ -1181,14 +1177,6 @@ void GracefulRestartTest::VerifyRoutingInstances(BgpServer *server) { BgpConfigManager::kMasterInstance)); } -bool GracefulRestartTest::AttemptGRHelperMode(BgpPeerTest *peer, int code, - int subcode) const { - if (code == BgpProto::Notification::Cease && - subcode == BgpProto::Notification::AdminShutdown) - return true; - return peer->AttemptGRHelperModeDefault(code, subcode); -} - // Invoke stale timer callbacks directly to speed up. void GracefulRestartTest::GRTimerCallback(PeerCloseManagerTest *pc) { CHECK_CONCURRENCY("timer::TimerTask"); From 7568bd793d9e27c500416488a1d34535aa7b3098 Mon Sep 17 00:00:00 2001 From: Albert Khoury Aouad Date: Wed, 11 Jan 2017 16:23:29 +0000 Subject: [PATCH 18/68] Fixing introspect bug When accessing control-node introspect, bgp standard communities does not show up that are configured as part of static routes. It is seen that extended communities associated with static routes override the bgp standard communities in introspect. Modify method FillRoutePathExtCommunityInfo in /opt/stack/contrail/controller/src/bgp/bgp_route.cc to avoid overwriting bgp standard communities. Change-Id: I13f2d2b338a5464afd56ebc558036fea507b2394 Closes-Bug:1655088 --- src/bgp/bgp_route.cc | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/bgp/bgp_route.cc b/src/bgp/bgp_route.cc index 59d08171638..52753a335d7 100644 --- a/src/bgp/bgp_route.cc +++ b/src/bgp/bgp_route.cc @@ -323,18 +323,15 @@ static void FillRoutePathClusterListInfo(const ClusterList *clist, } static void FillRoutePathCommunityInfo(const Community *comm, - ShowRoutePath *show_path) { - vector communities = vector(); - comm->BuildStringList(&communities); - show_path->set_communities(communities); + ShowRoutePath *show_path, vector *communities) { + comm->BuildStringList(communities); } static void FillRoutePathExtCommunityInfo(const BgpTable *table, const ExtCommunity *extcomm, - ShowRoutePath *show_path) { + ShowRoutePath *show_path, vector *communities) { const RoutingInstance *ri = table->routing_instance(); const RoutingInstanceMgr *ri_mgr = ri->manager(); - vector communities = vector(); vector tunnel_encap = vector(); const ExtCommunity::ExtCommunityList &v = extcomm->communities(); @@ -342,40 +339,40 @@ static void FillRoutePathExtCommunityInfo(const BgpTable *table, it != v.end(); ++it) { if (ExtCommunity::is_route_target(*it)) { RouteTarget rt(*it); - communities.push_back(rt.ToString()); + communities->push_back(rt.ToString()); } else if (ExtCommunity::is_default_gateway(*it)) { DefaultGateway dgw(*it); - communities.push_back(dgw.ToString()); + communities->push_back(dgw.ToString()); } else if (ExtCommunity::is_es_import(*it)) { EsImport es_import(*it); - communities.push_back(es_import.ToString()); + communities->push_back(es_import.ToString()); } else if (ExtCommunity::is_esi_label(*it)) { EsiLabel esi_label(*it); - communities.push_back(esi_label.ToString()); + communities->push_back(esi_label.ToString()); } else if (ExtCommunity::is_mac_mobility(*it)) { MacMobility mm(*it); - communities.push_back(mm.ToString()); + communities->push_back(mm.ToString()); show_path->set_sequence_no(mm.ToString()); } else if (ExtCommunity::is_origin_vn(*it)) { OriginVn origin_vn(*it); - communities.push_back(origin_vn.ToString()); + communities->push_back(origin_vn.ToString()); int vn_index = origin_vn.vn_index(); show_path->set_origin_vn( ri_mgr->GetVirtualNetworkByVnIndex(vn_index)); } else if (ExtCommunity::is_security_group(*it)) { SecurityGroup sg(*it); - communities.push_back(sg.ToString()); + communities->push_back(sg.ToString()); } else if (ExtCommunity::is_route_target(*it)) { SiteOfOrigin soo(*it); - communities.push_back(soo.ToString()); + communities->push_back(soo.ToString()); } else if (ExtCommunity::is_tunnel_encap(*it)) { TunnelEncap encap(*it); - communities.push_back(encap.ToString()); + communities->push_back(encap.ToString()); TunnelEncapType::Encap id = encap.tunnel_encap(); tunnel_encap.push_back(TunnelEncapType::TunnelEncapToString(id)); } else if (ExtCommunity::is_load_balance(*it)) { LoadBalance load_balance(*it); - communities.push_back(load_balance.ToString()); + communities->push_back(load_balance.ToString()); ShowLoadBalance show_load_balance; load_balance.ShowAttribute(&show_load_balance); @@ -386,10 +383,9 @@ static void FillRoutePathExtCommunityInfo(const BgpTable *table, for (size_t i = 0; i < it->size(); i++) { len += snprintf(temp+len, sizeof(temp) - len, "%02x", (*it)[i]); } - communities.push_back(string(temp)); + communities->push_back(string(temp)); } } - show_path->set_communities(communities); show_path->set_tunnel_encap(tunnel_encap); } @@ -482,11 +478,16 @@ void BgpRoute::FillRouteInfo(const BgpTable *table, if (attr->cluster_list()) { FillRoutePathClusterListInfo(attr->cluster_list(), &srp); } + vector communities; if (attr->community()) { - FillRoutePathCommunityInfo(attr->community(), &srp); + FillRoutePathCommunityInfo(attr->community(), &srp, &communities); } if (attr->ext_community()) { - FillRoutePathExtCommunityInfo(table, attr->ext_community(), &srp); + FillRoutePathExtCommunityInfo(table, attr->ext_community(), &srp, + &communities); + } + if (!communities.empty()) { + srp.set_communities(communities); } if (srp.get_origin_vn().empty() && !table->IsVpnTable() && path->IsVrfOriginated()) { From 003fae9806be30ffee748e0c8fb9687021ec5157 Mon Sep 17 00:00:00 2001 From: bfernando Date: Tue, 2 May 2017 16:32:55 +0200 Subject: [PATCH 19/68] Fix nework-id and device-id filters management * When both filters (network-id and device-id) are used, the vnc_openstack has to compare the device-id and the network-id. Both have to be present in the vmi_obj refs. The vnc_lib with both refs returns a result with an OR logical operation, therefore with the current comparison the vnc_openstack returns a result with an AND on both filters. * Add a new conditional entry that manages the case if there is only the network-id provided in the filters. Change-Id: I3054345a84caeb892e85a350533e4e600f353665 Closes-Bug: #1685764 (cherry picked from commit 8ff6c714ebe4afde5613b0d3cb4d3ff0ac2ee504) --- .../vnc_openstack/neutron_plugin_db.py | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py b/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py index 52d9899d31d..d626e66d17e 100644 --- a/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py +++ b/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py @@ -4021,14 +4021,6 @@ def port_list(self, context=None, filters=None): port_objs = [] if filters.get('device_id'): back_ref_ids = filters.get('device_id') - if filters.get('network_id'): - back_ref_ids += filters.get('network_id') - # Get all VM port - port_objs_filtered_by_device_id =\ - self._virtual_machine_interface_list( - obj_uuids=filters.get('id'), - back_ref_id=back_ref_ids) - port_objs_filtered_by_device_id = [] founded_device_ids = set() for vmi_obj in self._virtual_machine_interface_list( @@ -4036,7 +4028,18 @@ def port_list(self, context=None, filters=None): back_ref_id=back_ref_ids): for device_ref in vmi_obj.get_virtual_machine_refs() or [] +\ vmi_obj.get_logical_router_back_refs() or []: - if device_ref['uuid'] in filters.get('device_id'): + # check if the device-id matches and if the network-id + # filter is set + if device_ref['uuid'] in filters.get('device_id') and \ + filters.get('network_id'): + for vn_ref in vmi_obj.get_virtual_network_refs() or []: + # add only the vmi_obj that has also the same + # network-id + if vn_ref['uuid'] in filters.get('network_id'): + port_objs_filtered_by_device_id.append(vmi_obj) + founded_device_ids.add(device_ref['uuid']) + # without network-id filters + elif device_ref['uuid'] in filters.get('device_id'): port_objs_filtered_by_device_id.append(vmi_obj) founded_device_ids.add(device_ref['uuid']) @@ -4068,11 +4071,14 @@ def port_list(self, context=None, filters=None): if p.parent_uuid in project_ids]) else: port_objs.extend(port_objs_filtered_by_device_id) - else: + elif filters.get('network_id'): port_objs = self._virtual_machine_interface_list( obj_uuids=filters.get('id'), - parent_id=project_ids, back_ref_id=filters.get('network_id')) + else: + port_objs = self._virtual_machine_interface_list( + obj_uuids=filters.get('id'), + parent_id=project_ids) neutron_ports = self._port_list(port_objs) From 469072930c53be97442290b6fffc87e54b99f860 Mon Sep 17 00:00:00 2001 From: Nischal Sheth Date: Fri, 28 Apr 2017 16:43:22 -0700 Subject: [PATCH 20/68] Make BgpPeer buffer size configurable Following changes are implemented: - Use a vector instead of an array for BgpPeer buffer - Make the vector capacity configurable via an env variable - Add unit tests to exercise the new code Change-Id: I6490dad8797b23dfb2d65a4681c1e1f663e0dd85 Closes-Bug: 1687442 --- src/bgp/bgp_peer.cc | 43 ++++++--- src/bgp/bgp_peer.h | 10 ++- src/bgp/test/bgp_peer_test.cc | 160 +++++++++++++++++++++++++++------- 3 files changed, 167 insertions(+), 46 deletions(-) diff --git a/src/bgp/bgp_peer.cc b/src/bgp/bgp_peer.cc index 2b6dc71c1a9..a09696f57e0 100644 --- a/src/bgp/bgp_peer.cc +++ b/src/bgp/bgp_peer.cc @@ -398,7 +398,7 @@ BgpPeer::BgpPeer(BgpServer *server, RoutingInstance *instance, trigger_(boost::bind(&BgpPeer::ResumeClose, this), TaskScheduler::GetInstance()->GetTaskId("bgp::StateMachine"), GetTaskInstance()), - buffer_len_(0), + buffer_capacity_(GetBufferCapacity()), session_(NULL), keepalive_timer_(TimerManager::CreateTimer(*server->ioservice(), "BGP keepalive timer", @@ -429,6 +429,7 @@ BgpPeer::BgpPeer(BgpServer *server, RoutingInstance *instance, total_flap_count_(0), last_flap_(0), inuse_authkey_type_(AuthenticationData::NIL) { + buffer_.reserve(buffer_capacity_); close_manager_.reset( BgpObjectFactory::Create(peer_close_.get())); ostringstream oss1; @@ -480,7 +481,8 @@ BgpPeer::BgpPeer(BgpServer *server, RoutingInstance *instance, total_path_count_ = 0; primary_path_count_ = 0; - if (resolve_paths_) { + // Check rtinstance_ to accommodate unit tests. + if (resolve_paths_ && rtinstance_) { rtinstance_->GetTable(Address::INET)->LocatePathResolver(); rtinstance_->GetTable(Address::INET6)->LocatePathResolver(); } @@ -537,6 +539,26 @@ void BgpPeer::Initialize() { state_machine_->Initialize(); } +size_t BgpPeer::GetBufferCapacity() const { + // For testing only - configure through environment variable. + char *buffer_capacity_str = getenv("BGP_PEER_BUFFER_SIZE"); + if (buffer_capacity_str) { + size_t env_buffer_capacity = strtoul(buffer_capacity_str, NULL, 0); + if (env_buffer_capacity < kMinBufferCapacity) + env_buffer_capacity = kMinBufferCapacity; + if (env_buffer_capacity > kMaxBufferCapacity) + env_buffer_capacity = kMaxBufferCapacity; + return env_buffer_capacity; + } + + // Return internal default based on peer router-type. + if (router_type_ == "bgpaas-client") { + return kMinBufferCapacity; + } else { + return kMaxBufferCapacity; + } +} + void BgpPeer::NotifyEstablished(bool established) { if (established) { if (router_type_ == "bgpaas-client") { @@ -1229,36 +1251,35 @@ static bool SkipUpdateSend() { // // Accumulate the message in the update buffer. // Flush the existing buffer if the message can't fit. -// Note that FlushUpdateUnlocked resets buffer_len_ to 0. +// Note that FlushUpdateUnlocked clears the buffer. // bool BgpPeer::SendUpdate(const uint8_t *msg, size_t msgsize, const string *msg_str) { tbb::spin_mutex::scoped_lock lock(spin_mutex_); bool send_ready = true; - if (buffer_len_ + msgsize > kBufferSize) { + if (buffer_.size() + msgsize > buffer_capacity_) { send_ready = FlushUpdateUnlocked(); - assert(buffer_len_ == 0); + assert(buffer_.empty()); } - copy(msg, msg + msgsize, buffer_ + buffer_len_); - buffer_len_ += msgsize; + buffer_.insert(buffer_.end(), msg, msg + msgsize); inc_tx_update(); return send_ready; } bool BgpPeer::FlushUpdateUnlocked() { // Bail if the update buffer is empty. - if (buffer_len_ == 0) + if (buffer_.empty()) return true; // Bail if there's no session for the peer anymore. if (!session_) { - buffer_len_ = 0; + buffer_.clear(); return true; } if (!SkipUpdateSend()) { - send_ready_ = session_->Send(buffer_, buffer_len_, NULL); - buffer_len_ = 0; + send_ready_ = session_->Send(buffer_.data(), buffer_.size(), NULL); + buffer_.clear(); if (send_ready_) { StartKeepaliveTimerUnlocked(); } else { diff --git a/src/bgp/bgp_peer.h b/src/bgp/bgp_peer.h index d460dcbe8ec..efd79def791 100644 --- a/src/bgp/bgp_peer.h +++ b/src/bgp/bgp_peer.h @@ -79,7 +79,8 @@ class BgpPeer : public IPeer { static const int kMaxEndOfRibSendTimeUsecs = 60000000; // 60 Seconds static const int kEndOfRibSendRetryTimeMsecs = 2000; // 2 Seconds static const int kRouteTargetEndOfRibTimeSecs = 30; // Seconds - static const size_t kBufferSize = 32768; + static const size_t kMinBufferCapacity = 4096; + static const size_t kMaxBufferCapacity = 32768; typedef std::set AddressFamilyList; typedef AuthenticationData::KeyType KeyType; @@ -173,7 +174,7 @@ class BgpPeer : public IPeer { uint16_t hold_time() const { return hold_time_; } as_t local_as() const { return local_as_; } as_t peer_as() const { return peer_as_; } - size_t buffer_len() const { return buffer_len_; } + size_t buffer_size() const { return buffer_.size(); } // The BGP Identifier in host byte order. virtual uint32_t local_bgp_identifier() const; @@ -355,6 +356,7 @@ class BgpPeer : public IPeer { typedef std::map FamilyToCapabilityMap; typedef std::vector FamilyAttributesList; + size_t GetBufferCapacity() const; bool FlushUpdateUnlocked(); void KeepaliveTimerErrorHandler(std::string error_name, std::string error_message); @@ -441,8 +443,8 @@ class BgpPeer : public IPeer { // and the io thread should need to lock it once every few seconds at // most. Hence we choose a spin_mutex. tbb::spin_mutex spin_mutex_; - uint8_t buffer_[kBufferSize]; - size_t buffer_len_; + size_t buffer_capacity_; + std::vector buffer_; BgpSession *session_; Timer *keepalive_timer_; Timer *eor_receive_timer_[Address::NUM_FAMILIES]; diff --git a/src/bgp/test/bgp_peer_test.cc b/src/bgp/test/bgp_peer_test.cc index 5b262365387..cfe79390c1f 100644 --- a/src/bgp/test/bgp_peer_test.cc +++ b/src/bgp/test/bgp_peer_test.cc @@ -81,6 +81,7 @@ class BgpPeerMock : public BgpPeer { class BgpPeerTest : public ::testing::Test { protected: BgpPeerTest() : server_(&evm_), peer_(NULL) { + unsetenv("BGP_PEER_BUFFER_SIZE"); } void SetUp() { @@ -88,7 +89,7 @@ class BgpPeerTest : public ::testing::Test { session_.reset(new BgpSessionMock(server_.session_manager())); peer_.reset(new BgpPeerMock(&server_, &config_)); peer_->set_session(session_.get()); - TASK_UTIL_EXPECT_EQ(0, peer_->buffer_len()); + TASK_UTIL_EXPECT_EQ(0, peer_->buffer_size()); } void TearDown() { @@ -99,6 +100,14 @@ class BgpPeerTest : public ::testing::Test { peer_.reset(); } + void Reset(const BgpNeighborConfig *config) { + ConcurrencyScope scope("bgp::Config"); + peer_->clear_session(); + peer_->PostCloseRelease(); + peer_.reset(new BgpPeerMock(&server_, config)); + peer_->set_session(session_.get()); + } + RibExportPolicy BuildRibExportPolicy() { ConcurrencyScope scope("bgp::Config"); return peer_->BuildRibExportPolicy(Address::INETVPN); @@ -109,6 +118,8 @@ class BgpPeerTest : public ::testing::Test { peer_->ConfigUpdate(config); } + size_t BufferCapacity() { return peer_->buffer_capacity_; } + BgpNeighborConfig config_; BgpServer server_; EventManager evm_; @@ -170,12 +181,99 @@ TEST_F(BgpPeerTest, PrivateAsAction) { EXPECT_FALSE(policy.remove_private.replace); } +// +// Default buffer capacity for regular peer. +// +TEST_F(BgpPeerTest, BufferCapacity1) { + BgpNeighborConfig config; + config.set_router_type("control-node"); + Reset(&config); + size_t capacity = BgpPeer::kMaxBufferCapacity; + TASK_UTIL_EXPECT_EQ(capacity, BufferCapacity()); +} + +// +// Default buffer capacity for bgpaas peer. +// +TEST_F(BgpPeerTest, BufferCapacity2) { + BgpNeighborConfig config; + config.set_router_type("bgpaas-client"); + Reset(&config); + size_t capacity = BgpPeer::kMinBufferCapacity; + TASK_UTIL_EXPECT_EQ(capacity, BufferCapacity()); +} + +// +// Buffer capacity set via env variable for regular peer. +// +TEST_F(BgpPeerTest, BufferCapacity3) { + char value[8]; + snprintf(value, sizeof(value), "%zu", BgpPeer::kMaxBufferCapacity / 2); + setenv("BGP_PEER_BUFFER_SIZE", value, true); + BgpNeighborConfig config; + config.set_router_type("control-node"); + Reset(&config); + size_t capacity = BgpPeer::kMaxBufferCapacity / 2; + TASK_UTIL_EXPECT_EQ(capacity, BufferCapacity()); +} + +// +// Buffer capacity set via env variable for for bgpaas peer. +// +TEST_F(BgpPeerTest, BufferCapacity4) { + char value[8]; + snprintf(value, sizeof(value), "%zu", BgpPeer::kMaxBufferCapacity / 2); + setenv("BGP_PEER_BUFFER_SIZE", value, true); + BgpNeighborConfig config; + config.set_router_type("bgpaas-client"); + Reset(&config); + size_t capacity = BgpPeer::kMaxBufferCapacity / 2; + TASK_UTIL_EXPECT_EQ(capacity, BufferCapacity()); +} + +// +// Buffer capacity set via env variable is too small. +// +TEST_F(BgpPeerTest, BufferCapacity5) { + char value[8]; + snprintf(value, sizeof(value), "%zu", BgpPeer::kMinBufferCapacity - 1); + setenv("BGP_PEER_BUFFER_SIZE", value, true); + BgpNeighborConfig config; + Reset(&config); + size_t capacity = BgpPeer::kMinBufferCapacity; + TASK_UTIL_EXPECT_EQ(capacity, BufferCapacity()); +} + +// +// Buffer capacity set via env variable is too big. +// +TEST_F(BgpPeerTest, BufferCapacity6) { + char value[8]; + snprintf(value, sizeof(value), "%zu", BgpPeer::kMaxBufferCapacity + 1); + setenv("BGP_PEER_BUFFER_SIZE", value, true); + BgpNeighborConfig config; + Reset(&config); + size_t capacity = BgpPeer::kMaxBufferCapacity; + TASK_UTIL_EXPECT_EQ(capacity, BufferCapacity()); +} + +// +// Buffer capacity set via env variable is invalid. +// +TEST_F(BgpPeerTest, BufferCapacity7) { + setenv("BGP_PEER_BUFFER_SIZE", "xyz", true); + BgpNeighborConfig config; + Reset(&config); + size_t capacity = BgpPeer::kMinBufferCapacity; + TASK_UTIL_EXPECT_EQ(capacity, BufferCapacity()); +} + // // FlushUpdate with an empty buffer does not cause any problems. // TEST_F(BgpPeerTest, MessageBuffer1) { peer_->FlushUpdate(); - TASK_UTIL_EXPECT_EQ(0, peer_->buffer_len()); + TASK_UTIL_EXPECT_EQ(0, peer_->buffer_size()); TASK_UTIL_EXPECT_EQ(0, peer_->get_tx_update()); TASK_UTIL_EXPECT_EQ(0, session_->message_count()); } @@ -188,12 +286,12 @@ TEST_F(BgpPeerTest, MessageBuffer2) { uint8_t msg[msgsize]; peer_->SendUpdate(msg, msgsize); - TASK_UTIL_EXPECT_EQ(msgsize, peer_->buffer_len()); + TASK_UTIL_EXPECT_EQ(msgsize, peer_->buffer_size()); TASK_UTIL_EXPECT_EQ(1, peer_->get_tx_update()); TASK_UTIL_EXPECT_EQ(0, session_->message_count()); peer_->FlushUpdate(); - TASK_UTIL_EXPECT_EQ(0, peer_->buffer_len()); + TASK_UTIL_EXPECT_EQ(0, peer_->buffer_size()); TASK_UTIL_EXPECT_EQ(1, peer_->get_tx_update()); TASK_UTIL_EXPECT_EQ(1, session_->message_count()); } @@ -207,22 +305,22 @@ TEST_F(BgpPeerTest, MessageBuffer3) { uint8_t msg[msgsize]; peer_->SendUpdate(msg, msgsize); - TASK_UTIL_EXPECT_EQ(msgsize, peer_->buffer_len()); + TASK_UTIL_EXPECT_EQ(msgsize, peer_->buffer_size()); TASK_UTIL_EXPECT_EQ(1, peer_->get_tx_update()); TASK_UTIL_EXPECT_EQ(0, session_->message_count()); peer_->SendUpdate(msg, msgsize); - TASK_UTIL_EXPECT_EQ(2 * msgsize, peer_->buffer_len()); + TASK_UTIL_EXPECT_EQ(2 * msgsize, peer_->buffer_size()); TASK_UTIL_EXPECT_EQ(2, peer_->get_tx_update()); TASK_UTIL_EXPECT_EQ(0, session_->message_count()); peer_->SendUpdate(msg, msgsize); - TASK_UTIL_EXPECT_EQ(3 * msgsize, peer_->buffer_len()); + TASK_UTIL_EXPECT_EQ(3 * msgsize, peer_->buffer_size()); TASK_UTIL_EXPECT_EQ(3, peer_->get_tx_update()); TASK_UTIL_EXPECT_EQ(0, session_->message_count()); peer_->FlushUpdate(); - TASK_UTIL_EXPECT_EQ(0, peer_->buffer_len()); + TASK_UTIL_EXPECT_EQ(0, peer_->buffer_size()); TASK_UTIL_EXPECT_EQ(3, peer_->get_tx_update()); TASK_UTIL_EXPECT_EQ(1, session_->message_count()); } @@ -232,22 +330,22 @@ TEST_F(BgpPeerTest, MessageBuffer3) { // Buffer has 1 byte left after all SendUpdates. // TEST_F(BgpPeerTest, MessageBuffer4) { - static const size_t msgsize = BgpPeer::kBufferSize - 128; - static const size_t bufsize = BgpPeer::kBufferSize; + static const size_t msgsize = BufferCapacity() - 128; + static const size_t bufcap = BufferCapacity(); uint8_t msg[msgsize]; peer_->SendUpdate(msg, msgsize); - TASK_UTIL_EXPECT_EQ(msgsize, peer_->buffer_len()); + TASK_UTIL_EXPECT_EQ(msgsize, peer_->buffer_size()); TASK_UTIL_EXPECT_EQ(1, peer_->get_tx_update()); TASK_UTIL_EXPECT_EQ(0, session_->message_count()); peer_->SendUpdate(msg, 127); - TASK_UTIL_EXPECT_EQ(bufsize - 1, peer_->buffer_len()); + TASK_UTIL_EXPECT_EQ(bufcap - 1, peer_->buffer_size()); TASK_UTIL_EXPECT_EQ(2, peer_->get_tx_update()); TASK_UTIL_EXPECT_EQ(0, session_->message_count()); peer_->FlushUpdate(); - TASK_UTIL_EXPECT_EQ(0, peer_->buffer_len()); + TASK_UTIL_EXPECT_EQ(0, peer_->buffer_size()); TASK_UTIL_EXPECT_EQ(2, peer_->get_tx_update()); TASK_UTIL_EXPECT_EQ(1, session_->message_count()); } @@ -257,22 +355,22 @@ TEST_F(BgpPeerTest, MessageBuffer4) { // Buffer is exactly full after all SendUpdates. // TEST_F(BgpPeerTest, MessageBuffer5) { - static const size_t msgsize = BgpPeer::kBufferSize - 128; - static const size_t bufsize = BgpPeer::kBufferSize; + static const size_t msgsize = BufferCapacity() - 128; + static const size_t bufcap = BufferCapacity(); uint8_t msg[msgsize]; peer_->SendUpdate(msg, msgsize); - TASK_UTIL_EXPECT_EQ(msgsize, peer_->buffer_len()); + TASK_UTIL_EXPECT_EQ(msgsize, peer_->buffer_size()); TASK_UTIL_EXPECT_EQ(1, peer_->get_tx_update()); TASK_UTIL_EXPECT_EQ(0, session_->message_count()); peer_->SendUpdate(msg, 128); - TASK_UTIL_EXPECT_EQ(bufsize, peer_->buffer_len()); + TASK_UTIL_EXPECT_EQ(bufcap, peer_->buffer_size()); TASK_UTIL_EXPECT_EQ(2, peer_->get_tx_update()); TASK_UTIL_EXPECT_EQ(0, session_->message_count()); peer_->FlushUpdate(); - TASK_UTIL_EXPECT_EQ(0, peer_->buffer_len()); + TASK_UTIL_EXPECT_EQ(0, peer_->buffer_size()); TASK_UTIL_EXPECT_EQ(2, peer_->get_tx_update()); TASK_UTIL_EXPECT_EQ(1, session_->message_count()); } @@ -282,21 +380,21 @@ TEST_F(BgpPeerTest, MessageBuffer5) { // Another FlushUpdate flushes the last update. // TEST_F(BgpPeerTest, MessageBuffer6) { - static const size_t msgsize = BgpPeer::kBufferSize - 128; + static const size_t msgsize = BufferCapacity() - 128; uint8_t msg[msgsize]; peer_->SendUpdate(msg, msgsize); - TASK_UTIL_EXPECT_EQ(msgsize, peer_->buffer_len()); + TASK_UTIL_EXPECT_EQ(msgsize, peer_->buffer_size()); TASK_UTIL_EXPECT_EQ(1, peer_->get_tx_update()); TASK_UTIL_EXPECT_EQ(0, session_->message_count()); peer_->SendUpdate(msg, 129); - TASK_UTIL_EXPECT_EQ(129, peer_->buffer_len()); + TASK_UTIL_EXPECT_EQ(129, peer_->buffer_size()); TASK_UTIL_EXPECT_EQ(2, peer_->get_tx_update()); TASK_UTIL_EXPECT_EQ(1, session_->message_count()); peer_->FlushUpdate(); - TASK_UTIL_EXPECT_EQ(0, peer_->buffer_len()); + TASK_UTIL_EXPECT_EQ(0, peer_->buffer_size()); TASK_UTIL_EXPECT_EQ(2, peer_->get_tx_update()); TASK_UTIL_EXPECT_EQ(2, session_->message_count()); } @@ -307,23 +405,23 @@ TEST_F(BgpPeerTest, MessageBuffer6) { // Session gets cleared before the call to FlushUpdate. // TEST_F(BgpPeerTest, MessageBuffer7) { - static const size_t msgsize = BgpPeer::kBufferSize - 128; - static const size_t bufsize = BgpPeer::kBufferSize; + static const size_t msgsize = BufferCapacity() - 128; + static const size_t bufcap = BufferCapacity(); uint8_t msg[msgsize]; peer_->SendUpdate(msg, msgsize); - TASK_UTIL_EXPECT_EQ(msgsize, peer_->buffer_len()); + TASK_UTIL_EXPECT_EQ(msgsize, peer_->buffer_size()); TASK_UTIL_EXPECT_EQ(1, peer_->get_tx_update()); TASK_UTIL_EXPECT_EQ(0, session_->message_count()); peer_->SendUpdate(msg, 128); - TASK_UTIL_EXPECT_EQ(bufsize, peer_->buffer_len()); + TASK_UTIL_EXPECT_EQ(bufcap, peer_->buffer_size()); TASK_UTIL_EXPECT_EQ(2, peer_->get_tx_update()); TASK_UTIL_EXPECT_EQ(0, session_->message_count()); peer_->clear_session(); peer_->FlushUpdate(); - TASK_UTIL_EXPECT_EQ(0, peer_->buffer_len()); + TASK_UTIL_EXPECT_EQ(0, peer_->buffer_size()); TASK_UTIL_EXPECT_EQ(2, peer_->get_tx_update()); TASK_UTIL_EXPECT_EQ(0, session_->message_count()); } @@ -334,22 +432,22 @@ TEST_F(BgpPeerTest, MessageBuffer7) { // Another FlushUpdate flushes the last update. // TEST_F(BgpPeerTest, MessageBuffer8) { - static const size_t msgsize = BgpPeer::kBufferSize - 128; + static const size_t msgsize = BufferCapacity() - 128; uint8_t msg[msgsize]; peer_->SendUpdate(msg, msgsize); - TASK_UTIL_EXPECT_EQ(msgsize, peer_->buffer_len()); + TASK_UTIL_EXPECT_EQ(msgsize, peer_->buffer_size()); TASK_UTIL_EXPECT_EQ(1, peer_->get_tx_update()); TASK_UTIL_EXPECT_EQ(0, session_->message_count()); peer_->clear_session(); peer_->SendUpdate(msg, 129); - TASK_UTIL_EXPECT_EQ(129, peer_->buffer_len()); + TASK_UTIL_EXPECT_EQ(129, peer_->buffer_size()); TASK_UTIL_EXPECT_EQ(2, peer_->get_tx_update()); TASK_UTIL_EXPECT_EQ(0, session_->message_count()); peer_->FlushUpdate(); - TASK_UTIL_EXPECT_EQ(0, peer_->buffer_len()); + TASK_UTIL_EXPECT_EQ(0, peer_->buffer_size()); TASK_UTIL_EXPECT_EQ(2, peer_->get_tx_update()); TASK_UTIL_EXPECT_EQ(0, session_->message_count()); } From d7c97d3b418ebfa4c4a8639c27cae1a8679b6c7c Mon Sep 17 00:00:00 2001 From: Santosh Gupta Date: Wed, 29 Mar 2017 14:04:45 -0700 Subject: [PATCH 21/68] Making keystone auth params optional Made keystone auth params optional so that it works for keystone and no-auth cases. Change-Id: I83e255d057621d217d305c545285032a85bf2f59 Closes-Bug: #1677070 (cherry picked from commit cffa0bc550921d6d60357d3aa4ec3b514019d86b) --- src/config/utils/provision_alarm.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/config/utils/provision_alarm.py b/src/config/utils/provision_alarm.py index a85db80fb20..34090b6422e 100755 --- a/src/config/utils/provision_alarm.py +++ b/src/config/utils/provision_alarm.py @@ -75,16 +75,13 @@ def _parse_args(self, args_str): help="Port of api server") parser.add_argument( "--admin_user", - help="Name of keystone admin user", - required=True) + help="Name of keystone admin user") parser.add_argument( "--admin_password", - help="Password of keystone admin user", - required=True) + help="Password of keystone admin user") parser.add_argument( "--admin_tenant_name", - help="Tenant name for keystone admin user", - required=True) + help="Tenant name for keystone admin user") parser.add_argument( "--api_server_use_ssl", default=False, From 36ee7456719e5772541f68f446998b1dbf8fabc2 Mon Sep 17 00:00:00 2001 From: Nischal Sheth Date: Tue, 9 May 2017 16:39:22 -0700 Subject: [PATCH 22/68] Add bgp configured and operational peer lists to control node UVE These lists do not include BGPaaS peers. Change-Id: Ie1a359bb5c1b84e6cb93cbbb6e7d0c05b44fd694 Closes-Bug: 1638065 --- src/bgp/bgp_server.cc | 32 +++++++++++++++++++ src/control-node/sandesh/control_node.sandesh | 2 ++ 2 files changed, 34 insertions(+) diff --git a/src/bgp/bgp_server.cc b/src/bgp/bgp_server.cc index 8b08a9721ac..1c6721c7707 100644 --- a/src/bgp/bgp_server.cc +++ b/src/bgp/bgp_server.cc @@ -4,6 +4,7 @@ #include "bgp/bgp_server.h" +#include #include #include "base/connection_info.h" @@ -36,6 +37,7 @@ using std::boolalpha; using std::make_pair; using std::map; using std::noboolalpha; +using std::sort; using std::string; // The ConfigUpdater serves as glue between the BgpConfigManager and the @@ -932,5 +934,35 @@ bool BgpServer::CollectStats(BgpRouterState *state, bool first) const { change = true; } + vector bgp_config_peer_list; + BOOST_FOREACH(BgpConfigManager::NeighborMap::value_type value, + config_mgr_->NeighborMapItems(BgpConfigManager::kMasterInstance)) { + const BgpNeighborConfig *neighbor = value.second; + string name(BgpConfigManager::kMasterInstance); + name += ":"; + name += localname(); + name += ":"; + name += neighbor->name(); + bgp_config_peer_list.push_back(name); + } + sort(bgp_config_peer_list.begin(), bgp_config_peer_list.end()); + if (first || bgp_config_peer_list != state->get_bgp_config_peer_list()) { + state->set_bgp_config_peer_list(bgp_config_peer_list); + change = true; + } + + vector bgp_oper_peer_list; + const RoutingInstance *rtinstance = inst_mgr_->GetDefaultRoutingInstance(); + const PeerManager *peer_manager = rtinstance->peer_manager(); + for (const BgpPeer *peer = peer_manager->NextPeer(BgpPeerKey()); + peer != NULL; peer = peer_manager->NextPeer(peer->peer_key())) { + bgp_oper_peer_list.push_back(peer->ToUVEKey()); + } + sort(bgp_oper_peer_list.begin(), bgp_oper_peer_list.end()); + if (first || bgp_oper_peer_list != state->get_bgp_oper_peer_list()) { + state->set_bgp_oper_peer_list(bgp_oper_peer_list); + change = true; + } + return change; } diff --git a/src/control-node/sandesh/control_node.sandesh b/src/control-node/sandesh/control_node.sandesh index b49b3e51425..828869e169a 100644 --- a/src/control-node/sandesh/control_node.sandesh +++ b/src/control-node/sandesh/control_node.sandesh @@ -39,6 +39,8 @@ struct BgpRouterState { 14: optional string collector_ip; 15: optional list bgp_router_ip_list; 16: optional list core_files_list; + 36: optional list bgp_config_peer_list; + 37: optional list bgp_oper_peer_list; 17: optional ifmap_server_show.IFMapPeerServerInfoUI ifmap_info; 18: optional ifmap_server_show.IFMapServerInfoUI ifmap_server_info; } From d84517d8c37580be217dc17354f81e3714c5380a Mon Sep 17 00:00:00 2001 From: Sahil Date: Tue, 9 May 2017 17:18:09 -0700 Subject: [PATCH 23/68] Fix to catch exception when IP addr already in use Change-Id: I490a0919b8ffa8b07c91ee324a10569f7901109c Closes-Bug: 1683548 --- .../api-server/tests/test_crud_basic.py | 4 +-- src/config/api-server/tests/test_ip_alloc.py | 28 +++++++++---------- src/config/api-server/vnc_cfg_types.py | 6 ++-- .../vnc_openstack/tests/test_basic.py | 2 +- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/config/api-server/tests/test_crud_basic.py b/src/config/api-server/tests/test_crud_basic.py index b303e4c7b09..b14bd6b7108 100644 --- a/src/config/api-server/tests/test_crud_basic.py +++ b/src/config/api-server/tests/test_crud_basic.py @@ -1230,7 +1230,7 @@ def test_floatingip_as_instanceip(self): ip_allocated = fip_fixt.getObj().floating_ip_address logger.info("Creating auto-alloc instance-ip, expecting an error") - with ExpectedException(RefsExistError) as e: + with ExpectedException(BadRequest) as e: iip_fixt = self.useFixture( InstanceIpTestFixtureGen( self._vnc_lib, 'iip1', auto_prop_val=False, @@ -1266,7 +1266,7 @@ def test_aliasip_as_instanceip(self): ip_allocated = aip_fixt.getObj().alias_ip_address logger.info("Creating auto-alloc instance-ip, expecting an error") - with ExpectedException(RefsExistError) as e: + with ExpectedException(BadRequest) as e: iip_fixt = self.useFixture( InstanceIpTestFixtureGen( self._vnc_lib, 'iip1', auto_prop_val=False, diff --git a/src/config/api-server/tests/test_ip_alloc.py b/src/config/api-server/tests/test_ip_alloc.py index 010f7dc8902..11e15d5a0e2 100644 --- a/src/config/api-server/tests/test_ip_alloc.py +++ b/src/config/api-server/tests/test_ip_alloc.py @@ -746,7 +746,7 @@ def test_flat_subnet_ipam_flat_subnet_network(self): ipv4_obj2.uuid = ipv4_obj2.name ipv4_obj2.set_virtual_network(net_obj) logger.debug('Created Instance IPv4 object 2 %s', ipv4_obj2.uuid) - with ExpectedException(cfgm_common.exceptions.RefsExistError, + with ExpectedException(cfgm_common.exceptions.BadRequest, 'Ip address already in use') as e: ipv4_id2 = self._vnc_lib.instance_ip_create(ipv4_obj2) @@ -884,7 +884,7 @@ def test_hybrid_subnet_ipam_flat_subnet_network(self): #try allocating specific ip, which has been assigned already ipv4_obj5.set_instance_ip_address('12.1.1.4') - with ExpectedException(cfgm_common.exceptions.RefsExistError, + with ExpectedException(cfgm_common.exceptions.BadRequest, 'Ip address already in use') as e: ipv4_id5 = self._vnc_lib.instance_ip_create(ipv4_obj5) @@ -1032,7 +1032,7 @@ def test_hybrid_subnet_ipam_user_subnet_network(self): #try allocating specific ip, which has been assigned already ipv4_obj5.set_instance_ip_address('14.1.1.8') - with ExpectedException(cfgm_common.exceptions.RefsExistError, + with ExpectedException(cfgm_common.exceptions.BadRequest, 'Ip address already in use') as e: ipv4_id5 = self._vnc_lib.instance_ip_create(ipv4_obj5) @@ -1147,7 +1147,7 @@ def test_hybrid_subnet_ipam_ask_ip(self): # of ip in use ipv4_obj2.set_virtual_network(net_obj) ipv4_obj2.set_instance_ip_address('13.1.1.8') - with ExpectedException(cfgm_common.exceptions.RefsExistError, + with ExpectedException(cfgm_common.exceptions.BadRequest, 'Ip address already in use') as e: ipv4_id2 = self._vnc_lib.instance_ip_create(ipv4_obj2) @@ -1184,7 +1184,7 @@ def test_hybrid_subnet_ipam_ask_ip(self): # of ip in use. ipv4_obj4.set_virtual_network(net_obj) ipv4_obj4.set_instance_ip_address('11.1.1.8') - with ExpectedException(cfgm_common.exceptions.RefsExistError, + with ExpectedException(cfgm_common.exceptions.BadRequest, 'Ip address already in use') as e: ipv4_id4 = self._vnc_lib.instance_ip_create(ipv4_obj4) @@ -2407,13 +2407,13 @@ def test_ip_alloc_clash(self): iip2_obj = InstanceIp('clashing-iip-%s' %(self.id()), instance_ip_address=iip_obj.instance_ip_address) iip2_obj.add_virtual_network(vn_obj) - with ExpectedException(cfgm_common.exceptions.RefsExistError, + with ExpectedException(cfgm_common.exceptions.BadRequest, 'Ip address already in use') as e: self._vnc_lib.instance_ip_create(iip2_obj) # allocate instance-ip clashing with existing floating-ip iip2_obj.set_instance_ip_address(fip_obj.floating_ip_address) - with ExpectedException(cfgm_common.exceptions.RefsExistError, + with ExpectedException(cfgm_common.exceptions.BadRequest, 'Ip address already in use') as e: self._vnc_lib.instance_ip_create(iip2_obj) @@ -2421,7 +2421,7 @@ def test_ip_alloc_clash(self): fip2_obj = FloatingIp('clashing-fip-%s' %(self.id()), fip_pool_obj, floating_ip_address=fip_obj.floating_ip_address) fip2_obj.add_project(proj_obj) - with ExpectedException(cfgm_common.exceptions.RefsExistError, + with ExpectedException(cfgm_common.exceptions.BadRequest, 'Ip address already in use') as e: self._vnc_lib.floating_ip_create(fip2_obj) @@ -2429,37 +2429,37 @@ def test_ip_alloc_clash(self): aip2_obj = AliasIp('clashing-aip-%s' %(self.id()), aip_pool_obj, alias_ip_address=aip_obj.alias_ip_address) aip2_obj.add_project(proj_obj) - with ExpectedException(cfgm_common.exceptions.RefsExistError, + with ExpectedException(cfgm_common.exceptions.BadRequest, 'Ip address already in use') as e: self._vnc_lib.alias_ip_create(aip2_obj) # allocate floating-ip clashing with existing instance-ip fip2_obj.set_floating_ip_address(iip_obj.instance_ip_address) - with ExpectedException(cfgm_common.exceptions.RefsExistError, + with ExpectedException(cfgm_common.exceptions.BadRequest, 'Ip address already in use') as e: self._vnc_lib.floating_ip_create(fip2_obj) # allocate alias-ip clashing with existing instance-ip aip2_obj.set_alias_ip_address(iip_obj.instance_ip_address) - with ExpectedException(cfgm_common.exceptions.RefsExistError, + with ExpectedException(cfgm_common.exceptions.BadRequest, 'Ip address already in use') as e: self._vnc_lib.alias_ip_create(aip2_obj) # allocate alias-ip clashing with existing floating-ip aip2_obj.set_alias_ip_address(fip_obj.floating_ip_address) - with ExpectedException(cfgm_common.exceptions.RefsExistError, + with ExpectedException(cfgm_common.exceptions.BadRequest, 'Ip address already in use') as e: self._vnc_lib.alias_ip_create(aip2_obj) # allocate floating-ip with gateway ip and verify failure fip2_obj.set_floating_ip_address('11.1.1.254') - with ExpectedException(cfgm_common.exceptions.RefsExistError, + with ExpectedException(cfgm_common.exceptions.BadRequest, 'Ip address already in use') as e: self._vnc_lib.floating_ip_create(fip2_obj) # allocate alias-ip with gateway ip and verify failure aip2_obj.set_alias_ip_address('11.1.1.254') - with ExpectedException(cfgm_common.exceptions.RefsExistError, + with ExpectedException(cfgm_common.exceptions.BadRequest, 'Ip address already in use') as e: self._vnc_lib.alias_ip_create(aip2_obj) diff --git a/src/config/api-server/vnc_cfg_types.py b/src/config/api-server/vnc_cfg_types.py index 571f59e0031..949525624e2 100644 --- a/src/config/api-server/vnc_cfg_types.py +++ b/src/config/api-server/vnc_cfg_types.py @@ -235,7 +235,7 @@ def pre_dbe_create(cls, tenant_name, obj_dict, db_conn): vn_fq_name = obj_dict['fq_name'][:-2] req_ip = obj_dict.get("floating_ip_address") if req_ip and cls.addr_mgmt.is_ip_allocated(req_ip, vn_fq_name): - return (False, (409, 'Ip address already in use')) + return (False, (400, 'Ip address already in use')) try: fip_addr = cls.addr_mgmt.ip_alloc_req(vn_fq_name, asked_ip_addr=req_ip, @@ -305,7 +305,7 @@ def pre_dbe_create(cls, tenant_name, obj_dict, db_conn): vn_fq_name = obj_dict['fq_name'][:-2] req_ip = obj_dict.get("alias_ip_address") if req_ip and cls.addr_mgmt.is_ip_allocated(req_ip, vn_fq_name): - return (False, (409, 'Ip address already in use')) + return (False, (400, 'Ip address already in use')) try: aip_addr = cls.addr_mgmt.ip_alloc_req(vn_fq_name, asked_ip_addr=req_ip, @@ -435,7 +435,7 @@ def pre_dbe_create(cls, tenant_name, obj_dict, db_conn): if req_ip and cls.addr_mgmt.is_ip_allocated(req_ip, vn_fq_name, vn_uuid=vn_id): if not cls.addr_mgmt.is_gateway_ip(vn_dict, req_ip): - return (False, (409, 'Ip address already in use')) + return (False, (400, 'Ip address already in use')) elif cls._vmi_has_vm_ref(db_conn, obj_dict): return (False, (400, 'Gateway IP cannot be used by VM port')) diff --git a/src/config/vnc_openstack/vnc_openstack/tests/test_basic.py b/src/config/vnc_openstack/vnc_openstack/tests/test_basic.py index c39f2f9d3df..930a57931c7 100644 --- a/src/config/vnc_openstack/vnc_openstack/tests/test_basic.py +++ b/src/config/vnc_openstack/vnc_openstack/tests/test_basic.py @@ -557,7 +557,7 @@ def test_fixed_ip_conflicts_with_floating_ip(self): self.assertTrue(False, 'Create with fixed-ip conflicting with floating-ip passed') except webtest.app.AppError as e: - self.assertIsNot(re.search('Conflict', str(e)), None) + self.assertIsNot(re.search('BadRequest', str(e)), None) self.assertIsNot(re.search('Ip address already in use', str(e)), None) From 48d4323104608479e9f5d791ac7b629f1fed7d1c Mon Sep 17 00:00:00 2001 From: Jean-Philippe Braun Date: Tue, 18 Apr 2017 10:50:07 +0200 Subject: [PATCH 24/68] vrouter-port-control: remove unused imports and PEP8 Change-Id: I31f4e35d5588a746bd896ee06314f4680454eaf9 Closes-Bug: #1683672 (cherry picked from commit 9c182b4e57f78e8be48a7c890fa5ef019db2d223) --- src/vnsw/agent/port_ipc/vrouter-port-control | 138 ++++++++++--------- 1 file changed, 70 insertions(+), 68 deletions(-) diff --git a/src/vnsw/agent/port_ipc/vrouter-port-control b/src/vnsw/agent/port_ipc/vrouter-port-control index 75de5c7a5ff..d040afed511 100755 --- a/src/vnsw/agent/port_ipc/vrouter-port-control +++ b/src/vnsw/agent/port_ipc/vrouter-port-control @@ -1,12 +1,13 @@ #!/usr/bin/python # -#Copyright (c) 2015 Juniper Networks, Inc. All rights reserved. +# Copyright (c) 2015 Juniper Networks, Inc. All rights reserved. # import sys import argparse import ConfigParser import requests +from requests.exceptions import ConnectionError import json import os import errno @@ -16,21 +17,21 @@ import time import socket sys.path.insert(1, sys.path[0]+'/../api-venv/lib/python2.7/site-packages') -from vnc_api.vnc_api import * -from requests.exceptions import ConnectionError -PORT_PATH="/var/lib/contrail/ports/" -PORT_IPC_VROUTER_AGENT_PORT=9091 -TIMEOUT_SECS=15 -DPDK_NETLINK_TCP_PORT=20914 -PORT_OPEN_TIMEOUT_SECS=120 +PORT_PATH = "/var/lib/contrail/ports/" +PORT_IPC_VROUTER_AGENT_PORT = 9091 +TIMEOUT_SECS = 15 +DPDK_NETLINK_TCP_PORT = 20914 +PORT_OPEN_TIMEOUT_SECS = 120 + class VrouterPortControl(object): - def __init__(self, args_str = None): + + def __init__(self, args_str=None): self._args = None if not args_str: args_str = ' '.join(sys.argv[1:]) self._parse_args(args_str) - #TODO:Move this to other place as it tries to create directory every time + # TODO: Move this to other place as it tries to create directory every time self.CreateDirectoryTree(PORT_PATH) port_type_value = 0 ret_code = 0 @@ -58,7 +59,7 @@ class VrouterPortControl(object): u = u[:18] + '-' + u[18:] project_id = u[:23] + '-' + u[23:] - url = base_url + "/port" + url = base_url + "/port" payload = self.GetJSonDict(port_type_value, project_id) json_dump = json.dumps(payload) if not self._args.no_persist: @@ -74,9 +75,9 @@ class VrouterPortControl(object): else: ret_code = 0 if ret_code == 0 and self._args.vif_type == "VhostUser": - #In DPDK mode we should wait until socket is created by - #vrouter module - ret_code = self.WaitForSocketFile() + # In DPDK mode we should wait until socket is created by + # vrouter module + ret_code = self.WaitForSocketFile() except ConnectionError: '''In DPDK mode return failure when we are not able to connect @@ -90,19 +91,18 @@ class VrouterPortControl(object): self.DeleteFile() url = base_url + "/port/" + self._args.uuid try: - r = requests.delete(url, data=None, headers=headers); + r = requests.delete(url, data=None, headers=headers) if r.status_code != 200: ret_code = 1 except ConnectionError: pass sys.exit(ret_code) - - #end __init__ + # end __init__ def StripQuotes(self, arg): if arg and arg.startswith('"') and arg.endswith('"'): return arg[1:-1] - #end StripQuotes + # end StripQuotes def IsNumber(self, s): try: @@ -110,7 +110,7 @@ class VrouterPortControl(object): return True except ValueError: return False - #end IsNumber + # end IsNumber def _parse_args(self, args_str): strip_quotes = False @@ -120,10 +120,10 @@ class VrouterPortControl(object): else: regex = re.compile("\s+(?=\-\-)") # Turn off help, so we print all options in response to -h - conf_parser = argparse.ArgumentParser(add_help = False) + conf_parser = argparse.ArgumentParser(add_help=False) args, remaining_argv = conf_parser.parse_known_args( regex.split(args_str) - ) + ) # Don't surpress add_help here so it will handle -h parser = argparse.ArgumentParser( @@ -133,11 +133,11 @@ class VrouterPortControl(object): description=__doc__, # Don't mess with format of description formatter_class=argparse.RawDescriptionHelpFormatter, - ) + ) parser.add_argument('--oper', help="Operation add/delete ", - required = True) - parser.add_argument('--uuid', help="port UUID", required = True) + required=True) + parser.add_argument('--uuid', help="port UUID", required=True) parser.add_argument('--instance_uuid', help="instance UUID") parser.add_argument('--vn_uuid', help="VN UUID") parser.add_argument('--vm_project_uuid', help="VM UUID") @@ -146,14 +146,14 @@ class VrouterPortControl(object): parser.add_argument('--vm_name', help="VM Name") parser.add_argument('--mac', help="MAC address") parser.add_argument('--tap_name', help="System name of interface") - parser.add_argument("--port_type", help = "Port type", default="NovaVMPort") - parser.add_argument("--tx_vlan_id", help = "Transmit VLAN ID") - parser.add_argument("--rx_vlan_id", help = "Receive VLAN ID") - parser.add_argument("--no_persist", type = bool, - help = "Dont't store port information in files", + parser.add_argument("--port_type", help="Port type", default="NovaVMPort") + parser.add_argument("--tx_vlan_id", help="Transmit VLAN ID") + parser.add_argument("--rx_vlan_id", help="Receive VLAN ID") + parser.add_argument("--no_persist", type=bool, + help="Dont't store port information in files", default=False) - parser.add_argument("--vif_type", help = "VIF type", default="Vrouter") - parser.add_argument("--vhostuser_socket", help = "Path of vhostuser socket file") + parser.add_argument("--vif_type", help="VIF type", default="Vrouter") + parser.add_argument("--vhostuser_socket", help="Path of vhostuser socket file") self._args = parser.parse_args(remaining_argv) @@ -203,7 +203,7 @@ class VrouterPortControl(object): print "Invalid argument for tx_vlan_id %s" % (self._args.tx_vlan_id) sys.exit(1) self._args.tx_vlan_id = int(self._args.tx_vlan_id) - #end _parse_args + # end _parse_args def CreateDirectoryTree(self, path): try: @@ -211,31 +211,32 @@ class VrouterPortControl(object): except OSError as exc: if exc.errno == errno.EEXIST and os.path.isdir(path): pass - else: raise - #end CreateDirectoryTree + else: + raise + # end CreateDirectoryTree def GetJSonDict(self, port_type, project_id): data = { - "id" : self._args.uuid, - "instance-id" : self._args.instance_uuid, - "ip-address" : self._args.ip_address, - "ip6-address" : self._args.ipv6_address, - "vn-id" : self._args.vn_uuid, - "display-name": self._args.vm_name, - "vm-project-id" : project_id, - "mac-address" : self._args.mac, - "system-name" : self._args.tap_name, - "type" : port_type, - "rx-vlan-id" : self._args.rx_vlan_id, - "tx-vlan-id" : self._args.tx_vlan_id, - "author" : __file__, - "time" : str(datetime.datetime.now()) - } - return data; - #end GetJSonDict + "id": self._args.uuid, + "instance-id": self._args.instance_uuid, + "ip-address": self._args.ip_address, + "ip6-address": self._args.ipv6_address, + "vn-id": self._args.vn_uuid, + "display-name": self._args.vm_name, + "vm-project-id": project_id, + "mac-address": self._args.mac, + "system-name": self._args.tap_name, + "type": port_type, + "rx-vlan-id": self._args.rx_vlan_id, + "tx-vlan-id": self._args.tx_vlan_id, + "author": __file__, + "time": str(datetime.datetime.now()) + } + return data + # end GetJSonDict def WriteToFile(self, port_type, project_id): - filename = ("%s%s" %(PORT_PATH, self._args.uuid)) + filename = ("%s%s" % (PORT_PATH, self._args.uuid)) data = self.GetJSonDict(port_type, project_id) try: with open(filename, 'w') as outfile: @@ -244,24 +245,24 @@ class VrouterPortControl(object): except: return 1 return 0 - #end WriteToFile + # end WriteToFile def DeleteFile(self): - filename = ("%s%s" %(PORT_PATH, self._args.uuid)) + filename = ("%s%s" % (PORT_PATH, self._args.uuid)) if os.path.isfile(filename): os.remove(filename) - #end DeleteFile + # end DeleteFile def WaitForSocketFile(self): timeout_usecs = TIMEOUT_SECS * 1000000 sleep_usecs = 1000 - for i in range(1,(timeout_usecs/sleep_usecs)): + for i in range(1, (timeout_usecs / sleep_usecs)): if os.path.exists(self._args.vhostuser_socket): return 0 - #sleep takes time in seconds. Convert usecs to secs. - time.sleep(sleep_usecs/1000000.0) + # sleep takes time in seconds. Convert usecs to secs. + time.sleep(sleep_usecs / 1000000.0) return 6 - #end WaitForSocketFile + # end WaitForSocketFile def IsPortOpen(self, port_num): s = socket.socket() @@ -270,26 +271,27 @@ class VrouterPortControl(object): s.shutdown(socket.SHUT_RDWR) s.close() return True - except socket.error, e: + except socket.error: return False - #end IsPortOpen + # end IsPortOpen def WaitForPortOpen(self, port_num): timeout_usecs = PORT_OPEN_TIMEOUT_SECS * 1000000 sleep_usecs = 1000000 - for i in range(1,(timeout_usecs/sleep_usecs)): + for i in range(1, (timeout_usecs / sleep_usecs)): if self.IsPortOpen(port_num): return True - #sleep takes time in seconds. Convert usecs to secs. - time.sleep(sleep_usecs/1000000.0) + # sleep takes time in seconds. Convert usecs to secs. + time.sleep(sleep_usecs / 1000000.0) return False - #end WaitForPortOpen - + # end WaitForPortOpen # end class VrouterPortControl -def main(args_str = None): + +def main(args_str=None): VrouterPortControl(args_str) -#end main +# end main + if __name__ == "__main__": main() From 3109120955decc2bcefe7904a1f98fd8b546fbad Mon Sep 17 00:00:00 2001 From: Sahil Date: Mon, 15 May 2017 13:12:22 -0700 Subject: [PATCH 25/68] Fix to check port and router tenant id are same Added a check to see if router's and (port/subnet)'s tenant ids are same at the time of adding an interface to a router. Closes-Bug: 1686489 Change-Id: Ibc2fe74c6ce56e467aec87698077d145751f64cc --- .../vnc_openstack/vnc_openstack/neutron_plugin_db.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py b/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py index d626e66d17e..c331d9614df 100644 --- a/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py +++ b/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py @@ -3439,6 +3439,10 @@ def add_router_interface(self, context, router_id, port_id=None, subnet_id=None) router_obj = self._logical_router_read(router_id) if port_id: port = self.port_read(port_id) + if port['tenant_id'] != context['tenant_id'].replace('-', ''): + self._raise_contrail_exception('RouterInterfaceNotFound', + router_id=router_id, + port_id=port_id) if (port['device_owner'] == constants.DEVICE_OWNER_ROUTER_INTF and port['device_id']): self._raise_contrail_exception('PortInUse', @@ -3459,6 +3463,11 @@ def add_router_interface(self, context, router_id, port_id=None, subnet_id=None) elif subnet_id: subnet = self.subnet_read(subnet_id) + if subnet['tenant_id'] != context['tenant_id'].replace('-', ''): + self._raise_contrail_exception( + 'RouterInterfaceNotFoundForSubnet', + router_id=router_id, + subnet_id=subnet_id) if not subnet['gateway_ip']: self._raise_contrail_exception( 'BadRequest', resource='router', From 47a052590c27d297bf8644fc77973629610f72b8 Mon Sep 17 00:00:00 2001 From: arvindvis Date: Fri, 2 Jun 2017 14:33:50 -0700 Subject: [PATCH 26/68] ssl and crypto libraries are need for vizd because new librdkafka uses it. In 4.0 these libraries were added as part of securing introspect changes, so no build failures seen. But for 3.2 they have to be added. Closes-Bug:#1695543 Change-Id: I6f2644c3fb5d4c28eddc61d05966efd3d13d15cd --- src/analytics/SConscript | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/analytics/SConscript b/src/analytics/SConscript index a29f6199c2d..0f04a2d07bd 100644 --- a/src/analytics/SConscript +++ b/src/analytics/SConscript @@ -58,14 +58,14 @@ AnalyticsEnv.Prepend(LIBS=['cpuinfo', 'protobuf', 'zookeeper_client', 'zookeeper_mt', + 'ssl', + 'crypto', 'boost_filesystem', 'boost_program_options']) (PLATFORM, VERSION, EXTRA) = platform.linux_distribution() if PLATFORM.lower() != 'ubuntu': - AnalyticsEnv.Prepend(LIBS=['ssl', - 'crypto', - 'sasl2']) + AnalyticsEnv.Prepend(LIBS=['sasl2']) database_libs = ['cassandra_cql', 'cassandra'] From adf5c2ebb8f2c693e85536ae03924192abe49faf Mon Sep 17 00:00:00 2001 From: Yuvaraja Mariappan Date: Thu, 8 Jun 2017 12:24:57 -0700 Subject: [PATCH 27/68] Fixed contrail-analytics unavailablity issue contrail-svc-monitor uses the uve status if the query is succeed to the contrail-analytics. Otherwise it is better to continue with last known state of agents and vrouters Change-Id: I3b4c896a8834f5fef71ff4262dc09dc62cee36bc Closes-bug: #1696593 --- .../svc_monitor/scheduler/vrouter_scheduler.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/config/svc-monitor/svc_monitor/scheduler/vrouter_scheduler.py b/src/config/svc-monitor/svc_monitor/scheduler/vrouter_scheduler.py index 07e401db3b4..6916455518b 100644 --- a/src/config/svc-monitor/svc_monitor/scheduler/vrouter_scheduler.py +++ b/src/config/svc-monitor/svc_monitor/scheduler/vrouter_scheduler.py @@ -111,8 +111,8 @@ def query_uve(self, filter_string): for values in response['value']: response_dict[values['name']] = values['value'] except Exception as e: - pass - return response_dict + return False, response_dict + return True, response_dict def vrouters_running(self): # get az host list @@ -122,8 +122,12 @@ def vrouters_running(self): self._analytics = self.get_analytics_client() if not self._analytics: return - agents_status = self.query_uve("*?cfilt=NodeStatus:process_status") - vrouters_mode = self.query_uve("*?cfilt=VrouterAgent:mode") + query_status, agents_status = self.query_uve("*?cfilt=NodeStatus:process_status") + if not query_status: + return + query_status, vrouters_mode = self.query_uve("*?cfilt=VrouterAgent:mode") + if not query_status: + return for vr in VirtualRouterSM.values(): if az_vrs and vr.name not in az_vrs: From 35d163a0d17463c925bc356804ef712b562416a3 Mon Sep 17 00:00:00 2001 From: Ignatious Johnson Christopher Date: Sun, 11 Jun 2017 01:08:02 -0700 Subject: [PATCH 28/68] Currently max_reties is set to 30 in cassandra ConnectionPool initialization object. Which will retry (max_retries) time with the sleep of "_BASE_BACKOFF * (2 ** self._retry_count)" seconds Where _BASE_BACKOFF = 0.01 and self._retry_count can reach up to 30(max_retries), during an operation on a connection fails due to a TimedOutException or UnavailableException changing the max_reties to 15 so that the max sleep time will be approximately 5 minutes before the config services are restarted due to DatabaseUnavailableError Change-Id: Ib96c9e3f9745bcb06fea621e0d7fb52c8ff87f69 Closes-Bug: 1697252 (cherry picked from commit e31027fb45c53ce1d706aeb6be1a951e3b36a751) --- src/config/common/vnc_cassandra.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/common/vnc_cassandra.py b/src/config/common/vnc_cassandra.py index e0d96a6bda8..3239733a626 100644 --- a/src/config/common/vnc_cassandra.py +++ b/src/config/common/vnc_cassandra.py @@ -574,7 +574,7 @@ def _cassandra_init_conn_pools(self): pool = pycassa.ConnectionPool( keyspace, self._server_list, max_overflow=5, use_threadlocal=True, prefill=True, pool_size=20, pool_timeout=120, - max_retries=30, timeout=5, credentials=self._credential) + max_retries=15, timeout=5, credentials=self._credential) rd_consistency = pycassa.cassandra.ttypes.ConsistencyLevel.QUORUM wr_consistency = pycassa.cassandra.ttypes.ConsistencyLevel.QUORUM From 9cc6dc15eaaefe082d2f6ad6a5a33d705b399a4c Mon Sep 17 00:00:00 2001 From: Ignatious Johnson Christopher Date: Mon, 12 Jun 2017 14:45:12 -0700 Subject: [PATCH 29/68] Ignore errors during flush, similar to the base class handle method. Change-Id: I6b4ba7c20e9b13afa62704fb879e69550c68ad17 Closes-Bug: 1697548 (cherry picked from commit 37ba4e9d182fe5b3b49d65361c238ebc14918fee) --- src/config/common/vnc_cgitb.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/config/common/vnc_cgitb.py b/src/config/common/vnc_cgitb.py index d14e469792a..954dd046888 100644 --- a/src/config/common/vnc_cgitb.py +++ b/src/config/common/vnc_cgitb.py @@ -100,7 +100,11 @@ def handle(self, info=None): doc = local_buf.getvalue() local_buf.close() self.file.write(mask_password(doc)) - self.file.flush() + try: + self.file.flush() + except: + # Ignore errors during flush. + pass handler = Hook(format='text').handle From 590e50cad5586457060fadbaad2aed2062d4c82a Mon Sep 17 00:00:00 2001 From: Karl Klashinsky Date: Fri, 16 Jun 2017 12:39:04 -0700 Subject: [PATCH 30/68] Update release to 3.2.3.2 Change-Id: Ifcf3d3c81dccfbda808c2dc747290a05963d4de9 --- src/base/version.info | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/base/version.info b/src/base/version.info index e4424a9b05a..dd152b305b8 100644 --- a/src/base/version.info +++ b/src/base/version.info @@ -1 +1 @@ -3.2.3.1 +3.2.3.2 From 607a74221c4a29040d8a36f1768d72789d46ae27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89douard=20Thuleau?= Date: Tue, 6 Jun 2017 17:00:42 +0200 Subject: [PATCH 31/68] [VNC OpenStack] Admin can set router interface Authorize administrative users to set router interface even the router does not belongs to the user's project. Change-Id: Ie9f44b60a9018f67bd62a49b00aef22a6ec77ac7 Partial-Bug: #1686489 (cherry picked from commit 3bea664bd68d756441f86c5a687db2320af0b692) (cherry picked from commit 4b37571c93822aa70ce0f9e8020c6c3dc6114649) --- .../vnc_openstack/vnc_openstack/neutron_plugin_db.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py b/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py index c331d9614df..d9226f696e2 100644 --- a/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py +++ b/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py @@ -3439,7 +3439,8 @@ def add_router_interface(self, context, router_id, port_id=None, subnet_id=None) router_obj = self._logical_router_read(router_id) if port_id: port = self.port_read(port_id) - if port['tenant_id'] != context['tenant_id'].replace('-', ''): + if (not context.get('is_admin', False) and + port['tenant_id'] != context['tenant_id'].replace('-', '')): self._raise_contrail_exception('RouterInterfaceNotFound', router_id=router_id, port_id=port_id) @@ -3463,7 +3464,9 @@ def add_router_interface(self, context, router_id, port_id=None, subnet_id=None) elif subnet_id: subnet = self.subnet_read(subnet_id) - if subnet['tenant_id'] != context['tenant_id'].replace('-', ''): + if (not context.get('is_admin', False) and + subnet['tenant_id'] != + context['tenant_id'].replace('-', '')): self._raise_contrail_exception( 'RouterInterfaceNotFoundForSubnet', router_id=router_id, From 7481eb46469f9e50abc306688e996378ca2f8ddc Mon Sep 17 00:00:00 2001 From: Praveen K V Date: Fri, 31 Mar 2017 10:51:22 +0530 Subject: [PATCH 32/68] Fix health-check when fat flow enabled on vmi Problem: Fat flow is enabled on the vm-interface and not on vhost0 interface. So, packets from vhost0 result in non-fat flow and packets from vm-interface result in fat-flow. Health Check assumes that flows are always initiated from vhost0 interface. So, flows created due to packets from vm-interface do not have NAT translation based on health-check rules. As a result, packets from vm-interface are going to host-os Solution: 1. Health-Check status is ACTIVE When fat-flow is enabled, the fat-flow created due to packet from vm-interface will also apply health-check rules and create appropriated NAT-ed reverse flow. 2. Health-Check status is INACTIVE When health-check status is INACTIVE, the routes for IP address on vm-interface are also withdrawn. So, flows cannot be setup for packets from vm-interface. As a solution, we disable fat-flow on the vm-interface when health-check status is INACTIVE. As a result, packets from vm-interface will match flows created due to request and health-check session will succeed. Conflicts: src/vnsw/agent/pkt/test/SConscript Change-Id: I7766cbd9bbc25e1f740148c35ed7e115a4fd7d95 Closes-Bug: #1676721 --- src/vnsw/agent/oper/health_check.cc | 26 +- src/vnsw/agent/oper/health_check.h | 25 +- src/vnsw/agent/oper/metadata_ip.cc | 10 +- src/vnsw/agent/oper/metadata_ip.h | 7 +- src/vnsw/agent/oper/vm_interface.cc | 43 +++ src/vnsw/agent/oper/vm_interface.h | 6 +- src/vnsw/agent/pkt/pkt_flow_info.cc | 53 ++++ src/vnsw/agent/pkt/pkt_flow_info.h | 2 + src/vnsw/agent/pkt/test/SConscript | 1 + src/vnsw/agent/pkt/test/test_flow_hc.cc | 275 ++++++++++++++++++ src/vnsw/agent/uve/interface_uve_table.cc | 4 +- .../agent/vrouter/ksync/agent_ksync.sandesh | 1 + .../agent/vrouter/ksync/interface_ksync.cc | 21 +- .../agent/vrouter/ksync/interface_ksync.h | 1 + 14 files changed, 451 insertions(+), 24 deletions(-) create mode 100644 src/vnsw/agent/pkt/test/test_flow_hc.cc diff --git a/src/vnsw/agent/oper/health_check.cc b/src/vnsw/agent/oper/health_check.cc index 13c5e723668..23483cf3e1d 100644 --- a/src/vnsw/agent/oper/health_check.cc +++ b/src/vnsw/agent/oper/health_check.cc @@ -115,7 +115,7 @@ bool HealthCheckInstance::DestroyInstanceTask() { void HealthCheckInstance::UpdateInstanceTaskCommand() { if (service_->table_->agent()->test_mode()) { // in test mode, set task instance to run no-op shell - task_->set_cmd("/bin/sh"); + task_->set_cmd("sleep 1"); return; } @@ -258,11 +258,21 @@ bool HealthCheckService::Copy(HealthCheckTable *table, ret = true; } + if (ip_proto_ != data->ip_proto_) { + ip_proto_ = data->ip_proto_; + ret = true; + } + if (url_path_ != data->url_path_) { url_path_ = data->url_path_; ret = true; } + if (url_port_ != data->url_port_) { + url_port_ = data->url_port_; + ret = true; + } + if (expected_codes_ != data->expected_codes_) { expected_codes_ = data->expected_codes_; ret = true; @@ -441,11 +451,15 @@ static HealthCheckServiceData *BuildData(Agent *agent, IFMapNode *node, const autogen::ServiceHealthCheckType &p = s->properties(); Ip4Address dest_ip; std::string url_path; + uint8_t ip_proto = 0; + uint16_t url_port = 0; if (p.monitor_type.find("HTTP") == std::string::npos) { boost::system::error_code ec; dest_ip = Ip4Address::from_string(p.url_path, ec); url_path = p.url_path; + ip_proto = IPPROTO_ICMP; } else if (!p.url_path.empty()) { + ip_proto = IPPROTO_TCP; // parse url if available struct http_parser_url urldata; int ret = http_parser_parse_url(p.url_path.c_str(), p.url_path.size(), @@ -459,13 +473,17 @@ static HealthCheckServiceData *BuildData(Agent *agent, IFMapNode *node, // keep rest of the url string as is url_path = p.url_path.substr(urldata.field_data[UF_HOST].off +\ urldata.field_data[UF_HOST].len); + url_port = urldata.port; + if ((urldata.field_set & (1 << UF_PORT)) == 0) { + url_port = 80; + } } } HealthCheckServiceData *data = new HealthCheckServiceData(agent, dest_ip, node->name(), - p.monitor_type, p.http_method, url_path, - p.expected_codes, p.delay, p.timeout, - p.max_retries, node); + p.monitor_type, ip_proto, p.http_method, + url_path, url_port, p.expected_codes, + p.delay, p.timeout, p.max_retries, node); IFMapAgentTable *table = static_cast(node->table()); for (DBGraphVertex::adjacency_iterator iter = diff --git a/src/vnsw/agent/oper/health_check.h b/src/vnsw/agent/oper/health_check.h index 01b6cece736..2aee4a685e0 100644 --- a/src/vnsw/agent/oper/health_check.h +++ b/src/vnsw/agent/oper/health_check.h @@ -44,23 +44,28 @@ struct HealthCheckServiceData : public AgentOperDBData { HealthCheckServiceData(Agent *agent, IpAddress dest_ip, const std::string &name, const std::string &monitor_type, + uint8_t ip_proto, const std::string &http_method, const std::string &url_path, + uint16_t url_port, const std::string &expected_codes, uint32_t delay, uint32_t timeout, uint32_t max_retries, IFMapNode *ifmap_node) : AgentOperDBData(agent, ifmap_node), dest_ip_(dest_ip), name_(name), - monitor_type_(monitor_type), http_method_(http_method), - url_path_(url_path), expected_codes_(expected_codes), delay_(delay), - timeout_(timeout), max_retries_(max_retries) { + monitor_type_(monitor_type), ip_proto_(ip_proto), + http_method_(http_method), url_path_(url_path), url_port_(url_port), + expected_codes_(expected_codes), delay_(delay), timeout_(timeout), + max_retries_(max_retries) { } virtual ~HealthCheckServiceData() {} IpAddress dest_ip_; std::string name_; std::string monitor_type_; + uint8_t ip_proto_; std::string http_method_; std::string url_path_; + uint16_t url_port_; std::string expected_codes_; uint32_t delay_; uint32_t timeout_; @@ -86,7 +91,8 @@ struct HealthCheckInstanceEvent { DISALLOW_COPY_AND_ASSIGN(HealthCheckInstanceEvent); }; -struct HealthCheckInstance { +class HealthCheckInstance { +public: typedef InstanceTaskExecvp HeathCheckProcessInstance; static const std::string kHealthCheckCmd; @@ -117,6 +123,11 @@ struct HealthCheckInstance { bool active() {return active_;} bool IsRunning() const; + HealthCheckService *service() const { return service_.get(); } + const MetaDataIp *ip() const { return ip_.get(); } +private: + friend class HealthCheckService; + friend class HealthCheckTable; // reference to health check service under // which this instance is running HealthCheckServiceRef service_; @@ -165,6 +176,8 @@ class HealthCheckService : AgentRefCount, const boost::uuids::uuid &uuid() const { return uuid_; } const std::string &name() const { return name_; } + uint8_t ip_proto() const { return ip_proto_; } + uint16_t url_port() const { return url_port_; } private: friend class HealthCheckInstance; friend class HealthCheckInstanceEvent; @@ -175,8 +188,12 @@ class HealthCheckService : AgentRefCount, std::string name_; // monitor type of service PING/HTTP etc std::string monitor_type_; + // ip_proto derived from monitor_type_ + uint8_t ip_proto_; std::string http_method_; std::string url_path_; + // tcp/udp port numbers derived from url + uint16_t url_port_; std::string expected_codes_; uint32_t delay_; uint32_t timeout_; diff --git a/src/vnsw/agent/oper/metadata_ip.cc b/src/vnsw/agent/oper/metadata_ip.cc index f626d2a00aa..4e2cf7abf5c 100644 --- a/src/vnsw/agent/oper/metadata_ip.cc +++ b/src/vnsw/agent/oper/metadata_ip.cc @@ -35,13 +35,13 @@ MetaDataIp::~MetaDataIp() { allocator_->ReleaseIndex(this); } -Ip4Address MetaDataIp::GetLinkLocalIp() { +Ip4Address MetaDataIp::GetLinkLocalIp() const { uint32_t ip = METADATA_IP_ADDR & 0xFFFF0000; ip += (((uint32_t)index_) & 0xFFFF); return Ip4Address(ip); } -IpAddress MetaDataIp::service_ip() { +IpAddress MetaDataIp::service_ip() const { // check if explicit configuration of service ip is present for // this metadata ip if (service_ip_ == kDefaultIp) { @@ -66,11 +66,7 @@ IpAddress MetaDataIp::service_ip() { return service_ip_; } -void MetaDataIp::set_service_ip(const IpAddress &src_ip) { - service_ip_ = src_ip; -} - -IpAddress MetaDataIp::destination_ip() { +IpAddress MetaDataIp::destination_ip() const { if (destination_ip_.to_v4() == kDefaultIp) { return intf_->primary_ip_addr(); } diff --git a/src/vnsw/agent/oper/metadata_ip.h b/src/vnsw/agent/oper/metadata_ip.h index aa1edb7890f..84aec864d8c 100644 --- a/src/vnsw/agent/oper/metadata_ip.h +++ b/src/vnsw/agent/oper/metadata_ip.h @@ -42,12 +42,11 @@ class MetaDataIp { uint16_t index); ~MetaDataIp(); - Ip4Address GetLinkLocalIp(); + Ip4Address GetLinkLocalIp() const; - IpAddress service_ip(); - void set_service_ip(const IpAddress &src_ip); + IpAddress service_ip() const; - IpAddress destination_ip(); + IpAddress destination_ip() const; void set_destination_ip(const IpAddress &dst_ip); void set_active(bool active); diff --git a/src/vnsw/agent/oper/vm_interface.cc b/src/vnsw/agent/oper/vm_interface.cc index 9a1020e54f2..0ba993de5be 100644 --- a/src/vnsw/agent/oper/vm_interface.cc +++ b/src/vnsw/agent/oper/vm_interface.cc @@ -5235,6 +5235,49 @@ VmInterface::hc_instance_set() const { return hc_instance_set_; } +bool VmInterface::IsHealthCheckEnabled() const { + return hc_instance_set_.size() != 0; +} + +// Match the Health-Check instance for a packet from VM-Interface +// A packet from vmi is assumed to be response for health-check request from +// vhost0 +const HealthCheckInstance *VmInterface::GetHealthCheckFromVmiFlow +(const IpAddress &sip, const IpAddress &dip, uint8_t proto, + uint16_t sport) const { + HealthCheckInstanceSet::const_iterator it = hc_instance_set_.begin(); + while (it != hc_instance_set_.end()) { + const HealthCheckInstance *hc_instance = *it; + it++; + + // Match ip-proto and health-check port + const HealthCheckService *hc_service = hc_instance->service(); + if (hc_service == NULL) + continue; + + if (hc_service->ip_proto() != proto) + continue; + + if (hc_service->url_port() != sport) + continue; + + // The source-ip and destination-ip can be matched from MetaDataIp + // allocated for HealthCheck + const MetaDataIp *mip = hc_instance->ip(); + if (mip == NULL) + continue; + + if (mip->destination_ip() != sip) + continue; + + if (mip->service_ip() != dip) + continue; + + return hc_instance; + } + + return NULL; +} //////////////////////////////////////////////////////////////////////////// // VRF assign rule routines //////////////////////////////////////////////////////////////////////////// diff --git a/src/vnsw/agent/oper/vm_interface.h b/src/vnsw/agent/oper/vm_interface.h index a1b036e28a1..94eebec73f8 100644 --- a/src/vnsw/agent/oper/vm_interface.h +++ b/src/vnsw/agent/oper/vm_interface.h @@ -649,7 +649,11 @@ class VmInterface : public Interface { void InsertHealthCheckInstance(HealthCheckInstance *hc_inst); void DeleteHealthCheckInstance(HealthCheckInstance *hc_inst); const HealthCheckInstanceSet &hc_instance_set() const; - + bool IsHealthCheckEnabled() const; + const HealthCheckInstance *GetHealthCheckFromVmiFlow(const IpAddress &sip, + const IpAddress &dip, + uint8_t proto, + uint16_t sport) const; size_t GetFloatingIpCount() const { return floating_ip_list_.list_.size(); } size_t GetAliasIpCount() const { return alias_ip_list_.list_.size(); } bool HasServiceVlan() const { return service_vlan_list_.list_.size() != 0; } diff --git a/src/vnsw/agent/pkt/pkt_flow_info.cc b/src/vnsw/agent/pkt/pkt_flow_info.cc index 9240cba9239..e55284f6149 100644 --- a/src/vnsw/agent/pkt/pkt_flow_info.cc +++ b/src/vnsw/agent/pkt/pkt_flow_info.cc @@ -23,6 +23,7 @@ #include "oper/operdb_init.h" #include "oper/tunnel_nh.h" #include "oper/bgp_as_service.h" +#include "oper/health_check.h" #include "filter/packet_header.h" #include "filter/acl.h" @@ -782,6 +783,53 @@ void PktFlowInfo::BgpRouterServiceTranslate(const PktInfo *pkt, } } +// Consider Health Check is enabled on an interface and also FAT flow is +// enabled for the destination port. +// +// VRouter receives request from vhost0 interface. The vhost0 interface does +// not have fat-flow enabled, so 5-tuple flow is created ignoring the fat flow +// +// VRouter receives response from vm-interface that has fat-flow enabled. +// So, VRouter creates with fat-flow. Agent needs to ensure that fat flow +// created must do the NAT translation according to health-check rules +// +// Note, if FAT flow is not configured, then pacekt from vm-interface will +// hit the reverse flow created due to request +void PktFlowInfo::ProcessHealthCheckFatFlow(const VmInterface *vmi, + const PktInfo *pkt, + PktControlInfo *in, + PktControlInfo *out) { + // Health check valid for IPv4 only + if (pkt->ip_daddr.is_v4() == false || pkt->ip_saddr.is_v4() == false) + return; + + // Ensure fat-flow is configured for the port first + if (vmi->IsFatFlow(pkt->ip_proto, pkt->sport) == false) + return; + + // Look for health-check rule + const HealthCheckInstance *hc_instance = + vmi->GetHealthCheckFromVmiFlow(pkt->ip_saddr, pkt->ip_daddr, + pkt->ip_proto, pkt->sport); + if (hc_instance == NULL) + return; + + const MetaDataIp *mip = hc_instance->ip(); + if (mip == NULL) + return; + + nat_done = true; + nat_ip_saddr = mip->GetLinkLocalIp(); + nat_ip_daddr = agent->router_id(); + nat_dport = pkt->dport; + nat_sport = pkt->sport; + nat_vrf = agent->fabric_vrf()->vrf_id(); + nat_dest_vrf = dest_vrf; + dest_vrf = agent->fabric_vrf()->vrf_id(); + return; +} + + // DestNAT for packets entering into a VM with floating-ip. // Can come here in two paths, // - Packet originated on local vm. @@ -1157,6 +1205,11 @@ void PktFlowInfo::IngressProcess(const PktInfo *pkt, PktControlInfo *in, BgpRouterServiceTranslate(pkt, in, out); } + // Handle special case of health-check with fat-flow + if (vm_port && vm_port->IsHealthCheckEnabled()) { + ProcessHealthCheckFatFlow(vm_port, pkt, in, out); + } + // If out-interface was not found, get it based on out-route if (out->intf_ == NULL && out->rt_) { RouteToOutInfo(out->rt_, pkt, this, in, out); diff --git a/src/vnsw/agent/pkt/pkt_flow_info.h b/src/vnsw/agent/pkt/pkt_flow_info.h index 8795500e677..75697a4d487 100644 --- a/src/vnsw/agent/pkt/pkt_flow_info.h +++ b/src/vnsw/agent/pkt/pkt_flow_info.h @@ -69,6 +69,8 @@ class PktFlowInfo { PktControlInfo *out); void BgpRouterServiceTranslate(const PktInfo *pkt, PktControlInfo *in, PktControlInfo *out); + void ProcessHealthCheckFatFlow(const VmInterface *vmi, const PktInfo *pkt, + PktControlInfo *in, PktControlInfo *out); void FloatingIpSNat(const PktInfo *pkt, PktControlInfo *in, PktControlInfo *out); void FloatingIpDNat(const PktInfo *pkt, PktControlInfo *in, diff --git a/src/vnsw/agent/pkt/test/SConscript b/src/vnsw/agent/pkt/test/SConscript index 2513e9e2494..8e8e2f7ce14 100644 --- a/src/vnsw/agent/pkt/test/SConscript +++ b/src/vnsw/agent/pkt/test/SConscript @@ -23,6 +23,7 @@ test_flow_mgmt_route = AgentEnv.MakeTestCmd(env, 'test_flow_mgmt_route', pkt_test_suite) test_tsn_flow = AgentEnv.MakeTestCmd(env, 'test_tsn_flow', pkt_test_suite) test_flow_table = AgentEnv.MakeTestCmd(env, 'test_flow_table', pkt_test_suite) +test_flow_hc = AgentEnv.MakeTestCmd(env, 'test_flow_hc', pkt_test_suite) test_pkt = AgentEnv.MakeTestCmd(env, 'test_pkt', pkt_flaky_test_suite) test_pkt_flow_mock = AgentEnv.MakeTestCmd(env, 'test_pkt_flow_mock', pkt_flaky_test_suite) diff --git a/src/vnsw/agent/pkt/test/test_flow_hc.cc b/src/vnsw/agent/pkt/test/test_flow_hc.cc new file mode 100644 index 00000000000..0c273323fc6 --- /dev/null +++ b/src/vnsw/agent/pkt/test/test_flow_hc.cc @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved. + */ + +#include "base/os.h" +#include +#include +#include "test/test_cmn_util.h" +#include "test_flow_util.h" +#include "ksync/ksync_sock_user.h" +#include "oper/tunnel_nh.h" +#include "oper/health_check.h" +#include "oper/metadata_ip.h" +#include "pkt/flow_table.h" + +#define VMI_MAX_COUNT 256 + +IpamInfo ipam_info_1[] = { + {"1.1.1.0", 24, "1.1.1.254"}, +}; + +// A non-ecmp port +struct PortInfo input[] = { + {"vif1", 1, "1.1.1.1", "00:01:01:01:01:01", 1, 1}, + {"vif2", 2, "1.1.1.2", "00:01:01:01:01:02", 1, 1}, + {"vif3", 3, "1.1.1.3", "00:01:01:01:01:03", 1, 1}, +}; + +class HealthCheckFlowTest : public ::testing::Test { +public: + HealthCheckFlowTest() : + agent_(Agent::GetInstance()), + flow_proto_(agent_->pkt()->get_flow_proto()) { + } + virtual ~HealthCheckFlowTest() { } + + virtual void SetUp() { + CreateVmportEnv(input, 3); + AddIPAM("vn1", ipam_info_1, 1); + client->WaitForIdle(); + + AddHealthCheckService("HC-1", 1, "http://local-ip/", "HTTP"); + AddHealthCheckService("HC-2", 2, "http://local-ip:8080/", "HTTP"); + AddHealthCheckService("HC-3", 3, "local-ip", "PING"); + AddLink("virtual-machine-interface", "vif1", "service-health-check", + "HC-1", "service-port-health-check"); + AddLink("virtual-machine-interface", "vif2", "service-health-check", + "HC-2", "service-port-health-check"); + AddLink("virtual-machine-interface", "vif3", "service-health-check", + "HC-3", "service-port-health-check"); + client->WaitForIdle(); + + + FlowStatsTimerStartStop(agent_, true); + GetInfo(); + + EXPECT_TRUE(vmi_[1]->ip_active(Address::INET)); + EXPECT_TRUE(vmi_[2]->ip_active(Address::INET)); + EXPECT_TRUE(vmi_[3]->ip_active(Address::INET)); + + InetInterfaceKey key("vhost0"); + vhost_ = static_cast + (agent_->interface_table()->FindActiveEntry(&key)); + router_id_ = agent_->router_id(); + } + + virtual void TearDown() { + FlushFlowTable(); + + DeleteVmportEnv(input, 3, true); + client->WaitForIdle(); + + DelIPAM("vn1"); + client->WaitForIdle(); + + DelLink("virtual-machine-interface", "vif1", "service-health-check", + "HC-1"); + DelLink("virtual-machine-interface", "vif2", "service-health-check", + "HC-2"); + DelLink("virtual-machine-interface", "vif3", "service-health-check", + "HC-3"); + DelHealthCheckService("HC-1"); + DelHealthCheckService("HC-2"); + DelHealthCheckService("HC-3"); + client->WaitForIdle(); + + FlowStatsTimerStartStop(agent_, false); + WAIT_FOR(1000, 1000, (agent_->vrf_table()->Size() == 1)); + } + + void GetInfo() { + for (uint32_t i = 1; i <= VMI_MAX_COUNT; i++) { + vmi_[i] = VmInterfaceGet(i); + if (vmi_[i] == NULL) + continue; + + EXPECT_TRUE(VmPortActive(i)); + const VmInterface::HealthCheckInstanceSet &set = + vmi_[i]->hc_instance_set(); + hc_instance_[i] = *set.begin(); + mip_[i] = hc_instance_[i]->ip(); + } + } + + void FlushFlowTable() { + client->EnqueueFlowFlush(); + client->WaitForIdle(); + WAIT_FOR(1000, 1000, (flow_proto_->FlowCount() == 0)); + } + + void UpdateHealthCheck(uint8_t id, bool state) { + string msg = "success"; + if (state == false) + msg = "failure"; + HealthCheckInstanceEvent *event = new HealthCheckInstanceEvent + (hc_instance_[id], HealthCheckInstanceEvent::MESSAGE_READ, msg); + agent_->health_check_table()->InstanceEventEnqueue(event); + client->WaitForIdle(); + + WAIT_FOR(1000, 1000, (vmi_[id]->is_hc_active() == state)); + } + + void IcmpTest(uint8_t id) { + TxIpPacket(vhost_->id(), router_id_.to_string().c_str(), + mip_[id]->GetLinkLocalIp().to_string().c_str(), 1); + client->WaitForIdle(); + + FlowEntry *flow = FlowGet(vhost_->flow_key_nh()->id(), + router_id_.to_string(), + mip_[id]->GetLinkLocalIp().to_string(), 1, + 0, 0); + EXPECT_TRUE(flow != NULL); + EXPECT_FALSE(flow->IsShortFlow()); + EXPECT_TRUE(flow->IsNatFlow()); + + FlowEntry *rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow != NULL); + EXPECT_FALSE(rflow->IsShortFlow()); + EXPECT_TRUE(rflow->IsNatFlow()); + + EXPECT_TRUE(rflow->key().src_addr.to_v4() == + vmi_[id]->primary_ip_addr()); + EXPECT_TRUE(rflow->key().dst_addr == mip_[id]->service_ip()); + } + + void HttpTestFromVHost(uint8_t id, uint16_t port) { + TxTcpPacket(vhost_->id(), router_id_.to_string().c_str(), + mip_[id]->GetLinkLocalIp().to_string().c_str(), 10000, + port, false); + client->WaitForIdle(); + + FlowEntry *flow = FlowGet(vhost_->flow_key_nh()->id(), + router_id_.to_string(), + mip_[id]->GetLinkLocalIp().to_string(), + 6, 10000, port); + EXPECT_TRUE(flow != NULL); + EXPECT_FALSE(flow->IsShortFlow()); + EXPECT_TRUE(flow->IsNatFlow()); + + FlowEntry *rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow != NULL); + EXPECT_FALSE(rflow->IsShortFlow()); + EXPECT_TRUE(rflow->IsNatFlow()); + + EXPECT_TRUE(rflow->key().src_addr.to_v4() == + vmi_[id]->primary_ip_addr()); + EXPECT_TRUE(rflow->key().dst_addr == mip_[id]->service_ip()); + } + + void HttpTestFromVmi(uint8_t id, uint16_t sport, uint16_t dport) { + string sip = vmi_[id]->primary_ip_addr().to_string(); + TxTcpPacket(vmi_[id]->id(), sip.c_str(), + mip_[id]->service_ip().to_string().c_str(), sport, dport, + false); + client->WaitForIdle(); + + FlowEntry *flow = FlowGet(vmi_[id]->flow_key_nh()->id(), sip, + mip_[id]->service_ip().to_string(), + 6, sport, dport); + EXPECT_TRUE(flow != NULL); + EXPECT_FALSE(flow->IsShortFlow()); + EXPECT_TRUE(flow->IsNatFlow()); + + FlowEntry *rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow != NULL); + EXPECT_FALSE(rflow->IsShortFlow()); + EXPECT_TRUE(rflow->IsNatFlow()); + + EXPECT_TRUE(rflow->key().src_addr == router_id_); + EXPECT_TRUE(rflow->key().dst_addr.to_v4() == + mip_[id]->GetLinkLocalIp()); + } + + void AddFatFlow(struct PortInfo *input, uint8_t id, + const std::string &protocol, int port) { + ostringstream str; + + str << "" + "" + "" << protocol << "" + "" << port << "" + "" + ""; + AddNode("virtual-machine-interface", input[id].name, input[id].intf_id, + str.str().c_str()); + client->WaitForIdle(); + } + +protected: + Agent *agent_; + Ip4Address router_id_; + FlowProto *flow_proto_; + InetInterface *vhost_; + VmInterface *vmi_[VMI_MAX_COUNT]; + HealthCheckInstance *hc_instance_[VMI_MAX_COUNT]; + const MetaDataIp *mip_[VMI_MAX_COUNT]; +}; + +TEST_F(HealthCheckFlowTest, Ping_Active_1) { + IcmpTest(3); +} + +TEST_F(HealthCheckFlowTest, Http_Active_1) { + HttpTestFromVHost(1, 80); +} + +TEST_F(HealthCheckFlowTest, Http_Active_Non_Service_Port_1) { + HttpTestFromVHost(1, 81); +} + +TEST_F(HealthCheckFlowTest, Http_Active_Non_Default_Service_Port_1) { + EXPECT_EQ(hc_instance_[2]->service()->url_port(), 8080); + HttpTestFromVHost(2, 8080); +} + +TEST_F(HealthCheckFlowTest, Ping_InActive_1) { + UpdateHealthCheck(3, false); + IcmpTest(3); +} + +TEST_F(HealthCheckFlowTest, Http_InActive_Service_Port_1) { + UpdateHealthCheck(1, false); + HttpTestFromVHost(1, 80); +} + +TEST_F(HealthCheckFlowTest, Http_InActive_Non_Service_Port_1) { + UpdateHealthCheck(1, false); + HttpTestFromVHost(1, 81); +} + +TEST_F(HealthCheckFlowTest, Http_InActive_Non_Default_Service_Port_1) { + UpdateHealthCheck(2, false); + EXPECT_EQ(hc_instance_[2]->service()->url_port(), 8080); + HttpTestFromVHost(2, 8080); +} + +TEST_F(HealthCheckFlowTest, Active_FatFlow_1) { + AddFatFlow(input, 0, "tcp", 80); + HttpTestFromVHost(1, 80); +} + +TEST_F(HealthCheckFlowTest, Active_FatFlow_From_Vmi_1) { + AddFatFlow(input, 0, "tcp", 80); + HttpTestFromVmi(1, 80, 0); +} + +int main(int argc, char *argv[]) { + GETUSERARGS(); + client = TestInit(init_file, ksync_init, true, true, true, 100*1000); + int ret = RUN_ALL_TESTS(); + client->WaitForIdle(); + TestShutdown(); + delete client; + return ret; +} diff --git a/src/vnsw/agent/uve/interface_uve_table.cc b/src/vnsw/agent/uve/interface_uve_table.cc index f438cc3da41..31903159561 100644 --- a/src/vnsw/agent/uve/interface_uve_table.cc +++ b/src/vnsw/agent/uve/interface_uve_table.cc @@ -162,8 +162,8 @@ bool InterfaceUveTable::UveInterfaceEntry::FrameInterfaceMsg(const string &name, while (hc_it != hc_list.end()) { HealthCheckInstance *inst = (*hc_it); VmHealthCheckInstance uve_inst; - uve_inst.set_name(inst->service_->name()); - uve_inst.set_uuid(to_string(inst->service_->uuid())); + uve_inst.set_name(inst->service()->name()); + uve_inst.set_uuid(to_string(inst->service()->uuid())); uve_inst.set_status(inst->active() ? "Active" : "InActive"); uve_inst.set_is_running(inst->IsRunning()); hc_it++; diff --git a/src/vnsw/agent/vrouter/ksync/agent_ksync.sandesh b/src/vnsw/agent/vrouter/ksync/agent_ksync.sandesh index 7ad4b42b96e..4407d244c73 100644 --- a/src/vnsw/agent/vrouter/ksync/agent_ksync.sandesh +++ b/src/vnsw/agent/vrouter/ksync/agent_ksync.sandesh @@ -34,6 +34,7 @@ struct KSyncIntfInfo { 4: optional u32 os_idx; 5: optional u32 vrf_id; 6: optional u32 active; + 12: optional bool hc_active; 7: optional u32 policy_enabled; 8: optional u32 service_enabled; 9: optional string analyzer_name; diff --git a/src/vnsw/agent/vrouter/ksync/interface_ksync.cc b/src/vnsw/agent/vrouter/ksync/interface_ksync.cc index 45385bb9316..eb8ca538dab 100644 --- a/src/vnsw/agent/vrouter/ksync/interface_ksync.cc +++ b/src/vnsw/agent/vrouter/ksync/interface_ksync.cc @@ -48,7 +48,7 @@ InterfaceKSyncEntry::InterfaceKSyncEntry(InterfaceKSyncObject *obj, has_service_vlan_(entry->has_service_vlan_), interface_id_(entry->interface_id_), interface_name_(entry->interface_name_), - ip_(entry->ip_), ipv4_active_(false), + ip_(entry->ip_), hc_active_(false), ipv4_active_(false), layer3_forwarding_(entry->layer3_forwarding_), ksync_obj_(obj), l2_active_(false), metadata_l2_active_(entry->metadata_l2_active_), @@ -91,6 +91,7 @@ InterfaceKSyncEntry::InterfaceKSyncEntry(InterfaceKSyncObject *obj, interface_id_(intf->id()), interface_name_(intf->name()), ip_(0), + hc_active_(false), ipv4_active_(false), layer3_forwarding_(true), ksync_obj_(obj), @@ -189,6 +190,11 @@ bool InterfaceKSyncEntry::Sync(DBEntry *e) { Interface *intf = static_cast(e); bool ret = false; + if (hc_active_ != intf->is_hc_active()) { + hc_active_ = intf->is_hc_active(); + ret = true; + } + if (ipv4_active_ != intf->ipv4_active()) { ipv4_active_ = intf->ipv4_active(); ret = true; @@ -600,7 +606,17 @@ int InterfaceKSyncEntry::Encode(sandesh_op::type op, char *buf, int buf_len) { (const int8_t *)smac() + smac().size())); } - if (fat_flow_list_.list_.size() != 0) { + // Disable fat-flow when health-check status is inactive + // If fat-flow is active, then following problem happens, + // 1. Health Check Request from vhost0 interface creates a flow + // 2. Health Check response is received from VMI with fat-flow + // - Response creates new flow due to fat-flow configuration + // - If health-check status is inactive routes for interface are + // withdrawn + // - Flow created from response fails due to missing routes + // If fat-flow is disabled, the reverse packet hits flow created (1) + // and succeeds + if (hc_active_ && fat_flow_list_.list_.size() != 0) { std::vector fat_flow_list; for (VmInterface::FatFlowEntrySet::const_iterator it = fat_flow_list_.list_.begin(); it != fat_flow_list_.list_.end(); @@ -771,6 +787,7 @@ void InterfaceKSyncEntry::FillObjectLog(sandesh_op::type op, if (op == sandesh_op::ADD) { info.set_os_idx(os_index_); info.set_vrf_id(vrf_id_); + info.set_hc_active(hc_active_); info.set_l2_active(l2_active_); info.set_active(ipv4_active_); info.set_policy_enabled(policy_enabled_); diff --git a/src/vnsw/agent/vrouter/ksync/interface_ksync.h b/src/vnsw/agent/vrouter/ksync/interface_ksync.h index 9d51c6e8c49..ca731683cc1 100644 --- a/src/vnsw/agent/vrouter/ksync/interface_ksync.h +++ b/src/vnsw/agent/vrouter/ksync/interface_ksync.h @@ -87,6 +87,7 @@ class InterfaceKSyncEntry : public KSyncNetlinkDBEntry { uint32_t interface_id_; string interface_name_; // Key uint32_t ip_; + bool hc_active_; bool ipv4_active_; bool layer3_forwarding_; InterfaceKSyncObject *ksync_obj_; From e729f46010e2e985ce8b0dcb522b8de50f63363f Mon Sep 17 00:00:00 2001 From: Hari Prasad Killi Date: Thu, 1 Jun 2017 14:36:51 +0530 Subject: [PATCH 33/68] Always generate the global options in named config. If contrail-dns doesnt generate global options and named reads the config file in the time between contrail-dns generation and script update, it woulnt listen on port 8094. Change-Id: Iabb6203bbde1d900b7f20bbe2330373e001ff300 closes-bug: #1694927 (cherry picked from commit 7ee49697cdcd36eb283105ecae426d70531b2e03) --- src/dns/applynamedconfig.py | 3 +-- src/dns/bind/named_config.cc | 10 +++------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/dns/applynamedconfig.py b/src/dns/applynamedconfig.py index 4ee722a7f48..48d6a4f0ed5 100755 --- a/src/dns/applynamedconfig.py +++ b/src/dns/applynamedconfig.py @@ -101,7 +101,6 @@ def parse_contrail_dns_conf(): # end parse_contrail_dns_conf - def main(): if not os.path.exists('/etc/contrail/dns/contrail-named-base.conf'): # parse contrail-dns.conf and build contrail-named-base.conf @@ -125,7 +124,7 @@ def main(): file2.close() # delete all lines before the view stanza {} - del lines[1:count] + del lines[0:count] # open contrail-named.conf file3 = open('/etc/contrail/dns/contrail-named.conf', 'w') diff --git a/src/dns/bind/named_config.cc b/src/dns/bind/named_config.cc index 8c0300500c5..8a659631a59 100644 --- a/src/dns/bind/named_config.cc +++ b/src/dns/bind/named_config.cc @@ -156,13 +156,9 @@ void NamedConfig::CreateNamedConf(const VirtualDnsConfig *updated_vdns) { GetDefaultForwarders(); file_.open(named_config_file_.c_str()); - ifstream pyscript("/etc/contrail/dns/applynamedconfig.py"); - if (!pyscript.good()) { - WriteOptionsConfig(); - WriteRndcConfig(); - WriteLoggingConfig(); - } - + WriteOptionsConfig(); + WriteRndcConfig(); + WriteLoggingConfig(); WriteViewConfig(updated_vdns); file_.flush(); From 4e876d711a3128670db09ab40dbf8deae3a81888 Mon Sep 17 00:00:00 2001 From: Hari Prasad Killi Date: Wed, 21 Jun 2017 18:00:30 +0530 Subject: [PATCH 34/68] Enable flow setup when BGPaaS is configured on VMI with policy disabled Agent oper tables point the GW and DNS routes to pkt0 using NH with policy enabled. However, this is not being updated in vrouter due to an additional check in ksync. Removing the same so that traffic sent to pkt0 will always have flows setup. Change-Id: Icf3fb0be8bda3f8d482db3ecc7356a20d43a18cd closes-bug: #1636574 --- src/vnsw/agent/vrouter/ksync/nexthop_ksync.cc | 17 ++++------------- src/vnsw/agent/vrouter/ksync/nexthop_ksync.h | 1 - 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src/vnsw/agent/vrouter/ksync/nexthop_ksync.cc b/src/vnsw/agent/vrouter/ksync/nexthop_ksync.cc index 51fc1086ab4..58dac8d48bc 100644 --- a/src/vnsw/agent/vrouter/ksync/nexthop_ksync.cc +++ b/src/vnsw/agent/vrouter/ksync/nexthop_ksync.cc @@ -36,7 +36,7 @@ NHKSyncEntry::NHKSyncEntry(NHKSyncObject *obj, const NHKSyncEntry *entry, interface_(entry->interface_), sip_(entry->sip_), dip_(entry->dip_), sport_(entry->sport_), dport_(entry->dport_), smac_(entry->smac_), dmac_(entry->dmac_), valid_(entry->valid_), policy_(entry->policy_), - relaxed_policy_(false), is_mcast_nh_(entry->is_mcast_nh_), + is_mcast_nh_(entry->is_mcast_nh_), defer_(entry->defer_), component_nh_list_(entry->component_nh_list_), nh_(entry->nh_), vlan_tag_(entry->vlan_tag_), is_local_ecmp_nh_(entry->is_local_ecmp_nh_), @@ -52,8 +52,7 @@ NHKSyncEntry::NHKSyncEntry(NHKSyncObject *obj, const NHKSyncEntry *entry, NHKSyncEntry::NHKSyncEntry(NHKSyncObject *obj, const NextHop *nh) : KSyncNetlinkDBEntry(kInvalidIndex), ksync_obj_(obj), type_(nh->GetType()), vrf_id_(0), interface_(NULL), valid_(nh->IsValid()), - policy_(nh->PolicyEnabled()), relaxed_policy_(false), - is_mcast_nh_(false), nh_(nh), + policy_(nh->PolicyEnabled()), is_mcast_nh_(false), nh_(nh), vlan_tag_(VmInterface::kInvalidVlanId), is_bridge_(false), tunnel_type_(TunnelType::INVALID), prefix_len_(32), nh_id_(nh->id()), vxlan_nh_(false), flood_unknown_unicast_(false) { @@ -477,10 +476,6 @@ bool NHKSyncEntry::Sync(DBEntry *e) { (intf_nh->GetInterface()))->tx_vlan_id(); ret = vlan_tag != vlan_tag_; } - if (intf_nh->relaxed_policy() != relaxed_policy_) { - relaxed_policy_ = intf_nh->relaxed_policy(); - ret = true; - } break; } @@ -691,11 +686,7 @@ int NHKSyncEntry::Encode(sandesh_op::type op, char *buf, int buf_len) { } if (policy_) { - if (relaxed_policy_) { - flags |= NH_FLAG_FLOW_LOOKUP; - } else { - flags |= NH_FLAG_POLICY_ENABLED; - } + flags |= NH_FLAG_POLICY_ENABLED; } if_ksync = interface(); @@ -803,7 +794,7 @@ int NHKSyncEntry::Encode(sandesh_op::type op, char *buf, int buf_len) { //resulting flow with key NH of resolve NH //followed by next packet with ARP NH as key //resulting in flow drops - flags &= ~(NH_FLAG_POLICY_ENABLED | NH_FLAG_FLOW_LOOKUP); + flags &= ~NH_FLAG_POLICY_ENABLED; } encoder.set_nhr_type(NH_RESOLVE); break; diff --git a/src/vnsw/agent/vrouter/ksync/nexthop_ksync.h b/src/vnsw/agent/vrouter/ksync/nexthop_ksync.h index acd68c6fb98..57bd5eccc44 100644 --- a/src/vnsw/agent/vrouter/ksync/nexthop_ksync.h +++ b/src/vnsw/agent/vrouter/ksync/nexthop_ksync.h @@ -88,7 +88,6 @@ class NHKSyncEntry : public KSyncNetlinkDBEntry { MacAddress dmac_; bool valid_; bool policy_; - bool relaxed_policy_; bool is_mcast_nh_; bool defer_; KSyncComponentNHList component_nh_list_; From 4f69b38cdeb168323106f3e07b3bc0015854d7a5 Mon Sep 17 00:00:00 2001 From: Hari Prasad Killi Date: Fri, 23 Jun 2017 11:59:26 +0530 Subject: [PATCH 35/68] If IPAM DNS mode is set to none, consider it as default DNS mode. Change-Id: Ia60c141ccdaabef814cc97ccb28be3db7bb6e969 closes-bug: #1699983 --- src/vnsw/agent/services/dhcp_handler_base.cc | 1 + src/vnsw/agent/services/dns_handler.cc | 1 + 2 files changed, 2 insertions(+) diff --git a/src/vnsw/agent/services/dhcp_handler_base.cc b/src/vnsw/agent/services/dhcp_handler_base.cc index 0aa7aa59a80..13a0c130030 100644 --- a/src/vnsw/agent/services/dhcp_handler_base.cc +++ b/src/vnsw/agent/services/dhcp_handler_base.cc @@ -367,6 +367,7 @@ uint16_t DhcpHandlerBase::AddCompressedName(uint16_t opt_len, uint16_t DhcpHandlerBase::AddDnsServers(uint16_t opt_len) { if (ipam_type_.ipam_dns_method == "default-dns-server" || ipam_type_.ipam_dns_method == "virtual-dns-server" || + ipam_type_.ipam_dns_method == "none" || ipam_type_.ipam_dns_method == "") { if (!config_.dns_addr.is_unspecified()) { opt_len = AddIP(opt_len, config_.dns_addr.to_string()); diff --git a/src/vnsw/agent/services/dns_handler.cc b/src/vnsw/agent/services/dns_handler.cc index dccae5683ef..da174f7c228 100644 --- a/src/vnsw/agent/services/dns_handler.cc +++ b/src/vnsw/agent/services/dns_handler.cc @@ -154,6 +154,7 @@ bool DnsHandler::HandleRequest() { } if (ipam_type_.ipam_dns_method == "default-dns-server" || + ipam_type_.ipam_dns_method == "none" || ipam_type_.ipam_dns_method == "") { if (dns_->flags.op == DNS_OPCODE_UPDATE) { DNS_BIND_TRACE(DnsBindError, "Default DNS request : Update received, ignoring"); From a3cec14f470fb6e1fc0dd3342417b8a67f524bd5 Mon Sep 17 00:00:00 2001 From: Atul Moghe Date: Fri, 23 Jun 2017 11:04:44 -0700 Subject: [PATCH 36/68] gw_ip and dns_server_address were stored lower case in api database when request comes for the update where user has entered uppercase gw_ip or dns_server_address, string compare would fail, causing network_update to fail. Modify the validate check to compare lower case strings. Closes-Bug: #1667155 Change-Id: Iae6d0eb2621fc4d960beb0c643f3877a0452dfd1 --- src/config/api-server/vnc_addr_mgmt.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/config/api-server/vnc_addr_mgmt.py b/src/config/api-server/vnc_addr_mgmt.py index b451deedf21..d291751a566 100644 --- a/src/config/api-server/vnc_addr_mgmt.py +++ b/src/config/api-server/vnc_addr_mgmt.py @@ -722,7 +722,8 @@ def net_update_req(self, vn_fq_name, db_vn_dict, req_vn_dict, obj_uuid=None): req_subnet = req_subnet_dicts[key] if key in db_subnet_dicts.keys(): db_subnet = db_subnet_dicts[key] - if (req_subnet['gw'] and req_subnet['gw'] != db_subnet['gw']): + if (req_subnet['gw'] and + req_subnet['gw'].lower() != db_subnet['gw'].lower()): raise AddrMgmtSubnetInvalid(vn_fq_name_str, key) req_subnet['dns_server_address'] = db_subnet['dns_server_address'] @@ -1170,7 +1171,13 @@ def _validate_subnet_update(self, req_subnets, db_subnets): for req_subnet in req_subnets: req_cidr = req_subnet.get('subnet') req_df_gw = req_subnet.get('default_gateway') + if req_df_gw: + req_df_gw = req_df_gw.lower() + req_dns = req_subnet.get('dns_server_address') + if req_dns: + req_dns = req_dns.lower() + for db_subnet in db_subnets: db_cidr = db_subnet.get('subnet') if req_cidr is None or db_cidr is None: @@ -1180,6 +1187,9 @@ def _validate_subnet_update(self, req_subnets, db_subnets): # for a given subnet, default gateway should not be different db_df_gw = db_subnet.get('default_gateway') + if db_df_gw: + db_df_gw = db_df_gw.lower() + db_prefix = db_cidr.get('ip_prefix') db_prefix_len = db_cidr.get('ip_prefix_len') @@ -1206,6 +1216,8 @@ def _validate_subnet_update(self, req_subnets, db_subnets): # for a given subnet, dns server address should not be different db_dns = db_subnet.get('dns_server_address') + if db_dns: + db_dns = db_dns.lower() if db_dns != req_dns: invalid_update = False if ((req_dns is None) and (db_dns != df_dns)): From 89da0f638273b21574c76001c907c74284697103 Mon Sep 17 00:00:00 2001 From: Nischal Sheth Date: Mon, 12 Jun 2017 17:33:22 -0700 Subject: [PATCH 37/68] Handle NULL AsPaths during remove-private processing Change-Id: Ief5a66f266bd6accb7d99e0d06633bf98445e4f7 Closes-Bug: 1697530 (cherry picked from commit cc5e1b9a32306ab85e18cc6bc8264f63b48c8dc3) --- src/bgp/bgp_table.cc | 2 ++ src/bgp/test/bgp_table_export_test.cc | 46 ++++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/bgp/bgp_table.cc b/src/bgp/bgp_table.cc index 81f8fd63179..de41a813f4a 100644 --- a/src/bgp/bgp_table.cc +++ b/src/bgp/bgp_table.cc @@ -136,6 +136,8 @@ void BgpTable::ProcessRemovePrivate(const RibOut *ribout, BgpAttr *attr) const { return; if (!ribout->remove_private_enabled()) return; + if (!attr->as_path()) + return; bool all = ribout->remove_private_all(); bool replace = ribout->remove_private_replace(); diff --git a/src/bgp/test/bgp_table_export_test.cc b/src/bgp/test/bgp_table_export_test.cc index 542751edfe5..a0bbf43a40e 100644 --- a/src/bgp/test/bgp_table_export_test.cc +++ b/src/bgp/test/bgp_table_export_test.cc @@ -867,6 +867,48 @@ TEST_P(BgpTableExportParamTest2, EBgpStripNonTransitive) { VerifyAttrNoClusterList(); } +// +// Table : inet.0, bgp.l3vpn.0 +// Source: iBGP (effectively XMPP since AsPath is NULL) +// RibOut: eBGP +// Intent: Remove private all is handled gracefully when AsPath is empty. +// +TEST_P(BgpTableExportParamTest2, RemovePrivateAll1) { + RibExportPolicy policy( + BgpProto::EBGP, RibExportPolicy::BGP, 300, false, false, -1, 0); + bool all = true; bool replace = false; bool peer_loop_check = true; + policy.SetRemovePrivatePolicy(all, replace, peer_loop_check); + CreateRibOut(policy); + ResetAttrAsPath(); + AddPath(); + RunExport(); + VerifyExportAccept(); + VerifyAttrAsPrepend(); + VerifyAttrAsPathCount(1); + VerifyAttrAsPathAsCount(LocalAsNumber(), 1); +} + +// +// Table : inet.0, bgp.l3vpn.0 +// Source: iBGP +// RibOut: eBGP +// Intent: Remove private all is handled gracefully when AsPath has no +// segments. +// +TEST_P(BgpTableExportParamTest2, RemovePrivateAll2) { + RibExportPolicy policy( + BgpProto::EBGP, RibExportPolicy::BGP, 300, false, false, -1, 0); + bool all = true; bool replace = false; bool peer_loop_check = true; + policy.SetRemovePrivatePolicy(all, replace, peer_loop_check); + CreateRibOut(policy); + AddPath(); + RunExport(); + VerifyExportAccept(); + VerifyAttrAsPrepend(); + VerifyAttrAsPathCount(1); + VerifyAttrAsPathAsCount(LocalAsNumber(), 1); +} + INSTANTIATE_TEST_CASE_P(Instance, BgpTableExportParamTest2, ::testing::Values("inet.0", "bgp.l3vpn.0")); @@ -1110,8 +1152,6 @@ TEST_P(BgpTableExportParamTest3, RemovePrivateAllReplace1) { // Table : inet.0, bgp.l3vpn.0 // Source: eBGP // RibOut: iBGP -// Intent: Remove private all (w/ replace) replaces all private ASes. -// first public AS. // Intent: Remove private all (w/ replace) replaces all private ASes. All // private ASes up to the first public AS are replaced with the // leftmost public as. Remaining private ASes are replaced with the @@ -1140,8 +1180,6 @@ TEST_P(BgpTableExportParamTest3, RemovePrivateAllReplace2) { // Table : inet.0, bgp.l3vpn.0 // Source: eBGP // RibOut: iBGP -// Intent: Remove private all (w/ replace) replaces all private ASes. -// first public AS. // Intent: Remove private all (w/ replace) replaces all private ASes. All // private ASes up to the first public AS are replaced with the // leftmost public as. Remaining private ASes are replaced with the From 6a73386dcda70f9ae8cb780e7e23bc7501bbc362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Jedin=C3=BD?= Date: Tue, 27 Jun 2017 10:17:31 +0200 Subject: [PATCH 38/68] Fix messages for contrail exceptions _() was wrongly used for contrail exception messages in neutron_plugin_db. It is not imported nor the translations are available, so it was raising different exception without meaningful message. Change-Id: I07a20c66980deda0b053beaa691d9ea8e4c75d9b Partial-Bug: #1700622 --- .../vnc_openstack/neutron_plugin_db.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py b/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py index d9226f696e2..aed1b91af47 100644 --- a/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py +++ b/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py @@ -276,7 +276,7 @@ def _get_tenant_id_for_create(self, context, resource): tenant_id = resource['tenant_id'] elif ('tenant_id' in resource and resource['tenant_id'] != context['tenant_id']): - reason = _('Cannot create resource for another tenant') + reason = 'Cannot create resource for another tenant' self._raise_contrail_exception('AdminRequired', reason=reason) else: tenant_id = context['tenant_id'] @@ -1386,7 +1386,7 @@ def _subnet_neutron_to_vnc(self, subnet_q): self._raise_contrail_exception('BadRequest', resource='subnet', msg='Unknown IP family') elif cidr.version != int(subnet_q['ip_version']): - msg = _("cidr '%s' does not match the ip_version '%s'") \ + msg = "cidr '%s' does not match the ip_version '%s'" \ %(subnet_q['cidr'], subnet_q['ip_version']) self._raise_contrail_exception('InvalidInput', error_message=msg) if 'gateway_ip' in subnet_q: @@ -2801,8 +2801,8 @@ def subnet_create(self, subnet_q): if subnet_prefix == self._subnet_vnc_get_prefix(subnet): existing_sn_id = subnet.subnet_uuid # duplicate !! - msg = _("Cidr %s overlaps with another subnet of subnet %s" - ) % (subnet_q['cidr'], existing_sn_id) + msg = "Cidr %s overlaps with another subnet of subnet %s" \ + % (subnet_q['cidr'], existing_sn_id) self._raise_contrail_exception('BadRequest', resource='subnet', msg=msg) vnsn_data = net_ipam_ref['attr'] @@ -3411,8 +3411,8 @@ def _check_for_dup_router_subnet(self, router_id, for p in rports: for ip in p['fixed_ips']: if ip['subnet_id'] == subnet_id: - msg = (_("Router %s already has a port " - "on subnet %s") % (router_id, subnet_id)) + msg = "Router %s already has a port " \ + "on subnet %s" % (router_id, subnet_id) self._raise_contrail_exception( 'BadRequest', resource='router', msg=msg) sub_id = ip['subnet_id'] @@ -3426,9 +3426,9 @@ def _check_for_dup_router_subnet(self, router_id, 'subnet_id': subnet_id, 'cidr': cidr, 'sub_id': sub_id} - msg = (_("Cidr %(subnet_cidr)s of subnet " - "%(subnet_id)s overlaps with cidr %(cidr)s " - "of subnet %(sub_id)s") % data) + msg = "Cidr %(subnet_cidr)s of subnet " \ + "%(subnet_id)s overlaps with cidr %(cidr)s " \ + "of subnet %(sub_id)s" % data self._raise_contrail_exception( 'BadRequest', resource='router', msg=msg) except NoIdError: @@ -3539,7 +3539,7 @@ def remove_router_interface(self, router_id, port_id=None, subnet_id=None): if subnet_id == port_db['fixed_ips'][0]['subnet_id']: break else: - msg = _('Subnet %s not connected to router %s') % (subnet_id, + msg = 'Subnet %s not connected to router %s' % (subnet_id, router_id) self._raise_contrail_exception('BadRequest', resource='router', msg=msg) From 079d4ea44cad5948bda1cf23505f06380910efa7 Mon Sep 17 00:00:00 2001 From: Megh Bhatt Date: Wed, 24 May 2017 00:34:03 -0700 Subject: [PATCH 39/68] Fix ZooDelete in zookeeper client Fix ZooDelete in zookeeper client to retry recoverable errors Closes-Bug #1692453 Change-Id: If9a9d10d8675acd181aeb2f8436c1aa944cbb13f (cherry picked from commit 97b9d19cbe5abb90cb5c143147de49fd36622f37) --- src/zookeeper/test/zookeeper_client_test.cc | 29 +++++++++++++++++++++ src/zookeeper/zookeeper_client.cc | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/zookeeper/test/zookeeper_client_test.cc b/src/zookeeper/test/zookeeper_client_test.cc index ee83de5b087..39c3d495b89 100644 --- a/src/zookeeper/test/zookeeper_client_test.cc +++ b/src/zookeeper/test/zookeeper_client_test.cc @@ -212,6 +212,35 @@ TEST_F(ZookeeperClientTest, ZooCreateUnrecoverableError) { EXPECT_FALSE(cImpl->IsConnected()); } +TEST_F(ZookeeperClientTest, ZooDeleteRecoverableError) { + ZookeeperMockInterface *zmi(new ZookeeperMockInterface); + EXPECT_CALL(*zmi, ZooSetDebugLevel(_)); + impl::ZookeeperClientImpl *cImpl( + new impl::ZookeeperClientImpl("Test", "127.0.0.1:2181", zmi)); + std::auto_ptr client(CreateClient(cImpl)); + std::string zk_lock_name("/test-lock"); + ZookeeperLock zk_lock(client.get(), zk_lock_name.c_str()); + std::string zk_lock_id(GetLockId(zk_lock)); + int zkh(0xdeadbeef); + zhandle_t *zk_handle = (zhandle_t *)(&zkh); + EXPECT_CALL(*zmi, ZookeeperInit(StrEq("127.0.0.1:2181"), _, _, _, _, _)) + .WillOnce(Return(zk_handle)); + EXPECT_CALL(*zmi, ZooState(zk_handle)) + .WillOnce(Return(ZOO_CONNECTED_STATE)); + EXPECT_CALL(*zmi, ZooCreate(zk_handle, StrEq(zk_lock_name), + StrEq(zk_lock_id), zk_lock_id.length(), _, _, _, _)) + .WillOnce(Return(ZOK)); + EXPECT_TRUE(zk_lock.Lock()); + EXPECT_TRUE(cImpl->IsConnected()); + EXPECT_CALL(*zmi, ZooDelete(zk_handle, StrEq(zk_lock_name), _)) + .WillOnce(Return(ZCONNECTIONLOSS)) + .WillOnce(Return(ZOPERATIONTIMEOUT)) + .WillOnce(Return(ZOK)); + EXPECT_CALL(*zmi, ZookeeperClose(zk_handle)); + EXPECT_TRUE(zk_lock.Release()); + EXPECT_FALSE(cImpl->IsConnected()); +} + int main(int argc, char **argv) { LoggingInit(); ::testing::InitGoogleTest(&argc, argv); diff --git a/src/zookeeper/zookeeper_client.cc b/src/zookeeper/zookeeper_client.cc index 2a83698b08c..15c3b28cf6b 100644 --- a/src/zookeeper/zookeeper_client.cc +++ b/src/zookeeper/zookeeper_client.cc @@ -190,7 +190,7 @@ int ZookeeperClientImpl::DeleteNodeSync(const char *path, int *err) { retry: do { rc = zki_->ZooDelete(zk_handle_, path, -1); - } while (IsZooErrorUnrecoverable(rc)); + } while (IsZooErrorRecoverable(rc)); if (IsZooErrorUnrecoverable(rc)) { // Reconnect Reconnect(); From 8378c5af47ca83e050c4d72833b0c5f4e73a3a40 Mon Sep 17 00:00:00 2001 From: Vinay Vithal Mahuli Date: Mon, 3 Jul 2017 02:30:46 -0700 Subject: [PATCH 40/68] update version.info Change-Id: Ia9abb2540ebbf32d3e9e142a9ebfd7f54269628a --- src/base/version.info | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/base/version.info b/src/base/version.info index dd152b305b8..cee734990f8 100644 --- a/src/base/version.info +++ b/src/base/version.info @@ -1 +1 @@ -3.2.3.2 +3.2.3.3 From 7933bca348a56fff4bf46fdc5b550746ca47ff2d Mon Sep 17 00:00:00 2001 From: Megh Bhatt Date: Wed, 2 Aug 2017 11:29:30 -0700 Subject: [PATCH 41/68] Disable opserver:test temporarily from running in CI Change-Id: I0453a360b2f2708b1e48a93e35ddc4d241bf89db Closes-Bug: #1708239 --- ci_unittests.json | 1 - 1 file changed, 1 deletion(-) diff --git a/ci_unittests.json b/ci_unittests.json index 06a35516319..69ef78ff12c 100644 --- a/ci_unittests.json +++ b/ci_unittests.json @@ -10,7 +10,6 @@ ], "scons_test_targets" : [ "controller/src/analytics:test", - "controller/src/opserver:test", "controller/src/bfd:test", "controller/src/query_engine:test", "controller/src/database/gendb:test", From 1dcb361d7ed7977fed79023e13935b39e2ce871c Mon Sep 17 00:00:00 2001 From: arvindvis Date: Thu, 22 Jun 2017 19:07:54 -0700 Subject: [PATCH 42/68] Backport cql_if changes needed for newer version of libcassandra This fix adds a new API cass_value_is_null to our CQL library so that we can check if a value is null or not before trying to decode the datatype and readh the column value. Re-enable opserver:test Closes-Bug: #1699934 Conflicts: src/database/cassandra/cql/cql_if.cc src/database/cassandra/cql/cql_lib_if.h (cherry picked from commit 5b2442c037713523d3cdfc6b2e0aebe678db93f3) (cherry picked from commit 1f6331a21eeb653c0387f16d6a4ca4c9cb972514) Change-Id: I884f4c275c7cb92b19884d4cc4d45f9cd3a2c8d4 --- ci_unittests.json | 1 + src/database/cassandra/cql/cql_if.cc | 7 +++++++ src/database/cassandra/cql/cql_lib_if.h | 2 ++ 3 files changed, 10 insertions(+) diff --git a/ci_unittests.json b/ci_unittests.json index 69ef78ff12c..d9ad4d4d18b 100644 --- a/ci_unittests.json +++ b/ci_unittests.json @@ -11,6 +11,7 @@ "scons_test_targets" : [ "controller/src/analytics:test", "controller/src/bfd:test", + "controller/src/opserver:test", "controller/src/query_engine:test", "controller/src/database/gendb:test", "controller/src/database/cassandra/cql:test", diff --git a/src/database/cassandra/cql/cql_if.cc b/src/database/cassandra/cql/cql_if.cc index 7dff0de5031..dce9a4e4db7 100644 --- a/src/database/cassandra/cql/cql_if.cc +++ b/src/database/cassandra/cql/cql_if.cc @@ -783,6 +783,9 @@ std::string PartitionKeyAndClusteringKeyRange2CassSelectFromTable( static GenDb::DbDataValue CassValue2DbDataValue( interface::CassLibrary *cci, const CassValue *cvalue) { + if (cci->CassValueIsNull(cvalue)) { + return GenDb::DbDataValue(); + } CassValueType cvtype(cci->GetCassValueType(cvalue)); switch (cvtype) { case CASS_VALUE_TYPE_ASCII: @@ -2442,6 +2445,10 @@ CassValueType CassDatastaxLibrary::GetCassValueType(const CassValue* value) { return cass_value_type(value); } +cass_bool_t CassDatastaxLibrary::CassValueIsNull(const CassValue* value) { + return cass_value_is_null(value); +} + CassError CassDatastaxLibrary::CassValueGetString(const CassValue* value, const char** output, size_t* output_size) { return cass_value_get_string(value, output, output_size); diff --git a/src/database/cassandra/cql/cql_lib_if.h b/src/database/cassandra/cql/cql_lib_if.h index b094aad5d4a..54ed6ecef05 100644 --- a/src/database/cassandra/cql/cql_lib_if.h +++ b/src/database/cassandra/cql/cql_lib_if.h @@ -124,6 +124,7 @@ class CassLibrary { // CassValue virtual CassValueType GetCassValueType(const CassValue* value) = 0; + virtual cass_bool_t CassValueIsNull(const CassValue* value) = 0; virtual CassError CassValueGetString(const CassValue* value, const char** output, size_t* output_size) = 0; virtual CassError CassValueGetInt8(const CassValue* value, @@ -268,6 +269,7 @@ class CassDatastaxLibrary : public CassLibrary { // CassValue virtual CassValueType GetCassValueType(const CassValue* value); + virtual cass_bool_t CassValueIsNull(const CassValue* value); virtual CassError CassValueGetString(const CassValue* value, const char** output, size_t* output_size); virtual CassError CassValueGetInt8(const CassValue* value, From bba3d9c87beb51b00e482eeb8a0f51e2d7916a50 Mon Sep 17 00:00:00 2001 From: Naveen N Date: Wed, 4 Oct 2017 16:08:26 +0530 Subject: [PATCH 43/68] * Read reverse flow entry after acquiring the lock In case of short lived TCP session using same 5 tuple we might end up accesssing a reverse flow entry which might be already deleted and present in free list. Below set of events lead to this issue 1. F1 and R1 flow are added to flow table 2. R1 is written to vrouter 3. F1 is written to vrouter 4. R1 flow add response is received, triggering update of F1(not needed now as reverse flow index is not written to kernel?) 5. In the meantime flow is evicted in vrouter, hence flow update for F1 would result in error from vrouter resulting in short flow 6. Since F1 is shortflow Flow delete gets enqueued 7. Since R1 is evict marked, flow evict gets enqueued 8. Both event F1 and R1 delete and evict event can run in parallel,and hence reverse flow pointer obtained before FLOW lock could be invalid, hence read back the same Was able to simulate the same in UT, UT is disabled as it need some instrumentation to run it to hit the issue Change-Id: I368f465f9d446b43dfe0e4baa547d8c8cbfd6840 Closes-bug: #1714371 --- src/vnsw/agent/pkt/flow_table.cc | 15 ++++++++ src/vnsw/agent/pkt/test/test_ecmp_mx.cc | 50 +++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/src/vnsw/agent/pkt/flow_table.cc b/src/vnsw/agent/pkt/flow_table.cc index 8cbc4bd6b46..e6806693434 100644 --- a/src/vnsw/agent/pkt/flow_table.cc +++ b/src/vnsw/agent/pkt/flow_table.cc @@ -778,6 +778,21 @@ bool FlowTable::ProcessFlowEvent(const FlowEvent *req, FlowEntry *flow, //Now process events. switch (req->event()) { case FlowEvent::DELETE_FLOW: { + //In case of continous stream of short lived TCP flows with same 5 tuple, + //flow eviction logic might cause below set of event + //1> F1 and R1 flow are added to flow table + //2> R1 is written to vrouter + //3> F1 is written to vrouter + //4> R1 flow add response is received, triggering update of + // F1(not needed now as reverse flow index is not written to kernel?) + //5> In the meantime flow is evicted in vrouter, hence flow update for F1 + // would result in error from vrouter resulting in short flow + //6> Since F1 is shortflow Flow delete gets enqueued + //7> Since R1 is evict marked, flow evict gets enqueued + //8> Both event F1 and R1 delete and evict event can run in parallel, + // and hence reverse flow pointer obtained before FLOW lock could + // be invalid, hence read back the same + rflow = flow->reverse_flow_entry(); DeleteUnLocked(true, flow, rflow); break; } diff --git a/src/vnsw/agent/pkt/test/test_ecmp_mx.cc b/src/vnsw/agent/pkt/test/test_ecmp_mx.cc index a400f0f7c3e..984a1fe04e1 100644 --- a/src/vnsw/agent/pkt/test/test_ecmp_mx.cc +++ b/src/vnsw/agent/pkt/test/test_ecmp_mx.cc @@ -6,6 +6,7 @@ #include "test/test_cmn_util.h" #include "test_pkt_util.h" #include "pkt/flow_proto.h" +#include "ksync/ksync_sock_user.h" #define AGE_TIME 10*1000 @@ -445,6 +446,55 @@ TEST_F(EcmpTest, EcmpTest_8) { WAIT_FOR(1000, 1000, (get_flow_proto()->FlowCount() == 0)); } +//Test to simulate continous stream of flow packets getting evicted +//To run the test, please modify KSyncUserSockFlowContext::Process +//to return same flow handle and incremental gen_id +// +//+ static uint8_t gen_id = 0; +//+ fwd_flow_idx = 100; +// req_->set_fr_index(fwd_flow_idx); +//- req_->set_fr_gen_id((fwd_flow_idx % 255)); +//+ req_->set_fr_gen_id(gen_id++); +// +//and to pass ttl as gen id in below API +#if 0 +TEST_F(EcmpTest, TEST_1) { + Ip4Address server_ip = Ip4Address::from_string("10.1.1.3"); + Ip4Address zero = Ip4Address::from_string("0.0.0.0"); + TunnelType::TypeBmap bmap = (1 << TunnelType::MPLS_GRE); + PathPreference path_preference(0, PathPreference::HIGH, false, false); + Inet4TunnelRouteAdd(bgp_peer, "vrf1", zero, 0, server_ip, bmap, + 16, "vn1", SecurityGroupList(), path_preference); + client->WaitForIdle(); + + uint32_t gen_id = 0; + for (uint32_t i = 0; i < 10000; i++) { + gen_id++; + TxTcpPacket(VmPortGetId(1), "1.1.1.1", "2.1.1.1", + 10, 15, false, 1, VrfGet("vrf1")->vrf_id(), gen_id); + client->WaitForIdle(); + + FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), + "1.1.1.1", "2.1.1.1", 6, 10, 15, + GetFlowKeyNH(1)); + client->WaitForIdle(); + + if (entry) { + entry->MakeShortFlow(FlowEntry::SHORT_FAILED_VROUTER_INSTALL); + KSyncSockTypeMap::SetEvictedFlag(entry->reverse_flow_entry()-> + flow_handle()); + } + client->WaitForIdle(); + + if (gen_id % 10 == 0) { + WAIT_FOR(1000, 1000, (flow_proto_->FlowCount() == 0)); + } + } + + WAIT_FOR(1000, 1000, (flow_proto_->FlowCount() == 0)); +} +#endif + int main(int argc, char *argv[]) { GETUSERARGS(); client = TestInit(init_file, ksync_init, true, true, true, 100*1000); From 207fec18b4bc43bf07f91834cdbcebc3c4cf4a48 Mon Sep 17 00:00:00 2001 From: arvindvis Date: Sat, 26 Aug 2017 14:28:17 -0700 Subject: [PATCH 44/68] Add new libraries to be linked to the collector the new librdkafka requires collector to be linked against these new libraries. Closes-Bug:#1711833 Change-Id: Ie4f6e06372be6292a9834795409a09d7aea02990 --- src/analytics/SConscript | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/analytics/SConscript b/src/analytics/SConscript index 0f04a2d07bd..18cea4534ba 100644 --- a/src/analytics/SConscript +++ b/src/analytics/SConscript @@ -61,11 +61,13 @@ AnalyticsEnv.Prepend(LIBS=['cpuinfo', 'ssl', 'crypto', 'boost_filesystem', - 'boost_program_options']) + 'boost_program_options', + 'libdl', + 'sasl2']) (PLATFORM, VERSION, EXTRA) = platform.linux_distribution() -if PLATFORM.lower() != 'ubuntu': - AnalyticsEnv.Prepend(LIBS=['sasl2']) +if PLATFORM.lower() != 'ubuntu' or (PLATFORM.lower() == 'ubuntu' and VERSION.find('16.') == 0): + AnalyticsEnv.Prepend(LIBS=['lz4']) database_libs = ['cassandra_cql', 'cassandra'] From dae4afd6f5cc0ad35fe0a23bdb81f6b551fe7418 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Jedin=C3=BD?= Date: Wed, 11 Oct 2017 17:25:33 +0200 Subject: [PATCH 45/68] Fix subnet re-creation Avoid using old cached subnet when re-creating subnet with same CIDR but different gateway setting. Change-Id: I51b25c946b75a72fb0139e9250f319389588b380 Closes-Bug: #1722837 --- src/config/api-server/vnc_addr_mgmt.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/config/api-server/vnc_addr_mgmt.py b/src/config/api-server/vnc_addr_mgmt.py index d291751a566..0e50a5ab155 100644 --- a/src/config/api-server/vnc_addr_mgmt.py +++ b/src/config/api-server/vnc_addr_mgmt.py @@ -707,6 +707,8 @@ def net_update_req(self, vn_fq_name, db_vn_dict, req_vn_dict, obj_uuid=None): req_subnet_names = set(req_subnet_dicts.keys()) del_subnet_names = db_subnet_names - req_subnet_names + add_subnet_names = req_subnet_names - db_subnet_names + for subnet_name in del_subnet_names: Subnet.delete_cls('%s:%s' % (vn_fq_name_str, subnet_name)) try: @@ -714,6 +716,16 @@ def net_update_req(self, vn_fq_name, db_vn_dict, req_vn_dict, obj_uuid=None): except KeyError: pass + # Remove stale entries in _subnet_objs + vn_subnet_objs = self._subnet_objs.get(vn_uuid) + if vn_subnet_objs: + for subnet_name in add_subnet_names: + subnet_obj = vn_subnet_objs.pop(subnet_name, None) + if subnet_obj is not None: + msg = "Removing stale subnet entry: %s:%s" % ( + vn_fq_name_str, subnet_name,) + self.config_log(msg, level=SandeshLevel.SYS_WARN) + # check db_subnet_dicts and req_subnet_dicts # following parameters are same for subnets present in both dicts # default_gateway, dns_server_address @@ -1802,6 +1814,8 @@ def ipam_update_req(self, ipam_fq_name, db_ipam_dict, req_ipam_dict, req_subnet_names = set(req_subnet_dicts.keys()) del_subnet_names = db_subnet_names - req_subnet_names + add_subnet_names = req_subnet_names - db_subnet_names + for subnet_name in del_subnet_names: Subnet.delete_cls('%s:%s' % (ipam_fq_name_str, subnet_name)) try: @@ -1809,6 +1823,16 @@ def ipam_update_req(self, ipam_fq_name, db_ipam_dict, req_ipam_dict, except KeyError: pass + # Remove stale entries in _subnet_objs + ipam_subnet_objs = self._subnet_objs.get(obj_uuid) + if ipam_subnet_objs: + for subnet_name in add_subnet_names: + subnet_obj = ipam_subnet_objs.pop(subnet_name, None) + if subnet_obj is not None: + msg = "Removing stale ipam subnet entry: %s:%s" % ( + ipam_fq_name_str, subnet_name,) + self.config_log(msg, level=SandeshLevel.SYS_WARN) + # check db_subnet_dicts and req_subnet_dicts # following parameters are same for subnets present in both dicts # default_gateway,dns_server_address From fa5e0dd0b7a92fd37c810f35bad837e80c43decb Mon Sep 17 00:00:00 2001 From: Andrey Shestakov Date: Thu, 31 Aug 2017 13:36:58 +0300 Subject: [PATCH 46/68] Allow filter ports by mac_address This change allows filter port list by mac_address field. Closes-Bug: 1718447 Change-Id: If2425f8553701440eb4fedd0b391268752b237ed (cherry picked from commit e437fdeef657c34269fef594e896f867a50d129c) --- src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py b/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py index aed1b91af47..7416a7bdbdc 100644 --- a/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py +++ b/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py @@ -4106,6 +4106,9 @@ def port_list(self, context=None, filters=None): not self._port_fixed_ips_is_present(filters['fixed_ips'], neutron_port['fixed_ips'])): continue + if not self._filters_is_present(filters, 'mac_address', + neutron_port['mac_address']): + continue ret_list.append(neutron_port) return ret_list From 84e20ac021091738c3eb213745105f47c3d5616a Mon Sep 17 00:00:00 2001 From: Andrey Shestakov Date: Thu, 31 Aug 2017 13:43:30 +0300 Subject: [PATCH 47/68] Allow to update vnic_type when port is not bound Updating vnic type is legal procedure, and is used by Ironic. This patch allows to update vnic type when port is not bound (doesn't have references to VM or Vrouter) Closes-Bug: 1719268 Change-Id: Ic7b6a7232316c698b2f8521a9b3d46fb755d4e3d (cherry picked from commit 3ef7d84b3fdd3cae204931fb5e4c695b630f2a42) --- src/config/api-server/vnc_cfg_types.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/config/api-server/vnc_cfg_types.py b/src/config/api-server/vnc_cfg_types.py index 949525624e2..18208c1f6f6 100644 --- a/src/config/api-server/vnc_cfg_types.py +++ b/src/config/api-server/vnc_cfg_types.py @@ -714,6 +714,19 @@ def _check_port_security_and_address_pairs(cls, obj_dict, db_dict={}): return True, "" + @classmethod + def _is_port_bound(cls, obj_dict): + """Check whatever port is bound. + + We assume port is bound when it is linked to either VM or Vrouter. + + :param obj_dict: Port dict to check + :returns: True if port is bound, False otherwise. + """ + + return (obj_dict.get('logical_router_back_refs') or + obj_dict.get('virtual_machine_refs')) + @classmethod def pre_dbe_create(cls, tenant_name, obj_dict, db_conn): vn_dict = obj_dict['virtual_network_refs'][0] @@ -862,8 +875,10 @@ def pre_dbe_update(cls, id, fq_name, obj_dict, db_conn, if kvps: kvp_dict = cls._kvp_to_dict(kvps) new_vnic_type = kvp_dict.get('vnic_type', old_vnic_type) - if (old_vnic_type != new_vnic_type): - return (False, (409, "Vnic_type can not be modified")) + if (old_vnic_type != new_vnic_type): + if cls._is_port_bound(read_result): + return (False, (409, "Vnic_type can not be modified when " + "port is linked to Vrouter or VM.")) if old_vnic_type == cls.portbindings['VNIC_TYPE_DIRECT']: cls._check_vrouter_link(read_result, kvp_dict, obj_dict, db_conn) From ed6264e30ea7cb2e2c9155eb76aa1410d3ed77d8 Mon Sep 17 00:00:00 2001 From: Anish Mehta Date: Fri, 10 Nov 2017 22:21:32 -0800 Subject: [PATCH 48/68] Adding timestamp to UVE structs when they are aggregated and evaluated in alarmgen. Closes-Bug: #1697745 Change-Id: I1861c2415f05f0cd71d52172f422afff15abc821 Conflicts: src/opserver/alarmgen.py Change-Id: I1861c2415f05f0cd71d52172f422afff15abc821 Conflicts: src/opserver/alarmgen.py Change-Id: I1861c2415f05f0cd71d52172f422afff15abc821 --- src/opserver/alarmgen.py | 21 +++++++++++++------ src/opserver/test/test_alarm.py | 10 +++++++-- src/opserver/test/utils/analytics_fixture.py | 22 +++++--------------- 3 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/opserver/alarmgen.py b/src/opserver/alarmgen.py index af82a978372..e53c2e8aced 100644 --- a/src/opserver/alarmgen.py +++ b/src/opserver/alarmgen.py @@ -1810,12 +1810,21 @@ def handle_uve_notif(self, part, uves): # Examine UVE to check if alarm need to be raised/deleted self.examine_uve_for_alarms(uv, local_uve) if success: - uveq_trace = UVEQTrace() - uveq_trace.uves = output.keys() - uveq_trace.part = part - uveq_trace.oper = "proc-output" - uveq_trace.trace_msg(name="UVEQTrace",\ - sandesh=self._sandesh) + uts = UTCTimestampUsec() + uveq_trace = UVEQTrace() + uveq_trace.uves = [] + for k,v in output.iteritems(): + if isinstance(v,dict): + uveq_trace.uves.append(str((k,v.keys()))) + for ut in v: + if isinstance(v[ut],dict): + v[ut]["__T"] = uts + else: + uveq_trace.uves.append(str((k,None))) + uveq_trace.part = part + uveq_trace.oper = "proc-output" + uveq_trace.trace_msg(name="UVEQTrace",\ + sandesh=self._sandesh) self._logger.info("Ending UVE proc for part %d" % part) return output else: diff --git a/src/opserver/test/test_alarm.py b/src/opserver/test/test_alarm.py index 7030245b1f2..512c7a04aaf 100755 --- a/src/opserver/test/test_alarm.py +++ b/src/opserver/test/test_alarm.py @@ -67,6 +67,10 @@ def checker_dict(self,expected,actual,match=True): @retry(delay=1, tries=3) def checker_exact(self,expected,actual,match=True): result = False + for tk in actual: + if isinstance(actual[tk],dict): + if "__T" in actual[tk]: + del actual[tk]["__T"] logging.info("exact exp %s actual %s match %s" % \ (str(expected), str(actual), str(match))) if expected == actual: @@ -391,7 +395,8 @@ def test_00_init(self, self._ag.libpart_cb([1]) self.assertTrue(self.checker_dict([1, "ObjectXX", "uve1"], self._ag.ptab_info)) self.assertTrue(self.checker_exact(\ - self._ag.ptab_info[1]["ObjectXX"]["uve1"].values(), {"type1" : {"xx": 0}})) + {"type1" : {"xx": 0}}, + self._ag.ptab_info[1]["ObjectXX"]["uve1"].values())) # Shutdown partition self._ag.libpart_cb([]) @@ -436,7 +441,8 @@ def test_01_rxmsg(self, self.assertTrue(self.checker_dict([1, "ObjectXX", "uve1"], self._ag.ptab_info, False)) self.assertTrue(self.checker_dict([1, "ObjectYY", "uve2"], self._ag.ptab_info)) self.assertTrue(self.checker_exact(\ - self._ag.ptab_info[1]["ObjectYY"]["uve2"].values(), {"type2" : {"yy": 1}})) + {"type2" : {"yy": 1}}, + self._ag.ptab_info[1]["ObjectYY"]["uve2"].values())) @mock.patch('opserver.alarmgen.Controller.reconnect_agg_uve') @mock.patch('opserver.alarmgen.Controller.clear_agg_uve') diff --git a/src/opserver/test/utils/analytics_fixture.py b/src/opserver/test/utils/analytics_fixture.py index a24e89e2efe..dd491e78023 100644 --- a/src/opserver/test/utils/analytics_fixture.py +++ b/src/opserver/test/utils/analytics_fixture.py @@ -2483,6 +2483,11 @@ def _verify_uves(self, exp_uves, actual_uves): self.logger.info('Remove token from alarms') self._remove_alarm_token(exp_uve_value) self._remove_alarm_token(actual_uve_value) + self.logger.info('Remove Timestamps from actual') + for item in actual_uve_value: + for tk in item['value']: + if '__T' in item['value'][tk]: + del item['value'][tk]['__T'] exp_uve_value.sort() actual_uve_value.sort() self.logger.info('Expected UVE value: %s' % (str(exp_uve_value))) @@ -2589,23 +2594,6 @@ def verify_alarm_list_exclude(self, table, filts=None, unexpected_alms=[]): return len(actual_alarms) == 0 # end verify_alarm_list_exclude - def _verify_alarms(self, exp_alarms, actual_alarms): - self.logger.info('Expected Alarms: %s' % (str(exp_alarms))) - self.logger.info('Actual Alarms: %s' % (str(actual_alarms))) - if actual_alarms is None: - return False - exp_alarm_value = exp_alarms['value'] - actual_alarm_value = actual_alarms['value'] - self.logger.info('Remove token from alarms') - self._remove_alarm_token(exp_alarm_value) - self._remove_alarm_token(actual_alarm_value) - exp_alarm_value.sort() - actual_alarm_value.sort() - self.logger.info('Expected Alarm value: %s' % (str(exp_alarm_value))) - self.logger.info('Actual Alarm value: %s' % (str(actual_alarm_value))) - return actual_alarm_value == exp_alarm_value - # end _verify_alarms - @retry(delay=1, tries=3) def verify_alarm(self, table, key, expected_alarm): self.logger.info('verify_alarm: %s:%s' % (table, key)) From 158da92f29af86e5345d79a51e864271dbb9594f Mon Sep 17 00:00:00 2001 From: Hari Prasad Killi Date: Tue, 24 Oct 2017 20:35:17 +0530 Subject: [PATCH 49/68] Do not re-evaluate backrefs for ksync flow entries The backref entries are updated in DB and ksync tasks, which are mutually exclusive. The flow event task was accessing the same and it doesnt actually require. Avoiding backref reevaluation. Change-Id: I8cb9b92196626ea219f7f9e1055e2579c62c7c86 closes-bug: #1726709 (cherry picked from commit ef13c6b5ec0250df8bf7be0241a240c799e6b86f) --- src/ksync/ksync_entry.h | 1 + src/ksync/ksync_object.cc | 3 ++- src/vnsw/agent/vrouter/ksync/flowtable_ksync.h | 5 +++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ksync/ksync_entry.h b/src/ksync/ksync_entry.h index f3dcb3e7c1c..146fe43961d 100644 --- a/src/ksync/ksync_entry.h +++ b/src/ksync/ksync_entry.h @@ -121,6 +121,7 @@ class KSyncEntry { // Get an unresolved reference. // This entry will be added into resolveq_ of unresolved-entry virtual KSyncEntry *UnresolvedReference() = 0; + virtual bool ShouldReEvalBackReference() const { return true; } // Returns true if entry is resolved and referring entry can be written bool IsResolved(); diff --git a/src/ksync/ksync_object.cc b/src/ksync/ksync_object.cc index 5b1bbfbd4e1..c1fdb7ebb0a 100644 --- a/src/ksync/ksync_object.cc +++ b/src/ksync/ksync_object.cc @@ -1398,7 +1398,8 @@ void KSyncObject::NotifyEvent(KSyncEntry *entry, KSyncEntry::KSyncEvent event) { } entry->SetState(state); - if (dep_reval == true && entry->IsResolved()) { + if (dep_reval == true && entry->IsResolved() && + entry->ShouldReEvalBackReference()) { BackRefReEval(entry); } diff --git a/src/vnsw/agent/vrouter/ksync/flowtable_ksync.h b/src/vnsw/agent/vrouter/ksync/flowtable_ksync.h index 9ed3765c879..59212b70ac6 100644 --- a/src/vnsw/agent/vrouter/ksync/flowtable_ksync.h +++ b/src/vnsw/agent/vrouter/ksync/flowtable_ksync.h @@ -77,6 +77,11 @@ class FlowTableKSyncEntry : public KSyncNetlinkEntry { virtual uint32_t GetTableIndex() const; virtual bool Sync(); virtual KSyncEntry *UnresolvedReference(); + virtual bool ShouldReEvalBackReference() const { + // no need to re-eval unresolved reference for flow entries, as they + // do not depend on anything + return false; + } bool AllowDeleteStateComp() {return false;} virtual void ErrorHandler(int, uint32_t, KSyncEvent) const; virtual std::string VrouterError(uint32_t error) const; From aa81a95521d9321f881180c9b20b3ade4a658dc5 Mon Sep 17 00:00:00 2001 From: Hari Prasad Killi Date: Wed, 27 Sep 2017 16:25:46 +0530 Subject: [PATCH 50/68] Add task exclusion between agent profile & sandesh tasks Agent Profile task is updating maps in the profile data (like profile_stats_table_) while sandesh is fetching the same and iterating them, resulting in crash. Change-Id: I0503035340fd505610e8114f5341c5289ecdda12 closes-bug: #1719840 (cherry picked from commit 540592cc8c81918fe7e70f38d5d39d9269945301) --- src/vnsw/agent/cmn/agent.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vnsw/agent/cmn/agent.cc b/src/vnsw/agent/cmn/agent.cc index c89ff6f9283..308e26a8d07 100644 --- a/src/vnsw/agent/cmn/agent.cc +++ b/src/vnsw/agent/cmn/agent.cc @@ -187,6 +187,7 @@ void Agent::SetAgentTaskPolicy() { "Agent::StatsCollector", "io::ReaderTask", "Agent::PktFlowResponder", + "Agent::Profile", AGENT_SHUTDOWN_TASKNAME, AGENT_INIT_TASKNAME, AGENT_SANDESH_TASKNAME From f3b3049e42d744b7db1332cd4ced647174337e75 Mon Sep 17 00:00:00 2001 From: Naveen N Date: Tue, 26 Sep 2017 14:47:11 +0530 Subject: [PATCH 51/68] * Initialize flow entry variable in pkt flow info. Flow index passed in ECMP resolve trap could be -1 resulting in no flow entry being present in agent. In such case agent was accessing a invalid flow pointer, initializing the same. Change-Id: I559b6be8f75bae87442517e6edab00868742e397 Closes-bug: #1717507 (cherry picked from commit da656c5f29c66a4ce75a06efca973c990fe47c52) (cherry picked from commit 45e8e93c7894712455bee5c61bd5a706e36357e2) --- src/vnsw/agent/pkt/pkt_flow_info.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vnsw/agent/pkt/pkt_flow_info.h b/src/vnsw/agent/pkt/pkt_flow_info.h index 75697a4d487..26debfe4285 100644 --- a/src/vnsw/agent/pkt/pkt_flow_info.h +++ b/src/vnsw/agent/pkt/pkt_flow_info.h @@ -53,6 +53,7 @@ class PktFlowInfo { ecmp(false), out_component_nh_idx(-1), fip_snat(false), fip_dnat(false), snat_fip(), short_flow_reason(0), peer_vrouter(), tunnel_type(TunnelType::INVALID), + flow_entry(NULL), flood_unknown_unicast(false), bgp_router_service_flow(false), alias_ip_flow(false), ttl(0) { } From 57287c56e9f37fdcc68d30283f0ad31f5374d0dc Mon Sep 17 00:00:00 2001 From: Hari Prasad Killi Date: Thu, 28 Sep 2017 14:55:46 +0530 Subject: [PATCH 52/68] Add NULL check A flow is marked as reverse flow and its forward flow is NULL. Recompute flow is accessing this forward flow causing the crash. Updating to ignore this case. Change-Id: I08ac5beb3cc6b1879d8679d7a582ecf1eedb2286 closes-bug: #1719857 (cherry picked from commit 41779f46b692b36f9384af90d79e79d7b3a364fe) --- src/vnsw/agent/pkt/flow_table.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vnsw/agent/pkt/flow_table.cc b/src/vnsw/agent/pkt/flow_table.cc index 8cbc4bd6b46..4991b94eef2 100644 --- a/src/vnsw/agent/pkt/flow_table.cc +++ b/src/vnsw/agent/pkt/flow_table.cc @@ -519,7 +519,8 @@ void FlowTable::RecomputeFlow(FlowEntry *flow) { flow = flow->reverse_flow_entry(); } - agent_->pkt()->get_flow_proto()->MessageRequest(flow); + if (flow != NULL) + agent_->pkt()->get_flow_proto()->MessageRequest(flow); } // Handle deletion of a Route. Flow management module has identified that route From 04050146e7353caf89ebf38d5e1aa0521aea24ef Mon Sep 17 00:00:00 2001 From: Naveen N Date: Tue, 7 Nov 2017 11:53:05 +0530 Subject: [PATCH 53/68] * Increase no. of free requests processed in flow queue Flow addition and deletion to flow table happens in same task context, addition happens in flow_event work queue and deletion from flow tree and freeing of flow entry happens in tokenless_queue. For every flow add message processed we would have 2 flow entry created, hence free queue should process twice the no. of entries so as to relinquish free entries inline with flow addition rate. Change-Id: Ic99ced407594dec50b998d80e8e52b08876783f3 Closes-bug: #1728316 (cherry picked from commit 203064274bdb17cc3d2ad579dd7df60b5dac8d3e) --- src/vnsw/agent/pkt/flow_event.cc | 2 +- src/vnsw/agent/pkt/flow_proto.cc | 12 ++++++++---- src/vnsw/agent/pkt/test/test_flow_scale.cc | 22 ++++++++++++++++++++++ 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/vnsw/agent/pkt/flow_event.cc b/src/vnsw/agent/pkt/flow_event.cc index 6d2966489a6..39f5c6b1089 100644 --- a/src/vnsw/agent/pkt/flow_event.cc +++ b/src/vnsw/agent/pkt/flow_event.cc @@ -28,7 +28,7 @@ FlowEventQueueBase::FlowEventQueueBase(FlowProto *proto, events_processed_(0), latency_limit_(latency_limit) { queue_ = new Queue(task_id, task_instance, boost::bind(&FlowEventQueueBase::Handler, this, _1), - Queue::kMaxSize, Queue::kMaxIterations); + Queue::kMaxSize, max_iterations); char buff[100]; sprintf(buff, "%s-%d", name.c_str(), task_instance); queue_->set_name(buff); diff --git a/src/vnsw/agent/pkt/flow_proto.cc b/src/vnsw/agent/pkt/flow_proto.cc index 3588ce02cdf..1a68e56db79 100644 --- a/src/vnsw/agent/pkt/flow_proto.cc +++ b/src/vnsw/agent/pkt/flow_proto.cc @@ -43,19 +43,23 @@ FlowProto::FlowProto(Agent *agent, boost::asio::io_service &io) : uint16_t latency = agent->params()->flow_task_latency_limit(); flow_event_queue_.push_back (new FlowEventQueue(agent, this, flow_table_list_[i], - &add_tokens_, latency, 16)); + &add_tokens_, latency, + FlowEventQueue::Queue::kMaxIterations)); flow_tokenless_queue_.push_back (new FlowEventQueue(agent, this, flow_table_list_[i], - NULL, latency, 16)); + NULL, latency, + 2 * FlowEventQueue::Queue::kMaxIterations)); flow_delete_queue_.push_back (new DeleteFlowEventQueue(agent, this, flow_table_list_[i], - &del_tokens_, latency, 16)); + &del_tokens_, latency, + FlowEventQueue::Queue::kMaxIterations)); flow_ksync_queue_.push_back (new KSyncFlowEventQueue(agent, this, flow_table_list_[i], - &ksync_tokens_, latency, 32)); + &ksync_tokens_, latency, + FlowEventQueue::Queue::kMaxIterations)); } if (::getenv("USE_VROUTER_HASH") != NULL) { string opt = ::getenv("USE_VROUTER_HASH"); diff --git a/src/vnsw/agent/pkt/test/test_flow_scale.cc b/src/vnsw/agent/pkt/test/test_flow_scale.cc index b2e54365730..7134013e2e7 100644 --- a/src/vnsw/agent/pkt/test/test_flow_scale.cc +++ b/src/vnsw/agent/pkt/test/test_flow_scale.cc @@ -34,7 +34,9 @@ class FlowTest : public ::testing::Test { TunnelType::AllType(), 16, "TestVn", SecurityGroupList(), PathPreference()); client->WaitForIdle(); + EXPECT_EQ(0U, flow_proto_->FlowCount()); + free_queue_ = flow_proto_->flow_tokenless_queue_[0]->queue(); } virtual void TearDown() { @@ -57,6 +59,7 @@ class FlowTest : public ::testing::Test { char vnet_addr[32]; Agent *agent_; FlowProto *flow_proto_; + FlowEventQueue::Queue *free_queue_; }; TEST_F(FlowTest, FlowScaling_1) { @@ -79,6 +82,25 @@ TEST_F(FlowTest, FlowScaling_1) { (count == flow_count + (int) flow_proto_->FlowCount())); } +TEST_F(FlowTest, FlowScaling_2) { + char env[100]; + uint32_t intf_id = VmPortGetId(1); + int count = 1000; + if (getenv("AGENT_FLOW_SCALE_COUNT")) { + strcpy(env, getenv("AGENT_FLOW_SCALE_COUNT")); + count = strtoul(env, NULL, 0); + } + + for (int i = 0; i < count; i++) { + Ip4Address sip(i); + TxTcpPacket(intf_id, sip.to_string().c_str(), "33.3.3.3", + 10, 15, false); + } + + WAIT_FOR(count * 10, 10000, (flow_proto_->FlowCount() == 0)); + EXPECT_TRUE(free_queue_->max_queue_len() <= (uint32_t)(count/4)); +} + int main(int argc, char *argv[]) { int ret = 0; From 243d629d93f9bc5a8e912fa4e6dad60a7890a296 Mon Sep 17 00:00:00 2001 From: Hari Prasad Killi Date: Wed, 18 Oct 2017 19:32:56 +0530 Subject: [PATCH 54/68] Take only the v4 address for service health check IP. When we have dual interfaces, v4 and v6 service health check addresses are created and sent to agent. Agent oper data has only one service health check address, which is a problem. Updating to use only the v4 address. Need to add support for v6 health check separately. Change-Id: Ia5c1973c500d1e6d5d89ca83c3a48b8d94c0e579 closes-bug: #1692283 (cherry picked from commit 93d5d4e90766eb29a4f20ef03d16c486b912123a) --- src/vnsw/agent/oper/vm_interface.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vnsw/agent/oper/vm_interface.cc b/src/vnsw/agent/oper/vm_interface.cc index 0ba993de5be..4fd6601eb9b 100644 --- a/src/vnsw/agent/oper/vm_interface.cc +++ b/src/vnsw/agent/oper/vm_interface.cc @@ -657,7 +657,9 @@ static void BuildInstanceIp(Agent *agent, VmInterfaceConfigData *data, // it to instance ip list to allow route export and save it // under service_health_check_ip_ to allow usage for health // check service - data->service_health_check_ip_ = addr; + if (addr.is_v4()) { + data->service_health_check_ip_ = addr; + } } if (addr.is_v4()) { From 692d9e4f050493e25f2629326322a7662e77e6ea Mon Sep 17 00:00:00 2001 From: Sukhdev Kapur Date: Thu, 13 Apr 2017 16:37:16 -0700 Subject: [PATCH 55/68] Fix port-update API to support mac-address update If mac address of a port is chnaged via port-update API, it is ignored. This fix handles the updating of the mac address. Also, it ensures that the same mac address does not exixt on any other port in the same virtual network. Change-Id: Iec9290e0bc4485f2d59c38b223ddc458679aa1f4 Closes-Bug: 1682672 Partial-Bug: 1683547 (cherry picked from commit 3905546ac6172f001d1d214dbcdc6541c788ed29) --- .../vnc_openstack/neutron_plugin_db.py | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py b/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py index 7416a7bdbdc..ab47dd61ad7 100644 --- a/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py +++ b/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py @@ -1968,6 +1968,36 @@ def _port_neutron_to_vnc(self, port_q, net_obj, oper): port_obj.add_virtual_machine_interface_bindings( KeyValuePair(key=k, value=v)) + # Ironic may switch the mac address on a give port + net_id = (port_q.get('network_id') or + port_obj.get_virtual_network_refs()[0]['uuid']) + + # Allow updating of mac addres for baremetal deployments or + # when port is not attached to any VM + allowed_port = ( + 'binding:vnic_type' in port_q and port_q['binding:vnic_type'] == 'baremetal') + if not allowed_port: + port_bindings = port_obj.get_virtual_machine_interface_bindings() + kvps = port_bindings.get_key_value_pair() + for kvp in kvps: + if kvp.key == 'host_id' and kvp.value == "null": + allowed_port = True + break + if 'mac_address' in port_q and allowed_port: + # Ensure that duplicate mac address does not exist on this network + ports = self._virtual_machine_interface_list(back_ref_id=net_id) + for port in ports: + macs = port.get_virtual_machine_interface_mac_addresses() + for mac in macs.get_mac_address(): + #ensure that the same address is not on any other port + if mac == port_q['mac_address'] and port.uuid != port_q['id']: + raise self._raise_contrail_exception("MacAddressInUse", + net_id=net_id, mac=port_q['mac_address']) + # Update the mac accress if no duplicate found + mac_addrs_obj = MacAddressesType() + mac_addrs_obj.set_mac_address([port_q['mac_address']]) + port_obj.set_virtual_machine_interface_mac_addresses(mac_addrs_obj) + if oper == CREATE: if 'port_security_enabled' in port_q: port_security = port_q['port_security_enabled'] From 97cc90ba9379211ed09ed3b396ddc23749c6f2f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Jedin=C3=BD?= Date: Mon, 5 Feb 2018 14:34:45 +0100 Subject: [PATCH 56/68] Fix route target delete ordering Change-Id: I0c7821695cccffb60496a55b6e7af4a27ebfe5e2 Closes-Bug: #1747427 --- src/config/schema-transformer/config_db.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/schema-transformer/config_db.py b/src/config/schema-transformer/config_db.py index 9be036e1829..9394129413c 100644 --- a/src/config/schema-transformer/config_db.py +++ b/src/config/schema-transformer/config_db.py @@ -3828,8 +3828,8 @@ def delete_obj(self): self.update_multiple_refs('route_table', {}) self.update_virtual_networks() rtgt_num = int(self.route_target.split(':')[-1]) - self._cassandra.free_route_target_by_number(rtgt_num) RouteTargetST.delete_vnc_obj(self.route_target) + self._cassandra.free_route_target_by_number(rtgt_num) # end delete_obj def update_virtual_networks(self): From 5e7be3aca8ac064dadcaa4aeef39edb3fc9ba1c3 Mon Sep 17 00:00:00 2001 From: Sachin Bansal Date: Mon, 22 Jan 2018 10:01:43 -0800 Subject: [PATCH 57/68] Allow port security to be disabled if aap is present, but empty Change-Id: I9fa3f3f494ec2769cc03ec88987aa68903643999 Closes-Bug: #1735409 (cherry picked from commit ccdaf5159929bdf2769c1250fea61188a1eab312) (cherry picked from commit a6dfe12a888bbc4df959eea8d76cff9146d47686) --- src/config/api-server/vnc_cfg_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/api-server/vnc_cfg_types.py b/src/config/api-server/vnc_cfg_types.py index 18208c1f6f6..9d810b7fbe9 100644 --- a/src/config/api-server/vnc_cfg_types.py +++ b/src/config/api-server/vnc_cfg_types.py @@ -707,7 +707,7 @@ def _check_port_security_and_address_pairs(cls, obj_dict, db_dict={}): address_pairs = db_dict.get( 'virtual_machine_interface_allowed_address_pairs') - if not port_security and address_pairs is not None: + if not port_security and address_pairs: msg = "Allowed address pairs are not allowed when port "\ "security is disabled" return (False, (400, msg)) From 0d5b27649fa135457b2360e1687573f26d857778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Jedin=C3=BD?= Date: Wed, 21 Feb 2018 13:20:27 +0100 Subject: [PATCH 58/68] Disable port security on SNAT router_interface Change-Id: I6994be634e5b136d9ad78cb8a452bf81310cbf55 Closes-Bug: #1750795 --- src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py b/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py index ab47dd61ad7..f04cf99aec4 100644 --- a/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py +++ b/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py @@ -3518,7 +3518,8 @@ def add_router_interface(self, context, router_id, port_id=None, subnet_id=None) 'admin_state_up': True, 'device_id': router_id, 'device_owner': constants.DEVICE_OWNER_ROUTER_INTF, - 'name': ''}) + 'name': '', + 'port_security_enabled': False}) port_id = port['id'] From 64aa2d4ad52f36db0575fa556fd8e9e93489c636 Mon Sep 17 00:00:00 2001 From: Kumar Harsh Date: Fri, 20 Apr 2018 11:57:33 +0530 Subject: [PATCH 59/68] Remove gdb pretty printer for agent. Change-Id: Iebe0617e40a862d144c4d80cdc0d7d16970ac567 Closes-Bug: #1765599 --- src/vnsw/agent/gdb/agent_printer/printers.py | 276 ------------------- 1 file changed, 276 deletions(-) delete mode 100644 src/vnsw/agent/gdb/agent_printer/printers.py diff --git a/src/vnsw/agent/gdb/agent_printer/printers.py b/src/vnsw/agent/gdb/agent_printer/printers.py deleted file mode 100644 index cfc5fa60b76..00000000000 --- a/src/vnsw/agent/gdb/agent_printer/printers.py +++ /dev/null @@ -1,276 +0,0 @@ -# Pretty-printers for libstc++. - -# Copyright (C) 2008, 2009, 2010, 2011 Free Software Foundation, Inc. - -# 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 3 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, see . - -import gdb -import itertools -import re - -# Try to use the new-style pretty-printing if available. -_use_gdb_pp = True -try: - import gdb.printing -except ImportError: - _use_gdb_pp = False - -class TbbAtomicIntPrinter: - "Print TBB atomic varaiable of some kind" - def __init__ (self, typename, val): - self.typename = typename - self.val = val - - def to_string (self): - return '(tbb::atomic) %s' % (self.val['my_storage']['my_value']) - -class TbbConcurrentQueue: - """Print TBB Concurrent Queue of some kind""" - class Iterator: - def __init__(self, my_rep, hc): - self.my_rep = my_rep - self.hc = hc - self.element_type = self.my_rep.type.strip_typedefs().template_argument(0) - k = 0 - self.array = [] - while k < self.my_rep['n_queue']: - self.array.append(self.my_rep['array'][k]['head_page']['my_storage']['my_value'].dereference()) - k = k + 1 - self.count = 0 - - def __iter__(self): - return self - - def iter(self): - return self - - def next(self): - tc = self.my_rep['tail_counter']['my_storage']['my_value'] - if (self.hc == tc): - raise StopIteration - array_idx = int((self.hc * self.my_rep['phi']) % self.my_rep['n_queue']) - pg_index = (self.hc / self.my_rep['n_queue']) & (self.my_rep['items_per_page'] - 1) - if self.array[array_idx]['mask'] & ( 1 << pg_index): - item = self.array[array_idx].cast(gdb.lookup_type('tbb::strict_ppl::internal::micro_queue< ' + str(self.element_type) + ' >::padded_page'))['last'].address[pg_index] - else: - item = None - result = ('[%d]' % self.count, item) - if (pg_index == (self.my_rep['items_per_page'] - 1)): - self.array[array_idx] = self.array[array_idx]['next'].dereference() - self.count = self.count + 1 - self.hc = self.hc + 1 - return result - - def __next__(self): - return self.next() - - def __init__ (self, typename, val): - self.typename = typename - self.val = val - self.my_rep = val['my_rep'].dereference() - - def get_size (self): - hc = self.my_rep['head_counter']['my_storage']['my_value'] - nie = self.my_rep['n_invalid_entries']['my_storage']['my_value'] - tc = self.my_rep['tail_counter']['my_storage']['my_value'] - return (tc - hc - nie) - - def to_string (self): - return '%s with %d elements' % (self.typename, self.get_size()) - - def children (self): - return self.Iterator(self.my_rep, self.my_rep['head_counter']['my_storage']['my_value']) - -class MacAddressPrinter: - "Print TBB atomic varaiable of some kind" - def __init__ (self, typename, val): - self.typename = typename - self.val = val - - def to_string (self): - oct = self.val['addr_']['ether_addr_octet'] - return '%02x:%02x:%02x:%02x:%02x:%02x' %(oct[0], oct[1], oct[2], oct[3], oct[4], oct[5]) - -class FlowKeyPrinter: - "Print TBB atomic varaiable of some kind" - def __init__ (self, typename, val): - self.typename = typename - self.val = val - - def to_string (self): - nh = self.val['nh'] - family = self.val['family'] - proto = self.val['protocol'] - sport = self.val['src_port'] - dport = self.val['dst_port'] - sip = "FIXME" - dip = "FIXME" - if self.val['family'] == 1: - family = 'IPv4' - x = int(self.val['src_addr']['ipv4_address_']['addr_']['s_addr']) - x1 = x & 0xFF - x2 = (x & 0xFF00) >> 8 - x3 = (x & 0xFF0000) >> 16 - x4 = (x & 0xFF000000) >> 24 - sip = '%d.%d.%d.%d' %(x1, x2, x3, x4) - x = int(self.val['dst_addr']['ipv4_address_']['addr_']['s_addr']) - x1 = x & 0xFF - x2 = (x & 0xFF00) >> 8 - x3 = (x & 0xFF0000) >> 16 - x4 = (x & 0xFF000000) >> 24 - dip = '%d.%d.%d.%d' %(x1, x2, x3, x4) - else: - family = 'IPv6' - - return '<%s nh=%-6d sip=%-15s dip=%-15s proto=%-3d sport=%-5d dport=%-5d>' % (family, nh, sip, dip, proto, sport, dport) - -class IpPrinter: - "Print TBB atomic varaiable of some kind" - def __init__ (self, typename, val): - self.typename = typename - self.val = val - - def to_string (self): - ip_type = "" - addr = "" - if self.val['type_'] == 0: - x = int(self.val['ipv4_address_']['addr_']['s_addr']) - x1 = x & 0xFF - x2 = (x & 0xFF00) >> 8 - x3 = (x & 0xFF0000) >> 16 - x4 = (x & 0xFF000000) >> 24 - ip = '%d.%d.%d.%d' %(x1, x2, x3, x4) - ip_type = "IPv4" - addr = '%-15s' %(ip) - else: - addr = 'FIXME' - - return '<%s %-15s>' % (ip_type, addr) - -class Ipv4Printer: - "Print TBB atomic varaiable of some kind" - def __init__ (self, typename, val): - self.typename = typename - self.val = val - - def to_string (self): - addr = "" - x = int(self.val['addr_']['s_addr']) - x1 = x & 0xFF - x2 = (x & 0xFF00) >> 8 - x3 = (x & 0xFF0000) >> 16 - x4 = (x & 0xFF000000) >> 24 - ip = '<%d.%d.%d.%d>' %(x1, x2, x3, x4) - return '<%-15s>' %(ip) - -# A "regular expression" printer which conforms to the -# "SubPrettyPrinter" protocol from gdb.printing. -class RxPrinter(object): - def __init__(self, name, function): - super(RxPrinter, self).__init__() - self.name = name - self.function = function - self.enabled = True - - def invoke(self, value): - if not self.enabled: - return None - return self.function(self.name, value) - -# A pretty-printer that conforms to the "PrettyPrinter" protocol from -# gdb.printing. It can also be used directly as an old-style printer. -class Printer(object): - def __init__(self, name): - super(Printer, self).__init__() - self.name = name - self.subprinters = [] - self.lookup = {} - self.enabled = True - self.compiled_rx = re.compile('^([a-zA-Z0-9_:]+)<.*>$') - - def add(self, name, function): - # A small sanity check. - # FIXME - if not self.compiled_rx.match(name + '<>'): - raise ValueError('libstdc++ programming error: "%s" does not match' % name) - printer = RxPrinter(name, function) - self.subprinters.append(printer) - self.lookup[name] = printer - - @staticmethod - def get_basic_type(type): - # If it points to a reference, get the reference. - if type.code == gdb.TYPE_CODE_REF: - type = type.target () - - # Get the unqualified type, stripped of typedefs. - type = type.unqualified ().strip_typedefs () - - return type.tag - - def __call__(self, val): - typename = self.get_basic_type(val.type) - if not typename: - return None - - # All the types we match are template types, so we can use a - # dictionary. - match = self.compiled_rx.match(typename) - if not match: - basename = typename - else: - basename = match.group(1) - - if basename in self.lookup: - return self.lookup[basename].invoke(val) - - # Cannot find a pretty printer. Return None. - return None - -agent_printer = None -agent_printer_registered = False - -def register_agent_printers (obj): - "Register agent pretty-printers with objfile Obj." - - global _use_gdb_pp - global agent_printer - global agent_printer_registered - - if agent_printer_registered is True: - return - - agent_printer_registered = True - if _use_gdb_pp: - gdb.printing.register_pretty_printer(obj, agent_printer) - else: - if obj is None: - obj = gdb - obj.pretty_printers.append(agent_printer) - -def build_agent_dictionary (): - global agent_printer - - agent_printer = Printer("agent-printer") - - # tbb objects requiring pretty-printing. - # In order from: - agent_printer.add('FlowKey', FlowKeyPrinter) - agent_printer.add('boost::asio::ip::address', IpPrinter) - agent_printer.add('boost::asio::ip::address_v4', Ipv4Printer) - agent_printer.add('tbb::atomic', TbbAtomicIntPrinter) - agent_printer.add('tbb::strict_ppl::concurrent_queue', TbbConcurrentQueue) - agent_printer.add('tbb::concurrent_queue', TbbConcurrentQueue) - agent_printer.add('MacAddress', MacAddressPrinter) -build_agent_dictionary () From ac4e3970603f491ef400ab0fd408cef9a080e053 Mon Sep 17 00:00:00 2001 From: Praveen K V Date: Thu, 20 Apr 2017 19:53:48 +0530 Subject: [PATCH 60/68] Replace tbb::task::spawn() by tbb::task::enqueue() and also support for task monitoring Replace tbb::task::spawn() by tbb::task::enqueue(). enqueue() is more appropriate call since task library just fires the task without calling spawn_root_and_wait() Also, introduced a TaskMonitor class which monitors number of tasks enqueued to scheduler and number of tasks spawned by TBB. If the monitor finds tasks are enqueued but not scheduled for a configured time, it will exit the program. Conflicts: src/base/task.cc src/base/task.h src/vnsw/agent/cmn/agent.cc src/vnsw/agent/contrail-vrouter-agent.conf src/vnsw/agent/init/agent_param.cc Change-Id: Ia68d52f4aba0e79a3fe75f8c4fc248d49ab94faf Closes-Bug: #1684993 (cherry picked from commit a2eb06443bcfaef5279f65a6e402fe9bc9f055b7) --- src/base/SConscript | 2 + src/base/sandesh/task.sandesh | 19 ++ src/base/task.cc | 60 +++++-- src/base/task.h | 25 ++- src/base/task_monitor.cc | 120 +++++++++++++ src/base/task_monitor.h | 74 ++++++++ src/base/task_sandesh.cc | 27 +++ src/base/test/SConscript | 4 + src/base/test/test_task_monitor.cc | 198 +++++++++++++++++++++ src/vnsw/agent/cmn/agent.cc | 14 +- src/vnsw/agent/cmn/agent.h | 3 + src/vnsw/agent/contrail-vrouter-agent.conf | 6 +- src/vnsw/agent/init/agent_param.cc | 5 + src/vnsw/agent/init/agent_param.h | 5 + 14 files changed, 545 insertions(+), 17 deletions(-) create mode 100644 src/base/task_monitor.cc create mode 100644 src/base/task_monitor.h create mode 100644 src/base/test/test_task_monitor.cc diff --git a/src/base/SConscript b/src/base/SConscript index e9983f13332..afb7200a797 100644 --- a/src/base/SConscript +++ b/src/base/SConscript @@ -36,6 +36,7 @@ libcpuinfo = env.Library('cpuinfo', ['cpuinfo.cc'] + cpuinfo_sandesh_files_) task = except_env.Object('task.o', 'task.cc') timer = timer_env.Object('timer.o', 'timer.cc') +task_monitor = timer_env.Object('task_monitor.o', 'task_monitor.cc') ProcessInfoSandeshGenFiles = env.SandeshGenCpp('sandesh/process_info.sandesh') ProcessInfoSandeshGenSrcs = env.ExtractCpp(ProcessInfoSandeshGenFiles) @@ -66,6 +67,7 @@ libbase = env.Library('base', 'proto.cc', task, 'task_annotations.cc', + task_monitor, 'task_sandesh.cc', 'task_trigger.cc', 'tdigest.c', diff --git a/src/base/sandesh/task.sandesh b/src/base/sandesh/task.sandesh index 47dab2b1b7a..e0aedb92c9b 100644 --- a/src/base/sandesh/task.sandesh +++ b/src/base/sandesh/task.sandesh @@ -33,6 +33,7 @@ struct SandeshTaskGroup { response sandesh SandeshTaskScheduler { 1: bool running; + 5: bool use_spawn; 2: u64 total_count; 3: i32 thread_count; 4: list task_group_list; @@ -56,3 +57,21 @@ trace sandesh TaskTrace { 4: u32 latency; 5: string description; } + +response sandesh SandeshMonitorResponse { + 1: bool running; + 2: u64 inactivity_time_msec; + 3: u64 poll_interval_msec; + 4: u64 poll_count; + 5: u64 last_activity; + 6: u64 last_enqueue_count; + 7: u64 last_done_count; + 8: u64 tbb_keepawake_time; +} + +/** + * @description: sandesh request to get task monitoring status + * @cli_name: read task monitor + */ +request sandesh SandeshTaskMonitorRequest { +} diff --git a/src/base/task.cc b/src/base/task.cc index fba528b7756..b225b595f3f 100644 --- a/src/base/task.cc +++ b/src/base/task.cc @@ -14,6 +14,7 @@ #include "base/logging.h" #include "base/task.h" #include "base/task_annotations.h" +#include "base/task_monitor.h" #include #include @@ -245,6 +246,7 @@ class TaskGroup { tbb::task *TaskImpl::execute() { TaskInfo::reference running = task_running.local(); running = parent_; + parent_->SetTbbState(Task::TBB_EXEC); try { uint64_t t = 0; if (parent_->enqueue_time() != 0) { @@ -331,6 +333,13 @@ int TaskScheduler::GetThreadCount(int thread_count) { return num_cores_ * ThreadAmpFactor_; } +bool TaskScheduler::ShouldUseSpawn() { + if (getenv("TBB_USE_SPAWN")) + return true; + + return false; +} + //////////////////////////////////////////////////////////////////////////// // Implementation for class TaskScheduler //////////////////////////////////////////////////////////////////////////// @@ -340,10 +349,10 @@ int TaskScheduler::GetThreadCount(int thread_count) { // for task scheduling. But, in our case we dont want "main" thread to be // part of tbb. So, initialize TBB with one thread more than its default TaskScheduler::TaskScheduler(int task_count) : - task_scheduler_(GetThreadCount(task_count) + 1), + use_spawn_(ShouldUseSpawn()), task_scheduler_(GetThreadCount(task_count) + 1), running_(true), seqno_(0), id_max_(0), log_fn_(), track_run_time_(false), measure_delay_(false), schedule_delay_(0), execute_delay_(0), - enqueue_count_(0), done_count_(0), cancel_count_(0) { + enqueue_count_(0), done_count_(0), cancel_count_(0), task_monitor_(NULL) { hw_thread_count_ = GetThreadCount(task_count); task_group_db_.resize(TaskScheduler::kVectorGrowSize); stop_entry_ = new TaskEntry(-1); @@ -378,6 +387,18 @@ void TaskScheduler::Initialize(uint32_t thread_count) { singleton_.reset(new TaskScheduler((int)thread_count)); } +void TaskScheduler::EnableMonitor(EventManager *evm, + uint64_t tbb_keepawake_time_msec, + uint64_t inactivity_time_msec, + uint64_t poll_interval_msec) { + if (task_monitor_ != NULL) + return; + + task_monitor_ = new TaskMonitor(this, tbb_keepawake_time_msec, + inactivity_time_msec, poll_interval_msec); + task_monitor_->Start(evm); +} + void TaskScheduler::Log(const char *file_name, uint32_t line_no, const Task *task, const char *description, uint32_t delay) { @@ -621,6 +642,7 @@ void TaskScheduler::OnTaskExit(Task *t) { tbb::mutex::scoped_lock lock(mutex_); done_count_++; + t->SetTbbState(Task::TBB_DONE); TaskEntry *entry = QueryTaskEntry(t->GetTaskId(), t->GetTaskInstance()); entry->TaskExited(t, GetTaskGroup(t->GetTaskId())); @@ -641,7 +663,8 @@ void TaskScheduler::OnTaskExit(Task *t) { // Task is being recycled, reset the state, seq_no and TBB task handle t->task_impl_ = NULL; t->SetSeqNo(0); - t->state_ = Task::INIT; + t->SetState(Task::INIT); + t->SetTbbState(Task::TBB_INIT); EnqueueUnLocked(t); } @@ -869,6 +892,12 @@ void TaskScheduler::WaitForTerminateCompletion() { } void TaskScheduler::Terminate() { + if (task_monitor_) { + task_monitor_->Terminate(); + delete task_monitor_; + task_monitor_ = NULL; + } + for (int i = 0; i < 10000; i++) { if (IsEmpty()) break; usleep(1000); @@ -1194,7 +1223,7 @@ void TaskEntry::RunTask (Task *t) { TaskGroup *group = scheduler->QueryTaskGroup(t->GetTaskId()); group->TaskStarted(); - t->StartTask(); + t->StartTask(scheduler); } void TaskEntry::RunWaitQ() { @@ -1346,22 +1375,21 @@ int TaskEntry::GetTaskDeferEntrySeqno() const { // Implementation for class Task //////////////////////////////////////////////////////////////////////////// Task::Task(int task_id, int task_instance) : task_id_(task_id), - task_instance_(task_instance), task_impl_(NULL), state_(INIT), seqno_(0), - task_recycle_(false), task_cancel_(false), enqueue_time_(0), - schedule_time_(0), execute_delay_(0), schedule_delay_(0) { + task_instance_(task_instance), task_impl_(NULL), state_(INIT), + tbb_state_(TBB_INIT), seqno_(0), task_recycle_(false), task_cancel_(false), + enqueue_time_(0), schedule_time_(0), execute_delay_(0), schedule_delay_(0) { } Task::Task(int task_id) : task_id_(task_id), - task_instance_(-1), task_impl_(NULL), state_(INIT), seqno_(0), - task_recycle_(false), task_cancel_(false), enqueue_time_(0), + task_instance_(-1), task_impl_(NULL), state_(INIT), tbb_state_(TBB_INIT), + seqno_(0), task_recycle_(false), task_cancel_(false), enqueue_time_(0), schedule_time_(0), execute_delay_(0), schedule_delay_(0) { } // Start execution of task -void Task::StartTask() { +void Task::StartTask(TaskScheduler *scheduler) { if (enqueue_time_ != 0) { schedule_time_ = ClockMonotonicUsec(); - TaskScheduler *scheduler = TaskScheduler::GetInstance(); if ((schedule_time_ - enqueue_time_) > scheduler->schedule_delay(this)) { TASK_TRACE(scheduler, this, "Schedule delay(in usec) ", @@ -1369,9 +1397,14 @@ void Task::StartTask() { } } assert(task_impl_ == NULL); - state_ = RUN; + SetState(RUN); + SetTbbState(TBB_ENQUEUED); task_impl_ = new (task::allocate_root())TaskImpl(this); - task::spawn(*task_impl_); + if (scheduler->use_spawn()) { + task::spawn(*task_impl_); + } else { + task::enqueue(*task_impl_); + } } Task *Task::Running() { @@ -1437,6 +1470,7 @@ void TaskScheduler::GetSandeshData(SandeshTaskScheduler *resp, bool summary) { tbb::mutex::scoped_lock lock(mutex_); resp->set_running(running_); + resp->set_use_spawn(use_spawn_); resp->set_total_count(seqno_); resp->set_thread_count(hw_thread_count_); diff --git a/src/base/task.h b/src/base/task.h index 5817740b82f..b8f4ac00331 100644 --- a/src/base/task.h +++ b/src/base/task.h @@ -44,6 +44,9 @@ class TaskGroup; class TaskEntry; class SandeshTaskScheduler; +class EventManager; +class TaskMonitor; +class TaskScheduler; struct TaskStats { int wait_count_; // #Entries in waitq @@ -72,6 +75,13 @@ class Task { RUN }; + enum TbbState { + TBB_INIT, + TBB_ENQUEUED, + TBB_EXEC, + TBB_DONE + }; + const static int kTaskInstanceAny = -1; Task(int task_id, int task_instance); Task(int task_id); @@ -109,15 +119,17 @@ class Task { friend class TaskScheduler; friend class TaskImpl; void SetSeqNo(uint64_t seqno) {seqno_ = seqno;}; + void SetTbbState(TbbState s) { tbb_state_ = s; }; void SetState(State s) { state_ = s; }; void SetTaskRecycle() { task_recycle_ = true; }; void SetTaskComplete() { task_recycle_ = false; }; - void StartTask(); + void StartTask(TaskScheduler *scheduler); int task_id_; // The code path executed by the task. int task_instance_; // The dataset id within a code path. tbb::task *task_impl_; State state_; + TbbState tbb_state_; uint64_t seqno_; bool task_recycle_; bool task_cancel_; @@ -198,6 +210,7 @@ class TaskScheduler { // Get number of tbb worker threads. static int GetThreadCount(int thread_count = 0); + static bool ShouldUseSpawn(); uint64_t enqueue_count() const { return enqueue_count_; } uint64_t done_count() const { return done_count_; } @@ -223,6 +236,13 @@ class TaskScheduler { uint32_t schedule_delay(Task *task) const; uint32_t execute_delay(Task *task) const; + // Enable Task monitoring + void EnableMonitor(EventManager *evm, uint64_t tbb_keepawake_time_msec, + uint64_t inactivity_time_msec, + uint64_t poll_interval_msec); + const TaskMonitor *task_monitor() const { return task_monitor_; } + bool use_spawn() const { return use_spawn_; } + // following function allows one to increase max num of threads used by // TBB static void SetThreadAmpFactor(int n); @@ -245,6 +265,8 @@ class TaskScheduler { int CountThreadsPerPid(pid_t pid); + // Use spawn() to run a tbb::task instead of enqueue() + bool use_spawn_; TaskEntry *stop_entry_; tbb::task_scheduler_init task_scheduler_; @@ -273,6 +295,7 @@ class TaskScheduler { // following variable allows one to increase max num of threads used by // TBB static int ThreadAmpFactor_; + TaskMonitor *task_monitor_; DISALLOW_COPY_AND_ASSIGN(TaskScheduler); }; diff --git a/src/base/task_monitor.cc b/src/base/task_monitor.cc new file mode 100644 index 00000000000..38b59734b11 --- /dev/null +++ b/src/base/task_monitor.cc @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2017 Juniper Networks, Inc. All rights reserved. + */ +#include +#include "logging.h" +#include "io/event_manager.h" +#include "timer_impl.h" +#include "time_util.h" +#include "task.h" +#include "task_monitor.h" +#include "task_tbbkeepawake.h" + +#define kPollIntervalMultiplier 2 +#define kInactivityMultiplier 50 + +TaskMonitor::TaskMonitor(TaskScheduler *scheduler, + uint64_t tbb_keepawake_time_msec, + uint64_t inactivity_time_msec, + uint64_t poll_interval_msec) : + scheduler_(scheduler), cancelled_(false), timer_impl_(NULL), + inactivity_time_usec_(inactivity_time_msec * 1000), + poll_interval_msec_(poll_interval_msec), + tbb_keepawake_time_msec_(tbb_keepawake_time_msec), + last_activity_(ClockMonotonicUsec()), + last_enqueue_count_(0), last_done_count_(0), poll_count_(0) { +} + +TaskMonitor::~TaskMonitor() { +} + +void TaskMonitor::UpdateTimers() { + // Ensure polling interval for monitor is atleast 2*keep-awake-time + // It ensures when monitor is invoked, there is some job enqueued + // and executed + if ((tbb_keepawake_time_msec_ * kPollIntervalMultiplier) > poll_interval_msec_) { + poll_interval_msec_ = + kPollIntervalMultiplier * tbb_keepawake_time_msec_; + } + + // Ensure monitor timeout is atleast 50 * poll-interval + if ((poll_interval_msec_ * kInactivityMultiplier * 1000) > + inactivity_time_usec_) { + inactivity_time_usec_ = + poll_interval_msec_ * kInactivityMultiplier * 1000; + } + return; +} + +void TaskMonitor::Start(EventManager *evm) { + if (inactivity_time_usec_ == 0 || poll_interval_msec_ == 0) + return; + + UpdateTimers(); + timer_impl_.reset(new TimerImpl(*evm->io_service())); + Restart(); + return; +} + +void TaskMonitor::Terminate() { + cancelled_ = true; +} + +void TaskMonitor::Restart() { + boost::system::error_code ec; + timer_impl_->expires_from_now(poll_interval_msec_, ec); + if (ec) { + assert(0); + } + timer_impl_->async_wait(boost::bind(&TaskMonitor::Run, this, + boost::asio::placeholders::error)); +} + +bool TaskMonitor::Monitor(uint64_t t, uint64_t enqueue_count, + uint64_t done_count) { + // New tasks were spawned by TBB. Treat as activity seen + if (done_count != last_done_count_) { + last_done_count_ = done_count; + last_enqueue_count_ = enqueue_count; + last_activity_ = t; + return true; + } + + // No change in done_count_. Now validate enqueue_count. + // Note: We cannot match enqueue_count_ and done_count_. Both these numbers + // are updated by multiple threads and are not atomic numbers. As a result + // they can potentially be out-of-sync + // + // If no new tasks are enqueued, then assume there is no more tasks + // to run. Treat it similar to seeing activity + if (enqueue_count == last_enqueue_count_) { + last_done_count_ = done_count; + last_enqueue_count_ = enqueue_count; + last_activity_ = t; + return true; + } + + // Enqueues are happening, check if inactivity exceeds configured time + return ((t - last_activity_) <= inactivity_time_usec_); +} + +void TaskMonitor::Run(const boost::system::error_code &ec) { + poll_count_++; + + // ASIO API aborted. Just restart ASIO timer again + if (ec && ec.value() == boost::asio::error::operation_aborted) { + Restart(); + return; + } + + if (cancelled_) + return; + + if (Monitor(ClockMonotonicUsec(), scheduler_->enqueue_count(), + scheduler_->done_count())) { + Restart(); + return; + } + LOG(ERROR, "!!!! ERROR !!!! Task Monitor failed"); + assert(0); +} diff --git a/src/base/task_monitor.h b/src/base/task_monitor.h new file mode 100644 index 00000000000..1452380d522 --- /dev/null +++ b/src/base/task_monitor.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2017 Juniper Networks, Inc. All rights reserved. + */ + +#ifndef __BASE_TASK_MONITOR_H__ +#define __BASE_TASK_MONITOR_H__ + +/**************************************************************************** + * Monitor module for TBB. + * It is observed that TBB randomly goes into a state where no tasks are + * scheduled. The monitor here tracks the tasks enqueued and executed. + * + * If the monitor identifes that TBB is in a lockup state, then it will + * assert the process + ****************************************************************************/ +#include +#include + +#include "util.h" + +class TaskScheduler; +class TimerImpl; +class EventManager; + +class TaskMonitor { +public: + TaskMonitor(TaskScheduler *scheduler, uint64_t tbb_keepawake_time_msec, + uint64_t inactivity_time_msec, uint64_t poll_interval_msec); + ~TaskMonitor(); + + void Start(EventManager *evm); + void Terminate(); + void Run(const boost::system::error_code &ec); + + uint64_t last_activity() const { return last_activity_; } + uint64_t last_enqueue_count() const { return last_enqueue_count_; } + uint64_t last_done_count() const { return last_done_count_; } + + uint64_t inactivity_time_usec() const { return inactivity_time_usec_; } + uint64_t inactivity_time_msec() const { return inactivity_time_usec_/1000; } + uint64_t poll_interval_msec() const { return poll_interval_msec_; } + uint64_t tbb_keepawake_time_msec() const { return tbb_keepawake_time_msec_;} + uint64_t poll_count() const { return poll_count_; } +private: + friend class TaskMonitorTest; + + void UpdateTimers(); + void Restart(); + bool Monitor(uint64_t t, uint64_t enqueue_count, uint64_t done_count); + + TaskScheduler *scheduler_; + // Monitor terminated + bool cancelled_; + // ASIO Timer implementation class + std::auto_ptr timer_impl_; + + // inactivity in usec after which monitor must invoke assert() + uint64_t inactivity_time_usec_; + // interval at which tbb must be monitored + uint64_t poll_interval_msec_; + // TBB keepawake time + uint64_t tbb_keepawake_time_msec_; + // time of last activity seen + uint64_t last_activity_; + // TaskScheduler->enqueue_count() at last_change_ + uint64_t last_enqueue_count_; + // TaskScheduler->done_count() at last_change_ + uint64_t last_done_count_; + // Number of times monitor is run + uint64_t poll_count_; + DISALLOW_COPY_AND_ASSIGN(TaskMonitor); +}; + +#endif // __BASE_TASK_MONITOR_H__ diff --git a/src/base/task_sandesh.cc b/src/base/task_sandesh.cc index 4ae9bea6bfc..9ee66d45806 100644 --- a/src/base/task_sandesh.cc +++ b/src/base/task_sandesh.cc @@ -3,9 +3,11 @@ */ #include +#include #include #include #include +#include using std::string; @@ -25,3 +27,28 @@ void SandeshTaskRequest::HandleRequest() const { void SandeshTaskSummaryRequest::HandleRequest() const { HandleRequestCommon(context(), true); } + +void SandeshTaskMonitorRequest::HandleRequest() const { + TaskScheduler *scheduler = TaskScheduler::GetInstance(); + + SandeshMonitorResponse *resp = new SandeshMonitorResponse; + resp->set_context(context()); + resp->set_more(false); + + const TaskMonitor *monitor = scheduler->task_monitor(); + if (monitor && monitor->inactivity_time_usec() && + monitor->poll_interval_msec()) { + resp->set_running(true); + resp->set_inactivity_time_msec(monitor->inactivity_time_msec()); + resp->set_poll_interval_msec(monitor->poll_interval_msec()); + resp->set_poll_count(monitor->poll_count()); + resp->set_last_activity(monitor->last_activity()); + resp->set_last_enqueue_count(monitor->last_enqueue_count()); + resp->set_last_done_count(monitor->last_done_count()); + resp->set_tbb_keepawake_time(monitor->tbb_keepawake_time_msec()); + } else { + resp->set_running(false); + } + + resp->Response(); +} diff --git a/src/base/test/SConscript b/src/base/test/SConscript index ef85fb3e147..3d8982b6295 100644 --- a/src/base/test/SConscript +++ b/src/base/test/SConscript @@ -93,6 +93,9 @@ trace_test = BuildTest(env, 'trace_test', util_test = BuildTest(env, 'util_test', ['util_test.cc'], []) +test_task_monitor = env.UnitTest('test_task_monitor', ['test_task_monitor.cc']) +env.Alias('src/base:test_task_monitor', test_task_monitor) + test_suite = [ bitset_test, index_allocator_test, @@ -104,6 +107,7 @@ test_suite = [ task_annotations_test, factory_test, trace_test, + test_task_monitor, util_test, queue_task_test, conn_info_test, diff --git a/src/base/test/test_task_monitor.cc b/src/base/test/test_task_monitor.cc new file mode 100644 index 00000000000..4f3f1ebf9b9 --- /dev/null +++ b/src/base/test/test_task_monitor.cc @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2017 Juniper Networks, Inc. All rights reserved. + */ + +#include "testing/gunit.h" +#include "base/task_monitor.h" +#include "base/logging.h" + +using namespace std; + +class TaskMonitorTest : public ::testing::Test { +public: + virtual void SetUp() { + monitor_ = new TaskMonitor(NULL, 20, 1000, 20); + RestLastActivity(); + } + + virtual void TearDown() { + delete monitor_; + monitor_ = NULL; + } + + bool Monitor(uint64_t t, uint64_t enqueue_count, uint64_t done_count) { + return monitor_->Monitor(t*1000, enqueue_count, done_count); + } + + bool Monitor(TaskMonitor *m, uint64_t t, uint64_t enqueue_count, + uint64_t done_count) { + return m->Monitor(t*1000, enqueue_count, done_count); + } + + bool Validate(uint64_t t, uint64_t enqueue_count, uint64_t done_count) { + EXPECT_EQ(monitor_->last_activity() / 1000, t); + if (monitor_->last_activity() / 1000 != t) { + return false; + } + + EXPECT_EQ(monitor_->last_enqueue_count(), enqueue_count); + if (monitor_->last_enqueue_count() != enqueue_count) { + return false; + } + + EXPECT_EQ(monitor_->last_done_count(), done_count); + if (monitor_->last_done_count() != done_count) { + return false; + } + + return true; + } + + void RestLastActivity() { + monitor_->last_activity_ = 0; + } + + void SetTimers(uint64_t keepawake_time, uint64_t inactivity_time, + uint64_t poll_interval) { + monitor_->tbb_keepawake_time_msec_ = keepawake_time; + monitor_->inactivity_time_usec_ = inactivity_time * 1000; + monitor_->poll_interval_msec_ = poll_interval; + } + + void UpdateTimers() { + monitor_->UpdateTimers(); + } + + bool ValidateTimers(uint64_t inactivity, uint64_t poll) { + EXPECT_EQ(monitor_->inactivity_time_msec(), inactivity); + if (monitor_->inactivity_time_msec() != inactivity) { + return false; + } + + EXPECT_EQ(monitor_->poll_interval_msec(), poll); + if (monitor_->poll_interval_msec() != poll) { + return false; + } + + return true; + } + + +protected: + TaskMonitor *monitor_; +}; + +// Test init for different values of enqueue_count and done_count +TEST_F(TaskMonitorTest, init_1) { + EXPECT_TRUE(Monitor(1000, 0, 0)); +} + +TEST_F(TaskMonitorTest, init_2) { + EXPECT_TRUE(Monitor(1000, 1, 0)); +} + +TEST_F(TaskMonitorTest, init_3) { + EXPECT_TRUE(Monitor(1000, 0, 1)); +} + +TEST_F(TaskMonitorTest, init_4) { + EXPECT_TRUE(Monitor(1000, 1, 1)); +} + +// Activity detected since done_count changing +TEST_F(TaskMonitorTest, activity_1) { + // First invocation is for initialization + EXPECT_TRUE(Monitor(1000, 0, 0)); + + // Activity after timeout + EXPECT_TRUE(Monitor(1001, 1, 1)); + EXPECT_TRUE(Validate(1001, 1, 1)); + + EXPECT_TRUE(Monitor(3000, 10, 9)); + EXPECT_TRUE(Validate(3000, 10, 9)); + + EXPECT_TRUE(Monitor(4000, 20, 19)); + EXPECT_TRUE(Validate(4000, 20, 19)); +} + +// Inactivity ignored since enqueue not changed +TEST_F(TaskMonitorTest, no_enqueue_1) { + // First invocation is for initialization + EXPECT_TRUE(Monitor(1000, 0, 0)); + + // First activity + EXPECT_TRUE(Monitor(1001, 0, 0)); + EXPECT_TRUE(Validate(1001, 0, 0)); + + EXPECT_TRUE(Monitor(3000, 0, 0)); + EXPECT_TRUE(Validate(3000, 0, 0)); + + // Force some activity + EXPECT_TRUE(Monitor(4000, 1, 1)); + EXPECT_TRUE(Validate(4000, 1, 1)); + + EXPECT_TRUE(Monitor(5000, 1, 1)); + EXPECT_TRUE(Validate(5000, 1, 1)); +} + +// Tests with done_count_ not changing +TEST_F(TaskMonitorTest, no_done_1) { + // First invocation is for initialization + EXPECT_TRUE(Monitor(1000, 0, 0)); + + // Monitor succeeds since timeout not exceeded + EXPECT_TRUE(Monitor(1001, 1, 0)); + EXPECT_TRUE(Validate(1000, 0, 0)); + + EXPECT_TRUE(Monitor(1099, 2, 0)); + EXPECT_TRUE(Validate(1000, 0, 0)); + + // Monitor succeeds with timeout exceeded + EXPECT_FALSE(Monitor(2001, 3, 0)); + EXPECT_TRUE(Validate(1000, 0, 0)); + + // Monitor succeeds since done_count changed + EXPECT_TRUE(Monitor(3000, 3, 1)); + EXPECT_TRUE(Validate(3000, 3, 1)); + + // Monitor succeeds since timout is equal and doesnt exceed + EXPECT_TRUE(Monitor(4000, 4, 1)); + EXPECT_TRUE(Validate(3000, 3, 1)); + + EXPECT_FALSE(Monitor(5001, 5, 1)); + EXPECT_TRUE(Validate(3000, 3, 1)); + + EXPECT_TRUE(Monitor(6000, 10, 10)); + EXPECT_TRUE(Validate(6000, 10, 10)); + + EXPECT_TRUE(Monitor(6000, 10, 11)); + EXPECT_TRUE(Validate(6000, 10, 11)); +} + +TEST_F(TaskMonitorTest, timer_update_1) { + SetTimers(10, 100, 10); + UpdateTimers(); + EXPECT_TRUE(ValidateTimers(1000, 20)); + + SetTimers(20, 100, 10); + UpdateTimers(); + EXPECT_TRUE(ValidateTimers(2000, 40)); + + SetTimers(10, 50, 2); + UpdateTimers(); + EXPECT_TRUE(ValidateTimers(1000, 20)); + + SetTimers(10, 4000, 100); + UpdateTimers(); + EXPECT_TRUE(ValidateTimers(5000, 100)); + + SetTimers(10, 20000, 100); + UpdateTimers(); + EXPECT_TRUE(ValidateTimers(20000, 100)); +} + +int main(int argc, char **argv) { + LoggingInit(); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/src/vnsw/agent/cmn/agent.cc b/src/vnsw/agent/cmn/agent.cc index 308e26a8d07..18d92280393 100644 --- a/src/vnsw/agent/cmn/agent.cc +++ b/src/vnsw/agent/cmn/agent.cc @@ -407,6 +407,7 @@ void Agent::CopyConfig(AgentParam *params) { flow_del_tokens_ = params_->flow_del_tokens(); flow_update_tokens_ = params_->flow_update_tokens(); tbb_keepawake_timeout_ = params_->tbb_keepawake_timeout(); + task_monitor_timeout_msec_ = params_->task_monitor_timeout_msec(); send_ratelimit_ = params_->sandesh_send_rate_limit(); } @@ -460,7 +461,8 @@ void Agent::InitCollector() { } void Agent::InitDone() { - // Its observed that sometimes TBB doesnt scheduler misses spawn events + TaskScheduler *scheduler = TaskScheduler::GetInstance(); + // Its observed that sometimes TBB scheduler misses spawn events // and doesnt schedule a task till its triggered again. As a work around // start a dummy timer that fires and awake TBB periodically if (tbb_keepawake_timeout_) { @@ -468,6 +470,13 @@ void Agent::InitDone() { event_manager(), "Agent::TbbKeepAwake", tbb_keepawake_timeout_); } + + // Its observed that sometimes TBB stops scheduling tasks altogether. + // Initiate a monitor which asserts if no task is spawned for a given time. + if (task_monitor_timeout_msec_) { + scheduler->EnableMonitor(event_manager(), tbb_keepawake_timeout_, + task_monitor_timeout_msec_, 100); + } } void Agent::Shutdown() { @@ -592,7 +601,8 @@ Agent::Agent() : vrouter_max_interfaces_(0), vrouter_max_vrfs_(0), vrouter_max_mirror_entries_(0), vrouter_max_bridge_entries_(0), vrouter_max_oflow_bridge_entries_(0), flow_stats_req_handler_(NULL), - tbb_keepawake_timeout_(kDefaultTbbKeepawakeTimeout) { + tbb_keepawake_timeout_(kDefaultTbbKeepawakeTimeout), + task_monitor_timeout_msec_(kDefaultTaskMonitorTimeout) { assert(singleton_ == NULL); singleton_ = this; diff --git a/src/vnsw/agent/cmn/agent.h b/src/vnsw/agent/cmn/agent.h index c849da37a1c..e5334fc47c8 100644 --- a/src/vnsw/agent/cmn/agent.h +++ b/src/vnsw/agent/cmn/agent.h @@ -288,6 +288,7 @@ class Agent { // Max number of threads static const uint32_t kMaxTbbThreads = 8; static const uint32_t kDefaultTbbKeepawakeTimeout = (20); //time-millisecs + static const uint32_t kDefaultTaskMonitorTimeout = (5000); //time-millisecs // Default number of tx-buffers on pkt0 interface static const uint32_t kPkt0TxBufferCount = 1000; // Default value for cleanup of stale interface entries @@ -1277,6 +1278,8 @@ class Agent { FlowStatsReqHandler flow_stats_req_handler_; uint32_t tbb_keepawake_timeout_; + // Monitor task library and assert if inactivity detected + uint32_t task_monitor_timeout_msec_; // Constants public: static const std::string config_file_; diff --git a/src/vnsw/agent/contrail-vrouter-agent.conf b/src/vnsw/agent/contrail-vrouter-agent.conf index fb8e33b9ada..2916c8cab80 100644 --- a/src/vnsw/agent/contrail-vrouter-agent.conf +++ b/src/vnsw/agent/contrail-vrouter-agent.conf @@ -234,8 +234,12 @@ docker_command=/usr/bin/opencontrail-vrouter-docker # Log message if time taken to schedule task exceeds a threshold (in msec) # log_schedule_threshold = 25 # -# TBB Keepawake timer interval +# TBB Keepawake timer interval in msec # tbb_keepawake_timeout = 20 +# +# Timeout for task monitor in msec +# task_monitor_timeout = 50000 +# [SERVICES] # bgp_as_a_service_port_range - reserving set of ports to be used. diff --git a/src/vnsw/agent/init/agent_param.cc b/src/vnsw/agent/init/agent_param.cc index 91d963aa809..65f8f4350c2 100644 --- a/src/vnsw/agent/init/agent_param.cc +++ b/src/vnsw/agent/init/agent_param.cc @@ -883,6 +883,8 @@ void AgentParam::ParseTaskSectionArguments "TASK.log_schedule_threshold"); GetOptValue(var_map, tbb_keepawake_timeout_, "TASK.tbb_keepawake_timeout"); + GetOptValue(var_map, task_monitor_timeout_msec_, + "TASK.task_monitor_timeout"); } void AgentParam::ParseMetadataProxyArguments @@ -1485,6 +1487,7 @@ AgentParam::AgentParam(bool enable_flow_options, tbb_exec_delay_(0), tbb_schedule_delay_(0), tbb_keepawake_timeout_(Agent::kDefaultTbbKeepawakeTimeout), + task_monitor_timeout_msec_(Agent::kDefaultTaskMonitorTimeout), default_nic_queue_(Agent::kInvalidQueueId) { // Set common command line arguments supported boost::program_options::options_description generic("Generic options"); @@ -1680,6 +1683,8 @@ AgentParam::AgentParam(bool enable_flow_options, "Log message if task takes more than threshold (msec) to schedule") ("TASK.tbb_keepawake_timeout", opt::value(), "Timeout for the TBB keepawake timer") + ("TASK.task_monitor_timeout", opt::value(), + "Timeout for the Task monitoring") ; options_.add(tbb); } diff --git a/src/vnsw/agent/init/agent_param.h b/src/vnsw/agent/init/agent_param.h index 16a63d11867..996a79a2fbc 100644 --- a/src/vnsw/agent/init/agent_param.h +++ b/src/vnsw/agent/init/agent_param.h @@ -283,6 +283,9 @@ class AgentParam { uint32_t tbb_exec_delay() const { return tbb_exec_delay_; } uint32_t tbb_schedule_delay() const { return tbb_schedule_delay_; } uint32_t tbb_keepawake_timeout() const { return tbb_keepawake_timeout_; } + uint32_t task_monitor_timeout_msec() const { + return task_monitor_timeout_msec_; + } // pkt0 tx buffer uint32_t pkt0_tx_buffer_count() const { return pkt0_tx_buffer_count_; } @@ -536,6 +539,8 @@ class AgentParam { uint32_t tbb_exec_delay_; uint32_t tbb_schedule_delay_; uint32_t tbb_keepawake_timeout_; + // Monitor task library and assert if inactivity detected + uint32_t task_monitor_timeout_msec_; std::map qos_queue_map_; uint16_t default_nic_queue_; DISALLOW_COPY_AND_ASSIGN(AgentParam); From 99d18a2ac5271fd04a6acfac60bd13ca8ba729ef Mon Sep 17 00:00:00 2001 From: sangarshp Date: Fri, 20 Jul 2018 14:04:14 +0530 Subject: [PATCH 61/68] flow logging task can free the flow entry from flow table free list at present concurrency check is in place not to allow any task other than flow event task to free teh flow entry from free list. it is possible that flow log entry can be processed even after flow entry gets deleted ( log work queue entry holds flow entry pointer, so entry won't be freed until log queue entry gets processed) after processing log queue entry flow entry ref count becomes zero and it will freed up under flow logging task. added a check to allow only flow logging task and flow event task to free up the flow entry from flow table free list. Closes-Bug: #1779841 Change-Id: I9ee65f27c2a56869b99919745b5bd3ef6dbb7092 --- src/vnsw/agent/pkt/flow_table.cc | 23 +++++++++++++++++------ src/vnsw/agent/pkt/flow_table.h | 4 ++++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/vnsw/agent/pkt/flow_table.cc b/src/vnsw/agent/pkt/flow_table.cc index 96e533e2f20..0f911d08f04 100644 --- a/src/vnsw/agent/pkt/flow_table.cc +++ b/src/vnsw/agent/pkt/flow_table.cc @@ -65,7 +65,8 @@ FlowTable::FlowTable(Agent *agent, uint16_t table_index) : flow_task_id_(0), flow_update_task_id_(0), flow_delete_task_id_(0), - flow_ksync_task_id_(0) { + flow_ksync_task_id_(0), + flow_logging_task_id_(0) { } FlowTable::~FlowTable() { @@ -77,6 +78,7 @@ void FlowTable::Init() { flow_update_task_id_ = agent_->task_scheduler()->GetTaskId(kTaskFlowUpdate); flow_delete_task_id_ = agent_->task_scheduler()->GetTaskId(kTaskFlowDelete); flow_ksync_task_id_ = agent_->task_scheduler()->GetTaskId(kTaskFlowKSync); + flow_logging_task_id_ = agent_->task_scheduler()->GetTaskId(kTaskFlowLogging); FlowEntry::Init(); return; } @@ -89,7 +91,8 @@ void FlowTable::Shutdown() { // Concurrency check to ensure all flow-table and free-list manipulations // are done from FlowEvent task context only -bool FlowTable::ConcurrencyCheck(int task_id) { +//exception: freelist free function can be accessed by flow logging task +bool FlowTable::ConcurrencyCheck(int task_id, bool check_task_instance) { Task *current = Task::Running(); // test code invokes FlowTable API from main thread. The running task // will be NULL in such cases @@ -99,12 +102,17 @@ bool FlowTable::ConcurrencyCheck(int task_id) { if (current->GetTaskId() != task_id) return false; - - if (current->GetTaskInstance() != table_index_) - return false; + if (check_task_instance) { + if (current->GetTaskInstance() != table_index_) + return false; + } return true; } +bool FlowTable::ConcurrencyCheck(int task_id) { + return ConcurrencyCheck(task_id, true); +} + ///////////////////////////////////////////////////////////////////////////// // FlowTable Add/Delete routines ///////////////////////////////////////////////////////////////////////////// @@ -932,7 +940,10 @@ FlowEntry *FlowEntryFreeList::Allocate(const FlowKey &key) { } void FlowEntryFreeList::Free(FlowEntry *flow) { - assert(table_->ConcurrencyCheck(table_->flow_task_id()) == true); + // only flow logging task and flow event task can free up the flow entry , + assert((table_->ConcurrencyCheck(table_->flow_task_id()) == true) || + (table_->ConcurrencyCheck( + table_->flow_logging_task_id(), false) == true)); total_free_++; flow->Reset(); free_list_.push_back(*flow); diff --git a/src/vnsw/agent/pkt/flow_table.h b/src/vnsw/agent/pkt/flow_table.h index 48f80716bf2..b72e8f938f2 100644 --- a/src/vnsw/agent/pkt/flow_table.h +++ b/src/vnsw/agent/pkt/flow_table.h @@ -246,11 +246,14 @@ class FlowTable { // Concurrency check to ensure all flow-table and free-list manipulations // are done from FlowEvent task context only + //exception: freelist free function can be accessed by flow logging task + bool ConcurrencyCheck(int task_id, bool check_task_instance); bool ConcurrencyCheck(int task_id); int flow_task_id() const { return flow_task_id_; } int flow_update_task_id() const { return flow_update_task_id_; } int flow_delete_task_id() const { return flow_delete_task_id_; } int flow_ksync_task_id() const { return flow_ksync_task_id_; } + int flow_logging_task_id() const { return flow_logging_task_id_; } static void GetMutexSeq(tbb::mutex &mutex1, tbb::mutex &mutex2, tbb::mutex **mutex_ptr_1, tbb::mutex **mutex_ptr_2); @@ -297,6 +300,7 @@ class FlowTable { int flow_update_task_id_; int flow_delete_task_id_; int flow_ksync_task_id_; + int flow_logging_task_id_; DISALLOW_COPY_AND_ASSIGN(FlowTable); }; From c065431169a0e3c9bdfc19d1d9ab60547bee1c11 Mon Sep 17 00:00:00 2001 From: sangarshp Date: Mon, 9 Jul 2018 08:34:36 +0530 Subject: [PATCH 62/68] Check Gen id for processing recompute events for flows it is possible that enqueued forward flow become reverse flow when flows get evicted from vrouter and traffic is received for reverse flow, made changes to pass gen_id also when flow is enquwqued for recompute and when it gets processed for recompute , check whether gen id present in the event and gen id of flow matches, if it does not match, ignore the event. Change-Id: Ib647a157ecd852a3520a90ffba5f392ae3b33e1e Closes-Bug: #1768322 (cherry picked from commit e5df8e0a1c8c59a487f878942802d3eb36a323d2) --- src/vnsw/agent/pkt/flow_proto.cc | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/vnsw/agent/pkt/flow_proto.cc b/src/vnsw/agent/pkt/flow_proto.cc index 1a68e56db79..a9149946f6c 100644 --- a/src/vnsw/agent/pkt/flow_proto.cc +++ b/src/vnsw/agent/pkt/flow_proto.cc @@ -426,11 +426,28 @@ bool FlowProto::FlowEventHandler(FlowEvent *req, FlowTable *table) { case FlowEvent::FLOW_MESSAGE: { FlowEntry *flow = req->flow(); - FlowTaskMsg *flow_msg = new FlowTaskMsg(flow); - PktInfoPtr pkt_info(new PktInfo(PktHandler::FLOW, flow_msg)); - FlowHandler *handler = new FlowHandler(agent(), pkt_info, io_, + // process event only for forward flow with same gen_id + // it may happen that after enqueued for recompute, + // flow become reverse flow when the following sequence of + // events occur. + // 1. route is changed , flow is enqueued for recompute + // 2. flow get evicted in vrouter + // 3. traffic is received for reverse flow and get the same flow handle + // 4. since flow handle is same , existing flow entries in agent won't + // be deleted but forward flow become reverse and vice versa + // added check to process events only if gen id matches, + // otherwise ignore it. added assertion not to process reverseflow + // at this stage as we only enqueue forward flows. + + if ((flow->flow_handle() == req->flow_handle()) && + (flow->gen_id() == req->gen_id())) { + assert(flow->is_flags_set(FlowEntry::ReverseFlow) == false); + FlowTaskMsg *flow_msg = new FlowTaskMsg(flow); + PktInfoPtr pkt_info(new PktInfo(PktHandler::FLOW, flow_msg)); + FlowHandler *handler = new FlowHandler(agent(), pkt_info, io_, this, table->table_index()); - RunProtoHandler(handler); + RunProtoHandler(handler); + } break; } @@ -607,7 +624,8 @@ void FlowProto::KSyncEventRequest(KSyncEntry *ksync_entry, } void FlowProto::MessageRequest(FlowEntry *flow) { - EnqueueFlowEvent(new FlowEvent(FlowEvent::FLOW_MESSAGE, flow)); + EnqueueFlowEvent(new FlowEvent(FlowEvent::FLOW_MESSAGE, flow, + flow->flow_handle(), flow->gen_id())); return; } From 233a7f271c02433929fda8059b6c1e1e9702eeb6 Mon Sep 17 00:00:00 2001 From: Hari Prasad Killi Date: Wed, 6 Sep 2017 10:48:38 +0530 Subject: [PATCH 63/68] Increase the task monitor timeout In the crash seen in this bug, task monitor asserts although the task activity seems to be going fine. Agent in not in gdb, no system time change is seen (even if server is healvily loaded this should not be seen). As no other task monitor bug is seen, increasing the timeout to 20s. Change-Id: Iceb277c2b25c3041175c158efa3cb1c4bbbf91d3 partial-bug: #1714744 (cherry picked from commit 28a0fa2e3d44f08befcda3284d8e9f8a984ed67b) --- src/vnsw/agent/cmn/agent.cc | 2 +- src/vnsw/agent/cmn/agent.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vnsw/agent/cmn/agent.cc b/src/vnsw/agent/cmn/agent.cc index 18d92280393..b16486d27ed 100644 --- a/src/vnsw/agent/cmn/agent.cc +++ b/src/vnsw/agent/cmn/agent.cc @@ -475,7 +475,7 @@ void Agent::InitDone() { // Initiate a monitor which asserts if no task is spawned for a given time. if (task_monitor_timeout_msec_) { scheduler->EnableMonitor(event_manager(), tbb_keepawake_timeout_, - task_monitor_timeout_msec_, 100); + task_monitor_timeout_msec_, 400); } } diff --git a/src/vnsw/agent/cmn/agent.h b/src/vnsw/agent/cmn/agent.h index e5334fc47c8..f8bfbe3eb65 100644 --- a/src/vnsw/agent/cmn/agent.h +++ b/src/vnsw/agent/cmn/agent.h @@ -288,7 +288,7 @@ class Agent { // Max number of threads static const uint32_t kMaxTbbThreads = 8; static const uint32_t kDefaultTbbKeepawakeTimeout = (20); //time-millisecs - static const uint32_t kDefaultTaskMonitorTimeout = (5000); //time-millisecs + static const uint32_t kDefaultTaskMonitorTimeout = (20000); //time-millisecs // Default number of tx-buffers on pkt0 interface static const uint32_t kPkt0TxBufferCount = 1000; // Default value for cleanup of stale interface entries From 555d99945ef75636329ab5d72b8befc8612f3b48 Mon Sep 17 00:00:00 2001 From: Filip Pytloun Date: Mon, 10 Oct 2016 14:39:29 +0200 Subject: [PATCH 64/68] Fix build with newer TBB Partial-Bug: #1603881 Change-Id: Iefe4d410cbf5cfda21510d44b6cafac8bd31c33f (cherry picked from commit db50e81685290df3da491b31e51d0c5a20c63c0f) --- src/vnsw/agent/vrouter/ksync/ksync_flow_index_manager.cc | 4 +++- src/vnsw/agent/vrouter/ksync/ksync_flow_index_manager.h | 4 +--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vnsw/agent/vrouter/ksync/ksync_flow_index_manager.cc b/src/vnsw/agent/vrouter/ksync/ksync_flow_index_manager.cc index 113f8afe358..9c67db4e0cc 100644 --- a/src/vnsw/agent/vrouter/ksync/ksync_flow_index_manager.cc +++ b/src/vnsw/agent/vrouter/ksync/ksync_flow_index_manager.cc @@ -26,12 +26,14 @@ KSyncFlowIndexManager::KSyncFlowIndexManager(KSync *ksync) : } KSyncFlowIndexManager::~KSyncFlowIndexManager() { + if (count_ > 0) + delete [] index_list_; } void KSyncFlowIndexManager::InitDone(uint32_t count) { proto_ = ksync_->agent()->pkt()->get_flow_proto(); count_ = count; - index_list_.resize(count); + index_list_ = new struct IndexEntry[count_]; sm_log_count_ = ksync_->agent()->params()->flow_index_sm_log_count(); } diff --git a/src/vnsw/agent/vrouter/ksync/ksync_flow_index_manager.h b/src/vnsw/agent/vrouter/ksync/ksync_flow_index_manager.h index fec6d95300c..9874a35285c 100644 --- a/src/vnsw/agent/vrouter/ksync/ksync_flow_index_manager.h +++ b/src/vnsw/agent/vrouter/ksync/ksync_flow_index_manager.h @@ -40,8 +40,6 @@ class KSyncFlowIndexManager { tbb::mutex mutex_; FlowEntryPtr owner_; }; - typedef std::vector IndexList; - KSyncFlowIndexManager(KSync *ksync); virtual ~KSyncFlowIndexManager(); void InitDone(uint32_t count); @@ -68,7 +66,7 @@ class KSyncFlowIndexManager { KSync *ksync_; FlowProto *proto_; uint32_t count_; - IndexList index_list_; + struct IndexEntry *index_list_; uint16_t sm_log_count_; }; From 5095a90434e3db7a58597a331c2f73972b9c0583 Mon Sep 17 00:00:00 2001 From: Ananth Suryanarayana Date: Wed, 19 Sep 2018 14:25:24 -0700 Subject: [PATCH 65/68] Do not insert already sleeping tbb task back into sleeping list During testing, it was found that tbb sleeping threads singly linked list was corrupted and had become circular. This seemingly caused my_slack count to get permanently stuck at -1, as the sleeping list traversal would potentially never end. During testing, using a specific assert, it was confirmed that duplicate insertion did happen. Fixed it by modifying the sleeing threads singly linked list into a doubly linked list and then making sure that a thread if already in the list is never prepended back as the head of the list. https://github.com/01org/tbb/issues/86 Change-Id: I3375b8be324245c329a9bd3a1f001a38576f617d Closes-Bug: #1684993 --- lib/SConscript | 6 +----- lib/hiredis/SConscript | 2 +- lib/tbb/SConscript | 3 ++- src/SConscript | 7 +------ src/vnsw/agent/init/agent_param.cc | 2 ++ src/vnsw/agent/oper/mirror_table.cc | 2 +- 6 files changed, 8 insertions(+), 14 deletions(-) diff --git a/lib/SConscript b/lib/SConscript index d8df0f56fa5..7c8e020c6af 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -11,6 +11,7 @@ subdirs = [ 'rapidjson', 'thrift', 'openvswitch', + 'tbb', ] (distname, version, _) = platform.dist() @@ -23,10 +24,6 @@ elif distname == 'centos' and LooseVersion(version) >= LooseVersion('7.0'): 'gperftools', 'log4cplus', ] -elif distname == 'fedora' and LooseVersion(version) >= LooseVersion('20'): - pass -elif distname == 'SuSE' and LooseVersion(version) >= LooseVersion('12'): - subdirs += ['tbb'] else: subdirs += [ 'boost', @@ -34,7 +31,6 @@ else: 'gperftools', 'libxml2', 'log4cplus', - 'tbb', 'icu', ] diff --git a/lib/hiredis/SConscript b/lib/hiredis/SConscript index 57df546686b..7f951f0a697 100644 --- a/lib/hiredis/SConscript +++ b/lib/hiredis/SConscript @@ -8,7 +8,7 @@ hiredis_path = '#/third_party/hiredis-0.11.0' hiredis_asio_adapter_path = '#/third_party/hiredis-boostasio-adapter' include = [ third_party_path, hiredis_path, hiredis_path + '/adapters', - '#/build/include', '#/third_party/tbb40_20111130oss/include' ] + '#/build/include', '#/third_party/tbb-2018_U5/include' ] env.Append(CPPPATH = include) env.Append(CCFLAGS = ['-g']) diff --git a/lib/tbb/SConscript b/lib/tbb/SConscript index 8e752fc2f23..ee3fafd1ab3 100644 --- a/lib/tbb/SConscript +++ b/lib/tbb/SConscript @@ -5,7 +5,8 @@ import re import subprocess import sys -vpath = '#/third_party/tbb40_20111130oss' +#vpath = '#/third_party/tbb40_20111130oss' +vpath = '#/third_party/tbb-2018_U5' env = DefaultEnvironment() diff --git a/src/SConscript b/src/SConscript index 6200220ef5f..e48a4cc0cad 100644 --- a/src/SConscript +++ b/src/SConscript @@ -45,15 +45,10 @@ include = ['#/controller/src', '#/build/include', '#controller/lib'] libpath = ['#/build/lib'] -libs = ['boost_system', 'log4cplus', 'pthread'] +libs = ['boost_system', 'log4cplus', 'pthread', 'tbb'] common = DefaultEnvironment().Clone() -if common['OPT'] == 'production' or common.UseSystemTBB(): - libs.append('tbb') -else: - libs.append('tbb_debug') - common.Append(LIBPATH = libpath) common.Prepend(LIBS = libs) common.Append(CCFLAGS = '-Wall -Werror -Wsign-compare') diff --git a/src/vnsw/agent/init/agent_param.cc b/src/vnsw/agent/init/agent_param.cc index 65f8f4350c2..7063a952df2 100644 --- a/src/vnsw/agent/init/agent_param.cc +++ b/src/vnsw/agent/init/agent_param.cc @@ -543,6 +543,8 @@ void AgentParam::ParseTaskSection() { "TASK.tbb_keepawake_timeout")) { tbb_keepawake_timeout_ = Agent::kDefaultTbbKeepawakeTimeout; } + GetValueFromTree(task_monitor_timeout_msec_, + "TASK.task_monitor_timeout"); } void AgentParam::ParseMetadataProxy() { diff --git a/src/vnsw/agent/oper/mirror_table.cc b/src/vnsw/agent/oper/mirror_table.cc index 5c07c4c3447..7928c965b7a 100644 --- a/src/vnsw/agent/oper/mirror_table.cc +++ b/src/vnsw/agent/oper/mirror_table.cc @@ -59,7 +59,7 @@ DBEntry *MirrorTable::Add(const DBRequest *req) { bool MirrorTable::OnChange(MirrorEntry *mirror_entry) { bool ret = false; - NextHop *nh; + NextHop *nh = NULL; bool valid_nh = false; if (mirror_entry->mirror_flags_ == MirrorEntryData::DynamicNH_Without_JuniperHdr) { From bcc20bcc85be8ffdcff69a83372d95370b8b5bfd Mon Sep 17 00:00:00 2001 From: Jeba Paulaiyan Date: Fri, 22 Dec 2017 11:35:38 -0800 Subject: [PATCH 66/68] Revert "Fix port-update API to support mac-address update" This reverts commit 692d9e4f050493e25f2629326322a7662e77e6ea Change-Id: Id985be27253dd0aca929b4f75bc1a0ff292bc3f5 Closes-bug: #1683547 --- .../vnc_openstack/neutron_plugin_db.py | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py b/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py index f04cf99aec4..854eaa21af4 100644 --- a/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py +++ b/src/config/vnc_openstack/vnc_openstack/neutron_plugin_db.py @@ -1968,36 +1968,6 @@ def _port_neutron_to_vnc(self, port_q, net_obj, oper): port_obj.add_virtual_machine_interface_bindings( KeyValuePair(key=k, value=v)) - # Ironic may switch the mac address on a give port - net_id = (port_q.get('network_id') or - port_obj.get_virtual_network_refs()[0]['uuid']) - - # Allow updating of mac addres for baremetal deployments or - # when port is not attached to any VM - allowed_port = ( - 'binding:vnic_type' in port_q and port_q['binding:vnic_type'] == 'baremetal') - if not allowed_port: - port_bindings = port_obj.get_virtual_machine_interface_bindings() - kvps = port_bindings.get_key_value_pair() - for kvp in kvps: - if kvp.key == 'host_id' and kvp.value == "null": - allowed_port = True - break - if 'mac_address' in port_q and allowed_port: - # Ensure that duplicate mac address does not exist on this network - ports = self._virtual_machine_interface_list(back_ref_id=net_id) - for port in ports: - macs = port.get_virtual_machine_interface_mac_addresses() - for mac in macs.get_mac_address(): - #ensure that the same address is not on any other port - if mac == port_q['mac_address'] and port.uuid != port_q['id']: - raise self._raise_contrail_exception("MacAddressInUse", - net_id=net_id, mac=port_q['mac_address']) - # Update the mac accress if no duplicate found - mac_addrs_obj = MacAddressesType() - mac_addrs_obj.set_mac_address([port_q['mac_address']]) - port_obj.set_virtual_machine_interface_mac_addresses(mac_addrs_obj) - if oper == CREATE: if 'port_security_enabled' in port_q: port_security = port_q['port_security_enabled'] From 26ff2b4ad7b316930c31105e2cbef1661f381174 Mon Sep 17 00:00:00 2001 From: Shivayogi Ugaji Date: Wed, 27 Jun 2018 22:37:19 -0700 Subject: [PATCH 67/68] For scaled config schema re-init is taking time more than 2min without yeilding due to which zookeeper/RabbitMQ is timing out. This change yields schema such that it yields only if schema is runnning continuously half of zookeeper timeout value. Also correcting zookeeper client timeout to 120sec to be insync with zookeeper server timeout value. Change-Id: Ibe2778191c73832a704474c1803fdbcead81426a Closes-Bug: #1769728 --- src/config/common/vnc_amqp.py | 6 +++++- src/config/schema-transformer/st_amqp.py | 4 ++-- src/config/schema-transformer/to_bgp.py | 27 ++++++++++++++++++++++-- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/config/common/vnc_amqp.py b/src/config/common/vnc_amqp.py index 85d0b6e9570..36d9065a93d 100644 --- a/src/config/common/vnc_amqp.py +++ b/src/config/common/vnc_amqp.py @@ -11,13 +11,15 @@ class VncAmqpHandle(object): - def __init__(self, logger, db_cls, reaction_map, q_name_prefix, args=None): + def __init__(self, logger, db_cls, reaction_map, q_name_prefix, args=None, + timer_obj=None): self.logger = logger self.db_cls = db_cls self.reaction_map = reaction_map self.q_name_prefix = q_name_prefix self._db_resync_done = gevent.event.Event() self._args = args + self.timer = timer_obj def establish(self): q_name = '.'.join([self.q_name_prefix, socket.gethostname()]) @@ -197,6 +199,8 @@ def evaluate_dependency(self): res_obj = cls.get(res_id) if res_obj is not None: res_obj.evaluate() + if self.timer: + self.timer.timed_yield() def close(self): self._vnc_kombu.shutdown() diff --git a/src/config/schema-transformer/st_amqp.py b/src/config/schema-transformer/st_amqp.py index 99982a2dad8..e123b53b31a 100644 --- a/src/config/schema-transformer/st_amqp.py +++ b/src/config/schema-transformer/st_amqp.py @@ -15,10 +15,10 @@ class STAmqpHandle(VncAmqpHandle): - def __init__(self, logger, reaction_map, args): + def __init__(self, logger, reaction_map, args, timer_obj=None): q_name_prefix = 'schema_transformer' super(STAmqpHandle, self).__init__(logger, DBBaseST, reaction_map, - q_name_prefix, args=args) + q_name_prefix, args=args, timer_obj=timer_obj) def create_msgbus_trace(self, request_id, oper, uuid): self.msg_tracer = MessageBusNotifyTrace(request_id=request_id, diff --git a/src/config/schema-transformer/to_bgp.py b/src/config/schema-transformer/to_bgp.py index 1555c307a99..208290ec2d6 100644 --- a/src/config/schema-transformer/to_bgp.py +++ b/src/config/schema-transformer/to_bgp.py @@ -140,9 +140,26 @@ class SchemaTransformer(object): } } + class STtimer: + def __init__(self, zk_timeout): + self.timeout = time.time() + self.max_time = zk_timeout / 2; + self.total_yield_stats = 0 + # + # Sleep if we are continuously running without yielding + # + def timed_yield(self): + if time.time() > self.timeout: + gevent.sleep(0.001) + self.timeout = time.time() + self.max_time + self.total_yield_stats += 1 + # end timed_yield + + def __init__(self, st_logger=None, args=None): self._args = args self._fabric_rt_inst_obj = None + self.timer_obj = self.STtimer(self._args.zk_timeout) if st_logger is not None: self.logger = st_logger @@ -159,7 +176,7 @@ def __init__(self, st_logger=None, args=None): # Initialize amqp self._vnc_amqp = STAmqpHandle(self.logger, self.REACTION_MAP, - self._args) + self._args, timer_obj=self.timer_obj) self._vnc_amqp.establish() try: # Initialize cassandra @@ -219,6 +236,7 @@ def reinit(self): self.logger.error( "Error while deleting routing instance %s: %s"%( ri.get_fq_name_str(), str(e))) + self.timer_obj.timed_yield() # end for ri sg_list = list(SecurityGroupST.list_vnc_obj()) @@ -248,12 +266,14 @@ def reinit(self): except Exception as e: self.logger.error("Error while deleting acl %s: %s"%( acl.uuid, str(e))) + self.timer_obj.timed_yield() # end for acl gevent.sleep(0.001) for sg in sg_list: try: SecurityGroupST.locate(sg.get_fq_name_str(), sg, sg_acl_dict) + self.timer_obj.timed_yield() except Exception as e: self.logger.error("Error in reinit security-group %s: %s" % ( sg.get_fq_name_str(), str(e))) @@ -263,6 +283,7 @@ def reinit(self): for sg in SecurityGroupST.values(): try: sg.update_policy_entries() + self.timer_obj.timed_yield() except Exception as e: self.logger.error("Error in updating SG policies %s: %s" % ( sg.name, str(e))) @@ -336,6 +357,7 @@ def reinit(self): for vn_obj in VirtualNetworkST.values(): try: vn_obj.evaluate() + self.timer_obj.timed_yield() except Exception as e: self.logger.error("Error in reinit evaluate virtual network %s: %s" % ( vn_obj.name, str(e))) @@ -345,6 +367,7 @@ def reinit(self): for obj in cls.values(): try: obj.evaluate() + self.timer_obj.timed_yield() except Exception as e: self.logger.error("Error in reinit evaluate %s %s: %s" % ( cls.obj_type, obj.name, str(e))) @@ -419,7 +442,7 @@ def parse_args(args_str): 'kombu_ssl_keyfile': '', 'kombu_ssl_certfile': '', 'kombu_ssl_ca_certs': '', - 'zk_timeout': 400, + 'zk_timeout': 120, 'logical_routers_enabled': True, 'acl_direction_comp': False, } From 1125ad7728bc3683740959ef8570ab833244a5da Mon Sep 17 00:00:00 2001 From: Anand Narayanan Rao Date: Sat, 10 Nov 2018 01:19:30 +0530 Subject: [PATCH 68/68] Protect against invalid ICMP, IGMP and ICMPv6 pkts. In customer setup an ICMPv6 pkt was received with IPv4 header leading to crash. Was able to repro the issue with UT test case added. Change-Id: Id3c6c3c437ef068685dda0e9114806f5b2453955 Closes-Bug: #1792057 --- src/vnsw/agent/pkt/pkt_handler.cc | 13 +++++++++++++ src/vnsw/agent/pkt/test/test_pkt.cc | 25 +++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/vnsw/agent/pkt/pkt_handler.cc b/src/vnsw/agent/pkt/pkt_handler.cc index 8204fef76f8..e5f8d444155 100644 --- a/src/vnsw/agent/pkt/pkt_handler.cc +++ b/src/vnsw/agent/pkt/pkt_handler.cc @@ -616,6 +616,19 @@ int PktHandler::ParseUserPkt(PktInfo *pkt_info, Interface *intf, // IP Packets len += ParseIpPacket(pkt_info, pkt_type, (pkt + len)); + + // For ICMP, IGMP, make sure IP is IPv4, else fail the parsing + // so that we don't go ahead and access the ip header later + if (((pkt_info->ip_proto == IPPROTO_ICMP) || + (pkt_info->ip_proto == IPPROTO_IGMP)) && (!pkt_info->ip)) { + return -1; + } + // If ip proto is ICMPv6, then make sure IP is IPv6, else fail + // the parsing so that we don't go ahead and access the ip6 header + // later + if ((pkt_info->ip_proto == IPPROTO_ICMPV6) && (!pkt_info->ip6)) { + return -1; + } // If packet is an IP fragment and not flow trap, ignore it if (IgnoreFragmentedPacket(pkt_info)) { diff --git a/src/vnsw/agent/pkt/test/test_pkt.cc b/src/vnsw/agent/pkt/test/test_pkt.cc index 3d303f643bb..45d5c65aa91 100644 --- a/src/vnsw/agent/pkt/test/test_pkt.cc +++ b/src/vnsw/agent/pkt/test/test_pkt.cc @@ -175,6 +175,31 @@ TEST_F(PktTest, FlowAdd_1) { DeleteVmportEnv(input, 1, true, 1); } +TEST_F(PktTest, InvalidICMPv6) { + struct PortInfo input[] = { + {"vnet1", 1, "1.1.1.10", "00:00:00:01:01:01", 1, 1}, + }; + + client->Reset(); + CreateVmportEnv(input, 1, 1); + client->WaitForIdle(); + + EXPECT_TRUE(VmPortActive(input, 0)); + EXPECT_TRUE(VmPortPolicyEnable(input, 0)); + EXPECT_EQ(4U, Agent::GetInstance()->interface_table()->Size()); + EXPECT_EQ(1U, Agent::GetInstance()->vm_table()->Size()); + EXPECT_EQ(1U, Agent::GetInstance()->vn_table()->Size()); + EXPECT_EQ(1U, PortSubscribeSize(agent_)); + + // Generate packet and enqueue + VmInterface *intf = VmInterfaceGet(input[0].intf_id); + assert(intf); + TxIpPacket(intf->id(), "1.1.1.10", "1.1.1.1", 58); + client->WaitForIdle(); + + DeleteVmportEnv(input, 1, true, 1); +} + TEST_F(PktTest, tx_no_vlan_1) { int len; PktInfo pkt_info(agent_, 1024, PktHandler::ARP, 0);